From fd0c9762684821ed9931b3e59c812f1849f1a7d4 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 11 Jan 2026 15:08:46 -0500 Subject: Add missing functionality. --- src/environment.c | 20 +++- src/stdlib/reals.c | 346 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/stdlib/reals.h | 38 ++++-- 3 files changed, 390 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/environment.c b/src/environment.c index 8087820a..1dfeb6bc 100644 --- a/src/environment.c +++ b/src/environment.c @@ -292,15 +292,33 @@ env_t *global_env(bool source_mapping) { #undef C MAKE_TYPE( // "Real", REAL_TYPE, Text("Real_t"), Text("Real$info"), // + {"abs", "Real$abs", "func(x:Real -> Real)"}, // + {"acos", "Real$acos", "func(x:Real -> Real)"}, // + {"asin", "Real$asin", "func(x:Real -> Real)"}, // + {"atan", "Real$atan", "func(x:Real -> Real)"}, // + {"atan2", "Real$atan2", "func(y,x:Real -> Real)"}, // + {"ceil", "Real$ceil", "func(x:Real -> Real)"}, // + {"clamped", "Real$clamped", "func(x,low,high:Real -> Real)"}, + {"cos", "Real$cos", "func(x:Real -> Real)"}, // {"divided_by", "Real$divided_by", "func(x,y:Real -> Real)"}, // + {"exp", "Real$exp", "func(x:Real -> Real)"}, // + {"floor", "Real$floor", "func(x:Real -> Real)"}, // + {"is_between", "Real$is_between", "func(x,low,high:Real -> Bool)"}, // + {"log", "Real$log", "func(x:Real -> Real)"}, // + {"log10", "Real$log10", "func(x:Real -> Real)"}, // {"minus", "Real$minus", "func(x,y:Real -> Real)"}, // + {"mix", "Real$mix", "func(amount,x,y:Real -> Real)"}, // + {"mod", "Real$mod", "func(n,modulus:Real -> Real)"}, // + {"mod1", "Real$mod1", "func(n,modulus:Real -> Real)"}, // {"negative", "Real$negative", "func(x:Real -> Real)"}, // {"parse", "Real$parse", "func(text:Text, remainder:&Text?=none -> Real?)"}, // {"plus", "Real$plus", "func(x,y:Real -> Real)"}, // {"power", "Real$power", "func(base:Real,exponent:Real -> Real)"}, // + {"rounded_to", "Real$rounded_to", "func(x:Real, rounded_to:Real = 1.0 -> Real)"}, // + {"sin", "Real$sin", "func(x:Real -> Real)"}, // {"sqrt", "Real$sqrt", "func(x:Real -> Real)"}, // + {"tan", "Real$tan", "func(x:Real -> Real)"}, // {"times", "Real$times", "func(x,y:Real -> Real)"}, // - {"rounded_to", "Real$rounded_to", "func(x:Real, rounded_to:Real = 1.0 -> Real)"}, // ), MAKE_TYPE( // "CString", Type(CStringType), Text("char*"), Text("CString$info"), // diff --git a/src/stdlib/reals.c b/src/stdlib/reals.c index 5a9c1f85..87d1e64a 100644 --- a/src/stdlib/reals.c +++ b/src/stdlib/reals.c @@ -23,7 +23,30 @@ typedef struct { void *context; } constructive_t; -typedef enum { SYM_ADD, SYM_SUB, SYM_MUL, SYM_DIV, SYM_SQRT, SYM_POW, SYM_PI, SYM_E } sym_op_t; +typedef enum { + SYM_ADD, + SYM_SUB, + SYM_MUL, + SYM_DIV, + SYM_MOD, + SYM_SQRT, + SYM_POW, + SYM_SIN, + SYM_COS, + SYM_TAN, + SYM_ASIN, + SYM_ACOS, + SYM_ATAN, + SYM_ATAN2, + SYM_EXP, + SYM_LOG, + SYM_LOG10, + SYM_ABS, + SYM_FLOOR, + SYM_CEIL, + SYM_PI, + SYM_E +} sym_op_t; typedef struct symbolic { sym_op_t op; @@ -258,6 +281,98 @@ Real_t Real$divided_by(Real_t a, Real_t b) { return box_ptr(sym, REAL_TAG_SYMBOLIC); } +public +Real_t Real$mod(Real_t n, Real_t modulus) { + // Euclidean division, see: + // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf + + // For fast path with doubles + if (!is_boxed(n) && !is_boxed(modulus)) { + double r = remainder(n.d, modulus.d); + r -= (r < 0.0) * (2.0 * (modulus.d < 0.0) - 1.0) * modulus.d; + return make_double(r); + } + + // For rationals, compute exactly + if (get_tag(n) == REAL_TAG_RATIONAL && get_tag(modulus) == REAL_TAG_RATIONAL) { + rational_t *rn = get_ptr(n); + rational_t *rm = get_ptr(modulus); + + // Compute n - floor(n/modulus) * modulus + mpq_t quotient, floored, result; + mpq_init(quotient); + mpq_init(floored); + mpq_init(result); + + mpq_div(quotient, &rn->value, &rm->value); + + // Floor the quotient + mpz_t floor_z; + mpz_init(floor_z); + mpz_fdiv_q(floor_z, mpq_numref(quotient), mpq_denref(quotient)); + mpq_set_z(floored, floor_z); + + // result = n - floor * modulus + mpq_mul(result, floored, &rm->value); + mpq_sub(result, &rn->value, result); + + // Apply Euclidean correction + if (mpq_sgn(result) < 0) { + if (mpq_sgn(&rm->value) < 0) { + mpq_sub(result, result, &rm->value); + } else { + mpq_add(result, result, &rm->value); + } + } + + rational_t *res = GC_MALLOC(sizeof(rational_t)); + mpq_init(&res->value); + mpq_set(&res->value, result); + + mpq_clear(quotient); + mpq_clear(floored); + mpq_clear(result); + mpz_clear(floor_z); + + return box_ptr(res, REAL_TAG_RATIONAL); + } + + // Fallback to symbolic + symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t)); + sym->op = SYM_MOD; + sym->left = n; + sym->right = modulus; + return box_ptr(sym, REAL_TAG_SYMBOLIC); +} + +public +Real_t Real$mod1(Real_t n, Real_t modulus) { + Real_t one = make_double(1.0); + return Real$plus(one, Real$mod(Real$minus(n, one), modulus)); +} + +public +Real_t Real$mix(Real_t amount, Real_t x, Real_t y) { + // mix = (1 - amount) * x + amount * y + Real_t one = make_double(1.0); + Real_t one_minus_amount = Real$minus(one, amount); + Real_t left_term = Real$times(one_minus_amount, x); + Real_t right_term = Real$times(amount, y); + return Real$plus(left_term, right_term); +} + +public +bool Real$is_between(Real_t x, Real_t low, Real_t high) { + return Real$compare(&low, &x, NULL) <= 0 && Real$compare(&x, &high, NULL) <= 0; +} + +public +Real_t Real$clamped(Real_t x, Real_t low, Real_t high) { + if (Real$compare(&x, &low, NULL) <= 0) return low; + if (Real$compare(&x, &high, NULL) >= 0) return high; + return x; +} + public Real_t Real$sqrt(Real_t a) { if (!is_boxed(a)) { @@ -718,6 +833,235 @@ Real_t Real$rounded_to(Real_t x, Real_t round_to) { return box_ptr(result, REAL_TAG_RATIONAL); } +// Trigonometric functions - return symbolic expressions +public +Real_t Real$sin(Real_t x) { + if (!is_boxed(x)) { + double result = sin(x.d); + // Check if result is exact (e.g., sin(0) = 0) + if (result == 0.0 || result == 1.0 || result == -1.0) { + return make_double(result); + } + } + + // Create symbolic expression + symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t)); + sym->op = SYM_SIN; + sym->left = x; + sym->right = make_double(0); + return box_ptr(sym, REAL_TAG_SYMBOLIC); +} + +public +Real_t Real$cos(Real_t x) { + if (!is_boxed(x)) { + double result = cos(x.d); + if (result == 0.0 || result == 1.0 || result == -1.0) { + return make_double(result); + } + } + + symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t)); + sym->op = SYM_COS; + sym->left = x; + sym->right = make_double(0); + return box_ptr(sym, REAL_TAG_SYMBOLIC); +} + +public +Real_t Real$tan(Real_t x) { + if (!is_boxed(x)) { + double result = tan(x.d); + if (result == 0.0) return make_double(0.0); + } + + symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t)); + sym->op = SYM_TAN; + sym->left = x; + sym->right = make_double(0); + return box_ptr(sym, REAL_TAG_SYMBOLIC); +} + +public +Real_t Real$asin(Real_t x) { + if (!is_boxed(x)) { + double result = asin(x.d); + if (result == 0.0) return make_double(0.0); + } + + symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t)); + sym->op = SYM_ASIN; + sym->left = x; + sym->right = make_double(0); + return box_ptr(sym, REAL_TAG_SYMBOLIC); +} + +public +Real_t Real$acos(Real_t x) { + if (!is_boxed(x)) { + double result = acos(x.d); + if (result == 0.0) return make_double(0.0); + } + + symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t)); + sym->op = SYM_ACOS; + sym->left = x; + sym->right = make_double(0); + return box_ptr(sym, REAL_TAG_SYMBOLIC); +} + +public +Real_t Real$atan(Real_t x) { + if (!is_boxed(x)) { + double result = atan(x.d); + if (result == 0.0) return make_double(0.0); + } + + symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t)); + sym->op = SYM_ATAN; + sym->left = x; + sym->right = make_double(0); + return box_ptr(sym, REAL_TAG_SYMBOLIC); +} + +public +Real_t Real$atan2(Real_t y, Real_t x) { + if (!is_boxed(y) && !is_boxed(x)) { + double result = atan2(y.d, x.d); + if (result == 0.0) return make_double(0.0); + } + + symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t)); + sym->op = SYM_ATAN2; + sym->left = y; + sym->right = x; + return box_ptr(sym, REAL_TAG_SYMBOLIC); +} + +public +Real_t Real$exp(Real_t x) { + if (!is_boxed(x)) { + feclearexcept(FE_INEXACT); + double result = exp(x.d); + if (!fetestexcept(FE_INEXACT) && isfinite(result)) { + return make_double(result); + } + } + + symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t)); + sym->op = SYM_EXP; + sym->left = x; + sym->right = make_double(0); + return box_ptr(sym, REAL_TAG_SYMBOLIC); +} + +public +Real_t Real$log(Real_t x) { + if (!is_boxed(x)) { + feclearexcept(FE_INEXACT); + double result = log(x.d); + if (!fetestexcept(FE_INEXACT) && isfinite(result)) { + return make_double(result); + } + } + + symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t)); + sym->op = SYM_LOG; + sym->left = x; + sym->right = make_double(0); + return box_ptr(sym, REAL_TAG_SYMBOLIC); +} + +public +Real_t Real$log10(Real_t x) { + if (!is_boxed(x)) { + feclearexcept(FE_INEXACT); + double result = log10(x.d); + if (!fetestexcept(FE_INEXACT) && isfinite(result)) { + return make_double(result); + } + } + + symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t)); + sym->op = SYM_LOG10; + sym->left = x; + sym->right = make_double(0); + return box_ptr(sym, REAL_TAG_SYMBOLIC); +} + +public +Real_t Real$abs(Real_t x) { + if (!is_boxed(x)) { + return make_double(fabs(x.d)); + } + + if (get_tag(x) == REAL_TAG_RATIONAL) { + rational_t *r = get_ptr(x); + rational_t *result = GC_MALLOC(sizeof(rational_t)); + mpq_init(&result->value); + mpq_abs(&result->value, &r->value); + return box_ptr(result, REAL_TAG_RATIONAL); + } + + symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t)); + sym->op = SYM_ABS; + sym->left = x; + sym->right = make_double(0); + return box_ptr(sym, REAL_TAG_SYMBOLIC); +} + +public +Real_t Real$floor(Real_t x) { + if (!is_boxed(x)) { + return make_double(floor(x.d)); + } + + if (get_tag(x) == REAL_TAG_RATIONAL) { + rational_t *r = get_ptr(x); + mpz_t result; + mpz_init(result); + mpz_fdiv_q(result, mpq_numref(&r->value), mpq_denref(&r->value)); + + rational_t *rat = GC_MALLOC(sizeof(rational_t)); + mpq_init(&rat->value); + mpq_set_z(&rat->value, result); + mpz_clear(result); + return box_ptr(rat, REAL_TAG_RATIONAL); + } + + symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t)); + sym->op = SYM_FLOOR; + sym->left = x; + sym->right = make_double(0); + return box_ptr(sym, REAL_TAG_SYMBOLIC); +} + +public +Real_t Real$ceil(Real_t x) { + if (!is_boxed(x)) { + return make_double(ceil(x.d)); + } + + if (get_tag(x) == REAL_TAG_RATIONAL) { + rational_t *r = get_ptr(x); + mpz_t result; + mpz_init(result); + mpz_cdiv_q(result, mpq_numref(&r->value), mpq_denref(&r->value)); + + rational_t *rat = GC_MALLOC(sizeof(rational_t)); + mpq_init(&rat->value); + mpq_set_z(&rat->value, result); + mpz_clear(result); + return box_ptr(rat, REAL_TAG_RATIONAL); + } + + symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t)); + sym->op = SYM_CEIL; + sym->left = x; + sym->right = make_double(0); + return box_ptr(sym, REAL_TAG_SYMBOLIC); +} + public int Real$test() { GC_INIT(); diff --git a/src/stdlib/reals.h b/src/stdlib/reals.h index ddb91f66..10c071f9 100644 --- a/src/stdlib/reals.h +++ b/src/stdlib/reals.h @@ -19,28 +19,42 @@ #define NONE_REAL ((Real_t){.u64 = QNAN_MASK | REAL_TAG_NONE}) -Text_t Real$value_as_text(Real_t x); - +Int_t Real$as_int(Real_t x, bool truncate); OptionalReal_t Real$parse(Text_t text, Text_t *remainder); -Real_t Real$from_text(Text_t text); +Real_t Real$abs(Real_t x); +Real_t Real$acos(Real_t x); +Real_t Real$asin(Real_t x); +Real_t Real$atan(Real_t x); +Real_t Real$atan2(Real_t y, Real_t x); +Real_t Real$ceil(Real_t x); +Real_t Real$clamped(Real_t x, Real_t low, Real_t high); +Real_t Real$cos(Real_t x); +Real_t Real$divided_by(Real_t x, Real_t y); +Real_t Real$exp(Real_t x); +Real_t Real$floor(Real_t x); Real_t Real$from_float64(double n); Real_t Real$from_int(Int_t i); - -double Real$as_float64(Real_t n, bool truncate); -Int_t Real$as_int(Real_t x, bool truncate); - +Real_t Real$from_text(Text_t text); +Real_t Real$log(Real_t x); +Real_t Real$log10(Real_t x); +Real_t Real$minus(Real_t x, Real_t y); +Real_t Real$mix(Real_t amount, Real_t x, Real_t y); +Real_t Real$mod(Real_t n, Real_t modulus); +Real_t Real$mod1(Real_t n, Real_t modulus); Real_t Real$negative(Real_t x); Real_t Real$plus(Real_t x, Real_t y); -Real_t Real$minus(Real_t x, Real_t y); -Real_t Real$times(Real_t x, Real_t y); -Real_t Real$divided_by(Real_t x, Real_t y); Real_t Real$power(Real_t base, Real_t exp); +Real_t Real$rounded_to(Real_t x, Real_t round_to); +Real_t Real$sin(Real_t x); Real_t Real$sqrt(Real_t x); +Real_t Real$tan(Real_t x); +Real_t Real$times(Real_t x, Real_t y); +Text_t Real$value_as_text(Real_t x); bool Real$equal(const void *va, const void *vb, const TypeInfo_t *t); +bool Real$is_between(Real_t x, Real_t low, Real_t high); +double Real$as_float64(Real_t n, bool truncate); int32_t Real$compare(const void *va, const void *vb, const TypeInfo_t *t); -Real_t Real$rounded_to(Real_t x, Real_t round_to); - int Real$test(); extern const TypeInfo_t Real$info; -- cgit v1.2.3