Skip to main content

qtty_core/
unit_arithmetic.rs

1// SPDX-License-Identifier: BSD-3-Clause
2// Copyright (C) 2026 Vallés Puig, Ramon
3
4//! Stable, macro-generated unit arithmetic layer.
5//!
6//! This module provides the [`UnitDiv`] and [`UnitMul`] traits that control what
7//! unit type results from dividing or multiplying two quantities. The blanket
8//! `Quantity<N> / Quantity<D>` and `Quantity<A> * Quantity<B>` operator impls
9//! delegate to these traits to determine the output unit.
10//!
11//! Generic "recovery" impls are provided for common structural patterns:
12//!
13//! - `U / U → Unitless`
14//! - `N / Per<N, D> → D`
15//! - `Per<N, D> * D → N`
16//! - `D * Per<N, D> → N`
17//!
18//! For all built-in unit pairs (plain marker types), fallback impls produce the
19//! default composite types:
20//!
21//! - `A / B → Per<A, B>` (when `A ≠ B`)
22//! - `A * B → Prod<A, B>`
23//!
24//! Downstream crates can use the exported macros
25//! [`impl_unit_division_pairs!`], [`impl_unit_multiplication_pairs!`],
26//! [`impl_unit_arithmetic_pairs!`], and their `*_between!` variants to
27//! register their own custom units into the same fallback tables without
28//! regenerating built-in/built-in impls.
29
30use crate::dimension::{DimDiv, DimMul, Dimension};
31use crate::quantity::Quantity;
32use crate::scalar::Scalar;
33use crate::unit::{Per, Prod, Unit};
34
35// ─────────────────────────────────────────────────────────────────────────────
36// Core traits
37// ─────────────────────────────────────────────────────────────────────────────
38
39/// Determines the unit type produced by dividing a quantity of unit `Self` by
40/// a quantity of unit `Rhs`.
41///
42/// When `Self == Rhs` (same-unit division), `Output` is [`SameDivOutput`], which
43/// signals that the `Div` operator should return the raw scalar `S` rather than
44/// a wrapped `Quantity`.  For all other unit pairs, `Output` is a `Unit` type and
45/// the result is a `Quantity<Output, S>`.
46pub trait UnitDiv<Rhs: Unit>: Unit {
47    /// The resulting type token.  Either a [`Unit`] (wrapped in `Quantity` by
48    /// the `Div` impl) or [`SameDivOutput`] (unwrapped to the raw scalar).
49    type Output;
50}
51
52/// Determines the unit type produced by multiplying a quantity of unit `Self`
53/// by a quantity of unit `Rhs`.
54pub trait UnitMul<Rhs: Unit>: Unit {
55    /// The resulting unit type.
56    type Output: Unit;
57}
58
59/// Inverse of squaring at the type level: maps a "squared" unit back to its
60/// scalar root unit.
61///
62/// Implemented blanketly for [`Prod<U, U>`](Prod), so any unit that arises as
63/// the dimensional square of another unit (e.g. `Prod<Meter, Meter>` ≡
64/// `SquareMeter`) carries a `Root = U` and can be square-rooted via
65/// [`Quantity::sqrt`](crate::Quantity::sqrt).
66///
67/// This is the dimensionally correct inverse of the `Quantity<U> *
68/// Quantity<U> -> Quantity<Prod<U, U>>` `Mul` impl provided through
69/// [`UnitMul`].
70///
71/// # Note
72///
73/// The blanket impl deliberately only covers the **symmetric** product
74/// `Prod<U, U>`; mixed products like `Prod<Meter, Second>` have no
75/// well-defined square root in this system.
76pub trait UnitSqrt: Unit {
77    /// The unit obtained by taking the dimensional square root of `Self`.
78    type Root: Unit;
79}
80
81impl<U: Unit> UnitSqrt for Prod<U, U>
82where
83    U::Dim: DimMul<U::Dim>,
84    <U::Dim as DimMul<U::Dim>>::Output: Dimension,
85{
86    type Root = U;
87}
88
89// Marker for plain built-in units. This lets built-in multiplication use a
90// single generic impl instead of an O(n²) generated impl table.
91pub(crate) trait BuiltinUnit: Unit {}
92
93// ─────────────────────────────────────────────────────────────────────────────
94// Same-unit division output marker
95// ─────────────────────────────────────────────────────────────────────────────
96
97/// Marker returned by `UnitDiv<U>` when the numerator and denominator units
98/// are identical.  It is **not** a `Unit`; the `Div` impl uses it to produce
99/// the raw scalar `S` instead of a `Quantity`.
100#[derive(Clone, Copy, Debug, PartialEq)]
101pub struct SameDivOutput;
102
103// ─────────────────────────────────────────────────────────────────────────────
104// Dispatch trait: maps a UnitDiv::Output to the final Div::Output type
105// ─────────────────────────────────────────────────────────────────────────────
106
107/// Converts a `UnitDiv::Output` token into the concrete type returned by `Div`.
108///
109/// - `SameDivOutput` → `S`  (raw scalar; cancellation)
110/// - Any `U: Unit`     → `Quantity<U, S>`
111///
112/// Both implementations are coherent: `SameDivOutput` does not implement
113/// `Unit`, so the two impls are provably disjoint under Rust's orphan rules.
114pub trait QuantityDivOutput<S: Scalar> {
115    /// The type that `Div` will return.
116    type Output;
117    /// Wraps (or passes through) a raw scalar into the output type.
118    fn wrap(v: S) -> Self::Output;
119}
120
121impl<S: Scalar> QuantityDivOutput<S> for SameDivOutput {
122    type Output = S;
123    #[inline]
124    fn wrap(v: S) -> S {
125        v
126    }
127}
128
129impl<U: Unit, S: Scalar> QuantityDivOutput<S> for U {
130    type Output = Quantity<U, S>;
131    #[inline]
132    fn wrap(v: S) -> Quantity<U, S> {
133        Quantity::new(v)
134    }
135}
136
137// ─────────────────────────────────────────────────────────────────────────────
138// Generic structural recovery impls
139// ─────────────────────────────────────────────────────────────────────────────
140
141// U / U → SameDivOutput  (cancellation — the Div impl unwraps this to S)
142//
143// Any unit divided by itself produces the raw scalar.  This blanket impl covers
144// all units — built-in, composite (`Per`, `Prod`), and downstream custom types.
145impl<U: Unit> UnitDiv<U> for U
146where
147    U::Dim: DimDiv<U::Dim>,
148    <U::Dim as DimDiv<U::Dim>>::Output: Dimension,
149{
150    type Output = SameDivOutput;
151}
152
153// N / Per<N, D> → D
154impl<N: Unit, D: Unit> UnitDiv<Per<N, D>> for N
155where
156    N::Dim: DimDiv<D::Dim>,
157    <N::Dim as DimDiv<D::Dim>>::Output: Dimension,
158    N::Dim: DimDiv<<N::Dim as DimDiv<D::Dim>>::Output>,
159    <N::Dim as DimDiv<<N::Dim as DimDiv<D::Dim>>::Output>>::Output: Dimension,
160    // Disambiguate from the blanket U/U impl: this impl is only valid when
161    // `Per<N, D>` is NOT the same type as `N`. In practice it never is,
162    // because `Per` is a distinct wrapper struct.
163{
164    type Output = D;
165}
166
167// Per<N, D> * D → N
168impl<N: Unit, D: Unit> UnitMul<D> for Per<N, D>
169where
170    N::Dim: DimDiv<D::Dim>,
171    <N::Dim as DimDiv<D::Dim>>::Output: Dimension,
172    <N::Dim as DimDiv<D::Dim>>::Output: DimMul<D::Dim>,
173    <<N::Dim as DimDiv<D::Dim>>::Output as DimMul<D::Dim>>::Output: Dimension,
174{
175    type Output = N;
176}
177
178// D * Per<N, D> → N
179impl<N: Unit, D: Unit> UnitMul<Per<N, D>> for D
180where
181    N::Dim: DimDiv<D::Dim>,
182    <N::Dim as DimDiv<D::Dim>>::Output: Dimension,
183    D::Dim: DimMul<<N::Dim as DimDiv<D::Dim>>::Output>,
184    <D::Dim as DimMul<<N::Dim as DimDiv<D::Dim>>::Output>>::Output: Dimension,
185{
186    type Output = N;
187}
188
189// Built-in plain units multiply to a `Prod<A, B>`. Division still uses an
190// explicit pair table because `U / U -> Unitless` overlaps with a blanket
191// `A / B -> Per<A, B>` impl on stable Rust.
192impl<A: BuiltinUnit, B: BuiltinUnit> UnitMul<B> for A
193where
194    A::Dim: DimMul<B::Dim>,
195    <A::Dim as DimMul<B::Dim>>::Output: Dimension,
196{
197    type Output = Prod<A, B>;
198}
199
200// ─────────────────────────────────────────────────────────────────────────────
201// Fallback registration for plain units
202// ─────────────────────────────────────────────────────────────────────────────
203
204// The macros below generate `UnitDiv` and `UnitMul` impls for every ordered
205// pair of distinct marker types. Built-in units use the division table only;
206// multiplication uses the blanket `BuiltinUnit` impl above.
207
208/// Generates `UnitDiv` impls for all ordered pairs of distinct units.
209///
210/// For each pair `(A, B)` where `A ≠ B`, implements `UnitDiv<B> for A`
211/// with `Output = Per<A, B>`.
212///
213/// Self-pairs are skipped because they are covered by the blanket
214/// `impl<U> UnitDiv<U> for U { type Output = SameDivOutput; }` which returns the raw scalar.
215///
216/// # Example
217///
218/// ```ignore
219/// impl_unit_division_pairs!(Meter, Second, Kilogram);
220/// // Generates:
221/// //   impl UnitDiv<Second>   for Meter    { type Output = Per<Meter, Second>; }
222/// //   impl UnitDiv<Kilogram> for Meter    { type Output = Per<Meter, Kilogram>; }
223/// //   impl UnitDiv<Meter>    for Second   { type Output = Per<Second, Meter>; }
224/// //   impl UnitDiv<Kilogram> for Second   { type Output = Per<Second, Kilogram>; }
225/// //   impl UnitDiv<Meter>    for Kilogram { type Output = Per<Kilogram, Meter>; }
226/// //   impl UnitDiv<Second>   for Kilogram { type Output = Per<Kilogram, Second>; }
227/// ```
228#[macro_export]
229macro_rules! impl_unit_division_pairs {
230    // Base case: single unit, nothing to pair (self-pair covered by blanket).
231    ($unit:ty) => {};
232
233    // Recursive case: pair the first unit with every other, then recurse.
234    ($first:ty, $($rest:ty),+ $(,)?) => {
235        $(
236            impl $crate::unit_arithmetic::UnitDiv<$rest> for $first
237            where
238                <$first as $crate::Unit>::Dim: $crate::DimDiv<<$rest as $crate::Unit>::Dim>,
239                <<$first as $crate::Unit>::Dim as $crate::DimDiv<<$rest as $crate::Unit>::Dim>>::Output: $crate::Dimension,
240            {
241                type Output = $crate::Per<$first, $rest>;
242            }
243
244            impl $crate::unit_arithmetic::UnitDiv<$first> for $rest
245            where
246                <$rest as $crate::Unit>::Dim: $crate::DimDiv<<$first as $crate::Unit>::Dim>,
247                <<$rest as $crate::Unit>::Dim as $crate::DimDiv<<$first as $crate::Unit>::Dim>>::Output: $crate::Dimension,
248            {
249                type Output = $crate::Per<$rest, $first>;
250            }
251        )+
252
253        $crate::impl_unit_division_pairs!($($rest),+);
254    };
255}
256
257/// Generates `UnitDiv` impls between every unit in the **extra** group and
258/// every unit in the **base** group, plus all intra-extra pairs.
259///
260/// This does **not** regenerate intra-base pairs, which avoids conflicting with
261/// existing registrations for built-in units.
262///
263/// # Example
264///
265/// ```ignore
266/// impl_unit_division_pairs_between!(Meter, Kilometer; Smoot, Furlong);
267/// // Generates:
268/// //   Meter / Smoot, Smoot / Meter
269/// //   Kilometer / Smoot, Smoot / Kilometer
270/// //   Meter / Furlong, Furlong / Meter
271/// //   Kilometer / Furlong, Furlong / Kilometer
272/// //   Smoot / Furlong, Furlong / Smoot
273/// ```
274#[macro_export]
275macro_rules! impl_unit_division_pairs_between {
276    ($($base:ty),+; $($extra:ty),+ $(,)?) => {
277        $crate::__impl_div_pairs_each_extra_to_bases!({$($base),+} $($extra),+);
278        $crate::impl_unit_division_pairs!($($extra),+);
279    };
280}
281
282/// Generates `UnitMul` impls for all ordered pairs of units, **including**
283/// self-pairs (`A * A`).
284///
285/// For each pair `(A, B)`, implements `UnitMul<B> for A` with
286/// `Output = Prod<A, B>`, and symmetrically `UnitMul<A> for B` with
287/// `Output = Prod<B, A>`.
288///
289/// For self-pairs (`A * A`), a single `UnitMul<A> for A` is emitted.
290///
291/// # Example
292///
293/// ```ignore
294/// impl_unit_multiplication_pairs!(Meter, Second);
295/// // Generates:
296/// //   impl UnitMul<Meter>  for Meter  { type Output = Prod<Meter, Meter>; }
297/// //   impl UnitMul<Second> for Second { type Output = Prod<Second, Second>; }
298/// //   impl UnitMul<Second> for Meter  { type Output = Prod<Meter, Second>; }
299/// //   impl UnitMul<Meter>  for Second { type Output = Prod<Second, Meter>; }
300/// ```
301#[macro_export]
302macro_rules! impl_unit_multiplication_pairs {
303    // Base case: single unit – emit the self-pair.
304    ($unit:ty) => {
305        impl $crate::unit_arithmetic::UnitMul<$unit> for $unit
306        where
307            <$unit as $crate::Unit>::Dim: $crate::DimMul<<$unit as $crate::Unit>::Dim>,
308            <<$unit as $crate::Unit>::Dim as $crate::DimMul<<$unit as $crate::Unit>::Dim>>::Output: $crate::Dimension,
309        {
310            type Output = $crate::Prod<$unit, $unit>;
311        }
312    };
313
314    // Recursive case: self-pair for first, cross-pairs with all others, then recurse.
315    ($first:ty, $($rest:ty),+ $(,)?) => {
316        // Self-pair: first * first
317        impl $crate::unit_arithmetic::UnitMul<$first> for $first
318        where
319            <$first as $crate::Unit>::Dim: $crate::DimMul<<$first as $crate::Unit>::Dim>,
320            <<$first as $crate::Unit>::Dim as $crate::DimMul<<$first as $crate::Unit>::Dim>>::Output: $crate::Dimension,
321        {
322            type Output = $crate::Prod<$first, $first>;
323        }
324
325        $(
326            impl $crate::unit_arithmetic::UnitMul<$rest> for $first
327            where
328                <$first as $crate::Unit>::Dim: $crate::DimMul<<$rest as $crate::Unit>::Dim>,
329                <<$first as $crate::Unit>::Dim as $crate::DimMul<<$rest as $crate::Unit>::Dim>>::Output: $crate::Dimension,
330            {
331                type Output = $crate::Prod<$first, $rest>;
332            }
333
334            impl $crate::unit_arithmetic::UnitMul<$first> for $rest
335            where
336                <$rest as $crate::Unit>::Dim: $crate::DimMul<<$first as $crate::Unit>::Dim>,
337                <<$rest as $crate::Unit>::Dim as $crate::DimMul<<$first as $crate::Unit>::Dim>>::Output: $crate::Dimension,
338            {
339                type Output = $crate::Prod<$rest, $first>;
340            }
341        )+
342
343        $crate::impl_unit_multiplication_pairs!($($rest),+);
344    };
345}
346
347/// Generates `UnitMul` impls between every unit in the **extra** group and
348/// every unit in the **base** group, plus all intra-extra pairs.
349///
350/// This does **not** regenerate intra-base pairs, which avoids conflicting with
351/// existing registrations for built-in units.
352#[macro_export]
353macro_rules! impl_unit_multiplication_pairs_between {
354    ($($base:ty),+; $($extra:ty),+ $(,)?) => {
355        $crate::__impl_mul_pairs_each_extra_to_bases!({$($base),+} $($extra),+);
356        $crate::impl_unit_multiplication_pairs!($($extra),+);
357    };
358}
359
360/// Convenience macro that generates both division and multiplication pair
361/// tables for a set of units.
362///
363/// Equivalent to calling [`impl_unit_division_pairs!`] and
364/// [`impl_unit_multiplication_pairs!`] with the same unit list.
365#[macro_export]
366macro_rules! impl_unit_arithmetic_pairs {
367    ($($unit:ty),+ $(,)?) => {
368        $crate::impl_unit_division_pairs!($($unit),+);
369        $crate::impl_unit_multiplication_pairs!($($unit),+);
370    };
371}
372
373/// Convenience macro that generates both division and multiplication impls
374/// between an existing **base** group and a new **extra** group.
375///
376/// Equivalent to calling [`impl_unit_division_pairs_between!`] and
377/// [`impl_unit_multiplication_pairs_between!`] with the same arguments.
378#[macro_export]
379macro_rules! impl_unit_arithmetic_pairs_between {
380    ($($base:ty),+; $($extra:ty),+ $(,)?) => {
381        $crate::impl_unit_division_pairs_between!($($base),+; $($extra),+);
382        $crate::impl_unit_multiplication_pairs_between!($($base),+; $($extra),+);
383    };
384}
385
386/// Hidden helper for [`impl_unit_division_pairs_between!`].
387#[doc(hidden)]
388#[macro_export]
389macro_rules! __impl_div_pairs_each_extra_to_bases {
390    // Base case: single extra remaining.
391    ({$($base:ty),+} $extra:ty) => {
392        $(
393            impl $crate::unit_arithmetic::UnitDiv<$extra> for $base
394            where
395                <$base as $crate::Unit>::Dim: $crate::DimDiv<<$extra as $crate::Unit>::Dim>,
396                <<$base as $crate::Unit>::Dim as $crate::DimDiv<<$extra as $crate::Unit>::Dim>>::Output: $crate::Dimension,
397            {
398                type Output = $crate::Per<$base, $extra>;
399            }
400
401            impl $crate::unit_arithmetic::UnitDiv<$base> for $extra
402            where
403                <$extra as $crate::Unit>::Dim: $crate::DimDiv<<$base as $crate::Unit>::Dim>,
404                <<$extra as $crate::Unit>::Dim as $crate::DimDiv<<$base as $crate::Unit>::Dim>>::Output: $crate::Dimension,
405            {
406                type Output = $crate::Per<$extra, $base>;
407            }
408        )+
409    };
410    // Recursive case: peel the first extra, recurse on the rest.
411    ({$($base:ty),+} $first:ty, $($rest:ty),+) => {
412        $(
413            impl $crate::unit_arithmetic::UnitDiv<$first> for $base
414            where
415                <$base as $crate::Unit>::Dim: $crate::DimDiv<<$first as $crate::Unit>::Dim>,
416                <<$base as $crate::Unit>::Dim as $crate::DimDiv<<$first as $crate::Unit>::Dim>>::Output: $crate::Dimension,
417            {
418                type Output = $crate::Per<$base, $first>;
419            }
420
421            impl $crate::unit_arithmetic::UnitDiv<$base> for $first
422            where
423                <$first as $crate::Unit>::Dim: $crate::DimDiv<<$base as $crate::Unit>::Dim>,
424                <<$first as $crate::Unit>::Dim as $crate::DimDiv<<$base as $crate::Unit>::Dim>>::Output: $crate::Dimension,
425            {
426                type Output = $crate::Per<$first, $base>;
427            }
428        )+
429
430        $crate::__impl_div_pairs_each_extra_to_bases!({$($base),+} $($rest),+);
431    };
432}
433
434/// Hidden helper for [`impl_unit_multiplication_pairs_between!`].
435#[doc(hidden)]
436#[macro_export]
437macro_rules! __impl_mul_pairs_each_extra_to_bases {
438    // Base case: single extra remaining.
439    ({$($base:ty),+} $extra:ty) => {
440        $(
441            impl $crate::unit_arithmetic::UnitMul<$extra> for $base
442            where
443                <$base as $crate::Unit>::Dim: $crate::DimMul<<$extra as $crate::Unit>::Dim>,
444                <<$base as $crate::Unit>::Dim as $crate::DimMul<<$extra as $crate::Unit>::Dim>>::Output: $crate::Dimension,
445            {
446                type Output = $crate::Prod<$base, $extra>;
447            }
448
449            impl $crate::unit_arithmetic::UnitMul<$base> for $extra
450            where
451                <$extra as $crate::Unit>::Dim: $crate::DimMul<<$base as $crate::Unit>::Dim>,
452                <<$extra as $crate::Unit>::Dim as $crate::DimMul<<$base as $crate::Unit>::Dim>>::Output: $crate::Dimension,
453            {
454                type Output = $crate::Prod<$extra, $base>;
455            }
456        )+
457    };
458    // Recursive case: peel the first extra, recurse on the rest.
459    ({$($base:ty),+} $first:ty, $($rest:ty),+) => {
460        $(
461            impl $crate::unit_arithmetic::UnitMul<$first> for $base
462            where
463                <$base as $crate::Unit>::Dim: $crate::DimMul<<$first as $crate::Unit>::Dim>,
464                <<$base as $crate::Unit>::Dim as $crate::DimMul<<$first as $crate::Unit>::Dim>>::Output: $crate::Dimension,
465            {
466                type Output = $crate::Prod<$base, $first>;
467            }
468
469            impl $crate::unit_arithmetic::UnitMul<$base> for $first
470            where
471                <$first as $crate::Unit>::Dim: $crate::DimMul<<$base as $crate::Unit>::Dim>,
472                <<$first as $crate::Unit>::Dim as $crate::DimMul<<$base as $crate::Unit>::Dim>>::Output: $crate::Dimension,
473            {
474                type Output = $crate::Prod<$first, $base>;
475            }
476        )+
477
478        $crate::__impl_mul_pairs_each_extra_to_bases!({$($base),+} $($rest),+);
479    };
480}
481
482// ─────────────────────────────────────────────────────────────────────────────
483// Built-in unit registration
484// ─────────────────────────────────────────────────────────────────────────────
485
486// Register all built-in unit marker types so that cross-unit division and
487// multiplication "just work" for the standard catalog.
488
489macro_rules! register_builtin_units {
490    ($($unit:ty),+ $(,)?) => {
491        $(
492            impl BuiltinUnit for $unit {}
493        )+
494
495        impl_unit_division_pairs!($($unit),+);
496    };
497}
498
499/// Generate cross-group `UnitDiv` impls between two disjoint sets of units,
500/// plus intra-group pairs within the `extra` set.
501///
502/// `register_builtin_units_extend!(base1, base2, ...; extra1, extra2, ...)`
503/// produces:
504/// - `impl BuiltinUnit` for each extra unit
505/// - all-pairs `UnitDiv` within the extras
506/// - cross-pairs between every base and every extra
507#[cfg(any(
508    feature = "astro",
509    feature = "julian-time",
510    feature = "customary",
511    feature = "navigation",
512    feature = "fundamental-physics",
513    feature = "land-area",
514))]
515macro_rules! register_builtin_units_extend {
516    ($($base:ty),+; $($extra:ty),+) => {
517        // Mark each extension type as built-in.
518        $(impl BuiltinUnit for $extra {})+
519
520        $crate::impl_unit_division_pairs_between!($($base),+; $($extra),+);
521    };
522}
523
524/// Helper: invokes `$callback!` with the full list of always-available (base)
525/// unit types so that the list is written in exactly one place.
526macro_rules! with_base_units {
527    ($callback:ident) => {
528        $callback!(
529            // ── Length (metric SI) ────────────────────────────────────────────────
530            crate::units::length::Meter,
531            crate::units::length::Kilometer,
532            crate::units::length::Centimeter,
533            crate::units::length::Millimeter,
534            crate::units::length::Micrometer,
535            crate::units::length::Nanometer,
536            crate::units::length::Picometer,
537            crate::units::length::Femtometer,
538            crate::units::length::Attometer,
539            crate::units::length::Zeptometer,
540            crate::units::length::Yoctometer,
541            crate::units::length::Megameter,
542            crate::units::length::Decimeter,
543            crate::units::length::Decameter,
544            crate::units::length::Hectometer,
545            crate::units::length::Gigameter,
546            crate::units::length::Terameter,
547            crate::units::length::Petameter,
548            crate::units::length::Exameter,
549            crate::units::length::Zettameter,
550            crate::units::length::Yottameter,
551            // ── Time (SI + calendar) ──────────────────────────────────────────────
552            crate::units::time::Attosecond,
553            crate::units::time::Femtosecond,
554            crate::units::time::Picosecond,
555            crate::units::time::Nanosecond,
556            crate::units::time::Microsecond,
557            crate::units::time::Millisecond,
558            crate::units::time::Centisecond,
559            crate::units::time::Decisecond,
560            crate::units::time::Second,
561            crate::units::time::Decasecond,
562            crate::units::time::Hectosecond,
563            crate::units::time::Kilosecond,
564            crate::units::time::Megasecond,
565            crate::units::time::Gigasecond,
566            crate::units::time::Terasecond,
567            crate::units::time::Minute,
568            crate::units::time::Hour,
569            crate::units::time::Day,
570            crate::units::time::Week,
571            crate::units::time::Fortnight,
572            crate::units::time::Year,
573            crate::units::time::Decade,
574            crate::units::time::Century,
575            crate::units::time::Millennium,
576            // ── Mass (SI + tonne) ─────────────────────────────────────────────────
577            crate::units::mass::Gram,
578            crate::units::mass::Yoctogram,
579            crate::units::mass::Zeptogram,
580            crate::units::mass::Attogram,
581            crate::units::mass::Femtogram,
582            crate::units::mass::Picogram,
583            crate::units::mass::Nanogram,
584            crate::units::mass::Microgram,
585            crate::units::mass::Milligram,
586            crate::units::mass::Centigram,
587            crate::units::mass::Decigram,
588            crate::units::mass::Decagram,
589            crate::units::mass::Hectogram,
590            crate::units::mass::Kilogram,
591            crate::units::mass::Megagram,
592            crate::units::mass::Gigagram,
593            crate::units::mass::Teragram,
594            crate::units::mass::Petagram,
595            crate::units::mass::Exagram,
596            crate::units::mass::Zettagram,
597            crate::units::mass::Yottagram,
598            crate::units::mass::Tonne,
599            // ── Angular (base) ────────────────────────────────────────────────────
600            crate::units::angular::Degree,
601            crate::units::angular::Radian,
602            crate::units::angular::Milliradian,
603            crate::units::angular::Turn,
604            // ── Power (SI watts) ──────────────────────────────────────────────────
605            crate::units::power::Watt,
606            crate::units::power::Yoctowatt,
607            crate::units::power::Zeptowatt,
608            crate::units::power::Attowatt,
609            crate::units::power::Femtowatt,
610            crate::units::power::Picowatt,
611            crate::units::power::Nanowatt,
612            crate::units::power::Microwatt,
613            crate::units::power::Milliwatt,
614            crate::units::power::Deciwatt,
615            crate::units::power::Decawatt,
616            crate::units::power::Hectowatt,
617            crate::units::power::Kilowatt,
618            crate::units::power::Megawatt,
619            crate::units::power::Gigawatt,
620            crate::units::power::Terawatt,
621            crate::units::power::Petawatt,
622            crate::units::power::Exawatt,
623            crate::units::power::Zettawatt,
624            crate::units::power::Yottawatt,
625            // ── Area (metric) ─────────────────────────────────────────────────────
626            crate::units::area::SquareMeter,
627            crate::units::area::SquareKilometer,
628            crate::units::area::SquareCentimeter,
629            crate::units::area::SquareMillimeter,
630            // ── Volume (metric + litre) ───────────────────────────────────────────
631            crate::units::volume::CubicMeter,
632            crate::units::volume::CubicKilometer,
633            crate::units::volume::CubicCentimeter,
634            crate::units::volume::CubicMillimeter,
635            crate::units::volume::Liter,
636            crate::units::volume::Milliliter,
637            crate::units::volume::Microliter,
638            crate::units::volume::Centiliter,
639            crate::units::volume::Deciliter,
640            // ── Acceleration ──────────────────────────────────────────────────────
641            crate::units::acceleration::MeterPerSecondSquared,
642            crate::units::acceleration::StandardGravity,
643            // ── Force (SI newtons) ────────────────────────────────────────────────
644            crate::units::force::Newton,
645            crate::units::force::Micronewton,
646            crate::units::force::Millinewton,
647            crate::units::force::Kilonewton,
648            crate::units::force::Meganewton,
649            crate::units::force::Giganewton,
650            // ── Energy (SI joules + watt-hours) ──────────────────────────────────
651            crate::units::energy::Joule,
652            crate::units::energy::Picojoule,
653            crate::units::energy::Nanojoule,
654            crate::units::energy::Microjoule,
655            crate::units::energy::Millijoule,
656            crate::units::energy::Kilojoule,
657            crate::units::energy::Megajoule,
658            crate::units::energy::Gigajoule,
659            crate::units::energy::Terajoule,
660            crate::units::energy::WattHour,
661            crate::units::energy::KilowattHour,
662            // ── Pressure (SI pascals + bar) ───────────────────────────────────────
663            crate::units::pressure::Pascal,
664            crate::units::pressure::Millipascal,
665            crate::units::pressure::Hectopascal,
666            crate::units::pressure::Kilopascal,
667            crate::units::pressure::Megapascal,
668            crate::units::pressure::Gigapascal,
669            crate::units::pressure::Bar,
670            // ── Temperature (SI kelvin + Rankine) ────────────────────────────────
671            crate::units::temperature::Kelvin,
672            crate::units::temperature::Rankine,
673            // ── Solid angle (composed from base angular units) ────────────────────
674            crate::units::solid_angle::SquareDegree,
675            crate::units::solid_angle::Steradian,
676            crate::units::solid_angle::SquareMilliradian
677        );
678    };
679}
680
681// Register always-available (base) units.
682with_base_units!(register_builtin_units);
683
684// ── Feature-gated unit extensions ────────────────────────────────────────────
685// Each block extends the base registration with BuiltinUnit + cross-division
686// pairs against all base units.
687
688#[cfg(feature = "astro")]
689macro_rules! extend_with_astro {
690    ($($base:ty),+) => {
691        register_builtin_units_extend!(
692            $($base),+;
693            crate::units::length::AstronomicalUnit,
694            crate::units::length::LightYear,
695            crate::units::length::Parsec,
696            crate::units::length::Kiloparsec,
697            crate::units::length::Megaparsec,
698            crate::units::length::Gigaparsec,
699            crate::units::length::nominal::SolarRadius,
700            crate::units::length::nominal::SolarDiameter,
701            crate::units::length::nominal::EarthRadius,
702            crate::units::length::nominal::EarthEquatorialRadius,
703            crate::units::length::nominal::EarthPolarRadius,
704            crate::units::length::nominal::JupiterRadius,
705            crate::units::length::nominal::LunarRadius,
706            crate::units::length::nominal::LunarDistance,
707            crate::units::time::SiderealDay,
708            crate::units::time::SynodicMonth,
709            crate::units::time::SiderealYear,
710            crate::units::mass::SolarMass,
711            crate::units::angular::Arcminute,
712            crate::units::angular::Arcsecond,
713            crate::units::angular::MilliArcsecond,
714            crate::units::angular::MicroArcsecond,
715            crate::units::angular::HourAngle,
716            crate::units::power::SolarLuminosity,
717            crate::units::solid_angle::SquareArcminute,
718            crate::units::solid_angle::SquareArcsecond
719        );
720    };
721}
722#[cfg(feature = "astro")]
723with_base_units!(extend_with_astro);
724
725#[cfg(feature = "julian-time")]
726macro_rules! extend_with_julian_time {
727    ($($base:ty),+) => {
728        register_builtin_units_extend!(
729            $($base),+;
730            crate::units::time::JulianYear,
731            crate::units::time::JulianCentury
732        );
733    };
734}
735#[cfg(feature = "julian-time")]
736with_base_units!(extend_with_julian_time);
737
738#[cfg(feature = "customary")]
739macro_rules! extend_with_customary {
740    ($($base:ty),+) => {
741        register_builtin_units_extend!(
742            $($base),+;
743            crate::units::length::Inch,
744            crate::units::length::Foot,
745            crate::units::length::Yard,
746            crate::units::length::Mile,
747            crate::units::mass::Carat,
748            crate::units::mass::Grain,
749            crate::units::mass::Pound,
750            crate::units::mass::Ounce,
751            crate::units::mass::Stone,
752            crate::units::mass::ShortTon,
753            crate::units::mass::LongTon,
754            crate::units::power::HorsepowerMetric,
755            crate::units::power::HorsepowerElectric,
756            crate::units::area::SquareInch,
757            crate::units::area::SquareFoot,
758            crate::units::area::SquareYard,
759            crate::units::area::SquareMile,
760            crate::units::volume::CubicInch,
761            crate::units::volume::CubicFoot,
762            crate::units::volume::UsGallon,
763            crate::units::volume::UsFluidOunce,
764            crate::units::force::PoundForce,
765            crate::units::energy::Calorie,
766            crate::units::energy::Kilocalorie,
767            crate::units::energy::BritishThermalUnit,
768            crate::units::energy::Therm,
769            crate::units::pressure::Atmosphere,
770            crate::units::pressure::Torr,
771            crate::units::pressure::MillimeterOfMercury,
772            crate::units::pressure::PoundPerSquareInch,
773            crate::units::pressure::InchOfMercury
774        );
775    };
776}
777#[cfg(feature = "customary")]
778with_base_units!(extend_with_customary);
779
780#[cfg(feature = "navigation")]
781macro_rules! extend_with_navigation {
782    ($($base:ty),+) => {
783        register_builtin_units_extend!(
784            $($base),+;
785            crate::units::length::NauticalMile,
786            crate::units::length::Chain,
787            crate::units::length::Rod,
788            crate::units::length::Link,
789            crate::units::length::Fathom,
790            crate::units::length::EarthMeridionalCircumference,
791            crate::units::length::EarthEquatorialCircumference,
792            crate::units::angular::Gradian
793        );
794    };
795}
796#[cfg(feature = "navigation")]
797with_base_units!(extend_with_navigation);
798
799#[cfg(feature = "fundamental-physics")]
800macro_rules! extend_with_fundamental_physics {
801    ($($base:ty),+) => {
802        register_builtin_units_extend!(
803            $($base),+;
804            crate::units::length::BohrRadius,
805            crate::units::length::ClassicalElectronRadius,
806            crate::units::length::PlanckLength,
807            crate::units::length::ElectronReducedComptonWavelength,
808            crate::units::mass::AtomicMassUnit,
809            crate::units::power::ErgPerSecond,
810            crate::units::force::Dyne,
811            crate::units::energy::Erg,
812            crate::units::energy::Electronvolt,
813            crate::units::energy::Kiloelectronvolt,
814            crate::units::energy::Megaelectronvolt
815        );
816    };
817}
818#[cfg(feature = "fundamental-physics")]
819with_base_units!(extend_with_fundamental_physics);
820
821#[cfg(feature = "land-area")]
822macro_rules! extend_with_land_area {
823    ($($base:ty),+) => {
824        register_builtin_units_extend!(
825            $($base),+;
826            crate::units::area::Hectare,
827            crate::units::area::Are,
828            crate::units::area::Acre
829        );
830    };
831}
832#[cfg(feature = "land-area")]
833with_base_units!(extend_with_land_area);
834
835#[cfg(feature = "radiometry")]
836macro_rules! extend_with_radiometry {
837    ($($base:ty),+) => {
838        register_builtin_units_extend!(
839            $($base),+;
840            crate::units::radiometry::WattPerSquareMeterSteradian,
841            crate::units::radiometry::ErgPerSecondSquareCentimeterSteradian,
842            crate::units::radiometry::WattPerSquareMeterSteradianMeter,
843            crate::units::radiometry::WattPerSquareMeterSteradianNanometer,
844            crate::units::radiometry::ErgPerSecondSquareCentimeterSteradianAngstrom,
845            crate::units::radiometry::PhotonPerSquareMeterSecondSteradian,
846            crate::units::radiometry::PhotonPerSquareCentimeterSecondSteradian,
847            crate::units::radiometry::PhotonPerSquareCentimeterNanosecondSteradian,
848            crate::units::radiometry::PhotonPerSquareMeterSecondSteradianMeter,
849            crate::units::radiometry::PhotonPerSquareCentimeterSecondSteradianAngstrom,
850            crate::units::radiometry::PhotonPerSquareCentimeterSecondSteradianNanometer,
851            crate::units::radiometry::PhotonPerSquareCentimeterNanosecondSteradianNanometer,
852            crate::units::radiometry::S10
853        );
854    };
855}
856#[cfg(feature = "radiometry")]
857with_base_units!(extend_with_radiometry);
858
859#[cfg(feature = "frequency")]
860macro_rules! extend_with_frequency {
861    ($($base:ty),+) => {
862        register_builtin_units_extend!(
863            $($base),+;
864            crate::units::frequency::Hertz,
865            crate::units::frequency::Millihertz,
866            crate::units::frequency::Kilohertz,
867            crate::units::frequency::Megahertz,
868            crate::units::frequency::Gigahertz,
869            crate::units::frequency::Terahertz
870        );
871    };
872}
873#[cfg(feature = "frequency")]
874with_base_units!(extend_with_frequency);
875
876#[cfg(feature = "chemistry")]
877macro_rules! extend_with_chemistry {
878    ($($base:ty),+) => {
879        register_builtin_units_extend!(
880            $($base),+;
881            crate::units::amount::Nanomole,
882            crate::units::amount::Micromole,
883            crate::units::amount::Millimole,
884            crate::units::amount::Mole,
885            crate::units::amount::Kilomole
886        );
887    };
888}
889#[cfg(feature = "chemistry")]
890with_base_units!(extend_with_chemistry);
891
892#[cfg(feature = "electrical")]
893macro_rules! extend_with_electrical {
894    ($($base:ty),+) => {
895        register_builtin_units_extend!(
896            $($base),+;
897            crate::units::electrical::Ampere,
898            crate::units::electrical::Microampere,
899            crate::units::electrical::Milliampere,
900            crate::units::electrical::Kiloampere,
901            crate::units::electrical::Coulomb,
902            crate::units::electrical::Millicoulomb,
903            crate::units::electrical::Microcoulomb,
904            crate::units::electrical::Kilocoulomb,
905            crate::units::electrical::Volt,
906            crate::units::electrical::Microvolt,
907            crate::units::electrical::Millivolt,
908            crate::units::electrical::Kilovolt,
909            crate::units::electrical::Megavolt,
910            crate::units::electrical::Ohm,
911            crate::units::electrical::Milliohm,
912            crate::units::electrical::Kilohm,
913            crate::units::electrical::Megaohm,
914            crate::units::electrical::Farad,
915            crate::units::electrical::Picofarad,
916            crate::units::electrical::Nanofarad,
917            crate::units::electrical::Microfarad,
918            crate::units::electrical::Millifarad,
919            crate::units::electrical::Henry,
920            crate::units::electrical::Microhenry,
921            crate::units::electrical::Millihenry,
922            crate::units::electrical::Weber,
923            crate::units::electrical::Milliweber,
924            crate::units::electrical::Tesla,
925            crate::units::electrical::Microtesla,
926            crate::units::electrical::Millitesla
927        );
928    };
929}
930#[cfg(feature = "electrical")]
931with_base_units!(extend_with_electrical);
932
933#[cfg(feature = "density")]
934macro_rules! extend_with_density {
935    ($($base:ty),+) => {
936        register_builtin_units_extend!(
937            $($base),+;
938            crate::units::density::KilogramPerCubicMeter,
939            crate::units::density::GramPerCubicCentimeter,
940            crate::units::density::GramPerMilliliter
941        );
942    };
943}
944#[cfg(feature = "density")]
945with_base_units!(extend_with_density);
946
947#[cfg(all(feature = "density", feature = "customary"))]
948macro_rules! extend_density_with_customary {
949    ($($base:ty),+) => {
950        register_builtin_units_extend!(
951            $($base),+;
952            crate::units::density::PoundPerCubicFoot
953        );
954    };
955}
956#[cfg(all(feature = "density", feature = "customary"))]
957with_base_units!(extend_density_with_customary);
958
959#[cfg(feature = "photometry")]
960macro_rules! extend_with_photometry {
961    ($($base:ty),+) => {
962        register_builtin_units_extend!(
963            $($base),+;
964            crate::units::photometry::Candela,
965            crate::units::photometry::Lumen,
966            crate::units::photometry::Millilumen,
967            crate::units::photometry::Kilolumen,
968            crate::units::photometry::Lux,
969            crate::units::photometry::Millilux,
970            crate::units::photometry::Kilolux
971        );
972    };
973}
974#[cfg(feature = "photometry")]
975with_base_units!(extend_with_photometry);
976
977// ── Per-feature "as-base" UnitDiv helpers ─────────────────────────────────────
978//
979// Each macro below holds ONE optional feature's complete unit list.  When two
980// optional features are both enabled the macro for the *larger* family is
981// invoked with the *smaller* family's units as arguments, so each unit list
982// lives in exactly one place rather than being copy-pasted into every pairing.
983//
984// To add a new optional unit family F:
985//   1. Define `__impl_div_pairs_with_F_as_base!` here with F's full unit list.
986//   2. Add one `#[cfg(all(feature = "F", feature = "X"))]` call per existing
987//      family X, invoking whichever family's macro covers the larger set.
988// Nothing else in this file needs to change.
989
990#[cfg(feature = "astro")]
991macro_rules! __impl_div_pairs_with_astro_as_base {
992    ($($extra:ty),+ $(,)?) => {
993        crate::__impl_div_pairs_each_extra_to_bases!(
994            {
995                crate::units::length::AstronomicalUnit,
996                crate::units::length::LightYear,
997                crate::units::length::Parsec,
998                crate::units::length::Kiloparsec,
999                crate::units::length::Megaparsec,
1000                crate::units::length::Gigaparsec,
1001                crate::units::length::nominal::SolarRadius,
1002                crate::units::length::nominal::SolarDiameter,
1003                crate::units::length::nominal::EarthRadius,
1004                crate::units::length::nominal::EarthEquatorialRadius,
1005                crate::units::length::nominal::EarthPolarRadius,
1006                crate::units::length::nominal::JupiterRadius,
1007                crate::units::length::nominal::LunarRadius,
1008                crate::units::length::nominal::LunarDistance,
1009                crate::units::time::SiderealDay,
1010                crate::units::time::SynodicMonth,
1011                crate::units::time::SiderealYear,
1012                crate::units::mass::SolarMass,
1013                crate::units::angular::Arcminute,
1014                crate::units::angular::Arcsecond,
1015                crate::units::angular::MilliArcsecond,
1016                crate::units::angular::MicroArcsecond,
1017                crate::units::angular::HourAngle,
1018                crate::units::power::SolarLuminosity
1019            }
1020            $($extra),+
1021        );
1022    };
1023}
1024
1025#[cfg(feature = "customary")]
1026macro_rules! __impl_div_pairs_with_customary_as_base {
1027    ($($extra:ty),+ $(,)?) => {
1028        crate::__impl_div_pairs_each_extra_to_bases!(
1029            {
1030                crate::units::length::Inch,
1031                crate::units::length::Foot,
1032                crate::units::length::Yard,
1033                crate::units::length::Mile,
1034                crate::units::mass::Carat,
1035                crate::units::mass::Grain,
1036                crate::units::mass::Pound,
1037                crate::units::mass::Ounce,
1038                crate::units::mass::Stone,
1039                crate::units::mass::ShortTon,
1040                crate::units::mass::LongTon,
1041                crate::units::power::HorsepowerMetric,
1042                crate::units::power::HorsepowerElectric,
1043                crate::units::area::SquareInch,
1044                crate::units::area::SquareFoot,
1045                crate::units::area::SquareYard,
1046                crate::units::area::SquareMile,
1047                crate::units::volume::CubicInch,
1048                crate::units::volume::CubicFoot,
1049                crate::units::volume::UsGallon,
1050                crate::units::volume::UsFluidOunce,
1051                crate::units::force::PoundForce,
1052                crate::units::energy::Calorie,
1053                crate::units::energy::Kilocalorie
1054            }
1055            $($extra),+
1056        );
1057    };
1058}
1059
1060#[cfg(feature = "fundamental-physics")]
1061macro_rules! __impl_div_pairs_with_fundamental_physics_as_base {
1062    ($($extra:ty),+ $(,)?) => {
1063        crate::__impl_div_pairs_each_extra_to_bases!(
1064            {
1065                crate::units::length::BohrRadius,
1066                crate::units::length::ClassicalElectronRadius,
1067                crate::units::length::PlanckLength,
1068                crate::units::length::ElectronReducedComptonWavelength,
1069                crate::units::mass::AtomicMassUnit,
1070                crate::units::power::ErgPerSecond,
1071                crate::units::force::Dyne,
1072                crate::units::energy::Erg,
1073                crate::units::energy::Electronvolt,
1074                crate::units::energy::Kiloelectronvolt,
1075                crate::units::energy::Megaelectronvolt
1076            }
1077            $($extra),+
1078        );
1079    };
1080}
1081
1082#[cfg(feature = "navigation")]
1083macro_rules! __impl_div_pairs_with_navigation_as_base {
1084    ($($extra:ty),+ $(,)?) => {
1085        crate::__impl_div_pairs_each_extra_to_bases!(
1086            {
1087                crate::units::length::NauticalMile,
1088                crate::units::length::Chain,
1089                crate::units::length::Rod,
1090                crate::units::length::Link,
1091                crate::units::length::Fathom,
1092                crate::units::length::EarthMeridionalCircumference,
1093                crate::units::length::EarthEquatorialCircumference,
1094                crate::units::angular::Gradian
1095            }
1096            $($extra),+
1097        );
1098    };
1099}
1100
1101#[cfg(feature = "land-area")]
1102macro_rules! __impl_div_pairs_with_land_area_as_base {
1103    ($($extra:ty),+ $(,)?) => {
1104        crate::__impl_div_pairs_each_extra_to_bases!(
1105            {
1106                crate::units::area::Hectare,
1107                crate::units::area::Are,
1108                crate::units::area::Acre
1109            }
1110            $($extra),+
1111        );
1112    };
1113}
1114
1115#[cfg(feature = "frequency")]
1116macro_rules! __impl_div_pairs_with_frequency_as_base {
1117    ($($extra:ty),+ $(,)?) => {
1118        crate::__impl_div_pairs_each_extra_to_bases!(
1119            {
1120                crate::units::frequency::Hertz,
1121                crate::units::frequency::Millihertz,
1122                crate::units::frequency::Kilohertz,
1123                crate::units::frequency::Megahertz,
1124                crate::units::frequency::Gigahertz,
1125                crate::units::frequency::Terahertz
1126            }
1127            $($extra),+
1128        );
1129    };
1130}
1131
1132#[cfg(feature = "chemistry")]
1133macro_rules! __impl_div_pairs_with_chemistry_as_base {
1134    ($($extra:ty),+ $(,)?) => {
1135        crate::__impl_div_pairs_each_extra_to_bases!(
1136            {
1137                crate::units::amount::Nanomole,
1138                crate::units::amount::Micromole,
1139                crate::units::amount::Millimole,
1140                crate::units::amount::Mole,
1141                crate::units::amount::Kilomole
1142            }
1143            $($extra),+
1144        );
1145    };
1146}
1147
1148#[cfg(feature = "electrical")]
1149macro_rules! __impl_div_pairs_with_electrical_as_base {
1150    ($($extra:ty),+ $(,)?) => {
1151        crate::__impl_div_pairs_each_extra_to_bases!(
1152            {
1153                crate::units::electrical::Ampere,
1154                crate::units::electrical::Microampere,
1155                crate::units::electrical::Milliampere,
1156                crate::units::electrical::Kiloampere,
1157                crate::units::electrical::Coulomb,
1158                crate::units::electrical::Millicoulomb,
1159                crate::units::electrical::Microcoulomb,
1160                crate::units::electrical::Kilocoulomb,
1161                crate::units::electrical::Volt,
1162                crate::units::electrical::Microvolt,
1163                crate::units::electrical::Millivolt,
1164                crate::units::electrical::Kilovolt,
1165                crate::units::electrical::Megavolt,
1166                crate::units::electrical::Ohm,
1167                crate::units::electrical::Milliohm,
1168                crate::units::electrical::Kilohm,
1169                crate::units::electrical::Megaohm,
1170                crate::units::electrical::Farad,
1171                crate::units::electrical::Picofarad,
1172                crate::units::electrical::Nanofarad,
1173                crate::units::electrical::Microfarad,
1174                crate::units::electrical::Millifarad,
1175                crate::units::electrical::Henry,
1176                crate::units::electrical::Microhenry,
1177                crate::units::electrical::Millihenry,
1178                crate::units::electrical::Weber,
1179                crate::units::electrical::Milliweber,
1180                crate::units::electrical::Tesla,
1181                crate::units::electrical::Microtesla,
1182                crate::units::electrical::Millitesla
1183            }
1184            $($extra),+
1185        );
1186    };
1187}
1188
1189#[cfg(feature = "density")]
1190macro_rules! __impl_div_pairs_with_density_as_base {
1191    ($($extra:ty),+ $(,)?) => {
1192        crate::__impl_div_pairs_each_extra_to_bases!(
1193            {
1194                crate::units::density::KilogramPerCubicMeter,
1195                crate::units::density::GramPerCubicCentimeter,
1196                crate::units::density::GramPerMilliliter
1197            }
1198            $($extra),+
1199        );
1200    };
1201}
1202
1203#[cfg(feature = "photometry")]
1204macro_rules! __impl_div_pairs_with_photometry_as_base {
1205    ($($extra:ty),+ $(,)?) => {
1206        crate::__impl_div_pairs_each_extra_to_bases!(
1207            {
1208                crate::units::photometry::Candela,
1209                crate::units::photometry::Lumen,
1210                crate::units::photometry::Millilumen,
1211                crate::units::photometry::Kilolumen,
1212                crate::units::photometry::Lux,
1213                crate::units::photometry::Millilux,
1214                crate::units::photometry::Kilolux
1215            }
1216            $($extra),+
1217        );
1218    };
1219}
1220
1221// ── Cross-feature UnitDiv pairs ──────────────────────────────────────────────
1222//
1223// When two optional feature families are both enabled, units from each family
1224// need `UnitDiv` impls against each other so that cross-unit division "just
1225// works".  Multiplication is already covered by the `BuiltinUnit` blanket impl;
1226// division requires explicit pairs because the `U / U → Unitless` blanket
1227// overlaps with a hypothetical `A / B → Per<A, B>` blanket.
1228//
1229// For each pair the macro of the *larger* family is invoked so that the smaller
1230// family's (shorter) unit list is written at the call site.  Both `A / B` and
1231// `B / A` impls are generated by one call — see `__impl_div_pairs_each_extra_to_bases!`.
1232//
1233// Pair count grows as C(N, 2) with N feature families; unit lists do not grow
1234// with the pairing count because each list lives in exactly one macro above.
1235
1236// astro (24 units) — largest optional family; always the base for its pairs.
1237#[cfg(all(feature = "astro", feature = "julian-time"))]
1238__impl_div_pairs_with_astro_as_base!(
1239    crate::units::time::JulianYear,
1240    crate::units::time::JulianCentury
1241);
1242
1243#[cfg(all(feature = "astro", feature = "customary"))]
1244__impl_div_pairs_with_astro_as_base!(
1245    crate::units::length::Inch,
1246    crate::units::length::Foot,
1247    crate::units::length::Yard,
1248    crate::units::length::Mile,
1249    crate::units::mass::Carat,
1250    crate::units::mass::Grain,
1251    crate::units::mass::Pound,
1252    crate::units::mass::Ounce,
1253    crate::units::mass::Stone,
1254    crate::units::mass::ShortTon,
1255    crate::units::mass::LongTon,
1256    crate::units::power::HorsepowerMetric,
1257    crate::units::power::HorsepowerElectric,
1258    crate::units::area::SquareInch,
1259    crate::units::area::SquareFoot,
1260    crate::units::area::SquareYard,
1261    crate::units::area::SquareMile,
1262    crate::units::volume::CubicInch,
1263    crate::units::volume::CubicFoot,
1264    crate::units::volume::UsGallon,
1265    crate::units::volume::UsFluidOunce,
1266    crate::units::force::PoundForce,
1267    crate::units::energy::Calorie,
1268    crate::units::energy::Kilocalorie
1269);
1270
1271#[cfg(all(feature = "astro", feature = "navigation"))]
1272__impl_div_pairs_with_astro_as_base!(
1273    crate::units::length::NauticalMile,
1274    crate::units::length::Chain,
1275    crate::units::length::Rod,
1276    crate::units::length::Link,
1277    crate::units::length::Fathom,
1278    crate::units::length::EarthMeridionalCircumference,
1279    crate::units::length::EarthEquatorialCircumference,
1280    crate::units::angular::Gradian
1281);
1282
1283#[cfg(all(feature = "astro", feature = "fundamental-physics"))]
1284__impl_div_pairs_with_astro_as_base!(
1285    crate::units::length::BohrRadius,
1286    crate::units::length::ClassicalElectronRadius,
1287    crate::units::length::PlanckLength,
1288    crate::units::length::ElectronReducedComptonWavelength,
1289    crate::units::mass::AtomicMassUnit,
1290    crate::units::power::ErgPerSecond,
1291    crate::units::force::Dyne,
1292    crate::units::energy::Erg,
1293    crate::units::energy::Electronvolt,
1294    crate::units::energy::Kiloelectronvolt,
1295    crate::units::energy::Megaelectronvolt
1296);
1297
1298#[cfg(all(feature = "astro", feature = "land-area"))]
1299__impl_div_pairs_with_astro_as_base!(
1300    crate::units::area::Hectare,
1301    crate::units::area::Are,
1302    crate::units::area::Acre
1303);
1304
1305// customary (23 units) — base for its pairs with smaller families.
1306#[cfg(all(feature = "julian-time", feature = "customary"))]
1307__impl_div_pairs_with_customary_as_base!(
1308    crate::units::time::JulianYear,
1309    crate::units::time::JulianCentury
1310);
1311
1312#[cfg(all(feature = "customary", feature = "navigation"))]
1313__impl_div_pairs_with_customary_as_base!(
1314    crate::units::length::NauticalMile,
1315    crate::units::length::Chain,
1316    crate::units::length::Rod,
1317    crate::units::length::Link,
1318    crate::units::length::Fathom,
1319    crate::units::length::EarthMeridionalCircumference,
1320    crate::units::length::EarthEquatorialCircumference,
1321    crate::units::angular::Gradian
1322);
1323
1324#[cfg(all(feature = "customary", feature = "fundamental-physics"))]
1325__impl_div_pairs_with_customary_as_base!(
1326    crate::units::length::BohrRadius,
1327    crate::units::length::ClassicalElectronRadius,
1328    crate::units::length::PlanckLength,
1329    crate::units::length::ElectronReducedComptonWavelength,
1330    crate::units::mass::AtomicMassUnit,
1331    crate::units::power::ErgPerSecond,
1332    crate::units::force::Dyne,
1333    crate::units::energy::Erg,
1334    crate::units::energy::Electronvolt,
1335    crate::units::energy::Kiloelectronvolt,
1336    crate::units::energy::Megaelectronvolt
1337);
1338
1339#[cfg(all(feature = "customary", feature = "land-area"))]
1340__impl_div_pairs_with_customary_as_base!(
1341    crate::units::area::Hectare,
1342    crate::units::area::Are,
1343    crate::units::area::Acre
1344);
1345
1346// fundamental-physics (11 units) — base for its pairs with navigation and smaller families.
1347#[cfg(all(feature = "julian-time", feature = "fundamental-physics"))]
1348__impl_div_pairs_with_fundamental_physics_as_base!(
1349    crate::units::time::JulianYear,
1350    crate::units::time::JulianCentury
1351);
1352
1353#[cfg(all(feature = "navigation", feature = "fundamental-physics"))]
1354__impl_div_pairs_with_fundamental_physics_as_base!(
1355    crate::units::length::NauticalMile,
1356    crate::units::length::Chain,
1357    crate::units::length::Rod,
1358    crate::units::length::Link,
1359    crate::units::length::Fathom,
1360    crate::units::length::EarthMeridionalCircumference,
1361    crate::units::length::EarthEquatorialCircumference,
1362    crate::units::angular::Gradian
1363);
1364
1365#[cfg(all(feature = "fundamental-physics", feature = "land-area"))]
1366__impl_div_pairs_with_fundamental_physics_as_base!(
1367    crate::units::area::Hectare,
1368    crate::units::area::Are,
1369    crate::units::area::Acre
1370);
1371
1372// navigation (8 units) — base for its pairs with land-area and julian-time.
1373#[cfg(all(feature = "julian-time", feature = "navigation"))]
1374__impl_div_pairs_with_navigation_as_base!(
1375    crate::units::time::JulianYear,
1376    crate::units::time::JulianCentury
1377);
1378
1379#[cfg(all(feature = "navigation", feature = "land-area"))]
1380__impl_div_pairs_with_navigation_as_base!(
1381    crate::units::area::Hectare,
1382    crate::units::area::Are,
1383    crate::units::area::Acre
1384);
1385
1386// land-area (3 units) — base for the julian-time pair (land-area > julian-time).
1387#[cfg(all(feature = "julian-time", feature = "land-area"))]
1388__impl_div_pairs_with_land_area_as_base!(
1389    crate::units::time::JulianYear,
1390    crate::units::time::JulianCentury
1391);