iliekturtles/uom

Quantity-Aware access parts of quantity with complex storage type

mkalte666 opened this issue · 2 comments

It can be interesting (or needed) to access only the real or imaginary part of a complex quantity.

Currently, i have solved this for my codebase like this, but it would be nice to have this available in uom directly as well. For all i know, its already there; im known to miss the most obvious functions out there :D

/// Helper trait to grab real or complex parts of Quantities
pub trait ComplexAccess {
    type VT;
    type AT;
    /// Get the real part of Self
    fn real(&self) -> Self::VT;

    /// Get the imaginary part of Self
    fn imag(&self) -> Self::VT;

    /// Get the norm of Self
    fn norm(&self) -> Self::VT;

    /// Get the argument of Self
    fn arg(&self) -> Self::AT;
}

use uom::{storage_type_complex32, storage_type_complex64};

storage_types! {
    pub types: Complex;

    impl<D> crate::ComplexAccess for uom::si::Quantity<D,uom::si::SI<V>,V>
    where
        D: uom::si::Dimension + ?Sized,
    {
        type VT = uom::si::Quantity<D,uom::si::SI<VV>,VV>;
        type AT = uom::si::angle::Angle<uom::si::SI::<VV>,VV>;
        fn real(&self) -> Self::VT {
            Self::VT {
                dimension: self.dimension,
                units: std::marker::PhantomData::default(),
                value: self.value.re
            }
        }

        fn imag(&self) -> Self::VT {
            Self::VT {
                dimension: self.dimension,
                units: std::marker::PhantomData::default(),
                value: self.value.im
            }
        }

        fn norm(&self) -> Self::VT
        {
            Self::VT {
                dimension: self.dimension,
                units: std::marker::PhantomData::default(),
                value: self.value.norm()
            }
        }

        fn arg(&self) -> Self::AT
        {
           Self::AT::new::<uom::si::angle::radian>(self.value.arg())
        }
    }
}

Example usage would be somthing like this:

let z = si::complex64::ElectricalResistance::new::<ohm>(Complex64::new(123.0,321.0));
let im : si::f64::ElectricalResistance = z.real(); 
assert_eq!(im.get::<ohm>(), 321.0);
// do something with re; i.e compute a capacitance or whatever else 

I don't think the trait is necessary. Similar to how how methods for other underlying types are implemented, we would just need to add methods for Complex to return the re/im fields.

e.g. is_nan for floating point types:

uom/src/system.rs

Lines 759 to 780 in 85b665e

// Explicitly definte floating point methods for float and complex storage types.
// `Complex<T>` doesn't implement `Float`/`FloatCore`, but it does implement these methods
// when the underlying type, `T`, implements `FloatCore`.
mod float {
storage_types! {
types: Float, Complex;
use super::super::*;
impl<D, U> Quantity<D, U, V>
where
D: Dimension + ?Sized,
U: Units<V> + ?Sized,
{
/// Returns `true` if this value is `NAN` and `false` otherwise.
#[cfg_attr(feature = "cargo-clippy", allow(clippy::wrong_self_convention))]
#[must_use = "method returns a new number and does not mutate the original value"]
#[inline(always)]
pub fn is_nan(self) -> bool
{
self.value.is_nan()
}

oh sorry I should have clarified this; im using the trait because I don't wanna keep track of(rather, don't have the time for) more patching right now. Basically a quick and dirty way to get this to work for me right now without much thought^^

A solution like you described would be much preferable :D

Maybe I can get to that as well later, but right now I'm quite busy in way to may ways ;_;