unitary 0.0.2

A custom units of measurement system
Documentation
#[macro_export]
macro_rules! units {
    ($($i: ident), +) => {
        #[derive(Debug, PartialEq, PartialOrd, Ord, Clone, Copy, Eq)]
        #[allow(non_camel_case_types)]
        pub enum Units {
            $(
                $i,
            )+
        }

        impl unitary::Measurement for Units {}
    };
}

#[derive(Debug, Clone)]
pub struct Qty<T: Measurement>(pub f64, pub Vec<(T, f64)>);

#[macro_export]
macro_rules! qty {
    ($i: expr; $t: ident) => {
        unitary::Qty($i, vec![(Units::$t, 1.0)]);
    };
}

use std::fmt::{self, Debug};

pub trait Measurement: PartialEq + Ord + Copy + Debug + Eq {}

use std::ops::{Add, Div, Mul, Sub};

impl<T: Measurement> Div<Qty<T>> for Qty<T> {
    type Output = Qty<T>;

    fn div(self, rhs: Qty<T>) -> Qty<T> {
        let mut num = self.1;
        let mut denom = rhs.1.into_iter().map(|(x, y)| (x, -y)).collect();
        num.append(&mut denom);
        num.sort_by_key(|a| a.0);
        let num = get_units(&num);

        Qty(self.0 / rhs.0, num)
    }
}

impl<T: Measurement> Div<f64> for Qty<T> {
    type Output = Qty<T>;

    fn div(self, rhs: f64) -> Qty<T> {
        Qty(self.0 / rhs, self.1)
    }
}

impl<T: Measurement> Mul<Qty<T>> for Qty<T> {
    type Output = Qty<T>;

    fn mul(self, rhs: Qty<T>) -> Qty<T> {
        let mut num = self.1;
        let mut denom = rhs.1;
        num.append(&mut denom);
        num.sort_by_key(|a| a.0);
        let num = get_units(&num);

        Qty(self.0 * rhs.0, num)
    }
}

impl<T: Measurement> Mul<f64> for Qty<T> {
    type Output = Qty<T>;

    fn mul(self, rhs: f64) -> Qty<T> {
        Qty(self.0 * rhs, self.1)
    }
}

impl<T: Measurement> Add<Qty<T>> for Qty<T> {
    type Output = Qty<T>;

    fn add(self, rhs: Qty<T>) -> Qty<T> {
        if self.1 == rhs.1 {
            Qty(self.0 + rhs.0, self.1)
        } else {
            panic!("Differing Units")
        }
    }
}

impl<T: Measurement> Sub<Qty<T>> for Qty<T> {
    type Output = Qty<T>;

    fn sub(self, rhs: Qty<T>) -> Qty<T> {
        if self.1 == rhs.1 {
            Qty(self.0 - rhs.0, self.1)
        } else {
            panic!("Differing Units")
        }
    }
}

impl<T: Measurement> Into<f64> for Qty<T> {
    fn into(self) -> f64 {
        if self.1 == [] {
            self.0
        } else {
            panic!("Cannot unwrap non-zero units")
        }
    }
}

use std::f64;

impl<T: Measurement> fmt::Display for Qty<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{} {}",
            self.0,
            self.1
                .clone()
                .into_iter()
                .map(|(unit, coef)| if (coef - 1.0).abs() < f64::EPSILON {
                    format!("{:?}", unit)
                } else {
                    format!("⋅({:?}^{})", unit, coef)
                })
                .collect::<String>()
        )
    }
}

fn get_units<T: Measurement>(data: &[(T, f64)]) -> Vec<(T, f64)> {
    let mut single = data.to_owned();
    single.dedup_by_key(|a| a.0);
    let mut new = Vec::new();
    for &(t, _v) in &single {
        let mut acc = 0.0;
        for &(d, v) in data {
            if d == t {
                acc += v;
            }
        }
        if acc != 0.0 {
            new.push((t, acc));
        }
    }
    new
}

type Pair<T> = (Vec<(T, f64)>, (T, f64));

fn get_units_or_alias<T: Measurement>(data: Vec<(T, f64)>, aliases: &[Pair<T>]) -> Vec<(T, f64)> {
    for &(ref i, ref j) in aliases {
        if &data == i {
            return vec![*j];
        }
    }
    data
}

impl<T: Measurement> Qty<T> {
    pub fn powf(self, rhs: f64) -> Qty<T> {
        Qty(
            self.0.powf(rhs),
            self.1
                .into_iter()
                .map(|(x, y)| {
                    let c = y < 1.0;
                    let mut a = y.abs().powf(rhs);
                    if c {
                        a *= -1.0
                    }
                    (x, a)
                })
                .collect(),
        )
    }

    pub fn update(&mut self, aliases: &[Pair<T>]) {
        self.1 = get_units_or_alias(self.1.clone(), aliases)
    }
}