From b285dc8845daeca6cad8ff38d945afadac761cd6 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 | 67 ++++++++++++++++++++++++++++++------------------- 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, 102 insertions(+), 57 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2f9864b0..e0b7bea1 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 @@ -517,17 +514,18 @@ storage_types! { impl crate::Conversion for V { type T = V; + 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 4e1bccb4..e0adc5a9 100644 --- a/src/quantity.rs +++ b/src/quantity.rs @@ -132,7 +132,7 @@ macro_rules! quantity { /// /// [units]: https://jcgm.bipm.org/vim/en/1.13.html /// [factor]: https://jcgm.bipm.org/vim/en/1.24.html - pub trait Conversion: Unit + $crate::Conversion>::T> + pub trait Conversion: Unit + $crate::Conversion>::T, VT = >::VT> where V: $crate::Conversion, { @@ -213,7 +213,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, @@ -230,7 +230,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) } @@ -245,7 +245,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()) } @@ -260,7 +260,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()) } @@ -275,7 +275,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()) } @@ -289,7 +289,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()) } @@ -303,7 +303,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 bd789871..03d2390c 100644 --- a/src/si/ratio.rs +++ b/src/si/ratio.rs @@ -38,8 +38,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 5f03de91..03d6cdd3 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 d73ff3c1..a97f5992 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() }} @@ -1512,7 +1512,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 cf86fc7b..a016177f 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 {})+ @@ -174,6 +180,7 @@ macro_rules! unit { $(impl $crate::Conversion for super::$unit { type T = T; + type VT = T; #[inline(always)] fn coefficient() -> Self::T { @@ -182,9 +189,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 {})+ @@ -206,6 +218,7 @@ macro_rules! unit { $(impl $crate::Conversion for super::$unit { type T = T; + type VT = T; #[inline(always)] fn coefficient() -> Self::T { @@ -214,9 +227,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 {})+ @@ -232,6 +250,7 @@ macro_rules! unit { $(impl $crate::Conversion for super::$unit { type T = V; + type VT = V; #[inline(always)] fn coefficient() -> Self::T { @@ -240,9 +259,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 {})+ @@ -253,6 +277,7 @@ macro_rules! unit { $(impl $crate::Conversion for super::$unit { type T = VV; + type VT = V; #[inline(always)] #[allow(clippy::inconsistent_digit_grouping)] @@ -263,8 +288,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() } }