#[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)
}
}