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//!
37//! ## All length units
38//!
39//! ```rust
40//! use qtty_core::length::*;
41//! use qtty_core::length::nominal::*;
42//!
43//! macro_rules! touch {
44//!     ($T:ty, $v:expr) => {{
45//!         let q = <$T>::new($v);
46//!         let _cloned = q;
47//!         assert!(q == q);
48//!     }};
49//! }
50//!
51//! // SI sub-meter
52//! touch!(Meters, 1.0); touch!(Decimeters, 1.0); touch!(Centimeters, 1.0);
53//! touch!(Millimeters, 1.0); touch!(Micrometers, 1.0); touch!(Nanometers, 1.0);
54//! touch!(Picometers, 1.0); touch!(Femtometers, 1.0); touch!(Attometers, 1.0);
55//! touch!(Zeptometers, 1.0); touch!(Yoctometers, 1.0);
56//! // SI super-meter
57//! touch!(Decameters, 1.0); touch!(Hectometers, 1.0); touch!(Kilometers, 1.0);
58//! touch!(Megameters, 1.0); touch!(Gigameters, 1.0); touch!(Terameters, 1.0);
59//! touch!(Petameters, 1.0); touch!(Exameters, 1.0); touch!(Zettameters, 1.0);
60//! touch!(Yottameters, 1.0);
61//! // Astronomical
62//! touch!(AstronomicalUnits, 1.0); touch!(LightYears, 1.0); touch!(Parsecs, 1.0);
63//! touch!(Kiloparsecs, 1.0); touch!(Megaparsecs, 1.0); touch!(Gigaparsecs, 1.0);
64//! // Imperial
65//! touch!(Inches, 1.0); touch!(Feet, 1.0); touch!(Yards, 1.0);
66//! touch!(Miles, 1.0); touch!(NauticalMiles, 1.0); touch!(Chains, 1.0);
67//! touch!(Rods, 1.0); touch!(Links, 1.0); touch!(Fathoms, 1.0);
68//! // Geodesy
69//! touch!(EarthMeridionalCircumferences, 1.0);
70//! touch!(EarthEquatorialCircumferences, 1.0);
71//! // Physics
72//! touch!(BohrRadii, 1.0); touch!(ClassicalElectronRadii, 1.0);
73//! touch!(PlanckLengths, 1.0); touch!(ElectronReducedComptonWavelengths, 1.0);
74//! // Nominal
75//! touch!(SolarRadiuses, 1.0); touch!(EarthRadii, 1.0);
76//! touch!(EarthEquatorialRadii, 1.0); touch!(EarthPolarRadii, 1.0);
77//! touch!(LunarRadii, 1.0); touch!(JupiterRadii, 1.0);
78//! touch!(LunarDistances, 1.0); touch!(SolarDiameters, 1.0);
79//! ```
80
81use crate::{Quantity, Unit};
82use core::f64::consts::PI;
83use qtty_derive::Unit;
84
85/// Re-export from the dimension module.
86pub use crate::dimension::Length;
87
88/// Marker trait for any [`Unit`] whose dimension is [`Length`].
89pub trait LengthUnit: Unit<Dim = Length> {}
90impl<T: Unit<Dim = Length>> LengthUnit for T {}
91
92// ─────────────────────────────────────────────────────────────────────────────
93// SI base unit and core helpers
94// ─────────────────────────────────────────────────────────────────────────────
95
96/// Metre (SI base unit).
97#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
98#[unit(symbol = "m", dimension = Length, ratio = 1.0)]
99pub struct Meter;
100/// A quantity measured in metres.
101pub type Meters = Quantity<Meter>;
102/// One metre.
103pub const M: Meters = Meters::new(1.0);
104
105/// Kilometre (`1000 m`).
106#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
107#[unit(symbol = "km", dimension = Length, ratio = 1_000.0)]
108pub struct Kilometer;
109/// Type alias shorthand for [`Kilometer`].
110pub type Km = Kilometer;
111/// A quantity measured in kilometres.
112pub type Kilometers = Quantity<Km>;
113/// One kilometre.
114pub const KM: Kilometers = Kilometers::new(1.0);
115
116/// Centimetre (`1e-2 m`).
117#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
118#[unit(symbol = "cm", dimension = Length, ratio = 1e-2)]
119pub struct Centimeter;
120/// Type alias shorthand for [`Centimeter`].
121pub type Cm = Centimeter;
122/// A quantity measured in centimetres.
123pub type Centimeters = Quantity<Cm>;
124/// One centimetre.
125pub const CM: Centimeters = Centimeters::new(1.0);
126
127/// Millimetre (`1e-3 m`).
128#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
129#[unit(symbol = "mm", dimension = Length, ratio = 1e-3)]
130pub struct Millimeter;
131/// Type alias shorthand for [`Millimeter`].
132pub type Mm = Millimeter;
133/// A quantity measured in millimetres.
134pub type Millimeters = Quantity<Mm>;
135/// One millimetre.
136pub const MM: Millimeters = Millimeters::new(1.0);
137
138/// Micrometre (`1e-6 m`).
139#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
140#[unit(symbol = "μm", dimension = Length, ratio = 1e-6)]
141pub struct Micrometer;
142/// Type alias shorthand for [`Micrometer`].
143pub type Um = Micrometer;
144/// A quantity measured in micrometres.
145pub type Micrometers = Quantity<Um>;
146/// One micrometre.
147pub const UM: Micrometers = Micrometers::new(1.0);
148
149/// Nanometre (`1e-9 m`).
150#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
151#[unit(symbol = "nm", dimension = Length, ratio = 1e-9)]
152pub struct Nanometer;
153/// Type alias shorthand for [`Nanometer`].
154pub type Nm = Nanometer;
155/// A quantity measured in nanometres.
156pub type Nanometers = Quantity<Nm>;
157/// One nanometre.
158pub const NM: Nanometers = Nanometers::new(1.0);
159
160/// Picometre (`1e-12 m`).
161#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
162#[unit(symbol = "pm", dimension = Length, ratio = 1e-12)]
163pub struct Picometer;
164/// A quantity measured in picometres.
165pub type Picometers = Quantity<Picometer>;
166/// One picometre.
167pub const PMETER: Picometers = Picometers::new(1.0);
168
169/// Femtometre (`1e-15 m`).
170#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
171#[unit(symbol = "fm", dimension = Length, ratio = 1e-15)]
172pub struct Femtometer;
173/// A quantity measured in femtometres.
174pub type Femtometers = Quantity<Femtometer>;
175/// One femtometre.
176pub const FM: Femtometers = Femtometers::new(1.0);
177
178/// Attometre (`1e-18 m`).
179#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
180#[unit(symbol = "am", dimension = Length, ratio = 1e-18)]
181pub struct Attometer;
182/// A quantity measured in attometres.
183pub type Attometers = Quantity<Attometer>;
184/// One attometre.
185pub const AM: Attometers = Attometers::new(1.0);
186
187/// Zeptometre (`1e-21 m`).
188#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
189#[unit(symbol = "zm", dimension = Length, ratio = 1e-21)]
190pub struct Zeptometer;
191/// A quantity measured in zeptometres.
192pub type Zeptometers = Quantity<Zeptometer>;
193/// One zeptometre.
194pub const ZMETER: Zeptometers = Zeptometers::new(1.0);
195
196/// Yoctometre (`1e-24 m`).
197#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
198#[unit(symbol = "ym", dimension = Length, ratio = 1e-24)]
199pub struct Yoctometer;
200/// A quantity measured in yoctometres.
201pub type Yoctometers = Quantity<Yoctometer>;
202/// One yoctometre.
203pub const YMETER: Yoctometers = Yoctometers::new(1.0);
204
205/// Megametre (`1e6 m`).
206#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
207#[unit(symbol = "Mm", dimension = Length, ratio = 1e6)]
208pub struct Megameter;
209/// Type alias shorthand for [`Megameter`].
210pub type MegaMeter = Megameter;
211/// A quantity measured in megametres.
212pub type Megameters = Quantity<Megameter>;
213/// One megametre.
214pub const MEGAMETER: Megameters = Megameters::new(1.0);
215
216/// Decimetre (`1e-1 m`).
217#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
218#[unit(symbol = "dm", dimension = Length, ratio = 1e-1)]
219pub struct Decimeter;
220/// A quantity measured in decimetres.
221pub type Decimeters = Quantity<Decimeter>;
222/// One decimetre.
223pub const DM: Decimeters = Decimeters::new(1.0);
224
225/// Decametre (`1e1 m`).
226#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
227#[unit(symbol = "dam", dimension = Length, ratio = 1e1)]
228pub struct Decameter;
229/// A quantity measured in decametres.
230pub type Decameters = Quantity<Decameter>;
231/// One decametre.
232pub const DAM: Decameters = Decameters::new(1.0);
233
234/// Hectometre (`1e2 m`).
235#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
236#[unit(symbol = "hm", dimension = Length, ratio = 1e2)]
237pub struct Hectometer;
238/// A quantity measured in hectometres.
239pub type Hectometers = Quantity<Hectometer>;
240/// One hectometre.
241pub const HM: Hectometers = Hectometers::new(1.0);
242
243/// Gigametre (`1e9 m`).
244#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
245#[unit(symbol = "Gm", dimension = Length, ratio = 1e9)]
246pub struct Gigameter;
247/// A quantity measured in gigametres.
248pub type Gigameters = Quantity<Gigameter>;
249/// One gigametre.
250pub const GM: Gigameters = Gigameters::new(1.0);
251
252/// Terametre (`1e12 m`).
253#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
254#[unit(symbol = "Tm", dimension = Length, ratio = 1e12)]
255pub struct Terameter;
256/// A quantity measured in terametres.
257pub type Terameters = Quantity<Terameter>;
258/// One terametre.
259pub const TM: Terameters = Terameters::new(1.0);
260
261/// Petametre (`1e15 m`).
262#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
263#[unit(symbol = "Pm", dimension = Length, ratio = 1e15)]
264pub struct Petameter;
265/// A quantity measured in petametres.
266pub type Petameters = Quantity<Petameter>;
267/// One petametre.
268pub const PM: Petameters = Petameters::new(1.0);
269
270/// Exametre (`1e18 m`).
271#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
272#[unit(symbol = "Em", dimension = Length, ratio = 1e18)]
273pub struct Exameter;
274/// A quantity measured in exametres.
275pub type Exameters = Quantity<Exameter>;
276/// One exametre.
277pub const EM: Exameters = Exameters::new(1.0);
278
279/// Zettametre (`1e21 m`).
280#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
281#[unit(symbol = "Zm", dimension = Length, ratio = 1e21)]
282pub struct Zettameter;
283/// A quantity measured in zettametres.
284pub type Zettameters = Quantity<Zettameter>;
285/// One zettametre.
286pub const ZM: Zettameters = Zettameters::new(1.0);
287
288/// Yottametre (`1e24 m`).
289#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
290#[unit(symbol = "Ym", dimension = Length, ratio = 1e24)]
291pub struct Yottameter;
292/// A quantity measured in yottametres.
293pub type Yottameters = Quantity<Yottameter>;
294/// One yottametre.
295pub const YM: Yottameters = Yottameters::new(1.0);
296
297// ─────────────────────────────────────────────────────────────────────────────
298// Astronomical distance units
299// ─────────────────────────────────────────────────────────────────────────────
300
301/// Astronomical unit (au). Exact (IAU 2012): metres per au.
302#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
303#[unit(symbol = "au", dimension = Length, ratio = 149_597_870_700.0)]
304pub struct AstronomicalUnit;
305/// Type alias shorthand for [`AstronomicalUnit`].
306pub type Au = AstronomicalUnit;
307/// A quantity measured in astronomical units.
308pub type AstronomicalUnits = Quantity<Au>;
309/// One astronomical unit.
310pub const AU: AstronomicalUnits = AstronomicalUnits::new(1.0);
311
312// Exact speed of light and Julian year, used to derive the light‑year ratio.
313const SPEED_OF_LIGHT_M_PER_S: f64 = 299_792_458.0;
314const SECONDS_PER_DAY: f64 = 86_400.0;
315const DAYS_PER_JULIAN_YEAR: f64 = 36525.0 / 100.0; // 365.25 d
316const SECONDS_PER_JULIAN_YEAR: f64 = SECONDS_PER_DAY * DAYS_PER_JULIAN_YEAR;
317const METERS_PER_LIGHT_YEAR: f64 = SPEED_OF_LIGHT_M_PER_S * SECONDS_PER_JULIAN_YEAR;
318
319/// Light-year (ly): distance light travels in one Julian year (`365.25 d`) at `c = 299_792_458 m/s`.
320#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
321#[unit(symbol = "ly", dimension = Length, ratio = METERS_PER_LIGHT_YEAR)]
322pub struct LightYear;
323/// Type alias shorthand for [`LightYear`].
324pub type Ly = LightYear;
325/// A quantity measured in light-years.
326pub type LightYears = Quantity<Ly>;
327/// One light-year.
328pub const LY: LightYears = LightYears::new(1.0);
329
330/// Parsec (pc): `pc = au * 648000 / π` (exact given au).
331#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
332#[unit(symbol = "pc", dimension = Length, ratio = 149_597_870_700.0 * (648_000.0 / PI))]
333pub struct Parsec;
334/// Type alias shorthand for [`Parsec`].
335pub type Pc = Parsec;
336/// A quantity measured in parsecs.
337pub type Parsecs = Quantity<Pc>;
338/// One parsec.
339pub const PC: Parsecs = Parsecs::new(1.0);
340
341/// Kiloparsec (kpc): `1e3 pc`.
342#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
343#[unit(symbol = "kpc", dimension = Length, ratio = 1_000.0 * 149_597_870_700.0 * (648_000.0 / PI))]
344pub struct Kiloparsec;
345/// A quantity measured in kiloparsecs.
346pub type Kiloparsecs = Quantity<Kiloparsec>;
347/// One kiloparsec.
348pub const KPC: Kiloparsecs = Kiloparsecs::new(1.0);
349
350/// Megaparsec (Mpc): `1e6 pc`.
351#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
352#[unit(symbol = "Mpc", dimension = Length, ratio = 1_000_000.0 * 149_597_870_700.0 * (648_000.0 / PI))]
353pub struct Megaparsec;
354/// A quantity measured in megaparsecs.
355pub type Megaparsecs = Quantity<Megaparsec>;
356/// One megaparsec.
357pub const MPC: Megaparsecs = Megaparsecs::new(1.0);
358
359/// Gigaparsec (Gpc): `1e9 pc`.
360#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
361#[unit(symbol = "Gpc", dimension = Length, ratio = 1_000_000_000.0 * 149_597_870_700.0 * (648_000.0 / PI))]
362pub struct Gigaparsec;
363/// A quantity measured in gigaparsecs.
364pub type Gigaparsecs = Quantity<Gigaparsec>;
365/// One gigaparsec.
366pub const GPC: Gigaparsecs = Gigaparsecs::new(1.0);
367
368// ─────────────────────────────────────────────────────────────────────────────
369// Imperial, US customary, and surveying units
370// ─────────────────────────────────────────────────────────────────────────────
371
372/// Inch (`0.0254 m` exactly).
373#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
374#[unit(symbol = "in", dimension = Length, ratio = 254.0 / 10_000.0)]
375pub struct Inch;
376/// A quantity measured in inches.
377pub type Inches = Quantity<Inch>;
378/// One inch.
379pub const INCH: Inches = Inches::new(1.0);
380
381/// Foot (`0.3048 m` exactly).
382#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
383#[unit(symbol = "ft", dimension = Length, ratio = 3048.0 / 10_000.0)]
384pub struct Foot;
385/// A quantity measured in feet.
386pub type Feet = Quantity<Foot>;
387/// One foot.
388pub const FT: Feet = Feet::new(1.0);
389
390/// Yard (`0.9144 m` exactly).
391#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
392#[unit(symbol = "yd", dimension = Length, ratio = 9144.0 / 10_000.0)]
393pub struct Yard;
394/// A quantity measured in yards.
395pub type Yards = Quantity<Yard>;
396/// One yard.
397pub const YD: Yards = Yards::new(1.0);
398
399/// (Statute) mile (`1609.344 m` exactly).
400#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
401#[unit(symbol = "mi", dimension = Length, ratio = 1_609_344.0 / 1_000.0)]
402pub struct Mile;
403/// A quantity measured in miles.
404pub type Miles = Quantity<Mile>;
405/// One mile.
406pub const MI: Miles = Miles::new(1.0);
407
408/// Nautical mile (`1852 m` exactly).
409#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
410#[unit(symbol = "nmi", dimension = Length, ratio = 1_852.0)]
411pub struct NauticalMile;
412/// A quantity measured in nautical miles.
413pub type NauticalMiles = Quantity<NauticalMile>;
414/// One nautical mile.
415pub const NMI: NauticalMiles = NauticalMiles::new(1.0);
416
417/// Chain (`66 ft` exactly).
418#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
419#[unit(symbol = "ch", dimension = Length, ratio = 66.0 * (3048.0 / 10_000.0))]
420pub struct Chain;
421/// A quantity measured in chains.
422pub type Chains = Quantity<Chain>;
423/// One chain.
424pub const CHAIN: Chains = Chains::new(1.0);
425
426/// Rod / pole / perch (`16.5 ft` exactly).
427#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
428#[unit(symbol = "rd", dimension = Length, ratio = 16.5 * (3048.0 / 10_000.0))]
429pub struct Rod;
430/// A quantity measured in rods/poles/perches.
431pub type Rods = Quantity<Rod>;
432/// One rod.
433pub const ROD: Rods = Rods::new(1.0);
434
435/// Link (`1/100 of a chain`, i.e. `0.66 ft`).
436#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
437#[unit(symbol = "lk", dimension = Length, ratio = (66.0 / 100.0) * (3048.0 / 10_000.0))]
438pub struct Link;
439/// A quantity measured in links.
440pub type Links = Quantity<Link>;
441/// One link.
442pub const LINK: Links = Links::new(1.0);
443
444/// Fathom (`6 ft` exactly).
445#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
446#[unit(symbol = "ftm", dimension = Length, ratio = 6.0 * (3048.0 / 10_000.0))]
447pub struct Fathom;
448/// A quantity measured in fathoms.
449pub type Fathoms = Quantity<Fathom>;
450/// One fathom.
451pub const FTM: Fathoms = Fathoms::new(1.0);
452
453// ─────────────────────────────────────────────────────────────────────────────
454// Geodesy and navigation
455// ─────────────────────────────────────────────────────────────────────────────
456
457/// Earth meridional circumference (approximate mean value).
458#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
459#[unit(symbol = "Cmer", dimension = Length, ratio = 40_007_863.0)]
460pub struct EarthMeridionalCircumference;
461/// A quantity measured in Earth meridional circumferences.
462pub type EarthMeridionalCircumferences = Quantity<EarthMeridionalCircumference>;
463/// One Earth meridional circumference.
464pub const C_MERIDIONAL: EarthMeridionalCircumferences = EarthMeridionalCircumferences::new(1.0);
465
466/// Earth equatorial circumference.
467#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
468#[unit(symbol = "Ceq", dimension = Length, ratio = 40_075_017.0)]
469pub struct EarthEquatorialCircumference;
470/// A quantity measured in Earth equatorial circumferences.
471pub type EarthEquatorialCircumferences = Quantity<EarthEquatorialCircumference>;
472/// One Earth equatorial circumference.
473pub const C_EQUATORIAL: EarthEquatorialCircumferences = EarthEquatorialCircumferences::new(1.0);
474
475// ─────────────────────────────────────────────────────────────────────────────
476// Fundamental physics lengths (CODATA values)
477// ─────────────────────────────────────────────────────────────────────────────
478
479/// Bohr radius (`a0`). CODATA 2018 value in metres.
480#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
481#[unit(symbol = "a0", dimension = Length, ratio = 5.291_772_109_03e-11)]
482pub struct BohrRadius;
483/// A quantity measured in Bohr radii.
484pub type BohrRadii = Quantity<BohrRadius>;
485/// One Bohr radius.
486pub const A0: BohrRadii = BohrRadii::new(1.0);
487
488/// Classical electron radius (`re`). CODATA 2018 value in metres.
489#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
490#[unit(symbol = "re", dimension = Length, ratio = 2.817_940_326_2e-15)]
491pub struct ClassicalElectronRadius;
492/// A quantity measured in classical electron radii.
493pub type ClassicalElectronRadii = Quantity<ClassicalElectronRadius>;
494/// One classical electron radius.
495pub const RE: ClassicalElectronRadii = ClassicalElectronRadii::new(1.0);
496
497/// Planck length (`lp`). CODATA 2018 value in metres.
498#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
499#[unit(symbol = "lp", dimension = Length, ratio = 1.616_255e-35)]
500pub struct PlanckLength;
501/// A quantity measured in Planck lengths.
502pub type PlanckLengths = Quantity<PlanckLength>;
503/// One Planck length.
504pub const LP: PlanckLengths = PlanckLengths::new(1.0);
505
506/// Reduced Compton wavelength of the electron (`lambda_bar_e`). CODATA 2018 value in metres.
507#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
508#[unit(symbol = "lambda_bar_e", dimension = Length, ratio = 3.861_592_679_6e-13)]
509pub struct ElectronReducedComptonWavelength;
510/// A quantity measured in reduced Compton wavelengths of the electron.
511pub type ElectronReducedComptonWavelengths = Quantity<ElectronReducedComptonWavelength>;
512/// One reduced Compton wavelength of the electron.
513pub const LAMBDA_BAR_E: ElectronReducedComptonWavelengths =
514    ElectronReducedComptonWavelengths::new(1.0);
515
516// ─────────────────────────────────────────────────────────────────────────────
517// Nominal radii and distances
518// ─────────────────────────────────────────────────────────────────────────────
519
520/// Nominal astronomical and planetary radii and related distances.
521///
522/// Values in this module are **nominal** (conventionally rounded) and are kept separate from the
523/// main length namespace to avoid confusion with strictly defined units.
524pub mod nominal {
525    use super::*;
526
527    /// Solar radius (R☉). Nominal value: metres per R☉.
528    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
529    #[unit(symbol = "Rsun", dimension = Length, ratio = 695_700_000.0)]
530    pub struct SolarRadius;
531    /// A quantity measured in solar radii.
532    pub type SolarRadiuses = Quantity<SolarRadius>;
533    /// One solar radius.
534    pub const RSUN: SolarRadiuses = SolarRadiuses::new(1.0);
535
536    /// Earth mean radius (nominal).
537    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
538    #[unit(symbol = "Rearth", dimension = Length, ratio = 6_371_000.0)]
539    pub struct EarthRadius;
540    /// A quantity measured in Earth radii.
541    pub type EarthRadii = Quantity<EarthRadius>;
542    /// One Earth radius (mean).
543    pub const R_EARTH: EarthRadii = EarthRadii::new(1.0);
544
545    /// Earth equatorial radius (WGS84).
546    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
547    #[unit(symbol = "Rearth_eq", dimension = Length, ratio = 6_378_137.0)]
548    pub struct EarthEquatorialRadius;
549    /// A quantity measured in Earth equatorial radii.
550    pub type EarthEquatorialRadii = Quantity<EarthEquatorialRadius>;
551    /// One Earth equatorial radius.
552    pub const R_EARTH_EQ: EarthEquatorialRadii = EarthEquatorialRadii::new(1.0);
553
554    /// Earth polar radius.
555    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
556    #[unit(symbol = "Rearth_p", dimension = Length, ratio = 6_356_752.314_2)]
557    pub struct EarthPolarRadius;
558    /// A quantity measured in Earth polar radii.
559    pub type EarthPolarRadii = Quantity<EarthPolarRadius>;
560    /// One Earth polar radius.
561    pub const R_EARTH_P: EarthPolarRadii = EarthPolarRadii::new(1.0);
562
563    /// Lunar radius (mean, nominal).
564    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
565    #[unit(symbol = "Rmoon", dimension = Length, ratio = 1_737_400.0)]
566    pub struct LunarRadius;
567    /// A quantity measured in lunar radii.
568    pub type LunarRadii = Quantity<LunarRadius>;
569    /// One lunar radius.
570    pub const R_MOON: LunarRadii = LunarRadii::new(1.0);
571
572    /// Jupiter equatorial radius (nominal).
573    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
574    #[unit(symbol = "Rjup", dimension = Length, ratio = 71_492_000.0)]
575    pub struct JupiterRadius;
576    /// A quantity measured in Jupiter radii.
577    pub type JupiterRadii = Quantity<JupiterRadius>;
578    /// One Jupiter radius.
579    pub const R_JUPITER: JupiterRadii = JupiterRadii::new(1.0);
580
581    /// Lunar distance (Earth–Moon mean distance, LD).
582    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
583    #[unit(symbol = "LD", dimension = Length, ratio = 384_400_000.0)]
584    pub struct LunarDistance;
585    /// A quantity measured in lunar distances.
586    pub type LunarDistances = Quantity<LunarDistance>;
587    /// One lunar distance.
588    pub const LD: LunarDistances = LunarDistances::new(1.0);
589
590    /// Solar diameter (twice the solar radius).
591    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
592    #[unit(symbol = "Dsun", dimension = Length, ratio = 2.0 * 695_700_000.0)]
593    pub struct SolarDiameter;
594    /// A quantity measured in solar diameters.
595    pub type SolarDiameters = Quantity<SolarDiameter>;
596    /// One solar diameter.
597    pub const D_SUN: SolarDiameters = SolarDiameters::new(1.0);
598
599    // Allow convenient conversions between selected nominal units and core
600    // length units (e.g., SolarRadius <-> Kilometer) without polluting the
601    // main length namespace with nominal types.
602    crate::impl_unit_from_conversions!(SolarRadius, Kilometer);
603    #[cfg(feature = "cross-unit-ops")]
604    crate::impl_unit_cross_unit_ops!(SolarRadius, Kilometer);
605}
606
607// Generate all bidirectional From implementations between length units.
608//
609// This single invocation ensures that any quantity measured in one length unit can be
610// converted into any other via `From`/`Into`, mirroring the previous behavior while
611// including the extended unit set.
612crate::impl_unit_from_conversions!(
613    Meter,
614    Decimeter,
615    Centimeter,
616    Millimeter,
617    Micrometer,
618    Nanometer,
619    Picometer,
620    Femtometer,
621    Attometer,
622    Zeptometer,
623    Yoctometer,
624    Decameter,
625    Hectometer,
626    Kilometer,
627    Megameter,
628    Gigameter,
629    Terameter,
630    Petameter,
631    Exameter,
632    Zettameter,
633    Yottameter,
634    AstronomicalUnit,
635    LightYear,
636    Parsec,
637    Kiloparsec,
638    Megaparsec,
639    Gigaparsec,
640    Inch,
641    Foot,
642    Yard,
643    Mile,
644    NauticalMile,
645    Chain,
646    Rod,
647    Link,
648    Fathom,
649    EarthMeridionalCircumference,
650    EarthEquatorialCircumference,
651    BohrRadius,
652    ClassicalElectronRadius,
653    PlanckLength,
654    ElectronReducedComptonWavelength
655);
656
657// Optional cross-unit operator support (`==`, `<`, etc.).
658#[cfg(feature = "cross-unit-ops")]
659crate::impl_unit_cross_unit_ops!(
660    Meter,
661    Decimeter,
662    Centimeter,
663    Millimeter,
664    Micrometer,
665    Nanometer,
666    Picometer,
667    Femtometer,
668    Attometer,
669    Zeptometer,
670    Yoctometer,
671    Decameter,
672    Hectometer,
673    Kilometer,
674    Megameter,
675    Gigameter,
676    Terameter,
677    Petameter,
678    Exameter,
679    Zettameter,
680    Yottameter,
681    AstronomicalUnit,
682    LightYear,
683    Parsec,
684    Kiloparsec,
685    Megaparsec,
686    Gigaparsec,
687    Inch,
688    Foot,
689    Yard,
690    Mile,
691    NauticalMile,
692    Chain,
693    Rod,
694    Link,
695    Fathom,
696    EarthMeridionalCircumference,
697    EarthEquatorialCircumference,
698    BohrRadius,
699    ClassicalElectronRadius,
700    PlanckLength,
701    ElectronReducedComptonWavelength
702);
703
704#[cfg(test)]
705mod tests {
706    use super::nominal::SolarRadiuses;
707    use super::*;
708    use approx::{assert_abs_diff_eq, assert_relative_eq};
709    use proptest::prelude::*;
710
711    // ─────────────────────────────────────────────────────────────────────────────
712    // Basic conversions
713    // ─────────────────────────────────────────────────────────────────────────────
714
715    #[test]
716    fn kilometer_to_meter() {
717        let km = Kilometers::new(1.0);
718        let m = km.to::<Meter>();
719        assert_abs_diff_eq!(m.value(), 1000.0, epsilon = 1e-9);
720    }
721
722    #[test]
723    fn meter_to_kilometer() {
724        let m = Meters::new(1000.0);
725        let km = m.to::<Kilometer>();
726        assert_abs_diff_eq!(km.value(), 1.0, epsilon = 1e-12);
727    }
728
729    #[test]
730    fn au_to_meters() {
731        let au = AstronomicalUnits::new(1.0);
732        let m = au.to::<Meter>();
733        // 1 AU = 149,597,870,700 meters (exact, IAU 2012).
734        assert_abs_diff_eq!(m.value(), 149_597_870_700.0, epsilon = 1e-6);
735    }
736
737    #[test]
738    fn au_to_kilometers() {
739        let au = AstronomicalUnits::new(1.0);
740        let km = au.to::<Kilometer>();
741        // 1 AU = 149,597,870,700 m => 149,597,870.7 km.
742        assert_relative_eq!(km.value(), 149_597_870.7, max_relative = 1e-12);
743    }
744
745    #[test]
746    fn light_year_to_meters() {
747        let ly = LightYears::new(1.0);
748        let m = ly.to::<Meter>();
749        // 1 LY = c * 365.25 d, where d = 86400 s
750        assert_relative_eq!(m.value(), METERS_PER_LIGHT_YEAR, max_relative = 1e-12);
751    }
752
753    #[test]
754    fn light_year_to_kilometers() {
755        let ly = LightYears::new(1.0);
756        let km = ly.to::<Kilometer>();
757        // 1 LY ≈ 9.461e12 km
758        assert_relative_eq!(km.value(), 9_460_730_472_580.000_8, max_relative = 1e-9);
759    }
760
761    // ─────────────────────────────────────────────────────────────────────────────
762    // AU <-> LY conversions
763    // ─────────────────────────────────────────────────────────────────────────────
764
765    #[test]
766    fn au_to_light_year() {
767        let au = AstronomicalUnits::new(1.0);
768        let ly = au.to::<LightYear>();
769        // 1 AU ≈ 1.582e-5 LY
770        assert_relative_eq!(ly.value(), 1.582e-5, max_relative = 1e-3);
771    }
772
773    #[test]
774    fn light_year_to_au() {
775        let ly = LightYears::new(1.0);
776        let au = ly.to::<AstronomicalUnit>();
777        // 1 LY ≈ 63,241 AU
778        assert_relative_eq!(au.value(), 63241.0, max_relative = 1e-3);
779    }
780
781    #[test]
782    fn from_impl_au_to_ly() {
783        let au = 1.0 * AU;
784        let ly: LightYears = au.into();
785        assert_relative_eq!(ly.value(), 1.582e-5, max_relative = 1e-3);
786    }
787
788    #[test]
789    fn from_impl_ly_to_au() {
790        let ly = 1.0 * LY;
791        let au: AstronomicalUnits = ly.into();
792        assert_relative_eq!(au.value(), 63241.0, max_relative = 1e-3);
793    }
794
795    // ─────────────────────────────────────────────────────────────────────────────
796    // Parsec conversions
797    // ─────────────────────────────────────────────────────────────────────────────
798
799    #[test]
800    fn parsec_to_light_year() {
801        let pc = Parsecs::new(1.0);
802        let ly = pc.to::<LightYear>();
803        // 1 pc expressed in light-years, using the exact AU-based definition.
804        let expected = (AstronomicalUnit::RATIO * (648_000.0 / PI)) / LightYear::RATIO;
805        assert_relative_eq!(ly.value(), expected, max_relative = 1e-15);
806    }
807
808    #[test]
809    fn parsec_to_au() {
810        let pc = Parsecs::new(1.0);
811        let au = pc.to::<AstronomicalUnit>();
812        // 1 pc ≈ 206,265 AU (using exact definition: 1 pc = 3.26 LY, 1 LY ≈ 63241 AU)
813        // So 1 pc ≈ 3.26 * 63241 ≈ 206,165 AU
814        assert_relative_eq!(au.value(), 3.26 * 63241.0, max_relative = 1e-2);
815    }
816
817    #[test]
818    fn parsec_ratio_sanity() {
819        // Parsec is defined from AU: pc = au * 648000 / π
820        let lhs = Parsec::RATIO / AstronomicalUnit::RATIO;
821        let rhs = 648_000.0 / PI;
822        assert_relative_eq!(lhs, rhs, max_relative = 1e-12);
823    }
824
825    // ─────────────────────────────────────────────────────────────────────────────
826    // Solar radius
827    // ─────────────────────────────────────────────────────────────────────────────
828
829    #[test]
830    fn solar_radius_to_meters() {
831        let sr = SolarRadiuses::new(1.0);
832        let m = sr.to::<Meter>();
833        // 1 R☉ = 695,700 km = 695,700,000 m
834        assert_abs_diff_eq!(m.value(), 695_700_000.0, epsilon = 1e-3);
835    }
836
837    #[test]
838    fn solar_radius_to_km() {
839        let sr = SolarRadiuses::new(1.0);
840        let km = sr.to::<Kilometer>();
841        // 1 R☉ = 695,700 km
842        assert_abs_diff_eq!(km.value(), 695_700.0, epsilon = 1e-6);
843    }
844
845    // ─────────────────────────────────────────────────────────────────────────────
846    // Roundtrip conversions
847    // ─────────────────────────────────────────────────────────────────────────────
848
849    #[test]
850    fn roundtrip_km_m() {
851        let original = Kilometers::new(42.5);
852        let converted = original.to::<Meter>();
853        let back = converted.to::<Kilometer>();
854        assert_abs_diff_eq!(back.value(), original.value(), epsilon = 1e-12);
855    }
856
857    #[test]
858    fn roundtrip_au_ly() {
859        let original = AstronomicalUnits::new(10000.0);
860        let converted = original.to::<LightYear>();
861        let back = converted.to::<AstronomicalUnit>();
862        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
863    }
864
865    #[test]
866    fn roundtrip_parsec_ly() {
867        let original = Parsecs::new(5.0);
868        let converted = original.to::<LightYear>();
869        let back = converted.to::<Parsec>();
870        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
871    }
872
873    // ─────────────────────────────────────────────────────────────────────────
874    // Exact relationship tests for new units
875    // ─────────────────────────────────────────────────────────────────────────
876
877    #[test]
878    fn inch_to_meter_exact_ratio() {
879        let inch = Inches::new(1.0);
880        let m = inch.to::<Meter>();
881        // International inch: exactly 0.0254 m
882        assert_relative_eq!(m.value(), 0.0254, max_relative = 1e-16);
883    }
884
885    #[test]
886    fn nautical_mile_to_meter_exact_ratio() {
887        let nmi = NauticalMiles::new(1.0);
888        let m = nmi.to::<Meter>();
889        // International nautical mile: exactly 1852 m
890        assert_abs_diff_eq!(m.value(), 1852.0, epsilon = 1e-12);
891    }
892
893    // ─────────────────────────────────────────────────────────────────────────
894    // Roundtrip sanity for representative units
895    // ─────────────────────────────────────────────────────────────────────────
896
897    #[test]
898    fn roundtrip_inch_meter() {
899        let original = Inches::new(123.456);
900        let converted = original.to::<Meter>();
901        let back = converted.to::<Inch>();
902        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
903    }
904
905    #[test]
906    fn roundtrip_nautical_mile_meter() {
907        let original = NauticalMiles::new(3.75);
908        let converted = original.to::<Meter>();
909        let back = converted.to::<NauticalMile>();
910        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
911    }
912
913    #[test]
914    fn roundtrip_parsec_kpc() {
915        let original = Parsecs::new(12_345.0);
916        let converted = original.to::<Kiloparsec>();
917        let back = converted.to::<Parsec>();
918        assert_relative_eq!(back.value(), original.value(), max_relative = 1e-12);
919    }
920
921    // ─────────────────────────────────────────────────────────────────────────────
922    // Property-based tests
923    // ─────────────────────────────────────────────────────────────────────────────
924
925    proptest! {
926        #[test]
927        fn prop_roundtrip_km_m(k in -1e6..1e6f64) {
928            let original = Kilometers::new(k);
929            let converted = original.to::<Meter>();
930            let back = converted.to::<Kilometer>();
931            prop_assert!((back.value() - original.value()).abs() < 1e-9 * k.abs().max(1.0));
932        }
933
934        #[test]
935        fn prop_km_m_ratio(k in 1e-6..1e6f64) {
936            let km = Kilometers::new(k);
937            let m = km.to::<Meter>();
938            // 1 km = 1000 m
939            prop_assert!((m.value() / km.value() - 1000.0).abs() < 1e-9);
940        }
941
942        #[test]
943        fn prop_roundtrip_au_km(a in 1e-6..1e6f64) {
944            let original = AstronomicalUnits::new(a);
945            let converted = original.to::<Kilometer>();
946            let back = converted.to::<AstronomicalUnit>();
947            prop_assert!((back.value() - original.value()).abs() / original.value() < 1e-12);
948        }
949
950        #[test]
951        fn prop_roundtrip_inch_m(i in -1e6..1e6f64) {
952            let original = Inches::new(i);
953            let converted = original.to::<Meter>();
954            let back = converted.to::<Inch>();
955            let scale = i.abs().max(1.0);
956            prop_assert!((back.value() - original.value()).abs() < 1e-9 * scale);
957        }
958    }
959
960    // ─────────────────────────────────────────────────────────────────────────
961    // SI sub-meter ladder
962    // ─────────────────────────────────────────────────────────────────────────
963
964    #[test]
965    fn decimeter_to_meter() {
966        let q = Decimeters::new(10.0);
967        assert_relative_eq!(q.to::<Meter>().value(), 1.0, max_relative = 1e-15);
968    }
969
970    #[test]
971    fn centimeter_to_meter() {
972        let q = Centimeters::new(100.0);
973        assert_relative_eq!(q.to::<Meter>().value(), 1.0, max_relative = 1e-15);
974    }
975
976    #[test]
977    fn millimeter_to_centimeter() {
978        let q = Millimeters::new(10.0);
979        assert_relative_eq!(q.to::<Centimeter>().value(), 1.0, max_relative = 1e-15);
980    }
981
982    #[test]
983    fn micrometer_to_millimeter() {
984        let q = Micrometers::new(1_000.0);
985        assert_relative_eq!(q.to::<Millimeter>().value(), 1.0, max_relative = 1e-15);
986    }
987
988    #[test]
989    fn nanometer_to_micrometer() {
990        let q = Nanometers::new(1_000.0);
991        assert_relative_eq!(q.to::<Micrometer>().value(), 1.0, max_relative = 1e-15);
992    }
993
994    #[test]
995    fn picometer_to_nanometer() {
996        let q = Picometers::new(1_000.0);
997        assert_relative_eq!(q.to::<Nanometer>().value(), 1.0, max_relative = 1e-15);
998    }
999
1000    #[test]
1001    fn femtometer_to_picometer() {
1002        let q = Femtometers::new(1_000.0);
1003        assert_relative_eq!(q.to::<Picometer>().value(), 1.0, max_relative = 1e-15);
1004    }
1005
1006    #[test]
1007    fn attometer_to_femtometer() {
1008        let q = Attometers::new(1_000.0);
1009        assert_relative_eq!(q.to::<Femtometer>().value(), 1.0, max_relative = 1e-15);
1010    }
1011
1012    #[test]
1013    fn zeptometer_to_attometer() {
1014        let q = Zeptometers::new(1_000.0);
1015        assert_relative_eq!(q.to::<Attometer>().value(), 1.0, max_relative = 1e-15);
1016    }
1017
1018    #[test]
1019    fn yoctometer_to_zeptometer() {
1020        let q = Yoctometers::new(1_000.0);
1021        assert_relative_eq!(q.to::<Zeptometer>().value(), 1.0, max_relative = 1e-15);
1022    }
1023
1024    // ─────────────────────────────────────────────────────────────────────────
1025    // SI super-meter ladder
1026    // ─────────────────────────────────────────────────────────────────────────
1027
1028    #[test]
1029    fn decameter_to_meter() {
1030        let q = Decameters::new(1.0);
1031        assert_relative_eq!(q.to::<Meter>().value(), 10.0, max_relative = 1e-15);
1032    }
1033
1034    #[test]
1035    fn hectometer_to_meter() {
1036        let q = Hectometers::new(1.0);
1037        assert_relative_eq!(q.to::<Meter>().value(), 100.0, max_relative = 1e-15);
1038    }
1039
1040    #[test]
1041    fn megameter_to_kilometer() {
1042        let q = Megameters::new(1.0);
1043        assert_relative_eq!(q.to::<Kilometer>().value(), 1_000.0, max_relative = 1e-15);
1044    }
1045
1046    #[test]
1047    fn gigameter_to_megameter() {
1048        let q = Gigameters::new(1.0);
1049        assert_relative_eq!(q.to::<Megameter>().value(), 1_000.0, max_relative = 1e-15);
1050    }
1051
1052    #[test]
1053    fn terameter_to_gigameter() {
1054        let q = Terameters::new(1.0);
1055        assert_relative_eq!(q.to::<Gigameter>().value(), 1_000.0, max_relative = 1e-15);
1056    }
1057
1058    #[test]
1059    fn petameter_to_terameter() {
1060        let q = Petameters::new(1.0);
1061        assert_relative_eq!(q.to::<Terameter>().value(), 1_000.0, max_relative = 1e-15);
1062    }
1063
1064    #[test]
1065    fn exameter_to_petameter() {
1066        let q = Exameters::new(1.0);
1067        assert_relative_eq!(q.to::<Petameter>().value(), 1_000.0, max_relative = 1e-15);
1068    }
1069
1070    #[test]
1071    fn zettameter_to_exameter() {
1072        let q = Zettameters::new(1.0);
1073        assert_relative_eq!(q.to::<Exameter>().value(), 1_000.0, max_relative = 1e-15);
1074    }
1075
1076    #[test]
1077    fn yottameter_to_zettameter() {
1078        let q = Yottameters::new(1.0);
1079        assert_relative_eq!(q.to::<Zettameter>().value(), 1_000.0, max_relative = 1e-15);
1080    }
1081
1082    // ─────────────────────────────────────────────────────────────────────────
1083    // Imperial / surveying units
1084    // ─────────────────────────────────────────────────────────────────────────
1085
1086    #[test]
1087    fn foot_to_meter() {
1088        let q = Feet::new(1.0);
1089        // 1 ft = 0.3048 m exactly
1090        assert_relative_eq!(q.to::<Meter>().value(), 0.3048, max_relative = 1e-15);
1091    }
1092
1093    #[test]
1094    fn yard_to_meter() {
1095        let q = Yards::new(1.0);
1096        // 1 yd = 0.9144 m exactly
1097        assert_relative_eq!(q.to::<Meter>().value(), 0.9144, max_relative = 1e-15);
1098    }
1099
1100    #[test]
1101    fn mile_to_kilometer() {
1102        let q = Miles::new(1.0);
1103        // 1 mi = 1609.344 m exactly
1104        assert_relative_eq!(q.to::<Kilometer>().value(), 1.609_344, max_relative = 1e-15);
1105    }
1106
1107    #[test]
1108    fn fathom_to_foot() {
1109        let q = Fathoms::new(1.0);
1110        // 1 fathom = 6 ft
1111        assert_relative_eq!(q.to::<Foot>().value(), 6.0, max_relative = 1e-14);
1112    }
1113
1114    #[test]
1115    fn chain_to_foot() {
1116        let q = Chains::new(1.0);
1117        // 1 chain = 66 ft
1118        assert_relative_eq!(q.to::<Foot>().value(), 66.0, max_relative = 1e-14);
1119    }
1120
1121    #[test]
1122    fn rod_to_foot() {
1123        let q = Rods::new(1.0);
1124        // 1 rod = 16.5 ft
1125        assert_relative_eq!(q.to::<Foot>().value(), 16.5, max_relative = 1e-14);
1126    }
1127
1128    #[test]
1129    fn link_to_foot() {
1130        let q = Links::new(100.0);
1131        // 100 links = 1 chain = 66 ft
1132        assert_relative_eq!(q.to::<Foot>().value(), 66.0, max_relative = 1e-14);
1133    }
1134
1135    // ─────────────────────────────────────────────────────────────────────────
1136    // Larger astronomical parsec multiples
1137    // ─────────────────────────────────────────────────────────────────────────
1138
1139    #[test]
1140    fn megaparsec_to_kiloparsec() {
1141        let q = Megaparsecs::new(1.0);
1142        assert_relative_eq!(q.to::<Kiloparsec>().value(), 1_000.0, max_relative = 1e-12);
1143    }
1144
1145    #[test]
1146    fn gigaparsec_to_megaparsec() {
1147        let q = Gigaparsecs::new(1.0);
1148        assert_relative_eq!(q.to::<Megaparsec>().value(), 1_000.0, max_relative = 1e-12);
1149    }
1150
1151    // ─────────────────────────────────────────────────────────────────────────
1152    // Geodesy
1153    // ─────────────────────────────────────────────────────────────────────────
1154
1155    #[test]
1156    fn earth_meridional_circumference_to_km() {
1157        let q = EarthMeridionalCircumferences::new(1.0);
1158        // ≈ 40_007.863 km
1159        assert_relative_eq!(q.to::<Kilometer>().value(), 40_007.863, max_relative = 1e-6);
1160    }
1161
1162    #[test]
1163    fn earth_equatorial_circumference_to_km() {
1164        let q = EarthEquatorialCircumferences::new(1.0);
1165        // ≈ 40_075.017 km
1166        assert_relative_eq!(q.to::<Kilometer>().value(), 40_075.017, max_relative = 1e-6);
1167    }
1168
1169    // ─────────────────────────────────────────────────────────────────────────
1170    // Physics lengths
1171    // ─────────────────────────────────────────────────────────────────────────
1172
1173    #[test]
1174    fn bohr_radius_to_picometers() {
1175        let q = BohrRadii::new(1.0);
1176        // a0 ≈ 52.9177 pm
1177        assert_relative_eq!(q.to::<Picometer>().value(), 52.917_72, max_relative = 1e-5);
1178    }
1179
1180    #[test]
1181    fn classical_electron_radius_to_femtometers() {
1182        let q = ClassicalElectronRadii::new(1.0);
1183        // re ≈ 2.81794 fm
1184        assert_relative_eq!(
1185            q.to::<Femtometer>().value(),
1186            2.817_940_326_2,
1187            max_relative = 1e-9
1188        );
1189    }
1190
1191    #[test]
1192    fn planck_length_ratio() {
1193        // Just check ratio round-trips without numeric overflow
1194        let q = PlanckLengths::new(1.0);
1195        let back = q.to::<Meter>().to::<PlanckLength>();
1196        assert_relative_eq!(back.value(), 1.0, max_relative = 1e-9);
1197    }
1198
1199    #[test]
1200    fn electron_compton_wavelength_to_femtometers() {
1201        let q = ElectronReducedComptonWavelengths::new(1.0);
1202        // λ̄_e ≈ 386.159 fm
1203        assert_relative_eq!(
1204            q.to::<Femtometer>().value(),
1205            386.159_267_96,
1206            max_relative = 1e-7
1207        );
1208    }
1209
1210    // ─────────────────────────────────────────────────────────────────────────
1211    // Nominal submodule
1212    // ─────────────────────────────────────────────────────────────────────────
1213
1214    #[test]
1215    fn earth_radius_to_km() {
1216        let q = nominal::EarthRadii::new(1.0);
1217        assert_relative_eq!(q.to::<Kilometer>().value(), 6_371.0, max_relative = 1e-9);
1218    }
1219
1220    #[test]
1221    fn earth_equatorial_radius_to_km() {
1222        let q = nominal::EarthEquatorialRadii::new(1.0);
1223        assert_relative_eq!(q.to::<Kilometer>().value(), 6_378.137, max_relative = 1e-9);
1224    }
1225
1226    #[test]
1227    fn earth_polar_radius_to_km() {
1228        let q = nominal::EarthPolarRadii::new(1.0);
1229        assert_relative_eq!(
1230            q.to::<Kilometer>().value(),
1231            6_356.752_314_2,
1232            max_relative = 1e-9
1233        );
1234    }
1235
1236    #[test]
1237    fn lunar_radius_to_km() {
1238        let q = nominal::LunarRadii::new(1.0);
1239        assert_relative_eq!(q.to::<Kilometer>().value(), 1_737.4, max_relative = 1e-9);
1240    }
1241
1242    #[test]
1243    fn jupiter_radius_to_km() {
1244        let q = nominal::JupiterRadii::new(1.0);
1245        assert_relative_eq!(q.to::<Kilometer>().value(), 71_492.0, max_relative = 1e-9);
1246    }
1247
1248    #[test]
1249    fn lunar_distance_to_km() {
1250        let q = nominal::LunarDistances::new(1.0);
1251        assert_relative_eq!(q.to::<Kilometer>().value(), 384_400.0, max_relative = 1e-9);
1252    }
1253
1254    #[test]
1255    fn solar_diameter_to_solar_radius() {
1256        let diameters = nominal::SolarDiameters::new(1.0);
1257        let radii = diameters.to::<nominal::SolarRadius>();
1258        assert_relative_eq!(radii.value(), 2.0, max_relative = 1e-14);
1259    }
1260
1261    #[test]
1262    fn symbols_are_correct() {
1263        assert_eq!(Meter::SYMBOL, "m");
1264        assert_eq!(Kilometer::SYMBOL, "km");
1265        assert_eq!(Centimeter::SYMBOL, "cm");
1266        assert_eq!(Inch::SYMBOL, "in");
1267        assert_eq!(AstronomicalUnit::SYMBOL, "au");
1268        assert_eq!(Parsec::SYMBOL, "pc");
1269    }
1270}