diff --git a/DIRECTORY.md b/DIRECTORY.md
index 1e3711fe8dda..f0b1f7c13c2b 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -233,6 +233,7 @@
     * [Deque Doubly](data_structures/linked_list/deque_doubly.py)
     * [Doubly Linked List](data_structures/linked_list/doubly_linked_list.py)
     * [Doubly Linked List Two](data_structures/linked_list/doubly_linked_list_two.py)
+    * [Floyds Cycle Detection](data_structures/linked_list/floyds_cycle_detection.py)
     * [From Sequence](data_structures/linked_list/from_sequence.py)
     * [Has Loop](data_structures/linked_list/has_loop.py)
     * [Is Palindrome](data_structures/linked_list/is_palindrome.py)
@@ -394,6 +395,7 @@
   * [Interest](financial/interest.py)
   * [Present Value](financial/present_value.py)
   * [Price Plus Tax](financial/price_plus_tax.py)
+  * [Simple Moving Average](financial/simple_moving_average.py)
 
 ## Fractals
   * [Julia Sets](fractals/julia_sets.py)
@@ -640,10 +642,7 @@
     * [Intersection](maths/numerical_analysis/intersection.py)
     * [Nevilles Method](maths/numerical_analysis/nevilles_method.py)
     * [Newton Forward Interpolation](maths/numerical_analysis/newton_forward_interpolation.py)
-    * [Newton Method](maths/numerical_analysis/newton_method.py)
     * [Newton Raphson](maths/numerical_analysis/newton_raphson.py)
-    * [Newton Raphson 2](maths/numerical_analysis/newton_raphson_2.py)
-    * [Newton Raphson New](maths/numerical_analysis/newton_raphson_new.py)
     * [Numerical Integration](maths/numerical_analysis/numerical_integration.py)
     * [Runge Kutta](maths/numerical_analysis/runge_kutta.py)
     * [Runge Kutta Fehlberg 45](maths/numerical_analysis/runge_kutta_fehlberg_45.py)
@@ -706,6 +705,7 @@
     * [Polygonal Numbers](maths/special_numbers/polygonal_numbers.py)
     * [Pronic Number](maths/special_numbers/pronic_number.py)
     * [Proth Number](maths/special_numbers/proth_number.py)
+    * [Triangular Numbers](maths/special_numbers/triangular_numbers.py)
     * [Ugly Numbers](maths/special_numbers/ugly_numbers.py)
     * [Weird Number](maths/special_numbers/weird_number.py)
   * [Sum Of Arithmetic Series](maths/sum_of_arithmetic_series.py)
@@ -826,6 +826,7 @@
   * [Shear Stress](physics/shear_stress.py)
   * [Speed Of Sound](physics/speed_of_sound.py)
   * [Speeds Of Gas Molecules](physics/speeds_of_gas_molecules.py)
+  * [Terminal Velocity](physics/terminal_velocity.py)
 
 ## Project Euler
   * Problem 001
diff --git a/maths/numerical_analysis/newton_method.py b/maths/numerical_analysis/newton_method.py
deleted file mode 100644
index 5127bfcafd9a..000000000000
--- a/maths/numerical_analysis/newton_method.py
+++ /dev/null
@@ -1,54 +0,0 @@
-"""Newton's Method."""
-
-# Newton's Method - https://en.wikipedia.org/wiki/Newton%27s_method
-from collections.abc import Callable
-
-RealFunc = Callable[[float], float]  # type alias for a real -> real function
-
-
-# function is the f(x) and derivative is the f'(x)
-def newton(
-    function: RealFunc,
-    derivative: RealFunc,
-    starting_int: int,
-) -> float:
-    """
-    >>> newton(lambda x: x ** 3 - 2 * x - 5, lambda x: 3 * x ** 2 - 2, 3)
-    2.0945514815423474
-    >>> newton(lambda x: x ** 3 - 1, lambda x: 3 * x ** 2, -2)
-    1.0
-    >>> newton(lambda x: x ** 3 - 1, lambda x: 3 * x ** 2, -4)
-    1.0000000000000102
-    >>> import math
-    >>> newton(math.sin, math.cos, 1)
-    0.0
-    >>> newton(math.sin, math.cos, 2)
-    3.141592653589793
-    >>> newton(math.cos, lambda x: -math.sin(x), 2)
-    1.5707963267948966
-    >>> newton(math.cos, lambda x: -math.sin(x), 0)
-    Traceback (most recent call last):
-        ...
-    ZeroDivisionError: Could not find root
-    """
-    prev_guess = float(starting_int)
-    while True:
-        try:
-            next_guess = prev_guess - function(prev_guess) / derivative(prev_guess)
-        except ZeroDivisionError:
-            raise ZeroDivisionError("Could not find root") from None
-        if abs(prev_guess - next_guess) < 10**-5:
-            return next_guess
-        prev_guess = next_guess
-
-
-def f(x: float) -> float:
-    return (x**3) - (2 * x) - 5
-
-
-def f1(x: float) -> float:
-    return 3 * (x**2) - 2
-
-
-if __name__ == "__main__":
-    print(newton(f, f1, 3))
diff --git a/maths/numerical_analysis/newton_raphson.py b/maths/numerical_analysis/newton_raphson.py
index 8491ca8003bc..feee38f905dd 100644
--- a/maths/numerical_analysis/newton_raphson.py
+++ b/maths/numerical_analysis/newton_raphson.py
@@ -1,45 +1,113 @@
-# Implementing Newton Raphson method in Python
-# Author: Syed Haseeb Shah (github.com/QuantumNovice)
-# The Newton-Raphson method (also known as Newton's method) is a way to
-# quickly find a good approximation for the root of a real-valued function
-from __future__ import annotations
-
-from decimal import Decimal
-
-from sympy import diff, lambdify, symbols
-
-
-def newton_raphson(func: str, a: float | Decimal, precision: float = 1e-10) -> float:
-    """Finds root from the point 'a' onwards by Newton-Raphson method
-    >>> newton_raphson("sin(x)", 2)
-    3.1415926536808043
-    >>> newton_raphson("x**2 - 5*x + 2", 0.4)
-    0.4384471871911695
-    >>> newton_raphson("x**2 - 5", 0.1)
-    2.23606797749979
-    >>> newton_raphson("log(x) - 1", 2)
-    2.718281828458938
+"""
+The Newton-Raphson method (aka the Newton method) is a root-finding algorithm that
+approximates a root of a given real-valued function f(x). It is an iterative method
+given by the formula
+
+x_{n + 1} = x_n + f(x_n) / f'(x_n)
+
+with the precision of the approximation increasing as the number of iterations increase.
+
+Reference: https://en.wikipedia.org/wiki/Newton%27s_method
+"""
+from collections.abc import Callable
+
+RealFunc = Callable[[float], float]
+
+
+def calc_derivative(f: RealFunc, x: float, delta_x: float = 1e-3) -> float:
+    """
+    Approximate the derivative of a function f(x) at a point x using the finite
+    difference method
+
+    >>> import math
+    >>> tolerance = 1e-5
+    >>> derivative = calc_derivative(lambda x: x**2, 2)
+    >>> math.isclose(derivative, 4, abs_tol=tolerance)
+    True
+    >>> derivative = calc_derivative(math.sin, 0)
+    >>> math.isclose(derivative, 1, abs_tol=tolerance)
+    True
+    """
+    return (f(x + delta_x / 2) - f(x - delta_x / 2)) / delta_x
+
+
+def newton_raphson(
+    f: RealFunc,
+    x0: float = 0,
+    max_iter: int = 100,
+    step: float = 1e-6,
+    max_error: float = 1e-6,
+    log_steps: bool = False,
+) -> tuple[float, float, list[float]]:
     """
-    x = symbols("x")
-    f = lambdify(x, func, "math")
-    f_derivative = lambdify(x, diff(func), "math")
-    x_curr = a
-    while True:
-        x_curr = Decimal(x_curr) - Decimal(f(x_curr)) / Decimal(f_derivative(x_curr))
-        if abs(f(x_curr)) < precision:
-            return float(x_curr)
+    Find a root of the given function f using the Newton-Raphson method.
+
+    :param f: A real-valued single-variable function
+    :param x0: Initial guess
+    :param max_iter: Maximum number of iterations
+    :param step: Step size of x, used to approximate f'(x)
+    :param max_error: Maximum approximation error
+    :param log_steps: bool denoting whether to log intermediate steps
+
+    :return: A tuple containing the approximation, the error, and the intermediate
+        steps. If log_steps is False, then an empty list is returned for the third
+        element of the tuple.
+
+    :raises ZeroDivisionError: The derivative approaches 0.
+    :raises ArithmeticError: No solution exists, or the solution isn't found before the
+        iteration limit is reached.
+
+    >>> import math
+    >>> tolerance = 1e-15
+    >>> root, *_ = newton_raphson(lambda x: x**2 - 5*x + 2, 0.4, max_error=tolerance)
+    >>> math.isclose(root, (5 - math.sqrt(17)) / 2, abs_tol=tolerance)
+    True
+    >>> root, *_ = newton_raphson(lambda x: math.log(x) - 1, 2, max_error=tolerance)
+    >>> math.isclose(root, math.e, abs_tol=tolerance)
+    True
+    >>> root, *_ = newton_raphson(math.sin, 1, max_error=tolerance)
+    >>> math.isclose(root, 0, abs_tol=tolerance)
+    True
+    >>> newton_raphson(math.cos, 0)
+    Traceback (most recent call last):
+    ...
+    ZeroDivisionError: No converging solution found, zero derivative
+    >>> newton_raphson(lambda x: x**2 + 1, 2)
+    Traceback (most recent call last):
+    ...
+    ArithmeticError: No converging solution found, iteration limit reached
+    """
+
+    def f_derivative(x: float) -> float:
+        return calc_derivative(f, x, step)
+
+    a = x0  # Set initial guess
+    steps = []
+    for _ in range(max_iter):
+        if log_steps:  # Log intermediate steps
+            steps.append(a)
+
+        error = abs(f(a))
+        if error < max_error:
+            return a, error, steps
+
+        if f_derivative(a) == 0:
+            raise ZeroDivisionError("No converging solution found, zero derivative")
+        a -= f(a) / f_derivative(a)  # Calculate next estimate
+    raise ArithmeticError("No converging solution found, iteration limit reached")
 
 
 if __name__ == "__main__":
     import doctest
+    from math import exp, tanh
 
     doctest.testmod()
 
-    # Find value of pi
-    print(f"The root of sin(x) = 0 is {newton_raphson('sin(x)', 2)}")
-    # Find root of polynomial
-    print(f"The root of x**2 - 5*x + 2 = 0 is {newton_raphson('x**2 - 5*x + 2', 0.4)}")
-    # Find value of e
-    print(f"The root of log(x) - 1 = 0 is {newton_raphson('log(x) - 1', 2)}")
-    # Find root of exponential function
-    print(f"The root of exp(x) - 1 = 0 is {newton_raphson('exp(x) - 1', 0)}")
+    def func(x: float) -> float:
+        return tanh(x) ** 2 - exp(3 * x)
+
+    solution, err, steps = newton_raphson(
+        func, x0=10, max_iter=100, step=1e-6, log_steps=True
+    )
+    print(f"{solution=}, {err=}")
+    print("\n".join(str(x) for x in steps))
diff --git a/maths/numerical_analysis/newton_raphson_2.py b/maths/numerical_analysis/newton_raphson_2.py
deleted file mode 100644
index f6b227b5c9c1..000000000000
--- a/maths/numerical_analysis/newton_raphson_2.py
+++ /dev/null
@@ -1,64 +0,0 @@
-"""
-Author: P Shreyas Shetty
-Implementation of Newton-Raphson method for solving equations of kind
-f(x) = 0. It is an iterative method where solution is found by the expression
-    x[n+1] = x[n] + f(x[n])/f'(x[n])
-If no solution exists, then either the solution will not be found when iteration
-limit is reached or the gradient f'(x[n]) approaches zero. In both cases, exception
-is raised. If iteration limit is reached, try increasing maxiter.
-"""
-
-import math as m
-from collections.abc import Callable
-
-DerivativeFunc = Callable[[float], float]
-
-
-def calc_derivative(f: DerivativeFunc, a: float, h: float = 0.001) -> float:
-    """
-    Calculates derivative at point a for function f using finite difference
-    method
-    """
-    return (f(a + h) - f(a - h)) / (2 * h)
-
-
-def newton_raphson(
-    f: DerivativeFunc,
-    x0: float = 0,
-    maxiter: int = 100,
-    step: float = 0.0001,
-    maxerror: float = 1e-6,
-    logsteps: bool = False,
-) -> tuple[float, float, list[float]]:
-    a = x0  # set the initial guess
-    steps = [a]
-    error = abs(f(a))
-    f1 = lambda x: calc_derivative(f, x, h=step)  # noqa: E731  Derivative of f(x)
-    for _ in range(maxiter):
-        if f1(a) == 0:
-            raise ValueError("No converging solution found")
-        a = a - f(a) / f1(a)  # Calculate the next estimate
-        if logsteps:
-            steps.append(a)
-        if error < maxerror:
-            break
-    else:
-        raise ValueError("Iteration limit reached, no converging solution found")
-    if logsteps:
-        # If logstep is true, then log intermediate steps
-        return a, error, steps
-    return a, error, []
-
-
-if __name__ == "__main__":
-    from matplotlib import pyplot as plt
-
-    f = lambda x: m.tanh(x) ** 2 - m.exp(3 * x)  # noqa: E731
-    solution, error, steps = newton_raphson(
-        f, x0=10, maxiter=1000, step=1e-6, logsteps=True
-    )
-    plt.plot([abs(f(x)) for x in steps])
-    plt.xlabel("step")
-    plt.ylabel("error")
-    plt.show()
-    print(f"solution = {{{solution:f}}}, error = {{{error:f}}}")
diff --git a/maths/numerical_analysis/newton_raphson_new.py b/maths/numerical_analysis/newton_raphson_new.py
deleted file mode 100644
index f61841e2eb84..000000000000
--- a/maths/numerical_analysis/newton_raphson_new.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# Implementing Newton Raphson method in Python
-# Author: Saksham Gupta
-#
-# The Newton-Raphson method (also known as Newton's method) is a way to
-# quickly find a good approximation for the root of a functreal-valued ion
-# The method can also be extended to complex functions
-#
-# Newton's Method - https://en.wikipedia.org/wiki/Newton's_method
-
-from sympy import diff, lambdify, symbols
-from sympy.functions import *  # noqa: F403
-
-
-def newton_raphson(
-    function: str,
-    starting_point: complex,
-    variable: str = "x",
-    precision: float = 10**-10,
-    multiplicity: int = 1,
-) -> complex:
-    """Finds root from the 'starting_point' onwards by Newton-Raphson method
-    Refer to https://docs.sympy.org/latest/modules/functions/index.html
-    for usable mathematical functions
-
-    >>> newton_raphson("sin(x)", 2)
-    3.141592653589793
-    >>> newton_raphson("x**4 -5", 0.4 + 5j)
-    (-7.52316384526264e-37+1.4953487812212207j)
-    >>> newton_raphson('log(y) - 1', 2, variable='y')
-    2.7182818284590455
-    >>> newton_raphson('exp(x) - 1', 10, precision=0.005)
-    1.2186556186174883e-10
-    >>> newton_raphson('cos(x)', 0)
-    Traceback (most recent call last):
-        ...
-    ZeroDivisionError: Could not find root
-    """
-
-    x = symbols(variable)
-    func = lambdify(x, function)
-    diff_function = lambdify(x, diff(function, x))
-
-    prev_guess = starting_point
-
-    while True:
-        if diff_function(prev_guess) != 0:
-            next_guess = prev_guess - multiplicity * func(prev_guess) / diff_function(
-                prev_guess
-            )
-        else:
-            raise ZeroDivisionError("Could not find root") from None
-
-        # Precision is checked by comparing the difference of consecutive guesses
-        if abs(next_guess - prev_guess) < precision:
-            return next_guess
-
-        prev_guess = next_guess
-
-
-# Let's Execute
-if __name__ == "__main__":
-    # Find root of trigonometric function
-    # Find value of pi
-    print(f"The root of sin(x) = 0 is {newton_raphson('sin(x)', 2)}")
-
-    # Find root of polynomial
-    # Find fourth Root of 5
-    print(f"The root of x**4 - 5 = 0 is {newton_raphson('x**4 -5', 0.4 +5j)}")
-
-    # Find value of e
-    print(
-        "The root of log(y) - 1 = 0 is ",
-        f"{newton_raphson('log(y) - 1', 2, variable='y')}",
-    )
-
-    # Exponential Roots
-    print(
-        "The root of exp(x) - 1 = 0 is",
-        f"{newton_raphson('exp(x) - 1', 10, precision=0.005)}",
-    )
-
-    # Find root of cos(x)
-    print(f"The root of cos(x) = 0 is {newton_raphson('cos(x)', 0)}")