From 8d362118f66f7f7a0c6acc4e33d46acd01546f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20Kie=C3=9Fling?= Date: Mon, 15 Jan 2024 14:33:44 +0100 Subject: [PATCH] Conversion now has both a value and a factor type Initially, `ConversionFactor` and thus `Conversion::T` requred `PartialEq`. This makes sense for the conversion factor itself (i.e. scaling across units), however it breaks once you introduce complex numbers. Those can *still* be scaled just like normal numbers - you essentially just increase or decrese a vector length, but the conversion function cannot compare them - "Z_1 < Z_2" is not trivially decidable. It is, however, also not needed - unit scales are just that - scalars that scale. And those can be easily compared. This commit seperates `Conversion::T` into `Conversion::VT` and `Conversion::T` and moves the `PartialEq` requirements from `ConversionFactor` into `Conversion::TT` directly. This requires a lot of trait bounds added down the line, so im not 100% that this does not break anything down the line. There might be a nicer way to go about this, but i haven't found any. closes #452 --- src/lib.rs | 69 ++++++++++++++++++++++++++++++------------------- src/quantity.rs | 16 ++++++------ src/si/angle.rs | 2 +- src/si/ratio.rs | 4 +-- src/si/time.rs | 8 +++--- src/system.rs | 20 +++++++------- src/unit.rs | 42 +++++++++++++++++++++++++----- 7 files changed, 103 insertions(+), 58 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 498810d4..534fb929 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -408,13 +408,15 @@ pub enum ConstantOp { /// Trait to identify [units][units] which have a [conversion factor][factor]. /// /// ## Generic Parameters -/// * `V`: Underlying storage type trait is implemented for. -/// +/// * `T`: The type of the conversion factor. Usually same as `VT`, but for example complex storage types have this as float as it needs `PartialEq` +/// * `VT`: Underlying storage type trait is implemented for. Does not have to implement `PartialEq`, so its viable for complex data types. /// [units]: https://jcgm.bipm.org/vim/en/1.13.html /// [factor]: https://jcgm.bipm.org/vim/en/1.24.html pub trait Conversion { /// Conversion factor type specific to the underlying storage type. - type T: ConversionFactor; + type T: ConversionFactor + PartialOrd; + /// Value type of the underlying type. + type VT: ConversionFactor + From; /// Coefficient portion of [conversion factor](https://jcgm.bipm.org/vim/en/1.24.html) for /// converting the given unit. To convert to the base unit for the quantity use `(value + @@ -436,21 +438,17 @@ pub trait Conversion { #[must_use = "method returns a new number and does not mutate the original value"] #[inline(always)] #[allow(unused_variables)] - fn constant(op: ConstantOp) -> Self::T { - ::zero() + fn constant(op: ConstantOp) -> Self::VT { + ::zero() } /// Instance [conversion factor](https://jcgm.bipm.org/vim/en/1.24.html). /// /// Default implementation returns the coefficient: `Self::coefficient()`. #[must_use = "method returns a new number and does not mutate the original value"] - #[inline(always)] - fn conversion(&self) -> Self::T + fn conversion(&self) -> Self::VT where - Self: Sized, - { - Self::coefficient() - } + Self: Sized; } /// Trait representing a [conversion factor][factor]. @@ -461,8 +459,7 @@ pub trait Conversion { /// [factor]: https://jcgm.bipm.org/vim/en/1.24.html #[allow(unused_qualifications)] // lib:cmp::PartialOrder false positive. pub trait ConversionFactor: - lib::cmp::PartialOrd - + lib::ops::Add + lib::ops::Add + lib::ops::Sub + lib::ops::Mul + lib::ops::Div @@ -515,19 +512,20 @@ pub trait Kind: storage_types! { types: Float; - impl crate::Conversion for V { + impl crate::Conversion for V { type T = Self; + type VT = Self::T; #[inline(always)] - fn constant(op: crate::ConstantOp) -> Self::T { + fn constant(op: crate::ConstantOp) -> Self::VT { match op { - crate::ConstantOp::Add => -::zero(), - crate::ConstantOp::Sub => ::zero(), + crate::ConstantOp::Add => -::zero(), + crate::ConstantOp::Sub => ::zero(), } } #[inline(always)] - fn conversion(&self) -> Self::T { + fn conversion(&self) -> Self::VT { *self } } @@ -554,9 +552,10 @@ storage_types! { impl crate::Conversion for V { type T = crate::num::rational::Ratio; + type VT = Self::T; #[inline(always)] - fn conversion(&self) -> Self::T { + fn conversion(&self) -> Self::VT { (*self).into() } } @@ -583,9 +582,10 @@ storage_types! { impl crate::Conversion for V { type T = crate::num::rational::Ratio; + type VT = Self::T; #[inline(always)] - fn conversion(&self) -> Self::T { + fn conversion(&self) -> Self::VT { self.clone().into() } } @@ -612,9 +612,10 @@ storage_types! { impl crate::Conversion for V { type T = V; + type VT = Self::T; #[inline(always)] - fn conversion(&self) -> Self::T { + fn conversion(&self) -> Self::VT { *self } } @@ -637,9 +638,10 @@ storage_types! { impl crate::Conversion for V { type T = V; + type VT = Self::T; #[inline(always)] - fn conversion(&self) -> Self::T { + fn conversion(&self) -> Self::VT { self.clone() } } @@ -665,20 +667,21 @@ storage_types! { types: Complex; impl crate::Conversion for V { type T = VV; + type VT = V; #[inline(always)] - fn constant(op: crate::ConstantOp) -> Self::T { + fn constant(op: crate::ConstantOp) -> Self::VT { match op { - crate::ConstantOp::Add => -::zero(), - crate::ConstantOp::Sub => ::zero(), + crate::ConstantOp::Add => -::zero(), + crate::ConstantOp::Sub => ::zero(), } } #[inline(always)] - fn conversion(&self) -> Self::T { + fn conversion(&self) -> Self::VT { // Conversion factor is the norm of the number. Scaling with length again yields the // same number. - self.norm() + *self } } @@ -695,6 +698,18 @@ storage_types! { V::new(self, 0.0) } } + + impl crate::ConversionFactor for V { + #[inline(always)] + fn powi(self, e: i32) -> Self { + crate::num::complex::Complex::powi(&self,e) + } + + #[inline(always)] + fn value(self) -> V { + self + } + } } /// Utilities for formatting and printing quantities. diff --git a/src/quantity.rs b/src/quantity.rs index 27eee98b..598587fc 100644 --- a/src/quantity.rs +++ b/src/quantity.rs @@ -133,7 +133,7 @@ macro_rules! quantity { /// [units]: https://jcgm.bipm.org/vim/en/1.13.html /// [factor]: https://jcgm.bipm.org/vim/en/1.24.html #[allow(dead_code)] - pub trait Conversion: Unit + $crate::Conversion>::T> + pub trait Conversion: Unit + $crate::Conversion>::T, VT = >::VT> where V: $crate::Conversion, { @@ -218,7 +218,7 @@ macro_rules! quantity { #[inline(always)] pub fn new(v: V) -> Self where - N: Unit + $crate::Conversion, + N: Unit + $crate::Conversion, { $quantity { dimension: $crate::lib::marker::PhantomData, @@ -235,7 +235,7 @@ macro_rules! quantity { #[inline(always)] pub fn get(&self) -> V where - N: Unit + $crate::Conversion, + N: Unit + $crate::Conversion, { __system::from_base::(&self.value) } @@ -250,7 +250,7 @@ macro_rules! quantity { pub fn floor(self) -> Self where V: $crate::num::Float, - N: Unit + $crate::Conversion, + N: Unit + $crate::Conversion, { Self::new::(self.get::().floor()) } @@ -265,7 +265,7 @@ macro_rules! quantity { pub fn ceil(self) -> Self where V: $crate::num::Float, - N: Unit + $crate::Conversion, + N: Unit + $crate::Conversion, { Self::new::(self.get::().ceil()) } @@ -280,7 +280,7 @@ macro_rules! quantity { pub fn round(self) -> Self where V: $crate::num::Float, - N: Unit + $crate::Conversion, + N: Unit + $crate::Conversion, { Self::new::(self.get::().round()) } @@ -294,7 +294,7 @@ macro_rules! quantity { pub fn trunc(self) -> Self where V: $crate::num::Float, - N: Unit + $crate::Conversion, + N: Unit + $crate::Conversion, { Self::new::(self.get::().trunc()) } @@ -308,7 +308,7 @@ macro_rules! quantity { pub fn fract(self) -> Self where V: $crate::num::Float, - N: Unit + $crate::Conversion, + N: Unit + $crate::Conversion, { Self::new::(self.get::().fract()) } diff --git a/src/si/angle.rs b/src/si/angle.rs index 171a7c0d..df1d055b 100644 --- a/src/si/angle.rs +++ b/src/si/angle.rs @@ -127,7 +127,7 @@ where D: crate::si::Dimension + ?Sized, U: crate::si::Units + ?Sized, V: crate::num::Float + crate::Conversion, - radian: crate::Conversion, + radian: crate::Conversion, { /// Computes the four quadrant arctangent of self (y) and other (x). #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/src/si/ratio.rs b/src/si/ratio.rs index 8683b393..5b1d4de5 100644 --- a/src/si/ratio.rs +++ b/src/si/ratio.rs @@ -39,8 +39,8 @@ impl Ratio where U: crate::si::Units + ?Sized, V: crate::num::Float + crate::Conversion, - radian: crate::Conversion, - ratio: crate::Conversion, + radian: crate::Conversion, + ratio: crate::Conversion, { /// Computes the value of the inverse cosine of the ratio. #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/src/si/time.rs b/src/si/time.rs index a5c8124f..eeaf9721 100644 --- a/src/si/time.rs +++ b/src/si/time.rs @@ -82,8 +82,8 @@ impl crate::lib::convert::TryFrom> for Duration where U: crate::si::Units + ?Sized, V: crate::num::Num + crate::Conversion + PartialOrd + ToPrimitive, - second: crate::Conversion, - nanosecond: crate::Conversion, + second: crate::Conversion, + nanosecond: crate::Conversion, { type Error = TryFromError; @@ -117,8 +117,8 @@ impl crate::lib::convert::TryFrom for Time where U: crate::si::Units + ?Sized, V: crate::num::Num + crate::Conversion + FromPrimitive, - second: crate::Conversion, - nanosecond: crate::Conversion, + second: crate::Conversion, + nanosecond: crate::Conversion, { type Error = TryFromError; diff --git a/src/system.rs b/src/system.rs index b90e3b25..a6bf64ac 100644 --- a/src/system.rs +++ b/src/system.rs @@ -163,7 +163,7 @@ macro_rules! system { /// /// Base unit. #[allow(non_camel_case_types)] - type $name: Unit + $crate::Conversion;)+ + type $name: Unit + $crate::Conversion;)+ } /// Trait to identify [measurement units][measurement] of individual @@ -307,7 +307,7 @@ macro_rules! system { D: Dimension + ?Sized, U: Units + ?Sized, V: $crate::Conversion, - N: $crate::Conversion, + N: $crate::Conversion, { use $crate::typenum::Integer; use $crate::{Conversion, ConversionFactor}; @@ -318,10 +318,10 @@ macro_rules! system { let n_cons = N::constant($crate::ConstantOp::Sub); if n_coef < f { - (v * (f / n_coef) - n_cons).value() + (v * (f / n_coef).into() - n_cons.into()).value() } else { - (v / (n_coef / f) - n_cons).value() + (v / (n_coef / f).into() - n_cons.into()).value() } } @@ -338,7 +338,7 @@ macro_rules! system { D: Dimension + ?Sized, U: Units + ?Sized, V: $crate::Conversion, - N: $crate::Conversion, + N: $crate::Conversion, { use $crate::typenum::Integer; use $crate::{Conversion, ConversionFactor}; @@ -349,10 +349,10 @@ macro_rules! system { let n_cons = N::constant($crate::ConstantOp::Add); if n_coef >= f { - ((v + n_cons) * (n_coef / f)).value() + ((v + n_cons.into()) * (n_coef / f).into()).value() } else { - (((v + n_cons) * n_coef) / f).value() + (((v + n_cons.into()) * n_coef.into()) / f.into()).value() } } @@ -376,8 +376,8 @@ macro_rules! system { use $crate::typenum::Integer; use $crate::{Conversion, ConversionFactor}; - (v.conversion() $(* Ur::$name::coefficient().powi(D::$symbol::to_i32()) - / Ul::$name::coefficient().powi(D::$symbol::to_i32()))+) + (v.conversion() $(* Ur::$name::coefficient().powi(D::$symbol::to_i32()).into() + / Ul::$name::coefficient().powi(D::$symbol::to_i32()).into())+) .value() }} @@ -1508,7 +1508,7 @@ macro_rules! system { D: Dimension + ?Sized, U: Units + ?Sized, V: Num + Conversion + fmt::$style, - N: Unit + Conversion, + N: Unit + Conversion, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let value = from_base::(&self.quantity.value); diff --git a/src/unit.rs b/src/unit.rs index 06165e99..92b58ca7 100644 --- a/src/unit.rs +++ b/src/unit.rs @@ -145,6 +145,7 @@ macro_rules! unit { $(impl $crate::Conversion for super::$unit { type T = V; + type VT = V; #[inline(always)] #[allow(clippy::inconsistent_digit_grouping)] @@ -155,9 +156,14 @@ macro_rules! unit { #[inline(always)] #[allow(unused_variables)] #[allow(clippy::inconsistent_digit_grouping)] - fn constant(op: $crate::ConstantOp) -> Self::T { + fn constant(op: $crate::ConstantOp) -> Self::VT { unit!(@constant op $($conversion),+) } + + #[inline(always)] + fn conversion(&self) -> Self::VT { + unit!(@coefficient $($conversion),+) + } } impl super::Conversion for super::$unit { @@ -185,6 +191,7 @@ macro_rules! unit { $(impl $crate::Conversion for super::$unit { type T = T; + type VT = T; #[inline(always)] fn coefficient() -> Self::T { @@ -193,9 +200,14 @@ macro_rules! unit { #[inline(always)] #[allow(unused_variables)] - fn constant(op: $crate::ConstantOp) -> Self::T { + fn constant(op: $crate::ConstantOp) -> Self::VT { from_f64(unit!(@constant op $($conversion),+)) } + + #[inline(always)] + fn conversion(&self) -> Self::VT { + from_f64(unit!(@coefficient $($conversion),+)) + } } impl super::Conversion for super::$unit { @@ -241,6 +253,7 @@ macro_rules! unit { $(impl $crate::Conversion for super::$unit { type T = T; + type VT = T; #[inline(always)] fn coefficient() -> Self::T { @@ -249,9 +262,14 @@ macro_rules! unit { #[inline(always)] #[allow(unused_variables)] - fn constant(op: $crate::ConstantOp) -> Self::T { + fn constant(op: $crate::ConstantOp) -> Self::VT { from_f64(unit!(@constant op $($conversion),+)) } + + #[inline(always)] + fn conversion(&self) -> Self::VT { + from_f64(unit!(@coefficient $($conversion),+)) + } } impl super::Conversion for super::$unit { @@ -290,6 +308,7 @@ macro_rules! unit { $(impl $crate::Conversion for super::$unit { type T = V; + type VT = V; #[inline(always)] fn coefficient() -> Self::T { @@ -298,9 +317,14 @@ macro_rules! unit { #[inline(always)] #[allow(unused_variables)] - fn constant(op: $crate::ConstantOp) -> Self::T { + fn constant(op: $crate::ConstantOp) -> Self::VT { from_f64(unit!(@constant op $($conversion),+)) } + + #[inline(always)] + fn conversion(&self) -> Self::VT { + from_f64(unit!(@coefficient $($conversion),+)) + } } impl super::Conversion for super::$unit { @@ -335,6 +359,7 @@ macro_rules! unit { $(impl $crate::Conversion for super::$unit { type T = VV; + type VT = V; #[inline(always)] #[allow(clippy::inconsistent_digit_grouping)] @@ -345,8 +370,13 @@ macro_rules! unit { #[inline(always)] #[allow(unused_variables)] #[allow(clippy::inconsistent_digit_grouping)] - fn constant(op: $crate::ConstantOp) -> Self::T { - unit!(@constant op $($conversion),+) + fn constant(op: $crate::ConstantOp) -> Self::VT { + (unit!(@constant op $($conversion),+)).into() + } + + #[inline(always)] + fn conversion(&self) -> Self::VT { + (unit!(@coefficient $($conversion),+)).into() } }