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

import java.math.BigInteger
import scala.annotation.tailrec

object PiCalculator {
  // 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: Int, decimals: Int): BigInteger = {
    val scale = BigInteger.TEN.pow(decimals + 10)
    val xBig = BigInteger.valueOf(x)
    val xSquared = xBig.multiply(xBig)
    
    @tailrec
    def compute(term: BigInteger, result: BigInteger, n: Int): BigInteger = {
      if (term.equals(BigInteger.ZERO)) {
        result
      } else {
        // Add current term with sign
        val newResult = if (n % 2 == 0) result.add(term) else result.subtract(term)
        // Next term: term / (x² * (2n+3) / (2n+1))
        // Simplified: term * (2n+1) / ((2n+3) * x²)
        val numerator = term.multiply(BigInteger.valueOf(2L * n + 1))
        val denominator = BigInteger.valueOf(2L * n + 3).multiply(xSquared)
        val newTerm = numerator.divide(denominator)
        compute(newTerm, newResult, n + 1)
      }
    }
    
    val initialTerm = scale.divide(xBig)
    compute(initialTerm, BigInteger.ZERO, 0)
  }
  
  // Calculate pi using Machin's formula
  def calculatePi(decimals: Int): String = {
    val actualDecimals = if (decimals < 1) 100 else decimals
    
    // Machin's formula: pi/4 = 4*arctan(1/5) - arctan(1/239)
    // pi = 16*arctan(1/5) - 4*arctan(1/239)
    val arctan5 = arctan(5, actualDecimals)
    val arctan239 = arctan(239, actualDecimals)
    
    // pi = 16*arctan(1/5) - 4*arctan(1/239)
    val pi = arctan5.multiply(BigInteger.valueOf(16)).subtract(arctan239.multiply(BigInteger.valueOf(4)))
    
    // Format output
    val piStr = pi.toString()
    
    if (actualDecimals == 0) {
      "3"
    } else {
      // Pad with zeros if needed
      val padded = if (piStr.length < actualDecimals + 1) {
        val zerosNeeded = actualDecimals + 1 - piStr.length
        "0" * zerosNeeded + piStr
      } else {
        piStr
      }
      
      // Insert decimal point
      val beforeDecimal = padded.substring(0, 1)
      val afterDecimal = padded.substring(1)
      
      // Pad or truncate to desired length
      val finalAfterDecimal = if (afterDecimal.length < actualDecimals) {
        afterDecimal + "0" * (actualDecimals - afterDecimal.length)
      } else {
        afterDecimal.substring(0, actualDecimals)
      }
      
      s"$beforeDecimal.$finalAfterDecimal"
    }
  }
  
  def main(args: Array[String]): Unit = {
    val decimals = if (args.length > 0) {
      try {
        val d = args(0).toInt
        if (d < 1) 100 else d
      } catch {
        case _: NumberFormatException => 100
      }
    } else {
      100
    }
    
    println(calculatePi(decimals))
  }
}