! Pi Calculator using Machin's Formula
! pi/4 = 4*arctan(1/5) - arctan(1/239)
program pi_calculator
    implicit none
    integer :: decimals, argc, i, n
    character(len=32) :: arg
    integer, allocatable :: pi(:), a(:), b(:), term(:), contrib(:)
    integer :: scale_len
    integer :: carry, rem, x_sq, divisor
    
    argc = command_argument_count()
    if (argc > 0) then
        call get_command_argument(1, arg)
        read(arg, *) decimals
    else
        decimals = 100
    end if
    if (decimals <= 0) decimals = 100
    
    scale_len = decimals + 20
    allocate(pi(scale_len))
    allocate(a(scale_len))
    allocate(b(scale_len))
    allocate(term(scale_len))
    allocate(contrib(scale_len))
    
    ! Initialize
    pi = 0
    a = 0
    b = 0
    
    ! Calculate arctan(1/5) -> a
    call arctan(5, a, scale_len)
    
    ! Calculate arctan(1/239) -> b
    call arctan(239, b, scale_len)
    
    ! pi = 16*a - 4*b
    ! First multiply a by 16
    carry = 0
    do i = scale_len, 1, -1
        pi(i) = a(i) * 16 + carry
        carry = pi(i) / 10
        pi(i) = mod(pi(i), 10)
    end do
    
    ! Then multiply b by 4
    carry = 0
    do i = scale_len, 1, -1
        b(i) = b(i) * 4 + carry
        carry = b(i) / 10
        b(i) = mod(b(i), 10)
    end do
    
    ! Then subtract b from pi
    carry = 0
    do i = scale_len, 1, -1
        pi(i) = pi(i) - b(i) - carry
        if (pi(i) < 0) then
            pi(i) = pi(i) + 10
            carry = 1
        else
            carry = 0
        end if
    end do
    
    ! Print result
    write(*, '(A)', advance='no') '3.'
    do i = 2, decimals + 1
        write(*, '(I1)', advance='no') pi(i)
    end do
    write(*, *)
    
    deallocate(pi, a, b, term, contrib)
    
contains

    subroutine arctan(x, result, len)
        integer, intent(in) :: x, len
        integer, intent(out) :: result(len)
        integer :: n, i
        integer :: term(len), contrib(len)
        integer :: rem, x_sq, divisor, carry_local
        logical :: term_zero
        
        result = 0
        term = 0
        term(1) = 1
        
        ! term = 10^len / x
        rem = 0
        do i = 1, len
            rem = rem * 10 + term(i)
            term(i) = rem / x
            rem = mod(rem, x)
        end do
        
        x_sq = x * x
        n = 0
        
        do while (n < len * 3)
            divisor = 2 * n + 1
            
            ! contrib = term / divisor
            rem = 0
            do i = 1, len
                rem = rem * 10 + term(i)
                contrib(i) = rem / divisor
                rem = mod(rem, divisor)
            end do
            
            ! result = result +/- contrib
            if (mod(n, 2) == 0) then
                ! result = result + contrib
                carry_local = 0
                do i = len, 1, -1
                    result(i) = result(i) + contrib(i) + carry_local
                    carry_local = result(i) / 10
                    result(i) = mod(result(i), 10)
                end do
            else
                ! result = result - contrib
                carry_local = 0
                do i = len, 1, -1
                    result(i) = result(i) - contrib(i) - carry_local
                    if (result(i) < 0) then
                        result(i) = result(i) + 10
                        carry_local = 1
                    else
                        carry_local = 0
                    end if
                end do
            end if
            
            ! term = term / x^2
            rem = 0
            do i = 1, len
                rem = rem * 10 + term(i)
                term(i) = rem / x_sq
                rem = mod(rem, x_sq)
            end do
            
            ! Check if term is zero
            term_zero = .true.
            do i = 1, len
                if (term(i) /= 0) then
                    term_zero = .false.
                    exit
                end if
            end do
            if (term_zero) exit
            
            n = n + 1
        end do
    end subroutine arctan

end program pi_calculator