use float_cmp::{ApproxEq, F64Margin};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::iter::Sum;
use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
pub trait UnitType:
fmt::Debug
+ Copy
+ PartialEq
+ PartialOrd
+ Serialize
+ Add
+ Sub
+ Div
+ Mul<Dimensionless, Output = Self>
+ AddAssign
+ SubAssign
+ Sum
+ ApproxEq<Margin = F64Margin>
+ fmt::Display
{
fn new(value: f64) -> Self;
fn value(&self) -> f64;
fn is_normal(&self) -> bool;
fn is_finite(&self) -> bool;
fn abs(&self) -> Self;
fn max(&self, other: Self) -> Self;
fn min(&self, other: Self) -> Self;
fn total_cmp(&self, other: &Self) -> std::cmp::Ordering;
}
macro_rules! base_unit_struct {
($name:ident) => {
#[derive(
Debug,
Clone,
Copy,
PartialEq,
PartialOrd,
Serialize,
derive_more::Add,
derive_more::Sub,
)]
pub struct $name(pub f64);
impl std::ops::Div<$name> for $name {
type Output = Dimensionless;
fn div(self, rhs: $name) -> Dimensionless {
Dimensionless(self.0 / rhs.0)
}
}
impl std::iter::Sum for $name {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold($name(0.0), |a, b| $name(a.0 + b.0))
}
}
impl AddAssign for $name {
fn add_assign(&mut self, other: Self) {
self.0 += other.0;
}
}
impl SubAssign for $name {
fn sub_assign(&mut self, other: Self) {
self.0 -= other.0;
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl float_cmp::ApproxEq for $name {
type Margin = float_cmp::F64Margin;
fn approx_eq<T: Into<Self::Margin>>(self, other: Self, margin: T) -> bool {
self.0.approx_eq(other.0, margin)
}
}
impl std::ops::Neg for $name {
type Output = $name;
fn neg(self) -> $name {
$name(-self.0)
}
}
impl $name {
pub const EPSILON: $name = $name(f64::EPSILON);
pub fn new(value: f64) -> Self {
$name(value)
}
pub fn value(&self) -> f64 {
self.0
}
pub fn is_normal(&self) -> bool {
self.0.is_normal()
}
pub fn is_finite(&self) -> bool {
self.0.is_finite()
}
pub fn abs(&self) -> Self {
$name(self.0.abs())
}
pub fn max(&self, other: Self) -> Self {
Self(self.0.max(other.0))
}
pub fn min(&self, other: Self) -> Self {
Self(self.0.min(other.0))
}
pub fn total_cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.total_cmp(&other.0)
}
}
impl<'de> Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = f64::deserialize(deserializer)?;
if !value.is_finite() {
Err(serde::de::Error::custom("Value cannot be NaN or infinite"))?;
}
Ok($name(value))
}
}
impl UnitType for $name {
fn new(value: f64) -> Self {
Self::new(value)
}
fn value(&self) -> f64 {
Self::value(&self)
}
fn is_normal(&self) -> bool {
Self::is_normal(&self)
}
fn is_finite(&self) -> bool {
Self::is_finite(&self)
}
fn abs(&self) -> Self {
Self::abs(&self)
}
fn max(&self, other: Self) -> Self {
Self::max(&self, other)
}
fn min(&self, other: Self) -> Self {
Self::min(&self, other)
}
fn total_cmp(&self, other: &Self) -> std::cmp::Ordering {
Self::total_cmp(&self, other)
}
}
};
}
base_unit_struct!(Dimensionless);
impl From<f64> for Dimensionless {
fn from(val: f64) -> Self {
Self(val)
}
}
impl From<Dimensionless> for f64 {
fn from(val: Dimensionless) -> Self {
val.0
}
}
impl Mul for Dimensionless {
type Output = Dimensionless;
fn mul(self, rhs: Self) -> Self::Output {
Dimensionless(self.0 * rhs.0)
}
}
impl Dimensionless {
pub fn powi(self, rhs: i32) -> Self {
Dimensionless(self.0.powi(rhs))
}
}
macro_rules! unit_struct {
($name:ident) => {
base_unit_struct!($name);
impl std::ops::Mul<Dimensionless> for $name {
type Output = $name;
fn mul(self, rhs: Dimensionless) -> $name {
$name(self.0 * rhs.0)
}
}
impl std::ops::Mul<$name> for Dimensionless {
type Output = $name;
fn mul(self, rhs: $name) -> $name {
$name(self.0 * rhs.0)
}
}
impl std::ops::Div<Dimensionless> for $name {
type Output = $name;
fn div(self, rhs: Dimensionless) -> $name {
$name(self.0 / rhs.0)
}
}
};
}
unit_struct!(Money);
unit_struct!(Flow);
unit_struct!(Activity);
unit_struct!(Capacity);
unit_struct!(Year);
unit_struct!(MoneyPerYear);
unit_struct!(MoneyPerFlow);
unit_struct!(MoneyPerCapacity);
unit_struct!(MoneyPerCapacityPerYear);
unit_struct!(MoneyPerActivity);
unit_struct!(ActivityPerCapacity);
unit_struct!(FlowPerActivity);
unit_struct!(FlowPerCapacity);
unit_struct!(CapacityPerYear);
macro_rules! impl_div {
($Lhs:ident, $Rhs:ident, $Out:ident) => {
impl std::ops::Div<$Rhs> for $Lhs {
type Output = $Out;
fn div(self, rhs: $Rhs) -> $Out {
$Out(self.0 / rhs.0)
}
}
impl std::ops::Div<$Out> for $Lhs {
type Output = $Rhs;
fn div(self, rhs: $Out) -> $Rhs {
$Rhs(self.0 / rhs.0)
}
}
impl std::ops::Mul<$Rhs> for $Out {
type Output = $Lhs;
fn mul(self, by: $Rhs) -> $Lhs {
$Lhs(self.0 * by.0)
}
}
impl std::ops::Mul<$Lhs> for $Out {
type Output = $Rhs;
fn mul(self, by: $Lhs) -> $Rhs {
$Rhs(self.0 * by.0)
}
}
impl std::ops::Mul<$Out> for $Rhs {
type Output = $Lhs;
fn mul(self, by: $Out) -> $Lhs {
$Lhs(self.0 * by.0)
}
}
impl std::ops::Mul<$Out> for $Lhs {
type Output = $Rhs;
fn mul(self, by: $Out) -> $Rhs {
$Rhs(self.0 * by.0)
}
}
};
}
impl_div!(Flow, Activity, FlowPerActivity);
impl_div!(Flow, Capacity, FlowPerCapacity);
impl_div!(Money, Year, MoneyPerYear);
impl_div!(Money, Flow, MoneyPerFlow);
impl_div!(Money, Capacity, MoneyPerCapacity);
impl_div!(Money, Activity, MoneyPerActivity);
impl_div!(Activity, Capacity, ActivityPerCapacity);
impl_div!(MoneyPerYear, Capacity, MoneyPerCapacityPerYear);
impl_div!(MoneyPerActivity, FlowPerActivity, MoneyPerFlow);
impl_div!(MoneyPerCapacity, Year, MoneyPerCapacityPerYear);
impl_div!(FlowPerCapacity, ActivityPerCapacity, FlowPerActivity);
impl_div!(Capacity, Year, CapacityPerYear);