use core::{
fmt::Display,
marker::ConstParamTy,
ops::{Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
};
use num::{traits::Pow, NumCast};
use super::*;
impl ConstParamTy for SI {}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Quantity<V, const UNITS: SI>(pub V);
impl<V: Display, const UNITS: SI> Display for Quantity<V, UNITS> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.fmt(f)?;
f.write_str(" ")?;
UNITS.fmt(f)?;
Ok(())
}
}
impl<V, const UNITS: SI> Quantity<V, UNITS> {
pub fn powi<const EXP: i32>(self) -> Quantity<V::Output, { UNITS.powi(EXP) }>
where
V: Pow<i32>,
{
Quantity(self.0.pow(EXP))
}
pub fn powf<const EXP: (i32, u32)>(self) -> Quantity<V::Output, { UNITS.powf(EXP) }>
where
V: Pow<f64>,
{
Quantity(self.0.pow(EXP.0 as f64 / EXP.1 as f64))
}
pub fn convert_to<const NEW_UNITS: SI>(self) -> Quantity<V, NEW_UNITS>
where
assertion::Bool<{ UNITS.same_dimension(NEW_UNITS) }>: assertion::True,
V: Mul<V, Output = V> + Div<V, Output = V> + NumCast,
{
let scale = UNITS.div(NEW_UNITS).scale;
Quantity(
self.0 * V::from(scale.0).expect("Casting the scale value to type V to work")
/ V::from(scale.1).expect("Casting the scale value to type V to work"),
)
}
}
#[cfg(feature = "dyn")]
impl<V, const UNITS: SI> From<Quantity<V, UNITS>> for crate::DynQuantity<V> {
fn from(value: Quantity<V, UNITS>) -> Self {
crate::DynQuantity(value.0, UNITS)
}
}
pub mod assertion {
pub trait True {}
pub struct Bool<const COND: bool>();
impl True for Bool<true> {}
}
impl<V: Display, const UNITS: SI> Quantity<V, UNITS> {
#[allow(private_bounds)]
pub fn write_as<const AS: &'static str>(
&self,
f: &mut core::fmt::Formatter,
) -> Result<(), core::fmt::Error>
where
assertion::Bool<{ crate::si(AS).const_eq(UNITS) }>: assertion::True,
{
write!(f, "{} {AS}", self.0)
}
#[allow(private_bounds)]
#[cfg(any(feature = "std", test))]
pub fn format_as<const AS: &'static str>(&self) -> std::string::String
where
assertion::Bool<{ crate::si(AS).const_eq(UNITS) }>: assertion::True,
{
format!("{} {AS}", self.0)
}
}
impl<V> Deref for Quantity<V, DIMENSIONLESS> {
type Target = V;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<V> DerefMut for Quantity<V, DIMENSIONLESS> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<R, O, T: Add<R, Output = O>, const UNITS: SI> Add<Quantity<R, UNITS>> for Quantity<T, UNITS> {
type Output = Quantity<O, UNITS>;
fn add(self, rhs: Quantity<R, UNITS>) -> Self::Output {
Quantity(self.0 + rhs.0)
}
}
impl<R, O, T: Sub<R, Output = O>, const UNITS: SI> Sub<Quantity<R, UNITS>> for Quantity<T, UNITS> {
type Output = Quantity<O, UNITS>;
fn sub(self, rhs: Quantity<R, UNITS>) -> Self::Output {
Quantity(self.0 - rhs.0)
}
}
impl<R, T: AddAssign<R>, const UNITS: SI> AddAssign<Quantity<R, UNITS>> for Quantity<T, UNITS> {
fn add_assign(&mut self, rhs: Quantity<R, UNITS>) {
self.0 += rhs.0;
}
}
impl<R, T: SubAssign<R>, const UNITS: SI> SubAssign<Quantity<R, UNITS>> for Quantity<T, UNITS> {
fn sub_assign(&mut self, rhs: Quantity<R, UNITS>) {
self.0 -= rhs.0;
}
}
impl<R, T: MulAssign<R>, const UNITS: SI> MulAssign<Quantity<R, DIMENSIONLESS>>
for Quantity<T, UNITS>
{
fn mul_assign(&mut self, rhs: Quantity<R, DIMENSIONLESS>) {
self.0 *= rhs.0;
}
}
impl<R, T: DivAssign<R>, const UNITS: SI> DivAssign<Quantity<R, DIMENSIONLESS>>
for Quantity<T, UNITS>
{
fn div_assign(&mut self, rhs: Quantity<R, DIMENSIONLESS>) {
self.0 /= rhs.0;
}
}
impl<R, O, T: Mul<R, Output = O>, const LHS_UNITS: SI, const RHS_UNITS: SI>
Mul<Quantity<R, RHS_UNITS>> for Quantity<T, LHS_UNITS>
where
Quantity<O, { SI::mul(LHS_UNITS, RHS_UNITS) }>: Sized,
{
type Output = Quantity<O, { SI::mul(LHS_UNITS, RHS_UNITS) }>;
fn mul(self, rhs: Quantity<R, RHS_UNITS>) -> Self::Output {
Quantity(self.0 * rhs.0)
}
}
impl<R, O, T: Div<R, Output = O>, const LHS_UNITS: SI, const RHS_UNITS: SI>
Div<Quantity<R, RHS_UNITS>> for Quantity<T, LHS_UNITS>
where
Quantity<O, { SI::div(LHS_UNITS, RHS_UNITS) }>: Sized,
{
type Output = Quantity<O, { SI::div(LHS_UNITS, RHS_UNITS) }>;
fn div(self, rhs: Quantity<R, RHS_UNITS>) -> Self::Output {
Quantity(self.0 / rhs.0)
}
}