From e9e7c964655015819e0120694465928df1abefb0 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 8 Oct 2024 19:09:28 +0200 Subject: [PATCH 001/126] Create GitHub Pages docs with Sphinx (#11888) --- .devcontainer/Dockerfile | 2 +- .devcontainer/devcontainer.json | 2 +- .github/CODEOWNERS | 2 - .github/workflows/build.yml | 3 +- .github/workflows/sphinx.yml | 50 +++++++++ CONTRIBUTING.md | 2 +- DIRECTORY.md | 3 + LICENSE.md | 2 +- docs/{source => }/__init__.py | 0 docs/conf.py | 3 + financial/{ABOUT.md => README.md} | 2 +- index.md | 10 ++ .../{local_weighted_learning.md => README.md} | 0 pyproject.toml | 106 +++++++++++++++++- requirements.txt | 1 + source/__init__.py | 0 16 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/sphinx.yml rename docs/{source => }/__init__.py (100%) create mode 100644 docs/conf.py rename financial/{ABOUT.md => README.md} (97%) create mode 100644 index.md rename machine_learning/local_weighted_learning/{local_weighted_learning.md => README.md} (100%) delete mode 100644 source/__init__.py diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 6aa0073bf95b..a0bd05f47ec8 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,5 +1,5 @@ # https://github.com/microsoft/vscode-dev-containers/blob/main/containers/python-3/README.md -ARG VARIANT=3.12-bookworm +ARG VARIANT=3.13-bookworm FROM mcr.microsoft.com/vscode/devcontainers/python:${VARIANT} COPY requirements.txt /tmp/pip-tmp/ RUN python3 -m pip install --upgrade pip \ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ae1d4fb7494d..e23263f5b9de 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,7 +7,7 @@ // Update 'VARIANT' to pick a Python version: 3, 3.11, 3.10, 3.9, 3.8 // Append -bullseye or -buster to pin to an OS version. // Use -bullseye variants on local on arm64/Apple Silicon. - "VARIANT": "3.12-bookworm", + "VARIANT": "3.13-bookworm", } }, diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d2ac43c7df31..3cc25d1bae1c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -9,8 +9,6 @@ /.* @cclauss -# /arithmetic_analysis/ - # /backtracking/ # /bit_manipulation/ diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f54cc982d1ec..b5703e2f1ab6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,12 +25,13 @@ jobs: - name: Run tests # TODO: #8818 Re-enable quantum tests run: pytest - --ignore=quantum/q_fourier_transform.py --ignore=computer_vision/cnn_classification.py + --ignore=docs/conf.py --ignore=dynamic_programming/k_means_clustering_tensorflow.py --ignore=machine_learning/lstm/lstm_prediction.py --ignore=neural_network/input_data.py --ignore=project_euler/ + --ignore=quantum/q_fourier_transform.py --ignore=scripts/validate_solutions.py --cov-report=term-missing:skip-covered --cov=. . diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml new file mode 100644 index 000000000000..9dfe344f9743 --- /dev/null +++ b/.github/workflows/sphinx.yml @@ -0,0 +1,50 @@ +name: sphinx + +on: + # Triggers the workflow on push or pull request events but only for the "master" branch + push: + branches: ["master"] + pull_request: + branches: ["master"] + # Or manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build_docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.13 + allow-prereleases: true + - run: pip install --upgrade pip + - run: pip install myst-parser sphinx-autoapi sphinx-pyproject + - uses: actions/configure-pages@v5 + - run: sphinx-build -c docs . docs/_build/html + - uses: actions/upload-pages-artifact@v3 + with: + path: docs/_build/html + + deploy_docs: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + if: github.event_name != 'pull_request' + needs: build_docs + runs-on: ubuntu-latest + steps: + - uses: actions/deploy-pages@v4 + id: deployment diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b5113212929a..3df39f95b784 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -77,7 +77,7 @@ pre-commit run --all-files --show-diff-on-failure We want your work to be readable by others; therefore, we encourage you to note the following: -- Please write in Python 3.12+. For instance: `print()` is a function in Python 3 so `print "Hello"` will *not* work but `print("Hello")` will. +- Please write in Python 3.13+. For instance: `print()` is a function in Python 3 so `print "Hello"` will *not* work but `print("Hello")` will. - Please focus hard on the naming of functions, classes, and variables. Help your reader by using __descriptive names__ that can help you to remove redundant comments. - Single letter variable names are *old school* so please avoid them unless their life only spans a few lines. - Expand acronyms because `gcd()` is hard to understand but `greatest_common_divisor()` is not. diff --git a/DIRECTORY.md b/DIRECTORY.md index 0a3be2a06533..f0a34a553946 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -351,6 +351,9 @@ * [Power](divide_and_conquer/power.py) * [Strassen Matrix Multiplication](divide_and_conquer/strassen_matrix_multiplication.py) +## Docs + * [Conf](docs/conf.py) + ## Dynamic Programming * [Abbreviation](dynamic_programming/abbreviation.py) * [All Construct](dynamic_programming/all_construct.py) diff --git a/LICENSE.md b/LICENSE.md index 2897d02e2a01..de631c3ef333 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -MIT License +## MIT License Copyright (c) 2016-2022 TheAlgorithms and contributors diff --git a/docs/source/__init__.py b/docs/__init__.py similarity index 100% rename from docs/source/__init__.py rename to docs/__init__.py diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 000000000000..f2481f107267 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,3 @@ +from sphinx_pyproject import SphinxConfig + +project = SphinxConfig("../pyproject.toml", globalns=globals()).name diff --git a/financial/ABOUT.md b/financial/README.md similarity index 97% rename from financial/ABOUT.md rename to financial/README.md index f6b0647f8201..e5d3a84c8381 100644 --- a/financial/ABOUT.md +++ b/financial/README.md @@ -1,4 +1,4 @@ -### Interest +# Interest * Compound Interest: "Compound interest is calculated by multiplying the initial principal amount by one plus the annual interest rate raised to the number of compound periods minus one." [Compound Interest](https://www.investopedia.com/) * Simple Interest: "Simple interest paid or received over a certain period is a fixed percentage of the principal amount that was borrowed or lent. " [Simple Interest](https://www.investopedia.com/) diff --git a/index.md b/index.md new file mode 100644 index 000000000000..134520cb94aa --- /dev/null +++ b/index.md @@ -0,0 +1,10 @@ +# TheAlgorithms/Python +```{toctree} +:maxdepth: 2 +:caption: index.md + + +CONTRIBUTING.md +README.md +LICENSE.md +``` diff --git a/machine_learning/local_weighted_learning/local_weighted_learning.md b/machine_learning/local_weighted_learning/README.md similarity index 100% rename from machine_learning/local_weighted_learning/local_weighted_learning.md rename to machine_learning/local_weighted_learning/README.md diff --git a/pyproject.toml b/pyproject.toml index bb8657183164..c57419e79db3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,22 @@ +[project] +name = "thealgorithms-python" +version = "0.0.1" +description = "TheAlgorithms in Python" +authors = [ { name = "TheAlgorithms Contributors" } ] +requires-python = ">=3.13" +classifiers = [ + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.13", + +] +optional-dependencies.docs = [ + "myst-parser", + "sphinx-autoapi", + "sphinx-pyproject", +] + [tool.ruff] -target-version = "py312" +target-version = "py313" output-format = "full" lint.select = [ @@ -113,6 +130,9 @@ lint.pylint.max-statements = 88 # default: 50 ignore-words-list = "3rt,ans,bitap,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" skip = "./.*,*.json,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" +[tool.pyproject-fmt] +max_supported_python = "3.13" + [tool.pytest.ini_options] markers = [ "mat_ops: mark a test as utilizing matrix operations.", @@ -129,3 +149,87 @@ omit = [ "project_euler/*", ] sort = "Cover" + +[tool.sphinx-pyproject] +copyright = "2014, TheAlgorithms" +autoapi_dirs = [ + "audio_filters", + "backtracking", + "bit_manipulation", + "blockchain", + "boolean_algebra", + "cellular_automata", + "ciphers", + "compression", + "computer_vision", + "conversions", + "data_structures", + "digital_image_processing", + "divide_and_conquer", + "dynamic_programming", + "electronics", + "file_transfer", + "financial", + "fractals", + "fuzzy_logic", + "genetic_algorithm", + "geodesy", + "geometry", + "graphics", + "graphs", + "greedy_methods", + "hashes", + "knapsack", + "linear_algebra", + "linear_programming", + "machine_learning", + "maths", + "matrix", + "networking_flow", + "neural_network", + "other", + "physics", + "project_euler", + "quantum", + "scheduling", + "searches", + "sorts", + "strings", + "web_programming", +] +autoapi_member_order = "groupwise" +# autoapi_python_use_implicit_namespaces = true +exclude_patterns = [ + ".*/*", + "docs/", +] +extensions = [ + "autoapi.extension", + "myst_parser", +] +html_static_path = [ "_static" ] +html_theme = "alabaster" +myst_enable_extensions = [ + "amsmath", + "attrs_inline", + "colon_fence", + "deflist", + "dollarmath", + "fieldlist", + "html_admonition", + "html_image", + # "linkify", + "replacements", + "smartquotes", + "strikethrough", + "substitution", + "tasklist", +] +myst_fence_as_directive = [ + "include", +] +templates_path = [ "_templates" ] +[tool.sphinx-pyproject.source_suffix] +".rst" = "restructuredtext" +# ".txt" = "markdown" +".md" = "markdown" diff --git a/requirements.txt b/requirements.txt index afbf25ba6edc..6754363332c4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,6 +15,7 @@ requests rich # scikit-fuzzy # uncomment once fuzzy_logic/fuzzy_operations.py is fixed scikit-learn +sphinx_pyproject statsmodels sympy tensorflow ; python_version < '3.13' diff --git a/source/__init__.py b/source/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 From 03a42510b01c574292ca9c6525cbf0572ff5a2a5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 22:42:24 +0200 Subject: [PATCH 002/126] [pre-commit.ci] pre-commit autoupdate (#12071) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/tox-dev/pyproject-fmt: 2.2.4 → 2.3.0](https://github.com/tox-dev/pyproject-fmt/compare/2.2.4...2.3.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 77541027afb3..e1d185fabc12 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "2.2.4" + rev: "2.3.0" hooks: - id: pyproject-fmt From 6e24935f8860965dd7f2f5a50fd05724e84e9e8d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:22:34 +0200 Subject: [PATCH 003/126] [pre-commit.ci] pre-commit autoupdate (#12234) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.9 → v0.7.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.9...v0.7.0) - [github.com/tox-dev/pyproject-fmt: 2.3.0 → 2.4.3](https://github.com/tox-dev/pyproject-fmt/compare/2.3.0...2.4.3) - [github.com/abravalheri/validate-pyproject: v0.20.2 → v0.21](https://github.com/abravalheri/validate-pyproject/compare/v0.20.2...v0.21) - [github.com/pre-commit/mirrors-mypy: v1.11.2 → v1.12.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.2...v1.12.1) * project_euler/problem_047/sol1.py: def solution(n: int = 4) -> int | None: * Update sol1.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 8 ++++---- project_euler/problem_047/sol1.py | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e1d185fabc12..a849de0c4e16 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.9 + rev: v0.7.0 hooks: - id: ruff - id: ruff-format @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "2.3.0" + rev: "2.4.3" hooks: - id: pyproject-fmt @@ -42,12 +42,12 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.20.2 + rev: v0.21 hooks: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.2 + rev: v1.12.1 hooks: - id: mypy args: diff --git a/project_euler/problem_047/sol1.py b/project_euler/problem_047/sol1.py index 4ecd4f4b44c1..d174de27dcd0 100644 --- a/project_euler/problem_047/sol1.py +++ b/project_euler/problem_047/sol1.py @@ -24,7 +24,7 @@ def unique_prime_factors(n: int) -> set: """ Find unique prime factors of an integer. - Tests include sorting because only the set really matters, + Tests include sorting because only the set matters, not the order in which it is produced. >>> sorted(set(unique_prime_factors(14))) [2, 7] @@ -58,7 +58,7 @@ def upf_len(num: int) -> int: def equality(iterable: list) -> bool: """ - Check equality of ALL elements in an iterable + Check the equality of ALL elements in an iterable >>> equality([1, 2, 3, 4]) False >>> equality([2, 2, 2, 2]) @@ -69,7 +69,7 @@ def equality(iterable: list) -> bool: return len(set(iterable)) in (0, 1) -def run(n: int) -> list: +def run(n: int) -> list[int]: """ Runs core process to find problem solution. >>> run(3) @@ -77,7 +77,7 @@ def run(n: int) -> list: """ # Incrementor variable for our group list comprehension. - # This serves as the first number in each list of values + # This is the first number in each list of values # to test. base = 2 @@ -85,7 +85,7 @@ def run(n: int) -> list: # Increment each value of a generated range group = [base + i for i in range(n)] - # Run elements through out unique_prime_factors function + # Run elements through the unique_prime_factors function # Append our target number to the end. checker = [upf_len(x) for x in group] checker.append(n) @@ -98,7 +98,7 @@ def run(n: int) -> list: base += 1 -def solution(n: int = 4) -> int: +def solution(n: int = 4) -> int | None: """Return the first value of the first four consecutive integers to have four distinct prime factors each. >>> solution() From 52602ea5b6dd8179aa662c002891c6506f519435 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 21:27:00 +0100 Subject: [PATCH 004/126] [pre-commit.ci] pre-commit autoupdate (#12313) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.0 → v0.7.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.0...v0.7.1) - [github.com/tox-dev/pyproject-fmt: 2.4.3 → v2.4.3](https://github.com/tox-dev/pyproject-fmt/compare/2.4.3...v2.4.3) - [github.com/abravalheri/validate-pyproject: v0.21 → v0.22](https://github.com/abravalheri/validate-pyproject/compare/v0.21...v0.22) - [github.com/pre-commit/mirrors-mypy: v1.12.1 → v1.13.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.12.1...v1.13.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a849de0c4e16..0828b715106d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.0 + rev: v0.7.1 hooks: - id: ruff - id: ruff-format @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "2.4.3" + rev: "v2.4.3" hooks: - id: pyproject-fmt @@ -42,12 +42,12 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.21 + rev: v0.22 hooks: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.12.1 + rev: v1.13.0 hooks: - id: mypy args: From a19bede190ddb4fa3c1c9850b612a47fc69d6709 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 1 Nov 2024 13:40:09 +0100 Subject: [PATCH 005/126] Add scripts/find_git_conflicts.sh (#12343) --- scripts/find_git_conflicts.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100755 scripts/find_git_conflicts.sh diff --git a/scripts/find_git_conflicts.sh b/scripts/find_git_conflicts.sh new file mode 100755 index 000000000000..8af33fa75279 --- /dev/null +++ b/scripts/find_git_conflicts.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Replace with your repository (format: owner/repo) +REPO="TheAlgorithms/Python" + +# Fetch open pull requests with conflicts into a variable +echo "Checking for pull requests with conflicts in $REPO..." + +prs=$(gh pr list --repo "$REPO" --state open --json number,title,mergeable --jq '.[] | select(.mergeable == "CONFLICTING") | {number, title}' --limit 500) + +# Process each conflicting PR +echo "$prs" | jq -c '.[]' | while read -r pr; do + PR_NUMBER=$(echo "$pr" | jq -r '.number') + PR_TITLE=$(echo "$pr" | jq -r '.title') + echo "PR #$PR_NUMBER - $PR_TITLE has conflicts." +done From 3e9ca92ca972bbe752d32b43c71a88789dce94c0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 21:09:03 +0100 Subject: [PATCH 006/126] [pre-commit.ci] pre-commit autoupdate (#12349) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.1 → v0.7.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.1...v0.7.2) - [github.com/tox-dev/pyproject-fmt: v2.4.3 → v2.5.0](https://github.com/tox-dev/pyproject-fmt/compare/v2.4.3...v2.5.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0828b715106d..f112ee553b51 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.1 + rev: v0.7.2 hooks: - id: ruff - id: ruff-format @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "v2.4.3" + rev: "v2.5.0" hooks: - id: pyproject-fmt From e3f3d668be4ada7aee82eea0bc75c50436c1ab3a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 21:05:50 +0100 Subject: [PATCH 007/126] [pre-commit.ci] pre-commit autoupdate (#12370) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.2 → v0.7.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.2...v0.7.3) - [github.com/abravalheri/validate-pyproject: v0.22 → v0.23](https://github.com/abravalheri/validate-pyproject/compare/v0.22...v0.23) * Update sudoku_solver.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 4 ++-- data_structures/arrays/sudoku_solver.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f112ee553b51..9d794473cc01 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.2 + rev: v0.7.3 hooks: - id: ruff - id: ruff-format @@ -42,7 +42,7 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.22 + rev: v0.23 hooks: - id: validate-pyproject diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index a8157a520c97..70bcdc748195 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -172,7 +172,7 @@ def unitsolved(unit): def from_file(filename, sep="\n"): "Parse a file into a list of strings, separated by sep." - return open(filename).read().strip().split(sep) # noqa: SIM115 + return open(filename).read().strip().split(sep) def random_puzzle(assignments=17): From e3bd7721c8241a6db77254bac44757dced1b96f8 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 15 Nov 2024 14:59:14 +0100 Subject: [PATCH 008/126] `validate_filenames.py` Shebang `python` for Windows (#12371) --- scripts/validate_filenames.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/validate_filenames.py b/scripts/validate_filenames.py index 0890024dd349..e76b4dbfe288 100755 --- a/scripts/validate_filenames.py +++ b/scripts/validate_filenames.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!python import os try: From f3f32ae3ca818f64de2ed3267803882956681044 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 22:07:12 +0100 Subject: [PATCH 009/126] [pre-commit.ci] pre-commit autoupdate (#12385) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.3 → v0.7.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.3...v0.7.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9d794473cc01..6ad19f1fdcb1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.3 + rev: v0.7.4 hooks: - id: ruff - id: ruff-format From fc33c505935e9927cffb6142591891f721a7bcd9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 21:46:20 +0100 Subject: [PATCH 010/126] [pre-commit.ci] pre-commit autoupdate (#12398) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.4.10 → v0.5.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.10...v0.5.0) - [github.com/pre-commit/mirrors-mypy: v1.10.0 → v1.10.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.0...v1.10.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- cellular_automata/conways_game_of_life.py | 6 ++--- ciphers/playfair_cipher.py | 2 +- ciphers/simple_keyword_cypher.py | 2 +- ciphers/transposition_cipher.py | 6 ++--- compression/lempel_ziv.py | 4 ++-- data_structures/arrays/sudoku_solver.py | 2 +- .../binary_tree/binary_tree_traversals.py | 24 ++++++++----------- data_structures/linked_list/deque_doubly.py | 2 +- data_structures/queue/double_ended_queue.py | 2 +- docs/source/__init__.py | 0 electronics/electrical_impedance.py | 2 +- graphs/ant_colony_optimization_algorithms.py | 6 ++--- graphs/basic_graphs.py | 22 ++++++++--------- graphs/minimum_spanning_tree_boruvka.py | 8 +++---- hashes/md5.py | 2 +- machine_learning/frequent_pattern_growth.py | 4 ++-- maths/collatz_sequence.py | 2 +- maths/prime_numbers.py | 6 ++--- maths/volume.py | 2 +- neural_network/input_data.py | 10 ++++---- physics/basic_orbital_capture.py | 9 ++++--- physics/grahams_law.py | 2 +- project_euler/problem_025/sol2.py | 2 +- project_euler/problem_123/sol1.py | 2 +- pyproject.toml | 1 + source/__init__.py | 0 strings/frequency_finder.py | 2 +- strings/min_cost_string_conversion.py | 8 +++---- web_programming/fetch_jobs.py | 2 +- 30 files changed, 66 insertions(+), 78 deletions(-) create mode 100644 docs/source/__init__.py create mode 100644 source/__init__.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6ad19f1fdcb1..64d9a833cd21 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.4 + rev: v0.8.0 hooks: - id: ruff - id: ruff-format diff --git a/cellular_automata/conways_game_of_life.py b/cellular_automata/conways_game_of_life.py index 364a34c3aba6..485f0d47bd8b 100644 --- a/cellular_automata/conways_game_of_life.py +++ b/cellular_automata/conways_game_of_life.py @@ -58,10 +58,8 @@ def new_generation(cells: list[list[int]]) -> list[list[int]]: # 3. All other live cells die in the next generation. # Similarly, all other dead cells stay dead. alive = cells[i][j] == 1 - if ( - (alive and 2 <= neighbour_count <= 3) - or not alive - and neighbour_count == 3 + if (alive and 2 <= neighbour_count <= 3) or ( + not alive and neighbour_count == 3 ): next_generation_row.append(1) else: diff --git a/ciphers/playfair_cipher.py b/ciphers/playfair_cipher.py index 86b45bc4fb6a..d48f113f02e0 100644 --- a/ciphers/playfair_cipher.py +++ b/ciphers/playfair_cipher.py @@ -24,7 +24,7 @@ from collections.abc import Generator, Iterable -def chunker(seq: Iterable[str], size: int) -> Generator[tuple[str, ...], None, None]: +def chunker(seq: Iterable[str], size: int) -> Generator[tuple[str, ...]]: it = iter(seq) while True: chunk = tuple(itertools.islice(it, size)) diff --git a/ciphers/simple_keyword_cypher.py b/ciphers/simple_keyword_cypher.py index 1635471aebd1..9dc624e7762c 100644 --- a/ciphers/simple_keyword_cypher.py +++ b/ciphers/simple_keyword_cypher.py @@ -10,7 +10,7 @@ def remove_duplicates(key: str) -> str: key_no_dups = "" for ch in key: - if ch == " " or ch not in key_no_dups and ch.isalpha(): + if ch == " " or (ch not in key_no_dups and ch.isalpha()): key_no_dups += ch return key_no_dups diff --git a/ciphers/transposition_cipher.py b/ciphers/transposition_cipher.py index f1f07ddc3f35..76178cb6a1bc 100644 --- a/ciphers/transposition_cipher.py +++ b/ciphers/transposition_cipher.py @@ -52,10 +52,8 @@ def decrypt_message(key: int, message: str) -> str: plain_text[col] += symbol col += 1 - if ( - (col == num_cols) - or (col == num_cols - 1) - and (row >= num_rows - num_shaded_boxes) + if (col == num_cols) or ( + (col == num_cols - 1) and (row >= num_rows - num_shaded_boxes) ): col = 0 row += 1 diff --git a/compression/lempel_ziv.py b/compression/lempel_ziv.py index 2751a0ebcdb6..648b029471bd 100644 --- a/compression/lempel_ziv.py +++ b/compression/lempel_ziv.py @@ -35,8 +35,8 @@ def add_key_to_lexicon( lexicon[curr_string + "0"] = last_match_id if math.log2(index).is_integer(): - for curr_key in lexicon: - lexicon[curr_key] = "0" + lexicon[curr_key] + for curr_key, value in lexicon.items(): + lexicon[curr_key] = f"0{value}" lexicon[curr_string + "1"] = bin(index)[2:] diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index 70bcdc748195..7e38e1465728 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -156,7 +156,7 @@ def time_solve(grid): times, results = zip(*[time_solve(grid) for grid in grids]) if (n := len(grids)) > 1: print( - "Solved %d of %d %s puzzles (avg %.2f secs (%d Hz), max %.2f secs)." + "Solved %d of %d %s puzzles (avg %.2f secs (%d Hz), max %.2f secs)." # noqa: UP031 % (sum(results), n, name, sum(times) / n, n / sum(times), max(times)) ) diff --git a/data_structures/binary_tree/binary_tree_traversals.py b/data_structures/binary_tree/binary_tree_traversals.py index 49c208335b2c..5ba149d0cbc6 100644 --- a/data_structures/binary_tree/binary_tree_traversals.py +++ b/data_structures/binary_tree/binary_tree_traversals.py @@ -30,7 +30,7 @@ def make_tree() -> Node | None: return tree -def preorder(root: Node | None) -> Generator[int, None, None]: +def preorder(root: Node | None) -> Generator[int]: """ Pre-order traversal visits root node, left subtree, right subtree. >>> list(preorder(make_tree())) @@ -43,7 +43,7 @@ def preorder(root: Node | None) -> Generator[int, None, None]: yield from preorder(root.right) -def postorder(root: Node | None) -> Generator[int, None, None]: +def postorder(root: Node | None) -> Generator[int]: """ Post-order traversal visits left subtree, right subtree, root node. >>> list(postorder(make_tree())) @@ -56,7 +56,7 @@ def postorder(root: Node | None) -> Generator[int, None, None]: yield root.data -def inorder(root: Node | None) -> Generator[int, None, None]: +def inorder(root: Node | None) -> Generator[int]: """ In-order traversal visits left subtree, root node, right subtree. >>> list(inorder(make_tree())) @@ -69,7 +69,7 @@ def inorder(root: Node | None) -> Generator[int, None, None]: yield from inorder(root.right) -def reverse_inorder(root: Node | None) -> Generator[int, None, None]: +def reverse_inorder(root: Node | None) -> Generator[int]: """ Reverse in-order traversal visits right subtree, root node, left subtree. >>> list(reverse_inorder(make_tree())) @@ -93,7 +93,7 @@ def height(root: Node | None) -> int: return (max(height(root.left), height(root.right)) + 1) if root else 0 -def level_order(root: Node | None) -> Generator[int, None, None]: +def level_order(root: Node | None) -> Generator[int]: """ Returns a list of nodes value from a whole binary tree in Level Order Traverse. Level Order traverse: Visit nodes of the tree level-by-level. @@ -116,9 +116,7 @@ def level_order(root: Node | None) -> Generator[int, None, None]: process_queue.append(node.right) -def get_nodes_from_left_to_right( - root: Node | None, level: int -) -> Generator[int, None, None]: +def get_nodes_from_left_to_right(root: Node | None, level: int) -> Generator[int]: """ Returns a list of nodes value from a particular level: Left to right direction of the binary tree. @@ -128,7 +126,7 @@ def get_nodes_from_left_to_right( [2, 3] """ - def populate_output(root: Node | None, level: int) -> Generator[int, None, None]: + def populate_output(root: Node | None, level: int) -> Generator[int]: if not root: return if level == 1: @@ -140,9 +138,7 @@ def populate_output(root: Node | None, level: int) -> Generator[int, None, None] yield from populate_output(root, level) -def get_nodes_from_right_to_left( - root: Node | None, level: int -) -> Generator[int, None, None]: +def get_nodes_from_right_to_left(root: Node | None, level: int) -> Generator[int]: """ Returns a list of nodes value from a particular level: Right to left direction of the binary tree. @@ -152,7 +148,7 @@ def get_nodes_from_right_to_left( [3, 2] """ - def populate_output(root: Node | None, level: int) -> Generator[int, None, None]: + def populate_output(root: Node | None, level: int) -> Generator[int]: if not root: return if level == 1: @@ -164,7 +160,7 @@ def populate_output(root: Node | None, level: int) -> Generator[int, None, None] yield from populate_output(root, level) -def zigzag(root: Node | None) -> Generator[int, None, None]: +def zigzag(root: Node | None) -> Generator[int]: """ ZigZag traverse: Returns a list of nodes value from left to right and right to left, alternatively. diff --git a/data_structures/linked_list/deque_doubly.py b/data_structures/linked_list/deque_doubly.py index 2b9d70c223c4..e554ead91c5a 100644 --- a/data_structures/linked_list/deque_doubly.py +++ b/data_structures/linked_list/deque_doubly.py @@ -12,7 +12,7 @@ class _DoublyLinkedBase: """A Private class (to be inherited)""" class _Node: - __slots__ = "_prev", "_data", "_next" + __slots__ = "_data", "_next", "_prev" def __init__(self, link_p, element, link_n): self._prev = link_p diff --git a/data_structures/queue/double_ended_queue.py b/data_structures/queue/double_ended_queue.py index 607d0bda3df4..c28d46c65168 100644 --- a/data_structures/queue/double_ended_queue.py +++ b/data_structures/queue/double_ended_queue.py @@ -33,7 +33,7 @@ class Deque: the number of nodes """ - __slots__ = ("_front", "_back", "_len") + __slots__ = ("_back", "_front", "_len") @dataclass class _Node: diff --git a/docs/source/__init__.py b/docs/source/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/electronics/electrical_impedance.py b/electronics/electrical_impedance.py index 44041ff790b6..4f4f1d308293 100644 --- a/electronics/electrical_impedance.py +++ b/electronics/electrical_impedance.py @@ -6,7 +6,7 @@ from __future__ import annotations -from math import pow, sqrt +from math import pow, sqrt # noqa: A004 def electrical_impedance( diff --git a/graphs/ant_colony_optimization_algorithms.py b/graphs/ant_colony_optimization_algorithms.py index 13637da44874..753f4c0962c8 100644 --- a/graphs/ant_colony_optimization_algorithms.py +++ b/graphs/ant_colony_optimization_algorithms.py @@ -194,10 +194,8 @@ def city_select( IndexError: list index out of range """ probabilities = [] - for city in unvisited_cities: - city_distance = distance( - unvisited_cities[city], next(iter(current_city.values())) - ) + for city, value in unvisited_cities.items(): + city_distance = distance(value, next(iter(current_city.values()))) probability = (pheromone[city][next(iter(current_city.keys()))] ** alpha) * ( (1 / city_distance) ** beta ) diff --git a/graphs/basic_graphs.py b/graphs/basic_graphs.py index 25c8045b3d2b..567fa65040ae 100644 --- a/graphs/basic_graphs.py +++ b/graphs/basic_graphs.py @@ -133,18 +133,18 @@ def dijk(g, s): if len(known) == len(g) - 1: break mini = 100000 - for i in dist: - if i not in known and dist[i] < mini: - mini = dist[i] - u = i + for key, value in dist: + if key not in known and value < mini: + mini = value + u = key known.add(u) for v in g[u]: if v[0] not in known and dist[u] + v[1] < dist.get(v[0], 100000): dist[v[0]] = dist[u] + v[1] path[v[0]] = u - for i in dist: - if i != s: - print(dist[i]) + for key, value in dist.items(): + if key != s: + print(value) """ @@ -255,10 +255,10 @@ def prim(g, s): if len(known) == len(g) - 1: break mini = 100000 - for i in dist: - if i not in known and dist[i] < mini: - mini = dist[i] - u = i + for key, value in dist.items(): + if key not in known and value < mini: + mini = value + u = key known.add(u) for v in g[u]: if v[0] not in known and v[1] < dist.get(v[0], 100000): diff --git a/graphs/minimum_spanning_tree_boruvka.py b/graphs/minimum_spanning_tree_boruvka.py index 3c6888037948..f234d65ab765 100644 --- a/graphs/minimum_spanning_tree_boruvka.py +++ b/graphs/minimum_spanning_tree_boruvka.py @@ -185,12 +185,12 @@ def boruvka_mst(graph): if cheap_edge[set2] == -1 or cheap_edge[set2][2] > weight: cheap_edge[set2] = [head, tail, weight] - for vertex in cheap_edge: - if cheap_edge[vertex] != -1: - head, tail, weight = cheap_edge[vertex] + for head_tail_weight in cheap_edge.values(): + if head_tail_weight != -1: + head, tail, weight = head_tail_weight if union_find.find(head) != union_find.find(tail): union_find.union(head, tail) - mst_edges.append(cheap_edge[vertex]) + mst_edges.append(head_tail_weight) num_components = num_components - 1 mst = Graph.build(edges=mst_edges) return mst diff --git a/hashes/md5.py b/hashes/md5.py index 622a50d290e1..f9d802ff0308 100644 --- a/hashes/md5.py +++ b/hashes/md5.py @@ -131,7 +131,7 @@ def preprocess(message: bytes) -> bytes: return bit_string -def get_block_words(bit_string: bytes) -> Generator[list[int], None, None]: +def get_block_words(bit_string: bytes) -> Generator[list[int]]: """ Splits bit string into blocks of 512 chars and yields each block as a list of 32-bit words diff --git a/machine_learning/frequent_pattern_growth.py b/machine_learning/frequent_pattern_growth.py index 947f8692f298..fae2df16efb1 100644 --- a/machine_learning/frequent_pattern_growth.py +++ b/machine_learning/frequent_pattern_growth.py @@ -107,8 +107,8 @@ def create_tree(data_set: list, min_sup: int = 1) -> tuple[TreeNode, dict]: if not (freq_item_set := set(header_table)): return TreeNode("Null Set", 1, None), {} - for k in header_table: - header_table[k] = [header_table[k], None] + for key, value in header_table.items(): + header_table[key] = [value, None] fp_tree = TreeNode("Null Set", 1, None) # Parent is None for the root node for tran_set in data_set: diff --git a/maths/collatz_sequence.py b/maths/collatz_sequence.py index b47017146a1e..b00dca8d70b7 100644 --- a/maths/collatz_sequence.py +++ b/maths/collatz_sequence.py @@ -17,7 +17,7 @@ from collections.abc import Generator -def collatz_sequence(n: int) -> Generator[int, None, None]: +def collatz_sequence(n: int) -> Generator[int]: """ Generate the Collatz sequence starting at n. >>> tuple(collatz_sequence(2.1)) diff --git a/maths/prime_numbers.py b/maths/prime_numbers.py index 38cc6670385d..5ad12baf3dc3 100644 --- a/maths/prime_numbers.py +++ b/maths/prime_numbers.py @@ -2,7 +2,7 @@ from collections.abc import Generator -def slow_primes(max_n: int) -> Generator[int, None, None]: +def slow_primes(max_n: int) -> Generator[int]: """ Return a list of all primes numbers up to max. >>> list(slow_primes(0)) @@ -29,7 +29,7 @@ def slow_primes(max_n: int) -> Generator[int, None, None]: yield i -def primes(max_n: int) -> Generator[int, None, None]: +def primes(max_n: int) -> Generator[int]: """ Return a list of all primes numbers up to max. >>> list(primes(0)) @@ -58,7 +58,7 @@ def primes(max_n: int) -> Generator[int, None, None]: yield i -def fast_primes(max_n: int) -> Generator[int, None, None]: +def fast_primes(max_n: int) -> Generator[int]: """ Return a list of all primes numbers up to max. >>> list(fast_primes(0)) diff --git a/maths/volume.py b/maths/volume.py index 33be9bdd131a..23fcf6be6ef1 100644 --- a/maths/volume.py +++ b/maths/volume.py @@ -6,7 +6,7 @@ from __future__ import annotations -from math import pi, pow +from math import pi, pow # noqa: A004 def vol_cube(side_length: float) -> float: diff --git a/neural_network/input_data.py b/neural_network/input_data.py index f90287fe3f5b..72debabb566a 100644 --- a/neural_network/input_data.py +++ b/neural_network/input_data.py @@ -61,9 +61,8 @@ def _extract_images(f): with gzip.GzipFile(fileobj=f) as bytestream: magic = _read32(bytestream) if magic != 2051: - raise ValueError( - "Invalid magic number %d in MNIST image file: %s" % (magic, f.name) - ) + msg = f"Invalid magic number {magic} in MNIST image file: {f.name}" + raise ValueError(msg) num_images = _read32(bytestream) rows = _read32(bytestream) cols = _read32(bytestream) @@ -102,9 +101,8 @@ def _extract_labels(f, one_hot=False, num_classes=10): with gzip.GzipFile(fileobj=f) as bytestream: magic = _read32(bytestream) if magic != 2049: - raise ValueError( - "Invalid magic number %d in MNIST label file: %s" % (magic, f.name) - ) + msg = f"Invalid magic number {magic} in MNIST label file: {f.name}" + raise ValueError(msg) num_items = _read32(bytestream) buf = bytestream.read(num_items) labels = np.frombuffer(buf, dtype=np.uint8) diff --git a/physics/basic_orbital_capture.py b/physics/basic_orbital_capture.py index a5434b5cb7cb..eb1fdd9d6420 100644 --- a/physics/basic_orbital_capture.py +++ b/physics/basic_orbital_capture.py @@ -1,7 +1,3 @@ -from math import pow, sqrt - -from scipy.constants import G, c, pi - """ These two functions will return the radii of impact for a target object of mass M and radius R as well as it's effective cross sectional area sigma. @@ -14,9 +10,12 @@ cross section for capture as sigma=π*R_capture**2. This algorithm does not account for an N-body problem. - """ +from math import pow, sqrt # noqa: A004 + +from scipy.constants import G, c, pi + def capture_radii( target_body_radius: float, target_body_mass: float, projectile_velocity: float diff --git a/physics/grahams_law.py b/physics/grahams_law.py index 6e5d75127e83..c56359280ea4 100644 --- a/physics/grahams_law.py +++ b/physics/grahams_law.py @@ -14,7 +14,7 @@ (Description adapted from https://en.wikipedia.org/wiki/Graham%27s_law) """ -from math import pow, sqrt +from math import pow, sqrt # noqa: A004 def validate(*values: float) -> bool: diff --git a/project_euler/problem_025/sol2.py b/project_euler/problem_025/sol2.py index a0f056023bc9..4094b6251d50 100644 --- a/project_euler/problem_025/sol2.py +++ b/project_euler/problem_025/sol2.py @@ -27,7 +27,7 @@ from collections.abc import Generator -def fibonacci_generator() -> Generator[int, None, None]: +def fibonacci_generator() -> Generator[int]: """ A generator that produces numbers in the Fibonacci sequence diff --git a/project_euler/problem_123/sol1.py b/project_euler/problem_123/sol1.py index 3dd31a2e8505..265348d2d4c8 100644 --- a/project_euler/problem_123/sol1.py +++ b/project_euler/problem_123/sol1.py @@ -43,7 +43,7 @@ from collections.abc import Generator -def sieve() -> Generator[int, None, None]: +def sieve() -> Generator[int]: """ Returns a prime number generator using sieve method. >>> type(sieve()) diff --git a/pyproject.toml b/pyproject.toml index c57419e79db3..c60ec246144e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,6 +80,7 @@ lint.ignore = [ "EM101", # Exception must not use a string literal, assign to variable first "EXE001", # Shebang is present but file is not executable -- DO NOT FIX "G004", # Logging statement uses f-string + "ISC001", # Conflicts with ruff format -- DO NOT FIX "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX "PLW2901", # PLW2901: Redefined loop variable -- FIX ME diff --git a/source/__init__.py b/source/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/strings/frequency_finder.py b/strings/frequency_finder.py index 8479c81ae464..e5afee891bd9 100644 --- a/strings/frequency_finder.py +++ b/strings/frequency_finder.py @@ -67,7 +67,7 @@ def get_frequency_order(message: str) -> str: freq_to_letter_str: dict[int, str] = {} - for freq in freq_to_letter: + for freq in freq_to_letter: # noqa: PLC0206 freq_to_letter[freq].sort(key=ETAOIN.find, reverse=True) freq_to_letter_str[freq] = "".join(freq_to_letter[freq]) diff --git a/strings/min_cost_string_conversion.py b/strings/min_cost_string_conversion.py index a5a3c4a4e3f8..93791e2a7ed3 100644 --- a/strings/min_cost_string_conversion.py +++ b/strings/min_cost_string_conversion.py @@ -124,7 +124,7 @@ def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]: print("".join(string)) if op[0] == "C": - file.write("%-16s" % "Copy %c" % op[1]) + file.write("%-16s" % "Copy %c" % op[1]) # noqa: UP031 file.write("\t\t\t" + "".join(string)) file.write("\r\n") @@ -132,7 +132,7 @@ def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]: elif op[0] == "R": string[i] = op[2] - file.write("%-16s" % ("Replace %c" % op[1] + " with " + str(op[2]))) + file.write("%-16s" % ("Replace %c" % op[1] + " with " + str(op[2]))) # noqa: UP031 file.write("\t\t" + "".join(string)) file.write("\r\n") @@ -140,7 +140,7 @@ def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]: elif op[0] == "D": string.pop(i) - file.write("%-16s" % "Delete %c" % op[1]) + file.write("%-16s" % "Delete %c" % op[1]) # noqa: UP031 file.write("\t\t\t" + "".join(string)) file.write("\r\n") @@ -148,7 +148,7 @@ def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]: else: string.insert(i, op[1]) - file.write("%-16s" % "Insert %c" % op[1]) + file.write("%-16s" % "Insert %c" % op[1]) # noqa: UP031 file.write("\t\t\t" + "".join(string)) file.write("\r\n") diff --git a/web_programming/fetch_jobs.py b/web_programming/fetch_jobs.py index 0d89bf45de57..3753d25bbe5f 100644 --- a/web_programming/fetch_jobs.py +++ b/web_programming/fetch_jobs.py @@ -12,7 +12,7 @@ url = "https://www.indeed.co.in/jobs?q=mobile+app+development&l=" -def fetch_jobs(location: str = "mumbai") -> Generator[tuple[str, str], None, None]: +def fetch_jobs(location: str = "mumbai") -> Generator[tuple[str, str]]: soup = BeautifulSoup( requests.get(url + location, timeout=10).content, "html.parser" ) From c7921226326f35932bbc9d214e9742c2f3d310bf Mon Sep 17 00:00:00 2001 From: Anamaria Miranda Date: Mon, 2 Dec 2024 11:57:04 +0100 Subject: [PATCH 011/126] Added matrix based color game algorithm (#12400) * Added matrix based color game * updating DIRECTORY.md --------- Co-authored-by: Miranda13 --- DIRECTORY.md | 1 + matrix/matrix_based_game.py | 284 ++++++++++++++++++++++++++++++++++++ 2 files changed, 285 insertions(+) create mode 100644 matrix/matrix_based_game.py diff --git a/DIRECTORY.md b/DIRECTORY.md index f0a34a553946..d234d366df06 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -794,6 +794,7 @@ * [Cramers Rule 2X2](matrix/cramers_rule_2x2.py) * [Inverse Of Matrix](matrix/inverse_of_matrix.py) * [Largest Square Area In Matrix](matrix/largest_square_area_in_matrix.py) + * [Matrix Based Game](matrix/matrix_based_game.py) * [Matrix Class](matrix/matrix_class.py) * [Matrix Equalization](matrix/matrix_equalization.py) * [Matrix Multiplication Recursion](matrix/matrix_multiplication_recursion.py) diff --git a/matrix/matrix_based_game.py b/matrix/matrix_based_game.py new file mode 100644 index 000000000000..1ff0cbe93435 --- /dev/null +++ b/matrix/matrix_based_game.py @@ -0,0 +1,284 @@ +""" +Matrix-Based Game Script +========================= +This script implements a matrix-based game where players interact with a grid of +elements. The primary goals are to: +- Identify connected elements of the same type from a selected position. +- Remove those elements, adjust the matrix by simulating gravity, and reorganize empty + columns. +- Calculate and display the score based on the number of elements removed in each move. + +Functions: +----------- +1. `find_repeat`: Finds all connected elements of the same type. +2. `increment_score`: Calculates the score for a given move. +3. `move_x`: Simulates gravity in a column. +4. `move_y`: Reorganizes the matrix by shifting columns leftward when a column becomes + empty. +5. `play`: Executes a single move, updating the matrix and returning the score. + +Input Format: +-------------- +1. Matrix size (`lines`): Integer specifying the size of the matrix (N x N). +2. Matrix content (`matrix`): Rows of the matrix, each consisting of characters. +3. Number of moves (`movs`): Integer indicating the number of moves. +4. List of moves (`movements`): A comma-separated string of coordinates for each move. + +(0,0) position starts from first left column to last right, and below row to up row + + +Example Input: +--------------- +4 +RRBG +RBBG +YYGG +XYGG +2 +0 1,1 1 + +Example (0,0) = X + +Output: +-------- +The script outputs the total score after processing all moves. + +Usage: +------- +Run the script and provide the required inputs as prompted. + +""" + + +def validate_matrix_size(size: int) -> None: + """ + >>> validate_matrix_size(-1) + Traceback (most recent call last): + ... + ValueError: Matrix size must be a positive integer. + """ + if not isinstance(size, int) or size <= 0: + raise ValueError("Matrix size must be a positive integer.") + + +def validate_matrix_content(matrix: list[str], size: int) -> None: + """ + Validates that the number of elements in the matrix matches the given size. + + >>> validate_matrix_content(['aaaa', 'aaaa', 'aaaa', 'aaaa'], 3) + Traceback (most recent call last): + ... + ValueError: The matrix dont match with size. + >>> validate_matrix_content(['aa%', 'aaa', 'aaa'], 3) + Traceback (most recent call last): + ... + ValueError: Matrix rows can only contain letters and numbers. + >>> validate_matrix_content(['aaa', 'aaa', 'aaaa'], 3) + Traceback (most recent call last): + ... + ValueError: Each row in the matrix must have exactly 3 characters. + """ + print(matrix) + if len(matrix) != size: + raise ValueError("The matrix dont match with size.") + for row in matrix: + if len(row) != size: + msg = f"Each row in the matrix must have exactly {size} characters." + raise ValueError(msg) + if not all(char.isalnum() for char in row): + raise ValueError("Matrix rows can only contain letters and numbers.") + + +def validate_moves(moves: list[tuple[int, int]], size: int) -> None: + """ + >>> validate_moves([(1, 2), (-1, 0)], 3) + Traceback (most recent call last): + ... + ValueError: Move is out of bounds for a matrix. + """ + for move in moves: + x, y = move + if not (0 <= x < size and 0 <= y < size): + raise ValueError("Move is out of bounds for a matrix.") + + +def parse_moves(input_str: str) -> list[tuple[int, int]]: + """ + >>> parse_moves("0 1, 1 1") + [(0, 1), (1, 1)] + >>> parse_moves("0 1, 1 1, 2") + Traceback (most recent call last): + ... + ValueError: Each move must have exactly two numbers. + >>> parse_moves("0 1, 1 1, 2 4 5 6") + Traceback (most recent call last): + ... + ValueError: Each move must have exactly two numbers. + """ + moves = [] + for pair in input_str.split(","): + parts = pair.strip().split() + if len(parts) != 2: + raise ValueError("Each move must have exactly two numbers.") + x, y = map(int, parts) + moves.append((x, y)) + return moves + + +def find_repeat( + matrix_g: list[list[str]], row: int, column: int, size: int +) -> set[tuple[int, int]]: + """ + Finds all connected elements of the same type from a given position. + + >>> find_repeat([['A', 'B', 'A'], ['A', 'B', 'A'], ['A', 'A', 'A']], 0, 0, 3) + {(1, 2), (2, 1), (0, 0), (2, 0), (0, 2), (2, 2), (1, 0)} + >>> find_repeat([['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-']], 1, 1, 3) + set() + """ + + column = size - 1 - column + visited = set() + repeated = set() + + if (color := matrix_g[column][row]) != "-": + + def dfs(row_n: int, column_n: int) -> None: + if row_n < 0 or row_n >= size or column_n < 0 or column_n >= size: + return + if (row_n, column_n) in visited: + return + visited.add((row_n, column_n)) + if matrix_g[row_n][column_n] == color: + repeated.add((row_n, column_n)) + dfs(row_n - 1, column_n) + dfs(row_n + 1, column_n) + dfs(row_n, column_n - 1) + dfs(row_n, column_n + 1) + + dfs(column, row) + + return repeated + + +def increment_score(count: int) -> int: + """ + Calculates the score for a move based on the number of elements removed. + + >>> increment_score(3) + 6 + >>> increment_score(0) + 0 + """ + return int(count * (count + 1) / 2) + + +def move_x(matrix_g: list[list[str]], column: int, size: int) -> list[list[str]]: + """ + Simulates gravity in a specific column. + + >>> move_x([['-', 'A'], ['-', '-'], ['-', 'C']], 1, 2) + [['-', '-'], ['-', 'A'], ['-', 'C']] + """ + + new_list = [] + + for row in range(size): + if matrix_g[row][column] != "-": + new_list.append(matrix_g[row][column]) + else: + new_list.insert(0, matrix_g[row][column]) + for row in range(size): + matrix_g[row][column] = new_list[row] + return matrix_g + + +def move_y(matrix_g: list[list[str]], size: int) -> list[list[str]]: + """ + Shifts all columns leftward when an entire column becomes empty. + + >>> move_y([['-', 'A'], ['-', '-'], ['-', 'C']], 2) + [['A', '-'], ['-', '-'], ['-', 'C']] + """ + + empty_columns = [] + + for column in range(size - 1, -1, -1): + if all(matrix_g[row][column] == "-" for row in range(size)): + empty_columns.append(column) + + for column in empty_columns: + for col in range(column + 1, size): + for row in range(size): + matrix_g[row][col - 1] = matrix_g[row][col] + for row in range(size): + matrix_g[row][-1] = "-" + + return matrix_g + + +def play( + matrix_g: list[list[str]], pos_x: int, pos_y: int, size: int +) -> tuple[list[list[str]], int]: + """ + Processes a single move, updating the matrix and calculating the score. + + >>> play([['R', 'G'], ['R', 'G']], 0, 0, 2) + ([['G', '-'], ['G', '-']], 3) + """ + + same_colors = find_repeat(matrix_g, pos_x, pos_y, size) + + if len(same_colors) != 0: + for pos in same_colors: + matrix_g[pos[0]][pos[1]] = "-" + for column in range(size): + matrix_g = move_x(matrix_g, column, size) + + matrix_g = move_y(matrix_g, size) + + return (matrix_g, increment_score(len(same_colors))) + + +def process_game(size: int, matrix: list[str], moves: list[tuple[int, int]]) -> int: + """Processes the game logic for the given matrix and moves. + + Args: + size (int): Size of the game board. + matrix (List[str]): Initial game matrix. + moves (List[Tuple[int, int]]): List of moves as (x, y) coordinates. + + Returns: + int: The total score obtained. + >>> process_game(3, ['aaa', 'bbb', 'ccc'], [(0, 0)]) + 6 + """ + + game_matrix = [list(row) for row in matrix] + total_score = 0 + + for move in moves: + pos_x, pos_y = move + game_matrix, score = play(game_matrix, pos_x, pos_y, size) + total_score += score + + return total_score + + +if __name__ == "__main__": + import doctest + + doctest.testmod(verbose=True) + try: + size = int(input("Enter the size of the matrix: ")) + validate_matrix_size(size) + print(f"Enter the {size} rows of the matrix:") + matrix = [input(f"Row {i+1}: ") for i in range(size)] + validate_matrix_content(matrix, size) + moves_input = input("Enter the moves (e.g., '0 0, 1 1'): ") + moves = parse_moves(moves_input) + validate_moves(moves, size) + score = process_game(size, matrix, moves) + print(f"Total score: {score}") + except ValueError as e: + print(f"{e}") From b22fab0ea46c7b625d8137d1fb07d082e20d6d7b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 22:35:21 +0100 Subject: [PATCH 012/126] [pre-commit.ci] pre-commit autoupdate (#12404) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.0 → v0.8.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.0...v0.8.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 64d9a833cd21..bef251749c19 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.0 + rev: v0.8.1 hooks: - id: ruff - id: ruff-format From 0bcdfbdb34e03e24e2f5da90a7236226b721981d Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Thu, 5 Dec 2024 05:34:48 +0100 Subject: [PATCH 013/126] Use Astral uv (#12402) * Use Astral uv * uvx vs uv run * uv sync --group=euler-validate,test * uv sync --group=euler-validate --group=test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * --group=test --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 15 +- .github/workflows/project_euler.yml | 16 +- .github/workflows/ruff.yml | 4 +- .github/workflows/sphinx.yml | 6 +- pyproject.toml | 48 +- requirements.txt | 6 - uv.lock | 1246 +++++++++++++++++++++++++++ 7 files changed, 1301 insertions(+), 40 deletions(-) create mode 100644 uv.lock diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b5703e2f1ab6..a6f308715cc2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,21 +10,18 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v4 + with: + enable-cache: true + cache-dependency-glob: uv.lock - uses: actions/setup-python@v5 with: python-version: 3.13 allow-prereleases: true - - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip setuptools wheel - python -m pip install pytest-cov -r requirements.txt + - run: uv sync --group=test - name: Run tests # TODO: #8818 Re-enable quantum tests - run: pytest + run: uv run pytest --ignore=computer_vision/cnn_classification.py --ignore=docs/conf.py --ignore=dynamic_programming/k_means_clustering_tensorflow.py diff --git a/.github/workflows/project_euler.yml b/.github/workflows/project_euler.yml index 59e1208a650d..84c55335451e 100644 --- a/.github/workflows/project_euler.yml +++ b/.github/workflows/project_euler.yml @@ -15,25 +15,21 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v4 - uses: actions/setup-python@v5 with: python-version: 3.x - - name: Install pytest and pytest-cov - run: | - python -m pip install --upgrade pip - python -m pip install --upgrade numpy pytest pytest-cov - - run: pytest --doctest-modules --cov-report=term-missing:skip-covered --cov=project_euler/ project_euler/ + - run: uv sync --group=euler-validate --group=test + - run: uv run pytest --doctest-modules --cov-report=term-missing:skip-covered --cov=project_euler/ project_euler/ validate-solutions: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v4 - uses: actions/setup-python@v5 with: python-version: 3.x - - name: Install pytest and requests - run: | - python -m pip install --upgrade pip - python -m pip install --upgrade numpy pytest requests - - run: pytest scripts/validate_solutions.py + - run: uv sync --group=euler-validate --group=test + - run: uv run pytest scripts/validate_solutions.py env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index d354eba672ae..2c6f92fcf7bf 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -12,5 +12,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - run: pip install --user ruff - - run: ruff check --output-format=github . + - uses: astral-sh/setup-uv@v4 + - run: uvx ruff check --output-format=github . diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index 9dfe344f9743..e3e2ce81a95d 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -26,14 +26,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v4 - uses: actions/setup-python@v5 with: python-version: 3.13 allow-prereleases: true - - run: pip install --upgrade pip - - run: pip install myst-parser sphinx-autoapi sphinx-pyproject + - run: uv sync --group=docs - uses: actions/configure-pages@v5 - - run: sphinx-build -c docs . docs/_build/html + - run: uv run sphinx-build -c docs . docs/_build/html - uses: actions/upload-pages-artifact@v3 with: path: docs/_build/html diff --git a/pyproject.toml b/pyproject.toml index c60ec246144e..7b7176705c44 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,12 +7,43 @@ requires-python = ">=3.13" classifiers = [ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.13", +] +dependencies = [ + "beautifulsoup4>=4.12.3", + "fake-useragent>=1.5.1", + "imageio>=2.36.1", + "keras>=3.7", + "lxml>=5.3", + "matplotlib>=3.9.3", + "numpy>=2.1.3", + "opencv-python>=4.10.0.84", + "pandas>=2.2.3", + "pillow>=11", + "requests>=2.32.3", + "rich>=13.9.4", + "scikit-learn>=1.5.2", + "sphinx-pyproject>=0.3", + "statsmodels>=0.14.4", + "sympy>=1.13.3", + "tweepy>=4.14", + "typing-extensions>=4.12.2", + "xgboost>=2.1.3", +] +[dependency-groups] +test = [ + "pytest>=8.3.4", + "pytest-cov>=6", ] -optional-dependencies.docs = [ - "myst-parser", - "sphinx-autoapi", - "sphinx-pyproject", + +docs = [ + "myst-parser>=4", + "sphinx-autoapi>=3.4", + "sphinx-pyproject>=0.3", +] +euler-validate = [ + "numpy>=2.1.3", + "requests>=2.32.3", ] [tool.ruff] @@ -61,8 +92,8 @@ lint.select = [ "UP", # pyupgrade "W", # pycodestyle "YTT", # flake8-2020 - # "ANN", # flake8-annotations # FIX ME? - # "COM", # flake8-commas + # "ANN", # flake8-annotations -- FIX ME? + # "COM", # flake8-commas -- DO NOT FIX # "D", # pydocstyle -- FIX ME? # "ERA", # eradicate -- DO NOT FIX # "FBT", # flake8-boolean-trap # FIX ME @@ -129,10 +160,7 @@ lint.pylint.max-statements = 88 # default: 50 [tool.codespell] ignore-words-list = "3rt,ans,bitap,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" -skip = "./.*,*.json,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" - -[tool.pyproject-fmt] -max_supported_python = "3.13" +skip = "./.*,*.json,*.lock,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" [tool.pytest.ini_options] markers = [ diff --git a/requirements.txt b/requirements.txt index 6754363332c4..4cc83f44987d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,18 +8,12 @@ numpy opencv-python pandas pillow -# projectq # uncomment once quantum/quantum_random.py is fixed -qiskit ; python_version < '3.12' -qiskit-aer ; python_version < '3.12' requests rich -# scikit-fuzzy # uncomment once fuzzy_logic/fuzzy_operations.py is fixed scikit-learn sphinx_pyproject statsmodels sympy -tensorflow ; python_version < '3.13' tweepy -# yulewalker # uncomment once audio_filters/equal_loudness_filter.py is fixed typing_extensions xgboost diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000000..077288f041a1 --- /dev/null +++ b/uv.lock @@ -0,0 +1,1246 @@ +version = 1 +requires-python = ">=3.13" +resolution-markers = [ + "platform_system == 'Darwin'", + "platform_machine == 'aarch64' and platform_system == 'Linux'", + "(platform_machine != 'aarch64' and platform_system != 'Darwin') or (platform_system != 'Darwin' and platform_system != 'Linux')", +] + +[[package]] +name = "absl-py" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/8f/fc001b92ecc467cc32ab38398bd0bfb45df46e7523bf33c2ad22a505f06e/absl-py-2.1.0.tar.gz", hash = "sha256:7820790efbb316739cde8b4e19357243fc3608a152024288513dd968d7d959ff", size = 118055 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/ad/e0d3c824784ff121c03cc031f944bc7e139a8f1870ffd2845cc2dd76f6c4/absl_py-2.1.0-py3-none-any.whl", hash = "sha256:526a04eadab8b4ee719ce68f204172ead1027549089702d99b9059f129ff1308", size = 133706 }, +] + +[[package]] +name = "alabaster" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 }, +] + +[[package]] +name = "astroid" +version = "3.3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/1e/326fb1d3d83a3bb77c9f9be29d31f2901e35acb94b0605c3f2e5085047f9/astroid-3.3.5.tar.gz", hash = "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d", size = 397229 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/30/624365383fa4a40329c0f0bbbc151abc4a64e30dfc110fc8f6e2afcd02bb/astroid-3.3.5-py3-none-any.whl", hash = "sha256:a9d1c946ada25098d790e079ba2a1b112157278f3fb7e718ae6a9252f5835dc8", size = 274586 }, +] + +[[package]] +name = "babel" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925 }, +] + +[[package]] +name = "certifi" +version = "2024.8.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/4f/e1808dc01273379acc506d18f1504eb2d299bd4131743b9fc54d7be4df1e/charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", size = 106620 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/89/68a4c86f1a0002810a27f12e9a7b22feb198c59b2f05231349fbce5c06f4/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", size = 194617 }, + { url = "https://files.pythonhosted.org/packages/4f/cd/8947fe425e2ab0aa57aceb7807af13a0e4162cd21eee42ef5b053447edf5/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", size = 125310 }, + { url = "https://files.pythonhosted.org/packages/5b/f0/b5263e8668a4ee9becc2b451ed909e9c27058337fda5b8c49588183c267a/charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", size = 119126 }, + { url = "https://files.pythonhosted.org/packages/ff/6e/e445afe4f7fda27a533f3234b627b3e515a1b9429bc981c9a5e2aa5d97b6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", size = 139342 }, + { url = "https://files.pythonhosted.org/packages/a1/b2/4af9993b532d93270538ad4926c8e37dc29f2111c36f9c629840c57cd9b3/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", size = 149383 }, + { url = "https://files.pythonhosted.org/packages/fb/6f/4e78c3b97686b871db9be6f31d64e9264e889f8c9d7ab33c771f847f79b7/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", size = 142214 }, + { url = "https://files.pythonhosted.org/packages/2b/c9/1c8fe3ce05d30c87eff498592c89015b19fade13df42850aafae09e94f35/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", size = 144104 }, + { url = "https://files.pythonhosted.org/packages/ee/68/efad5dcb306bf37db7db338338e7bb8ebd8cf38ee5bbd5ceaaaa46f257e6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", size = 146255 }, + { url = "https://files.pythonhosted.org/packages/0c/75/1ed813c3ffd200b1f3e71121c95da3f79e6d2a96120163443b3ad1057505/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", size = 140251 }, + { url = "https://files.pythonhosted.org/packages/7d/0d/6f32255c1979653b448d3c709583557a4d24ff97ac4f3a5be156b2e6a210/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", size = 148474 }, + { url = "https://files.pythonhosted.org/packages/ac/a0/c1b5298de4670d997101fef95b97ac440e8c8d8b4efa5a4d1ef44af82f0d/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", size = 151849 }, + { url = "https://files.pythonhosted.org/packages/04/4f/b3961ba0c664989ba63e30595a3ed0875d6790ff26671e2aae2fdc28a399/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", size = 149781 }, + { url = "https://files.pythonhosted.org/packages/d8/90/6af4cd042066a4adad58ae25648a12c09c879efa4849c705719ba1b23d8c/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482", size = 144970 }, + { url = "https://files.pythonhosted.org/packages/cc/67/e5e7e0cbfefc4ca79025238b43cdf8a2037854195b37d6417f3d0895c4c2/charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", size = 94973 }, + { url = "https://files.pythonhosted.org/packages/65/97/fc9bbc54ee13d33dc54a7fcf17b26368b18505500fc01e228c27b5222d80/charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", size = 102308 }, + { url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 }, +] + +[[package]] +name = "codespell" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a0/a9/98353dfc7afcdf18cffd2dd3e959a25eaaf2728cf450caa59af89648a8e4/codespell-2.3.0.tar.gz", hash = "sha256:360c7d10f75e65f67bad720af7007e1060a5d395670ec11a7ed1fed9dd17471f", size = 329791 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/20/b6019add11e84f821184234cea0ad91442373489ef7ccfa3d73a71b908fa/codespell-2.3.0-py3-none-any.whl", hash = "sha256:a9c7cef2501c9cfede2110fd6d4e5e62296920efe9abfb84648df866e47f58d1", size = 329167 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "contourpy" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/c2/fc7193cc5383637ff390a712e88e4ded0452c9fbcf84abe3de5ea3df1866/contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699", size = 13465753 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/e7/de62050dce687c5e96f946a93546910bc67e483fe05324439e329ff36105/contourpy-1.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2", size = 271548 }, + { url = "https://files.pythonhosted.org/packages/78/4d/c2a09ae014ae984c6bdd29c11e74d3121b25eaa117eca0bb76340efd7e1c/contourpy-1.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5", size = 255576 }, + { url = "https://files.pythonhosted.org/packages/ab/8a/915380ee96a5638bda80cd061ccb8e666bfdccea38d5741cb69e6dbd61fc/contourpy-1.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81", size = 306635 }, + { url = "https://files.pythonhosted.org/packages/29/5c/c83ce09375428298acd4e6582aeb68b1e0d1447f877fa993d9bf6cd3b0a0/contourpy-1.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2", size = 345925 }, + { url = "https://files.pythonhosted.org/packages/29/63/5b52f4a15e80c66c8078a641a3bfacd6e07106835682454647aca1afc852/contourpy-1.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7", size = 318000 }, + { url = "https://files.pythonhosted.org/packages/9a/e2/30ca086c692691129849198659bf0556d72a757fe2769eb9620a27169296/contourpy-1.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c", size = 322689 }, + { url = "https://files.pythonhosted.org/packages/6b/77/f37812ef700f1f185d348394debf33f22d531e714cf6a35d13d68a7003c7/contourpy-1.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3", size = 1268413 }, + { url = "https://files.pythonhosted.org/packages/3f/6d/ce84e79cdd128542ebeb268f84abb4b093af78e7f8ec504676673d2675bc/contourpy-1.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1", size = 1326530 }, + { url = "https://files.pythonhosted.org/packages/72/22/8282f4eae20c73c89bee7a82a19c4e27af9b57bb602ecaa00713d5bdb54d/contourpy-1.3.1-cp313-cp313-win32.whl", hash = "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82", size = 175315 }, + { url = "https://files.pythonhosted.org/packages/e3/d5/28bca491f65312b438fbf076589dcde7f6f966b196d900777f5811b9c4e2/contourpy-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd", size = 220987 }, + { url = "https://files.pythonhosted.org/packages/2f/24/a4b285d6adaaf9746e4700932f579f1a7b6f9681109f694cfa233ae75c4e/contourpy-1.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30", size = 285001 }, + { url = "https://files.pythonhosted.org/packages/48/1d/fb49a401b5ca4f06ccf467cd6c4f1fd65767e63c21322b29b04ec40b40b9/contourpy-1.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751", size = 268553 }, + { url = "https://files.pythonhosted.org/packages/79/1e/4aef9470d13fd029087388fae750dccb49a50c012a6c8d1d634295caa644/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342", size = 310386 }, + { url = "https://files.pythonhosted.org/packages/b0/34/910dc706ed70153b60392b5305c708c9810d425bde12499c9184a1100888/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c", size = 349806 }, + { url = "https://files.pythonhosted.org/packages/31/3c/faee6a40d66d7f2a87f7102236bf4780c57990dd7f98e5ff29881b1b1344/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f", size = 321108 }, + { url = "https://files.pythonhosted.org/packages/17/69/390dc9b20dd4bb20585651d7316cc3054b7d4a7b4f8b710b2b698e08968d/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda", size = 327291 }, + { url = "https://files.pythonhosted.org/packages/ef/74/7030b67c4e941fe1e5424a3d988080e83568030ce0355f7c9fc556455b01/contourpy-1.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242", size = 1263752 }, + { url = "https://files.pythonhosted.org/packages/f0/ed/92d86f183a8615f13f6b9cbfc5d4298a509d6ce433432e21da838b4b63f4/contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1", size = 1318403 }, + { url = "https://files.pythonhosted.org/packages/b3/0e/c8e4950c77dcfc897c71d61e56690a0a9df39543d2164040301b5df8e67b/contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1", size = 185117 }, + { url = "https://files.pythonhosted.org/packages/c1/31/1ae946f11dfbd229222e6d6ad8e7bd1891d3d48bde5fbf7a0beb9491f8e3/contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546", size = 236668 }, +] + +[[package]] +name = "coverage" +version = "7.6.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/75/aecfd0a3adbec6e45753976bc2a9fed62b42cea9a206d10fd29244a77953/coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc", size = 801425 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/84/6f0ccf94a098ac3d6d6f236bd3905eeac049a9e0efcd9a63d4feca37ac4b/coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb", size = 207313 }, + { url = "https://files.pythonhosted.org/packages/db/2b/e3b3a3a12ebec738c545897ac9f314620470fcbc368cdac88cf14974ba20/coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63", size = 207574 }, + { url = "https://files.pythonhosted.org/packages/db/c0/5bf95d42b6a8d21dfce5025ce187f15db57d6460a59b67a95fe8728162f1/coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365", size = 240090 }, + { url = "https://files.pythonhosted.org/packages/57/b8/d6fd17d1a8e2b0e1a4e8b9cb1f0f261afd422570735899759c0584236916/coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002", size = 237237 }, + { url = "https://files.pythonhosted.org/packages/d4/e4/a91e9bb46809c8b63e68fc5db5c4d567d3423b6691d049a4f950e38fbe9d/coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3", size = 239225 }, + { url = "https://files.pythonhosted.org/packages/31/9c/9b99b0591ec4555b7292d271e005f27b465388ce166056c435b288db6a69/coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022", size = 238888 }, + { url = "https://files.pythonhosted.org/packages/a6/85/285c2df9a04bc7c31f21fd9d4a24d19e040ec5e2ff06e572af1f6514c9e7/coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e", size = 236974 }, + { url = "https://files.pythonhosted.org/packages/cb/a1/95ec8522206f76cdca033bf8bb61fff56429fb414835fc4d34651dfd29fc/coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b", size = 238815 }, + { url = "https://files.pythonhosted.org/packages/8d/ac/687e9ba5e6d0979e9dab5c02e01c4f24ac58260ef82d88d3b433b3f84f1e/coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146", size = 209957 }, + { url = "https://files.pythonhosted.org/packages/2f/a3/b61cc8e3fcf075293fb0f3dee405748453c5ba28ac02ceb4a87f52bdb105/coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28", size = 210711 }, + { url = "https://files.pythonhosted.org/packages/ee/4b/891c8b9acf1b62c85e4a71dac142ab9284e8347409b7355de02e3f38306f/coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d", size = 208053 }, + { url = "https://files.pythonhosted.org/packages/18/a9/9e330409b291cc002723d339346452800e78df1ce50774ca439ade1d374f/coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451", size = 208329 }, + { url = "https://files.pythonhosted.org/packages/9c/0d/33635fd429f6589c6e1cdfc7bf581aefe4c1792fbff06383f9d37f59db60/coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764", size = 251052 }, + { url = "https://files.pythonhosted.org/packages/23/32/8a08da0e46f3830bbb9a5b40614241b2e700f27a9c2889f53122486443ed/coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf", size = 246765 }, + { url = "https://files.pythonhosted.org/packages/56/3f/3b86303d2c14350fdb1c6c4dbf9bc76000af2382f42ca1d4d99c6317666e/coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5", size = 249125 }, + { url = "https://files.pythonhosted.org/packages/36/cb/c4f081b9023f9fd8646dbc4ef77be0df090263e8f66f4ea47681e0dc2cff/coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4", size = 248615 }, + { url = "https://files.pythonhosted.org/packages/32/ee/53bdbf67760928c44b57b2c28a8c0a4bf544f85a9ee129a63ba5c78fdee4/coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83", size = 246507 }, + { url = "https://files.pythonhosted.org/packages/57/49/5a57910bd0af6d8e802b4ca65292576d19b54b49f81577fd898505dee075/coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b", size = 247785 }, + { url = "https://files.pythonhosted.org/packages/bd/37/e450c9f6b297c79bb9858407396ed3e084dcc22990dd110ab01d5ceb9770/coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71", size = 210605 }, + { url = "https://files.pythonhosted.org/packages/44/79/7d0c7dd237c6905018e2936cd1055fe1d42e7eba2ebab3c00f4aad2a27d7/coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc", size = 211777 }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, +] + +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, +] + +[[package]] +name = "dom-toml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "domdf-python-tools" }, + { name = "tomli" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/34/f7690cf288eaa86b55c8f1b890d0834e6df44a026a88eca12274fcd624ab/dom_toml-2.0.0.tar.gz", hash = "sha256:3c07e8436538994974127b1ae037661d1a779ac915c44fd06b3ab5fe140ff589", size = 11133 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/99/b6fc87dff3138491d81676bdcbf1531080925ba41486ec1dafd86e33fdbc/dom_toml-2.0.0-py3-none-any.whl", hash = "sha256:0b6d02a72bcbc6be8175c61afc30623bbb6b74c4650f2a806fbc3fb7fe86935d", size = 13376 }, +] + +[[package]] +name = "domdf-python-tools" +version = "3.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "natsort" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6b/78/974e10c583ba9d2302e748c9585313a7f2c7ba00e4f600324f432e38fe68/domdf_python_tools-3.9.0.tar.gz", hash = "sha256:1f8a96971178333a55e083e35610d7688cd7620ad2b99790164e1fc1a3614c18", size = 103792 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/e9/7447a88b217650a74927d3444a89507986479a69b83741900eddd34167fe/domdf_python_tools-3.9.0-py3-none-any.whl", hash = "sha256:4e1ef365cbc24627d6d1e90cf7d46d8ab8df967e1237f4a26885f6986c78872e", size = 127106 }, +] + +[[package]] +name = "fake-useragent" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/a1/1f662631ab153975fa8dbf09296324ecbaf53370dce922054e8de6b57370/fake-useragent-1.5.1.tar.gz", hash = "sha256:6387269f5a2196b5ba7ed8935852f75486845a1c95c50e72460e6a8e762f5c49", size = 22631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/99/60d8cf1b26938c2e0a57e232f7f15641dfcd6f8deda454d73e4145910ff6/fake_useragent-1.5.1-py3-none-any.whl", hash = "sha256:57415096557c8a4e23b62a375c21c55af5fd4ba30549227f562d2c4f5b60e3b3", size = 17190 }, +] + +[[package]] +name = "fonttools" +version = "4.55.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4e/053fe1b5c0ce346c0a9d0557492c654362bafb14f026eae0d3ee98009152/fonttools-4.55.0.tar.gz", hash = "sha256:7636acc6ab733572d5e7eec922b254ead611f1cdad17be3f0be7418e8bfaca71", size = 3490431 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/87/a669ac26c6077e37ffb06abf29c5571789eefe518d06c52df392181ee694/fonttools-4.55.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8118dc571921dc9e4b288d9cb423ceaf886d195a2e5329cc427df82bba872cd9", size = 2752519 }, + { url = "https://files.pythonhosted.org/packages/0c/e9/4822ad238fe215133c7df20f1cdb1a58cfb634a31523e77ff0fb2033970a/fonttools-4.55.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01124f2ca6c29fad4132d930da69158d3f49b2350e4a779e1efbe0e82bd63f6c", size = 2286819 }, + { url = "https://files.pythonhosted.org/packages/3e/a4/d7941c3897129e60fe68d20e4819fda4d0c4858d77badae0e80ca6440b36/fonttools-4.55.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81ffd58d2691f11f7c8438796e9f21c374828805d33e83ff4b76e4635633674c", size = 4770382 }, + { url = "https://files.pythonhosted.org/packages/31/cf/c51ea1348f9fba9c627439afad9dee0090040809ab431f4422b5bfdda34c/fonttools-4.55.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5435e5f1eb893c35c2bc2b9cd3c9596b0fcb0a59e7a14121562986dd4c47b8dd", size = 4858336 }, + { url = "https://files.pythonhosted.org/packages/73/be/36c1fe0e5c9a96b068ddd7e82001243bbe7fe12549c8d14e1bd025bf40c9/fonttools-4.55.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d12081729280c39d001edd0f4f06d696014c26e6e9a0a55488fabc37c28945e4", size = 4756072 }, + { url = "https://files.pythonhosted.org/packages/5c/18/6dd381c29f215a017f79aa9fea0722424a0046b47991c4390a78ff87ce0c/fonttools-4.55.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a7ad1f1b98ab6cb927ab924a38a8649f1ffd7525c75fe5b594f5dab17af70e18", size = 5008668 }, + { url = "https://files.pythonhosted.org/packages/b8/95/316f20092b389b927dba1d1dccd3f541853f96e707e210f1b9f4e7bacdd5/fonttools-4.55.0-cp313-cp313-win32.whl", hash = "sha256:abe62987c37630dca69a104266277216de1023cf570c1643bb3a19a9509e7a1b", size = 2155841 }, + { url = "https://files.pythonhosted.org/packages/35/ca/b4638aa3e446184892e2f9cc8ef44bb506f47fea04580df7fb84f5a4363d/fonttools-4.55.0-cp313-cp313-win_amd64.whl", hash = "sha256:2863555ba90b573e4201feaf87a7e71ca3b97c05aa4d63548a4b69ea16c9e998", size = 2200587 }, + { url = "https://files.pythonhosted.org/packages/b4/4a/786589606d4989cb34d8bc766cd687d955aaf3039c367fe7104bcf82dc98/fonttools-4.55.0-py3-none-any.whl", hash = "sha256:12db5888cd4dd3fcc9f0ee60c6edd3c7e1fd44b7dd0f31381ea03df68f8a153f", size = 1100249 }, +] + +[[package]] +name = "h5py" +version = "3.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/0c/5c2b0a88158682aeafb10c1c2b735df5bc31f165bfe192f2ee9f2a23b5f1/h5py-3.12.1.tar.gz", hash = "sha256:326d70b53d31baa61f00b8aa5f95c2fcb9621a3ee8365d770c551a13dbbcbfdf", size = 411457 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/1c/ecdd0efab52c24f2a9bf2324289828b860e8dd1e3c5ada3cf0889e14fdc1/h5py-3.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:513171e90ed92236fc2ca363ce7a2fc6f2827375efcbb0cc7fbdd7fe11fecafc", size = 3346239 }, + { url = "https://files.pythonhosted.org/packages/93/cd/5b6f574bf3e318bbe305bc93ba45181676550eb44ba35e006d2e98004eaa/h5py-3.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:59400f88343b79655a242068a9c900001a34b63e3afb040bd7cdf717e440f653", size = 2843416 }, + { url = "https://files.pythonhosted.org/packages/8a/4f/b74332f313bfbe94ba03fff784219b9db385e6139708e55b11490149f90a/h5py-3.12.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3e465aee0ec353949f0f46bf6c6f9790a2006af896cee7c178a8c3e5090aa32", size = 5154390 }, + { url = "https://files.pythonhosted.org/packages/1a/57/93ea9e10a6457ea8d3b867207deb29a527e966a08a84c57ffd954e32152a/h5py-3.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba51c0c5e029bb5420a343586ff79d56e7455d496d18a30309616fdbeed1068f", size = 5378244 }, + { url = "https://files.pythonhosted.org/packages/50/51/0bbf3663062b2eeee78aa51da71e065f8a0a6e3cb950cc7020b4444999e6/h5py-3.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:52ab036c6c97055b85b2a242cb540ff9590bacfda0c03dd0cf0661b311f522f8", size = 2979760 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "imageio" +version = "2.36.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "pillow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/aa/2e7a49259339e691ff2b477ae0696b1784a09313c5872700bbbdd00a3030/imageio-2.36.1.tar.gz", hash = "sha256:e4e1d231f47f9a9e16100b0f7ce1a86e8856fb4d1c0fa2c4365a316f1746be62", size = 389522 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/f9/f78e7f5ac8077c481bf6b43b8bc736605363034b3d5eb3ce8eb79f53f5f1/imageio-2.36.1-py3-none-any.whl", hash = "sha256:20abd2cae58e55ca1af8a8dcf43293336a59adf0391f1917bf8518633cfc2cdf", size = 315435 }, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, +] + +[[package]] +name = "joblib" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/64/33/60135848598c076ce4b231e1b1895170f45fbcaeaa2c9d5e38b04db70c35/joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e", size = 2116621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6", size = 301817 }, +] + +[[package]] +name = "keras" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py" }, + { name = "h5py" }, + { name = "ml-dtypes" }, + { name = "namex" }, + { name = "numpy" }, + { name = "optree" }, + { name = "packaging" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/c3/56fc6800c5eab94bd0f5e930751bd4c0fa1ee0aee272fad4a72723ffae87/keras-3.7.0.tar.gz", hash = "sha256:a4451a5591e75dfb414d0b84a3fd2fb9c0240cc87ebe7e397f547ce10b0e67b7", size = 924719 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/bf/9e3f10e55df30b0fb4bf6c2ee7d50bda2e070599b86f62ea3f9954af172b/keras-3.7.0-py3-none-any.whl", hash = "sha256:546a64f302e4779c129c06d9826fa586de752cdfd43d7dc4010c31b282587969", size = 1228365 }, +] + +[[package]] +name = "kiwisolver" +version = "1.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/4d/2255e1c76304cbd60b48cee302b66d1dde4468dc5b1160e4b7cb43778f2a/kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60", size = 97286 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/06/7da99b04259b0f18b557a4effd1b9c901a747f7fdd84cf834ccf520cb0b2/kiwisolver-1.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e", size = 121913 }, + { url = "https://files.pythonhosted.org/packages/97/f5/b8a370d1aa593c17882af0a6f6755aaecd643640c0ed72dcfd2eafc388b9/kiwisolver-1.4.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6", size = 65627 }, + { url = "https://files.pythonhosted.org/packages/2a/fc/6c0374f7503522539e2d4d1b497f5ebad3f8ed07ab51aed2af988dd0fb65/kiwisolver-1.4.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750", size = 63888 }, + { url = "https://files.pythonhosted.org/packages/bf/3e/0b7172793d0f41cae5c923492da89a2ffcd1adf764c16159ca047463ebd3/kiwisolver-1.4.7-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d", size = 1369145 }, + { url = "https://files.pythonhosted.org/packages/77/92/47d050d6f6aced2d634258123f2688fbfef8ded3c5baf2c79d94d91f1f58/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379", size = 1461448 }, + { url = "https://files.pythonhosted.org/packages/9c/1b/8f80b18e20b3b294546a1adb41701e79ae21915f4175f311a90d042301cf/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c", size = 1578750 }, + { url = "https://files.pythonhosted.org/packages/a4/fe/fe8e72f3be0a844f257cadd72689c0848c6d5c51bc1d60429e2d14ad776e/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34", size = 1507175 }, + { url = "https://files.pythonhosted.org/packages/39/fa/cdc0b6105d90eadc3bee525fecc9179e2b41e1ce0293caaf49cb631a6aaf/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1", size = 1463963 }, + { url = "https://files.pythonhosted.org/packages/6e/5c/0c03c4e542720c6177d4f408e56d1c8315899db72d46261a4e15b8b33a41/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f", size = 2248220 }, + { url = "https://files.pythonhosted.org/packages/3d/ee/55ef86d5a574f4e767df7da3a3a7ff4954c996e12d4fbe9c408170cd7dcc/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b", size = 2404463 }, + { url = "https://files.pythonhosted.org/packages/0f/6d/73ad36170b4bff4825dc588acf4f3e6319cb97cd1fb3eb04d9faa6b6f212/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27", size = 2352842 }, + { url = "https://files.pythonhosted.org/packages/0b/16/fa531ff9199d3b6473bb4d0f47416cdb08d556c03b8bc1cccf04e756b56d/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a", size = 2501635 }, + { url = "https://files.pythonhosted.org/packages/78/7e/aa9422e78419db0cbe75fb86d8e72b433818f2e62e2e394992d23d23a583/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee", size = 2314556 }, + { url = "https://files.pythonhosted.org/packages/a8/b2/15f7f556df0a6e5b3772a1e076a9d9f6c538ce5f05bd590eca8106508e06/kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07", size = 46364 }, + { url = "https://files.pythonhosted.org/packages/0b/db/32e897e43a330eee8e4770bfd2737a9584b23e33587a0812b8e20aac38f7/kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76", size = 55887 }, + { url = "https://files.pythonhosted.org/packages/c8/a4/df2bdca5270ca85fd25253049eb6708d4127be2ed0e5c2650217450b59e9/kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650", size = 48530 }, +] + +[[package]] +name = "lxml" +version = "5.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/6b/20c3a4b24751377aaa6307eb230b66701024012c29dd374999cc92983269/lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f", size = 3679318 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/6a/42141e4d373903bfea6f8e94b2f554d05506dfda522ada5343c651410dc8/lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a", size = 8156284 }, + { url = "https://files.pythonhosted.org/packages/91/5e/fa097f0f7d8b3d113fb7312c6308af702f2667f22644441715be961f2c7e/lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd", size = 4432407 }, + { url = "https://files.pythonhosted.org/packages/2d/a1/b901988aa6d4ff937f2e5cfc114e4ec561901ff00660c3e56713642728da/lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51", size = 5048331 }, + { url = "https://files.pythonhosted.org/packages/30/0f/b2a54f48e52de578b71bbe2a2f8160672a8a5e103df3a78da53907e8c7ed/lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b", size = 4744835 }, + { url = "https://files.pythonhosted.org/packages/82/9d/b000c15538b60934589e83826ecbc437a1586488d7c13f8ee5ff1f79a9b8/lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002", size = 5316649 }, + { url = "https://files.pythonhosted.org/packages/e3/ee/ffbb9eaff5e541922611d2c56b175c45893d1c0b8b11e5a497708a6a3b3b/lxml-5.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4", size = 4812046 }, + { url = "https://files.pythonhosted.org/packages/15/ff/7ff89d567485c7b943cdac316087f16b2399a8b997007ed352a1248397e5/lxml-5.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492", size = 4918597 }, + { url = "https://files.pythonhosted.org/packages/c6/a3/535b6ed8c048412ff51268bdf4bf1cf052a37aa7e31d2e6518038a883b29/lxml-5.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3", size = 4738071 }, + { url = "https://files.pythonhosted.org/packages/7a/8f/cbbfa59cb4d4fd677fe183725a76d8c956495d7a3c7f111ab8f5e13d2e83/lxml-5.3.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4", size = 5342213 }, + { url = "https://files.pythonhosted.org/packages/5c/fb/db4c10dd9958d4b52e34d1d1f7c1f434422aeaf6ae2bbaaff2264351d944/lxml-5.3.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367", size = 4893749 }, + { url = "https://files.pythonhosted.org/packages/f2/38/bb4581c143957c47740de18a3281a0cab7722390a77cc6e610e8ebf2d736/lxml-5.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832", size = 4945901 }, + { url = "https://files.pythonhosted.org/packages/fc/d5/18b7de4960c731e98037bd48fa9f8e6e8f2558e6fbca4303d9b14d21ef3b/lxml-5.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff", size = 4815447 }, + { url = "https://files.pythonhosted.org/packages/97/a8/cd51ceaad6eb849246559a8ef60ae55065a3df550fc5fcd27014361c1bab/lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd", size = 5411186 }, + { url = "https://files.pythonhosted.org/packages/89/c3/1e3dabab519481ed7b1fdcba21dcfb8832f57000733ef0e71cf6d09a5e03/lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb", size = 5324481 }, + { url = "https://files.pythonhosted.org/packages/b6/17/71e9984cf0570cd202ac0a1c9ed5c1b8889b0fc8dc736f5ef0ffb181c284/lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b", size = 5011053 }, + { url = "https://files.pythonhosted.org/packages/69/68/9f7e6d3312a91e30829368c2b3217e750adef12a6f8eb10498249f4e8d72/lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957", size = 3485634 }, + { url = "https://files.pythonhosted.org/packages/7d/db/214290d58ad68c587bd5d6af3d34e56830438733d0d0856c0275fde43652/lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d", size = 3814417 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "matplotlib" +version = "3.9.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/75/9f/562ed484b11ac9f4bb4f9d2d7546954ec106a8c0f06cc755d6f63e519274/matplotlib-3.9.3.tar.gz", hash = "sha256:cd5dbbc8e25cad5f706845c4d100e2c8b34691b412b93717ce38d8ae803bcfa5", size = 36113438 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/04/949640040982822416c471d9ebe4e9e6c69ca9f9bb6ba82ed30808863c02/matplotlib-3.9.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:203d18df84f5288973b2d56de63d4678cc748250026ca9e1ad8f8a0fd8a75d83", size = 7883417 }, + { url = "https://files.pythonhosted.org/packages/9f/90/ebd37143cd3150b6c650ee1580024df3dd649d176e68d346f826b8d24e37/matplotlib-3.9.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b651b0d3642991259109dc0351fc33ad44c624801367bb8307be9bfc35e427ad", size = 7768720 }, + { url = "https://files.pythonhosted.org/packages/dc/84/6591e6b55d755d16dacdc113205067031867c1f5e3c08b32c01aad831420/matplotlib-3.9.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66d7b171fecf96940ce069923a08ba3df33ef542de82c2ff4fe8caa8346fa95a", size = 8192723 }, + { url = "https://files.pythonhosted.org/packages/29/09/146a17d37e32313507f11ac984e65311f2d5805d731eb981d4f70eb928dc/matplotlib-3.9.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be0ba61f6ff2e6b68e4270fb63b6813c9e7dec3d15fc3a93f47480444fd72f0", size = 8305801 }, + { url = "https://files.pythonhosted.org/packages/85/cb/d2690572c08f19ca7c0f44b1fb4d11c121d63467a57b508cc3656ff80b43/matplotlib-3.9.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d6b2e8856dec3a6db1ae51aec85c82223e834b228c1d3228aede87eee2b34f9", size = 9086564 }, + { url = "https://files.pythonhosted.org/packages/28/dd/0a5176027c1cb94fe75f69f76cb274180c8abf740df6fc0e6a1e4cbaec3f/matplotlib-3.9.3-cp313-cp313-win_amd64.whl", hash = "sha256:90a85a004fefed9e583597478420bf904bb1a065b0b0ee5b9d8d31b04b0f3f70", size = 7833257 }, + { url = "https://files.pythonhosted.org/packages/42/d4/e477d50a8e4b437c2afbb5c665cb8e5d79b06abe6fe3c6915d6f7f0c2ef2/matplotlib-3.9.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3119b2f16de7f7b9212ba76d8fe6a0e9f90b27a1e04683cd89833a991682f639", size = 7911906 }, + { url = "https://files.pythonhosted.org/packages/ae/a1/ba5ab89666c42ace8e31b4ff5a2c76a17e4d6f91aefce476b064c56ff61d/matplotlib-3.9.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:87ad73763d93add1b6c1f9fcd33af662fd62ed70e620c52fcb79f3ac427cf3a6", size = 7801336 }, + { url = "https://files.pythonhosted.org/packages/77/59/4dcdb3a6695af6c698a95aec13016a550ef2f85144d22f61f81d1e064148/matplotlib-3.9.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:026bdf3137ab6022c866efa4813b6bbeddc2ed4c9e7e02f0e323a7bca380dfa0", size = 8218178 }, + { url = "https://files.pythonhosted.org/packages/4f/27/7c72db0d0ee35d9237572565ffa3c0eb25fc46a3f47e0f16412a587bc9d8/matplotlib-3.9.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760a5e89ebbb172989e8273024a1024b0f084510b9105261b3b00c15e9c9f006", size = 8327768 }, + { url = "https://files.pythonhosted.org/packages/de/ad/213eee624feadba7b77e881c9d2c04c1e036efe69d19031e3fa927fdb5dc/matplotlib-3.9.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a42b9dc42de2cfe357efa27d9c50c7833fc5ab9b2eb7252ccd5d5f836a84e1e4", size = 9094075 }, + { url = "https://files.pythonhosted.org/packages/19/1b/cb8e99a5fe2e2b14e3b8234cb1649a675be63f74a5224a648ae4ab61f60c/matplotlib-3.9.3-cp313-cp313t-win_amd64.whl", hash = "sha256:e0fcb7da73fbf67b5f4bdaa57d85bb585a4e913d4a10f3e15b32baea56a67f0a", size = 7888937 }, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "ml-dtypes" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/79/717c5e22ad25d63ce3acdfe8ff8d64bdedec18914256c59b838218708b16/ml_dtypes-0.5.0.tar.gz", hash = "sha256:3e7d3a380fe73a63c884f06136f8baa7a5249cc8e9fdec677997dd78549f8128", size = 699367 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/4a/18f670a2703e771a6775fbc354208e597ff062a88efb0cecc220a282210b/ml_dtypes-0.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d3b3db9990c3840986a0e70524e122cfa32b91139c3653df76121ba7776e015f", size = 753345 }, + { url = "https://files.pythonhosted.org/packages/ed/c6/358d85e274e22d53def0c85f3cbe0933475fa3cf6922e9dca66eb25cb22f/ml_dtypes-0.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e04fde367b2fe901b1d47234426fe8819909bd1dd862a5adb630f27789c20599", size = 4424962 }, + { url = "https://files.pythonhosted.org/packages/4c/b4/d766586e24e7a073333c8eb8bd9275f3c6fe0569b509ae7b1699d4f00c74/ml_dtypes-0.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54415257f00eb44fbcc807454efac3356f75644f1cbfc2d4e5522a72ae1dacab", size = 4475201 }, + { url = "https://files.pythonhosted.org/packages/14/87/30323ad2e52f56262019a4493fe5f5e71067c5561ce7e2f9c75de520f5e8/ml_dtypes-0.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:cb5cc7b25acabd384f75bbd78892d0c724943f3e2e1986254665a1aa10982e07", size = 213195 }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, +] + +[[package]] +name = "myst-parser" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "jinja2" }, + { name = "markdown-it-py" }, + { name = "mdit-py-plugins" }, + { name = "pyyaml" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/55/6d1741a1780e5e65038b74bce6689da15f620261c490c3511eb4c12bac4b/myst_parser-4.0.0.tar.gz", hash = "sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531", size = 93858 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/b4/b036f8fdb667587bb37df29dc6644681dd78b7a2a6321a34684b79412b28/myst_parser-4.0.0-py3-none-any.whl", hash = "sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d", size = 84563 }, +] + +[[package]] +name = "namex" +version = "0.0.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/48/d275cdb6216c6bb4f9351675795a0b48974e138f16b1ffe0252c1f8faa28/namex-0.0.8.tar.gz", hash = "sha256:32a50f6c565c0bb10aa76298c959507abdc0e850efe085dc38f3440fcb3aa90b", size = 6623 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/59/7854fbfb59f8ae35483ce93493708be5942ebb6328cd85b3a609df629736/namex-0.0.8-py3-none-any.whl", hash = "sha256:7ddb6c2bb0e753a311b7590f84f6da659dd0c05e65cb89d519d54c0a250c0487", size = 5806 }, +] + +[[package]] +name = "natsort" +version = "8.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268 }, +] + +[[package]] +name = "numpy" +version = "2.1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ca/1166b75c21abd1da445b97bf1fa2f14f423c6cfb4fc7c4ef31dccf9f6a94/numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761", size = 20166090 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/0b/620591441457e25f3404c8057eb924d04f161244cb8a3680d529419aa86e/numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f", size = 20836263 }, + { url = "https://files.pythonhosted.org/packages/45/e1/210b2d8b31ce9119145433e6ea78046e30771de3fe353f313b2778142f34/numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598", size = 13507771 }, + { url = "https://files.pythonhosted.org/packages/55/44/aa9ee3caee02fa5a45f2c3b95cafe59c44e4b278fbbf895a93e88b308555/numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57", size = 5075805 }, + { url = "https://files.pythonhosted.org/packages/78/d6/61de6e7e31915ba4d87bbe1ae859e83e6582ea14c6add07c8f7eefd8488f/numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe", size = 6608380 }, + { url = "https://files.pythonhosted.org/packages/3e/46/48bdf9b7241e317e6cf94276fe11ba673c06d1fdf115d8b4ebf616affd1a/numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43", size = 13602451 }, + { url = "https://files.pythonhosted.org/packages/70/50/73f9a5aa0810cdccda9c1d20be3cbe4a4d6ea6bfd6931464a44c95eef731/numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56", size = 16039822 }, + { url = "https://files.pythonhosted.org/packages/ad/cd/098bc1d5a5bc5307cfc65ee9369d0ca658ed88fbd7307b0d49fab6ca5fa5/numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a", size = 16411822 }, + { url = "https://files.pythonhosted.org/packages/83/a2/7d4467a2a6d984549053b37945620209e702cf96a8bc658bc04bba13c9e2/numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef", size = 14079598 }, + { url = "https://files.pythonhosted.org/packages/e9/6a/d64514dcecb2ee70bfdfad10c42b76cab657e7ee31944ff7a600f141d9e9/numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f", size = 6236021 }, + { url = "https://files.pythonhosted.org/packages/bb/f9/12297ed8d8301a401e7d8eb6b418d32547f1d700ed3c038d325a605421a4/numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed", size = 12560405 }, + { url = "https://files.pythonhosted.org/packages/a7/45/7f9244cd792e163b334e3a7f02dff1239d2890b6f37ebf9e82cbe17debc0/numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f", size = 20859062 }, + { url = "https://files.pythonhosted.org/packages/b1/b4/a084218e7e92b506d634105b13e27a3a6645312b93e1c699cc9025adb0e1/numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4", size = 13515839 }, + { url = "https://files.pythonhosted.org/packages/27/45/58ed3f88028dcf80e6ea580311dc3edefdd94248f5770deb980500ef85dd/numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e", size = 5116031 }, + { url = "https://files.pythonhosted.org/packages/37/a8/eb689432eb977d83229094b58b0f53249d2209742f7de529c49d61a124a0/numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0", size = 6629977 }, + { url = "https://files.pythonhosted.org/packages/42/a3/5355ad51ac73c23334c7caaed01adadfda49544f646fcbfbb4331deb267b/numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408", size = 13575951 }, + { url = "https://files.pythonhosted.org/packages/c4/70/ea9646d203104e647988cb7d7279f135257a6b7e3354ea6c56f8bafdb095/numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6", size = 16022655 }, + { url = "https://files.pythonhosted.org/packages/14/ce/7fc0612903e91ff9d0b3f2eda4e18ef9904814afcae5b0f08edb7f637883/numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f", size = 16399902 }, + { url = "https://files.pythonhosted.org/packages/ef/62/1d3204313357591c913c32132a28f09a26357e33ea3c4e2fe81269e0dca1/numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17", size = 14067180 }, + { url = "https://files.pythonhosted.org/packages/24/d7/78a40ed1d80e23a774cb8a34ae8a9493ba1b4271dde96e56ccdbab1620ef/numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48", size = 6291907 }, + { url = "https://files.pythonhosted.org/packages/86/09/a5ab407bd7f5f5599e6a9261f964ace03a73e7c6928de906981c31c38082/numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4", size = 12644098 }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.23.4" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/3a/0112397396dec37ffc8edd7836d48261b4d14ca60ec8ed7bc857cce1d916/nvidia_nccl_cu12-2.23.4-py3-none-manylinux2014_aarch64.whl", hash = "sha256:aa946c8327e22ced28e7cef508a334673abc42064ec85f02d005ba1785ea4cec", size = 198953892 }, + { url = "https://files.pythonhosted.org/packages/ed/1f/6482380ec8dcec4894e7503490fc536d846b0d59694acad9cf99f27d0e7d/nvidia_nccl_cu12-2.23.4-py3-none-manylinux2014_x86_64.whl", hash = "sha256:b097258d9aab2fa9f686e33c6fe40ae57b27df60cedbd15d139701bb5509e0c1", size = 198954603 }, +] + +[[package]] +name = "oauthlib" +version = "3.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/fa/fbf4001037904031639e6bfbfc02badfc7e12f137a8afa254df6c4c8a670/oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918", size = 177352 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688 }, +] + +[[package]] +name = "opencv-python" +version = "4.10.0.84" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/b70a2d9ab205110d715906fc8ec83fbb00404aeb3a37a0654fdb68eb0c8c/opencv-python-4.10.0.84.tar.gz", hash = "sha256:72d234e4582e9658ffea8e9cae5b63d488ad06994ef12d81dc303b17472f3526", size = 95103981 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/82/564168a349148298aca281e342551404ef5521f33fba17b388ead0a84dc5/opencv_python-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc182f8f4cda51b45f01c64e4cbedfc2f00aff799debebc305d8d0210c43f251", size = 54835524 }, + { url = "https://files.pythonhosted.org/packages/64/4a/016cda9ad7cf18c58ba074628a4eaae8aa55f3fd06a266398cef8831a5b9/opencv_python-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:71e575744f1d23f79741450254660442785f45a0797212852ee5199ef12eed98", size = 56475426 }, + { url = "https://files.pythonhosted.org/packages/81/e4/7a987ebecfe5ceaf32db413b67ff18eb3092c598408862fff4d7cc3fd19b/opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09a332b50488e2dda866a6c5573ee192fe3583239fb26ff2f7f9ceb0bc119ea6", size = 41746971 }, + { url = "https://files.pythonhosted.org/packages/3f/a4/d2537f47fd7fcfba966bd806e3ec18e7ee1681056d4b0a9c8d983983e4d5/opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ace140fc6d647fbe1c692bcb2abce768973491222c067c131d80957c595b71f", size = 62548253 }, + { url = "https://files.pythonhosted.org/packages/1e/39/bbf57e7b9dab623e8773f6ff36385456b7ae7fa9357a5e53db732c347eac/opencv_python-4.10.0.84-cp37-abi3-win32.whl", hash = "sha256:2db02bb7e50b703f0a2d50c50ced72e95c574e1e5a0bb35a8a86d0b35c98c236", size = 28737688 }, + { url = "https://files.pythonhosted.org/packages/ec/6c/fab8113424af5049f85717e8e527ca3773299a3c6b02506e66436e19874f/opencv_python-4.10.0.84-cp37-abi3-win_amd64.whl", hash = "sha256:32dbbd94c26f611dc5cc6979e6b7aa1f55a64d6b463cc1dcd3c95505a63e48fe", size = 38842521 }, +] + +[[package]] +name = "optree" +version = "0.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/f2/56afdaeaae36b076659be7db8e72be0924dd64ebd1c131675c77f7e704a6/optree-0.13.1.tar.gz", hash = "sha256:af67856aa8073d237fe67313d84f8aeafac32c1cef7239c628a2768d02679c43", size = 155738 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/53/f3727cad24f16a06666f328f1212476988cadac9b9e7919ddfb2c22eb662/optree-0.13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f788b2ad120deb73b4908a74473cd6de79cfb9f33bbe9dcb59cea2e2477d4e28", size = 608270 }, + { url = "https://files.pythonhosted.org/packages/64/f2/68beb9da2dd52baa50e7a589ed2bd8434fdd70cdba06754aa5910263da06/optree-0.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2909cb42add6bb1a5a2b0243bdd8c4b861bf072f3741e26239481907ac8ad4e6", size = 325703 }, + { url = "https://files.pythonhosted.org/packages/45/db/08921e56f3425bf649eb593eb28775263c935d029985d35572dc5690cc1a/optree-0.13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbc5fa2ff5090389f3a906567446f01d692bd6fe5cfcc5ae2d5861f24e8e0e4d", size = 355813 }, + { url = "https://files.pythonhosted.org/packages/e5/e3/587e0d28dc2cee064902adfebca97db124e12b275dbe9c2b05a70a22345f/optree-0.13.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4711f5cac5a2a49c3d6c9f0eca7b77c22b452170bb33ea01c3214ebb17931db9", size = 402566 }, + { url = "https://files.pythonhosted.org/packages/8a/1d/0d5bbab8c99580b732b89ef2c5fcdd6ef410478295949fdf2984fa1bfc28/optree-0.13.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c4ab1d391b89cb88eb3c63383d5eb0930bc21141de9d5acd277feed9e38eb65", size = 397005 }, + { url = "https://files.pythonhosted.org/packages/16/fa/fc2a8183e14f0d195d25824bf65095ff32b34bd469614a6c30d0a596a30f/optree-0.13.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5e5f09c85ae558a6bdaea57e63168082e728e777391393e9e2792f0d15b7b59", size = 369400 }, + { url = "https://files.pythonhosted.org/packages/9f/42/8c08ce4ebb3d9a6e4415f1a97830c84879e2d1a43710a7c8a18b2c3e169d/optree-0.13.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c8ee1e988c634a451146b87d9ebdbf650a75dc1f52a9cffcd89fabb7289321c", size = 390179 }, + { url = "https://files.pythonhosted.org/packages/06/02/3a701d6307fdfefe4fcecbac644803e2a4314ab2406ff465e03129cc85f6/optree-0.13.1-cp313-cp313-win32.whl", hash = "sha256:5b6531cd4eb23fadbbf77faf834e1119da06d7af3154f55786b59953cd87bb8a", size = 264264 }, + { url = "https://files.pythonhosted.org/packages/ef/f9/8a1421181c5eb0c0f81d1423a900baeb3faba68a48747bbdffb7581239ac/optree-0.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:27d81dc43b522ba47ba7d2e7d91dbb486940348b1bf85caeb0afc2815c0aa492", size = 293682 }, + { url = "https://files.pythonhosted.org/packages/80/34/d1b1849a6240385c4a3af5da9425b11912204d0b1cf142d802815319b73a/optree-0.13.1-cp313-cp313-win_arm64.whl", hash = "sha256:f39c7174a3f3cdc3f5fe6fb4b832f608c40ac174d7567ed6734b2ee952094631", size = 293670 }, + { url = "https://files.pythonhosted.org/packages/0d/d6/f81e6748bcc3f35a2f570a814014e3418b0ed425d7cbc2b42d88d12863d5/optree-0.13.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:3010ae24e994f6e00071098d34e98e78eb995b7454a2ef629a0bf7df17441b24", size = 702861 }, + { url = "https://files.pythonhosted.org/packages/08/7f/70a2d02110ccb245bc57bd9ad57668acfea0ff364c27d7dfe1735ede79ed/optree-0.13.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5b5626c38d4a18a144063db5c1dbb558431d83ca10682324f74665a12214801f", size = 370740 }, + { url = "https://files.pythonhosted.org/packages/63/37/4ddf05267467809236203e2007e9443519c4d55e0744ce7eea1aa74dffee/optree-0.13.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1935639dd498a42367633e3877797e1330e39d44d48bbca1a136bb4dbe4c1bc9", size = 374695 }, + { url = "https://files.pythonhosted.org/packages/19/f2/51a63a799f6dce31813d7e02a7547394aebcb39f407e62038ecbd999d490/optree-0.13.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01819c3df950696f32c91faf8d376ae6b695ffdba18f330f1cab6b8e314e4612", size = 418671 }, + { url = "https://files.pythonhosted.org/packages/f0/7c/a08191e0c9202f2be9c415057eea3cf3a5af18e9a6d81f4c7b0e6faf0a1f/optree-0.13.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48c29d9c6c64c8dc48c8ee97f7c1d5cdb83e37320f0be0857c06ce4b97994aea", size = 414966 }, + { url = "https://files.pythonhosted.org/packages/8f/37/7bf815f4da7234e387863228b17246b42b8c02553882581a4013a64a88d0/optree-0.13.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:025d23400b8b579462a251420f0a9ae77d3d3593f84276f3465985731d79d722", size = 389219 }, + { url = "https://files.pythonhosted.org/packages/3d/84/bb521a66d3a84fe2f1500ef67d245c2cc1a26277fcaaf4bc70b22c06e99b/optree-0.13.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55e82426bef151149cfa41d68ac957730fcd420996c0db8324fca81aa6a810ba", size = 405377 }, + { url = "https://files.pythonhosted.org/packages/06/99/3eb53829c4c0b6dc20115d957d2d8e945630ddf40c656dc4e39c5a6e51f2/optree-0.13.1-cp313-cp313t-win32.whl", hash = "sha256:e40f018f522fcfd244688d1b3a360518e636ba7f636385aae0566eae3e7d29bc", size = 292734 }, + { url = "https://files.pythonhosted.org/packages/2f/59/d7601959ad0b90d309794c0975a256304488b4c5671f24e3e12101ade7ef/optree-0.13.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d580f1bf23bb352c4db6b3544f282f1ac08dcb0d9ab537d25e56220353438cf7", size = 331457 }, + { url = "https://files.pythonhosted.org/packages/8b/36/c01a5bc34660d46c6a3b1fe090bbdc8c76af7b5c1a6613cc671aa6df8349/optree-0.13.1-cp313-cp313t-win_arm64.whl", hash = "sha256:c4d13f55dbd509d27be3af54d53b4ca0751bc518244ced6d0567e518e51452a2", size = 331470 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pandas" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.13'" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643 }, + { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573 }, + { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085 }, + { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809 }, + { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316 }, + { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055 }, + { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175 }, + { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650 }, + { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177 }, + { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526 }, + { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013 }, + { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620 }, + { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 }, +] + +[[package]] +name = "patsy" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/81/74f6a65b848ffd16c18f920620ce999fe45fe27f01ab3911260ce4ed85e4/patsy-1.0.1.tar.gz", hash = "sha256:e786a9391eec818c054e359b737bbce692f051aee4c661f4141cc88fb459c0c4", size = 396010 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/2b/b50d3d08ea0fc419c183a84210571eba005328efa62b6b98bc28e9ead32a/patsy-1.0.1-py2.py3-none-any.whl", hash = "sha256:751fb38f9e97e62312e921a1954b81e1bb2bcda4f5eeabaf94db251ee791509c", size = 232923 }, +] + +[[package]] +name = "pillow" +version = "11.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/26/0d95c04c868f6bdb0c447e3ee2de5564411845e36a858cfd63766bc7b563/pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739", size = 46737780 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/24/e2e15e392d00fcf4215907465d8ec2a2f23bcec1481a8ebe4ae760459995/pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699", size = 3147300 }, + { url = "https://files.pythonhosted.org/packages/43/72/92ad4afaa2afc233dc44184adff289c2e77e8cd916b3ddb72ac69495bda3/pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38", size = 2978742 }, + { url = "https://files.pythonhosted.org/packages/9e/da/c8d69c5bc85d72a8523fe862f05ababdc52c0a755cfe3d362656bb86552b/pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2", size = 4194349 }, + { url = "https://files.pythonhosted.org/packages/cd/e8/686d0caeed6b998351d57796496a70185376ed9c8ec7d99e1d19ad591fc6/pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2", size = 4298714 }, + { url = "https://files.pythonhosted.org/packages/ec/da/430015cec620d622f06854be67fd2f6721f52fc17fca8ac34b32e2d60739/pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527", size = 4208514 }, + { url = "https://files.pythonhosted.org/packages/44/ae/7e4f6662a9b1cb5f92b9cc9cab8321c381ffbee309210940e57432a4063a/pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa", size = 4380055 }, + { url = "https://files.pythonhosted.org/packages/74/d5/1a807779ac8a0eeed57f2b92a3c32ea1b696e6140c15bd42eaf908a261cd/pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f", size = 4296751 }, + { url = "https://files.pythonhosted.org/packages/38/8c/5fa3385163ee7080bc13026d59656267daaaaf3c728c233d530e2c2757c8/pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb", size = 4430378 }, + { url = "https://files.pythonhosted.org/packages/ca/1d/ad9c14811133977ff87035bf426875b93097fb50af747793f013979facdb/pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798", size = 2249588 }, + { url = "https://files.pythonhosted.org/packages/fb/01/3755ba287dac715e6afdb333cb1f6d69740a7475220b4637b5ce3d78cec2/pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de", size = 2567509 }, + { url = "https://files.pythonhosted.org/packages/c0/98/2c7d727079b6be1aba82d195767d35fcc2d32204c7a5820f822df5330152/pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84", size = 2254791 }, + { url = "https://files.pythonhosted.org/packages/eb/38/998b04cc6f474e78b563716b20eecf42a2fa16a84589d23c8898e64b0ffd/pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b", size = 3150854 }, + { url = "https://files.pythonhosted.org/packages/13/8e/be23a96292113c6cb26b2aa3c8b3681ec62b44ed5c2bd0b258bd59503d3c/pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003", size = 2982369 }, + { url = "https://files.pythonhosted.org/packages/97/8a/3db4eaabb7a2ae8203cd3a332a005e4aba00067fc514aaaf3e9721be31f1/pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2", size = 4333703 }, + { url = "https://files.pythonhosted.org/packages/28/ac/629ffc84ff67b9228fe87a97272ab125bbd4dc462745f35f192d37b822f1/pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a", size = 4412550 }, + { url = "https://files.pythonhosted.org/packages/d6/07/a505921d36bb2df6868806eaf56ef58699c16c388e378b0dcdb6e5b2fb36/pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8", size = 4461038 }, + { url = "https://files.pythonhosted.org/packages/d6/b9/fb620dd47fc7cc9678af8f8bd8c772034ca4977237049287e99dda360b66/pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8", size = 2253197 }, + { url = "https://files.pythonhosted.org/packages/df/86/25dde85c06c89d7fc5db17940f07aae0a56ac69aa9ccb5eb0f09798862a8/pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904", size = 2572169 }, + { url = "https://files.pythonhosted.org/packages/51/85/9c33f2517add612e17f3381aee7c4072779130c634921a756c97bc29fb49/pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3", size = 2256828 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "pygments" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, +] + +[[package]] +name = "pyparsing" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/d5/e5aeee5387091148a19e1145f63606619cb5f20b83fccb63efae6474e7b2/pyparsing-3.2.0.tar.gz", hash = "sha256:cbf74e27246d595d9a74b186b810f6fbb86726dbf3b9532efb343f6d7294fe9c", size = 920984 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/ec/2eb3cd785efd67806c46c13a17339708ddc346cbb684eade7a6e6f79536a/pyparsing-3.2.0-py3-none-any.whl", hash = "sha256:93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84", size = 106921 }, +] + +[[package]] +name = "pytest" +version = "8.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, +] + +[[package]] +name = "pytest-cov" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "pytz" +version = "2024.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "oauthlib" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/52/531ef197b426646f26b53815a7d2a67cb7a331ef098bb276db26a68ac49f/requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a", size = 52027 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/bb/5deac77a9af870143c684ab46a7934038a53eb4aa975bc0687ed6ca2c610/requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5", size = 23892 }, +] + +[[package]] +name = "rich" +version = "13.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, +] + +[[package]] +name = "ruff" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/d0/8ff5b189d125f4260f2255d143bf2fa413b69c2610c405ace7a0a8ec81ec/ruff-0.8.1.tar.gz", hash = "sha256:3583db9a6450364ed5ca3f3b4225958b24f78178908d5c4bc0f46251ccca898f", size = 3313222 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/d6/1a6314e568db88acdbb5121ed53e2c52cebf3720d3437a76f82f923bf171/ruff-0.8.1-py3-none-linux_armv6l.whl", hash = "sha256:fae0805bd514066f20309f6742f6ee7904a773eb9e6c17c45d6b1600ca65c9b5", size = 10532605 }, + { url = "https://files.pythonhosted.org/packages/89/a8/a957a8812e31facffb6a26a30be0b5b4af000a6e30c7d43a22a5232a3398/ruff-0.8.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8a4f7385c2285c30f34b200ca5511fcc865f17578383db154e098150ce0a087", size = 10278243 }, + { url = "https://files.pythonhosted.org/packages/a8/23/9db40fa19c453fabf94f7a35c61c58f20e8200b4734a20839515a19da790/ruff-0.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd054486da0c53e41e0086e1730eb77d1f698154f910e0cd9e0d64274979a209", size = 9917739 }, + { url = "https://files.pythonhosted.org/packages/e2/a0/6ee2d949835d5701d832fc5acd05c0bfdad5e89cfdd074a171411f5ccad5/ruff-0.8.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2029b8c22da147c50ae577e621a5bfbc5d1fed75d86af53643d7a7aee1d23871", size = 10779153 }, + { url = "https://files.pythonhosted.org/packages/7a/25/9c11dca9404ef1eb24833f780146236131a3c7941de394bc356912ef1041/ruff-0.8.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2666520828dee7dfc7e47ee4ea0d928f40de72056d929a7c5292d95071d881d1", size = 10304387 }, + { url = "https://files.pythonhosted.org/packages/c8/b9/84c323780db1b06feae603a707d82dbbd85955c8c917738571c65d7d5aff/ruff-0.8.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:333c57013ef8c97a53892aa56042831c372e0bb1785ab7026187b7abd0135ad5", size = 11360351 }, + { url = "https://files.pythonhosted.org/packages/6b/e1/9d4bbb2ace7aad14ded20e4674a48cda5b902aed7a1b14e6b028067060c4/ruff-0.8.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:288326162804f34088ac007139488dcb43de590a5ccfec3166396530b58fb89d", size = 12022879 }, + { url = "https://files.pythonhosted.org/packages/75/28/752ff6120c0e7f9981bc4bc275d540c7f36db1379ba9db9142f69c88db21/ruff-0.8.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b12c39b9448632284561cbf4191aa1b005882acbc81900ffa9f9f471c8ff7e26", size = 11610354 }, + { url = "https://files.pythonhosted.org/packages/ba/8c/967b61c2cc8ebd1df877607fbe462bc1e1220b4a30ae3352648aec8c24bd/ruff-0.8.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:364e6674450cbac8e998f7b30639040c99d81dfb5bbc6dfad69bc7a8f916b3d1", size = 12813976 }, + { url = "https://files.pythonhosted.org/packages/7f/29/e059f945d6bd2d90213387b8c360187f2fefc989ddcee6bbf3c241329b92/ruff-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b22346f845fec132aa39cd29acb94451d030c10874408dbf776af3aaeb53284c", size = 11154564 }, + { url = "https://files.pythonhosted.org/packages/55/47/cbd05e5a62f3fb4c072bc65c1e8fd709924cad1c7ec60a1000d1e4ee8307/ruff-0.8.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b2f2f7a7e7648a2bfe6ead4e0a16745db956da0e3a231ad443d2a66a105c04fa", size = 10760604 }, + { url = "https://files.pythonhosted.org/packages/bb/ee/4c3981c47147c72647a198a94202633130cfda0fc95cd863a553b6f65c6a/ruff-0.8.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:adf314fc458374c25c5c4a4a9270c3e8a6a807b1bec018cfa2813d6546215540", size = 10391071 }, + { url = "https://files.pythonhosted.org/packages/6b/e6/083eb61300214590b188616a8ac6ae1ef5730a0974240fb4bec9c17de78b/ruff-0.8.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a885d68342a231b5ba4d30b8c6e1b1ee3a65cf37e3d29b3c74069cdf1ee1e3c9", size = 10896657 }, + { url = "https://files.pythonhosted.org/packages/77/bd/aacdb8285d10f1b943dbeb818968efca35459afc29f66ae3bd4596fbf954/ruff-0.8.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d2c16e3508c8cc73e96aa5127d0df8913d2290098f776416a4b157657bee44c5", size = 11228362 }, + { url = "https://files.pythonhosted.org/packages/39/72/fcb7ad41947f38b4eaa702aca0a361af0e9c2bf671d7fd964480670c297e/ruff-0.8.1-py3-none-win32.whl", hash = "sha256:93335cd7c0eaedb44882d75a7acb7df4b77cd7cd0d2255c93b28791716e81790", size = 8803476 }, + { url = "https://files.pythonhosted.org/packages/e4/ea/cae9aeb0f4822c44651c8407baacdb2e5b4dcd7b31a84e1c5df33aa2cc20/ruff-0.8.1-py3-none-win_amd64.whl", hash = "sha256:2954cdbe8dfd8ab359d4a30cd971b589d335a44d444b6ca2cb3d1da21b75e4b6", size = 9614463 }, + { url = "https://files.pythonhosted.org/packages/eb/76/fbb4bd23dfb48fa7758d35b744413b650a9fd2ddd93bca77e30376864414/ruff-0.8.1-py3-none-win_arm64.whl", hash = "sha256:55873cc1a473e5ac129d15eccb3c008c096b94809d693fc7053f588b67822737", size = 8959621 }, +] + +[[package]] +name = "scikit-learn" +version = "1.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "numpy" }, + { name = "scipy" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/37/59/44985a2bdc95c74e34fef3d10cb5d93ce13b0e2a7baefffe1b53853b502d/scikit_learn-1.5.2.tar.gz", hash = "sha256:b4237ed7b3fdd0a4882792e68ef2545d5baa50aca3bb45aa7df468138ad8f94d", size = 7001680 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/50/8891028437858cc510e13578fe7046574a60c2aaaa92b02d64aac5b1b412/scikit_learn-1.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9a702e2de732bbb20d3bad29ebd77fc05a6b427dc49964300340e4c9328b3f5", size = 12025584 }, + { url = "https://files.pythonhosted.org/packages/d2/79/17feef8a1c14149436083bec0e61d7befb4812e272d5b20f9d79ea3e9ab1/scikit_learn-1.5.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:b0768ad641981f5d3a198430a1d31c3e044ed2e8a6f22166b4d546a5116d7908", size = 10959795 }, + { url = "https://files.pythonhosted.org/packages/b1/c8/f08313f9e2e656bd0905930ae8bf99a573ea21c34666a813b749c338202f/scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:178ddd0a5cb0044464fc1bfc4cca5b1833bfc7bb022d70b05db8530da4bb3dd3", size = 12077302 }, + { url = "https://files.pythonhosted.org/packages/a7/48/fbfb4dc72bed0fe31fe045fb30e924909ad03f717c36694351612973b1a9/scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7284ade780084d94505632241bf78c44ab3b6f1e8ccab3d2af58e0e950f9c12", size = 13002811 }, + { url = "https://files.pythonhosted.org/packages/a5/e7/0c869f9e60d225a77af90d2aefa7a4a4c0e745b149325d1450f0f0ce5399/scikit_learn-1.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:b7b0f9a0b1040830d38c39b91b3a44e1b643f4b36e36567b80b7c6bd2202a27f", size = 10951354 }, +] + +[[package]] +name = "scipy" +version = "1.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/11/4d44a1f274e002784e4dbdb81e0ea96d2de2d1045b2132d5af62cc31fd28/scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417", size = 58620554 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/ef/ac98346db016ff18a6ad7626a35808f37074d25796fd0234c2bb0ed1e054/scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79", size = 39091068 }, + { url = "https://files.pythonhosted.org/packages/b9/cc/70948fe9f393b911b4251e96b55bbdeaa8cca41f37c26fd1df0232933b9e/scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e", size = 29875417 }, + { url = "https://files.pythonhosted.org/packages/3b/2e/35f549b7d231c1c9f9639f9ef49b815d816bf54dd050da5da1c11517a218/scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73", size = 23084508 }, + { url = "https://files.pythonhosted.org/packages/3f/d6/b028e3f3e59fae61fb8c0f450db732c43dd1d836223a589a8be9f6377203/scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e", size = 25503364 }, + { url = "https://files.pythonhosted.org/packages/a7/2f/6c142b352ac15967744d62b165537a965e95d557085db4beab2a11f7943b/scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d", size = 35292639 }, + { url = "https://files.pythonhosted.org/packages/56/46/2449e6e51e0d7c3575f289f6acb7f828938eaab8874dbccfeb0cd2b71a27/scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e", size = 40798288 }, + { url = "https://files.pythonhosted.org/packages/32/cd/9d86f7ed7f4497c9fd3e39f8918dd93d9f647ba80d7e34e4946c0c2d1a7c/scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06", size = 42524647 }, + { url = "https://files.pythonhosted.org/packages/f5/1b/6ee032251bf4cdb0cc50059374e86a9f076308c1512b61c4e003e241efb7/scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84", size = 44469524 }, +] + +[[package]] +name = "six" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 }, +] + +[[package]] +name = "soupsieve" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 }, +] + +[[package]] +name = "sphinx" +version = "8.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils" }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 }, +] + +[[package]] +name = "sphinx-autoapi" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid", marker = "python_full_version >= '3.13'" }, + { name = "jinja2" }, + { name = "pyyaml" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4a/eb/cc243583bb1d518ca3b10998c203d919a8ed90affd4831f2b61ad09043d2/sphinx_autoapi-3.4.0.tar.gz", hash = "sha256:e6d5371f9411bbb9fca358c00a9e57aef3ac94cbfc5df4bab285946462f69e0c", size = 29292 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/d6/f2acdc2567337fd5f5dc091a4e58d8a0fb14927b9779fc1e5ecee96d9824/sphinx_autoapi-3.4.0-py3-none-any.whl", hash = "sha256:4027fef2875a22c5f2a57107c71641d82f6166bf55beb407a47aaf3ef14e7b92", size = 34095 }, +] + +[[package]] +name = "sphinx-pyproject" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dom-toml" }, + { name = "domdf-python-tools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/97/aa8cec3da3e78f2c396b63332e2fe92fe43f7ff2ad19b3998735f28b0a7f/sphinx_pyproject-0.3.0.tar.gz", hash = "sha256:efc4ee9d96f579c4e4ed1ac273868c64565e88c8e37fe6ec2dc59fbcd57684ab", size = 7695 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/d5/89cb47c6399fd57ca451af15361499813c5d53e588cb6e00d89411ce724f/sphinx_pyproject-0.3.0-py3-none-any.whl", hash = "sha256:3aca968919f5ecd390f96874c3f64a43c9c7fcfdc2fd4191a781ad9228501b52", size = 23076 }, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 }, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 }, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 }, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 }, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 }, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, +] + +[[package]] +name = "statsmodels" +version = "0.14.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "patsy" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1f/3b/963a015dd8ea17e10c7b0e2f14d7c4daec903baf60a017e756b57953a4bf/statsmodels-0.14.4.tar.gz", hash = "sha256:5d69e0f39060dc72c067f9bb6e8033b6dccdb0bae101d76a7ef0bcc94e898b67", size = 20354802 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/f8/2662e6a101315ad336f75168fa9bac71f913ebcb92a6be84031d84a0f21f/statsmodels-0.14.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5a24f5d2c22852d807d2b42daf3a61740820b28d8381daaf59dcb7055bf1a79", size = 10186886 }, + { url = "https://files.pythonhosted.org/packages/fa/c0/ee6e8ed35fc1ca9c7538c592f4974547bf72274bc98db1ae4a6e87481a83/statsmodels-0.14.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df4f7864606fa843d7e7c0e6af288f034a2160dba14e6ccc09020a3cf67cb092", size = 9880066 }, + { url = "https://files.pythonhosted.org/packages/d1/97/3380ca6d8fd66cfb3d12941e472642f26e781a311c355a4e97aab2ed0216/statsmodels-0.14.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91341cbde9e8bea5fb419a76e09114e221567d03f34ca26e6d67ae2c27d8fe3c", size = 10283521 }, + { url = "https://files.pythonhosted.org/packages/fe/2a/55c5b5c5e5124a202ea3fe0bcdbdeceaf91b4ec6164b8434acb9dd97409c/statsmodels-0.14.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1322286a7bfdde2790bf72d29698a1b76c20b8423a55bdcd0d457969d0041f72", size = 10723228 }, + { url = "https://files.pythonhosted.org/packages/4f/76/67747e49dc758daae06f33aad8247b718cd7d224f091d2cd552681215bb2/statsmodels-0.14.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e31b95ac603415887c9f0d344cb523889cf779bc52d68e27e2d23c358958fec7", size = 10859503 }, + { url = "https://files.pythonhosted.org/packages/1d/eb/cb8b01f5edf8f135eb3d0553d159db113a35b2948d0e51eeb735e7ae09ea/statsmodels-0.14.4-cp313-cp313-win_amd64.whl", hash = "sha256:81030108d27aecc7995cac05aa280cf8c6025f6a6119894eef648997936c2dd0", size = 9817574 }, +] + +[[package]] +name = "sympy" +version = "1.13.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/11/8a/5a7fd6284fa8caac23a26c9ddf9c30485a48169344b4bd3b0f02fef1890f/sympy-1.13.3.tar.gz", hash = "sha256:b27fd2c6530e0ab39e275fc9b683895367e51d5da91baa8d3d64db2565fec4d9", size = 7533196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/ff/c87e0622b1dadea79d2fb0b25ade9ed98954c9033722eb707053d310d4f3/sympy-1.13.3-py3-none-any.whl", hash = "sha256:54612cf55a62755ee71824ce692986f23c88ffa77207b30c1368eda4a7060f73", size = 6189483 }, +] + +[[package]] +name = "thealgorithms-python" +version = "0.0.1" +source = { virtual = "." } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "fake-useragent" }, + { name = "imageio" }, + { name = "keras" }, + { name = "lxml" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "opencv-python" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "requests" }, + { name = "rich" }, + { name = "scikit-learn" }, + { name = "sphinx-pyproject" }, + { name = "statsmodels" }, + { name = "sympy" }, + { name = "tweepy" }, + { name = "typing-extensions" }, + { name = "xgboost" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, + { name = "pytest-cov" }, +] +docs = [ + { name = "myst-parser" }, + { name = "sphinx-autoapi" }, + { name = "sphinx-pyproject" }, +] +euler-validate = [ + { name = "numpy" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "requests" }, +] +lint = [ + { name = "codespell" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "beautifulsoup4", specifier = ">=4.12.3" }, + { name = "fake-useragent", specifier = ">=1.5.1" }, + { name = "imageio", specifier = ">=2.36.1" }, + { name = "keras", specifier = ">=3.7" }, + { name = "lxml", specifier = ">=5.3" }, + { name = "matplotlib", specifier = ">=3.9.3" }, + { name = "numpy", specifier = ">=2.1.3" }, + { name = "opencv-python", specifier = ">=4.10.0.84" }, + { name = "pandas", specifier = ">=2.2.3" }, + { name = "pillow", specifier = ">=11" }, + { name = "requests", specifier = ">=2.32.3" }, + { name = "rich", specifier = ">=13.9.4" }, + { name = "scikit-learn", specifier = ">=1.5.2" }, + { name = "sphinx-pyproject", specifier = ">=0.3" }, + { name = "statsmodels", specifier = ">=0.14.4" }, + { name = "sympy", specifier = ">=1.13.3" }, + { name = "tweepy", specifier = ">=4.14" }, + { name = "typing-extensions", specifier = ">=4.12.2" }, + { name = "xgboost", specifier = ">=2.1.3" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "pytest", specifier = ">=8.3.4" }, + { name = "pytest-cov", specifier = ">=6" }, +] +docs = [ + { name = "myst-parser", specifier = ">=4.0.0" }, + { name = "sphinx-autoapi", specifier = ">=3.4.0" }, + { name = "sphinx-pyproject", specifier = ">=0.3.0" }, +] +euler-validate = [ + { name = "numpy", specifier = ">=2.1.3" }, + { name = "pytest", specifier = ">=8.3.4" }, + { name = "pytest-cov", specifier = ">=6.0.0" }, + { name = "requests", specifier = ">=2.32.3" }, +] +lint = [ + { name = "codespell", specifier = ">=2.3" }, + { name = "ruff", specifier = ">=0.8.1" }, +] + +[[package]] +name = "threadpoolctl" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/55/b5148dcbf72f5cde221f8bfe3b6a540da7aa1842f6b491ad979a6c8b84af/threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107", size = 41936 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467", size = 18414 }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, +] + +[[package]] +name = "tweepy" +version = "4.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "oauthlib" }, + { name = "requests" }, + { name = "requests-oauthlib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/75/1c/0db8c3cf9d31bf63853ff612d201060ae78e6db03468a70e063bef0eda62/tweepy-4.14.0.tar.gz", hash = "sha256:1f9f1707d6972de6cff6c5fd90dfe6a449cd2e0d70bd40043ffab01e07a06c8c", size = 88623 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/78/ba0065d5636bbf4a35b78c4f81b74e7858b609cdf69e629d6da5c91b9d92/tweepy-4.14.0-py3-none-any.whl", hash = "sha256:db6d3844ccc0c6d27f339f12ba8acc89912a961da513c1ae50fa2be502a56afb", size = 98520 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "tzdata" +version = "2024.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/34/943888654477a574a86a98e9896bae89c7aa15078ec29f490fef2f1e5384/tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", size = 193282 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/ab/7e5f53c3b9d14972843a647d8d7a853969a58aecc7559cb3267302c94774/tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd", size = 346586 }, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 }, +] + +[[package]] +name = "xgboost" +version = "2.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine != 'aarch64' and platform_system == 'Linux'" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/b0/131ffc4a15fd3acee9be3a7baa6b2fa6faa479799c51b880de9fc3ddf550/xgboost-2.1.3.tar.gz", hash = "sha256:7699ec4226156887d3afc665c63ab87469db9d46e361c702ba9fccd22535730c", size = 1090326 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/c6/773ebd84414879bd0566788868ae46a6574f6efaf81e694f01ea1fed3277/xgboost-2.1.3-py3-none-macosx_10_15_x86_64.macosx_11_0_x86_64.macosx_12_0_x86_64.whl", hash = "sha256:c9b0c92f13e3650e1e1cf92ff9ecef3efc6f5dc3d10ce17858df2081a89976ef", size = 2139909 }, + { url = "https://files.pythonhosted.org/packages/28/3c/ddf5d9eb742cdb7fbcd5c854bce07471bad01194ac37de91db64fbef0c58/xgboost-2.1.3-py3-none-macosx_12_0_arm64.whl", hash = "sha256:fcbf1912a852bd07a7007be350c8dc3a484c5e775b612f2b3cd082fc76240eb3", size = 1938631 }, + { url = "https://files.pythonhosted.org/packages/4a/3a/8cd69a216993fd9d54ceb079d1b357b7ef50678b3c2695d8a71962b8d0aa/xgboost-2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:27af88df1162cee016c67f267a0a16c3db1c48f256e12f64c45c8f8edf9571cd", size = 4441261 }, + { url = "https://files.pythonhosted.org/packages/48/bc/05d7db90d421c5e3d681a12fd1eb087e37bf2e9bbe2b105422d6319ecc92/xgboost-2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:32a43526208fe676527c698cb852e0e9515e6d7294143780e476d335290a131b", size = 4532380 }, + { url = "https://files.pythonhosted.org/packages/0f/c8/f679a816c06a4a6d23da3f4b448d5f0615b51de2886ad3e3e695d17121b3/xgboost-2.1.3-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:5d33090880f3d474f8cf5dda557c7bf8dbceefb62f2fd655c77efcabb9cac222", size = 4207000 }, + { url = "https://files.pythonhosted.org/packages/32/93/66826e2f50cefecbb0a44bd1e667316bf0a3c8e78cd1f0cdf52f5b2c5c6f/xgboost-2.1.3-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:8d85d38553855a1f8c40b8fbccca86af19202f91b244e2c7f77afbb2a6d9d785", size = 153894508 }, + { url = "https://files.pythonhosted.org/packages/70/58/2f94976df39470fb00eec2cb4f914dde44cd0df8d96483208bf7db4bc97e/xgboost-2.1.3-py3-none-win_amd64.whl", hash = "sha256:25c0ffcbd62aac5bc22c79e08b5b2edad1d5e37f16610ebefa5f06f3e2ea3d96", size = 124909665 }, +] From 98391e33ea2a87375a7f744eba3d57918237b4e7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 18:40:43 +0100 Subject: [PATCH 014/126] [pre-commit.ci] pre-commit autoupdate (#12428) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.1 → v0.8.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.1...v0.8.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bef251749c19..884b10661a49 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.1 + rev: v0.8.2 hooks: - id: ruff - id: ruff-format From f8e595e048f1cbd763e0a1f8c0ffb4dff335b841 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:53:36 +0100 Subject: [PATCH 015/126] [pre-commit.ci] pre-commit autoupdate (#12439) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.2 → v0.8.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.2...v0.8.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 884b10661a49..0c8108ac55be 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.2 + rev: v0.8.3 hooks: - id: ruff - id: ruff-format From 4abfce2791c081f65580bc1fefdf5a4d8ee7b5fc Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 23 Dec 2024 06:55:22 +0300 Subject: [PATCH 016/126] Fix sphinx/build_docs warnings for audio_filters (#12449) * updating DIRECTORY.md * Fix sphinx/build_docs warnings for audio_filters * Improve * Fix * Fix * Fix --------- Co-authored-by: MaximSmolskiy --- audio_filters/iir_filter.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/audio_filters/iir_filter.py b/audio_filters/iir_filter.py index f3c1ad43b001..fa3e6c54b33f 100644 --- a/audio_filters/iir_filter.py +++ b/audio_filters/iir_filter.py @@ -10,13 +10,17 @@ class IIRFilter: Implementation details: Based on the 2nd-order function from - https://en.wikipedia.org/wiki/Digital_biquad_filter, + https://en.wikipedia.org/wiki/Digital_biquad_filter, this generalized N-order function was made. Using the following transfer function - H(z)=\frac{b_{0}+b_{1}z^{-1}+b_{2}z^{-2}+...+b_{k}z^{-k}}{a_{0}+a_{1}z^{-1}+a_{2}z^{-2}+...+a_{k}z^{-k}} + .. math:: H(z)=\frac{b_{0}+b_{1}z^{-1}+b_{2}z^{-2}+...+b_{k}z^{-k}} + {a_{0}+a_{1}z^{-1}+a_{2}z^{-2}+...+a_{k}z^{-k}} + we can rewrite this to - y[n]={\frac{1}{a_{0}}}\left(\left(b_{0}x[n]+b_{1}x[n-1]+b_{2}x[n-2]+...+b_{k}x[n-k]\right)-\left(a_{1}y[n-1]+a_{2}y[n-2]+...+a_{k}y[n-k]\right)\right) + .. math:: y[n]={\frac{1}{a_{0}}} + \left(\left(b_{0}x[n]+b_{1}x[n-1]+b_{2}x[n-2]+...+b_{k}x[n-k]\right)- + \left(a_{1}y[n-1]+a_{2}y[n-2]+...+a_{k}y[n-k]\right)\right) """ def __init__(self, order: int) -> None: @@ -34,17 +38,19 @@ def __init__(self, order: int) -> None: def set_coefficients(self, a_coeffs: list[float], b_coeffs: list[float]) -> None: """ - Set the coefficients for the IIR filter. These should both be of size order + 1. - a_0 may be left out, and it will use 1.0 as default value. + Set the coefficients for the IIR filter. + These should both be of size `order` + 1. + :math:`a_0` may be left out, and it will use 1.0 as default value. This method works well with scipy's filter design functions - >>> # Make a 2nd-order 1000Hz butterworth lowpass filter - >>> import scipy.signal - >>> b_coeffs, a_coeffs = scipy.signal.butter(2, 1000, - ... btype='lowpass', - ... fs=48000) - >>> filt = IIRFilter(2) - >>> filt.set_coefficients(a_coeffs, b_coeffs) + + >>> # Make a 2nd-order 1000Hz butterworth lowpass filter + >>> import scipy.signal + >>> b_coeffs, a_coeffs = scipy.signal.butter(2, 1000, + ... btype='lowpass', + ... fs=48000) + >>> filt = IIRFilter(2) + >>> filt.set_coefficients(a_coeffs, b_coeffs) """ if len(a_coeffs) < self.order: a_coeffs = [1.0, *a_coeffs] @@ -68,7 +74,7 @@ def set_coefficients(self, a_coeffs: list[float], b_coeffs: list[float]) -> None def process(self, sample: float) -> float: """ - Calculate y[n] + Calculate :math:`y[n]` >>> filt = IIRFilter(2) >>> filt.process(0) From 47cd21a110d8e2fc038414bc7f3c7ca8e91d6653 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 23 Dec 2024 14:56:42 +0300 Subject: [PATCH 017/126] Fix sphinx/build_docs warnings for cellular_automata (#12454) * updating DIRECTORY.md * Fix sphinx/build_docs warnings for cellular_automata * Fix * Improve --------- Co-authored-by: MaximSmolskiy --- cellular_automata/wa_tor.py | 54 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/cellular_automata/wa_tor.py b/cellular_automata/wa_tor.py index e423d1595bdb..29f7ea510bfe 100644 --- a/cellular_automata/wa_tor.py +++ b/cellular_automata/wa_tor.py @@ -1,9 +1,9 @@ """ Wa-Tor algorithm (1984) -@ https://en.wikipedia.org/wiki/Wa-Tor -@ https://beltoforion.de/en/wator/ -@ https://beltoforion.de/en/wator/images/wator_medium.webm +| @ https://en.wikipedia.org/wiki/Wa-Tor +| @ https://beltoforion.de/en/wator/ +| @ https://beltoforion.de/en/wator/images/wator_medium.webm This solution aims to completely remove any systematic approach to the Wa-Tor planet, and utilise fully random methods. @@ -97,8 +97,8 @@ class WaTor: :attr time_passed: A function that is called every time time passes (a chronon) in order to visually display - the new Wa-Tor planet. The time_passed function can block - using time.sleep to slow the algorithm progression. + the new Wa-Tor planet. The `time_passed` function can block + using ``time.sleep`` to slow the algorithm progression. >>> wt = WaTor(10, 15) >>> wt.width @@ -216,7 +216,7 @@ def get_surrounding_prey(self, entity: Entity) -> list[Entity]: """ Returns all the prey entities around (N, S, E, W) a predator entity. - Subtly different to the try_to_move_to_unoccupied square. + Subtly different to the `move_and_reproduce`. >>> wt = WaTor(WIDTH, HEIGHT) >>> wt.set_planet([ @@ -260,7 +260,7 @@ def move_and_reproduce( """ Attempts to move to an unoccupied neighbouring square in either of the four directions (North, South, East, West). - If the move was successful and the remaining_reproduction time is + If the move was successful and the `remaining_reproduction_time` is equal to 0, then a new prey or predator can also be created in the previous square. @@ -351,12 +351,12 @@ def perform_prey_actions( Performs the actions for a prey entity For prey the rules are: - 1. At each chronon, a prey moves randomly to one of the adjacent unoccupied - squares. If there are no free squares, no movement takes place. - 2. Once a prey has survived a certain number of chronons it may reproduce. - This is done as it moves to a neighbouring square, - leaving behind a new prey in its old position. - Its reproduction time is also reset to zero. + 1. At each chronon, a prey moves randomly to one of the adjacent unoccupied + squares. If there are no free squares, no movement takes place. + 2. Once a prey has survived a certain number of chronons it may reproduce. + This is done as it moves to a neighbouring square, + leaving behind a new prey in its old position. + Its reproduction time is also reset to zero. >>> wt = WaTor(WIDTH, HEIGHT) >>> reproducable_entity = Entity(True, coords=(0, 1)) @@ -382,15 +382,15 @@ def perform_predator_actions( :param occupied_by_prey_coords: Move to this location if there is prey there For predators the rules are: - 1. At each chronon, a predator moves randomly to an adjacent square occupied - by a prey. If there is none, the predator moves to a random adjacent - unoccupied square. If there are no free squares, no movement takes place. - 2. At each chronon, each predator is deprived of a unit of energy. - 3. Upon reaching zero energy, a predator dies. - 4. If a predator moves to a square occupied by a prey, - it eats the prey and earns a certain amount of energy. - 5. Once a predator has survived a certain number of chronons - it may reproduce in exactly the same way as the prey. + 1. At each chronon, a predator moves randomly to an adjacent square occupied + by a prey. If there is none, the predator moves to a random adjacent + unoccupied square. If there are no free squares, no movement takes place. + 2. At each chronon, each predator is deprived of a unit of energy. + 3. Upon reaching zero energy, a predator dies. + 4. If a predator moves to a square occupied by a prey, + it eats the prey and earns a certain amount of energy. + 5. Once a predator has survived a certain number of chronons + it may reproduce in exactly the same way as the prey. >>> wt = WaTor(WIDTH, HEIGHT) >>> wt.set_planet([[Entity(True, coords=(0, 0)), Entity(False, coords=(0, 1))]]) @@ -430,7 +430,7 @@ def perform_predator_actions( def run(self, *, iteration_count: int) -> None: """ - Emulate time passing by looping iteration_count times + Emulate time passing by looping `iteration_count` times >>> wt = WaTor(WIDTH, HEIGHT) >>> wt.run(iteration_count=PREDATOR_INITIAL_ENERGY_VALUE - 1) @@ -484,11 +484,9 @@ def visualise(wt: WaTor, iter_number: int, *, colour: bool = True) -> None: an ascii code in terminal to clear and re-print the Wa-Tor planet at intervals. - Uses ascii colour codes to colourfully display - the predators and prey. - - (0x60f197) Prey = # - (0xfffff) Predator = x + Uses ascii colour codes to colourfully display the predators and prey: + * (0x60f197) Prey = ``#`` + * (0xfffff) Predator = ``x`` >>> wt = WaTor(30, 30) >>> wt.set_planet([ From c5e603ae4234e5d516d700b01d47f78d42c18008 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 23 Dec 2024 15:43:16 +0300 Subject: [PATCH 018/126] Fix sphinx/build_docs warnings for geodesy (#12462) * updating DIRECTORY.md * Fix sphinx/build_docs warnings for geodesy/haversine_distance.py * Improve --------- Co-authored-by: MaximSmolskiy --- geodesy/haversine_distance.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/geodesy/haversine_distance.py b/geodesy/haversine_distance.py index 93e625770f9d..39cd250af965 100644 --- a/geodesy/haversine_distance.py +++ b/geodesy/haversine_distance.py @@ -21,10 +21,11 @@ def haversine_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> fl computation like Haversine can be handy for shorter range distances. Args: - lat1, lon1: latitude and longitude of coordinate 1 - lat2, lon2: latitude and longitude of coordinate 2 + * `lat1`, `lon1`: latitude and longitude of coordinate 1 + * `lat2`, `lon2`: latitude and longitude of coordinate 2 Returns: geographical distance between two points in metres + >>> from collections import namedtuple >>> point_2d = namedtuple("point_2d", "lat lon") >>> SAN_FRANCISCO = point_2d(37.774856, -122.424227) From b0cb13fea54854b3a60eced27026db9a9c5dc5ab Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 23 Dec 2024 16:11:58 +0300 Subject: [PATCH 019/126] Fix sphinx/build_docs warnings for greedy_methods (#12463) * updating DIRECTORY.md * Fix sphinx/build_docs warnings for greedy_methods * Improve --------- Co-authored-by: MaximSmolskiy --- greedy_methods/smallest_range.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/greedy_methods/smallest_range.py b/greedy_methods/smallest_range.py index e2b7f8d7e96a..9adb12bf9029 100644 --- a/greedy_methods/smallest_range.py +++ b/greedy_methods/smallest_range.py @@ -14,12 +14,13 @@ def smallest_range(nums: list[list[int]]) -> list[int]: Uses min heap for efficiency. The range includes at least one number from each list. Args: - nums: List of k sorted integer lists. + `nums`: List of k sorted integer lists. Returns: list: Smallest range as a two-element list. Examples: + >>> smallest_range([[4, 10, 15, 24, 26], [0, 9, 12, 20], [5, 18, 22, 30]]) [20, 24] >>> smallest_range([[1, 2, 3], [1, 2, 3], [1, 2, 3]]) From 04fbfd6eae38b9897c1b8ff6aee487dd2523665b Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 24 Dec 2024 03:14:11 +0300 Subject: [PATCH 020/126] Fix sphinx/build_docs warnings for maths/volume (#12464) * Fix sphinx/build_docs warnings for maths/volume * Fix * Fix * Fix * Fix * Fix * Fix * Fix --- maths/volume.py | 149 ++++++++++++++++++++++++++++++------------------ 1 file changed, 95 insertions(+), 54 deletions(-) diff --git a/maths/volume.py b/maths/volume.py index 23fcf6be6ef1..08bdf72b013b 100644 --- a/maths/volume.py +++ b/maths/volume.py @@ -1,5 +1,6 @@ """ Find the volume of various shapes. + * https://en.wikipedia.org/wiki/Volume * https://en.wikipedia.org/wiki/Spherical_cap """ @@ -12,6 +13,7 @@ def vol_cube(side_length: float) -> float: """ Calculate the Volume of a Cube. + >>> vol_cube(1) 1.0 >>> vol_cube(3) @@ -33,6 +35,7 @@ def vol_cube(side_length: float) -> float: def vol_spherical_cap(height: float, radius: float) -> float: """ Calculate the volume of the spherical cap. + >>> vol_spherical_cap(1, 2) 5.235987755982988 >>> vol_spherical_cap(1.6, 2.6) @@ -57,20 +60,29 @@ def vol_spherical_cap(height: float, radius: float) -> float: def vol_spheres_intersect( radius_1: float, radius_2: float, centers_distance: float ) -> float: - """ + r""" Calculate the volume of the intersection of two spheres. + The intersection is composed by two spherical caps and therefore its volume is the - sum of the volumes of the spherical caps. First, it calculates the heights (h1, h2) - of the spherical caps, then the two volumes and it returns the sum. + sum of the volumes of the spherical caps. + First, it calculates the heights :math:`(h_1, h_2)` of the spherical caps, + then the two volumes and it returns the sum. The height formulas are - h1 = (radius_1 - radius_2 + centers_distance) - * (radius_1 + radius_2 - centers_distance) - / (2 * centers_distance) - h2 = (radius_2 - radius_1 + centers_distance) - * (radius_2 + radius_1 - centers_distance) - / (2 * centers_distance) - if centers_distance is 0 then it returns the volume of the smallers sphere - :return vol_spherical_cap(h1, radius_2) + vol_spherical_cap(h2, radius_1) + + .. math:: + h_1 = \frac{(radius_1 - radius_2 + centers\_distance) + \cdot (radius_1 + radius_2 - centers\_distance)} + {2 \cdot centers\_distance} + + h_2 = \frac{(radius_2 - radius_1 + centers\_distance) + \cdot (radius_2 + radius_1 - centers\_distance)} + {2 \cdot centers\_distance} + + if `centers_distance` is 0 then it returns the volume of the smallers sphere + + :return: ``vol_spherical_cap`` (:math:`h_1`, :math:`radius_2`) + + ``vol_spherical_cap`` (:math:`h_2`, :math:`radius_1`) + >>> vol_spheres_intersect(2, 2, 1) 21.205750411731103 >>> vol_spheres_intersect(2.6, 2.6, 1.6) @@ -112,14 +124,18 @@ def vol_spheres_intersect( def vol_spheres_union( radius_1: float, radius_2: float, centers_distance: float ) -> float: - """ + r""" Calculate the volume of the union of two spheres that possibly intersect. - It is the sum of sphere A and sphere B minus their intersection. - First, it calculates the volumes (v1, v2) of the spheres, - then the volume of the intersection (i) and it returns the sum v1+v2-i. - If centers_distance is 0 then it returns the volume of the larger sphere - :return vol_sphere(radius_1) + vol_sphere(radius_2) - - vol_spheres_intersect(radius_1, radius_2, centers_distance) + + It is the sum of sphere :math:`A` and sphere :math:`B` minus their intersection. + First, it calculates the volumes :math:`(v_1, v_2)` of the spheres, + then the volume of the intersection :math:`i` and + it returns the sum :math:`v_1 + v_2 - i`. + If `centers_distance` is 0 then it returns the volume of the larger sphere + + :return: ``vol_sphere`` (:math:`radius_1`) + ``vol_sphere`` (:math:`radius_2`) + - ``vol_spheres_intersect`` + (:math:`radius_1`, :math:`radius_2`, :math:`centers\_distance`) >>> vol_spheres_union(2, 2, 1) 45.814892864851146 @@ -157,7 +173,9 @@ def vol_spheres_union( def vol_cuboid(width: float, height: float, length: float) -> float: """ Calculate the Volume of a Cuboid. - :return multiple of width, length and height + + :return: multiple of `width`, `length` and `height` + >>> vol_cuboid(1, 1, 1) 1.0 >>> vol_cuboid(1, 2, 3) @@ -185,10 +203,12 @@ def vol_cuboid(width: float, height: float, length: float) -> float: def vol_cone(area_of_base: float, height: float) -> float: - """ - Calculate the Volume of a Cone. - Wikipedia reference: https://en.wikipedia.org/wiki/Cone - :return (1/3) * area_of_base * height + r""" + | Calculate the Volume of a Cone. + | Wikipedia reference: https://en.wikipedia.org/wiki/Cone + + :return: :math:`\frac{1}{3} \cdot area\_of\_base \cdot height` + >>> vol_cone(10, 3) 10.0 >>> vol_cone(1, 1) @@ -212,10 +232,12 @@ def vol_cone(area_of_base: float, height: float) -> float: def vol_right_circ_cone(radius: float, height: float) -> float: - """ - Calculate the Volume of a Right Circular Cone. - Wikipedia reference: https://en.wikipedia.org/wiki/Cone - :return (1/3) * pi * radius^2 * height + r""" + | Calculate the Volume of a Right Circular Cone. + | Wikipedia reference: https://en.wikipedia.org/wiki/Cone + + :return: :math:`\frac{1}{3} \cdot \pi \cdot radius^2 \cdot height` + >>> vol_right_circ_cone(2, 3) 12.566370614359172 >>> vol_right_circ_cone(0, 0) @@ -237,10 +259,12 @@ def vol_right_circ_cone(radius: float, height: float) -> float: def vol_prism(area_of_base: float, height: float) -> float: - """ - Calculate the Volume of a Prism. - Wikipedia reference: https://en.wikipedia.org/wiki/Prism_(geometry) - :return V = Bh + r""" + | Calculate the Volume of a Prism. + | Wikipedia reference: https://en.wikipedia.org/wiki/Prism_(geometry) + + :return: :math:`V = B \cdot h` + >>> vol_prism(10, 2) 20.0 >>> vol_prism(11, 1) @@ -264,10 +288,12 @@ def vol_prism(area_of_base: float, height: float) -> float: def vol_pyramid(area_of_base: float, height: float) -> float: - """ - Calculate the Volume of a Pyramid. - Wikipedia reference: https://en.wikipedia.org/wiki/Pyramid_(geometry) - :return (1/3) * Bh + r""" + | Calculate the Volume of a Pyramid. + | Wikipedia reference: https://en.wikipedia.org/wiki/Pyramid_(geometry) + + :return: :math:`\frac{1}{3} \cdot B \cdot h` + >>> vol_pyramid(10, 3) 10.0 >>> vol_pyramid(1.5, 3) @@ -291,10 +317,12 @@ def vol_pyramid(area_of_base: float, height: float) -> float: def vol_sphere(radius: float) -> float: - """ - Calculate the Volume of a Sphere. - Wikipedia reference: https://en.wikipedia.org/wiki/Sphere - :return (4/3) * pi * r^3 + r""" + | Calculate the Volume of a Sphere. + | Wikipedia reference: https://en.wikipedia.org/wiki/Sphere + + :return: :math:`\frac{4}{3} \cdot \pi \cdot r^3` + >>> vol_sphere(5) 523.5987755982989 >>> vol_sphere(1) @@ -315,10 +343,13 @@ def vol_sphere(radius: float) -> float: def vol_hemisphere(radius: float) -> float: - """Calculate the volume of a hemisphere - Wikipedia reference: https://en.wikipedia.org/wiki/Hemisphere - Other references: https://www.cuemath.com/geometry/hemisphere - :return 2/3 * pi * radius^3 + r""" + | Calculate the volume of a hemisphere + | Wikipedia reference: https://en.wikipedia.org/wiki/Hemisphere + | Other references: https://www.cuemath.com/geometry/hemisphere + + :return: :math:`\frac{2}{3} \cdot \pi \cdot radius^3` + >>> vol_hemisphere(1) 2.0943951023931953 >>> vol_hemisphere(7) @@ -339,9 +370,12 @@ def vol_hemisphere(radius: float) -> float: def vol_circular_cylinder(radius: float, height: float) -> float: - """Calculate the Volume of a Circular Cylinder. - Wikipedia reference: https://en.wikipedia.org/wiki/Cylinder - :return pi * radius^2 * height + r""" + | Calculate the Volume of a Circular Cylinder. + | Wikipedia reference: https://en.wikipedia.org/wiki/Cylinder + + :return: :math:`\pi \cdot radius^2 \cdot height` + >>> vol_circular_cylinder(1, 1) 3.141592653589793 >>> vol_circular_cylinder(4, 3) @@ -368,7 +402,9 @@ def vol_circular_cylinder(radius: float, height: float) -> float: def vol_hollow_circular_cylinder( inner_radius: float, outer_radius: float, height: float ) -> float: - """Calculate the Volume of a Hollow Circular Cylinder. + """ + Calculate the Volume of a Hollow Circular Cylinder. + >>> vol_hollow_circular_cylinder(1, 2, 3) 28.274333882308138 >>> vol_hollow_circular_cylinder(1.6, 2.6, 3.6) @@ -405,8 +441,9 @@ def vol_hollow_circular_cylinder( def vol_conical_frustum(height: float, radius_1: float, radius_2: float) -> float: - """Calculate the Volume of a Conical Frustum. - Wikipedia reference: https://en.wikipedia.org/wiki/Frustum + """ + | Calculate the Volume of a Conical Frustum. + | Wikipedia reference: https://en.wikipedia.org/wiki/Frustum >>> vol_conical_frustum(45, 7, 28) 48490.482608158454 @@ -443,9 +480,12 @@ def vol_conical_frustum(height: float, radius_1: float, radius_2: float) -> floa def vol_torus(torus_radius: float, tube_radius: float) -> float: - """Calculate the Volume of a Torus. - Wikipedia reference: https://en.wikipedia.org/wiki/Torus - :return 2pi^2 * torus_radius * tube_radius^2 + r""" + | Calculate the Volume of a Torus. + | Wikipedia reference: https://en.wikipedia.org/wiki/Torus + + :return: :math:`2 \pi^2 \cdot torus\_radius \cdot tube\_radius^2` + >>> vol_torus(1, 1) 19.739208802178716 >>> vol_torus(4, 3) @@ -471,8 +511,9 @@ def vol_torus(torus_radius: float, tube_radius: float) -> float: def vol_icosahedron(tri_side: float) -> float: - """Calculate the Volume of an Icosahedron. - Wikipedia reference: https://en.wikipedia.org/wiki/Regular_icosahedron + """ + | Calculate the Volume of an Icosahedron. + | Wikipedia reference: https://en.wikipedia.org/wiki/Regular_icosahedron >>> from math import isclose >>> isclose(vol_icosahedron(2.5), 34.088984228514256) From e9721aad59743d01e82582017884db528bad3e21 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 24 Dec 2024 06:06:59 +0300 Subject: [PATCH 021/126] Fix sphinx/build_docs warnings for physics/horizontal_projectile_motion (#12467) --- physics/horizontal_projectile_motion.py | 68 +++++++++++++++---------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/physics/horizontal_projectile_motion.py b/physics/horizontal_projectile_motion.py index 80f85a1b7146..60f21c2b39c4 100644 --- a/physics/horizontal_projectile_motion.py +++ b/physics/horizontal_projectile_motion.py @@ -1,15 +1,18 @@ """ Horizontal Projectile Motion problem in physics. + This algorithm solves a specific problem in which -the motion starts from the ground as can be seen below: - (v = 0) - * * - * * - * * - * * - * * - * * -GROUND GROUND +the motion starts from the ground as can be seen below:: + + (v = 0) + * * + * * + * * + * * + * * + * * + GROUND GROUND + For more info: https://en.wikipedia.org/wiki/Projectile_motion """ @@ -43,14 +46,17 @@ def check_args(init_velocity: float, angle: float) -> None: def horizontal_distance(init_velocity: float, angle: float) -> float: - """ + r""" Returns the horizontal distance that the object cover + Formula: - v_0^2 * sin(2 * alpha) - --------------------- - g - v_0 - initial velocity - alpha - angle + .. math:: + \frac{v_0^2 \cdot \sin(2 \alpha)}{g} + + v_0 - \text{initial velocity} + + \alpha - \text{angle} + >>> horizontal_distance(30, 45) 91.77 >>> horizontal_distance(100, 78) @@ -70,14 +76,17 @@ def horizontal_distance(init_velocity: float, angle: float) -> float: def max_height(init_velocity: float, angle: float) -> float: - """ + r""" Returns the maximum height that the object reach + Formula: - v_0^2 * sin^2(alpha) - -------------------- - 2g - v_0 - initial velocity - alpha - angle + .. math:: + \frac{v_0^2 \cdot \sin^2 (\alpha)}{2 g} + + v_0 - \text{initial velocity} + + \alpha - \text{angle} + >>> max_height(30, 45) 22.94 >>> max_height(100, 78) @@ -97,14 +106,17 @@ def max_height(init_velocity: float, angle: float) -> float: def total_time(init_velocity: float, angle: float) -> float: - """ + r""" Returns total time of the motion + Formula: - 2 * v_0 * sin(alpha) - -------------------- - g - v_0 - initial velocity - alpha - angle + .. math:: + \frac{2 v_0 \cdot \sin (\alpha)}{g} + + v_0 - \text{initial velocity} + + \alpha - \text{angle} + >>> total_time(30, 45) 4.33 >>> total_time(100, 78) @@ -125,6 +137,8 @@ def total_time(init_velocity: float, angle: float) -> float: def test_motion() -> None: """ + Test motion + >>> test_motion() """ v0, angle = 25, 20 From c36aaf0fbcbc0f1a6c82b689ee87e383104b9e96 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 24 Dec 2024 11:48:37 +0300 Subject: [PATCH 022/126] Fix sphinx/build_docs warnings for graphs/check_bipatrite (#12469) * Fix sphinx/build_docs warnings for graphs/check_bipatrite * Fix --- graphs/check_bipatrite.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/graphs/check_bipatrite.py b/graphs/check_bipatrite.py index 10b9cc965251..213f3f9480b5 100644 --- a/graphs/check_bipatrite.py +++ b/graphs/check_bipatrite.py @@ -6,16 +6,17 @@ def is_bipartite_dfs(graph: defaultdict[int, list[int]]) -> bool: Check if a graph is bipartite using depth-first search (DFS). Args: - graph: Adjacency list representing the graph. + `graph`: Adjacency list representing the graph. Returns: - True if bipartite, False otherwise. + ``True`` if bipartite, ``False`` otherwise. Checks if the graph can be divided into two sets of vertices, such that no two vertices within the same set are connected by an edge. Examples: - # FIXME: This test should pass. + + >>> # FIXME: This test should pass. >>> is_bipartite_dfs(defaultdict(list, {0: [1, 2], 1: [0, 3], 2: [0, 4]})) Traceback (most recent call last): ... @@ -37,7 +38,7 @@ def is_bipartite_dfs(graph: defaultdict[int, list[int]]) -> bool: ... KeyError: 0 - # FIXME: This test should fails with KeyError: 4. + >>> # FIXME: This test should fails with KeyError: 4. >>> is_bipartite_dfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 9: [0]}) False >>> is_bipartite_dfs({0: [-1, 3], 1: [0, -2]}) @@ -51,7 +52,8 @@ def is_bipartite_dfs(graph: defaultdict[int, list[int]]) -> bool: ... KeyError: 0 - # FIXME: This test should fails with TypeError: list indices must be integers or... + >>> # FIXME: This test should fails with + >>> # TypeError: list indices must be integers or... >>> is_bipartite_dfs({0: [1.0, 3.0], 1.0: [0, 2.0], 2.0: [1.0, 3.0], 3.0: [0, 2.0]}) True >>> is_bipartite_dfs({"a": [1, 3], "b": [0, 2], "c": [1, 3], "d": [0, 2]}) @@ -95,16 +97,17 @@ def is_bipartite_bfs(graph: defaultdict[int, list[int]]) -> bool: Check if a graph is bipartite using a breadth-first search (BFS). Args: - graph: Adjacency list representing the graph. + `graph`: Adjacency list representing the graph. Returns: - True if bipartite, False otherwise. + ``True`` if bipartite, ``False`` otherwise. Check if the graph can be divided into two sets of vertices, such that no two vertices within the same set are connected by an edge. Examples: - # FIXME: This test should pass. + + >>> # FIXME: This test should pass. >>> is_bipartite_bfs(defaultdict(list, {0: [1, 2], 1: [0, 3], 2: [0, 4]})) Traceback (most recent call last): ... @@ -126,7 +129,7 @@ def is_bipartite_bfs(graph: defaultdict[int, list[int]]) -> bool: ... KeyError: 0 - # FIXME: This test should fails with KeyError: 4. + >>> # FIXME: This test should fails with KeyError: 4. >>> is_bipartite_bfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 9: [0]}) False >>> is_bipartite_bfs({0: [-1, 3], 1: [0, -2]}) @@ -140,7 +143,8 @@ def is_bipartite_bfs(graph: defaultdict[int, list[int]]) -> bool: ... KeyError: 0 - # FIXME: This test should fails with TypeError: list indices must be integers or... + >>> # FIXME: This test should fails with + >>> # TypeError: list indices must be integers or... >>> is_bipartite_bfs({0: [1.0, 3.0], 1.0: [0, 2.0], 2.0: [1.0, 3.0], 3.0: [0, 2.0]}) True >>> is_bipartite_bfs({"a": [1, 3], "b": [0, 2], "c": [1, 3], "d": [0, 2]}) From ae28fa7fe362c8cb0238dbb6b237d42179e8beb3 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 24 Dec 2024 16:17:22 +0300 Subject: [PATCH 023/126] Fix sphinx/build_docs warnings for data_structures/binary_tree/mirror_binary_tree (#12470) --- .../binary_tree/mirror_binary_tree.py | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/data_structures/binary_tree/mirror_binary_tree.py b/data_structures/binary_tree/mirror_binary_tree.py index 62e2f08dd4e0..f6611d66d676 100644 --- a/data_structures/binary_tree/mirror_binary_tree.py +++ b/data_structures/binary_tree/mirror_binary_tree.py @@ -56,6 +56,8 @@ def mirror(self) -> Node: def make_tree_seven() -> Node: r""" Return a binary tree with 7 nodes that looks like this: + :: + 1 / \ 2 3 @@ -81,13 +83,15 @@ def make_tree_seven() -> Node: def make_tree_nine() -> Node: r""" Return a binary tree with 9 nodes that looks like this: - 1 - / \ - 2 3 - / \ \ - 4 5 6 - / \ \ - 7 8 9 + :: + + 1 + / \ + 2 3 + / \ \ + 4 5 6 + / \ \ + 7 8 9 >>> tree_nine = make_tree_nine() >>> len(tree_nine) @@ -117,23 +121,25 @@ def main() -> None: >>> tuple(tree.mirror()) (6, 3, 1, 9, 5, 2, 8, 4, 7) - nine_tree: - 1 - / \ - 2 3 - / \ \ - 4 5 6 - / \ \ - 7 8 9 - - The mirrored tree looks like this: + nine_tree:: + + 1 + / \ + 2 3 + / \ \ + 4 5 6 + / \ \ + 7 8 9 + + The mirrored tree looks like this:: + 1 - / \ - 3 2 - / / \ - 6 5 4 - / / \ - 9 8 7 + / \ + 3 2 + / / \ + 6 5 4 + / / \ + 9 8 7 """ trees = {"zero": Node(0), "seven": make_tree_seven(), "nine": make_tree_nine()} for name, tree in trees.items(): From eb652cf3d48fbd3b51450e95640ce5aec63a066b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 28 Dec 2024 00:18:08 +0300 Subject: [PATCH 024/126] Bump astral-sh/setup-uv from 4 to 5 (#12445) Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 4 to 5. - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/v4...v5) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- .github/workflows/project_euler.yml | 4 ++-- .github/workflows/ruff.yml | 2 +- .github/workflows/sphinx.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a6f308715cc2..62829b2b45a5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v4 + - uses: astral-sh/setup-uv@v5 with: enable-cache: true cache-dependency-glob: uv.lock diff --git a/.github/workflows/project_euler.yml b/.github/workflows/project_euler.yml index 84c55335451e..8d51ad8850cf 100644 --- a/.github/workflows/project_euler.yml +++ b/.github/workflows/project_euler.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v4 + - uses: astral-sh/setup-uv@v5 - uses: actions/setup-python@v5 with: python-version: 3.x @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v4 + - uses: astral-sh/setup-uv@v5 - uses: actions/setup-python@v5 with: python-version: 3.x diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 2c6f92fcf7bf..cfe127b3521f 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -12,5 +12,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v4 + - uses: astral-sh/setup-uv@v5 - run: uvx ruff check --output-format=github . diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index e3e2ce81a95d..d02435d98028 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v4 + - uses: astral-sh/setup-uv@v5 - uses: actions/setup-python@v5 with: python-version: 3.13 From 5bef6ac9296c20250db7d494bbbc9c8bf4bfccdc Mon Sep 17 00:00:00 2001 From: Scarfinos <158184182+Scarfinos@users.noreply.github.com> Date: Fri, 27 Dec 2024 23:22:36 +0100 Subject: [PATCH 025/126] Improve coverage special_numbers (#12414) * Improve coverage bell_numbers * improve more function * Update hamming_numbers.py --------- Co-authored-by: Maxim Smolskiy --- maths/special_numbers/bell_numbers.py | 4 ++++ maths/special_numbers/hamming_numbers.py | 6 +++++- maths/special_numbers/harshad_numbers.py | 8 ++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/maths/special_numbers/bell_numbers.py b/maths/special_numbers/bell_numbers.py index 5d99334d7add..d573e7a3962d 100644 --- a/maths/special_numbers/bell_numbers.py +++ b/maths/special_numbers/bell_numbers.py @@ -21,6 +21,10 @@ def bell_numbers(max_set_length: int) -> list[int]: list: A list of Bell numbers for sets of lengths from 0 to max_set_length. Examples: + >>> bell_numbers(-2) + Traceback (most recent call last): + ... + ValueError: max_set_length must be non-negative >>> bell_numbers(0) [1] >>> bell_numbers(1) diff --git a/maths/special_numbers/hamming_numbers.py b/maths/special_numbers/hamming_numbers.py index 4575119c8a95..a473cc93883b 100644 --- a/maths/special_numbers/hamming_numbers.py +++ b/maths/special_numbers/hamming_numbers.py @@ -13,6 +13,10 @@ def hamming(n_element: int) -> list: :param n_element: The number of elements on the list :return: The nth element of the list + >>> hamming(-5) + Traceback (most recent call last): + ... + ValueError: n_element should be a positive number >>> hamming(5) [1, 2, 3, 4, 5] >>> hamming(10) @@ -22,7 +26,7 @@ def hamming(n_element: int) -> list: """ n_element = int(n_element) if n_element < 1: - my_error = ValueError("a should be a positive number") + my_error = ValueError("n_element should be a positive number") raise my_error hamming_list = [1] diff --git a/maths/special_numbers/harshad_numbers.py b/maths/special_numbers/harshad_numbers.py index 61667adfa127..417120bd840e 100644 --- a/maths/special_numbers/harshad_numbers.py +++ b/maths/special_numbers/harshad_numbers.py @@ -11,6 +11,8 @@ def int_to_base(number: int, base: int) -> str: Where 'base' ranges from 2 to 36. Examples: + >>> int_to_base(0, 21) + '0' >>> int_to_base(23, 2) '10111' >>> int_to_base(58, 5) @@ -26,6 +28,10 @@ def int_to_base(number: int, base: int) -> str: Traceback (most recent call last): ... ValueError: 'base' must be between 2 and 36 inclusive + >>> int_to_base(-99, 16) + Traceback (most recent call last): + ... + ValueError: number must be a positive integer """ if base < 2 or base > 36: @@ -101,6 +107,8 @@ def harshad_numbers_in_base(limit: int, base: int) -> list[str]: Traceback (most recent call last): ... ValueError: 'base' must be between 2 and 36 inclusive + >>> harshad_numbers_in_base(-12, 6) + [] """ if base < 2 or base > 36: From 8bbe8caa256882ef2ebdbb3274e6f99f804716bd Mon Sep 17 00:00:00 2001 From: Scarfinos <158184182+Scarfinos@users.noreply.github.com> Date: Fri, 27 Dec 2024 23:40:35 +0100 Subject: [PATCH 026/126] Improve test coverage for matrix exponentiation (#12388) * #9943 : Adding coverage test for basic_graphs.py * #9943 : Adding coverage test for basic_graphs.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Solve problem of line too long * Improving coverage for matrix_exponentiation.py * fix more than one file * Update matrix_exponentiation.py * Update matrix_exponentiation.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- maths/matrix_exponentiation.py | 36 ++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/maths/matrix_exponentiation.py b/maths/matrix_exponentiation.py index 7c37151c87ca..7cdac9d34674 100644 --- a/maths/matrix_exponentiation.py +++ b/maths/matrix_exponentiation.py @@ -39,6 +39,21 @@ def modular_exponentiation(a, b): def fibonacci_with_matrix_exponentiation(n, f1, f2): + """ + Returns the nth number of the Fibonacci sequence that + starts with f1 and f2 + Uses the matrix exponentiation + >>> fibonacci_with_matrix_exponentiation(1, 5, 6) + 5 + >>> fibonacci_with_matrix_exponentiation(2, 10, 11) + 11 + >>> fibonacci_with_matrix_exponentiation(13, 0, 1) + 144 + >>> fibonacci_with_matrix_exponentiation(10, 5, 9) + 411 + >>> fibonacci_with_matrix_exponentiation(9, 2, 3) + 89 + """ # Trivial Cases if n == 1: return f1 @@ -50,21 +65,34 @@ def fibonacci_with_matrix_exponentiation(n, f1, f2): def simple_fibonacci(n, f1, f2): + """ + Returns the nth number of the Fibonacci sequence that + starts with f1 and f2 + Uses the definition + >>> simple_fibonacci(1, 5, 6) + 5 + >>> simple_fibonacci(2, 10, 11) + 11 + >>> simple_fibonacci(13, 0, 1) + 144 + >>> simple_fibonacci(10, 5, 9) + 411 + >>> simple_fibonacci(9, 2, 3) + 89 + """ # Trivial Cases if n == 1: return f1 elif n == 2: return f2 - fn_1 = f1 - fn_2 = f2 n -= 2 while n > 0: - fn_1, fn_2 = fn_1 + fn_2, fn_1 + f2, f1 = f1 + f2, f2 n -= 1 - return fn_1 + return f2 def matrix_exponentiation_time(): From 76471819bd5b9df6fe5fde4c763396412ce45edc Mon Sep 17 00:00:00 2001 From: Scarfinos <158184182+Scarfinos@users.noreply.github.com> Date: Fri, 27 Dec 2024 23:52:40 +0100 Subject: [PATCH 027/126] Improve test coverage for armstrong numbers (#12327) --- maths/special_numbers/armstrong_numbers.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/maths/special_numbers/armstrong_numbers.py b/maths/special_numbers/armstrong_numbers.py index b2b4010a8f5b..a3cb69b814de 100644 --- a/maths/special_numbers/armstrong_numbers.py +++ b/maths/special_numbers/armstrong_numbers.py @@ -43,9 +43,9 @@ def armstrong_number(n: int) -> bool: def pluperfect_number(n: int) -> bool: """Return True if n is a pluperfect number or False if it is not - >>> all(armstrong_number(n) for n in PASSING) + >>> all(pluperfect_number(n) for n in PASSING) True - >>> any(armstrong_number(n) for n in FAILING) + >>> any(pluperfect_number(n) for n in FAILING) False """ if not isinstance(n, int) or n < 1: @@ -70,9 +70,9 @@ def pluperfect_number(n: int) -> bool: def narcissistic_number(n: int) -> bool: """Return True if n is a narcissistic number or False if it is not. - >>> all(armstrong_number(n) for n in PASSING) + >>> all(narcissistic_number(n) for n in PASSING) True - >>> any(armstrong_number(n) for n in FAILING) + >>> any(narcissistic_number(n) for n in FAILING) False """ if not isinstance(n, int) or n < 1: From 2ae9534fc68b1901d8056331aa2a4dbedc9d947e Mon Sep 17 00:00:00 2001 From: Anamaria Miranda Date: Sat, 28 Dec 2024 00:03:13 +0100 Subject: [PATCH 028/126] Added test to linear regression (#12353) --- machine_learning/linear_regression.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/machine_learning/linear_regression.py b/machine_learning/linear_regression.py index 839a5366d1cc..1d11e5a9cc2b 100644 --- a/machine_learning/linear_regression.py +++ b/machine_learning/linear_regression.py @@ -41,6 +41,14 @@ def run_steep_gradient_descent(data_x, data_y, len_data, alpha, theta): :param theta : Feature vector (weight's for our model) ;param return : Updated Feature's, using curr_features - alpha_ * gradient(w.r.t. feature) + >>> import numpy as np + >>> data_x = np.array([[1, 2], [3, 4]]) + >>> data_y = np.array([5, 6]) + >>> len_data = len(data_x) + >>> alpha = 0.01 + >>> theta = np.array([0.1, 0.2]) + >>> run_steep_gradient_descent(data_x, data_y, len_data, alpha, theta) + array([0.196, 0.343]) """ n = len_data @@ -58,6 +66,12 @@ def sum_of_square_error(data_x, data_y, len_data, theta): :param len_data : len of the dataset :param theta : contains the feature vector :return : sum of square error computed from given feature's + + Example: + >>> vc_x = np.array([[1.1], [2.1], [3.1]]) + >>> vc_y = np.array([1.2, 2.2, 3.2]) + >>> round(sum_of_square_error(vc_x, vc_y, 3, np.array([1])),3) + np.float64(0.005) """ prod = np.dot(theta, data_x.transpose()) prod -= data_y.transpose() @@ -93,6 +107,11 @@ def mean_absolute_error(predicted_y, original_y): :param predicted_y : contains the output of prediction (result vector) :param original_y : contains values of expected outcome :return : mean absolute error computed from given feature's + + >>> predicted_y = [3, -0.5, 2, 7] + >>> original_y = [2.5, 0.0, 2, 8] + >>> mean_absolute_error(predicted_y, original_y) + 0.5 """ total = sum(abs(y - predicted_y[i]) for i, y in enumerate(original_y)) return total / len(original_y) @@ -114,4 +133,7 @@ def main(): if __name__ == "__main__": + import doctest + + doctest.testmod() main() From 1652d05e9ee25d54eea5576976d537975dcad9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julia=20Arag=C3=A3o?= <101305675+juliaaragao@users.noreply.github.com> Date: Sat, 28 Dec 2024 00:26:29 +0100 Subject: [PATCH 029/126] adding test to electronics/electric_power.py (#12387) * test electric_power * Update electric_power.py * Update electric_power.py * Update electric_power.py * Update electric_power.py --------- Co-authored-by: Julia Co-authored-by: Maxim Smolskiy --- electronics/electric_power.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/electronics/electric_power.py b/electronics/electric_power.py index 8b92e320ace3..8e3454e39c3f 100644 --- a/electronics/electric_power.py +++ b/electronics/electric_power.py @@ -23,20 +23,22 @@ def electric_power(voltage: float, current: float, power: float) -> tuple: >>> electric_power(voltage=2, current=4, power=2) Traceback (most recent call last): ... - ValueError: Only one argument must be 0 + ValueError: Exactly one argument must be 0 >>> electric_power(voltage=0, current=0, power=2) Traceback (most recent call last): ... - ValueError: Only one argument must be 0 + ValueError: Exactly one argument must be 0 >>> electric_power(voltage=0, current=2, power=-4) Traceback (most recent call last): ... ValueError: Power cannot be negative in any electrical/electronics system >>> electric_power(voltage=2.2, current=2.2, power=0) Result(name='power', value=4.84) + >>> electric_power(current=0, power=6, voltage=2) + Result(name='current', value=3.0) """ if (voltage, current, power).count(0) != 1: - raise ValueError("Only one argument must be 0") + raise ValueError("Exactly one argument must be 0") elif power < 0: raise ValueError( "Power cannot be negative in any electrical/electronics system" @@ -48,7 +50,7 @@ def electric_power(voltage: float, current: float, power: float) -> tuple: elif power == 0: return Result("power", float(round(abs(voltage * current), 2))) else: - raise ValueError("Exactly one argument must be 0") + raise AssertionError if __name__ == "__main__": From 929b7dc057cd56f90b260cd665fb67886bcadeea Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sat, 28 Dec 2024 11:43:25 +0300 Subject: [PATCH 030/126] Fix Gaussian elimination pivoting (#11393) * updating DIRECTORY.md * Fix Gaussian elimination pivoting * Fix review issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: MaximSmolskiy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../src/gaussian_elimination_pivoting.py | 39 ++++++++----------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/linear_algebra/src/gaussian_elimination_pivoting.py b/linear_algebra/src/gaussian_elimination_pivoting.py index ecaacce19a31..efc1ddd64a2e 100644 --- a/linear_algebra/src/gaussian_elimination_pivoting.py +++ b/linear_algebra/src/gaussian_elimination_pivoting.py @@ -22,40 +22,33 @@ def solve_linear_system(matrix: np.ndarray) -> np.ndarray: >>> solution = solve_linear_system(np.column_stack((A, B))) >>> np.allclose(solution, np.array([2., 3., -1.])) True - >>> solve_linear_system(np.array([[0, 0], [0, 0]], dtype=float)) - array([nan, nan]) + >>> solve_linear_system(np.array([[0, 0, 0]], dtype=float)) + Traceback (most recent call last): + ... + ValueError: Matrix is not square + >>> solve_linear_system(np.array([[0, 0, 0], [0, 0, 0]], dtype=float)) + Traceback (most recent call last): + ... + ValueError: Matrix is singular """ ab = np.copy(matrix) num_of_rows = ab.shape[0] num_of_columns = ab.shape[1] - 1 x_lst: list[float] = [] - # Lead element search - for column_num in range(num_of_rows): - for i in range(column_num, num_of_columns): - if abs(ab[i][column_num]) > abs(ab[column_num][column_num]): - ab[[column_num, i]] = ab[[i, column_num]] - if ab[column_num, column_num] == 0.0: - raise ValueError("Matrix is not correct") - else: - pass - if column_num != 0: - for i in range(column_num, num_of_rows): - ab[i, :] -= ( - ab[i, column_num - 1] - / ab[column_num - 1, column_num - 1] - * ab[column_num - 1, :] - ) + if num_of_rows != num_of_columns: + raise ValueError("Matrix is not square") - # Upper triangular matrix for column_num in range(num_of_rows): + # Lead element search for i in range(column_num, num_of_columns): if abs(ab[i][column_num]) > abs(ab[column_num][column_num]): ab[[column_num, i]] = ab[[i, column_num]] - if ab[column_num, column_num] == 0.0: - raise ValueError("Matrix is not correct") - else: - pass + + # Upper triangular matrix + if abs(ab[column_num, column_num]) < 1e-8: + raise ValueError("Matrix is singular") + if column_num != 0: for i in range(column_num, num_of_rows): ab[i, :] -= ( From b5c8fbf2e8254b53056b741aacce3842736ba177 Mon Sep 17 00:00:00 2001 From: Joy Khandelwal <116290658+joy-programs@users.noreply.github.com> Date: Sat, 28 Dec 2024 14:21:28 +0530 Subject: [PATCH 031/126] Add additional doctests, fix grammatical errors for maths/perfect_number.py (#12477) * Add additional doctests for the perfect number algorithm and fix grammatical errors. Contributes to #9943 * Added newline at End of file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- maths/perfect_number.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/maths/perfect_number.py b/maths/perfect_number.py index df6b6e3d91d8..52c816cc7895 100644 --- a/maths/perfect_number.py +++ b/maths/perfect_number.py @@ -46,17 +46,27 @@ def perfect(number: int) -> bool: False >>> perfect(-1) False + >>> perfect(33550336) # Large perfect number + True + >>> perfect(33550337) # Just above a large perfect number + False + >>> perfect(1) # Edge case: 1 is not a perfect number + False + >>> perfect("123") # String representation of a number + Traceback (most recent call last): + ... + ValueError: number must be an integer >>> perfect(12.34) Traceback (most recent call last): ... - ValueError: number must an integer + ValueError: number must be an integer >>> perfect("Hello") Traceback (most recent call last): ... - ValueError: number must an integer + ValueError: number must be an integer """ if not isinstance(number, int): - raise ValueError("number must an integer") + raise ValueError("number must be an integer") if number <= 0: return False return sum(i for i in range(1, number // 2 + 1) if number % i == 0) == number @@ -70,8 +80,7 @@ def perfect(number: int) -> bool: try: number = int(input("Enter a positive integer: ").strip()) except ValueError: - msg = "number must an integer" - print(msg) + msg = "number must be an integer" raise ValueError(msg) print(f"{number} is {'' if perfect(number) else 'not '}a Perfect Number.") From d496d5611fe266b7123b4a1f3efb4bcb7c2f38f2 Mon Sep 17 00:00:00 2001 From: Shi Entong <144505619+setbit123@users.noreply.github.com> Date: Sat, 28 Dec 2024 17:22:07 +0800 Subject: [PATCH 032/126] Remove inaccessible URL in computer_vision/README.md (#12383) Remove inaccessible URL. --- computer_vision/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/computer_vision/README.md b/computer_vision/README.md index 1657128fd25e..61462567b662 100644 --- a/computer_vision/README.md +++ b/computer_vision/README.md @@ -8,4 +8,3 @@ Image processing and computer vision are a little different from each other. Ima While computer vision comes from modelling image processing using the techniques of machine learning, computer vision applies machine learning to recognize patterns for interpretation of images (much like the process of visual reasoning of human vision). * -* From 1909f2272f11ebe7626d2dee78c11a91134e39e7 Mon Sep 17 00:00:00 2001 From: jperezr <122382210+MRJPEREZR@users.noreply.github.com> Date: Sat, 28 Dec 2024 11:03:24 +0100 Subject: [PATCH 033/126] adding doctests to maths/trapezoidal_rule.py (#12193) * adding doctests to trapezoidal_rule.py * adding algorithm delta-star transformation * updating DIRECTORY.md * delete file star_delta_transform.py * updating DIRECTORY.md * modified: ../DIRECTORY.md --------- Co-authored-by: MRJPEREZR --- maths/trapezoidal_rule.py | 48 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/maths/trapezoidal_rule.py b/maths/trapezoidal_rule.py index 9a4ddc8af66b..0186629ee378 100644 --- a/maths/trapezoidal_rule.py +++ b/maths/trapezoidal_rule.py @@ -5,13 +5,25 @@ method 1: "extended trapezoidal rule" +int(f) = dx/2 * (f1 + 2f2 + ... + fn) """ def method_1(boundary, steps): - # "extended trapezoidal rule" - # int(f) = dx/2 * (f1 + 2f2 + ... + fn) + """ + Apply the extended trapezoidal rule to approximate the integral of function f(x) + over the interval defined by 'boundary' with the number of 'steps'. + + Args: + boundary (list of floats): A list containing the start and end values [a, b]. + steps (int): The number of steps or subintervals. + Returns: + float: Approximation of the integral of f(x) over [a, b]. + Examples: + >>> method_1([0, 1], 10) + 0.3349999999999999 + """ h = (boundary[1] - boundary[0]) / steps a = boundary[0] b = boundary[1] @@ -26,13 +38,40 @@ def method_1(boundary, steps): def make_points(a, b, h): + """ + Generates points between 'a' and 'b' with step size 'h', excluding the end points. + Args: + a (float): Start value + b (float): End value + h (float): Step size + Examples: + >>> list(make_points(0, 10, 2.5)) + [2.5, 5.0, 7.5] + + >>> list(make_points(0, 10, 2)) + [2, 4, 6, 8] + + >>> list(make_points(1, 21, 5)) + [6, 11, 16] + + >>> list(make_points(1, 5, 2)) + [3] + + >>> list(make_points(1, 4, 3)) + [] + """ x = a + h - while x < (b - h): + while x <= (b - h): yield x x = x + h def f(x): # enter your function here + """ + Example: + >>> f(2) + 4 + """ y = (x - 0) * (x - 0) return y @@ -47,4 +86,7 @@ def main(): if __name__ == "__main__": + import doctest + + doctest.testmod() main() From 2b58ab040295fbbf2e463ba8cd77ad935d942968 Mon Sep 17 00:00:00 2001 From: Andrwaa <165920381+Andrwaa@users.noreply.github.com> Date: Sat, 28 Dec 2024 12:17:48 +0100 Subject: [PATCH 034/126] compare-method added to Vector class in lib.py (#12448) * compare-method added to Vector class in lib.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated lib.py with suggestions * Updated lib.py with suggestions * Updated lib.py with __eq__ method --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- linear_algebra/src/lib.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/linear_algebra/src/lib.py b/linear_algebra/src/lib.py index 5af6c62e3ad4..0d6a348475cd 100644 --- a/linear_algebra/src/lib.py +++ b/linear_algebra/src/lib.py @@ -46,7 +46,6 @@ class Vector: change_component(pos: int, value: float): changes specified component euclidean_length(): returns the euclidean length of the vector angle(other: Vector, deg: bool): returns the angle between two vectors - TODO: compare-operator """ def __init__(self, components: Collection[float] | None = None) -> None: @@ -96,6 +95,16 @@ def __sub__(self, other: Vector) -> Vector: else: # error case raise Exception("must have the same size") + def __eq__(self, other: object) -> bool: + """ + performs the comparison between two vectors + """ + if not isinstance(other, Vector): + return NotImplemented + if len(self) != len(other): + return False + return all(self.component(i) == other.component(i) for i in range(len(self))) + @overload def __mul__(self, other: float) -> Vector: ... From 2d68bb50e5f12532b5a0d616305c4f805d2b8ff9 Mon Sep 17 00:00:00 2001 From: KICH Yassine Date: Sun, 29 Dec 2024 12:56:36 +0100 Subject: [PATCH 035/126] Fix split function to handle trailing delimiters correctly (#12423) * Fix split function to handle trailing delimiters correctly * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update split.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- strings/split.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/strings/split.py b/strings/split.py index b62b86d2401f..ed194ec69c2f 100644 --- a/strings/split.py +++ b/strings/split.py @@ -14,6 +14,9 @@ def split(string: str, separator: str = " ") -> list: >>> split("12:43:39",separator = ":") ['12', '43', '39'] + + >>> split(";abbb;;c;", separator=';') + ['', 'abbb', '', 'c', ''] """ split_words = [] @@ -23,7 +26,7 @@ def split(string: str, separator: str = " ") -> list: if char == separator: split_words.append(string[last_index:index]) last_index = index + 1 - elif index + 1 == len(string): + if index + 1 == len(string): split_words.append(string[last_index : index + 1]) return split_words From 972a5c1e432e0a3fa9e990422318269219192a53 Mon Sep 17 00:00:00 2001 From: RajdeepBakolia2004 <144157867+RajdeepBakolia2004@users.noreply.github.com> Date: Sun, 29 Dec 2024 19:05:33 +0530 Subject: [PATCH 036/126] fixed the issue in strings/join.py (#12434) * fixed the issue in strings/join.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update join.py * Update join.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- strings/join.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/strings/join.py b/strings/join.py index 5c02f65a20ce..cdcc3a1377f4 100644 --- a/strings/join.py +++ b/strings/join.py @@ -24,6 +24,8 @@ def join(separator: str, separated: list[str]) -> str: 'a' >>> join(" ", ["You", "are", "amazing!"]) 'You are amazing!' + >>> join(",", ["", "", ""]) + ',,' This example should raise an exception for non-string elements: @@ -37,15 +39,33 @@ def join(separator: str, separated: list[str]) -> str: 'apple-banana-cherry' """ - joined = "" + # Check that all elements are strings for word_or_phrase in separated: + # If the element is not a string, raise an exception if not isinstance(word_or_phrase, str): raise Exception("join() accepts only strings") + + joined: str = "" + """ + The last element of the list is not followed by the separator. + So, we need to iterate through the list and join each element + with the separator except the last element. + """ + last_index: int = len(separated) - 1 + """ + Iterate through the list and join each element with the separator. + Except the last element, all other elements are followed by the separator. + """ + for word_or_phrase in separated[:last_index]: + # join the element with the separator. joined += word_or_phrase + separator - # Remove the trailing separator - # by stripping it from the result - return joined.strip(separator) + # If the list is not empty, join the last element. + if separated != []: + joined += separated[last_index] + + # Return the joined string. + return joined if __name__ == "__main__": From d9092d88dd8b47323d14f87025195e0c76fe7889 Mon Sep 17 00:00:00 2001 From: Sankalpa Sarkar <137193167+sanks011@users.noreply.github.com> Date: Sun, 29 Dec 2024 19:23:31 +0530 Subject: [PATCH 037/126] fixes requirements error (#12438) * fixes join.py action * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixes split.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed two requirements * Custom Implementation of join.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated join.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update split.py * Update join.py * Update join.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4cc83f44987d..b104505e01bc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ beautifulsoup4 -fake_useragent +fake-useragent imageio keras lxml @@ -11,7 +11,7 @@ pillow requests rich scikit-learn -sphinx_pyproject +sphinx-pyproject statsmodels sympy tweepy From bfc804a41c6fb7f3c2e371b15d50ba4830bab3a7 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sun, 29 Dec 2024 18:41:28 +0300 Subject: [PATCH 038/126] Fix sphinx/build_docs warnings for physics/newtons_second_law_of_motion (#12480) * Fix sphinx/build_docs warnings for physics/newtons_second_law_of_motion * Fix * Fix * Fix review issue --- physics/newtons_second_law_of_motion.py | 83 ++++++++++++++----------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/physics/newtons_second_law_of_motion.py b/physics/newtons_second_law_of_motion.py index 53fab6ce78b9..4149e2494f31 100644 --- a/physics/newtons_second_law_of_motion.py +++ b/physics/newtons_second_law_of_motion.py @@ -1,18 +1,22 @@ -""" -Description : -Newton's second law of motion pertains to the behavior of objects for which -all existing forces are not balanced. -The second law states that the acceleration of an object is dependent upon two variables -- the net force acting upon the object and the mass of the object. -The acceleration of an object depends directly -upon the net force acting upon the object, -and inversely upon the mass of the object. -As the force acting upon an object is increased, -the acceleration of the object is increased. -As the mass of an object is increased, the acceleration of the object is decreased. +r""" +Description: + Newton's second law of motion pertains to the behavior of objects for which + all existing forces are not balanced. + The second law states that the acceleration of an object is dependent upon + two variables - the net force acting upon the object and the mass of the object. + The acceleration of an object depends directly + upon the net force acting upon the object, + and inversely upon the mass of the object. + As the force acting upon an object is increased, + the acceleration of the object is increased. + As the mass of an object is increased, the acceleration of the object is decreased. + Source: https://www.physicsclassroom.com/class/newtlaws/Lesson-3/Newton-s-Second-Law -Formulation: Fnet = m • a -Diagrammatic Explanation: + +Formulation: F_net = m • a + +Diagrammatic Explanation:: + Forces are unbalanced | | @@ -26,35 +30,42 @@ / \ / \ / \ - __________________ ____ ________________ - |The acceleration | |The acceleration | - |depends directly | |depends inversely | - |on the net Force | |upon the object's | - |_________________| |mass_______________| -Units: -1 Newton = 1 kg X meters / (seconds^2) + __________________ ____________________ + | The acceleration | | The acceleration | + | depends directly | | depends inversely | + | on the net force | | upon the object's | + | | | mass | + |__________________| |____________________| + +Units: 1 Newton = 1 kg • meters/seconds^2 + How to use? -Inputs: - ___________________________________________________ - |Name | Units | Type | - |-------------|-------------------------|-----------| - |mass | (in kgs) | float | - |-------------|-------------------------|-----------| - |acceleration | (in meters/(seconds^2)) | float | - |_____________|_________________________|___________| - -Output: - ___________________________________________________ - |Name | Units | Type | - |-------------|-------------------------|-----------| - |force | (in Newtons) | float | - |_____________|_________________________|___________| + +Inputs:: + + ______________ _____________________ ___________ + | Name | Units | Type | + |--------------|---------------------|-----------| + | mass | in kgs | float | + |--------------|---------------------|-----------| + | acceleration | in meters/seconds^2 | float | + |______________|_____________________|___________| + +Output:: + + ______________ _______________________ ___________ + | Name | Units | Type | + |--------------|-----------------------|-----------| + | force | in Newtons | float | + |______________|_______________________|___________| """ def newtons_second_law_of_motion(mass: float, acceleration: float) -> float: """ + Calculates force from `mass` and `acceleration` + >>> newtons_second_law_of_motion(10, 10) 100 >>> newtons_second_law_of_motion(2.0, 1) From c93288389d220297f972137293f4565c62131516 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 29 Dec 2024 18:16:45 +0100 Subject: [PATCH 039/126] [pre-commit.ci] pre-commit autoupdate (#12466) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.3 → v0.8.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.3...v0.8.4) - [github.com/pre-commit/mirrors-mypy: v1.13.0 → v1.14.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.13.0...v1.14.0) * Update convert_number_to_words.py * Update convert_number_to_words.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 4 ++-- conversions/convert_number_to_words.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0c8108ac55be..71ac72c29b5f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.3 + rev: v0.8.4 hooks: - id: ruff - id: ruff-format @@ -47,7 +47,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.13.0 + rev: v1.14.0 hooks: - id: mypy args: diff --git a/conversions/convert_number_to_words.py b/conversions/convert_number_to_words.py index dbab44c72e1f..6aa43738b9fe 100644 --- a/conversions/convert_number_to_words.py +++ b/conversions/convert_number_to_words.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import ClassVar, Literal +from typing import Literal class NumberingSystem(Enum): @@ -54,7 +54,7 @@ def max_value(cls, system: str) -> int: class NumberWords(Enum): - ONES: ClassVar[dict[int, str]] = { + ONES = { # noqa: RUF012 0: "", 1: "one", 2: "two", @@ -67,7 +67,7 @@ class NumberWords(Enum): 9: "nine", } - TEENS: ClassVar[dict[int, str]] = { + TEENS = { # noqa: RUF012 0: "ten", 1: "eleven", 2: "twelve", @@ -80,7 +80,7 @@ class NumberWords(Enum): 9: "nineteen", } - TENS: ClassVar[dict[int, str]] = { + TENS = { # noqa: RUF012 2: "twenty", 3: "thirty", 4: "forty", From bfb0447efb73dd049c6a56331cea36cb1345686b Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sun, 29 Dec 2024 20:29:48 +0300 Subject: [PATCH 040/126] Fix sphinx/build_docs warnings for maths/zellers_congruence (#12481) * Fix sphinx/build_docs warnings for maths/zellers_congruence * Fix --- maths/zellers_congruence.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/maths/zellers_congruence.py b/maths/zellers_congruence.py index 483fb000f86b..b958ed3b8659 100644 --- a/maths/zellers_congruence.py +++ b/maths/zellers_congruence.py @@ -4,13 +4,14 @@ def zeller(date_input: str) -> str: """ - Zellers Congruence Algorithm - Find the day of the week for nearly any Gregorian or Julian calendar date + | Zellers Congruence Algorithm + | Find the day of the week for nearly any Gregorian or Julian calendar date >>> zeller('01-31-2010') 'Your date 01-31-2010, is a Sunday!' - Validate out of range month + Validate out of range month: + >>> zeller('13-31-2010') Traceback (most recent call last): ... @@ -21,6 +22,7 @@ def zeller(date_input: str) -> str: ValueError: invalid literal for int() with base 10: '.2' Validate out of range date: + >>> zeller('01-33-2010') Traceback (most recent call last): ... @@ -31,30 +33,35 @@ def zeller(date_input: str) -> str: ValueError: invalid literal for int() with base 10: '.4' Validate second separator: + >>> zeller('01-31*2010') Traceback (most recent call last): ... ValueError: Date separator must be '-' or '/' Validate first separator: + >>> zeller('01^31-2010') Traceback (most recent call last): ... ValueError: Date separator must be '-' or '/' Validate out of range year: + >>> zeller('01-31-8999') Traceback (most recent call last): ... ValueError: Year out of range. There has to be some sort of limit...right? Test null input: + >>> zeller() Traceback (most recent call last): ... TypeError: zeller() missing 1 required positional argument: 'date_input' - Test length of date_input: + Test length of `date_input`: + >>> zeller('') Traceback (most recent call last): ... From ce036db2131626b86b94ab87854c82a9bc6c3d0e Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sun, 29 Dec 2024 23:01:15 +0300 Subject: [PATCH 041/126] Fix sphinx/build_docs warnings for physics/speeds_of_gas_molecules (#12471) * Fix sphinx/build_docs warnings for physics/speeds_of_gas_molecules * Fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * Fix review issue * Fix * Fix * Fix --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- physics/speeds_of_gas_molecules.py | 36 ++++++++++++++++-------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/physics/speeds_of_gas_molecules.py b/physics/speeds_of_gas_molecules.py index a50d1c0f6d76..42f90a9fd6f3 100644 --- a/physics/speeds_of_gas_molecules.py +++ b/physics/speeds_of_gas_molecules.py @@ -4,43 +4,43 @@ distribution is a probability distribution that describes the distribution of speeds of particles in an ideal gas. -The distribution is given by the following equation: +The distribution is given by the following equation:: ------------------------------------------------- | f(v) = (M/2πRT)^(3/2) * 4πv^2 * e^(-Mv^2/2RT) | ------------------------------------------------- where: - f(v) is the fraction of molecules with a speed v - M is the molar mass of the gas in kg/mol - R is the gas constant - T is the absolute temperature + * ``f(v)`` is the fraction of molecules with a speed ``v`` + * ``M`` is the molar mass of the gas in kg/mol + * ``R`` is the gas constant + * ``T`` is the absolute temperature More information about the Maxwell-Boltzmann distribution can be found here: https://en.wikipedia.org/wiki/Maxwell%E2%80%93Boltzmann_distribution The average speed can be calculated by integrating the Maxwell-Boltzmann distribution -from 0 to infinity and dividing by the total number of molecules. The result is: +from 0 to infinity and dividing by the total number of molecules. The result is:: - --------------------- - | vavg = √(8RT/πM) | - --------------------- + ---------------------- + | v_avg = √(8RT/πM) | + ---------------------- The most probable speed is the speed at which the Maxwell-Boltzmann distribution is at its maximum. This can be found by differentiating the Maxwell-Boltzmann -distribution with respect to v and setting the result equal to zero. The result is: +distribution with respect to ``v`` and setting the result equal to zero. The result is:: - --------------------- - | vmp = √(2RT/M) | - --------------------- + ---------------------- + | v_mp = √(2RT/M) | + ---------------------- The root-mean-square speed is another measure of the average speed of the molecules in a gas. It is calculated by taking the square root -of the average of the squares of the speeds of the molecules. The result is: +of the average of the squares of the speeds of the molecules. The result is:: - --------------------- - | vrms = √(3RT/M) | - --------------------- + ---------------------- + | v_rms = √(3RT/M) | + ---------------------- Here we have defined functions to calculate the average and most probable speeds of molecules in a gas given the @@ -57,6 +57,7 @@ def avg_speed_of_molecule(temperature: float, molar_mass: float) -> float: and returns the average speed of a molecule in the gas (in m/s). Examples: + >>> avg_speed_of_molecule(273, 0.028) # nitrogen at 273 K 454.3488755020387 >>> avg_speed_of_molecule(300, 0.032) # oxygen at 300 K @@ -84,6 +85,7 @@ def mps_speed_of_molecule(temperature: float, molar_mass: float) -> float: and returns the most probable speed of a molecule in the gas (in m/s). Examples: + >>> mps_speed_of_molecule(273, 0.028) # nitrogen at 273 K 402.65620701908966 >>> mps_speed_of_molecule(300, 0.032) # oxygen at 300 K From 3622e940c9db74ebac06a5b12f83fd638d7c5511 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sun, 29 Dec 2024 23:31:53 +0300 Subject: [PATCH 042/126] Fix sphinx/build_docs warnings for other (#12482) * Fix sphinx/build_docs warnings for other * Fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- other/bankers_algorithm.py | 16 +++-- other/davis_putnam_logemann_loveland.py | 94 ++++++++++++++----------- other/scoring_algorithm.py | 30 ++++---- 3 files changed, 77 insertions(+), 63 deletions(-) diff --git a/other/bankers_algorithm.py b/other/bankers_algorithm.py index d4254f479a4f..b1da851fc0f3 100644 --- a/other/bankers_algorithm.py +++ b/other/bankers_algorithm.py @@ -10,9 +10,10 @@ predetermined maximum possible amounts of all resources, and then makes a "s-state" check to test for possible deadlock conditions for all other pending activities, before deciding whether allocation should be allowed to continue. -[Source] Wikipedia -[Credit] Rosetta Code C implementation helped very much. - (https://rosettacode.org/wiki/Banker%27s_algorithm) + +| [Source] Wikipedia +| [Credit] Rosetta Code C implementation helped very much. +| (https://rosettacode.org/wiki/Banker%27s_algorithm) """ from __future__ import annotations @@ -75,7 +76,7 @@ def __available_resources(self) -> list[int]: def __need(self) -> list[list[int]]: """ Implement safety checker that calculates the needs by ensuring that - max_claim[i][j] - alloc_table[i][j] <= avail[j] + ``max_claim[i][j] - alloc_table[i][j] <= avail[j]`` """ return [ list(np.array(self.__maximum_claim_table[i]) - np.array(allocated_resource)) @@ -86,7 +87,9 @@ def __need_index_manager(self) -> dict[int, list[int]]: """ This function builds an index control dictionary to track original ids/indices of processes when altered during execution of method "main" - Return: {0: [a: int, b: int], 1: [c: int, d: int]} + + :Return: {0: [a: int, b: int], 1: [c: int, d: int]} + >>> index_control = BankersAlgorithm( ... test_claim_vector, test_allocated_res_table, test_maximum_claim_table ... )._BankersAlgorithm__need_index_manager() @@ -100,7 +103,8 @@ def __need_index_manager(self) -> dict[int, list[int]]: def main(self, **kwargs) -> None: """ Utilize various methods in this class to simulate the Banker's algorithm - Return: None + :Return: None + >>> BankersAlgorithm(test_claim_vector, test_allocated_res_table, ... test_maximum_claim_table).main(describe=True) Allocated Resource Table diff --git a/other/davis_putnam_logemann_loveland.py b/other/davis_putnam_logemann_loveland.py index 0f3100b1bc2e..e95bf371a817 100644 --- a/other/davis_putnam_logemann_loveland.py +++ b/other/davis_putnam_logemann_loveland.py @@ -17,13 +17,15 @@ class Clause: """ - A clause represented in Conjunctive Normal Form. - A clause is a set of literals, either complemented or otherwise. + | A clause represented in Conjunctive Normal Form. + | A clause is a set of literals, either complemented or otherwise. + For example: - {A1, A2, A3'} is the clause (A1 v A2 v A3') - {A5', A2', A1} is the clause (A5' v A2' v A1) + * {A1, A2, A3'} is the clause (A1 v A2 v A3') + * {A5', A2', A1} is the clause (A5' v A2' v A1) Create model + >>> clause = Clause(["A1", "A2'", "A3"]) >>> clause.evaluate({"A1": True}) True @@ -39,6 +41,7 @@ def __init__(self, literals: list[str]) -> None: def __str__(self) -> str: """ To print a clause as in Conjunctive Normal Form. + >>> str(Clause(["A1", "A2'", "A3"])) "{A1 , A2' , A3}" """ @@ -47,6 +50,7 @@ def __str__(self) -> str: def __len__(self) -> int: """ To print a clause as in Conjunctive Normal Form. + >>> len(Clause([])) 0 >>> len(Clause(["A1", "A2'", "A3"])) @@ -72,11 +76,13 @@ def assign(self, model: dict[str, bool | None]) -> None: def evaluate(self, model: dict[str, bool | None]) -> bool | None: """ Evaluates the clause with the assignments in model. + This has the following steps: - 1. Return True if both a literal and its complement exist in the clause. - 2. Return True if a single literal has the assignment True. - 3. Return None(unable to complete evaluation) if a literal has no assignment. - 4. Compute disjunction of all values assigned in clause. + 1. Return ``True`` if both a literal and its complement exist in the clause. + 2. Return ``True`` if a single literal has the assignment ``True``. + 3. Return ``None`` (unable to complete evaluation) + if a literal has no assignment. + 4. Compute disjunction of all values assigned in clause. """ for literal in self.literals: symbol = literal.rstrip("'") if literal.endswith("'") else literal + "'" @@ -92,10 +98,10 @@ def evaluate(self, model: dict[str, bool | None]) -> bool | None: class Formula: """ - A formula represented in Conjunctive Normal Form. - A formula is a set of clauses. - For example, - {{A1, A2, A3'}, {A5', A2', A1}} is ((A1 v A2 v A3') and (A5' v A2' v A1)) + | A formula represented in Conjunctive Normal Form. + | A formula is a set of clauses. + | For example, + | {{A1, A2, A3'}, {A5', A2', A1}} is ((A1 v A2 v A3') and (A5' v A2' v A1)) """ def __init__(self, clauses: Iterable[Clause]) -> None: @@ -107,7 +113,8 @@ def __init__(self, clauses: Iterable[Clause]) -> None: def __str__(self) -> str: """ To print a formula as in Conjunctive Normal Form. - str(Formula([Clause(["A1", "A2'", "A3"]), Clause(["A5'", "A2'", "A1"])])) + + >>> str(Formula([Clause(["A1", "A2'", "A3"]), Clause(["A5'", "A2'", "A1"])])) "{{A1 , A2' , A3} , {A5' , A2' , A1}}" """ return "{" + " , ".join(str(clause) for clause in self.clauses) + "}" @@ -115,8 +122,8 @@ def __str__(self) -> str: def generate_clause() -> Clause: """ - Randomly generate a clause. - All literals have the name Ax, where x is an integer from 1 to 5. + | Randomly generate a clause. + | All literals have the name Ax, where x is an integer from ``1`` to ``5``. """ literals = [] no_of_literals = random.randint(1, 5) @@ -149,11 +156,12 @@ def generate_formula() -> Formula: def generate_parameters(formula: Formula) -> tuple[list[Clause], list[str]]: """ - Return the clauses and symbols from a formula. - A symbol is the uncomplemented form of a literal. + | Return the clauses and symbols from a formula. + | A symbol is the uncomplemented form of a literal. + For example, - Symbol of A3 is A3. - Symbol of A5' is A5. + * Symbol of A3 is A3. + * Symbol of A5' is A5. >>> formula = Formula([Clause(["A1", "A2'", "A3"]), Clause(["A5'", "A2'", "A1"])]) >>> clauses, symbols = generate_parameters(formula) @@ -177,21 +185,20 @@ def find_pure_symbols( clauses: list[Clause], symbols: list[str], model: dict[str, bool | None] ) -> tuple[list[str], dict[str, bool | None]]: """ - Return pure symbols and their values to satisfy clause. - Pure symbols are symbols in a formula that exist only - in one form, either complemented or otherwise. - For example, - { { A4 , A3 , A5' , A1 , A3' } , { A4 } , { A3 } } has - pure symbols A4, A5' and A1. + | Return pure symbols and their values to satisfy clause. + | Pure symbols are symbols in a formula that exist only in one form, + | either complemented or otherwise. + | For example, + | {{A4 , A3 , A5' , A1 , A3'} , {A4} , {A3}} has pure symbols A4, A5' and A1. + This has the following steps: - 1. Ignore clauses that have already evaluated to be True. - 2. Find symbols that occur only in one form in the rest of the clauses. - 3. Assign value True or False depending on whether the symbols occurs - in normal or complemented form respectively. + 1. Ignore clauses that have already evaluated to be ``True``. + 2. Find symbols that occur only in one form in the rest of the clauses. + 3. Assign value ``True`` or ``False`` depending on whether the symbols occurs + in normal or complemented form respectively. >>> formula = Formula([Clause(["A1", "A2'", "A3"]), Clause(["A5'", "A2'", "A1"])]) >>> clauses, symbols = generate_parameters(formula) - >>> pure_symbols, values = find_pure_symbols(clauses, symbols, {}) >>> pure_symbols ['A1', 'A2', 'A3', 'A5'] @@ -231,20 +238,21 @@ def find_unit_clauses( ) -> tuple[list[str], dict[str, bool | None]]: """ Returns the unit symbols and their values to satisfy clause. + Unit symbols are symbols in a formula that are: - - Either the only symbol in a clause - - Or all other literals in that clause have been assigned False + - Either the only symbol in a clause + - Or all other literals in that clause have been assigned ``False`` + This has the following steps: - 1. Find symbols that are the only occurrences in a clause. - 2. Find symbols in a clause where all other literals are assigned False. - 3. Assign True or False depending on whether the symbols occurs in - normal or complemented form respectively. + 1. Find symbols that are the only occurrences in a clause. + 2. Find symbols in a clause where all other literals are assigned ``False``. + 3. Assign ``True`` or ``False`` depending on whether the symbols occurs in + normal or complemented form respectively. >>> clause1 = Clause(["A4", "A3", "A5'", "A1", "A3'"]) >>> clause2 = Clause(["A4"]) >>> clause3 = Clause(["A3"]) >>> clauses, symbols = generate_parameters(Formula([clause1, clause2, clause3])) - >>> unit_clauses, values = find_unit_clauses(clauses, {}) >>> unit_clauses ['A4', 'A3'] @@ -278,16 +286,16 @@ def dpll_algorithm( clauses: list[Clause], symbols: list[str], model: dict[str, bool | None] ) -> tuple[bool | None, dict[str, bool | None] | None]: """ - Returns the model if the formula is satisfiable, else None + Returns the model if the formula is satisfiable, else ``None`` + This has the following steps: - 1. If every clause in clauses is True, return True. - 2. If some clause in clauses is False, return False. - 3. Find pure symbols. - 4. Find unit symbols. + 1. If every clause in clauses is ``True``, return ``True``. + 2. If some clause in clauses is ``False``, return ``False``. + 3. Find pure symbols. + 4. Find unit symbols. >>> formula = Formula([Clause(["A4", "A3", "A5'", "A1", "A3'"]), Clause(["A4"])]) >>> clauses, symbols = generate_parameters(formula) - >>> soln, model = dpll_algorithm(clauses, symbols, {}) >>> soln True diff --git a/other/scoring_algorithm.py b/other/scoring_algorithm.py index af04f432e433..0185d7a2e0c0 100644 --- a/other/scoring_algorithm.py +++ b/other/scoring_algorithm.py @@ -1,25 +1,26 @@ """ -developed by: markmelnic -original repo: https://github.com/markmelnic/Scoring-Algorithm +| developed by: markmelnic +| original repo: https://github.com/markmelnic/Scoring-Algorithm Analyse data using a range based percentual proximity algorithm and calculate the linear maximum likelihood estimation. The basic principle is that all values supplied will be broken -down to a range from 0 to 1 and each column's score will be added +down to a range from ``0`` to ``1`` and each column's score will be added up to get the total score. -========== Example for data of vehicles -price|mileage|registration_year -20k |60k |2012 -22k |50k |2011 -23k |90k |2015 -16k |210k |2010 +:: + + price|mileage|registration_year + 20k |60k |2012 + 22k |50k |2011 + 23k |90k |2015 + 16k |210k |2010 We want the vehicle with the lowest price, lowest mileage but newest registration year. Thus the weights for each column are as follows: -[0, 0, 1] +``[0, 0, 1]`` """ @@ -97,10 +98,11 @@ def procentual_proximity( source_data: list[list[float]], weights: list[int] ) -> list[list[float]]: """ - weights - int list - possible values - 0 / 1 - 0 if lower values have higher weight in the data set - 1 if higher values have higher weight in the data set + | `weights` - ``int`` list + | possible values - ``0`` / ``1`` + + * ``0`` if lower values have higher weight in the data set + * ``1`` if higher values have higher weight in the data set >>> procentual_proximity([[20, 60, 2012],[23, 90, 2015],[22, 50, 2011]], [0, 0, 1]) [[20, 60, 2012, 2.0], [23, 90, 2015, 1.0], [22, 50, 2011, 1.3333333333333335]] From 94b3777936101bcc592fc5ef143ac08ad49195e7 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 30 Dec 2024 00:35:34 +0300 Subject: [PATCH 043/126] Fix sphinx/build_docs warnings for linear_algebra (#12483) * Fix sphinx/build_docs warnings for linear_algebra/ * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- linear_algebra/gaussian_elimination.py | 28 +++++++++++++------ linear_algebra/lu_decomposition.py | 20 +++++++------ .../src/gaussian_elimination_pivoting.py | 7 +++-- linear_algebra/src/rank_of_matrix.py | 6 +++- linear_algebra/src/schur_complement.py | 13 +++++---- linear_algebra/src/transformations_2d.py | 14 ++++++---- 6 files changed, 54 insertions(+), 34 deletions(-) diff --git a/linear_algebra/gaussian_elimination.py b/linear_algebra/gaussian_elimination.py index 724773c0db98..6f4075b710fd 100644 --- a/linear_algebra/gaussian_elimination.py +++ b/linear_algebra/gaussian_elimination.py @@ -1,6 +1,6 @@ """ -Gaussian elimination method for solving a system of linear equations. -Gaussian elimination - https://en.wikipedia.org/wiki/Gaussian_elimination +| Gaussian elimination method for solving a system of linear equations. +| Gaussian elimination - https://en.wikipedia.org/wiki/Gaussian_elimination """ import numpy as np @@ -13,12 +13,17 @@ def retroactive_resolution( ) -> NDArray[float64]: """ This function performs a retroactive linear system resolution - for triangular matrix + for triangular matrix Examples: - 2x1 + 2x2 - 1x3 = 5 2x1 + 2x2 = -1 - 0x1 - 2x2 - 1x3 = -7 0x1 - 2x2 = -1 - 0x1 + 0x2 + 5x3 = 15 + 1. + * 2x1 + 2x2 - 1x3 = 5 + * 0x1 - 2x2 - 1x3 = -7 + * 0x1 + 0x2 + 5x3 = 15 + 2. + * 2x1 + 2x2 = -1 + * 0x1 - 2x2 = -1 + >>> gaussian_elimination([[2, 2, -1], [0, -2, -1], [0, 0, 5]], [[5], [-7], [15]]) array([[2.], [2.], @@ -45,9 +50,14 @@ def gaussian_elimination( This function performs Gaussian elimination method Examples: - 1x1 - 4x2 - 2x3 = -2 1x1 + 2x2 = 5 - 5x1 + 2x2 - 2x3 = -3 5x1 + 2x2 = 5 - 1x1 - 1x2 + 0x3 = 4 + 1. + * 1x1 - 4x2 - 2x3 = -2 + * 5x1 + 2x2 - 2x3 = -3 + * 1x1 - 1x2 + 0x3 = 4 + 2. + * 1x1 + 2x2 = 5 + * 5x1 + 2x2 = 5 + >>> gaussian_elimination([[1, -4, -2], [5, 2, -2], [1, -1, 0]], [[-2], [-3], [4]]) array([[ 2.3 ], [-1.7 ], diff --git a/linear_algebra/lu_decomposition.py b/linear_algebra/lu_decomposition.py index 3620674835cd..3d89b53a48fb 100644 --- a/linear_algebra/lu_decomposition.py +++ b/linear_algebra/lu_decomposition.py @@ -2,13 +2,14 @@ Lower-upper (LU) decomposition factors a matrix as a product of a lower triangular matrix and an upper triangular matrix. A square matrix has an LU decomposition under the following conditions: + - If the matrix is invertible, then it has an LU decomposition if and only - if all of its leading principal minors are non-zero (see - https://en.wikipedia.org/wiki/Minor_(linear_algebra) for an explanation of - leading principal minors of a matrix). + if all of its leading principal minors are non-zero (see + https://en.wikipedia.org/wiki/Minor_(linear_algebra) for an explanation of + leading principal minors of a matrix). - If the matrix is singular (i.e., not invertible) and it has a rank of k - (i.e., it has k linearly independent columns), then it has an LU - decomposition if its first k leading principal minors are non-zero. + (i.e., it has k linearly independent columns), then it has an LU + decomposition if its first k leading principal minors are non-zero. This algorithm will simply attempt to perform LU decomposition on any square matrix and raise an error if no such decomposition exists. @@ -25,6 +26,7 @@ def lower_upper_decomposition(table: np.ndarray) -> tuple[np.ndarray, np.ndarray """ Perform LU decomposition on a given matrix and raises an error if the matrix isn't square or if no such decomposition exists + >>> matrix = np.array([[2, -2, 1], [0, 1, 2], [5, 3, 1]]) >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) >>> lower_mat @@ -45,7 +47,7 @@ def lower_upper_decomposition(table: np.ndarray) -> tuple[np.ndarray, np.ndarray array([[ 4. , 3. ], [ 0. , -1.5]]) - # Matrix is not square + >>> # Matrix is not square >>> matrix = np.array([[2, -2, 1], [0, 1, 2]]) >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) Traceback (most recent call last): @@ -54,14 +56,14 @@ def lower_upper_decomposition(table: np.ndarray) -> tuple[np.ndarray, np.ndarray [[ 2 -2 1] [ 0 1 2]] - # Matrix is invertible, but its first leading principal minor is 0 + >>> # Matrix is invertible, but its first leading principal minor is 0 >>> matrix = np.array([[0, 1], [1, 0]]) >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) Traceback (most recent call last): ... ArithmeticError: No LU decomposition exists - # Matrix is singular, but its first leading principal minor is 1 + >>> # Matrix is singular, but its first leading principal minor is 1 >>> matrix = np.array([[1, 0], [1, 0]]) >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) >>> lower_mat @@ -71,7 +73,7 @@ def lower_upper_decomposition(table: np.ndarray) -> tuple[np.ndarray, np.ndarray array([[1., 0.], [0., 0.]]) - # Matrix is singular, but its first leading principal minor is 0 + >>> # Matrix is singular, but its first leading principal minor is 0 >>> matrix = np.array([[0, 1], [0, 1]]) >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) Traceback (most recent call last): diff --git a/linear_algebra/src/gaussian_elimination_pivoting.py b/linear_algebra/src/gaussian_elimination_pivoting.py index efc1ddd64a2e..540f57b0cff6 100644 --- a/linear_algebra/src/gaussian_elimination_pivoting.py +++ b/linear_algebra/src/gaussian_elimination_pivoting.py @@ -6,17 +6,18 @@ def solve_linear_system(matrix: np.ndarray) -> np.ndarray: Solve a linear system of equations using Gaussian elimination with partial pivoting Args: - - matrix: Coefficient matrix with the last column representing the constants. + - `matrix`: Coefficient matrix with the last column representing the constants. Returns: - - Solution vector. + - Solution vector. Raises: - - ValueError: If the matrix is not correct (i.e., singular). + - ``ValueError``: If the matrix is not correct (i.e., singular). https://courses.engr.illinois.edu/cs357/su2013/lect.htm Lecture 7 Example: + >>> A = np.array([[2, 1, -1], [-3, -1, 2], [-2, 1, 2]], dtype=float) >>> B = np.array([8, -11, -3], dtype=float) >>> solution = solve_linear_system(np.column_stack((A, B))) diff --git a/linear_algebra/src/rank_of_matrix.py b/linear_algebra/src/rank_of_matrix.py index 7ff3c1699a69..2c4fe2a8d1da 100644 --- a/linear_algebra/src/rank_of_matrix.py +++ b/linear_algebra/src/rank_of_matrix.py @@ -8,11 +8,15 @@ def rank_of_matrix(matrix: list[list[int | float]]) -> int: """ Finds the rank of a matrix. + Args: - matrix: The matrix as a list of lists. + `matrix`: The matrix as a list of lists. + Returns: The rank of the matrix. + Example: + >>> matrix1 = [[1, 2, 3], ... [4, 5, 6], ... [7, 8, 9]] diff --git a/linear_algebra/src/schur_complement.py b/linear_algebra/src/schur_complement.py index 7c79bb70abfc..74ac75e3fce2 100644 --- a/linear_algebra/src/schur_complement.py +++ b/linear_algebra/src/schur_complement.py @@ -12,13 +12,14 @@ def schur_complement( ) -> np.ndarray: """ Schur complement of a symmetric matrix X given as a 2x2 block matrix - consisting of matrices A, B and C. - Matrix A must be quadratic and non-singular. - In case A is singular, a pseudo-inverse may be provided using - the pseudo_inv argument. + consisting of matrices `A`, `B` and `C`. + Matrix `A` must be quadratic and non-singular. + In case `A` is singular, a pseudo-inverse may be provided using + the `pseudo_inv` argument. + + | Link to Wiki: https://en.wikipedia.org/wiki/Schur_complement + | See also Convex Optimization - Boyd and Vandenberghe, A.5.5 - Link to Wiki: https://en.wikipedia.org/wiki/Schur_complement - See also Convex Optimization - Boyd and Vandenberghe, A.5.5 >>> import numpy as np >>> a = np.array([[1, 2], [2, 1]]) >>> b = np.array([[0, 3], [3, 0]]) diff --git a/linear_algebra/src/transformations_2d.py b/linear_algebra/src/transformations_2d.py index b4185cd2848f..5dee59024752 100644 --- a/linear_algebra/src/transformations_2d.py +++ b/linear_algebra/src/transformations_2d.py @@ -3,13 +3,15 @@ I have added the codes for reflection, projection, scaling and rotation 2D matrices. +.. code-block:: python + scaling(5) = [[5.0, 0.0], [0.0, 5.0]] - rotation(45) = [[0.5253219888177297, -0.8509035245341184], - [0.8509035245341184, 0.5253219888177297]] -projection(45) = [[0.27596319193541496, 0.446998331800279], - [0.446998331800279, 0.7240368080645851]] -reflection(45) = [[0.05064397763545947, 0.893996663600558], - [0.893996663600558, 0.7018070490682369]] + rotation(45) = [[0.5253219888177297, -0.8509035245341184], + [0.8509035245341184, 0.5253219888177297]] + projection(45) = [[0.27596319193541496, 0.446998331800279], + [0.446998331800279, 0.7240368080645851]] + reflection(45) = [[0.05064397763545947, 0.893996663600558], + [0.893996663600558, 0.7018070490682369]] """ from math import cos, sin From f45e392cf6e94259eca8c47b13cd3ae22bcd901e Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 30 Dec 2024 12:56:24 +0300 Subject: [PATCH 044/126] Fix sphinx/build_docs warnings for ciphers (#12485) * Fix sphinx/build_docs warnings for ciphers * Fix --- ciphers/autokey.py | 7 +- ciphers/caesar_cipher.py | 77 ++++++++++------- ciphers/decrypt_caesar_with_chi_squared.py | 99 +++++++++++----------- ciphers/enigma_machine2.py | 64 +++++++------- ciphers/rsa_factorization.py | 13 +-- ciphers/simple_keyword_cypher.py | 10 ++- ciphers/trifid_cipher.py | 25 +++--- 7 files changed, 170 insertions(+), 125 deletions(-) diff --git a/ciphers/autokey.py b/ciphers/autokey.py index 05d8c066b139..7751a32d7546 100644 --- a/ciphers/autokey.py +++ b/ciphers/autokey.py @@ -1,5 +1,6 @@ """ https://en.wikipedia.org/wiki/Autokey_cipher + An autokey cipher (also known as the autoclave cipher) is a cipher that incorporates the message (the plaintext) into the key. The key is generated from the message in some automated fashion, @@ -10,8 +11,9 @@ def encrypt(plaintext: str, key: str) -> str: """ - Encrypt a given plaintext (string) and key (string), returning the + Encrypt a given `plaintext` (string) and `key` (string), returning the encrypted ciphertext. + >>> encrypt("hello world", "coffee") 'jsqqs avvwo' >>> encrypt("coffee is good as python", "TheAlgorithms") @@ -74,8 +76,9 @@ def encrypt(plaintext: str, key: str) -> str: def decrypt(ciphertext: str, key: str) -> str: """ - Decrypt a given ciphertext (string) and key (string), returning the decrypted + Decrypt a given `ciphertext` (string) and `key` (string), returning the decrypted ciphertext. + >>> decrypt("jsqqs avvwo", "coffee") 'hello world' >>> decrypt("vvjfpk wj ohvp su ddylsv", "TheAlgorithms") diff --git a/ciphers/caesar_cipher.py b/ciphers/caesar_cipher.py index d19b9a337221..9c096fe8a7da 100644 --- a/ciphers/caesar_cipher.py +++ b/ciphers/caesar_cipher.py @@ -7,24 +7,29 @@ def encrypt(input_string: str, key: int, alphabet: str | None = None) -> str: """ encrypt ======= + Encodes a given string with the caesar cipher and returns the encoded message Parameters: ----------- - * input_string: the plain-text that needs to be encoded - * key: the number of letters to shift the message by + + * `input_string`: the plain-text that needs to be encoded + * `key`: the number of letters to shift the message by Optional: - * alphabet (None): the alphabet used to encode the cipher, if not + + * `alphabet` (``None``): the alphabet used to encode the cipher, if not specified, the standard english alphabet with upper and lowercase letters is used Returns: + * A string containing the encoded cipher-text More on the caesar cipher ========================= + The caesar cipher is named after Julius Caesar who used it when sending secret military messages to his troops. This is a simple substitution cipher where every character in the plain-text is shifted by a certain number known @@ -32,26 +37,28 @@ def encrypt(input_string: str, key: int, alphabet: str | None = None) -> str: Example: Say we have the following message: - "Hello, captain" + ``Hello, captain`` And our alphabet is made up of lower and uppercase letters: - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + ``abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`` - And our shift is "2" + And our shift is ``2`` - We can then encode the message, one letter at a time. "H" would become "J", - since "J" is two letters away, and so on. If the shift is ever two large, or + We can then encode the message, one letter at a time. ``H`` would become ``J``, + since ``J`` is two letters away, and so on. If the shift is ever two large, or our letter is at the end of the alphabet, we just start at the beginning - ("Z" would shift to "a" then "b" and so on). + (``Z`` would shift to ``a`` then ``b`` and so on). - Our final message would be "Jgnnq, ecrvckp" + Our final message would be ``Jgnnq, ecrvckp`` Further reading =============== + * https://en.m.wikipedia.org/wiki/Caesar_cipher Doctests ======== + >>> encrypt('The quick brown fox jumps over the lazy dog', 8) 'bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo' @@ -85,23 +92,28 @@ def decrypt(input_string: str, key: int, alphabet: str | None = None) -> str: """ decrypt ======= + Decodes a given string of cipher-text and returns the decoded plain-text Parameters: ----------- - * input_string: the cipher-text that needs to be decoded - * key: the number of letters to shift the message backwards by to decode + + * `input_string`: the cipher-text that needs to be decoded + * `key`: the number of letters to shift the message backwards by to decode Optional: - * alphabet (None): the alphabet used to decode the cipher, if not + + * `alphabet` (``None``): the alphabet used to decode the cipher, if not specified, the standard english alphabet with upper and lowercase letters is used Returns: + * A string containing the decoded plain-text More on the caesar cipher ========================= + The caesar cipher is named after Julius Caesar who used it when sending secret military messages to his troops. This is a simple substitution cipher where very character in the plain-text is shifted by a certain number known @@ -110,27 +122,29 @@ def decrypt(input_string: str, key: int, alphabet: str | None = None) -> str: Example: Say we have the following cipher-text: - "Jgnnq, ecrvckp" + ``Jgnnq, ecrvckp`` And our alphabet is made up of lower and uppercase letters: - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + ``abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`` - And our shift is "2" + And our shift is ``2`` To decode the message, we would do the same thing as encoding, but in - reverse. The first letter, "J" would become "H" (remember: we are decoding) - because "H" is two letters in reverse (to the left) of "J". We would - continue doing this. A letter like "a" would shift back to the end of - the alphabet, and would become "Z" or "Y" and so on. + reverse. The first letter, ``J`` would become ``H`` (remember: we are decoding) + because ``H`` is two letters in reverse (to the left) of ``J``. We would + continue doing this. A letter like ``a`` would shift back to the end of + the alphabet, and would become ``Z`` or ``Y`` and so on. - Our final message would be "Hello, captain" + Our final message would be ``Hello, captain`` Further reading =============== + * https://en.m.wikipedia.org/wiki/Caesar_cipher Doctests ======== + >>> decrypt('bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo', 8) 'The quick brown fox jumps over the lazy dog' @@ -150,41 +164,44 @@ def brute_force(input_string: str, alphabet: str | None = None) -> dict[int, str """ brute_force =========== + Returns all the possible combinations of keys and the decoded strings in the form of a dictionary Parameters: ----------- - * input_string: the cipher-text that needs to be used during brute-force + + * `input_string`: the cipher-text that needs to be used during brute-force Optional: - * alphabet: (None): the alphabet used to decode the cipher, if not + + * `alphabet` (``None``): the alphabet used to decode the cipher, if not specified, the standard english alphabet with upper and lowercase letters is used More about brute force ====================== + Brute force is when a person intercepts a message or password, not knowing the key and tries every single combination. This is easy with the caesar cipher since there are only all the letters in the alphabet. The more complex the cipher, the larger amount of time it will take to do brute force Ex: - Say we have a 5 letter alphabet (abcde), for simplicity and we intercepted the - following message: - - "dbc" - + Say we have a ``5`` letter alphabet (``abcde``), for simplicity and we intercepted + the following message: ``dbc``, we could then just write out every combination: - ecd... and so on, until we reach a combination that makes sense: - "cab" + ``ecd``... and so on, until we reach a combination that makes sense: + ``cab`` Further reading =============== + * https://en.wikipedia.org/wiki/Brute_force Doctests ======== + >>> brute_force("jFyuMy xIH'N vLONy zILwy Gy!")[20] "Please don't brute force me!" diff --git a/ciphers/decrypt_caesar_with_chi_squared.py b/ciphers/decrypt_caesar_with_chi_squared.py index 10832203e531..fb95c0f90628 100644 --- a/ciphers/decrypt_caesar_with_chi_squared.py +++ b/ciphers/decrypt_caesar_with_chi_squared.py @@ -11,33 +11,31 @@ def decrypt_caesar_with_chi_squared( """ Basic Usage =========== + Arguments: - * ciphertext (str): the text to decode (encoded with the caesar cipher) + * `ciphertext` (str): the text to decode (encoded with the caesar cipher) Optional Arguments: - * cipher_alphabet (list): the alphabet used for the cipher (each letter is - a string separated by commas) - * frequencies_dict (dict): a dictionary of word frequencies where keys are - the letters and values are a percentage representation of the frequency as - a decimal/float - * case_sensitive (bool): a boolean value: True if the case matters during - decryption, False if it doesn't + * `cipher_alphabet` (list): the alphabet used for the cipher (each letter is + a string separated by commas) + * `frequencies_dict` (dict): a dictionary of word frequencies where keys are + the letters and values are a percentage representation of the frequency as + a decimal/float + * `case_sensitive` (bool): a boolean value: ``True`` if the case matters during + decryption, ``False`` if it doesn't Returns: - * A tuple in the form of: - ( - most_likely_cipher, - most_likely_cipher_chi_squared_value, - decoded_most_likely_cipher - ) + * A tuple in the form of: + (`most_likely_cipher`, `most_likely_cipher_chi_squared_value`, + `decoded_most_likely_cipher`) - where... - - most_likely_cipher is an integer representing the shift of the smallest - chi-squared statistic (most likely key) - - most_likely_cipher_chi_squared_value is a float representing the - chi-squared statistic of the most likely shift - - decoded_most_likely_cipher is a string with the decoded cipher - (decoded by the most_likely_cipher key) + where... + - `most_likely_cipher` is an integer representing the shift of the smallest + chi-squared statistic (most likely key) + - `most_likely_cipher_chi_squared_value` is a float representing the + chi-squared statistic of the most likely shift + - `decoded_most_likely_cipher` is a string with the decoded cipher + (decoded by the most_likely_cipher key) The Chi-squared test @@ -45,52 +43,57 @@ def decrypt_caesar_with_chi_squared( The caesar cipher ----------------- + The caesar cipher is a very insecure encryption algorithm, however it has been used since Julius Caesar. The cipher is a simple substitution cipher where each character in the plain text is replaced by a character in the alphabet a certain number of characters after the original character. The number of characters away is called the shift or key. For example: - Plain text: hello - Key: 1 - Cipher text: ifmmp - (each letter in hello has been shifted one to the right in the eng. alphabet) + | Plain text: ``hello`` + | Key: ``1`` + | Cipher text: ``ifmmp`` + | (each letter in ``hello`` has been shifted one to the right in the eng. alphabet) As you can imagine, this doesn't provide lots of security. In fact decrypting ciphertext by brute-force is extremely easy even by hand. However - one way to do that is the chi-squared test. + one way to do that is the chi-squared test. The chi-squared test - ------------------- + -------------------- + Each letter in the english alphabet has a frequency, or the amount of times it shows up compared to other letters (usually expressed as a decimal representing the percentage likelihood). The most common letter in the - english language is "e" with a frequency of 0.11162 or 11.162%. The test is - completed in the following fashion. + english language is ``e`` with a frequency of ``0.11162`` or ``11.162%``. + The test is completed in the following fashion. 1. The ciphertext is decoded in a brute force way (every combination of the - 26 possible combinations) + ``26`` possible combinations) 2. For every combination, for each letter in the combination, the average amount of times the letter should appear the message is calculated by - multiplying the total number of characters by the frequency of the letter + multiplying the total number of characters by the frequency of the letter. + + | For example: + | In a message of ``100`` characters, ``e`` should appear around ``11.162`` + times. - For example: - In a message of 100 characters, e should appear around 11.162 times. + 3. Then, to calculate the margin of error (the amount of times the letter + SHOULD appear with the amount of times the letter DOES appear), we use + the chi-squared test. The following formula is used: - 3. Then, to calculate the margin of error (the amount of times the letter - SHOULD appear with the amount of times the letter DOES appear), we use - the chi-squared test. The following formula is used: + Let: + - n be the number of times the letter actually appears + - p be the predicted value of the number of times the letter should + appear (see item ``2``) + - let v be the chi-squared test result (referred to here as chi-squared + value/statistic) - Let: - - n be the number of times the letter actually appears - - p be the predicted value of the number of times the letter should - appear (see #2) - - let v be the chi-squared test result (referred to here as chi-squared - value/statistic) + :: - (n - p)^2 - --------- = v - p + (n - p)^2 + --------- = v + p 4. Each chi squared value for each letter is then added up to the total. The total is the chi-squared statistic for that encryption key. @@ -98,16 +101,16 @@ def decrypt_caesar_with_chi_squared( to be the decoded answer. Further Reading - ================ + =============== - * http://practicalcryptography.com/cryptanalysis/text-characterisation/chi-squared- - statistic/ + * http://practicalcryptography.com/cryptanalysis/text-characterisation/chi-squared-statistic/ * https://en.wikipedia.org/wiki/Letter_frequency * https://en.wikipedia.org/wiki/Chi-squared_test * https://en.m.wikipedia.org/wiki/Caesar_cipher Doctests ======== + >>> decrypt_caesar_with_chi_squared( ... 'dof pz aol jhlzhy jpwoly zv wvwbshy? pa pz avv lhzf av jyhjr!' ... ) # doctest: +NORMALIZE_WHITESPACE diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index 163aa7172c11..e42fdd82ed41 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -1,14 +1,16 @@ """ -Wikipedia: https://en.wikipedia.org/wiki/Enigma_machine -Video explanation: https://youtu.be/QwQVMqfoB2E -Also check out Numberphile's and Computerphile's videos on this topic +| Wikipedia: https://en.wikipedia.org/wiki/Enigma_machine +| Video explanation: https://youtu.be/QwQVMqfoB2E +| Also check out Numberphile's and Computerphile's videos on this topic -This module contains function 'enigma' which emulates +This module contains function ``enigma`` which emulates the famous Enigma machine from WWII. + Module includes: -- enigma function + +- ``enigma`` function - showcase of function usage -- 9 randomly generated rotors +- ``9`` randomly generated rotors - reflector (aka static rotor) - original alphabet @@ -73,7 +75,7 @@ def _validator( rotpos: RotorPositionT, rotsel: RotorSelectionT, pb: str ) -> tuple[RotorPositionT, RotorSelectionT, dict[str, str]]: """ - Checks if the values can be used for the 'enigma' function + Checks if the values can be used for the ``enigma`` function >>> _validator((1,1,1), (rotor1, rotor2, rotor3), 'POLAND') ((1, 1, 1), ('EGZWVONAHDCLFQMSIPJBYUKXTR', 'FOBHMDKEXQNRAULPGSJVTYICZW', \ @@ -83,7 +85,7 @@ def _validator( :param rotpos: rotor_positon :param rotsel: rotor_selection :param pb: plugb -> validated and transformed - :return: (rotpos, rotsel, pb) + :return: (`rotpos`, `rotsel`, `pb`) """ # Checks if there are 3 unique rotors @@ -118,9 +120,10 @@ def _plugboard(pbstring: str) -> dict[str, str]: >>> _plugboard('POLAND') {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'} - In the code, 'pb' stands for 'plugboard' + In the code, ``pb`` stands for ``plugboard`` Pairs can be separated by spaces + :param pbstring: string containing plugboard setting for the Enigma machine :return: dictionary containing converted pairs """ @@ -168,31 +171,34 @@ def enigma( plugb: str = "", ) -> str: """ - The only difference with real-world enigma is that I allowed string input. + The only difference with real-world enigma is that ``I`` allowed string input. All characters are converted to uppercase. (non-letter symbol are ignored) - How it works: - (for every letter in the message) + + | How it works: + | (for every letter in the message) - Input letter goes into the plugboard. - If it is connected to another one, switch it. + If it is connected to another one, switch it. + + - Letter goes through ``3`` rotors. + Each rotor can be represented as ``2`` sets of symbol, where one is shuffled. + Each symbol from the first set has corresponding symbol in + the second set and vice versa. - - Letter goes through 3 rotors. - Each rotor can be represented as 2 sets of symbol, where one is shuffled. - Each symbol from the first set has corresponding symbol in - the second set and vice versa. + example:: - example: - | ABCDEFGHIJKLMNOPQRSTUVWXYZ | e.g. F=D and D=F - | VKLEPDBGRNWTFCJOHQAMUZYIXS | + | ABCDEFGHIJKLMNOPQRSTUVWXYZ | e.g. F=D and D=F + | VKLEPDBGRNWTFCJOHQAMUZYIXS | - Symbol then goes through reflector (static rotor). - There it is switched with paired symbol - The reflector can be represented as2 sets, each with half of the alphanet. - There are usually 10 pairs of letters. + There it is switched with paired symbol. + The reflector can be represented as ``2`` sets, each with half of the alphanet. + There are usually ``10`` pairs of letters. + + Example:: - Example: - | ABCDEFGHIJKLM | e.g. E is paired to X - | ZYXWVUTSRQPON | so when E goes in X goes out and vice versa + | ABCDEFGHIJKLM | e.g. E is paired to X + | ZYXWVUTSRQPON | so when E goes in X goes out and vice versa - Letter then goes through the rotors again @@ -211,9 +217,9 @@ def enigma( :param text: input message - :param rotor_position: tuple with 3 values in range 1..26 - :param rotor_selection: tuple with 3 rotors () - :param plugb: string containing plugboard configuration (default '') + :param rotor_position: tuple with ``3`` values in range ``1``.. ``26`` + :param rotor_selection: tuple with ``3`` rotors + :param plugb: string containing plugboard configuration (default ``''``) :return: en/decrypted string """ diff --git a/ciphers/rsa_factorization.py b/ciphers/rsa_factorization.py index 0a358a4fc2d4..585b21fac856 100644 --- a/ciphers/rsa_factorization.py +++ b/ciphers/rsa_factorization.py @@ -3,8 +3,10 @@ The program can efficiently factor RSA prime number given the private key d and public key e. -Source: on page 3 of https://crypto.stanford.edu/~dabo/papers/RSA-survey.pdf -More readable source: https://www.di-mgt.com.au/rsa_factorize_n.html + +| Source: on page ``3`` of https://crypto.stanford.edu/~dabo/papers/RSA-survey.pdf +| More readable source: https://www.di-mgt.com.au/rsa_factorize_n.html + large number can take minutes to factor, therefore are not included in doctest. """ @@ -17,13 +19,14 @@ def rsafactor(d: int, e: int, n: int) -> list[int]: """ This function returns the factors of N, where p*q=N - Return: [p, q] + + Return: [p, q] We call N the RSA modulus, e the encryption exponent, and d the decryption exponent. The pair (N, e) is the public key. As its name suggests, it is public and is used to - encrypt messages. + encrypt messages. The pair (N, d) is the secret key or private key and is known only to the recipient - of encrypted messages. + of encrypted messages. >>> rsafactor(3, 16971, 25777) [149, 173] diff --git a/ciphers/simple_keyword_cypher.py b/ciphers/simple_keyword_cypher.py index 9dc624e7762c..bde137d826c3 100644 --- a/ciphers/simple_keyword_cypher.py +++ b/ciphers/simple_keyword_cypher.py @@ -1,9 +1,11 @@ def remove_duplicates(key: str) -> str: """ Removes duplicate alphabetic characters in a keyword (letter is ignored after its - first appearance). + first appearance). + :param key: Keyword to use :return: String with duplicates removed + >>> remove_duplicates('Hello World!!') 'Helo Wrd' """ @@ -18,6 +20,7 @@ def remove_duplicates(key: str) -> str: def create_cipher_map(key: str) -> dict[str, str]: """ Returns a cipher map given a keyword. + :param key: keyword to use :return: dictionary cipher map """ @@ -43,9 +46,11 @@ def create_cipher_map(key: str) -> dict[str, str]: def encipher(message: str, cipher_map: dict[str, str]) -> str: """ Enciphers a message given a cipher map. + :param message: Message to encipher :param cipher_map: Cipher map :return: enciphered string + >>> encipher('Hello World!!', create_cipher_map('Goodbye!!')) 'CYJJM VMQJB!!' """ @@ -55,9 +60,11 @@ def encipher(message: str, cipher_map: dict[str, str]) -> str: def decipher(message: str, cipher_map: dict[str, str]) -> str: """ Deciphers a message given a cipher map + :param message: Message to decipher :param cipher_map: Dictionary mapping to use :return: Deciphered string + >>> cipher_map = create_cipher_map('Goodbye!!') >>> decipher(encipher('Hello World!!', cipher_map), cipher_map) 'HELLO WORLD!!' @@ -70,6 +77,7 @@ def decipher(message: str, cipher_map: dict[str, str]) -> str: def main() -> None: """ Handles I/O + :return: void """ message = input("Enter message to encode or decode: ").strip() diff --git a/ciphers/trifid_cipher.py b/ciphers/trifid_cipher.py index 16b9faf67688..9613cee0669d 100644 --- a/ciphers/trifid_cipher.py +++ b/ciphers/trifid_cipher.py @@ -22,7 +22,7 @@ def __encrypt_part(message_part: str, character_to_number: dict[str, str]) -> str: """ - Arrange the triagram value of each letter of 'message_part' vertically and join + Arrange the triagram value of each letter of `message_part` vertically and join them horizontally. >>> __encrypt_part('ASK', TEST_CHARACTER_TO_NUMBER) @@ -65,8 +65,8 @@ def __prepare( """ A helper function that generates the triagrams and assigns each letter of the alphabet to its corresponding triagram and stores this in a dictionary - ("character_to_number" and "number_to_character") after confirming if the - alphabet's length is 27. + (`character_to_number` and `number_to_character`) after confirming if the + alphabet's length is ``27``. >>> test = __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVwxYZ+') >>> expected = ('IAMABOY','ABCDEFGHIJKLMNOPQRSTUVWXYZ+', @@ -75,24 +75,28 @@ def __prepare( True Testing with incomplete alphabet + >>> __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVw') Traceback (most recent call last): ... KeyError: 'Length of alphabet has to be 27.' Testing with extra long alphabets + >>> __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVwxyzzwwtyyujjgfd') Traceback (most recent call last): ... KeyError: 'Length of alphabet has to be 27.' Testing with punctuations that are not in the given alphabet + >>> __prepare('am i a boy?','abCdeFghijkLmnopqrStuVwxYZ+') Traceback (most recent call last): ... ValueError: Each message character has to be included in alphabet! Testing with numbers + >>> __prepare(500,'abCdeFghijkLmnopqrStuVwxYZ+') Traceback (most recent call last): ... @@ -130,9 +134,9 @@ def encrypt_message( PARAMETERS ---------- - * message: The message you want to encrypt. - * alphabet (optional): The characters to be used for the cipher . - * period (optional): The number of characters you want in a group whilst + * `message`: The message you want to encrypt. + * `alphabet` (optional): The characters to be used for the cipher . + * `period` (optional): The number of characters you want in a group whilst encrypting. >>> encrypt_message('I am a boy') @@ -169,20 +173,21 @@ def decrypt_message( decrypt_message =============== - Decrypts a trifid_cipher encrypted message . + Decrypts a trifid_cipher encrypted message. PARAMETERS ---------- - * message: The message you want to decrypt . - * alphabet (optional): The characters used for the cipher. - * period (optional): The number of characters used in grouping when it + * `message`: The message you want to decrypt. + * `alphabet` (optional): The characters used for the cipher. + * `period` (optional): The number of characters used in grouping when it was encrypted. >>> decrypt_message('BCDGBQY') 'IAMABOY' Decrypting with your own alphabet and period + >>> decrypt_message('FMJFVOISSUFTFPUFEQQC','FELIXMARDSTBCGHJKNOPQUVWYZ+',5) 'AIDETOILECIELTAIDERA' """ From 68b4c6b4793867126f71ebf2a399402b02472edb Mon Sep 17 00:00:00 2001 From: mahdi tavasoli Date: Mon, 30 Dec 2024 13:52:20 +0330 Subject: [PATCH 045/126] fix is_ip_v4_address_valid.py (#12394) * fix is_ip_v4_address_valid * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update is_ip_v4_address_valid.py --------- Co-authored-by: m.tavasoli Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- maths/is_ip_v4_address_valid.py | 37 +++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/maths/is_ip_v4_address_valid.py b/maths/is_ip_v4_address_valid.py index 0ae8e021ead1..305afabffed3 100644 --- a/maths/is_ip_v4_address_valid.py +++ b/maths/is_ip_v4_address_valid.py @@ -1,13 +1,15 @@ """ +wiki: https://en.wikipedia.org/wiki/IPv4 + Is IP v4 address valid? A valid IP address must be four octets in the form of A.B.C.D, -where A,B,C and D are numbers from 0-254 -for example: 192.168.23.1, 172.254.254.254 are valid IP address - 192.168.255.0, 255.192.3.121 are invalid IP address +where A, B, C and D are numbers from 0-255 +for example: 192.168.23.1, 172.255.255.255 are valid IP address + 192.168.256.0, 256.192.3.121 are invalid IP address """ -def is_ip_v4_address_valid(ip_v4_address: str) -> bool: +def is_ip_v4_address_valid(ip: str) -> bool: """ print "Valid IP address" If IP is valid. or @@ -16,13 +18,13 @@ def is_ip_v4_address_valid(ip_v4_address: str) -> bool: >>> is_ip_v4_address_valid("192.168.0.23") True - >>> is_ip_v4_address_valid("192.255.15.8") + >>> is_ip_v4_address_valid("192.256.15.8") False >>> is_ip_v4_address_valid("172.100.0.8") True - >>> is_ip_v4_address_valid("254.255.0.255") + >>> is_ip_v4_address_valid("255.256.0.256") False >>> is_ip_v4_address_valid("1.2.33333333.4") @@ -45,12 +47,29 @@ def is_ip_v4_address_valid(ip_v4_address: str) -> bool: >>> is_ip_v4_address_valid("1.2.3.") False + + >>> is_ip_v4_address_valid("1.2.3.05") + False """ - octets = [int(i) for i in ip_v4_address.split(".") if i.isdigit()] - return len(octets) == 4 and all(0 <= int(octet) <= 254 for octet in octets) + octets = ip.split(".") + if len(octets) != 4: + return False + + for octet in octets: + if not octet.isdigit(): + return False + + number = int(octet) + if len(str(number)) != len(octet): + return False + + if not 0 <= number <= 255: + return False + + return True if __name__ == "__main__": ip = input().strip() valid_or_invalid = "valid" if is_ip_v4_address_valid(ip) else "invalid" - print(f"{ip} is a {valid_or_invalid} IP v4 address.") + print(f"{ip} is a {valid_or_invalid} IPv4 address.") From 2ca96b7c8ec2134a7282bed13f1cc93358c13c45 Mon Sep 17 00:00:00 2001 From: jperezr <122382210+MRJPEREZR@users.noreply.github.com> Date: Mon, 30 Dec 2024 11:37:21 +0100 Subject: [PATCH 046/126] current_stock_price test added (#12390) * adding test to web_programming/current_stock_price * adding test to web_programming/current_stock_price * Update current_stock_price.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update current_stock_price.py --------- Co-authored-by: Maxim Smolskiy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- web_programming/current_stock_price.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/web_programming/current_stock_price.py b/web_programming/current_stock_price.py index 9567c05b0558..d0a65e9aac84 100644 --- a/web_programming/current_stock_price.py +++ b/web_programming/current_stock_price.py @@ -1,14 +1,30 @@ import requests from bs4 import BeautifulSoup +""" +Get the HTML code of finance yahoo and select the current qsp-price +Current AAPL stock price is 228.43 +Current AMZN stock price is 201.85 +Current IBM stock price is 210.30 +Current GOOG stock price is 177.86 +Current MSFT stock price is 414.82 +Current ORCL stock price is 188.87 +""" + def stock_price(symbol: str = "AAPL") -> str: + """ + >>> stock_price("EEEE") + '-' + >>> isinstance(float(stock_price("GOOG")),float) + True + """ url = f"https://finance.yahoo.com/quote/%7Bsymbol%7D?p={symbol}" yahoo_finance_source = requests.get( url, headers={"USER-AGENT": "Mozilla/5.0"}, timeout=10 ).text soup = BeautifulSoup(yahoo_finance_source, "html.parser") - specific_fin_streamer_tag = soup.find("fin-streamer", {"data-test": "qsp-price"}) + specific_fin_streamer_tag = soup.find("fin-streamer", {"data-testid": "qsp-price"}) if specific_fin_streamer_tag: text = specific_fin_streamer_tag.get_text() @@ -18,5 +34,9 @@ def stock_price(symbol: str = "AAPL") -> str: # Search for the symbol at https://finance.yahoo.com/lookup if __name__ == "__main__": + from doctest import testmod + + testmod() + for symbol in "AAPL AMZN IBM GOOG MSFT ORCL".split(): print(f"Current {symbol:<4} stock price is {stock_price(symbol):>8}") From 24923ee635973a05f7713dd672fea07361fa0466 Mon Sep 17 00:00:00 2001 From: Shikhar Maheshwari <83123897+shikhar-sm@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:21:10 +0530 Subject: [PATCH 047/126] Add doctest to maths/numerical_analysis/intersection.py (#12148) --- maths/numerical_analysis/intersection.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/maths/numerical_analysis/intersection.py b/maths/numerical_analysis/intersection.py index 826c0ead0a00..325abeaca996 100644 --- a/maths/numerical_analysis/intersection.py +++ b/maths/numerical_analysis/intersection.py @@ -42,6 +42,11 @@ def intersection(function: Callable[[float], float], x0: float, x1: float) -> fl def f(x: float) -> float: + """ + function is f(x) = x^3 - 2x - 5 + >>> f(2) + -1.0 + """ return math.pow(x, 3) - (2 * x) - 5 From da587d06ac88e338e7db8f10fa8ca2ae556e7bae Mon Sep 17 00:00:00 2001 From: Sankabapur <152031570+Parthjhalani07@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:29:03 +0530 Subject: [PATCH 048/126] Added doctest to /maths/power_using_recursion.py (#11994) --- maths/power_using_recursion.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/maths/power_using_recursion.py b/maths/power_using_recursion.py index 29283ca0f67c..eb775b161ae8 100644 --- a/maths/power_using_recursion.py +++ b/maths/power_using_recursion.py @@ -38,6 +38,14 @@ def power(base: int, exponent: int) -> float: Traceback (most recent call last): ... RecursionError: maximum recursion depth exceeded + >>> power(0, 0) + 1 + >>> power(0, 1) + 0 + >>> power(5,6) + 15625 + >>> power(23, 12) + 21914624432020321 """ return base * power(base, (exponent - 1)) if exponent else 1 From 493a7c153c1ca1805e60c109842ee1a1ee63cde2 Mon Sep 17 00:00:00 2001 From: Jeel Rupapara Date: Mon, 30 Dec 2024 16:40:44 +0530 Subject: [PATCH 049/126] feat: add testcase of assemble_transformation (#11810) --- strings/min_cost_string_conversion.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/strings/min_cost_string_conversion.py b/strings/min_cost_string_conversion.py index 93791e2a7ed3..87eb5189e16a 100644 --- a/strings/min_cost_string_conversion.py +++ b/strings/min_cost_string_conversion.py @@ -91,6 +91,14 @@ def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]: >>> y1 = len(ops1[0]) - 1 >>> assemble_transformation(ops1, x1, y1) [] + + >>> ops2 = [['0', 'I1', 'I2', 'I3'], + ... ['D1', 'C1', 'I2', 'I3'], + ... ['D2', 'D2', 'R23', 'R23']] + >>> x2 = len(ops2) - 1 + >>> y2 = len(ops2[0]) - 1 + >>> assemble_transformation(ops2, x2, y2) + ['C1', 'I2', 'R23'] """ if i == 0 and j == 0: return [] From 7fa9b4bf1bc9822517bb0046aebc2e8b2997d3e1 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 30 Dec 2024 14:52:03 +0300 Subject: [PATCH 050/126] Fix sphinx/build_docs warnings for dynamic_programming (#12484) * Fix sphinx/build_docs warnings for dynamic_programming * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * Fix * Fix * Fix * Fix * Fix --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- dynamic_programming/all_construct.py | 7 +- dynamic_programming/combination_sum_iv.py | 23 +- dynamic_programming/fizz_buzz.py | 11 +- dynamic_programming/knapsack.py | 40 ++-- .../longest_common_substring.py | 14 +- .../longest_increasing_subsequence.py | 13 +- .../matrix_chain_multiplication.py | 89 ++++---- dynamic_programming/max_product_subarray.py | 3 +- .../minimum_squares_to_represent_a_number.py | 1 + dynamic_programming/regex_match.py | 22 +- dynamic_programming/rod_cutting.py | 83 ++++--- dynamic_programming/subset_generation.py | 63 +++--- dynamic_programming/viterbi.py | 212 ++++++++---------- 13 files changed, 295 insertions(+), 286 deletions(-) diff --git a/dynamic_programming/all_construct.py b/dynamic_programming/all_construct.py index 5d585fc7fcec..ca00f2beb06a 100644 --- a/dynamic_programming/all_construct.py +++ b/dynamic_programming/all_construct.py @@ -8,9 +8,10 @@ def all_construct(target: str, word_bank: list[str] | None = None) -> list[list[str]]: """ - returns the list containing all the possible - combinations a string(target) can be constructed from - the given list of substrings(word_bank) + returns the list containing all the possible + combinations a string(`target`) can be constructed from + the given list of substrings(`word_bank`) + >>> all_construct("hello", ["he", "l", "o"]) [['he', 'l', 'l', 'o']] >>> all_construct("purple",["purp","p","ur","le","purpl"]) diff --git a/dynamic_programming/combination_sum_iv.py b/dynamic_programming/combination_sum_iv.py index 113c06a27a9e..ed8dcd88e6fd 100644 --- a/dynamic_programming/combination_sum_iv.py +++ b/dynamic_programming/combination_sum_iv.py @@ -1,24 +1,25 @@ """ Question: -You are given an array of distinct integers and you have to tell how many -different ways of selecting the elements from the array are there such that -the sum of chosen elements is equal to the target number tar. + You are given an array of distinct integers and you have to tell how many + different ways of selecting the elements from the array are there such that + the sum of chosen elements is equal to the target number tar. Example Input: -N = 3 -target = 5 -array = [1, 2, 5] + * N = 3 + * target = 5 + * array = [1, 2, 5] Output: -9 + 9 Approach: -The basic idea is to go over recursively to find the way such that the sum -of chosen elements is “tar”. For every element, we have two choices - 1. Include the element in our set of chosen elements. - 2. Don't include the element in our set of chosen elements. + The basic idea is to go over recursively to find the way such that the sum + of chosen elements is `target`. For every element, we have two choices + + 1. Include the element in our set of chosen elements. + 2. Don't include the element in our set of chosen elements. """ diff --git a/dynamic_programming/fizz_buzz.py b/dynamic_programming/fizz_buzz.py index e29116437a93..0cb48897875b 100644 --- a/dynamic_programming/fizz_buzz.py +++ b/dynamic_programming/fizz_buzz.py @@ -3,11 +3,12 @@ def fizz_buzz(number: int, iterations: int) -> str: """ - Plays FizzBuzz. - Prints Fizz if number is a multiple of 3. - Prints Buzz if its a multiple of 5. - Prints FizzBuzz if its a multiple of both 3 and 5 or 15. - Else Prints The Number Itself. + | Plays FizzBuzz. + | Prints Fizz if number is a multiple of ``3``. + | Prints Buzz if its a multiple of ``5``. + | Prints FizzBuzz if its a multiple of both ``3`` and ``5`` or ``15``. + | Else Prints The Number Itself. + >>> fizz_buzz(1,7) '1 2 Fizz 4 Buzz Fizz 7 ' >>> fizz_buzz(1,0) diff --git a/dynamic_programming/knapsack.py b/dynamic_programming/knapsack.py index 489b5ada450a..28c5b19dbe36 100644 --- a/dynamic_programming/knapsack.py +++ b/dynamic_programming/knapsack.py @@ -11,7 +11,7 @@ def mf_knapsack(i, wt, val, j): """ This code involves the concept of memory functions. Here we solve the subproblems which are needed unlike the below example - F is a 2D array with -1s filled up + F is a 2D array with ``-1`` s filled up """ global f # a global dp table for knapsack if f[i][j] < 0: @@ -45,22 +45,24 @@ def knapsack_with_example_solution(w: int, wt: list, val: list): the several possible optimal subsets. Parameters - --------- + ---------- - W: int, the total maximum weight for the given knapsack problem. - wt: list, the vector of weights for all items where wt[i] is the weight - of the i-th item. - val: list, the vector of values for all items where val[i] is the value - of the i-th item + * `w`: int, the total maximum weight for the given knapsack problem. + * `wt`: list, the vector of weights for all items where ``wt[i]`` is the weight + of the ``i``-th item. + * `val`: list, the vector of values for all items where ``val[i]`` is the value + of the ``i``-th item Returns ------- - optimal_val: float, the optimal value for the given knapsack problem - example_optional_set: set, the indices of one of the optimal subsets - which gave rise to the optimal value. + + * `optimal_val`: float, the optimal value for the given knapsack problem + * `example_optional_set`: set, the indices of one of the optimal subsets + which gave rise to the optimal value. Examples - ------- + -------- + >>> knapsack_with_example_solution(10, [1, 3, 5, 2], [10, 20, 100, 22]) (142, {2, 3, 4}) >>> knapsack_with_example_solution(6, [4, 3, 2, 3], [3, 2, 4, 4]) @@ -104,19 +106,19 @@ def _construct_solution(dp: list, wt: list, i: int, j: int, optimal_set: set): a filled DP table and the vector of weights Parameters - --------- - - dp: list of list, the table of a solved integer weight dynamic programming problem + ---------- - wt: list or tuple, the vector of weights of the items - i: int, the index of the item under consideration - j: int, the current possible maximum weight - optimal_set: set, the optimal subset so far. This gets modified by the function. + * `dp`: list of list, the table of a solved integer weight dynamic programming + problem + * `wt`: list or tuple, the vector of weights of the items + * `i`: int, the index of the item under consideration + * `j`: int, the current possible maximum weight + * `optimal_set`: set, the optimal subset so far. This gets modified by the function. Returns ------- - None + ``None`` """ # for the current item i at a maximum weight j to be part of an optimal subset, # the optimal value at (i, j) must be greater than the optimal value at (i-1, j). diff --git a/dynamic_programming/longest_common_substring.py b/dynamic_programming/longest_common_substring.py index e2f944a5e336..ea5233eb2d17 100644 --- a/dynamic_programming/longest_common_substring.py +++ b/dynamic_programming/longest_common_substring.py @@ -1,15 +1,19 @@ """ -Longest Common Substring Problem Statement: Given two sequences, find the -longest common substring present in both of them. A substring is -necessarily continuous. -Example: "abcdef" and "xabded" have two longest common substrings, "ab" or "de". -Therefore, algorithm should return any one of them. +Longest Common Substring Problem Statement: + Given two sequences, find the + longest common substring present in both of them. A substring is + necessarily continuous. + +Example: + ``abcdef`` and ``xabded`` have two longest common substrings, ``ab`` or ``de``. + Therefore, algorithm should return any one of them. """ def longest_common_substring(text1: str, text2: str) -> str: """ Finds the longest common substring between two strings. + >>> longest_common_substring("", "") '' >>> longest_common_substring("a","") diff --git a/dynamic_programming/longest_increasing_subsequence.py b/dynamic_programming/longest_increasing_subsequence.py index 2a78e2e7ad1d..d839757f6da5 100644 --- a/dynamic_programming/longest_increasing_subsequence.py +++ b/dynamic_programming/longest_increasing_subsequence.py @@ -4,11 +4,13 @@ This is a pure Python implementation of Dynamic Programming solution to the longest increasing subsequence of a given sequence. -The problem is : -Given an array, to find the longest and increasing sub-array in that given array and -return it. -Example: [10, 22, 9, 33, 21, 50, 41, 60, 80] as input will return - [10, 22, 33, 41, 60, 80] as output +The problem is: + Given an array, to find the longest and increasing sub-array in that given array and + return it. + +Example: + ``[10, 22, 9, 33, 21, 50, 41, 60, 80]`` as input will return + ``[10, 22, 33, 41, 60, 80]`` as output """ from __future__ import annotations @@ -17,6 +19,7 @@ def longest_subsequence(array: list[int]) -> list[int]: # This function is recursive """ Some examples + >>> longest_subsequence([10, 22, 9, 33, 21, 50, 41, 60, 80]) [10, 22, 33, 41, 60, 80] >>> longest_subsequence([4, 8, 7, 5, 1, 12, 2, 3, 9]) diff --git a/dynamic_programming/matrix_chain_multiplication.py b/dynamic_programming/matrix_chain_multiplication.py index da6e525ce816..10e136b9f0db 100644 --- a/dynamic_programming/matrix_chain_multiplication.py +++ b/dynamic_programming/matrix_chain_multiplication.py @@ -1,42 +1,48 @@ """ -Find the minimum number of multiplications needed to multiply chain of matrices. -Reference: https://www.geeksforgeeks.org/matrix-chain-multiplication-dp-8/ +| Find the minimum number of multiplications needed to multiply chain of matrices. +| Reference: https://www.geeksforgeeks.org/matrix-chain-multiplication-dp-8/ -The algorithm has interesting real-world applications. Example: -1. Image transformations in Computer Graphics as images are composed of matrix. -2. Solve complex polynomial equations in the field of algebra using least processing - power. -3. Calculate overall impact of macroeconomic decisions as economic equations involve a - number of variables. -4. Self-driving car navigation can be made more accurate as matrix multiplication can - accurately determine position and orientation of obstacles in short time. +The algorithm has interesting real-world applications. -Python doctests can be run with the following command: -python -m doctest -v matrix_chain_multiply.py +Example: + 1. Image transformations in Computer Graphics as images are composed of matrix. + 2. Solve complex polynomial equations in the field of algebra using least processing + power. + 3. Calculate overall impact of macroeconomic decisions as economic equations involve a + number of variables. + 4. Self-driving car navigation can be made more accurate as matrix multiplication can + accurately determine position and orientation of obstacles in short time. -Given a sequence arr[] that represents chain of 2D matrices such that the dimension of -the ith matrix is arr[i-1]*arr[i]. -So suppose arr = [40, 20, 30, 10, 30] means we have 4 matrices of dimensions -40*20, 20*30, 30*10 and 10*30. +Python doctests can be run with the following command:: -matrix_chain_multiply() returns an integer denoting minimum number of multiplications to -multiply the chain. + python -m doctest -v matrix_chain_multiply.py + +Given a sequence ``arr[]`` that represents chain of 2D matrices such that the dimension +of the ``i`` th matrix is ``arr[i-1]*arr[i]``. +So suppose ``arr = [40, 20, 30, 10, 30]`` means we have ``4`` matrices of dimensions +``40*20``, ``20*30``, ``30*10`` and ``10*30``. + +``matrix_chain_multiply()`` returns an integer denoting minimum number of +multiplications to multiply the chain. We do not need to perform actual multiplication here. We only need to decide the order in which to perform the multiplication. Hints: -1. Number of multiplications (ie cost) to multiply 2 matrices -of size m*p and p*n is m*p*n. -2. Cost of matrix multiplication is associative ie (M1*M2)*M3 != M1*(M2*M3) -3. Matrix multiplication is not commutative. So, M1*M2 does not mean M2*M1 can be done. -4. To determine the required order, we can try different combinations. + 1. Number of multiplications (ie cost) to multiply ``2`` matrices + of size ``m*p`` and ``p*n`` is ``m*p*n``. + 2. Cost of matrix multiplication is not associative ie ``(M1*M2)*M3 != M1*(M2*M3)`` + 3. Matrix multiplication is not commutative. So, ``M1*M2`` does not mean ``M2*M1`` + can be done. + 4. To determine the required order, we can try different combinations. + So, this problem has overlapping sub-problems and can be solved using recursion. We use Dynamic Programming for optimal time complexity. Example input: -arr = [40, 20, 30, 10, 30] -output: 26000 + ``arr = [40, 20, 30, 10, 30]`` +output: + ``26000`` """ from collections.abc import Iterator @@ -50,25 +56,25 @@ def matrix_chain_multiply(arr: list[int]) -> int: Find the minimum number of multiplcations required to multiply the chain of matrices Args: - arr: The input array of integers. + `arr`: The input array of integers. Returns: Minimum number of multiplications needed to multiply the chain Examples: - >>> matrix_chain_multiply([1, 2, 3, 4, 3]) - 30 - >>> matrix_chain_multiply([10]) - 0 - >>> matrix_chain_multiply([10, 20]) - 0 - >>> matrix_chain_multiply([19, 2, 19]) - 722 - >>> matrix_chain_multiply(list(range(1, 100))) - 323398 - - # >>> matrix_chain_multiply(list(range(1, 251))) - # 2626798 + + >>> matrix_chain_multiply([1, 2, 3, 4, 3]) + 30 + >>> matrix_chain_multiply([10]) + 0 + >>> matrix_chain_multiply([10, 20]) + 0 + >>> matrix_chain_multiply([19, 2, 19]) + 722 + >>> matrix_chain_multiply(list(range(1, 100))) + 323398 + >>> # matrix_chain_multiply(list(range(1, 251))) + # 2626798 """ if len(arr) < 2: return 0 @@ -93,8 +99,10 @@ def matrix_chain_multiply(arr: list[int]) -> int: def matrix_chain_order(dims: list[int]) -> int: """ Source: https://en.wikipedia.org/wiki/Matrix_chain_multiplication + The dynamic programming solution is faster than cached the recursive solution and can handle larger inputs. + >>> matrix_chain_order([1, 2, 3, 4, 3]) 30 >>> matrix_chain_order([10]) @@ -105,8 +113,7 @@ def matrix_chain_order(dims: list[int]) -> int: 722 >>> matrix_chain_order(list(range(1, 100))) 323398 - - # >>> matrix_chain_order(list(range(1, 251))) # Max before RecursionError is raised + >>> # matrix_chain_order(list(range(1, 251))) # Max before RecursionError is raised # 2626798 """ diff --git a/dynamic_programming/max_product_subarray.py b/dynamic_programming/max_product_subarray.py index 425859bc03e3..6f4f38e38942 100644 --- a/dynamic_programming/max_product_subarray.py +++ b/dynamic_programming/max_product_subarray.py @@ -1,9 +1,10 @@ def max_product_subarray(numbers: list[int]) -> int: """ Returns the maximum product that can be obtained by multiplying a - contiguous subarray of the given integer list `nums`. + contiguous subarray of the given integer list `numbers`. Example: + >>> max_product_subarray([2, 3, -2, 4]) 6 >>> max_product_subarray((-2, 0, -1)) diff --git a/dynamic_programming/minimum_squares_to_represent_a_number.py b/dynamic_programming/minimum_squares_to_represent_a_number.py index bf5849f5bcb3..98c0602fa831 100644 --- a/dynamic_programming/minimum_squares_to_represent_a_number.py +++ b/dynamic_programming/minimum_squares_to_represent_a_number.py @@ -5,6 +5,7 @@ def minimum_squares_to_represent_a_number(number: int) -> int: """ Count the number of minimum squares to represent a number + >>> minimum_squares_to_represent_a_number(25) 1 >>> minimum_squares_to_represent_a_number(37) diff --git a/dynamic_programming/regex_match.py b/dynamic_programming/regex_match.py index 200a882831c0..e94d82093c8b 100644 --- a/dynamic_programming/regex_match.py +++ b/dynamic_programming/regex_match.py @@ -1,23 +1,25 @@ """ Regex matching check if a text matches pattern or not. Pattern: - '.' Matches any single character. - '*' Matches zero or more of the preceding element. + + 1. ``.`` Matches any single character. + 2. ``*`` Matches zero or more of the preceding element. + More info: https://medium.com/trick-the-interviwer/regular-expression-matching-9972eb74c03 """ def recursive_match(text: str, pattern: str) -> bool: - """ + r""" Recursive matching algorithm. - Time complexity: O(2 ^ (|text| + |pattern|)) - Space complexity: Recursion depth is O(|text| + |pattern|). + | Time complexity: O(2^(\|text\| + \|pattern\|)) + | Space complexity: Recursion depth is O(\|text\| + \|pattern\|). :param text: Text to match. :param pattern: Pattern to match. - :return: True if text matches pattern, False otherwise. + :return: ``True`` if `text` matches `pattern`, ``False`` otherwise. >>> recursive_match('abc', 'a.c') True @@ -48,15 +50,15 @@ def recursive_match(text: str, pattern: str) -> bool: def dp_match(text: str, pattern: str) -> bool: - """ + r""" Dynamic programming matching algorithm. - Time complexity: O(|text| * |pattern|) - Space complexity: O(|text| * |pattern|) + | Time complexity: O(\|text\| * \|pattern\|) + | Space complexity: O(\|text\| * \|pattern\|) :param text: Text to match. :param pattern: Pattern to match. - :return: True if text matches pattern, False otherwise. + :return: ``True`` if `text` matches `pattern`, ``False`` otherwise. >>> dp_match('abc', 'a.c') True diff --git a/dynamic_programming/rod_cutting.py b/dynamic_programming/rod_cutting.py index f80fa440ae86..d12c759dc928 100644 --- a/dynamic_programming/rod_cutting.py +++ b/dynamic_programming/rod_cutting.py @@ -1,7 +1,7 @@ """ This module provides two implementations for the rod-cutting problem: -1. A naive recursive implementation which has an exponential runtime -2. Two dynamic programming implementations which have quadratic runtime + 1. A naive recursive implementation which has an exponential runtime + 2. Two dynamic programming implementations which have quadratic runtime The rod-cutting problem is the problem of finding the maximum possible revenue obtainable from a rod of length ``n`` given a list of prices for each integral piece @@ -20,18 +20,21 @@ def naive_cut_rod_recursive(n: int, prices: list): Runtime: O(2^n) Arguments - ------- - n: int, the length of the rod - prices: list, the prices for each piece of rod. ``p[i-i]`` is the - price for a rod of length ``i`` + --------- + + * `n`: int, the length of the rod + * `prices`: list, the prices for each piece of rod. ``p[i-i]`` is the + price for a rod of length ``i`` Returns ------- - The maximum revenue obtainable for a rod of length n given the list of prices + + The maximum revenue obtainable for a rod of length `n` given the list of prices for each piece. Examples -------- + >>> naive_cut_rod_recursive(4, [1, 5, 8, 9]) 10 >>> naive_cut_rod_recursive(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]) @@ -54,28 +57,30 @@ def top_down_cut_rod(n: int, prices: list): """ Constructs a top-down dynamic programming solution for the rod-cutting problem via memoization. This function serves as a wrapper for - _top_down_cut_rod_recursive + ``_top_down_cut_rod_recursive`` Runtime: O(n^2) Arguments - -------- - n: int, the length of the rod - prices: list, the prices for each piece of rod. ``p[i-i]`` is the - price for a rod of length ``i`` + --------- - Note - ---- - For convenience and because Python's lists using 0-indexing, length(max_rev) = - n + 1, to accommodate for the revenue obtainable from a rod of length 0. + * `n`: int, the length of the rod + * `prices`: list, the prices for each piece of rod. ``p[i-i]`` is the + price for a rod of length ``i`` + + .. note:: + For convenience and because Python's lists using ``0``-indexing, ``length(max_rev) + = n + 1``, to accommodate for the revenue obtainable from a rod of length ``0``. Returns ------- - The maximum revenue obtainable for a rod of length n given the list of prices + + The maximum revenue obtainable for a rod of length `n` given the list of prices for each piece. Examples - ------- + -------- + >>> top_down_cut_rod(4, [1, 5, 8, 9]) 10 >>> top_down_cut_rod(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]) @@ -94,16 +99,18 @@ def _top_down_cut_rod_recursive(n: int, prices: list, max_rev: list): Runtime: O(n^2) Arguments - -------- - n: int, the length of the rod - prices: list, the prices for each piece of rod. ``p[i-i]`` is the - price for a rod of length ``i`` - max_rev: list, the computed maximum revenue for a piece of rod. - ``max_rev[i]`` is the maximum revenue obtainable for a rod of length ``i`` + --------- + + * `n`: int, the length of the rod + * `prices`: list, the prices for each piece of rod. ``p[i-i]`` is the + price for a rod of length ``i`` + * `max_rev`: list, the computed maximum revenue for a piece of rod. + ``max_rev[i]`` is the maximum revenue obtainable for a rod of length ``i`` Returns ------- - The maximum revenue obtainable for a rod of length n given the list of prices + + The maximum revenue obtainable for a rod of length `n` given the list of prices for each piece. """ if max_rev[n] >= 0: @@ -130,18 +137,21 @@ def bottom_up_cut_rod(n: int, prices: list): Runtime: O(n^2) Arguments - ---------- - n: int, the maximum length of the rod. - prices: list, the prices for each piece of rod. ``p[i-i]`` is the - price for a rod of length ``i`` + --------- + + * `n`: int, the maximum length of the rod. + * `prices`: list, the prices for each piece of rod. ``p[i-i]`` is the + price for a rod of length ``i`` Returns ------- - The maximum revenue obtainable from cutting a rod of length n given + + The maximum revenue obtainable from cutting a rod of length `n` given the prices for each piece of rod p. Examples - ------- + -------- + >>> bottom_up_cut_rod(4, [1, 5, 8, 9]) 10 >>> bottom_up_cut_rod(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]) @@ -168,13 +178,12 @@ def _enforce_args(n: int, prices: list): """ Basic checks on the arguments to the rod-cutting algorithms - n: int, the length of the rod - prices: list, the price list for each piece of rod. - - Throws ValueError: + * `n`: int, the length of the rod + * `prices`: list, the price list for each piece of rod. - if n is negative or there are fewer items in the price list than the length of - the rod + Throws ``ValueError``: + if `n` is negative or there are fewer items in the price list than the length of + the rod """ if n < 0: msg = f"n must be greater than or equal to 0. Got n = {n}" diff --git a/dynamic_programming/subset_generation.py b/dynamic_programming/subset_generation.py index d490bca737ba..08daaac6f88a 100644 --- a/dynamic_programming/subset_generation.py +++ b/dynamic_programming/subset_generation.py @@ -1,38 +1,41 @@ def subset_combinations(elements: list[int], n: int) -> list: """ Compute n-element combinations from a given list using dynamic programming. + Args: - elements: The list of elements from which combinations will be generated. - n: The number of elements in each combination. + * `elements`: The list of elements from which combinations will be generated. + * `n`: The number of elements in each combination. + Returns: - A list of tuples, each representing a combination of n elements. - >>> subset_combinations(elements=[10, 20, 30, 40], n=2) - [(10, 20), (10, 30), (10, 40), (20, 30), (20, 40), (30, 40)] - >>> subset_combinations(elements=[1, 2, 3], n=1) - [(1,), (2,), (3,)] - >>> subset_combinations(elements=[1, 2, 3], n=3) - [(1, 2, 3)] - >>> subset_combinations(elements=[42], n=1) - [(42,)] - >>> subset_combinations(elements=[6, 7, 8, 9], n=4) - [(6, 7, 8, 9)] - >>> subset_combinations(elements=[10, 20, 30, 40, 50], n=0) - [()] - >>> subset_combinations(elements=[1, 2, 3, 4], n=2) - [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)] - >>> subset_combinations(elements=[1, 'apple', 3.14], n=2) - [(1, 'apple'), (1, 3.14), ('apple', 3.14)] - >>> subset_combinations(elements=['single'], n=0) - [()] - >>> subset_combinations(elements=[], n=9) - [] - >>> from itertools import combinations - >>> all(subset_combinations(items, n) == list(combinations(items, n)) - ... for items, n in ( - ... ([10, 20, 30, 40], 2), ([1, 2, 3], 1), ([1, 2, 3], 3), ([42], 1), - ... ([6, 7, 8, 9], 4), ([10, 20, 30, 40, 50], 1), ([1, 2, 3, 4], 2), - ... ([1, 'apple', 3.14], 2), (['single'], 0), ([], 9))) - True + A list of tuples, each representing a combination of `n` elements. + + >>> subset_combinations(elements=[10, 20, 30, 40], n=2) + [(10, 20), (10, 30), (10, 40), (20, 30), (20, 40), (30, 40)] + >>> subset_combinations(elements=[1, 2, 3], n=1) + [(1,), (2,), (3,)] + >>> subset_combinations(elements=[1, 2, 3], n=3) + [(1, 2, 3)] + >>> subset_combinations(elements=[42], n=1) + [(42,)] + >>> subset_combinations(elements=[6, 7, 8, 9], n=4) + [(6, 7, 8, 9)] + >>> subset_combinations(elements=[10, 20, 30, 40, 50], n=0) + [()] + >>> subset_combinations(elements=[1, 2, 3, 4], n=2) + [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)] + >>> subset_combinations(elements=[1, 'apple', 3.14], n=2) + [(1, 'apple'), (1, 3.14), ('apple', 3.14)] + >>> subset_combinations(elements=['single'], n=0) + [()] + >>> subset_combinations(elements=[], n=9) + [] + >>> from itertools import combinations + >>> all(subset_combinations(items, n) == list(combinations(items, n)) + ... for items, n in ( + ... ([10, 20, 30, 40], 2), ([1, 2, 3], 1), ([1, 2, 3], 3), ([42], 1), + ... ([6, 7, 8, 9], 4), ([10, 20, 30, 40, 50], 1), ([1, 2, 3, 4], 2), + ... ([1, 'apple', 3.14], 2), (['single'], 0), ([], 9))) + True """ r = len(elements) if n > r: diff --git a/dynamic_programming/viterbi.py b/dynamic_programming/viterbi.py index 764d45dc2c05..5b78fa9e46d0 100644 --- a/dynamic_programming/viterbi.py +++ b/dynamic_programming/viterbi.py @@ -9,119 +9,102 @@ def viterbi( emission_probabilities: dict, ) -> list: """ - Viterbi Algorithm, to find the most likely path of - states from the start and the expected output. - https://en.wikipedia.org/wiki/Viterbi_algorithm - sdafads - Wikipedia example - >>> observations = ["normal", "cold", "dizzy"] - >>> states = ["Healthy", "Fever"] - >>> start_p = {"Healthy": 0.6, "Fever": 0.4} - >>> trans_p = { - ... "Healthy": {"Healthy": 0.7, "Fever": 0.3}, - ... "Fever": {"Healthy": 0.4, "Fever": 0.6}, - ... } - >>> emit_p = { - ... "Healthy": {"normal": 0.5, "cold": 0.4, "dizzy": 0.1}, - ... "Fever": {"normal": 0.1, "cold": 0.3, "dizzy": 0.6}, - ... } - >>> viterbi(observations, states, start_p, trans_p, emit_p) - ['Healthy', 'Healthy', 'Fever'] + Viterbi Algorithm, to find the most likely path of + states from the start and the expected output. - >>> viterbi((), states, start_p, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: There's an empty parameter - - >>> viterbi(observations, (), start_p, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: There's an empty parameter - - >>> viterbi(observations, states, {}, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: There's an empty parameter - - >>> viterbi(observations, states, start_p, {}, emit_p) - Traceback (most recent call last): - ... - ValueError: There's an empty parameter - - >>> viterbi(observations, states, start_p, trans_p, {}) - Traceback (most recent call last): - ... - ValueError: There's an empty parameter - - >>> viterbi("invalid", states, start_p, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: observations_space must be a list + https://en.wikipedia.org/wiki/Viterbi_algorithm - >>> viterbi(["valid", 123], states, start_p, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: observations_space must be a list of strings - - >>> viterbi(observations, "invalid", start_p, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: states_space must be a list - - >>> viterbi(observations, ["valid", 123], start_p, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: states_space must be a list of strings - - >>> viterbi(observations, states, "invalid", trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: initial_probabilities must be a dict - - >>> viterbi(observations, states, {2:2}, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: initial_probabilities all keys must be strings - - >>> viterbi(observations, states, {"a":2}, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: initial_probabilities all values must be float + Wikipedia example - >>> viterbi(observations, states, start_p, "invalid", emit_p) - Traceback (most recent call last): - ... - ValueError: transition_probabilities must be a dict - - >>> viterbi(observations, states, start_p, {"a":2}, emit_p) - Traceback (most recent call last): - ... - ValueError: transition_probabilities all values must be dict - - >>> viterbi(observations, states, start_p, {2:{2:2}}, emit_p) - Traceback (most recent call last): - ... - ValueError: transition_probabilities all keys must be strings - - >>> viterbi(observations, states, start_p, {"a":{2:2}}, emit_p) - Traceback (most recent call last): - ... - ValueError: transition_probabilities all keys must be strings - - >>> viterbi(observations, states, start_p, {"a":{"b":2}}, emit_p) - Traceback (most recent call last): - ... - ValueError: transition_probabilities nested dictionary all values must be float - - >>> viterbi(observations, states, start_p, trans_p, "invalid") - Traceback (most recent call last): - ... - ValueError: emission_probabilities must be a dict - - >>> viterbi(observations, states, start_p, trans_p, None) - Traceback (most recent call last): - ... - ValueError: There's an empty parameter + >>> observations = ["normal", "cold", "dizzy"] + >>> states = ["Healthy", "Fever"] + >>> start_p = {"Healthy": 0.6, "Fever": 0.4} + >>> trans_p = { + ... "Healthy": {"Healthy": 0.7, "Fever": 0.3}, + ... "Fever": {"Healthy": 0.4, "Fever": 0.6}, + ... } + >>> emit_p = { + ... "Healthy": {"normal": 0.5, "cold": 0.4, "dizzy": 0.1}, + ... "Fever": {"normal": 0.1, "cold": 0.3, "dizzy": 0.6}, + ... } + >>> viterbi(observations, states, start_p, trans_p, emit_p) + ['Healthy', 'Healthy', 'Fever'] + >>> viterbi((), states, start_p, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: There's an empty parameter + >>> viterbi(observations, (), start_p, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: There's an empty parameter + >>> viterbi(observations, states, {}, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: There's an empty parameter + >>> viterbi(observations, states, start_p, {}, emit_p) + Traceback (most recent call last): + ... + ValueError: There's an empty parameter + >>> viterbi(observations, states, start_p, trans_p, {}) + Traceback (most recent call last): + ... + ValueError: There's an empty parameter + >>> viterbi("invalid", states, start_p, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: observations_space must be a list + >>> viterbi(["valid", 123], states, start_p, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: observations_space must be a list of strings + >>> viterbi(observations, "invalid", start_p, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: states_space must be a list + >>> viterbi(observations, ["valid", 123], start_p, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: states_space must be a list of strings + >>> viterbi(observations, states, "invalid", trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: initial_probabilities must be a dict + >>> viterbi(observations, states, {2:2}, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: initial_probabilities all keys must be strings + >>> viterbi(observations, states, {"a":2}, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: initial_probabilities all values must be float + >>> viterbi(observations, states, start_p, "invalid", emit_p) + Traceback (most recent call last): + ... + ValueError: transition_probabilities must be a dict + >>> viterbi(observations, states, start_p, {"a":2}, emit_p) + Traceback (most recent call last): + ... + ValueError: transition_probabilities all values must be dict + >>> viterbi(observations, states, start_p, {2:{2:2}}, emit_p) + Traceback (most recent call last): + ... + ValueError: transition_probabilities all keys must be strings + >>> viterbi(observations, states, start_p, {"a":{2:2}}, emit_p) + Traceback (most recent call last): + ... + ValueError: transition_probabilities all keys must be strings + >>> viterbi(observations, states, start_p, {"a":{"b":2}}, emit_p) + Traceback (most recent call last): + ... + ValueError: transition_probabilities nested dictionary all values must be float + >>> viterbi(observations, states, start_p, trans_p, "invalid") + Traceback (most recent call last): + ... + ValueError: emission_probabilities must be a dict + >>> viterbi(observations, states, start_p, trans_p, None) + Traceback (most recent call last): + ... + ValueError: There's an empty parameter """ _validation( @@ -213,7 +196,6 @@ def _validation( ... "Fever": {"normal": 0.1, "cold": 0.3, "dizzy": 0.6}, ... } >>> _validation(observations, states, start_p, trans_p, emit_p) - >>> _validation([], states, start_p, trans_p, emit_p) Traceback (most recent call last): ... @@ -242,7 +224,6 @@ def _validate_not_empty( """ >>> _validate_not_empty(["a"], ["b"], {"c":0.5}, ... {"d": {"e": 0.6}}, {"f": {"g": 0.7}}) - >>> _validate_not_empty(["a"], ["b"], {"c":0.5}, {}, {"f": {"g": 0.7}}) Traceback (most recent call last): ... @@ -267,12 +248,10 @@ def _validate_not_empty( def _validate_lists(observations_space: Any, states_space: Any) -> None: """ >>> _validate_lists(["a"], ["b"]) - >>> _validate_lists(1234, ["b"]) Traceback (most recent call last): ... ValueError: observations_space must be a list - >>> _validate_lists(["a"], [3]) Traceback (most recent call last): ... @@ -285,7 +264,6 @@ def _validate_lists(observations_space: Any, states_space: Any) -> None: def _validate_list(_object: Any, var_name: str) -> None: """ >>> _validate_list(["a"], "mock_name") - >>> _validate_list("a", "mock_name") Traceback (most recent call last): ... @@ -294,7 +272,6 @@ def _validate_list(_object: Any, var_name: str) -> None: Traceback (most recent call last): ... ValueError: mock_name must be a list of strings - """ if not isinstance(_object, list): msg = f"{var_name} must be a list" @@ -313,7 +290,6 @@ def _validate_dicts( ) -> None: """ >>> _validate_dicts({"c":0.5}, {"d": {"e": 0.6}}, {"f": {"g": 0.7}}) - >>> _validate_dicts("invalid", {"d": {"e": 0.6}}, {"f": {"g": 0.7}}) Traceback (most recent call last): ... @@ -339,7 +315,6 @@ def _validate_dicts( def _validate_nested_dict(_object: Any, var_name: str) -> None: """ >>> _validate_nested_dict({"a":{"b": 0.5}}, "mock_name") - >>> _validate_nested_dict("invalid", "mock_name") Traceback (most recent call last): ... @@ -367,7 +342,6 @@ def _validate_dict( ) -> None: """ >>> _validate_dict({"b": 0.5}, "mock_name", float) - >>> _validate_dict("invalid", "mock_name", float) Traceback (most recent call last): ... From a2be5adf673c32720bbfb649c368af8fd7ae9dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julia=20Arag=C3=A3o?= <101305675+juliaaragao@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:36:55 +0100 Subject: [PATCH 051/126] Tests electronics/electric_conductivity.py #9943 (#12437) * Function conversion rectangular number to polar * #9943 : adding test to elelectronics/electric_conductivity.py * updating DIRECTORY.md * Apply suggestions from code review * updating DIRECTORY.md * Rename rec_to_pol.py to rectangular_to_polar.py * updating DIRECTORY.md * Update conversions/rectangular_to_polar.py * Update conversions/rectangular_to_polar.py --------- Co-authored-by: Julia Co-authored-by: juliaaragao Co-authored-by: Christian Clauss Co-authored-by: cclauss --- DIRECTORY.md | 1 + conversions/rectangular_to_polar.py | 32 ++++++++++++++++++++++++++++ electronics/electric_conductivity.py | 20 +++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 conversions/rectangular_to_polar.py diff --git a/DIRECTORY.md b/DIRECTORY.md index d234d366df06..44d0414a37c8 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -169,6 +169,7 @@ * [Prefix Conversions](conversions/prefix_conversions.py) * [Prefix Conversions String](conversions/prefix_conversions_string.py) * [Pressure Conversions](conversions/pressure_conversions.py) + * [Rectangular To Polar](conversions/rectangular_to_polar.py) * [Rgb Cmyk Conversion](conversions/rgb_cmyk_conversion.py) * [Rgb Hsv Conversion](conversions/rgb_hsv_conversion.py) * [Roman Numerals](conversions/roman_numerals.py) diff --git a/conversions/rectangular_to_polar.py b/conversions/rectangular_to_polar.py new file mode 100644 index 000000000000..bed97d7410ec --- /dev/null +++ b/conversions/rectangular_to_polar.py @@ -0,0 +1,32 @@ +import math + + +def rectangular_to_polar(real: float, img: float) -> tuple[float, float]: + """ + https://en.wikipedia.org/wiki/Polar_coordinate_system + + >>> rectangular_to_polar(5,-5) + (7.07, -45.0) + >>> rectangular_to_polar(-1,1) + (1.41, 135.0) + >>> rectangular_to_polar(-1,-1) + (1.41, -135.0) + >>> rectangular_to_polar(1e-10,1e-10) + (0.0, 45.0) + >>> rectangular_to_polar(-1e-10,1e-10) + (0.0, 135.0) + >>> rectangular_to_polar(9.75,5.93) + (11.41, 31.31) + >>> rectangular_to_polar(10000,99999) + (100497.76, 84.29) + """ + + mod = round(math.sqrt((real**2) + (img**2)), 2) + ang = round(math.degrees(math.atan2(img, real)), 2) + return (mod, ang) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/electronics/electric_conductivity.py b/electronics/electric_conductivity.py index 11f2a607d214..65bb6c5ceaf0 100644 --- a/electronics/electric_conductivity.py +++ b/electronics/electric_conductivity.py @@ -21,6 +21,26 @@ def electric_conductivity( ('conductivity', 5.12672e-14) >>> electric_conductivity(conductivity=1000, electron_conc=0, mobility=1200) ('electron_conc', 5.201506356240767e+18) + >>> electric_conductivity(conductivity=-10, electron_conc=100, mobility=0) + Traceback (most recent call last): + ... + ValueError: Conductivity cannot be negative + >>> electric_conductivity(conductivity=50, electron_conc=-10, mobility=0) + Traceback (most recent call last): + ... + ValueError: Electron concentration cannot be negative + >>> electric_conductivity(conductivity=50, electron_conc=0, mobility=-10) + Traceback (most recent call last): + ... + ValueError: mobility cannot be negative + >>> electric_conductivity(conductivity=50, electron_conc=0, mobility=0) + Traceback (most recent call last): + ... + ValueError: You cannot supply more or less than 2 values + >>> electric_conductivity(conductivity=50, electron_conc=200, mobility=300) + Traceback (most recent call last): + ... + ValueError: You cannot supply more or less than 2 values """ if (conductivity, electron_conc, mobility).count(0) != 1: raise ValueError("You cannot supply more or less than 2 values") From f24ddba5b2600486f7c3a4c5807cf2aeed421870 Mon Sep 17 00:00:00 2001 From: Matej <83732219+IsxImattI@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:04:28 +0100 Subject: [PATCH 052/126] Implemented doctests for geometry-related classes (#12368) * Implemented doctests for geometry-related classes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Removed unused noqa directive * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * refactored sudoku_solver.py * refactored sudoku_solver.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * context manager for file handling changed too in from_file function --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- data_structures/arrays/sudoku_solver.py | 5 +++-- geometry/geometry.py | 29 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index 7e38e1465728..fd1a4f3e37b8 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -23,7 +23,7 @@ def cross(items_a, items_b): + [cross(rs, cs) for rs in ("ABC", "DEF", "GHI") for cs in ("123", "456", "789")] ) units = {s: [u for u in unitlist if s in u] for s in squares} -peers = {s: set(sum(units[s], [])) - {s} for s in squares} # noqa: RUF017 +peers = {s: {x for u in units[s] for x in u} - {s} for s in squares} def test(): @@ -172,7 +172,8 @@ def unitsolved(unit): def from_file(filename, sep="\n"): "Parse a file into a list of strings, separated by sep." - return open(filename).read().strip().split(sep) + with open(filename) as file: + return file.read().strip().split(sep) def random_puzzle(assignments=17): diff --git a/geometry/geometry.py b/geometry/geometry.py index 9e353dee17a7..a0be8eb3befc 100644 --- a/geometry/geometry.py +++ b/geometry/geometry.py @@ -48,6 +48,18 @@ class Side: Side(length=5, angle=Angle(degrees=45.6), next_side=None) >>> Side(5, Angle(45.6), Side(1, Angle(2))) # doctest: +ELLIPSIS Side(length=5, angle=Angle(degrees=45.6), next_side=Side(length=1, angle=Angle(d... + >>> Side(-1) + Traceback (most recent call last): + ... + TypeError: length must be a positive numeric value. + >>> Side(5, None) + Traceback (most recent call last): + ... + TypeError: angle must be an Angle object. + >>> Side(5, Angle(90), "Invalid next_side") + Traceback (most recent call last): + ... + TypeError: next_side must be a Side or None. """ length: float @@ -162,6 +174,19 @@ class Polygon: >>> Polygon() Polygon(sides=[]) + >>> polygon = Polygon() + >>> polygon.add_side(Side(5)).get_side(0) + Side(length=5, angle=Angle(degrees=90), next_side=None) + >>> polygon.get_side(1) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> polygon.set_side(0, Side(10)).get_side(0) + Side(length=10, angle=Angle(degrees=90), next_side=None) + >>> polygon.set_side(1, Side(10)) + Traceback (most recent call last): + ... + IndexError: list assignment index out of range """ sides: list[Side] = field(default_factory=list) @@ -207,6 +232,10 @@ class Rectangle(Polygon): 30 >>> rectangle_one.area() 50 + >>> Rectangle(-5, 10) + Traceback (most recent call last): + ... + TypeError: length must be a positive numeric value. """ def __init__(self, short_side_length: float, long_side_length: float) -> None: From 77425364c87908bf061ad78b770ec840086b4efb Mon Sep 17 00:00:00 2001 From: SUDO_USER <110802232+AtharvMalusare@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:42:04 +0530 Subject: [PATCH 053/126] Intensity_based_Segmentation (#12491) * Add files via upload * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update intensity-based_segmentation.py * Update and rename intensity-based_segmentation.py to intensity_based_segmentation.py * Update intensity_based_segmentation.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review * [0, 1, 1]], dtype=int32) --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../intensity_based_segmentation.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 computer_vision/intensity_based_segmentation.py diff --git a/computer_vision/intensity_based_segmentation.py b/computer_vision/intensity_based_segmentation.py new file mode 100644 index 000000000000..7f2b1141acc4 --- /dev/null +++ b/computer_vision/intensity_based_segmentation.py @@ -0,0 +1,62 @@ +# Source: "https://www.ijcse.com/docs/IJCSE11-02-03-117.pdf" + +# Importing necessary libraries +import matplotlib.pyplot as plt +import numpy as np +from PIL import Image + + +def segment_image(image: np.ndarray, thresholds: list[int]) -> np.ndarray: + """ + Performs image segmentation based on intensity thresholds. + + Args: + image: Input grayscale image as a 2D array. + thresholds: Intensity thresholds to define segments. + + Returns: + A labeled 2D array where each region corresponds to a threshold range. + + Example: + >>> img = np.array([[80, 120, 180], [40, 90, 150], [20, 60, 100]]) + >>> segment_image(img, [50, 100, 150]) + array([[1, 2, 3], + [0, 1, 2], + [0, 1, 1]], dtype=int32) + """ + # Initialize segmented array with zeros + segmented = np.zeros_like(image, dtype=np.int32) + + # Assign labels based on thresholds + for i, threshold in enumerate(thresholds): + segmented[image > threshold] = i + 1 + + return segmented + + +if __name__ == "__main__": + # Load the image + image_path = "path_to_image" # Replace with your image path + original_image = Image.open(image_path).convert("L") + image_array = np.array(original_image) + + # Define thresholds + thresholds = [50, 100, 150, 200] + + # Perform segmentation + segmented_image = segment_image(image_array, thresholds) + + # Display the results + plt.figure(figsize=(10, 5)) + + plt.subplot(1, 2, 1) + plt.title("Original Image") + plt.imshow(image_array, cmap="gray") + plt.axis("off") + + plt.subplot(1, 2, 2) + plt.title("Segmented Image") + plt.imshow(segmented_image, cmap="tab20") + plt.axis("off") + + plt.show() From 75c5c411133f7e0f339c8d68c7c76c8054eb4249 Mon Sep 17 00:00:00 2001 From: Scarfinos <158184182+Scarfinos@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:12:26 +0100 Subject: [PATCH 054/126] #9943 : Adding coverage test for basic_graphs.py (#12354) * #9943 : Adding coverage test for basic_graphs.py * #9943 : Adding coverage test for basic_graphs.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Solve problem of line too long --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- graphs/basic_graphs.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/graphs/basic_graphs.py b/graphs/basic_graphs.py index 567fa65040ae..286e9b195796 100644 --- a/graphs/basic_graphs.py +++ b/graphs/basic_graphs.py @@ -77,6 +77,14 @@ def initialize_weighted_undirected_graph( def dfs(g, s): + """ + >>> dfs({1: [2, 3], 2: [4, 5], 3: [], 4: [], 5: []}, 1) + 1 + 2 + 4 + 5 + 3 + """ vis, _s = {s}, [s] print(s) while _s: @@ -104,6 +112,17 @@ def dfs(g, s): def bfs(g, s): + """ + >>> bfs({1: [2, 3], 2: [4, 5], 3: [6, 7], 4: [], 5: [8], 6: [], 7: [], 8: []}, 1) + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + """ vis, q = {s}, deque([s]) print(s) while q: @@ -128,6 +147,19 @@ def bfs(g, s): def dijk(g, s): + """ + dijk({1: [(2, 7), (3, 9), (6, 14)], + 2: [(1, 7), (3, 10), (4, 15)], + 3: [(1, 9), (2, 10), (4, 11), (6, 2)], + 4: [(2, 15), (3, 11), (5, 6)], + 5: [(4, 6), (6, 9)], + 6: [(1, 14), (3, 2), (5, 9)]}, 1) + 7 + 9 + 11 + 20 + 20 + """ dist, known, path = {s: 0}, set(), {s: 0} while True: if len(known) == len(g) - 1: From 7e55fb6474a06ecf0000fe11494fe5eefeeb54ab Mon Sep 17 00:00:00 2001 From: Jeffrey Yancey Date: Mon, 30 Dec 2024 12:00:30 -0700 Subject: [PATCH 055/126] - Implemented `find_lanczos_eigenvectors` to approximate the largest eigenvalues and corresponding eigenvectors of a graph based on its adjacency list. (#11906) - Utilized `lanczos_iteration` to construct tridiagonal matrices, optimized for large, sparse matrices. - Added `multiply_matrix_vector` for efficient matrix-vector multiplication using adjacency lists. - Included `validate_adjacency_list` for input validation. - Supports varied graph analysis applications, particularly for analyzing graph centrality. - Included type hints, comprehensive docstrings, and doctests. - PEP-8 compliant, with optimized handling of inputs and outputs. This module provides essential tools for eigenvalue-based graph analysis, ideal for centrality insights and structural assessments. --- graphs/lanczos_eigenvectors.py | 206 +++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 graphs/lanczos_eigenvectors.py diff --git a/graphs/lanczos_eigenvectors.py b/graphs/lanczos_eigenvectors.py new file mode 100644 index 000000000000..581a81a1127f --- /dev/null +++ b/graphs/lanczos_eigenvectors.py @@ -0,0 +1,206 @@ +""" +Lanczos Method for Finding Eigenvalues and Eigenvectors of a Graph. + +This module demonstrates the Lanczos method to approximate the largest eigenvalues +and corresponding eigenvectors of a symmetric matrix represented as a graph's +adjacency list. The method efficiently handles large, sparse matrices by converting +the graph to a tridiagonal matrix, whose eigenvalues and eigenvectors are then +computed. + +Key Functions: +- `find_lanczos_eigenvectors`: Computes the k largest eigenvalues and vectors. +- `lanczos_iteration`: Constructs the tridiagonal matrix and orthonormal basis vectors. +- `multiply_matrix_vector`: Multiplies an adjacency list graph with a vector. + +Complexity: +- Time: O(k * n), where k is the number of eigenvalues and n is the matrix size. +- Space: O(n), due to sparse representation and tridiagonal matrix structure. + +Further Reading: +- Lanczos Algorithm: https://en.wikipedia.org/wiki/Lanczos_algorithm +- Eigenvector Centrality: https://en.wikipedia.org/wiki/Eigenvector_centrality + +Example Usage: +Given a graph represented by an adjacency list, the `find_lanczos_eigenvectors` +function returns the largest eigenvalues and eigenvectors. This can be used to +analyze graph centrality. +""" + +import numpy as np + + +def validate_adjacency_list(graph: list[list[int | None]]) -> None: + """Validates the adjacency list format for the graph. + + Args: + graph: A list of lists where each sublist contains the neighbors of a node. + + Raises: + ValueError: If the graph is not a list of lists, or if any node has + invalid neighbors (e.g., out-of-range or non-integer values). + + >>> validate_adjacency_list([[1, 2], [0], [0, 1]]) + >>> validate_adjacency_list([[]]) # No neighbors, valid case + >>> validate_adjacency_list([[1], [2], [-1]]) # Invalid neighbor + Traceback (most recent call last): + ... + ValueError: Invalid neighbor -1 in node 2 adjacency list. + """ + if not isinstance(graph, list): + raise ValueError("Graph should be a list of lists.") + + for node_index, neighbors in enumerate(graph): + if not isinstance(neighbors, list): + no_neighbors_message: str = ( + f"Node {node_index} should have a list of neighbors." + ) + raise ValueError(no_neighbors_message) + for neighbor_index in neighbors: + if ( + not isinstance(neighbor_index, int) + or neighbor_index < 0 + or neighbor_index >= len(graph) + ): + invalid_neighbor_message: str = ( + f"Invalid neighbor {neighbor_index} in node {node_index} " + f"adjacency list." + ) + raise ValueError(invalid_neighbor_message) + + +def lanczos_iteration( + graph: list[list[int | None]], num_eigenvectors: int +) -> tuple[np.ndarray, np.ndarray]: + """Constructs the tridiagonal matrix and orthonormal basis vectors using the + Lanczos method. + + Args: + graph: The graph represented as a list of adjacency lists. + num_eigenvectors: The number of largest eigenvalues and eigenvectors + to approximate. + + Returns: + A tuple containing: + - tridiagonal_matrix: A (num_eigenvectors x num_eigenvectors) symmetric + matrix. + - orthonormal_basis: A (num_nodes x num_eigenvectors) matrix of orthonormal + basis vectors. + + Raises: + ValueError: If num_eigenvectors is less than 1 or greater than the number of + nodes. + + >>> graph = [[1, 2], [0, 2], [0, 1]] + >>> T, Q = lanczos_iteration(graph, 2) + >>> T.shape == (2, 2) and Q.shape == (3, 2) + True + """ + num_nodes: int = len(graph) + if not (1 <= num_eigenvectors <= num_nodes): + raise ValueError( + "Number of eigenvectors must be between 1 and the number of " + "nodes in the graph." + ) + + orthonormal_basis: np.ndarray = np.zeros((num_nodes, num_eigenvectors)) + tridiagonal_matrix: np.ndarray = np.zeros((num_eigenvectors, num_eigenvectors)) + + rng = np.random.default_rng() + initial_vector: np.ndarray = rng.random(num_nodes) + initial_vector /= np.sqrt(np.dot(initial_vector, initial_vector)) + orthonormal_basis[:, 0] = initial_vector + + prev_beta: float = 0.0 + for iter_index in range(num_eigenvectors): + result_vector: np.ndarray = multiply_matrix_vector( + graph, orthonormal_basis[:, iter_index] + ) + if iter_index > 0: + result_vector -= prev_beta * orthonormal_basis[:, iter_index - 1] + alpha_value: float = np.dot(orthonormal_basis[:, iter_index], result_vector) + result_vector -= alpha_value * orthonormal_basis[:, iter_index] + + prev_beta = np.sqrt(np.dot(result_vector, result_vector)) + if iter_index < num_eigenvectors - 1 and prev_beta > 1e-10: + orthonormal_basis[:, iter_index + 1] = result_vector / prev_beta + tridiagonal_matrix[iter_index, iter_index] = alpha_value + if iter_index < num_eigenvectors - 1: + tridiagonal_matrix[iter_index, iter_index + 1] = prev_beta + tridiagonal_matrix[iter_index + 1, iter_index] = prev_beta + return tridiagonal_matrix, orthonormal_basis + + +def multiply_matrix_vector( + graph: list[list[int | None]], vector: np.ndarray +) -> np.ndarray: + """Performs multiplication of a graph's adjacency list representation with a vector. + + Args: + graph: The adjacency list of the graph. + vector: A 1D numpy array representing the vector to multiply. + + Returns: + A numpy array representing the product of the adjacency list and the vector. + + Raises: + ValueError: If the vector's length does not match the number of nodes in the + graph. + + >>> multiply_matrix_vector([[1, 2], [0, 2], [0, 1]], np.array([1, 1, 1])) + array([2., 2., 2.]) + >>> multiply_matrix_vector([[1, 2], [0, 2], [0, 1]], np.array([0, 1, 0])) + array([1., 0., 1.]) + """ + num_nodes: int = len(graph) + if vector.shape[0] != num_nodes: + raise ValueError("Vector length must match the number of nodes in the graph.") + + result: np.ndarray = np.zeros(num_nodes) + for node_index, neighbors in enumerate(graph): + for neighbor_index in neighbors: + result[node_index] += vector[neighbor_index] + return result + + +def find_lanczos_eigenvectors( + graph: list[list[int | None]], num_eigenvectors: int +) -> tuple[np.ndarray, np.ndarray]: + """Computes the largest eigenvalues and their corresponding eigenvectors using the + Lanczos method. + + Args: + graph: The graph as a list of adjacency lists. + num_eigenvectors: Number of largest eigenvalues and eigenvectors to compute. + + Returns: + A tuple containing: + - eigenvalues: 1D array of the largest eigenvalues in descending order. + - eigenvectors: 2D array where each column is an eigenvector corresponding + to an eigenvalue. + + Raises: + ValueError: If the graph format is invalid or num_eigenvectors is out of bounds. + + >>> eigenvalues, eigenvectors = find_lanczos_eigenvectors( + ... [[1, 2], [0, 2], [0, 1]], 2 + ... ) + >>> len(eigenvalues) == 2 and eigenvectors.shape[1] == 2 + True + """ + validate_adjacency_list(graph) + tridiagonal_matrix, orthonormal_basis = lanczos_iteration(graph, num_eigenvectors) + eigenvalues, eigenvectors = np.linalg.eigh(tridiagonal_matrix) + return eigenvalues[::-1], np.dot(orthonormal_basis, eigenvectors[:, ::-1]) + + +def main() -> None: + """ + Main driver function for testing the implementation with doctests. + """ + import doctest + + doctest.testmod() + + +if __name__ == "__main__": + main() From 8921b56a8517cdc9455d764d7cddb10b2d7f2145 Mon Sep 17 00:00:00 2001 From: Melih Mehmet Sahin Date: Mon, 30 Dec 2024 19:53:50 +0000 Subject: [PATCH 056/126] Adding tests to monotonic_array.py (#12073) * Contributes to #9943 by adding tests to monotonic_array.py Addeded doctest in the if __name__. Checks for negaitves and an array of same integers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- data_structures/arrays/monotonic_array.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/data_structures/arrays/monotonic_array.py b/data_structures/arrays/monotonic_array.py index c50a21530814..342d443a9cfc 100644 --- a/data_structures/arrays/monotonic_array.py +++ b/data_structures/arrays/monotonic_array.py @@ -9,6 +9,16 @@ def is_monotonic(nums: list[int]) -> bool: True >>> is_monotonic([1, 3, 2]) False + >>> is_monotonic([1,2,3,4,5,6,5]) + False + >>> is_monotonic([-3,-2,-1]) + True + >>> is_monotonic([-5,-6,-7]) + True + >>> is_monotonic([0,0,0]) + True + >>> is_monotonic([-100,0,100]) + True """ return all(nums[i] <= nums[i + 1] for i in range(len(nums) - 1)) or all( nums[i] >= nums[i + 1] for i in range(len(nums) - 1) @@ -21,3 +31,7 @@ def is_monotonic(nums: list[int]) -> bool: print(is_monotonic([1, 2, 2, 3])) # Output: True print(is_monotonic([6, 5, 4, 4])) # Output: True print(is_monotonic([1, 3, 2])) # Output: False + + import doctest + + doctest.testmod() From 5942059cb571b213e5ec82fe9b45e5a9bef4864b Mon Sep 17 00:00:00 2001 From: Giulio Tantaro Date: Mon, 30 Dec 2024 21:03:31 +0100 Subject: [PATCH 057/126] add doctest for quick_sort_3_partition (#11779) --- sorts/quick_sort_3_partition.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sorts/quick_sort_3_partition.py b/sorts/quick_sort_3_partition.py index 1a6db6a364f0..279b9a68f5a6 100644 --- a/sorts/quick_sort_3_partition.py +++ b/sorts/quick_sort_3_partition.py @@ -1,4 +1,27 @@ def quick_sort_3partition(sorting: list, left: int, right: int) -> None: + """ " + Python implementation of quick sort algorithm with 3-way partition. + The idea of 3-way quick sort is based on "Dutch National Flag algorithm". + + :param sorting: sort list + :param left: left endpoint of sorting + :param right: right endpoint of sorting + :return: None + + Examples: + >>> array1 = [5, -1, -1, 5, 5, 24, 0] + >>> quick_sort_3partition(array1, 0, 6) + >>> array1 + [-1, -1, 0, 5, 5, 5, 24] + >>> array2 = [9, 0, 2, 6] + >>> quick_sort_3partition(array2, 0, 3) + >>> array2 + [0, 2, 6, 9] + >>> array3 = [] + >>> quick_sort_3partition(array3, 0, 0) + >>> array3 + [] + """ if right <= left: return a = i = left From 8767d1d72436b8aff89f9c11d045ad95bec02ba4 Mon Sep 17 00:00:00 2001 From: Rodrigo Castro Date: Mon, 30 Dec 2024 21:36:41 -0300 Subject: [PATCH 058/126] add some documentation for heap sort (#9949) * add some documentation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix typing * Update heap_sort.py * Update heap_sort.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- sorts/heap_sort.py | 47 +++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/sorts/heap_sort.py b/sorts/heap_sort.py index 4dca879bd89c..44ee1d4b39f1 100644 --- a/sorts/heap_sort.py +++ b/sorts/heap_sort.py @@ -1,17 +1,22 @@ """ -This is a pure Python implementation of the heap sort algorithm. - -For doctests run following command: -python -m doctest -v heap_sort.py -or -python3 -m doctest -v heap_sort.py - -For manual testing run: -python heap_sort.py +A pure Python implementation of the heap sort algorithm. """ -def heapify(unsorted, index, heap_size): +def heapify(unsorted: list[int], index: int, heap_size: int) -> None: + """ + :param unsorted: unsorted list containing integers numbers + :param index: index + :param heap_size: size of the heap + :return: None + >>> unsorted = [1, 4, 3, 5, 2] + >>> heapify(unsorted, 0, len(unsorted)) + >>> unsorted + [4, 5, 3, 1, 2] + >>> heapify(unsorted, 0, len(unsorted)) + >>> unsorted + [5, 4, 3, 1, 2] + """ largest = index left_index = 2 * index + 1 right_index = 2 * index + 2 @@ -22,26 +27,26 @@ def heapify(unsorted, index, heap_size): largest = right_index if largest != index: - unsorted[largest], unsorted[index] = unsorted[index], unsorted[largest] + unsorted[largest], unsorted[index] = (unsorted[index], unsorted[largest]) heapify(unsorted, largest, heap_size) -def heap_sort(unsorted): +def heap_sort(unsorted: list[int]) -> list[int]: """ - Pure implementation of the heap sort algorithm in Python - :param collection: some mutable ordered collection with heterogeneous - comparable items inside + A pure Python implementation of the heap sort algorithm + + :param collection: a mutable ordered collection of heterogeneous comparable items :return: the same collection ordered by ascending Examples: >>> heap_sort([0, 5, 3, 2, 2]) [0, 2, 2, 3, 5] - >>> heap_sort([]) [] - >>> heap_sort([-2, -5, -45]) [-45, -5, -2] + >>> heap_sort([3, 7, 9, 28, 123, -5, 8, -30, -200, 0, 4]) + [-200, -30, -5, 0, 3, 4, 7, 8, 9, 28, 123] """ n = len(unsorted) for i in range(n // 2 - 1, -1, -1): @@ -53,6 +58,10 @@ def heap_sort(unsorted): if __name__ == "__main__": + import doctest + + doctest.testmod() user_input = input("Enter numbers separated by a comma:\n").strip() - unsorted = [int(item) for item in user_input.split(",")] - print(heap_sort(unsorted)) + if user_input: + unsorted = [int(item) for item in user_input.split(",")] + print(f"{heap_sort(unsorted) = }") From 8439fa8d1da94370250d153cd57f9bdcc382a062 Mon Sep 17 00:00:00 2001 From: Paarth Goyal <138299656+pluto-tofu@users.noreply.github.com> Date: Tue, 31 Dec 2024 06:17:41 +0530 Subject: [PATCH 059/126] Added the algorithm to compute the time period of a simple pendulum (#10265) * Added the algorithm to compute the time period of a simple pendulum * imported g form scipy and changed doctests accordingly * fixed formatting * applied all suggested changes from code review * Apply suggestions from code review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Tianyi Zheng Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- physics/period_of_pendulum.py | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 physics/period_of_pendulum.py diff --git a/physics/period_of_pendulum.py b/physics/period_of_pendulum.py new file mode 100644 index 000000000000..2e3c7bc3ef1e --- /dev/null +++ b/physics/period_of_pendulum.py @@ -0,0 +1,53 @@ +""" +Title : Computing the time period of a simple pendulum + +The simple pendulum is a mechanical system that sways or moves in an +oscillatory motion. The simple pendulum comprises of a small bob of +mass m suspended by a thin string of length L and secured to a platform +at its upper end. Its motion occurs in a vertical plane and is mainly +driven by gravitational force. The period of the pendulum depends on the +length of the string and the amplitude (the maximum angle) of oscillation. +However, the effect of the amplitude can be ignored if the amplitude is +small. It should be noted that the period does not depend on the mass of +the bob. + +For small amplitudes, the period of a simple pendulum is given by the +following approximation: +T ≈ 2π * √(L / g) + +where: +L = length of string from which the bob is hanging (in m) +g = acceleration due to gravity (approx 9.8 m/s²) + +Reference : https://byjus.com/jee/simple-pendulum/ +""" + +from math import pi + +from scipy.constants import g + + +def period_of_pendulum(length: float) -> float: + """ + >>> period_of_pendulum(1.23) + 2.2252155506257845 + >>> period_of_pendulum(2.37) + 3.0888278441908574 + >>> period_of_pendulum(5.63) + 4.76073193364765 + >>> period_of_pendulum(-12) + Traceback (most recent call last): + ... + ValueError: The length should be non-negative + >>> period_of_pendulum(0) + 0.0 + """ + if length < 0: + raise ValueError("The length should be non-negative") + return 2 * pi * (length / g) ** 0.5 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From a4399022e516dfba1097f91cedc0b7f4213bab84 Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Tue, 31 Dec 2024 02:11:29 +0100 Subject: [PATCH 060/126] chore: improve comments and add tests to trapezoidal rule (#11640) * chore: improve comments and add tests to trapezoidal rule * fix: too much characters in line * Update maths/trapezoidal_rule.py Co-authored-by: Tianyi Zheng * Update maths/trapezoidal_rule.py Co-authored-by: Tianyi Zheng * Update maths/trapezoidal_rule.py Co-authored-by: Tianyi Zheng * Update maths/trapezoidal_rule.py Co-authored-by: Tianyi Zheng * fix: change function name in calls * modify tests, changes numbers to remove coma * updating DIRECTORY.md * Fix doctest whitespace * Try to fix line length in doctest --------- Co-authored-by: Tianyi Zheng Co-authored-by: tianyizheng02 --- DIRECTORY.md | 3 ++ maths/trapezoidal_rule.py | 97 ++++++++++++++++++++++----------------- 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 44d0414a37c8..1248a290d294 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -142,6 +142,7 @@ * [Haralick Descriptors](computer_vision/haralick_descriptors.py) * [Harris Corner](computer_vision/harris_corner.py) * [Horn Schunck](computer_vision/horn_schunck.py) + * [Intensity Based Segmentation](computer_vision/intensity_based_segmentation.py) * [Mean Threshold](computer_vision/mean_threshold.py) * [Mosaic Augmentation](computer_vision/mosaic_augmentation.py) * [Pooling Functions](computer_vision/pooling_functions.py) @@ -507,6 +508,7 @@ * [Kahns Algorithm Long](graphs/kahns_algorithm_long.py) * [Kahns Algorithm Topo](graphs/kahns_algorithm_topo.py) * [Karger](graphs/karger.py) + * [Lanczos Eigenvectors](graphs/lanczos_eigenvectors.py) * [Markov Chain](graphs/markov_chain.py) * [Matching Min Vertex Cover](graphs/matching_min_vertex_cover.py) * [Minimum Path Sum](graphs/minimum_path_sum.py) @@ -886,6 +888,7 @@ * [N Body Simulation](physics/n_body_simulation.py) * [Newtons Law Of Gravitation](physics/newtons_law_of_gravitation.py) * [Newtons Second Law Of Motion](physics/newtons_second_law_of_motion.py) + * [Period Of Pendulum](physics/period_of_pendulum.py) * [Photoelectric Effect](physics/photoelectric_effect.py) * [Potential Energy](physics/potential_energy.py) * [Rainfall Intensity](physics/rainfall_intensity.py) diff --git a/maths/trapezoidal_rule.py b/maths/trapezoidal_rule.py index 0186629ee378..21b10b239b5f 100644 --- a/maths/trapezoidal_rule.py +++ b/maths/trapezoidal_rule.py @@ -1,28 +1,25 @@ """ Numerical integration or quadrature for a smooth function f with known values at x_i - -This method is the classical approach of suming 'Equally Spaced Abscissas' - -method 1: -"extended trapezoidal rule" -int(f) = dx/2 * (f1 + 2f2 + ... + fn) - """ -def method_1(boundary, steps): +def trapezoidal_rule(boundary, steps): """ - Apply the extended trapezoidal rule to approximate the integral of function f(x) - over the interval defined by 'boundary' with the number of 'steps'. - - Args: - boundary (list of floats): A list containing the start and end values [a, b]. - steps (int): The number of steps or subintervals. - Returns: - float: Approximation of the integral of f(x) over [a, b]. - Examples: - >>> method_1([0, 1], 10) - 0.3349999999999999 + Implements the extended trapezoidal rule for numerical integration. + The function f(x) is provided below. + + :param boundary: List containing the lower and upper bounds of integration [a, b] + :param steps: The number of steps (intervals) used in the approximation + :return: The numerical approximation of the integral + + >>> abs(trapezoidal_rule([0, 1], 10) - 0.33333) < 0.01 + True + >>> abs(trapezoidal_rule([0, 1], 100) - 0.33333) < 0.01 + True + >>> abs(trapezoidal_rule([0, 2], 1000) - 2.66667) < 0.01 + True + >>> abs(trapezoidal_rule([1, 2], 1000) - 2.33333) < 0.01 + True """ h = (boundary[1] - boundary[0]) / steps a = boundary[0] @@ -31,7 +28,6 @@ def method_1(boundary, steps): y = 0.0 y += (h / 2.0) * f(a) for i in x_i: - # print(i) y += h * f(i) y += (h / 2.0) * f(b) return y @@ -39,49 +35,66 @@ def method_1(boundary, steps): def make_points(a, b, h): """ - Generates points between 'a' and 'b' with step size 'h', excluding the end points. - Args: - a (float): Start value - b (float): End value - h (float): Step size - Examples: + Generates points between a and b with step size h for trapezoidal integration. + + :param a: The lower bound of integration + :param b: The upper bound of integration + :param h: The step size + :yield: The next x-value in the range (a, b) + + >>> list(make_points(0, 1, 0.1)) # doctest: +NORMALIZE_WHITESPACE + [0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6, 0.7, 0.7999999999999999, \ + 0.8999999999999999] >>> list(make_points(0, 10, 2.5)) [2.5, 5.0, 7.5] - >>> list(make_points(0, 10, 2)) [2, 4, 6, 8] - >>> list(make_points(1, 21, 5)) [6, 11, 16] - >>> list(make_points(1, 5, 2)) [3] - >>> list(make_points(1, 4, 3)) [] """ x = a + h while x <= (b - h): yield x - x = x + h + x += h -def f(x): # enter your function here +def f(x): """ - Example: - >>> f(2) - 4 + This is the function to integrate, f(x) = (x - 0)^2 = x^2. + + :param x: The input value + :return: The value of f(x) + + >>> f(0) + 0 + >>> f(1) + 1 + >>> f(0.5) + 0.25 """ - y = (x - 0) * (x - 0) - return y + return x**2 def main(): - a = 0.0 # Lower bound of integration - b = 1.0 # Upper bound of integration - steps = 10.0 # define number of steps or resolution - boundary = [a, b] # define boundary of integration - y = method_1(boundary, steps) + """ + Main function to test the trapezoidal rule. + :a: Lower bound of integration + :b: Upper bound of integration + :steps: define number of steps or resolution + :boundary: define boundary of integration + + >>> main() + y = 0.3349999999999999 + """ + a = 0.0 + b = 1.0 + steps = 10.0 + boundary = [a, b] + y = trapezoidal_rule(boundary, steps) print(f"y = {y}") From 91a22c2e36477623b1f81518ff18c6f8617f81fb Mon Sep 17 00:00:00 2001 From: SEIKH NABAB UDDIN <93948993+nababuddin@users.noreply.github.com> Date: Tue, 31 Dec 2024 07:39:14 +0530 Subject: [PATCH 061/126] Create digital differential analyzer_line.py (#10929) * Create DDA_line_drawing.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Rename DDA_line_drawing.py to digital differential analyzer_line_drawing.py * Rename DDA_line_drawing.py to digital_differential_analyzer_line_drawing.py * Update digital_differential_analyzer_line_drawing.py * Update digital_differential_analyzer_line_drawing.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line_drawing.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line_drawing.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line_drawing.py * Update digital_differential_analyzer_line_drawing.py * Update digital_differential_analyzer_line_drawing.py * Update digital_differential_analyzer_line_drawing.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review Co-authored-by: Tianyi Zheng * Update and rename digital_differential_analyzer_line_drawing.py to digital_differential_analyzer_line.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line.py * Update digital_differential_analyzer_line.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review * Fix doctest * Trigger GH workflows * Fix function call in main block --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- .../digital_differential_analyzer_line.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 graphics/digital_differential_analyzer_line.py diff --git a/graphics/digital_differential_analyzer_line.py b/graphics/digital_differential_analyzer_line.py new file mode 100644 index 000000000000..a51cb0b8dc37 --- /dev/null +++ b/graphics/digital_differential_analyzer_line.py @@ -0,0 +1,52 @@ +import matplotlib.pyplot as plt + + +def digital_differential_analyzer_line( + p1: tuple[int, int], p2: tuple[int, int] +) -> list[tuple[int, int]]: + """ + Draws a line between two points using the DDA algorithm. + + Args: + - p1: Coordinates of the starting point. + - p2: Coordinates of the ending point. + Returns: + - List of coordinate points that form the line. + + >>> digital_differential_analyzer_line((1, 1), (4, 4)) + [(2, 2), (3, 3), (4, 4)] + """ + x1, y1 = p1 + x2, y2 = p2 + dx = x2 - x1 + dy = y2 - y1 + steps = max(abs(dx), abs(dy)) + x_increment = dx / float(steps) + y_increment = dy / float(steps) + coordinates = [] + x: float = x1 + y: float = y1 + for _ in range(steps): + x += x_increment + y += y_increment + coordinates.append((int(round(x)), int(round(y)))) + return coordinates + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + x1 = int(input("Enter the x-coordinate of the starting point: ")) + y1 = int(input("Enter the y-coordinate of the starting point: ")) + x2 = int(input("Enter the x-coordinate of the ending point: ")) + y2 = int(input("Enter the y-coordinate of the ending point: ")) + coordinates = digital_differential_analyzer_line((x1, y1), (x2, y2)) + x_points, y_points = zip(*coordinates) + plt.plot(x_points, y_points, marker="o") + plt.title("Digital Differential Analyzer Line Drawing Algorithm") + plt.xlabel("X-axis") + plt.ylabel("Y-axis") + plt.grid() + plt.show() From 12b1023a9d97ca19be761c10129cba5509c9b450 Mon Sep 17 00:00:00 2001 From: Kaustubh Mani Tripathi <129510465+kmtGryffindor20@users.noreply.github.com> Date: Tue, 31 Dec 2024 07:46:32 +0530 Subject: [PATCH 062/126] [ADDED] Implementation of Geometric Mean. (#10421) * [ADDED] Implementation of Geometric Mean. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Rectified type hints * Typo * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- maths/geometric_mean.py | 55 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 maths/geometric_mean.py diff --git a/maths/geometric_mean.py b/maths/geometric_mean.py new file mode 100644 index 000000000000..240d519ad398 --- /dev/null +++ b/maths/geometric_mean.py @@ -0,0 +1,55 @@ +""" +The Geometric Mean of n numbers is defined as the n-th root of the product +of those numbers. It is used to measure the central tendency of the numbers. +https://en.wikipedia.org/wiki/Geometric_mean +""" + + +def compute_geometric_mean(*args: int) -> float: + """ + Return the geometric mean of the argument numbers. + >>> compute_geometric_mean(2,8) + 4.0 + >>> compute_geometric_mean('a', 4) + Traceback (most recent call last): + ... + TypeError: Not a Number + >>> compute_geometric_mean(5, 125) + 25.0 + >>> compute_geometric_mean(1, 0) + 0.0 + >>> compute_geometric_mean(1, 5, 25, 5) + 5.0 + >>> compute_geometric_mean(2, -2) + Traceback (most recent call last): + ... + ArithmeticError: Cannot Compute Geometric Mean for these numbers. + >>> compute_geometric_mean(-5, 25, 1) + -5.0 + """ + product = 1 + for number in args: + if not isinstance(number, int) and not isinstance(number, float): + raise TypeError("Not a Number") + product *= number + # Cannot calculate the even root for negative product. + # Frequently they are restricted to being positive. + if product < 0 and len(args) % 2 == 0: + raise ArithmeticError("Cannot Compute Geometric Mean for these numbers.") + mean = abs(product) ** (1 / len(args)) + # Since python calculates complex roots for negative products with odd roots. + if product < 0: + mean = -mean + # Since it does floating point arithmetic, it gives 64**(1/3) as 3.99999996 + possible_mean = float(round(mean)) + # To check if the rounded number is actually the mean. + if possible_mean ** len(args) == product: + mean = possible_mean + return mean + + +if __name__ == "__main__": + from doctest import testmod + + testmod(name="compute_geometric_mean") + print(compute_geometric_mean(-3, -27)) From bae33acf9008aa6c80351b3c68f492ba0c4a1352 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 21:12:13 +0100 Subject: [PATCH 063/126] [pre-commit.ci] pre-commit autoupdate (#12507) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.4 → v0.8.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.4...v0.8.6) - [github.com/pre-commit/mirrors-mypy: v1.14.0 → v1.14.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.14.0...v1.14.1) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 71ac72c29b5f..ec1dbca3a41c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.4 + rev: v0.8.6 hooks: - id: ruff - id: ruff-format @@ -47,7 +47,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.14.0 + rev: v1.14.1 hooks: - id: mypy args: diff --git a/DIRECTORY.md b/DIRECTORY.md index 1248a290d294..3f0a5dbb140f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -462,6 +462,7 @@ ## Graphics * [Bezier Curve](graphics/bezier_curve.py) + * [Digital Differential Analyzer Line](graphics/digital_differential_analyzer_line.py) * [Vector3 For 2D Rendering](graphics/vector3_for_2d_rendering.py) ## Graphs @@ -663,6 +664,7 @@ * [Gamma](maths/gamma.py) * [Gaussian](maths/gaussian.py) * [Gcd Of N Numbers](maths/gcd_of_n_numbers.py) + * [Geometric Mean](maths/geometric_mean.py) * [Germain Primes](maths/germain_primes.py) * [Greatest Common Divisor](maths/greatest_common_divisor.py) * [Hardy Ramanujanalgo](maths/hardy_ramanujanalgo.py) From b653aee627a95de423f1cad97f283de904271ff7 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sun, 12 Jan 2025 19:05:08 +0300 Subject: [PATCH 064/126] Fix ruff (#12515) * Empty commit * Fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * Fix * Fix * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: MaximSmolskiy --- DIRECTORY.md | 4 ++-- ciphers/{base64.py => base64_cipher.py} | 0 project_euler/problem_002/sol4.py | 2 +- strings/{wave.py => wave_string.py} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename ciphers/{base64.py => base64_cipher.py} (100%) rename strings/{wave.py => wave_string.py} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 3f0a5dbb140f..aad6c72aa8ee 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -86,7 +86,7 @@ * [Baconian Cipher](ciphers/baconian_cipher.py) * [Base16](ciphers/base16.py) * [Base32](ciphers/base32.py) - * [Base64](ciphers/base64.py) + * [Base64 Cipher](ciphers/base64_cipher.py) * [Base85](ciphers/base85.py) * [Beaufort Cipher](ciphers/beaufort_cipher.py) * [Bifid](ciphers/bifid.py) @@ -1331,7 +1331,7 @@ * [Title](strings/title.py) * [Top K Frequent Words](strings/top_k_frequent_words.py) * [Upper](strings/upper.py) - * [Wave](strings/wave.py) + * [Wave String](strings/wave_string.py) * [Wildcard Pattern Matching](strings/wildcard_pattern_matching.py) * [Word Occurrence](strings/word_occurrence.py) * [Word Patterns](strings/word_patterns.py) diff --git a/ciphers/base64.py b/ciphers/base64_cipher.py similarity index 100% rename from ciphers/base64.py rename to ciphers/base64_cipher.py diff --git a/project_euler/problem_002/sol4.py b/project_euler/problem_002/sol4.py index 3a2e4fce341c..a13d34fd760e 100644 --- a/project_euler/problem_002/sol4.py +++ b/project_euler/problem_002/sol4.py @@ -61,7 +61,7 @@ def solution(n: int = 4000000) -> int: if n <= 0: raise ValueError("Parameter n must be greater than or equal to one.") getcontext().prec = 100 - phi = (Decimal(5) ** Decimal(0.5) + 1) / Decimal(2) + phi = (Decimal(5) ** Decimal("0.5") + 1) / Decimal(2) index = (math.floor(math.log(n * (phi + 2), phi) - 1) // 3) * 3 + 2 num = Decimal(round(phi ** Decimal(index + 1))) / (phi + 2) diff --git a/strings/wave.py b/strings/wave_string.py similarity index 100% rename from strings/wave.py rename to strings/wave_string.py From 4c92de5e03310811a376058e110db8d615769087 Mon Sep 17 00:00:00 2001 From: Sanjay Muthu Date: Mon, 13 Jan 2025 05:05:22 +0530 Subject: [PATCH 065/126] Fix dynamic_programming/longest_increasing_subsequence.py (#12517) * Fix #12510 * Added the doctest mentioned in the issue * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed Grammer Mistake * Update longest_increasing_subsequence.py * Update longest_increasing_subsequence.py * Update longest_increasing_subsequence.py * Update longest_increasing_subsequence.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- dynamic_programming/longest_increasing_subsequence.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dynamic_programming/longest_increasing_subsequence.py b/dynamic_programming/longest_increasing_subsequence.py index d839757f6da5..1863a882c41e 100644 --- a/dynamic_programming/longest_increasing_subsequence.py +++ b/dynamic_programming/longest_increasing_subsequence.py @@ -24,8 +24,10 @@ def longest_subsequence(array: list[int]) -> list[int]: # This function is recu [10, 22, 33, 41, 60, 80] >>> longest_subsequence([4, 8, 7, 5, 1, 12, 2, 3, 9]) [1, 2, 3, 9] + >>> longest_subsequence([28, 26, 12, 23, 35, 39]) + [12, 23, 35, 39] >>> longest_subsequence([9, 8, 7, 6, 5, 7]) - [8] + [5, 7] >>> longest_subsequence([1, 1, 1]) [1, 1, 1] >>> longest_subsequence([]) @@ -44,7 +46,7 @@ def longest_subsequence(array: list[int]) -> list[int]: # This function is recu while not is_found and i < array_length: if array[i] < pivot: is_found = True - temp_array = [element for element in array[i:] if element >= array[i]] + temp_array = array[i:] temp_array = longest_subsequence(temp_array) if len(temp_array) > len(longest_subseq): longest_subseq = temp_array From 787aa5d3b59640b2d9161b56ca8fde763597efe4 Mon Sep 17 00:00:00 2001 From: Siddhant <87547498+Siddhant231xyz@users.noreply.github.com> Date: Sun, 12 Jan 2025 20:54:23 -0500 Subject: [PATCH 066/126] doctest all_combinations.py (#12506) * doctest in all_combinations.py * added doctest in all_combinations.py * doctests in all_combinations.py * add doctest all_combinations.py * add --------- Co-authored-by: Siddhant Jain --- backtracking/all_combinations.py | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/backtracking/all_combinations.py b/backtracking/all_combinations.py index 390decf3a05b..1d15c6263e14 100644 --- a/backtracking/all_combinations.py +++ b/backtracking/all_combinations.py @@ -12,6 +12,8 @@ def combination_lists(n: int, k: int) -> list[list[int]]: """ + Generates all possible combinations of k numbers out of 1 ... n using itertools. + >>> combination_lists(n=4, k=2) [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] """ @@ -20,6 +22,8 @@ def combination_lists(n: int, k: int) -> list[list[int]]: def generate_all_combinations(n: int, k: int) -> list[list[int]]: """ + Generates all possible combinations of k numbers out of 1 ... n using backtracking. + >>> generate_all_combinations(n=4, k=2) [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] >>> generate_all_combinations(n=0, k=0) @@ -34,6 +38,14 @@ def generate_all_combinations(n: int, k: int) -> list[list[int]]: ValueError: n must not be negative >>> generate_all_combinations(n=5, k=4) [[1, 2, 3, 4], [1, 2, 3, 5], [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]] + >>> generate_all_combinations(n=3, k=3) + [[1, 2, 3]] + >>> generate_all_combinations(n=3, k=1) + [[1], [2], [3]] + >>> generate_all_combinations(n=1, k=0) + [[]] + >>> generate_all_combinations(n=1, k=1) + [[1]] >>> from itertools import combinations >>> all(generate_all_combinations(n, k) == combination_lists(n, k) ... for n in range(1, 6) for k in range(1, 6)) @@ -56,6 +68,28 @@ def create_all_state( current_list: list[int], total_list: list[list[int]], ) -> None: + """ + Helper function to recursively build all combinations. + + >>> create_all_state(1, 4, 2, [], result := []) + >>> result + [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] + >>> create_all_state(1, 3, 3, [], result := []) + >>> result + [[1, 2, 3]] + >>> create_all_state(2, 2, 1, [1], result := []) + >>> result + [[1, 2]] + >>> create_all_state(1, 0, 0, [], result := []) + >>> result + [[]] + >>> create_all_state(1, 4, 0, [1, 2], result := []) + >>> result + [[1, 2]] + >>> create_all_state(5, 4, 2, [1, 2], result := []) + >>> result + [] + """ if level == 0: total_list.append(current_list[:]) return From cfcc84edf7d14cb56f52ba6fbd8c8deb2e9a7852 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 13 Jan 2025 23:49:07 +0300 Subject: [PATCH 067/126] Fix build (#12516) * Empty commit * Fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- web_programming/current_stock_price.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/web_programming/current_stock_price.py b/web_programming/current_stock_price.py index d0a65e9aac84..573e1f575c8e 100644 --- a/web_programming/current_stock_price.py +++ b/web_programming/current_stock_price.py @@ -15,7 +15,7 @@ def stock_price(symbol: str = "AAPL") -> str: """ >>> stock_price("EEEE") - '-' + '- ' >>> isinstance(float(stock_price("GOOG")),float) True """ @@ -24,12 +24,10 @@ def stock_price(symbol: str = "AAPL") -> str: url, headers={"USER-AGENT": "Mozilla/5.0"}, timeout=10 ).text soup = BeautifulSoup(yahoo_finance_source, "html.parser") - specific_fin_streamer_tag = soup.find("fin-streamer", {"data-testid": "qsp-price"}) - if specific_fin_streamer_tag: - text = specific_fin_streamer_tag.get_text() - return text - return "No tag with the specified data-test attribute found." + if specific_fin_streamer_tag := soup.find("span", {"data-testid": "qsp-price"}): + return specific_fin_streamer_tag.get_text() + return "No tag with the specified data-testid attribute found." # Search for the symbol at https://finance.yahoo.com/lookup From 4fe50bc1fcf82fceb61839bae314720c092c0692 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 21:52:12 +0100 Subject: [PATCH 068/126] [pre-commit.ci] pre-commit autoupdate -- ruff 2025 stable format (#12521) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.6 → v0.9.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.6...v0.9.1) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update maths/dual_number_automatic_differentiation.py * Update maths/dual_number_automatic_differentiation.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update dual_number_automatic_differentiation.py * Update dual_number_automatic_differentiation.py * No tag with the specified data-test attribute found. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 2 +- ciphers/base64_cipher.py | 12 +++--- ciphers/caesar_cipher.py | 2 +- computer_vision/flip_augmentation.py | 2 +- computer_vision/mosaic_augmentation.py | 2 +- .../hashing/number_theory/prime_numbers.py | 6 +-- data_structures/heap/min_heap.py | 6 +-- data_structures/kd_tree/tests/test_kdtree.py | 12 +++--- .../suffix_tree/tests/test_suffix_tree.py | 24 +++++------ dynamic_programming/climbing_stairs.py | 6 +-- .../iterating_through_submasks.py | 6 +-- .../matrix_chain_multiplication.py | 2 +- .../linear_discriminant_analysis.py | 4 +- .../dual_number_automatic_differentiation.py | 6 +-- maths/max_sum_sliding_window.py | 4 +- .../integration_by_simpson_approx.py | 12 +++--- maths/prime_check.py | 12 +++--- maths/primelib.py | 42 +++++++++---------- matrix/matrix_based_game.py | 2 +- neural_network/input_data.py | 6 +-- scripts/validate_solutions.py | 6 +-- strings/jaro_winkler.py | 4 +- web_programming/fetch_anime_and_play.py | 4 +- 23 files changed, 93 insertions(+), 91 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ec1dbca3a41c..3b1dd9658d7f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.6 + rev: v0.9.1 hooks: - id: ruff - id: ruff-format diff --git a/ciphers/base64_cipher.py b/ciphers/base64_cipher.py index 2b950b1be37d..038d13963d95 100644 --- a/ciphers/base64_cipher.py +++ b/ciphers/base64_cipher.py @@ -105,13 +105,13 @@ def base64_decode(encoded_data: str) -> bytes: # Check if the encoded string contains non base64 characters if padding: - assert all( - char in B64_CHARSET for char in encoded_data[:-padding] - ), "Invalid base64 character(s) found." + assert all(char in B64_CHARSET for char in encoded_data[:-padding]), ( + "Invalid base64 character(s) found." + ) else: - assert all( - char in B64_CHARSET for char in encoded_data - ), "Invalid base64 character(s) found." + assert all(char in B64_CHARSET for char in encoded_data), ( + "Invalid base64 character(s) found." + ) # Check the padding assert len(encoded_data) % 4 == 0 and padding < 3, "Incorrect padding" diff --git a/ciphers/caesar_cipher.py b/ciphers/caesar_cipher.py index 9c096fe8a7da..1cf4d67cbaed 100644 --- a/ciphers/caesar_cipher.py +++ b/ciphers/caesar_cipher.py @@ -225,7 +225,7 @@ def brute_force(input_string: str, alphabet: str | None = None) -> dict[int, str if __name__ == "__main__": while True: - print(f'\n{"-" * 10}\n Menu\n{"-" * 10}') + print(f"\n{'-' * 10}\n Menu\n{'-' * 10}") print(*["1.Encrypt", "2.Decrypt", "3.BruteForce", "4.Quit"], sep="\n") # get user input diff --git a/computer_vision/flip_augmentation.py b/computer_vision/flip_augmentation.py index 77a8cbd7b14f..7301424824df 100644 --- a/computer_vision/flip_augmentation.py +++ b/computer_vision/flip_augmentation.py @@ -33,7 +33,7 @@ def main() -> None: file_name = paths[index].split(os.sep)[-1].rsplit(".", 1)[0] file_root = f"{OUTPUT_DIR}/{file_name}_FLIP_{letter_code}" cv2.imwrite(f"{file_root}.jpg", image, [cv2.IMWRITE_JPEG_QUALITY, 85]) - print(f"Success {index+1}/{len(new_images)} with {file_name}") + print(f"Success {index + 1}/{len(new_images)} with {file_name}") annos_list = [] for anno in new_annos[index]: obj = f"{anno[0]} {anno[1]} {anno[2]} {anno[3]} {anno[4]}" diff --git a/computer_vision/mosaic_augmentation.py b/computer_vision/mosaic_augmentation.py index cd923dfe095f..d881347121ea 100644 --- a/computer_vision/mosaic_augmentation.py +++ b/computer_vision/mosaic_augmentation.py @@ -41,7 +41,7 @@ def main() -> None: file_name = path.split(os.sep)[-1].rsplit(".", 1)[0] file_root = f"{OUTPUT_DIR}/{file_name}_MOSAIC_{letter_code}" cv2.imwrite(f"{file_root}.jpg", new_image, [cv2.IMWRITE_JPEG_QUALITY, 85]) - print(f"Succeeded {index+1}/{NUMBER_IMAGES} with {file_name}") + print(f"Succeeded {index + 1}/{NUMBER_IMAGES} with {file_name}") annos_list = [] for anno in new_annos: width = anno[3] - anno[1] diff --git a/data_structures/hashing/number_theory/prime_numbers.py b/data_structures/hashing/number_theory/prime_numbers.py index 2549a1477b2b..82071b5e9f09 100644 --- a/data_structures/hashing/number_theory/prime_numbers.py +++ b/data_structures/hashing/number_theory/prime_numbers.py @@ -32,9 +32,9 @@ def is_prime(number: int) -> bool: """ # precondition - assert isinstance(number, int) and ( - number >= 0 - ), "'number' must been an int and positive" + assert isinstance(number, int) and (number >= 0), ( + "'number' must been an int and positive" + ) if 1 < number < 4: # 2 and 3 are primes diff --git a/data_structures/heap/min_heap.py b/data_structures/heap/min_heap.py index ce7ed570a58d..577b98d788a1 100644 --- a/data_structures/heap/min_heap.py +++ b/data_structures/heap/min_heap.py @@ -124,9 +124,9 @@ def is_empty(self): return len(self.heap) == 0 def decrease_key(self, node, new_value): - assert ( - self.heap[self.idx_of_element[node]].val > new_value - ), "newValue must be less that current value" + assert self.heap[self.idx_of_element[node]].val > new_value, ( + "newValue must be less that current value" + ) node.val = new_value self.heap_dict[node.name] = new_value self.sift_up(self.idx_of_element[node]) diff --git a/data_structures/kd_tree/tests/test_kdtree.py b/data_structures/kd_tree/tests/test_kdtree.py index dce5e4f34ff4..d6a4a66dd24d 100644 --- a/data_structures/kd_tree/tests/test_kdtree.py +++ b/data_structures/kd_tree/tests/test_kdtree.py @@ -48,14 +48,14 @@ def test_build_kdtree(num_points, cube_size, num_dimensions, depth, expected_res assert kdtree is not None, "Expected a KDNode, got None" # Check if root has correct dimensions - assert ( - len(kdtree.point) == num_dimensions - ), f"Expected point dimension {num_dimensions}, got {len(kdtree.point)}" + assert len(kdtree.point) == num_dimensions, ( + f"Expected point dimension {num_dimensions}, got {len(kdtree.point)}" + ) # Check that the tree is balanced to some extent (simplistic check) - assert isinstance( - kdtree, KDNode - ), f"Expected KDNode instance, got {type(kdtree)}" + assert isinstance(kdtree, KDNode), ( + f"Expected KDNode instance, got {type(kdtree)}" + ) def test_nearest_neighbour_search(): diff --git a/data_structures/suffix_tree/tests/test_suffix_tree.py b/data_structures/suffix_tree/tests/test_suffix_tree.py index 45c6790ac48a..c9dbe199d19d 100644 --- a/data_structures/suffix_tree/tests/test_suffix_tree.py +++ b/data_structures/suffix_tree/tests/test_suffix_tree.py @@ -22,18 +22,18 @@ def test_search_existing_patterns(self) -> None: patterns = ["ana", "ban", "na"] for pattern in patterns: with self.subTest(pattern=pattern): - assert self.suffix_tree.search( - pattern - ), f"Pattern '{pattern}' should be found." + assert self.suffix_tree.search(pattern), ( + f"Pattern '{pattern}' should be found." + ) def test_search_non_existing_patterns(self) -> None: """Test searching for patterns that do not exist in the suffix tree.""" patterns = ["xyz", "apple", "cat"] for pattern in patterns: with self.subTest(pattern=pattern): - assert not self.suffix_tree.search( - pattern - ), f"Pattern '{pattern}' should not be found." + assert not self.suffix_tree.search(pattern), ( + f"Pattern '{pattern}' should not be found." + ) def test_search_empty_pattern(self) -> None: """Test searching for an empty pattern.""" @@ -41,18 +41,18 @@ def test_search_empty_pattern(self) -> None: def test_search_full_text(self) -> None: """Test searching for the full text.""" - assert self.suffix_tree.search( - self.text - ), "The full text should be found in the suffix tree." + assert self.suffix_tree.search(self.text), ( + "The full text should be found in the suffix tree." + ) def test_search_substrings(self) -> None: """Test searching for substrings of the full text.""" substrings = ["ban", "ana", "a", "na"] for substring in substrings: with self.subTest(substring=substring): - assert self.suffix_tree.search( - substring - ), f"Substring '{substring}' should be found." + assert self.suffix_tree.search(substring), ( + f"Substring '{substring}' should be found." + ) if __name__ == "__main__": diff --git a/dynamic_programming/climbing_stairs.py b/dynamic_programming/climbing_stairs.py index d6273d025f08..38bdb427eedc 100644 --- a/dynamic_programming/climbing_stairs.py +++ b/dynamic_programming/climbing_stairs.py @@ -25,9 +25,9 @@ def climb_stairs(number_of_steps: int) -> int: ... AssertionError: number_of_steps needs to be positive integer, your input -7 """ - assert ( - isinstance(number_of_steps, int) and number_of_steps > 0 - ), f"number_of_steps needs to be positive integer, your input {number_of_steps}" + assert isinstance(number_of_steps, int) and number_of_steps > 0, ( + f"number_of_steps needs to be positive integer, your input {number_of_steps}" + ) if number_of_steps == 1: return 1 previous, current = 1, 1 diff --git a/dynamic_programming/iterating_through_submasks.py b/dynamic_programming/iterating_through_submasks.py index 372dd2c74a71..efab6dacff3f 100644 --- a/dynamic_programming/iterating_through_submasks.py +++ b/dynamic_programming/iterating_through_submasks.py @@ -37,9 +37,9 @@ def list_of_submasks(mask: int) -> list[int]: """ - assert ( - isinstance(mask, int) and mask > 0 - ), f"mask needs to be positive integer, your input {mask}" + assert isinstance(mask, int) and mask > 0, ( + f"mask needs to be positive integer, your input {mask}" + ) """ first submask iterated will be mask itself then operation will be performed diff --git a/dynamic_programming/matrix_chain_multiplication.py b/dynamic_programming/matrix_chain_multiplication.py index 10e136b9f0db..4c0c771f9092 100644 --- a/dynamic_programming/matrix_chain_multiplication.py +++ b/dynamic_programming/matrix_chain_multiplication.py @@ -134,7 +134,7 @@ def elapsed_time(msg: str) -> Iterator: start = perf_counter_ns() yield - print(f"Finished: {msg} in {(perf_counter_ns() - start) / 10 ** 9} seconds.") + print(f"Finished: {msg} in {(perf_counter_ns() - start) / 10**9} seconds.") if __name__ == "__main__": diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index 86f28aef671a..8528ccbbae51 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -322,7 +322,7 @@ def main(): user_count = valid_input( input_type=int, condition=lambda x: x > 0, - input_msg=(f"Enter The number of instances for class_{i+1}: "), + input_msg=(f"Enter The number of instances for class_{i + 1}: "), err_msg="Number of instances should be positive!", ) counts.append(user_count) @@ -333,7 +333,7 @@ def main(): for a in range(n_classes): user_mean = valid_input( input_type=float, - input_msg=(f"Enter the value of mean for class_{a+1}: "), + input_msg=(f"Enter the value of mean for class_{a + 1}: "), err_msg="This is an invalid value.", ) user_means.append(user_mean) diff --git a/maths/dual_number_automatic_differentiation.py b/maths/dual_number_automatic_differentiation.py index f98997c8be4d..09aeb17a4aea 100644 --- a/maths/dual_number_automatic_differentiation.py +++ b/maths/dual_number_automatic_differentiation.py @@ -17,10 +17,8 @@ def __init__(self, real, rank): self.duals = rank def __repr__(self): - return ( - f"{self.real}+" - f"{'+'.join(str(dual)+'E'+str(n+1)for n,dual in enumerate(self.duals))}" - ) + s = "+".join(f"{dual}E{n}" for n, dual in enumerate(self.duals, 1)) + return f"{self.real}+{s}" def reduce(self): cur = self.duals.copy() diff --git a/maths/max_sum_sliding_window.py b/maths/max_sum_sliding_window.py index 090117429604..c7492978a6c9 100644 --- a/maths/max_sum_sliding_window.py +++ b/maths/max_sum_sliding_window.py @@ -43,4 +43,6 @@ def max_sum_in_array(array: list[int], k: int) -> int: testmod() array = [randint(-1000, 1000) for i in range(100)] k = randint(0, 110) - print(f"The maximum sum of {k} consecutive elements is {max_sum_in_array(array,k)}") + print( + f"The maximum sum of {k} consecutive elements is {max_sum_in_array(array, k)}" + ) diff --git a/maths/numerical_analysis/integration_by_simpson_approx.py b/maths/numerical_analysis/integration_by_simpson_approx.py index 934299997aac..043f3a9a72af 100644 --- a/maths/numerical_analysis/integration_by_simpson_approx.py +++ b/maths/numerical_analysis/integration_by_simpson_approx.py @@ -88,18 +88,18 @@ def simpson_integration(function, a: float, b: float, precision: int = 4) -> flo AssertionError: precision should be positive integer your input : -1 """ - assert callable( - function - ), f"the function(object) passed should be callable your input : {function}" + assert callable(function), ( + f"the function(object) passed should be callable your input : {function}" + ) assert isinstance(a, (float, int)), f"a should be float or integer your input : {a}" assert isinstance(function(a), (float, int)), ( "the function should return integer or float return type of your function, " f"{type(a)}" ) assert isinstance(b, (float, int)), f"b should be float or integer your input : {b}" - assert ( - isinstance(precision, int) and precision > 0 - ), f"precision should be positive integer your input : {precision}" + assert isinstance(precision, int) and precision > 0, ( + f"precision should be positive integer your input : {precision}" + ) # just applying the formula of simpson for approximate integration written in # mentioned article in first comment of this file and above this function diff --git a/maths/prime_check.py b/maths/prime_check.py index f1bc4def2469..a757c4108f24 100644 --- a/maths/prime_check.py +++ b/maths/prime_check.py @@ -73,12 +73,12 @@ def test_primes(self): def test_not_primes(self): with pytest.raises(ValueError): is_prime(-19) - assert not is_prime( - 0 - ), "Zero doesn't have any positive factors, primes must have exactly two." - assert not is_prime( - 1 - ), "One only has 1 positive factor, primes must have exactly two." + assert not is_prime(0), ( + "Zero doesn't have any positive factors, primes must have exactly two." + ) + assert not is_prime(1), ( + "One only has 1 positive factor, primes must have exactly two." + ) assert not is_prime(2 * 2) assert not is_prime(2 * 3) assert not is_prime(3 * 3) diff --git a/maths/primelib.py b/maths/primelib.py index a26b0eaeb328..3a966e5cd936 100644 --- a/maths/primelib.py +++ b/maths/primelib.py @@ -66,9 +66,9 @@ def is_prime(number: int) -> bool: """ # precondition - assert isinstance(number, int) and ( - number >= 0 - ), "'number' must been an int and positive" + assert isinstance(number, int) and (number >= 0), ( + "'number' must been an int and positive" + ) status = True @@ -254,9 +254,9 @@ def greatest_prime_factor(number): """ # precondition - assert isinstance(number, int) and ( - number >= 0 - ), "'number' must been an int and >= 0" + assert isinstance(number, int) and (number >= 0), ( + "'number' must been an int and >= 0" + ) ans = 0 @@ -296,9 +296,9 @@ def smallest_prime_factor(number): """ # precondition - assert isinstance(number, int) and ( - number >= 0 - ), "'number' must been an int and >= 0" + assert isinstance(number, int) and (number >= 0), ( + "'number' must been an int and >= 0" + ) ans = 0 @@ -399,9 +399,9 @@ def goldbach(number): """ # precondition - assert ( - isinstance(number, int) and (number > 2) and is_even(number) - ), "'number' must been an int, even and > 2" + assert isinstance(number, int) and (number > 2) and is_even(number), ( + "'number' must been an int, even and > 2" + ) ans = [] # this list will returned @@ -525,9 +525,9 @@ def kg_v(number1, number2): done.append(n) # precondition - assert isinstance(ans, int) and ( - ans >= 0 - ), "'ans' must been from type int and positive" + assert isinstance(ans, int) and (ans >= 0), ( + "'ans' must been from type int and positive" + ) return ans @@ -574,9 +574,9 @@ def get_prime(n): ans += 1 # precondition - assert isinstance(ans, int) and is_prime( - ans - ), "'ans' must been a prime number and from type int" + assert isinstance(ans, int) and is_prime(ans), ( + "'ans' must been a prime number and from type int" + ) return ans @@ -705,9 +705,9 @@ def is_perfect_number(number): """ # precondition - assert isinstance(number, int) and ( - number > 1 - ), "'number' must been an int and >= 1" + assert isinstance(number, int) and (number > 1), ( + "'number' must been an int and >= 1" + ) divisors = get_divisors(number) diff --git a/matrix/matrix_based_game.py b/matrix/matrix_based_game.py index 1ff0cbe93435..6181086c6704 100644 --- a/matrix/matrix_based_game.py +++ b/matrix/matrix_based_game.py @@ -273,7 +273,7 @@ def process_game(size: int, matrix: list[str], moves: list[tuple[int, int]]) -> size = int(input("Enter the size of the matrix: ")) validate_matrix_size(size) print(f"Enter the {size} rows of the matrix:") - matrix = [input(f"Row {i+1}: ") for i in range(size)] + matrix = [input(f"Row {i + 1}: ") for i in range(size)] validate_matrix_content(matrix, size) moves_input = input("Enter the moves (e.g., '0 0, 1 1'): ") moves = parse_moves(moves_input) diff --git a/neural_network/input_data.py b/neural_network/input_data.py index 72debabb566a..3a8628f939f8 100644 --- a/neural_network/input_data.py +++ b/neural_network/input_data.py @@ -160,9 +160,9 @@ def __init__( self._num_examples = 10000 self.one_hot = one_hot else: - assert ( - images.shape[0] == labels.shape[0] - ), f"images.shape: {images.shape} labels.shape: {labels.shape}" + assert images.shape[0] == labels.shape[0], ( + f"images.shape: {images.shape} labels.shape: {labels.shape}" + ) self._num_examples = images.shape[0] # Convert shape from [num examples, rows, columns, depth] diff --git a/scripts/validate_solutions.py b/scripts/validate_solutions.py index 325c245e0d77..df5d01086bbe 100755 --- a/scripts/validate_solutions.py +++ b/scripts/validate_solutions.py @@ -94,6 +94,6 @@ def test_project_euler(solution_path: pathlib.Path) -> None: solution_module = convert_path_to_module(solution_path) answer = str(solution_module.solution()) answer = hashlib.sha256(answer.encode()).hexdigest() - assert ( - answer == expected - ), f"Expected solution to {problem_number} to have hash {expected}, got {answer}" + assert answer == expected, ( + f"Expected solution to {problem_number} to have hash {expected}, got {answer}" + ) diff --git a/strings/jaro_winkler.py b/strings/jaro_winkler.py index cae2068fabc1..0ce5d83b3c41 100644 --- a/strings/jaro_winkler.py +++ b/strings/jaro_winkler.py @@ -33,7 +33,9 @@ def get_matched_characters(_str1: str, _str2: str) -> str: right = int(min(i + limit + 1, len(_str2))) if char in _str2[left:right]: matched.append(char) - _str2 = f"{_str2[0:_str2.index(char)]} {_str2[_str2.index(char) + 1:]}" + _str2 = ( + f"{_str2[0 : _str2.index(char)]} {_str2[_str2.index(char) + 1 :]}" + ) return "".join(matched) diff --git a/web_programming/fetch_anime_and_play.py b/web_programming/fetch_anime_and_play.py index fd7c3a3a7381..e56b7124eeb5 100644 --- a/web_programming/fetch_anime_and_play.py +++ b/web_programming/fetch_anime_and_play.py @@ -165,7 +165,7 @@ def get_anime_episode(episode_endpoint: str) -> list: print(f"Found {len(anime_list)} results: ") for i, anime in enumerate(anime_list): anime_title = anime["title"] - print(f"{i+1}. {anime_title}") + print(f"{i + 1}. {anime_title}") anime_choice = int(input("\nPlease choose from the following list: ").strip()) chosen_anime = anime_list[anime_choice - 1] @@ -177,7 +177,7 @@ def get_anime_episode(episode_endpoint: str) -> list: else: print(f"Found {len(episode_list)} results: ") for i, episode in enumerate(episode_list): - print(f"{i+1}. {episode['title']}") + print(f"{i + 1}. {episode['title']}") episode_choice = int(input("\nChoose an episode by serial no: ").strip()) chosen_episode = episode_list[episode_choice - 1] From f04d308431266759dce36265d8701dfb106932af Mon Sep 17 00:00:00 2001 From: Sanjay Muthu Date: Wed, 15 Jan 2025 02:19:04 +0530 Subject: [PATCH 069/126] Create longest_increasing_subsequence_iterative.py (#12524) * Create longest_increasing_subsequence_iterative.py * Update longest_increasing_subsequence_iterative.py * Update longest_increasing_subsequence_iterative.py --------- Co-authored-by: Maxim Smolskiy --- ...ongest_increasing_subsequence_iterative.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 dynamic_programming/longest_increasing_subsequence_iterative.py diff --git a/dynamic_programming/longest_increasing_subsequence_iterative.py b/dynamic_programming/longest_increasing_subsequence_iterative.py new file mode 100644 index 000000000000..665c86a35d2e --- /dev/null +++ b/dynamic_programming/longest_increasing_subsequence_iterative.py @@ -0,0 +1,72 @@ +""" +Author : Sanjay Muthu + +This is a pure Python implementation of Dynamic Programming solution to the longest +increasing subsequence of a given sequence. + +The problem is: + Given an array, to find the longest and increasing sub-array in that given array and + return it. + +Example: + ``[10, 22, 9, 33, 21, 50, 41, 60, 80]`` as input will return + ``[10, 22, 33, 50, 60, 80]`` as output +""" + +from __future__ import annotations + +import copy + + +def longest_subsequence(array: list[int]) -> list[int]: + """ + Some examples + + >>> longest_subsequence([10, 22, 9, 33, 21, 50, 41, 60, 80]) + [10, 22, 33, 50, 60, 80] + >>> longest_subsequence([4, 8, 7, 5, 1, 12, 2, 3, 9]) + [1, 2, 3, 9] + >>> longest_subsequence([9, 8, 7, 6, 5, 7]) + [7, 7] + >>> longest_subsequence([28, 26, 12, 23, 35, 39]) + [12, 23, 35, 39] + >>> longest_subsequence([1, 1, 1]) + [1, 1, 1] + >>> longest_subsequence([]) + [] + """ + n = len(array) + # The longest increasing subsequence ending at array[i] + longest_increasing_subsequence = [] + for i in range(n): + longest_increasing_subsequence.append([array[i]]) + + for i in range(1, n): + for prev in range(i): + # If array[prev] is less than or equal to array[i], then + # longest_increasing_subsequence[prev] + array[i] + # is a valid increasing subsequence + + # longest_increasing_subsequence[i] is only set to + # longest_increasing_subsequence[prev] + array[i] if the length is longer. + + if array[prev] <= array[i] and len( + longest_increasing_subsequence[prev] + ) + 1 > len(longest_increasing_subsequence[i]): + longest_increasing_subsequence[i] = copy.copy( + longest_increasing_subsequence[prev] + ) + longest_increasing_subsequence[i].append(array[i]) + + result: list[int] = [] + for i in range(n): + if len(longest_increasing_subsequence[i]) > len(result): + result = longest_increasing_subsequence[i] + + return result + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 0040ad47f928f299dadbb97c5cea00bc1daf8c75 Mon Sep 17 00:00:00 2001 From: aydinomer00 <109145643+aydinomer00@users.noreply.github.com> Date: Wed, 15 Jan 2025 00:24:36 +0300 Subject: [PATCH 070/126] Add butterfly pattern implementation (#12493) * Add butterfly pattern implementation * Add butterfly pattern implementation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add finalized butterfly pattern implementation and test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Delete graphics/test_butterfly_pattern.py * Update butterfly_pattern.py * Update butterfly_pattern.py * Update butterfly_pattern.py * Update butterfly_pattern.py * Update butterfly_pattern.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- graphics/butterfly_pattern.py | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 graphics/butterfly_pattern.py diff --git a/graphics/butterfly_pattern.py b/graphics/butterfly_pattern.py new file mode 100644 index 000000000000..7913b03a7e95 --- /dev/null +++ b/graphics/butterfly_pattern.py @@ -0,0 +1,46 @@ +def butterfly_pattern(n: int) -> str: + """ + Creates a butterfly pattern of size n and returns it as a string. + + >>> print(butterfly_pattern(3)) + * * + ** ** + ***** + ** ** + * * + >>> print(butterfly_pattern(5)) + * * + ** ** + *** *** + **** **** + ********* + **** **** + *** *** + ** ** + * * + """ + result = [] + + # Upper part + for i in range(1, n): + left_stars = "*" * i + spaces = " " * (2 * (n - i) - 1) + right_stars = "*" * i + result.append(left_stars + spaces + right_stars) + + # Middle part + result.append("*" * (2 * n - 1)) + + # Lower part + for i in range(n - 1, 0, -1): + left_stars = "*" * i + spaces = " " * (2 * (n - i) - 1) + right_stars = "*" * i + result.append(left_stars + spaces + right_stars) + + return "\n".join(result) + + +if __name__ == "__main__": + n = int(input("Enter the size of the butterfly pattern: ")) + print(butterfly_pattern(n)) From 533767ff46bbcf5c594ff8196894ae2e8130bc3e Mon Sep 17 00:00:00 2001 From: Nguyen Thi Thanh Minh <140883075+minh-swinburne@users.noreply.github.com> Date: Sat, 18 Jan 2025 10:07:44 +0700 Subject: [PATCH 071/126] Doomsday Algorithm: Fix leap year check (#12396) * Fix leap year check Replace `!=` in `(year % 400) != 0` (line 49) with `==` Justification: Years that are divisible by 100 (centurian == 100) but not by 400 (year % 400 != 0) are skipped and NOT leap year. * Update parentheses Correct the parentheses to make clear the precedence of the conditional check * Update other/doomsday.py Co-authored-by: Tianyi Zheng --------- Co-authored-by: Tianyi Zheng --- other/doomsday.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/other/doomsday.py b/other/doomsday.py index d8fe261156a1..be3b18eeecaa 100644 --- a/other/doomsday.py +++ b/other/doomsday.py @@ -46,7 +46,7 @@ def get_week_day(year: int, month: int, day: int) -> str: ) % 7 day_anchor = ( DOOMSDAY_NOT_LEAP[month - 1] - if (year % 4 != 0) or (centurian == 0 and (year % 400) == 0) + if year % 4 != 0 or (centurian == 0 and year % 400 != 0) else DOOMSDAY_LEAP[month - 1] ) week_day = (dooms_day + day - day_anchor) % 7 From 91ebea1d99735ee2798b01ebcea0fc06e9a6af49 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 19 Jan 2025 08:33:35 +0100 Subject: [PATCH 072/126] Sphinx runs on ubuntu 24.04 arm (#12530) * Speed up our Sphinx GitHub Action with ARM # `runs-on: ubuntu-24.04-arm` https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources * updating DIRECTORY.md --------- Co-authored-by: cclauss --- .github/workflows/sphinx.yml | 2 +- DIRECTORY.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index d02435d98028..16ff284a74f2 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -23,7 +23,7 @@ concurrency: jobs: build_docs: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm steps: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v5 diff --git a/DIRECTORY.md b/DIRECTORY.md index aad6c72aa8ee..941e30dfe721 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -377,6 +377,7 @@ * [Longest Common Subsequence](dynamic_programming/longest_common_subsequence.py) * [Longest Common Substring](dynamic_programming/longest_common_substring.py) * [Longest Increasing Subsequence](dynamic_programming/longest_increasing_subsequence.py) + * [Longest Increasing Subsequence Iterative](dynamic_programming/longest_increasing_subsequence_iterative.py) * [Longest Increasing Subsequence O Nlogn](dynamic_programming/longest_increasing_subsequence_o_nlogn.py) * [Longest Palindromic Subsequence](dynamic_programming/longest_palindromic_subsequence.py) * [Matrix Chain Multiplication](dynamic_programming/matrix_chain_multiplication.py) @@ -462,6 +463,7 @@ ## Graphics * [Bezier Curve](graphics/bezier_curve.py) + * [Butterfly Pattern](graphics/butterfly_pattern.py) * [Digital Differential Analyzer Line](graphics/digital_differential_analyzer_line.py) * [Vector3 For 2D Rendering](graphics/vector3_for_2d_rendering.py) From 1f74db0c06df7557e7ae3a17ebcc303f753f824e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 21:22:02 +0100 Subject: [PATCH 073/126] [pre-commit.ci] pre-commit autoupdate (#12536) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.1 → v0.9.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.1...v0.9.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3b1dd9658d7f..c4480f47faa1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.1 + rev: v0.9.2 hooks: - id: ruff - id: ruff-format From 9fb51b4169e0f7a4952e9eb460b91f4d7ffb819f Mon Sep 17 00:00:00 2001 From: Ronald Ngounou <74538524+ronaldngounou@users.noreply.github.com> Date: Thu, 23 Jan 2025 09:02:46 +0100 Subject: [PATCH 074/126] Update docstrings in the functions definitions. (#11797) --- data_structures/arrays/sudoku_solver.py | 61 +++++++++++++++++-------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index fd1a4f3e37b8..e1714e57ece8 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -9,7 +9,9 @@ def cross(items_a, items_b): - "Cross product of elements in A and elements in B." + """ + Cross product of elements in A and elements in B. + """ return [a + b for a in items_a for b in items_b] @@ -27,7 +29,7 @@ def cross(items_a, items_b): def test(): - "A set of unit tests." + """A set of unit tests.""" assert len(squares) == 81 assert len(unitlist) == 27 assert all(len(units[s]) == 3 for s in squares) @@ -47,8 +49,10 @@ def test(): def parse_grid(grid): - """Convert grid to a dict of possible values, {square: digits}, or - return False if a contradiction is detected.""" + """ + Convert grid to a dict of possible values, {square: digits}, or + return False if a contradiction is detected. + """ ## To start, every square can be any digit; then assign values from the grid. values = {s: digits for s in squares} for s, d in grid_values(grid).items(): @@ -58,15 +62,19 @@ def parse_grid(grid): def grid_values(grid): - "Convert grid into a dict of {square: char} with '0' or '.' for empties." + """ + Convert grid into a dict of {square: char} with '0' or '.' for empties. + """ chars = [c for c in grid if c in digits or c in "0."] assert len(chars) == 81 return dict(zip(squares, chars)) def assign(values, s, d): - """Eliminate all the other values (except d) from values[s] and propagate. - Return values, except return False if a contradiction is detected.""" + """ + Eliminate all the other values (except d) from values[s] and propagate. + Return values, except return False if a contradiction is detected. + """ other_values = values[s].replace(d, "") if all(eliminate(values, s, d2) for d2 in other_values): return values @@ -75,8 +83,10 @@ def assign(values, s, d): def eliminate(values, s, d): - """Eliminate d from values[s]; propagate when values or places <= 2. - Return values, except return False if a contradiction is detected.""" + """ + Eliminate d from values[s]; propagate when values or places <= 2. + Return values, except return False if a contradiction is detected. + """ if d not in values[s]: return values ## Already eliminated values[s] = values[s].replace(d, "") @@ -99,7 +109,9 @@ def eliminate(values, s, d): def display(values): - "Display these values as a 2-D grid." + """ + Display these values as a 2-D grid. + """ width = 1 + max(len(values[s]) for s in squares) line = "+".join(["-" * (width * 3)] * 3) for r in rows: @@ -114,11 +126,14 @@ def display(values): def solve(grid): + """ + Solve the grid. + """ return search(parse_grid(grid)) def some(seq): - "Return some element of seq that is true." + """Return some element of seq that is true.""" for e in seq: if e: return e @@ -126,7 +141,9 @@ def some(seq): def search(values): - "Using depth-first search and propagation, try all possible values." + """ + Using depth-first search and propagation, try all possible values. + """ if values is False: return False ## Failed earlier if all(len(values[s]) == 1 for s in squares): @@ -137,9 +154,11 @@ def search(values): def solve_all(grids, name="", showif=0.0): - """Attempt to solve a sequence of grids. Report results. + """ + Attempt to solve a sequence of grids. Report results. When showif is a number of seconds, display puzzles that take longer. - When showif is None, don't display any puzzles.""" + When showif is None, don't display any puzzles. + """ def time_solve(grid): start = time.monotonic() @@ -162,7 +181,9 @@ def time_solve(grid): def solved(values): - "A puzzle is solved if each unit is a permutation of the digits 1 to 9." + """ + A puzzle is solved if each unit is a permutation of the digits 1 to 9. + """ def unitsolved(unit): return {values[s] for s in unit} == set(digits) @@ -177,9 +198,11 @@ def from_file(filename, sep="\n"): def random_puzzle(assignments=17): - """Make a random puzzle with N or more assignments. Restart on contradictions. + """ + Make a random puzzle with N or more assignments. Restart on contradictions. Note the resulting puzzle is not guaranteed to be solvable, but empirically - about 99.8% of them are solvable. Some have multiple solutions.""" + about 99.8% of them are solvable. Some have multiple solutions. + """ values = {s: digits for s in squares} for s in shuffled(squares): if not assign(values, s, random.choice(values[s])): @@ -191,7 +214,9 @@ def random_puzzle(assignments=17): def shuffled(seq): - "Return a randomly shuffled copy of the input sequence." + """ + Return a randomly shuffled copy of the input sequence. + """ seq = list(seq) random.shuffle(seq) return seq From c666db3729b6d9f73e2f7756a3974f53279caa50 Mon Sep 17 00:00:00 2001 From: Vijayalaxmi Wakode Date: Fri, 24 Jan 2025 03:31:47 +0530 Subject: [PATCH 075/126] Add Doc test bubble sort (#12070) * The string manipulation - replace() * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update replace.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updating DIRECTORY.md * Add doc test to bubble_sort * Update DIRECTORY.md * Delete strings/replace.py * Update bubble_sort.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: vijayalaxmi777 Co-authored-by: Maxim Smolskiy --- sorts/bubble_sort.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sorts/bubble_sort.py b/sorts/bubble_sort.py index bdf85c70dd35..9ec3d5384f38 100644 --- a/sorts/bubble_sort.py +++ b/sorts/bubble_sort.py @@ -85,6 +85,8 @@ def bubble_sort_recursive(collection: list[Any]) -> list[Any]: [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7] >>> bubble_sort_recursive([1, 3.3, 5, 7.7, 2, 4.4, 6]) [1, 2, 3.3, 4.4, 5, 6, 7.7] + >>> bubble_sort_recursive(['a', 'Z', 'B', 'C', 'A', 'c']) + ['A', 'B', 'C', 'Z', 'a', 'c'] >>> import random >>> collection_arg = random.sample(range(-50, 50), 100) >>> bubble_sort_recursive(collection_arg) == sorted(collection_arg) From 13e4d3e76cfaa74d8b14314d319fb6c089aa051e Mon Sep 17 00:00:00 2001 From: Rachel Spears <103690982+Rosepetal2022@users.noreply.github.com> Date: Thu, 23 Jan 2025 21:59:36 -0800 Subject: [PATCH 076/126] Fix error in avl_tree del_node function (#11510) * fixed error in del_node function * Update avl_tree.py --------- Co-authored-by: Maxim Smolskiy --- data_structures/binary_tree/avl_tree.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index 9fca7237404c..8558305eefe4 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -221,6 +221,10 @@ def del_node(root: MyNode, data: Any) -> MyNode | None: else: root.set_right(del_node(right_child, data)) + # Re-fetch left_child and right_child references + left_child = root.get_left() + right_child = root.get_right() + if get_height(right_child) - get_height(left_child) == 2: assert right_child is not None if get_height(right_child.get_right()) > get_height(right_child.get_left()): From 6c92c5a539276d387b85eedc89be1f888962647d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 22:05:20 +0100 Subject: [PATCH 077/126] [pre-commit.ci] pre-commit autoupdate (#12542) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.2 → v0.9.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.2...v0.9.3) - [github.com/codespell-project/codespell: v2.3.0 → v2.4.0](https://github.com/codespell-project/codespell/compare/v2.3.0...v2.4.0) * Update trifid_cipher.py * Update pyproject.toml * Update trifid_cipher.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 4 ++-- ciphers/trifid_cipher.py | 4 ++-- pyproject.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c4480f47faa1..e34b563b05dd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,13 +16,13 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.2 + rev: v0.9.3 hooks: - id: ruff - id: ruff-format - repo: https://github.com/codespell-project/codespell - rev: v2.3.0 + rev: v2.4.0 hooks: - id: codespell additional_dependencies: diff --git a/ciphers/trifid_cipher.py b/ciphers/trifid_cipher.py index 9613cee0669d..13a47e9dd03b 100644 --- a/ciphers/trifid_cipher.py +++ b/ciphers/trifid_cipher.py @@ -88,7 +88,7 @@ def __prepare( ... KeyError: 'Length of alphabet has to be 27.' - Testing with punctuations that are not in the given alphabet + Testing with punctuation not in the given alphabet >>> __prepare('am i a boy?','abCdeFghijkLmnopqrStuVwxYZ+') Traceback (most recent call last): @@ -128,7 +128,7 @@ def encrypt_message( encrypt_message =============== - Encrypts a message using the trifid_cipher. Any punctuatuions that + Encrypts a message using the trifid_cipher. Any punctuatuion chars that would be used should be added to the alphabet. PARAMETERS diff --git a/pyproject.toml b/pyproject.toml index 7b7176705c44..2135f1f5825a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -159,7 +159,7 @@ lint.pylint.max-returns = 8 # default: 6 lint.pylint.max-statements = 88 # default: 50 [tool.codespell] -ignore-words-list = "3rt,ans,bitap,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" +ignore-words-list = "3rt,abd,aer,ans,bitap,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" skip = "./.*,*.json,*.lock,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" [tool.pytest.ini_options] From e59d819d091efdb30e385f4ecfe9ab5d36c3be71 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2025 20:47:41 +0100 Subject: [PATCH 078/126] [pre-commit.ci] pre-commit autoupdate (#12554) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.3 → v0.9.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.3...v0.9.4) - [github.com/codespell-project/codespell: v2.4.0 → v2.4.1](https://github.com/codespell-project/codespell/compare/v2.4.0...v2.4.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e34b563b05dd..d9477e216b96 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,13 +16,13 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.3 + rev: v0.9.4 hooks: - id: ruff - id: ruff-format - repo: https://github.com/codespell-project/codespell - rev: v2.4.0 + rev: v2.4.1 hooks: - id: codespell additional_dependencies: From 338cbafe0d5b07d57f83060ea0f9ba3a6c1155e7 Mon Sep 17 00:00:00 2001 From: lighting9999 <120090117+lighting9999@users.noreply.github.com> Date: Mon, 10 Feb 2025 01:51:18 +0800 Subject: [PATCH 079/126] Improve power.py (#12567) * Fix And Add power.py To fix the inaccuracies and allow handling of negative exponents and bases, the key issue lies in how negative numbers are handled in the power calculation, especially when dividing. ## Example Output: ```python >>> power(4, 6) 4096 >>> power(2, 3) 8 >>> power(-2, 3) -8 >>> power(2, -3) 0.125 >>> power(-2, -3) -0.125 ``` * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update power.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update power.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update power.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update power.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- divide_and_conquer/power.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/divide_and_conquer/power.py b/divide_and_conquer/power.py index faf6a3476d40..492ee6dd12f0 100644 --- a/divide_and_conquer/power.py +++ b/divide_and_conquer/power.py @@ -1,4 +1,4 @@ -def actual_power(a: int, b: int): +def actual_power(a: int, b: int) -> int: """ Function using divide and conquer to calculate a^b. It only works for integer a,b. @@ -19,10 +19,12 @@ def actual_power(a: int, b: int): """ if b == 0: return 1 + half = actual_power(a, b // 2) + if (b % 2) == 0: - return actual_power(a, int(b / 2)) * actual_power(a, int(b / 2)) + return half * half else: - return a * actual_power(a, int(b / 2)) * actual_power(a, int(b / 2)) + return a * half * half def power(a: int, b: int) -> float: @@ -43,9 +45,9 @@ def power(a: int, b: int) -> float: -0.125 """ if b < 0: - return 1 / actual_power(a, b) + return 1 / actual_power(a, -b) return actual_power(a, b) if __name__ == "__main__": - print(power(-2, -3)) + print(power(-2, -3)) # output -0.125 From 738253e80030ffdd35ac57ff64cda816f85eda71 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Feb 2025 10:05:23 +0100 Subject: [PATCH 080/126] git mv data_structures/queue data_structures/queues (#12577) Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 18 +++++++++--------- data_structures/{queue => queues}/__init__.py | 0 .../{queue => queues}/circular_queue.py | 10 +++++++--- .../circular_queue_linked_list.py | 0 .../{queue => queues}/double_ended_queue.py | 0 .../{queue => queues}/linked_queue.py | 0 .../priority_queue_using_list.py | 6 +++--- .../{queue => queues}/queue_by_list.py | 0 .../{queue => queues}/queue_by_two_stacks.py | 0 .../{queue => queues}/queue_on_pseudo_stack.py | 0 11 files changed, 21 insertions(+), 17 deletions(-) rename data_structures/{queue => queues}/__init__.py (100%) rename data_structures/{queue => queues}/circular_queue.py (87%) rename data_structures/{queue => queues}/circular_queue_linked_list.py (100%) rename data_structures/{queue => queues}/double_ended_queue.py (100%) rename data_structures/{queue => queues}/linked_queue.py (100%) rename data_structures/{queue => queues}/priority_queue_using_list.py (96%) rename data_structures/{queue => queues}/queue_by_list.py (100%) rename data_structures/{queue => queues}/queue_by_two_stacks.py (100%) rename data_structures/{queue => queues}/queue_on_pseudo_stack.py (100%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d9477e216b96..a603109fd79f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.4 + rev: v0.9.6 hooks: - id: ruff - id: ruff-format @@ -47,7 +47,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.14.1 + rev: v1.15.0 hooks: - id: mypy args: diff --git a/DIRECTORY.md b/DIRECTORY.md index 941e30dfe721..a535f12cb59a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -275,15 +275,15 @@ * [Singly Linked List](data_structures/linked_list/singly_linked_list.py) * [Skip List](data_structures/linked_list/skip_list.py) * [Swap Nodes](data_structures/linked_list/swap_nodes.py) - * Queue - * [Circular Queue](data_structures/queue/circular_queue.py) - * [Circular Queue Linked List](data_structures/queue/circular_queue_linked_list.py) - * [Double Ended Queue](data_structures/queue/double_ended_queue.py) - * [Linked Queue](data_structures/queue/linked_queue.py) - * [Priority Queue Using List](data_structures/queue/priority_queue_using_list.py) - * [Queue By List](data_structures/queue/queue_by_list.py) - * [Queue By Two Stacks](data_structures/queue/queue_by_two_stacks.py) - * [Queue On Pseudo Stack](data_structures/queue/queue_on_pseudo_stack.py) + * Queues + * [Circular Queue](data_structures/queues/circular_queue.py) + * [Circular Queue Linked List](data_structures/queues/circular_queue_linked_list.py) + * [Double Ended Queue](data_structures/queues/double_ended_queue.py) + * [Linked Queue](data_structures/queues/linked_queue.py) + * [Priority Queue Using List](data_structures/queues/priority_queue_using_list.py) + * [Queue By List](data_structures/queues/queue_by_list.py) + * [Queue By Two Stacks](data_structures/queues/queue_by_two_stacks.py) + * [Queue On Pseudo Stack](data_structures/queues/queue_on_pseudo_stack.py) * Stacks * [Balanced Parentheses](data_structures/stacks/balanced_parentheses.py) * [Dijkstras Two Stack Algorithm](data_structures/stacks/dijkstras_two_stack_algorithm.py) diff --git a/data_structures/queue/__init__.py b/data_structures/queues/__init__.py similarity index 100% rename from data_structures/queue/__init__.py rename to data_structures/queues/__init__.py diff --git a/data_structures/queue/circular_queue.py b/data_structures/queues/circular_queue.py similarity index 87% rename from data_structures/queue/circular_queue.py rename to data_structures/queues/circular_queue.py index f2fb4c01e467..efbf1efdc42d 100644 --- a/data_structures/queue/circular_queue.py +++ b/data_structures/queues/circular_queue.py @@ -17,7 +17,9 @@ def __len__(self) -> int: >>> len(cq) 0 >>> cq.enqueue("A") # doctest: +ELLIPSIS - >> cq.array + ['A', None, None, None, None] >>> len(cq) 1 """ @@ -51,11 +53,13 @@ def enqueue(self, data): as an index. >>> cq = CircularQueue(5) >>> cq.enqueue("A") # doctest: +ELLIPSIS - >> (cq.size, cq.first()) (1, 'A') >>> cq.enqueue("B") # doctest: +ELLIPSIS - >> cq.array + ['A', 'B', None, None, None] >>> (cq.size, cq.first()) (2, 'A') """ diff --git a/data_structures/queue/circular_queue_linked_list.py b/data_structures/queues/circular_queue_linked_list.py similarity index 100% rename from data_structures/queue/circular_queue_linked_list.py rename to data_structures/queues/circular_queue_linked_list.py diff --git a/data_structures/queue/double_ended_queue.py b/data_structures/queues/double_ended_queue.py similarity index 100% rename from data_structures/queue/double_ended_queue.py rename to data_structures/queues/double_ended_queue.py diff --git a/data_structures/queue/linked_queue.py b/data_structures/queues/linked_queue.py similarity index 100% rename from data_structures/queue/linked_queue.py rename to data_structures/queues/linked_queue.py diff --git a/data_structures/queue/priority_queue_using_list.py b/data_structures/queues/priority_queue_using_list.py similarity index 96% rename from data_structures/queue/priority_queue_using_list.py rename to data_structures/queues/priority_queue_using_list.py index f61b5e8e664d..15e56c557069 100644 --- a/data_structures/queue/priority_queue_using_list.py +++ b/data_structures/queues/priority_queue_using_list.py @@ -59,12 +59,12 @@ class FixedPriorityQueue: >>> fpq.dequeue() Traceback (most recent call last): ... - data_structures.queue.priority_queue_using_list.UnderFlowError: All queues are empty + data_structures.queues.priority_queue_using_list.UnderFlowError: All queues are empty >>> print(fpq) Priority 0: [] Priority 1: [] Priority 2: [] - """ + """ # noqa: E501 def __init__(self): self.queues = [ @@ -141,7 +141,7 @@ class ElementPriorityQueue: >>> epq.dequeue() Traceback (most recent call last): ... - data_structures.queue.priority_queue_using_list.UnderFlowError: The queue is empty + data_structures.queues.priority_queue_using_list.UnderFlowError: The queue is empty >>> print(epq) [] """ diff --git a/data_structures/queue/queue_by_list.py b/data_structures/queues/queue_by_list.py similarity index 100% rename from data_structures/queue/queue_by_list.py rename to data_structures/queues/queue_by_list.py diff --git a/data_structures/queue/queue_by_two_stacks.py b/data_structures/queues/queue_by_two_stacks.py similarity index 100% rename from data_structures/queue/queue_by_two_stacks.py rename to data_structures/queues/queue_by_two_stacks.py diff --git a/data_structures/queue/queue_on_pseudo_stack.py b/data_structures/queues/queue_on_pseudo_stack.py similarity index 100% rename from data_structures/queue/queue_on_pseudo_stack.py rename to data_structures/queues/queue_on_pseudo_stack.py From a5aed92b4c20fd3e99c6e7a9202afcc9cf502883 Mon Sep 17 00:00:00 2001 From: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Date: Thu, 20 Feb 2025 21:09:01 +0100 Subject: [PATCH 081/126] fix: typo in data_structures/linked_list/from_sequence.py (#12584) --- data_structures/linked_list/from_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/linked_list/from_sequence.py b/data_structures/linked_list/from_sequence.py index 94b44f15037f..fa43f4d10e08 100644 --- a/data_structures/linked_list/from_sequence.py +++ b/data_structures/linked_list/from_sequence.py @@ -1,4 +1,4 @@ -# Recursive Prorgam to create a Linked List from a sequence and +# Recursive Program to create a Linked List from a sequence and # print a string representation of it. From 183fa06f40e80c6e86ceda6e7c7d23eaf91507ac Mon Sep 17 00:00:00 2001 From: sector <104625848+infrablue1@users.noreply.github.com> Date: Sat, 22 Feb 2025 16:16:29 +0800 Subject: [PATCH 082/126] Fix n-queens problem (#12583) * Fix n-queens problem * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update n_queens.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update n_queens.py * Update n_queens.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- backtracking/n_queens.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/backtracking/n_queens.py b/backtracking/n_queens.py index 81668b17a0ac..d10181f319b3 100644 --- a/backtracking/n_queens.py +++ b/backtracking/n_queens.py @@ -27,21 +27,28 @@ def is_safe(board: list[list[int]], row: int, column: int) -> bool: >>> is_safe([[0, 0, 0], [0, 0, 0], [0, 0, 0]], 1, 1) True + >>> is_safe([[0, 1, 0], [0, 0, 0], [0, 0, 0]], 1, 1) + False >>> is_safe([[1, 0, 0], [0, 0, 0], [0, 0, 0]], 1, 1) False + >>> is_safe([[0, 0, 1], [0, 0, 0], [0, 0, 0]], 1, 1) + False """ n = len(board) # Size of the board - # Check if there is any queen in the same row, column, - # left upper diagonal, and right upper diagonal + # Check if there is any queen in the same upper column, + # left upper diagonal and right upper diagonal return ( - all(board[i][j] != 1 for i, j in zip(range(row, -1, -1), range(column, n))) + all(board[i][j] != 1 for i, j in zip(range(row), [column] * row)) + and all( + board[i][j] != 1 + for i, j in zip(range(row - 1, -1, -1), range(column - 1, -1, -1)) + ) and all( - board[i][j] != 1 for i, j in zip(range(row, -1, -1), range(column, -1, -1)) + board[i][j] != 1 + for i, j in zip(range(row - 1, -1, -1), range(column + 1, n)) ) - and all(board[i][j] != 1 for i, j in zip(range(row, n), range(column, n))) - and all(board[i][j] != 1 for i, j in zip(range(row, n), range(column, -1, -1))) ) From 114d4283b98e52396e2460c802f18d45eeacd90c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 19:27:10 +0100 Subject: [PATCH 083/126] [pre-commit.ci] pre-commit autoupdate (#12591) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.6 → v0.9.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.6...v0.9.7) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a603109fd79f..8de90b11767f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.6 + rev: v0.9.7 hooks: - id: ruff - id: ruff-format From f528ce350b366ce40e0494fc94da65cfd4509c7d Mon Sep 17 00:00:00 2001 From: Sanjay Muthu Date: Thu, 27 Feb 2025 17:01:08 +0530 Subject: [PATCH 084/126] Added dynamic_programming/range_sum_query.py (#12592) * Create prefix_sum.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix pre-commit and ruff errors * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Rename prefix_sum.py to range_sum_query.py * Refactor description * Fix * Refactor code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- dynamic_programming/range_sum_query.py | 92 ++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 dynamic_programming/range_sum_query.py diff --git a/dynamic_programming/range_sum_query.py b/dynamic_programming/range_sum_query.py new file mode 100644 index 000000000000..484fcf785fda --- /dev/null +++ b/dynamic_programming/range_sum_query.py @@ -0,0 +1,92 @@ +""" +Author: Sanjay Muthu + +This is an implementation of the Dynamic Programming solution to the Range Sum Query. + +The problem statement is: + Given an array and q queries, + each query stating you to find the sum of elements from l to r (inclusive) + +Example: + arr = [1, 4, 6, 2, 61, 12] + queries = 3 + l_1 = 2, r_1 = 5 + l_2 = 1, r_2 = 5 + l_3 = 3, r_3 = 4 + + as input will return + + [81, 85, 63] + + as output + +0-indexing: +NOTE: 0-indexing means the indexing of the array starts from 0 +Example: a = [1, 2, 3, 4, 5, 6] + Here, the 0th index of a is 1, + the 1st index of a is 2, + and so forth + +Time Complexity: O(N + Q) +* O(N) pre-calculation time to calculate the prefix sum array +* and O(1) time per each query = O(1 * Q) = O(Q) time + +Space Complexity: O(N) +* O(N) to store the prefix sum + +Algorithm: +So, first we calculate the prefix sum (dp) of the array. +The prefix sum of the index i is the sum of all elements indexed +from 0 to i (inclusive). +The prefix sum of the index i is the prefix sum of index (i - 1) + the current element. +So, the state of the dp is dp[i] = dp[i - 1] + a[i]. + +After we calculate the prefix sum, +for each query [l, r] +the answer is dp[r] - dp[l - 1] (we need to be careful because l might be 0). +For example take this array: + [4, 2, 1, 6, 3] +The prefix sum calculated for this array would be: + [4, 4 + 2, 4 + 2 + 1, 4 + 2 + 1 + 6, 4 + 2 + 1 + 6 + 3] + ==> [4, 6, 7, 13, 16] +If the query was l = 3, r = 4, +the answer would be 6 + 3 = 9 but this would require O(r - l + 1) time ≈ O(N) time + +If we use prefix sums we can find it in O(1) by using the formula +prefix[r] - prefix[l - 1]. +This formula works because prefix[r] is the sum of elements from [0, r] +and prefix[l - 1] is the sum of elements from [0, l - 1], +so if we do prefix[r] - prefix[l - 1] it will be +[0, r] - [0, l - 1] = [0, l - 1] + [l, r] - [0, l - 1] = [l, r] +""" + + +def prefix_sum(array: list[int], queries: list[tuple[int, int]]) -> list[int]: + """ + >>> prefix_sum([1, 4, 6, 2, 61, 12], [(2, 5), (1, 5), (3, 4)]) + [81, 85, 63] + >>> prefix_sum([4, 2, 1, 6, 3], [(3, 4), (1, 3), (0, 2)]) + [9, 9, 7] + """ + # The prefix sum array + dp = [0] * len(array) + dp[0] = array[0] + for i in range(1, len(array)): + dp[i] = dp[i - 1] + array[i] + + # See Algorithm section (Line 44) + result = [] + for query in queries: + left, right = query + res = dp[right] + if left > 0: + res -= dp[left - 1] + result.append(res) + + return result + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 8826ad3a4d75f7a4e1d7b1a682682528c2c73672 Mon Sep 17 00:00:00 2001 From: PARIKSHIT SINGH <90330646+parikshit2111@users.noreply.github.com> Date: Sun, 2 Mar 2025 16:33:12 +0530 Subject: [PATCH 085/126] feat: Implement Principal Component Analysis (PCA) (#12596) - Added PCA implementation with dataset standardization. - Used Singular Value Decomposition (SVD) for computing principal components. - Fixed import sorting to comply with PEP 8 (Ruff I001). - Ensured type hints and docstrings for better readability. - Added doctests to validate correctness. - Passed all Ruff checks and automated tests. --- DIRECTORY.md | 2 + .../principle_component_analysis.py | 85 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 machine_learning/principle_component_analysis.py diff --git a/DIRECTORY.md b/DIRECTORY.md index a535f12cb59a..ab3259b9a766 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -395,6 +395,7 @@ * [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py) * [Optimal Binary Search Tree](dynamic_programming/optimal_binary_search_tree.py) * [Palindrome Partitioning](dynamic_programming/palindrome_partitioning.py) + * [Range Sum Query](dynamic_programming/range_sum_query.py) * [Regex Match](dynamic_programming/regex_match.py) * [Rod Cutting](dynamic_programming/rod_cutting.py) * [Smith Waterman](dynamic_programming/smith_waterman.py) @@ -608,6 +609,7 @@ * [Mfcc](machine_learning/mfcc.py) * [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py) * [Polynomial Regression](machine_learning/polynomial_regression.py) + * [Principle Component Analysis](machine_learning/principle_component_analysis.py) * [Scoring Functions](machine_learning/scoring_functions.py) * [Self Organizing Map](machine_learning/self_organizing_map.py) * [Sequential Minimum Optimization](machine_learning/sequential_minimum_optimization.py) diff --git a/machine_learning/principle_component_analysis.py b/machine_learning/principle_component_analysis.py new file mode 100644 index 000000000000..46ccdb968494 --- /dev/null +++ b/machine_learning/principle_component_analysis.py @@ -0,0 +1,85 @@ +""" +Principal Component Analysis (PCA) is a dimensionality reduction technique +used in machine learning. It transforms high-dimensional data into a lower-dimensional +representation while retaining as much variance as possible. + +This implementation follows best practices, including: +- Standardizing the dataset. +- Computing principal components using Singular Value Decomposition (SVD). +- Returning transformed data and explained variance ratio. +""" + +import doctest + +import numpy as np +from sklearn.datasets import load_iris +from sklearn.decomposition import PCA +from sklearn.preprocessing import StandardScaler + + +def collect_dataset() -> tuple[np.ndarray, np.ndarray]: + """ + Collects the dataset (Iris dataset) and returns feature matrix and target values. + + :return: Tuple containing feature matrix (X) and target labels (y) + + Example: + >>> X, y = collect_dataset() + >>> X.shape + (150, 4) + >>> y.shape + (150,) + """ + data = load_iris() + return np.array(data.data), np.array(data.target) + + +def apply_pca(data_x: np.ndarray, n_components: int) -> tuple[np.ndarray, np.ndarray]: + """ + Applies Principal Component Analysis (PCA) to reduce dimensionality. + + :param data_x: Original dataset (features) + :param n_components: Number of principal components to retain + :return: Tuple containing transformed dataset and explained variance ratio + + Example: + >>> X, _ = collect_dataset() + >>> transformed_X, variance = apply_pca(X, 2) + >>> transformed_X.shape + (150, 2) + >>> len(variance) == 2 + True + """ + # Standardizing the dataset + scaler = StandardScaler() + data_x_scaled = scaler.fit_transform(data_x) + + # Applying PCA + pca = PCA(n_components=n_components) + principal_components = pca.fit_transform(data_x_scaled) + + return principal_components, pca.explained_variance_ratio_ + + +def main() -> None: + """ + Driver function to execute PCA and display results. + """ + data_x, data_y = collect_dataset() + + # Number of principal components to retain + n_components = 2 + + # Apply PCA + transformed_data, variance_ratio = apply_pca(data_x, n_components) + + print("Transformed Dataset (First 5 rows):") + print(transformed_data[:5]) + + print("\nExplained Variance Ratio:") + print(variance_ratio) + + +if __name__ == "__main__": + doctest.testmod() + main() From fff34ed528a7c1af373aeae68693d67639ff616b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 19:10:41 +0100 Subject: [PATCH 086/126] [pre-commit.ci] pre-commit autoupdate (#12599) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.7 → v0.9.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.7...v0.9.9) - [github.com/tox-dev/pyproject-fmt: v2.5.0 → v2.5.1](https://github.com/tox-dev/pyproject-fmt/compare/v2.5.0...v2.5.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8de90b11767f..a0952928a775 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.7 + rev: v0.9.9 hooks: - id: ruff - id: ruff-format @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "v2.5.0" + rev: "v2.5.1" hooks: - id: pyproject-fmt From a415a953c3f1bb741f14a4ba06f067e6d94653ed Mon Sep 17 00:00:00 2001 From: Ankana Pari <143877643+ankana2113@users.noreply.github.com> Date: Sun, 9 Mar 2025 03:05:07 +0530 Subject: [PATCH 087/126] Add largest rectangle histogram (#12269) * added ridge regression * added ridge regression * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added ridge regression * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ridge regression * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * resolved errors * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * resolved conflicts * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added doctests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ruff and minor checks * minor chenges * minor checks * minor checks * minor changes * descriptive names * Fix ruff check in loss_functions.py * fixed pre-commit issues * added largest rectangle histogram function * added largest rectangle histogram function * Update frequent_pattern_growth.py * Update loss_functions.py * Delete machine_learning/ridge_regression/__init__.py * Delete machine_learning/ridge_regression/ADRvsRating.csv * Delete machine_learning/ridge_regression/ridge_regression.py * Delete machine_learning/ridge_regression/test_ridge_regression.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- .../stacks/largest_rectangle_histogram.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 data_structures/stacks/largest_rectangle_histogram.py diff --git a/data_structures/stacks/largest_rectangle_histogram.py b/data_structures/stacks/largest_rectangle_histogram.py new file mode 100644 index 000000000000..7575bd9f628d --- /dev/null +++ b/data_structures/stacks/largest_rectangle_histogram.py @@ -0,0 +1,39 @@ +def largest_rectangle_area(heights: list[int]) -> int: + """ + Inputs an array of integers representing the heights of bars, + and returns the area of the largest rectangle that can be formed + + >>> largest_rectangle_area([2, 1, 5, 6, 2, 3]) + 10 + + >>> largest_rectangle_area([2, 4]) + 4 + + >>> largest_rectangle_area([6, 2, 5, 4, 5, 1, 6]) + 12 + + >>> largest_rectangle_area([1]) + 1 + """ + stack: list[int] = [] + max_area = 0 + heights = [*heights, 0] # make a new list by appending the sentinel 0 + n = len(heights) + + for i in range(n): + # make sure the stack remains in increasing order + while stack and heights[i] < heights[stack[-1]]: + h = heights[stack.pop()] # height of the bar + # if stack is empty, it means entire width can be taken from index 0 to i-1 + w = i if not stack else i - stack[-1] - 1 # calculate width + max_area = max(max_area, h * w) + + stack.append(i) + + return max_area + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 4fbd350b6e08aeb22741d694ce2e64182c66ac92 Mon Sep 17 00:00:00 2001 From: PAUL ADUTWUM Date: Sat, 8 Mar 2025 16:47:04 -0500 Subject: [PATCH 088/126] Improved test coverage in decimal_to_fraction.py (#12608) * Imporved test coverage in decimal_to_fraction.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update decimal_to_fraction.py * Update decimal_to_fraction.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- maths/decimal_to_fraction.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/maths/decimal_to_fraction.py b/maths/decimal_to_fraction.py index 2aa8e3c3dfd6..7f1299b33c5c 100644 --- a/maths/decimal_to_fraction.py +++ b/maths/decimal_to_fraction.py @@ -16,6 +16,20 @@ def decimal_to_fraction(decimal: float | str) -> tuple[int, int]: >>> decimal_to_fraction("78td") Traceback (most recent call last): ValueError: Please enter a valid number + >>> decimal_to_fraction(0) + (0, 1) + >>> decimal_to_fraction(-2.5) + (-5, 2) + >>> decimal_to_fraction(0.125) + (1, 8) + >>> decimal_to_fraction(1000000.25) + (4000001, 4) + >>> decimal_to_fraction(1.3333) + (13333, 10000) + >>> decimal_to_fraction("1.23e2") + (123, 1) + >>> decimal_to_fraction("0.500") + (1, 2) """ try: decimal = float(decimal) From e3fb5309da98e2d07699ae39eb0a55836a063532 Mon Sep 17 00:00:00 2001 From: PAUL ADUTWUM Date: Sat, 8 Mar 2025 16:52:20 -0500 Subject: [PATCH 089/126] Improve decimal_to_fraction.py (#12611) * Update decimal_to_fraction.py * Update decimal_to_fraction.py --------- Co-authored-by: Maxim Smolskiy --- maths/decimal_to_fraction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maths/decimal_to_fraction.py b/maths/decimal_to_fraction.py index 7f1299b33c5c..be42b9fb3b5a 100644 --- a/maths/decimal_to_fraction.py +++ b/maths/decimal_to_fraction.py @@ -48,8 +48,8 @@ def decimal_to_fraction(decimal: float | str) -> tuple[int, int]: if remainder == 0: break dividend, divisor = divisor, remainder - numerator, denominator = numerator / divisor, denominator / divisor - return int(numerator), int(denominator) + numerator, denominator = numerator // divisor, denominator // divisor + return numerator, denominator if __name__ == "__main__": From 23eb17462940e20b830aacce5d2eb80113a7f973 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 08:51:28 +0100 Subject: [PATCH 090/126] [pre-commit.ci] pre-commit autoupdate (#12614) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.9 → v0.9.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.9...v0.9.10) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] --- .pre-commit-config.yaml | 2 +- DIRECTORY.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a0952928a775..32580f8c7398 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.9 + rev: v0.9.10 hooks: - id: ruff - id: ruff-format diff --git a/DIRECTORY.md b/DIRECTORY.md index ab3259b9a766..1c02c191bd14 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -289,6 +289,7 @@ * [Dijkstras Two Stack Algorithm](data_structures/stacks/dijkstras_two_stack_algorithm.py) * [Infix To Postfix Conversion](data_structures/stacks/infix_to_postfix_conversion.py) * [Infix To Prefix Conversion](data_structures/stacks/infix_to_prefix_conversion.py) + * [Largest Rectangle Histogram](data_structures/stacks/largest_rectangle_histogram.py) * [Lexicographical Numbers](data_structures/stacks/lexicographical_numbers.py) * [Next Greater Element](data_structures/stacks/next_greater_element.py) * [Postfix Evaluation](data_structures/stacks/postfix_evaluation.py) From 7ce998b91c45090bd9c4cdfac6ed0220497b4810 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 11 Mar 2025 17:29:13 +0300 Subject: [PATCH 091/126] Fix some RUF012 per file ignores (#11399) * updating DIRECTORY.md * Fix some RUF012 per file ignores * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * Fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * Improve * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Improve * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: MaximSmolskiy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- other/lfu_cache.py | 18 ++++++++++-------- other/lru_cache.py | 18 ++++++++++-------- pyproject.toml | 3 --- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/other/lfu_cache.py b/other/lfu_cache.py index 788fdf19bb60..5a143c739b9d 100644 --- a/other/lfu_cache.py +++ b/other/lfu_cache.py @@ -196,9 +196,6 @@ class LFUCache(Generic[T, U]): CacheInfo(hits=196, misses=100, capacity=100, current_size=100) """ - # class variable to map the decorator functions to their respective instance - decorator_function_to_instance_map: dict[Callable[[T], U], LFUCache[T, U]] = {} - def __init__(self, capacity: int): self.list: DoubleLinkedList[T, U] = DoubleLinkedList() self.capacity = capacity @@ -291,18 +288,23 @@ def decorator( """ def cache_decorator_inner(func: Callable[[T], U]) -> Callable[..., U]: + # variable to map the decorator functions to their respective instance + decorator_function_to_instance_map: dict[ + Callable[[T], U], LFUCache[T, U] + ] = {} + def cache_decorator_wrapper(*args: T) -> U: - if func not in cls.decorator_function_to_instance_map: - cls.decorator_function_to_instance_map[func] = LFUCache(size) + if func not in decorator_function_to_instance_map: + decorator_function_to_instance_map[func] = LFUCache(size) - result = cls.decorator_function_to_instance_map[func].get(args[0]) + result = decorator_function_to_instance_map[func].get(args[0]) if result is None: result = func(*args) - cls.decorator_function_to_instance_map[func].put(args[0], result) + decorator_function_to_instance_map[func].put(args[0], result) return result def cache_info() -> LFUCache[T, U]: - return cls.decorator_function_to_instance_map[func] + return decorator_function_to_instance_map[func] setattr(cache_decorator_wrapper, "cache_info", cache_info) # noqa: B010 diff --git a/other/lru_cache.py b/other/lru_cache.py index 1e5eeac45b4e..4f0c843c86cc 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -209,9 +209,6 @@ class LRUCache(Generic[T, U]): CacheInfo(hits=194, misses=99, capacity=100, current size=99) """ - # class variable to map the decorator functions to their respective instance - decorator_function_to_instance_map: dict[Callable[[T], U], LRUCache[T, U]] = {} - def __init__(self, capacity: int): self.list: DoubleLinkedList[T, U] = DoubleLinkedList() self.capacity = capacity @@ -308,18 +305,23 @@ def decorator( """ def cache_decorator_inner(func: Callable[[T], U]) -> Callable[..., U]: + # variable to map the decorator functions to their respective instance + decorator_function_to_instance_map: dict[ + Callable[[T], U], LRUCache[T, U] + ] = {} + def cache_decorator_wrapper(*args: T) -> U: - if func not in cls.decorator_function_to_instance_map: - cls.decorator_function_to_instance_map[func] = LRUCache(size) + if func not in decorator_function_to_instance_map: + decorator_function_to_instance_map[func] = LRUCache(size) - result = cls.decorator_function_to_instance_map[func].get(args[0]) + result = decorator_function_to_instance_map[func].get(args[0]) if result is None: result = func(*args) - cls.decorator_function_to_instance_map[func].put(args[0], result) + decorator_function_to_instance_map[func].put(args[0], result) return result def cache_info() -> LRUCache[T, U]: - return cls.decorator_function_to_instance_map[func] + return decorator_function_to_instance_map[func] setattr(cache_decorator_wrapper, "cache_info", cache_info) # noqa: B010 diff --git a/pyproject.toml b/pyproject.toml index 2135f1f5825a..4a76c4ad6d11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -135,9 +135,6 @@ lint.per-file-ignores."machine_learning/sequential_minimum_optimization.py" = [ lint.per-file-ignores."matrix/sherman_morrison.py" = [ "SIM103", ] -lint.per-file-ignores."other/l*u_cache.py" = [ - "RUF012", -] lint.per-file-ignores."physics/newtons_second_law_of_motion.py" = [ "BLE001", ] From edf7c372a9a6a3e01a33ef92021d958029e99319 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Mar 2025 09:53:49 +0100 Subject: [PATCH 092/126] [pre-commit.ci] pre-commit autoupdate (#12623) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.10 → v0.11.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.10...v0.11.0) - [github.com/abravalheri/validate-pyproject: v0.23 → v0.24](https://github.com/abravalheri/validate-pyproject/compare/v0.23...v0.24) * Fix ruff issues --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 4 ++-- conversions/prefix_conversions_string.py | 4 ++-- data_structures/arrays/sudoku_solver.py | 4 ++-- graphics/digital_differential_analyzer_line.py | 2 +- graphs/minimum_spanning_tree_prims2.py | 4 ++-- hashes/enigma_machine.py | 4 ++-- linear_algebra/src/test_linear_algebra.py | 2 +- maths/primelib.py | 2 +- other/davis_putnam_logemann_loveland.py | 2 +- other/quine.py | 2 +- project_euler/problem_028/sol1.py | 2 +- pyproject.toml | 1 + scripts/validate_filenames.py | 17 +++++++---------- sorts/external_sort.py | 2 +- strings/frequency_finder.py | 2 +- 15 files changed, 26 insertions(+), 28 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 32580f8c7398..5deb66a5e5a2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.10 + rev: v0.11.0 hooks: - id: ruff - id: ruff-format @@ -42,7 +42,7 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.23 + rev: v0.24 hooks: - id: validate-pyproject diff --git a/conversions/prefix_conversions_string.py b/conversions/prefix_conversions_string.py index 9344c9672a1f..c5fef49874ca 100644 --- a/conversions/prefix_conversions_string.py +++ b/conversions/prefix_conversions_string.py @@ -53,7 +53,7 @@ class SIUnit(Enum): yocto = -24 @classmethod - def get_positive(cls: type[T]) -> dict: + def get_positive(cls) -> dict: """ Returns a dictionary with only the elements of this enum that has a positive value @@ -68,7 +68,7 @@ def get_positive(cls: type[T]) -> dict: return {unit.name: unit.value for unit in cls if unit.value > 0} @classmethod - def get_negative(cls: type[T]) -> dict: + def get_negative(cls) -> dict: """ Returns a dictionary with only the elements of this enum that has a negative value diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index e1714e57ece8..4c722f12fd6e 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -54,7 +54,7 @@ def parse_grid(grid): return False if a contradiction is detected. """ ## To start, every square can be any digit; then assign values from the grid. - values = {s: digits for s in squares} + values = dict.fromkeys(squares, digits) for s, d in grid_values(grid).items(): if d in digits and not assign(values, s, d): return False ## (Fail if we can't assign d to square s.) @@ -203,7 +203,7 @@ def random_puzzle(assignments=17): Note the resulting puzzle is not guaranteed to be solvable, but empirically about 99.8% of them are solvable. Some have multiple solutions. """ - values = {s: digits for s in squares} + values = dict.fromkeys(squares, digits) for s in shuffled(squares): if not assign(values, s, random.choice(values[s])): break diff --git a/graphics/digital_differential_analyzer_line.py b/graphics/digital_differential_analyzer_line.py index a51cb0b8dc37..f7269ab09856 100644 --- a/graphics/digital_differential_analyzer_line.py +++ b/graphics/digital_differential_analyzer_line.py @@ -29,7 +29,7 @@ def digital_differential_analyzer_line( for _ in range(steps): x += x_increment y += y_increment - coordinates.append((int(round(x)), int(round(y)))) + coordinates.append((round(x), round(y))) return coordinates diff --git a/graphs/minimum_spanning_tree_prims2.py b/graphs/minimum_spanning_tree_prims2.py index cc918f81dfe8..6870cc80f844 100644 --- a/graphs/minimum_spanning_tree_prims2.py +++ b/graphs/minimum_spanning_tree_prims2.py @@ -239,8 +239,8 @@ def prims_algo( 13 """ # prim's algorithm for minimum spanning tree - dist: dict[T, int] = {node: maxsize for node in graph.connections} - parent: dict[T, T | None] = {node: None for node in graph.connections} + dist: dict[T, int] = dict.fromkeys(graph.connections, maxsize) + parent: dict[T, T | None] = dict.fromkeys(graph.connections) priority_queue: MinPriorityQueue[T] = MinPriorityQueue() for node, weight in dist.items(): diff --git a/hashes/enigma_machine.py b/hashes/enigma_machine.py index d95437d12c34..0da8e4113de9 100644 --- a/hashes/enigma_machine.py +++ b/hashes/enigma_machine.py @@ -15,12 +15,12 @@ def rotator(): gear_one.append(i) del gear_one[0] gear_one_pos += 1 - if gear_one_pos % int(len(alphabets)) == 0: + if gear_one_pos % len(alphabets) == 0: i = gear_two[0] gear_two.append(i) del gear_two[0] gear_two_pos += 1 - if gear_two_pos % int(len(alphabets)) == 0: + if gear_two_pos % len(alphabets) == 0: i = gear_three[0] gear_three.append(i) del gear_three[0] diff --git a/linear_algebra/src/test_linear_algebra.py b/linear_algebra/src/test_linear_algebra.py index fc5f90fd5cbe..5209c152013e 100644 --- a/linear_algebra/src/test_linear_algebra.py +++ b/linear_algebra/src/test_linear_algebra.py @@ -181,7 +181,7 @@ def test_component_matrix(self) -> None: test for Matrix method component() """ a = Matrix([[1, 2, 3], [2, 4, 5], [6, 7, 8]], 3, 3) - assert a.component(2, 1) == 7, 0.01 + assert a.component(2, 1) == 7, "0.01" def test__add__matrix(self) -> None: """ diff --git a/maths/primelib.py b/maths/primelib.py index 3a966e5cd936..9f031efc50a9 100644 --- a/maths/primelib.py +++ b/maths/primelib.py @@ -76,7 +76,7 @@ def is_prime(number: int) -> bool: if number <= 1: status = False - for divisor in range(2, int(round(sqrt(number))) + 1): + for divisor in range(2, round(sqrt(number)) + 1): # if 'number' divisible by 'divisor' then sets 'status' # of false and break up the loop. if number % divisor == 0: diff --git a/other/davis_putnam_logemann_loveland.py b/other/davis_putnam_logemann_loveland.py index e95bf371a817..7d0bcce15a29 100644 --- a/other/davis_putnam_logemann_loveland.py +++ b/other/davis_putnam_logemann_loveland.py @@ -36,7 +36,7 @@ def __init__(self, literals: list[str]) -> None: Represent the literals and an assignment in a clause." """ # Assign all literals to None initially - self.literals: dict[str, bool | None] = {literal: None for literal in literals} + self.literals: dict[str, bool | None] = dict.fromkeys(literals) def __str__(self) -> str: """ diff --git a/other/quine.py b/other/quine.py index 08e885bc1ce7..0fc78333fed1 100644 --- a/other/quine.py +++ b/other/quine.py @@ -1,5 +1,5 @@ #!/bin/python3 -# ruff: noqa +# ruff: noqa: PLC3002 """ Quine: diff --git a/project_euler/problem_028/sol1.py b/project_euler/problem_028/sol1.py index 1ea5d4fcafd4..0a4648af36c4 100644 --- a/project_euler/problem_028/sol1.py +++ b/project_euler/problem_028/sol1.py @@ -37,7 +37,7 @@ def solution(n: int = 1001) -> int: """ total = 1 - for i in range(1, int(ceil(n / 2.0))): + for i in range(1, ceil(n / 2.0)): odd = 2 * i + 1 even = 2 * i total = total + 4 * odd**2 - 6 * even diff --git a/pyproject.toml b/pyproject.toml index 4a76c4ad6d11..60f8d4ffc96f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -119,6 +119,7 @@ lint.ignore = [ "PT018", # Assertion should be broken down into multiple parts "S101", # Use of `assert` detected -- DO NOT FIX "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME + "SIM905", # Consider using a list literal instead of `str.split` -- DO NOT FIX "SLF001", # Private member accessed: `_Iterator` -- FIX ME "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX ] diff --git a/scripts/validate_filenames.py b/scripts/validate_filenames.py index e76b4dbfe288..80399673cced 100755 --- a/scripts/validate_filenames.py +++ b/scripts/validate_filenames.py @@ -9,28 +9,25 @@ filepaths = list(good_file_paths()) assert filepaths, "good_file_paths() failed!" -upper_files = [file for file in filepaths if file != file.lower()] -if upper_files: +if upper_files := [file for file in filepaths if file != file.lower()]: print(f"{len(upper_files)} files contain uppercase characters:") print("\n".join(upper_files) + "\n") -space_files = [file for file in filepaths if " " in file] -if space_files: +if space_files := [file for file in filepaths if " " in file]: print(f"{len(space_files)} files contain space characters:") print("\n".join(space_files) + "\n") -hyphen_files = [file for file in filepaths if "-" in file] -if hyphen_files: +if hyphen_files := [ + file for file in filepaths if "-" in file and "/site-packages/" not in file +]: print(f"{len(hyphen_files)} files contain hyphen characters:") print("\n".join(hyphen_files) + "\n") -nodir_files = [file for file in filepaths if os.sep not in file] -if nodir_files: +if nodir_files := [file for file in filepaths if os.sep not in file]: print(f"{len(nodir_files)} files are not in a directory:") print("\n".join(nodir_files) + "\n") -bad_files = len(upper_files + space_files + hyphen_files + nodir_files) -if bad_files: +if bad_files := len(upper_files + space_files + hyphen_files + nodir_files): import sys sys.exit(bad_files) diff --git a/sorts/external_sort.py b/sorts/external_sort.py index 3fa7cacc0592..cfddee4fe7f8 100644 --- a/sorts/external_sort.py +++ b/sorts/external_sort.py @@ -61,7 +61,7 @@ def __init__(self, files): self.files = files self.empty = set() self.num_buffers = len(files) - self.buffers = {i: None for i in range(self.num_buffers)} + self.buffers = dict.fromkeys(range(self.num_buffers)) def get_dict(self): return { diff --git a/strings/frequency_finder.py b/strings/frequency_finder.py index e5afee891bd9..98720dc36d6e 100644 --- a/strings/frequency_finder.py +++ b/strings/frequency_finder.py @@ -36,7 +36,7 @@ def get_letter_count(message: str) -> dict[str, int]: - letter_count = {letter: 0 for letter in string.ascii_uppercase} + letter_count = dict.fromkeys(string.ascii_uppercase, 0) for letter in message.upper(): if letter in LETTERS: letter_count[letter] += 1 From 580273eeca28c30a8a5da114800d21b89fdfb930 Mon Sep 17 00:00:00 2001 From: Pranjay kumar <110048711+pranjaykumar926@users.noreply.github.com> Date: Thu, 20 Mar 2025 05:03:46 +0530 Subject: [PATCH 093/126] Improve prefix_sum.py (#12560) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update prefix_sum.py Index Validation for get_sum Raises ValueError if start or end is out of range or start > end. Handles cases where the array is empty. ✅ Empty Array Support If an empty array is passed, get_sum raises an appropriate error instead of failing unexpectedly. ✅ Optimized contains_sum Initialization Initializes sums with {0} for efficient subarray sum checking. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update prefix_sum.py * Update prefix_sum.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update prefix_sum.py * Update prefix_sum.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- data_structures/arrays/prefix_sum.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/data_structures/arrays/prefix_sum.py b/data_structures/arrays/prefix_sum.py index 2243a5308937..717b5f9d7e7e 100644 --- a/data_structures/arrays/prefix_sum.py +++ b/data_structures/arrays/prefix_sum.py @@ -30,11 +30,29 @@ def get_sum(self, start: int, end: int) -> int: 5 >>> PrefixSum([1,2,3]).get_sum(2, 2) 3 + >>> PrefixSum([]).get_sum(0, 0) + Traceback (most recent call last): + ... + ValueError: The array is empty. + >>> PrefixSum([1,2,3]).get_sum(-1, 2) + Traceback (most recent call last): + ... + ValueError: Invalid range specified. >>> PrefixSum([1,2,3]).get_sum(2, 3) Traceback (most recent call last): ... - IndexError: list index out of range + ValueError: Invalid range specified. + >>> PrefixSum([1,2,3]).get_sum(2, 1) + Traceback (most recent call last): + ... + ValueError: Invalid range specified. """ + if not self.prefix_sum: + raise ValueError("The array is empty.") + + if start < 0 or end >= len(self.prefix_sum) or start > end: + raise ValueError("Invalid range specified.") + if start == 0: return self.prefix_sum[end] From e3773dbec1504de17047c4fe013c0f1aaef20b38 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 20:05:41 +0100 Subject: [PATCH 094/126] [pre-commit.ci] pre-commit autoupdate (#12631) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.0 → v0.11.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.0...v0.11.2) - [github.com/abravalheri/validate-pyproject: v0.24 → v0.24.1](https://github.com/abravalheri/validate-pyproject/compare/v0.24...v0.24.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5deb66a5e5a2..0fc8b2b14e07 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.0 + rev: v0.11.2 hooks: - id: ruff - id: ruff-format @@ -42,7 +42,7 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.24 + rev: v0.24.1 hooks: - id: validate-pyproject From 74b540ad73bd3b1187ed6e3c89bb8f309ef543fd Mon Sep 17 00:00:00 2001 From: Tony Dang <62843153+Dang-Hoang-Tung@users.noreply.github.com> Date: Sat, 29 Mar 2025 08:13:47 +0000 Subject: [PATCH 095/126] Genetic Algorithm: Fix bug in multi-threading (#12644) * Fix bug in multi-threading - Multi-threading (despite being commented out) had a tiny bug: missing target argument (2nd argument). - Commented out code was also slightly hard to understand, added (Option 1/2) in comments to clarify where a user may choose between 2 implementations. * Update basic_string.py --------- Co-authored-by: Maxim Smolskiy --- genetic_algorithm/basic_string.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/genetic_algorithm/basic_string.py b/genetic_algorithm/basic_string.py index a906ce85a779..b75491d9a949 100644 --- a/genetic_algorithm/basic_string.py +++ b/genetic_algorithm/basic_string.py @@ -144,18 +144,18 @@ def basic(target: str, genes: list[str], debug: bool = True) -> tuple[int, int, # Random population created. Now it's time to evaluate. - # Adding a bit of concurrency can make everything faster, + # (Option 1) Adding a bit of concurrency can make everything faster, # # import concurrent.futures # population_score: list[tuple[str, float]] = [] # with concurrent.futures.ThreadPoolExecutor( # max_workers=NUM_WORKERS) as executor: - # futures = {executor.submit(evaluate, item) for item in population} + # futures = {executor.submit(evaluate, item, target) for item in population} # concurrent.futures.wait(futures) # population_score = [item.result() for item in futures] # # but with a simple algorithm like this, it will probably be slower. - # We just need to call evaluate for every item inside the population. + # (Option 2) We just need to call evaluate for every item inside the population. population_score = [evaluate(item, target) for item in population] # Check if there is a matching evolution. From f10a5cbfccc5ee9ddb5ddd9906591ecaad58f672 Mon Sep 17 00:00:00 2001 From: Isidro Date: Mon, 31 Mar 2025 23:09:14 +0200 Subject: [PATCH 096/126] prefix_evaluation: Add alternative recursive implementation (#12646) * prefix_evaluation: Add alternative recursive implementation * improve doc * better variable name calc->operators * Update prefix_evaluation.py * Update prefix_evaluation.py * Update prefix_evaluation.py * Update prefix_evaluation.py --------- Co-authored-by: Maxim Smolskiy --- data_structures/stacks/prefix_evaluation.py | 39 +++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/data_structures/stacks/prefix_evaluation.py b/data_structures/stacks/prefix_evaluation.py index f48eca23d7b5..03a70d884725 100644 --- a/data_structures/stacks/prefix_evaluation.py +++ b/data_structures/stacks/prefix_evaluation.py @@ -1,8 +1,9 @@ """ -Python3 program to evaluate a prefix expression. +Program to evaluate a prefix expression. +https://en.wikipedia.org/wiki/Polish_notation """ -calc = { +operators = { "+": lambda x, y: x + y, "-": lambda x, y: x - y, "*": lambda x, y: x * y, @@ -31,6 +32,10 @@ def evaluate(expression): 21 >>> evaluate("/ * 10 2 + 4 1 ") 4.0 + >>> evaluate("2") + 2 + >>> evaluate("+ * 2 3 / 8 4") + 8.0 """ stack = [] @@ -45,11 +50,39 @@ def evaluate(expression): # push the result onto the stack again o1 = stack.pop() o2 = stack.pop() - stack.append(calc[c](o1, o2)) + stack.append(operators[c](o1, o2)) return stack.pop() +def evaluate_recursive(expression: list[str]): + """ + Alternative recursive implementation + + >>> evaluate_recursive(['2']) + 2 + >>> expression = ['+', '*', '2', '3', '/', '8', '4'] + >>> evaluate_recursive(expression) + 8.0 + >>> expression + [] + >>> evaluate_recursive(['+', '9', '*', '2', '6']) + 21 + >>> evaluate_recursive(['/', '*', '10', '2', '+', '4', '1']) + 4.0 + """ + + op = expression.pop(0) + if is_operand(op): + return int(op) + + operation = operators[op] + + a = evaluate_recursive(expression) + b = evaluate_recursive(expression) + return operation(a, b) + + # Driver code if __name__ == "__main__": test_expression = "+ 9 * 2 6" From baab802965c37fa1740054a559cad8c119b2ee35 Mon Sep 17 00:00:00 2001 From: Isidro Date: Tue, 1 Apr 2025 20:55:14 +0200 Subject: [PATCH 097/126] doubly linked list: add dataclass and typing (#12647) * Node is a dataclass * fix mypy errors * LinkedList is a dataclass * fix mypy errors * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py --------- Co-authored-by: Maxim Smolskiy --- .../linked_list/doubly_linked_list_two.py | 62 +++++++++---------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/data_structures/linked_list/doubly_linked_list_two.py b/data_structures/linked_list/doubly_linked_list_two.py index e993cc5a20af..3d3bfb0cde30 100644 --- a/data_structures/linked_list/doubly_linked_list_two.py +++ b/data_structures/linked_list/doubly_linked_list_two.py @@ -9,25 +9,19 @@ Delete operation is more efficient """ +from dataclasses import dataclass +from typing import Self + +@dataclass class Node: - def __init__(self, data: int, previous=None, next_node=None): - self.data = data - self.previous = previous - self.next = next_node + data: int + previous: Self | None = None + next: Self | None = None def __str__(self) -> str: return f"{self.data}" - def get_data(self) -> int: - return self.data - - def get_next(self): - return self.next - - def get_previous(self): - return self.previous - class LinkedListIterator: def __init__(self, head): @@ -40,30 +34,30 @@ def __next__(self): if not self.current: raise StopIteration else: - value = self.current.get_data() - self.current = self.current.get_next() + value = self.current.data + self.current = self.current.next return value +@dataclass class LinkedList: - def __init__(self): - self.head = None # First node in list - self.tail = None # Last node in list + head: Node | None = None # First node in list + tail: Node | None = None # Last node in list def __str__(self): current = self.head nodes = [] while current is not None: - nodes.append(current.get_data()) - current = current.get_next() + nodes.append(current.data) + current = current.next return " ".join(str(node) for node in nodes) def __contains__(self, value: int): current = self.head while current: - if current.get_data() == value: + if current.data == value: return True - current = current.get_next() + current = current.next return False def __iter__(self): @@ -71,12 +65,12 @@ def __iter__(self): def get_head_data(self): if self.head: - return self.head.get_data() + return self.head.data return None def get_tail_data(self): if self.tail: - return self.tail.get_data() + return self.tail.data return None def set_head(self, node: Node) -> None: @@ -103,18 +97,20 @@ def insert_before_node(self, node: Node, node_to_insert: Node) -> None: node_to_insert.next = node node_to_insert.previous = node.previous - if node.get_previous() is None: + if node.previous is None: self.head = node_to_insert else: node.previous.next = node_to_insert node.previous = node_to_insert - def insert_after_node(self, node: Node, node_to_insert: Node) -> None: + def insert_after_node(self, node: Node | None, node_to_insert: Node) -> None: + assert node is not None + node_to_insert.previous = node node_to_insert.next = node.next - if node.get_next() is None: + if node.next is None: self.tail = node_to_insert else: node.next.previous = node_to_insert @@ -136,27 +132,27 @@ def insert_at_position(self, position: int, value: int) -> None: def get_node(self, item: int) -> Node: node = self.head while node: - if node.get_data() == item: + if node.data == item: return node - node = node.get_next() + node = node.next raise Exception("Node not found") def delete_value(self, value): if (node := self.get_node(value)) is not None: if node == self.head: - self.head = self.head.get_next() + self.head = self.head.next if node == self.tail: - self.tail = self.tail.get_previous() + self.tail = self.tail.previous self.remove_node_pointers(node) @staticmethod def remove_node_pointers(node: Node) -> None: - if node.get_next(): + if node.next: node.next.previous = node.previous - if node.get_previous(): + if node.previous: node.previous.next = node.next node.next = None From 0c8cf8e9871a5f91182d767adf173dccf87c2c0f Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Wed, 2 Apr 2025 10:23:55 +0300 Subject: [PATCH 098/126] Fix bug for data_structures/linked_list/doubly_linked_list_two.py (#12651) * Fix bug for data_structures/linked_list/doubly_linked_list_two.py * Fix * Fix * Fix * Fix * Fix * Fix * Fix * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py --- .../linked_list/doubly_linked_list_two.py | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/data_structures/linked_list/doubly_linked_list_two.py b/data_structures/linked_list/doubly_linked_list_two.py index 3d3bfb0cde30..8c93cddd5d31 100644 --- a/data_structures/linked_list/doubly_linked_list_two.py +++ b/data_structures/linked_list/doubly_linked_list_two.py @@ -81,8 +81,9 @@ def set_head(self, node: Node) -> None: self.insert_before_node(self.head, node) def set_tail(self, node: Node) -> None: - if self.head is None: - self.set_head(node) + if self.tail is None: + self.head = node + self.tail = node else: self.insert_after_node(self.tail, node) @@ -104,9 +105,7 @@ def insert_before_node(self, node: Node, node_to_insert: Node) -> None: node.previous = node_to_insert - def insert_after_node(self, node: Node | None, node_to_insert: Node) -> None: - assert node is not None - + def insert_after_node(self, node: Node, node_to_insert: Node) -> None: node_to_insert.previous = node node_to_insert.next = node.next @@ -127,7 +126,7 @@ def insert_at_position(self, position: int, value: int) -> None: return current_position += 1 node = node.next - self.insert_after_node(self.tail, new_node) + self.set_tail(new_node) def get_node(self, item: int) -> Node: node = self.head @@ -237,6 +236,22 @@ def create_linked_list() -> None: 7 8 9 + >>> linked_list = LinkedList() + >>> linked_list.insert_at_position(position=1, value=10) + >>> str(linked_list) + '10' + >>> linked_list.insert_at_position(position=2, value=20) + >>> str(linked_list) + '10 20' + >>> linked_list.insert_at_position(position=1, value=30) + >>> str(linked_list) + '30 10 20' + >>> linked_list.insert_at_position(position=3, value=40) + >>> str(linked_list) + '30 10 40 20' + >>> linked_list.insert_at_position(position=5, value=50) + >>> str(linked_list) + '30 10 40 20 50' """ From 5afe02994eb0aafb0b462fec32fe5f6ecedf7305 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 23:20:19 +0200 Subject: [PATCH 099/126] [pre-commit.ci] pre-commit autoupdate (#12661) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.2 → v0.11.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.2...v0.11.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0fc8b2b14e07..20065c433062 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.2 + rev: v0.11.4 hooks: - id: ruff - id: ruff-format From a4576dc2a42cbfc7585a7ce6f28917cb97f83c45 Mon Sep 17 00:00:00 2001 From: Kim Date: Wed, 9 Apr 2025 15:24:37 +0900 Subject: [PATCH 100/126] fix: correct typo "util" to "until" (#12653) --- dynamic_programming/bitmask.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dynamic_programming/bitmask.py b/dynamic_programming/bitmask.py index a6e6a0cda7bf..4737a3419e8e 100644 --- a/dynamic_programming/bitmask.py +++ b/dynamic_programming/bitmask.py @@ -42,7 +42,7 @@ def count_ways_until(self, mask, task_no): return self.dp[mask][task_no] # Number of ways when we don't this task in the arrangement - total_ways_util = self.count_ways_until(mask, task_no + 1) + total_ways_until = self.count_ways_until(mask, task_no + 1) # now assign the tasks one by one to all possible persons and recursively # assign for the remaining tasks. @@ -54,10 +54,10 @@ def count_ways_until(self, mask, task_no): # assign this task to p and change the mask value. And recursively # assign tasks with the new mask value. - total_ways_util += self.count_ways_until(mask | (1 << p), task_no + 1) + total_ways_until += self.count_ways_until(mask | (1 << p), task_no + 1) # save the value. - self.dp[mask][task_no] = total_ways_util + self.dp[mask][task_no] = total_ways_until return self.dp[mask][task_no] From 4ed61418a8fef5a0fe3c5a05a49c7cbc5ac8298c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 19:55:55 +0200 Subject: [PATCH 101/126] [pre-commit.ci] pre-commit autoupdate (#12671) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.4 → v0.11.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.4...v0.11.5) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 20065c433062..8a8697ca778a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.4 + rev: v0.11.5 hooks: - id: ruff - id: ruff-format From cc621f1fddac7391389270d7bc38326507b4b495 Mon Sep 17 00:00:00 2001 From: parth-6945 <140963864+parth-6945@users.noreply.github.com> Date: Mon, 14 Apr 2025 23:31:29 +0530 Subject: [PATCH 102/126] Add find_unique_number algorithm to bit manipulation (#12654) * Add find_unique_number algorithm to bit manipulation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- bit_manipulation/find_unique_number.py | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 bit_manipulation/find_unique_number.py diff --git a/bit_manipulation/find_unique_number.py b/bit_manipulation/find_unique_number.py new file mode 100644 index 000000000000..77970b4865d1 --- /dev/null +++ b/bit_manipulation/find_unique_number.py @@ -0,0 +1,37 @@ +def find_unique_number(arr: list[int]) -> int: + """ + Given a list of integers where every element appears twice except for one, + this function returns the element that appears only once using bitwise XOR. + + >>> find_unique_number([1, 1, 2, 2, 3]) + 3 + >>> find_unique_number([4, 5, 4, 6, 6]) + 5 + >>> find_unique_number([7]) + 7 + >>> find_unique_number([10, 20, 10]) + 20 + >>> find_unique_number([]) + Traceback (most recent call last): + ... + ValueError: input list must not be empty + >>> find_unique_number([1, 'a', 1]) + Traceback (most recent call last): + ... + TypeError: all elements must be integers + """ + if not arr: + raise ValueError("input list must not be empty") + if not all(isinstance(x, int) for x in arr): + raise TypeError("all elements must be integers") + + result = 0 + for num in arr: + result ^= num + return result + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From d123cbc649e0777717b02dc74b4466872941a8d5 Mon Sep 17 00:00:00 2001 From: Mindaugas <76015221+mindaugl@users.noreply.github.com> Date: Tue, 15 Apr 2025 02:30:25 +0800 Subject: [PATCH 103/126] Solution for the Euler Project Problem 122 (#12655) * Add initial version for euler project problem 122. * Add doctests and documentation for the project euler problem 122. * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py --------- Co-authored-by: Maxim Smolskiy --- project_euler/problem_122/__init__.py | 0 project_euler/problem_122/sol1.py | 89 +++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 project_euler/problem_122/__init__.py create mode 100644 project_euler/problem_122/sol1.py diff --git a/project_euler/problem_122/__init__.py b/project_euler/problem_122/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_122/sol1.py b/project_euler/problem_122/sol1.py new file mode 100644 index 000000000000..cd8b1e67708c --- /dev/null +++ b/project_euler/problem_122/sol1.py @@ -0,0 +1,89 @@ +""" +Project Euler Problem 122: https://projecteuler.net/problem=122 + +Efficient Exponentiation + +The most naive way of computing n^15 requires fourteen multiplications: + + n x n x ... x n = n^15. + +But using a "binary" method you can compute it in six multiplications: + + n x n = n^2 + n^2 x n^2 = n^4 + n^4 x n^4 = n^8 + n^8 x n^4 = n^12 + n^12 x n^2 = n^14 + n^14 x n = n^15 + +However it is yet possible to compute it in only five multiplications: + + n x n = n^2 + n^2 x n = n^3 + n^3 x n^3 = n^6 + n^6 x n^6 = n^12 + n^12 x n^3 = n^15 + +We shall define m(k) to be the minimum number of multiplications to compute n^k; +for example m(15) = 5. + +Find sum_{k = 1}^200 m(k). + +It uses the fact that for rather small n, applicable for this problem, the solution +for each number can be formed by increasing the largest element. + +References: +- https://en.wikipedia.org/wiki/Addition_chain +""" + + +def solve(nums: list[int], goal: int, depth: int) -> bool: + """ + Checks if nums can have a sum equal to goal, given that length of nums does + not exceed depth. + + >>> solve([1], 2, 2) + True + >>> solve([1], 2, 0) + False + """ + if len(nums) > depth: + return False + for el in nums: + if el + nums[-1] == goal: + return True + nums.append(el + nums[-1]) + if solve(nums=nums, goal=goal, depth=depth): + return True + del nums[-1] + return False + + +def solution(n: int = 200) -> int: + """ + Calculates sum of smallest number of multiplactions for each number up to + and including n. + + >>> solution(1) + 0 + >>> solution(2) + 1 + >>> solution(14) + 45 + >>> solution(15) + 50 + """ + total = 0 + for i in range(2, n + 1): + max_length = 0 + while True: + nums = [1] + max_length += 1 + if solve(nums=nums, goal=i, depth=max_length): + break + total += max_length + return total + + +if __name__ == "__main__": + print(f"{solution() = }") From 42820634f3795e7d7397ad2e688d091a79c1eb83 Mon Sep 17 00:00:00 2001 From: Naitik Dwivedi Date: Tue, 15 Apr 2025 00:27:13 +0530 Subject: [PATCH 104/126] Add matrix inversion algorithm using NumPy (#12657) * Create matrix_inversion.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update matrix_inversion.py * Update matrix_inversion.py * Update matrix_inversion.py * Update matrix_inversion.py * Update matrix_inversion.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- linear_algebra/matrix_inversion.py | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 linear_algebra/matrix_inversion.py diff --git a/linear_algebra/matrix_inversion.py b/linear_algebra/matrix_inversion.py new file mode 100644 index 000000000000..50dae1c2e825 --- /dev/null +++ b/linear_algebra/matrix_inversion.py @@ -0,0 +1,36 @@ +import numpy as np + + +def invert_matrix(matrix: list[list[float]]) -> list[list[float]]: + """ + Returns the inverse of a square matrix using NumPy. + + Parameters: + matrix (list[list[float]]): A square matrix. + + Returns: + list[list[float]]: Inverted matrix if invertible, else raises error. + + >>> invert_matrix([[4.0, 7.0], [2.0, 6.0]]) + [[0.6000000000000001, -0.7000000000000001], [-0.2, 0.4]] + >>> invert_matrix([[1.0, 2.0], [0.0, 0.0]]) + Traceback (most recent call last): + ... + ValueError: Matrix is not invertible + """ + np_matrix = np.array(matrix) + + try: + inv_matrix = np.linalg.inv(np_matrix) + except np.linalg.LinAlgError: + raise ValueError("Matrix is not invertible") + + return inv_matrix.tolist() + + +if __name__ == "__main__": + mat = [[4.0, 7.0], [2.0, 6.0]] + print("Original Matrix:") + print(mat) + print("Inverted Matrix:") + print(invert_matrix(mat)) From c585cb122718e219870ea7a2af110939b55e52f9 Mon Sep 17 00:00:00 2001 From: Mindaugas <76015221+mindaugl@users.noreply.github.com> Date: Fri, 18 Apr 2025 07:16:15 +0800 Subject: [PATCH 105/126] Solution for the Euler Project problem 136 (#12658) * Add initial version of file for the Euler project problem 136 solution. * Add documentation and tests for the Euler project problem 136 solution. * Update sol1.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update sol1.py * Update sol1.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py --------- Co-authored-by: Maxim Smolskiy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- project_euler/problem_136/__init__.py | 0 project_euler/problem_136/sol1.py | 63 +++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 project_euler/problem_136/__init__.py create mode 100644 project_euler/problem_136/sol1.py diff --git a/project_euler/problem_136/__init__.py b/project_euler/problem_136/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_136/sol1.py b/project_euler/problem_136/sol1.py new file mode 100644 index 000000000000..688a9a5d7f24 --- /dev/null +++ b/project_euler/problem_136/sol1.py @@ -0,0 +1,63 @@ +""" +Project Euler Problem 136: https://projecteuler.net/problem=136 + +Singleton Difference + +The positive integers, x, y, and z, are consecutive terms of an arithmetic progression. +Given that n is a positive integer, the equation, x^2 - y^2 - z^2 = n, +has exactly one solution when n = 20: + 13^2 - 10^2 - 7^2 = 20. + +In fact there are twenty-five values of n below one hundred for which +the equation has a unique solution. + +How many values of n less than fifty million have exactly one solution? + +By change of variables + +x = y + delta +z = y - delta + +The expression can be rewritten: + +x^2 - y^2 - z^2 = y * (4 * delta - y) = n + +The algorithm loops over delta and y, which is restricted in upper and lower limits, +to count how many solutions each n has. +In the end it is counted how many n's have one solution. +""" + + +def solution(n_limit: int = 50 * 10**6) -> int: + """ + Define n count list and loop over delta, y to get the counts, then check + which n has count == 1. + + >>> solution(3) + 0 + >>> solution(10) + 3 + >>> solution(100) + 25 + >>> solution(110) + 27 + """ + n_sol = [0] * n_limit + + for delta in range(1, (n_limit + 1) // 4 + 1): + for y in range(4 * delta - 1, delta, -1): + n = y * (4 * delta - y) + if n >= n_limit: + break + n_sol[n] += 1 + + ans = 0 + for i in range(n_limit): + if n_sol[i] == 1: + ans += 1 + + return ans + + +if __name__ == "__main__": + print(f"{solution() = }") From a1aa6313e08657f0e9ae337afa81d6b6f95357c9 Mon Sep 17 00:00:00 2001 From: Samuel Willis <109305646+konsoleSam@users.noreply.github.com> Date: Thu, 17 Apr 2025 17:33:08 -0600 Subject: [PATCH 106/126] Adding time and a half pay calculator algorithm to financial folder (#12662) * Create time&half-pay.py * Update time&half-pay.py * Update time&half-pay.py * Rename time&half-pay.py to time_and_half_pay.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update time_and_half_pay.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update time_and_half_pay.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update time_and_half_pay.py * Update time_and_half_pay.py * Update time_and_half_pay.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- financial/time_and_half_pay.py | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 financial/time_and_half_pay.py diff --git a/financial/time_and_half_pay.py b/financial/time_and_half_pay.py new file mode 100644 index 000000000000..c5dff1bc1ce1 --- /dev/null +++ b/financial/time_and_half_pay.py @@ -0,0 +1,40 @@ +""" +Calculate time and a half pay +""" + + +def pay(hours_worked: float, pay_rate: float, hours: float = 40) -> float: + """ + hours_worked = The total hours worked + pay_rate = Amount of money per hour + hours = Number of hours that must be worked before you receive time and a half + + >>> pay(41, 1) + 41.5 + >>> pay(65, 19) + 1472.5 + >>> pay(10, 1) + 10.0 + """ + # Check that all input parameters are float or integer + assert isinstance(hours_worked, (float, int)), ( + "Parameter 'hours_worked' must be of type 'int' or 'float'" + ) + assert isinstance(pay_rate, (float, int)), ( + "Parameter 'pay_rate' must be of type 'int' or 'float'" + ) + assert isinstance(hours, (float, int)), ( + "Parameter 'hours' must be of type 'int' or 'float'" + ) + + normal_pay = hours_worked * pay_rate + over_time = max(0, hours_worked - hours) + over_time_pay = over_time * pay_rate / 2 + return normal_pay + over_time_pay + + +if __name__ == "__main__": + # Test + import doctest + + doctest.testmod() From 9891d2bc3051ab4242ed041ea30edd10b94925bd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 19:54:11 +0200 Subject: [PATCH 107/126] [pre-commit.ci] pre-commit autoupdate (#12680) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.5 → v0.11.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.5...v0.11.6) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] --- .pre-commit-config.yaml | 2 +- DIRECTORY.md | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8a8697ca778a..8474deebb7ba 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.5 + rev: v0.11.6 hooks: - id: ruff - id: ruff-format diff --git a/DIRECTORY.md b/DIRECTORY.md index 1c02c191bd14..fa731e32ff23 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -40,6 +40,7 @@ * [Count Number Of One Bits](bit_manipulation/count_number_of_one_bits.py) * [Excess 3 Code](bit_manipulation/excess_3_code.py) * [Find Previous Power Of Two](bit_manipulation/find_previous_power_of_two.py) + * [Find Unique Number](bit_manipulation/find_unique_number.py) * [Gray Code Sequence](bit_manipulation/gray_code_sequence.py) * [Highest Set Bit](bit_manipulation/highest_set_bit.py) * [Index Of Rightmost Set Bit](bit_manipulation/index_of_rightmost_set_bit.py) @@ -442,6 +443,7 @@ * [Present Value](financial/present_value.py) * [Price Plus Tax](financial/price_plus_tax.py) * [Simple Moving Average](financial/simple_moving_average.py) + * [Time And Half Pay](financial/time_and_half_pay.py) ## Fractals * [Julia Sets](fractals/julia_sets.py) @@ -570,6 +572,7 @@ * [Gaussian Elimination](linear_algebra/gaussian_elimination.py) * [Jacobi Iteration Method](linear_algebra/jacobi_iteration_method.py) * [Lu Decomposition](linear_algebra/lu_decomposition.py) + * [Matrix Inversion](linear_algebra/matrix_inversion.py) * Src * [Conjugate Gradient](linear_algebra/src/conjugate_gradient.py) * [Gaussian Elimination Pivoting](linear_algebra/src/gaussian_elimination_pivoting.py) @@ -1153,6 +1156,8 @@ * [Sol1](project_euler/problem_120/sol1.py) * Problem 121 * [Sol1](project_euler/problem_121/sol1.py) + * Problem 122 + * [Sol1](project_euler/problem_122/sol1.py) * Problem 123 * [Sol1](project_euler/problem_123/sol1.py) * Problem 125 @@ -1163,6 +1168,8 @@ * [Sol1](project_euler/problem_131/sol1.py) * Problem 135 * [Sol1](project_euler/problem_135/sol1.py) + * Problem 136 + * [Sol1](project_euler/problem_136/sol1.py) * Problem 144 * [Sol1](project_euler/problem_144/sol1.py) * Problem 145 From 11a61d15dc3aebc69f153adca8568076a25f7110 Mon Sep 17 00:00:00 2001 From: Isidro Date: Mon, 21 Apr 2025 21:04:39 +0200 Subject: [PATCH 108/126] Generic type hint in DDL (#12677) * Generic type hint in DDL Instead of forcing int * Update doubly_linked_list_two.py * Update doubly_linked_list_two.py --------- Co-authored-by: Maxim Smolskiy --- .../linked_list/doubly_linked_list_two.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/data_structures/linked_list/doubly_linked_list_two.py b/data_structures/linked_list/doubly_linked_list_two.py index 8c93cddd5d31..a7f639a6e289 100644 --- a/data_structures/linked_list/doubly_linked_list_two.py +++ b/data_structures/linked_list/doubly_linked_list_two.py @@ -10,12 +10,14 @@ """ from dataclasses import dataclass -from typing import Self +from typing import Self, TypeVar + +DataType = TypeVar("DataType") @dataclass -class Node: - data: int +class Node[DataType]: + data: DataType previous: Self | None = None next: Self | None = None @@ -52,7 +54,7 @@ def __str__(self): current = current.next return " ".join(str(node) for node in nodes) - def __contains__(self, value: int): + def __contains__(self, value: DataType): current = self.head while current: if current.data == value: @@ -87,7 +89,7 @@ def set_tail(self, node: Node) -> None: else: self.insert_after_node(self.tail, node) - def insert(self, value: int) -> None: + def insert(self, value: DataType) -> None: node = Node(value) if self.head is None: self.set_head(node) @@ -116,7 +118,7 @@ def insert_after_node(self, node: Node, node_to_insert: Node) -> None: node.next = node_to_insert - def insert_at_position(self, position: int, value: int) -> None: + def insert_at_position(self, position: int, value: DataType) -> None: current_position = 1 new_node = Node(value) node = self.head @@ -128,7 +130,7 @@ def insert_at_position(self, position: int, value: int) -> None: node = node.next self.set_tail(new_node) - def get_node(self, item: int) -> Node: + def get_node(self, item: DataType) -> Node: node = self.head while node: if node.data == item: From 29afed0df65fd84f53ad697ff1dbfc86c6e83631 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 08:30:42 +0200 Subject: [PATCH 109/126] Bump astral-sh/setup-uv from 5 to 6 (#12683) * Bump astral-sh/setup-uv from 5 to 6 Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 5 to 6. - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/v5...v6) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * uv run pytest --ignore=web_programming/fetch_anime_and_play.py --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .github/workflows/build.yml | 3 ++- .github/workflows/project_euler.yml | 4 ++-- .github/workflows/ruff.yml | 2 +- .github/workflows/sphinx.yml | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 62829b2b45a5..8b83cb41c79a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v5 + - uses: astral-sh/setup-uv@v6 with: enable-cache: true cache-dependency-glob: uv.lock @@ -30,6 +30,7 @@ jobs: --ignore=project_euler/ --ignore=quantum/q_fourier_transform.py --ignore=scripts/validate_solutions.py + --ignore=web_programming/fetch_anime_and_play.py --cov-report=term-missing:skip-covered --cov=. . - if: ${{ success() }} diff --git a/.github/workflows/project_euler.yml b/.github/workflows/project_euler.yml index 8d51ad8850cf..eaf4150e4eaa 100644 --- a/.github/workflows/project_euler.yml +++ b/.github/workflows/project_euler.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v5 + - uses: astral-sh/setup-uv@v6 - uses: actions/setup-python@v5 with: python-version: 3.x @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v5 + - uses: astral-sh/setup-uv@v6 - uses: actions/setup-python@v5 with: python-version: 3.x diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index cfe127b3521f..ec9f0202bd7e 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -12,5 +12,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v5 + - uses: astral-sh/setup-uv@v6 - run: uvx ruff check --output-format=github . diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index 16ff284a74f2..2010041d80c5 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-24.04-arm steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v5 + - uses: astral-sh/setup-uv@v6 - uses: actions/setup-python@v5 with: python-version: 3.13 From 0a3a96534767c37a0f1561d0f5148b5d1d5e0272 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 19:55:57 +0200 Subject: [PATCH 110/126] [pre-commit.ci] pre-commit autoupdate (#12692) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.6 → v0.11.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.6...v0.11.7) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8474deebb7ba..034493b10912 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.6 + rev: v0.11.7 hooks: - id: ruff - id: ruff-format From 145879b8b2546c74fc51446ac607823876a0f601 Mon Sep 17 00:00:00 2001 From: Mindaugas <76015221+mindaugl@users.noreply.github.com> Date: Mon, 5 May 2025 15:00:32 +0800 Subject: [PATCH 111/126] Add solution for the Euler project problem 164. (#12663) * Add solution for the Euler project problem 164. * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update sol1.py --------- Co-authored-by: Maxim Smolskiy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- project_euler/problem_164/__init__.py | 0 project_euler/problem_164/sol1.py | 65 +++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 project_euler/problem_164/__init__.py create mode 100644 project_euler/problem_164/sol1.py diff --git a/project_euler/problem_164/__init__.py b/project_euler/problem_164/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_164/sol1.py b/project_euler/problem_164/sol1.py new file mode 100644 index 000000000000..5387c89bd757 --- /dev/null +++ b/project_euler/problem_164/sol1.py @@ -0,0 +1,65 @@ +""" +Project Euler Problem 164: https://projecteuler.net/problem=164 + +Three Consecutive Digital Sum Limit + +How many 20 digit numbers n (without any leading zero) exist such that no three +consecutive digits of n have a sum greater than 9? + +Brute-force recursive solution with caching of intermediate results. +""" + + +def solve( + digit: int, prev1: int, prev2: int, sum_max: int, first: bool, cache: dict[str, int] +) -> int: + """ + Solve for remaining 'digit' digits, with previous 'prev1' digit, and + previous-previous 'prev2' digit, total sum of 'sum_max'. + Pass around 'cache' to store/reuse intermediate results. + + >>> solve(digit=1, prev1=0, prev2=0, sum_max=9, first=True, cache={}) + 9 + >>> solve(digit=1, prev1=0, prev2=0, sum_max=9, first=False, cache={}) + 10 + """ + if digit == 0: + return 1 + + cache_str = f"{digit},{prev1},{prev2}" + if cache_str in cache: + return cache[cache_str] + + comb = 0 + for curr in range(sum_max - prev1 - prev2 + 1): + if first and curr == 0: + continue + + comb += solve( + digit=digit - 1, + prev1=curr, + prev2=prev1, + sum_max=sum_max, + first=False, + cache=cache, + ) + + cache[cache_str] = comb + return comb + + +def solution(n_digits: int = 20) -> int: + """ + Solves the problem for n_digits number of digits. + + >>> solution(2) + 45 + >>> solution(10) + 21838806 + """ + cache: dict[str, int] = {} + return solve(digit=n_digits, prev1=0, prev2=0, sum_max=9, first=True, cache=cache) + + +if __name__ == "__main__": + print(f"{solution(10) = }") From 40f4c510b6047d95d07f03c9915a53bbf84789e4 Mon Sep 17 00:00:00 2001 From: Mindaugas <76015221+mindaugl@users.noreply.github.com> Date: Mon, 5 May 2025 15:14:56 +0800 Subject: [PATCH 112/126] Add solution for the Euler problem 190 (#12664) * Add solution for the Euler project problem 164. * Add solution for the Euler project problem 190. * Delete project_euler/problem_164/sol1.py * Delete project_euler/problem_164/__init__.py * Update sol1.py * Update sol1.py * Update sol1.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Maxim Smolskiy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- project_euler/problem_190/__init__.py | 0 project_euler/problem_190/sol1.py | 48 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 project_euler/problem_190/__init__.py create mode 100644 project_euler/problem_190/sol1.py diff --git a/project_euler/problem_190/__init__.py b/project_euler/problem_190/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_190/sol1.py b/project_euler/problem_190/sol1.py new file mode 100644 index 000000000000..b18d45be16b4 --- /dev/null +++ b/project_euler/problem_190/sol1.py @@ -0,0 +1,48 @@ +""" +Project Euler Problem 190: https://projecteuler.net/problem=190 + +Maximising a Weighted Product + +Let S_m = (x_1, x_2, ..., x_m) be the m-tuple of positive real numbers with +x_1 + x_2 + ... + x_m = m for which P_m = x_1 * x_2^2 * ... * x_m^m is maximised. + +For example, it can be verified that |_ P_10 _| = 4112 +(|_ _| is the integer part function). + +Find Sum_{m=2}^15 = |_ P_m _|. + +Solution: +- Fix x_1 = m - x_2 - ... - x_m. +- Calculate partial derivatives of P_m wrt the x_2, ..., x_m. This gives that + x_2 = 2 * x_1, x_3 = 3 * x_1, ..., x_m = m * x_1. +- Calculate partial second order derivatives of P_m wrt the x_2, ..., x_m. + By plugging in the values from the previous step, can verify that solution is maximum. +""" + + +def solution(n: int = 15) -> int: + """ + Calculate sum of |_ P_m _| for m from 2 to n. + + >>> solution(2) + 1 + >>> solution(3) + 2 + >>> solution(4) + 4 + >>> solution(5) + 10 + """ + total = 0 + for m in range(2, n + 1): + x1 = 2 / (m + 1) + p = 1.0 + for i in range(1, m + 1): + xi = i * x1 + p *= xi**i + total += int(p) + return total + + +if __name__ == "__main__": + print(f"{solution() = }") From 7ed7f042feeb3567ace384a00707d83c327310ab Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 19:23:31 +0100 Subject: [PATCH 113/126] [pre-commit.ci] pre-commit autoupdate (#12708) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.7 → v0.11.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.7...v0.11.8) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] --- .pre-commit-config.yaml | 2 +- DIRECTORY.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 034493b10912..9e13416dc78d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.7 + rev: v0.11.8 hooks: - id: ruff - id: ruff-format diff --git a/DIRECTORY.md b/DIRECTORY.md index fa731e32ff23..04e09c29de97 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1174,6 +1174,8 @@ * [Sol1](project_euler/problem_144/sol1.py) * Problem 145 * [Sol1](project_euler/problem_145/sol1.py) + * Problem 164 + * [Sol1](project_euler/problem_164/sol1.py) * Problem 173 * [Sol1](project_euler/problem_173/sol1.py) * Problem 174 @@ -1184,6 +1186,8 @@ * [Sol1](project_euler/problem_187/sol1.py) * Problem 188 * [Sol1](project_euler/problem_188/sol1.py) + * Problem 190 + * [Sol1](project_euler/problem_190/sol1.py) * Problem 191 * [Sol1](project_euler/problem_191/sol1.py) * Problem 203 From d9d56b10464f6825dc762c1665716e76b70c5fa2 Mon Sep 17 00:00:00 2001 From: Mindaugas <76015221+mindaugl@users.noreply.github.com> Date: Wed, 7 May 2025 03:49:59 +0800 Subject: [PATCH 114/126] Add solution for the Euler project problem 345 (#12666) * Add solution for the Euler project problem 345. * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update sol1.py * Update sol1.py * Update sol1.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Maxim Smolskiy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- project_euler/problem_345/__init__.py | 0 project_euler/problem_345/sol1.py | 117 ++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 project_euler/problem_345/__init__.py create mode 100644 project_euler/problem_345/sol1.py diff --git a/project_euler/problem_345/__init__.py b/project_euler/problem_345/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_345/sol1.py b/project_euler/problem_345/sol1.py new file mode 100644 index 000000000000..4234458c5ad5 --- /dev/null +++ b/project_euler/problem_345/sol1.py @@ -0,0 +1,117 @@ +""" +Project Euler Problem 345: https://projecteuler.net/problem=345 + +Matrix Sum + +We define the Matrix Sum of a matrix as the maximum possible sum of +matrix elements such that none of the selected elements share the same row or column. + +For example, the Matrix Sum of the matrix below equals +3315 ( = 863 + 383 + 343 + 959 + 767): + 7 53 183 439 863 + 497 383 563 79 973 + 287 63 343 169 583 + 627 343 773 959 943 + 767 473 103 699 303 + +Find the Matrix Sum of: + 7 53 183 439 863 497 383 563 79 973 287 63 343 169 583 + 627 343 773 959 943 767 473 103 699 303 957 703 583 639 913 + 447 283 463 29 23 487 463 993 119 883 327 493 423 159 743 + 217 623 3 399 853 407 103 983 89 463 290 516 212 462 350 + 960 376 682 962 300 780 486 502 912 800 250 346 172 812 350 + 870 456 192 162 593 473 915 45 989 873 823 965 425 329 803 + 973 965 905 919 133 673 665 235 509 613 673 815 165 992 326 + 322 148 972 962 286 255 941 541 265 323 925 281 601 95 973 + 445 721 11 525 473 65 511 164 138 672 18 428 154 448 848 + 414 456 310 312 798 104 566 520 302 248 694 976 430 392 198 + 184 829 373 181 631 101 969 613 840 740 778 458 284 760 390 + 821 461 843 513 17 901 711 993 293 157 274 94 192 156 574 + 34 124 4 878 450 476 712 914 838 669 875 299 823 329 699 + 815 559 813 459 522 788 168 586 966 232 308 833 251 631 107 + 813 883 451 509 615 77 281 613 459 205 380 274 302 35 805 + +Brute force solution, with caching intermediate steps to speed up the calculation. +""" + +import numpy as np +from numpy.typing import NDArray + +MATRIX_1 = [ + "7 53 183 439 863", + "497 383 563 79 973", + "287 63 343 169 583", + "627 343 773 959 943", + "767 473 103 699 303", +] + +MATRIX_2 = [ + "7 53 183 439 863 497 383 563 79 973 287 63 343 169 583", + "627 343 773 959 943 767 473 103 699 303 957 703 583 639 913", + "447 283 463 29 23 487 463 993 119 883 327 493 423 159 743", + "217 623 3 399 853 407 103 983 89 463 290 516 212 462 350", + "960 376 682 962 300 780 486 502 912 800 250 346 172 812 350", + "870 456 192 162 593 473 915 45 989 873 823 965 425 329 803", + "973 965 905 919 133 673 665 235 509 613 673 815 165 992 326", + "322 148 972 962 286 255 941 541 265 323 925 281 601 95 973", + "445 721 11 525 473 65 511 164 138 672 18 428 154 448 848", + "414 456 310 312 798 104 566 520 302 248 694 976 430 392 198", + "184 829 373 181 631 101 969 613 840 740 778 458 284 760 390", + "821 461 843 513 17 901 711 993 293 157 274 94 192 156 574", + "34 124 4 878 450 476 712 914 838 669 875 299 823 329 699", + "815 559 813 459 522 788 168 586 966 232 308 833 251 631 107", + "813 883 451 509 615 77 281 613 459 205 380 274 302 35 805", +] + + +def solve(arr: NDArray, row: int, cols: set[int], cache: dict[str, int]) -> int: + """ + Finds the max sum for array `arr` starting with row index `row`, and with columns + included in `cols`. `cache` is used for caching intermediate results. + + >>> solve(arr=np.array([[1, 2], [3, 4]]), row=0, cols={0, 1}, cache={}) + 5 + """ + + cache_id = f"{row}, {sorted(cols)}" + if cache_id in cache: + return cache[cache_id] + + if row == len(arr): + return 0 + + max_sum = 0 + for col in cols: + new_cols = cols - {col} + max_sum = max( + max_sum, + int(arr[row, col]) + + solve(arr=arr, row=row + 1, cols=new_cols, cache=cache), + ) + cache[cache_id] = max_sum + return max_sum + + +def solution(matrix_str: list[str] = MATRIX_2) -> int: + """ + Takes list of strings `matrix_str` to parse the matrix and calculates the max sum. + + >>> solution(["1 2", "3 4"]) + 5 + >>> solution(MATRIX_1) + 3315 + """ + + n = len(matrix_str) + arr = np.empty(shape=(n, n), dtype=int) + for row, matrix_row_str in enumerate(matrix_str): + matrix_row_list_str = matrix_row_str.split() + for col, elem_str in enumerate(matrix_row_list_str): + arr[row, col] = int(elem_str) + + cache: dict[str, int] = {} + return solve(arr=arr, row=0, cols=set(range(n)), cache=cache) + + +if __name__ == "__main__": + print(f"{solution() = }") From b720f24b89c328944f8a0d6c18db0e09d9bcffba Mon Sep 17 00:00:00 2001 From: Mindaugas <76015221+mindaugl@users.noreply.github.com> Date: Sat, 10 May 2025 19:13:07 +0800 Subject: [PATCH 115/126] Add solution for the Euler project problem 95. (#12669) * Add documentation and tests for the Euler project problem 95 solution. * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update sol1.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update sol1.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update sol1.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py * Update sol1.py --------- Co-authored-by: Maxim Smolskiy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- project_euler/problem_095/__init__.py | 0 project_euler/problem_095/sol1.py | 164 ++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 project_euler/problem_095/__init__.py create mode 100644 project_euler/problem_095/sol1.py diff --git a/project_euler/problem_095/__init__.py b/project_euler/problem_095/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_095/sol1.py b/project_euler/problem_095/sol1.py new file mode 100644 index 000000000000..82d84c5544de --- /dev/null +++ b/project_euler/problem_095/sol1.py @@ -0,0 +1,164 @@ +""" +Project Euler Problem 95: https://projecteuler.net/problem=95 + +Amicable Chains + +The proper divisors of a number are all the divisors excluding the number itself. +For example, the proper divisors of 28 are 1, 2, 4, 7, and 14. +As the sum of these divisors is equal to 28, we call it a perfect number. + +Interestingly the sum of the proper divisors of 220 is 284 and +the sum of the proper divisors of 284 is 220, forming a chain of two numbers. +For this reason, 220 and 284 are called an amicable pair. + +Perhaps less well known are longer chains. +For example, starting with 12496, we form a chain of five numbers: + 12496 -> 14288 -> 15472 -> 14536 -> 14264 (-> 12496 -> ...) + +Since this chain returns to its starting point, it is called an amicable chain. + +Find the smallest member of the longest amicable chain with +no element exceeding one million. + +Solution is doing the following: +- Get relevant prime numbers +- Iterate over product combination of prime numbers to generate all non-prime + numbers up to max number, by keeping track of prime factors +- Calculate the sum of factors for each number +- Iterate over found some factors to find longest chain +""" + +from math import isqrt + + +def generate_primes(max_num: int) -> list[int]: + """ + Calculates the list of primes up to and including `max_num`. + + >>> generate_primes(6) + [2, 3, 5] + """ + are_primes = [True] * (max_num + 1) + are_primes[0] = are_primes[1] = False + for i in range(2, isqrt(max_num) + 1): + if are_primes[i]: + for j in range(i * i, max_num + 1, i): + are_primes[j] = False + + return [prime for prime, is_prime in enumerate(are_primes) if is_prime] + + +def multiply( + chain: list[int], + primes: list[int], + min_prime_idx: int, + prev_num: int, + max_num: int, + prev_sum: int, + primes_degrees: dict[int, int], +) -> None: + """ + Run over all prime combinations to generate non-prime numbers. + + >>> chain = [0] * 3 + >>> primes_degrees = {} + >>> multiply( + ... chain=chain, + ... primes=[2], + ... min_prime_idx=0, + ... prev_num=1, + ... max_num=2, + ... prev_sum=0, + ... primes_degrees=primes_degrees, + ... ) + >>> chain + [0, 0, 1] + >>> primes_degrees + {2: 1} + """ + + min_prime = primes[min_prime_idx] + num = prev_num * min_prime + + min_prime_degree = primes_degrees.get(min_prime, 0) + min_prime_degree += 1 + primes_degrees[min_prime] = min_prime_degree + + new_sum = prev_sum * min_prime + (prev_sum + prev_num) * (min_prime - 1) // ( + min_prime**min_prime_degree - 1 + ) + chain[num] = new_sum + + for prime_idx in range(min_prime_idx, len(primes)): + if primes[prime_idx] * num > max_num: + break + + multiply( + chain=chain, + primes=primes, + min_prime_idx=prime_idx, + prev_num=num, + max_num=max_num, + prev_sum=new_sum, + primes_degrees=primes_degrees.copy(), + ) + + +def find_longest_chain(chain: list[int], max_num: int) -> int: + """ + Finds the smallest element of longest chain + + >>> find_longest_chain(chain=[0, 0, 0, 0, 0, 0, 6], max_num=6) + 6 + """ + + max_len = 0 + min_elem = 0 + for start in range(2, len(chain)): + visited = {start} + elem = chain[start] + length = 1 + + while elem > 1 and elem <= max_num and elem not in visited: + visited.add(elem) + elem = chain[elem] + length += 1 + + if elem == start and length > max_len: + max_len = length + min_elem = start + + return min_elem + + +def solution(max_num: int = 1000000) -> int: + """ + Runs the calculation for numbers <= `max_num`. + + >>> solution(10) + 6 + >>> solution(200000) + 12496 + """ + + primes = generate_primes(max_num) + chain = [0] * (max_num + 1) + for prime_idx, prime in enumerate(primes): + if prime**2 > max_num: + break + + multiply( + chain=chain, + primes=primes, + min_prime_idx=prime_idx, + prev_num=1, + max_num=max_num, + prev_sum=0, + primes_degrees={}, + ) + + return find_longest_chain(chain=chain, max_num=max_num) + + +if __name__ == "__main__": + print(f"{solution() = }") From a728cc96ab4f05248ac3389365a26f01dfaf6f8e Mon Sep 17 00:00:00 2001 From: NidhaNureen <165757787+NidhaNureen@users.noreply.github.com> Date: Sat, 10 May 2025 23:32:45 +1200 Subject: [PATCH 116/126] Added/Improved doctests for lowest_common_ancestor.py (#12673) * added doctests to functions in lowest_common_ancestor.py * fixed doctests to be less excessive * Update lowest_common_ancestor.py * Update lowest_common_ancestor.py * Update lowest_common_ancestor.py * Update lowest_common_ancestor.py * Update lowest_common_ancestor.py * Update lowest_common_ancestor.py --------- Co-authored-by: Maxim Smolskiy --- .../binary_tree/lowest_common_ancestor.py | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/data_structures/binary_tree/lowest_common_ancestor.py b/data_structures/binary_tree/lowest_common_ancestor.py index 651037703b95..ea0e31256903 100644 --- a/data_structures/binary_tree/lowest_common_ancestor.py +++ b/data_structures/binary_tree/lowest_common_ancestor.py @@ -15,6 +15,8 @@ def swap(a: int, b: int) -> tuple[int, int]: (4, 3) >>> swap(67, 12) (12, 67) + >>> swap(3,-4) + (-4, 3) """ a ^= b b ^= a @@ -25,6 +27,23 @@ def swap(a: int, b: int) -> tuple[int, int]: def create_sparse(max_node: int, parent: list[list[int]]) -> list[list[int]]: """ creating sparse table which saves each nodes 2^i-th parent + >>> max_node = 6 + >>> parent = [[0, 0, 1, 1, 2, 2, 3]] + [[0] * 7 for _ in range(19)] + >>> parent = create_sparse(max_node=max_node, parent=parent) + >>> parent[0] + [0, 0, 1, 1, 2, 2, 3] + >>> parent[1] + [0, 0, 0, 0, 1, 1, 1] + >>> parent[2] + [0, 0, 0, 0, 0, 0, 0] + + >>> max_node = 1 + >>> parent = [[0, 0]] + [[0] * 2 for _ in range(19)] + >>> parent = create_sparse(max_node=max_node, parent=parent) + >>> parent[0] + [0, 0] + >>> parent[1] + [0, 0] """ j = 1 while (1 << j) < max_node: @@ -38,6 +57,21 @@ def create_sparse(max_node: int, parent: list[list[int]]) -> list[list[int]]: def lowest_common_ancestor( u: int, v: int, level: list[int], parent: list[list[int]] ) -> int: + """ + Return the lowest common ancestor between u and v + + >>> level = [-1, 0, 1, 1, 2, 2, 2] + >>> parent = [[0, 0, 1, 1, 2, 2, 3],[0, 0, 0, 0, 1, 1, 1]] + \ + [[0] * 7 for _ in range(17)] + >>> lowest_common_ancestor(u=4, v=5, level=level, parent=parent) + 2 + >>> lowest_common_ancestor(u=4, v=6, level=level, parent=parent) + 1 + >>> lowest_common_ancestor(u=2, v=3, level=level, parent=parent) + 1 + >>> lowest_common_ancestor(u=6, v=6, level=level, parent=parent) + 6 + """ # u must be deeper in the tree than v if level[u] < level[v]: u, v = swap(u, v) @@ -68,6 +102,26 @@ def breadth_first_search( sets every nodes direct parent parent of root node is set to 0 calculates depth of each node from root node + >>> level = [-1] * 7 + >>> parent = [[0] * 7 for _ in range(20)] + >>> graph = {1: [2, 3], 2: [4, 5], 3: [6], 4: [], 5: [], 6: []} + >>> level, parent = breadth_first_search( + ... level=level, parent=parent, max_node=6, graph=graph, root=1) + >>> level + [-1, 0, 1, 1, 2, 2, 2] + >>> parent[0] + [0, 0, 1, 1, 2, 2, 3] + + + >>> level = [-1] * 2 + >>> parent = [[0] * 2 for _ in range(20)] + >>> graph = {1: []} + >>> level, parent = breadth_first_search( + ... level=level, parent=parent, max_node=1, graph=graph, root=1) + >>> level + [-1, 0] + >>> parent[0] + [0, 0] """ level[root] = 0 q: Queue[int] = Queue(maxsize=max_node) From 59c3c8bbf384ef05d3cb9862a41bba39bb098fe9 Mon Sep 17 00:00:00 2001 From: robohie Date: Sat, 10 May 2025 12:47:22 +0100 Subject: [PATCH 117/126] Add N Input AND Gate (#12717) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update and_gate.py J'ai nourri ce programme en ajoutant une porte And à n entrées. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update and_gate.py Commentaires en anglais * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update and_gate.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- boolean_algebra/and_gate.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/boolean_algebra/and_gate.py b/boolean_algebra/and_gate.py index 6ae66b5b0a77..650017b7ae10 100644 --- a/boolean_algebra/and_gate.py +++ b/boolean_algebra/and_gate.py @@ -1,8 +1,8 @@ """ -An AND Gate is a logic gate in boolean algebra which results to 1 (True) if both the -inputs are 1, and 0 (False) otherwise. +An AND Gate is a logic gate in boolean algebra which results to 1 (True) if all the +inputs are 1 (True), and 0 (False) otherwise. -Following is the truth table of an AND Gate: +Following is the truth table of a Two Input AND Gate: ------------------------------ | Input 1 | Input 2 | Output | ------------------------------ @@ -12,7 +12,7 @@ | 1 | 1 | 1 | ------------------------------ -Refer - https://www.geeksforgeeks.org/logic-gates-in-python/ +Refer - https://www.geeksforgeeks.org/logic-gates/ """ @@ -32,6 +32,18 @@ def and_gate(input_1: int, input_2: int) -> int: return int(input_1 and input_2) +def n_input_and_gate(inputs: list[int]) -> int: + """ + Calculate AND of a list of input values + + >>> n_input_and_gate([1, 0, 1, 1, 0]) + 0 + >>> n_input_and_gate([1, 1, 1, 1, 1]) + 1 + """ + return int(all(inputs)) + + if __name__ == "__main__": import doctest From 47a44abe23870ca0f7c8062601278645039b1c70 Mon Sep 17 00:00:00 2001 From: Alfredo Hernandez Baeza Date: Sat, 10 May 2025 07:57:43 -0400 Subject: [PATCH 118/126] Improve longest_common_substring.py (#12705) * Update longest_common_substring.py - Combined the ans_index and ans_length into a single tuple to track the best match (position + length) more cleanly. - Early exit for empty strings. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update longest_common_substring.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- dynamic_programming/longest_common_substring.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/dynamic_programming/longest_common_substring.py b/dynamic_programming/longest_common_substring.py index ea5233eb2d17..3ba83f3d9f03 100644 --- a/dynamic_programming/longest_common_substring.py +++ b/dynamic_programming/longest_common_substring.py @@ -43,22 +43,25 @@ def longest_common_substring(text1: str, text2: str) -> str: if not (isinstance(text1, str) and isinstance(text2, str)): raise ValueError("longest_common_substring() takes two strings for inputs") + if not text1 or not text2: + return "" + text1_length = len(text1) text2_length = len(text2) dp = [[0] * (text2_length + 1) for _ in range(text1_length + 1)] - ans_index = 0 - ans_length = 0 + end_pos = 0 + max_length = 0 for i in range(1, text1_length + 1): for j in range(1, text2_length + 1): if text1[i - 1] == text2[j - 1]: dp[i][j] = 1 + dp[i - 1][j - 1] - if dp[i][j] > ans_length: - ans_index = i - ans_length = dp[i][j] + if dp[i][j] > max_length: + end_pos = i + max_length = dp[i][j] - return text1[ans_index - ans_length : ans_index] + return text1[end_pos - max_length : end_pos] if __name__ == "__main__": From 131765574f5ccfaff4214a6f848412ce2fe4ab20 Mon Sep 17 00:00:00 2001 From: robohie Date: Sat, 10 May 2025 21:18:02 +0100 Subject: [PATCH 119/126] Fix error messages for horizontal_projectile_motion.py (#12722) * Update horizontal_projectile_motion.py This commit is about logic of this program. Changes made aim to allow a good understanding of what is done. * Update horizontal_projectile_motion.py --------- Co-authored-by: Maxim Smolskiy --- physics/horizontal_projectile_motion.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/physics/horizontal_projectile_motion.py b/physics/horizontal_projectile_motion.py index 60f21c2b39c4..32dc5eb36f35 100644 --- a/physics/horizontal_projectile_motion.py +++ b/physics/horizontal_projectile_motion.py @@ -17,7 +17,7 @@ """ # Importing packages -from math import radians as angle_to_radians +from math import radians as deg_to_rad from math import sin # Acceleration Constant on Earth (unit m/s^2) @@ -31,10 +31,10 @@ def check_args(init_velocity: float, angle: float) -> None: # Ensure valid instance if not isinstance(init_velocity, (int, float)): - raise TypeError("Invalid velocity. Should be a positive number.") + raise TypeError("Invalid velocity. Should be an integer or float.") if not isinstance(angle, (int, float)): - raise TypeError("Invalid angle. Range is 1-90 degrees.") + raise TypeError("Invalid angle. Should be an integer or float.") # Ensure valid angle if angle > 90 or angle < 1: @@ -71,7 +71,7 @@ def horizontal_distance(init_velocity: float, angle: float) -> float: ValueError: Invalid angle. Range is 1-90 degrees. """ check_args(init_velocity, angle) - radians = angle_to_radians(2 * angle) + radians = deg_to_rad(2 * angle) return round(init_velocity**2 * sin(radians) / g, 2) @@ -94,14 +94,14 @@ def max_height(init_velocity: float, angle: float) -> float: >>> max_height("a", 20) Traceback (most recent call last): ... - TypeError: Invalid velocity. Should be a positive number. + TypeError: Invalid velocity. Should be an integer or float. >>> horizontal_distance(30, "b") Traceback (most recent call last): ... - TypeError: Invalid angle. Range is 1-90 degrees. + TypeError: Invalid angle. Should be an integer or float. """ check_args(init_velocity, angle) - radians = angle_to_radians(angle) + radians = deg_to_rad(angle) return round(init_velocity**2 * sin(radians) ** 2 / (2 * g), 2) @@ -128,10 +128,10 @@ def total_time(init_velocity: float, angle: float) -> float: >>> total_time(30, "b") Traceback (most recent call last): ... - TypeError: Invalid angle. Range is 1-90 degrees. + TypeError: Invalid angle. Should be an integer or float. """ check_args(init_velocity, angle) - radians = angle_to_radians(angle) + radians = deg_to_rad(angle) return round(2 * init_velocity * sin(radians) / g, 2) From 95fb181f5a944427fdbc5766cbf4e1cb699d4a6d Mon Sep 17 00:00:00 2001 From: S Sajeev <167018420+SajeevSenthil@users.noreply.github.com> Date: Sun, 11 May 2025 02:13:39 +0530 Subject: [PATCH 120/126] Add escape velocity calculator using standard physics formula (#12721) * Added iterative solution for power calculation * Added iterative solution for power calculation * Added iterative solution for power calculation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added iterative solution for power calculation fixes #12709 * Added iterative solution for power calculation FIXES NUMBER 12709 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Escape velocity is the minimum speed an object must have to break free from a celestial body's gravitational pull without further propulsion. Takes input as the Mass of the Celestial body (M) and Radius fron the center of mass (M) * Fix: added header comment to escape_velocity.py * Trigger re-PR with a minor change * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix: resolve Ruff linter errors and add Wikipedia reference * Delete maths/power_using_iteration.py * Test doctests * Update escape_velocity.py * Update escape_velocity.py * Update escape_velocity.py * Update escape_velocity.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- physics/escape_velocity.py | 67 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 physics/escape_velocity.py diff --git a/physics/escape_velocity.py b/physics/escape_velocity.py new file mode 100644 index 000000000000..e54ed5e50798 --- /dev/null +++ b/physics/escape_velocity.py @@ -0,0 +1,67 @@ +import math + + +def escape_velocity(mass: float, radius: float) -> float: + """ + Calculates the escape velocity needed to break free from a celestial body's + gravitational field. + + The formula used is: + v = sqrt(2 * G * M / R) + + where: + v = escape velocity (m/s) + G = gravitational constant (6.67430 * 10^-11 m^3 kg^-1 s^-2) + M = mass of the celestial body (kg) + R = radius from the center of mass (m) + + Source: + https://en.wikipedia.org/wiki/Escape_velocity + + Args: + mass (float): Mass of the celestial body in kilograms. + radius (float): Radius from the center of mass in meters. + + Returns: + float: Escape velocity in meters per second, rounded to 3 decimal places. + + Examples: + >>> escape_velocity(mass=5.972e24, radius=6.371e6) # Earth + 11185.978 + >>> escape_velocity(mass=7.348e22, radius=1.737e6) # Moon + 2376.307 + >>> escape_velocity(mass=1.898e27, radius=6.9911e7) # Jupiter + 60199.545 + >>> escape_velocity(mass=0, radius=1.0) + 0.0 + >>> escape_velocity(mass=1.0, radius=0) + Traceback (most recent call last): + ... + ZeroDivisionError: Radius cannot be zero. + """ + gravitational_constant = 6.67430e-11 # m^3 kg^-1 s^-2 + + if radius == 0: + raise ZeroDivisionError("Radius cannot be zero.") + + velocity = math.sqrt(2 * gravitational_constant * mass / radius) + return round(velocity, 3) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + print("Calculate escape velocity of a celestial body...\n") + + try: + mass = float(input("Enter mass of the celestial body (in kgs): ").strip()) + radius = float(input("Enter radius from the center of mass (in ms): ").strip()) + + velocity = escape_velocity(mass=mass, radius=radius) + print(f"Escape velocity is {velocity} m/s") + + except ValueError: + print("Invalid input. Please enter valid numeric values.") + except ZeroDivisionError as e: + print(e) From 1ea05feffec48250a3d6d0ccbd1378242d680cbc Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 12 May 2025 11:59:19 +0200 Subject: [PATCH 121/126] Prep for Python 3.14: Rename compression to data_compression (#12725) * Prep for Python 3.14: Rename compression to data_compression * updating DIRECTORY.md --------- Co-authored-by: cclauss --- DIRECTORY.md | 23 +++++++++++------- compression/image_data/__init__.py | 0 {compression => data_compression}/README.md | 0 {compression => data_compression}/__init__.py | 0 .../burrows_wheeler.py | 0 {compression => data_compression}/huffman.py | 0 .../image_data/PSNR-example-base.png | Bin .../image_data/PSNR-example-comp-10.jpg | Bin .../image_data/compressed_image.png | Bin .../image_data/example_image.jpg | Bin .../image_data/example_wikipedia_image.jpg | Bin .../image_data/original_image.png | Bin .../lempel_ziv.py | 0 .../lempel_ziv_decompress.py | 0 {compression => data_compression}/lz77.py | 0 .../peak_signal_to_noise_ratio.py | 0 .../run_length_encoding.py | 0 pyproject.toml | 2 +- 18 files changed, 15 insertions(+), 10 deletions(-) delete mode 100644 compression/image_data/__init__.py rename {compression => data_compression}/README.md (100%) rename {compression => data_compression}/__init__.py (100%) rename {compression => data_compression}/burrows_wheeler.py (100%) rename {compression => data_compression}/huffman.py (100%) rename {compression => data_compression}/image_data/PSNR-example-base.png (100%) rename {compression => data_compression}/image_data/PSNR-example-comp-10.jpg (100%) rename {compression => data_compression}/image_data/compressed_image.png (100%) rename {compression => data_compression}/image_data/example_image.jpg (100%) rename {compression => data_compression}/image_data/example_wikipedia_image.jpg (100%) rename {compression => data_compression}/image_data/original_image.png (100%) rename {compression => data_compression}/lempel_ziv.py (100%) rename {compression => data_compression}/lempel_ziv_decompress.py (100%) rename {compression => data_compression}/lz77.py (100%) rename {compression => data_compression}/peak_signal_to_noise_ratio.py (100%) rename {compression => data_compression}/run_length_encoding.py (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 04e09c29de97..85dcc243462e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -128,15 +128,6 @@ * [Vigenere Cipher](ciphers/vigenere_cipher.py) * [Xor Cipher](ciphers/xor_cipher.py) -## Compression - * [Burrows Wheeler](compression/burrows_wheeler.py) - * [Huffman](compression/huffman.py) - * [Lempel Ziv](compression/lempel_ziv.py) - * [Lempel Ziv Decompress](compression/lempel_ziv_decompress.py) - * [Lz77](compression/lz77.py) - * [Peak Signal To Noise Ratio](compression/peak_signal_to_noise_ratio.py) - * [Run Length Encoding](compression/run_length_encoding.py) - ## Computer Vision * [Cnn Classification](computer_vision/cnn_classification.py) * [Flip Augmentation](computer_vision/flip_augmentation.py) @@ -181,6 +172,15 @@ * [Volume Conversions](conversions/volume_conversions.py) * [Weight Conversion](conversions/weight_conversion.py) +## Data Compression + * [Burrows Wheeler](data_compression/burrows_wheeler.py) + * [Huffman](data_compression/huffman.py) + * [Lempel Ziv](data_compression/lempel_ziv.py) + * [Lempel Ziv Decompress](data_compression/lempel_ziv_decompress.py) + * [Lz77](data_compression/lz77.py) + * [Peak Signal To Noise Ratio](data_compression/peak_signal_to_noise_ratio.py) + * [Run Length Encoding](data_compression/run_length_encoding.py) + ## Data Structures * Arrays * [Equilibrium Index In Array](data_structures/arrays/equilibrium_index_in_array.py) @@ -884,6 +884,7 @@ * [Centripetal Force](physics/centripetal_force.py) * [Coulombs Law](physics/coulombs_law.py) * [Doppler Frequency](physics/doppler_frequency.py) + * [Escape Velocity](physics/escape_velocity.py) * [Grahams Law](physics/grahams_law.py) * [Horizontal Projectile Motion](physics/horizontal_projectile_motion.py) * [Hubble Parameter](physics/hubble_parameter.py) @@ -1122,6 +1123,8 @@ * [Sol1](project_euler/problem_092/sol1.py) * Problem 094 * [Sol1](project_euler/problem_094/sol1.py) + * Problem 095 + * [Sol1](project_euler/problem_095/sol1.py) * Problem 097 * [Sol1](project_euler/problem_097/sol1.py) * Problem 099 @@ -1202,6 +1205,8 @@ * [Sol1](project_euler/problem_234/sol1.py) * Problem 301 * [Sol1](project_euler/problem_301/sol1.py) + * Problem 345 + * [Sol1](project_euler/problem_345/sol1.py) * Problem 493 * [Sol1](project_euler/problem_493/sol1.py) * Problem 551 diff --git a/compression/image_data/__init__.py b/compression/image_data/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/compression/README.md b/data_compression/README.md similarity index 100% rename from compression/README.md rename to data_compression/README.md diff --git a/compression/__init__.py b/data_compression/__init__.py similarity index 100% rename from compression/__init__.py rename to data_compression/__init__.py diff --git a/compression/burrows_wheeler.py b/data_compression/burrows_wheeler.py similarity index 100% rename from compression/burrows_wheeler.py rename to data_compression/burrows_wheeler.py diff --git a/compression/huffman.py b/data_compression/huffman.py similarity index 100% rename from compression/huffman.py rename to data_compression/huffman.py diff --git a/compression/image_data/PSNR-example-base.png b/data_compression/image_data/PSNR-example-base.png similarity index 100% rename from compression/image_data/PSNR-example-base.png rename to data_compression/image_data/PSNR-example-base.png diff --git a/compression/image_data/PSNR-example-comp-10.jpg b/data_compression/image_data/PSNR-example-comp-10.jpg similarity index 100% rename from compression/image_data/PSNR-example-comp-10.jpg rename to data_compression/image_data/PSNR-example-comp-10.jpg diff --git a/compression/image_data/compressed_image.png b/data_compression/image_data/compressed_image.png similarity index 100% rename from compression/image_data/compressed_image.png rename to data_compression/image_data/compressed_image.png diff --git a/compression/image_data/example_image.jpg b/data_compression/image_data/example_image.jpg similarity index 100% rename from compression/image_data/example_image.jpg rename to data_compression/image_data/example_image.jpg diff --git a/compression/image_data/example_wikipedia_image.jpg b/data_compression/image_data/example_wikipedia_image.jpg similarity index 100% rename from compression/image_data/example_wikipedia_image.jpg rename to data_compression/image_data/example_wikipedia_image.jpg diff --git a/compression/image_data/original_image.png b/data_compression/image_data/original_image.png similarity index 100% rename from compression/image_data/original_image.png rename to data_compression/image_data/original_image.png diff --git a/compression/lempel_ziv.py b/data_compression/lempel_ziv.py similarity index 100% rename from compression/lempel_ziv.py rename to data_compression/lempel_ziv.py diff --git a/compression/lempel_ziv_decompress.py b/data_compression/lempel_ziv_decompress.py similarity index 100% rename from compression/lempel_ziv_decompress.py rename to data_compression/lempel_ziv_decompress.py diff --git a/compression/lz77.py b/data_compression/lz77.py similarity index 100% rename from compression/lz77.py rename to data_compression/lz77.py diff --git a/compression/peak_signal_to_noise_ratio.py b/data_compression/peak_signal_to_noise_ratio.py similarity index 100% rename from compression/peak_signal_to_noise_ratio.py rename to data_compression/peak_signal_to_noise_ratio.py diff --git a/compression/run_length_encoding.py b/data_compression/run_length_encoding.py similarity index 100% rename from compression/run_length_encoding.py rename to data_compression/run_length_encoding.py diff --git a/pyproject.toml b/pyproject.toml index 60f8d4ffc96f..c320a2f2bbfe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -187,9 +187,9 @@ autoapi_dirs = [ "boolean_algebra", "cellular_automata", "ciphers", - "compression", "computer_vision", "conversions", + "data_compression", "data_structures", "digital_image_processing", "divide_and_conquer", From 088c74e84026fcb77b3e3e0c15c83111a6979ae7 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 12 May 2025 12:45:53 +0200 Subject: [PATCH 122/126] Delete empty source directory (#12730) --- source/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 source/__init__.py diff --git a/source/__init__.py b/source/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 From 485f688d0683ca026959e1b68f12e5d221724860 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 12 May 2025 12:52:27 +0200 Subject: [PATCH 123/126] Add PEP723 header to scripts/validate_solutions.py (#12731) Enable `uv run scripts/validate_solutions.py` or `pipx run scripts/validate_solutions.py` * https://peps.python.org/pep-0723 * https://docs.astral.sh/uv/guides/scripts/#declaring-script-dependencies --- scripts/validate_solutions.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/validate_solutions.py b/scripts/validate_solutions.py index df5d01086bbe..c3f872203591 100755 --- a/scripts/validate_solutions.py +++ b/scripts/validate_solutions.py @@ -1,4 +1,13 @@ #!/usr/bin/env python3 + +# /// script +# requires-python = ">=3.13" +# dependencies = [ +# "pytest", +# "requests", +# ] +# /// + import hashlib import importlib.util import json From f721e598e5677ed368b7dbd119092eb409ab2fb0 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 12 May 2025 13:33:33 +0200 Subject: [PATCH 124/126] Add a proper shebang line to scripts/validate_filenames.py (#12733) --- scripts/validate_filenames.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/validate_filenames.py b/scripts/validate_filenames.py index 80399673cced..a7328e099dde 100755 --- a/scripts/validate_filenames.py +++ b/scripts/validate_filenames.py @@ -1,4 +1,5 @@ -#!python +#!/usr/bin/env python3 + import os try: From ee3a1732e007d32b5835635ac7c7fbb2b5464f53 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 19:59:33 +0200 Subject: [PATCH 125/126] [pre-commit.ci] pre-commit autoupdate (#12736) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.8 → v0.11.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.8...v0.11.9) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9e13416dc78d..288e3f591403 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.8 + rev: v0.11.9 hooks: - id: ruff - id: ruff-format From 6e4d1b376546cdf347b6513ff5f493510b956309 Mon Sep 17 00:00:00 2001 From: S Sajeev <167018420+SajeevSenthil@users.noreply.github.com> Date: Tue, 13 May 2025 14:44:05 +0530 Subject: [PATCH 126/126] Physics orbital_transfer_work (#12728) * Added iterative solution for power calculation * Added iterative solution for power calculation * Added iterative solution for power calculation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added iterative solution for power calculation fixes #12709 * Added iterative solution for power calculation FIXES NUMBER 12709 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Escape velocity is the minimum speed an object must have to break free from a celestial body's gravitational pull without further propulsion. Takes input as the Mass of the Celestial body (M) and Radius fron the center of mass (M) * Fix: added header comment to escape_velocity.py * Trigger re-PR with a minor change * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix: resolve Ruff linter errors and add Wikipedia reference * Add: work done calculation for orbital transfer between orbits * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update escape_velocity.py * Delete maths/power_using_iteration.py * Update and rename workdone.py to orbital_transfer_work.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- physics/orbital_transfer_work.py | 73 ++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 physics/orbital_transfer_work.py diff --git a/physics/orbital_transfer_work.py b/physics/orbital_transfer_work.py new file mode 100644 index 000000000000..7fb90fe9be0e --- /dev/null +++ b/physics/orbital_transfer_work.py @@ -0,0 +1,73 @@ +def orbital_transfer_work( + mass_central: float, mass_object: float, r_initial: float, r_final: float +) -> str: + """ + Calculates the work required to move an object from one orbit to another in a + gravitational field based on the change in total mechanical energy. + + The formula used is: + W = (G * M * m / 2) * (1/r_initial - 1/r_final) + + where: + W = work done (Joules) + G = gravitational constant (6.67430 * 10^-11 m^3 kg^-1 s^-2) + M = mass of the central body (kg) + m = mass of the orbiting object (kg) + r_initial = initial orbit radius (m) + r_final = final orbit radius (m) + + Args: + mass_central (float): Mass of the central body (kg) + mass_object (float): Mass of the object being moved (kg) + r_initial (float): Initial orbital radius (m) + r_final (float): Final orbital radius (m) + + Returns: + str: Work done in Joules as a string in scientific notation (3 decimals) + + Examples: + >>> orbital_transfer_work(5.972e24, 1000, 6.371e6, 7e6) + '2.811e+09' + >>> orbital_transfer_work(5.972e24, 500, 7e6, 6.371e6) + '-1.405e+09' + >>> orbital_transfer_work(1.989e30, 1000, 1.5e11, 2.28e11) + '1.514e+11' + """ + gravitational_constant = 6.67430e-11 + + if r_initial <= 0 or r_final <= 0: + raise ValueError("Orbital radii must be greater than zero.") + + work = (gravitational_constant * mass_central * mass_object / 2) * ( + 1 / r_initial - 1 / r_final + ) + return f"{work:.3e}" + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + print("Orbital transfer work calculator\n") + + try: + M = float(input("Enter mass of central body (kg): ").strip()) + if M <= 0: + r1 = float(input("Enter initial orbit radius (m): ").strip()) + if r1 <= 0: + raise ValueError("Initial orbit radius must be greater than zero.") + + r2 = float(input("Enter final orbit radius (m): ").strip()) + if r2 <= 0: + raise ValueError("Final orbit radius must be greater than zero.") + m = float(input("Enter mass of orbiting object (kg): ").strip()) + if m <= 0: + raise ValueError("Mass of the orbiting object must be greater than zero.") + r1 = float(input("Enter initial orbit radius (m): ").strip()) + r2 = float(input("Enter final orbit radius (m): ").strip()) + + result = orbital_transfer_work(M, m, r1, r2) + print(f"Work done in orbital transfer: {result} Joules") + + except ValueError as e: + print(f"Input error: {e}")