package main

import "core:fmt"
import "core:os"
import "core:strings"
import "core:strconv"

// Simple BigInt implementation using base 10^9
BigInt :: struct {
    digits: [dynamic]u64,
}

big_int_init :: proc(value: u64) -> BigInt {
    result: BigInt
    if value == 0 {
        append(&result.digits, 0)
    } else {
        append(&result.digits, value)
    }
    return result
}

big_int_add :: proc(a: ^BigInt, b: BigInt) {
    carry: u64 = 0
    max_len := max(len(a.digits), len(b.digits))
    
    for i in 0..<max_len {
        x := a.digits[i] if i < len(a.digits) else 0
        y := b.digits[i] if i < len(b.digits) else 0
        sum := x + y + carry
        
        if i < len(a.digits) {
            a.digits[i] = sum % 1_000_000_000
        } else {
            append(&a.digits, sum % 1_000_000_000)
        }
        carry = sum / 1_000_000_000
    }
    
    if carry > 0 {
        append(&a.digits, carry)
    }
}

big_int_sub :: proc(a: ^BigInt, b: BigInt) {
    borrow: i64 = 0
    
    for i in 0..<len(a.digits) {
        x := i64(a.digits[i])
        y := i64(b.digits[i]) if i < len(b.digits) else 0
        diff := x - y - borrow
        
        if diff < 0 {
            diff += 1_000_000_000
            borrow = 1
        } else {
            borrow = 0
        }
        
        a.digits[i] = u64(diff)
    }
    
    // Remove leading zeros
    for len(a.digits) > 1 && a.digits[len(a.digits) - 1] == 0 {
        pop(&a.digits)
    }
}

big_int_mul :: proc(a: BigInt, b: u64) -> BigInt {
    result: BigInt
    resize(&result.digits, len(a.digits))
    
    carry: u64 = 0
    for i in 0..<len(a.digits) {
        prod := a.digits[i] * b + carry
        result.digits[i] = prod % 1_000_000_000
        carry = prod / 1_000_000_000
    }
    
    if carry > 0 {
        append(&result.digits, carry)
    }
    
    return result
}

big_int_div :: proc(a: BigInt, divisor: u64) -> BigInt {
    result: BigInt
    resize(&result.digits, len(a.digits))
    
    remainder: u64 = 0
    for i in 0..<len(a.digits) {
        j := len(a.digits) - 1 - i
        cur := remainder * 1_000_000_000 + a.digits[j]
        result.digits[j] = cur / divisor
        remainder = cur % divisor
    }
    
    // Remove leading zeros
    for len(result.digits) > 1 && result.digits[len(result.digits) - 1] == 0 {
        pop(&result.digits)
    }
    
    return result
}

big_int_is_zero :: proc(a: BigInt) -> bool {
    return len(a.digits) == 1 && a.digits[0] == 0
}

big_int_to_string :: proc(a: BigInt) -> string {
    // Build string using dynamic array of bytes
    result_bytes: [dynamic]u8
    
    // Print digits from most significant to least significant
    for i in 0..<len(a.digits) {
        j := len(a.digits) - 1 - i
        if j == len(a.digits) - 1 {
            // Most significant digit - print without leading zeros
            digit_str := fmt.aprintf("{}", a.digits[j])
            append(&result_bytes, digit_str)
        } else {
            // Other digits - print with leading zeros to fill 9 positions
            digit_str := fmt.aprintf("{}", a.digits[j])
            // Pad with leading zeros if needed
            for k in len(digit_str)..<9 {
                append(&result_bytes, '0')
            }
            append(&result_bytes, digit_str)
        }
    }
    
    return string(result_bytes[:])
}

pow10 :: proc(exp: int) -> BigInt {
    result := big_int_init(1)
    for i in 0..<exp {
        temp := big_int_mul(result, 10)
        result = temp
    }
    return result
}

arctan :: proc(x: u64, decimals: int) -> BigInt {
    // Use base 10^9 for efficiency
    num_digits := (decimals + 10) / 9 + 10
    
    result := big_int_init(0)
    term := big_int_init(1)
    
    // Initialize term = 10^(decimals+10) / x
    for i in 0..<(decimals + 10) {
        temp := big_int_mul(term, 10)
        term = temp
    }
    term = big_int_div(term, x)
    
    x_squared := x * x
    n: int = 0
    
    for !big_int_is_zero(term) && n < decimals * 2 {
        divisor := u64(2 * n + 1)
        contrib := big_int_div(term, divisor)
        
        if n % 2 == 0 {
            big_int_add(&result, contrib)
        } else {
            big_int_sub(&result, contrib)
        }
        
        term = big_int_div(term, x_squared)
        n += 1
    }
    
    return result
}

calculate_pi :: proc(decimals: int) -> string {
    atan1_5 := arctan(5, decimals)
    atan1_239 := arctan(239, decimals)
    
    // pi = 16*arctan(1/5) - 4*arctan(1/239)
    pi16 := big_int_mul(atan1_5, 16)
    pi4 := big_int_mul(atan1_239, 4)
    big_int_sub(&pi16, pi4)
    
    pi_str := big_int_to_string(pi16)
    
    // Format with decimal point using dynamic array of bytes
    result_bytes: [dynamic]u8
    
    // First digit before decimal point
    if len(pi_str) > 0 {
        append(&result_bytes, pi_str[0])
    } else {
        append(&result_bytes, '3')
    }
    append(&result_bytes, '.')
    
    // Remaining digits after decimal point
    for i in 1..<(len(pi_str)) {
        if i <= decimals {
            append(&result_bytes, pi_str[i])
        }
    }
    
    // Pad with zeros if needed
    for i in len(pi_str)..<(decimals + 1) {
        append(&result_bytes, '0')
    }
    
    return string(result_bytes[:])
}

main :: proc() {
    decimals: int = 100
    
    if len(os.args) > 1 {
        val, ok := strconv.parse_int(os.args[1])
        if ok {
            decimals = int(val)
        }
    }
    
    pi := calculate_pi(decimals)
    fmt.println(pi)
}