use std::ops::{Add, Div, Mul, MulAssign, Sub};
use std::str::FromStr;
#[cfg(test)]
macro_rules! assert_eq_err {
($lhs: expr, $rhs: expr, $err: expr) => {
let lhs = $lhs as f64;
let rhs = $rhs as f64;
if (1.0 - lhs / rhs).abs() >= $err {
dbg!(lhs);
dbg!(rhs);
assert!(false);
}
};
($lhs: expr, $rhs: expr) => {
assert_eq_err!($lhs, $rhs, 1e-6)
};
}
#[cfg(test)]
macro_rules! assert_eq_q {
($lhs: expr, $rhs: expr, $err: expr) => {
assert_eq!($lhs.dimension(), $rhs.dimension());
assert_eq_err!($lhs.magnitude(), $rhs.magnitude(), $err);
};
($lhs: expr, $rhs: expr) => {
assert_eq_q!($lhs, $rhs, 1e-6)
};
}
pub mod ast;
use ast::*;
pub mod dimension;
use dimension::*;
pub mod error;
use error::*;
pub mod parser;
use parser::*;
pub mod quantity;
use quantity::*;
pub mod special;
pub mod system;
use system::*;
pub mod prelude {
pub use crate::quantity::Quantity;
pub use crate::system::{UnitSystem, UnitSystemFactory};
}
#[cfg(test)]
mod test {
use super::*;
use test_case::test_case;
#[test_case("1000 ms", "1 s")]
#[test_case("2 dam2", "200m2")]
#[test_case("200 dm2", "2m2")]
#[test_case("3J", "3 kg.m2/s2")]
#[test_case("3kHz", "3000s-1")]
#[test_case("40 m/s", "40 m.s-1")]
#[test_case("41 m/s2", "41 m.s-2")]
#[test_case("42 m/s2", "42 m/(s.s)")]
#[test_case("43 m/s2", "43 s-2/m-1")]
#[test_case("44 m/s2", "4 s-2.11/m-1")]
fn equivalent(txt1: &str, txt2: &str) {
let system = UnitSystem::<f64>::default();
let q1 = system.parse(txt1).unwrap();
let q2 = system.parse(txt2).unwrap();
assert_eq!(q1.dimension(), q2.dimension());
assert_eq_err!(q1.magnitude(), q2.magnitude(), 1e-6);
}
#[test_case("1m", "2m")]
#[test_case("10cm", "2m")]
#[test_case("1e6um", "2m")]
fn commensurable(txt1: &str, txt2: &str) {
let system = UnitSystem::<f64>::default();
let q1 = system.parse(txt1).unwrap();
let q2 = system.parse(txt2).unwrap();
assert!(q1 <= q2);
assert!(!(q1 >= q2));
}
#[test_case("1m", "1g")]
#[test_case("1J", "1g")]
#[test_case("1J", "1W")]
fn not_commensurable(txt1: &str, txt2: &str) {
let system = UnitSystem::<f64>::default();
let q1 = system.parse(txt1).unwrap();
let q2 = system.parse(txt2).unwrap();
assert!(!(q1 <= q2));
assert!(!(q1 >= q2));
}
#[test]
fn special_alone() {
let system = UnitSystem::<f64>::default();
let q1 = system.parse("0 Cel").unwrap();
let q2 = system.parse("273.15 K").unwrap();
assert_eq_q!(q1, q2);
}
#[test]
fn special_combined() {
let system = UnitSystem::<f64>::default();
let u1 = system.parse("1 Cel/s").unwrap();
let u2 = system.parse("1 K/s").unwrap();
assert_eq!(u1, u2);
}
#[test]
fn special_exponent() {
let system = UnitSystem::<f64>::default();
let u1 = system.parse("2 Cel2").unwrap();
let u2 = system.parse("2 K2").unwrap();
assert_eq!(u1, u2);
}
#[test]
fn celsius_is_metric() {
let system = UnitSystem::<f64>::default();
let q1 = system.parse("1 kCel").unwrap();
let q2 = system.parse("1273.15 K").unwrap();
assert_eq_q!(q1, q2);
}
#[test]
fn ten_star() {
let system = UnitSystem::<f64>::default();
let q1 = system.parse("10*").unwrap();
let q2 = system.parse("10 {dimless}").unwrap();
assert_eq_q!(q1, q2);
}
#[test]
fn ten_carret() {
let system = UnitSystem::<f64>::default();
let q1 = system.parse("10^").unwrap();
let q2 = system.parse("10 {dimless}").unwrap();
assert_eq_q!(q1, q2);
}
#[test_case("s", "s")]
#[test_case("M", "m")]
#[test_case("CM", "cm")]
#[test_case("km", "km")]
#[test_case("mG", "mg")]
#[test_case("Ms", "ms")]
#[test_case("mam", "Mm")]
#[test_case("HPAL", "hPa")]
#[test_case("Sie", "S")]
fn case_insensitive(txt1: &str, txt2: &str) {
let ci_system = UnitSystemFactory::<f64>::new()
.case_sensitive(false)
.build();
let cs_system = UnitSystem::<f64>::default();
let q1 = ci_system.parse(txt1).unwrap();
let q2 = cs_system.parse(txt2).unwrap();
assert_eq!(q1, q2);
}
}