use dyn_quantity::{DynQuantity, Unit, UnitsNotEqual};
use crate::{IsQuantityFunction, filter_unary_function};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Polynomial {
coefficients: Vec<DynQuantity<f64>>,
#[cfg_attr(feature = "serde", serde(skip_serializing))]
influencing_factor_unit: Unit,
#[cfg_attr(feature = "serde", serde(skip_serializing))]
coefficients_val: Vec<f64>,
#[cfg_attr(feature = "serde", serde(skip_serializing))]
default_value: DynQuantity<f64>,
}
impl Polynomial {
pub fn new(coefficients: Vec<DynQuantity<f64>>) -> Result<Self, UnitsNotEqual> {
let l = coefficients.len();
let influencing_factor_unit: Unit = if l > 1 {
let last = coefficients[l - 1];
let second_to_last = coefficients[l - 2];
last.unit / second_to_last.unit
} else {
Unit::default()
};
let default_value = match coefficients.last() {
Some(b) => {
let base_unit = b.unit;
for (exponent, c) in coefficients.iter().rev().enumerate().skip(1) {
let res_unit = c.unit * influencing_factor_unit.powi(exponent as i32);
if base_unit != res_unit {
return Err(UnitsNotEqual(base_unit, res_unit));
}
}
b.clone()
}
None => DynQuantity::new(0.0, Unit::default()),
};
let coefficients_val = coefficients.iter().map(|q| q.value).collect();
return Ok(Self {
coefficients,
influencing_factor_unit,
coefficients_val,
default_value,
});
}
pub fn coefficients(&self) -> &[DynQuantity<f64>] {
return self.coefficients.as_slice();
}
pub fn influencing_factor_unit(&self) -> Unit {
return self.influencing_factor_unit;
}
pub fn output_unit(&self) -> Unit {
return self.default_value.unit;
}
}
#[cfg_attr(feature = "serde", typetag::serde)]
impl IsQuantityFunction for Polynomial {
fn call(&self, conditions: &[DynQuantity<f64>]) -> DynQuantity<f64> {
return filter_unary_function(
conditions,
self.influencing_factor_unit,
|input| {
let val =
horner::eval_polynomial(input.value, self.coefficients_val.as_slice()).unwrap();
return DynQuantity::new(val, self.default_value.unit);
},
|| self.default_value,
);
}
fn dyn_eq(&self, other: &dyn IsQuantityFunction) -> bool {
(other as &dyn std::any::Any).downcast_ref::<Self>() == Some(self)
}
}
#[cfg(feature = "serde")]
mod serde_impl {
use super::*;
use serde::de::{Deserialize, Deserializer};
impl<'de> Deserialize<'de> for Polynomial {
fn deserialize<D>(deserializer: D) -> Result<Polynomial, D::Error>
where
D: Deserializer<'de>,
{
#[derive(serde::Deserialize)]
struct PolynomialAlias {
coefficients: Vec<DynQuantity<f64>>,
}
let alias = PolynomialAlias::deserialize(deserializer)?;
Self::new(alias.coefficients).map_err(serde::de::Error::custom)
}
}
}
#[cfg(feature = "serde")]
#[cfg_attr(feature = "serde", typetag::serde)]
impl IsQuantityFunction for crate::ClampedQuantity<Polynomial> {
fn call(&self, conditions: &[DynQuantity<f64>]) -> DynQuantity<f64> {
return self.call_clamped(conditions);
}
fn dyn_eq(&self, other: &dyn IsQuantityFunction) -> bool {
(other as &dyn std::any::Any).downcast_ref::<Self>() == Some(self)
}
}