Skip to main content

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