# Pi calculation using Machin's formula
# pi/4 = 4*arctan(1/5) - arctan(1/239)

defmodule PiCalculator do
  # Calculate arctan(1/x) using Taylor series with arbitrary precision
  # arctan(1/x) = 1/x - 1/(3*x^3) + 1/(5*x^5) - ...
  def arctan(x, decimals) do
    # Use integer power to avoid float precision loss
    scale = power(10, decimals + 10)
    x_squared = x * x
    
    # Taylor series: arctan(1/x) = sum((-1)^n * 1/((2n+1) * x^(2n+1)))
    # We compute this iteratively:
    # term_0 = scale / x  (represents 1/x with scale factor)
    # For each n:
    #   contrib = term / (2n+1)
    #   result += sign * contrib
    #   term = term / x²
    #   sign = -sign
    
    term = div(scale, x)
    compute(x_squared, term, 0, 0, true, decimals * 3)
  end
  
  # Calculate 10^n using integer arithmetic
  defp power(10, n) when n >= 0 do
    Integer.pow(10, n)
  end
  
  defp compute(_x_squared, 0, result, _n, _sign, _max_iterations), do: result
  defp compute(_x_squared, _term, result, n, _sign, max_iterations) when n >= max_iterations, do: result
  defp compute(x_squared, term, result, n, sign, max_iterations) do
    divisor = 2 * n + 1
    
    # Only compute if divisor > 0
    if divisor > 0 do
      contrib = div(term, divisor)
      
      new_result = if sign do
        result + contrib
      else
        result - contrib
      end
      
      new_term = div(term, x_squared)
      
      compute(x_squared, new_term, new_result, n + 1, not sign, max_iterations)
    else
      result
    end
  end
  
  # Calculate pi using Machin's formula
  def calculate_pi(decimals) do
    actual_decimals = if decimals < 1, do: 100, else: decimals
    
    # Machin's formula: pi/4 = 4*arctan(1/5) - arctan(1/239)
    # pi = 16*arctan(1/5) - 4*arctan(1/239)
    arctan5 = arctan(5, actual_decimals)
    arctan239 = arctan(239, actual_decimals)
    
    pi = 16 * arctan5 - 4 * arctan239
    
    # Format output
    pi_str = Integer.to_string(pi)
    
    if actual_decimals == 0 do
      "3"
    else
      # Pad with zeros if needed
      padded = if String.length(pi_str) < actual_decimals + 1 do
        zeros_needed = actual_decimals + 1 - String.length(pi_str)
        String.duplicate("0", zeros_needed) <> pi_str
      else
        pi_str
      end
      
      # Insert decimal point
      before_decimal = String.slice(padded, 0, 1)
      after_decimal = String.slice(padded, 1..-1//1)
      
      # Pad or truncate to desired length
      final_after_decimal = if String.length(after_decimal) < actual_decimals do
        after_decimal <> String.duplicate("0", actual_decimals - String.length(after_decimal))
      else
        String.slice(after_decimal, 0, actual_decimals)
      end
      
      "#{before_decimal}.#{final_after_decimal}"
    end
  end
  
  def main(args) do
    decimals = case args do
      [d | _] ->
        case Integer.parse(d) do
          {n, _} when n > 0 -> n
          _ -> 100
        end
      [] -> 100
    end
    
    IO.puts(calculate_pi(decimals))
  end
end

# Run with: elixir print_hej.exs [decimals]
PiCalculator.main(System.argv())