#![allow(dead_code, unused_imports)]
extern crate num;
use num::Signed;
use num::rational::Ratio;
use num::bigint::{BigInt, Sign, ToBigInt};
use num::traits::Zero;
use std::fmt::{Display, Error, Formatter};
use std::marker::{PhantomData, PhantomFn};
use std::ops::{Add, Div, Mul, Neg};
use MetricPrefix::*;
type BigBase = BigInt;
#[derive(Clone, PartialEq, Eq)]
struct Power { sign: Sign, digit: usize }
impl Power {
fn new(sign: Sign, digit: usize) -> Power {
if sign != Sign::NoSign {
Power { sign: sign, digit: digit }
} else {
Power { sign: sign, digit: 0usize }
}
}
#[unstable]
fn from_i64(digit: i64) -> Power {
if digit.is_positive() {
Power { sign: Sign::Plus, digit: digit as usize }
} else if digit.is_negative() {
Power { sign: Sign::Minus, digit: digit.abs() as usize }
} else {
Power { sign: Sign::NoSign, digit: 0usize }
}
}
#[unstable]
fn to_i64(&self) -> i64 {
if self.is_positive() {
self.digit as i64
} else if self.is_negative() {
(self.digit as i64).neg()
} else {
self.digit as i64
}
}
fn is_positive(&self) -> bool { self.sign == Sign::Plus }
fn is_negative(&self) -> bool { self.sign == Sign::Minus }
fn is_zero(&self) -> bool { self.sign == Sign::NoSign }
}
impl Add for Power {
type Output = Power;
fn add(self, _rhs: Power) -> Power {
match self {
_ if self.is_positive() => {
if _rhs.is_positive() {
Power::new(Sign::Plus, self.digit + _rhs.digit)
} else if _rhs.is_negative() {
Power::from_i64(self.to_i64() + _rhs.to_i64())
} else {
self
}
},
_ if self.is_negative() => {
if _rhs.is_positive() {
Power::from_i64(self.to_i64() + _rhs.to_i64())
} else if _rhs.is_negative() {
Power::new(Sign::Minus, self.digit + _rhs.digit)
} else {
self
}
},
_ => _rhs
}
}
}
pub struct BigExponentiation(BigBase, Power);
impl BigExponentiation {
pub fn new_base10(sign: Sign, digit: usize) -> BigExponentiation {
let base = (10i32).to_bigint().unwrap();
let power = Power::new(sign, digit);
return BigExponentiation(base, power);
}
fn calculate(self) -> Ratio<BigInt> {
let BigExponentiation(base, pow) = self;
if pow.is_positive() {
return num::pow(Ratio::from_integer(base), pow.digit);
} else if pow.is_negative() {
assert!(!base.is_zero(), "dividing by zero is not allowed");
let divisor = num::pow(Ratio::from_integer(base), pow.digit);
let dividend = Ratio::from_integer((1i8).to_bigint().unwrap());
return dividend / divisor;
} else {
let bigint = (1i8).to_bigint().unwrap();
return Ratio::from_integer(bigint);
}
}
}
#[derive(Clone, PartialEq, Eq)]
enum MetricPrefix {
Yotta, Zetta, Exa, Peta, Tera,
Giga, Mega, Kilo, Hecto, Deca,
NoMetricPrefix,
Deci, Centi, Milli, Micro, Nano,
Pico, Femto, Atto, Zepto, Yocto
}
impl MetricPrefix {
pub fn name(&self) -> &str { self.prefix().0 }
pub fn symbol(&self) -> &str { self.prefix().1 }
pub fn exponential(&self) -> BigExponentiation { self.prefix().2 }
pub fn multiplier(&self) -> Ratio<BigInt> { self.prefix().2.calculate() }
fn prefix(&self) -> (&str, &str, BigExponentiation) {
match *self {
Yotta => ("yotta", "Y", BigExponentiation::new_base10(Sign::Plus, 24)),
Zetta => ("zetta", "Z", BigExponentiation::new_base10(Sign::Plus, 21)),
Exa => ("exa", "E", BigExponentiation::new_base10(Sign::Plus, 18)),
Peta => ("peta", "P", BigExponentiation::new_base10(Sign::Plus, 15)),
Tera => ("tera", "T", BigExponentiation::new_base10(Sign::Plus, 12)),
Giga => ("giga", "G", BigExponentiation::new_base10(Sign::Plus, 9)),
Mega => ("mega", "M", BigExponentiation::new_base10(Sign::Plus, 6)),
Kilo => ("kilo", "k", BigExponentiation::new_base10(Sign::Plus, 3)),
Hecto => ("hecto", "h", BigExponentiation::new_base10(Sign::Plus, 2)),
Deca => ("deca", "da", BigExponentiation::new_base10(Sign::Plus, 1)),
NoMetricPrefix => ("", "", BigExponentiation::new_base10(Sign::NoSign, 0)),
Deci => ("deci", "d", BigExponentiation::new_base10(Sign::Minus, 1)),
Centi => ("centi", "c", BigExponentiation::new_base10(Sign::Minus, 2)),
Milli => ("milli", "m", BigExponentiation::new_base10(Sign::Minus, 3)),
Micro => ("micro", "µ", BigExponentiation::new_base10(Sign::Minus, 6)),
Nano => ("nano", "n", BigExponentiation::new_base10(Sign::Minus, 9)),
Pico => ("pico", "p", BigExponentiation::new_base10(Sign::Minus, 12)),
Femto => ("femto", "f", BigExponentiation::new_base10(Sign::Minus, 15)),
Atto => ("atto", "a", BigExponentiation::new_base10(Sign::Minus, 18)),
Zepto => ("zepto", "z", BigExponentiation::new_base10(Sign::Minus, 21)),
Yocto => ("yocto", "y", BigExponentiation::new_base10(Sign::Minus, 24))
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum UnitType {
Ampere, Candela, Gram, Kelvin, Metre, Mole, Radian, Second, Steradian
}
impl UnitType {
fn names(&self) -> [&str; 2] {
match *self {
UnitType::Ampere => ["ampere", "A"],
UnitType::Candela => ["candela", "cd"],
UnitType::Gram => ["gram", "g"],
UnitType::Kelvin => ["kelvin", "K"],
UnitType::Metre => ["metre", "m"],
UnitType::Mole => ["mole", "mol"],
UnitType::Radian => ["radian", "rad"],
UnitType::Second => ["second", "s"],
UnitType::Steradian => ["steradian", "sr"]
}
}
}
#[derive(Clone)]
pub struct Unit {
prefix: MetricPrefix,
typ: UnitType,
pow: Power
}
impl Unit {
fn new(prefix: MetricPrefix, typ: UnitType) -> Unit {
Unit {
prefix: prefix,
typ: typ,
pow: Power::new(Sign::Plus, 1)
}
}
fn flip_sign(mut self) -> Unit {
assert!(self.pow.sign != Sign::NoSign);
self.pow.sign = if self.pow.sign == Sign::Plus { Sign::Minus } else { Sign::Plus };
return self;
}
fn flip_sign_ref(&mut self) {
assert!(self.pow.sign != Sign::NoSign);
self.pow.sign = if self.pow.sign == Sign::Plus { Sign::Minus } else { Sign::Plus };
}
}
pub trait Dim: PhantomFn<Self> + Clone { }
#[derive(Clone)]
pub struct SI<D> where D: Dim {
prefix: MetricPrefix,
magn: Ratio<BigInt>,
uncertainty: Ratio<BigInt>,
dims: Vec<Unit>,
_marker: PhantomData<D>,
}
impl<D: Dim> SI<D> {
fn new_generic(prefix: MetricPrefix, dims: Vec<Unit>) -> SI<D> {
SI {
prefix: prefix,
magn: Ratio::from_integer((1i8).to_bigint().unwrap()),
uncertainty: Ratio::from_integer((1i8).to_bigint().unwrap()),
dims: dims,
_marker: PhantomData,
}
}
fn flip_signs(mut self) -> SI<D> {
for dim in self.dims.iter_mut() {
dim.flip_sign_ref();
}
return self;
}
fn apply_prefixes(&mut self) {
let mut magnitude = self.prefix.multiplier();
magnitude = self.magn.clone() * magnitude;
for dim in self.dims.iter_mut() {
match dim.typ {
UnitType::Gram if dim.prefix.ne(&Kilo) => { magnitude = magnitude * dim.prefix.multiplier();
dim.prefix = Kilo;
},
_ if dim.typ.ne(&UnitType::Gram) => {
dim.prefix = NoMetricPrefix;
magnitude = magnitude * dim.prefix.multiplier();
},
_ => {}
}
}
self.magn = magnitude;
}
pub fn check_dims(&mut self) {
self.dims.retain(|ref dim| dim.pow.sign != Sign::NoSign)
}
}
impl Derived {
pub fn pow(mut self, _rhs: i8) -> Derived {
if _rhs.is_positive() {
for dim in self.dims.iter_mut() {
dim.pow.digit = dim.pow.digit * _rhs as usize;
}
self.magn = num::pow(self.magn.clone(), _rhs as usize)
} else if _rhs.is_negative() {
assert!(!self.magn.is_zero(), "dividing by zero is not allowed");
for dim in self.dims.iter_mut() {
if dim.pow.is_negative() {
dim.pow.sign = Sign::Plus;
} else if dim.pow.is_positive() {
dim.pow.sign = Sign::Minus;
}
dim.pow.digit = dim.pow.digit / _rhs as usize;
}
self.magn =
Ratio::from_integer((1i8).to_bigint().unwrap())
/ num::pow(self.magn.clone(), _rhs.abs() as usize);
} else {
self.dims.clear();
self.magn = Ratio::from_integer((1i8).to_bigint().unwrap());
}
self
}
}
macro_rules! construct_units { ($($name:ident),*) => (
mod basic {
use Dim;
#[derive(Clone)]
pub struct Derived;
impl Dim for Derived { }
#[derive(Clone)]
pub struct Kilogram;
impl Dim for Kilogram { }
$(
#[derive(Clone)]
pub struct $name;
impl Dim for $name { }
)*
}
pub type Derived = SI<basic::Derived>;
pub type Kilogram = SI<basic::Derived>;
$(
pub type $name = SI<basic::$name>;
impl $name {
pub fn new() -> $name {
$name::new_generic(NoMetricPrefix, vec![Unit::new(NoMetricPrefix, UnitType::$name)])
}
}
)*
)
}
construct_units!(Ampere, Candela, Kelvin, Metre, Mole, Radian, Second, Steradian);
impl Derived {
pub fn per(self, mut _rhs: Derived) -> Derived {
self.div(_rhs)
}
pub fn named(&self) -> String {
unimplemented!()
}
}
impl Kelvin {
pub fn to_celsius(&self) -> String {
unimplemented!()
}
pub fn to_fahrenheit(&self) -> String {
unimplemented!()
}
}
impl Metre {
pub fn as_angstrom(mut self) -> String {
assert!(self.dims.len() == 1 && self.dims[0].typ == UnitType::Metre);
assert!(self.dims[0].pow.digit == 1);
self.apply_prefixes();
let magnitude = self.magn * Ratio::from_float(10f64.powi(10)).unwrap();
return format!("{} angstrom", magnitude);
}
}
impl Kilogram {
pub fn as_dalton(&self) -> String {
unimplemented!()
}
pub fn as_amu(&self) -> String {
unimplemented!()
}
}
impl<D: Dim> Mul<f64> for SI<D> {
type Output = SI<D>;
fn mul(mut self, _rhs: f64) -> SI<D> {
self.magn = self.magn.clone() * Ratio::from_float(_rhs).unwrap();
self
}
}
impl<D: Dim> Mul<SI<D>> for f64 {
type Output = SI<D>;
fn mul(self, _rhs: SI<D>) -> SI<D> {
_rhs.mul(self)
}
}
impl<D: Dim> Mul<Ratio<BigInt>> for SI<D> {
type Output = SI<D>;
fn mul(mut self, _rhs: Ratio<BigInt>) -> SI<D> {
self.magn = self.magn.clone() * _rhs;
self
}
}
impl<D: Dim> Mul<SI<D>> for Ratio<BigInt> {
type Output = SI<D>;
fn mul(self, _rhs: SI<D>) -> SI<D> {
_rhs.mul(self)
}
}
impl<D: Dim> Mul<BigExponentiation> for SI<D> {
type Output = SI<D>;
fn mul(mut self, _rhs: BigExponentiation) -> SI<D> {
self.magn = self.magn.clone() * _rhs.calculate();
self
}
}
impl<D: Dim> Mul<SI<D>> for BigExponentiation {
type Output = SI<D>;
fn mul(self, _rhs: SI<D>) -> SI<D> {
_rhs.mul(self)
}
}
impl Mul for Derived {
type Output = Derived;
fn mul(mut self, mut _rhs: Derived) -> Derived {
self.apply_prefixes();
_rhs.apply_prefixes();
Derived {
prefix: NoMetricPrefix,
magn: self.magn * _rhs.magn,
uncertainty: Ratio::zero(),
dims: {
let mut dim_vec: Vec<Unit> = Vec::new();
for dim in self.dims.iter() {
let mut inserted = false;
for _dim in _rhs.dims.iter() {
if dim.typ.eq(&_dim.typ) {
let pow = dim.pow.clone() + _dim.pow.clone();
if !pow.is_zero() && !inserted {
dim_vec.push(Unit {
prefix: if dim.typ.eq(&UnitType::Gram) { Kilo } else { NoMetricPrefix },
typ: dim.typ.clone(),
pow: pow
});
inserted = true;
}
} else {
if !inserted {
dim_vec.push(dim.clone());
inserted = true;
}
}
}
}
_rhs.dims.retain(|ref _dim| {
let mut is_in_vec = false;
for dim in dim_vec.iter() {
if dim.typ.eq(&_dim.typ) {
is_in_vec = true
}
}
!is_in_vec
});
for _dim in _rhs.dims.iter() {
dim_vec.push(_dim.clone());
}
dim_vec
},
_marker: PhantomData
}
}
}
impl Div<Derived> for Derived {
type Output = Derived;
fn div(self, mut _rhs: Derived) -> Derived {
let _rhs = _rhs.flip_signs();
self.mul(_rhs)
}
}
impl<D: Dim> Display for SI<D> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
let mut as_str = String::new();
for (index, dim) in self.dims.iter().enumerate() {
if index.ne(&0) {
as_str.push_str(" * ")
}
as_str.push_str(dim.prefix.prefix().1);
as_str.push_str(dim.typ.names()[1]);
if dim.pow == Power::new(Sign::Plus, 1usize) {
} else if dim.pow.sign == Sign::Plus {
as_str.push_str("^");
as_str.push_str(dim.pow.digit.to_string().as_ref());
} else {
as_str.push_str("^-");
as_str.push_str(dim.pow.digit.to_string().as_ref());
};
}
write!(fmt, "{} {}{}", self.magn, self.prefix.prefix().0, as_str)
}
}
pub fn ampere() -> Derived {
Derived::new_generic(
NoMetricPrefix, vec![Unit::new(NoMetricPrefix, UnitType::Ampere)]
)
}
pub fn candela() -> Derived {
Derived::new_generic(
NoMetricPrefix, vec![Unit::new(NoMetricPrefix, UnitType::Candela)]
)
}
pub fn kelvin() -> Derived {
Derived::new_generic(
NoMetricPrefix, vec![Unit::new(NoMetricPrefix, UnitType::Kelvin)]
)
}
pub fn kilogram() -> Derived {
Derived::new_generic(
NoMetricPrefix, vec![Unit::new(Kilo, UnitType::Gram)]
)
}
pub fn metre() -> Derived {
Derived::new_generic(
NoMetricPrefix, vec![Unit::new(NoMetricPrefix, UnitType::Metre)]
)
}
pub fn mole() -> Derived {
Derived::new_generic(
NoMetricPrefix, vec![Unit::new(NoMetricPrefix, UnitType::Mole)]
)
}
pub fn second() -> Derived {
Derived::new_generic(
NoMetricPrefix, vec![Unit::new(NoMetricPrefix, UnitType::Second)]
)
}
pub fn radian() -> Derived {
Derived::new_generic(
NoMetricPrefix, vec![Unit::new(NoMetricPrefix, UnitType::Radian)]
)
}
pub fn steradian() -> Derived {
Derived::new_generic(
NoMetricPrefix, vec![Unit::new(NoMetricPrefix, UnitType::Steradian)]
)
}