From 4a6903e58aeacfb66fcb7879ac8176076c60d954 Mon Sep 17 00:00:00 2001 From: hanez Date: Sat, 3 Jan 2026 04:10:11 +0100 Subject: [PATCH] More math opcode Fun. (0.37.44) --- CMakeLists.txt | 2 +- examples/math_fmin_fmax.fun | 32 +++++++++++++ examples/math_gcd_lcm.fun | 35 +++++++++++++++ examples/math_isqrt.fun | 38 ++++++++++++++++ examples/math_sign.fun | 32 +++++++++++++ src/bytecode.c | 6 +++ src/bytecode.h | 12 ++++- src/fun_test.c | 89 +++++++++++++++++++++++++++++++++++++ src/parser.c | 46 +++++++++++++++++++ src/vm.c | 6 +++ src/vm/math/fmax.c | 41 +++++++++++++++++ src/vm/math/fmin.c | 41 +++++++++++++++++ src/vm/math/gcd.c | 40 +++++++++++++++++ src/vm/math/isqrt.c | 47 ++++++++++++++++++++ src/vm/math/lcm.c | 48 ++++++++++++++++++++ src/vm/math/sign.c | 33 ++++++++++++++ 16 files changed, 546 insertions(+), 2 deletions(-) create mode 100644 examples/math_fmin_fmax.fun create mode 100644 examples/math_gcd_lcm.fun create mode 100644 examples/math_isqrt.fun create mode 100644 examples/math_sign.fun create mode 100644 src/vm/math/fmax.c create mode 100644 src/vm/math/fmin.c create mode 100644 src/vm/math/gcd.c create mode 100644 src/vm/math/isqrt.c create mode 100644 src/vm/math/lcm.c create mode 100644 src/vm/math/sign.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 506310c..1f9fe34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(fun VERSION 0.37.43 LANGUAGES C) +project(fun VERSION 0.37.44 LANGUAGES C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) diff --git a/examples/math_fmin_fmax.fun b/examples/math_fmin_fmax.fun new file mode 100644 index 0000000..2b1a49d --- /dev/null +++ b/examples/math_fmin_fmax.fun @@ -0,0 +1,32 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2026 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2026-01-03 + */ + +// Demo of fmin(x,y) / fmax(x,y) — float-aware min/max with C99 NaN handling + +print("=== math fmin/fmax demo start ===") + +print("fmin(3.2, 4) -> " + to_string(fmin(3.2, 4))) // 3.2 +print("fmax(3.2, 4) -> " + to_string(fmax(3.2, 4))) // 4 +print("fmin(-5, -2.5) -> " + to_string(fmin(-5, -2.5))) // -5 +print("fmax(-5, -2.5) -> " + to_string(fmax(-5, -2.5))) // -2.5 + +print("=== math fmin/fmax demo end ===") + +/* Expected output: +=== math fmin/fmax demo start === +fmin(3.2, 4) -> 3.2000000000000002 +fmax(3.2, 4) -> 4 +fmin(-5, -2.5) -> -5 +fmax(-5, -2.5) -> -2.5 +=== math fmin/fmax demo end === +*/ diff --git a/examples/math_gcd_lcm.fun b/examples/math_gcd_lcm.fun new file mode 100644 index 0000000..4f0fbab --- /dev/null +++ b/examples/math_gcd_lcm.fun @@ -0,0 +1,35 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2026 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2026-01-03 + */ + +print("=== math gcd/lcm demo start ===") + +print("gcd(48,18) -> " + to_string(gcd(48,18))) // 6 +print("gcd(0,5) -> " + to_string(gcd(0,5))) // 5 +print("gcd(-21,6) -> " + to_string(gcd(-21,6))) // 3 + +print("lcm(21,6) -> " + to_string(lcm(21,6))) // 42 +print("lcm(0,5) -> " + to_string(lcm(0,5))) // 0 +print("lcm(-4,6) -> " + to_string(lcm(-4,6))) // 12 + +print("=== math gcd/lcm demo end ===") + +/* Expected output (values): +=== math gcd/lcm demo start === +gcd(48,18) -> 6 +gcd(0,5) -> 5 +gcd(-21,6) -> 3 +lcm(21,6) -> 42 +lcm(0,5) -> 0 +lcm(-4,6) -> 12 +=== math gcd/lcm demo end === +*/ diff --git a/examples/math_isqrt.fun b/examples/math_isqrt.fun new file mode 100644 index 0000000..e232444 --- /dev/null +++ b/examples/math_isqrt.fun @@ -0,0 +1,38 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2026 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2026-01-03 + */ + +print("=== math isqrt demo start ===") + +print("isqrt(-4) -> " + to_string(isqrt(-4))) // 0 (non-negative only) +print("isqrt(0) -> " + to_string(isqrt(0))) // 0 +print("isqrt(1) -> " + to_string(isqrt(1))) // 1 +print("isqrt(2) -> " + to_string(isqrt(2))) // 1 +print("isqrt(3) -> " + to_string(isqrt(3))) // 1 +print("isqrt(4) -> " + to_string(isqrt(4))) // 2 +print("isqrt(15) -> " + to_string(isqrt(15))) // 3 +print("isqrt(16) -> " + to_string(isqrt(16))) // 4 + +print("=== math isqrt demo end ===") + +/* Expected output (values): +=== math isqrt demo start === +isqrt(-4) -> 0 +isqrt(0) -> 0 +isqrt(1) -> 1 +isqrt(2) -> 1 +isqrt(3) -> 1 +isqrt(4) -> 2 +isqrt(15) -> 3 +isqrt(16) -> 4 +=== math isqrt demo end === +*/ diff --git a/examples/math_sign.fun b/examples/math_sign.fun new file mode 100644 index 0000000..9fdfd6c --- /dev/null +++ b/examples/math_sign.fun @@ -0,0 +1,32 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2026 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2026-01-03 + */ + +print("=== math sign demo start ===") + +print("sign(-5) -> " + to_string(sign(-5))) // -1 +print("sign(0) -> " + to_string(sign(0))) // 0 +print("sign(7) -> " + to_string(sign(7))) // 1 +print("sign(-0.0) -> " + to_string(sign(-0.0))) // 0 +print("sign(3.14) -> " + to_string(sign(3.14))) // 1 + +print("=== math sign demo end ===") + +/* Expected output (values): +=== math sign demo start === +sign(-5) -> -1 +sign(0) -> 0 +sign(7) -> 1 +sign(-0.0) -> 0 +sign(3.14) -> 1 +=== math sign demo end === +*/ diff --git a/src/bytecode.c b/src/bytecode.c index 04eabb6..80a4ce7 100644 --- a/src/bytecode.c +++ b/src/bytecode.c @@ -200,6 +200,12 @@ static const char *opcode_name(OpCode op) { case OP_LOG: return "LOG"; case OP_LOG10: return "LOG10"; case OP_SQRT: return "SQRT"; + case OP_GCD: return "GCD"; + case OP_LCM: return "LCM"; + case OP_ISQRT: return "ISQRT"; + case OP_SIGN: return "SIGN"; + case OP_FMIN: return "FMIN"; + case OP_FMAX: return "FMAX"; default: return "???"; } } diff --git a/src/bytecode.h b/src/bytecode.h index fb2bfd8..7944573 100644 --- a/src/bytecode.h +++ b/src/bytecode.h @@ -251,7 +251,17 @@ typedef enum { OP_EXP, // pops x (int/float); pushes exp(x) OP_LOG, // pops x (int/float); pushes natural log ln(x) OP_LOG10, // pops x (int/float); pushes log10(x) - OP_SQRT // pops x (int/float); pushes sqrt(x) + OP_SQRT, // pops x (int/float); pushes sqrt(x) + + // Integer math helpers + OP_GCD, // pops b, a; pushes gcd(|a|,|b|) + OP_LCM, // pops b, a; pushes lcm(|a|,|b|) (0 if either is 0) + OP_ISQRT, // pops x; pushes floor(sqrt(max(0,x))) for integers + OP_SIGN, // pops x; pushes -1, 0, or 1 depending on the sign + + // Min/Max variants (float-aware, C99 semantics) + OP_FMIN, // pops b, a (int/float); pushes fmin(a,b) (NaN handling per C99) + OP_FMAX // pops b, a (int/float); pushes fmax(a,b) (NaN handling per C99) } OpCode; typedef struct { diff --git a/src/fun_test.c b/src/fun_test.c index 5642d0e..fa28b2c 100644 --- a/src/fun_test.c +++ b/src/fun_test.c @@ -11,6 +11,7 @@ #include "value.h" #include "vm.h" #include +#include #define ASSERT_EQ(val, expected) \ if ((val).type != VAL_INT || (val).i != (expected)) { \ @@ -41,7 +42,17 @@ int main(void) { int cfn3_8 = bytecode_add_constant(bc, make_float(-3.8)); int cf0 = bytecode_add_constant(bc, make_float(0.0)); int cf1 = bytecode_add_constant(bc, make_float(1.0)); + int cf5 = bytecode_add_constant(bc, make_float(5.0)); + int c4 = bytecode_add_constant(bc, make_int(4)); int c9 = bytecode_add_constant(bc, make_int(9)); + int c48 = bytecode_add_constant(bc, make_int(48)); + int c18 = bytecode_add_constant(bc, make_int(18)); + int c21 = bytecode_add_constant(bc, make_int(21)); + int c6 = bytecode_add_constant(bc, make_int(6)); + int c15 = bytecode_add_constant(bc, make_int(15)); + int c16 = bytecode_add_constant(bc, make_int(16)); + int cneg5 = bytecode_add_constant(bc, make_int(-5)); + int c7 = bytecode_add_constant(bc, make_int(7)); // ---------- Arithmetic ---------- bytecode_add_instruction(bc, OP_LOAD_CONST, c42); @@ -201,6 +212,84 @@ int main(void) { bytecode_add_instruction(bc, OP_SQRT, 0); bytecode_add_instruction(bc, OP_PRINT, 0); + // ---------- Integer math (gcd/lcm/isqrt/sign) demo ---------- + // gcd(48,18)=6 + bytecode_add_instruction(bc, OP_LOAD_CONST, c48); + bytecode_add_instruction(bc, OP_LOAD_CONST, c18); + bytecode_add_instruction(bc, OP_GCD, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + + // lcm(21,6)=42 + bytecode_add_instruction(bc, OP_LOAD_CONST, c21); + bytecode_add_instruction(bc, OP_LOAD_CONST, c6); + bytecode_add_instruction(bc, OP_LCM, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + + // isqrt cases: 0, 1, 15->3, 16->4 + bytecode_add_instruction(bc, OP_LOAD_CONST, c0); + bytecode_add_instruction(bc, OP_ISQRT, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + bytecode_add_instruction(bc, OP_LOAD_CONST, c1); + bytecode_add_instruction(bc, OP_ISQRT, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + bytecode_add_instruction(bc, OP_LOAD_CONST, c15); + bytecode_add_instruction(bc, OP_ISQRT, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + bytecode_add_instruction(bc, OP_LOAD_CONST, c16); + bytecode_add_instruction(bc, OP_ISQRT, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + + // sign(-5)=-1, sign(0)=0, sign(7)=1 + bytecode_add_instruction(bc, OP_LOAD_CONST, cneg5); + bytecode_add_instruction(bc, OP_SIGN, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + bytecode_add_instruction(bc, OP_LOAD_CONST, c0); + bytecode_add_instruction(bc, OP_SIGN, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + bytecode_add_instruction(bc, OP_LOAD_CONST, c7); + bytecode_add_instruction(bc, OP_SIGN, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + + // ---------- fmin/fmax demo ---------- + // fmin(3.2, 4) -> 3.2 + bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2); + bytecode_add_instruction(bc, OP_LOAD_CONST, c4); + bytecode_add_instruction(bc, OP_FMIN, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + // fmax(3.2, 4) -> 4 + bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2); + bytecode_add_instruction(bc, OP_LOAD_CONST, c4); + bytecode_add_instruction(bc, OP_FMAX, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + // NaN cases + double nanv = NAN; + int cNaN = bytecode_add_constant(bc, make_float(nanv)); + // fmin(NaN, 5.0) -> 5.0 + bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN); + bytecode_add_instruction(bc, OP_LOAD_CONST, cf5); + bytecode_add_instruction(bc, OP_FMIN, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + // fmax(NaN, 5.0) -> 5.0 + bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN); + bytecode_add_instruction(bc, OP_LOAD_CONST, cf5); + bytecode_add_instruction(bc, OP_FMAX, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + // fmin(5.0, NaN) -> 5.0 + bytecode_add_instruction(bc, OP_LOAD_CONST, cf5); + bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN); + bytecode_add_instruction(bc, OP_FMIN, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + // fmax(5.0, NaN) -> 5.0 + bytecode_add_instruction(bc, OP_LOAD_CONST, cf5); + bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN); + bytecode_add_instruction(bc, OP_FMAX, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + // fmin(NaN, NaN) -> NaN (prints as nan) + bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN); + bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN); + bytecode_add_instruction(bc, OP_FMIN, 0); + bytecode_add_instruction(bc, OP_PRINT, 0); + // ---------- HALT ---------- bytecode_add_instruction(bc, OP_HALT, 0); diff --git a/src/parser.c b/src/parser.c index 38e2a26..d014a73 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1742,6 +1742,22 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos) free(name); return 1; } + if (strcmp(name, "fmin") == 0) { + (*pos)++; /* '(' */ + if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "fmin expects 2 args"); free(name); return 0; } + if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "fmin expects 2 args"); free(name); return 0; } + bytecode_add_instruction(bc, OP_FMIN, 0); + free(name); + return 1; + } + if (strcmp(name, "fmax") == 0) { + (*pos)++; /* '(' */ + if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "fmax expects 2 args"); free(name); return 0; } + if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "fmax expects 2 args"); free(name); return 0; } + bytecode_add_instruction(bc, OP_FMAX, 0); + free(name); + return 1; + } if (strcmp(name, "clamp") == 0) { (*pos)++; /* '(' */ if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "clamp expects 3 args"); free(name); return 0; } @@ -1835,6 +1851,36 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos) free(name); return 1; } + if (strcmp(name, "gcd") == 0) { + (*pos)++; /* '(' */ + if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "gcd expects 2 args"); free(name); return 0; } + if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "gcd expects 2 args"); free(name); return 0; } + bytecode_add_instruction(bc, OP_GCD, 0); + free(name); + return 1; + } + if (strcmp(name, "lcm") == 0) { + (*pos)++; /* '(' */ + if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "lcm expects 2 args"); free(name); return 0; } + if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "lcm expects 2 args"); free(name); return 0; } + bytecode_add_instruction(bc, OP_LCM, 0); + free(name); + return 1; + } + if (strcmp(name, "isqrt") == 0) { + (*pos)++; /* '(' */ + if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "isqrt expects 1 arg"); free(name); return 0; } + bytecode_add_instruction(bc, OP_ISQRT, 0); + free(name); + return 1; + } + if (strcmp(name, "sign") == 0) { + (*pos)++; /* '(' */ + if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "sign expects 1 arg"); free(name); return 0; } + bytecode_add_instruction(bc, OP_SIGN, 0); + free(name); + return 1; + } if (strcmp(name, "pow") == 0) { (*pos)++; /* '(' */ if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "pow expects 2 args"); free(name); return 0; } diff --git a/src/vm.c b/src/vm.c index 2e553e3..22a45c4 100644 --- a/src/vm.c +++ b/src/vm.c @@ -672,6 +672,8 @@ void vm_run(VM *vm, Bytecode *entry) { #include "vm/math/clamp.c" #include "vm/math/max.c" #include "vm/math/min.c" + #include "vm/math/fmax.c" + #include "vm/math/fmin.c" #include "vm/math/mod.c" #include "vm/math/pow.c" #include "vm/math/floor.c" @@ -687,6 +689,10 @@ void vm_run(VM *vm, Bytecode *entry) { #include "vm/math/sqrt.c" #include "vm/math/random_int.c" #include "vm/math/random_seed.c" + #include "vm/math/gcd.c" + #include "vm/math/lcm.c" + #include "vm/math/isqrt.c" + #include "vm/math/sign.c" #include "vm/os/env.c" #include "vm/os/env_all.c" diff --git a/src/vm/math/fmax.c b/src/vm/math/fmax.c new file mode 100644 index 0000000..b8504ba --- /dev/null +++ b/src/vm/math/fmax.c @@ -0,0 +1,41 @@ +/** + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2026 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2026-01-03 + */ + +/** +* @file fmax.c + * @brief Implements the OP_FMAX opcode using C99 math.h fmax(). + * Accepts int or float; follows IEEE-754 NaN handling per fmax. + */ + +#include + +case OP_FMAX: { + Value b = pop_value(vm); + Value a = pop_value(vm); + if (!((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT))) { + fprintf(stderr, "Runtime type error: FMAX expects numbers, got %s and %s\n", + value_type_name(a.type), value_type_name(b.type)); + exit(1); + } + double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i; + double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i; + double r = fmax(da, db); + Value out; + if (!isnan(r) && !isinf(r) && r >= (double)INT64_MIN && r <= (double)INT64_MAX) { + int64_t ii = (int64_t)r; + if ((double)ii == r) out = make_int(ii); else out = make_float(r); + } else { + out = make_float(r); + } + free_value(a); free_value(b); + push_value(vm, out); + break; +} diff --git a/src/vm/math/fmin.c b/src/vm/math/fmin.c new file mode 100644 index 0000000..ded0719 --- /dev/null +++ b/src/vm/math/fmin.c @@ -0,0 +1,41 @@ +/** + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2026 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2026-01-03 + */ + +/** +* @file fmin.c + * @brief Implements the OP_FMIN opcode using C99 math.h fmin(). + * Accepts int or float; follows IEEE-754 NaN handling per fmin. + */ + +#include + +case OP_FMIN: { + Value b = pop_value(vm); + Value a = pop_value(vm); + if (!((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT))) { + fprintf(stderr, "Runtime type error: FMIN expects numbers, got %s and %s\n", + value_type_name(a.type), value_type_name(b.type)); + exit(1); + } + double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i; + double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i; + double r = fmin(da, db); + Value out; + if (!isnan(r) && !isinf(r) && r >= (double)INT64_MIN && r <= (double)INT64_MAX) { + int64_t ii = (int64_t)r; + if ((double)ii == r) out = make_int(ii); else out = make_float(r); + } else { + out = make_float(r); + } + free_value(a); free_value(b); + push_value(vm, out); + break; +} diff --git a/src/vm/math/gcd.c b/src/vm/math/gcd.c new file mode 100644 index 0000000..106a5e4 --- /dev/null +++ b/src/vm/math/gcd.c @@ -0,0 +1,40 @@ +/** + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2026 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2026-01-03 + */ + +/** +* @file gcd.c + * @brief Implements the OP_GCD opcode for greatest common divisor. + */ + +case OP_GCD: { + Value vb = pop_value(vm); + Value va = pop_value(vm); + if (!((va.type == VAL_INT) || (va.type == VAL_FLOAT)) || + !((vb.type == VAL_INT) || (vb.type == VAL_FLOAT))) { + fprintf(stderr, "Runtime type error: GCD expects numbers, got %s and %s\n", + value_type_name(va.type), value_type_name(vb.type)); + exit(1); + } + int64_t a = (va.type == VAL_INT) ? va.i : (int64_t)va.d; + int64_t b = (vb.type == VAL_INT) ? vb.i : (int64_t)vb.d; + if (a == INT64_MIN) a = (int64_t)INT64_MAX; else if (a < 0) a = -a; + if (b == INT64_MIN) b = (int64_t)INT64_MAX; else if (b < 0) b = -b; + while (b != 0) { + int64_t t = a % b; + a = b; + b = t; + } + Value res = make_int(a); + free_value(va); + free_value(vb); + push_value(vm, res); + break; +} diff --git a/src/vm/math/isqrt.c b/src/vm/math/isqrt.c new file mode 100644 index 0000000..6c60f29 --- /dev/null +++ b/src/vm/math/isqrt.c @@ -0,0 +1,47 @@ +/** + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2026 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2026-01-03 + */ + +/** +* @file isqrt.c + * @brief Implements the OP_ISQRT opcode for integer square root (floor). + */ + +case OP_ISQRT: { + Value v = pop_value(vm); + if (!((v.type == VAL_INT) || (v.type == VAL_FLOAT))) { + fprintf(stderr, "Runtime type error: ISQRT expects number, got %s\n", value_type_name(v.type)); + exit(1); + } + int64_t a = (v.type == VAL_INT) ? v.i : (int64_t)v.d; + if (a <= 0) { + free_value(v); + push_value(vm, make_int(0)); + break; + } + /* Binary isqrt inline */ + uint64_t n = (uint64_t)a; + uint64_t x = 0; + uint64_t bit = (uint64_t)1 << 62; /* highest even bit set */ + while (bit > n) bit >>= 2; + while (bit != 0) { + if (n >= x + bit) { + n -= x + bit; + x = (x >> 1) + bit; + } else { + x >>= 1; + } + bit >>= 2; + } + int64_t r = (int64_t)x; + free_value(v); + push_value(vm, make_int(r)); + break; +} diff --git a/src/vm/math/lcm.c b/src/vm/math/lcm.c new file mode 100644 index 0000000..b477c3e --- /dev/null +++ b/src/vm/math/lcm.c @@ -0,0 +1,48 @@ +/** + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2026 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2026-01-03 + */ + +/** +* @file lcm.c + * @brief Implements the OP_LCM opcode for least common multiple. + */ + +case OP_LCM: { + Value vb = pop_value(vm); + Value va = pop_value(vm); + if (!((va.type == VAL_INT) || (va.type == VAL_FLOAT)) || + !((vb.type == VAL_INT) || (vb.type == VAL_FLOAT))) { + fprintf(stderr, "Runtime type error: LCM expects numbers, got %s and %s\n", + value_type_name(va.type), value_type_name(vb.type)); + exit(1); + } + int64_t a = (va.type == VAL_INT) ? va.i : (int64_t)va.d; + int64_t b = (vb.type == VAL_INT) ? vb.i : (int64_t)vb.d; + if (a == INT64_MIN) a = (int64_t)INT64_MAX; else if (a < 0) a = -a; + if (b == INT64_MIN) b = (int64_t)INT64_MAX; else if (b < 0) b = -b; + if (a == 0 || b == 0) { + free_value(va); free_value(vb); + push_value(vm, make_int(0)); + break; + } + /* gcd(a,b) */ + int64_t x = a, y = b; + while (y != 0) { + int64_t t = x % y; x = y; y = t; + } + int64_t g = x; + /* lcm = (a/g)*b (attempt to reduce overflow) */ + int64_t l = (a / g) * b; + Value res = make_int(l); + free_value(va); + free_value(vb); + push_value(vm, res); + break; +} diff --git a/src/vm/math/sign.c b/src/vm/math/sign.c new file mode 100644 index 0000000..e54a787 --- /dev/null +++ b/src/vm/math/sign.c @@ -0,0 +1,33 @@ +/** + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2026 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2026-01-03 + */ + +/** +* @file sign.c + * @brief Implements the OP_SIGN opcode returning -1, 0, or 1. + */ + +case OP_SIGN: { + Value v = pop_value(vm); + int out = 0; + if (v.type == VAL_INT) { + out = (v.i > 0) - (v.i < 0); + } else if (v.type == VAL_FLOAT) { + if (v.d > 0.0) out = 1; + else if (v.d < 0.0) out = -1; + else out = 0; + } else { + fprintf(stderr, "Runtime type error: SIGN expects number, got %s\n", value_type_name(v.type)); + exit(1); + } + free_value(v); + push_value(vm, make_int(out)); + break; +}