use core:: {
fmt:: { self, Debug, Display },
marker::PhantomData,
ops:: { Add, Sub, Mul, MulAssign, Div, DivAssign },
};
use typenum:: { Diff, Integer, Negate, Sum };
pub trait Dimension
: Copy
+ Debug
+ Display
+ Default
{
fn length(&self) -> i8;
fn mass(&self) -> i8;
fn time(&self) -> i8;
fn temperature(&self) -> i8;
fn amount(&self) -> i8;
fn current(&self) -> i8;
fn luminous(&self) -> i8;
fn dim_code(&self) -> u64 {
self.length() as u8 as u64
| (self.mass() as u8 as u64) << 8
| (self.time() as u8 as u64) << 16
| (self.temperature() as u8 as u64) << 24
| (self.amount() as u8 as u64) << 32
| (self.current() as u8 as u64) << 40
| (self.luminous() as u8 as u64) << 48
}
}
fn fmt_impl<D: Dimension>(dim: &D, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if dim.dim_code() == 0 {
f.write_str("\u{2014}")
} else {
if dim.length() != 0 { f.write_fmt(format_args!("m{}", dim.length()))?; }
if dim.mass() != 0 { f.write_fmt(format_args!("g{}", dim.mass()))?; }
if dim.time() != 0 { f.write_fmt(format_args!("s{}", dim.time()))?; }
if dim.temperature() != 0 { f.write_fmt(format_args!("K{}", dim.temperature()))?; }
if dim.amount() != 0 { f.write_fmt(format_args!("mol{}", dim.amount()))?; }
if dim.current() != 0 { f.write_fmt(format_args!("A{}", dim.current()))?; }
if dim.luminous() != 0 { f.write_fmt(format_args!("cd{}", dim.luminous()))?; }
Ok(())
}
}
pub fn is_equal<D0, D1>(lhs: &D0, rhs: &D1) -> bool
where
D0: Dimension, D1: Dimension
{
lhs.length() == rhs.length() &&
lhs.mass() == rhs.mass() &&
lhs.time() == rhs.time() &&
lhs.temperature() == rhs.temperature() &&
lhs.amount() == rhs.amount() &&
lhs.current() == rhs.current() &&
lhs.luminous() == rhs.luminous()
}
#[derive(Clone, Copy, Debug, Default)]
pub struct DynDim(pub i8, pub i8, pub i8, pub i8, pub i8, pub i8, pub i8);
impl DynDim {
pub const fn new(dim_code: u64) -> Self {
Self(
dim_code as i8,
(dim_code >> 8) as i8,
(dim_code >> 16) as i8,
(dim_code >> 24) as i8,
(dim_code >> 32) as i8,
(dim_code >> 40) as i8,
(dim_code >> 48) as i8
)
}
pub const fn prod(self, rhs: Self) -> Self {
Self(
self.0 + rhs.0,
self.1 + rhs.1,
self.2 + rhs.2,
self.3 + rhs.3,
self.4 + rhs.4,
self.5 + rhs.5,
self.6 + rhs.6
)
}
pub const fn quot(self, rhs: Self) -> Self {
Self(
self.0 - rhs.0,
self.1 - rhs.1,
self.2 - rhs.2,
self.3 - rhs.3,
self.4 - rhs.4,
self.5 - rhs.5,
self.6 - rhs.6
)
}
pub const fn powi(self, n: i8) -> Self {
Self(
self.0 * n,
self.1 * n,
self.2 * n,
self.3 * n,
self.4 * n,
self.5 * n,
self.6 * n
)
}
pub const fn is_equal(&self, rhs: &Self) -> bool {
self.0 == rhs.0 && self.1 == rhs.1 && self.2 == rhs.2 && self.3 == rhs.3 &&
self.4 == rhs.4 && self.5 == rhs.5 && self.6 == rhs.6
}
}
impl Dimension for DynDim {
fn length(&self) -> i8 { self.0 }
fn mass(&self) -> i8 { self.1 }
fn time(&self) -> i8 { self.2 }
fn temperature(&self) -> i8 { self.3 }
fn amount(&self) -> i8 { self.4 }
fn current(&self) -> i8 { self.5 }
fn luminous(&self) -> i8 { self.6 }
}
impl From<u64> for DynDim {
fn from(code: u64) -> Self {
Self::new(code)
}
}
impl Display for DynDim {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_impl(self, f)
}
}
impl<D0: Dimension> PartialEq<D0> for DynDim {
fn eq(&self, other: &D0) -> bool {
is_equal(self, other)
}
}
#[allow(non_camel_case_types)]
impl<L, M, T, θ, N, I, J> From<Dim<L, M, T, θ, N, I, J>> for DynDim
where
L: Integer, M: Integer, T: Integer, θ: Integer, N: Integer, I: Integer, J: Integer,
{
fn from(_: Dim<L, M, T, θ, N, I, J>) -> Self {
Self(L::I8, M::I8, T::I8, θ::I8, N::I8, I::I8, J::I8)
}
}
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Default)]
pub struct Dim<L, M, T, θ, N, I, J>(
PhantomData<L>, PhantomData<M>, PhantomData<T>, PhantomData<θ>,
PhantomData<N>, PhantomData<I>, PhantomData<J>)
;
#[allow(non_camel_case_types)]
impl<L, M, T, θ, N, I, J> Dim<L, M, T, θ, N, I, J> {
pub const fn new() -> Self {
Self(PhantomData, PhantomData, PhantomData, PhantomData, PhantomData, PhantomData, PhantomData)
}
}
#[allow(non_camel_case_types)]
impl<L, M, T, θ, N, I, J> Dim<L, M, T, θ, N, I, J>
where
L: Integer, M: Integer, T: Integer, θ: Integer, N: Integer, I: Integer, J: Integer,
{
pub const CODE: u64 =
L::I8 as u8 as u64
| (M::I8 as u8 as u64) << 8
| (T::I8 as u8 as u64) << 16
| (θ::I8 as u8 as u64) << 24
| (N::I8 as u8 as u64) << 32
| (I::I8 as u8 as u64) << 40
| (J::I8 as u8 as u64) << 48
;
}
#[allow(non_camel_case_types)]
impl<L, M, T, θ, N, I, J> Dimension for Dim<L, M, T, θ, N, I, J>
where
L: Integer, M: Integer, T: Integer, θ: Integer, N: Integer, I: Integer, J: Integer,
{
fn length(&self) -> i8 { L::I8 }
fn mass(&self) -> i8 { M::I8 }
fn time(&self) -> i8 { T::I8 }
fn temperature(&self) -> i8 { θ::I8 }
fn amount(&self) -> i8 { N::I8 }
fn current(&self) -> i8 { I::I8 }
fn luminous(&self) -> i8 { J::I8 }
fn dim_code(&self) -> u64 { Self::CODE }
}
#[allow(non_camel_case_types)]
impl<L, M, T, θ, N, I, J> Debug for Dim<L, M, T, θ, N, I, J>
where
L: Integer, M: Integer, T: Integer, θ: Integer, N: Integer, I: Integer, J: Integer,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Dim")
.field("L", &self.length())
.field("M", &self.mass())
.field("T", &self.time())
.field("θ", &self.temperature())
.field("N", &self.amount())
.field("I", &self.current())
.field("J", &self.luminous())
.finish()
}
}
#[allow(non_camel_case_types)]
impl<L, M, T, θ, N, I, J> Display for Dim<L, M, T, θ, N, I, J>
where
L: Integer, M: Integer, T: Integer, θ: Integer, N: Integer, I: Integer, J: Integer,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_impl(self, f)
}
}
#[allow(non_camel_case_types)]
impl<L, M, T, θ, N, I, J, D> PartialEq<D> for Dim<L, M, T, θ, N, I, J>
where
L: Integer, M: Integer, T: Integer, θ: Integer, N: Integer, I: Integer, J: Integer,
D: Dimension,
{
fn eq(&self, other: &D) -> bool {
is_equal(self, other)
}
}
macro_rules! binop_impl {
($trait:ident, $method:ident, $dim_trait:ident, $dim_op:ident, $typenum_trait:ident) => {
impl<D: Dimension> $trait<D> for DynDim {
type Output = Self;
fn $method(self, rhs: D) -> Self::Output {
Self(
self.length().$dim_op(rhs.length()),
self.mass().$dim_op(rhs.mass()),
self.time().$dim_op(rhs.time()),
self.temperature().$dim_op(rhs.temperature()),
self.amount().$dim_op(rhs.amount()),
self.current().$dim_op(rhs.current()),
self.luminous().$dim_op(rhs.luminous()),
)
}
}
#[allow(non_camel_case_types)]
impl<L0, M0, T0, θ0, N0, I0, J0, L1, M1, T1, θ1, N1, I1, J1>
$trait<Dim<L1, M1, T1, θ1, N1, I1, J1>> for Dim<L0, M0, T0, θ0, N0, I0, J0>
where
L0: Integer + $dim_trait<L1>, L1: Integer, $typenum_trait<L0, L1>: Integer,
M0: Integer + $dim_trait<M1>, M1: Integer, $typenum_trait<M0, M1>: Integer,
T0: Integer + $dim_trait<T1>, T1: Integer, $typenum_trait<T0, T1>: Integer,
θ0: Integer + $dim_trait<θ1>, θ1: Integer, $typenum_trait<θ0, θ1>: Integer,
N0: Integer + $dim_trait<N1>, N1: Integer, $typenum_trait<N0, N1>: Integer,
I0: Integer + $dim_trait<I1>, I1: Integer, $typenum_trait<I0, I1>: Integer,
J0: Integer + $dim_trait<J1>, J1: Integer, $typenum_trait<J0, J1>: Integer,
{
type Output = Dim<
$typenum_trait<L0, L1>,
$typenum_trait<M0, M1>,
$typenum_trait<T0, T1>,
$typenum_trait<θ0, θ1>,
$typenum_trait<N0, N1>,
$typenum_trait<I0, I1>,
$typenum_trait<J0, J1>,
>;
fn $method(self, _: Dim<L1, M1, T1, θ1, N1, I1, J1>) -> Self::Output {
Default::default()
}
}
#[allow(non_camel_case_types)]
impl<L, M, T, θ, N, I, J> $trait<DynDim> for Dim<L, M, T, θ, N, I, J>
where
L: Integer, M: Integer, T: Integer, θ: Integer, N: Integer, I: Integer, J: Integer,
{
type Output = DynDim;
fn $method(self, rhs: DynDim) -> Self::Output {
DynDim(
self.length().$dim_op(rhs.length()),
self.mass().$dim_op(rhs.mass()),
self.time().$dim_op(rhs.time()),
self.temperature().$dim_op(rhs.temperature()),
self.amount().$dim_op(rhs.amount()),
self.current().$dim_op(rhs.current()),
self.luminous().$dim_op(rhs.luminous()),
)
}
}
};
}
binop_impl! { Mul, mul, Add, add, Sum }
binop_impl! { Div, div, Sub, sub, Diff }
macro_rules! assignop_impl {
($trait:ident, $method:ident) => {
impl<D: Dimension> $trait<D> for DynDim {
fn $method(&mut self, rhs: D) {
self.0.$method(rhs.length());
self.1.$method(rhs.mass());
self.2.$method(rhs.time());
self.3.$method(rhs.temperature());
self.4.$method(rhs.amount());
self.5.$method(rhs.current());
self.5.$method(rhs.luminous());
}
}
};
}
assignop_impl! { MulAssign, mul_assign }
assignop_impl! { DivAssign, div_assign }
#[allow(non_camel_case_types)]
pub type InvDim<L, M, T, θ, N, I, J>
= Dim<Negate<L>, Negate<M>, Negate<T>, Negate<θ>, Negate<N>, Negate<I>, Negate<J>>;