Skip to content

Commit

Permalink
Conversion now has both a value and a factor type
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mkalte666 committed Oct 14, 2024
1 parent edee7cb commit 2a8261a
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 60 deletions.
69 changes: 42 additions & 27 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<V> {
/// Conversion factor type specific to the underlying storage type.
type T: ConversionFactor<V>;
type T: ConversionFactor<V> + PartialOrd;
/// Value type of the underlying type.
type VT: ConversionFactor<V> + From<Self::T>;

/// 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 +
Expand All @@ -436,21 +438,17 @@ pub trait Conversion<V> {
#[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 {
<Self::T as num::Zero>::zero()
fn constant(op: ConstantOp) -> Self::VT {
<Self::VT as num::Zero>::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].
Expand All @@ -461,8 +459,7 @@ pub trait Conversion<V> {
/// [factor]: https://jcgm.bipm.org/vim/en/1.24.html
#[allow(unused_qualifications)] // lib:cmp::PartialOrder false positive.
pub trait ConversionFactor<V>:
lib::cmp::PartialOrd
+ lib::ops::Add<Self, Output = Self>
lib::ops::Add<Self, Output = Self>
+ lib::ops::Sub<Self, Output = Self>
+ lib::ops::Mul<Self, Output = Self>
+ lib::ops::Div<Self, Output = Self>
Expand Down Expand Up @@ -515,19 +512,20 @@ pub trait Kind:
storage_types! {
types: Float;

impl crate::Conversion<Self> for V {
impl crate::Conversion<V> 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 => -<Self::T as crate::num::Zero>::zero(),
crate::ConstantOp::Sub => <Self::T as crate::num::Zero>::zero(),
crate::ConstantOp::Add => -<Self::VT as crate::num::Zero>::zero(),
crate::ConstantOp::Sub => <Self::VT as crate::num::Zero>::zero(),
}
}

#[inline(always)]
fn conversion(&self) -> Self::T {
fn conversion(&self) -> Self::VT {
*self
}
}
Expand All @@ -554,9 +552,10 @@ storage_types! {

impl crate::Conversion<V> for V {
type T = crate::num::rational::Ratio<V>;
type VT = Self::T;

#[inline(always)]
fn conversion(&self) -> Self::T {
fn conversion(&self) -> Self::VT {
(*self).into()
}
}
Expand All @@ -583,9 +582,10 @@ storage_types! {

impl crate::Conversion<V> for V {
type T = crate::num::rational::Ratio<V>;
type VT = Self::T;

#[inline(always)]
fn conversion(&self) -> Self::T {
fn conversion(&self) -> Self::VT {
self.clone().into()
}
}
Expand All @@ -612,9 +612,10 @@ storage_types! {

impl crate::Conversion<V> for V {
type T = V;
type VT = Self::T;

#[inline(always)]
fn conversion(&self) -> Self::T {
fn conversion(&self) -> Self::VT {
*self
}
}
Expand All @@ -637,9 +638,10 @@ storage_types! {

impl crate::Conversion<V> for V {
type T = V;
type VT = Self::T;

#[inline(always)]
fn conversion(&self) -> Self::T {
fn conversion(&self) -> Self::VT {
self.clone()
}
}
Expand All @@ -665,20 +667,21 @@ storage_types! {
types: Complex;
impl crate::Conversion<V> 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 => -<Self::T as crate::num::Zero>::zero(),
crate::ConstantOp::Sub => <Self::T as crate::num::Zero>::zero(),
crate::ConstantOp::Add => -<Self::VT as crate::num::Zero>::zero(),
crate::ConstantOp::Sub => <Self::VT as crate::num::Zero>::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
}
}

Expand All @@ -695,6 +698,18 @@ storage_types! {
V::new(self, 0.0)
}
}

impl crate::ConversionFactor<V> 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.
Expand Down
16 changes: 8 additions & 8 deletions src/quantity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<V>: Unit + $crate::Conversion<V, T = <V as $crate::Conversion<V>>::T>
pub trait Conversion<V>: Unit + $crate::Conversion<V, T = <V as $crate::Conversion<V>>::T, VT = <V as $crate::Conversion<V>>::VT>
where
V: $crate::Conversion<V>,
{
Expand Down Expand Up @@ -218,7 +218,7 @@ macro_rules! quantity {
#[inline(always)]
pub fn new<N>(v: V) -> Self
where
N: Unit + $crate::Conversion<V, T = V::T>,
N: Unit + $crate::Conversion<V, T = V::T, VT = V::VT>,
{
$quantity {
dimension: $crate::lib::marker::PhantomData,
Expand All @@ -235,7 +235,7 @@ macro_rules! quantity {
#[inline(always)]
pub fn get<N>(&self) -> V
where
N: Unit + $crate::Conversion<V, T = V::T>,
N: Unit + $crate::Conversion<V, T = V::T, VT = V::VT>,
{
__system::from_base::<Dimension, U, V, N>(&self.value)
}
Expand All @@ -250,7 +250,7 @@ macro_rules! quantity {
pub fn floor<N>(self) -> Self
where
V: $crate::num::Float,
N: Unit + $crate::Conversion<V, T = V::T>,
N: Unit + $crate::Conversion<V, T = V::T, VT = V::VT>,
{
Self::new::<N>(self.get::<N>().floor())
}
Expand All @@ -265,7 +265,7 @@ macro_rules! quantity {
pub fn ceil<N>(self) -> Self
where
V: $crate::num::Float,
N: Unit + $crate::Conversion<V, T = V::T>,
N: Unit + $crate::Conversion<V, T = V::T, VT = V::VT>,
{
Self::new::<N>(self.get::<N>().ceil())
}
Expand All @@ -280,7 +280,7 @@ macro_rules! quantity {
pub fn round<N>(self) -> Self
where
V: $crate::num::Float,
N: Unit + $crate::Conversion<V, T = V::T>,
N: Unit + $crate::Conversion<V, T = V::T, VT = V::VT>,
{
Self::new::<N>(self.get::<N>().round())
}
Expand All @@ -294,7 +294,7 @@ macro_rules! quantity {
pub fn trunc<N>(self) -> Self
where
V: $crate::num::Float,
N: Unit + $crate::Conversion<V, T = V::T>,
N: Unit + $crate::Conversion<V, T = V::T, VT = V::VT>,
{
Self::new::<N>(self.get::<N>().trunc())
}
Expand All @@ -308,7 +308,7 @@ macro_rules! quantity {
pub fn fract<N>(self) -> Self
where
V: $crate::num::Float,
N: Unit + $crate::Conversion<V, T = V::T>,
N: Unit + $crate::Conversion<V, T = V::T, VT = V::VT>,
{
Self::new::<N>(self.get::<N>().fract())
}
Expand Down
2 changes: 1 addition & 1 deletion src/si/angle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ where
D: crate::si::Dimension + ?Sized,
U: crate::si::Units<V> + ?Sized,
V: crate::num::Float + crate::Conversion<V>,
radian: crate::Conversion<V, T = V::T>,
radian: crate::Conversion<V, T = V::T, VT = V::VT>,
{
/// 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"]
Expand Down
4 changes: 2 additions & 2 deletions src/si/ratio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ impl<U, V> Ratio<U, V>
where
U: crate::si::Units<V> + ?Sized,
V: crate::num::Float + crate::Conversion<V>,
radian: crate::Conversion<V, T = V::T>,
ratio: crate::Conversion<V, T = V::T>,
radian: crate::Conversion<V, T = V::T, VT = V::VT>,
ratio: crate::Conversion<V, T = V::T, VT = V::VT>,
{
/// Computes the value of the inverse cosine of the ratio.
#[must_use = "method returns a new number and does not mutate the original value"]
Expand Down
8 changes: 4 additions & 4 deletions src/si/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ impl<U, V> crate::lib::convert::TryFrom<Time<U, V>> for Duration
where
U: crate::si::Units<V> + ?Sized,
V: crate::num::Num + crate::Conversion<V> + PartialOrd + ToPrimitive,
second: crate::Conversion<V, T = V::T>,
nanosecond: crate::Conversion<V, T = V::T>,
second: crate::Conversion<V, T = V::T, VT = V::VT>,
nanosecond: crate::Conversion<V, T = V::T, VT = V::VT>,
{
type Error = TryFromError;

Expand Down Expand Up @@ -117,8 +117,8 @@ impl<U, V> crate::lib::convert::TryFrom<Duration> for Time<U, V>
where
U: crate::si::Units<V> + ?Sized,
V: crate::num::Num + crate::Conversion<V> + FromPrimitive,
second: crate::Conversion<V, T = V::T>,
nanosecond: crate::Conversion<V, T = V::T>,
second: crate::Conversion<V, T = V::T, VT = V::VT>,
nanosecond: crate::Conversion<V, T = V::T, VT = V::VT>,
{
type Error = TryFromError;

Expand Down
20 changes: 10 additions & 10 deletions src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ macro_rules! system {
///
/// Base unit.
#[allow(non_camel_case_types)]
type $name: Unit + $crate::Conversion<V, T = V::T>;)+
type $name: Unit + $crate::Conversion<V, T = V::T, VT = V::VT>;)+
}

/// Trait to identify [measurement units][measurement] of individual
Expand Down Expand Up @@ -307,7 +307,7 @@ macro_rules! system {
D: Dimension + ?Sized,
U: Units<V> + ?Sized,
V: $crate::Conversion<V>,
N: $crate::Conversion<V, T = V::T>,
N: $crate::Conversion<V, T = V::T, VT = V::VT>,
{
use $crate::typenum::Integer;
use $crate::{Conversion, ConversionFactor};
Expand All @@ -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()
}
}

Expand All @@ -338,7 +338,7 @@ macro_rules! system {
D: Dimension + ?Sized,
U: Units<V> + ?Sized,
V: $crate::Conversion<V>,
N: $crate::Conversion<V, T = V::T>,
N: $crate::Conversion<V, T = V::T, VT= V::VT>,
{
use $crate::typenum::Integer;
use $crate::{Conversion, ConversionFactor};
Expand All @@ -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()
}
}

Expand All @@ -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()
}}

Expand Down Expand Up @@ -1508,7 +1508,7 @@ macro_rules! system {
D: Dimension + ?Sized,
U: Units<V> + ?Sized,
V: Num + Conversion<V> + fmt::$style,
N: Unit + Conversion<V, T = V::T>,
N: Unit + Conversion<V, T = V::T, VT = V::VT>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let value = from_base::<D, U, V, N>(&self.quantity.value);
Expand Down
Loading

0 comments on commit 2a8261a

Please sign in to comment.