use alloc::{vec::Vec, vec};
use num_integer::Integer;
use rust_decimal::{MathematicalOps, Decimal};
use crate::{Number, error::MathsError};
use super::{structured::{EvaluationSettings, AngleUnit}, unstructured::Serializable};
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Function {
Sine,
Cosine,
GreatestCommonDenominator,
}
impl Function {
pub fn render_name(&self) -> &'static str {
match self {
Self::Sine => "sin",
Self::Cosine => "cos",
Self::GreatestCommonDenominator => "gcd",
}
}
pub fn argument_count(&self) -> usize {
match self {
Self::Sine | Self::Cosine => 1,
Self::GreatestCommonDenominator => 2,
}
}
pub fn evaluate(&self, arguments: &[Number], settings: &EvaluationSettings) -> Result<Number, MathsError> {
if arguments.len() != self.argument_count() {
panic!("rbop function {:?} expected {} arguments, but got {}", self, self.argument_count(), arguments.len());
}
match self {
Self::Sine | Self::Cosine => {
let mut target = arguments[0].to_decimal();
if settings.angle_unit == AngleUnit::Degree {
target *= Decimal::PI / Decimal::from(180)
}
Ok(Number::Decimal(match self {
Self::Sine => target.sin(),
Self::Cosine => target.cos(),
_ => unreachable!()
}))
},
Self::GreatestCommonDenominator => {
let int_a = if let Some(x) = arguments[0].to_whole() { x } else {
return Ok(Number::Decimal(Decimal::ONE))
};
let int_b = if let Some(x) = arguments[1].to_whole() { x } else {
return Ok(Number::Decimal(Decimal::ONE))
};
Ok(int_a.gcd(&int_b).into())
}
}
}
}
impl Serializable for Function {
fn serialize(&self) -> Vec<u8> {
vec![match self {
Function::Sine => 1,
Function::Cosine => 2,
Function::GreatestCommonDenominator => 3,
}]
}
fn deserialize(bytes: &mut dyn Iterator<Item = u8>) -> Option<Self> {
match bytes.next() {
Some(1) => Some(Function::Sine),
Some(2) => Some(Function::Cosine),
Some(3) => Some(Function::GreatestCommonDenominator),
_ => None,
}
}
}