1use crate::{Quantity, Unit};
19use qtty_derive::Unit;
20
21pub use crate::dimension::Mass;
23
24pub trait MassUnit: Unit<Dim = Mass> {}
26impl<T: Unit<Dim = Mass>> MassUnit for T {}
27
28#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
30#[unit(symbol = "g", dimension = Mass, ratio = 1.0)]
31pub struct Gram;
32pub type Grams = Quantity<Gram>;
34pub const G: Grams = Grams::new(1.0);
36
37macro_rules! si_gram {
48 ($name:ident, $sym:literal, $ratio:expr, $alias:ident, $qty:ident, $one:ident) => {
49 #[doc = concat!("SI mass unit `", stringify!($name), "` with gram-based prefix (symbol `", $sym,"`).")]
50 #[doc = concat!("By definition, `1 ", $sym, " = ", stringify!($ratio), " g`.")]
51 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
52 #[unit(symbol = $sym, dimension = Mass, ratio = $ratio)]
53 pub struct $name;
54
55 #[doc = concat!("Shorthand alias for [`", stringify!($name), "`]." )]
56 pub type $alias = $name;
57
58 #[doc = concat!("Quantity measured in ", stringify!($name), " (",$sym,").")]
59 pub type $qty = Quantity<$alias>;
60
61 #[doc = concat!("Constant equal to one ", stringify!($name), " (1 ",$sym,").")]
62 pub const $one: $qty = $qty::new(1.0);
63 };
64}
65
66si_gram!(Yoctogram, "yg", 1e-24, Yg, Yoctograms, YG);
68si_gram!(Zeptogram, "zg", 1e-21, Zg, Zeptograms, ZG);
69si_gram!(Attogram, "ag", 1e-18, Ag, Attograms, AG);
70si_gram!(Femtogram, "fg", 1e-15, Fg, Femtograms, FG);
71si_gram!(Picogram, "pg", 1e-12, Pg, Picograms, PG);
72si_gram!(Nanogram, "ng", 1e-9, Ng, Nanograms, NG);
73si_gram!(Microgram, "µg", 1e-6, Ug, Micrograms, UG);
74si_gram!(Milligram, "mg", 1e-3, Mg, Milligrams, MG);
75si_gram!(Centigram, "cg", 1e-2, Cg, Centigrams, CG);
76si_gram!(Decigram, "dg", 1e-1, Dg, Decigrams, DG);
77
78si_gram!(Decagram, "dag", 1e1, Dag, Decagrams, DAG);
79si_gram!(Hectogram, "hg", 1e2, Hg, Hectograms, HG);
80si_gram!(Kilogram, "kg", 1e3, Kg, Kilograms, KG);
81si_gram!(Megagram, "Mg", 1e6, MgG, Megagrams, MEGAGRAM);
82si_gram!(Gigagram, "Gg", 1e9, Gg, Gigagrams, GG);
83si_gram!(Teragram, "Tg", 1e12, Tg, Teragrams, TG);
84si_gram!(Petagram, "Pg", 1e15, PgG, Petagrams, PETAGRAM);
85si_gram!(Exagram, "Eg", 1e18, Eg, Exagrams, EG);
86si_gram!(Zettagram, "Zg", 1e21, ZgG, Zettagrams, ZETTAGRAM);
87si_gram!(Yottagram, "Yg", 1e24, YgG, Yottagrams, YOTTAGRAM);
88
89#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
91#[unit(symbol = "t", dimension = Mass, ratio = 1_000_000.0)]
92pub struct Tonne;
93pub type T = Tonne;
95pub type Tonnes = Quantity<T>;
97pub const TONE: Tonnes = Tonnes::new(1.0);
99
100#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
102#[unit(symbol = "ct", dimension = Mass, ratio = 1.0 / 5.0)]
103pub struct Carat;
104pub type Ct = Carat;
106pub type Carats = Quantity<Ct>;
108pub const CT: Carats = Carats::new(1.0);
110
111#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
113#[unit(symbol = "gr", dimension = Mass, ratio = 6_479_891.0 / 1_000_000_000.0)]
114pub struct Grain;
115pub type Gr = Grain;
117pub type Grains = Quantity<Gr>;
119pub const GR: Grains = Grains::new(1.0);
121
122#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
124#[unit(symbol = "lb", dimension = Mass, ratio = 45_359_237.0 / 100_000.0)]
125pub struct Pound;
126pub type Lb = Pound;
128pub type Pounds = Quantity<Lb>;
130pub const LB: Pounds = Pounds::new(1.0);
132
133#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
135#[unit(symbol = "oz", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) / 16.0)]
136pub struct Ounce;
137pub type Oz = Ounce;
139pub type Ounces = Quantity<Oz>;
141pub const OZ: Ounces = Ounces::new(1.0);
143
144#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
146#[unit(symbol = "st", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) * 14.0)]
147pub struct Stone;
148pub type St = Stone;
150pub type Stones = Quantity<St>;
152pub const ST: Stones = Stones::new(1.0);
154
155#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
157#[unit(symbol = "ton_us", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) * 2000.0)]
158pub struct ShortTon;
159pub type ShortTons = Quantity<ShortTon>;
161pub const TON_US: ShortTons = ShortTons::new(1.0);
163
164#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
166#[unit(symbol = "ton_uk", dimension = Mass, ratio = (45_359_237.0 / 100_000.0) * 2240.0)]
167pub struct LongTon;
168pub type LongTons = Quantity<LongTon>;
170pub const TON_UK: LongTons = LongTons::new(1.0);
172
173#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
177#[unit(symbol = "u", dimension = Mass, ratio = 1.660_539_068_92e-24)]
178pub struct AtomicMassUnit;
179pub type Dalton = AtomicMassUnit;
181pub type AtomicMassUnits = Quantity<AtomicMassUnit>;
183pub const U: AtomicMassUnits = AtomicMassUnits::new(1.0);
185
186#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
190#[unit(symbol = "M☉", dimension = Mass, ratio = 1.988_416e33)]
191pub struct SolarMass;
192pub type SolarMasses = Quantity<SolarMass>;
194pub const MSUN: SolarMasses = SolarMasses::new(1.0);
196
197crate::impl_unit_from_conversions!(
199 Gram,
200 Yoctogram,
201 Zeptogram,
202 Attogram,
203 Femtogram,
204 Picogram,
205 Nanogram,
206 Microgram,
207 Milligram,
208 Centigram,
209 Decigram,
210 Decagram,
211 Hectogram,
212 Kilogram,
213 Megagram,
214 Gigagram,
215 Teragram,
216 Petagram,
217 Exagram,
218 Zettagram,
219 Yottagram,
220 Tonne,
221 Carat,
222 Grain,
223 Pound,
224 Ounce,
225 Stone,
226 ShortTon,
227 LongTon,
228 AtomicMassUnit,
229 SolarMass
230);
231
232#[cfg(feature = "cross-unit-ops")]
234crate::impl_unit_cross_unit_ops!(
235 Gram,
236 Yoctogram,
237 Zeptogram,
238 Attogram,
239 Femtogram,
240 Picogram,
241 Nanogram,
242 Microgram,
243 Milligram,
244 Centigram,
245 Decigram,
246 Decagram,
247 Hectogram,
248 Kilogram,
249 Megagram,
250 Gigagram,
251 Teragram,
252 Petagram,
253 Exagram,
254 Zettagram,
255 Yottagram,
256 Tonne,
257 Carat,
258 Grain,
259 Pound,
260 Ounce,
261 Stone,
262 ShortTon,
263 LongTon,
264 AtomicMassUnit,
265 SolarMass
266);
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271 use approx::{assert_abs_diff_eq, assert_relative_eq};
272 use proptest::prelude::*;
273
274 #[test]
279 fn gram_to_kilogram() {
280 let g = Grams::new(1000.0);
281 let kg = g.to::<Kilogram>();
282 assert_abs_diff_eq!(kg.value(), 1.0, epsilon = 1e-12);
283 }
284
285 #[test]
286 fn kilogram_to_gram() {
287 let kg = Kilograms::new(1.0);
288 let g = kg.to::<Gram>();
289 assert_abs_diff_eq!(g.value(), 1000.0, epsilon = 1e-9);
290 }
291
292 #[test]
293 fn solar_mass_to_grams() {
294 let sm = SolarMasses::new(1.0);
295 let g = sm.to::<Gram>();
296 assert_relative_eq!(g.value(), 1.988416e33, max_relative = 1e-5);
298 }
299
300 #[test]
301 fn solar_mass_to_kilograms() {
302 let sm = SolarMasses::new(1.0);
303 let kg = sm.to::<Kilogram>();
304 assert_relative_eq!(kg.value(), 1.988416e30, max_relative = 1e-5);
306 }
307
308 #[test]
309 fn kilograms_to_solar_mass() {
310 let earth_kg = Kilograms::new(5.97e24);
312 let earth_sm = earth_kg.to::<SolarMass>();
313 assert_relative_eq!(earth_sm.value(), 3.0e-6, max_relative = 0.01);
314 }
315
316 #[test]
321 fn solar_mass_ratio_sanity() {
322 assert_relative_eq!(SolarMass::RATIO, 1.988416e33, max_relative = 1e-5);
324 }
325
326 #[test]
327 fn solar_mass_order_of_magnitude() {
328 let sun = SolarMasses::new(1.0);
330 let kg = sun.to::<Kilogram>();
331 assert!(kg.value() > 1e30);
332 assert!(kg.value() < 1e31);
333 }
334
335 #[test]
340 fn roundtrip_g_kg() {
341 let original = Grams::new(5000.0);
342 let converted = original.to::<Kilogram>();
343 let back = converted.to::<Gram>();
344 assert_abs_diff_eq!(back.value(), original.value(), epsilon = 1e-9);
345 }
346
347 #[test]
348 fn roundtrip_kg_solar() {
349 let original = Kilograms::new(1e30);
350 let converted = original.to::<SolarMass>();
351 let back = converted.to::<Kilogram>();
352 assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
353 }
354
355 proptest! {
360 #[test]
361 fn prop_roundtrip_g_kg(g in 1e-6..1e6f64) {
362 let original = Grams::new(g);
363 let converted = original.to::<Kilogram>();
364 let back = converted.to::<Gram>();
365 prop_assert!((back.value() - original.value()).abs() < 1e-9 * g.abs().max(1.0));
366 }
367
368 #[test]
369 fn prop_g_kg_ratio(g in 1e-6..1e6f64) {
370 let grams = Grams::new(g);
371 let kg = grams.to::<Kilogram>();
372 prop_assert!((grams.value() / kg.value() - 1000.0).abs() < 1e-9);
374 }
375 }
376}