1
0
Fork 0
forked from fun/fun

More math opcode Fun. (0.37.44)

This commit is contained in:
Johannes Findeisen 2026-01-03 04:10:11 +01:00
commit 4a6903e58a
16 changed files with 546 additions and 2 deletions

View file

@ -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)

View file

@ -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 <you@hanez.org>
* 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 ===
*/

35
examples/math_gcd_lcm.fun Normal file
View file

@ -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 <you@hanez.org>
* 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 ===
*/

38
examples/math_isqrt.fun Normal file
View file

@ -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 <you@hanez.org>
* 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 ===
*/

32
examples/math_sign.fun Normal file
View file

@ -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 <you@hanez.org>
* 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 ===
*/

View file

@ -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 "???";
}
}

View file

@ -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 {

View file

@ -11,6 +11,7 @@
#include "value.h"
#include "vm.h"
#include <stdio.h>
#include <math.h>
#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);

View file

@ -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; }

View file

@ -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"

41
src/vm/math/fmax.c Normal file
View file

@ -0,0 +1,41 @@
/**
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
* 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 <math.h>
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;
}

41
src/vm/math/fmin.c Normal file
View file

@ -0,0 +1,41 @@
/**
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
* 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 <math.h>
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;
}

40
src/vm/math/gcd.c Normal file
View file

@ -0,0 +1,40 @@
/**
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
* 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;
}

47
src/vm/math/isqrt.c Normal file
View file

@ -0,0 +1,47 @@
/**
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
* 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;
}

48
src/vm/math/lcm.c Normal file
View file

@ -0,0 +1,48 @@
/**
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
* 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;
}

33
src/vm/math/sign.c Normal file
View file

@ -0,0 +1,33 @@
/**
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
* 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;
}