use crate::context::{ArgNotFound, Args, Context, Ret};
use crate::{Float, Nil, Number, Type};
use alloc::boxed::Box;
use core::f64::consts::PI;
pub fn abs<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let arg = cx.arg(1);
match arg.as_int(false) {
Some(mut n) => {
if n < 0 {
n = 0u64.wrapping_sub(n as u64) as i64;
}
cx.push(n)?;
}
None => cx.push(arg.to_float()?.0.abs())?,
}
Ok(cx.into())
}
pub fn acos<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let Float(arg) = cx.arg(1).to_float()?;
cx.push(arg.acos())?;
Ok(cx.into())
}
pub fn asin<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let Float(arg) = cx.arg(1).to_float()?;
cx.push(arg.asin())?;
Ok(cx.into())
}
pub fn atan<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let Float(y) = cx.arg(1).to_float()?;
let Float(x) = cx.arg(2).to_nilable_float(false)?.unwrap_or(Float(1.0));
cx.push(y.atan2(x))?;
Ok(cx.into())
}
pub fn ceil<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let arg = cx.arg(1);
match arg.as_int(false) {
Some(v) => cx.push(v)?,
None => {
let Float(arg) = arg.to_float()?;
cx.push(pushnumint(arg.ceil().into()))?;
}
}
Ok(cx.into())
}
pub fn cos<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let Float(arg) = cx.arg(1).to_float()?;
cx.push(arg.cos())?;
Ok(cx.into())
}
pub fn deg<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let Float(arg) = cx.arg(1).to_float()?;
cx.push(arg * (180.0 / PI))?;
Ok(cx.into())
}
pub fn exp<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let Float(arg) = cx.arg(1).to_float()?;
cx.push(arg.exp())?;
Ok(cx.into())
}
pub fn floor<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let v = cx.arg(1);
let r = if v.is_int() == Some(true) {
let mut r = cx.into_results(1);
r.truncate(1);
r
} else {
cx.push(pushnumint(v.to_float()?.0.floor()))?;
cx.into()
};
Ok(r)
}
pub fn fmod<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let x = cx.arg(1);
let y = cx.arg(2);
match (x.as_int(false), y.as_int(false)) {
(Some(x), Some(d)) => {
if (d as u64).wrapping_add(1) <= 1 {
if d == 0 {
return Err(y.error("zero"));
}
cx.push(0)?;
} else {
cx.push(x % d)?;
}
}
_ => {
let Float(x) = x.to_float()?;
let Float(y) = y.to_float()?;
cx.push(libm::fmod(x, y))?;
}
}
Ok(cx.into())
}
pub fn log<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let Float(v) = cx.arg(1).to_float()?;
match cx.arg(2).to_nilable_float(false)? {
Some(Float(2.0)) => cx.push(v.log2())?,
Some(Float(10.0)) => cx.push(v.log10())?,
Some(Float(b)) => cx.push(v.log(b))?,
None => cx.push(libm::log(v.into()))?,
}
Ok(cx.into())
}
pub fn max<D>(cx: Context<D, Args>) -> Result<Context<D, Ret>, Box<dyn core::error::Error>> {
let mut r = cx.arg(1).exists()?;
for i in 2..=cx.args() {
let v = cx.arg(i);
if cx.is_value_lt(&r, &v)? {
r = v;
}
}
cx.push(r)?;
Ok(cx.into())
}
pub fn min<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let mut r = cx.arg(1).exists()?;
for i in 2..=cx.args() {
let v = cx.arg(i);
if cx.is_value_lt(&v, &r)? {
r = v;
}
}
cx.push(r)?;
Ok(cx.into())
}
pub fn modf<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let v = cx.arg(1);
if let Some(v) = v.as_int(false) {
cx.push(v)?;
cx.push(0.0)?;
} else {
let Float(n) = v.to_float()?;
let ip = if n < 0.0 { n.ceil() } else { n.floor() };
cx.push(pushnumint(ip))?;
cx.push(if n == ip { 0.0 } else { n - ip })?;
}
Ok(cx.into())
}
pub fn rad<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let Float(arg) = cx.arg(1).to_float()?;
cx.push(arg * (PI / 180.0))?;
Ok(cx.into())
}
pub fn sin<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let Float(v) = cx.arg(1).to_float()?;
cx.push(v.sin())?;
Ok(cx.into())
}
pub fn sqrt<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let Float(arg) = cx.arg(1).to_float()?;
cx.push(arg.sqrt())?;
Ok(cx.into())
}
pub fn tan<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let Float(v) = cx.arg(1).to_float()?;
cx.push(v.tan())?;
Ok(cx.into())
}
pub fn tointeger<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let n = cx.arg(1);
match n.as_int(true) {
Some(n) => cx.push(n)?,
None => {
n.exists()?;
cx.push(Nil)?;
}
}
Ok(cx.into())
}
pub fn r#type<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let v = cx.arg(1);
if v.ty().ok_or_else(|| v.error(ArgNotFound))? == Type::Number {
if v.is_int() == Some(true) {
cx.push_str("integer")?;
} else {
cx.push_str("float")?;
}
} else {
cx.push(Nil)?;
}
Ok(cx.into())
}
pub fn ult<A>(cx: Context<A, Args>) -> Result<Context<A, Ret>, Box<dyn core::error::Error>> {
let a = cx.arg(1).to_int()?;
let b = cx.arg(2).to_int()?;
cx.push((a as u64) < (b as u64))?;
Ok(cx.into())
}
#[inline(always)]
fn pushnumint(d: f64) -> Number {
if d >= i64::MIN as f64 && d < -(i64::MIN as f64) {
Number::Int(f64::from(d) as i64)
} else {
Number::Float(d.into())
}
}