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_conversions!(SolarRadius, Kilometer);
559}
560
561// Generate all bidirectional From implementations between length units.
562//
563// This single invocation ensures that any quantity measured in one length unit can be
564// converted into any other via `From`/`Into`, mirroring the previous behavior while
565// including the extended unit set.
566crate::impl_unit_conversions!(
567    Meter,
568    Decimeter,
569    Centimeter,
570    Millimeter,
571    Micrometer,
572    Nanometer,
573    Picometer,
574    Femtometer,
575    Attometer,
576    Zeptometer,
577    Yoctometer,
578    Decameter,
579    Hectometer,
580    Kilometer,
581    Megameter,
582    Gigameter,
583    Terameter,
584    Petameter,
585    Exameter,
586    Zettameter,
587    Yottameter,
588    AstronomicalUnit,
589    LightYear,
590    Parsec,
591    Kiloparsec,
592    Megaparsec,
593    Gigaparsec,
594    Inch,
595    Foot,
596    Yard,
597    Mile,
598    NauticalMile,
599    Chain,
600    Rod,
601    Link,
602    Fathom,
603    EarthMeridionalCircumference,
604    EarthEquatorialCircumference,
605    BohrRadius,
606    ClassicalElectronRadius,
607    PlanckLength,
608    ElectronReducedComptonWavelength
609);
610
611#[cfg(test)]
612mod tests {
613    use super::nominal::SolarRadiuses;
614    use super::*;
615    use approx::{assert_abs_diff_eq, assert_relative_eq};
616    use proptest::prelude::*;
617
618    // ─────────────────────────────────────────────────────────────────────────────
619    // Basic conversions
620    // ─────────────────────────────────────────────────────────────────────────────
621
622    #[test]
623    fn kilometer_to_meter() {
624        let km = Kilometers::new(1.0);
625        let m = km.to::<Meter>();
626        assert_abs_diff_eq!(m.value(), 1000.0, epsilon = 1e-9);
627    }
628
629    #[test]
630    fn meter_to_kilometer() {
631        let m = Meters::new(1000.0);
632        let km = m.to::<Kilometer>();
633        assert_abs_diff_eq!(km.value(), 1.0, epsilon = 1e-12);
634    }
635
636    #[test]
637    fn au_to_meters() {
638        let au = AstronomicalUnits::new(1.0);
639        let m = au.to::<Meter>();
640        // 1 AU = 149,597,870,700 meters (exact, IAU 2012).
641        assert_abs_diff_eq!(m.value(), 149_597_870_700.0, epsilon = 1e-6);
642    }
643
644    #[test]
645    fn au_to_kilometers() {
646        let au = AstronomicalUnits::new(1.0);
647        let km = au.to::<Kilometer>();
648        // 1 AU = 149,597,870,700 m => 149,597,870.7 km.
649        assert_relative_eq!(km.value(), 149_597_870.7, max_relative = 1e-12);
650    }
651
652    #[test]
653    fn light_year_to_meters() {
654        let ly = LightYears::new(1.0);
655        let m = ly.to::<Meter>();
656        // 1 LY = c * 365.25 d, where d = 86400 s
657        assert_relative_eq!(m.value(), METERS_PER_LIGHT_YEAR, max_relative = 1e-12);
658    }
659
660    #[test]
661    fn light_year_to_kilometers() {
662        let ly = LightYears::new(1.0);
663        let km = ly.to::<Kilometer>();
664        // 1 LY ≈ 9.461e12 km
665        assert_relative_eq!(km.value(), 9_460_730_472_580.000_8, max_relative = 1e-9);
666    }
667
668    // ─────────────────────────────────────────────────────────────────────────────
669    // AU <-> LY conversions
670    // ─────────────────────────────────────────────────────────────────────────────
671
672    #[test]
673    fn au_to_light_year() {
674        let au = AstronomicalUnits::new(1.0);
675        let ly = au.to::<LightYear>();
676        // 1 AU ≈ 1.582e-5 LY
677        assert_relative_eq!(ly.value(), 1.582e-5, max_relative = 1e-3);
678    }
679
680    #[test]
681    fn light_year_to_au() {
682        let ly = LightYears::new(1.0);
683        let au = ly.to::<AstronomicalUnit>();
684        // 1 LY ≈ 63,241 AU
685        assert_relative_eq!(au.value(), 63241.0, max_relative = 1e-3);
686    }
687
688    #[test]
689    fn from_impl_au_to_ly() {
690        let au = 1.0 * AU;
691        let ly: LightYears = au.into();
692        assert_relative_eq!(ly.value(), 1.582e-5, max_relative = 1e-3);
693    }
694
695    #[test]
696    fn from_impl_ly_to_au() {
697        let ly = 1.0 * LY;
698        let au: AstronomicalUnits = ly.into();
699        assert_relative_eq!(au.value(), 63241.0, max_relative = 1e-3);
700    }
701
702    // ─────────────────────────────────────────────────────────────────────────────
703    // Parsec conversions
704    // ─────────────────────────────────────────────────────────────────────────────
705
706    #[test]
707    fn parsec_to_light_year() {
708        let pc = Parsecs::new(1.0);
709        let ly = pc.to::<LightYear>();
710        // 1 pc expressed in light-years, using the exact AU-based definition.
711        let expected = (AstronomicalUnit::RATIO * (648_000.0 / PI)) / LightYear::RATIO;
712        assert_relative_eq!(ly.value(), expected, max_relative = 1e-15);
713    }
714
715    #[test]
716    fn parsec_to_au() {
717        let pc = Parsecs::new(1.0);
718        let au = pc.to::<AstronomicalUnit>();
719        // 1 pc ≈ 206,265 AU (using exact definition: 1 pc = 3.26 LY, 1 LY ≈ 63241 AU)
720        // So 1 pc ≈ 3.26 * 63241 ≈ 206,165 AU
721        assert_relative_eq!(au.value(), 3.26 * 63241.0, max_relative = 1e-2);
722    }
723
724    #[test]
725    fn parsec_ratio_sanity() {
726        // Parsec is defined from AU: pc = au * 648000 / π
727        let lhs = Parsec::RATIO / AstronomicalUnit::RATIO;
728        let rhs = 648_000.0 / PI;
729        assert_relative_eq!(lhs, rhs, max_relative = 1e-12);
730    }
731
732    // ─────────────────────────────────────────────────────────────────────────────
733    // Solar radius
734    // ─────────────────────────────────────────────────────────────────────────────
735
736    #[test]
737    fn solar_radius_to_meters() {
738        let sr = SolarRadiuses::new(1.0);
739        let m = sr.to::<Meter>();
740        // 1 R☉ = 695,700 km = 695,700,000 m
741        assert_abs_diff_eq!(m.value(), 695_700_000.0, epsilon = 1e-3);
742    }
743
744    #[test]
745    fn solar_radius_to_km() {
746        let sr = SolarRadiuses::new(1.0);
747        let km = sr.to::<Kilometer>();
748        // 1 R☉ = 695,700 km
749        assert_abs_diff_eq!(km.value(), 695_700.0, epsilon = 1e-6);
750    }
751
752    // ─────────────────────────────────────────────────────────────────────────────
753    // Roundtrip conversions
754    // ─────────────────────────────────────────────────────────────────────────────
755
756    #[test]
757    fn roundtrip_km_m() {
758        let original = Kilometers::new(42.5);
759        let converted = original.to::<Meter>();
760        let back = converted.to::<Kilometer>();
761        assert_abs_diff_eq!(back.value(), original.value(), epsilon = 1e-12);
762    }
763
764    #[test]
765    fn roundtrip_au_ly() {
766        let original = AstronomicalUnits::new(10000.0);
767        let converted = original.to::<LightYear>();
768        let back = converted.to::<AstronomicalUnit>();
769        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
770    }
771
772    #[test]
773    fn roundtrip_parsec_ly() {
774        let original = Parsecs::new(5.0);
775        let converted = original.to::<LightYear>();
776        let back = converted.to::<Parsec>();
777        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
778    }
779
780    // ─────────────────────────────────────────────────────────────────────────
781    // Exact relationship tests for new units
782    // ─────────────────────────────────────────────────────────────────────────
783
784    #[test]
785    fn inch_to_meter_exact_ratio() {
786        let inch = Inches::new(1.0);
787        let m = inch.to::<Meter>();
788        // International inch: exactly 0.0254 m
789        assert_relative_eq!(m.value(), 0.0254, max_relative = 1e-16);
790    }
791
792    #[test]
793    fn nautical_mile_to_meter_exact_ratio() {
794        let nmi = NauticalMiles::new(1.0);
795        let m = nmi.to::<Meter>();
796        // International nautical mile: exactly 1852 m
797        assert_abs_diff_eq!(m.value(), 1852.0, epsilon = 1e-12);
798    }
799
800    // ─────────────────────────────────────────────────────────────────────────
801    // Roundtrip sanity for representative units
802    // ─────────────────────────────────────────────────────────────────────────
803
804    #[test]
805    fn roundtrip_inch_meter() {
806        let original = Inches::new(123.456);
807        let converted = original.to::<Meter>();
808        let back = converted.to::<Inch>();
809        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
810    }
811
812    #[test]
813    fn roundtrip_nautical_mile_meter() {
814        let original = NauticalMiles::new(3.75);
815        let converted = original.to::<Meter>();
816        let back = converted.to::<NauticalMile>();
817        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
818    }
819
820    #[test]
821    fn roundtrip_parsec_kpc() {
822        let original = Parsecs::new(12_345.0);
823        let converted = original.to::<Kiloparsec>();
824        let back = converted.to::<Parsec>();
825        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
826    }
827
828    // ─────────────────────────────────────────────────────────────────────────────
829    // Property-based tests
830    // ─────────────────────────────────────────────────────────────────────────────
831
832    proptest! {
833        #[test]
834        fn prop_roundtrip_km_m(k in -1e6..1e6f64) {
835            let original = Kilometers::new(k);
836            let converted = original.to::<Meter>();
837            let back = converted.to::<Kilometer>();
838            prop_assert!((back.value() - original.value()).abs() < 1e-9 * k.abs().max(1.0));
839        }
840
841        #[test]
842        fn prop_km_m_ratio(k in 1e-6..1e6f64) {
843            let km = Kilometers::new(k);
844            let m = km.to::<Meter>();
845            // 1 km = 1000 m
846            prop_assert!((m.value() / km.value() - 1000.0).abs() < 1e-9);
847        }
848
849        #[test]
850        fn prop_roundtrip_au_km(a in 1e-6..1e6f64) {
851            let original = AstronomicalUnits::new(a);
852            let converted = original.to::<Kilometer>();
853            let back = converted.to::<AstronomicalUnit>();
854            prop_assert!((back.value() - original.value()).abs() / original.value() < 1e-12);
855        }
856
857        #[test]
858        fn prop_roundtrip_inch_m(i in -1e6..1e6f64) {
859            let original = Inches::new(i);
860            let converted = original.to::<Meter>();
861            let back = converted.to::<Inch>();
862            let scale = i.abs().max(1.0);
863            prop_assert!((back.value() - original.value()).abs() < 1e-9 * scale);
864        }
865    }
866}