From 0f75804451cbb0b1ada4f2896131d90b88dfc4bc Mon Sep 17 00:00:00 2001
From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com>
Date: Sat, 14 Oct 2023 21:21:54 +0530
Subject: [PATCH 1/3] Performance: 80% faster Project Euler145

---
 project_euler/problem_145/sol1.py | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/project_euler/problem_145/sol1.py b/project_euler/problem_145/sol1.py
index e9fc1a199161..c69a1440e543 100644
--- a/project_euler/problem_145/sol1.py
+++ b/project_euler/problem_145/sol1.py
@@ -30,6 +30,10 @@ def reversible_numbers(
     >>> reversible_numbers(3, 0, [0] * 3, 3)
     100
     """
+    # There exist no reversible 1, 5, 9, 13 (ie. 4k+1) digit numbers
+    if (length - 1) % 4 == 0:
+        return 0
+
     if remaining_length == 0:
         if digits[0] == 0 or digits[-1] == 0:
             return 0
@@ -94,3 +98,6 @@ def solution(max_power: int = 9) -> int:
 
 if __name__ == "__main__":
     print(f"{solution() = }")
+
+    # for i in range(1, 15):
+    #     print(f"{i}. {reversible_numbers(i, 0, [0]*i, i)}")

From bf77690f36f6fe6b7a5d01dd909d35173ea7a4b8 Mon Sep 17 00:00:00 2001
From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com>
Date: Sat, 14 Oct 2023 22:32:31 +0530
Subject: [PATCH 2/3] Added timeit benchmark

---
 project_euler/problem_145/sol1.py | 71 ++++++++++++++++++++++++++-----
 1 file changed, 60 insertions(+), 11 deletions(-)

diff --git a/project_euler/problem_145/sol1.py b/project_euler/problem_145/sol1.py
index c69a1440e543..7f761d834973 100644
--- a/project_euler/problem_145/sol1.py
+++ b/project_euler/problem_145/sol1.py
@@ -17,23 +17,19 @@
 ODD_DIGITS = [1, 3, 5, 7, 9]
 
 
-def reversible_numbers(
+def slow_reversible_numbers(
     remaining_length: int, remainder: int, digits: list[int], length: int
 ) -> int:
     """
     Count the number of reversible numbers of given length.
     Iterate over possible digits considering parity of current sum remainder.
-    >>> reversible_numbers(1, 0, [0], 1)
+    >>> slow_reversible_numbers(1, 0, [0], 1)
     0
-    >>> reversible_numbers(2, 0, [0] * 2, 2)
+    >>> slow_reversible_numbers(2, 0, [0] * 2, 2)
     20
-    >>> reversible_numbers(3, 0, [0] * 3, 3)
+    >>> slow_reversible_numbers(3, 0, [0] * 3, 3)
     100
     """
-    # There exist no reversible 1, 5, 9, 13 (ie. 4k+1) digit numbers
-    if (length - 1) % 4 == 0:
-        return 0
-
     if remaining_length == 0:
         if digits[0] == 0 or digits[-1] == 0:
             return 0
@@ -55,7 +51,7 @@ def reversible_numbers(
         result = 0
         for digit in range(10):
             digits[length // 2] = digit
-            result += reversible_numbers(
+            result += slow_reversible_numbers(
                 0, (remainder + 2 * digit) // 10, digits, length
             )
         return result
@@ -71,7 +67,7 @@ def reversible_numbers(
 
         for digit2 in other_parity_digits:
             digits[(length - remaining_length) // 2] = digit2
-            result += reversible_numbers(
+            result += slow_reversible_numbers(
                 remaining_length - 2,
                 (remainder + digit1 + digit2) // 10,
                 digits,
@@ -80,6 +76,42 @@ def reversible_numbers(
     return result
 
 
+def slow_solution(max_power: int = 9) -> int:
+    """
+    To evaluate the solution, use solution()
+    >>> solution(3)
+    120
+    >>> solution(6)
+    18720
+    >>> solution(7)
+    68720
+    """
+    result = 0
+    for length in range(1, max_power + 1):
+        result += slow_reversible_numbers(length, 0, [0] * length, length)
+    return result
+
+
+def reversible_numbers(
+    remaining_length: int, remainder: int, digits: list[int], length: int
+) -> int:
+    """
+    Count the number of reversible numbers of given length.
+    Iterate over possible digits considering parity of current sum remainder.
+    >>> reversible_numbers(1, 0, [0], 1)
+    0
+    >>> reversible_numbers(2, 0, [0] * 2, 2)
+    20
+    >>> reversible_numbers(3, 0, [0] * 3, 3)
+    100
+    """
+    # There exist no reversible 1, 5, 9, 13 (ie. 4k+1) digit numbers
+    if (length - 1) % 4 == 0:
+        return 0
+
+    return slow_reversible_numbers(length, 0, [0] * length, length)
+
+
 def solution(max_power: int = 9) -> int:
     """
     To evaluate the solution, use solution()
@@ -96,8 +128,25 @@ def solution(max_power: int = 9) -> int:
     return result
 
 
+def benchmark() -> None:
+    """
+    Benchmarks
+    """
+    # Running performance benchmarks...
+    # slow_solution : 292.9300301000003
+    # solution      : 54.90970860000016
+
+    from timeit import timeit
+
+    print("Running performance benchmarks...")
+
+    print(f"slow_solution : {timeit('slow_solution()', globals=globals(), number=10)}")
+    print(f"solution      : {timeit('solution()', globals=globals(), number=10)}")
+
+
 if __name__ == "__main__":
-    print(f"{solution() = }")
+    print(f"Solution : {solution()}")
+    benchmark()
 
     # for i in range(1, 15):
     #     print(f"{i}. {reversible_numbers(i, 0, [0]*i, i)}")

From c1dfb5a26f3c4afceef3b76568d15b6e502b078d Mon Sep 17 00:00:00 2001
From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com>
Date: Sat, 14 Oct 2023 22:44:24 +0530
Subject: [PATCH 3/3] >>> slow_solution() doctest

---
 project_euler/problem_145/sol1.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/project_euler/problem_145/sol1.py b/project_euler/problem_145/sol1.py
index 7f761d834973..71b851178fdb 100644
--- a/project_euler/problem_145/sol1.py
+++ b/project_euler/problem_145/sol1.py
@@ -79,11 +79,11 @@ def slow_reversible_numbers(
 def slow_solution(max_power: int = 9) -> int:
     """
     To evaluate the solution, use solution()
-    >>> solution(3)
+    >>> slow_solution(3)
     120
-    >>> solution(6)
+    >>> slow_solution(6)
     18720
-    >>> solution(7)
+    >>> slow_solution(7)
     68720
     """
     result = 0