Initial commit: Pi calculation benchmark with 34 languages

- Added implementations for: bash, brainfuck, c, cpp, crystal, csharp, d, dart, elixir, erlang, fortran, go, haskell, java, javascript, julia, kotlin, objective-c, scala, typescript, lua, nim, odin, perl, php, python, r, ruby, rust, swift, zig, assembly, vimscript, wolfram
- All implementations use Machin's formula: π/4 = 4*arctan(1/5) - arctan(1/239)
- Build system with ./build.sh, test system with ./test.sh
- Performance testing with ./run_all.sh
- Comprehensive README.md explaining performance differences
- Test framework verifies correctness against known π values
This commit is contained in:
Ein Anderssono
2026-04-23 00:26:18 +02:00
commit 54d2fecee0
182 changed files with 17471 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
package pi_test
import "core:os"
import "core:fmt"
import "core:strings"
import "core:testing"
SCRIPT_PATH :: "/Users/einand/Code/test/odin/print_hej"
run_script :: proc(args: []string) -> string {
cmd: strings.Builder
strings.init_builder(&cmd)
defer strings.free_builder(&cmd)
strings.write_string(&cmd, SCRIPT_PATH)
for arg in args {
strings.write_byte(&cmd, ' ')
strings.write_string(&cmd, arg)
}
process := os.create_process(strings.to_string(cmd))
defer os.destroy_process(process)
output := os.read_entire_file_from_process(process)
defer free(output.data)
// Remove trailing newline
result := string(output.data[:])
if len(result) > 0 && result[len(result)-1] == '\n' {
result = result[:len(result)-1]
}
return result
}
@test "10 decimals"
{
result := run_script([]string{"10"})
expected := "3.1415926535"
testing.expect(result == expected)
}
@test "5 decimals"
{
result := run_script([]string{"5"})
expected := "3.14159"
testing.expect(result == expected)
}
@test "1 decimal"
{
result := run_script([]string{"1"})
expected := "3.1"
testing.expect(result == expected)
}
@test "100 decimals"
{
result := run_script([]string{"100"})
expected := "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679"
testing.expect(result == expected)
}
@test "default 100 decimals"
{
result := run_script([]string{})
expected := "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679"
testing.expect(result == expected)
}
@test "10000 decimals"
{
result := run_script([]string{"10000"})
// Check length: "3." + 10000 digits = 10002 characters
testing.expect(len(result) == 10002)
testing.expect(strings.has_prefix(result, "3.14159"))
}
+228
View File
@@ -0,0 +1,228 @@
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)
}
+27
View File
@@ -0,0 +1,27 @@
package main
import "core:fmt"
import "core:strings"
main :: proc() {
// Test string building
builder: strings.Builder
strings.builder_init(&builder, context.temp_allocator)
// Test 1: Using fmt.sbprintf
fmt.sbprintf(&builder, "{}", 123)
fmt.sbprintf(&builder, "{:09}", 456)
result := strings.to_string(builder)
fmt.println("Result:", result)
// Test 2: Using append to dynamic array
bytes: [dynamic]u8
append(&bytes, 51) // 3
append(&bytes, 46) // .
append(&bytes, 49) // 1
append(&bytes, 52) // 4
str := string(bytes[:])
fmt.println("String from bytes:", str)
}