https:en.wikipedia.orgwikiLeibnizformulaforCF80 Leibniz Formula for Pi The Leibniz formula is the special case arctan1  pi  4. Leibniz's formula converges extremely slowly: it exhibits sublinear convergence. Convergence https:en.wikipedia.orgwikiLeibnizformulaforCF80Convergence We cannot try to prove against an interrupted, uncompleted generation. https:en.wikipedia.orgwikiLeibnizformulaforCF80Unusualbehaviour The errors can in fact be predicted, but those calculations also approach infinity for accuracy. Our output will be a string so that we can definitely store all digits.  import math  floatcalculatepi15  math.pi True Since we cannot predict errors or interrupt any infinite alternating series generation since they approach infinity, or interrupt any alternating series, we'll need math.isclose  math.isclosefloatcalculatepi50, math.pi True  math.isclosefloatcalculatepi100, math.pi True Since math.pi contains only 16 digits, here are some tests with known values:  calculatepi50 '3.14159265358979323846264338327950288419716939937510'  calculatepi80 '3.14159265358979323846264338327950288419716939937510582097494459230781640628620899' Variables used for the iteration process We can't compare against anything if we make a generator, so we'll stick with plain return logic
def calculate_pi(limit: int) -> str:
    """
    https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80
    Leibniz Formula for Pi

    The Leibniz formula is the special case arctan(1) = pi / 4.
    Leibniz's formula converges extremely slowly: it exhibits sublinear convergence.

    Convergence (https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80#Convergence)

    We cannot try to prove against an interrupted, uncompleted generation.
    https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80#Unusual_behaviour
    The errors can in fact be predicted, but those calculations also approach infinity
    for accuracy.

    Our output will be a string so that we can definitely store all digits.

    >>> import math
    >>> float(calculate_pi(15)) == math.pi
    True

    Since we cannot predict errors or interrupt any infinite alternating series
    generation since they approach infinity, or interrupt any alternating series, we'll
    need math.isclose()

    >>> math.isclose(float(calculate_pi(50)), math.pi)
    True
    >>> math.isclose(float(calculate_pi(100)), math.pi)
    True

    Since math.pi contains only 16 digits, here are some tests with known values:

    >>> calculate_pi(50)
    '3.14159265358979323846264338327950288419716939937510'
    >>> calculate_pi(80)
    '3.14159265358979323846264338327950288419716939937510582097494459230781640628620899'
    """
    # Variables used for the iteration process
    q = 1
    r = 0
    t = 1
    k = 1
    n = 3
    l = 3

    decimal = limit
    counter = 0

    result = ""

    # We can't compare against anything if we make a generator,
    # so we'll stick with plain return logic
    while counter != decimal + 1:
        if 4 * q + r - t < n * t:
            result += str(n)
            if counter == 0:
                result += "."

            if decimal == counter:
                break

            counter += 1
            nr = 10 * (r - n * t)
            n = ((10 * (3 * q + r)) // t) - 10 * n
            q *= 10
            r = nr
        else:
            nr = (2 * q + r) * l
            nn = (q * (7 * k) + 2 + (r * l)) // (t * l)
            q *= k
            t *= l
            l += 2
            k += 1
            n = nn
            r = nr
    return result


def main() -> None:
    print(f"{calculate_pi(50) = }")
    import doctest

    doctest.testmod()


if __name__ == "__main__":
    main()
