From f5211cc56d11c815a7183c880b548b8d1c5564e2 Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Tue, 1 Oct 2024 21:43:13 +0530
Subject: [PATCH 01/24] feat: add Matrix Exponentiation method docs: updated
 the header documentation and added new documentation for the new function.

---
 maths/fibonacci.py | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index 927700b0418e..cfa813188752 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -7,6 +7,8 @@
 
 NOTE 2: the Binet's formula function is much more limited in the size of inputs
 that it can handle due to the size limitations of Python floats
+NOTE 3: the matrix function is the fastest and most memory efficient for large n
+
 
 See benchmark numbers in __main__ for performance comparisons/
 https://en.wikipedia.org/wiki/Fibonacci_number for more information
@@ -230,6 +232,32 @@ def fib_binet(n: int) -> list[int]:
     return [round(phi**i / sqrt_5) for i in range(n + 1)]
 
 
+def fib_matrix(n: int) -> int:
+    def matrix_mult(a, b):
+        return [[a[0][0] * b[0][0] + a[0][1] * b[1][0],
+                 a[0][0] * b[0][1] + a[0][1] * b[1][1]],
+                [a[1][0] * b[0][0] + a[1][1] * b[1][0],
+                 a[1][0] * b[0][1] + a[1][1] * b[1][1]]]
+
+    def matrix_pow(m, power):
+        result = [[1, 0], [0, 1]]  # Identity matrix
+        base = m
+        while power:
+            if power % 2 == 1:
+                result = matrix_mult(result, base)
+            base = matrix_mult(base, base)
+            power //= 2
+        return result
+
+    if n < 0:
+        raise ValueError("n is negative")
+    if n == 0:
+        return 0
+    m = [[1, 1], [1, 0]]
+    result = matrix_pow(m, n - 1)
+    return result[0][0]
+
+
 if __name__ == "__main__":
     from doctest import testmod
 
@@ -242,3 +270,4 @@ def fib_binet(n: int) -> list[int]:
     time_func(fib_memoization, num)  # 0.0100 ms
     time_func(fib_recursive_cached, num)  # 0.0153 ms
     time_func(fib_recursive, num)  # 257.0910 ms
+    time_func(fib_matrix, num)  # 0.0000 ms

From 95421d7b9a8620c530d211e2cf8e2aeef4ff2f2e Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Tue, 1 Oct 2024 21:52:31 +0530
Subject: [PATCH 02/24] feat: added new function matrix exponetiation method

---
 maths/fibonacci.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index cfa813188752..fb40e24d8dd5 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -233,6 +233,8 @@ def fib_binet(n: int) -> list[int]:
 
 
 def fib_matrix(n: int) -> int:
+    # https://www.nayuki.io/page/fast-fibonacci-algorithms#:~:text=
+    # Summary:%20The%20two%20fast%20Fibonacci%20algorithms%20are%20matrix
     def matrix_mult(a, b):
         return [[a[0][0] * b[0][0] + a[0][1] * b[1][0],
                  a[0][0] * b[0][1] + a[0][1] * b[1][1]],

From adf10bf3b7ea8b1e54b895497c2963cb8236a007 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 1 Oct 2024 16:26:52 +0000
Subject: [PATCH 03/24] [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
---
 maths/fibonacci.py | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index fb40e24d8dd5..02288204da18 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -236,10 +236,16 @@ def fib_matrix(n: int) -> int:
     # https://www.nayuki.io/page/fast-fibonacci-algorithms#:~:text=
     # Summary:%20The%20two%20fast%20Fibonacci%20algorithms%20are%20matrix
     def matrix_mult(a, b):
-        return [[a[0][0] * b[0][0] + a[0][1] * b[1][0],
-                 a[0][0] * b[0][1] + a[0][1] * b[1][1]],
-                [a[1][0] * b[0][0] + a[1][1] * b[1][0],
-                 a[1][0] * b[0][1] + a[1][1] * b[1][1]]]
+        return [
+            [
+                a[0][0] * b[0][0] + a[0][1] * b[1][0],
+                a[0][0] * b[0][1] + a[0][1] * b[1][1],
+            ],
+            [
+                a[1][0] * b[0][0] + a[1][1] * b[1][0],
+                a[1][0] * b[0][1] + a[1][1] * b[1][1],
+            ],
+        ]
 
     def matrix_pow(m, power):
         result = [[1, 0], [0, 1]]  # Identity matrix

From ba1cbc6e8bff7195c5ed16599f479fbff1514f53 Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Tue, 1 Oct 2024 22:34:25 +0530
Subject: [PATCH 04/24] feat: This function uses the tail-recursive form of the
 Euclidean algorithm to calculate

---
 maths/gcd_of_n_numbers.py | 46 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/maths/gcd_of_n_numbers.py b/maths/gcd_of_n_numbers.py
index 63236c236ada..9c61c91a6b56 100644
--- a/maths/gcd_of_n_numbers.py
+++ b/maths/gcd_of_n_numbers.py
@@ -1,6 +1,7 @@
 """
 Gcd of N Numbers
 Reference: https://en.wikipedia.org/wiki/Greatest_common_divisor
+Reference for tail recursion: https://www.geeksforgeeks.org/tail-recursion/
 """
 
 from collections import Counter
@@ -104,6 +105,51 @@ def get_greatest_common_divisor(*numbers: int) -> int:
         mult *= m
     return mult
 
+def gcd_tail_recursive(a: int, b: int) -> int:
+    """
+    Calculate the Greatest Common Divisor (GCD) using a tail-recursive approach.
+
+    This function uses the tail-recursive form of the Euclidean algorithm to calculate
+    the GCD of two integers `a` and `b`. The GCD is the largest integer that divides both
+    `a` and `b` without leaving a remainder.
+
+    Tail recursion is a form of recursion where the recursive call is the last operation
+    in the function. In languages that support tail call optimization, this allows the
+    function to be optimized by reusing the current function's stack frame for the
+    next call. Python, however, does not support tail call optimization, but using this
+    style can still help structure the recursion for better clarity.
+
+    Args:
+        a (int): The first integer.
+        b (int): The second integer.
+
+    Returns:
+        int: The greatest common divisor of `a` and `b`.
+
+    Raises:
+        ValueError: If both `a` and `b` are zero, as the GCD is not defined for this case.
+
+    Example:
+        >>> gcd_tail_recursive(24, 40)
+        8
+        >>> gcd_tail_recursive(11, 37)
+        1
+        >>> gcd_tail_recursive(0, 5)
+        5
+        >>> gcd_tail_recursive(5, 0)
+        5
+        >>> gcd_tail_recursive(0, 0)
+        ValueError: GCD is not defined for both a and b being zero.
+
+    Notes:
+        - gcd(a, 0) = abs(a)
+        - gcd(0, b) = abs(b)
+        - gcd(0, 0) is undefined.
+    """
+    if b == 0:
+        return abs(a)
+    return gcd_tail_recursive(b, a % b)
 
 if __name__ == "__main__":
     print(get_greatest_common_divisor(18, 45))  # 9
+    print(gcd_tail_recursive(23, 37))  # 1

From fd37108779d40ecfb161900b6bc296c479f67c28 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 1 Oct 2024 17:05:15 +0000
Subject: [PATCH 05/24] [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
---
 maths/gcd_of_n_numbers.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/maths/gcd_of_n_numbers.py b/maths/gcd_of_n_numbers.py
index 9c61c91a6b56..da6b767a1b22 100644
--- a/maths/gcd_of_n_numbers.py
+++ b/maths/gcd_of_n_numbers.py
@@ -105,6 +105,7 @@ def get_greatest_common_divisor(*numbers: int) -> int:
         mult *= m
     return mult
 
+
 def gcd_tail_recursive(a: int, b: int) -> int:
     """
     Calculate the Greatest Common Divisor (GCD) using a tail-recursive approach.
@@ -150,6 +151,7 @@ def gcd_tail_recursive(a: int, b: int) -> int:
         return abs(a)
     return gcd_tail_recursive(b, a % b)
 
+
 if __name__ == "__main__":
     print(get_greatest_common_divisor(18, 45))  # 9
     print(gcd_tail_recursive(23, 37))  # 1

From 50fd0586908478a59267d122811d7f6921bffb69 Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Tue, 1 Oct 2024 22:42:17 +0530
Subject: [PATCH 06/24] reduced the number of characters per line in the
 comments

---
 maths/gcd_of_n_numbers.py | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/maths/gcd_of_n_numbers.py b/maths/gcd_of_n_numbers.py
index 9c61c91a6b56..8f524913ae73 100644
--- a/maths/gcd_of_n_numbers.py
+++ b/maths/gcd_of_n_numbers.py
@@ -109,15 +109,22 @@ def gcd_tail_recursive(a: int, b: int) -> int:
     """
     Calculate the Greatest Common Divisor (GCD) using a tail-recursive approach.
 
-    This function uses the tail-recursive form of the Euclidean algorithm to calculate
-    the GCD of two integers `a` and `b`. The GCD is the largest integer that divides both
+    This function uses the tail-recursive form of
+    the Euclidean algorithm to calculate
+    the GCD of two integers `a` and `b`. The GCD
+    is the largest integer that divides both
     `a` and `b` without leaving a remainder.
 
-    Tail recursion is a form of recursion where the recursive call is the last operation
-    in the function. In languages that support tail call optimization, this allows the
-    function to be optimized by reusing the current function's stack frame for the
-    next call. Python, however, does not support tail call optimization, but using this
-    style can still help structure the recursion for better clarity.
+    Tail recursion is a form of recursion where the
+    recursive call is the last operation
+    in the function. In languages that
+    support tail call optimization, this allows the
+    function to be optimized by reusing the current
+    function's stack frame for the
+    next call. Python, however, does not support tail
+    call optimization, but using this
+    style can still help structure the
+    recursion for better clarity.
 
     Args:
         a (int): The first integer.

From ede7215515a9e236ff5a480ca5278bf468a2ead6 Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Tue, 1 Oct 2024 22:46:40 +0530
Subject: [PATCH 07/24] removed unwanted code

---
 maths/gcd_of_n_numbers.py | 55 ---------------------------------------
 1 file changed, 55 deletions(-)

diff --git a/maths/gcd_of_n_numbers.py b/maths/gcd_of_n_numbers.py
index 4a066e321477..63236c236ada 100644
--- a/maths/gcd_of_n_numbers.py
+++ b/maths/gcd_of_n_numbers.py
@@ -1,7 +1,6 @@
 """
 Gcd of N Numbers
 Reference: https://en.wikipedia.org/wiki/Greatest_common_divisor
-Reference for tail recursion: https://www.geeksforgeeks.org/tail-recursion/
 """
 
 from collections import Counter
@@ -106,59 +105,5 @@ def get_greatest_common_divisor(*numbers: int) -> int:
     return mult
 
 
-def gcd_tail_recursive(a: int, b: int) -> int:
-    """
-    Calculate the Greatest Common Divisor (GCD) using a tail-recursive approach.
-
-    This function uses the tail-recursive form of
-    the Euclidean algorithm to calculate
-    the GCD of two integers `a` and `b`. The GCD
-    is the largest integer that divides both
-    `a` and `b` without leaving a remainder.
-
-    Tail recursion is a form of recursion where the
-    recursive call is the last operation
-    in the function. In languages that
-    support tail call optimization, this allows the
-    function to be optimized by reusing the current
-    function's stack frame for the
-    next call. Python, however, does not support tail
-    call optimization, but using this
-    style can still help structure the
-    recursion for better clarity.
-
-    Args:
-        a (int): The first integer.
-        b (int): The second integer.
-
-    Returns:
-        int: The greatest common divisor of `a` and `b`.
-
-    Raises:
-        ValueError: If both `a` and `b` are zero, as the GCD is not defined for this case.
-
-    Example:
-        >>> gcd_tail_recursive(24, 40)
-        8
-        >>> gcd_tail_recursive(11, 37)
-        1
-        >>> gcd_tail_recursive(0, 5)
-        5
-        >>> gcd_tail_recursive(5, 0)
-        5
-        >>> gcd_tail_recursive(0, 0)
-        ValueError: GCD is not defined for both a and b being zero.
-
-    Notes:
-        - gcd(a, 0) = abs(a)
-        - gcd(0, b) = abs(b)
-        - gcd(0, 0) is undefined.
-    """
-    if b == 0:
-        return abs(a)
-    return gcd_tail_recursive(b, a % b)
-
-
 if __name__ == "__main__":
     print(get_greatest_common_divisor(18, 45))  # 9
-    print(gcd_tail_recursive(23, 37))  # 1

From 0f939a80d2b1ebad46be86ed8f3c7a85d03c3fb5 Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Tue, 1 Oct 2024 23:07:52 +0530
Subject: [PATCH 08/24] feat: Implemented a new function to swaap numbers
 without dummy variable

---
 ...bles_without_using _temporary_variables.py | 68 +++++++++++++++++++
 bit_manipulation/tempCodeRunnerFile.py        |  1 +
 2 files changed, 69 insertions(+)
 create mode 100644 bit_manipulation/swaping_two_variables_without_using _temporary_variables.py
 create mode 100644 bit_manipulation/tempCodeRunnerFile.py

diff --git a/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py b/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py
new file mode 100644
index 000000000000..f81b1c230349
--- /dev/null
+++ b/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py	
@@ -0,0 +1,68 @@
+def xor_swap(a: int, b: int) -> (int, int):
+    """
+    Swap two integers using bitwise XOR
+    without using a temporary variable.
+
+    This algorithm utilizes the properties
+    of the bitwise XOR operation to swap the values 
+    of two integers `a` and `b` without
+    the use of a temporary variable. XOR swap is a 
+    rarely used trick but showcases the power
+    of bit manipulation for efficient operations 
+    at the hardware level.
+
+    The steps involved are:
+    1. `a = a ^ b`
+    2. `b = a ^ b` (Now `b` holds the original value of `a`)
+    3. `a = a ^ b` (Now `a` holds the original value of `b`)
+
+    Although this technique can swap variables
+    without extra space, it is generally not 
+    recommended in production code because it is
+    less readable than using a temporary variable.
+
+    Args:
+        a (int): The first integer to be swapped.
+        b (int): The second integer to be swapped.
+
+    Returns:
+        (int, int): The swapped values of `a` and `b`.
+
+    Raises:
+        None
+
+    Example:
+        >>> xor_swap(5, 10)
+        (10, 5)
+        >>> xor_swap(0, 100)
+        (100, 0)
+        >>> xor_swap(-3, 9)
+        (9, -3)
+
+    Notes:
+        - Swapping using XOR can lead to confusion and
+        should generally be avoided in 
+        favor of more readable methods.
+        - This algorithm does not work if both `a` and `b`
+        refer to the same variable.
+
+    """
+    if a == b:
+        print("Both values are the same; no swap needed.")
+        return a, b
+    
+    # print(f"Original a = {a}, b = {b}")
+    a = a ^ b  # Step 1
+    b = a ^ b  # Step 2: Now b is the original value of a
+    a = a ^ b  # Step 3: Now a is the original value of b
+    # print(f"Swapped a = {a}, b = {b}")
+    return a, b
+
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
+    print(xor_swap(5, 10)) # (10, 5)
+    print(xor_swap(0, 100)) # (100, 0)
+    print(xor_swap(-3, 9))  # (9, -3)
+    
\ No newline at end of file
diff --git a/bit_manipulation/tempCodeRunnerFile.py b/bit_manipulation/tempCodeRunnerFile.py
new file mode 100644
index 000000000000..e5b844c5fc6a
--- /dev/null
+++ b/bit_manipulation/tempCodeRunnerFile.py
@@ -0,0 +1 @@
+ # print(f"Swapped a = {a}, b = {b}")
\ No newline at end of file

From 5dc67f8a1a524d112457a02a51b0c6040a224b19 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 1 Oct 2024 17:39:26 +0000
Subject: [PATCH 09/24] [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
---
 ...ables_without_using _temporary_variables.py | 18 +++++++++---------
 bit_manipulation/tempCodeRunnerFile.py         |  2 +-
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py b/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py
index f81b1c230349..7bb98f1e929e 100644
--- a/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py	
+++ b/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py	
@@ -4,11 +4,11 @@ def xor_swap(a: int, b: int) -> (int, int):
     without using a temporary variable.
 
     This algorithm utilizes the properties
-    of the bitwise XOR operation to swap the values 
+    of the bitwise XOR operation to swap the values
     of two integers `a` and `b` without
-    the use of a temporary variable. XOR swap is a 
+    the use of a temporary variable. XOR swap is a
     rarely used trick but showcases the power
-    of bit manipulation for efficient operations 
+    of bit manipulation for efficient operations
     at the hardware level.
 
     The steps involved are:
@@ -17,7 +17,7 @@ def xor_swap(a: int, b: int) -> (int, int):
     3. `a = a ^ b` (Now `a` holds the original value of `b`)
 
     Although this technique can swap variables
-    without extra space, it is generally not 
+    without extra space, it is generally not
     recommended in production code because it is
     less readable than using a temporary variable.
 
@@ -41,7 +41,7 @@ def xor_swap(a: int, b: int) -> (int, int):
 
     Notes:
         - Swapping using XOR can lead to confusion and
-        should generally be avoided in 
+        should generally be avoided in
         favor of more readable methods.
         - This algorithm does not work if both `a` and `b`
         refer to the same variable.
@@ -50,7 +50,7 @@ def xor_swap(a: int, b: int) -> (int, int):
     if a == b:
         print("Both values are the same; no swap needed.")
         return a, b
-    
+
     # print(f"Original a = {a}, b = {b}")
     a = a ^ b  # Step 1
     b = a ^ b  # Step 2: Now b is the original value of a
@@ -61,8 +61,8 @@ def xor_swap(a: int, b: int) -> (int, int):
 
 if __name__ == "__main__":
     import doctest
+
     doctest.testmod()
-    print(xor_swap(5, 10)) # (10, 5)
-    print(xor_swap(0, 100)) # (100, 0)
+    print(xor_swap(5, 10))  # (10, 5)
+    print(xor_swap(0, 100))  # (100, 0)
     print(xor_swap(-3, 9))  # (9, -3)
-    
\ No newline at end of file
diff --git a/bit_manipulation/tempCodeRunnerFile.py b/bit_manipulation/tempCodeRunnerFile.py
index e5b844c5fc6a..a3108748d632 100644
--- a/bit_manipulation/tempCodeRunnerFile.py
+++ b/bit_manipulation/tempCodeRunnerFile.py
@@ -1 +1 @@
- # print(f"Swapped a = {a}, b = {b}")
\ No newline at end of file
+# print(f"Swapped a = {a}, b = {b}")

From b581cf5c1907bc8323880b31d120731fb8cbbd07 Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Tue, 1 Oct 2024 23:12:07 +0530
Subject: [PATCH 10/24] removed previos code

---
 ...bles_without_using _temporary_variables.py | 68 -------------------
 1 file changed, 68 deletions(-)
 delete mode 100644 bit_manipulation/swaping_two_variables_without_using _temporary_variables.py

diff --git a/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py b/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py
deleted file mode 100644
index f81b1c230349..000000000000
--- a/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py	
+++ /dev/null
@@ -1,68 +0,0 @@
-def xor_swap(a: int, b: int) -> (int, int):
-    """
-    Swap two integers using bitwise XOR
-    without using a temporary variable.
-
-    This algorithm utilizes the properties
-    of the bitwise XOR operation to swap the values 
-    of two integers `a` and `b` without
-    the use of a temporary variable. XOR swap is a 
-    rarely used trick but showcases the power
-    of bit manipulation for efficient operations 
-    at the hardware level.
-
-    The steps involved are:
-    1. `a = a ^ b`
-    2. `b = a ^ b` (Now `b` holds the original value of `a`)
-    3. `a = a ^ b` (Now `a` holds the original value of `b`)
-
-    Although this technique can swap variables
-    without extra space, it is generally not 
-    recommended in production code because it is
-    less readable than using a temporary variable.
-
-    Args:
-        a (int): The first integer to be swapped.
-        b (int): The second integer to be swapped.
-
-    Returns:
-        (int, int): The swapped values of `a` and `b`.
-
-    Raises:
-        None
-
-    Example:
-        >>> xor_swap(5, 10)
-        (10, 5)
-        >>> xor_swap(0, 100)
-        (100, 0)
-        >>> xor_swap(-3, 9)
-        (9, -3)
-
-    Notes:
-        - Swapping using XOR can lead to confusion and
-        should generally be avoided in 
-        favor of more readable methods.
-        - This algorithm does not work if both `a` and `b`
-        refer to the same variable.
-
-    """
-    if a == b:
-        print("Both values are the same; no swap needed.")
-        return a, b
-    
-    # print(f"Original a = {a}, b = {b}")
-    a = a ^ b  # Step 1
-    b = a ^ b  # Step 2: Now b is the original value of a
-    a = a ^ b  # Step 3: Now a is the original value of b
-    # print(f"Swapped a = {a}, b = {b}")
-    return a, b
-
-
-if __name__ == "__main__":
-    import doctest
-    doctest.testmod()
-    print(xor_swap(5, 10)) # (10, 5)
-    print(xor_swap(0, 100)) # (100, 0)
-    print(xor_swap(-3, 9))  # (9, -3)
-    
\ No newline at end of file

From 31c56459284198f8a0709d83d362d5c9904b3482 Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Wed, 2 Oct 2024 15:44:22 +0530
Subject: [PATCH 11/24] Done with the required changes

---
 maths/fibonacci.py | 96 ++++++++++++++++++++++++++++++++--------------
 1 file changed, 68 insertions(+), 28 deletions(-)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index 02288204da18..fb93ed36c243 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -18,6 +18,7 @@
 from collections.abc import Iterator
 from math import sqrt
 from time import time
+import numpy as np
 
 
 def time_func(func, *args, **kwargs):
@@ -232,38 +233,77 @@ def fib_binet(n: int) -> list[int]:
     return [round(phi**i / sqrt_5) for i in range(n + 1)]
 
 
-def fib_matrix(n: int) -> int:
-    # https://www.nayuki.io/page/fast-fibonacci-algorithms#:~:text=
-    # Summary:%20The%20two%20fast%20Fibonacci%20algorithms%20are%20matrix
-    def matrix_mult(a, b):
-        return [
-            [
-                a[0][0] * b[0][0] + a[0][1] * b[1][0],
-                a[0][0] * b[0][1] + a[0][1] * b[1][1],
-            ],
-            [
-                a[1][0] * b[0][0] + a[1][1] * b[1][0],
-                a[1][0] * b[0][1] + a[1][1] * b[1][1],
-            ],
-        ]
-
-    def matrix_pow(m, power):
-        result = [[1, 0], [0, 1]]  # Identity matrix
-        base = m
-        while power:
-            if power % 2 == 1:
-                result = matrix_mult(result, base)
-            base = matrix_mult(base, base)
-            power //= 2
-        return result
+def matrix_mult_np(a, b):
+    """
+    Multiplies two matrices using numpy's dot product.
+
+    Args:
+        a: First matrix as a numpy array
+        b: Second matrix as a numpy array
+
+    Returns:
+        The product of matrices a and b.
+    """
+    return np.dot(a, b)
+
+
+def matrix_pow_np(m, power):
+    """
+    Raises a matrix to the power of 'power' using binary exponentiation.
+
+    Args:
+        m: Matrix as a numpy array.
+        power: The power to which the matrix is to be raised.
+
+    Returns:
+        The matrix raised to the power.
+    """
+    result = np.identity(2, dtype=int)  # Identity matrix
+    base = m
+    while power:
+        if power % 2 == 1:
+            result = matrix_mult_np(result, base)
+        base = matrix_mult_np(base, base)
+        power //= 2
+    return result
 
+
+def fib_matrix_np(n: int) -> int:
+    """
+    Calculates the n-th Fibonacci number using matrix exponentiation.
+    https://www.nayuki.io/page/fast-fibonacci-algorithms#:~:text=
+    Summary:%20The%20two%20fast%20Fibonacci%20algorithms%20are%20matrix
+
+    Args:
+        n: Fibonacci sequence index
+
+    Returns:
+        The n-th Fibonacci number.
+
+    Raises:
+        ValueError: If n is negative.
+
+    >>> fib_matrix_np(0)
+    0
+    >>> fib_matrix_np(1)
+    1
+    >>> fib_matrix_np(10)
+    55
+    >>> fib_matrix_np(-1)
+    Traceback (most recent call last):
+        ...
+    ValueError: n is negative
+    """
     if n < 0:
         raise ValueError("n is negative")
     if n == 0:
         return 0
-    m = [[1, 1], [1, 0]]
-    result = matrix_pow(m, n - 1)
-    return result[0][0]
+
+    m = np.array([[1, 1], [1, 0]], dtype=int)
+    result = matrix_pow_np(m, n - 1)
+    return result[0, 0]
+
+
 
 
 if __name__ == "__main__":
@@ -278,4 +318,4 @@ def matrix_pow(m, power):
     time_func(fib_memoization, num)  # 0.0100 ms
     time_func(fib_recursive_cached, num)  # 0.0153 ms
     time_func(fib_recursive, num)  # 257.0910 ms
-    time_func(fib_matrix, num)  # 0.0000 ms
+    time_func(fib_matrix_np, num)  # 0.0000 ms

From 6ee1a40285903d5ede52b63a447c58e70dad03ef Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 2 Oct 2024 10:18:27 +0000
Subject: [PATCH 12/24] [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
---
 maths/fibonacci.py | 2 --
 1 file changed, 2 deletions(-)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index fb93ed36c243..c5bdb6ff26cf 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -304,8 +304,6 @@ def fib_matrix_np(n: int) -> int:
     return result[0, 0]
 
 
-
-
 if __name__ == "__main__":
     from doctest import testmod
 

From e48a3f3e13532c8e1a9f6621c54a9cec82bbd085 Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Wed, 2 Oct 2024 16:09:12 +0530
Subject: [PATCH 13/24] Done with the required changes

---
 maths/fibonacci.py | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index fb93ed36c243..6a7792fe4059 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -18,9 +18,12 @@
 from collections.abc import Iterator
 from math import sqrt
 from time import time
+
 import numpy as np
 
 
+
+
 def time_func(func, *args, **kwargs):
     """
     Times the execution of a function with parameters
@@ -287,12 +290,14 @@ def fib_matrix_np(n: int) -> int:
     0
     >>> fib_matrix_np(1)
     1
+    >>> fib_matrix_np(5)
+    5
     >>> fib_matrix_np(10)
     55
     >>> fib_matrix_np(-1)
     Traceback (most recent call last):
         ...
-    ValueError: n is negative
+    Exception: n is negative
     """
     if n < 0:
         raise ValueError("n is negative")

From f82bd8c3735967af24e064e4601cae9725b40c17 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 2 Oct 2024 10:40:25 +0000
Subject: [PATCH 14/24] [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
---
 maths/fibonacci.py | 2 --
 1 file changed, 2 deletions(-)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index 302e8a2aa2ae..62dde20385b1 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -22,8 +22,6 @@
 import numpy as np
 
 
-
-
 def time_func(func, *args, **kwargs):
     """
     Times the execution of a function with parameters

From 8bf138985ee7941b5a741fb9661476dc51f01273 Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Wed, 2 Oct 2024 16:20:21 +0530
Subject: [PATCH 15/24] Done with the required changes

---
 maths/fibonacci.py | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index 302e8a2aa2ae..3f3a6366c348 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -250,7 +250,7 @@ def matrix_mult_np(a, b):
     return np.dot(a, b)
 
 
-def matrix_pow_np(m, power):
+def matrix_pow_np(m: int, power: int) -> int:
     """
     Raises a matrix to the power of 'power' using binary exponentiation.
 
@@ -294,10 +294,6 @@ def fib_matrix_np(n: int) -> int:
     5
     >>> fib_matrix_np(10)
     55
-    >>> fib_matrix_np(-1)
-    Traceback (most recent call last):
-        ...
-    Exception: n is negative
     """
     if n < 0:
         raise ValueError("n is negative")

From 9ffd634a7154e9e45e593ea34c421b2422ea4c43 Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Wed, 2 Oct 2024 16:25:49 +0530
Subject: [PATCH 16/24] Done with the required changes

---
 maths/fibonacci.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index 9581a586a7dc..3f06e5b55e4d 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -292,6 +292,10 @@ def fib_matrix_np(n: int) -> int:
     5
     >>> fib_matrix_np(10)
     55
+    >>> fib_matrix_np(-1)
+    Traceback (most recent call last):
+        ...
+    ValueError: n is negative
     """
     if n < 0:
         raise ValueError("n is negative")
@@ -300,7 +304,7 @@ def fib_matrix_np(n: int) -> int:
 
     m = np.array([[1, 1], [1, 0]], dtype=int)
     result = matrix_pow_np(m, n - 1)
-    return result[0, 0]
+    return int(result[0, 0])
 
 
 if __name__ == "__main__":

From 4e2456f51869c6eb942d5faf294f325d6bbd7096 Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Wed, 2 Oct 2024 16:35:56 +0530
Subject: [PATCH 17/24] Done with the required changes

---
 maths/fibonacci.py | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index 3f06e5b55e4d..99aabaf7f1de 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -20,6 +20,7 @@
 from time import time
 
 import numpy as np
+from numpy import ndarray
 
 
 def time_func(func, *args, **kwargs):
@@ -247,8 +248,7 @@ def matrix_mult_np(a, b):
     """
     return np.dot(a, b)
 
-
-def matrix_pow_np(m: int, power: int) -> int:
+def matrix_pow_np(m: ndarray, power: int) -> ndarray:
     """
     Raises a matrix to the power of 'power' using binary exponentiation.
 
@@ -259,12 +259,12 @@ def matrix_pow_np(m: int, power: int) -> int:
     Returns:
         The matrix raised to the power.
     """
-    result = np.identity(2, dtype=int)  # Identity matrix
+    result = np.array([[1, 0], [0, 1]], dtype=int)  # Identity matrix
     base = m
     while power:
         if power % 2 == 1:
-            result = matrix_mult_np(result, base)
-        base = matrix_mult_np(base, base)
+            result = np.dot(result, base)
+        base = np.dot(base, base)
         power //= 2
     return result
 

From 07ed6b9c4e537f4620b90d6cfec7f317c235bf3e Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 2 Oct 2024 11:07:22 +0000
Subject: [PATCH 18/24] [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
---
 maths/fibonacci.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index 99aabaf7f1de..7d10586196b0 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -248,6 +248,7 @@ def matrix_mult_np(a, b):
     """
     return np.dot(a, b)
 
+
 def matrix_pow_np(m: ndarray, power: int) -> ndarray:
     """
     Raises a matrix to the power of 'power' using binary exponentiation.

From 955c7929319e266baa55cb7044ba8d580b72acd7 Mon Sep 17 00:00:00 2001
From: ARNAV RAJ <126798788+Acuspeedster@users.noreply.github.com>
Date: Fri, 4 Oct 2024 15:44:01 +0530
Subject: [PATCH 19/24] Update maths/fibonacci.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>
---
 maths/fibonacci.py | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index 7d10586196b0..b5bd0ef2f83f 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -235,18 +235,6 @@ def fib_binet(n: int) -> list[int]:
     return [round(phi**i / sqrt_5) for i in range(n + 1)]
 
 
-def matrix_mult_np(a, b):
-    """
-    Multiplies two matrices using numpy's dot product.
-
-    Args:
-        a: First matrix as a numpy array
-        b: Second matrix as a numpy array
-
-    Returns:
-        The product of matrices a and b.
-    """
-    return np.dot(a, b)
 
 
 def matrix_pow_np(m: ndarray, power: int) -> ndarray:

From 70dd92de617a2b73186928872e3e1e9e4b65286c Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Fri, 4 Oct 2024 10:15:43 +0000
Subject: [PATCH 20/24] [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
---
 maths/fibonacci.py | 2 --
 1 file changed, 2 deletions(-)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index b5bd0ef2f83f..c53ddf3e33c6 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -235,8 +235,6 @@ def fib_binet(n: int) -> list[int]:
     return [round(phi**i / sqrt_5) for i in range(n + 1)]
 
 
-
-
 def matrix_pow_np(m: ndarray, power: int) -> ndarray:
     """
     Raises a matrix to the power of 'power' using binary exponentiation.

From 19091434724f003c7e359e0dd39c56f6736865be Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Fri, 4 Oct 2024 15:48:21 +0530
Subject: [PATCH 21/24] Done with the required changes

---
 maths/fibonacci.py | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index b5bd0ef2f83f..2a68c36f141f 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -247,6 +247,27 @@ def matrix_pow_np(m: ndarray, power: int) -> ndarray:
 
     Returns:
         The matrix raised to the power.
+
+    Raises:
+        ValueError: If power is negative.
+
+    >>> m = np.array([[1, 1], [1, 0]], dtype=int)
+    >>> matrix_pow_np(m, 0)  # Identity matrix when raised to the power of 0
+    array([[1, 0],
+           [0, 1]])
+
+    >>> matrix_pow_np(m, 1)  # Same matrix when raised to the power of 1
+    array([[1, 1],
+           [1, 0]])
+
+    >>> matrix_pow_np(m, 5)
+    array([[8, 5],
+           [5, 3]])
+
+    >>> matrix_pow_np(m, -1)
+    Traceback (most recent call last):
+        ...
+    ValueError: power is negative
     """
     result = np.array([[1, 0], [0, 1]], dtype=int)  # Identity matrix
     base = m

From b7db4ca56d99eb0244b1ebc65f63fa72bfbee3eb Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Fri, 4 Oct 2024 15:53:39 +0530
Subject: [PATCH 22/24] Done with the required changes

---
 maths/fibonacci.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index 113a88d962b7..cb7d0c4458c5 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -267,7 +267,7 @@ def matrix_pow_np(m: ndarray, power: int) -> ndarray:
         ...
     ValueError: power is negative
     """
-    result = np.array([[1, 0], [0, 1]], dtype=int)  # Identity matrix
+    result = np.array([[1, 0], [0, 1]], dtype=int)  # Identity Matrix
     base = m
     while power:
         if power % 2 == 1:

From b70657279d5ecc3991376e1737461deb41ccd698 Mon Sep 17 00:00:00 2001
From: Acuspeedster <arnavrajsingh@gmail.com>
Date: Fri, 4 Oct 2024 21:43:09 +0530
Subject: [PATCH 23/24] Done with the required changes

---
 maths/fibonacci.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index cb7d0c4458c5..bde9f4f8af51 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -269,6 +269,8 @@ def matrix_pow_np(m: ndarray, power: int) -> ndarray:
     """
     result = np.array([[1, 0], [0, 1]], dtype=int)  # Identity Matrix
     base = m
+    if(power < 0):  # Negative power is not allowed
+        raise ValueError("power is negative")
     while power:
         if power % 2 == 1:
             result = np.dot(result, base)

From 6cfdbc3e401d3d8f341b07c83f7ff0a85175c448 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Fri, 4 Oct 2024 16:13:46 +0000
Subject: [PATCH 24/24] [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
---
 maths/fibonacci.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/maths/fibonacci.py b/maths/fibonacci.py
index bde9f4f8af51..24b2d7ae449e 100644
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -269,7 +269,7 @@ def matrix_pow_np(m: ndarray, power: int) -> ndarray:
     """
     result = np.array([[1, 0], [0, 1]], dtype=int)  # Identity Matrix
     base = m
-    if(power < 0):  # Negative power is not allowed
+    if power < 0:  # Negative power is not allowed
         raise ValueError("power is negative")
     while power:
         if power % 2 == 1: