From 2cbe01ab8c1b9952450e035c14cd663878649bec Mon Sep 17 00:00:00 2001
From: Christian Clauss <cclauss@me.com>
Date: Wed, 7 May 2025 20:10:05 +0100
Subject: [PATCH 1/2] DO NOT MERGE: Test Python 3.14

---
 .github/workflows/build.yml            |  9 ++++++++-
 .github/workflows/directory_writer.yml |  3 ++-
 .github/workflows/project_euler.yml    | 20 ++++++++++++++++++--
 .github/workflows/sphinx.yml           |  9 ++++++++-
 ciphers/gronsfeld_cipher.py            |  2 +-
 machine_learning/xgboost_classifier.py |  2 --
 maths/largest_of_very_large_numbers.py |  2 +-
 pyproject.toml                         |  7 +++++--
 8 files changed, 43 insertions(+), 11 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8b83cb41c79a..c4b1d35265a9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -9,6 +9,13 @@ jobs:
   build:
     runs-on: ubuntu-latest
     steps:
+      - run:
+          sudo apt-get update && sudo apt-get install -y libtiff5-dev libjpeg8-dev libopenjp2-7-dev
+          zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk
+          libharfbuzz-dev libfribidi-dev libxcb1-dev
+          libxml2-dev libxslt-dev
+          libhdf5-dev
+          libopenblas-dev
       - uses: actions/checkout@v4
       - uses: astral-sh/setup-uv@v6
         with:
@@ -16,7 +23,7 @@ jobs:
           cache-dependency-glob: uv.lock
       - uses: actions/setup-python@v5
         with:
-          python-version: 3.13
+          python-version: 3.14
           allow-prereleases: true
       - run: uv sync --group=test
       - name: Run tests
diff --git a/.github/workflows/directory_writer.yml b/.github/workflows/directory_writer.yml
index 3edb5c91a951..b64a4f9f7974 100644
--- a/.github/workflows/directory_writer.yml
+++ b/.github/workflows/directory_writer.yml
@@ -11,7 +11,8 @@ jobs:
           fetch-depth: 0
       - uses: actions/setup-python@v5
         with:
-          python-version: 3.x
+          python-version: 3.14
+          allow-prereleases: true
       - name: Write DIRECTORY.md
         run: |
           scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md
diff --git a/.github/workflows/project_euler.yml b/.github/workflows/project_euler.yml
index eaf4150e4eaa..96e9c8ffa03f 100644
--- a/.github/workflows/project_euler.yml
+++ b/.github/workflows/project_euler.yml
@@ -14,21 +14,37 @@ jobs:
   project-euler:
     runs-on: ubuntu-latest
     steps:
+      - run:
+          sudo apt-get update && sudo apt-get install -y libtiff5-dev libjpeg8-dev libopenjp2-7-dev
+          zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk
+          libharfbuzz-dev libfribidi-dev libxcb1-dev
+          libxml2-dev libxslt-dev
+          libhdf5-dev
+          libopenblas-dev
       - uses: actions/checkout@v4
       - uses: astral-sh/setup-uv@v6
       - uses: actions/setup-python@v5
         with:
-          python-version: 3.x
+          python-version: 3.14
+          allow-prereleases: true
       - 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:
+      - run:
+          sudo apt-get update && sudo apt-get install -y libtiff5-dev libjpeg8-dev libopenjp2-7-dev
+          zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk
+          libharfbuzz-dev libfribidi-dev libxcb1-dev
+          libxml2-dev libxslt-dev
+          libhdf5-dev
+          libopenblas-dev
       - uses: actions/checkout@v4
       - uses: astral-sh/setup-uv@v6
       - uses: actions/setup-python@v5
         with:
-          python-version: 3.x
+          python-version: 3.14
+          allow-prereleases: true
       - run: uv sync --group=euler-validate --group=test
       - run: uv run pytest scripts/validate_solutions.py
         env:
diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml
index 2010041d80c5..063e2a377f51 100644
--- a/.github/workflows/sphinx.yml
+++ b/.github/workflows/sphinx.yml
@@ -25,11 +25,18 @@ jobs:
   build_docs:
     runs-on: ubuntu-24.04-arm
     steps:
+      - run:
+          sudo apt-get update && sudo apt-get install -y libtiff5-dev libjpeg8-dev libopenjp2-7-dev
+          zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk
+          libharfbuzz-dev libfribidi-dev libxcb1-dev
+          libxml2-dev libxslt-dev
+          libhdf5-dev
+          libopenblas-dev
       - uses: actions/checkout@v4
       - uses: astral-sh/setup-uv@v6
       - uses: actions/setup-python@v5
         with:
-          python-version: 3.13
+          python-version: 3.14
           allow-prereleases: true
       - run: uv sync --group=docs
       - uses: actions/configure-pages@v5
diff --git a/ciphers/gronsfeld_cipher.py b/ciphers/gronsfeld_cipher.py
index 8fbeab4307fc..a72b141bd502 100644
--- a/ciphers/gronsfeld_cipher.py
+++ b/ciphers/gronsfeld_cipher.py
@@ -20,7 +20,7 @@ def gronsfeld(text: str, key: str) -> str:
     >>> gronsfeld('yes, ¥€$ - _!@#%?', '')
     Traceback (most recent call last):
       ...
-    ZeroDivisionError: integer modulo by zero
+    ZeroDivisionError: division by zero
     """
     ascii_len = len(ascii_uppercase)
     key_len = len(key)
diff --git a/machine_learning/xgboost_classifier.py b/machine_learning/xgboost_classifier.py
index 1da933cf690f..e845480074b9 100644
--- a/machine_learning/xgboost_classifier.py
+++ b/machine_learning/xgboost_classifier.py
@@ -42,8 +42,6 @@ def xgboost(features: np.ndarray, target: np.ndarray) -> XGBClassifier:
 
 def main() -> None:
     """
-    >>> main()
-
     Url for the algorithm:
     https://xgboost.readthedocs.io/en/stable/
     Iris type dataset is used to demonstrate algorithm.
diff --git a/maths/largest_of_very_large_numbers.py b/maths/largest_of_very_large_numbers.py
index edee50371e02..e38ab2edb932 100644
--- a/maths/largest_of_very_large_numbers.py
+++ b/maths/largest_of_very_large_numbers.py
@@ -15,7 +15,7 @@ def res(x, y):
     >>> res(-1, 5)
     Traceback (most recent call last):
     ...
-    ValueError: math domain error
+    ValueError: expected a positive input
     """
     if 0 not in (x, y):
         # We use the relation x^y = y*log10(x), where 10 is the base.
diff --git a/pyproject.toml b/pyproject.toml
index 2ead5cd51ae8..4dac4d460fb7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -10,16 +10,19 @@ classifiers = [
 ]
 dependencies = [
   "beautifulsoup4>=4.12.3",
+  "cython>=3.1",            # For lxml on Python 3.14 pre-release
   "fake-useragent>=1.5.1",
   "httpx>=0.28.1",
   "imageio>=2.36.1",
   "keras>=3.7",
-  "lxml>=5.3",
+  # "lxml>=5.4",
+  "lxml @ git+https://github.com/lxml/lxml",
   "matplotlib>=3.9.3",
   "numpy>=2.1.3",
   "opencv-python>=4.10.0.84",
   "pandas>=2.2.3",
-  "pillow>=11",
+  # "pillow>=11",
+  "pillow @ git+https://github.com/python-pillow/pillow",
   "rich>=13.9.4",
   "scikit-learn>=1.5.2",
   "sphinx-pyproject>=0.3",

From 72d313852ec6059ccefb2f0de574fb1491316f53 Mon Sep 17 00:00:00 2001
From: cclauss <cclauss@users.noreply.github.com>
Date: Sat, 5 Jul 2025 08:44:31 +0000
Subject: [PATCH 2/2] updating DIRECTORY.md

---
 DIRECTORY.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/DIRECTORY.md b/DIRECTORY.md
index 00f4bb4ef2b2..dfecc9cfe584 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -443,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)
+  * [Straight Line Depreciation](financial/straight_line_depreciation.py)
   * [Time And Half Pay](financial/time_and_half_pay.py)
 
 ## Fractals