1use crate::{Dimension, Quantity, Unit};
19use qtty_derive::Unit;
20
21pub enum Mass {}
23impl Dimension for Mass {}
24
25pub trait MassUnit: Unit<Dim = Mass> {}
27impl<T: Unit<Dim = Mass>> MassUnit for T {}
28
29#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
31#[unit(symbol = "g", dimension = Mass, ratio = 1.0)]
32pub struct Gram;
33pub type Grams = Quantity<Gram>;
35pub const G: Grams = Grams::new(1.0);
37
38macro_rules! si_gram {
49 ($name:ident, $sym:literal, $ratio:expr, $alias:ident, $qty:ident, $one:ident) => {
50 #[doc = concat!("SI mass unit `", stringify!($name), "` with gram-based prefix (symbol `", $sym,"`).")]
51 #[doc = concat!("By definition, `1 ", $sym, " = ", stringify!($ratio), " g`.")]
52 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
53 #[unit(symbol = $sym, dimension = Mass, ratio = $ratio)]
54 pub struct $name;
55
56 #[doc = concat!("Shorthand alias for [`", stringify!($name), "`]." )]
57 pub type $alias = $name;
58
59 #[doc = concat!("Quantity measured in ", stringify!($name), " (",$sym,").")]
60 pub type $qty = Quantity<$alias>;
61
62 #[doc = concat!("Constant equal to one ", stringify!($name), " (1 ",$sym,").")]
63 pub const $one: $qty = $qty::new(1.0);
64 };
65}
66
67si_gram!(Yoctogram, "yg", 1e-24, Yg, Yoctograms, YG);
69si_gram!(Zeptogram, "zg", 1e-21, Zg, Zeptograms, ZG);
70si_gram!(Attogram, "ag", 1e-18, Ag, Attograms, AG);
71si_gram!(Femtogram, "fg", 1e-15, Fg, Femtograms, FG);
72si_gram!(Picogram, "pg", 1e-12, Pg, Picograms, PG);
73si_gram!(Nanogram, "ng", 1e-9, Ng, Nanograms, NG);
74si_gram!(Microgram, "µg", 1e-6, Ug, Micrograms, UG);
75si_gram!(Milligram, "mg", 1e-3, Mg, Milligrams, MG);
76si_gram!(Centigram, "cg", 1e-2, Cg, Centigrams, CG);
77si_gram!(Decigram, "dg", 1e-1, Dg, Decigrams, DG);
78
79si_gram!(Decagram, "dag", 1e1, Dag, Decagrams, DAG);
80si_gram!(Hectogram, "hg", 1e2, Hg, Hectograms, HG);
81si_gram!(Kilogram, "kg", 1e3, Kg, Kilograms, KG);
82si_gram!(Megagram, "Mg", 1e6, MgG, Megagrams, MEGAGRAM);
83si_gram!(Gigagram, "Gg", 1e9, Gg, Gigagrams, GG);
84si_gram!(Teragram, "Tg", 1e12, Tg, Teragrams, TG);
85si_gram!(Petagram, "Pg", 1e15, PgG, Petagrams, PETAGRAM);
86si_gram!(Exagram, "Eg", 1e18, Eg, Exagrams, EG);
87si_gram!(Zettagram, "Zg", 1e21, ZgG, Zettagrams, ZETTAGRAM);
88si_gram!(Yottagram, "Yg", 1e24, YgG, Yottagrams, YOTTAGRAM);
89
90#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
92#[unit(symbol = "t", dimension = Mass, ratio = 1_000_000.0)]
93pub struct Tonne;
94pub type T = Tonne;
96pub type Tonnes = Quantity<T>;
98pub const TONE: Tonnes = Tonnes::new(1.0);
100
101#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
103#[unit(symbol = "ct", dimension = Mass, ratio = 1.0 / 5.0)]
104pub struct Carat;
105pub type Ct = Carat;
107pub type Carats = Quantity<Ct>;
109pub const CT: Carats = Carats::new(1.0);
111
112#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
114#[unit(symbol = "gr", dimension = Mass, ratio = 6_479_891.0 / 1_000_000_000.0)]
115pub struct Grain;
116pub type Gr = Grain;
118pub type Grains = Quantity<Gr>;
120pub const GR: Grains = Grains::new(1.0);
122
123#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
125#[unit(symbol = "lb", dimension = Mass, ratio = 45_359_237.0 / 100_000.0)]
126pub struct Pound;
127pub type Lb = Pound;
129pub type Pounds = Quantity<Lb>;
131pub const LB: Pounds = Pounds::new(1.0);
133
134#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
136#[unit(symbol = "oz", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) / 16.0)]
137pub struct Ounce;
138pub type Oz = Ounce;
140pub type Ounces = Quantity<Oz>;
142pub const OZ: Ounces = Ounces::new(1.0);
144
145#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
147#[unit(symbol = "st", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) * 14.0)]
148pub struct Stone;
149pub type St = Stone;
151pub type Stones = Quantity<St>;
153pub const ST: Stones = Stones::new(1.0);
155
156#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
158#[unit(symbol = "ton_us", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) * 2000.0)]
159pub struct ShortTon;
160pub type ShortTons = Quantity<ShortTon>;
162pub const TON_US: ShortTons = ShortTons::new(1.0);
164
165#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
167#[unit(symbol = "ton_uk", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) * 2240.0)]
168pub struct LongTon;
169pub type LongTons = Quantity<LongTon>;
171pub const TON_UK: LongTons = LongTons::new(1.0);
173
174#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
178#[unit(symbol = "u", dimension = Mass, ratio = 1.660_539_068_92e-24)]
179pub struct AtomicMassUnit;
180pub type Dalton = AtomicMassUnit;
182pub type AtomicMassUnits = Quantity<AtomicMassUnit>;
184pub const U: AtomicMassUnits = AtomicMassUnits::new(1.0);
186
187#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
191#[unit(symbol = "M☉", dimension = Mass, ratio = 1.988_416e33)]
192pub struct SolarMass;
193pub type SolarMasses = Quantity<SolarMass>;
195pub const MSUN: SolarMasses = SolarMasses::new(1.0);
197
198crate::impl_unit_conversions!(
200 Gram,
201 Yoctogram,
202 Zeptogram,
203 Attogram,
204 Femtogram,
205 Picogram,
206 Nanogram,
207 Microgram,
208 Milligram,
209 Centigram,
210 Decigram,
211 Decagram,
212 Hectogram,
213 Kilogram,
214 Megagram,
215 Gigagram,
216 Teragram,
217 Petagram,
218 Exagram,
219 Zettagram,
220 Yottagram,
221 Tonne,
222 Carat,
223 Grain,
224 Pound,
225 Ounce,
226 Stone,
227 ShortTon,
228 LongTon,
229 AtomicMassUnit,
230 SolarMass
231);
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236 use approx::{assert_abs_diff_eq, assert_relative_eq};
237 use proptest::prelude::*;
238
239 #[test]
244 fn gram_to_kilogram() {
245 let g = Grams::new(1000.0);
246 let kg = g.to::<Kilogram>();
247 assert_abs_diff_eq!(kg.value(), 1.0, epsilon = 1e-12);
248 }
249
250 #[test]
251 fn kilogram_to_gram() {
252 let kg = Kilograms::new(1.0);
253 let g = kg.to::<Gram>();
254 assert_abs_diff_eq!(g.value(), 1000.0, epsilon = 1e-9);
255 }
256
257 #[test]
258 fn solar_mass_to_grams() {
259 let sm = SolarMasses::new(1.0);
260 let g = sm.to::<Gram>();
261 assert_relative_eq!(g.value(), 1.988416e33, max_relative = 1e-5);
263 }
264
265 #[test]
266 fn solar_mass_to_kilograms() {
267 let sm = SolarMasses::new(1.0);
268 let kg = sm.to::<Kilogram>();
269 assert_relative_eq!(kg.value(), 1.988416e30, max_relative = 1e-5);
271 }
272
273 #[test]
274 fn kilograms_to_solar_mass() {
275 let earth_kg = Kilograms::new(5.97e24);
277 let earth_sm = earth_kg.to::<SolarMass>();
278 assert_relative_eq!(earth_sm.value(), 3.0e-6, max_relative = 0.01);
279 }
280
281 #[test]
286 fn solar_mass_ratio_sanity() {
287 assert_relative_eq!(SolarMass::RATIO, 1.988416e33, max_relative = 1e-5);
289 }
290
291 #[test]
292 fn solar_mass_order_of_magnitude() {
293 let sun = SolarMasses::new(1.0);
295 let kg = sun.to::<Kilogram>();
296 assert!(kg.value() > 1e30);
297 assert!(kg.value() < 1e31);
298 }
299
300 #[test]
305 fn roundtrip_g_kg() {
306 let original = Grams::new(5000.0);
307 let converted = original.to::<Kilogram>();
308 let back = converted.to::<Gram>();
309 assert_abs_diff_eq!(back.value(), original.value(), epsilon = 1e-9);
310 }
311
312 #[test]
313 fn roundtrip_kg_solar() {
314 let original = Kilograms::new(1e30);
315 let converted = original.to::<SolarMass>();
316 let back = converted.to::<Kilogram>();
317 assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
318 }
319
320 proptest! {
325 #[test]
326 fn prop_roundtrip_g_kg(g in 1e-6..1e6f64) {
327 let original = Grams::new(g);
328 let converted = original.to::<Kilogram>();
329 let back = converted.to::<Gram>();
330 prop_assert!((back.value() - original.value()).abs() < 1e-9 * g.abs().max(1.0));
331 }
332
333 #[test]
334 fn prop_g_kg_ratio(g in 1e-6..1e6f64) {
335 let grams = Grams::new(g);
336 let kg = grams.to::<Kilogram>();
337 prop_assert!((grams.value() / kg.value() - 1000.0).abs() < 1e-9);
339 }
340 }
341}