use std::cmp::Ordering;
use std::convert::TryInto;
use rand::{Rng, SeedableRng};
use rand_xoshiro::Xoshiro256StarStar;
use super::StdLib;
use crate::error::{LuaError, Result};
use crate::vm::{BinOp, Numeric, Oper, Value, VM};
pub(super) fn module(stdlib: &mut StdLib) -> Result<()> {
stdlib
.module("math")
.func("abs", abs)?
.func("acos", acos)?
.func("asin", asin)?
.func("atan", atan)?
.func("ceil", ceil)?
.func("cos", cos)?
.func("deg", deg)?
.func("exp", exp)?
.func("floor", floor)?
.func("fmod", fmod)?
.cons("huge", Value::float(HUGE))?
.func("log", log)?
.func("max", max)?
.cons("maxinteger", Value::int(MAX_INTEGER))?
.func("min", min)?
.cons("mininteger", Value::int(MIN_INTEGER))?
.func("modf", modf)?
.cons("pi", Value::float(PI))?
.func("rad", rad)?
.func("random", random)?
.func("randomseed", randomseed)?
.func("sin", sin)?
.func("sqrt", sqrt)?
.func("tan", tan)?
.func("tointeger", tointeger)?
.func("type", r#type)?
.func("ult", ult)?;
Ok(())
}
fn abs(vm: &mut VM) -> Result<Value> {
Ok(Value::Number(match vm.arg_number_coerce(0)? {
Numeric::Integer(i) => Numeric::Integer(i.wrapping_abs()),
Numeric::Float(f) => Numeric::Float(f.abs()),
}))
}
fn acos(vm: &mut VM) -> Result<Value> {
Ok(Value::float(vm.arg_float_coerce(0)?.acos()))
}
fn asin(vm: &mut VM) -> Result<Value> {
Ok(Value::float(vm.arg_float_coerce(0)?.asin()))
}
fn atan(vm: &mut VM) -> Result<Value> {
let y = vm.arg_float_coerce(0)?;
Ok(Value::float(match vm.arg_opt(1) {
Some(v) => y.atan2(v.to_float_coerce()?),
None => y.atan(),
}))
}
fn ceil(vm: &mut VM) -> Result<Value> {
Ok(Value::Number(match vm.arg_number_coerce(0)? {
Numeric::Integer(n) => Numeric::Integer(n),
Numeric::Float(f) => Numeric::from_f64_try_int(f.ceil()),
}))
}
fn cos(vm: &mut VM) -> Result<Value> {
Ok(Value::float(vm.arg_float_coerce(0)?.cos()))
}
fn deg(vm: &mut VM) -> Result<Value> {
Ok(Value::float(vm.arg_float_coerce(0)?.to_degrees()))
}
fn exp(vm: &mut VM) -> Result<Value> {
Ok(Value::float(vm.arg_float_coerce(0)?.exp()))
}
fn floor(vm: &mut VM) -> Result<Value> {
Ok(Value::Number(match vm.arg_number_coerce(0)? {
Numeric::Integer(n) => Numeric::Integer(n),
Numeric::Float(f) => Numeric::from_f64_try_int(f.floor()),
}))
}
fn fmod(vm: &mut VM) -> Result<Value> {
let x = vm.arg_number_coerce(0)?;
let y = vm.arg_number_coerce(1)?;
if y.to_float() == 0.0 {
return err!(LuaError::MathFmodZero);
}
Ok(Value::Number(x.rem(&y)?))
}
const HUGE: f64 = std::f64::INFINITY;
fn log(vm: &mut VM) -> Result<Value> {
let x = vm.arg_float_coerce(0)?;
Ok(Value::float(match vm.arg_opt(1) {
Some(v) => x.log(v.to_float_coerce()?),
None => x.ln(),
}))
}
fn max(vm: &mut VM) -> Result<Value> {
let mut max = vm.arg(0)?;
for val in vm.arg_split(1).into_iter() {
if vm
.bin_op(Oper::Raw(max.clone()), BinOp::Lt, Oper::Raw(val.clone()))?
.is_truthy()
{
max = val;
}
}
Ok(max)
}
const MAX_INTEGER: i64 = std::i64::MAX;
fn min(vm: &mut VM) -> Result<Value> {
let mut min = vm.arg(0)?;
for val in vm.arg_split(1).into_iter() {
if vm
.bin_op(Oper::Raw(val.clone()), BinOp::Lt, Oper::Raw(min.clone()))?
.is_truthy()
{
min = val;
}
}
Ok(min)
}
const MIN_INTEGER: i64 = std::i64::MIN;
fn modf(vm: &mut VM) -> Result<Value> {
let x = vm.arg_float_coerce(0)?;
Ok(Value::Mult(vec![
Value::Number(Numeric::from_f64_try_int(x.trunc())),
Value::float(if x.is_infinite() { 0.0 } else { x.fract() }),
]))
}
const PI: f64 = std::f64::consts::PI;
fn rad(vm: &mut VM) -> Result<Value> {
Ok(Value::float(vm.arg_float_coerce(0)?.to_radians()))
}
fn random(vm: &mut VM) -> Result<Value> {
if vm.arg_len() > 2 {
return err!(LuaError::ArgumentCount);
}
let lower = vm
.arg_opt(0)
.as_ref()
.map(Value::to_int_coerce)
.transpose()?;
let upper = vm
.arg_opt(1)
.as_ref()
.map(Value::to_int_coerce)
.transpose()?;
_random(vm, lower, upper)
}
fn _random(vm: &mut VM, lower: Option<i64>, upper: Option<i64>) -> Result<Value> {
Ok(match lower {
Some(lower) => match upper {
Some(upper) => {
let range = lower..=upper;
if range.is_empty() {
return err!(LuaError::EmptyInterval);
}
Value::int(vm.rng.gen_range(range))
}
None => match lower.cmp(&0) {
Ordering::Greater => Value::int(vm.rng.gen_range(1..=lower)),
Ordering::Equal => Value::int(vm.rng.gen()),
Ordering::Less => panic!(),
},
},
None => Value::float(vm.rng.gen()),
})
}
fn randomseed(vm: &mut VM) -> Result<Value> {
let x = match vm.arg_opt(0) {
Some(v) => v.to_number_coerce()?.to_int()?,
None => _random(vm, Some(0), None)?.to_number()?.to_int()?,
};
let y = match vm.arg_opt(1) {
Some(v) => v.to_number_coerce()?.to_int()?,
None => 0,
};
let seed: Vec<u8> = [
x.to_ne_bytes(),
0xffu64.to_ne_bytes(),
y.to_ne_bytes(),
0u64.to_ne_bytes(),
]
.concat();
vm.rng = Xoshiro256StarStar::from_seed(seed.try_into().unwrap());
for _ in 0..16 {
_random(vm, Some(0), None)?;
}
Ok(Value::Mult(vec![Value::int(x), Value::int(y)]))
}
fn sin(vm: &mut VM) -> Result<Value> {
Ok(Value::float(vm.arg_float_coerce(0)?.sin()))
}
fn sqrt(vm: &mut VM) -> Result<Value> {
Ok(Value::float(vm.arg_float_coerce(0)?.sqrt()))
}
fn tan(vm: &mut VM) -> Result<Value> {
Ok(Value::float(vm.arg_float_coerce(0)?.tan()))
}
fn tointeger(vm: &mut VM) -> Result<Value> {
Ok(match vm.arg(0)?.to_int_coerce() {
Ok(n) => Value::int(n),
Err(..) => Value::Nil,
})
}
fn r#type(vm: &mut VM) -> Result<Value> {
Ok(match vm.arg_opt(0) {
Some(Value::Number(n)) => Value::str(match n {
Numeric::Integer(..) => "integer",
Numeric::Float(..) => "float",
}),
_ => Value::Nil,
})
}
fn ult(vm: &mut VM) -> Result<Value> {
Ok(Value::Bool(
(vm.arg_int_coerce(0)? as u64) < (vm.arg_int_coerce(1)? as u64),
))
}