aboutsummaryrefslogtreecommitdiff
path: root/src/stdlib/reals.c
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2026-01-12 02:42:13 -0500
committerBruce Hill <bruce@bruce-hill.com>2026-01-12 02:42:13 -0500
commit43b3ee4f0e8c385bfaac61d1607e0fdbdd37298e (patch)
tree3e85b066c2f48ac83248954444f1d497b8baf70f /src/stdlib/reals.c
parente8b87d0167877bb1b0828c6421a592e86f4de844 (diff)
Overhaul of how parsing and compiling works for reals.
Diffstat (limited to 'src/stdlib/reals.c')
-rw-r--r--src/stdlib/reals.c502
1 files changed, 277 insertions, 225 deletions
diff --git a/src/stdlib/reals.c b/src/stdlib/reals.c
index 3ffd1708..484a1ff8 100644
--- a/src/stdlib/reals.c
+++ b/src/stdlib/reals.c
@@ -14,71 +14,23 @@
#include "text.h"
#include "types.h"
-typedef struct {
- __mpq_struct value;
-} rational_t;
-
-typedef struct {
- double (*compute)(void *ctx, int precision);
- void *context;
-} constructive_t;
-
-typedef enum {
- SYM_INVALID,
- 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;
- Real_t left;
- Real_t right;
-} symbolic_t;
-
// Check if num is boxed pointer
public
CONSTFUNC bool Real$is_boxed(Real_t n) {
- return (n.u64 & QNAN_MASK) == QNAN_MASK;
+ return (n.bits & QNAN_MASK) == 0;
}
public
CONSTFUNC uint64_t Real$tag(Real_t n) {
- return n.u64 & TAG_MASK;
-}
-
-static inline void *get_ptr(Real_t n) {
- return (void *)(uintptr_t)(n.u64 & PTR_MASK);
-}
-
-static inline PUREFUNC Real_t box_ptr(void *ptr, uint64_t tag) {
- assert(((uint64_t)(uintptr_t)ptr & (~PTR_MASK)) == 0);
- Real_t n;
- n.u64 = QNAN_MASK | tag | ((uint64_t)(uintptr_t)ptr & PTR_MASK);
- return n;
+ return n.bits & TAG_MASK;
}
static inline Real_t make_double(double d) {
- return (Real_t){.d = d};
+ union {
+ double d;
+ uint64_t bits;
+ } input = {.d = d};
+ return (Real_t){.bits = input.bits ^ QNAN_MASK};
}
public
@@ -88,7 +40,9 @@ Real_t Real$from_int64(int64_t i) {
Int_t *b = GC_MALLOC(sizeof(Int_t));
*b = I(i);
- return box_ptr(b, REAL_TAG_BIGINT);
+ Real_t ret = {.bigint = b};
+ ret.bits |= REAL_TAG_BIGINT;
+ return ret;
}
public
@@ -104,7 +58,9 @@ Real_t Real$from_rational(int64_t num, int64_t den) {
if (den == 0) fail("Division by zero");
mpq_set_si(&r->value, num, (unsigned long)den);
mpq_canonicalize(&r->value);
- return box_ptr(r, REAL_TAG_RATIONAL);
+ Real_t ret = {.rational = r};
+ ret.bits |= REAL_TAG_RATIONAL;
+ return ret;
}
public
@@ -113,7 +69,7 @@ bool Real$get_rational(Real_t x, int64_t *num, int64_t *den) {
return false;
}
- rational_t *r = get_ptr(x);
+ rational_t *r = REAL_RATIONAL(x);
// Check if both numerator and denominator fit in int64_t
if (!mpz_fits_slong_p(mpq_numref(&r->value)) || !mpz_fits_slong_p(mpq_denref(&r->value))) {
@@ -127,14 +83,16 @@ bool Real$get_rational(Real_t x, int64_t *num, int64_t *den) {
}
// Promote double to exact type if needed
-static Real_t promote_double(double d) {
+static Real_t Real$as_rational(double d) {
if (!isfinite(d)) {
return make_double(d);
}
rational_t *r = GC_MALLOC(sizeof(rational_t));
mpq_init(&r->value);
mpq_set_d(&r->value, d);
- return box_ptr(r, REAL_TAG_RATIONAL);
+ Real_t ret = {.rational = r};
+ ret.bits |= REAL_TAG_RATIONAL;
+ return ret;
}
public
@@ -149,32 +107,35 @@ Real_t Real$from_int(Int_t i) {
Int_t *b = GC_MALLOC(sizeof(Int_t));
*b = i;
- return box_ptr(b, REAL_TAG_BIGINT);
+ Real_t ret = {.bigint = b};
+ ret.bits |= REAL_TAG_BIGINT;
+ return ret;
}
public
double Real$as_float64(Real_t n, bool truncate) {
- if (!Real$is_boxed(n)) return n.d;
+ if (!Real$is_boxed(n)) {
+ return REAL_DOUBLE(n);
+ }
switch (Real$tag(n)) {
case REAL_TAG_BIGINT: {
- Int_t *b = get_ptr(n);
- return Float64$from_int(*b, truncate);
+ return Float64$from_int(*REAL_BIGINT(n), truncate);
}
case REAL_TAG_RATIONAL: {
- rational_t *r = get_ptr(n);
- return mpq_get_d(&r->value);
+ rational_t *rational = REAL_RATIONAL(n);
+ return mpq_get_d(&rational->value);
}
case REAL_TAG_CONSTRUCTIVE: {
- constructive_t *c = get_ptr(n);
+ constructive_t *c = REAL_CONSTRUCTIVE(n);
return c->compute(c->context, 53);
}
case REAL_TAG_SYMBOLIC: {
- symbolic_t *s = get_ptr(n);
+ symbolic_t *s = REAL_SYMBOLIC(n);
double left = Real$as_float64(s->left, truncate);
double right = Real$as_float64(s->right, truncate);
switch (s->op) {
- case SYM_INVALID: fail("Invalid!");
+ case SYM_INVALID: fail("Invalid number!");
case SYM_ADD: return left + right;
case SYM_SUB: return left - right;
case SYM_MUL: return left * right;
@@ -196,6 +157,7 @@ double Real$as_float64(Real_t n, bool truncate) {
case SYM_FLOOR: return floor(left);
case SYM_CEIL: return ceil(left);
case SYM_PI: return M_PI;
+ case SYM_TAU: return 2. * M_PI;
case SYM_E: return M_E;
default: return NAN;
}
@@ -204,30 +166,42 @@ double Real$as_float64(Real_t n, bool truncate) {
}
}
+symbolic_t pi_symbol = {.op = SYM_PI};
+public
+Real_t Real$pi = {.symbolic = (void *)&pi_symbol + (ptrdiff_t)REAL_TAG_SYMBOLIC};
+
+symbolic_t tau_symbol = {.op = SYM_TAU};
+public
+Real_t Real$tau = {.symbolic = (void *)&tau_symbol + (ptrdiff_t)REAL_TAG_SYMBOLIC};
+
+symbolic_t e_symbol = {.op = SYM_E};
+public
+Real_t Real$e = {.symbolic = (void *)&e_symbol + (ptrdiff_t)REAL_TAG_SYMBOLIC};
+
public
Real_t Real$plus(Real_t a, Real_t b) {
- if (a.u64 == 0) return b;
- else if (b.u64 == 0) return a;
+ if (Real$is_zero(a)) return b;
+ else if (Real$is_zero(b)) return a;
if (!Real$is_boxed(a) && !Real$is_boxed(b)) {
feclearexcept(FE_INEXACT);
- volatile double result = a.d + b.d;
+ volatile double result = REAL_DOUBLE(a) + REAL_DOUBLE(b);
if (!fetestexcept(FE_INEXACT) && isfinite(result)) {
return make_double(result);
}
}
- if (!Real$is_boxed(a)) a = promote_double(a.d);
- if (!Real$is_boxed(b)) b = promote_double(b.d);
+ if (!Real$is_boxed(a)) a = Real$as_rational(REAL_DOUBLE(a));
+ if (!Real$is_boxed(b)) b = Real$as_rational(REAL_DOUBLE(b));
// Handle exact rational arithmetic
if (Real$tag(a) == REAL_TAG_RATIONAL && Real$tag(b) == REAL_TAG_RATIONAL) {
- rational_t *ra = get_ptr(a);
- rational_t *rb = get_ptr(b);
rational_t *result = GC_MALLOC(sizeof(rational_t));
mpq_init(&result->value);
- mpq_add(&result->value, &ra->value, &rb->value);
- return box_ptr(result, REAL_TAG_RATIONAL);
+ mpq_add(&result->value, &REAL_RATIONAL(a)->value, &REAL_RATIONAL(b)->value);
+ Real_t ret = {.rational = result};
+ ret.bits |= REAL_TAG_RATIONAL;
+ return ret;
}
// Fallback: create symbolic expression
@@ -235,76 +209,81 @@ Real_t Real$plus(Real_t a, Real_t b) {
sym->op = SYM_ADD;
sym->left = a;
sym->right = b;
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$minus(Real_t a, Real_t b) {
- if (b.u64 == 0) return a;
- else if (a.u64 == 0) return Real$negative(b);
+ if (Real$is_zero(b)) return a;
+ else if (Real$is_zero(a)) return Real$negative(b);
if (!Real$is_boxed(a) && !Real$is_boxed(b)) {
feclearexcept(FE_INEXACT);
- volatile double result = a.d - b.d;
+ volatile double result = REAL_DOUBLE(a) - REAL_DOUBLE(b);
if (!fetestexcept(FE_INEXACT) && isfinite(result)) {
return make_double(result);
}
}
- if (!Real$is_boxed(a)) a = promote_double(a.d);
- if (!Real$is_boxed(b)) b = promote_double(b.d);
+ if (!Real$is_boxed(a)) a = Real$as_rational(REAL_DOUBLE(a));
+ if (!Real$is_boxed(b)) b = Real$as_rational(REAL_DOUBLE(b));
if (Real$tag(a) == REAL_TAG_RATIONAL && Real$tag(b) == REAL_TAG_RATIONAL) {
- rational_t *ra = get_ptr(a);
- rational_t *rb = get_ptr(b);
rational_t *result = GC_MALLOC(sizeof(rational_t));
mpq_init(&result->value);
- mpq_sub(&result->value, &ra->value, &rb->value);
- return box_ptr(result, REAL_TAG_RATIONAL);
+ mpq_sub(&result->value, &REAL_RATIONAL(a)->value, &REAL_RATIONAL(b)->value);
+ Real_t ret = {.rational = result};
+ ret.bits |= REAL_TAG_RATIONAL;
+ return ret;
}
symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t));
sym->op = SYM_SUB;
sym->left = a;
sym->right = b;
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$times(Real_t a, Real_t b) {
- if (a.d == 1.0) return b;
- if (b.d == 1.0) return a;
+ if (!Real$is_boxed(a) && REAL_DOUBLE(a) == 1.0) return b;
+ if (!Real$is_boxed(b) && REAL_DOUBLE(b) == 1.0) return a;
if (!Real$is_boxed(a) && !Real$is_boxed(b)) {
feclearexcept(FE_INEXACT);
- volatile double result = a.d * b.d;
+ volatile double result = REAL_DOUBLE(a) * REAL_DOUBLE(b);
if (!fetestexcept(FE_INEXACT) && isfinite(result)) {
return make_double(result);
}
}
- if (!Real$is_boxed(a)) a = promote_double(a.d);
- if (!Real$is_boxed(b)) b = promote_double(b.d);
+ if (!Real$is_boxed(a)) a = Real$as_rational(REAL_DOUBLE(a));
+ if (!Real$is_boxed(b)) b = Real$as_rational(REAL_DOUBLE(b));
if (Real$tag(a) == REAL_TAG_RATIONAL && Real$tag(b) == REAL_TAG_RATIONAL) {
- rational_t *ra = get_ptr(a);
- rational_t *rb = get_ptr(b);
+ rational_t *ra = REAL_RATIONAL(a);
+ rational_t *rb = REAL_RATIONAL(b);
rational_t *result = GC_MALLOC(sizeof(rational_t));
mpq_init(&result->value);
mpq_mul(&result->value, &ra->value, &rb->value);
- return box_ptr(result, REAL_TAG_RATIONAL);
+
+ Real_t ret = {.rational = result};
+ ret.bits |= REAL_TAG_RATIONAL;
+ return ret;
}
// Check for sqrt(x) * sqrt(x) = x
if (Real$tag(a) == REAL_TAG_SYMBOLIC && Real$tag(b) == REAL_TAG_SYMBOLIC) {
- symbolic_t *sa = get_ptr(a);
- symbolic_t *sb = get_ptr(b);
+ symbolic_t *sa = REAL_SYMBOLIC(a);
+ symbolic_t *sb = REAL_SYMBOLIC(b);
if (sa->op == SYM_SQRT && sb->op == SYM_SQRT) {
- // Compare if same argument (simple pointer equality check)
- if (sa->left.u64 == sb->left.u64) {
- return sa->left;
- }
- // Also check if arguments are equal values (not just pointers)
+ // Check if arguments are equal
if (Real$equal_values(sa->left, sb->left)) {
return sa->left;
}
@@ -315,38 +294,46 @@ Real_t Real$times(Real_t a, Real_t b) {
sym->op = SYM_MUL;
sym->left = a;
sym->right = b;
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$divided_by(Real_t a, Real_t b) {
- if (b.d == 1.0) return a;
+ if (REAL_DOUBLE(b) == 1.0) return a;
if (!Real$is_boxed(a) && !Real$is_boxed(b)) {
feclearexcept(FE_INEXACT);
- volatile double result = a.d / b.d;
+ volatile double result = REAL_DOUBLE(a) / REAL_DOUBLE(b);
if (!fetestexcept(FE_INEXACT) && isfinite(result)) {
return make_double(result);
}
}
- if (!Real$is_boxed(a)) a = promote_double(a.d);
- if (!Real$is_boxed(b)) b = promote_double(b.d);
+ if (!Real$is_boxed(a)) a = Real$as_rational(REAL_DOUBLE(a));
+ if (!Real$is_boxed(b)) b = Real$as_rational(REAL_DOUBLE(b));
if (Real$tag(a) == REAL_TAG_RATIONAL && Real$tag(b) == REAL_TAG_RATIONAL) {
- rational_t *ra = get_ptr(a);
- rational_t *rb = get_ptr(b);
+ rational_t *ra = REAL_RATIONAL(a);
+ rational_t *rb = REAL_RATIONAL(b);
rational_t *result = GC_MALLOC(sizeof(rational_t));
mpq_init(&result->value);
mpq_div(&result->value, &ra->value, &rb->value);
- return box_ptr(result, REAL_TAG_RATIONAL);
+
+ Real_t ret = {.rational = result};
+ ret.bits |= REAL_TAG_RATIONAL;
+ return ret;
}
symbolic_t *sym = GC_MALLOC(sizeof(symbolic_t));
sym->op = SYM_DIV;
sym->left = a;
sym->right = b;
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
@@ -356,15 +343,15 @@ Real_t Real$mod(Real_t n, Real_t modulus) {
// For fast path with doubles
if (!Real$is_boxed(n) && !Real$is_boxed(modulus)) {
- double r = remainder(n.d, modulus.d);
- r -= (r < 0.0) * (2.0 * (modulus.d < 0.0) - 1.0) * modulus.d;
+ double r = remainder(REAL_DOUBLE(n), REAL_DOUBLE(modulus));
+ r -= (r < 0.0) * (2.0 * (REAL_DOUBLE(modulus) < 0.0) - 1.0) * REAL_DOUBLE(modulus);
return make_double(r);
}
// For rationals, compute exactly
if (Real$tag(n) == REAL_TAG_RATIONAL && Real$tag(modulus) == REAL_TAG_RATIONAL) {
- rational_t *rn = get_ptr(n);
- rational_t *rm = get_ptr(modulus);
+ rational_t *rn = REAL_RATIONAL(n);
+ rational_t *rm = REAL_RATIONAL(modulus);
// Compute n - floor(n/modulus) * modulus
mpq_t quotient, floored, result;
@@ -402,7 +389,9 @@ Real_t Real$mod(Real_t n, Real_t modulus) {
mpq_clear(result);
mpz_clear(floor_z);
- return box_ptr(res, REAL_TAG_RATIONAL);
+ Real_t ret = {.rational = res};
+ ret.bits |= REAL_TAG_RATIONAL;
+ return ret;
}
// Fallback to symbolic
@@ -410,7 +399,10 @@ Real_t Real$mod(Real_t n, Real_t modulus) {
sym->op = SYM_MOD;
sym->left = n;
sym->right = modulus;
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
@@ -445,16 +437,16 @@ public
Real_t Real$sqrt(Real_t a) {
if (!Real$is_boxed(a)) {
feclearexcept(FE_INEXACT);
- volatile double d = sqrt(a.d);
+ volatile double d = sqrt(REAL_DOUBLE(a));
volatile double check = d * d;
- if (!fetestexcept(FE_INEXACT) && check == a.d) {
+ if (!fetestexcept(FE_INEXACT) && check == REAL_DOUBLE(a)) {
return make_double(d);
}
}
// Check for perfect square in rationals
if (Real$tag(a) == REAL_TAG_RATIONAL) {
- rational_t *r = get_ptr(a);
+ rational_t *r = REAL_RATIONAL(a);
mpz_t num_sqrt, den_sqrt;
mpz_init(num_sqrt);
mpz_init(den_sqrt);
@@ -471,7 +463,10 @@ Real_t Real$sqrt(Real_t a) {
mpz_clear(num_sqrt);
mpz_clear(den_sqrt);
- return box_ptr(result, REAL_TAG_RATIONAL);
+
+ Real_t ret = {.rational = result};
+ ret.bits |= REAL_TAG_RATIONAL;
+ return ret;
}
mpz_clear(num_sqrt);
mpz_clear(den_sqrt);
@@ -481,7 +476,10 @@ Real_t Real$sqrt(Real_t a) {
sym->op = SYM_SQRT;
sym->left = a;
sym->right = make_double(0);
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
// Because the libm implementation of pow() is often inexact for common
@@ -515,7 +513,7 @@ public
Real_t Real$power(Real_t base, Real_t exp) {
if (!Real$is_boxed(base) && !Real$is_boxed(exp)) {
feclearexcept(FE_INEXACT);
- volatile double result = less_inexact_pow(base.d, exp.d);
+ volatile double result = less_inexact_pow(REAL_DOUBLE(base), REAL_DOUBLE(exp));
if (!fetestexcept(FE_INEXACT) && isfinite(result)) {
return make_double(result);
}
@@ -525,27 +523,10 @@ Real_t Real$power(Real_t base, Real_t exp) {
sym->op = SYM_POW;
sym->left = base;
sym->right = exp;
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
-}
-
-// Helper function for binary operations in parentheses
-static Text_t format_binary_op(Text_t left, Text_t right, const char *op, bool colorize) {
- const char *operator_color = colorize ? "\033[33m" : "";
- const char *paren_color = colorize ? "\033[37m" : "";
- const char *reset = colorize ? "\033[m" : "";
-
- return colorize ? Texts(paren_color, "(", reset, left, operator_color, op, reset, right, paren_color, ")", reset)
- : Texts("(", left, op, right, ")");
-}
-// Helper function for unary functions
-static Text_t format_unary_func(Text_t arg, const char *func_name, bool colorize) {
- const char *operator_color = colorize ? "\033[33m" : "";
- const char *paren_color = colorize ? "\033[37m" : "";
- const char *reset = colorize ? "\033[m" : "";
-
- return colorize ? Texts(operator_color, func_name, paren_color, "(", reset, arg, paren_color, ")", reset)
- : Texts(func_name, "(", arg, ")");
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
// Helper function for binary functions
@@ -581,18 +562,18 @@ Text_t Real$as_text(const void *n, bool colorize, const TypeInfo_t *type) {
if (!Real$is_boxed(num)) {
char buf[64];
- snprintf(buf, sizeof(buf), "%.17g", num.d);
+ snprintf(buf, sizeof(buf), "%.17g", REAL_DOUBLE(num));
return colorize ? Texts(number_color, buf, reset) : Text$from_str(buf);
}
switch (Real$tag(num)) {
case REAL_TAG_BIGINT: {
- Int_t *b = get_ptr(num);
+ Int_t *b = REAL_BIGINT(num);
Text_t int_text = Int$value_as_text(*b);
return colorize ? Texts(number_color, int_text, reset) : int_text;
}
case REAL_TAG_RATIONAL: {
- rational_t *r = get_ptr(num);
+ rational_t *r = REAL_RATIONAL(num);
// Check if denominator is 1 (integer)
if (mpz_cmp_ui(mpq_denref(&r->value), 1) == 0) {
@@ -675,42 +656,63 @@ Text_t Real$as_text(const void *n, bool colorize, const TypeInfo_t *type) {
return result;
}
case REAL_TAG_CONSTRUCTIVE: {
- constructive_t *c = get_ptr(num);
+ constructive_t *c = REAL_CONSTRUCTIVE(num);
double approx = c->compute(c->context, 53);
char buf[64];
snprintf(buf, sizeof(buf), "~%.17g", approx);
return colorize ? Texts(operator_color, "~", number_color, buf + 1, reset) : Text$from_str(buf);
}
case REAL_TAG_SYMBOLIC: {
- symbolic_t *s = get_ptr(num);
- Text_t left = Real$as_text(&s->left, colorize, type);
- Text_t right = Real$as_text(&s->right, colorize, type);
-
+ symbolic_t *s = REAL_SYMBOLIC(num);
+ const char *func = NULL;
+ const char *binop = NULL;
switch (s->op) {
case SYM_INVALID: return Text("INVALID REAL NUMBER");
- case SYM_ADD: return format_binary_op(left, right, " + ", colorize);
- case SYM_SUB: return format_binary_op(left, right, " - ", colorize);
- case SYM_MUL: return format_binary_op(left, right, " * ", colorize);
- case SYM_DIV: return format_binary_op(left, right, " / ", colorize);
- case SYM_MOD: return format_binary_op(left, right, " % ", colorize);
- case SYM_SQRT: return format_unary_func(left, "sqrt", colorize);
- case SYM_POW: return colorize ? Texts(left, operator_color, "^", reset, right) : Texts(left, "^", right);
- case SYM_SIN: return format_unary_func(left, "sin", colorize);
- case SYM_COS: return format_unary_func(left, "cos", colorize);
- case SYM_TAN: return format_unary_func(left, "tan", colorize);
- case SYM_ASIN: return format_unary_func(left, "asin", colorize);
- case SYM_ACOS: return format_unary_func(left, "acos", colorize);
- case SYM_ATAN: return format_unary_func(left, "atan", colorize);
- case SYM_ATAN2: return format_binary_func(left, right, "atan2", colorize);
- case SYM_EXP: return format_unary_func(left, "exp", colorize);
- case SYM_LOG: return format_unary_func(left, "log", colorize);
- case SYM_LOG10: return format_unary_func(left, "log10", colorize);
- case SYM_ABS: return format_unary_func(left, "abs", colorize);
- case SYM_FLOOR: return format_unary_func(left, "floor", colorize);
- case SYM_CEIL: return format_unary_func(left, "ceil", colorize);
+ case SYM_ADD: binop = " + "; break;
+ case SYM_SUB: binop = " - "; break;
+ case SYM_MUL: binop = " * "; break;
+ case SYM_DIV: binop = " / "; break;
+ case SYM_MOD: binop = " mod "; break;
+ case SYM_SQRT: func = "sqrt"; break;
+ case SYM_POW: binop = "^"; break;
+ case SYM_SIN: func = "sin"; break;
+ case SYM_COS: func = "cos"; break;
+ case SYM_TAN: func = "tan"; break;
+ case SYM_ASIN: func = "asin"; break;
+ case SYM_ACOS: func = "acos"; break;
+ case SYM_ATAN: func = "atan"; break;
+ case SYM_ATAN2: {
+ Text_t left = Real$as_text(&s->left, colorize, type);
+ Text_t right = Real$as_text(&s->right, colorize, type);
+ return format_binary_func(left, right, "atan2", colorize);
+ }
+ case SYM_EXP: func = "exp"; break;
+ case SYM_LOG: func = "log"; break;
+ case SYM_LOG10: func = "log10"; break;
+ case SYM_ABS: func = "abs"; break;
+ case SYM_FLOOR: func = "floor"; break;
+ case SYM_CEIL: func = "ceil"; break;
case SYM_PI: return format_constant("π", colorize);
+ case SYM_TAU: return format_constant("τ", colorize);
case SYM_E: return format_constant("e", colorize);
- default: return format_binary_op(left, right, " ? ", colorize);
+ default: {
+ Text_t left = Real$as_text(&s->left, colorize, type);
+ Text_t right = Real$as_text(&s->right, colorize, type);
+ return format_binary_func(left, right, "???", colorize);
+ }
+ }
+
+ const char *paren_color = colorize ? "\033[37m" : "";
+ if (func) {
+ Text_t arg = Real$as_text(&s->left, colorize, type);
+ return colorize ? Texts(operator_color, func, paren_color, "(", reset, arg, paren_color, ")", reset)
+ : Texts(func, "(", arg, ")");
+ } else {
+ Text_t left = Real$as_text(&s->left, colorize, type);
+ Text_t right = Real$as_text(&s->right, colorize, type);
+ return colorize ? Texts(paren_color, "(", reset, left, operator_color, binop, reset, right, paren_color,
+ ")", reset)
+ : Texts("(", left, binop, right, ")");
}
}
default: return colorize ? Texts(operator_color, "NaN", reset) : Text("NaN");
@@ -769,8 +771,8 @@ OptionalReal_t Real$parse(Text_t text, Text_t *remainder) {
// n *= 10^exp
if (!Int$is_zero(exponent)) {
if (Int$is_negative(exponent)) {
- ret = Real$divided_by(
- ret, Real$power(Real$from_float64(10.), Real$from_float64(-Float64$from_int(exponent, true))));
+ ret =
+ Real$divided_by(ret, Real$power(Real$from_float64(10.), Real$from_int(Int$negative(exponent))));
} else {
ret = Real$times(ret, Real$power(Real$from_float64(10.), Real$from_int(exponent)));
}
@@ -791,20 +793,21 @@ public
PUREFUNC bool Real$is_none(const void *vn, const TypeInfo_t *type) {
(void)type;
Real_t n = *(Real_t *)vn;
- return Real$is_boxed(n) && Real$tag(n) == REAL_TAG_NONE;
+ return n.bits == REAL_TAG_NONE;
}
// Equality check (may be undecidable for some symbolics)
public
bool Real$equal_values(Real_t a, Real_t b) {
- if (!Real$is_boxed(a) && !Real$is_boxed(b)) return a.d == b.d;
+ if (a.bits == b.bits) return true;
+ if (!Real$is_boxed(a) && !Real$is_boxed(b)) return REAL_DOUBLE(a) == REAL_DOUBLE(b);
- if (!Real$is_boxed(a)) a = promote_double(a.d);
- if (!Real$is_boxed(b)) b = promote_double(b.d);
+ if (!Real$is_boxed(a)) a = Real$as_rational(REAL_DOUBLE(a));
+ if (!Real$is_boxed(b)) b = Real$as_rational(REAL_DOUBLE(b));
if (Real$tag(a) == REAL_TAG_RATIONAL && Real$tag(b) == REAL_TAG_RATIONAL) {
- rational_t *ra = get_ptr(a);
- rational_t *rb = get_ptr(b);
+ rational_t *ra = REAL_RATIONAL(a);
+ rational_t *rb = REAL_RATIONAL(b);
return mpq_equal(&ra->value, &rb->value);
}
@@ -833,12 +836,12 @@ uint64_t Real$hash(const void *vr, const TypeInfo_t *type) {
public
int32_t Real$compare_values(Real_t a, Real_t b) {
if (!Real$is_boxed(a) && !Real$is_boxed(b)) {
- return (a.d > b.d) - (a.d < b.d);
+ return (REAL_DOUBLE(a) > REAL_DOUBLE(b)) - (REAL_DOUBLE(a) < REAL_DOUBLE(b));
}
if (Real$tag(a) == REAL_TAG_RATIONAL && Real$tag(b) == REAL_TAG_RATIONAL) {
- rational_t *ra = get_ptr(a);
- rational_t *rb = get_ptr(b);
+ rational_t *ra = REAL_RATIONAL(a);
+ rational_t *rb = REAL_RATIONAL(b);
return mpq_cmp(&ra->value, &rb->value);
}
@@ -858,14 +861,16 @@ int32_t Real$compare(const void *va, const void *vb, const TypeInfo_t *t) {
// Unary negation
public
Real_t Real$negative(Real_t a) {
- if (!Real$is_boxed(a)) return make_double(-a.d);
+ if (!Real$is_boxed(a)) return make_double(-REAL_DOUBLE(a));
if (Real$tag(a) == REAL_TAG_RATIONAL) {
- rational_t *r = get_ptr(a);
+ rational_t *r = REAL_RATIONAL(a);
rational_t *result = GC_MALLOC(sizeof(rational_t));
mpq_init(&result->value);
mpq_neg(&result->value, &r->value);
- return box_ptr(result, REAL_TAG_RATIONAL);
+ Real_t ret = {.rational = result};
+ ret.bits |= REAL_TAG_RATIONAL;
+ return ret;
}
return Real$times(make_double(-1.0), a);
@@ -881,18 +886,18 @@ Real_t Real$rounded_to(Real_t x, Real_t round_to) {
// Convert x to rational
if (!Real$is_boxed(x)) {
- mpq_set_d(&rx->value, x.d);
+ mpq_set_d(&rx->value, REAL_DOUBLE(x));
} else if (Real$tag(x) == REAL_TAG_RATIONAL) {
- mpq_set(&rx->value, &((rational_t *)get_ptr(x))->value);
+ mpq_set(&rx->value, &REAL_RATIONAL(x)->value);
} else {
mpq_set_d(&rx->value, Real$as_float64(x, true));
}
// Convert round_to to rational
if (!Real$is_boxed(round_to)) {
- mpq_set_d(&rr->value, round_to.d);
+ mpq_set_d(&rr->value, REAL_DOUBLE(round_to));
} else if (Real$tag(round_to) == REAL_TAG_RATIONAL) {
- mpq_set(&rr->value, &((rational_t *)get_ptr(round_to))->value);
+ mpq_set(&rr->value, &REAL_RATIONAL(round_to)->value);
} else {
mpq_set_d(&rr->value, Real$as_float64(round_to, true));
}
@@ -930,14 +935,16 @@ Real_t Real$rounded_to(Real_t x, Real_t round_to) {
mpz_clear(remainder);
mpz_clear(doubled_num);
- return box_ptr(result, REAL_TAG_RATIONAL);
+ Real_t ret = {.rational = result};
+ ret.bits |= REAL_TAG_RATIONAL;
+ return ret;
}
// Trigonometric functions - return symbolic expressions
public
Real_t Real$sin(Real_t x) {
if (!Real$is_boxed(x)) {
- double result = sin(x.d);
+ double result = sin(REAL_DOUBLE(x));
// Check if result is exact (e.g., sin(0) = 0)
if (result == 0.0 || result == 1.0 || result == -1.0) {
return make_double(result);
@@ -949,13 +956,16 @@ Real_t Real$sin(Real_t x) {
sym->op = SYM_SIN;
sym->left = x;
sym->right = make_double(0);
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$cos(Real_t x) {
if (!Real$is_boxed(x)) {
- double result = cos(x.d);
+ double result = cos(REAL_DOUBLE(x));
if (result == 0.0 || result == 1.0 || result == -1.0) {
return make_double(result);
}
@@ -965,13 +975,16 @@ Real_t Real$cos(Real_t x) {
sym->op = SYM_COS;
sym->left = x;
sym->right = make_double(0);
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$tan(Real_t x) {
if (!Real$is_boxed(x)) {
- double result = tan(x.d);
+ double result = tan(REAL_DOUBLE(x));
if (result == 0.0) return make_double(0.0);
}
@@ -979,13 +992,16 @@ Real_t Real$tan(Real_t x) {
sym->op = SYM_TAN;
sym->left = x;
sym->right = make_double(0);
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$asin(Real_t x) {
if (!Real$is_boxed(x)) {
- double result = asin(x.d);
+ double result = asin(REAL_DOUBLE(x));
if (result == 0.0) return make_double(0.0);
}
@@ -993,13 +1009,16 @@ Real_t Real$asin(Real_t x) {
sym->op = SYM_ASIN;
sym->left = x;
sym->right = make_double(0);
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$acos(Real_t x) {
if (!Real$is_boxed(x)) {
- double result = acos(x.d);
+ double result = acos(REAL_DOUBLE(x));
if (result == 0.0) return make_double(0.0);
}
@@ -1007,13 +1026,16 @@ Real_t Real$acos(Real_t x) {
sym->op = SYM_ACOS;
sym->left = x;
sym->right = make_double(0);
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$atan(Real_t x) {
if (!Real$is_boxed(x)) {
- double result = atan(x.d);
+ double result = atan(REAL_DOUBLE(x));
if (result == 0.0) return make_double(0.0);
}
@@ -1021,13 +1043,16 @@ Real_t Real$atan(Real_t x) {
sym->op = SYM_ATAN;
sym->left = x;
sym->right = make_double(0);
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$atan2(Real_t y, Real_t x) {
if (!Real$is_boxed(y) && !Real$is_boxed(x)) {
- double result = atan2(y.d, x.d);
+ double result = atan2(REAL_DOUBLE(y), REAL_DOUBLE(x));
if (result == 0.0) return make_double(0.0);
}
@@ -1035,14 +1060,17 @@ Real_t Real$atan2(Real_t y, Real_t x) {
sym->op = SYM_ATAN2;
sym->left = y;
sym->right = x;
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$exp(Real_t x) {
if (!Real$is_boxed(x)) {
feclearexcept(FE_INEXACT);
- volatile double result = exp(x.d);
+ volatile double result = exp(REAL_DOUBLE(x));
if (!fetestexcept(FE_INEXACT) && isfinite(result)) {
return make_double(result);
}
@@ -1052,14 +1080,17 @@ Real_t Real$exp(Real_t x) {
sym->op = SYM_EXP;
sym->left = x;
sym->right = make_double(0);
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$log(Real_t x) {
if (!Real$is_boxed(x)) {
feclearexcept(FE_INEXACT);
- volatile double result = log(x.d);
+ volatile double result = log(REAL_DOUBLE(x));
if (!fetestexcept(FE_INEXACT) && isfinite(result)) {
return make_double(result);
}
@@ -1069,14 +1100,17 @@ Real_t Real$log(Real_t x) {
sym->op = SYM_LOG;
sym->left = x;
sym->right = make_double(0);
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$log10(Real_t x) {
if (!Real$is_boxed(x)) {
feclearexcept(FE_INEXACT);
- volatile double result = log10(x.d);
+ volatile double result = log10(REAL_DOUBLE(x));
if (!fetestexcept(FE_INEXACT) && isfinite(result)) {
return make_double(result);
}
@@ -1086,39 +1120,47 @@ Real_t Real$log10(Real_t x) {
sym->op = SYM_LOG10;
sym->left = x;
sym->right = make_double(0);
- return box_ptr(sym, REAL_TAG_SYMBOLIC);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$abs(Real_t x) {
if (!Real$is_boxed(x)) {
- return make_double(fabs(x.d));
+ return make_double(fabs(REAL_DOUBLE(x)));
}
if (Real$tag(x) == REAL_TAG_RATIONAL) {
- rational_t *r = get_ptr(x);
+ rational_t *r = REAL_RATIONAL(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);
+ Real_t ret = {.rational = result};
+ ret.bits |= REAL_TAG_RATIONAL;
+ return ret;
}
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);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$floor(Real_t x) {
if (!Real$is_boxed(x)) {
// TODO: this may be inexact in some rare cases
- return make_double(floor(x.d));
+ return make_double(floor(REAL_DOUBLE(x)));
}
if (Real$tag(x) == REAL_TAG_RATIONAL) {
- rational_t *r = get_ptr(x);
+ rational_t *r = REAL_RATIONAL(x);
mpz_t result;
mpz_init(result);
mpz_fdiv_q(result, mpq_numref(&r->value), mpq_denref(&r->value));
@@ -1127,24 +1169,29 @@ Real_t Real$floor(Real_t x) {
mpq_init(&rat->value);
mpq_set_z(&rat->value, result);
mpz_clear(result);
- return box_ptr(rat, REAL_TAG_RATIONAL);
+ Real_t ret = {.rational = rat};
+ ret.bits |= REAL_TAG_RATIONAL;
+ return ret;
}
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);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public
Real_t Real$ceil(Real_t x) {
if (!Real$is_boxed(x)) {
- return make_double(ceil(x.d));
+ return make_double(ceil(REAL_DOUBLE(x)));
}
if (Real$tag(x) == REAL_TAG_RATIONAL) {
- rational_t *r = get_ptr(x);
+ rational_t *r = REAL_RATIONAL(x);
mpz_t result;
mpz_init(result);
mpz_cdiv_q(result, mpq_numref(&r->value), mpq_denref(&r->value));
@@ -1153,14 +1200,19 @@ Real_t Real$ceil(Real_t x) {
mpq_init(&rat->value);
mpq_set_z(&rat->value, result);
mpz_clear(result);
- return box_ptr(rat, REAL_TAG_RATIONAL);
+ Real_t ret = {.rational = rat};
+ ret.bits |= REAL_TAG_RATIONAL;
+ return ret;
}
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);
+
+ Real_t ret = {.symbolic = sym};
+ ret.bits |= REAL_TAG_SYMBOLIC;
+ return ret;
}
public