use core:: {
fmt:: { self, Debug, Display },
cmp:: { PartialEq, PartialOrd, Ordering },
ops:: { Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Neg },
};
use crate:: {
Dim, Dimension, DynDim,
predefined::dim:: { self, DIMENSIONLESS },
traits::Real,
error,
dimension:: { self, InvDim },
};
use typenum::Integer;
pub struct PhysicalQuantity<R, D> {
pub value: R,
pub dim: D,
}
impl<R> PhysicalQuantity<R, DynDim>
where
R: Real,
{
pub fn recip(self) -> Self {
Self {
value: R::from_int(1).div_ref(&self.value),
dim: dim::DIMENSIONLESS / self.dim,
}
}
}
#[allow(non_camel_case_types)]
impl<R, L, M, T, θ, N, I, J> PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>
where
R: Real,
L: Neg, M: Neg, T: Neg, θ: Neg, N: Neg, I: Neg, J: Neg,
{
pub fn recip(self) -> PhysicalQuantity<R, InvDim<L, M, T, θ, N, I, J>> {
PhysicalQuantity {
value: R::from_int(1).div_ref(&self.value),
dim: InvDim::<L, M, T, θ, N, I, J>::new(),
}
}
}
impl<R, D> Clone for PhysicalQuantity<R, D>
where
D: Dimension,
R: Clone,
{
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
.. *self
}
}
}
impl<R, D> Copy for PhysicalQuantity<R, D>
where
D: Dimension,
R: Copy,
{}
impl<R> From<R> for PhysicalQuantity<R, dim::Dimensionless> {
fn from(value: R) -> Self {
Self {
value,
dim: dim::Dimensionless::new(),
}
}
}
#[allow(non_camel_case_types)]
impl<R, L, M, T, θ, N, I, J> From<PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>>
for PhysicalQuantity<R, DynDim>
where
L: Integer, M: Integer, T: Integer, θ: Integer, N: Integer, I: Integer, J: Integer,
{
fn from(pq: PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>) -> Self {
Self {
value: pq.value,
dim: pq.dim.into(),
}
}
}
#[allow(non_camel_case_types)]
impl<R, L, M, T, θ, N, I, J> TryFrom<PhysicalQuantity<R, DynDim>>
for PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>
where
R: Real,
L: Integer, M: Integer, T: Integer, θ: Integer, N: Integer, I: Integer, J: Integer,
{
type Error = error::Error;
fn try_from(pq: PhysicalQuantity<R, DynDim>) -> Result<Self, Self::Error> {
let dim = Dim::<L, M, T, θ, N, I, J>::new();
if !dimension::is_equal(&dim, &pq.dim) {
let inv = DynDim::from(dim::DIMENSIONLESS) / dim;
if !dimension::is_equal(&inv, &pq.dim) {
Err(error::Error::DimensionMismatch)
} else {
Ok(Self {
value: R::from_int(1).div_ref(&pq.value),
dim,
})
}
} else {
Ok(Self {
value: pq.value,
dim,
})
}
}
}
macro_rules! same_dim_binop_impl {
($trait:ident, $method:ident, $ref_method:ident) => {
#[allow(non_camel_case_types)]
impl<'a, R, L, M, T, θ, N, I, J> $trait<&'a Self> for PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>
where
R: Real,
{
type Output = Self;
fn $method(self, rhs: &'a Self) -> Self::Output {
Self {
value: self.value.$ref_method(&rhs.value),
.. self
}
}
}
#[allow(non_camel_case_types)]
impl<R, L, M, T, θ, N, I, J> $trait<Self> for PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>
where
R: Real,
{
type Output = Self;
fn $method(self, rhs: Self) -> Self::Output {
$trait::$method(self, &rhs)
}
}
#[allow(non_camel_case_types)]
impl<'a, R, L, M, T, θ, N, I, J> $trait<&'a PhysicalQuantity<R, DynDim>>
for PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>
where
L: Integer, M: Integer, T: Integer, θ: Integer, N: Integer, I: Integer, J: Integer,
R: Real,
{
type Output = Self;
fn $method(self, rhs: &'a PhysicalQuantity<R, DynDim>) -> Self::Output {
assert_eq!(self.dim, rhs.dim);
Self {
value: self.value.$ref_method(&rhs.value),
.. self
}
}
}
#[allow(non_camel_case_types)]
impl<R, L, M, T, θ, N, I, J> $trait<PhysicalQuantity<R, DynDim>>
for PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>
where
L: Integer, M: Integer, T: Integer, θ: Integer, N: Integer, I: Integer, J: Integer,
R: Real,
{
type Output = Self;
fn $method(self, rhs: PhysicalQuantity<R, DynDim>) -> Self::Output {
$trait::$method(self, &rhs)
}
}
#[allow(non_camel_case_types)]
impl<'a, R, L, M, T, θ, N, I, J> $trait<&'a PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>>
for PhysicalQuantity<R, DynDim>
where
L: Integer, M: Integer, T: Integer, θ: Integer, N: Integer, I: Integer, J: Integer,
R: Real,
{
type Output = PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>;
fn $method(self, rhs: &'a Self::Output) -> Self::Output {
assert_eq!(self.dim, rhs.dim);
PhysicalQuantity {
value: self.value.$ref_method(&rhs.value),
dim: Dim::<L, M, T, θ, N, I, J>::new(),
}
}
}
#[allow(non_camel_case_types)]
impl<R, L, M, T, θ, N, I, J> $trait<PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>>
for PhysicalQuantity<R, DynDim>
where
L: Integer, M: Integer, T: Integer, θ: Integer, N: Integer, I: Integer, J: Integer,
R: Real,
{
type Output = PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>;
fn $method(self, rhs: Self::Output) -> Self::Output {
$trait::$method(self, &rhs)
}
}
impl<'a, R> $trait<&'a Self> for PhysicalQuantity<R, DynDim>
where
R: Real,
{
type Output = Self;
fn $method(self, rhs: &'a Self) -> Self::Output {
assert_eq!(self.dim, rhs.dim);
Self {
value: self.value.$ref_method(&rhs.value),
.. self
}
}
}
impl<R> $trait<Self> for PhysicalQuantity<R, DynDim>
where
R: Real,
{
type Output = Self;
fn $method(self, rhs: Self) -> Self::Output {
$trait::$method(self, &rhs)
}
}
};
}
macro_rules! binop_impl {
($trait:ident, $method:ident, $ref_method:ident) => {
impl<'a, D0, D1, R> $trait<&'a PhysicalQuantity<R, D1>> for PhysicalQuantity<R, D0>
where
D0: Dimension + $trait<D1>, <D0 as $trait<D1>>::Output: Dimension,
D1: Dimension,
R: Real,
{
type Output = PhysicalQuantity<R, <D0 as $trait<D1>>::Output>;
fn $method(self, rhs: &'a PhysicalQuantity<R, D1>) -> Self::Output {
PhysicalQuantity {
value: self.value.$ref_method(&rhs.value),
dim: self.dim.$method(rhs.dim),
}
}
}
impl<D0, D1, R> $trait<PhysicalQuantity<R, D1>> for PhysicalQuantity<R, D0>
where
D0: Dimension + $trait<D1>, <D0 as $trait<D1>>::Output: Dimension,
D1: Dimension,
R: Real,
{
type Output = PhysicalQuantity<R, <D0 as $trait<D1>>::Output>;
fn $method(self, rhs: PhysicalQuantity<R, D1>) -> Self::Output {
$trait::$method(self, &rhs)
}
}
};
}
same_dim_binop_impl! { Add, add, add_ref }
same_dim_binop_impl! { Sub, sub, sub_ref }
binop_impl! { Mul, mul, mul_ref }
binop_impl! { Div, div, div_ref }
macro_rules! same_dim_assign_impl {
($trait:ident, $method:ident) => {
#[allow(non_camel_case_types)]
impl<R, L, M, T, θ, N, I, J> $trait<Self> for PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>
where
R: Real,
{
fn $method(&mut self, rhs: Self) {
self.value.$method(rhs.value);
}
}
#[allow(non_camel_case_types)]
impl<R, L, M, T, θ, N, I, J> $trait<PhysicalQuantity<R, DynDim>>
for PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>
where
L: Integer, M: Integer, T: Integer, θ: Integer, N: Integer, I: Integer, J: Integer,
R: Real,
{
fn $method(&mut self, rhs: PhysicalQuantity<R, DynDim>) {
assert_eq!(self.dim, rhs.dim);
self.value.$method(rhs.value);
}
}
impl<R, D> $trait<PhysicalQuantity<R, D>> for PhysicalQuantity<R, DynDim>
where
D: Dimension,
R: Real,
{
fn $method(&mut self, rhs: PhysicalQuantity<R, D>) {
assert_eq!(self.dim, rhs.dim);
self.value.$method(rhs.value);
}
}
};
}
macro_rules! dimension_less_assign_impl {
($trait:ident, $method:ident) => {
#[allow(non_camel_case_types)]
impl<R, L, M, T, θ, N, I, J> $trait<PhysicalQuantity<R, dim::Dimensionless>>
for PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>
where
R: Real,
{
fn $method(&mut self, rhs: PhysicalQuantity<R, dim::Dimensionless>) {
self.value.$method(rhs.value);
}
}
#[allow(non_camel_case_types)]
impl<R, L, M, T, θ, N, I, J> $trait<PhysicalQuantity<R, DynDim>>
for PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>
where
R: Real,
{
fn $method(&mut self, rhs: PhysicalQuantity<R, DynDim>) {
assert_eq!(rhs.dim, DIMENSIONLESS);
self.value.$method(rhs.value);
}
}
impl<R, D> $trait<PhysicalQuantity<R, D>> for PhysicalQuantity<R, DynDim>
where
D: Dimension,
R: Real,
{
fn $method(&mut self, rhs: PhysicalQuantity<R, D>) {
assert!( DIMENSIONLESS == self.dim || DIMENSIONLESS == rhs.dim );
self.value.$method(rhs.value);
self.dim.$method(rhs.dim);
}
}
};
}
same_dim_assign_impl! { AddAssign, add_assign }
same_dim_assign_impl! { SubAssign, sub_assign }
dimension_less_assign_impl! { MulAssign, mul_assign }
dimension_less_assign_impl! { DivAssign, div_assign }
impl<R, D> Neg for PhysicalQuantity<R, D>
where
D: Dimension,
R: Real,
{
type Output = Self;
fn neg(self) -> Self::Output {
Self {
value: -self.value,
.. self
}
}
}
impl<D0, D1, R> PartialEq<PhysicalQuantity<R, D1>> for PhysicalQuantity<R, D0>
where
D0: Dimension + PartialEq<D1>, D1: Dimension,
R: PartialEq,
{
fn eq(&self, other: &PhysicalQuantity<R, D1>) -> bool {
self.dim == other.dim &&
self.value == other.value
}
}
impl<D0, D1, R> PartialOrd<PhysicalQuantity<R, D1>> for PhysicalQuantity<R, D0>
where
D0: Dimension + PartialEq<D1>, D1: Dimension,
R: PartialOrd,
{
fn partial_cmp(&self, other: &PhysicalQuantity<R, D1>) -> Option<Ordering> {
match self.dim.dim_code().partial_cmp(&other.dim.dim_code()) {
Some(Ordering::Equal) => self.value.partial_cmp(&other.value),
order => order,
}
}
}
impl<R, D> Debug for PhysicalQuantity<R, D>
where
D: Dimension, R: Real,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("PhysicalQuantity")
.field("value", &self.value)
.field("dim", &self.dim)
.finish()
}
}
impl<R, D> Display for PhysicalQuantity<R, D>
where
D: Dimension, R: Real,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
Debug::fmt(self, f)
}
}