1use crate::{Quantity, Unit};
34use qtty_derive::Unit;
35
36pub use crate::dimension::Mass;
38
39pub trait MassUnit: Unit<Dim = Mass> {}
41impl<T: Unit<Dim = Mass>> MassUnit for T {}
42
43#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
45#[unit(symbol = "g", dimension = Mass, ratio = 1.0)]
46pub struct Gram;
47pub type Grams = Quantity<Gram>;
49pub const G: Grams = Grams::new(1.0);
51
52macro_rules! si_gram {
63 ($name:ident, $sym:literal, $ratio:expr, $alias:ident, $qty:ident, $one:ident) => {
64 #[doc = concat!("SI mass unit `", stringify!($name), "` with gram-based prefix (symbol `", $sym,"`).")]
65 #[doc = concat!("By definition, `1 ", $sym, " = ", stringify!($ratio), " g`.")]
66 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
67 #[unit(symbol = $sym, dimension = Mass, ratio = $ratio)]
68 pub struct $name;
69
70 #[doc = concat!("Shorthand alias for [`", stringify!($name), "`]." )]
71 pub type $alias = $name;
72
73 #[doc = concat!("Quantity measured in ", stringify!($name), " (",$sym,").")]
74 pub type $qty = Quantity<$alias>;
75
76 #[doc = concat!("Constant equal to one ", stringify!($name), " (1 ",$sym,").")]
77 pub const $one: $qty = $qty::new(1.0);
78 };
79}
80
81si_gram!(Yoctogram, "yg", 1e-24, Yg, Yoctograms, YG);
83si_gram!(Zeptogram, "zg", 1e-21, Zg, Zeptograms, ZG);
84si_gram!(Attogram, "ag", 1e-18, Ag, Attograms, AG);
85si_gram!(Femtogram, "fg", 1e-15, Fg, Femtograms, FG);
86si_gram!(Picogram, "pg", 1e-12, Pg, Picograms, PG);
87si_gram!(Nanogram, "ng", 1e-9, Ng, Nanograms, NG);
88si_gram!(Microgram, "µg", 1e-6, Ug, Micrograms, UG);
89si_gram!(Milligram, "mg", 1e-3, Mg, Milligrams, MG);
90si_gram!(Centigram, "cg", 1e-2, Cg, Centigrams, CG);
91si_gram!(Decigram, "dg", 1e-1, Dg, Decigrams, DG);
92
93si_gram!(Decagram, "dag", 1e1, Dag, Decagrams, DAG);
94si_gram!(Hectogram, "hg", 1e2, Hg, Hectograms, HG);
95si_gram!(Kilogram, "kg", 1e3, Kg, Kilograms, KG);
96si_gram!(Megagram, "Mg", 1e6, MgG, Megagrams, MEGAGRAM);
97si_gram!(Gigagram, "Gg", 1e9, Gg, Gigagrams, GG);
98si_gram!(Teragram, "Tg", 1e12, Tg, Teragrams, TG);
99si_gram!(Petagram, "Pg", 1e15, PgG, Petagrams, PETAGRAM);
100si_gram!(Exagram, "Eg", 1e18, Eg, Exagrams, EG);
101si_gram!(Zettagram, "Zg", 1e21, ZgG, Zettagrams, ZETTAGRAM);
102si_gram!(Yottagram, "Yg", 1e24, YgG, Yottagrams, YOTTAGRAM);
103
104#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
106#[unit(symbol = "t", dimension = Mass, ratio = 1_000_000.0)]
107pub struct Tonne;
108pub type T = Tonne;
110pub type Tonnes = Quantity<T>;
112pub const TONE: Tonnes = Tonnes::new(1.0);
114
115#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
117#[unit(symbol = "ct", dimension = Mass, ratio = 1.0 / 5.0)]
118pub struct Carat;
119pub type Ct = Carat;
121pub type Carats = Quantity<Ct>;
123pub const CT: Carats = Carats::new(1.0);
125
126#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
128#[unit(symbol = "gr", dimension = Mass, ratio = 6_479_891.0 / 1_000_000_000.0)]
129pub struct Grain;
130pub type Gr = Grain;
132pub type Grains = Quantity<Gr>;
134pub const GR: Grains = Grains::new(1.0);
136
137#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
139#[unit(symbol = "lb", dimension = Mass, ratio = 45_359_237.0 / 100_000.0)]
140pub struct Pound;
141pub type Lb = Pound;
143pub type Pounds = Quantity<Lb>;
145pub const LB: Pounds = Pounds::new(1.0);
147
148#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
150#[unit(symbol = "oz", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) / 16.0)]
151pub struct Ounce;
152pub type Oz = Ounce;
154pub type Ounces = Quantity<Oz>;
156pub const OZ: Ounces = Ounces::new(1.0);
158
159#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
161#[unit(symbol = "st", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) * 14.0)]
162pub struct Stone;
163pub type St = Stone;
165pub type Stones = Quantity<St>;
167pub const ST: Stones = Stones::new(1.0);
169
170#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
172#[unit(symbol = "ton_us", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) * 2000.0)]
173pub struct ShortTon;
174pub type ShortTons = Quantity<ShortTon>;
176pub const TON_US: ShortTons = ShortTons::new(1.0);
178
179#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
181#[unit(symbol = "ton_uk", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) * 2240.0)]
182pub struct LongTon;
183pub type LongTons = Quantity<LongTon>;
185pub const TON_UK: LongTons = LongTons::new(1.0);
187
188#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
192#[unit(symbol = "u", dimension = Mass, ratio = 1.660_539_068_92e-24)]
193pub struct AtomicMassUnit;
194pub type Dalton = AtomicMassUnit;
196pub type AtomicMassUnits = Quantity<AtomicMassUnit>;
198pub const U: AtomicMassUnits = AtomicMassUnits::new(1.0);
200
201#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
205#[unit(symbol = "M☉", dimension = Mass, ratio = 1.988_416e33)]
206pub struct SolarMass;
207pub type SolarMasses = Quantity<SolarMass>;
209pub const MSUN: SolarMasses = SolarMasses::new(1.0);
211
212crate::impl_unit_from_conversions!(
214 Gram,
215 Yoctogram,
216 Zeptogram,
217 Attogram,
218 Femtogram,
219 Picogram,
220 Nanogram,
221 Microgram,
222 Milligram,
223 Centigram,
224 Decigram,
225 Decagram,
226 Hectogram,
227 Kilogram,
228 Megagram,
229 Gigagram,
230 Teragram,
231 Petagram,
232 Exagram,
233 Zettagram,
234 Yottagram,
235 Tonne,
236 Carat,
237 Grain,
238 Pound,
239 Ounce,
240 Stone,
241 ShortTon,
242 LongTon,
243 AtomicMassUnit,
244 SolarMass
245);
246
247#[cfg(feature = "cross-unit-ops")]
249crate::impl_unit_cross_unit_ops!(
250 Gram,
251 Yoctogram,
252 Zeptogram,
253 Attogram,
254 Femtogram,
255 Picogram,
256 Nanogram,
257 Microgram,
258 Milligram,
259 Centigram,
260 Decigram,
261 Decagram,
262 Hectogram,
263 Kilogram,
264 Megagram,
265 Gigagram,
266 Teragram,
267 Petagram,
268 Exagram,
269 Zettagram,
270 Yottagram,
271 Tonne,
272 Carat,
273 Grain,
274 Pound,
275 Ounce,
276 Stone,
277 ShortTon,
278 LongTon,
279 AtomicMassUnit,
280 SolarMass
281);
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286 use approx::{assert_abs_diff_eq, assert_relative_eq};
287 use proptest::prelude::*;
288
289 #[test]
294 fn gram_to_kilogram() {
295 let g = Grams::new(1000.0);
296 let kg = g.to::<Kilogram>();
297 assert_abs_diff_eq!(kg.value(), 1.0, epsilon = 1e-12);
298 }
299
300 #[test]
301 fn kilogram_to_gram() {
302 let kg = Kilograms::new(1.0);
303 let g = kg.to::<Gram>();
304 assert_abs_diff_eq!(g.value(), 1000.0, epsilon = 1e-9);
305 }
306
307 #[test]
308 fn solar_mass_to_grams() {
309 let sm = SolarMasses::new(1.0);
310 let g = sm.to::<Gram>();
311 assert_relative_eq!(g.value(), 1.988416e33, max_relative = 1e-5);
313 }
314
315 #[test]
316 fn solar_mass_to_kilograms() {
317 let sm = SolarMasses::new(1.0);
318 let kg = sm.to::<Kilogram>();
319 assert_relative_eq!(kg.value(), 1.988416e30, max_relative = 1e-5);
321 }
322
323 #[test]
324 fn kilograms_to_solar_mass() {
325 let earth_kg = Kilograms::new(5.97e24);
327 let earth_sm = earth_kg.to::<SolarMass>();
328 assert_relative_eq!(earth_sm.value(), 3.0e-6, max_relative = 0.01);
329 }
330
331 #[test]
336 fn solar_mass_ratio_sanity() {
337 assert_relative_eq!(SolarMass::RATIO, 1.988416e33, max_relative = 1e-5);
339 }
340
341 #[test]
342 fn solar_mass_order_of_magnitude() {
343 let sun = SolarMasses::new(1.0);
345 let kg = sun.to::<Kilogram>();
346 assert!(kg.value() > 1e30);
347 assert!(kg.value() < 1e31);
348 }
349
350 #[test]
355 fn roundtrip_g_kg() {
356 let original = Grams::new(5000.0);
357 let converted = original.to::<Kilogram>();
358 let back = converted.to::<Gram>();
359 assert_abs_diff_eq!(back.value(), original.value(), epsilon = 1e-9);
360 }
361
362 #[test]
363 fn roundtrip_kg_solar() {
364 let original = Kilograms::new(1e30);
365 let converted = original.to::<SolarMass>();
366 let back = converted.to::<Kilogram>();
367 assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
368 }
369
370 proptest! {
375 #[test]
376 fn prop_roundtrip_g_kg(g in 1e-6..1e6f64) {
377 let original = Grams::new(g);
378 let converted = original.to::<Kilogram>();
379 let back = converted.to::<Gram>();
380 prop_assert!((back.value() - original.value()).abs() < 1e-9 * g.abs().max(1.0));
381 }
382
383 #[test]
384 fn prop_g_kg_ratio(g in 1e-6..1e6f64) {
385 let grams = Grams::new(g);
386 let kg = grams.to::<Kilogram>();
387 prop_assert!((grams.value() / kg.value() - 1000.0).abs() < 1e-9);
389 }
390 }
391
392 #[test]
395 fn tonne_to_kilogram() {
396 let t = Tonnes::new(1.0);
397 let kg = t.to::<Kilogram>();
398 assert_relative_eq!(kg.value(), 1_000.0, max_relative = 1e-12);
399 }
400
401 #[test]
402 fn carat_to_gram() {
403 let ct = Carats::new(5.0);
404 let g = ct.to::<Gram>();
405 assert_relative_eq!(g.value(), 1.0, max_relative = 1e-12);
407 }
408
409 #[test]
410 fn grain_to_milligram() {
411 let gr = Grains::new(1.0);
412 let mg = gr.to::<Milligram>();
413 assert_relative_eq!(mg.value(), 6.479_891, max_relative = 1e-6);
415 }
416
417 #[test]
418 fn pound_to_gram() {
419 let lb = Pounds::new(1.0);
420 let g = lb.to::<Gram>();
421 assert_relative_eq!(g.value(), 453.592_37, max_relative = 1e-9);
423 }
424
425 #[test]
426 fn ounce_to_gram() {
427 let oz = Ounces::new(16.0);
428 let g = oz.to::<Gram>();
429 assert_relative_eq!(g.value(), 453.592_37, max_relative = 1e-9);
431 }
432
433 #[test]
434 fn stone_to_pound() {
435 let st = Stones::new(1.0);
436 let lb = st.to::<Pound>();
437 assert_relative_eq!(lb.value(), 14.0, max_relative = 1e-12);
439 }
440
441 #[test]
442 fn short_ton_to_pound() {
443 let ton = ShortTons::new(1.0);
444 let lb = ton.to::<Pound>();
445 assert_relative_eq!(lb.value(), 2000.0, max_relative = 1e-12);
447 }
448
449 #[test]
450 fn long_ton_to_pound() {
451 let ton = LongTons::new(1.0);
452 let lb = ton.to::<Pound>();
453 assert_relative_eq!(lb.value(), 2240.0, max_relative = 1e-12);
455 }
456
457 #[test]
458 fn atomic_mass_unit_to_gram() {
459 let u = AtomicMassUnits::new(1.0);
461 let g = u.to::<Gram>();
462 assert_relative_eq!(g.value(), 1.660_539_068_92e-24, max_relative = 1e-6);
463 }
464
465 #[test]
468 fn milligram_to_gram() {
469 let mg = Milligrams::new(1000.0);
470 let g = mg.to::<Gram>();
471 assert_relative_eq!(g.value(), 1.0, max_relative = 1e-12);
472 }
473
474 #[test]
475 fn microgram_to_milligram() {
476 let ug = Micrograms::new(1000.0);
477 let mg = ug.to::<Milligram>();
478 assert_relative_eq!(mg.value(), 1.0, max_relative = 1e-12);
479 }
480
481 #[test]
482 fn symbols_are_correct() {
483 assert_eq!(Kilogram::SYMBOL, "kg");
484 assert_eq!(Gram::SYMBOL, "g");
485 assert_eq!(Pound::SYMBOL, "lb");
486 assert_eq!(Tonne::SYMBOL, "t");
487 }
488}