use crate::rational::Rational;
use num::{One, ToPrimitive};
use syntree::Span;
use crate::error::ErrorKind::*;
use crate::{Error, Numeric};
type Result<T, E = Error> = std::result::Result<T, E>;
fn one(range: Span<u32>, arguments: Vec<Numeric>) -> Result<Numeric> {
let actual = arguments.len();
let mut it = arguments.into_iter();
match (it.next(), it.next()) {
(Some(first), None) if actual == 1 => Ok(first),
_ => Err(Error::new(
range,
ArgumentMismatch {
expected: 1,
actual,
},
)),
}
}
pub(crate) fn sin(range: Span<u32>, arguments: Vec<Numeric>) -> Result<Numeric> {
let first = one(range, arguments)?;
let value = match first.value.to_f64() {
Some(value) => value.sin(),
None => return Err(Error::new(range, BadArgument { argument: 0 })),
};
let value = Rational::from_f64(value).ok_or_else(|| Error::new(range, NonFinite))?;
Ok(Numeric::new(value, first.unit))
}
pub(crate) fn cos(range: Span<u32>, arguments: Vec<Numeric>) -> Result<Numeric> {
let first = one(range, arguments)?;
let value = match first.value.to_f64() {
Some(value) => value.cos(),
None => return Err(Error::new(range, BadArgument { argument: 0 })),
};
let value = Rational::from_f64(value).ok_or_else(|| Error::new(range, NonFinite))?;
Ok(Numeric::new(value, first.unit))
}
pub(crate) fn round(range: Span<u32>, arguments: Vec<Numeric>) -> Result<Numeric> {
let actual = arguments.len();
let mut it = arguments.into_iter();
let (mut first, second) = match (it.next(), it.next()) {
(Some(first), None) if actual == 1 => (first, 0),
(Some(first), Some(second)) if actual == 2 => (
first,
match second.value.to_i32() {
Some(second) => second,
None => return Err(Error::new(range, BadArgument { argument: 0 })),
},
),
_ => {
return Err(Error::new(
range,
ArgumentMismatch {
expected: if actual == 0 { 1 } else { 2 },
actual,
},
));
}
};
let value = if second >= 0 && first.value.denom().is_one() {
first.value
} else if second == 0 {
first.value.round()
} else {
let ten = Rational::new(10u32, 1u32).pow(second);
first.value *= &ten;
let mut value = first.value.round();
value /= &ten;
value
};
debug_assert!(value.denom().is_one());
Ok(Numeric::new(value, first.unit))
}
pub(crate) fn floor(range: Span<u32>, arguments: Vec<Numeric>) -> Result<Numeric> {
let first = one(range, arguments)?;
let value = first.value.floor();
debug_assert!(value.denom().is_one());
Ok(Numeric::new(value, first.unit))
}
pub(crate) fn ceil(range: Span<u32>, arguments: Vec<Numeric>) -> Result<Numeric> {
let first = one(range, arguments)?;
let value = first.value.ceil();
debug_assert!(value.denom().is_one());
Ok(Numeric::new(value, first.unit))
}