use core::fmt;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub type PowerLawRange = (f64, f64);
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PowerLawDecay {
k: f64,
alpha: f64,
range: Option<PowerLawRange>,
}
#[derive(Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
struct PowerLawSerde {
k: f64,
alpha: f64,
range: Option<PowerLawRange>,
}
impl Serialize for PowerLawDecay {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.validate().map_err(serde::ser::Error::custom)?;
PowerLawSerde {
k: self.k,
alpha: self.alpha,
range: self.range,
}
.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for PowerLawDecay {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = PowerLawSerde::deserialize(deserializer)?;
Self::new(value.k, value.alpha, value.range).map_err(serde::de::Error::custom)
}
}
impl PowerLawDecay {
pub fn new(k: f64, alpha: f64, range: Option<PowerLawRange>) -> Result<Self, PowerLawError> {
let payload = Self { k, alpha, range };
payload.validate()?;
Ok(payload)
}
pub const fn strength(&self) -> f64 {
self.k
}
pub const fn exponent(&self) -> f64 {
self.alpha
}
pub const fn range(&self) -> Option<PowerLawRange> {
self.range
}
pub fn validate(&self) -> Result<(), PowerLawError> {
if !self.k.is_finite() {
return Err(PowerLawError::InvalidStrength { k: self.k });
}
if !self.alpha.is_finite() {
return Err(PowerLawError::InvalidExponent { alpha: self.alpha });
}
if let Some((min, max)) = self.range
&& (!min.is_finite() || !max.is_finite() || min < 0.0 || max < min)
{
return Err(PowerLawError::InvalidRange { min, max });
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum PowerLawError {
InvalidStrength {
k: f64,
},
InvalidExponent {
alpha: f64,
},
InvalidRange {
min: f64,
max: f64,
},
}
impl fmt::Display for PowerLawError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidStrength { k } => {
write!(f, "power-law strength must be finite; got {k}")
}
Self::InvalidExponent { alpha } => {
write!(f, "power-law exponent must be finite; got {alpha}")
}
Self::InvalidRange { min, max } => write!(
f,
"power-law active range must satisfy finite 0 <= min <= max; got min={min}, max={max}"
),
}
}
}
impl std::error::Error for PowerLawError {}