use dyn_quantity::{DynQuantity, Unit, UnitsNotEqual};
#[cfg(feature = "serde")]
use dyn_quantity::serialize_quantity;
use crate::{IsQuantityFunction, filter_unary_function};
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ExpTerm {
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_quantity"))]
pub amplitude: DynQuantity<f64>,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_quantity"))]
pub exponent: DynQuantity<f64>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Exponential {
terms: Vec<ExpTerm>,
#[cfg_attr(feature = "serde", serde(skip))]
output_unit: Unit,
#[cfg_attr(feature = "serde", serde(skip))]
influencing_factor_unit: Unit,
}
impl Exponential {
pub fn new(terms: Vec<ExpTerm>) -> Result<Self, UnitsNotEqual> {
let influencing_factor_unit: Unit = match terms.first() {
Some(t) => t.exponent.unit.powi(-1),
None => Unit::default(),
};
if let Some((first, second)) = terms.windows(2).find_map(|terms| {
let term1 = terms[0];
let term2 = terms[1];
if term1.amplitude.unit != term2.amplitude.unit {
return Some((term1.amplitude.unit, term2.amplitude.unit));
}
if term1.exponent.unit != term2.exponent.unit {
return Some((term1.exponent.unit, term2.exponent.unit));
}
return None;
}) {
return Err(UnitsNotEqual(first, second));
}
let output_unit = terms
.get(0)
.map(|term| term.amplitude.unit)
.unwrap_or(Default::default());
return Ok(Self {
terms,
output_unit,
influencing_factor_unit,
});
}
pub fn terms(&self) -> &[ExpTerm] {
return &self.terms;
}
pub fn influencing_factor_unit(&self) -> Unit {
return self.influencing_factor_unit;
}
pub fn output_unit(&self) -> Unit {
return self.output_unit;
}
}
#[cfg_attr(feature = "serde", typetag::serde)]
impl IsQuantityFunction for Exponential {
fn call(&self, conditions: &[DynQuantity<f64>]) -> DynQuantity<f64> {
return filter_unary_function(
conditions,
self.influencing_factor_unit,
|input| {
let sum = self
.terms
.iter()
.map(|t| t.amplitude.value * (t.exponent.value * input.value).exp())
.sum::<f64>();
return DynQuantity::new(sum, self.output_unit);
},
|| {
let sum = self.terms.iter().map(|t| t.amplitude.value).sum::<f64>();
return DynQuantity::new(sum, self.output_unit);
},
);
}
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 Exponential {
fn deserialize<D>(deserializer: D) -> Result<Exponential, D::Error>
where
D: Deserializer<'de>,
{
#[derive(serde::Deserialize)]
struct ExponentialAlias {
terms: Vec<ExpTerm>,
}
let alias = ExponentialAlias::deserialize(deserializer)?;
Self::new(alias.terms).map_err(serde::de::Error::custom)
}
}
}
#[cfg(feature = "serde")]
#[cfg_attr(feature = "serde", typetag::serde)]
impl IsQuantityFunction for crate::ClampedQuantity<Exponential> {
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)
}
}