Skip to main content

decimal_scaled/types/
widths.rs

1//! Core type definitions for every decimal width and their scale aliases.
2//!
3//! Each width is a `#[repr(transparent)]` newtype around an integer
4//! storage of the matching size. The stored integer equals
5//! `actual_value * 10^SCALE`. Widths:
6//!
7//! | Type | Storage | `MAX_SCALE` |
8//! |------|---------|-------------|
9//! | [`D9<SCALE>`]  | `i32`             | 9   |
10//! | [`D18<SCALE>`] | `i64`             | 18  |
11//! | [`D38<SCALE>`] | `i128`            | 38  |
12//! | [`D76<SCALE>`] | `crate::wide_int::I256` | 76 |
13//! | [`D153<SCALE>`] | `crate::wide_int::I512` | 153 |
14//! | [`D307<SCALE>`] | `crate::wide_int::I1024` | 307 |
15//!
16//! The `#[repr(transparent)]` annotation is load-bearing: it guarantees
17//! the same ABI as the underlying integer, so `from_bits` / `to_bits`
18//! round-trips are exact and the types are safe to embed in C-ABI
19//! plugin payloads when the underlying integer matches a primitive.
20
21/// Scaled fixed-point decimal with 128-bit storage. Now a type alias
22/// of the unified [`crate::D`] generic decimal type: `D38<S>` is
23/// `D<i128, S>`. Both spellings are interchangeable.
24///
25/// `SCALE` is the base-10 exponent. A logical value `v` is stored as
26/// `v * 10^SCALE` in the underlying `i128`. For example, with `SCALE = 12`
27/// the number `1.5` is stored as `i128(1_500_000_000_000)`.
28///
29/// The `#[repr(transparent)]` layout over `i128` is preserved through
30/// the alias because the underlying [`crate::D`] is itself
31/// `#[repr(transparent)]` over its storage parameter.
32///
33/// # Precision
34///
35/// N/A: type definition, no arithmetic performed.
36///
37/// # Determinism
38///
39/// All arithmetic is integer arithmetic on `i128`. The same inputs produce
40/// the same bit-pattern on every platform.
41///
42/// # Equality and ordering
43///
44/// `Hash`, `Eq`, and `Ord` are derived from `i128`. Two `D38<S>` values
45/// are equal if and only if their underlying `i128` fields are bit-equal.
46/// This works because the scale is fixed at compile time -- each logical
47/// value has exactly one representation.
48///
49/// # Const-generic scale
50///
51/// The const generic allows scale variants (`D38<9>`, `D38<6>`, etc.)
52/// as trivial type aliases without duplicating any method implementations.
53/// Mixed-scale arithmetic is deliberately not provided; callers convert
54/// explicitly.
55pub type D38<const SCALE: u32> = crate::D<i128, SCALE>;
56
57// Manual `Debug` is implemented in `display.rs` (via the
58// `decl_decimal_display!` macro) and renders via `Display` so the
59// canonical decimal string is shown rather than the raw i128.
60
61/// `Default` returns `ZERO`, matching `i128::default() == 0`.
62///
63/// This lets `#[derive(Default)]` work correctly on structs that contain
64/// `D38<S>` fields.
65///
66/// Implemented on the underlying `crate::D<i128, SCALE>` because
67/// `D38<SCALE>` is now an alias of that type. `ZERO` is emitted by
68/// the basics macro further down in this file.
69impl<const SCALE: u32> Default for crate::D<i128, SCALE> {
70    #[inline]
71    fn default() -> Self {
72        Self::ZERO
73    }
74}
75
76// Scale aliases: D38s0 through D38s38.
77//
78// Each alias names a specific SCALE value. The const-generic impl block
79// makes every method generic, so adding aliases is purely additive.
80//
81// Representable integer range is approximately `i128::MAX / 10^SCALE`.
82// `i128::MAX` is approximately 1.7e38.
83//
84// SCALE = 0 is supported (mg_divide special-cases it to plain i128
85// arithmetic). SCALE = 38 is the upper bound: 10^38 < i128::MAX, but
86// 10^39 overflows. The math constants (pi, tau, e, golden) have a
87// 35-digit reference in consts.rs (SCALE_REF = 35); at SCALE > 35
88// they are zero-extended and gain no real precision.
89
90/// Scale alias: `D38<0>`. 1 LSB = 1 (thin `i128` wrapper, no rescale).
91/// Range ~+/-1.7e38.
92///
93/// # Precision
94///
95/// N/A: constant value, no arithmetic performed.
96pub type D38s0 = D38<0>;
97
98/// Scale alias: `D38<1>`. 1 LSB = 10^-1 (1 decimal digit).
99/// Range ~+/-1.7e37.
100///
101/// # Precision
102///
103/// N/A: constant value, no arithmetic performed.
104pub type D38s1 = D38<1>;
105
106/// Scale alias: `D38<2>`. 1 LSB = 10^-2 (cents). Range ~+/-1.7e36.
107///
108/// # Precision
109///
110/// N/A: constant value, no arithmetic performed.
111pub type D38s2 = D38<2>;
112
113/// Scale alias: `D38<3>`. 1 LSB = 10^-3 (thousandths; 1 mm at m units).
114/// Range ~+/-1.7e35.
115///
116/// # Precision
117///
118/// N/A: constant value, no arithmetic performed.
119pub type D38s3 = D38<3>;
120
121/// Scale alias: `D38<4>`. 1 LSB = 10^-4 (basis points). Range ~+/-1.7e34.
122///
123/// # Precision
124///
125/// N/A: constant value, no arithmetic performed.
126pub type D38s4 = D38<4>;
127
128/// Scale alias: `D38<5>`. 1 LSB = 10^-5. Range ~+/-1.7e33.
129///
130/// # Precision
131///
132/// N/A: constant value, no arithmetic performed.
133pub type D38s5 = D38<5>;
134
135/// Scale alias: `D38<6>`. 1 LSB = 10^-6 (1 um at mm units; ppm).
136/// Range ~+/-1.7e32.
137///
138/// # Precision
139///
140/// N/A: constant value, no arithmetic performed.
141pub type D38s6 = D38<6>;
142
143/// Scale alias: `D38<7>`. 1 LSB = 10^-7. Range ~+/-1.7e31.
144///
145/// # Precision
146///
147/// N/A: constant value, no arithmetic performed.
148pub type D38s7 = D38<7>;
149
150/// Scale alias: `D38<8>`. 1 LSB = 10^-8 (satoshi-grade). Range ~+/-1.7e30.
151///
152/// # Precision
153///
154/// N/A: constant value, no arithmetic performed.
155pub type D38s8 = D38<8>;
156
157/// Scale alias: `D38<9>`. 1 LSB = 10^-9 (1 nm at mm units; ppb).
158/// Range ~+/-1.7e29.
159///
160/// # Precision
161///
162/// N/A: constant value, no arithmetic performed.
163pub type D38s9 = D38<9>;
164
165/// Scale alias: `D38<10>`. 1 LSB = 10^-10. Range ~+/-1.7e28.
166///
167/// # Precision
168///
169/// N/A: constant value, no arithmetic performed.
170pub type D38s10 = D38<10>;
171
172/// Scale alias: `D38<11>`. 1 LSB = 10^-11. Range ~+/-1.7e27.
173///
174/// # Precision
175///
176/// N/A: constant value, no arithmetic performed.
177pub type D38s11 = D38<11>;
178
179/// Scale alias: `D38<12>`. 1 LSB = 10^-12 (1 pm at mm units).
180/// Range ~+/-1.7e14 model units.
181///
182/// This is the primary concrete alias for general use. At `SCALE = 12`:
183/// - 1 LSB is `10^-12` model units.
184/// - The representable integer range is approximately +/-1.7e14 model units.
185/// - Squared-component operations (e.g. dot products) overflow beyond
186/// roughly 13,000 km at mm units.
187///
188/// # Precision
189///
190/// N/A: constant value, no arithmetic performed.
191pub type D38s12 = D38<12>;
192
193/// Scale alias: `D38<13>`. 1 LSB = 10^-13. Range ~+/-1.7e25.
194///
195/// # Precision
196///
197/// N/A: constant value, no arithmetic performed.
198pub type D38s13 = D38<13>;
199
200/// Scale alias: `D38<14>`. 1 LSB = 10^-14. Range ~+/-1.7e24.
201///
202/// # Precision
203///
204/// N/A: constant value, no arithmetic performed.
205pub type D38s14 = D38<14>;
206
207/// Scale alias: `D38<15>`. 1 LSB = 10^-15 (femto). Range ~+/-1.7e23.
208///
209/// # Precision
210///
211/// N/A: constant value, no arithmetic performed.
212pub type D38s15 = D38<15>;
213
214/// Scale alias: `D38<16>`. 1 LSB = 10^-16. Range ~+/-1.7e22.
215///
216/// # Precision
217///
218/// N/A: constant value, no arithmetic performed.
219pub type D38s16 = D38<16>;
220
221/// Scale alias: `D38<17>`. 1 LSB = 10^-17. Range ~+/-1.7e21.
222///
223/// # Precision
224///
225/// N/A: constant value, no arithmetic performed.
226pub type D38s17 = D38<17>;
227
228/// Scale alias: `D38<18>`. 1 LSB = 10^-18 (atto; high-precision scientific).
229/// Range ~+/-1.7e20.
230///
231/// # Precision
232///
233/// N/A: constant value, no arithmetic performed.
234pub type D38s18 = D38<18>;
235
236/// Scale alias: `D38<19>`. 1 LSB = 10^-19. Range ~+/-1.7e19.
237///
238/// # Precision
239///
240/// N/A: constant value, no arithmetic performed.
241pub type D38s19 = D38<19>;
242
243/// Scale alias: `D38<20>`. 1 LSB = 10^-20. Range ~+/-1.7e18.
244///
245/// # Precision
246///
247/// N/A: constant value, no arithmetic performed.
248pub type D38s20 = D38<20>;
249
250/// Scale alias: `D38<21>`. 1 LSB = 10^-21 (zepto). Range ~+/-1.7e17.
251///
252/// # Precision
253///
254/// N/A: constant value, no arithmetic performed.
255pub type D38s21 = D38<21>;
256
257/// Scale alias: `D38<22>`. 1 LSB = 10^-22. Range ~+/-1.7e16.
258///
259/// # Precision
260///
261/// N/A: constant value, no arithmetic performed.
262pub type D38s22 = D38<22>;
263
264/// Scale alias: `D38<23>`. 1 LSB = 10^-23. Range ~+/-1.7e15.
265///
266/// # Precision
267///
268/// N/A: constant value, no arithmetic performed.
269pub type D38s23 = D38<23>;
270
271/// Scale alias: `D38<24>`. 1 LSB = 10^-24 (yocto). Range ~+/-1.7e14.
272///
273/// # Precision
274///
275/// N/A: constant value, no arithmetic performed.
276pub type D38s24 = D38<24>;
277
278/// Scale alias: `D38<25>`. 1 LSB = 10^-25. Range ~+/-1.7e13.
279///
280/// # Precision
281///
282/// N/A: constant value, no arithmetic performed.
283pub type D38s25 = D38<25>;
284
285/// Scale alias: `D38<26>`. 1 LSB = 10^-26. Range ~+/-1.7e12.
286///
287/// # Precision
288///
289/// N/A: constant value, no arithmetic performed.
290pub type D38s26 = D38<26>;
291
292/// Scale alias: `D38<27>`. 1 LSB = 10^-27. Range ~+/-1.7e11.
293///
294/// # Precision
295///
296/// N/A: constant value, no arithmetic performed.
297pub type D38s27 = D38<27>;
298
299/// Scale alias: `D38<28>`. 1 LSB = 10^-28. Range ~+/-1.7e10.
300///
301/// # Precision
302///
303/// N/A: constant value, no arithmetic performed.
304pub type D38s28 = D38<28>;
305
306/// Scale alias: `D38<29>`. 1 LSB = 10^-29. Range ~+/-1.7e9.
307///
308/// # Precision
309///
310/// N/A: constant value, no arithmetic performed.
311pub type D38s29 = D38<29>;
312
313/// Scale alias: `D38<30>`. 1 LSB = 10^-30. Range ~+/-1.7e8.
314///
315/// # Precision
316///
317/// N/A: constant value, no arithmetic performed.
318pub type D38s30 = D38<30>;
319
320/// Scale alias: `D38<31>`. 1 LSB = 10^-31. Range ~+/-1.7e7.
321///
322/// # Precision
323///
324/// N/A: constant value, no arithmetic performed.
325pub type D38s31 = D38<31>;
326
327/// Scale alias: `D38<32>`. 1 LSB = 10^-32. Range ~+/-1.7e6.
328///
329/// # Precision
330///
331/// N/A: constant value, no arithmetic performed.
332pub type D38s32 = D38<32>;
333
334/// Scale alias: `D38<33>`. 1 LSB = 10^-33. Range ~+/-1.7e5.
335///
336/// # Precision
337///
338/// N/A: constant value, no arithmetic performed.
339pub type D38s33 = D38<33>;
340
341/// Scale alias: `D38<34>`. 1 LSB = 10^-34. Range ~+/-1.7e4.
342///
343/// # Precision
344///
345/// N/A: constant value, no arithmetic performed.
346pub type D38s34 = D38<34>;
347
348/// Scale alias: `D38<35>`. 1 LSB = 10^-35. Range ~+/-1.7e3.
349///
350/// Matches `SCALE_REF` in `consts.rs`: the math constants `pi`, `tau`,
351/// `e`, and `golden` are stored at this reference scale internally, so
352/// at `SCALE = 35` they round-trip without precision loss.
353///
354/// # Precision
355///
356/// N/A: constant value, no arithmetic performed.
357pub type D38s35 = D38<35>;
358
359/// Scale alias: `D38<36>`. 1 LSB = 10^-36. Range ~+/-170.
360///
361/// The math constants (`pi`, `tau`, `e`, `golden`) are stored at a
362/// 35-digit reference. Above `SCALE = 35` they are scaled up from that
363/// reference, so trailing digits are zero-extended rather than
364/// meaningfully precise.
365///
366/// # Precision
367///
368/// N/A: constant value, no arithmetic performed.
369pub type D38s36 = D38<36>;
370
371/// Scale alias: `D38<37>`. 1 LSB = 10^-37. Range ~+/-17.
372///
373/// This is the maximum supported scale: `MAX_SCALE = 37` guarantees at
374/// least one integer digit (`|x| >= 1`) for every representable value.
375/// `10^38 < i128::MAX < 10^39`, so the storage could in principle hold a
376/// scale-38 representation, but doing so would leave `|x| < 1.7` with no
377/// integer-digit headroom -- the v0.4.0 cap rules this out by design.
378/// Math constants lose precision above `SCALE = 35`; see `D38s36`.
379///
380/// # Precision
381///
382/// N/A: constant value, no arithmetic performed.
383pub type D38s37 = D38<37>;
384
385// The `ParseError` enum lives in `src/error.rs` and is re-exported
386// from the crate root. It is not width-specific.
387pub use crate::support::error::ParseError;
388
389// Inherent basics + Decimal trait impl: emitted by the macro generator
390// (one invocation per width). See src/decimal_macro.rs for the macro
391// definition and the surface it produces.
392crate::macros::basics::decl_decimal_basics!(D38, i128, 37);
393crate::macros::display::decl_decimal_display!(D38);
394// FromStr and the raw-storage hex / octal / binary formatters: the
395// shared macros. D38's hand-coded versions were equivalent (`FromStr`
396// delegated to the same `parse_decimal` path; the formatters delegate
397// straight to the `i128` formatter).
398crate::macros::from_str::decl_decimal_from_str!(D38, i128);
399crate::macros::storage_formatters::decl_decimal_storage_formatters!(D38);
400// Bitwise operators (BitAnd/Or/Xor/Not, Shl/Shr) and bit-manipulation
401// methods (unsigned_shr, rotate_*, *_zeros, count_*, *_power_of_two) on
402// the raw storage. Previously hand-coded for D38 only; now a shared
403// macro so every width has the surface.
404crate::macros::bitwise::decl_decimal_bitwise!(D38, i128);
405// Euclidean / floor / ceil division, abs_diff, midpoint, and the
406// is_zero / is_normal / is_nan / is_infinite / is_finite predicates.
407crate::macros::int_methods::decl_decimal_int_methods!(D38, i128);
408// FromPrimitive / ToPrimitive / NumCast via the shared macro.
409crate::macros::num_traits::decl_decimal_num_traits_conversions!(D38, i128);
410crate::macros::float_bridge::decl_decimal_float_bridge!(D38, i128);
411crate::macros::conversions::decl_from_primitive!(D38, i128, i8);
412crate::macros::conversions::decl_from_primitive!(D38, i128, i16);
413crate::macros::conversions::decl_from_primitive!(D38, i128, i32);
414crate::macros::conversions::decl_from_primitive!(D38, i128, i64);
415crate::macros::conversions::decl_from_primitive!(D38, i128, u8);
416crate::macros::conversions::decl_from_primitive!(D38, i128, u16);
417crate::macros::conversions::decl_from_primitive!(D38, i128, u32);
418crate::macros::conversions::decl_from_primitive!(D38, i128, u64);
419crate::macros::conversions::decl_try_from_i128!(D38, i128);
420crate::macros::conversions::decl_try_from_u128!(D38, i128);
421crate::macros::conversions::decl_try_from_i128!(D18, i64);
422crate::macros::conversions::decl_try_from_u128!(D18, i64);
423crate::macros::conversions::decl_try_from_i128!(D9, i32);
424crate::macros::conversions::decl_try_from_u128!(D9, i32);
425crate::macros::conversions::decl_try_from_f64!(D38, i128);
426crate::macros::conversions::decl_try_from_f32!(D38, i128);
427crate::macros::conversions::decl_try_from_f64!(D18, i64);
428crate::macros::conversions::decl_try_from_f32!(D18, i64);
429crate::macros::conversions::decl_try_from_f64!(D9, i32);
430crate::macros::conversions::decl_try_from_f32!(D9, i32);
431crate::macros::conversions::decl_decimal_int_conversion_methods!(D38, i128, i64);
432// abs / signum / is_positive / is_negative, min / max / clamp / recip /
433// copysign, and floor / ceil / round / trunc / fract are emitted by the
434// shared macros — D38's hand-coded versions were byte-identical to the
435// macro output (see `src/macros/{sign,helpers,rounding_methods}.rs`).
436crate::macros::sign::decl_decimal_sign_methods!(D38, i128);
437crate::macros::helpers::decl_decimal_helpers!(D38);
438crate::macros::rounding_methods::decl_decimal_rounding_methods!(D38);
439// Overflow-variant families for add / sub / neg / rem: the macro's
440// shared `@common` arm. D38's hand-coded versions were byte-identical.
441// The mul / div variants stay hand-coded in `src/overflow_variants.rs`
442// because they route through the type-specific `mg_divide` path.
443crate::macros::overflow::decl_decimal_overflow_variants!(@common D38, i128);
444// Add / Sub / Neg / Rem operator impls (and their `*Assign` forms): the
445// arithmetic macro's shared `@common` arm. Mul / Div stay hand-coded in
446// `src/arithmetic.rs` (the `mg_divide` 256-bit-widening path).
447crate::macros::arithmetic::decl_decimal_arithmetic!(@common D38, i128);
448// num-traits: Zero / One / Num / Bounded / Signed / Checked{Add,Sub,Mul,
449// Div,Rem,Neg} via the shared macro — D38's hand-coded impls were
450// equivalent. FromPrimitive / ToPrimitive / NumCast stay hand-coded in
451// `src/num_traits_impls.rs` (not part of the macro surface).
452crate::macros::num_traits::decl_decimal_num_traits_basics!(D38);
453crate::macros::transcendental_trait::decl_decimal_transcendental_impl!(D38);
454
455// D38 strict transcendentals: hand-tuned per-type kernels.
456//
457// The canonical public `*_strict` surface (`ln_strict`, `exp_strict`,
458// `sin_strict`, `powf_strict`, …) is emitted by the per-type files
459// `types/log_exp.rs` / `types/trig.rs` / `types/powers.rs` using
460// the hand-tuned 256-bit `algos::fixed_d38::Fixed` work integer. They
461// are the **chosen winners** per the per-type-kernel policy:
462//
463// - `decl_wide_transcendental!(D38, i128, Int512, …)` would deliver
464//   the same surface using the generic limb arithmetic. Bench
465//   analysis (ln 29 µs hand-tuned vs ≈ 100+ µs macro path) puts the
466//   macro firmly past the 1.5× crossover, so the hand-tuned kernel
467//   wins.
468//
469// The alternative macro implementation is **not compiled** in normal
470// builds — invoking the macro here unconditionally would emit
471// duplicate-name methods that conflict with the canonical override.
472// Under a future `bench-alt` feature the macro can be re-invoked
473// with a renamed-suffix shape (`*_strict_macro_alt`) so a benchmark
474// can compare both paths in one binary; until that knob exists the
475// macro path stays dormant for D38.
476//
477// Same naming convention applies to per-type lossy overrides as
478// they land: `*_lossy_override` opt-in companion, canonical name
479// reserved for the chosen-winner implementation.
480
481crate::macros::conversions::decl_decimal_int_conversion_methods!(D18, i64, i64);
482crate::macros::conversions::decl_decimal_int_conversion_methods!(D9, i32, i32);
483
484// ─── D38 narrow ───────────────────────────────────────────────────────
485// D38::widen is wide-tier-only and is emitted further down in the
486// wide block. D38::narrow is always available.
487
488impl<const SCALE: u32> D38<SCALE> {
489    /// Demote to the previous storage tier ([`D18`]) at the same
490    /// `SCALE`. Returns `Err(ConvertError::OutOfRange)` if the value
491    /// doesn't fit `i64`'s range at the given scale.
492    ///
493    /// ```
494    /// use decimal_scaled::D38s9;
495    /// let a = D38s9::from_int(1_000_000);
496    /// let b = a.narrow().unwrap();
497    /// assert_eq!(b.to_bits() as i128, a.to_bits());
498    /// ```
499    #[inline]
500    pub fn narrow(self) -> Result<D18<SCALE>, crate::support::error::ConvertError> {
501        self.try_into()
502    }
503}
504
505// ---------------------------------------------------------------------
506// D9 — 32-bit storage, scale 0..=9. Embedded / register-sized ledger
507// type. SCALE = 9 fits ~21.5 with 9 decimal digits of precision; SCALE
508// = 0 covers ±2.1 × 10⁹ unscaled. Only the basics emitted in this
509// sub-phase; arithmetic / display / num_traits land incrementally.
510// ---------------------------------------------------------------------
511
512/// Scaled fixed-point decimal with 32-bit storage. See [`D38`] for the
513/// shape documentation; D9 has the same surface scaled to `i32` and
514/// `MAX_SCALE = 9`.
515///
516/// Now a type alias of the unified [`crate::D`] generic decimal type:
517/// `D9<S>` is `D<i32, S>`. Both spellings are interchangeable. The
518/// `#[repr(transparent)]` layout over `i32` is preserved through the
519/// alias because the underlying [`crate::D`] is itself
520/// `#[repr(transparent)]` over its storage parameter.
521pub type D9<const SCALE: u32> = crate::D<i32, SCALE>;
522
523/// `Default` returns `ZERO`, matching `i32::default() == 0`.
524///
525/// Implemented on the underlying `crate::D<i32, SCALE>` because
526/// `D9<SCALE>` is now an alias of that type. `ZERO` is emitted by
527/// the basics macro further down in this file.
528impl<const SCALE: u32> Default for crate::D<i32, SCALE> {
529    #[inline]
530    fn default() -> Self {
531        Self::ZERO
532    }
533}
534
535crate::macros::basics::decl_decimal_basics!(D9, i32, 8);
536crate::macros::arithmetic::decl_decimal_arithmetic!(D9, i32, i64);
537crate::macros::conversions::decl_from_primitive!(D9, i32, i8);
538crate::macros::conversions::decl_from_primitive!(D9, i32, i16);
539crate::macros::conversions::decl_from_primitive!(D9, i32, u8);
540crate::macros::conversions::decl_from_primitive!(D9, i32, u16);
541crate::macros::display::decl_decimal_display!(D9);
542crate::macros::overflow::decl_decimal_overflow_variants!(D9, i32, i64);
543crate::macros::num_traits::decl_decimal_num_traits_basics!(D9);
544crate::macros::sign::decl_decimal_sign_methods!(D9, i32);
545crate::macros::consts::decl_decimal_consts!(D9, i32);
546crate::macros::from_str::decl_decimal_from_str!(D9, i32);
547crate::macros::float_bridge::decl_decimal_float_bridge!(D9, i32);
548crate::macros::storage_formatters::decl_decimal_storage_formatters!(D9);
549crate::macros::strict_transcendentals::decl_strict_transcendentals_via_d38!(D9);
550crate::macros::transcendental_trait::decl_decimal_transcendental_impl!(D9);
551crate::macros::fast_transcendentals::decl_fast_transcendentals_via_f64!(D9);
552crate::macros::pow::decl_decimal_pow!(D9);
553crate::macros::rounding_methods::decl_decimal_rounding_methods!(D9);
554crate::macros::helpers::decl_decimal_helpers!(D9);
555crate::macros::bitwise::decl_decimal_bitwise!(D9, i32);
556crate::macros::int_methods::decl_decimal_int_methods!(D9, i32);
557crate::macros::num_traits::decl_decimal_num_traits_conversions!(D9, i32);
558
559/// Scale alias: `D9<0>`. 1 LSB = 1 (thin `i32` wrapper). Range ±2.1 × 10⁹.
560pub type D9s0 = D9<0>;
561/// Scale alias: `D9<1>`. 1 LSB = 10^-1. Range ±2.1 × 10⁸.
562pub type D9s1 = D9<1>;
563/// Scale alias: `D9<2>`. 1 LSB = 10^-2 (cents). Range ±2.1 × 10⁷.
564pub type D9s2 = D9<2>;
565/// Scale alias: `D9<3>`. 1 LSB = 10^-3 (mills). Range ±2.1 × 10⁶.
566pub type D9s3 = D9<3>;
567/// Scale alias: `D9<4>`. 1 LSB = 10^-4 (basis points). Range ±2.1 × 10⁵.
568pub type D9s4 = D9<4>;
569/// Scale alias: `D9<5>`. 1 LSB = 10^-5. Range ±2.1 × 10⁴.
570pub type D9s5 = D9<5>;
571/// Scale alias: `D9<6>`. 1 LSB = 10^-6 (ppm). Range ±2.1 × 10³.
572pub type D9s6 = D9<6>;
573/// Scale alias: `D9<7>`. 1 LSB = 10^-7. Range ±214.
574pub type D9s7 = D9<7>;
575/// Scale alias: `D9<8>`. 1 LSB = 10^-8 (satoshi). Range ±21.4.
576///
577/// Maximum supported scale (v0.4.0 cap: `MAX_SCALE = name - 1` guarantees
578/// at least one integer digit at every legal SCALE).
579pub type D9s8 = D9<8>;
580
581// ---------------------------------------------------------------------
582// D18 — 64-bit storage, scale 0..=18. Interchange size; fits a GPR on
583// 64-bit hosts and maps cleanly to ANSI / SQL `DECIMAL(18, S)` columns.
584// ---------------------------------------------------------------------
585
586/// Scaled fixed-point decimal with 64-bit storage. See [`D38`] for the
587/// shape documentation; D18 has the same surface scaled to `i64` and
588/// `MAX_SCALE = 18`.
589///
590/// Now a type alias of the unified [`crate::D`] generic decimal type:
591/// `D18<S>` is `D<i64, S>`. Both spellings are interchangeable. The
592/// `#[repr(transparent)]` layout over `i64` is preserved through the
593/// alias because the underlying [`crate::D`] is itself
594/// `#[repr(transparent)]` over its storage parameter.
595pub type D18<const SCALE: u32> = crate::D<i64, SCALE>;
596
597/// `Default` returns `ZERO`, matching `i64::default() == 0`.
598///
599/// Implemented on the underlying `crate::D<i64, SCALE>` because
600/// `D18<SCALE>` is now an alias of that type. `ZERO` is emitted by
601/// the basics macro further down in this file.
602impl<const SCALE: u32> Default for crate::D<i64, SCALE> {
603    #[inline]
604    fn default() -> Self {
605        Self::ZERO
606    }
607}
608
609crate::macros::basics::decl_decimal_basics!(D18, i64, 17);
610crate::macros::arithmetic::decl_decimal_arithmetic!(D18, i64, i128);
611crate::macros::conversions::decl_from_primitive!(D18, i64, i8);
612crate::macros::conversions::decl_from_primitive!(D18, i64, i16);
613crate::macros::conversions::decl_from_primitive!(D18, i64, i32);
614crate::macros::conversions::decl_from_primitive!(D18, i64, u8);
615crate::macros::conversions::decl_from_primitive!(D18, i64, u16);
616crate::macros::conversions::decl_from_primitive!(D18, i64, u32);
617crate::macros::display::decl_decimal_display!(D18);
618crate::macros::overflow::decl_decimal_overflow_variants!(D18, i64, i128);
619crate::macros::num_traits::decl_decimal_num_traits_basics!(D18);
620crate::macros::sign::decl_decimal_sign_methods!(D18, i64);
621crate::macros::consts::decl_decimal_consts!(D18, i64);
622crate::macros::from_str::decl_decimal_from_str!(D18, i64);
623crate::macros::float_bridge::decl_decimal_float_bridge!(D18, i64);
624crate::macros::storage_formatters::decl_decimal_storage_formatters!(D18);
625crate::macros::strict_transcendentals::decl_strict_transcendentals_via_d38!(D18);
626crate::macros::transcendental_trait::decl_decimal_transcendental_impl!(D18);
627crate::macros::fast_transcendentals::decl_fast_transcendentals_via_f64!(D18);
628crate::macros::pow::decl_decimal_pow!(D18);
629crate::macros::rounding_methods::decl_decimal_rounding_methods!(D18);
630crate::macros::helpers::decl_decimal_helpers!(D18);
631crate::macros::bitwise::decl_decimal_bitwise!(D18, i64);
632crate::macros::int_methods::decl_decimal_int_methods!(D18, i64);
633crate::macros::num_traits::decl_decimal_num_traits_conversions!(D18, i64);
634
635// Cross-width widening (lossless). D9 -> D18, D9 -> D38, D18 -> D38.
636crate::macros::conversions::decl_cross_width_widening!(D18, i64, D9, i32);
637crate::macros::conversions::decl_cross_width_widening!(D38, i128, D9, i32);
638crate::macros::conversions::decl_cross_width_widening!(D38, i128, D18, i64);
639
640// Cross-width narrowing (fallible). D38 -> D18, D38 -> D9, D18 -> D9.
641crate::macros::conversions::decl_cross_width_narrowing!(D18, i64, D38, i128);
642crate::macros::conversions::decl_cross_width_narrowing!(D9, i32, D38, i128);
643crate::macros::conversions::decl_cross_width_narrowing!(D9, i32, D18, i64);
644
645// ─── `widen` / `narrow` — hop one storage tier at a time ──────────────
646//
647// `widen` always succeeds (the next-larger storage strictly covers
648// every value the smaller one can hold). `narrow` returns
649// `Result<NarrowerType<SCALE>, ConvertError>` because the value may
650// not fit. Both keep the scale unchanged; combine with `rescale` if
651// you need to change scale and width together.
652
653impl<const SCALE: u32> D9<SCALE> {
654    /// Promote to the next storage tier ([`D18`]) at the same `SCALE`.
655    ///
656    /// Lossless — every `D9<SCALE>` value fits `D18<SCALE>` by
657    /// construction (`i32::MAX` < `i64::MAX`). Chains via further
658    /// `widen` calls if you need to climb to D38 / D76 / etc.
659    ///
660    /// ```
661    /// use decimal_scaled::D9s6;
662    /// let a = D9s6::from_int(123);
663    /// let b = a.widen();              // D18<6>
664    /// assert_eq!(b.to_bits(), a.to_bits() as i64);
665    /// ```
666    #[inline]
667    #[must_use]
668    pub fn widen(self) -> D18<SCALE> {
669        self.into()
670    }
671}
672
673impl<const SCALE: u32> D18<SCALE> {
674    /// Promote to the next storage tier ([`D38`]) at the same `SCALE`.
675    /// Lossless.
676    ///
677    /// ```
678    /// use decimal_scaled::D18s9;
679    /// let a = D18s9::from_int(7);
680    /// let b = a.widen();              // D38<9>
681    /// assert_eq!(b.to_bits(), a.to_bits() as i128);
682    /// ```
683    #[inline]
684    #[must_use]
685    pub fn widen(self) -> D38<SCALE> {
686        self.into()
687    }
688
689    /// Demote to the previous storage tier ([`D9`]) at the same
690    /// `SCALE`.
691    ///
692    /// # Errors
693    ///
694    /// Returns `Err(ConvertError::OutOfRange)` if the value doesn't
695    /// fit `i32`'s range at the given scale.
696    ///
697    /// ```
698    /// use decimal_scaled::D18s5;
699    /// let a = D18s5::from_int(7);
700    /// let b = a.narrow().unwrap();    // D9<5>
701    /// assert_eq!(b.to_bits() as i64, a.to_bits());
702    /// ```
703    #[inline]
704    pub fn narrow(self) -> Result<D9<SCALE>, crate::support::error::ConvertError> {
705        self.try_into()
706    }
707}
708
709/// Scale alias: `D18<0>`. 1 LSB = 1. Range ±9.2 × 10¹⁸.
710pub type D18s0 = D18<0>;
711/// Scale alias: `D18<1>`. 1 LSB = 10^-1. Range ±9.2 × 10¹⁷.
712pub type D18s1 = D18<1>;
713/// Scale alias: `D18<2>`. 1 LSB = 10^-2 (cents). Range ±9.2 × 10¹⁶.
714pub type D18s2 = D18<2>;
715/// Scale alias: `D18<3>`. 1 LSB = 10^-3 (mills). Range ±9.2 × 10¹⁵.
716pub type D18s3 = D18<3>;
717/// Scale alias: `D18<4>`. 1 LSB = 10^-4 (basis points). Range ±9.2 × 10¹⁴.
718pub type D18s4 = D18<4>;
719/// Scale alias: `D18<5>`. 1 LSB = 10^-5. Range ±9.2 × 10¹³.
720pub type D18s5 = D18<5>;
721/// Scale alias: `D18<6>`. 1 LSB = 10^-6 (ppm). Range ±9.2 × 10¹².
722pub type D18s6 = D18<6>;
723/// Scale alias: `D18<7>`. 1 LSB = 10^-7. Range ±9.2 × 10¹¹.
724pub type D18s7 = D18<7>;
725/// Scale alias: `D18<8>`. 1 LSB = 10^-8 (satoshi). Range ±9.2 × 10¹⁰.
726pub type D18s8 = D18<8>;
727/// Scale alias: `D18<9>`. 1 LSB = 10^-9 (nano). Range ±9.2 × 10⁹.
728pub type D18s9 = D18<9>;
729/// Scale alias: `D18<10>`. 1 LSB = 10^-10. Range ±9.2 × 10⁸.
730pub type D18s10 = D18<10>;
731/// Scale alias: `D18<11>`. 1 LSB = 10^-11. Range ±9.2 × 10⁷.
732pub type D18s11 = D18<11>;
733/// Scale alias: `D18<12>`. 1 LSB = 10^-12 (pico). Range ±9.2 × 10⁶.
734pub type D18s12 = D18<12>;
735/// Scale alias: `D18<13>`. 1 LSB = 10^-13. Range ±9.2 × 10⁵.
736pub type D18s13 = D18<13>;
737/// Scale alias: `D18<14>`. 1 LSB = 10^-14. Range ±9.2 × 10⁴.
738pub type D18s14 = D18<14>;
739/// Scale alias: `D18<15>`. 1 LSB = 10^-15 (femto). Range ±9200.
740pub type D18s15 = D18<15>;
741/// Scale alias: `D18<16>`. 1 LSB = 10^-16. Range ±920.
742pub type D18s16 = D18<16>;
743/// Scale alias: `D18<17>`. 1 LSB = 10^-17. Range ±92.
744///
745/// Maximum supported scale (v0.4.0 cap: `MAX_SCALE = name - 1`
746/// guarantees at least one integer digit at every legal SCALE).
747pub type D18s17 = D18<17>;
748
749// ---------------------------------------------------------------------
750// D76 — 256-bit storage (`Int256`), scale 0..=76. First of the
751// wide tier; gated behind the `d76` / `wide` Cargo features. Covers
752// the full IEEE-754 decimal128 range and gives 35-digit fractional
753// precision with integer-part headroom (see research doc §1).
754// ---------------------------------------------------------------------
755
756/// Scaled fixed-point decimal with 256-bit storage. See [`D38`] for the
757/// shape documentation; D76 has the same surface scaled to a 256-bit
758/// signed integer and `MAX_SCALE = 76`. Now a type alias of the unified
759/// [`crate::D`] generic decimal type: `D76<S>` is
760/// `D<crate::wide_int::Int256, S>`. Both spellings are interchangeable.
761///
762/// The `#[repr(transparent)]` layout over `Int256` is preserved through
763/// the alias because the underlying [`crate::D`] is itself
764/// `#[repr(transparent)]` over its storage parameter.
765///
766/// Gated behind the `d76` (or umbrella `wide`) Cargo feature. The
767/// storage backend is `Int256`.
768#[cfg(any(feature = "d76", feature = "wide"))]
769pub type D76<const SCALE: u32> = crate::D<crate::wide_int::Int256, SCALE>;
770
771/// `Default` returns `ZERO`, matching the all-zero limb pattern of
772/// `Int256`.
773///
774/// Implemented on the underlying `crate::D<crate::wide_int::Int256, SCALE>`
775/// because `D76<SCALE>` is now an alias of that type. `ZERO` is emitted
776/// by the basics macro further down in this file.
777#[cfg(any(feature = "d76", feature = "wide"))]
778impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int256, SCALE> {
779    #[inline]
780    fn default() -> Self {
781        Self::ZERO
782    }
783}
784
785#[cfg(any(feature = "d76", feature = "wide"))]
786crate::macros::full::decl_decimal_full!(
787    wide D76,
788    crate::wide_int::I256,
789    crate::wide_int::U256,
790    crate::wide_int::I512,
791    crate::wide_int::Int512,
792    crate::wide_int::Int1024,
793    crate::wide_int::Int1024,
794    crate::wide_int::Int2048,
795    wide_trig_d76,
796    75
797);
798// Cross-width widening into D76 (lossless): D9 / D18 / D38 -> D76.
799#[cfg(any(feature = "d76", feature = "wide"))]
800crate::macros::conversions::decl_cross_width_widening!(wide D76, crate::wide_int::I256, D9, i32);
801#[cfg(any(feature = "d76", feature = "wide"))]
802crate::macros::conversions::decl_cross_width_widening!(wide D76, crate::wide_int::I256, D18, i64);
803#[cfg(any(feature = "d76", feature = "wide"))]
804crate::macros::conversions::decl_cross_width_widening!(wide D76, crate::wide_int::I256, D38, i128);
805// Cross-width narrowing from D76 (fallible): D76 -> D38 / D18 / D9.
806#[cfg(any(feature = "d76", feature = "wide"))]
807crate::macros::conversions::decl_cross_width_narrowing!(wide D38, i128, D76, crate::wide_int::I256);
808#[cfg(any(feature = "d76", feature = "wide"))]
809crate::macros::conversions::decl_cross_width_narrowing!(wide D18, i64, D76, crate::wide_int::I256);
810#[cfg(any(feature = "d76", feature = "wide"))]
811crate::macros::conversions::decl_cross_width_narrowing!(wide D9, i32, D76, crate::wide_int::I256);
812
813// ─── D38::widen / D76 hop methods ─────────────────────────────────────
814
815#[cfg(any(feature = "d76", feature = "wide"))]
816impl<const SCALE: u32> D38<SCALE> {
817    /// Promote to the next storage tier ([`D57`]) at the same `SCALE`.
818    /// Lossless. Available with the `d57` (or umbrella `wide`) Cargo
819    /// feature enabled.
820    ///
821    /// ```
822    /// # #[cfg(feature = "wide")] {
823    /// use decimal_scaled::D38s12;
824    /// let a = D38s12::from_int(1_000_000);
825    /// let _wider = a.widen();  // D57<12>
826    /// # }
827    /// ```
828    #[inline]
829    #[must_use]
830    pub fn widen(self) -> D57<SCALE> {
831        self.into()
832    }
833}
834
835#[cfg(any(feature = "d76", feature = "wide"))]
836impl<const SCALE: u32> D76<SCALE> {
837    /// Demote to the previous storage tier ([`D57`]) at the same
838    /// `SCALE`. Returns `Err(ConvertError::Overflow)` if the value
839    /// doesn't fit `D57`'s range at the given scale.
840    #[inline]
841    pub fn narrow(self) -> Result<D57<SCALE>, crate::support::error::ConvertError> {
842        self.try_into()
843    }
844}
845
846/// Scale alias: `D76<0>`. 1 LSB = 1 (256-bit integer ledger).
847#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s0  = D76<0>;
848/// Scale alias: `D76<1>`. 1 LSB = 10^-1 (tenths).
849#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s1  = D76<1>;
850/// Scale alias: `D76<2>`. 1 LSB = 10^-2 (cents).
851#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s2  = D76<2>;
852#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s3  = D76<3>;
853#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s4  = D76<4>;
854/// Scale alias: `D76<6>`. 1 LSB = 10^-6 (ppm).
855#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s6  = D76<6>;
856#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s9  = D76<9>;
857/// Scale alias: `D76<12>`. 1 LSB = 10^-12 (pico; financial standard).
858#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s12 = D76<12>;
859#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s15 = D76<15>;
860/// Scale alias: `D76<18>`. 1 LSB = 10^-18 (atto).
861#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s18 = D76<18>;
862#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s20 = D76<20>;
863#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s24 = D76<24>;
864#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s28 = D76<28>;
865#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s32 = D76<32>;
866/// Scale alias: `D76<35>`. 1 LSB = 10^-35 (matches `SCALE_REF`).
867#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s35 = D76<35>;
868#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s38 = D76<38>;
869#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s42 = D76<42>;
870#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s48 = D76<48>;
871/// Scale alias: `D76<50>`. 1 LSB = 10^-50 (deep scientific precision).
872#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s50 = D76<50>;
873#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s56 = D76<56>;
874#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s64 = D76<64>;
875#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s70 = D76<70>;
876/// Scale alias: `D76<75>`. 1 LSB = 10^-75. Maximum supported scale
877/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
878#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s75 = D76<75>;
879
880// ---------------------------------------------------------------------
881// D153 — 512-bit storage (`Int512`), scale 0..=153. Wide-scientific
882// tier; gated behind the `d153` / `wide` Cargo features.
883// ---------------------------------------------------------------------
884
885/// Scaled fixed-point decimal with 512-bit storage. See [`D38`] for the
886/// shape documentation; D153 has the same surface scaled to a 512-bit
887/// signed integer and `MAX_SCALE = 153`. Now a type alias of the unified
888/// [`crate::D`] generic decimal type: `D153<S>` is
889/// `D<crate::wide_int::Int512, S>`. Both spellings are interchangeable.
890///
891/// The `#[repr(transparent)]` layout over `Int512` is preserved through
892/// the alias because the underlying [`crate::D`] is itself
893/// `#[repr(transparent)]` over its storage parameter.
894///
895/// Gated behind the `d153` (or umbrella `wide`) Cargo feature. The
896/// storage backend is `Int512`.
897#[cfg(any(feature = "d153", feature = "wide"))]
898pub type D153<const SCALE: u32> = crate::D<crate::wide_int::Int512, SCALE>;
899
900/// `Default` returns `ZERO`, matching the all-zero limb pattern of
901/// `Int512`.
902///
903/// Implemented on the underlying `crate::D<crate::wide_int::Int512, SCALE>`
904/// because `D153<SCALE>` is now an alias of that type. `ZERO` is emitted
905/// by the basics macro further down in this file.
906#[cfg(any(feature = "d153", feature = "wide"))]
907impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int512, SCALE> {
908    #[inline]
909    fn default() -> Self {
910        Self::ZERO
911    }
912}
913
914#[cfg(any(feature = "d153", feature = "wide"))]
915crate::macros::full::decl_decimal_full!(
916    wide D153,
917    crate::wide_int::I512,
918    crate::wide_int::U512,
919    crate::wide_int::I1024,
920    crate::wide_int::Int1024,
921    crate::wide_int::Int2048,
922    crate::wide_int::Int2048,
923    crate::wide_int::Int4096,
924    wide_trig_d153,
925    152
926);
927// Cross-width widening into D153 (lossless): D38 / D76 -> D153.
928#[cfg(any(feature = "d153", feature = "wide"))]
929crate::macros::conversions::decl_cross_width_widening!(wide D153, crate::wide_int::I512, D38, i128);
930#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d76", feature = "wide")))]
931crate::macros::conversions::decl_cross_width_widening!(wide D153, crate::wide_int::I512, D76, crate::wide_int::I256);
932// Cross-width narrowing from D153 (fallible): D153 -> D76 / D38.
933#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d76", feature = "wide")))]
934crate::macros::conversions::decl_cross_width_narrowing!(wide D76, crate::wide_int::I256, D153, crate::wide_int::I512);
935#[cfg(any(feature = "d153", feature = "wide"))]
936crate::macros::conversions::decl_cross_width_narrowing!(wide D38, i128, D153, crate::wide_int::I512);
937
938// ─── D76::widen / D153 hop methods ────────────────────────────────────
939
940#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d115", feature = "wide")))]
941impl<const SCALE: u32> D76<SCALE> {
942    /// Promote to the next storage tier ([`D115`]) at the same
943    /// `SCALE`. Lossless.
944    #[inline]
945    #[must_use]
946    pub fn widen(self) -> D115<SCALE> {
947        self.into()
948    }
949}
950
951#[cfg(any(feature = "d153", feature = "wide"))]
952impl<const SCALE: u32> D153<SCALE> {
953    /// Demote to the previous storage tier ([`D115`]) at the same
954    /// `SCALE`. Returns `Err(ConvertError::Overflow)` if the value
955    /// doesn't fit the narrower storage's range at the given scale.
956    #[cfg(any(feature = "d115", feature = "wide"))]
957    #[inline]
958    pub fn narrow(self) -> Result<D115<SCALE>, crate::support::error::ConvertError> {
959        self.try_into()
960    }
961}
962
963/// Scale alias: `D153<0>`. 1 LSB = 1 (512-bit integer ledger).
964#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s0   = D153<0>;
965#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s1   = D153<1>;
966#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s2   = D153<2>;
967#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s4   = D153<4>;
968#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s6   = D153<6>;
969#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s9   = D153<9>;
970#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s12  = D153<12>;
971#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s15  = D153<15>;
972#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s18  = D153<18>;
973#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s20  = D153<20>;
974#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s24  = D153<24>;
975#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s28  = D153<28>;
976#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s32  = D153<32>;
977/// Scale alias: `D153<35>`. 1 LSB = 10^-35 (matches D38 `SCALE_REF`).
978#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s35  = D153<35>;
979#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s38  = D153<38>;
980#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s50  = D153<50>;
981#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s57  = D153<57>;
982/// Scale alias: `D153<75>`. 1 LSB = 10^-75 (wide-scientific midpoint).
983#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s75  = D153<75>;
984#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s76  = D153<76>;
985#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s100 = D153<100>;
986#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s115 = D153<115>;
987#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s140 = D153<140>;
988/// Scale alias: `D153<150>`. 1 LSB = 10^-150.
989#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s150 = D153<150>;
990/// Scale alias: `D153<152>`. 1 LSB = 10^-152. Maximum supported scale
991/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
992#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s152 = D153<152>;
993
994// ---------------------------------------------------------------------
995// D307 — 1024-bit storage (`Int1024`), scale 0..=307. Deep
996// arbitrary-precision tier; gated behind the `d307` / `wide` features.
997// ---------------------------------------------------------------------
998
999/// Scaled fixed-point decimal with 1024-bit storage. See [`D38`] for
1000/// the shape documentation; D307 has the same surface scaled to a
1001/// 1024-bit signed integer and `MAX_SCALE = 307`. Now a type alias of
1002/// the unified [`crate::D`] generic decimal type: `D307<S>` is
1003/// `D<crate::wide_int::Int1024, S>`. Both spellings are interchangeable.
1004///
1005/// The `#[repr(transparent)]` layout over `Int1024` is preserved through
1006/// the alias because the underlying [`crate::D`] is itself
1007/// `#[repr(transparent)]` over its storage parameter.
1008///
1009/// Gated behind the `d307` (or umbrella `wide`) Cargo feature. The
1010/// storage backend is `Int1024`.
1011#[cfg(any(feature = "d307", feature = "wide"))]
1012pub type D307<const SCALE: u32> = crate::D<crate::wide_int::Int1024, SCALE>;
1013
1014/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1015/// `Int1024`.
1016///
1017/// Implemented on the underlying `crate::D<crate::wide_int::Int1024, SCALE>`
1018/// because `D307<SCALE>` is now an alias of that type. `ZERO` is emitted
1019/// by the basics macro further down in this file.
1020#[cfg(any(feature = "d307", feature = "wide"))]
1021impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int1024, SCALE> {
1022    #[inline]
1023    fn default() -> Self {
1024        Self::ZERO
1025    }
1026}
1027
1028#[cfg(any(feature = "d307", feature = "wide"))]
1029crate::macros::full::decl_decimal_full!(
1030    wide D307,
1031    crate::wide_int::I1024,
1032    crate::wide_int::U1024,
1033    crate::wide_int::I2048,
1034    crate::wide_int::Int2048,
1035    crate::wide_int::Int4096,
1036    crate::wide_int::Int4096,
1037    crate::wide_int::Int8192,
1038    wide_trig_d307,
1039    306
1040);
1041// Cross-width widening into D307 (lossless): D76 / D153 -> D307.
1042#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d76", feature = "wide")))]
1043crate::macros::conversions::decl_cross_width_widening!(wide D307, crate::wide_int::I1024, D76, crate::wide_int::I256);
1044#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d153", feature = "wide")))]
1045crate::macros::conversions::decl_cross_width_widening!(wide D307, crate::wide_int::I1024, D153, crate::wide_int::I512);
1046// Cross-width narrowing from D307 (fallible): D307 -> D153 / D76.
1047#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d153", feature = "wide")))]
1048crate::macros::conversions::decl_cross_width_narrowing!(wide D153, crate::wide_int::I512, D307, crate::wide_int::I1024);
1049#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d76", feature = "wide")))]
1050crate::macros::conversions::decl_cross_width_narrowing!(wide D76, crate::wide_int::I256, D307, crate::wide_int::I1024);
1051
1052// ─── D153::widen / D307 hop methods ───────────────────────────────────
1053
1054#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d230", feature = "wide")))]
1055impl<const SCALE: u32> D153<SCALE> {
1056    /// Promote to the next storage tier ([`D230`]) at the same
1057    /// `SCALE`. Lossless.
1058    #[inline]
1059    #[must_use]
1060    pub fn widen(self) -> D230<SCALE> {
1061        self.into()
1062    }
1063}
1064
1065#[cfg(any(feature = "d307", feature = "wide"))]
1066impl<const SCALE: u32> D307<SCALE> {
1067    /// Demote to the previous storage tier ([`D230`]) at the same
1068    /// `SCALE`. Returns `Err(ConvertError::Overflow)` if the value
1069    /// doesn't fit the narrower storage's range at the given scale.
1070    #[cfg(any(feature = "d230", feature = "wide"))]
1071    #[inline]
1072    pub fn narrow(self) -> Result<D230<SCALE>, crate::support::error::ConvertError> {
1073        self.try_into()
1074    }
1075
1076    /// Promote to the next storage tier ([`D462`]) at the same
1077    /// `SCALE`. Lossless. Requires `d462` / `x-wide`.
1078    #[cfg(any(feature = "d462", feature = "x-wide"))]
1079    #[inline]
1080    #[must_use]
1081    pub fn widen(self) -> D462<SCALE> {
1082        self.into()
1083    }
1084}
1085
1086/// Scale alias: `D307<0>`. 1 LSB = 1 (1024-bit integer ledger).
1087#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s0   = D307<0>;
1088#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s1   = D307<1>;
1089#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s2   = D307<2>;
1090#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s4   = D307<4>;
1091#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s6   = D307<6>;
1092#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s9   = D307<9>;
1093#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s12  = D307<12>;
1094#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s15  = D307<15>;
1095#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s18  = D307<18>;
1096#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s20  = D307<20>;
1097#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s24  = D307<24>;
1098#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s28  = D307<28>;
1099#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s32  = D307<32>;
1100/// Scale alias: `D307<35>`. 1 LSB = 10^-35 (matches D38 `SCALE_REF`).
1101#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s35  = D307<35>;
1102#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s38  = D307<38>;
1103#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s50  = D307<50>;
1104#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s75  = D307<75>;
1105#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s100 = D307<100>;
1106#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s115 = D307<115>;
1107/// Scale alias: `D307<150>`. 1 LSB = 10^-150.
1108#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s150 = D307<150>;
1109#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s153 = D307<153>;
1110#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s200 = D307<200>;
1111#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s230 = D307<230>;
1112#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s275 = D307<275>;
1113/// Scale alias: `D307<300>`. 1 LSB = 10^-300.
1114#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s300 = D307<300>;
1115/// Scale alias: `D307<306>`. 1 LSB = 10^-306. Maximum supported scale
1116/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1117#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s306 = D307<306>;
1118
1119// ─── Half-width and wider tiers (D57 / D115 / D230 / D462 / D616 / D924 / D1232) ───
1120//
1121// These fill the (2^n + 2^(n+1))/2 gaps between the existing
1122// power-of-two storage tiers, plus extend the top end past D307.
1123// Each tier has the same surface as D76 / D153 / D307: full
1124// `decl_decimal_full!` emission (every arithmetic / transcendental
1125// method), plus scale aliases at 0 / mid / max.
1126//
1127// Cross-width widening / narrowing methods are emitted to the
1128// immediate-neighbour tiers only — `D57 ↔ D38`, `D57 ↔ D76`, etc.
1129// Multi-tier hops go via the chain (e.g. D57 → D76 → D153) at the
1130// cost of one intermediate.
1131
1132// ── D57 (192-bit / 3 u64 limbs) ────────────────────────────────────────
1133
1134/// Scaled fixed-point decimal with 192-bit storage. Half-width tier
1135/// between D38 and D76 — useful when the D38 i128 ceiling is in
1136/// reach but D76's 256-bit storage is wasteful. Now a type alias of
1137/// the unified [`crate::D`] generic decimal type: `D57<S>` is
1138/// `D<crate::wide_int::Int192, S>`. Both spellings are interchangeable.
1139///
1140/// The `#[repr(transparent)]` layout over `Int192` is preserved
1141/// through the alias because the underlying [`crate::D`] is itself
1142/// `#[repr(transparent)]` over its storage parameter.
1143///
1144/// Gated behind the `d57` (or umbrella `wide`) Cargo feature.
1145#[cfg(any(feature = "d57", feature = "wide"))]
1146pub type D57<const SCALE: u32> = crate::D<crate::wide_int::Int192, SCALE>;
1147
1148/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1149/// `Int192`.
1150///
1151/// Implemented on the underlying `crate::D<crate::wide_int::Int192, SCALE>`
1152/// because `D57<SCALE>` is now an alias of that type. `ZERO` is emitted
1153/// by the basics macro further down in this file.
1154#[cfg(any(feature = "d57", feature = "wide"))]
1155impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int192, SCALE> {
1156    #[inline]
1157    fn default() -> Self { Self::ZERO }
1158}
1159
1160#[cfg(any(feature = "d57", feature = "wide"))]
1161crate::macros::full::decl_decimal_full!(
1162    wide D57,
1163    crate::wide_int::I192,
1164    crate::wide_int::U192,
1165    crate::wide_int::I384,
1166    crate::wide_int::Int384,
1167    crate::wide_int::Int512,
1168    crate::wide_int::Int1024,
1169    crate::wide_int::Int2048,
1170    wide_trig_d57,
1171    56
1172);
1173#[cfg(any(feature = "d57", feature = "wide"))]
1174pub type D57s0 = D57<0>;
1175#[cfg(any(feature = "d57", feature = "wide"))]
1176pub type D57s1 = D57<1>;
1177#[cfg(any(feature = "d57", feature = "wide"))]
1178pub type D57s2 = D57<2>;
1179#[cfg(any(feature = "d57", feature = "wide"))]
1180pub type D57s4 = D57<4>;
1181#[cfg(any(feature = "d57", feature = "wide"))]
1182pub type D57s6 = D57<6>;
1183#[cfg(any(feature = "d57", feature = "wide"))]
1184pub type D57s9 = D57<9>;
1185#[cfg(any(feature = "d57", feature = "wide"))]
1186pub type D57s12 = D57<12>;
1187#[cfg(any(feature = "d57", feature = "wide"))]
1188pub type D57s18 = D57<18>;
1189#[cfg(any(feature = "d57", feature = "wide"))]
1190pub type D57s20 = D57<20>;
1191#[cfg(any(feature = "d57", feature = "wide"))]
1192pub type D57s24 = D57<24>;
1193#[cfg(any(feature = "d57", feature = "wide"))]
1194pub type D57s28 = D57<28>;
1195#[cfg(any(feature = "d57", feature = "wide"))]
1196pub type D57s32 = D57<32>;
1197#[cfg(any(feature = "d57", feature = "wide"))]
1198pub type D57s38 = D57<38>;
1199#[cfg(any(feature = "d57", feature = "wide"))]
1200pub type D57s42 = D57<42>;
1201#[cfg(any(feature = "d57", feature = "wide"))]
1202pub type D57s48 = D57<48>;
1203#[cfg(any(feature = "d57", feature = "wide"))]
1204pub type D57s52 = D57<52>;
1205/// Scale alias: `D57<56>`. 1 LSB = 10^-56. Maximum supported scale
1206/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1207#[cfg(any(feature = "d57", feature = "wide"))]
1208pub type D57s56 = D57<56>;
1209
1210// ── D115 (384-bit / 6 u64 limbs) ───────────────────────────────────────
1211
1212/// Scaled fixed-point decimal with 384-bit storage. Half-width tier
1213/// between D76 and D153. Now a type alias of the unified [`crate::D`]
1214/// generic decimal type: `D115<S>` is `D<crate::wide_int::Int384, S>`.
1215/// Both spellings are interchangeable.
1216///
1217/// The `#[repr(transparent)]` layout over `Int384` is preserved through
1218/// the alias because the underlying [`crate::D`] is itself
1219/// `#[repr(transparent)]` over its storage parameter.
1220///
1221/// Gated behind the `d115` (or umbrella `wide`) Cargo feature.
1222#[cfg(any(feature = "d115", feature = "wide"))]
1223pub type D115<const SCALE: u32> = crate::D<crate::wide_int::Int384, SCALE>;
1224
1225/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1226/// `Int384`.
1227///
1228/// Implemented on the underlying `crate::D<crate::wide_int::Int384, SCALE>`
1229/// because `D115<SCALE>` is now an alias of that type. `ZERO` is emitted
1230/// by the basics macro further down in this file.
1231#[cfg(any(feature = "d115", feature = "wide"))]
1232impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int384, SCALE> {
1233    #[inline]
1234    fn default() -> Self { Self::ZERO }
1235}
1236
1237#[cfg(any(feature = "d115", feature = "wide"))]
1238crate::macros::full::decl_decimal_full!(
1239    wide D115,
1240    crate::wide_int::I384,
1241    crate::wide_int::U384,
1242    crate::wide_int::I768,
1243    crate::wide_int::Int768,
1244    crate::wide_int::Int1024,
1245    crate::wide_int::Int2048,
1246    crate::wide_int::Int4096,
1247    wide_trig_d115,
1248    114
1249);
1250#[cfg(any(feature = "d115", feature = "wide"))]
1251pub type D115s0 = D115<0>;
1252#[cfg(any(feature = "d115", feature = "wide"))]
1253pub type D115s1 = D115<1>;
1254#[cfg(any(feature = "d115", feature = "wide"))]
1255pub type D115s4 = D115<4>;
1256#[cfg(any(feature = "d115", feature = "wide"))]
1257pub type D115s8 = D115<8>;
1258#[cfg(any(feature = "d115", feature = "wide"))]
1259pub type D115s16 = D115<16>;
1260#[cfg(any(feature = "d115", feature = "wide"))]
1261pub type D115s24 = D115<24>;
1262#[cfg(any(feature = "d115", feature = "wide"))]
1263pub type D115s32 = D115<32>;
1264#[cfg(any(feature = "d115", feature = "wide"))]
1265pub type D115s38 = D115<38>;
1266#[cfg(any(feature = "d115", feature = "wide"))]
1267pub type D115s50 = D115<50>;
1268#[cfg(any(feature = "d115", feature = "wide"))]
1269pub type D115s57 = D115<57>;
1270#[cfg(any(feature = "d115", feature = "wide"))]
1271pub type D115s64 = D115<64>;
1272#[cfg(any(feature = "d115", feature = "wide"))]
1273pub type D115s76 = D115<76>;
1274#[cfg(any(feature = "d115", feature = "wide"))]
1275pub type D115s90 = D115<90>;
1276#[cfg(any(feature = "d115", feature = "wide"))]
1277pub type D115s100 = D115<100>;
1278#[cfg(any(feature = "d115", feature = "wide"))]
1279pub type D115s110 = D115<110>;
1280/// Scale alias: `D115<114>`. 1 LSB = 10^-114. Maximum supported scale
1281/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1282#[cfg(any(feature = "d115", feature = "wide"))]
1283pub type D115s114 = D115<114>;
1284
1285// ── D230 (768-bit / 12 u64 limbs) ──────────────────────────────────────
1286
1287/// Scaled fixed-point decimal with 768-bit storage. Half-width tier
1288/// between D153 and D307. Now a type alias of the unified [`crate::D`]
1289/// generic decimal type: `D230<S>` is `D<crate::wide_int::Int768, S>`.
1290/// Both spellings are interchangeable.
1291///
1292/// The `#[repr(transparent)]` layout over `Int768` is preserved through
1293/// the alias because the underlying [`crate::D`] is itself
1294/// `#[repr(transparent)]` over its storage parameter.
1295///
1296/// Gated behind the `d230` (or umbrella `wide`) Cargo feature.
1297#[cfg(any(feature = "d230", feature = "wide"))]
1298pub type D230<const SCALE: u32> = crate::D<crate::wide_int::Int768, SCALE>;
1299
1300/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1301/// `Int768`.
1302///
1303/// Implemented on the underlying `crate::D<crate::wide_int::Int768, SCALE>`
1304/// because `D230<SCALE>` is now an alias of that type. `ZERO` is emitted
1305/// by the basics macro further down in this file.
1306#[cfg(any(feature = "d230", feature = "wide"))]
1307impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int768, SCALE> {
1308    #[inline]
1309    fn default() -> Self { Self::ZERO }
1310}
1311
1312#[cfg(any(feature = "d230", feature = "wide"))]
1313crate::macros::full::decl_decimal_full!(
1314    wide D230,
1315    crate::wide_int::I768,
1316    crate::wide_int::U768,
1317    crate::wide_int::I1536,
1318    crate::wide_int::Int1536,
1319    crate::wide_int::Int3072,
1320    crate::wide_int::Int3072,
1321    crate::wide_int::Int6144,
1322    wide_trig_d230,
1323    229
1324);
1325#[cfg(any(feature = "d230", feature = "wide"))]
1326pub type D230s0 = D230<0>;
1327#[cfg(any(feature = "d230", feature = "wide"))]
1328pub type D230s1 = D230<1>;
1329#[cfg(any(feature = "d230", feature = "wide"))]
1330pub type D230s6 = D230<6>;
1331#[cfg(any(feature = "d230", feature = "wide"))]
1332pub type D230s18 = D230<18>;
1333#[cfg(any(feature = "d230", feature = "wide"))]
1334pub type D230s38 = D230<38>;
1335#[cfg(any(feature = "d230", feature = "wide"))]
1336pub type D230s57 = D230<57>;
1337#[cfg(any(feature = "d230", feature = "wide"))]
1338pub type D230s75 = D230<75>;
1339#[cfg(any(feature = "d230", feature = "wide"))]
1340pub type D230s100 = D230<100>;
1341#[cfg(any(feature = "d230", feature = "wide"))]
1342pub type D230s115 = D230<115>;
1343#[cfg(any(feature = "d230", feature = "wide"))]
1344pub type D230s140 = D230<140>;
1345#[cfg(any(feature = "d230", feature = "wide"))]
1346pub type D230s153 = D230<153>;
1347#[cfg(any(feature = "d230", feature = "wide"))]
1348pub type D230s175 = D230<175>;
1349#[cfg(any(feature = "d230", feature = "wide"))]
1350pub type D230s200 = D230<200>;
1351#[cfg(any(feature = "d230", feature = "wide"))]
1352pub type D230s215 = D230<215>;
1353#[cfg(any(feature = "d230", feature = "wide"))]
1354pub type D230s225 = D230<225>;
1355/// Scale alias: `D230<229>`. 1 LSB = 10^-229. Maximum supported scale
1356/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1357#[cfg(any(feature = "d230", feature = "wide"))]
1358pub type D230s229 = D230<229>;
1359
1360// ── D462 (1536-bit / 24 u64 limbs) ─────────────────────────────────────
1361
1362/// Scaled fixed-point decimal with 1536-bit storage. Half-width tier
1363/// between D307 and D616. Now a type alias of the unified [`crate::D`]
1364/// generic decimal type: `D462<S>` is `D<crate::wide_int::Int1536, S>`.
1365/// Both spellings are interchangeable.
1366///
1367/// The `#[repr(transparent)]` layout over `Int1536` is preserved through
1368/// the alias because the underlying [`crate::D`] is itself
1369/// `#[repr(transparent)]` over its storage parameter.
1370///
1371/// Gated behind the `d462` (or umbrella `x-wide`) Cargo feature.
1372#[cfg(any(feature = "d462", feature = "x-wide"))]
1373pub type D462<const SCALE: u32> = crate::D<crate::wide_int::Int1536, SCALE>;
1374
1375/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1376/// `Int1536`.
1377///
1378/// Implemented on the underlying `crate::D<crate::wide_int::Int1536, SCALE>`
1379/// because `D462<SCALE>` is now an alias of that type. `ZERO` is emitted
1380/// by the basics macro further down in this file.
1381#[cfg(any(feature = "d462", feature = "x-wide"))]
1382impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int1536, SCALE> {
1383    #[inline]
1384    fn default() -> Self { Self::ZERO }
1385}
1386
1387#[cfg(any(feature = "d462", feature = "x-wide"))]
1388crate::macros::full::decl_decimal_full!(
1389    wide D462,
1390    crate::wide_int::I1536,
1391    crate::wide_int::U1536,
1392    crate::wide_int::I3072,
1393    crate::wide_int::Int3072,
1394    crate::wide_int::Int4096,
1395    crate::wide_int::Int4096,
1396    crate::wide_int::Int8192,
1397    wide_trig_d462,
1398    461
1399);
1400#[cfg(any(feature = "d462", feature = "x-wide"))]
1401pub type D462s0 = D462<0>;
1402#[cfg(any(feature = "d462", feature = "x-wide"))]
1403pub type D462s1 = D462<1>;
1404#[cfg(any(feature = "d462", feature = "x-wide"))]
1405pub type D462s18 = D462<18>;
1406#[cfg(any(feature = "d462", feature = "x-wide"))]
1407pub type D462s38 = D462<38>;
1408#[cfg(any(feature = "d462", feature = "x-wide"))]
1409pub type D462s75 = D462<75>;
1410#[cfg(any(feature = "d462", feature = "x-wide"))]
1411pub type D462s115 = D462<115>;
1412#[cfg(any(feature = "d462", feature = "x-wide"))]
1413pub type D462s153 = D462<153>;
1414#[cfg(any(feature = "d462", feature = "x-wide"))]
1415pub type D462s200 = D462<200>;
1416#[cfg(any(feature = "d462", feature = "x-wide"))]
1417pub type D462s230 = D462<230>;
1418#[cfg(any(feature = "d462", feature = "x-wide"))]
1419pub type D462s275 = D462<275>;
1420#[cfg(any(feature = "d462", feature = "x-wide"))]
1421pub type D462s307 = D462<307>;
1422#[cfg(any(feature = "d462", feature = "x-wide"))]
1423pub type D462s350 = D462<350>;
1424#[cfg(any(feature = "d462", feature = "x-wide"))]
1425pub type D462s400 = D462<400>;
1426#[cfg(any(feature = "d462", feature = "x-wide"))]
1427pub type D462s440 = D462<440>;
1428#[cfg(any(feature = "d462", feature = "x-wide"))]
1429pub type D462s460 = D462<460>;
1430/// Scale alias: `D462<461>`. 1 LSB = 10^-461. Maximum supported scale
1431/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1432#[cfg(any(feature = "d462", feature = "x-wide"))]
1433pub type D462s461 = D462<461>;
1434
1435// ── D616 (2048-bit / 32 u64 limbs) ─────────────────────────────────────
1436
1437/// Scaled fixed-point decimal with 2048-bit storage. New top tier
1438/// beyond D307; supports correctly-rounded transcendentals at scale
1439/// up to 616 decimal digits. Now a type alias of the unified
1440/// [`crate::D`] generic decimal type: `D616<S>` is
1441/// `D<crate::wide_int::Int2048, S>`. Both spellings are interchangeable.
1442///
1443/// The `#[repr(transparent)]` layout over `Int2048` is preserved through
1444/// the alias because the underlying [`crate::D`] is itself
1445/// `#[repr(transparent)]` over its storage parameter.
1446///
1447/// Gated behind the `d616` (or umbrella `x-wide`) Cargo feature.
1448#[cfg(any(feature = "d616", feature = "x-wide"))]
1449pub type D616<const SCALE: u32> = crate::D<crate::wide_int::Int2048, SCALE>;
1450
1451/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1452/// `Int2048`.
1453///
1454/// Implemented on the underlying `crate::D<crate::wide_int::Int2048, SCALE>`
1455/// because `D616<SCALE>` is now an alias of that type. `ZERO` is emitted
1456/// by the basics macro further down in this file.
1457#[cfg(any(feature = "d616", feature = "x-wide"))]
1458impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int2048, SCALE> {
1459    #[inline]
1460    fn default() -> Self { Self::ZERO }
1461}
1462
1463#[cfg(any(feature = "d616", feature = "x-wide"))]
1464crate::macros::full::decl_decimal_full!(
1465    wide D616,
1466    crate::wide_int::I2048,
1467    crate::wide_int::U2048,
1468    crate::wide_int::I4096,
1469    crate::wide_int::Int4096,
1470    crate::wide_int::Int8192,
1471    crate::wide_int::Int8192,
1472    crate::wide_int::Int16384,
1473    wide_trig_d616,
1474    615
1475);
1476#[cfg(any(feature = "d616", feature = "x-wide"))]
1477pub type D616s0 = D616<0>;
1478#[cfg(any(feature = "d616", feature = "x-wide"))]
1479pub type D616s1 = D616<1>;
1480#[cfg(any(feature = "d616", feature = "x-wide"))]
1481pub type D616s38 = D616<38>;
1482#[cfg(any(feature = "d616", feature = "x-wide"))]
1483pub type D616s75 = D616<75>;
1484#[cfg(any(feature = "d616", feature = "x-wide"))]
1485pub type D616s115 = D616<115>;
1486#[cfg(any(feature = "d616", feature = "x-wide"))]
1487pub type D616s153 = D616<153>;
1488#[cfg(any(feature = "d616", feature = "x-wide"))]
1489pub type D616s200 = D616<200>;
1490#[cfg(any(feature = "d616", feature = "x-wide"))]
1491pub type D616s230 = D616<230>;
1492#[cfg(any(feature = "d616", feature = "x-wide"))]
1493pub type D616s275 = D616<275>;
1494#[cfg(any(feature = "d616", feature = "x-wide"))]
1495pub type D616s308 = D616<308>;
1496#[cfg(any(feature = "d616", feature = "x-wide"))]
1497pub type D616s380 = D616<380>;
1498#[cfg(any(feature = "d616", feature = "x-wide"))]
1499pub type D616s462 = D616<462>;
1500#[cfg(any(feature = "d616", feature = "x-wide"))]
1501pub type D616s500 = D616<500>;
1502#[cfg(any(feature = "d616", feature = "x-wide"))]
1503pub type D616s555 = D616<555>;
1504#[cfg(any(feature = "d616", feature = "x-wide"))]
1505pub type D616s600 = D616<600>;
1506/// Scale alias: `D616<615>`. 1 LSB = 10^-615. Maximum supported scale
1507/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1508#[cfg(any(feature = "d616", feature = "x-wide"))]
1509pub type D616s615 = D616<615>;
1510
1511// ── D924 (3072-bit / 48 u64 limbs) ─────────────────────────────────────
1512
1513/// Scaled fixed-point decimal with 3072-bit storage. Half-width tier
1514/// between D616 and D1232; supports SCALE up to 924 digits. Now a type
1515/// alias of the unified [`crate::D`] generic decimal type: `D924<S>`
1516/// is `D<crate::wide_int::Int3072, S>`. Both spellings are interchangeable.
1517///
1518/// The `#[repr(transparent)]` layout over `Int3072` is preserved through
1519/// the alias because the underlying [`crate::D`] is itself
1520/// `#[repr(transparent)]` over its storage parameter.
1521///
1522/// Gated behind the `d924` (or umbrella `xx-wide`) Cargo feature.
1523#[cfg(any(feature = "d924", feature = "xx-wide"))]
1524pub type D924<const SCALE: u32> = crate::D<crate::wide_int::Int3072, SCALE>;
1525
1526/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1527/// `Int3072`.
1528///
1529/// Implemented on the underlying `crate::D<crate::wide_int::Int3072, SCALE>`
1530/// because `D924<SCALE>` is now an alias of that type. `ZERO` is emitted
1531/// by the basics macro further down in this file.
1532#[cfg(any(feature = "d924", feature = "xx-wide"))]
1533impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int3072, SCALE> {
1534    #[inline]
1535    fn default() -> Self { Self::ZERO }
1536}
1537
1538#[cfg(any(feature = "d924", feature = "xx-wide"))]
1539// `no_const_table`: 953-entry `Int12288` POW10_TABLE build exceeds
1540// the stable-rust const-eval step budget. Stays on the per-thread
1541// `Vec<(u32, W)>` cache.
1542crate::macros::full::decl_decimal_full!(
1543    wide D924,
1544    crate::wide_int::I3072,
1545    crate::wide_int::U3072,
1546    crate::wide_int::I6144,
1547    crate::wide_int::Int6144,
1548    crate::wide_int::Int12288,
1549    crate::wide_int::Int12288,
1550    crate::wide_int::Int16384,
1551    wide_trig_d924,
1552    923,
1553    no_const_table
1554);
1555#[cfg(any(feature = "d924", feature = "xx-wide"))]
1556pub type D924s0 = D924<0>;
1557#[cfg(any(feature = "d924", feature = "xx-wide"))]
1558pub type D924s1 = D924<1>;
1559#[cfg(any(feature = "d924", feature = "xx-wide"))]
1560pub type D924s75 = D924<75>;
1561#[cfg(any(feature = "d924", feature = "xx-wide"))]
1562pub type D924s153 = D924<153>;
1563#[cfg(any(feature = "d924", feature = "xx-wide"))]
1564pub type D924s230 = D924<230>;
1565#[cfg(any(feature = "d924", feature = "xx-wide"))]
1566pub type D924s307 = D924<307>;
1567#[cfg(any(feature = "d924", feature = "xx-wide"))]
1568pub type D924s400 = D924<400>;
1569#[cfg(any(feature = "d924", feature = "xx-wide"))]
1570pub type D924s461 = D924<461>;
1571#[cfg(any(feature = "d924", feature = "xx-wide"))]
1572pub type D924s462 = D924<462>;
1573#[cfg(any(feature = "d924", feature = "xx-wide"))]
1574pub type D924s500 = D924<500>;
1575#[cfg(any(feature = "d924", feature = "xx-wide"))]
1576pub type D924s616 = D924<616>;
1577#[cfg(any(feature = "d924", feature = "xx-wide"))]
1578pub type D924s700 = D924<700>;
1579#[cfg(any(feature = "d924", feature = "xx-wide"))]
1580pub type D924s800 = D924<800>;
1581#[cfg(any(feature = "d924", feature = "xx-wide"))]
1582pub type D924s860 = D924<860>;
1583#[cfg(any(feature = "d924", feature = "xx-wide"))]
1584pub type D924s900 = D924<900>;
1585#[cfg(any(feature = "d924", feature = "xx-wide"))]
1586pub type D924s920 = D924<920>;
1587/// Scale alias: `D924<923>`. 1 LSB = 10^-923. Maximum supported scale
1588/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1589#[cfg(any(feature = "d924", feature = "xx-wide"))]
1590pub type D924s923 = D924<923>;
1591
1592// ── D1232 (4096-bit / 64 u64 limbs) ────────────────────────────────────
1593
1594/// Scaled fixed-point decimal with 4096-bit storage. Widest tier
1595/// shipped; supports SCALE up to 1232 digits. Now a type alias of the
1596/// unified [`crate::D`] generic decimal type: `D1232<S>` is
1597/// `D<crate::wide_int::Int4096, S>`. Both spellings are interchangeable.
1598///
1599/// The `#[repr(transparent)]` layout over `Int4096` is preserved through
1600/// the alias because the underlying [`crate::D`] is itself
1601/// `#[repr(transparent)]` over its storage parameter.
1602///
1603/// Gated behind the `d1232` (or umbrella `xx-wide`) Cargo feature.
1604#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1605pub type D1232<const SCALE: u32> = crate::D<crate::wide_int::Int4096, SCALE>;
1606
1607/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1608/// `Int4096`.
1609///
1610/// Implemented on the underlying `crate::D<crate::wide_int::Int4096, SCALE>`
1611/// because `D1232<SCALE>` is now an alias of that type. `ZERO` is emitted
1612/// by the basics macro further down in this file.
1613#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1614impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int4096, SCALE> {
1615    #[inline]
1616    fn default() -> Self { Self::ZERO }
1617}
1618
1619#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1620// `no_const_table`: 1262-entry `Int16384` POW10_TABLE build exceeds
1621// the stable-rust const-eval step budget. Stays on the per-thread
1622// `Vec<(u32, W)>` cache.
1623crate::macros::full::decl_decimal_full!(
1624    wide D1232,
1625    crate::wide_int::I4096,
1626    crate::wide_int::U4096,
1627    crate::wide_int::I8192,
1628    crate::wide_int::Int8192,
1629    crate::wide_int::Int16384,
1630    crate::wide_int::Int16384,
1631    crate::wide_int::Int16384,
1632    wide_trig_d1232,
1633    1231,
1634    no_const_table
1635);
1636#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1637pub type D1232s0 = D1232<0>;
1638#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1639pub type D1232s1 = D1232<1>;
1640#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1641pub type D1232s75 = D1232<75>;
1642#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1643pub type D1232s153 = D1232<153>;
1644#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1645pub type D1232s230 = D1232<230>;
1646#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1647pub type D1232s307 = D1232<307>;
1648#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1649pub type D1232s461 = D1232<461>;
1650#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1651pub type D1232s616 = D1232<616>;
1652#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1653pub type D1232s700 = D1232<700>;
1654#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1655pub type D1232s800 = D1232<800>;
1656#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1657pub type D1232s900 = D1232<900>;
1658#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1659pub type D1232s924 = D1232<924>;
1660#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1661pub type D1232s1000 = D1232<1000>;
1662#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1663pub type D1232s1100 = D1232<1100>;
1664#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1665pub type D1232s1180 = D1232<1180>;
1666#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1667pub type D1232s1220 = D1232<1220>;
1668#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1669pub type D1232s1230 = D1232<1230>;
1670/// Scale alias: `D1232<1231>`. 1 LSB = 10^-1231. Maximum supported scale
1671/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1672#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1673pub type D1232s1231 = D1232<1231>;
1674
1675// ─── Cross-tier next-neighbour widen/narrow chain ─────────────────────
1676//
1677// The historical .widen() / .narrow() methods on D38/D76/D153/D307
1678// follow the power-of-two storage sequence (D38→D76→D153→D307). The
1679// 0.2.6 tier ladder fills in half-widths between each pair plus
1680// extends to D1232; the complete ladder is:
1681//
1682//   D9 → D18 → D38 → D57 → D76 → D115 → D153 → D230 → D307 →
1683//   D462 → D616 → D924 → D1232
1684//
1685// The next-neighbour .widen() / .narrow() methods on the new tiers go
1686// to the immediate adjacent rung (D57.widen() → D76, D76.widen()
1687// already returns D153 which is the existing power-of-two next-up,
1688// etc.). The cross-tier From / TryFrom impls below cover the
1689// neighbour pairs that weren't already declared by the legacy
1690// D38/D76/D153/D307 blocks.
1691//
1692// Coverage strategy: declare every NEW adjacent pair both ways. The
1693// existing legacy declarations (D9↔D18, D9/D18/D38↔D76, D38/D76↔D153,
1694// D76/D153↔D307) stay where they are; this block adds the conversions
1695// that hop through the new tiers (D38↔D57, D57↔D76, D76↔D115, etc.).
1696
1697// D38 ↔ D57
1698#[cfg(any(feature = "d57", feature = "wide"))]
1699crate::macros::conversions::decl_cross_width_widening!(wide D57, crate::wide_int::I192, D38, i128);
1700#[cfg(any(feature = "d57", feature = "wide"))]
1701crate::macros::conversions::decl_cross_width_narrowing!(wide D38, i128, D57, crate::wide_int::I192);
1702
1703// D57 ↔ D76
1704#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d76", feature = "wide")))]
1705crate::macros::conversions::decl_cross_width_widening!(wide D76, crate::wide_int::I256, D57, crate::wide_int::I192);
1706#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d76", feature = "wide")))]
1707crate::macros::conversions::decl_cross_width_narrowing!(wide D57, crate::wide_int::I192, D76, crate::wide_int::I256);
1708
1709// D76 ↔ D115
1710#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d115", feature = "wide")))]
1711crate::macros::conversions::decl_cross_width_widening!(wide D115, crate::wide_int::I384, D76, crate::wide_int::I256);
1712#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d115", feature = "wide")))]
1713crate::macros::conversions::decl_cross_width_narrowing!(wide D76, crate::wide_int::I256, D115, crate::wide_int::I384);
1714
1715// D115 ↔ D153
1716#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d153", feature = "wide")))]
1717crate::macros::conversions::decl_cross_width_widening!(wide D153, crate::wide_int::I512, D115, crate::wide_int::I384);
1718#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d153", feature = "wide")))]
1719crate::macros::conversions::decl_cross_width_narrowing!(wide D115, crate::wide_int::I384, D153, crate::wide_int::I512);
1720
1721// D153 ↔ D230
1722#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d230", feature = "wide")))]
1723crate::macros::conversions::decl_cross_width_widening!(wide D230, crate::wide_int::I768, D153, crate::wide_int::I512);
1724#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d230", feature = "wide")))]
1725crate::macros::conversions::decl_cross_width_narrowing!(wide D153, crate::wide_int::I512, D230, crate::wide_int::I768);
1726
1727// D230 ↔ D307
1728#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d307", feature = "wide")))]
1729crate::macros::conversions::decl_cross_width_widening!(wide D307, crate::wide_int::I1024, D230, crate::wide_int::I768);
1730#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d307", feature = "wide")))]
1731crate::macros::conversions::decl_cross_width_narrowing!(wide D230, crate::wide_int::I768, D307, crate::wide_int::I1024);
1732
1733// D307 ↔ D462
1734#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
1735crate::macros::conversions::decl_cross_width_widening!(wide D462, crate::wide_int::I1536, D307, crate::wide_int::I1024);
1736#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
1737crate::macros::conversions::decl_cross_width_narrowing!(wide D307, crate::wide_int::I1024, D462, crate::wide_int::I1536);
1738
1739// D462 ↔ D616
1740#[cfg(all(any(feature = "d462", feature = "x-wide"), any(feature = "d616", feature = "x-wide")))]
1741crate::macros::conversions::decl_cross_width_widening!(wide D616, crate::wide_int::I2048, D462, crate::wide_int::I1536);
1742#[cfg(all(any(feature = "d462", feature = "x-wide"), any(feature = "d616", feature = "x-wide")))]
1743crate::macros::conversions::decl_cross_width_narrowing!(wide D462, crate::wide_int::I1536, D616, crate::wide_int::I2048);
1744
1745// D616 ↔ D924
1746#[cfg(all(any(feature = "d616", feature = "x-wide"), any(feature = "d924", feature = "xx-wide")))]
1747crate::macros::conversions::decl_cross_width_widening!(wide D924, crate::wide_int::I3072, D616, crate::wide_int::I2048);
1748#[cfg(all(any(feature = "d616", feature = "x-wide"), any(feature = "d924", feature = "xx-wide")))]
1749crate::macros::conversions::decl_cross_width_narrowing!(wide D616, crate::wide_int::I2048, D924, crate::wide_int::I3072);
1750
1751// D924 ↔ D1232
1752#[cfg(all(any(feature = "d924", feature = "xx-wide"), any(feature = "d1232", feature = "xx-wide")))]
1753crate::macros::conversions::decl_cross_width_widening!(wide D1232, crate::wide_int::I4096, D924, crate::wide_int::I3072);
1754#[cfg(all(any(feature = "d924", feature = "xx-wide"), any(feature = "d1232", feature = "xx-wide")))]
1755crate::macros::conversions::decl_cross_width_narrowing!(wide D924, crate::wide_int::I3072, D1232, crate::wide_int::I4096);
1756
1757// .widen() / .narrow() methods on the new tiers — each points at the
1758// IMMEDIATE neighbour in the comprehensive ladder above. The legacy
1759// .widen() / .narrow() on D38/D76/D153/D307 are unchanged (still go
1760// to the power-of-two next-up) for source compatibility; users who
1761// want to traverse through the half-widths should use the methods
1762// declared here, or the From / TryFrom impls directly.
1763
1764#[cfg(any(feature = "d57", feature = "wide"))]
1765impl<const SCALE: u32> D57<SCALE> {
1766    /// Demote to the immediate previous tier ([`D38`]) at the same `SCALE`.
1767    /// Returns `Err(ConvertError::Overflow)` if the value exceeds `i128` range.
1768    #[inline]
1769    pub fn narrow(self) -> Result<D38<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1770    /// Promote to the next storage tier ([`D76`]) at the same `SCALE`. Lossless.
1771    #[inline] #[must_use]
1772    pub fn widen(self) -> D76<SCALE> { self.into() }
1773}
1774
1775#[cfg(any(feature = "d115", feature = "wide"))]
1776impl<const SCALE: u32> D115<SCALE> {
1777    /// Demote to the immediate previous tier ([`D76`]) at the same `SCALE`.
1778    #[inline]
1779    pub fn narrow(self) -> Result<D76<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1780    /// Promote to the next storage tier ([`D153`]) at the same `SCALE`. Lossless.
1781    #[inline] #[must_use]
1782    pub fn widen(self) -> D153<SCALE> { self.into() }
1783}
1784
1785#[cfg(any(feature = "d230", feature = "wide"))]
1786impl<const SCALE: u32> D230<SCALE> {
1787    /// Demote to the immediate previous tier ([`D153`]) at the same `SCALE`.
1788    #[inline]
1789    pub fn narrow(self) -> Result<D153<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1790    /// Promote to the next storage tier ([`D307`]) at the same `SCALE`. Lossless.
1791    #[inline] #[must_use]
1792    pub fn widen(self) -> D307<SCALE> { self.into() }
1793}
1794
1795#[cfg(any(feature = "d462", feature = "x-wide"))]
1796impl<const SCALE: u32> D462<SCALE> {
1797    /// Demote to the immediate previous tier ([`D307`]) at the same `SCALE`.
1798    #[inline]
1799    pub fn narrow(self) -> Result<D307<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1800    /// Promote to the next storage tier ([`D616`]) at the same `SCALE`. Lossless.
1801    #[inline] #[must_use]
1802    pub fn widen(self) -> D616<SCALE> { self.into() }
1803}
1804
1805#[cfg(any(feature = "d616", feature = "x-wide"))]
1806impl<const SCALE: u32> D616<SCALE> {
1807    /// Demote to the immediate previous tier ([`D462`]) at the same `SCALE`.
1808    #[inline]
1809    pub fn narrow(self) -> Result<D462<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1810}
1811
1812// `widen` lives in a second impl gated on D924's feature — D616 can
1813// be enabled without xx-wide (docs.rs builds this case), in which
1814// case D924 doesn't exist as a type and the unconditional `widen`
1815// method above breaks the doc build.
1816#[cfg(all(
1817    any(feature = "d616", feature = "x-wide"),
1818    any(feature = "d924", feature = "xx-wide"),
1819))]
1820impl<const SCALE: u32> D616<SCALE> {
1821    /// Promote to the next storage tier ([`D924`]) at the same `SCALE`. Lossless.
1822    #[inline] #[must_use]
1823    pub fn widen(self) -> D924<SCALE> { self.into() }
1824}
1825
1826#[cfg(any(feature = "d924", feature = "xx-wide"))]
1827impl<const SCALE: u32> D924<SCALE> {
1828    /// Demote to the immediate previous tier ([`D616`]) at the same `SCALE`.
1829    #[inline]
1830    pub fn narrow(self) -> Result<D616<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1831    /// Promote to the next storage tier ([`D1232`]) at the same `SCALE`. Lossless.
1832    #[inline] #[must_use]
1833    pub fn widen(self) -> D1232<SCALE> { self.into() }
1834}
1835
1836#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1837impl<const SCALE: u32> D1232<SCALE> {
1838    /// Demote to the immediate previous tier ([`D924`]) at the same `SCALE`.
1839    /// D1232 is the widest shipped tier, so there is no `.widen()` method.
1840    #[inline]
1841    pub fn narrow(self) -> Result<D924<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1842}
1843
1844// ─── Cross-scale-op constructors + comparators ─────────────────────────
1845//
1846// One invocation per width emits `mul_of`, `add_of`, `sub_of`, `div_of`,
1847// `rem_of`, `max_of`, `min_of`, `clamp_of`, `cmp_of`, `eq_of`, `ne_of`,
1848// `lt_of`, `le_of`, `gt_of`, `ge_of` (plus the `_with(mode)` siblings
1849// for the constructors that involve a possibly-lossy rescale of inputs).
1850// Operands of any width ≤ Self's storage are accepted via the
1851// `WidthLE` bound; operands of any SCALE are accepted via the
1852// const-generic `S1` / `S2` parameters. See
1853// `crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops` for
1854// the body.
1855
1856crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D9, i32);
1857crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D18, i64);
1858crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D38, i128);
1859
1860#[cfg(any(feature = "d57", feature = "wide"))]
1861crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D57, crate::wide_int::Int192);
1862
1863#[cfg(any(feature = "d76", feature = "wide"))]
1864crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D76, crate::wide_int::Int256);
1865
1866#[cfg(any(feature = "d115", feature = "wide"))]
1867crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D115, crate::wide_int::Int384);
1868
1869#[cfg(any(feature = "d153", feature = "wide"))]
1870crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D153, crate::wide_int::Int512);
1871
1872#[cfg(any(feature = "d230", feature = "wide"))]
1873crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D230, crate::wide_int::Int768);
1874
1875#[cfg(any(feature = "d307", feature = "wide"))]
1876crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D307, crate::wide_int::Int1024);
1877
1878#[cfg(any(feature = "d462", feature = "x-wide"))]
1879crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D462, crate::wide_int::Int1536);
1880
1881#[cfg(any(feature = "d616", feature = "x-wide"))]
1882crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D616, crate::wide_int::Int2048);
1883
1884#[cfg(any(feature = "d924", feature = "xx-wide"))]
1885crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D924, crate::wide_int::Int3072);
1886
1887#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1888crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D1232, crate::wide_int::Int4096);
1889
1890// ─── Cross-width `==` / `<` / `>` operator overloads (same SCALE) ──────
1891//
1892// One invocation per (narrower, wider) width pair emits four impls:
1893// PartialEq + PartialOrd in both directions. With these in place,
1894// ordinary `a == b` / `a < b` work across widths at the same SCALE
1895// without an explicit `.widen()`.
1896//
1897// Pairs are organised by narrower-width row. Feature gates ensure the
1898// impl is only emitted when both types in the pair exist in the build.
1899
1900// D9 row.
1901crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D18, i64);
1902crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D38, i128);
1903#[cfg(any(feature = "d57", feature = "wide"))]
1904crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D57, crate::wide_int::Int192);
1905#[cfg(any(feature = "d76", feature = "wide"))]
1906crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D76, crate::wide_int::Int256);
1907#[cfg(any(feature = "d115", feature = "wide"))]
1908crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D115, crate::wide_int::Int384);
1909#[cfg(any(feature = "d153", feature = "wide"))]
1910crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D153, crate::wide_int::Int512);
1911#[cfg(any(feature = "d230", feature = "wide"))]
1912crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D230, crate::wide_int::Int768);
1913#[cfg(any(feature = "d307", feature = "wide"))]
1914crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D307, crate::wide_int::Int1024);
1915#[cfg(any(feature = "d462", feature = "x-wide"))]
1916crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D462, crate::wide_int::Int1536);
1917#[cfg(any(feature = "d616", feature = "x-wide"))]
1918crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D616, crate::wide_int::Int2048);
1919#[cfg(any(feature = "d924", feature = "xx-wide"))]
1920crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D924, crate::wide_int::Int3072);
1921#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1922crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D1232, crate::wide_int::Int4096);
1923
1924// D18 row.
1925crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D38, i128);
1926#[cfg(any(feature = "d57", feature = "wide"))]
1927crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D57, crate::wide_int::Int192);
1928#[cfg(any(feature = "d76", feature = "wide"))]
1929crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D76, crate::wide_int::Int256);
1930#[cfg(any(feature = "d115", feature = "wide"))]
1931crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D115, crate::wide_int::Int384);
1932#[cfg(any(feature = "d153", feature = "wide"))]
1933crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D153, crate::wide_int::Int512);
1934#[cfg(any(feature = "d230", feature = "wide"))]
1935crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D230, crate::wide_int::Int768);
1936#[cfg(any(feature = "d307", feature = "wide"))]
1937crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D307, crate::wide_int::Int1024);
1938#[cfg(any(feature = "d462", feature = "x-wide"))]
1939crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D462, crate::wide_int::Int1536);
1940#[cfg(any(feature = "d616", feature = "x-wide"))]
1941crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D616, crate::wide_int::Int2048);
1942#[cfg(any(feature = "d924", feature = "xx-wide"))]
1943crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D924, crate::wide_int::Int3072);
1944#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1945crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D1232, crate::wide_int::Int4096);
1946
1947// D38 row.
1948#[cfg(any(feature = "d57", feature = "wide"))]
1949crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D57, crate::wide_int::Int192);
1950#[cfg(any(feature = "d76", feature = "wide"))]
1951crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D76, crate::wide_int::Int256);
1952#[cfg(any(feature = "d115", feature = "wide"))]
1953crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D115, crate::wide_int::Int384);
1954#[cfg(any(feature = "d153", feature = "wide"))]
1955crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D153, crate::wide_int::Int512);
1956#[cfg(any(feature = "d230", feature = "wide"))]
1957crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D230, crate::wide_int::Int768);
1958#[cfg(any(feature = "d307", feature = "wide"))]
1959crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D307, crate::wide_int::Int1024);
1960#[cfg(any(feature = "d462", feature = "x-wide"))]
1961crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D462, crate::wide_int::Int1536);
1962#[cfg(any(feature = "d616", feature = "x-wide"))]
1963crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D616, crate::wide_int::Int2048);
1964#[cfg(any(feature = "d924", feature = "xx-wide"))]
1965crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D924, crate::wide_int::Int3072);
1966#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1967crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D1232, crate::wide_int::Int4096);
1968
1969// D57 row.
1970#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d76", feature = "wide")))]
1971crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D76, crate::wide_int::Int256);
1972#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d115", feature = "wide")))]
1973crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D115, crate::wide_int::Int384);
1974#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d153", feature = "wide")))]
1975crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D153, crate::wide_int::Int512);
1976#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d230", feature = "wide")))]
1977crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D230, crate::wide_int::Int768);
1978#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d307", feature = "wide")))]
1979crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D307, crate::wide_int::Int1024);
1980#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
1981crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D462, crate::wide_int::Int1536);
1982#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d616", feature = "x-wide")))]
1983crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D616, crate::wide_int::Int2048);
1984#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d924", feature = "xx-wide")))]
1985crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D924, crate::wide_int::Int3072);
1986#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d1232", feature = "xx-wide")))]
1987crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D1232, crate::wide_int::Int4096);
1988
1989// D76 row.
1990#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d115", feature = "wide")))]
1991crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D115, crate::wide_int::Int384);
1992#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d153", feature = "wide")))]
1993crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D153, crate::wide_int::Int512);
1994#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d230", feature = "wide")))]
1995crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D230, crate::wide_int::Int768);
1996#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d307", feature = "wide")))]
1997crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D307, crate::wide_int::Int1024);
1998#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
1999crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D462, crate::wide_int::Int1536);
2000#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d616", feature = "x-wide")))]
2001crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D616, crate::wide_int::Int2048);
2002#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d924", feature = "xx-wide")))]
2003crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D924, crate::wide_int::Int3072);
2004#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d1232", feature = "xx-wide")))]
2005crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D1232, crate::wide_int::Int4096);
2006
2007// D115 row.
2008#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d153", feature = "wide")))]
2009crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D115, crate::wide_int::Int384, D153, crate::wide_int::Int512);
2010#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d230", feature = "wide")))]
2011crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D115, crate::wide_int::Int384, D230, crate::wide_int::Int768);
2012#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d307", feature = "wide")))]
2013crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D115, crate::wide_int::Int384, D307, crate::wide_int::Int1024);
2014#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
2015crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D115, crate::wide_int::Int384, D462, crate::wide_int::Int1536);
2016#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d616", feature = "x-wide")))]
2017crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D115, crate::wide_int::Int384, D616, crate::wide_int::Int2048);
2018#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d924", feature = "xx-wide")))]
2019crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D115, crate::wide_int::Int384, D924, crate::wide_int::Int3072);
2020#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d1232", feature = "xx-wide")))]
2021crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D115, crate::wide_int::Int384, D1232, crate::wide_int::Int4096);
2022
2023// D153 row.
2024#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d230", feature = "wide")))]
2025crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D153, crate::wide_int::Int512, D230, crate::wide_int::Int768);
2026#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d307", feature = "wide")))]
2027crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D153, crate::wide_int::Int512, D307, crate::wide_int::Int1024);
2028#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
2029crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D153, crate::wide_int::Int512, D462, crate::wide_int::Int1536);
2030#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d616", feature = "x-wide")))]
2031crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D153, crate::wide_int::Int512, D616, crate::wide_int::Int2048);
2032#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d924", feature = "xx-wide")))]
2033crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D153, crate::wide_int::Int512, D924, crate::wide_int::Int3072);
2034#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d1232", feature = "xx-wide")))]
2035crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D153, crate::wide_int::Int512, D1232, crate::wide_int::Int4096);
2036
2037// D230 row.
2038#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d307", feature = "wide")))]
2039crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D230, crate::wide_int::Int768, D307, crate::wide_int::Int1024);
2040#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
2041crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D230, crate::wide_int::Int768, D462, crate::wide_int::Int1536);
2042#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d616", feature = "x-wide")))]
2043crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D230, crate::wide_int::Int768, D616, crate::wide_int::Int2048);
2044#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d924", feature = "xx-wide")))]
2045crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D230, crate::wide_int::Int768, D924, crate::wide_int::Int3072);
2046#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d1232", feature = "xx-wide")))]
2047crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D230, crate::wide_int::Int768, D1232, crate::wide_int::Int4096);
2048
2049// D307 row.
2050#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
2051crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D307, crate::wide_int::Int1024, D462, crate::wide_int::Int1536);
2052#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d616", feature = "x-wide")))]
2053crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D307, crate::wide_int::Int1024, D616, crate::wide_int::Int2048);
2054#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d924", feature = "xx-wide")))]
2055crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D307, crate::wide_int::Int1024, D924, crate::wide_int::Int3072);
2056#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d1232", feature = "xx-wide")))]
2057crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D307, crate::wide_int::Int1024, D1232, crate::wide_int::Int4096);
2058
2059// D462 row.
2060#[cfg(all(any(feature = "d462", feature = "x-wide"), any(feature = "d616", feature = "x-wide")))]
2061crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D462, crate::wide_int::Int1536, D616, crate::wide_int::Int2048);
2062#[cfg(all(any(feature = "d462", feature = "x-wide"), any(feature = "d924", feature = "xx-wide")))]
2063crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D462, crate::wide_int::Int1536, D924, crate::wide_int::Int3072);
2064#[cfg(all(any(feature = "d462", feature = "x-wide"), any(feature = "d1232", feature = "xx-wide")))]
2065crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D462, crate::wide_int::Int1536, D1232, crate::wide_int::Int4096);
2066
2067// D616 row.
2068#[cfg(all(any(feature = "d616", feature = "x-wide"), any(feature = "d924", feature = "xx-wide")))]
2069crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D616, crate::wide_int::Int2048, D924, crate::wide_int::Int3072);
2070#[cfg(all(any(feature = "d616", feature = "x-wide"), any(feature = "d1232", feature = "xx-wide")))]
2071crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D616, crate::wide_int::Int2048, D1232, crate::wide_int::Int4096);
2072
2073// D924 row.
2074#[cfg(all(any(feature = "d924", feature = "xx-wide"), any(feature = "d1232", feature = "xx-wide")))]
2075crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D924, crate::wide_int::Int3072, D1232, crate::wide_int::Int4096);
2076
2077#[cfg(test)]
2078mod tests {
2079    use super::*;
2080
2081    /// `from_bits` / `to_bits` round-trip is exact.
2082    #[test]
2083    fn from_bits_to_bits_round_trip() {
2084        let raw: i128 = 1_500_000_000_000;
2085        let v: D38s12 = D38s12::from_bits(raw);
2086        assert_eq!(v.to_bits(), raw);
2087    }
2088
2089    /// `ZERO` has raw bit value 0.
2090    #[test]
2091    fn zero_is_zero_bits() {
2092        assert_eq!(D38s12::ZERO.to_bits(), 0);
2093    }
2094
2095    /// Two instances with identical raw bits compare equal.
2096    #[test]
2097    fn equal_by_underlying_bits() {
2098        assert_eq!(
2099            D38s12::from_bits(42_000_000_000_000),
2100            D38s12::from_bits(42_000_000_000_000)
2101        );
2102        assert_ne!(D38s12::from_bits(42), D38s12::from_bits(43));
2103    }
2104
2105    /// Ord is derived from i128: smaller bits compare less.
2106    #[test]
2107    fn ord_by_underlying_bits() {
2108        assert!(D38s12::from_bits(1) < D38s12::from_bits(2));
2109        assert!(D38s12::from_bits(-1) < D38s12::from_bits(0));
2110    }
2111
2112    /// `multiplier()` returns 10^SCALE. At SCALE = 12 that is 10^12.
2113    #[test]
2114    fn multiplier_is_ten_to_scale() {
2115        assert_eq!(D38s12::multiplier(), 1_000_000_000_000_i128);
2116    }
2117
2118    /// `SCALE` associated const returns the const-generic scale.
2119    #[test]
2120    fn scale_const_matches_type_parameter() {
2121        assert_eq!(D38s12::SCALE, 12);
2122        const N: u32 = D38s12::SCALE;
2123        assert_eq!(N, 12);
2124    }
2125
2126    /// `scale()` method returns the const-generic scale and is
2127    /// independent of the instance's value.
2128    #[test]
2129    fn scale_method_matches_type_parameter() {
2130        assert_eq!(D38s12::ZERO.scale(), 12);
2131        assert_eq!(D38s12::ONE.scale(), 12);
2132        assert_eq!(D38s12::from_bits(i128::MAX).scale(), 12);
2133        assert_eq!(D38s12::from_bits(-7).scale(), 12);
2134    }
2135
2136    /// Both forms agree at non-default scales.
2137    #[test]
2138    fn scale_at_other_scales() {
2139        type D6 = super::D38<6>;
2140        type D0 = super::D38<0>;
2141        type D38 = super::D38<38>;
2142        assert_eq!(D6::SCALE, 6);
2143        assert_eq!(D0::SCALE, 0);
2144        assert_eq!(D38::SCALE, 38);
2145        assert_eq!(D6::ZERO.scale(), 6);
2146        assert_eq!(D0::ZERO.scale(), 0);
2147        assert_eq!(D38::ZERO.scale(), 38);
2148    }
2149
2150    /// `ONE` has bit pattern 10^SCALE so that the logical value is 1.
2151    #[test]
2152    fn one_has_scaled_bit_pattern() {
2153        assert_eq!(D38s12::ONE.to_bits(), 1_000_000_000_000_i128);
2154    }
2155
2156    /// `MAX` is `i128::MAX`.
2157    #[test]
2158    fn max_is_i128_max() {
2159        assert_eq!(D38s12::MAX.to_bits(), i128::MAX);
2160    }
2161
2162    /// `MIN` is `i128::MIN`.
2163    #[test]
2164    fn min_is_i128_min() {
2165        assert_eq!(D38s12::MIN.to_bits(), i128::MIN);
2166    }
2167
2168    /// `ONE` is not equal to `ZERO`.
2169    #[test]
2170    fn one_is_not_zero() {
2171        assert_ne!(D38s12::ONE, D38s12::ZERO);
2172        assert!(D38s12::ONE.is_positive());
2173    }
2174
2175    /// `multiplier()` works correctly at non-default scales.
2176    #[test]
2177    fn multiplier_at_other_scales() {
2178        type D6 = super::D38<6>;
2179        assert_eq!(D6::multiplier(), 1_000_000_i128);
2180        assert_eq!(D6::ONE.to_bits(), 1_000_000_i128);
2181
2182        type D0 = super::D38<0>;
2183        assert_eq!(D0::multiplier(), 1_i128);
2184        assert_eq!(D0::ONE.to_bits(), 1_i128);
2185    }
2186
2187    // ----- D9 / D18 sanity tests -----
2188
2189    #[test]
2190    fn d9_basics() {
2191        assert_eq!(super::D9s2::ZERO.to_bits(), 0_i32);
2192        assert_eq!(super::D9s2::ONE.to_bits(), 100_i32);
2193        assert_eq!(super::D9s2::MAX.to_bits(), i32::MAX);
2194        assert_eq!(super::D9s2::MIN.to_bits(), i32::MIN);
2195        assert_eq!(super::D9s2::multiplier(), 100_i32);
2196        assert_eq!(super::D9s2::SCALE, 2);
2197    }
2198
2199    #[test]
2200    fn d18_basics() {
2201        assert_eq!(super::D18s9::ZERO.to_bits(), 0_i64);
2202        assert_eq!(super::D18s9::ONE.to_bits(), 1_000_000_000_i64);
2203        assert_eq!(super::D18s9::multiplier(), 1_000_000_000_i64);
2204        assert_eq!(super::D18s9::SCALE, 9);
2205    }
2206
2207    #[test]
2208    fn d9_arithmetic() {
2209        let a = super::D9s2::from_bits(150); // 1.50
2210        let b = super::D9s2::from_bits(250); // 2.50
2211        assert_eq!((a + b).to_bits(), 400);
2212        assert_eq!((b - a).to_bits(), 100);
2213        assert_eq!((-a).to_bits(), -150);
2214
2215        let x = super::D9s2::from_bits(200); // 2.00
2216        let y = super::D9s2::from_bits(300); // 3.00
2217        assert_eq!((x * y).to_bits(), 600); // 6.00
2218        assert_eq!((y / x).to_bits(), 150); // 1.50
2219        assert_eq!((y % x).to_bits(), 100); // 1.00
2220    }
2221
2222    #[test]
2223    fn d18_arithmetic() {
2224        let a = super::D18s9::from_bits(1_500_000_000); // 1.5
2225        let b = super::D18s9::from_bits(2_500_000_000); // 2.5
2226        assert_eq!((a + b).to_bits(), 4_000_000_000);
2227        assert_eq!((b - a).to_bits(), 1_000_000_000);
2228        assert_eq!((-a).to_bits(), -1_500_000_000);
2229
2230        let x = super::D18s9::from_bits(2_000_000_000); // 2.0
2231        let y = super::D18s9::from_bits(3_000_000_000); // 3.0
2232        assert_eq!((x * y).to_bits(), 6_000_000_000);
2233        assert_eq!((y / x).to_bits(), 1_500_000_000);
2234        assert_eq!((y % x).to_bits(), 1_000_000_000);
2235    }
2236
2237    #[test]
2238    fn d9_display() {
2239        let v: super::D9s2 = super::D9s2::from_bits(150); // 1.50
2240        let s = alloc::format!("{}", v);
2241        assert_eq!(s, "1.50");
2242        let neg: super::D9s2 = super::D9s2::from_bits(-2050); // -20.50
2243        assert_eq!(alloc::format!("{}", neg), "-20.50");
2244        let zero: super::D9s2 = super::D9s2::ZERO;
2245        assert_eq!(alloc::format!("{}", zero), "0.00");
2246        let int_only: super::D9s0 = super::D9s0::from_bits(42);
2247        assert_eq!(alloc::format!("{}", int_only), "42");
2248    }
2249
2250    #[test]
2251    fn d18_display() {
2252        let v: super::D18s9 = super::D18s9::from_bits(1_500_000_000); // 1.500000000
2253        assert_eq!(alloc::format!("{}", v), "1.500000000");
2254        let neg: super::D18s9 = super::D18s9::from_bits(-1_500_000_000);
2255        assert_eq!(alloc::format!("{}", neg), "-1.500000000");
2256    }
2257
2258    #[test]
2259    fn d9_debug() {
2260        let v: super::D9s2 = super::D9s2::from_bits(150);
2261        let s = alloc::format!("{:?}", v);
2262        assert_eq!(s, "D9<2>(1.50)");
2263    }
2264
2265    #[test]
2266    fn cross_width_widening_d9_to_d18() {
2267        let small: super::D9s2 = super::D9s2::from_bits(150);
2268        let wider: super::D18s2 = small.into();
2269        assert_eq!(wider.to_bits(), 150_i64);
2270    }
2271
2272    #[test]
2273    fn cross_width_widening_d9_to_d38() {
2274        let small: super::D9s2 = super::D9s2::from_bits(-150);
2275        let wider: super::D38s2 = small.into();
2276        assert_eq!(wider.to_bits(), -150_i128);
2277    }
2278
2279    #[test]
2280    fn cross_width_widening_d18_to_d38() {
2281        let mid: super::D18s9 = super::D18s9::from_bits(i64::MAX);
2282        let wider: super::D38s9 = mid.into();
2283        assert_eq!(wider.to_bits(), i64::MAX as i128);
2284    }
2285
2286    #[test]
2287    fn cross_width_narrowing_d38_to_d18_in_range() {
2288        let wide: super::D38s9 = super::D38s9::from_bits(1_500_000_000);
2289        let narrow: super::D18s9 = wide.try_into().unwrap();
2290        assert_eq!(narrow.to_bits(), 1_500_000_000);
2291    }
2292
2293    #[test]
2294    fn cross_width_narrowing_d38_to_d18_out_of_range() {
2295        let wide: super::D38s9 = super::D38s9::from_bits(i128::MAX);
2296        let narrow: Result<super::D18s9, _> = wide.try_into();
2297        assert!(narrow.is_err());
2298    }
2299
2300    #[test]
2301    fn cross_width_narrowing_d18_to_d9_in_range() {
2302        let mid: super::D18s2 = super::D18s2::from_bits(150);
2303        let narrow: super::D9s2 = mid.try_into().unwrap();
2304        assert_eq!(narrow.to_bits(), 150);
2305    }
2306
2307    #[test]
2308    fn cross_width_narrowing_d18_to_d9_out_of_range() {
2309        let mid: super::D18s2 = super::D18s2::from_bits(i64::MAX);
2310        let narrow: Result<super::D9s2, _> = mid.try_into();
2311        assert!(narrow.is_err());
2312    }
2313
2314    #[test]
2315    fn d9_consts() {
2316        if !crate::support::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
2317        use crate::types::consts::DecimalConstants;
2318        type D9s4 = super::D9<4>;
2319        // pi at scale 4 = 3.1416 -> bits = 31416.
2320        assert_eq!(D9s4::pi().to_bits(), 31416);
2321        // e at scale 4 = 2.7183 -> bits = 27183.
2322        assert_eq!(D9s4::e().to_bits(), 27183);
2323    }
2324
2325    #[test]
2326    fn d9_from_str() {
2327        use core::str::FromStr;
2328        let v = super::D9s2::from_str("1.50").unwrap();
2329        assert_eq!(v.to_bits(), 150);
2330        let neg = super::D9s2::from_str("-20.50").unwrap();
2331        assert_eq!(neg.to_bits(), -2050);
2332        // Out of range for D9s2 (i32::MAX is ~2.1e9).
2333        assert!(super::D9s2::from_str("1000000000000.00").is_err());
2334    }
2335
2336    #[test]
2337    fn d18_from_str() {
2338        use core::str::FromStr;
2339        let v = super::D18s9::from_str("1.500000000").unwrap();
2340        assert_eq!(v.to_bits(), 1_500_000_000);
2341        let neg = super::D18s9::from_str("-1.500000000").unwrap();
2342        assert_eq!(neg.to_bits(), -1_500_000_000);
2343    }
2344
2345    #[test]
2346    fn d18_consts() {
2347        if !crate::support::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
2348        use crate::types::consts::DecimalConstants;
2349        type D18s12 = super::D18<12>;
2350        // pi at scale 12 = 3.141592653590 (matches D38s12).
2351        assert_eq!(D18s12::pi().to_bits(), 3_141_592_653_590);
2352        // tau at scale 12 = 6.283185307180.
2353        assert_eq!(D18s12::tau().to_bits(), 6_283_185_307_180);
2354    }
2355
2356    #[cfg(any(feature = "d76", feature = "wide"))]
2357    #[test]
2358    fn d76_basics() {
2359        
2360        use crate::types::traits::arithmetic::DecimalArithmetic;
2361        use crate::wide_int::I256;
2362        assert_eq!(super::D76s2::ZERO.to_bits(), I256::from_str_radix("0", 10).unwrap());
2363        assert_eq!(super::D76s2::ONE.to_bits(), I256::from_str_radix("100", 10).unwrap());
2364        assert_eq!(super::D76s2::MAX.to_bits(), I256::MAX);
2365        assert_eq!(super::D76s2::MIN.to_bits(), I256::MIN);
2366        assert_eq!(super::D76s2::multiplier(), I256::from_str_radix("100", 10).unwrap());
2367        assert_eq!(super::D76s2::SCALE, 2);
2368        assert_eq!(super::D76s2::ZERO.scale(), 2);
2369        // SCALE = 75 (new MAX_SCALE) multiplier is 10^75, well within 256-bit range.
2370        let m75 = super::D76s75::multiplier();
2371        assert_eq!(
2372            m75,
2373            I256::from_str_radix("1000000000000000000000000000000000000000000000000000000000000000000000000000", 10).unwrap()
2374        );
2375        assert_eq!(<super::D76s12 as DecimalArithmetic>::MAX_SCALE, 75);
2376        // round-trip
2377        let raw = I256::from_str_radix("123456789012345678901234567890", 10).unwrap();
2378        assert_eq!(super::D76s12::from_bits(raw).to_bits(), raw);
2379    }
2380
2381    #[cfg(any(feature = "d76", feature = "wide"))]
2382    #[test]
2383    fn d76_arithmetic() {
2384        type D = super::D76<12>;
2385        let one = D::ONE;
2386        let two = D::from_bits(D::multiplier() + D::multiplier());
2387        let three = D::from_bits(D::multiplier() * crate::wide_int::I256::from_str_radix("3", 10).unwrap());
2388        // add / sub / neg
2389        assert_eq!((one + two), three);
2390        assert_eq!((three - one), two);
2391        assert_eq!((-one).to_bits(), -D::multiplier());
2392        // mul: 2 * 3 == 6
2393        let six = D::from_bits(D::multiplier() * crate::wide_int::I256::from_str_radix("6", 10).unwrap());
2394        assert_eq!((two * three), six);
2395        // div: 6 / 2 == 3
2396        assert_eq!((six / two), three);
2397        // rem: 6 % 2 == 0 (storage-level remainder)
2398        assert_eq!((six % two), D::ZERO);
2399        // assign forms
2400        let mut v = one;
2401        v += two;
2402        assert_eq!(v, three);
2403        v *= two;
2404        assert_eq!(v, six);
2405        v /= two;
2406        assert_eq!(v, three);
2407        v -= one;
2408        assert_eq!(v, two);
2409        v %= two;
2410        assert_eq!(v, D::ZERO);
2411        // fractional: 1.5 * 1.5 == 2.25 at scale 12
2412        let half = D::from_bits(D::multiplier() / crate::wide_int::I256::from_str_radix("2", 10).unwrap());
2413        let one_and_half = one + half;
2414        let product = one_and_half * one_and_half;
2415        let expected = D::from_bits(
2416            D::multiplier() * crate::wide_int::I256::from_str_radix("2", 10).unwrap()
2417                + D::multiplier() / crate::wide_int::I256::from_str_radix("4", 10).unwrap(),
2418        );
2419        assert_eq!(product, expected);
2420    }
2421
2422    #[cfg(any(feature = "d76", feature = "wide"))]
2423    #[test]
2424    fn d76_display() {
2425        type D = super::D76<12>;
2426        let one = D::ONE;
2427        assert_eq!(alloc::format!("{}", one), "1.000000000000");
2428        assert_eq!(alloc::format!("{}", -one), "-1.000000000000");
2429        assert_eq!(alloc::format!("{}", D::ZERO), "0.000000000000");
2430        let half = D::from_bits(D::multiplier() / crate::wide_int::I256::from_str_radix("2", 10).unwrap());
2431        assert_eq!(alloc::format!("{}", half), "0.500000000000");
2432        assert_eq!(alloc::format!("{:?}", one), "D76<12>(1.000000000000)");
2433        // scale 0 prints no fractional part
2434        let int_only: super::D76<0> = super::D76::<0>::ONE;
2435        assert_eq!(alloc::format!("{}", int_only), "1");
2436        // very large magnitude near the 75-digit ceiling (new MAX_SCALE)
2437        let big = super::D76s75::MAX;
2438        let s = alloc::format!("{}", big);
2439        assert!(s.starts_with("57.8960446"));
2440        assert_eq!(s.len(), "57.".len() + 75);
2441    }
2442
2443    #[cfg(any(feature = "d76", feature = "wide"))]
2444    #[test]
2445    fn d76_sign_and_helpers() {
2446        type D = super::D76<6>;
2447        let neg = -D::ONE;
2448        assert!(neg.is_negative());
2449        assert!(D::ONE.is_positive());
2450        assert!(!D::ZERO.is_positive());
2451        assert_eq!(neg.abs(), D::ONE);
2452        assert_eq!(D::ONE.signum(), D::ONE);
2453        assert_eq!(neg.signum(), neg);
2454        assert_eq!(D::ZERO.signum(), D::ZERO);
2455        // min / max / clamp
2456        let two = D::ONE + D::ONE;
2457        assert_eq!(D::ONE.min(two), D::ONE);
2458        assert_eq!(D::ONE.max(two), two);
2459        assert_eq!(two.clamp(D::ZERO, D::ONE), D::ONE);
2460        // copysign
2461        assert_eq!(D::ONE.copysign(neg), neg);
2462        assert_eq!(neg.copysign(D::ONE), D::ONE);
2463        // recip: 1/2 at scale 6
2464        let half = D::from_bits(D::multiplier() / crate::wide_int::I256::from_str_radix("2", 10).unwrap());
2465        assert_eq!(two.recip(), half);
2466    }
2467
2468    #[cfg(any(feature = "d76", feature = "wide"))]
2469    #[test]
2470    fn d76_overflow_variants() {
2471        type D = super::D76<2>;
2472        // checked_add overflow at MAX
2473        assert_eq!(D::MAX.checked_add(D::ONE), None);
2474        assert_eq!(D::ONE.checked_add(D::ONE), Some(D::ONE + D::ONE));
2475        // saturating
2476        assert_eq!(D::MAX.saturating_add(D::ONE), D::MAX);
2477        assert_eq!(D::MIN.saturating_sub(D::ONE), D::MIN);
2478        // checked_neg of MIN overflows
2479        assert_eq!(D::MIN.checked_neg(), None);
2480        assert_eq!(D::ONE.checked_neg(), Some(-D::ONE));
2481        // checked_mul / checked_div
2482        let two = D::ONE + D::ONE;
2483        let three = two + D::ONE;
2484        assert_eq!(two.checked_mul(three), Some(D::from_bits(D::multiplier() * crate::wide_int::I256::from_str_radix("6", 10).unwrap())));
2485        assert_eq!(D::ONE.checked_div(D::ZERO), None);
2486        assert_eq!((three).checked_div(D::ONE), Some(three));
2487        // wrapping_add of one storage LSB at MAX wraps around to MIN.
2488        let one_lsb = D::from_bits(crate::wide_int::I256::from_str_radix("1", 10).unwrap());
2489        assert_eq!(D::MAX.wrapping_add(one_lsb), D::MIN);
2490        // overflowing
2491        assert_eq!(D::ONE.overflowing_add(D::ONE), (two, false));
2492        assert_eq!(D::MAX.overflowing_add(D::ONE).1, true);
2493    }
2494
2495    #[cfg(any(feature = "d76", feature = "wide"))]
2496    #[test]
2497    fn d76_consts_and_from_str() {
2498        use crate::types::consts::DecimalConstants;
2499        use core::str::FromStr;
2500        // pi at scale 12 matches the D38 reference.
2501        assert_eq!(
2502            super::D76::<12>::pi().to_bits(),
2503            crate::wide_int::I256::from_str_radix("3141592653590", 10).unwrap()
2504        );
2505        assert_eq!(
2506            super::D76::<4>::e().to_bits(),
2507            crate::wide_int::I256::from_str_radix("27183", 10).unwrap()
2508        );
2509        // FromStr within i128 range
2510        let v = super::D76::<2>::from_str("1.50").unwrap();
2511        assert_eq!(v.to_bits(), crate::wide_int::I256::from_str_radix("150", 10).unwrap());
2512        let neg = super::D76::<2>::from_str("-20.50").unwrap();
2513        assert_eq!(neg.to_bits(), crate::wide_int::I256::from_str_radix("-2050", 10).unwrap());
2514        // num_traits Zero / One
2515        use ::num_traits::{One, Zero};
2516        assert!(super::D76::<6>::zero().is_zero());
2517        assert!(super::D76::<6>::one().is_one());
2518    }
2519
2520    #[cfg(any(feature = "d76", feature = "wide"))]
2521    #[test]
2522    fn d76_conversions() {
2523        use crate::wide_int::I256;
2524        type D = super::D76<6>;
2525        // From<primitive int>
2526        let from_i32: D = 5i32.into();
2527        assert_eq!(from_i32.to_bits(), I256::from_str_radix("5000000", 10).unwrap());
2528        let from_u64: D = 7u64.into();
2529        assert_eq!(from_u64.to_bits(), I256::from_str_radix("7000000", 10).unwrap());
2530        let from_neg: D = (-3i16).into();
2531        assert_eq!(from_neg.to_bits(), I256::from_str_radix("-3000000", 10).unwrap());
2532        // TryFrom<i128> / TryFrom<u128>
2533        let from_i128 = D::try_from(123i128).unwrap();
2534        assert_eq!(from_i128.to_bits(), I256::from_str_radix("123000000", 10).unwrap());
2535        let from_u128 = D::try_from(u128::MAX).unwrap();
2536        assert_eq!(
2537            from_u128.to_bits(),
2538            I256::from_str_radix("340282366920938463463374607431768211455", 10).unwrap()
2539                * I256::from_str_radix("1000000", 10).unwrap()
2540        );
2541        // TryFrom<f64>
2542        let from_f64 = D::try_from(2.5f64).unwrap();
2543        assert_eq!(from_f64.to_bits(), I256::from_str_radix("2500000", 10).unwrap());
2544        assert!(D::try_from(f64::NAN).is_err());
2545        // from_int / from_i32
2546        assert_eq!(D::from_int(9i128), D::from(9i32));
2547        assert_eq!(D::from_i32(-4), D::from(-4i32));
2548        // to_int: 2.5 with HalfToEven -> 2
2549        use crate::support::rounding::RoundingMode;
2550        let two_and_half = D::from_bits(I256::from_str_radix("2500000", 10).unwrap());
2551        assert_eq!(two_and_half.to_int_with(RoundingMode::HalfToEven), 2);
2552        assert_eq!(two_and_half.to_int_with(RoundingMode::HalfAwayFromZero), 3);
2553        assert_eq!(two_and_half.to_int_with(RoundingMode::Ceiling), 3);
2554        assert_eq!(two_and_half.to_int_with(RoundingMode::Floor), 2);
2555        let neg_two_and_half = -two_and_half;
2556        assert_eq!(neg_two_and_half.to_int_with(RoundingMode::Floor), -3);
2557        assert_eq!(neg_two_and_half.to_int_with(RoundingMode::Trunc), -2);
2558        // cross-width widening D38 -> D76 (lossless)
2559        let d38: super::D38s6 = super::D38s6::from_bits(-150);
2560        let widened: super::D76<6> = d38.into();
2561        assert_eq!(widened.to_bits(), I256::from_str_radix("-150", 10).unwrap());
2562        // cross-width narrowing D76 -> D38 in range
2563        let in_range: super::D76<6> = super::D76::<6>::from_bits(I256::from_str_radix("999", 10).unwrap());
2564        let narrowed: super::D38s6 = in_range.try_into().unwrap();
2565        assert_eq!(narrowed.to_bits(), 999i128);
2566        // cross-width narrowing D76 -> D38 out of range
2567        let out_of_range = super::D76s75::MAX;
2568        let narrow_fail: Result<super::D38<75>, _> = out_of_range.try_into();
2569        assert!(narrow_fail.is_err());
2570    }
2571
2572    #[cfg(any(feature = "d76", feature = "wide"))]
2573    #[test]
2574    fn d76_rescale_rounding_floats() {
2575        use crate::support::rounding::RoundingMode;
2576        use crate::wide_int::I256;
2577        type D6 = super::D76<6>;
2578        // rescale up (lossless): scale 6 -> scale 9
2579        let v = D6::from_bits(I256::from_str_radix("1500000", 10).unwrap()); // 1.5
2580        let up: super::D76<9> = v.rescale::<9>();
2581        assert_eq!(up.to_bits(), I256::from_str_radix("1500000000", 10).unwrap());
2582        // rescale down (lossy, HalfToEven): scale 6 -> scale 2
2583        let down: super::D76<2> = v.rescale::<2>();
2584        assert_eq!(down.to_bits(), I256::from_str_radix("150", 10).unwrap());
2585        // rescale down with explicit mode: 2.5 (scale 0 representation) ...
2586        let two_p_five = super::D76::<1>::from_bits(I256::from_str_radix("25", 10).unwrap());
2587        let r0: super::D76<0> = two_p_five.rescale_with::<0>(RoundingMode::HalfToEven);
2588        assert_eq!(r0.to_bits(), I256::from_str_radix("2", 10).unwrap());
2589        let r0b: super::D76<0> = two_p_five.rescale_with::<0>(RoundingMode::HalfAwayFromZero);
2590        assert_eq!(r0b.to_bits(), I256::from_str_radix("3", 10).unwrap());
2591        // floor / ceil / round / trunc / fract on 1.5 at scale 6
2592        assert_eq!(v.floor(), D6::ONE);
2593        assert_eq!(v.ceil(), D6::ONE + D6::ONE);
2594        assert_eq!(v.round(), D6::ONE + D6::ONE); // half away from zero
2595        assert_eq!(v.trunc(), D6::ONE);
2596        assert_eq!(v.fract(), D6::from_bits(I256::from_str_radix("500000", 10).unwrap()));
2597        // negative: -1.5
2598        let neg = -v;
2599        assert_eq!(neg.floor(), -(D6::ONE + D6::ONE));
2600        assert_eq!(neg.ceil(), -D6::ONE);
2601        assert_eq!(neg.round(), -(D6::ONE + D6::ONE));
2602        // float bridge
2603        let from_f = D6::from_f64(2.5);
2604        assert_eq!(from_f.to_bits(), I256::from_str_radix("2500000", 10).unwrap());
2605        assert_eq!(D6::from_f64(f64::NAN), D6::ZERO);
2606        assert_eq!(D6::from_f64(f64::INFINITY), D6::MAX);
2607        let round_trip = D6::ONE.to_f64();
2608        assert!((round_trip - 1.0).abs() < 1e-9);
2609    }
2610
2611    #[cfg(any(feature = "d153", feature = "wide"))]
2612    #[test]
2613    fn d153_smoke() {
2614        
2615        use crate::types::traits::arithmetic::DecimalArithmetic;
2616        use crate::wide_int::I512;
2617        type D = super::D153<35>;
2618        assert_eq!(<D as DecimalArithmetic>::MAX_SCALE, 152);
2619        assert_eq!(D::ZERO.to_bits(), I512::from_str_radix("0", 10).unwrap());
2620        let one = D::ONE;
2621        let two = one + one;
2622        let three = two + one;
2623        assert_eq!(two * three, D::from_int(6i128));
2624        assert_eq!((three * two) / two, three);
2625        assert_eq!(alloc::format!("{}", one).len(), "1.".len() + 35);
2626        assert_eq!(D::from_int(5i128).to_int(), 5);
2627        // rescale across the wide range
2628        let up: super::D153<150> = one.rescale::<150>();
2629        assert_eq!(up, super::D153::<150>::ONE);
2630        // 152-digit ceiling multiplier fits in I512 (new MAX_SCALE)
2631        let _ = super::D153s152::multiplier();
2632    }
2633
2634    #[cfg(any(feature = "d307", feature = "wide"))]
2635    #[test]
2636    fn d307_smoke() {
2637        
2638        use crate::types::traits::arithmetic::DecimalArithmetic;
2639        use crate::wide_int::I1024;
2640        type D = super::D307<35>;
2641        assert_eq!(<D as DecimalArithmetic>::MAX_SCALE, 306);
2642        let one = D::ONE;
2643        let two = one + one;
2644        let three = two + one;
2645        assert_eq!(two * three, D::from_int(6i128));
2646        assert_eq!((three * two) / two, three);
2647        assert_eq!(D::ZERO.to_bits(), I1024::from_str_radix("0", 10).unwrap());
2648        assert_eq!(alloc::format!("{}", one).len(), "1.".len() + 35);
2649        // cross-width: D76 -> D307 widening, D307 -> D76 narrowing
2650        #[cfg(any(feature = "d76", feature = "wide"))]
2651        {
2652            let small: super::D76<35> = super::D76::<35>::ONE;
2653            let widened: super::D307<35> = small.into();
2654            assert_eq!(widened, D::ONE);
2655            let narrowed: super::D76<35> = widened.try_into().unwrap();
2656            assert_eq!(narrowed, super::D76::<35>::ONE);
2657        }
2658        // 306-digit ceiling multiplier fits in I1024 (new MAX_SCALE)
2659        let _ = super::D307s306::multiplier();
2660    }
2661
2662    #[test]
2663    fn d9_op_assign() {
2664        let mut v = super::D9s2::from_bits(100);
2665        v += super::D9s2::from_bits(50);
2666        assert_eq!(v.to_bits(), 150);
2667        v -= super::D9s2::from_bits(25);
2668        assert_eq!(v.to_bits(), 125);
2669        v *= super::D9s2::from_bits(200); // *2.00
2670        assert_eq!(v.to_bits(), 250);
2671        v /= super::D9s2::from_bits(200); // /2.00
2672        assert_eq!(v.to_bits(), 125);
2673        v %= super::D9s2::from_bits(100);
2674        assert_eq!(v.to_bits(), 25);
2675    }
2676}