"""Compute pi."""
from decimal import Decimal, getcontext
import argparse
import itertools


class ComputePi:
    """Compute pi to a specific precision using multiple algorithms."""

    @staticmethod
    def BBP(precision):
        """Compute pi using the Bailey-Borwein-Plouffe formula."""
        getcontext().prec = precision + 20

        pi = Decimal(0)
        for k in itertools.count():
            term = (Decimal(4)/(8*k+1) - Decimal(2)/(8*k+4) - Decimal(1)/(8*k+5) - Decimal(1)/(8*k+6))
            term /= Decimal(16)**k
            pi += term

            if term < Decimal(10)**(-precision-10):
                break

        pi = str(pi)[:-19]
        return pi

    @staticmethod
    def arctan_euler(x, one=1000000):
        """Calculate arctan(1/x) using euler's accelerated formula.

        Based on http://www.craig-wood.com/nick/articles/pi-machin/"""
        x_squared = x * x
        x_squared_plus_1 = x_squared + 1
        term = (x * one) // x_squared_plus_1
        total = term
        two_n = 2
        while 1:
            divisor = (two_n+1) * x_squared_plus_1
            term *= two_n
            term += divisor // 2    # round the division
            term = term // divisor
            if term == 0:
                break
            total += term
            two_n += 2
        return total

    @staticmethod
    def machin_euler(digits):
        """Compute pi using Machin's formula.

        Based on http://www.craig-wood.com/nick/articles/pi-machin/"""
        one = 10**(digits + 20)
        pi = 4*(4*ComputePi.arctan_euler(5, one) - ComputePi.arctan_euler(239, one))
        pi //= 10**20
        return '3.{}'.format(str(pi)[1:])



if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Calculates pi.')
    parser.add_argument('--precision', type=int, default=100,
                        help='The desired precision of pi (default: 100 digits)')

    args = parser.parse_args()
    pi_computer = ComputePi()

    print(pi_computer.machin_euler(args.precision))

[((61, 13, 61, 66), 'argparse.ArgumentParser', 'argparse.ArgumentParser', (), '', False, 'import argparse\n'), ((15, 13, 15, 23), 'decimal.Decimal', 'Decimal', ({(15, 21, 15, 22): '0'}, {}), '(0)', False, 'from decimal import Decimal, getcontext\n'), ((16, 17, 16, 34), 'itertools.count', 'itertools.count', ({}, {}), '()', False, 'import itertools\n'), ((13, 8, 13, 20), 'decimal.getcontext', 'getcontext', ({}, {}), '()', False, 'from decimal import Decimal, getcontext\n'), ((18, 20, 18, 31), 'decimal.Decimal', 'Decimal', ({(18, 28, 18, 30): '(16)'}, {}), '(16)', False, 'from decimal import Decimal, getcontext\n'), ((17, 83, 17, 93), 'decimal.Decimal', 'Decimal', ({(17, 91, 17, 92): '(1)'}, {}), '(1)', False, 'from decimal import Decimal, getcontext\n'), ((21, 22, 21, 33), 'decimal.Decimal', 'Decimal', ({(21, 30, 21, 32): '(10)'}, {}), '(10)', False, 'from decimal import Decimal, getcontext\n'), ((17, 62, 17, 72), 'decimal.Decimal', 'Decimal', ({(17, 70, 17, 71): '(1)'}, {}), '(1)', False, 'from decimal import Decimal, getcontext\n'), ((17, 20, 17, 30), 'decimal.Decimal', 'Decimal', ({(17, 28, 17, 29): '(4)'}, {}), '(4)', False, 'from decimal import Decimal, getcontext\n'), ((17, 41, 17, 51), 'decimal.Decimal', 'Decimal', ({(17, 49, 17, 50): '(2)'}, {}), '(2)', False, 'from decimal import Decimal, getcontext\n')]