use crate::{Quantity, Unit};
use qtty_derive::Unit;
pub use crate::dimension::Mass;
pub trait MassUnit: Unit<Dim = Mass> {}
impl<T: Unit<Dim = Mass>> MassUnit for T {}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "g", dimension = Mass, ratio = 1.0)]
pub struct Gram;
pub type Grams = Quantity<Gram>;
pub const G: Grams = Grams::new(1.0);
macro_rules! si_gram {
($name:ident, $sym:literal, $ratio:expr, $alias:ident, $qty:ident, $one:ident) => {
#[doc = concat!("SI mass unit `", stringify!($name), "` with gram-based prefix (symbol `", $sym,"`).")]
#[doc = concat!("By definition, `1 ", $sym, " = ", stringify!($ratio), " g`.")]
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = $sym, dimension = Mass, ratio = $ratio)]
pub struct $name;
#[doc = concat!("Shorthand alias for [`", stringify!($name), "`]." )]
pub type $alias = $name;
#[doc = concat!("Quantity measured in ", stringify!($name), " (",$sym,").")]
pub type $qty = Quantity<$alias>;
#[doc = concat!("Constant equal to one ", stringify!($name), " (1 ",$sym,").")]
pub const $one: $qty = $qty::new(1.0);
};
}
si_gram!(Yoctogram, "yg", 1e-24, Yg, Yoctograms, YG);
si_gram!(Zeptogram, "zg", 1e-21, Zg, Zeptograms, ZG);
si_gram!(Attogram, "ag", 1e-18, Ag, Attograms, AG);
si_gram!(Femtogram, "fg", 1e-15, Fg, Femtograms, FG);
si_gram!(Picogram, "pg", 1e-12, Pg, Picograms, PG);
si_gram!(Nanogram, "ng", 1e-9, Ng, Nanograms, NG);
si_gram!(Microgram, "µg", 1e-6, Ug, Micrograms, UG);
si_gram!(Milligram, "mg", 1e-3, Mg, Milligrams, MG);
si_gram!(Centigram, "cg", 1e-2, Cg, Centigrams, CG);
si_gram!(Decigram, "dg", 1e-1, Dg, Decigrams, DG);
si_gram!(Decagram, "dag", 1e1, Dag, Decagrams, DAG);
si_gram!(Hectogram, "hg", 1e2, Hg, Hectograms, HG);
si_gram!(Kilogram, "kg", 1e3, Kg, Kilograms, KG);
si_gram!(Megagram, "Mg", 1e6, MgG, Megagrams, MEGAGRAM);
si_gram!(Gigagram, "Gg", 1e9, Gg, Gigagrams, GG);
si_gram!(Teragram, "Tg", 1e12, Tg, Teragrams, TG);
si_gram!(Petagram, "Pg", 1e15, PgG, Petagrams, PETAGRAM);
si_gram!(Exagram, "Eg", 1e18, Eg, Exagrams, EG);
si_gram!(Zettagram, "Zg", 1e21, ZgG, Zettagrams, ZETTAGRAM);
si_gram!(Yottagram, "Yg", 1e24, YgG, Yottagrams, YOTTAGRAM);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "t", dimension = Mass, ratio = 1_000_000.0)]
pub struct Tonne;
pub type T = Tonne;
pub type Tonnes = Quantity<T>;
pub const TONE: Tonnes = Tonnes::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "ct", dimension = Mass, ratio = 1.0 / 5.0)]
pub struct Carat;
pub type Ct = Carat;
pub type Carats = Quantity<Ct>;
pub const CT: Carats = Carats::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "gr", dimension = Mass, ratio = 6_479_891.0 / 1_000_000_000.0)]
pub struct Grain;
pub type Gr = Grain;
pub type Grains = Quantity<Gr>;
pub const GR: Grains = Grains::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "lb", dimension = Mass, ratio = 45_359_237.0 / 100_000.0)]
pub struct Pound;
pub type Lb = Pound;
pub type Pounds = Quantity<Lb>;
pub const LB: Pounds = Pounds::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "oz", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) / 16.0)]
pub struct Ounce;
pub type Oz = Ounce;
pub type Ounces = Quantity<Oz>;
pub const OZ: Ounces = Ounces::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "st", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) * 14.0)]
pub struct Stone;
pub type St = Stone;
pub type Stones = Quantity<St>;
pub const ST: Stones = Stones::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "ton_us", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) * 2000.0)]
pub struct ShortTon;
pub type ShortTons = Quantity<ShortTon>;
pub const TON_US: ShortTons = ShortTons::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "ton_uk", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) * 2240.0)]
pub struct LongTon;
pub type LongTons = Quantity<LongTon>;
pub const TON_UK: LongTons = LongTons::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "u", dimension = Mass, ratio = 1.660_539_068_92e-24)]
pub struct AtomicMassUnit;
pub type Dalton = AtomicMassUnit;
pub type AtomicMassUnits = Quantity<AtomicMassUnit>;
pub const U: AtomicMassUnits = AtomicMassUnits::new(1.0);
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
#[unit(symbol = "M☉", dimension = Mass, ratio = 1.988_416e33)]
pub struct SolarMass;
pub type SolarMasses = Quantity<SolarMass>;
pub const MSUN: SolarMasses = SolarMasses::new(1.0);
crate::impl_unit_from_conversions!(
Gram,
Yoctogram,
Zeptogram,
Attogram,
Femtogram,
Picogram,
Nanogram,
Microgram,
Milligram,
Centigram,
Decigram,
Decagram,
Hectogram,
Kilogram,
Megagram,
Gigagram,
Teragram,
Petagram,
Exagram,
Zettagram,
Yottagram,
Tonne,
Carat,
Grain,
Pound,
Ounce,
Stone,
ShortTon,
LongTon,
AtomicMassUnit,
SolarMass
);
#[cfg(feature = "cross-unit-ops")]
crate::impl_unit_cross_unit_ops!(
Gram,
Yoctogram,
Zeptogram,
Attogram,
Femtogram,
Picogram,
Nanogram,
Microgram,
Milligram,
Centigram,
Decigram,
Decagram,
Hectogram,
Kilogram,
Megagram,
Gigagram,
Teragram,
Petagram,
Exagram,
Zettagram,
Yottagram,
Tonne,
Carat,
Grain,
Pound,
Ounce,
Stone,
ShortTon,
LongTon,
AtomicMassUnit,
SolarMass
);
#[cfg(test)]
mod tests {
use super::*;
use approx::{assert_abs_diff_eq, assert_relative_eq};
use proptest::prelude::*;
#[test]
fn gram_to_kilogram() {
let g = Grams::new(1000.0);
let kg = g.to::<Kilogram>();
assert_abs_diff_eq!(kg.value(), 1.0, epsilon = 1e-12);
}
#[test]
fn kilogram_to_gram() {
let kg = Kilograms::new(1.0);
let g = kg.to::<Gram>();
assert_abs_diff_eq!(g.value(), 1000.0, epsilon = 1e-9);
}
#[test]
fn solar_mass_to_grams() {
let sm = SolarMasses::new(1.0);
let g = sm.to::<Gram>();
assert_relative_eq!(g.value(), 1.988416e33, max_relative = 1e-5);
}
#[test]
fn solar_mass_to_kilograms() {
let sm = SolarMasses::new(1.0);
let kg = sm.to::<Kilogram>();
assert_relative_eq!(kg.value(), 1.988416e30, max_relative = 1e-5);
}
#[test]
fn kilograms_to_solar_mass() {
let earth_kg = Kilograms::new(5.97e24);
let earth_sm = earth_kg.to::<SolarMass>();
assert_relative_eq!(earth_sm.value(), 3.0e-6, max_relative = 0.01);
}
#[test]
fn solar_mass_ratio_sanity() {
assert_relative_eq!(SolarMass::RATIO, 1.988416e33, max_relative = 1e-5);
}
#[test]
fn solar_mass_order_of_magnitude() {
let sun = SolarMasses::new(1.0);
let kg = sun.to::<Kilogram>();
assert!(kg.value() > 1e30);
assert!(kg.value() < 1e31);
}
#[test]
fn roundtrip_g_kg() {
let original = Grams::new(5000.0);
let converted = original.to::<Kilogram>();
let back = converted.to::<Gram>();
assert_abs_diff_eq!(back.value(), original.value(), epsilon = 1e-9);
}
#[test]
fn roundtrip_kg_solar() {
let original = Kilograms::new(1e30);
let converted = original.to::<SolarMass>();
let back = converted.to::<Kilogram>();
assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
}
proptest! {
#[test]
fn prop_roundtrip_g_kg(g in 1e-6..1e6f64) {
let original = Grams::new(g);
let converted = original.to::<Kilogram>();
let back = converted.to::<Gram>();
prop_assert!((back.value() - original.value()).abs() < 1e-9 * g.abs().max(1.0));
}
#[test]
fn prop_g_kg_ratio(g in 1e-6..1e6f64) {
let grams = Grams::new(g);
let kg = grams.to::<Kilogram>();
prop_assert!((grams.value() / kg.value() - 1000.0).abs() < 1e-9);
}
}
#[test]
fn tonne_to_kilogram() {
let t = Tonnes::new(1.0);
let kg = t.to::<Kilogram>();
assert_relative_eq!(kg.value(), 1_000.0, max_relative = 1e-12);
}
#[test]
fn carat_to_gram() {
let ct = Carats::new(5.0);
let g = ct.to::<Gram>();
assert_relative_eq!(g.value(), 1.0, max_relative = 1e-12);
}
#[test]
fn grain_to_milligram() {
let gr = Grains::new(1.0);
let mg = gr.to::<Milligram>();
assert_relative_eq!(mg.value(), 6.479_891, max_relative = 1e-6);
}
#[test]
fn pound_to_gram() {
let lb = Pounds::new(1.0);
let g = lb.to::<Gram>();
assert_relative_eq!(g.value(), 453.592_37, max_relative = 1e-9);
}
#[test]
fn ounce_to_gram() {
let oz = Ounces::new(16.0);
let g = oz.to::<Gram>();
assert_relative_eq!(g.value(), 453.592_37, max_relative = 1e-9);
}
#[test]
fn stone_to_pound() {
let st = Stones::new(1.0);
let lb = st.to::<Pound>();
assert_relative_eq!(lb.value(), 14.0, max_relative = 1e-12);
}
#[test]
fn short_ton_to_pound() {
let ton = ShortTons::new(1.0);
let lb = ton.to::<Pound>();
assert_relative_eq!(lb.value(), 2000.0, max_relative = 1e-12);
}
#[test]
fn long_ton_to_pound() {
let ton = LongTons::new(1.0);
let lb = ton.to::<Pound>();
assert_relative_eq!(lb.value(), 2240.0, max_relative = 1e-12);
}
#[test]
fn atomic_mass_unit_to_gram() {
let u = AtomicMassUnits::new(1.0);
let g = u.to::<Gram>();
assert_relative_eq!(g.value(), 1.660_539_068_92e-24, max_relative = 1e-6);
}
#[test]
fn milligram_to_gram() {
let mg = Milligrams::new(1000.0);
let g = mg.to::<Gram>();
assert_relative_eq!(g.value(), 1.0, max_relative = 1e-12);
}
#[test]
fn microgram_to_milligram() {
let ug = Micrograms::new(1000.0);
let mg = ug.to::<Milligram>();
assert_relative_eq!(mg.value(), 1.0, max_relative = 1e-12);
}
#[test]
fn symbols_are_correct() {
assert_eq!(Kilogram::SYMBOL, "kg");
assert_eq!(Gram::SYMBOL, "g");
assert_eq!(Pound::SYMBOL, "lb");
assert_eq!(Tonne::SYMBOL, "t");
}
}