qtty_core/units/
length.rs

1//! Length units.
2//!
3//! The canonical scaling unit for this dimension is [`Meter`] (`Meter::RATIO == 1.0`). All other
4//! length units are expressed as exact or best‑available ratios to metres.
5//!
6//! This module provides:
7//!
8//! - **SI ladder**: the full metric prefix family for metres from yocto‑ to yotta‑.
9//! - **Common defined units**: inch, foot, yard, (statute) mile, nautical mile, surveying units.
10//! - **Astronomy**: astronomical unit (au), light‑year (ly), parsec (pc) and its multiples.
11//! - **Geodesy and navigation**: Earth circumferences and related standards distances.
12//! - **Fundamental physics lengths**: Bohr radius, Planck length, and related constants.
13//! - **Nominal radii and distances**: available under the [`nominal`] submodule.
14//!
15//! Notes on definitions used here:
16//!
17//! - **Astronomical unit (au)** is **exactly** `149_597_870_700 m` (IAU 2012).
18//! - **Parsec (pc)** is defined from au via `pc = au * 648000 / π` (exact, given au).
19//! - **Light‑year (ly)** is derived from the exact speed of light `c = 299_792_458 m/s` and one
20//!   **Julian year** (`365.25 d`, `d = 86400 s`).
21//! - **Imperial and surveying units** follow the current international definitions (e.g. the
22//!   international inch is exactly `0.0254 m`).
23//! - **Nominal** astronomical/geodetic radii are grouped into [`nominal`] to avoid mixing them with
24//!   strictly defined units.
25//!
26//! This module aims to avoid avoidable precision loss by preferring rational expressions and exact
27//! relationships over rounded convenience factors wherever practical.
28//!
29//! ```rust
30//! use qtty_core::length::{AstronomicalUnits, Kilometer};
31//!
32//! let au = AstronomicalUnits::new(1.0);
33//! let km = au.to::<Kilometer>();
34//! assert_eq!(km.value(), 149_597_870.7);
35//! ```
36
37use crate::{Dimension, Quantity, Unit};
38use core::f64::consts::PI;
39use qtty_derive::Unit;
40
41/// Dimension tag for length.
42pub enum Length {}
43impl Dimension for Length {}
44
45/// Marker trait for any [`Unit`] whose dimension is [`Length`].
46pub trait LengthUnit: Unit<Dim = Length> {}
47impl<T: Unit<Dim = Length>> LengthUnit for T {}
48
49// ─────────────────────────────────────────────────────────────────────────────
50// SI base unit and core helpers
51// ─────────────────────────────────────────────────────────────────────────────
52
53/// Metre (SI base unit).
54#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
55#[unit(symbol = "m", dimension = Length, ratio = 1.0)]
56pub struct Meter;
57/// A quantity measured in metres.
58pub type Meters = Quantity<Meter>;
59/// One metre.
60pub const M: Meters = Meters::new(1.0);
61
62/// Kilometre (`1000 m`).
63#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
64#[unit(symbol = "Km", dimension = Length, ratio = 1_000.0)]
65pub struct Kilometer;
66/// Type alias shorthand for [`Kilometer`].
67pub type Km = Kilometer;
68/// A quantity measured in kilometres.
69pub type Kilometers = Quantity<Km>;
70/// One kilometre.
71pub const KM: Kilometers = Kilometers::new(1.0);
72
73/// Centimetre (`1e-2 m`).
74#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
75#[unit(symbol = "cm", dimension = Length, ratio = 1e-2)]
76pub struct Centimeter;
77/// Type alias shorthand for [`Centimeter`].
78pub type Cm = Centimeter;
79/// A quantity measured in centimetres.
80pub type Centimeters = Quantity<Cm>;
81/// One centimetre.
82pub const CM: Centimeters = Centimeters::new(1.0);
83
84/// Millimetre (`1e-3 m`).
85#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
86#[unit(symbol = "mm", dimension = Length, ratio = 1e-3)]
87pub struct Millimeter;
88/// Type alias shorthand for [`Millimeter`].
89pub type Mm = Millimeter;
90/// A quantity measured in millimetres.
91pub type Millimeters = Quantity<Mm>;
92/// One millimetre.
93pub const MM: Millimeters = Millimeters::new(1.0);
94
95/// Micrometre (`1e-6 m`).
96#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
97#[unit(symbol = "um", dimension = Length, ratio = 1e-6)]
98pub struct Micrometer;
99/// Type alias shorthand for [`Micrometer`].
100pub type Um = Micrometer;
101/// A quantity measured in micrometres.
102pub type Micrometers = Quantity<Um>;
103/// One micrometre.
104pub const UM: Micrometers = Micrometers::new(1.0);
105
106/// Nanometre (`1e-9 m`).
107#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
108#[unit(symbol = "nm", dimension = Length, ratio = 1e-9)]
109pub struct Nanometer;
110/// Type alias shorthand for [`Nanometer`].
111pub type Nm = Nanometer;
112/// A quantity measured in nanometres.
113pub type Nanometers = Quantity<Nm>;
114/// One nanometre.
115pub const NM: Nanometers = Nanometers::new(1.0);
116
117/// Picometre (`1e-12 m`).
118#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
119#[unit(symbol = "pm", dimension = Length, ratio = 1e-12)]
120pub struct Picometer;
121/// A quantity measured in picometres.
122pub type Picometers = Quantity<Picometer>;
123/// One picometre.
124pub const PMETER: Picometers = Picometers::new(1.0);
125
126/// Femtometre (`1e-15 m`).
127#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
128#[unit(symbol = "fm", dimension = Length, ratio = 1e-15)]
129pub struct Femtometer;
130/// A quantity measured in femtometres.
131pub type Femtometers = Quantity<Femtometer>;
132/// One femtometre.
133pub const FM: Femtometers = Femtometers::new(1.0);
134
135/// Attometre (`1e-18 m`).
136#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
137#[unit(symbol = "am", dimension = Length, ratio = 1e-18)]
138pub struct Attometer;
139/// A quantity measured in attometres.
140pub type Attometers = Quantity<Attometer>;
141/// One attometre.
142pub const AM: Attometers = Attometers::new(1.0);
143
144/// Zeptometre (`1e-21 m`).
145#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
146#[unit(symbol = "zm", dimension = Length, ratio = 1e-21)]
147pub struct Zeptometer;
148/// A quantity measured in zeptometres.
149pub type Zeptometers = Quantity<Zeptometer>;
150/// One zeptometre.
151pub const ZMETER: Zeptometers = Zeptometers::new(1.0);
152
153/// Yoctometre (`1e-24 m`).
154#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
155#[unit(symbol = "ym", dimension = Length, ratio = 1e-24)]
156pub struct Yoctometer;
157/// A quantity measured in yoctometres.
158pub type Yoctometers = Quantity<Yoctometer>;
159/// One yoctometre.
160pub const YMETER: Yoctometers = Yoctometers::new(1.0);
161
162/// Megametre (`1e6 m`).
163#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
164#[unit(symbol = "Mm", dimension = Length, ratio = 1e6)]
165pub struct Megameter;
166/// Type alias shorthand for [`Megameter`].
167pub type MegaMeter = Megameter;
168/// A quantity measured in megametres.
169pub type Megameters = Quantity<Megameter>;
170/// One megametre.
171pub const MEGAMETER: Megameters = Megameters::new(1.0);
172
173/// Decimetre (`1e-1 m`).
174#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
175#[unit(symbol = "dm", dimension = Length, ratio = 1e-1)]
176pub struct Decimeter;
177/// A quantity measured in decimetres.
178pub type Decimeters = Quantity<Decimeter>;
179/// One decimetre.
180pub const DM: Decimeters = Decimeters::new(1.0);
181
182/// Decametre (`1e1 m`).
183#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
184#[unit(symbol = "dam", dimension = Length, ratio = 1e1)]
185pub struct Decameter;
186/// A quantity measured in decametres.
187pub type Decameters = Quantity<Decameter>;
188/// One decametre.
189pub const DAM: Decameters = Decameters::new(1.0);
190
191/// Hectometre (`1e2 m`).
192#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
193#[unit(symbol = "hm", dimension = Length, ratio = 1e2)]
194pub struct Hectometer;
195/// A quantity measured in hectometres.
196pub type Hectometers = Quantity<Hectometer>;
197/// One hectometre.
198pub const HM: Hectometers = Hectometers::new(1.0);
199
200/// Gigametre (`1e9 m`).
201#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
202#[unit(symbol = "Gm", dimension = Length, ratio = 1e9)]
203pub struct Gigameter;
204/// A quantity measured in gigametres.
205pub type Gigameters = Quantity<Gigameter>;
206/// One gigametre.
207pub const GM: Gigameters = Gigameters::new(1.0);
208
209/// Terametre (`1e12 m`).
210#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
211#[unit(symbol = "Tm", dimension = Length, ratio = 1e12)]
212pub struct Terameter;
213/// A quantity measured in terametres.
214pub type Terameters = Quantity<Terameter>;
215/// One terametre.
216pub const TM: Terameters = Terameters::new(1.0);
217
218/// Petametre (`1e15 m`).
219#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
220#[unit(symbol = "Pm", dimension = Length, ratio = 1e15)]
221pub struct Petameter;
222/// A quantity measured in petametres.
223pub type Petameters = Quantity<Petameter>;
224/// One petametre.
225pub const PM: Petameters = Petameters::new(1.0);
226
227/// Exametre (`1e18 m`).
228#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
229#[unit(symbol = "Em", dimension = Length, ratio = 1e18)]
230pub struct Exameter;
231/// A quantity measured in exametres.
232pub type Exameters = Quantity<Exameter>;
233/// One exametre.
234pub const EM: Exameters = Exameters::new(1.0);
235
236/// Zettametre (`1e21 m`).
237#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
238#[unit(symbol = "Zm", dimension = Length, ratio = 1e21)]
239pub struct Zettameter;
240/// A quantity measured in zettametres.
241pub type Zettameters = Quantity<Zettameter>;
242/// One zettametre.
243pub const ZM: Zettameters = Zettameters::new(1.0);
244
245/// Yottametre (`1e24 m`).
246#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
247#[unit(symbol = "Ym", dimension = Length, ratio = 1e24)]
248pub struct Yottameter;
249/// A quantity measured in yottametres.
250pub type Yottameters = Quantity<Yottameter>;
251/// One yottametre.
252pub const YM: Yottameters = Yottameters::new(1.0);
253
254// ─────────────────────────────────────────────────────────────────────────────
255// Astronomical distance units
256// ─────────────────────────────────────────────────────────────────────────────
257
258/// Astronomical unit (au). Exact (IAU 2012): metres per au.
259#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
260#[unit(symbol = "au", dimension = Length, ratio = 149_597_870_700.0)]
261pub struct AstronomicalUnit;
262/// Type alias shorthand for [`AstronomicalUnit`].
263pub type Au = AstronomicalUnit;
264/// A quantity measured in astronomical units.
265pub type AstronomicalUnits = Quantity<Au>;
266/// One astronomical unit.
267pub const AU: AstronomicalUnits = AstronomicalUnits::new(1.0);
268
269// Exact speed of light and Julian year, used to derive the light‑year ratio.
270const SPEED_OF_LIGHT_M_PER_S: f64 = 299_792_458.0;
271const SECONDS_PER_DAY: f64 = 86_400.0;
272const DAYS_PER_JULIAN_YEAR: f64 = 36525.0 / 100.0; // 365.25 d
273const SECONDS_PER_JULIAN_YEAR: f64 = SECONDS_PER_DAY * DAYS_PER_JULIAN_YEAR;
274const METERS_PER_LIGHT_YEAR: f64 = SPEED_OF_LIGHT_M_PER_S * SECONDS_PER_JULIAN_YEAR;
275
276/// Light-year (ly): distance light travels in one Julian year (`365.25 d`) at `c = 299_792_458 m/s`.
277#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
278#[unit(symbol = "ly", dimension = Length, ratio = METERS_PER_LIGHT_YEAR)]
279pub struct LightYear;
280/// Type alias shorthand for [`LightYear`].
281pub type Ly = LightYear;
282/// A quantity measured in light-years.
283pub type LightYears = Quantity<Ly>;
284/// One light-year.
285pub const LY: LightYears = LightYears::new(1.0);
286
287/// Parsec (pc): `pc = au * 648000 / π` (exact given au).
288#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
289#[unit(symbol = "pc", dimension = Length, ratio = 149_597_870_700.0 * (648_000.0 / PI))]
290pub struct Parsec;
291/// Type alias shorthand for [`Parsec`].
292pub type Pc = Parsec;
293/// A quantity measured in parsecs.
294pub type Parsecs = Quantity<Pc>;
295/// One parsec.
296pub const PC: Parsecs = Parsecs::new(1.0);
297
298/// Kiloparsec (kpc): `1e3 pc`.
299#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
300#[unit(symbol = "kpc", dimension = Length, ratio = 1_000.0 * 149_597_870_700.0 * (648_000.0 / PI))]
301pub struct Kiloparsec;
302/// A quantity measured in kiloparsecs.
303pub type Kiloparsecs = Quantity<Kiloparsec>;
304/// One kiloparsec.
305pub const KPC: Kiloparsecs = Kiloparsecs::new(1.0);
306
307/// Megaparsec (Mpc): `1e6 pc`.
308#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
309#[unit(symbol = "Mpc", dimension = Length, ratio = 1_000_000.0 * 149_597_870_700.0 * (648_000.0 / PI))]
310pub struct Megaparsec;
311/// A quantity measured in megaparsecs.
312pub type Megaparsecs = Quantity<Megaparsec>;
313/// One megaparsec.
314pub const MPC: Megaparsecs = Megaparsecs::new(1.0);
315
316/// Gigaparsec (Gpc): `1e9 pc`.
317#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
318#[unit(symbol = "Gpc", dimension = Length, ratio = 1_000_000_000.0 * 149_597_870_700.0 * (648_000.0 / PI))]
319pub struct Gigaparsec;
320/// A quantity measured in gigaparsecs.
321pub type Gigaparsecs = Quantity<Gigaparsec>;
322/// One gigaparsec.
323pub const GPC: Gigaparsecs = Gigaparsecs::new(1.0);
324
325// ─────────────────────────────────────────────────────────────────────────────
326// Imperial, US customary, and surveying units
327// ─────────────────────────────────────────────────────────────────────────────
328
329/// Inch (`0.0254 m` exactly).
330#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
331#[unit(symbol = "in", dimension = Length, ratio = 254.0 / 10_000.0)]
332pub struct Inch;
333/// A quantity measured in inches.
334pub type Inches = Quantity<Inch>;
335/// One inch.
336pub const INCH: Inches = Inches::new(1.0);
337
338/// Foot (`0.3048 m` exactly).
339#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
340#[unit(symbol = "ft", dimension = Length, ratio = 3048.0 / 10_000.0)]
341pub struct Foot;
342/// A quantity measured in feet.
343pub type Feet = Quantity<Foot>;
344/// One foot.
345pub const FT: Feet = Feet::new(1.0);
346
347/// Yard (`0.9144 m` exactly).
348#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
349#[unit(symbol = "yd", dimension = Length, ratio = 9144.0 / 10_000.0)]
350pub struct Yard;
351/// A quantity measured in yards.
352pub type Yards = Quantity<Yard>;
353/// One yard.
354pub const YD: Yards = Yards::new(1.0);
355
356/// (Statute) mile (`1609.344 m` exactly).
357#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
358#[unit(symbol = "mi", dimension = Length, ratio = 1_609_344.0 / 1_000.0)]
359pub struct Mile;
360/// A quantity measured in miles.
361pub type Miles = Quantity<Mile>;
362/// One mile.
363pub const MI: Miles = Miles::new(1.0);
364
365/// Nautical mile (`1852 m` exactly).
366#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
367#[unit(symbol = "nmi", dimension = Length, ratio = 1_852.0)]
368pub struct NauticalMile;
369/// A quantity measured in nautical miles.
370pub type NauticalMiles = Quantity<NauticalMile>;
371/// One nautical mile.
372pub const NMI: NauticalMiles = NauticalMiles::new(1.0);
373
374/// Chain (`66 ft` exactly).
375#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
376#[unit(symbol = "ch", dimension = Length, ratio = 66.0 * (3048.0 / 10_000.0))]
377pub struct Chain;
378/// A quantity measured in chains.
379pub type Chains = Quantity<Chain>;
380/// One chain.
381pub const CHAIN: Chains = Chains::new(1.0);
382
383/// Rod / pole / perch (`16.5 ft` exactly).
384#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
385#[unit(symbol = "rd", dimension = Length, ratio = 16.5 * (3048.0 / 10_000.0))]
386pub struct Rod;
387/// A quantity measured in rods/poles/perches.
388pub type Rods = Quantity<Rod>;
389/// One rod.
390pub const ROD: Rods = Rods::new(1.0);
391
392/// Link (`1/100 of a chain`, i.e. `0.66 ft`).
393#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
394#[unit(symbol = "lk", dimension = Length, ratio = (66.0 / 100.0) * (3048.0 / 10_000.0))]
395pub struct Link;
396/// A quantity measured in links.
397pub type Links = Quantity<Link>;
398/// One link.
399pub const LINK: Links = Links::new(1.0);
400
401/// Fathom (`6 ft` exactly).
402#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
403#[unit(symbol = "ftm", dimension = Length, ratio = 6.0 * (3048.0 / 10_000.0))]
404pub struct Fathom;
405/// A quantity measured in fathoms.
406pub type Fathoms = Quantity<Fathom>;
407/// One fathom.
408pub const FTM: Fathoms = Fathoms::new(1.0);
409
410// ─────────────────────────────────────────────────────────────────────────────
411// Geodesy and navigation
412// ─────────────────────────────────────────────────────────────────────────────
413
414/// Earth meridional circumference (approximate mean value).
415#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
416#[unit(symbol = "Cmer", dimension = Length, ratio = 40_007_863.0)]
417pub struct EarthMeridionalCircumference;
418/// A quantity measured in Earth meridional circumferences.
419pub type EarthMeridionalCircumferences = Quantity<EarthMeridionalCircumference>;
420/// One Earth meridional circumference.
421pub const C_MERIDIONAL: EarthMeridionalCircumferences = EarthMeridionalCircumferences::new(1.0);
422
423/// Earth equatorial circumference.
424#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
425#[unit(symbol = "Ceq", dimension = Length, ratio = 40_075_017.0)]
426pub struct EarthEquatorialCircumference;
427/// A quantity measured in Earth equatorial circumferences.
428pub type EarthEquatorialCircumferences = Quantity<EarthEquatorialCircumference>;
429/// One Earth equatorial circumference.
430pub const C_EQUATORIAL: EarthEquatorialCircumferences = EarthEquatorialCircumferences::new(1.0);
431
432// ─────────────────────────────────────────────────────────────────────────────
433// Fundamental physics lengths (CODATA values)
434// ─────────────────────────────────────────────────────────────────────────────
435
436/// Bohr radius (`a0`). CODATA 2018 value in metres.
437#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
438#[unit(symbol = "a0", dimension = Length, ratio = 5.291_772_109_03e-11)]
439pub struct BohrRadius;
440/// A quantity measured in Bohr radii.
441pub type BohrRadii = Quantity<BohrRadius>;
442/// One Bohr radius.
443pub const A0: BohrRadii = BohrRadii::new(1.0);
444
445/// Classical electron radius (`re`). CODATA 2018 value in metres.
446#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
447#[unit(symbol = "re", dimension = Length, ratio = 2.817_940_326_2e-15)]
448pub struct ClassicalElectronRadius;
449/// A quantity measured in classical electron radii.
450pub type ClassicalElectronRadii = Quantity<ClassicalElectronRadius>;
451/// One classical electron radius.
452pub const RE: ClassicalElectronRadii = ClassicalElectronRadii::new(1.0);
453
454/// Planck length (`lp`). CODATA 2018 value in metres.
455#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
456#[unit(symbol = "lp", dimension = Length, ratio = 1.616_255e-35)]
457pub struct PlanckLength;
458/// A quantity measured in Planck lengths.
459pub type PlanckLengths = Quantity<PlanckLength>;
460/// One Planck length.
461pub const LP: PlanckLengths = PlanckLengths::new(1.0);
462
463/// Reduced Compton wavelength of the electron (`lambda_bar_e`). CODATA 2018 value in metres.
464#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
465#[unit(symbol = "lambda_bar_e", dimension = Length, ratio = 3.861_592_679_6e-13)]
466pub struct ElectronReducedComptonWavelength;
467/// A quantity measured in reduced Compton wavelengths of the electron.
468pub type ElectronReducedComptonWavelengths = Quantity<ElectronReducedComptonWavelength>;
469/// One reduced Compton wavelength of the electron.
470pub const LAMBDA_BAR_E: ElectronReducedComptonWavelengths =
471    ElectronReducedComptonWavelengths::new(1.0);
472
473// ─────────────────────────────────────────────────────────────────────────────
474// Nominal radii and distances
475// ─────────────────────────────────────────────────────────────────────────────
476
477/// Nominal astronomical and planetary radii and related distances.
478///
479/// Values in this module are **nominal** (conventionally rounded) and are kept separate from the
480/// main length namespace to avoid confusion with strictly defined units.
481pub mod nominal {
482    use super::*;
483
484    /// Solar radius (R☉). Nominal value: metres per R☉.
485    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
486    #[unit(symbol = "Rsun", dimension = Length, ratio = 695_700_000.0)]
487    pub struct SolarRadius;
488    /// A quantity measured in solar radii.
489    pub type SolarRadiuses = Quantity<SolarRadius>;
490    /// One solar radius.
491    pub const RSUN: SolarRadiuses = SolarRadiuses::new(1.0);
492
493    /// Earth mean radius (nominal).
494    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
495    #[unit(symbol = "Rearth", dimension = Length, ratio = 6_371_000.0)]
496    pub struct EarthRadius;
497    /// A quantity measured in Earth radii.
498    pub type EarthRadii = Quantity<EarthRadius>;
499    /// One Earth radius (mean).
500    pub const R_EARTH: EarthRadii = EarthRadii::new(1.0);
501
502    /// Earth equatorial radius (WGS84).
503    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
504    #[unit(symbol = "Rearth_eq", dimension = Length, ratio = 6_378_137.0)]
505    pub struct EarthEquatorialRadius;
506    /// A quantity measured in Earth equatorial radii.
507    pub type EarthEquatorialRadii = Quantity<EarthEquatorialRadius>;
508    /// One Earth equatorial radius.
509    pub const R_EARTH_EQ: EarthEquatorialRadii = EarthEquatorialRadii::new(1.0);
510
511    /// Earth polar radius.
512    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
513    #[unit(symbol = "Rearth_p", dimension = Length, ratio = 6_356_752.314_2)]
514    pub struct EarthPolarRadius;
515    /// A quantity measured in Earth polar radii.
516    pub type EarthPolarRadii = Quantity<EarthPolarRadius>;
517    /// One Earth polar radius.
518    pub const R_EARTH_P: EarthPolarRadii = EarthPolarRadii::new(1.0);
519
520    /// Lunar radius (mean, nominal).
521    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
522    #[unit(symbol = "Rmoon", dimension = Length, ratio = 1_737_400.0)]
523    pub struct LunarRadius;
524    /// A quantity measured in lunar radii.
525    pub type LunarRadii = Quantity<LunarRadius>;
526    /// One lunar radius.
527    pub const R_MOON: LunarRadii = LunarRadii::new(1.0);
528
529    /// Jupiter equatorial radius (nominal).
530    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
531    #[unit(symbol = "Rjup", dimension = Length, ratio = 71_492_000.0)]
532    pub struct JupiterRadius;
533    /// A quantity measured in Jupiter radii.
534    pub type JupiterRadii = Quantity<JupiterRadius>;
535    /// One Jupiter radius.
536    pub const R_JUPITER: JupiterRadii = JupiterRadii::new(1.0);
537
538    /// Lunar distance (Earth–Moon mean distance, LD).
539    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
540    #[unit(symbol = "LD", dimension = Length, ratio = 384_400_000.0)]
541    pub struct LunarDistance;
542    /// A quantity measured in lunar distances.
543    pub type LunarDistances = Quantity<LunarDistance>;
544    /// One lunar distance.
545    pub const LD: LunarDistances = LunarDistances::new(1.0);
546
547    /// Solar diameter (twice the solar radius).
548    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
549    #[unit(symbol = "Dsun", dimension = Length, ratio = 2.0 * 695_700_000.0)]
550    pub struct SolarDiameter;
551    /// A quantity measured in solar diameters.
552    pub type SolarDiameters = Quantity<SolarDiameter>;
553    /// One solar diameter.
554    pub const D_SUN: SolarDiameters = SolarDiameters::new(1.0);
555
556    // Allow convenient conversions between selected nominal units and core
557    // length units (e.g., SolarRadius <-> Kilometer) without polluting the
558    // main length namespace with nominal types.
559    crate::impl_unit_conversions!(SolarRadius, Kilometer);
560}
561
562// Generate all bidirectional From implementations between length units.
563//
564// This single invocation ensures that any quantity measured in one length unit can be
565// converted into any other via `From`/`Into`, mirroring the previous behavior while
566// including the extended unit set.
567crate::impl_unit_conversions!(
568    Meter,
569    Decimeter,
570    Centimeter,
571    Millimeter,
572    Micrometer,
573    Nanometer,
574    Picometer,
575    Femtometer,
576    Attometer,
577    Zeptometer,
578    Yoctometer,
579    Decameter,
580    Hectometer,
581    Kilometer,
582    Megameter,
583    Gigameter,
584    Terameter,
585    Petameter,
586    Exameter,
587    Zettameter,
588    Yottameter,
589    AstronomicalUnit,
590    LightYear,
591    Parsec,
592    Kiloparsec,
593    Megaparsec,
594    Gigaparsec,
595    Inch,
596    Foot,
597    Yard,
598    Mile,
599    NauticalMile,
600    Chain,
601    Rod,
602    Link,
603    Fathom,
604    EarthMeridionalCircumference,
605    EarthEquatorialCircumference,
606    BohrRadius,
607    ClassicalElectronRadius,
608    PlanckLength,
609    ElectronReducedComptonWavelength
610);
611
612#[cfg(test)]
613mod tests {
614    use super::nominal::SolarRadiuses;
615    use super::*;
616    use approx::{assert_abs_diff_eq, assert_relative_eq};
617    use proptest::prelude::*;
618
619    // ─────────────────────────────────────────────────────────────────────────────
620    // Basic conversions
621    // ─────────────────────────────────────────────────────────────────────────────
622
623    #[test]
624    fn kilometer_to_meter() {
625        let km = Kilometers::new(1.0);
626        let m = km.to::<Meter>();
627        assert_abs_diff_eq!(m.value(), 1000.0, epsilon = 1e-9);
628    }
629
630    #[test]
631    fn meter_to_kilometer() {
632        let m = Meters::new(1000.0);
633        let km = m.to::<Kilometer>();
634        assert_abs_diff_eq!(km.value(), 1.0, epsilon = 1e-12);
635    }
636
637    #[test]
638    fn au_to_meters() {
639        let au = AstronomicalUnits::new(1.0);
640        let m = au.to::<Meter>();
641        // 1 AU = 149,597,870,700 meters (exact, IAU 2012).
642        assert_abs_diff_eq!(m.value(), 149_597_870_700.0, epsilon = 1e-6);
643    }
644
645    #[test]
646    fn au_to_kilometers() {
647        let au = AstronomicalUnits::new(1.0);
648        let km = au.to::<Kilometer>();
649        // 1 AU = 149,597,870,700 m => 149,597,870.7 km.
650        assert_relative_eq!(km.value(), 149_597_870.7, max_relative = 1e-12);
651    }
652
653    #[test]
654    fn light_year_to_meters() {
655        let ly = LightYears::new(1.0);
656        let m = ly.to::<Meter>();
657        // 1 LY = c * 365.25 d, where d = 86400 s
658        assert_relative_eq!(m.value(), METERS_PER_LIGHT_YEAR, max_relative = 1e-12);
659    }
660
661    #[test]
662    fn light_year_to_kilometers() {
663        let ly = LightYears::new(1.0);
664        let km = ly.to::<Kilometer>();
665        // 1 LY ≈ 9.461e12 km
666        assert_relative_eq!(km.value(), 9_460_730_472_580.000_8, max_relative = 1e-9);
667    }
668
669    // ─────────────────────────────────────────────────────────────────────────────
670    // AU <-> LY conversions
671    // ─────────────────────────────────────────────────────────────────────────────
672
673    #[test]
674    fn au_to_light_year() {
675        let au = AstronomicalUnits::new(1.0);
676        let ly = au.to::<LightYear>();
677        // 1 AU ≈ 1.582e-5 LY
678        assert_relative_eq!(ly.value(), 1.582e-5, max_relative = 1e-3);
679    }
680
681    #[test]
682    fn light_year_to_au() {
683        let ly = LightYears::new(1.0);
684        let au = ly.to::<AstronomicalUnit>();
685        // 1 LY ≈ 63,241 AU
686        assert_relative_eq!(au.value(), 63241.0, max_relative = 1e-3);
687    }
688
689    #[test]
690    fn from_impl_au_to_ly() {
691        let au = 1.0 * AU;
692        let ly: LightYears = au.into();
693        assert_relative_eq!(ly.value(), 1.582e-5, max_relative = 1e-3);
694    }
695
696    #[test]
697    fn from_impl_ly_to_au() {
698        let ly = 1.0 * LY;
699        let au: AstronomicalUnits = ly.into();
700        assert_relative_eq!(au.value(), 63241.0, max_relative = 1e-3);
701    }
702
703    // ─────────────────────────────────────────────────────────────────────────────
704    // Parsec conversions
705    // ─────────────────────────────────────────────────────────────────────────────
706
707    #[test]
708    fn parsec_to_light_year() {
709        let pc = Parsecs::new(1.0);
710        let ly = pc.to::<LightYear>();
711        // 1 pc expressed in light-years, using the exact AU-based definition.
712        let expected = (AstronomicalUnit::RATIO * (648_000.0 / PI)) / LightYear::RATIO;
713        assert_relative_eq!(ly.value(), expected, max_relative = 1e-15);
714    }
715
716    #[test]
717    fn parsec_to_au() {
718        let pc = Parsecs::new(1.0);
719        let au = pc.to::<AstronomicalUnit>();
720        // 1 pc ≈ 206,265 AU (using exact definition: 1 pc = 3.26 LY, 1 LY ≈ 63241 AU)
721        // So 1 pc ≈ 3.26 * 63241 ≈ 206,165 AU
722        assert_relative_eq!(au.value(), 3.26 * 63241.0, max_relative = 1e-2);
723    }
724
725    #[test]
726    fn parsec_ratio_sanity() {
727        // Parsec is defined from AU: pc = au * 648000 / π
728        let lhs = Parsec::RATIO / AstronomicalUnit::RATIO;
729        let rhs = 648_000.0 / PI;
730        assert_relative_eq!(lhs, rhs, max_relative = 1e-12);
731    }
732
733    // ─────────────────────────────────────────────────────────────────────────────
734    // Solar radius
735    // ─────────────────────────────────────────────────────────────────────────────
736
737    #[test]
738    fn solar_radius_to_meters() {
739        let sr = SolarRadiuses::new(1.0);
740        let m = sr.to::<Meter>();
741        // 1 R☉ = 695,700 km = 695,700,000 m
742        assert_abs_diff_eq!(m.value(), 695_700_000.0, epsilon = 1e-3);
743    }
744
745    #[test]
746    fn solar_radius_to_km() {
747        let sr = SolarRadiuses::new(1.0);
748        let km = sr.to::<Kilometer>();
749        // 1 R☉ = 695,700 km
750        assert_abs_diff_eq!(km.value(), 695_700.0, epsilon = 1e-6);
751    }
752
753    // ─────────────────────────────────────────────────────────────────────────────
754    // Roundtrip conversions
755    // ─────────────────────────────────────────────────────────────────────────────
756
757    #[test]
758    fn roundtrip_km_m() {
759        let original = Kilometers::new(42.5);
760        let converted = original.to::<Meter>();
761        let back = converted.to::<Kilometer>();
762        assert_abs_diff_eq!(back.value(), original.value(), epsilon = 1e-12);
763    }
764
765    #[test]
766    fn roundtrip_au_ly() {
767        let original = AstronomicalUnits::new(10000.0);
768        let converted = original.to::<LightYear>();
769        let back = converted.to::<AstronomicalUnit>();
770        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
771    }
772
773    #[test]
774    fn roundtrip_parsec_ly() {
775        let original = Parsecs::new(5.0);
776        let converted = original.to::<LightYear>();
777        let back = converted.to::<Parsec>();
778        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
779    }
780
781    // ─────────────────────────────────────────────────────────────────────────
782    // Exact relationship tests for new units
783    // ─────────────────────────────────────────────────────────────────────────
784
785    #[test]
786    fn inch_to_meter_exact_ratio() {
787        let inch = Inches::new(1.0);
788        let m = inch.to::<Meter>();
789        // International inch: exactly 0.0254 m
790        assert_relative_eq!(m.value(), 0.0254, max_relative = 1e-16);
791    }
792
793    #[test]
794    fn nautical_mile_to_meter_exact_ratio() {
795        let nmi = NauticalMiles::new(1.0);
796        let m = nmi.to::<Meter>();
797        // International nautical mile: exactly 1852 m
798        assert_abs_diff_eq!(m.value(), 1852.0, epsilon = 1e-12);
799    }
800
801    // ─────────────────────────────────────────────────────────────────────────
802    // Roundtrip sanity for representative units
803    // ─────────────────────────────────────────────────────────────────────────
804
805    #[test]
806    fn roundtrip_inch_meter() {
807        let original = Inches::new(123.456);
808        let converted = original.to::<Meter>();
809        let back = converted.to::<Inch>();
810        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
811    }
812
813    #[test]
814    fn roundtrip_nautical_mile_meter() {
815        let original = NauticalMiles::new(3.75);
816        let converted = original.to::<Meter>();
817        let back = converted.to::<NauticalMile>();
818        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
819    }
820
821    #[test]
822    fn roundtrip_parsec_kpc() {
823        let original = Parsecs::new(12_345.0);
824        let converted = original.to::<Kiloparsec>();
825        let back = converted.to::<Parsec>();
826        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
827    }
828
829    // ─────────────────────────────────────────────────────────────────────────────
830    // Property-based tests
831    // ─────────────────────────────────────────────────────────────────────────────
832
833    proptest! {
834        #[test]
835        fn prop_roundtrip_km_m(k in -1e6..1e6f64) {
836            let original = Kilometers::new(k);
837            let converted = original.to::<Meter>();
838            let back = converted.to::<Kilometer>();
839            prop_assert!((back.value() - original.value()).abs() < 1e-9 * k.abs().max(1.0));
840        }
841
842        #[test]
843        fn prop_km_m_ratio(k in 1e-6..1e6f64) {
844            let km = Kilometers::new(k);
845            let m = km.to::<Meter>();
846            // 1 km = 1000 m
847            prop_assert!((m.value() / km.value() - 1000.0).abs() < 1e-9);
848        }
849
850        #[test]
851        fn prop_roundtrip_au_km(a in 1e-6..1e6f64) {
852            let original = AstronomicalUnits::new(a);
853            let converted = original.to::<Kilometer>();
854            let back = converted.to::<AstronomicalUnit>();
855            prop_assert!((back.value() - original.value()).abs() / original.value() < 1e-12);
856        }
857
858        #[test]
859        fn prop_roundtrip_inch_m(i in -1e6..1e6f64) {
860            let original = Inches::new(i);
861            let converted = original.to::<Meter>();
862            let back = converted.to::<Inch>();
863            let scale = i.abs().max(1.0);
864            prop_assert!((back.value() - original.value()).abs() < 1e-9 * scale);
865        }
866    }
867}