use crate::{Error, Number, TulispContext, TulispObject};
pub(crate) fn add(ctx: &mut TulispContext) {
ctx.defun("sqrt", |val: f64| -> f64 { val.sqrt() });
ctx.defun("isnan", |x: TulispObject| -> Result<bool, Error> {
if !x.floatp() {
return Err(Error::type_mismatch(format!(
"isnan: expected float, got: {x}"
)));
}
Ok(x.as_float()?.is_nan())
});
ctx.defun(
"expt",
|base: Number, exponent: Number| -> Result<Number, Error> {
if let (Number::Int(b), Number::Int(e)) = (base, exponent)
&& e >= 0
&& let Ok(e_u32) = u32::try_from(e)
{
return b.checked_pow(e_u32).map(Number::Int).ok_or_else(|| {
Error::out_of_range(format!("integer overflow: expt {} {}", b, e))
});
}
let b_f = match base {
Number::Int(v) => v as f64,
Number::Float(v) => v,
};
let e_f = match exponent {
Number::Int(v) => v as f64,
Number::Float(v) => v,
};
Ok(Number::Float(b_f.powf(e_f)))
},
);
}
#[cfg(test)]
mod tests {
use crate::{TulispContext, test_utils::eval_assert_equal};
#[test]
fn test_sqrt() {
let mut ctx = TulispContext::new();
eval_assert_equal(&mut ctx, "(sqrt 4.0)", "2.0");
eval_assert_equal(&mut ctx, "(sqrt 0.0)", "0.0");
eval_assert_equal(&mut ctx, "(sqrt 2.25)", "1.5");
eval_assert_equal(&mut ctx, "(sqrt 4)", "2.0");
eval_assert_equal(&mut ctx, "(isnan (sqrt -4))", "t");
}
#[test]
fn test_isnan() {
let mut ctx = TulispContext::new();
eval_assert_equal(&mut ctx, "(isnan (sqrt -1))", "t");
eval_assert_equal(&mut ctx, "(isnan 0.0e+NaN)", "t");
eval_assert_equal(&mut ctx, "(isnan -0.0e+NaN)", "t");
eval_assert_equal(&mut ctx, "(isnan 1.0)", "nil");
eval_assert_equal(&mut ctx, "(isnan 1.0e+INF)", "nil");
assert_eq!(
ctx.eval_string("(isnan 5)").unwrap_err().format(&ctx),
r#"ERR TypeMismatch: isnan: expected float, got: 5
<eval_string>:1.1-1.9: at (isnan 5)
"#
);
}
#[test]
fn test_expt() {
let ctx = &mut TulispContext::new();
eval_assert_equal(ctx, "(expt 2 3)", "8");
eval_assert_equal(ctx, "(expt 5 0)", "1");
eval_assert_equal(ctx, "(expt -5 0)", "1");
eval_assert_equal(ctx, "(expt -2 3)", "-8");
eval_assert_equal(ctx, "(expt 0 2)", "0");
eval_assert_equal(ctx, "(expt 0 0)", "1");
eval_assert_equal(ctx, "(integerp (expt 2 3))", "t");
eval_assert_equal(ctx, "(expt 4 0.5)", "2.0");
eval_assert_equal(ctx, "(expt 9 0.5)", "3.0");
eval_assert_equal(ctx, "(expt 2 -2)", "0.25");
eval_assert_equal(ctx, "(expt -2 -2)", "0.25");
eval_assert_equal(ctx, "(floatp (expt 4 0.5))", "t");
eval_assert_equal(ctx, "(floatp (expt 2 -2))", "t");
eval_assert_equal(ctx, "(numberp (expt 0 -2))", "t");
assert_eq!(
ctx.eval_string("(expt 2 64)").unwrap_err().format(ctx),
r#"ERR OutOfRange: integer overflow: expt 2 64
<eval_string>:1.1-1.11: at (expt 2 64)
"#
);
}
}