Files
el/lang/runtime/math.el
2026-05-05 01:38:51 -05:00

171 lines
5.2 KiB
EmacsLisp
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// runtime/math.el Float math, integer utilities, and numeric conversions.
//
// Implements the math/float surface from el-compiler/runtime/legacy/el_runtime.c
// (lines 303305 for el_abs/max/min, lines 47254771 for float/format ops)
// in pure El, using seed primitives.
//
// Seed primitives consumed:
// __sqrt_f(f: Float) -> Float
// __log_f(f: Float) -> Float
// __ln_f(f: Float) -> Float
// __sin_f(f: Float) -> Float
// __cos_f(f: Float) -> Float
// __pi_f() -> Float
// __float_to_str(f: Float) -> String
// __str_to_float(s: String) -> Float
// __int_to_str(n: Int) -> String
// __str_to_int(s: String) -> Int
// ---------------------------------------------------------------------------
// Integer math el_abs, el_max, el_min.
//
// Matches legacy el_abs, el_max, el_min (lines 303305).
// ---------------------------------------------------------------------------
// el_abs absolute value of an integer.
fn el_abs(n: Int) -> Int {
if n < 0 { return -n }
return n
}
// el_max larger of two integers.
fn el_max(a: Int, b: Int) -> Int {
if a > b { return a }
return b
}
// el_min smaller of two integers.
fn el_min(a: Int, b: Int) -> Int {
if a < b { return a }
return b
}
// ---------------------------------------------------------------------------
// Float math thin wrappers over seed primitives.
//
// Matches legacy math_sqrt, math_log, math_ln, math_sin, math_cos, math_pi
// (lines 47664771).
// ---------------------------------------------------------------------------
// math_sqrt square root.
fn math_sqrt(f: Float) -> Float {
return __sqrt_f(f)
}
// math_log base-10 logarithm.
fn math_log(f: Float) -> Float {
return __log_f(f)
}
// math_ln natural logarithm.
fn math_ln(f: Float) -> Float {
return __ln_f(f)
}
// math_sin sine (radians).
fn math_sin(f: Float) -> Float {
return __sin_f(f)
}
// math_cos cosine (radians).
fn math_cos(f: Float) -> Float {
return __cos_f(f)
}
// math_pi the constant π.
fn math_pi() -> Float {
return __pi_f()
}
// ---------------------------------------------------------------------------
// Float conversions float_to_str, int_to_float, float_to_int, str_to_float.
//
// Matches legacy float_to_str, int_to_float, float_to_int, str_to_float
// (lines 47254762).
// ---------------------------------------------------------------------------
// float_to_str format a float using %g (shortest exact representation).
// Matches legacy float_to_str() snprintf "%g".
fn float_to_str(f: Float) -> String {
return __float_to_str(f)
}
// int_to_float convert an integer to a float.
// Matches legacy int_to_float() (double)(int64_t)n.
fn int_to_float(n: Int) -> Float {
return __int_to_float(n)
}
// float_to_int truncate a float to an integer (toward zero).
// Matches legacy float_to_int() (int64_t)el_to_float(f).
fn float_to_int(f: Float) -> Int {
return __float_to_int(f)
}
// str_to_float parse a float from a string. Returns 0.0 on failure.
// Matches legacy str_to_float() strtod(str, NULL).
fn str_to_float(s: String) -> Float {
return __str_to_float(s)
}
// ---------------------------------------------------------------------------
// format_float format a float to a fixed number of decimal places.
//
// decimals is clamped to [0, 30]. Matches legacy format_float() snprintf "%.*f".
// ---------------------------------------------------------------------------
fn format_float(f: Float, decimals: Int) -> String {
let d: Int = decimals
if d < 0 { d = 0 }
if d > 30 { d = 30 }
// Delegate to seed; the seed exposes __format_float(f, d) -> String.
// This matches snprintf(buf, 128, "%.*f", d, v) in the legacy runtime.
return __format_float(f, d)
}
// ---------------------------------------------------------------------------
// decimal_round round a float to d decimal places (half-away-from-zero).
//
// Matches legacy decimal_round():
// mul = pow(10, d)
// r = (v >= 0 ? floor(v*mul + 0.5) : -floor(-v*mul + 0.5)) / mul
//
// We implement pow(10, d) via a loop (d <= 15, so at most 15 multiplications).
// ---------------------------------------------------------------------------
// _pow10 10^n as a Float for n in [0, 15].
fn _pow10(n: Int) -> Float {
let result: Float = 1.0
let i: Int = 0
while i < n {
result = result * 10.0
i = i + 1
}
return result
}
// _floor_f floor of a float: largest integer <= f.
// Uses __float_to_int (truncation) with correction for negative non-integers.
fn _floor_f(f: Float) -> Float {
let t: Int = __float_to_int(f)
let tf: Float = __int_to_float(t)
// if f was negative and not already an integer, subtract 1
if f < 0.0 {
if tf > f {
return tf - 1.0
}
}
return tf
}
fn decimal_round(f: Float, decimals: Int) -> Float {
let d: Int = decimals
if d < 0 { d = 0 }
if d > 15 { d = 15 }
let mul: Float = _pow10(d)
if f >= 0.0 {
return _floor_f(f * mul + 0.5) / mul
}
return 0.0 - _floor_f((0.0 - f) * mul + 0.5) / mul
}