720 lines
19 KiB
Rust
720 lines
19 KiB
Rust
use std::cmp::Ordering;
|
|
|
|
use lazy_static::lazy_static;
|
|
use talc_lang::{exception::Result, lstring::LString, parse_int, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{ops::RatioExt, Complex64, Value}, Vm};
|
|
use talc_macros::native_func;
|
|
|
|
use crate::{unpack_args, unpack_varargs};
|
|
|
|
lazy_static! {
|
|
static ref SYM_NAN: Symbol = Symbol::get("nan");
|
|
static ref SYM_INFINITE: Symbol = Symbol::get("infinite");
|
|
static ref SYM_ZERO: Symbol = Symbol::get("zero");
|
|
static ref SYM_SUBNORMAL: Symbol = Symbol::get("subnormal");
|
|
static ref SYM_NORMAL: Symbol = Symbol::get("normal");
|
|
}
|
|
|
|
#[inline]
|
|
fn to_floaty(v: Value) -> Value {
|
|
match v {
|
|
Value::Int(v) => Value::Float(v as f64),
|
|
Value::Ratio(v) => Value::Float(v.to_f64()),
|
|
Value::Float(v) => Value::Float(v),
|
|
Value::Complex(v) => Value::Complex(v),
|
|
v => v,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn to_complex(v: Value) -> Value {
|
|
match v {
|
|
Value::Int(v) => Value::Complex((v as f64).into()),
|
|
Value::Ratio(v) => Value::Complex(v.to_f64().into()),
|
|
Value::Float(v) => Value::Complex(v.into()),
|
|
Value::Complex(v) => Value::Complex(v),
|
|
v => v,
|
|
}
|
|
}
|
|
|
|
//
|
|
// load
|
|
//
|
|
|
|
pub fn load(vm: &mut Vm) {
|
|
vm.set_global_name("e", std::f64::consts::E.into());
|
|
vm.set_global_name("tau", std::f64::consts::TAU.into());
|
|
vm.set_global_name("pi", std::f64::consts::PI.into());
|
|
vm.set_global_name("egamma", 0.577_215_664_901_532_9.into());
|
|
vm.set_global_name("phi", 1.618_033_988_749_895.into());
|
|
|
|
vm.set_global_name("inf", (f64::INFINITY).into());
|
|
vm.set_global_name("NaN", (f64::NAN).into());
|
|
vm.set_global_name("infi", Complex64::new(0.0,f64::INFINITY).into());
|
|
vm.set_global_name("NaNi", Complex64::new(0.0,f64::NAN).into());
|
|
|
|
vm.set_global_name("bin", bin().into());
|
|
vm.set_global_name("sex", sex().into());
|
|
vm.set_global_name("oct", oct().into());
|
|
vm.set_global_name("hex", hex().into());
|
|
vm.set_global_name("to_radix", to_radix().into());
|
|
vm.set_global_name("to_radix_upper", to_radix_upper().into());
|
|
vm.set_global_name("from_radix", from_radix().into());
|
|
|
|
vm.set_global_name("gcd", gcd().into());
|
|
vm.set_global_name("lcm", lcm().into());
|
|
vm.set_global_name("isqrt", isqrt().into());
|
|
vm.set_global_name("isprime", isprime().into());
|
|
vm.set_global_name("factors", factors().into());
|
|
|
|
vm.set_global_name("min", min().into());
|
|
vm.set_global_name("max", max().into());
|
|
vm.set_global_name("floor", floor().into());
|
|
vm.set_global_name("ceil", ceil().into());
|
|
vm.set_global_name("round", round().into());
|
|
vm.set_global_name("trunc", trunc().into());
|
|
vm.set_global_name("fract", fract().into());
|
|
vm.set_global_name("sign", sign().into());
|
|
|
|
vm.set_global_name("signum", signum().into());
|
|
vm.set_global_name("classify", classify().into());
|
|
vm.set_global_name("isnan", isnan().into());
|
|
vm.set_global_name("isfinite", isfinite().into());
|
|
vm.set_global_name("isinfinite", isinfinite().into());
|
|
vm.set_global_name("float_to_bits", float_to_bits().into());
|
|
vm.set_global_name("float_of_bits", float_of_bits().into());
|
|
|
|
vm.set_global_name("numer", numer().into());
|
|
vm.set_global_name("denom", denom().into());
|
|
|
|
vm.set_global_name("re", re().into());
|
|
vm.set_global_name("im", im().into());
|
|
vm.set_global_name("arg", arg().into());
|
|
vm.set_global_name("abs", abs().into());
|
|
vm.set_global_name("abs_sq", abs_sq().into());
|
|
|
|
vm.set_global_name("sqrt", sqrt().into());
|
|
vm.set_global_name("cbrt", cbrt().into());
|
|
vm.set_global_name("ln", ln().into());
|
|
vm.set_global_name("log2", log2().into());
|
|
vm.set_global_name("exp", exp().into());
|
|
vm.set_global_name("exp2", exp2().into());
|
|
|
|
vm.set_global_name("sin", sin().into());
|
|
vm.set_global_name("cos", cos().into());
|
|
vm.set_global_name("tan", tan().into());
|
|
vm.set_global_name("asin", asin().into());
|
|
vm.set_global_name("acos", acos().into());
|
|
vm.set_global_name("atan", atan().into());
|
|
vm.set_global_name("sinh", sinh().into());
|
|
vm.set_global_name("cosh", cosh().into());
|
|
vm.set_global_name("tanh", tanh().into());
|
|
vm.set_global_name("asinh", asinh().into());
|
|
vm.set_global_name("acosh", acosh().into());
|
|
vm.set_global_name("atanh", atanh().into());
|
|
vm.set_global_name("atanh", atanh().into());
|
|
vm.set_global_name("atan2", atan2().into());
|
|
}
|
|
|
|
//
|
|
// base conversions
|
|
//
|
|
|
|
fn to_radix_inner(n: i64, radix: u32, upper: bool) -> LString {
|
|
let mut result = vec![];
|
|
let mut begin = 0;
|
|
|
|
let mut x;
|
|
if n < 0 {
|
|
result.push('-' as u32 as u8);
|
|
begin = 1;
|
|
x = (-n) as u64;
|
|
} else {
|
|
x = n as u64;
|
|
}
|
|
|
|
loop {
|
|
let m = x % (radix as u64);
|
|
x /= radix as u64;
|
|
|
|
let mut c = char::from_digit(m as u32, radix).unwrap();
|
|
if upper { c.make_ascii_uppercase(); }
|
|
result.push(c as u8);
|
|
if x == 0 {
|
|
break;
|
|
}
|
|
}
|
|
result[begin..].reverse();
|
|
LString::from(result)
|
|
}
|
|
|
|
|
|
#[native_func(1)]
|
|
pub fn bin(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
let Value::Int(x) = x else {
|
|
throw!(*SYM_TYPE_ERROR, "bin expected integer argument, got {x:#}")
|
|
};
|
|
Ok(to_radix_inner(x, 2, false).into())
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn sex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
let Value::Int(x) = x else {
|
|
throw!(*SYM_TYPE_ERROR, "sex expected integer argument, got {x:#}")
|
|
};
|
|
Ok(to_radix_inner(x, 6, false).into())
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn oct(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
let Value::Int(x) = x else {
|
|
throw!(*SYM_TYPE_ERROR, "oct expected integer argument, got {x:#}")
|
|
};
|
|
Ok(to_radix_inner(x, 8, false).into())
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn hex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
let Value::Int(x) = x else {
|
|
throw!(*SYM_TYPE_ERROR, "hex expected integer argument, got {x:#}")
|
|
};
|
|
Ok(to_radix_inner(x, 16, false).into())
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x, radix] = unpack_args!(args);
|
|
let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else {
|
|
throw!(*SYM_TYPE_ERROR, "to_radix expected integer arguments, got {x:#} and {radix:#}")
|
|
};
|
|
if *radix < 2 || *radix > 36 {
|
|
throw!(*SYM_TYPE_ERROR, "to_radix expected radix in range 0..=36")
|
|
}
|
|
Ok(to_radix_inner(*x, *radix as u32, false).into())
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x, radix] = unpack_args!(args);
|
|
let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else {
|
|
throw!(*SYM_TYPE_ERROR, "to_radix_upper expected integer arguments, got {x:#} and {radix:#}")
|
|
};
|
|
if *radix < 2 || *radix > 36 {
|
|
throw!(*SYM_TYPE_ERROR, "to_radix_upper expected radix in range 0..=36")
|
|
}
|
|
Ok(to_radix_inner(*x, *radix as u32, true).into())
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, s, radix] = unpack_args!(args);
|
|
let (Value::String(s), Value::Int(radix)) = (&s, &radix) else {
|
|
throw!(*SYM_TYPE_ERROR, "from_radix expected string and integer arguments, got {s:#} and {radix:#}")
|
|
};
|
|
if *radix < 2 || *radix > 36 {
|
|
throw!(*SYM_TYPE_ERROR, "from_radix expected radix in range 0..=36")
|
|
}
|
|
match parse_int(s.as_ref(), *radix as u32) {
|
|
Ok(v) => Ok(v.into()),
|
|
Err(_) => throw!(*SYM_TYPE_ERROR, "string was not a valid integer in given radix"),
|
|
}
|
|
}
|
|
|
|
//
|
|
// integer operations
|
|
//
|
|
|
|
fn isqrt_inner(mut n: i64) -> i64 {
|
|
assert!(n >= 0, "isqrt input should be nonnegative");
|
|
if n < 2 { return n }
|
|
|
|
let mut c = 0;
|
|
let mut d = 1 << 62;
|
|
|
|
while d > n {
|
|
d >>= 2;
|
|
}
|
|
|
|
while d != 0 {
|
|
if n >= c + d {
|
|
n -= c + d;
|
|
c = (c >> 1) + d;
|
|
}
|
|
else {
|
|
c >>= 1;
|
|
}
|
|
d >>= 2;
|
|
}
|
|
c
|
|
}
|
|
|
|
pub fn gcd_inner(a: i64, b: i64) -> i64 {
|
|
let (mut a, mut b) = (a.abs(), b.abs());
|
|
if a == 0 {
|
|
return b
|
|
}
|
|
if b == 0 {
|
|
return a
|
|
}
|
|
|
|
let az = a.trailing_zeros();
|
|
a >>= az;
|
|
let bz = b.trailing_zeros();
|
|
b >>= bz;
|
|
let z = az.min(bz);
|
|
|
|
loop {
|
|
if a > b {
|
|
std::mem::swap(&mut a, &mut b);
|
|
}
|
|
b -= a;
|
|
if b == 0 {
|
|
return a << z;
|
|
}
|
|
b >>= b.trailing_zeros();
|
|
}
|
|
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
let Value::Int(x) = x else {
|
|
throw!(*SYM_TYPE_ERROR, "isqrt expected integer argument, got {x:#}")
|
|
};
|
|
if x < 0 {
|
|
throw!(*SYM_TYPE_ERROR, "isqrt: argument must be positive")
|
|
}
|
|
Ok(isqrt_inner(x).into())
|
|
}
|
|
|
|
|
|
#[native_func(1)]
|
|
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
let Value::Int(x) = x else {
|
|
throw!(*SYM_TYPE_ERROR, "isprime expected integer argument, got {x:#}")
|
|
};
|
|
if x < 2 {
|
|
return Ok(false.into())
|
|
}
|
|
for p in [2, 3, 5, 7] {
|
|
if x % p == 0 {
|
|
return Ok((x == p).into())
|
|
}
|
|
}
|
|
if x < 11 {
|
|
return Ok(false.into())
|
|
}
|
|
let lim = isqrt_inner(x);
|
|
let mut i = 12;
|
|
while i <= lim+1 {
|
|
if x % (i - 1) == 0 { return Ok(false.into()) }
|
|
if x % (i + 1) == 0 { return Ok(false.into()) }
|
|
i += 6;
|
|
}
|
|
Ok(true.into())
|
|
}
|
|
|
|
#[native_func(2..)]
|
|
pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let ([_, x, y], rest) = unpack_varargs!(args);
|
|
let Value::Int(x) = x else {
|
|
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}")
|
|
};
|
|
let Value::Int(y) = y else {
|
|
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}")
|
|
};
|
|
let mut g = gcd_inner(x, y);
|
|
for a in rest {
|
|
let Value::Int(a) = a else {
|
|
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {a:#}")
|
|
};
|
|
g = gcd_inner(g, a);
|
|
}
|
|
Ok(g.into())
|
|
}
|
|
|
|
#[native_func(2..)]
|
|
pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let ([_, x, y], rest) = unpack_varargs!(args);
|
|
let Value::Int(x) = x else {
|
|
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}")
|
|
};
|
|
let Value::Int(y) = y else {
|
|
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}")
|
|
};
|
|
let mut g = gcd_inner(x, y);
|
|
let mut prod = y;
|
|
for a in rest {
|
|
let Value::Int(a) = a else {
|
|
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {a:#}")
|
|
};
|
|
prod *= a;
|
|
g = gcd_inner(g, a);
|
|
}
|
|
Ok((x/g * prod).into())
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
let Value::Int(mut x) = x else {
|
|
throw!(*SYM_TYPE_ERROR, "factords expected integer argument, got {x:#}")
|
|
};
|
|
let mut factors = Vec::new();
|
|
if x <= 1 {
|
|
return Ok(factors.into())
|
|
}
|
|
while x & 1 == 0 {
|
|
x >>= 1;
|
|
factors.push(Value::Int(2));
|
|
}
|
|
while x % 3 == 0 {
|
|
x /= 3;
|
|
factors.push(Value::Int(3));
|
|
}
|
|
let mut i = 5;
|
|
while x >= i*i {
|
|
while x % i == 0 {
|
|
x /= i;
|
|
factors.push(Value::Int(i));
|
|
}
|
|
i += 2;
|
|
while x % i == 0 {
|
|
x /= i;
|
|
factors.push(Value::Int(i));
|
|
}
|
|
i += 4;
|
|
}
|
|
if x > 1 {
|
|
factors.push(Value::Int(x));
|
|
}
|
|
Ok(factors.into())
|
|
}
|
|
|
|
|
|
//
|
|
// numeric operations
|
|
//
|
|
|
|
#[native_func(1..)]
|
|
pub fn min(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let ([_, mut x], rest) = unpack_varargs!(args);
|
|
for val in rest {
|
|
if val < x {
|
|
x = val;
|
|
}
|
|
}
|
|
Ok(x)
|
|
}
|
|
|
|
#[native_func(1..)]
|
|
pub fn max(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let ([_, mut x], rest) = unpack_varargs!(args);
|
|
for val in rest {
|
|
if val > x {
|
|
x = val;
|
|
}
|
|
}
|
|
Ok(x)
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn floor(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
match x {
|
|
Value::Int(x) => Ok(Value::Int(x)),
|
|
Value::Float(x) => Ok(Value::Float(x.floor())),
|
|
Value::Ratio(x) => Ok(Value::Ratio(x.floor())),
|
|
x => throw!(*SYM_TYPE_ERROR, "floor expected real argument, got {x:#}"),
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn ceil(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
match x {
|
|
Value::Int(x) => Ok(Value::Int(x)),
|
|
Value::Float(x) => Ok(Value::Float(x.ceil())),
|
|
Value::Ratio(x) => Ok(Value::Ratio(x.ceil())),
|
|
x => throw!(*SYM_TYPE_ERROR, "ceil expected real argument, got {x:#}"),
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn round(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
match x {
|
|
Value::Int(x) => Ok(Value::Int(x)),
|
|
Value::Float(x) => Ok(Value::Float(x.round())),
|
|
Value::Ratio(x) => Ok(Value::Ratio(x.round())),
|
|
x => throw!(*SYM_TYPE_ERROR, "round expected real argument, got {x:#}"),
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn trunc(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
match x {
|
|
Value::Int(x) => Ok(Value::Int(x)),
|
|
Value::Float(x) => Ok(Value::Float(x.trunc())),
|
|
Value::Ratio(x) => Ok(Value::Ratio(x.trunc())),
|
|
x => throw!(*SYM_TYPE_ERROR, "trunc expected real argument, got {x:#}"),
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn fract(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
match x {
|
|
Value::Int(_) => Ok(Value::Int(0)),
|
|
Value::Float(x) => Ok(Value::Float(x.fract())),
|
|
Value::Ratio(x) => Ok(Value::Ratio(x.fract())),
|
|
x => throw!(*SYM_TYPE_ERROR, "fract expected real argument, got {x:#}"),
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn sign(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
match x {
|
|
Value::Int(x) => Ok(Value::Int(x.signum())),
|
|
Value::Float(x) => Ok(Value::Float(if x == 0.0 { 0.0 } else { x.signum() })),
|
|
Value::Ratio(x) => match x.cmp(&0.into()) {
|
|
Ordering::Greater => Ok(Value::Ratio(1.into())),
|
|
Ordering::Less => Ok(Value::Ratio((-1).into())),
|
|
Ordering::Equal => Ok(Value::Ratio(0.into())),
|
|
}
|
|
x => throw!(*SYM_TYPE_ERROR, "sign expected real argument, got {x:#}"),
|
|
}
|
|
}
|
|
|
|
//
|
|
// floating-point operations
|
|
//
|
|
|
|
|
|
#[native_func(1)]
|
|
pub fn signum(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
match to_floaty(x) {
|
|
Value::Float(x) => Ok(Value::Float(x.signum())),
|
|
x => throw!(*SYM_TYPE_ERROR, "signum expected real argument, got {x:#}"),
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn classify(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
let x = to_floaty(x);
|
|
let Value::Float(x) = x else {
|
|
throw!(*SYM_TYPE_ERROR, "classify expected real argument, got {x:#}")
|
|
};
|
|
Ok(match x.classify() {
|
|
std::num::FpCategory::Nan => *SYM_NAN,
|
|
std::num::FpCategory::Infinite => *SYM_INFINITE,
|
|
std::num::FpCategory::Zero => *SYM_ZERO,
|
|
std::num::FpCategory::Subnormal => *SYM_SUBNORMAL,
|
|
std::num::FpCategory::Normal => *SYM_NORMAL,
|
|
}.into())
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn isnan(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, v] = unpack_args!(args);
|
|
match v {
|
|
Value::Int(_) | Value::Ratio(_) => Ok(false.into()),
|
|
Value::Float(x) => Ok(x.is_nan().into()),
|
|
Value::Complex(z) => Ok(z.is_nan().into()),
|
|
v => throw!(*SYM_TYPE_ERROR, "isnan expected numeric argument, got {v:#}"),
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn isfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, v] = unpack_args!(args);
|
|
match v {
|
|
Value::Int(_) | Value::Ratio(_) => Ok(true.into()),
|
|
Value::Float(x) => Ok(x.is_finite().into()),
|
|
Value::Complex(z) => Ok(z.is_finite().into()),
|
|
v => throw!(*SYM_TYPE_ERROR, "isfinite expected numeric argument, got {v:#}"),
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn isinfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, v] = unpack_args!(args);
|
|
match v {
|
|
Value::Int(_) | Value::Ratio(_) => Ok(false.into()),
|
|
Value::Float(x) => Ok(x.is_infinite().into()),
|
|
Value::Complex(z) => Ok(z.is_infinite().into()),
|
|
v => throw!(*SYM_TYPE_ERROR, "isinfinite expected numeric argument, got {v:#}"),
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn float_to_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, val] = unpack_args!(args);
|
|
let Value::Float(f) = val else {
|
|
throw!(*SYM_TYPE_ERROR, "float_to_bits expected float argument, got {val:#}")
|
|
};
|
|
Ok(Value::Int(f.to_bits() as i64))
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn float_of_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, val] = unpack_args!(args);
|
|
let Value::Int(i) = val else {
|
|
throw!(*SYM_TYPE_ERROR, "float_of_bits expected integer argument, got {val:#}")
|
|
};
|
|
Ok(Value::Float(f64::from_bits(i as u64)))
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// rational operations
|
|
//
|
|
|
|
|
|
#[native_func(1)]
|
|
pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, v] = unpack_args!(args);
|
|
match v {
|
|
Value::Int(x) => Ok(Value::Int(x)),
|
|
Value::Ratio(x) => Ok(Value::Int(*x.numer())),
|
|
v => throw!(*SYM_TYPE_ERROR, "numer expected rational argument, got {v:#}"),
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, v] = unpack_args!(args);
|
|
match v {
|
|
Value::Int(_) => Ok(Value::Int(1)),
|
|
Value::Ratio(x) => Ok(Value::Int(*x.denom())),
|
|
v => throw!(*SYM_TYPE_ERROR, "denom expected rational argument, got {v:#}"),
|
|
}
|
|
}
|
|
|
|
//
|
|
// complex operations
|
|
//
|
|
|
|
|
|
|
|
#[native_func(1)]
|
|
pub fn re(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, v] = unpack_args!(args);
|
|
match v {
|
|
Value::Int(x) => Ok(Value::Int(x)),
|
|
Value::Ratio(x) => Ok(Value::Ratio(x)),
|
|
Value::Float(x) => Ok(Value::Float(x)),
|
|
Value::Complex(z) => Ok(Value::Float(z.re)),
|
|
v => throw!(*SYM_TYPE_ERROR, "re expected numeric argument, got {v:#}"),
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn im(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, v] = unpack_args!(args);
|
|
match to_complex(v) {
|
|
Value::Int(_) => Ok(Value::Int(0)),
|
|
Value::Ratio(_) => Ok(Value::Ratio(0.into())),
|
|
Value::Float(_) => Ok(Value::Float(0.0)),
|
|
Value::Complex(z) => Ok(Value::Float(z.im)),
|
|
v => throw!(*SYM_TYPE_ERROR, "im expected numeric argument, got {v:#}"),
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn arg(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, v] = unpack_args!(args);
|
|
match to_complex(v) {
|
|
Value::Complex(z) => Ok(Value::Float(z.arg())),
|
|
v => throw!(*SYM_TYPE_ERROR, "im expected numeric argument, got {v:#}"),
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn abs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
match x {
|
|
Value::Int(x) => Ok(Value::Int(x.abs())),
|
|
Value::Ratio(x) => Ok(Value::Ratio(if x < 0.into() { -x } else { x })),
|
|
Value::Float(x) => Ok(Value::Float(x.abs())),
|
|
Value::Complex(x) => Ok(Value::Float(x.norm())),
|
|
x => throw!(*SYM_TYPE_ERROR, "abs expected numeric argument, got {x:#}"),
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, x] = unpack_args!(args);
|
|
match x {
|
|
Value::Int(x) => Ok(Value::Int(x * x)),
|
|
Value::Ratio(x) => Ok(Value::Ratio(x * x)),
|
|
Value::Float(x) => Ok(Value::Float(x * x)),
|
|
Value::Complex(x) => Ok(Value::Float(x.norm_sqr())),
|
|
x => throw!(*SYM_TYPE_ERROR, "abs_sq expected numeric argument, got {x:#}"),
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// continuous operations
|
|
//
|
|
|
|
|
|
macro_rules! float_func {
|
|
($name:ident) => {
|
|
#[native_func(1)]
|
|
pub fn $name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, v] = unpack_args!(args);
|
|
match to_floaty(v) {
|
|
Value::Float(x) => Ok(Value::Float(x.$name())),
|
|
Value::Complex(z) => Ok(Value::Complex(z.$name())),
|
|
v => throw!(*SYM_TYPE_ERROR,
|
|
"{} expected numeric argument, got {v:#}", stringify!($name)),
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
float_func!(sqrt);
|
|
float_func!(cbrt);
|
|
float_func!(ln);
|
|
float_func!(log2);
|
|
float_func!(exp);
|
|
float_func!(exp2);
|
|
|
|
float_func!(sin);
|
|
float_func!(cos);
|
|
float_func!(tan);
|
|
float_func!(asin);
|
|
float_func!(acos);
|
|
float_func!(atan);
|
|
float_func!(sinh);
|
|
float_func!(cosh);
|
|
float_func!(tanh);
|
|
float_func!(asinh);
|
|
float_func!(acosh);
|
|
float_func!(atanh);
|
|
|
|
#[native_func(2)]
|
|
pub fn atan2(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, y, x] = unpack_args!(args);
|
|
match (to_floaty(y), to_floaty(x)) {
|
|
(Value::Float(y), Value::Float(x))
|
|
=> Ok(Value::Float(x.atan2(y))),
|
|
(y,x) => throw!(*SYM_TYPE_ERROR,
|
|
"atan2 expected real arguments, got {y:#} and {x:#}"),
|
|
}
|
|
}
|
|
|