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    wide_trig_d76,
795    75
796);
797// Cross-width widening into D76 (lossless): D9 / D18 / D38 -> D76.
798#[cfg(any(feature = "d76", feature = "wide"))]
799crate::macros::conversions::decl_cross_width_widening!(wide D76, crate::wide_int::I256, D9, i32);
800#[cfg(any(feature = "d76", feature = "wide"))]
801crate::macros::conversions::decl_cross_width_widening!(wide D76, crate::wide_int::I256, D18, i64);
802#[cfg(any(feature = "d76", feature = "wide"))]
803crate::macros::conversions::decl_cross_width_widening!(wide D76, crate::wide_int::I256, D38, i128);
804// Cross-width narrowing from D76 (fallible): D76 -> D38 / D18 / D9.
805#[cfg(any(feature = "d76", feature = "wide"))]
806crate::macros::conversions::decl_cross_width_narrowing!(wide D38, i128, D76, crate::wide_int::I256);
807#[cfg(any(feature = "d76", feature = "wide"))]
808crate::macros::conversions::decl_cross_width_narrowing!(wide D18, i64, D76, crate::wide_int::I256);
809#[cfg(any(feature = "d76", feature = "wide"))]
810crate::macros::conversions::decl_cross_width_narrowing!(wide D9, i32, D76, crate::wide_int::I256);
811
812// ─── D38::widen / D76 hop methods ─────────────────────────────────────
813
814#[cfg(any(feature = "d76", feature = "wide"))]
815impl<const SCALE: u32> D38<SCALE> {
816    /// Promote to the next storage tier ([`D57`]) at the same `SCALE`.
817    /// Lossless. Available with the `d57` (or umbrella `wide`) Cargo
818    /// feature enabled.
819    ///
820    /// ```
821    /// # #[cfg(feature = "wide")] {
822    /// use decimal_scaled::D38s12;
823    /// let a = D38s12::from_int(1_000_000);
824    /// let _wider = a.widen();  // D57<12>
825    /// # }
826    /// ```
827    #[inline]
828    #[must_use]
829    pub fn widen(self) -> D57<SCALE> {
830        self.into()
831    }
832}
833
834#[cfg(any(feature = "d76", feature = "wide"))]
835impl<const SCALE: u32> D76<SCALE> {
836    /// Demote to the previous storage tier ([`D57`]) at the same
837    /// `SCALE`. Returns `Err(ConvertError::Overflow)` if the value
838    /// doesn't fit `D57`'s range at the given scale.
839    #[inline]
840    pub fn narrow(self) -> Result<D57<SCALE>, crate::support::error::ConvertError> {
841        self.try_into()
842    }
843}
844
845/// Scale alias: `D76<0>`. 1 LSB = 1 (256-bit integer ledger).
846#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s0  = D76<0>;
847/// Scale alias: `D76<1>`. 1 LSB = 10^-1 (tenths).
848#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s1  = D76<1>;
849/// Scale alias: `D76<2>`. 1 LSB = 10^-2 (cents).
850#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s2  = D76<2>;
851#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s3  = D76<3>;
852#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s4  = D76<4>;
853/// Scale alias: `D76<6>`. 1 LSB = 10^-6 (ppm).
854#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s6  = D76<6>;
855#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s9  = D76<9>;
856/// Scale alias: `D76<12>`. 1 LSB = 10^-12 (pico; financial standard).
857#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s12 = D76<12>;
858#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s15 = D76<15>;
859/// Scale alias: `D76<18>`. 1 LSB = 10^-18 (atto).
860#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s18 = D76<18>;
861#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s20 = D76<20>;
862#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s24 = D76<24>;
863#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s28 = D76<28>;
864#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s32 = D76<32>;
865/// Scale alias: `D76<35>`. 1 LSB = 10^-35 (matches `SCALE_REF`).
866#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s35 = D76<35>;
867#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s38 = D76<38>;
868#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s42 = D76<42>;
869#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s48 = D76<48>;
870/// Scale alias: `D76<50>`. 1 LSB = 10^-50 (deep scientific precision).
871#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s50 = D76<50>;
872#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s56 = D76<56>;
873#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s64 = D76<64>;
874#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s70 = D76<70>;
875/// Scale alias: `D76<75>`. 1 LSB = 10^-75. Maximum supported scale
876/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
877#[cfg(any(feature = "d76", feature = "wide"))] pub type D76s75 = D76<75>;
878
879// ---------------------------------------------------------------------
880// D153 — 512-bit storage (`Int512`), scale 0..=153. Wide-scientific
881// tier; gated behind the `d153` / `wide` Cargo features.
882// ---------------------------------------------------------------------
883
884/// Scaled fixed-point decimal with 512-bit storage. See [`D38`] for the
885/// shape documentation; D153 has the same surface scaled to a 512-bit
886/// signed integer and `MAX_SCALE = 153`. Now a type alias of the unified
887/// [`crate::D`] generic decimal type: `D153<S>` is
888/// `D<crate::wide_int::Int512, S>`. Both spellings are interchangeable.
889///
890/// The `#[repr(transparent)]` layout over `Int512` is preserved through
891/// the alias because the underlying [`crate::D`] is itself
892/// `#[repr(transparent)]` over its storage parameter.
893///
894/// Gated behind the `d153` (or umbrella `wide`) Cargo feature. The
895/// storage backend is `Int512`.
896#[cfg(any(feature = "d153", feature = "wide"))]
897pub type D153<const SCALE: u32> = crate::D<crate::wide_int::Int512, SCALE>;
898
899/// `Default` returns `ZERO`, matching the all-zero limb pattern of
900/// `Int512`.
901///
902/// Implemented on the underlying `crate::D<crate::wide_int::Int512, SCALE>`
903/// because `D153<SCALE>` is now an alias of that type. `ZERO` is emitted
904/// by the basics macro further down in this file.
905#[cfg(any(feature = "d153", feature = "wide"))]
906impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int512, SCALE> {
907    #[inline]
908    fn default() -> Self {
909        Self::ZERO
910    }
911}
912
913#[cfg(any(feature = "d153", feature = "wide"))]
914crate::macros::full::decl_decimal_full!(
915    wide D153,
916    crate::wide_int::I512,
917    crate::wide_int::U512,
918    crate::wide_int::I1024,
919    crate::wide_int::Int1024,
920    crate::wide_int::Int2048,
921    crate::wide_int::Int2048,
922    wide_trig_d153,
923    152
924);
925// Cross-width widening into D153 (lossless): D38 / D76 -> D153.
926#[cfg(any(feature = "d153", feature = "wide"))]
927crate::macros::conversions::decl_cross_width_widening!(wide D153, crate::wide_int::I512, D38, i128);
928#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d76", feature = "wide")))]
929crate::macros::conversions::decl_cross_width_widening!(wide D153, crate::wide_int::I512, D76, crate::wide_int::I256);
930// Cross-width narrowing from D153 (fallible): D153 -> D76 / D38.
931#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d76", feature = "wide")))]
932crate::macros::conversions::decl_cross_width_narrowing!(wide D76, crate::wide_int::I256, D153, crate::wide_int::I512);
933#[cfg(any(feature = "d153", feature = "wide"))]
934crate::macros::conversions::decl_cross_width_narrowing!(wide D38, i128, D153, crate::wide_int::I512);
935
936// ─── D76::widen / D153 hop methods ────────────────────────────────────
937
938#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d115", feature = "wide")))]
939impl<const SCALE: u32> D76<SCALE> {
940    /// Promote to the next storage tier ([`D115`]) at the same
941    /// `SCALE`. Lossless.
942    #[inline]
943    #[must_use]
944    pub fn widen(self) -> D115<SCALE> {
945        self.into()
946    }
947}
948
949#[cfg(any(feature = "d153", feature = "wide"))]
950impl<const SCALE: u32> D153<SCALE> {
951    /// Demote to the previous storage tier ([`D115`]) at the same
952    /// `SCALE`. Returns `Err(ConvertError::Overflow)` if the value
953    /// doesn't fit the narrower storage's range at the given scale.
954    #[cfg(any(feature = "d115", feature = "wide"))]
955    #[inline]
956    pub fn narrow(self) -> Result<D115<SCALE>, crate::support::error::ConvertError> {
957        self.try_into()
958    }
959}
960
961/// Scale alias: `D153<0>`. 1 LSB = 1 (512-bit integer ledger).
962#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s0   = D153<0>;
963#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s1   = D153<1>;
964#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s2   = D153<2>;
965#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s4   = D153<4>;
966#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s6   = D153<6>;
967#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s9   = D153<9>;
968#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s12  = D153<12>;
969#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s15  = D153<15>;
970#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s18  = D153<18>;
971#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s20  = D153<20>;
972#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s24  = D153<24>;
973#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s28  = D153<28>;
974#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s32  = D153<32>;
975/// Scale alias: `D153<35>`. 1 LSB = 10^-35 (matches D38 `SCALE_REF`).
976#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s35  = D153<35>;
977#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s38  = D153<38>;
978#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s50  = D153<50>;
979#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s57  = D153<57>;
980/// Scale alias: `D153<75>`. 1 LSB = 10^-75 (wide-scientific midpoint).
981#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s75  = D153<75>;
982#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s76  = D153<76>;
983#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s100 = D153<100>;
984#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s115 = D153<115>;
985#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s140 = D153<140>;
986/// Scale alias: `D153<150>`. 1 LSB = 10^-150.
987#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s150 = D153<150>;
988/// Scale alias: `D153<152>`. 1 LSB = 10^-152. Maximum supported scale
989/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
990#[cfg(any(feature = "d153", feature = "wide"))] pub type D153s152 = D153<152>;
991
992// ---------------------------------------------------------------------
993// D307 — 1024-bit storage (`Int1024`), scale 0..=307. Deep
994// arbitrary-precision tier; gated behind the `d307` / `wide` features.
995// ---------------------------------------------------------------------
996
997/// Scaled fixed-point decimal with 1024-bit storage. See [`D38`] for
998/// the shape documentation; D307 has the same surface scaled to a
999/// 1024-bit signed integer and `MAX_SCALE = 307`. Now a type alias of
1000/// the unified [`crate::D`] generic decimal type: `D307<S>` is
1001/// `D<crate::wide_int::Int1024, S>`. Both spellings are interchangeable.
1002///
1003/// The `#[repr(transparent)]` layout over `Int1024` is preserved through
1004/// the alias because the underlying [`crate::D`] is itself
1005/// `#[repr(transparent)]` over its storage parameter.
1006///
1007/// Gated behind the `d307` (or umbrella `wide`) Cargo feature. The
1008/// storage backend is `Int1024`.
1009#[cfg(any(feature = "d307", feature = "wide"))]
1010pub type D307<const SCALE: u32> = crate::D<crate::wide_int::Int1024, SCALE>;
1011
1012/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1013/// `Int1024`.
1014///
1015/// Implemented on the underlying `crate::D<crate::wide_int::Int1024, SCALE>`
1016/// because `D307<SCALE>` is now an alias of that type. `ZERO` is emitted
1017/// by the basics macro further down in this file.
1018#[cfg(any(feature = "d307", feature = "wide"))]
1019impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int1024, SCALE> {
1020    #[inline]
1021    fn default() -> Self {
1022        Self::ZERO
1023    }
1024}
1025
1026#[cfg(any(feature = "d307", feature = "wide"))]
1027crate::macros::full::decl_decimal_full!(
1028    wide D307,
1029    crate::wide_int::I1024,
1030    crate::wide_int::U1024,
1031    crate::wide_int::I2048,
1032    crate::wide_int::Int2048,
1033    crate::wide_int::Int4096,
1034    crate::wide_int::Int4096,
1035    wide_trig_d307,
1036    306
1037);
1038// Cross-width widening into D307 (lossless): D76 / D153 -> D307.
1039#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d76", feature = "wide")))]
1040crate::macros::conversions::decl_cross_width_widening!(wide D307, crate::wide_int::I1024, D76, crate::wide_int::I256);
1041#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d153", feature = "wide")))]
1042crate::macros::conversions::decl_cross_width_widening!(wide D307, crate::wide_int::I1024, D153, crate::wide_int::I512);
1043// Cross-width narrowing from D307 (fallible): D307 -> D153 / D76.
1044#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d153", feature = "wide")))]
1045crate::macros::conversions::decl_cross_width_narrowing!(wide D153, crate::wide_int::I512, D307, crate::wide_int::I1024);
1046#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d76", feature = "wide")))]
1047crate::macros::conversions::decl_cross_width_narrowing!(wide D76, crate::wide_int::I256, D307, crate::wide_int::I1024);
1048
1049// ─── D153::widen / D307 hop methods ───────────────────────────────────
1050
1051#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d230", feature = "wide")))]
1052impl<const SCALE: u32> D153<SCALE> {
1053    /// Promote to the next storage tier ([`D230`]) at the same
1054    /// `SCALE`. Lossless.
1055    #[inline]
1056    #[must_use]
1057    pub fn widen(self) -> D230<SCALE> {
1058        self.into()
1059    }
1060}
1061
1062#[cfg(any(feature = "d307", feature = "wide"))]
1063impl<const SCALE: u32> D307<SCALE> {
1064    /// Demote to the previous storage tier ([`D230`]) at the same
1065    /// `SCALE`. Returns `Err(ConvertError::Overflow)` if the value
1066    /// doesn't fit the narrower storage's range at the given scale.
1067    #[cfg(any(feature = "d230", feature = "wide"))]
1068    #[inline]
1069    pub fn narrow(self) -> Result<D230<SCALE>, crate::support::error::ConvertError> {
1070        self.try_into()
1071    }
1072
1073    /// Promote to the next storage tier ([`D462`]) at the same
1074    /// `SCALE`. Lossless. Requires `d462` / `x-wide`.
1075    #[cfg(any(feature = "d462", feature = "x-wide"))]
1076    #[inline]
1077    #[must_use]
1078    pub fn widen(self) -> D462<SCALE> {
1079        self.into()
1080    }
1081}
1082
1083/// Scale alias: `D307<0>`. 1 LSB = 1 (1024-bit integer ledger).
1084#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s0   = D307<0>;
1085#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s1   = D307<1>;
1086#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s2   = D307<2>;
1087#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s4   = D307<4>;
1088#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s6   = D307<6>;
1089#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s9   = D307<9>;
1090#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s12  = D307<12>;
1091#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s15  = D307<15>;
1092#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s18  = D307<18>;
1093#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s20  = D307<20>;
1094#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s24  = D307<24>;
1095#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s28  = D307<28>;
1096#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s32  = D307<32>;
1097/// Scale alias: `D307<35>`. 1 LSB = 10^-35 (matches D38 `SCALE_REF`).
1098#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s35  = D307<35>;
1099#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s38  = D307<38>;
1100#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s50  = D307<50>;
1101#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s75  = D307<75>;
1102#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s100 = D307<100>;
1103#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s115 = D307<115>;
1104/// Scale alias: `D307<150>`. 1 LSB = 10^-150.
1105#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s150 = D307<150>;
1106#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s153 = D307<153>;
1107#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s200 = D307<200>;
1108#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s230 = D307<230>;
1109#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s275 = D307<275>;
1110/// Scale alias: `D307<300>`. 1 LSB = 10^-300.
1111#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s300 = D307<300>;
1112/// Scale alias: `D307<306>`. 1 LSB = 10^-306. Maximum supported scale
1113/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1114#[cfg(any(feature = "d307", feature = "wide"))] pub type D307s306 = D307<306>;
1115
1116// ─── Half-width and wider tiers (D57 / D115 / D230 / D462 / D616 / D924 / D1232) ───
1117//
1118// These fill the (2^n + 2^(n+1))/2 gaps between the existing
1119// power-of-two storage tiers, plus extend the top end past D307.
1120// Each tier has the same surface as D76 / D153 / D307: full
1121// `decl_decimal_full!` emission (every arithmetic / transcendental
1122// method), plus scale aliases at 0 / mid / max.
1123//
1124// Cross-width widening / narrowing methods are emitted to the
1125// immediate-neighbour tiers only — `D57 ↔ D38`, `D57 ↔ D76`, etc.
1126// Multi-tier hops go via the chain (e.g. D57 → D76 → D153) at the
1127// cost of one intermediate.
1128
1129// ── D57 (192-bit / 3 u64 limbs) ────────────────────────────────────────
1130
1131/// Scaled fixed-point decimal with 192-bit storage. Half-width tier
1132/// between D38 and D76 — useful when the D38 i128 ceiling is in
1133/// reach but D76's 256-bit storage is wasteful. Now a type alias of
1134/// the unified [`crate::D`] generic decimal type: `D57<S>` is
1135/// `D<crate::wide_int::Int192, S>`. Both spellings are interchangeable.
1136///
1137/// The `#[repr(transparent)]` layout over `Int192` is preserved
1138/// through the alias because the underlying [`crate::D`] is itself
1139/// `#[repr(transparent)]` over its storage parameter.
1140///
1141/// Gated behind the `d57` (or umbrella `wide`) Cargo feature.
1142#[cfg(any(feature = "d57", feature = "wide"))]
1143pub type D57<const SCALE: u32> = crate::D<crate::wide_int::Int192, SCALE>;
1144
1145/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1146/// `Int192`.
1147///
1148/// Implemented on the underlying `crate::D<crate::wide_int::Int192, SCALE>`
1149/// because `D57<SCALE>` is now an alias of that type. `ZERO` is emitted
1150/// by the basics macro further down in this file.
1151#[cfg(any(feature = "d57", feature = "wide"))]
1152impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int192, SCALE> {
1153    #[inline]
1154    fn default() -> Self { Self::ZERO }
1155}
1156
1157#[cfg(any(feature = "d57", feature = "wide"))]
1158crate::macros::full::decl_decimal_full!(
1159    wide D57,
1160    crate::wide_int::I192,
1161    crate::wide_int::U192,
1162    crate::wide_int::I384,
1163    crate::wide_int::Int384,
1164    crate::wide_int::Int512,
1165    crate::wide_int::Int1024,
1166    wide_trig_d57,
1167    56
1168);
1169#[cfg(any(feature = "d57", feature = "wide"))]
1170pub type D57s0 = D57<0>;
1171#[cfg(any(feature = "d57", feature = "wide"))]
1172pub type D57s1 = D57<1>;
1173#[cfg(any(feature = "d57", feature = "wide"))]
1174pub type D57s2 = D57<2>;
1175#[cfg(any(feature = "d57", feature = "wide"))]
1176pub type D57s4 = D57<4>;
1177#[cfg(any(feature = "d57", feature = "wide"))]
1178pub type D57s6 = D57<6>;
1179#[cfg(any(feature = "d57", feature = "wide"))]
1180pub type D57s9 = D57<9>;
1181#[cfg(any(feature = "d57", feature = "wide"))]
1182pub type D57s12 = D57<12>;
1183#[cfg(any(feature = "d57", feature = "wide"))]
1184pub type D57s18 = D57<18>;
1185#[cfg(any(feature = "d57", feature = "wide"))]
1186pub type D57s20 = D57<20>;
1187#[cfg(any(feature = "d57", feature = "wide"))]
1188pub type D57s24 = D57<24>;
1189#[cfg(any(feature = "d57", feature = "wide"))]
1190pub type D57s28 = D57<28>;
1191#[cfg(any(feature = "d57", feature = "wide"))]
1192pub type D57s32 = D57<32>;
1193#[cfg(any(feature = "d57", feature = "wide"))]
1194pub type D57s38 = D57<38>;
1195#[cfg(any(feature = "d57", feature = "wide"))]
1196pub type D57s42 = D57<42>;
1197#[cfg(any(feature = "d57", feature = "wide"))]
1198pub type D57s48 = D57<48>;
1199#[cfg(any(feature = "d57", feature = "wide"))]
1200pub type D57s52 = D57<52>;
1201/// Scale alias: `D57<56>`. 1 LSB = 10^-56. Maximum supported scale
1202/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1203#[cfg(any(feature = "d57", feature = "wide"))]
1204pub type D57s56 = D57<56>;
1205
1206// ── D115 (384-bit / 6 u64 limbs) ───────────────────────────────────────
1207
1208/// Scaled fixed-point decimal with 384-bit storage. Half-width tier
1209/// between D76 and D153. Now a type alias of the unified [`crate::D`]
1210/// generic decimal type: `D115<S>` is `D<crate::wide_int::Int384, S>`.
1211/// Both spellings are interchangeable.
1212///
1213/// The `#[repr(transparent)]` layout over `Int384` is preserved through
1214/// the alias because the underlying [`crate::D`] is itself
1215/// `#[repr(transparent)]` over its storage parameter.
1216///
1217/// Gated behind the `d115` (or umbrella `wide`) Cargo feature.
1218#[cfg(any(feature = "d115", feature = "wide"))]
1219pub type D115<const SCALE: u32> = crate::D<crate::wide_int::Int384, SCALE>;
1220
1221/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1222/// `Int384`.
1223///
1224/// Implemented on the underlying `crate::D<crate::wide_int::Int384, SCALE>`
1225/// because `D115<SCALE>` is now an alias of that type. `ZERO` is emitted
1226/// by the basics macro further down in this file.
1227#[cfg(any(feature = "d115", feature = "wide"))]
1228impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int384, SCALE> {
1229    #[inline]
1230    fn default() -> Self { Self::ZERO }
1231}
1232
1233#[cfg(any(feature = "d115", feature = "wide"))]
1234crate::macros::full::decl_decimal_full!(
1235    wide D115,
1236    crate::wide_int::I384,
1237    crate::wide_int::U384,
1238    crate::wide_int::I768,
1239    crate::wide_int::Int768,
1240    crate::wide_int::Int1024,
1241    crate::wide_int::Int2048,
1242    wide_trig_d115,
1243    114
1244);
1245#[cfg(any(feature = "d115", feature = "wide"))]
1246pub type D115s0 = D115<0>;
1247#[cfg(any(feature = "d115", feature = "wide"))]
1248pub type D115s1 = D115<1>;
1249#[cfg(any(feature = "d115", feature = "wide"))]
1250pub type D115s4 = D115<4>;
1251#[cfg(any(feature = "d115", feature = "wide"))]
1252pub type D115s8 = D115<8>;
1253#[cfg(any(feature = "d115", feature = "wide"))]
1254pub type D115s16 = D115<16>;
1255#[cfg(any(feature = "d115", feature = "wide"))]
1256pub type D115s24 = D115<24>;
1257#[cfg(any(feature = "d115", feature = "wide"))]
1258pub type D115s32 = D115<32>;
1259#[cfg(any(feature = "d115", feature = "wide"))]
1260pub type D115s38 = D115<38>;
1261#[cfg(any(feature = "d115", feature = "wide"))]
1262pub type D115s50 = D115<50>;
1263#[cfg(any(feature = "d115", feature = "wide"))]
1264pub type D115s57 = D115<57>;
1265#[cfg(any(feature = "d115", feature = "wide"))]
1266pub type D115s64 = D115<64>;
1267#[cfg(any(feature = "d115", feature = "wide"))]
1268pub type D115s76 = D115<76>;
1269#[cfg(any(feature = "d115", feature = "wide"))]
1270pub type D115s90 = D115<90>;
1271#[cfg(any(feature = "d115", feature = "wide"))]
1272pub type D115s100 = D115<100>;
1273#[cfg(any(feature = "d115", feature = "wide"))]
1274pub type D115s110 = D115<110>;
1275/// Scale alias: `D115<114>`. 1 LSB = 10^-114. Maximum supported scale
1276/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1277#[cfg(any(feature = "d115", feature = "wide"))]
1278pub type D115s114 = D115<114>;
1279
1280// ── D230 (768-bit / 12 u64 limbs) ──────────────────────────────────────
1281
1282/// Scaled fixed-point decimal with 768-bit storage. Half-width tier
1283/// between D153 and D307. Now a type alias of the unified [`crate::D`]
1284/// generic decimal type: `D230<S>` is `D<crate::wide_int::Int768, S>`.
1285/// Both spellings are interchangeable.
1286///
1287/// The `#[repr(transparent)]` layout over `Int768` is preserved through
1288/// the alias because the underlying [`crate::D`] is itself
1289/// `#[repr(transparent)]` over its storage parameter.
1290///
1291/// Gated behind the `d230` (or umbrella `wide`) Cargo feature.
1292#[cfg(any(feature = "d230", feature = "wide"))]
1293pub type D230<const SCALE: u32> = crate::D<crate::wide_int::Int768, SCALE>;
1294
1295/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1296/// `Int768`.
1297///
1298/// Implemented on the underlying `crate::D<crate::wide_int::Int768, SCALE>`
1299/// because `D230<SCALE>` is now an alias of that type. `ZERO` is emitted
1300/// by the basics macro further down in this file.
1301#[cfg(any(feature = "d230", feature = "wide"))]
1302impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int768, SCALE> {
1303    #[inline]
1304    fn default() -> Self { Self::ZERO }
1305}
1306
1307#[cfg(any(feature = "d230", feature = "wide"))]
1308crate::macros::full::decl_decimal_full!(
1309    wide D230,
1310    crate::wide_int::I768,
1311    crate::wide_int::U768,
1312    crate::wide_int::I1536,
1313    crate::wide_int::Int1536,
1314    crate::wide_int::Int3072,
1315    crate::wide_int::Int3072,
1316    wide_trig_d230,
1317    229
1318);
1319#[cfg(any(feature = "d230", feature = "wide"))]
1320pub type D230s0 = D230<0>;
1321#[cfg(any(feature = "d230", feature = "wide"))]
1322pub type D230s1 = D230<1>;
1323#[cfg(any(feature = "d230", feature = "wide"))]
1324pub type D230s6 = D230<6>;
1325#[cfg(any(feature = "d230", feature = "wide"))]
1326pub type D230s18 = D230<18>;
1327#[cfg(any(feature = "d230", feature = "wide"))]
1328pub type D230s38 = D230<38>;
1329#[cfg(any(feature = "d230", feature = "wide"))]
1330pub type D230s57 = D230<57>;
1331#[cfg(any(feature = "d230", feature = "wide"))]
1332pub type D230s75 = D230<75>;
1333#[cfg(any(feature = "d230", feature = "wide"))]
1334pub type D230s100 = D230<100>;
1335#[cfg(any(feature = "d230", feature = "wide"))]
1336pub type D230s115 = D230<115>;
1337#[cfg(any(feature = "d230", feature = "wide"))]
1338pub type D230s140 = D230<140>;
1339#[cfg(any(feature = "d230", feature = "wide"))]
1340pub type D230s153 = D230<153>;
1341#[cfg(any(feature = "d230", feature = "wide"))]
1342pub type D230s175 = D230<175>;
1343#[cfg(any(feature = "d230", feature = "wide"))]
1344pub type D230s200 = D230<200>;
1345#[cfg(any(feature = "d230", feature = "wide"))]
1346pub type D230s215 = D230<215>;
1347#[cfg(any(feature = "d230", feature = "wide"))]
1348pub type D230s225 = D230<225>;
1349/// Scale alias: `D230<229>`. 1 LSB = 10^-229. Maximum supported scale
1350/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1351#[cfg(any(feature = "d230", feature = "wide"))]
1352pub type D230s229 = D230<229>;
1353
1354// ── D462 (1536-bit / 24 u64 limbs) ─────────────────────────────────────
1355
1356/// Scaled fixed-point decimal with 1536-bit storage. Half-width tier
1357/// between D307 and D616. Now a type alias of the unified [`crate::D`]
1358/// generic decimal type: `D462<S>` is `D<crate::wide_int::Int1536, S>`.
1359/// Both spellings are interchangeable.
1360///
1361/// The `#[repr(transparent)]` layout over `Int1536` is preserved through
1362/// the alias because the underlying [`crate::D`] is itself
1363/// `#[repr(transparent)]` over its storage parameter.
1364///
1365/// Gated behind the `d462` (or umbrella `x-wide`) Cargo feature.
1366#[cfg(any(feature = "d462", feature = "x-wide"))]
1367pub type D462<const SCALE: u32> = crate::D<crate::wide_int::Int1536, SCALE>;
1368
1369/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1370/// `Int1536`.
1371///
1372/// Implemented on the underlying `crate::D<crate::wide_int::Int1536, SCALE>`
1373/// because `D462<SCALE>` is now an alias of that type. `ZERO` is emitted
1374/// by the basics macro further down in this file.
1375#[cfg(any(feature = "d462", feature = "x-wide"))]
1376impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int1536, SCALE> {
1377    #[inline]
1378    fn default() -> Self { Self::ZERO }
1379}
1380
1381#[cfg(any(feature = "d462", feature = "x-wide"))]
1382crate::macros::full::decl_decimal_full!(
1383    wide D462,
1384    crate::wide_int::I1536,
1385    crate::wide_int::U1536,
1386    crate::wide_int::I3072,
1387    crate::wide_int::Int3072,
1388    crate::wide_int::Int4096,
1389    crate::wide_int::Int4096,
1390    wide_trig_d462,
1391    461
1392);
1393#[cfg(any(feature = "d462", feature = "x-wide"))]
1394pub type D462s0 = D462<0>;
1395#[cfg(any(feature = "d462", feature = "x-wide"))]
1396pub type D462s1 = D462<1>;
1397#[cfg(any(feature = "d462", feature = "x-wide"))]
1398pub type D462s18 = D462<18>;
1399#[cfg(any(feature = "d462", feature = "x-wide"))]
1400pub type D462s38 = D462<38>;
1401#[cfg(any(feature = "d462", feature = "x-wide"))]
1402pub type D462s75 = D462<75>;
1403#[cfg(any(feature = "d462", feature = "x-wide"))]
1404pub type D462s115 = D462<115>;
1405#[cfg(any(feature = "d462", feature = "x-wide"))]
1406pub type D462s153 = D462<153>;
1407#[cfg(any(feature = "d462", feature = "x-wide"))]
1408pub type D462s200 = D462<200>;
1409#[cfg(any(feature = "d462", feature = "x-wide"))]
1410pub type D462s230 = D462<230>;
1411#[cfg(any(feature = "d462", feature = "x-wide"))]
1412pub type D462s275 = D462<275>;
1413#[cfg(any(feature = "d462", feature = "x-wide"))]
1414pub type D462s307 = D462<307>;
1415#[cfg(any(feature = "d462", feature = "x-wide"))]
1416pub type D462s350 = D462<350>;
1417#[cfg(any(feature = "d462", feature = "x-wide"))]
1418pub type D462s400 = D462<400>;
1419#[cfg(any(feature = "d462", feature = "x-wide"))]
1420pub type D462s440 = D462<440>;
1421#[cfg(any(feature = "d462", feature = "x-wide"))]
1422pub type D462s460 = D462<460>;
1423/// Scale alias: `D462<461>`. 1 LSB = 10^-461. Maximum supported scale
1424/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1425#[cfg(any(feature = "d462", feature = "x-wide"))]
1426pub type D462s461 = D462<461>;
1427
1428// ── D616 (2048-bit / 32 u64 limbs) ─────────────────────────────────────
1429
1430/// Scaled fixed-point decimal with 2048-bit storage. New top tier
1431/// beyond D307; supports correctly-rounded transcendentals at scale
1432/// up to 616 decimal digits. Now a type alias of the unified
1433/// [`crate::D`] generic decimal type: `D616<S>` is
1434/// `D<crate::wide_int::Int2048, S>`. Both spellings are interchangeable.
1435///
1436/// The `#[repr(transparent)]` layout over `Int2048` is preserved through
1437/// the alias because the underlying [`crate::D`] is itself
1438/// `#[repr(transparent)]` over its storage parameter.
1439///
1440/// Gated behind the `d616` (or umbrella `x-wide`) Cargo feature.
1441#[cfg(any(feature = "d616", feature = "x-wide"))]
1442pub type D616<const SCALE: u32> = crate::D<crate::wide_int::Int2048, SCALE>;
1443
1444/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1445/// `Int2048`.
1446///
1447/// Implemented on the underlying `crate::D<crate::wide_int::Int2048, SCALE>`
1448/// because `D616<SCALE>` is now an alias of that type. `ZERO` is emitted
1449/// by the basics macro further down in this file.
1450#[cfg(any(feature = "d616", feature = "x-wide"))]
1451impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int2048, SCALE> {
1452    #[inline]
1453    fn default() -> Self { Self::ZERO }
1454}
1455
1456#[cfg(any(feature = "d616", feature = "x-wide"))]
1457crate::macros::full::decl_decimal_full!(
1458    wide D616,
1459    crate::wide_int::I2048,
1460    crate::wide_int::U2048,
1461    crate::wide_int::I4096,
1462    crate::wide_int::Int4096,
1463    crate::wide_int::Int8192,
1464    crate::wide_int::Int8192,
1465    wide_trig_d616,
1466    615
1467);
1468#[cfg(any(feature = "d616", feature = "x-wide"))]
1469pub type D616s0 = D616<0>;
1470#[cfg(any(feature = "d616", feature = "x-wide"))]
1471pub type D616s1 = D616<1>;
1472#[cfg(any(feature = "d616", feature = "x-wide"))]
1473pub type D616s38 = D616<38>;
1474#[cfg(any(feature = "d616", feature = "x-wide"))]
1475pub type D616s75 = D616<75>;
1476#[cfg(any(feature = "d616", feature = "x-wide"))]
1477pub type D616s115 = D616<115>;
1478#[cfg(any(feature = "d616", feature = "x-wide"))]
1479pub type D616s153 = D616<153>;
1480#[cfg(any(feature = "d616", feature = "x-wide"))]
1481pub type D616s200 = D616<200>;
1482#[cfg(any(feature = "d616", feature = "x-wide"))]
1483pub type D616s230 = D616<230>;
1484#[cfg(any(feature = "d616", feature = "x-wide"))]
1485pub type D616s275 = D616<275>;
1486#[cfg(any(feature = "d616", feature = "x-wide"))]
1487pub type D616s308 = D616<308>;
1488#[cfg(any(feature = "d616", feature = "x-wide"))]
1489pub type D616s380 = D616<380>;
1490#[cfg(any(feature = "d616", feature = "x-wide"))]
1491pub type D616s462 = D616<462>;
1492#[cfg(any(feature = "d616", feature = "x-wide"))]
1493pub type D616s500 = D616<500>;
1494#[cfg(any(feature = "d616", feature = "x-wide"))]
1495pub type D616s555 = D616<555>;
1496#[cfg(any(feature = "d616", feature = "x-wide"))]
1497pub type D616s600 = D616<600>;
1498/// Scale alias: `D616<615>`. 1 LSB = 10^-615. Maximum supported scale
1499/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1500#[cfg(any(feature = "d616", feature = "x-wide"))]
1501pub type D616s615 = D616<615>;
1502
1503// ── D924 (3072-bit / 48 u64 limbs) ─────────────────────────────────────
1504
1505/// Scaled fixed-point decimal with 3072-bit storage. Half-width tier
1506/// between D616 and D1232; supports SCALE up to 924 digits. Now a type
1507/// alias of the unified [`crate::D`] generic decimal type: `D924<S>`
1508/// is `D<crate::wide_int::Int3072, S>`. Both spellings are interchangeable.
1509///
1510/// The `#[repr(transparent)]` layout over `Int3072` is preserved through
1511/// the alias because the underlying [`crate::D`] is itself
1512/// `#[repr(transparent)]` over its storage parameter.
1513///
1514/// Gated behind the `d924` (or umbrella `xx-wide`) Cargo feature.
1515#[cfg(any(feature = "d924", feature = "xx-wide"))]
1516pub type D924<const SCALE: u32> = crate::D<crate::wide_int::Int3072, SCALE>;
1517
1518/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1519/// `Int3072`.
1520///
1521/// Implemented on the underlying `crate::D<crate::wide_int::Int3072, SCALE>`
1522/// because `D924<SCALE>` is now an alias of that type. `ZERO` is emitted
1523/// by the basics macro further down in this file.
1524#[cfg(any(feature = "d924", feature = "xx-wide"))]
1525impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int3072, SCALE> {
1526    #[inline]
1527    fn default() -> Self { Self::ZERO }
1528}
1529
1530#[cfg(any(feature = "d924", feature = "xx-wide"))]
1531// `no_const_table`: 953-entry `Int12288` POW10_TABLE build exceeds
1532// the stable-rust const-eval step budget. Stays on the per-thread
1533// `Vec<(u32, W)>` cache.
1534crate::macros::full::decl_decimal_full!(
1535    wide D924,
1536    crate::wide_int::I3072,
1537    crate::wide_int::U3072,
1538    crate::wide_int::I6144,
1539    crate::wide_int::Int6144,
1540    crate::wide_int::Int12288,
1541    crate::wide_int::Int12288,
1542    wide_trig_d924,
1543    923,
1544    no_const_table
1545);
1546#[cfg(any(feature = "d924", feature = "xx-wide"))]
1547pub type D924s0 = D924<0>;
1548#[cfg(any(feature = "d924", feature = "xx-wide"))]
1549pub type D924s1 = D924<1>;
1550#[cfg(any(feature = "d924", feature = "xx-wide"))]
1551pub type D924s75 = D924<75>;
1552#[cfg(any(feature = "d924", feature = "xx-wide"))]
1553pub type D924s153 = D924<153>;
1554#[cfg(any(feature = "d924", feature = "xx-wide"))]
1555pub type D924s230 = D924<230>;
1556#[cfg(any(feature = "d924", feature = "xx-wide"))]
1557pub type D924s307 = D924<307>;
1558#[cfg(any(feature = "d924", feature = "xx-wide"))]
1559pub type D924s400 = D924<400>;
1560#[cfg(any(feature = "d924", feature = "xx-wide"))]
1561pub type D924s461 = D924<461>;
1562#[cfg(any(feature = "d924", feature = "xx-wide"))]
1563pub type D924s462 = D924<462>;
1564#[cfg(any(feature = "d924", feature = "xx-wide"))]
1565pub type D924s500 = D924<500>;
1566#[cfg(any(feature = "d924", feature = "xx-wide"))]
1567pub type D924s616 = D924<616>;
1568#[cfg(any(feature = "d924", feature = "xx-wide"))]
1569pub type D924s700 = D924<700>;
1570#[cfg(any(feature = "d924", feature = "xx-wide"))]
1571pub type D924s800 = D924<800>;
1572#[cfg(any(feature = "d924", feature = "xx-wide"))]
1573pub type D924s860 = D924<860>;
1574#[cfg(any(feature = "d924", feature = "xx-wide"))]
1575pub type D924s900 = D924<900>;
1576#[cfg(any(feature = "d924", feature = "xx-wide"))]
1577pub type D924s920 = D924<920>;
1578/// Scale alias: `D924<923>`. 1 LSB = 10^-923. Maximum supported scale
1579/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1580#[cfg(any(feature = "d924", feature = "xx-wide"))]
1581pub type D924s923 = D924<923>;
1582
1583// ── D1232 (4096-bit / 64 u64 limbs) ────────────────────────────────────
1584
1585/// Scaled fixed-point decimal with 4096-bit storage. Widest tier
1586/// shipped; supports SCALE up to 1232 digits. Now a type alias of the
1587/// unified [`crate::D`] generic decimal type: `D1232<S>` is
1588/// `D<crate::wide_int::Int4096, S>`. Both spellings are interchangeable.
1589///
1590/// The `#[repr(transparent)]` layout over `Int4096` is preserved through
1591/// the alias because the underlying [`crate::D`] is itself
1592/// `#[repr(transparent)]` over its storage parameter.
1593///
1594/// Gated behind the `d1232` (or umbrella `xx-wide`) Cargo feature.
1595#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1596pub type D1232<const SCALE: u32> = crate::D<crate::wide_int::Int4096, SCALE>;
1597
1598/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1599/// `Int4096`.
1600///
1601/// Implemented on the underlying `crate::D<crate::wide_int::Int4096, SCALE>`
1602/// because `D1232<SCALE>` is now an alias of that type. `ZERO` is emitted
1603/// by the basics macro further down in this file.
1604#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1605impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int4096, SCALE> {
1606    #[inline]
1607    fn default() -> Self { Self::ZERO }
1608}
1609
1610#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1611// `no_const_table`: 1262-entry `Int16384` POW10_TABLE build exceeds
1612// the stable-rust const-eval step budget. Stays on the per-thread
1613// `Vec<(u32, W)>` cache.
1614crate::macros::full::decl_decimal_full!(
1615    wide D1232,
1616    crate::wide_int::I4096,
1617    crate::wide_int::U4096,
1618    crate::wide_int::I8192,
1619    crate::wide_int::Int8192,
1620    crate::wide_int::Int16384,
1621    crate::wide_int::Int16384,
1622    wide_trig_d1232,
1623    1231,
1624    no_const_table
1625);
1626#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1627pub type D1232s0 = D1232<0>;
1628#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1629pub type D1232s1 = D1232<1>;
1630#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1631pub type D1232s75 = D1232<75>;
1632#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1633pub type D1232s153 = D1232<153>;
1634#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1635pub type D1232s230 = D1232<230>;
1636#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1637pub type D1232s307 = D1232<307>;
1638#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1639pub type D1232s461 = D1232<461>;
1640#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1641pub type D1232s616 = D1232<616>;
1642#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1643pub type D1232s700 = D1232<700>;
1644#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1645pub type D1232s800 = D1232<800>;
1646#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1647pub type D1232s900 = D1232<900>;
1648#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1649pub type D1232s924 = D1232<924>;
1650#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1651pub type D1232s1000 = D1232<1000>;
1652#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1653pub type D1232s1100 = D1232<1100>;
1654#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1655pub type D1232s1180 = D1232<1180>;
1656#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1657pub type D1232s1220 = D1232<1220>;
1658#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1659pub type D1232s1230 = D1232<1230>;
1660/// Scale alias: `D1232<1231>`. 1 LSB = 10^-1231. Maximum supported scale
1661/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1662#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1663pub type D1232s1231 = D1232<1231>;
1664
1665// ─── Cross-tier next-neighbour widen/narrow chain ─────────────────────
1666//
1667// The historical .widen() / .narrow() methods on D38/D76/D153/D307
1668// follow the power-of-two storage sequence (D38→D76→D153→D307). The
1669// 0.2.6 tier ladder fills in half-widths between each pair plus
1670// extends to D1232; the complete ladder is:
1671//
1672//   D9 → D18 → D38 → D57 → D76 → D115 → D153 → D230 → D307 →
1673//   D462 → D616 → D924 → D1232
1674//
1675// The next-neighbour .widen() / .narrow() methods on the new tiers go
1676// to the immediate adjacent rung (D57.widen() → D76, D76.widen()
1677// already returns D153 which is the existing power-of-two next-up,
1678// etc.). The cross-tier From / TryFrom impls below cover the
1679// neighbour pairs that weren't already declared by the legacy
1680// D38/D76/D153/D307 blocks.
1681//
1682// Coverage strategy: declare every NEW adjacent pair both ways. The
1683// existing legacy declarations (D9↔D18, D9/D18/D38↔D76, D38/D76↔D153,
1684// D76/D153↔D307) stay where they are; this block adds the conversions
1685// that hop through the new tiers (D38↔D57, D57↔D76, D76↔D115, etc.).
1686
1687// D38 ↔ D57
1688#[cfg(any(feature = "d57", feature = "wide"))]
1689crate::macros::conversions::decl_cross_width_widening!(wide D57, crate::wide_int::I192, D38, i128);
1690#[cfg(any(feature = "d57", feature = "wide"))]
1691crate::macros::conversions::decl_cross_width_narrowing!(wide D38, i128, D57, crate::wide_int::I192);
1692
1693// D57 ↔ D76
1694#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d76", feature = "wide")))]
1695crate::macros::conversions::decl_cross_width_widening!(wide D76, crate::wide_int::I256, D57, crate::wide_int::I192);
1696#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d76", feature = "wide")))]
1697crate::macros::conversions::decl_cross_width_narrowing!(wide D57, crate::wide_int::I192, D76, crate::wide_int::I256);
1698
1699// D76 ↔ D115
1700#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d115", feature = "wide")))]
1701crate::macros::conversions::decl_cross_width_widening!(wide D115, crate::wide_int::I384, D76, crate::wide_int::I256);
1702#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d115", feature = "wide")))]
1703crate::macros::conversions::decl_cross_width_narrowing!(wide D76, crate::wide_int::I256, D115, crate::wide_int::I384);
1704
1705// D115 ↔ D153
1706#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d153", feature = "wide")))]
1707crate::macros::conversions::decl_cross_width_widening!(wide D153, crate::wide_int::I512, D115, crate::wide_int::I384);
1708#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d153", feature = "wide")))]
1709crate::macros::conversions::decl_cross_width_narrowing!(wide D115, crate::wide_int::I384, D153, crate::wide_int::I512);
1710
1711// D153 ↔ D230
1712#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d230", feature = "wide")))]
1713crate::macros::conversions::decl_cross_width_widening!(wide D230, crate::wide_int::I768, D153, crate::wide_int::I512);
1714#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d230", feature = "wide")))]
1715crate::macros::conversions::decl_cross_width_narrowing!(wide D153, crate::wide_int::I512, D230, crate::wide_int::I768);
1716
1717// D230 ↔ D307
1718#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d307", feature = "wide")))]
1719crate::macros::conversions::decl_cross_width_widening!(wide D307, crate::wide_int::I1024, D230, crate::wide_int::I768);
1720#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d307", feature = "wide")))]
1721crate::macros::conversions::decl_cross_width_narrowing!(wide D230, crate::wide_int::I768, D307, crate::wide_int::I1024);
1722
1723// D307 ↔ D462
1724#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
1725crate::macros::conversions::decl_cross_width_widening!(wide D462, crate::wide_int::I1536, D307, crate::wide_int::I1024);
1726#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
1727crate::macros::conversions::decl_cross_width_narrowing!(wide D307, crate::wide_int::I1024, D462, crate::wide_int::I1536);
1728
1729// D462 ↔ D616
1730#[cfg(all(any(feature = "d462", feature = "x-wide"), any(feature = "d616", feature = "x-wide")))]
1731crate::macros::conversions::decl_cross_width_widening!(wide D616, crate::wide_int::I2048, D462, crate::wide_int::I1536);
1732#[cfg(all(any(feature = "d462", feature = "x-wide"), any(feature = "d616", feature = "x-wide")))]
1733crate::macros::conversions::decl_cross_width_narrowing!(wide D462, crate::wide_int::I1536, D616, crate::wide_int::I2048);
1734
1735// D616 ↔ D924
1736#[cfg(all(any(feature = "d616", feature = "x-wide"), any(feature = "d924", feature = "xx-wide")))]
1737crate::macros::conversions::decl_cross_width_widening!(wide D924, crate::wide_int::I3072, D616, crate::wide_int::I2048);
1738#[cfg(all(any(feature = "d616", feature = "x-wide"), any(feature = "d924", feature = "xx-wide")))]
1739crate::macros::conversions::decl_cross_width_narrowing!(wide D616, crate::wide_int::I2048, D924, crate::wide_int::I3072);
1740
1741// D924 ↔ D1232
1742#[cfg(all(any(feature = "d924", feature = "xx-wide"), any(feature = "d1232", feature = "xx-wide")))]
1743crate::macros::conversions::decl_cross_width_widening!(wide D1232, crate::wide_int::I4096, D924, crate::wide_int::I3072);
1744#[cfg(all(any(feature = "d924", feature = "xx-wide"), any(feature = "d1232", feature = "xx-wide")))]
1745crate::macros::conversions::decl_cross_width_narrowing!(wide D924, crate::wide_int::I3072, D1232, crate::wide_int::I4096);
1746
1747// .widen() / .narrow() methods on the new tiers — each points at the
1748// IMMEDIATE neighbour in the comprehensive ladder above. The legacy
1749// .widen() / .narrow() on D38/D76/D153/D307 are unchanged (still go
1750// to the power-of-two next-up) for source compatibility; users who
1751// want to traverse through the half-widths should use the methods
1752// declared here, or the From / TryFrom impls directly.
1753
1754#[cfg(any(feature = "d57", feature = "wide"))]
1755impl<const SCALE: u32> D57<SCALE> {
1756    /// Demote to the immediate previous tier ([`D38`]) at the same `SCALE`.
1757    /// Returns `Err(ConvertError::Overflow)` if the value exceeds `i128` range.
1758    #[inline]
1759    pub fn narrow(self) -> Result<D38<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1760    /// Promote to the next storage tier ([`D76`]) at the same `SCALE`. Lossless.
1761    #[inline] #[must_use]
1762    pub fn widen(self) -> D76<SCALE> { self.into() }
1763}
1764
1765#[cfg(any(feature = "d115", feature = "wide"))]
1766impl<const SCALE: u32> D115<SCALE> {
1767    /// Demote to the immediate previous tier ([`D76`]) at the same `SCALE`.
1768    #[inline]
1769    pub fn narrow(self) -> Result<D76<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1770    /// Promote to the next storage tier ([`D153`]) at the same `SCALE`. Lossless.
1771    #[inline] #[must_use]
1772    pub fn widen(self) -> D153<SCALE> { self.into() }
1773}
1774
1775#[cfg(any(feature = "d230", feature = "wide"))]
1776impl<const SCALE: u32> D230<SCALE> {
1777    /// Demote to the immediate previous tier ([`D153`]) at the same `SCALE`.
1778    #[inline]
1779    pub fn narrow(self) -> Result<D153<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1780    /// Promote to the next storage tier ([`D307`]) at the same `SCALE`. Lossless.
1781    #[inline] #[must_use]
1782    pub fn widen(self) -> D307<SCALE> { self.into() }
1783}
1784
1785#[cfg(any(feature = "d462", feature = "x-wide"))]
1786impl<const SCALE: u32> D462<SCALE> {
1787    /// Demote to the immediate previous tier ([`D307`]) at the same `SCALE`.
1788    #[inline]
1789    pub fn narrow(self) -> Result<D307<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1790    /// Promote to the next storage tier ([`D616`]) at the same `SCALE`. Lossless.
1791    #[inline] #[must_use]
1792    pub fn widen(self) -> D616<SCALE> { self.into() }
1793}
1794
1795#[cfg(any(feature = "d616", feature = "x-wide"))]
1796impl<const SCALE: u32> D616<SCALE> {
1797    /// Demote to the immediate previous tier ([`D462`]) at the same `SCALE`.
1798    #[inline]
1799    pub fn narrow(self) -> Result<D462<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1800}
1801
1802// `widen` lives in a second impl gated on D924's feature — D616 can
1803// be enabled without xx-wide (docs.rs builds this case), in which
1804// case D924 doesn't exist as a type and the unconditional `widen`
1805// method above breaks the doc build.
1806#[cfg(all(
1807    any(feature = "d616", feature = "x-wide"),
1808    any(feature = "d924", feature = "xx-wide"),
1809))]
1810impl<const SCALE: u32> D616<SCALE> {
1811    /// Promote to the next storage tier ([`D924`]) at the same `SCALE`. Lossless.
1812    #[inline] #[must_use]
1813    pub fn widen(self) -> D924<SCALE> { self.into() }
1814}
1815
1816#[cfg(any(feature = "d924", feature = "xx-wide"))]
1817impl<const SCALE: u32> D924<SCALE> {
1818    /// Demote to the immediate previous tier ([`D616`]) at the same `SCALE`.
1819    #[inline]
1820    pub fn narrow(self) -> Result<D616<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1821    /// Promote to the next storage tier ([`D1232`]) at the same `SCALE`. Lossless.
1822    #[inline] #[must_use]
1823    pub fn widen(self) -> D1232<SCALE> { self.into() }
1824}
1825
1826#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1827impl<const SCALE: u32> D1232<SCALE> {
1828    /// Demote to the immediate previous tier ([`D924`]) at the same `SCALE`.
1829    /// D1232 is the widest shipped tier, so there is no `.widen()` method.
1830    #[inline]
1831    pub fn narrow(self) -> Result<D924<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1832}
1833
1834// ─── Cross-scale-op constructors + comparators ─────────────────────────
1835//
1836// One invocation per width emits `mul_of`, `add_of`, `sub_of`, `div_of`,
1837// `rem_of`, `max_of`, `min_of`, `clamp_of`, `cmp_of`, `eq_of`, `ne_of`,
1838// `lt_of`, `le_of`, `gt_of`, `ge_of` (plus the `_with(mode)` siblings
1839// for the constructors that involve a possibly-lossy rescale of inputs).
1840// Operands of any width ≤ Self's storage are accepted via the
1841// `WidthLE` bound; operands of any SCALE are accepted via the
1842// const-generic `S1` / `S2` parameters. See
1843// `crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops` for
1844// the body.
1845
1846crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D9, i32);
1847crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D18, i64);
1848crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D38, i128);
1849
1850#[cfg(any(feature = "d57", feature = "wide"))]
1851crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D57, crate::wide_int::Int192);
1852
1853#[cfg(any(feature = "d76", feature = "wide"))]
1854crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D76, crate::wide_int::Int256);
1855
1856#[cfg(any(feature = "d115", feature = "wide"))]
1857crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D115, crate::wide_int::Int384);
1858
1859#[cfg(any(feature = "d153", feature = "wide"))]
1860crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D153, crate::wide_int::Int512);
1861
1862#[cfg(any(feature = "d230", feature = "wide"))]
1863crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D230, crate::wide_int::Int768);
1864
1865#[cfg(any(feature = "d307", feature = "wide"))]
1866crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D307, crate::wide_int::Int1024);
1867
1868#[cfg(any(feature = "d462", feature = "x-wide"))]
1869crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D462, crate::wide_int::Int1536);
1870
1871#[cfg(any(feature = "d616", feature = "x-wide"))]
1872crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D616, crate::wide_int::Int2048);
1873
1874#[cfg(any(feature = "d924", feature = "xx-wide"))]
1875crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D924, crate::wide_int::Int3072);
1876
1877#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1878crate::macros::cross_scale_ops::decl_decimal_cross_scale_ops!(D1232, crate::wide_int::Int4096);
1879
1880// ─── Cross-width `==` / `<` / `>` operator overloads (same SCALE) ──────
1881//
1882// One invocation per (narrower, wider) width pair emits four impls:
1883// PartialEq + PartialOrd in both directions. With these in place,
1884// ordinary `a == b` / `a < b` work across widths at the same SCALE
1885// without an explicit `.widen()`.
1886//
1887// Pairs are organised by narrower-width row. Feature gates ensure the
1888// impl is only emitted when both types in the pair exist in the build.
1889
1890// D9 row.
1891crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D18, i64);
1892crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D38, i128);
1893#[cfg(any(feature = "d57", feature = "wide"))]
1894crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D57, crate::wide_int::Int192);
1895#[cfg(any(feature = "d76", feature = "wide"))]
1896crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D76, crate::wide_int::Int256);
1897#[cfg(any(feature = "d115", feature = "wide"))]
1898crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D115, crate::wide_int::Int384);
1899#[cfg(any(feature = "d153", feature = "wide"))]
1900crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D153, crate::wide_int::Int512);
1901#[cfg(any(feature = "d230", feature = "wide"))]
1902crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D230, crate::wide_int::Int768);
1903#[cfg(any(feature = "d307", feature = "wide"))]
1904crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D307, crate::wide_int::Int1024);
1905#[cfg(any(feature = "d462", feature = "x-wide"))]
1906crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D462, crate::wide_int::Int1536);
1907#[cfg(any(feature = "d616", feature = "x-wide"))]
1908crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D616, crate::wide_int::Int2048);
1909#[cfg(any(feature = "d924", feature = "xx-wide"))]
1910crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D924, crate::wide_int::Int3072);
1911#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1912crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D9, i32, D1232, crate::wide_int::Int4096);
1913
1914// D18 row.
1915crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D38, i128);
1916#[cfg(any(feature = "d57", feature = "wide"))]
1917crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D57, crate::wide_int::Int192);
1918#[cfg(any(feature = "d76", feature = "wide"))]
1919crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D76, crate::wide_int::Int256);
1920#[cfg(any(feature = "d115", feature = "wide"))]
1921crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D115, crate::wide_int::Int384);
1922#[cfg(any(feature = "d153", feature = "wide"))]
1923crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D153, crate::wide_int::Int512);
1924#[cfg(any(feature = "d230", feature = "wide"))]
1925crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D230, crate::wide_int::Int768);
1926#[cfg(any(feature = "d307", feature = "wide"))]
1927crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D307, crate::wide_int::Int1024);
1928#[cfg(any(feature = "d462", feature = "x-wide"))]
1929crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D462, crate::wide_int::Int1536);
1930#[cfg(any(feature = "d616", feature = "x-wide"))]
1931crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D616, crate::wide_int::Int2048);
1932#[cfg(any(feature = "d924", feature = "xx-wide"))]
1933crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D924, crate::wide_int::Int3072);
1934#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1935crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D18, i64, D1232, crate::wide_int::Int4096);
1936
1937// D38 row.
1938#[cfg(any(feature = "d57", feature = "wide"))]
1939crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D57, crate::wide_int::Int192);
1940#[cfg(any(feature = "d76", feature = "wide"))]
1941crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D76, crate::wide_int::Int256);
1942#[cfg(any(feature = "d115", feature = "wide"))]
1943crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D115, crate::wide_int::Int384);
1944#[cfg(any(feature = "d153", feature = "wide"))]
1945crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D153, crate::wide_int::Int512);
1946#[cfg(any(feature = "d230", feature = "wide"))]
1947crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D230, crate::wide_int::Int768);
1948#[cfg(any(feature = "d307", feature = "wide"))]
1949crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D307, crate::wide_int::Int1024);
1950#[cfg(any(feature = "d462", feature = "x-wide"))]
1951crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D462, crate::wide_int::Int1536);
1952#[cfg(any(feature = "d616", feature = "x-wide"))]
1953crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D616, crate::wide_int::Int2048);
1954#[cfg(any(feature = "d924", feature = "xx-wide"))]
1955crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D924, crate::wide_int::Int3072);
1956#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1957crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D38, i128, D1232, crate::wide_int::Int4096);
1958
1959// D57 row.
1960#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d76", feature = "wide")))]
1961crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D76, crate::wide_int::Int256);
1962#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d115", feature = "wide")))]
1963crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D115, crate::wide_int::Int384);
1964#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d153", feature = "wide")))]
1965crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D153, crate::wide_int::Int512);
1966#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d230", feature = "wide")))]
1967crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D230, crate::wide_int::Int768);
1968#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d307", feature = "wide")))]
1969crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D307, crate::wide_int::Int1024);
1970#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
1971crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D462, crate::wide_int::Int1536);
1972#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d616", feature = "x-wide")))]
1973crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D616, crate::wide_int::Int2048);
1974#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d924", feature = "xx-wide")))]
1975crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D924, crate::wide_int::Int3072);
1976#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d1232", feature = "xx-wide")))]
1977crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D57, crate::wide_int::Int192, D1232, crate::wide_int::Int4096);
1978
1979// D76 row.
1980#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d115", feature = "wide")))]
1981crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D115, crate::wide_int::Int384);
1982#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d153", feature = "wide")))]
1983crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D153, crate::wide_int::Int512);
1984#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d230", feature = "wide")))]
1985crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D230, crate::wide_int::Int768);
1986#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d307", feature = "wide")))]
1987crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D307, crate::wide_int::Int1024);
1988#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
1989crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D462, crate::wide_int::Int1536);
1990#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d616", feature = "x-wide")))]
1991crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D616, crate::wide_int::Int2048);
1992#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d924", feature = "xx-wide")))]
1993crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D924, crate::wide_int::Int3072);
1994#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d1232", feature = "xx-wide")))]
1995crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D76, crate::wide_int::Int256, D1232, crate::wide_int::Int4096);
1996
1997// D115 row.
1998#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d153", feature = "wide")))]
1999crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D115, crate::wide_int::Int384, D153, crate::wide_int::Int512);
2000#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d230", feature = "wide")))]
2001crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D115, crate::wide_int::Int384, D230, crate::wide_int::Int768);
2002#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d307", feature = "wide")))]
2003crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D115, crate::wide_int::Int384, D307, crate::wide_int::Int1024);
2004#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
2005crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D115, crate::wide_int::Int384, D462, crate::wide_int::Int1536);
2006#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d616", feature = "x-wide")))]
2007crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D115, crate::wide_int::Int384, D616, crate::wide_int::Int2048);
2008#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d924", feature = "xx-wide")))]
2009crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D115, crate::wide_int::Int384, D924, crate::wide_int::Int3072);
2010#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d1232", feature = "xx-wide")))]
2011crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D115, crate::wide_int::Int384, D1232, crate::wide_int::Int4096);
2012
2013// D153 row.
2014#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d230", feature = "wide")))]
2015crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D153, crate::wide_int::Int512, D230, crate::wide_int::Int768);
2016#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d307", feature = "wide")))]
2017crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D153, crate::wide_int::Int512, D307, crate::wide_int::Int1024);
2018#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
2019crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D153, crate::wide_int::Int512, D462, crate::wide_int::Int1536);
2020#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d616", feature = "x-wide")))]
2021crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D153, crate::wide_int::Int512, D616, crate::wide_int::Int2048);
2022#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d924", feature = "xx-wide")))]
2023crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D153, crate::wide_int::Int512, D924, crate::wide_int::Int3072);
2024#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d1232", feature = "xx-wide")))]
2025crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D153, crate::wide_int::Int512, D1232, crate::wide_int::Int4096);
2026
2027// D230 row.
2028#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d307", feature = "wide")))]
2029crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D230, crate::wide_int::Int768, D307, crate::wide_int::Int1024);
2030#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
2031crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D230, crate::wide_int::Int768, D462, crate::wide_int::Int1536);
2032#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d616", feature = "x-wide")))]
2033crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D230, crate::wide_int::Int768, D616, crate::wide_int::Int2048);
2034#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d924", feature = "xx-wide")))]
2035crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D230, crate::wide_int::Int768, D924, crate::wide_int::Int3072);
2036#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d1232", feature = "xx-wide")))]
2037crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D230, crate::wide_int::Int768, D1232, crate::wide_int::Int4096);
2038
2039// D307 row.
2040#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
2041crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D307, crate::wide_int::Int1024, D462, crate::wide_int::Int1536);
2042#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d616", feature = "x-wide")))]
2043crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D307, crate::wide_int::Int1024, D616, crate::wide_int::Int2048);
2044#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d924", feature = "xx-wide")))]
2045crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D307, crate::wide_int::Int1024, D924, crate::wide_int::Int3072);
2046#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d1232", feature = "xx-wide")))]
2047crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D307, crate::wide_int::Int1024, D1232, crate::wide_int::Int4096);
2048
2049// D462 row.
2050#[cfg(all(any(feature = "d462", feature = "x-wide"), any(feature = "d616", feature = "x-wide")))]
2051crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D462, crate::wide_int::Int1536, D616, crate::wide_int::Int2048);
2052#[cfg(all(any(feature = "d462", feature = "x-wide"), any(feature = "d924", feature = "xx-wide")))]
2053crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D462, crate::wide_int::Int1536, D924, crate::wide_int::Int3072);
2054#[cfg(all(any(feature = "d462", feature = "x-wide"), any(feature = "d1232", feature = "xx-wide")))]
2055crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D462, crate::wide_int::Int1536, D1232, crate::wide_int::Int4096);
2056
2057// D616 row.
2058#[cfg(all(any(feature = "d616", feature = "x-wide"), any(feature = "d924", feature = "xx-wide")))]
2059crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D616, crate::wide_int::Int2048, D924, crate::wide_int::Int3072);
2060#[cfg(all(any(feature = "d616", feature = "x-wide"), any(feature = "d1232", feature = "xx-wide")))]
2061crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D616, crate::wide_int::Int2048, D1232, crate::wide_int::Int4096);
2062
2063// D924 row.
2064#[cfg(all(any(feature = "d924", feature = "xx-wide"), any(feature = "d1232", feature = "xx-wide")))]
2065crate::macros::cross_width_cmp::decl_cross_width_eq_ord!(D924, crate::wide_int::Int3072, D1232, crate::wide_int::Int4096);
2066
2067#[cfg(test)]
2068mod tests {
2069    use super::*;
2070
2071    /// `from_bits` / `to_bits` round-trip is exact.
2072    #[test]
2073    fn from_bits_to_bits_round_trip() {
2074        let raw: i128 = 1_500_000_000_000;
2075        let v: D38s12 = D38s12::from_bits(raw);
2076        assert_eq!(v.to_bits(), raw);
2077    }
2078
2079    /// `ZERO` has raw bit value 0.
2080    #[test]
2081    fn zero_is_zero_bits() {
2082        assert_eq!(D38s12::ZERO.to_bits(), 0);
2083    }
2084
2085    /// Two instances with identical raw bits compare equal.
2086    #[test]
2087    fn equal_by_underlying_bits() {
2088        assert_eq!(
2089            D38s12::from_bits(42_000_000_000_000),
2090            D38s12::from_bits(42_000_000_000_000)
2091        );
2092        assert_ne!(D38s12::from_bits(42), D38s12::from_bits(43));
2093    }
2094
2095    /// Ord is derived from i128: smaller bits compare less.
2096    #[test]
2097    fn ord_by_underlying_bits() {
2098        assert!(D38s12::from_bits(1) < D38s12::from_bits(2));
2099        assert!(D38s12::from_bits(-1) < D38s12::from_bits(0));
2100    }
2101
2102    /// `multiplier()` returns 10^SCALE. At SCALE = 12 that is 10^12.
2103    #[test]
2104    fn multiplier_is_ten_to_scale() {
2105        assert_eq!(D38s12::multiplier(), 1_000_000_000_000_i128);
2106    }
2107
2108    /// `SCALE` associated const returns the const-generic scale.
2109    #[test]
2110    fn scale_const_matches_type_parameter() {
2111        assert_eq!(D38s12::SCALE, 12);
2112        const N: u32 = D38s12::SCALE;
2113        assert_eq!(N, 12);
2114    }
2115
2116    /// `scale()` method returns the const-generic scale and is
2117    /// independent of the instance's value.
2118    #[test]
2119    fn scale_method_matches_type_parameter() {
2120        assert_eq!(D38s12::ZERO.scale(), 12);
2121        assert_eq!(D38s12::ONE.scale(), 12);
2122        assert_eq!(D38s12::from_bits(i128::MAX).scale(), 12);
2123        assert_eq!(D38s12::from_bits(-7).scale(), 12);
2124    }
2125
2126    /// Both forms agree at non-default scales.
2127    #[test]
2128    fn scale_at_other_scales() {
2129        type D6 = super::D38<6>;
2130        type D0 = super::D38<0>;
2131        type D38 = super::D38<38>;
2132        assert_eq!(D6::SCALE, 6);
2133        assert_eq!(D0::SCALE, 0);
2134        assert_eq!(D38::SCALE, 38);
2135        assert_eq!(D6::ZERO.scale(), 6);
2136        assert_eq!(D0::ZERO.scale(), 0);
2137        assert_eq!(D38::ZERO.scale(), 38);
2138    }
2139
2140    /// `ONE` has bit pattern 10^SCALE so that the logical value is 1.
2141    #[test]
2142    fn one_has_scaled_bit_pattern() {
2143        assert_eq!(D38s12::ONE.to_bits(), 1_000_000_000_000_i128);
2144    }
2145
2146    /// `MAX` is `i128::MAX`.
2147    #[test]
2148    fn max_is_i128_max() {
2149        assert_eq!(D38s12::MAX.to_bits(), i128::MAX);
2150    }
2151
2152    /// `MIN` is `i128::MIN`.
2153    #[test]
2154    fn min_is_i128_min() {
2155        assert_eq!(D38s12::MIN.to_bits(), i128::MIN);
2156    }
2157
2158    /// `ONE` is not equal to `ZERO`.
2159    #[test]
2160    fn one_is_not_zero() {
2161        assert_ne!(D38s12::ONE, D38s12::ZERO);
2162        assert!(D38s12::ONE.is_positive());
2163    }
2164
2165    /// `multiplier()` works correctly at non-default scales.
2166    #[test]
2167    fn multiplier_at_other_scales() {
2168        type D6 = super::D38<6>;
2169        assert_eq!(D6::multiplier(), 1_000_000_i128);
2170        assert_eq!(D6::ONE.to_bits(), 1_000_000_i128);
2171
2172        type D0 = super::D38<0>;
2173        assert_eq!(D0::multiplier(), 1_i128);
2174        assert_eq!(D0::ONE.to_bits(), 1_i128);
2175    }
2176
2177    // ----- D9 / D18 sanity tests -----
2178
2179    #[test]
2180    fn d9_basics() {
2181        assert_eq!(super::D9s2::ZERO.to_bits(), 0_i32);
2182        assert_eq!(super::D9s2::ONE.to_bits(), 100_i32);
2183        assert_eq!(super::D9s2::MAX.to_bits(), i32::MAX);
2184        assert_eq!(super::D9s2::MIN.to_bits(), i32::MIN);
2185        assert_eq!(super::D9s2::multiplier(), 100_i32);
2186        assert_eq!(super::D9s2::SCALE, 2);
2187    }
2188
2189    #[test]
2190    fn d18_basics() {
2191        assert_eq!(super::D18s9::ZERO.to_bits(), 0_i64);
2192        assert_eq!(super::D18s9::ONE.to_bits(), 1_000_000_000_i64);
2193        assert_eq!(super::D18s9::multiplier(), 1_000_000_000_i64);
2194        assert_eq!(super::D18s9::SCALE, 9);
2195    }
2196
2197    #[test]
2198    fn d9_arithmetic() {
2199        let a = super::D9s2::from_bits(150); // 1.50
2200        let b = super::D9s2::from_bits(250); // 2.50
2201        assert_eq!((a + b).to_bits(), 400);
2202        assert_eq!((b - a).to_bits(), 100);
2203        assert_eq!((-a).to_bits(), -150);
2204
2205        let x = super::D9s2::from_bits(200); // 2.00
2206        let y = super::D9s2::from_bits(300); // 3.00
2207        assert_eq!((x * y).to_bits(), 600); // 6.00
2208        assert_eq!((y / x).to_bits(), 150); // 1.50
2209        assert_eq!((y % x).to_bits(), 100); // 1.00
2210    }
2211
2212    #[test]
2213    fn d18_arithmetic() {
2214        let a = super::D18s9::from_bits(1_500_000_000); // 1.5
2215        let b = super::D18s9::from_bits(2_500_000_000); // 2.5
2216        assert_eq!((a + b).to_bits(), 4_000_000_000);
2217        assert_eq!((b - a).to_bits(), 1_000_000_000);
2218        assert_eq!((-a).to_bits(), -1_500_000_000);
2219
2220        let x = super::D18s9::from_bits(2_000_000_000); // 2.0
2221        let y = super::D18s9::from_bits(3_000_000_000); // 3.0
2222        assert_eq!((x * y).to_bits(), 6_000_000_000);
2223        assert_eq!((y / x).to_bits(), 1_500_000_000);
2224        assert_eq!((y % x).to_bits(), 1_000_000_000);
2225    }
2226
2227    #[test]
2228    fn d9_display() {
2229        let v: super::D9s2 = super::D9s2::from_bits(150); // 1.50
2230        let s = alloc::format!("{}", v);
2231        assert_eq!(s, "1.50");
2232        let neg: super::D9s2 = super::D9s2::from_bits(-2050); // -20.50
2233        assert_eq!(alloc::format!("{}", neg), "-20.50");
2234        let zero: super::D9s2 = super::D9s2::ZERO;
2235        assert_eq!(alloc::format!("{}", zero), "0.00");
2236        let int_only: super::D9s0 = super::D9s0::from_bits(42);
2237        assert_eq!(alloc::format!("{}", int_only), "42");
2238    }
2239
2240    #[test]
2241    fn d18_display() {
2242        let v: super::D18s9 = super::D18s9::from_bits(1_500_000_000); // 1.500000000
2243        assert_eq!(alloc::format!("{}", v), "1.500000000");
2244        let neg: super::D18s9 = super::D18s9::from_bits(-1_500_000_000);
2245        assert_eq!(alloc::format!("{}", neg), "-1.500000000");
2246    }
2247
2248    #[test]
2249    fn d9_debug() {
2250        let v: super::D9s2 = super::D9s2::from_bits(150);
2251        let s = alloc::format!("{:?}", v);
2252        assert_eq!(s, "D9<2>(1.50)");
2253    }
2254
2255    #[test]
2256    fn cross_width_widening_d9_to_d18() {
2257        let small: super::D9s2 = super::D9s2::from_bits(150);
2258        let wider: super::D18s2 = small.into();
2259        assert_eq!(wider.to_bits(), 150_i64);
2260    }
2261
2262    #[test]
2263    fn cross_width_widening_d9_to_d38() {
2264        let small: super::D9s2 = super::D9s2::from_bits(-150);
2265        let wider: super::D38s2 = small.into();
2266        assert_eq!(wider.to_bits(), -150_i128);
2267    }
2268
2269    #[test]
2270    fn cross_width_widening_d18_to_d38() {
2271        let mid: super::D18s9 = super::D18s9::from_bits(i64::MAX);
2272        let wider: super::D38s9 = mid.into();
2273        assert_eq!(wider.to_bits(), i64::MAX as i128);
2274    }
2275
2276    #[test]
2277    fn cross_width_narrowing_d38_to_d18_in_range() {
2278        let wide: super::D38s9 = super::D38s9::from_bits(1_500_000_000);
2279        let narrow: super::D18s9 = wide.try_into().unwrap();
2280        assert_eq!(narrow.to_bits(), 1_500_000_000);
2281    }
2282
2283    #[test]
2284    fn cross_width_narrowing_d38_to_d18_out_of_range() {
2285        let wide: super::D38s9 = super::D38s9::from_bits(i128::MAX);
2286        let narrow: Result<super::D18s9, _> = wide.try_into();
2287        assert!(narrow.is_err());
2288    }
2289
2290    #[test]
2291    fn cross_width_narrowing_d18_to_d9_in_range() {
2292        let mid: super::D18s2 = super::D18s2::from_bits(150);
2293        let narrow: super::D9s2 = mid.try_into().unwrap();
2294        assert_eq!(narrow.to_bits(), 150);
2295    }
2296
2297    #[test]
2298    fn cross_width_narrowing_d18_to_d9_out_of_range() {
2299        let mid: super::D18s2 = super::D18s2::from_bits(i64::MAX);
2300        let narrow: Result<super::D9s2, _> = mid.try_into();
2301        assert!(narrow.is_err());
2302    }
2303
2304    #[test]
2305    fn d9_consts() {
2306        if !crate::support::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
2307        use crate::types::consts::DecimalConstants;
2308        type D9s4 = super::D9<4>;
2309        // pi at scale 4 = 3.1416 -> bits = 31416.
2310        assert_eq!(D9s4::pi().to_bits(), 31416);
2311        // e at scale 4 = 2.7183 -> bits = 27183.
2312        assert_eq!(D9s4::e().to_bits(), 27183);
2313    }
2314
2315    #[test]
2316    fn d9_from_str() {
2317        use core::str::FromStr;
2318        let v = super::D9s2::from_str("1.50").unwrap();
2319        assert_eq!(v.to_bits(), 150);
2320        let neg = super::D9s2::from_str("-20.50").unwrap();
2321        assert_eq!(neg.to_bits(), -2050);
2322        // Out of range for D9s2 (i32::MAX is ~2.1e9).
2323        assert!(super::D9s2::from_str("1000000000000.00").is_err());
2324    }
2325
2326    #[test]
2327    fn d18_from_str() {
2328        use core::str::FromStr;
2329        let v = super::D18s9::from_str("1.500000000").unwrap();
2330        assert_eq!(v.to_bits(), 1_500_000_000);
2331        let neg = super::D18s9::from_str("-1.500000000").unwrap();
2332        assert_eq!(neg.to_bits(), -1_500_000_000);
2333    }
2334
2335    #[test]
2336    fn d18_consts() {
2337        if !crate::support::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
2338        use crate::types::consts::DecimalConstants;
2339        type D18s12 = super::D18<12>;
2340        // pi at scale 12 = 3.141592653590 (matches D38s12).
2341        assert_eq!(D18s12::pi().to_bits(), 3_141_592_653_590);
2342        // tau at scale 12 = 6.283185307180.
2343        assert_eq!(D18s12::tau().to_bits(), 6_283_185_307_180);
2344    }
2345
2346    #[cfg(any(feature = "d76", feature = "wide"))]
2347    #[test]
2348    fn d76_basics() {
2349        
2350        use crate::types::traits::arithmetic::DecimalArithmetic;
2351        use crate::wide_int::I256;
2352        assert_eq!(super::D76s2::ZERO.to_bits(), I256::from_str_radix("0", 10).unwrap());
2353        assert_eq!(super::D76s2::ONE.to_bits(), I256::from_str_radix("100", 10).unwrap());
2354        assert_eq!(super::D76s2::MAX.to_bits(), I256::MAX);
2355        assert_eq!(super::D76s2::MIN.to_bits(), I256::MIN);
2356        assert_eq!(super::D76s2::multiplier(), I256::from_str_radix("100", 10).unwrap());
2357        assert_eq!(super::D76s2::SCALE, 2);
2358        assert_eq!(super::D76s2::ZERO.scale(), 2);
2359        // SCALE = 75 (new MAX_SCALE) multiplier is 10^75, well within 256-bit range.
2360        let m75 = super::D76s75::multiplier();
2361        assert_eq!(
2362            m75,
2363            I256::from_str_radix("1000000000000000000000000000000000000000000000000000000000000000000000000000", 10).unwrap()
2364        );
2365        assert_eq!(<super::D76s12 as DecimalArithmetic>::MAX_SCALE, 75);
2366        // round-trip
2367        let raw = I256::from_str_radix("123456789012345678901234567890", 10).unwrap();
2368        assert_eq!(super::D76s12::from_bits(raw).to_bits(), raw);
2369    }
2370
2371    #[cfg(any(feature = "d76", feature = "wide"))]
2372    #[test]
2373    fn d76_arithmetic() {
2374        type D = super::D76<12>;
2375        let one = D::ONE;
2376        let two = D::from_bits(D::multiplier() + D::multiplier());
2377        let three = D::from_bits(D::multiplier() * crate::wide_int::I256::from_str_radix("3", 10).unwrap());
2378        // add / sub / neg
2379        assert_eq!((one + two), three);
2380        assert_eq!((three - one), two);
2381        assert_eq!((-one).to_bits(), -D::multiplier());
2382        // mul: 2 * 3 == 6
2383        let six = D::from_bits(D::multiplier() * crate::wide_int::I256::from_str_radix("6", 10).unwrap());
2384        assert_eq!((two * three), six);
2385        // div: 6 / 2 == 3
2386        assert_eq!((six / two), three);
2387        // rem: 6 % 2 == 0 (storage-level remainder)
2388        assert_eq!((six % two), D::ZERO);
2389        // assign forms
2390        let mut v = one;
2391        v += two;
2392        assert_eq!(v, three);
2393        v *= two;
2394        assert_eq!(v, six);
2395        v /= two;
2396        assert_eq!(v, three);
2397        v -= one;
2398        assert_eq!(v, two);
2399        v %= two;
2400        assert_eq!(v, D::ZERO);
2401        // fractional: 1.5 * 1.5 == 2.25 at scale 12
2402        let half = D::from_bits(D::multiplier() / crate::wide_int::I256::from_str_radix("2", 10).unwrap());
2403        let one_and_half = one + half;
2404        let product = one_and_half * one_and_half;
2405        let expected = D::from_bits(
2406            D::multiplier() * crate::wide_int::I256::from_str_radix("2", 10).unwrap()
2407                + D::multiplier() / crate::wide_int::I256::from_str_radix("4", 10).unwrap(),
2408        );
2409        assert_eq!(product, expected);
2410    }
2411
2412    #[cfg(any(feature = "d76", feature = "wide"))]
2413    #[test]
2414    fn d76_display() {
2415        type D = super::D76<12>;
2416        let one = D::ONE;
2417        assert_eq!(alloc::format!("{}", one), "1.000000000000");
2418        assert_eq!(alloc::format!("{}", -one), "-1.000000000000");
2419        assert_eq!(alloc::format!("{}", D::ZERO), "0.000000000000");
2420        let half = D::from_bits(D::multiplier() / crate::wide_int::I256::from_str_radix("2", 10).unwrap());
2421        assert_eq!(alloc::format!("{}", half), "0.500000000000");
2422        assert_eq!(alloc::format!("{:?}", one), "D76<12>(1.000000000000)");
2423        // scale 0 prints no fractional part
2424        let int_only: super::D76<0> = super::D76::<0>::ONE;
2425        assert_eq!(alloc::format!("{}", int_only), "1");
2426        // very large magnitude near the 75-digit ceiling (new MAX_SCALE)
2427        let big = super::D76s75::MAX;
2428        let s = alloc::format!("{}", big);
2429        assert!(s.starts_with("57.8960446"));
2430        assert_eq!(s.len(), "57.".len() + 75);
2431    }
2432
2433    #[cfg(any(feature = "d76", feature = "wide"))]
2434    #[test]
2435    fn d76_sign_and_helpers() {
2436        type D = super::D76<6>;
2437        let neg = -D::ONE;
2438        assert!(neg.is_negative());
2439        assert!(D::ONE.is_positive());
2440        assert!(!D::ZERO.is_positive());
2441        assert_eq!(neg.abs(), D::ONE);
2442        assert_eq!(D::ONE.signum(), D::ONE);
2443        assert_eq!(neg.signum(), neg);
2444        assert_eq!(D::ZERO.signum(), D::ZERO);
2445        // min / max / clamp
2446        let two = D::ONE + D::ONE;
2447        assert_eq!(D::ONE.min(two), D::ONE);
2448        assert_eq!(D::ONE.max(two), two);
2449        assert_eq!(two.clamp(D::ZERO, D::ONE), D::ONE);
2450        // copysign
2451        assert_eq!(D::ONE.copysign(neg), neg);
2452        assert_eq!(neg.copysign(D::ONE), D::ONE);
2453        // recip: 1/2 at scale 6
2454        let half = D::from_bits(D::multiplier() / crate::wide_int::I256::from_str_radix("2", 10).unwrap());
2455        assert_eq!(two.recip(), half);
2456    }
2457
2458    #[cfg(any(feature = "d76", feature = "wide"))]
2459    #[test]
2460    fn d76_overflow_variants() {
2461        type D = super::D76<2>;
2462        // checked_add overflow at MAX
2463        assert_eq!(D::MAX.checked_add(D::ONE), None);
2464        assert_eq!(D::ONE.checked_add(D::ONE), Some(D::ONE + D::ONE));
2465        // saturating
2466        assert_eq!(D::MAX.saturating_add(D::ONE), D::MAX);
2467        assert_eq!(D::MIN.saturating_sub(D::ONE), D::MIN);
2468        // checked_neg of MIN overflows
2469        assert_eq!(D::MIN.checked_neg(), None);
2470        assert_eq!(D::ONE.checked_neg(), Some(-D::ONE));
2471        // checked_mul / checked_div
2472        let two = D::ONE + D::ONE;
2473        let three = two + D::ONE;
2474        assert_eq!(two.checked_mul(three), Some(D::from_bits(D::multiplier() * crate::wide_int::I256::from_str_radix("6", 10).unwrap())));
2475        assert_eq!(D::ONE.checked_div(D::ZERO), None);
2476        assert_eq!((three).checked_div(D::ONE), Some(three));
2477        // wrapping_add of one storage LSB at MAX wraps around to MIN.
2478        let one_lsb = D::from_bits(crate::wide_int::I256::from_str_radix("1", 10).unwrap());
2479        assert_eq!(D::MAX.wrapping_add(one_lsb), D::MIN);
2480        // overflowing
2481        assert_eq!(D::ONE.overflowing_add(D::ONE), (two, false));
2482        assert_eq!(D::MAX.overflowing_add(D::ONE).1, true);
2483    }
2484
2485    #[cfg(any(feature = "d76", feature = "wide"))]
2486    #[test]
2487    fn d76_consts_and_from_str() {
2488        use crate::types::consts::DecimalConstants;
2489        use core::str::FromStr;
2490        // pi at scale 12 matches the D38 reference.
2491        assert_eq!(
2492            super::D76::<12>::pi().to_bits(),
2493            crate::wide_int::I256::from_str_radix("3141592653590", 10).unwrap()
2494        );
2495        assert_eq!(
2496            super::D76::<4>::e().to_bits(),
2497            crate::wide_int::I256::from_str_radix("27183", 10).unwrap()
2498        );
2499        // FromStr within i128 range
2500        let v = super::D76::<2>::from_str("1.50").unwrap();
2501        assert_eq!(v.to_bits(), crate::wide_int::I256::from_str_radix("150", 10).unwrap());
2502        let neg = super::D76::<2>::from_str("-20.50").unwrap();
2503        assert_eq!(neg.to_bits(), crate::wide_int::I256::from_str_radix("-2050", 10).unwrap());
2504        // num_traits Zero / One
2505        use ::num_traits::{One, Zero};
2506        assert!(super::D76::<6>::zero().is_zero());
2507        assert!(super::D76::<6>::one().is_one());
2508    }
2509
2510    #[cfg(any(feature = "d76", feature = "wide"))]
2511    #[test]
2512    fn d76_conversions() {
2513        use crate::wide_int::I256;
2514        type D = super::D76<6>;
2515        // From<primitive int>
2516        let from_i32: D = 5i32.into();
2517        assert_eq!(from_i32.to_bits(), I256::from_str_radix("5000000", 10).unwrap());
2518        let from_u64: D = 7u64.into();
2519        assert_eq!(from_u64.to_bits(), I256::from_str_radix("7000000", 10).unwrap());
2520        let from_neg: D = (-3i16).into();
2521        assert_eq!(from_neg.to_bits(), I256::from_str_radix("-3000000", 10).unwrap());
2522        // TryFrom<i128> / TryFrom<u128>
2523        let from_i128 = D::try_from(123i128).unwrap();
2524        assert_eq!(from_i128.to_bits(), I256::from_str_radix("123000000", 10).unwrap());
2525        let from_u128 = D::try_from(u128::MAX).unwrap();
2526        assert_eq!(
2527            from_u128.to_bits(),
2528            I256::from_str_radix("340282366920938463463374607431768211455", 10).unwrap()
2529                * I256::from_str_radix("1000000", 10).unwrap()
2530        );
2531        // TryFrom<f64>
2532        let from_f64 = D::try_from(2.5f64).unwrap();
2533        assert_eq!(from_f64.to_bits(), I256::from_str_radix("2500000", 10).unwrap());
2534        assert!(D::try_from(f64::NAN).is_err());
2535        // from_int / from_i32
2536        assert_eq!(D::from_int(9i128), D::from(9i32));
2537        assert_eq!(D::from_i32(-4), D::from(-4i32));
2538        // to_int: 2.5 with HalfToEven -> 2
2539        use crate::support::rounding::RoundingMode;
2540        let two_and_half = D::from_bits(I256::from_str_radix("2500000", 10).unwrap());
2541        assert_eq!(two_and_half.to_int_with(RoundingMode::HalfToEven), 2);
2542        assert_eq!(two_and_half.to_int_with(RoundingMode::HalfAwayFromZero), 3);
2543        assert_eq!(two_and_half.to_int_with(RoundingMode::Ceiling), 3);
2544        assert_eq!(two_and_half.to_int_with(RoundingMode::Floor), 2);
2545        let neg_two_and_half = -two_and_half;
2546        assert_eq!(neg_two_and_half.to_int_with(RoundingMode::Floor), -3);
2547        assert_eq!(neg_two_and_half.to_int_with(RoundingMode::Trunc), -2);
2548        // cross-width widening D38 -> D76 (lossless)
2549        let d38: super::D38s6 = super::D38s6::from_bits(-150);
2550        let widened: super::D76<6> = d38.into();
2551        assert_eq!(widened.to_bits(), I256::from_str_radix("-150", 10).unwrap());
2552        // cross-width narrowing D76 -> D38 in range
2553        let in_range: super::D76<6> = super::D76::<6>::from_bits(I256::from_str_radix("999", 10).unwrap());
2554        let narrowed: super::D38s6 = in_range.try_into().unwrap();
2555        assert_eq!(narrowed.to_bits(), 999i128);
2556        // cross-width narrowing D76 -> D38 out of range
2557        let out_of_range = super::D76s75::MAX;
2558        let narrow_fail: Result<super::D38<75>, _> = out_of_range.try_into();
2559        assert!(narrow_fail.is_err());
2560    }
2561
2562    #[cfg(any(feature = "d76", feature = "wide"))]
2563    #[test]
2564    fn d76_rescale_rounding_floats() {
2565        use crate::support::rounding::RoundingMode;
2566        use crate::wide_int::I256;
2567        type D6 = super::D76<6>;
2568        // rescale up (lossless): scale 6 -> scale 9
2569        let v = D6::from_bits(I256::from_str_radix("1500000", 10).unwrap()); // 1.5
2570        let up: super::D76<9> = v.rescale::<9>();
2571        assert_eq!(up.to_bits(), I256::from_str_radix("1500000000", 10).unwrap());
2572        // rescale down (lossy, HalfToEven): scale 6 -> scale 2
2573        let down: super::D76<2> = v.rescale::<2>();
2574        assert_eq!(down.to_bits(), I256::from_str_radix("150", 10).unwrap());
2575        // rescale down with explicit mode: 2.5 (scale 0 representation) ...
2576        let two_p_five = super::D76::<1>::from_bits(I256::from_str_radix("25", 10).unwrap());
2577        let r0: super::D76<0> = two_p_five.rescale_with::<0>(RoundingMode::HalfToEven);
2578        assert_eq!(r0.to_bits(), I256::from_str_radix("2", 10).unwrap());
2579        let r0b: super::D76<0> = two_p_five.rescale_with::<0>(RoundingMode::HalfAwayFromZero);
2580        assert_eq!(r0b.to_bits(), I256::from_str_radix("3", 10).unwrap());
2581        // floor / ceil / round / trunc / fract on 1.5 at scale 6
2582        assert_eq!(v.floor(), D6::ONE);
2583        assert_eq!(v.ceil(), D6::ONE + D6::ONE);
2584        assert_eq!(v.round(), D6::ONE + D6::ONE); // half away from zero
2585        assert_eq!(v.trunc(), D6::ONE);
2586        assert_eq!(v.fract(), D6::from_bits(I256::from_str_radix("500000", 10).unwrap()));
2587        // negative: -1.5
2588        let neg = -v;
2589        assert_eq!(neg.floor(), -(D6::ONE + D6::ONE));
2590        assert_eq!(neg.ceil(), -D6::ONE);
2591        assert_eq!(neg.round(), -(D6::ONE + D6::ONE));
2592        // float bridge
2593        let from_f = D6::from_f64(2.5);
2594        assert_eq!(from_f.to_bits(), I256::from_str_radix("2500000", 10).unwrap());
2595        assert_eq!(D6::from_f64(f64::NAN), D6::ZERO);
2596        assert_eq!(D6::from_f64(f64::INFINITY), D6::MAX);
2597        let round_trip = D6::ONE.to_f64();
2598        assert!((round_trip - 1.0).abs() < 1e-9);
2599    }
2600
2601    #[cfg(any(feature = "d153", feature = "wide"))]
2602    #[test]
2603    fn d153_smoke() {
2604        
2605        use crate::types::traits::arithmetic::DecimalArithmetic;
2606        use crate::wide_int::I512;
2607        type D = super::D153<35>;
2608        assert_eq!(<D as DecimalArithmetic>::MAX_SCALE, 152);
2609        assert_eq!(D::ZERO.to_bits(), I512::from_str_radix("0", 10).unwrap());
2610        let one = D::ONE;
2611        let two = one + one;
2612        let three = two + one;
2613        assert_eq!(two * three, D::from_int(6i128));
2614        assert_eq!((three * two) / two, three);
2615        assert_eq!(alloc::format!("{}", one).len(), "1.".len() + 35);
2616        assert_eq!(D::from_int(5i128).to_int(), 5);
2617        // rescale across the wide range
2618        let up: super::D153<150> = one.rescale::<150>();
2619        assert_eq!(up, super::D153::<150>::ONE);
2620        // 152-digit ceiling multiplier fits in I512 (new MAX_SCALE)
2621        let _ = super::D153s152::multiplier();
2622    }
2623
2624    #[cfg(any(feature = "d307", feature = "wide"))]
2625    #[test]
2626    fn d307_smoke() {
2627        
2628        use crate::types::traits::arithmetic::DecimalArithmetic;
2629        use crate::wide_int::I1024;
2630        type D = super::D307<35>;
2631        assert_eq!(<D as DecimalArithmetic>::MAX_SCALE, 306);
2632        let one = D::ONE;
2633        let two = one + one;
2634        let three = two + one;
2635        assert_eq!(two * three, D::from_int(6i128));
2636        assert_eq!((three * two) / two, three);
2637        assert_eq!(D::ZERO.to_bits(), I1024::from_str_radix("0", 10).unwrap());
2638        assert_eq!(alloc::format!("{}", one).len(), "1.".len() + 35);
2639        // cross-width: D76 -> D307 widening, D307 -> D76 narrowing
2640        #[cfg(any(feature = "d76", feature = "wide"))]
2641        {
2642            let small: super::D76<35> = super::D76::<35>::ONE;
2643            let widened: super::D307<35> = small.into();
2644            assert_eq!(widened, D::ONE);
2645            let narrowed: super::D76<35> = widened.try_into().unwrap();
2646            assert_eq!(narrowed, super::D76::<35>::ONE);
2647        }
2648        // 306-digit ceiling multiplier fits in I1024 (new MAX_SCALE)
2649        let _ = super::D307s306::multiplier();
2650    }
2651
2652    #[test]
2653    fn d9_op_assign() {
2654        let mut v = super::D9s2::from_bits(100);
2655        v += super::D9s2::from_bits(50);
2656        assert_eq!(v.to_bits(), 150);
2657        v -= super::D9s2::from_bits(25);
2658        assert_eq!(v.to_bits(), 125);
2659        v *= super::D9s2::from_bits(200); // *2.00
2660        assert_eq!(v.to_bits(), 250);
2661        v /= super::D9s2::from_bits(200); // /2.00
2662        assert_eq!(v.to_bits(), 125);
2663        v %= super::D9s2::from_bits(100);
2664        assert_eq!(v.to_bits(), 25);
2665    }
2666}