use std::ops::{Deref, DerefMut};
use crate::{lang::SError, parse_number, Library, SDoc, SNum, SUnits, SVal};
#[derive(Default, Debug)]
pub struct NumberLibrary;
impl Library for NumberLibrary {
fn scope(&self) -> String {
"Number".to_string()
}
fn call(&self, pid: &str, doc: &mut SDoc, name: &str, parameters: &mut Vec<SVal>) -> Result<SVal, SError> {
if parameters.len() > 0 {
match name {
"toString" => {
return Ok(SVal::String(parameters[0].print(doc)));
},
"or" => {
for param in parameters.drain(..) {
if !param.is_empty() {
return Ok(param);
}
}
return Ok(SVal::Null);
},
"parse" => {
return parse_number(¶meters[0].to_string());
},
"parseHex" => {
let mut value = parameters[0].to_string();
if !value.starts_with("0x") { value = format!("0x{}", value); }
return parse_number(&value);
},
"parseOct" => {
let mut value = parameters[0].to_string();
if !value.starts_with("0o") { value = format!("0o{}", value); }
return parse_number(&value);
},
"parseBin" => {
let mut value = parameters[0].to_string();
if !value.starts_with("0b") { value = format!("0b{}", value); }
return parse_number(&value);
},
_ => {}
}
let mut params;
if parameters.len() > 1 {
params = parameters.drain(1..).collect();
} else {
params = Vec::new();
}
match &mut parameters[0] {
SVal::Number(num) => {
return self.operate(pid, doc, name, num, &mut params);
},
SVal::Boxed(val) => {
let mut val = val.lock().unwrap();
let val = val.deref_mut();
match val {
SVal::Number(num) => {
return self.operate(pid, doc, name, num, &mut params);
},
_ => {
return Err(SError::num(pid, &doc, "InvalidArgument", "number argument not found"));
}
}
},
_ => {
return Err(SError::num(pid, &doc, "InvalidArgument", "number argument not found"));
}
}
} else {
return Err(SError::num(pid, &doc, "InvalidArgument", "number argument not found"));
}
}
}
impl NumberLibrary {
pub fn operate(&self, pid: &str, doc: &mut SDoc, name: &str, nval: &mut SNum, parameters: &mut Vec<SVal>) -> Result<SVal, SError> {
match name {
"len" => return Ok(SVal::Number(nval.int().into())),
"units" => return Self::units(nval),
"removeUnits" => return Self::remove_units(nval),
"hasUnits" | "isUnits" => return Self::has_units(nval),
"isAngle" => return Self::is_angle(nval),
"isDegrees" => return Self::is_degrees(nval),
"isPositiveDegrees" => return Self::is_pdegrees(nval),
"isRadians" => return Self::is_radians(nval),
"isPositiveRadians" => return Self::is_pradians(nval),
"isTemperature" | "isTemp" => return Self::is_temp(nval),
"isLength" => return Self::is_length(nval),
"isTime" => return Self::is_time(nval),
"isMass" => return Self::is_mass(nval),
"sqrt" => return Self::sqrt(nval),
"cbrt" => return Self::cbrt(nval),
"abs" => return Self::abs(nval),
"floor" => return Self::floor(nval),
"ceil" => return Self::ceil(nval),
"trunc" => return Self::trunc(nval),
"fract" => return Self::fract(nval),
"signum" => return Self::signum(nval),
"exp" => return Self::exp(nval),
"exp2" => return Self::exp2(nval),
"ln" => return Self::ln(nval),
"sin" => return Self::sin(nval),
"cos" => return Self::cos(nval),
"tan" => return Self::tan(nval),
"asin" => return Self::asin(nval),
"acos" => return Self::acos(nval),
"atan" => return Self::atan(nval),
"sinh" => return Self::sinh(nval),
"cosh" => return Self::cosh(nval),
"tanh" => return Self::tanh(nval),
"asinh" => return Self::asinh(nval),
"acosh" => return Self::acosh(nval),
"atanh" => return Self::atanh(nval),
"pow" => {
if parameters.len() < 1 {
return Err(SError::num(pid, &doc, "pow", "invalid arguments"));
}
let power = parameters.pop().unwrap().unbox();
match &power {
SVal::Number(second) => {
Self::pow(nval, second)
},
SVal::Boxed(val) => {
let val = val.lock().unwrap();
let val = val.deref();
match val {
SVal::Number(second) => {
Self::pow(nval, second)
},
_ => {
Err(SError::num(pid, &doc, "pow", "invalid arguments"))
}
}
},
_ => {
Err(SError::num(pid, &doc, "pow", "invalid arguments"))
}
}
},
"log" => {
if parameters.len() < 1 {
return Err(SError::num(pid, &doc, "log", "base value not found"));
}
let base = parameters.pop().unwrap().unbox();
match &base {
SVal::Number(second) => {
Self::log(nval, second)
},
SVal::Boxed(val) => {
let val = val.lock().unwrap();
let val = val.deref();
match val {
SVal::Number(second) => {
Self::log(nval, second)
},
_ => {
Err(SError::num(pid, &doc, "log", "non-numerical base value not supported"))
}
}
},
_ => {
Err(SError::num(pid, &doc, "log", "non-numerical base value not supported"))
}
}
},
"atan2" => {
if parameters.len() < 1 {
return Err(SError::num(pid, &doc, "atan2", "invalid arguments"));
}
let second = parameters.pop().unwrap().unbox();
match &second {
SVal::Number(second) => {
Self::atan2(nval, second)
},
SVal::Boxed(val) => {
let val = val.lock().unwrap();
let val = val.deref();
match val {
SVal::Number(second) => {
Self::atan2(nval, second)
},
_ => {
Err(SError::num(pid, &doc, "atan2", "non-numerical arguments not supported"))
}
}
},
_ => {
Err(SError::num(pid, &doc, "atan2", "non-numerical arguments not supported"))
}
}
},
"round" => {
if parameters.len() < 1 {
return Self::round(nval);
}
let second = parameters.pop().unwrap().unbox();
match &second {
SVal::Number(second) => {
Self::round2(nval, second)
},
SVal::Boxed(val) => {
let val = val.lock().unwrap();
let val = val.deref();
match val {
SVal::Number(second) => {
Self::round2(nval, second)
},
_ => {
Err(SError::num(pid, &doc, "round", "non-numerical rounding place value not supported"))
}
}
},
_ => {
Err(SError::num(pid, &doc, "round", "non-numerical rounding place value not supported"))
}
}
},
"at" => {
if parameters.len() < 1 {
return Err(SError::num(pid, &doc, "at", "index argument not found"));
}
let second = parameters.pop().unwrap().unbox();
match &second {
SVal::Number(second) => {
let first = nval.int();
let second = second.int();
if second < first {
Ok(SVal::Number(second.into()))
} else {
Ok(SVal::Number(first.into()))
}
},
SVal::Boxed(val) => {
let val = val.lock().unwrap();
let val = val.deref();
match val {
SVal::Number(second) => {
let first = nval.int();
let second = second.int();
if second < first {
Ok(SVal::Number(second.into()))
} else {
Ok(SVal::Number(first.into()))
}
},
_ => {
Err(SError::num(pid, &doc, "at", "non-numerical index not supported"))
}
}
},
_ => {
Err(SError::num(pid, &doc, "at", "non-numerical index not supported"))
}
}
},
"toHexString" => {
let int = nval.int();
Ok(SVal::String(format!("{:X}", int)))
},
"toBinString" => {
let int = nval.int();
Ok(SVal::String(format!("{:b}", int)))
},
"toOctString" => {
let int = nval.int();
Ok(SVal::String(format!("{:o}", int)))
},
_ => {
Err(SError::num(pid, &doc, "NotFound", &format!("{} is not a function in the Number Library", name)))
}
}
}
pub fn units(number: &SNum) -> Result<SVal, SError> {
match number {
SNum::Units(_, units) => Ok(SVal::String(units.to_string())),
_ => Ok(SVal::Null)
}
}
pub fn remove_units(number: &SNum) -> Result<SVal, SError> {
match number {
SNum::Units(val, _) => Ok(SVal::Number(SNum::F64(*val))),
_ => Ok(SVal::Number(number.clone()))
}
}
pub fn has_units(number: &SNum) -> Result<SVal, SError> {
match number {
SNum::Units(_, units) => Ok(SVal::Bool(units.has_units())),
_ => Ok(SVal::Bool(false))
}
}
pub fn is_angle(number: &SNum) -> Result<SVal, SError> {
match number {
SNum::Units(_, units) => Ok(SVal::Bool(units.is_angle())),
_ => Ok(SVal::Bool(false)),
}
}
pub fn is_degrees(number: &SNum) -> Result<SVal, SError> {
match number {
SNum::Units(_, units) => Ok(SVal::Bool(units.is_degrees())),
_ => Ok(SVal::Bool(false)),
}
}
pub fn is_radians(number: &SNum) -> Result<SVal, SError> {
match number {
SNum::Units(_, units) => Ok(SVal::Bool(units.is_radians())),
_ => Ok(SVal::Bool(false)),
}
}
pub fn is_pdegrees(number: &SNum) -> Result<SVal, SError> {
match number {
SNum::Units(_, units) => Ok(SVal::Bool(units.is_degrees() && units.is_positive_angle())),
_ => Ok(SVal::Bool(false)),
}
}
pub fn is_pradians(number: &SNum) -> Result<SVal, SError> {
match number {
SNum::Units(_, units) => Ok(SVal::Bool(units.is_radians() && units.is_positive_angle())),
_ => Ok(SVal::Bool(false)),
}
}
pub fn is_mass(number: &SNum) -> Result<SVal, SError> {
match number {
SNum::Units(_, units) => Ok(SVal::Bool(units.is_mass())),
_ => Ok(SVal::Bool(false))
}
}
pub fn is_time(number: &SNum) -> Result<SVal, SError> {
match number {
SNum::Units(_, units) => Ok(SVal::Bool(units.is_time())),
_ => Ok(SVal::Bool(false))
}
}
pub fn is_temp(number: &SNum) -> Result<SVal, SError> {
match number {
SNum::Units(_, units) => Ok(SVal::Bool(units.is_temperature())),
_ => Ok(SVal::Bool(false))
}
}
pub fn is_length(number: &SNum) -> Result<SVal, SError> {
match number {
SNum::Units(_, units) => Ok(SVal::Bool(units.is_length())),
_ => Ok(SVal::Bool(false))
}
}
pub fn pow(number: &SNum, power: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().powf(power.float()), units)));
}
Ok(SVal::Number(SNum::F64(number.float().powf(power.float()))))
}
pub fn sqrt(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().sqrt(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().sqrt())))
}
pub fn cbrt(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().cbrt(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().cbrt())))
}
pub fn abs(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().abs(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().abs())))
}
pub fn floor(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().floor(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().floor())))
}
pub fn ceil(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().ceil(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().ceil())))
}
pub fn round(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().round(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().round())))
}
pub fn round2(number: &SNum, digits: &SNum) -> Result<SVal, SError> {
let digits = digits.int();
let mut float = number.float();
if digits > 0 {
let mut scale = 1;
for _ in 0..digits {
scale *= 10;
}
float = (float * scale as f64).round()/(scale as f64);
}
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(float, units)));
}
Ok(SVal::Number(SNum::F64(float)))
}
pub fn trunc(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().trunc(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().trunc())))
}
pub fn fract(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().fract(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().fract())))
}
pub fn signum(number: &SNum) -> Result<SVal, SError> {
Ok(SVal::Number(SNum::F64(number.float().signum())))
}
pub fn exp(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().exp(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().exp())))
}
pub fn exp2(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().exp2(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().exp2())))
}
pub fn ln(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().ln(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().ln())))
}
pub fn log(number: &SNum, base: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().log(base.float()), units)));
}
Ok(SVal::Number(SNum::F64(number.float().log(base.float()))))
}
pub fn sin(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
let mut val = number.float(); if units.is_degrees() {
val = SUnits::to_radians(val, SUnits::Degrees);
}
return Ok(SVal::Number(SNum::F64(val.sin())));
}
Ok(SVal::Number(SNum::F64(number.float().sin())))
}
pub fn cos(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
let mut val = number.float();
if units.is_degrees() {
val = SUnits::to_radians(val, SUnits::Degrees);
}
return Ok(SVal::Number(SNum::F64(val.cos())));
}
Ok(SVal::Number(SNum::F64(number.float().cos())))
}
pub fn tan(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
let mut val = number.float();
if units.is_degrees() {
val = SUnits::to_radians(val, SUnits::Degrees);
}
return Ok(SVal::Number(SNum::F64(val.tan())));
}
Ok(SVal::Number(SNum::F64(number.float().tan())))
}
pub fn asin(number: &SNum) -> Result<SVal, SError> {
let val = number.float().asin();
if val.is_nan() || val.is_infinite() {
return Ok(SVal::Null);
}
Ok(SVal::Number(SNum::Units(val, SUnits::Radians)))
}
pub fn acos(number: &SNum) -> Result<SVal, SError> {
let val = number.float().acos();
if val.is_nan() || val.is_infinite() {
return Ok(SVal::Null);
}
Ok(SVal::Number(SNum::Units(val, SUnits::Radians)))
}
pub fn atan(number: &SNum) -> Result<SVal, SError> {
let val = number.float().atan();
if val.is_nan() || val.is_infinite() {
return Ok(SVal::Null);
}
Ok(SVal::Number(SNum::Units(val, SUnits::Radians)))
}
pub fn atan2(number: &SNum, other: &SNum) -> Result<SVal, SError> {
let val = number.float().atan2(other.float());
if val.is_nan() || val.is_infinite() {
return Ok(SVal::Null);
}
Ok(SVal::Number(SNum::Units(val, SUnits::Radians)))
}
pub fn sinh(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().sinh(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().sinh())))
}
pub fn cosh(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().cosh(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().cosh())))
}
pub fn tanh(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().tanh(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().tanh())))
}
pub fn asinh(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().asinh(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().asinh())))
}
pub fn acosh(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().acosh(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().acosh())))
}
pub fn atanh(number: &SNum) -> Result<SVal, SError> {
if let Some(units) = number.get_units() {
return Ok(SVal::Number(SNum::Units(number.float().atanh(), units)));
}
Ok(SVal::Number(SNum::F64(number.float().atanh())))
}
}