package main

import (
	"fmt"
	"math/big"
	"os"
	"strconv"
)

func main() {
	// Hämta antal decimaler från argument
	decimals := 100
	if len(os.Args) > 1 {
		if d, err := strconv.Atoi(os.Args[1]); err == nil && d > 0 {
			decimals = d
		}
	}

	// Beräkna pi med Machins formel: pi/4 = 4*arctan(1/5) - arctan(1/239)
	// Använd exakt aritmetik med big.Int
	pi := calculatePiExact(decimals)

	// Skriv ut resultatet
	fmt.Println(formatPiExact(pi, decimals))
}

func calculatePiExact(decimals int) *big.Int {
	// pi = 4 * (4*arctan(1/5) - arctan(1/239))
	// Skala med 10^(decimals+10) för exakt aritmetik

	scale := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(decimals+10)), nil)

	atan1_5 := arctanExact(5, scale)
	atan1_239 := arctanExact(239, scale)

	// 4 * atan(1/5)
	result := new(big.Int).Mul(big.NewInt(4), atan1_5)

	// - atan(1/239)
	result.Sub(result, atan1_239)

	// * 4
	result.Mul(result, big.NewInt(4))

	return result
}

func arctanExact(x int64, scale *big.Int) *big.Int {
	// arctan(1/x) = 1/x - 1/(3*x^3) + 1/(5*x^5) - ...
	// Med skala: scale/x - scale/(3*x^3) + scale/(5*x^5) - ...

	result := big.NewInt(0)

	xBig := big.NewInt(x)
	xSquared := new(big.Int).Mul(xBig, xBig)

	// term = scale / x
	term := new(big.Int).Div(scale, xBig)

	sign := big.NewInt(1)

	for n := 0; n < 100000; n++ {
		// Lägg till term / (2n+1) med rätt tecken
		divisor := big.NewInt(int64(2*n + 1))
		contrib := new(big.Int).Div(term, divisor)

		if sign.Sign() > 0 {
			result.Add(result, contrib)
		} else {
			result.Sub(result, contrib)
		}

		// Nästa term: term = term / x^2
		term.Div(term, xSquared)

		// Växla tecken
		sign.Neg(sign)

		// Avbryt om termen är 0
		if term.Sign() == 0 {
			break
		}
	}

	return result
}

func formatPiExact(pi *big.Int, decimals int) string {
	// Konvertera till sträng
	piStr := pi.String()

	// pi är skalad med 10^(decimals+10), så vi behöver justera decimalpunkten
	// För pi ≈ 3.14159..., pi*10^110 ≈ 314159... (110 siffror)

	// Lägg till ledande noll om nödvändigt
	for len(piStr) < decimals+10 {
		piStr = "0" + piStr
	}

	// Ta första siffran, sedan decimalpunkt, sedan resten
	if len(piStr) > decimals+10 {
		piStr = piStr[:decimals+10]
	}

	// Resultatet bör vara "3" följt av decimaler
	result := string(piStr[0]) + "." + piStr[1:decimals+1]

	return result
}