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::Int1024,
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"))]
1531crate::macros::full::decl_decimal_full!(
1532    wide D924,
1533    crate::wide_int::I3072,
1534    crate::wide_int::U3072,
1535    crate::wide_int::I6144,
1536    crate::wide_int::Int6144,
1537    crate::wide_int::Int12288,
1538    crate::wide_int::Int12288,
1539    wide_trig_d924,
1540    923
1541);
1542#[cfg(any(feature = "d924", feature = "xx-wide"))]
1543pub type D924s0 = D924<0>;
1544#[cfg(any(feature = "d924", feature = "xx-wide"))]
1545pub type D924s1 = D924<1>;
1546#[cfg(any(feature = "d924", feature = "xx-wide"))]
1547pub type D924s75 = D924<75>;
1548#[cfg(any(feature = "d924", feature = "xx-wide"))]
1549pub type D924s153 = D924<153>;
1550#[cfg(any(feature = "d924", feature = "xx-wide"))]
1551pub type D924s230 = D924<230>;
1552#[cfg(any(feature = "d924", feature = "xx-wide"))]
1553pub type D924s307 = D924<307>;
1554#[cfg(any(feature = "d924", feature = "xx-wide"))]
1555pub type D924s400 = D924<400>;
1556#[cfg(any(feature = "d924", feature = "xx-wide"))]
1557pub type D924s461 = D924<461>;
1558#[cfg(any(feature = "d924", feature = "xx-wide"))]
1559pub type D924s462 = D924<462>;
1560#[cfg(any(feature = "d924", feature = "xx-wide"))]
1561pub type D924s500 = D924<500>;
1562#[cfg(any(feature = "d924", feature = "xx-wide"))]
1563pub type D924s616 = D924<616>;
1564#[cfg(any(feature = "d924", feature = "xx-wide"))]
1565pub type D924s700 = D924<700>;
1566#[cfg(any(feature = "d924", feature = "xx-wide"))]
1567pub type D924s800 = D924<800>;
1568#[cfg(any(feature = "d924", feature = "xx-wide"))]
1569pub type D924s860 = D924<860>;
1570#[cfg(any(feature = "d924", feature = "xx-wide"))]
1571pub type D924s900 = D924<900>;
1572#[cfg(any(feature = "d924", feature = "xx-wide"))]
1573pub type D924s920 = D924<920>;
1574/// Scale alias: `D924<923>`. 1 LSB = 10^-923. Maximum supported scale
1575/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1576#[cfg(any(feature = "d924", feature = "xx-wide"))]
1577pub type D924s923 = D924<923>;
1578
1579// ── D1232 (4096-bit / 64 u64 limbs) ────────────────────────────────────
1580
1581/// Scaled fixed-point decimal with 4096-bit storage. Widest tier
1582/// shipped; supports SCALE up to 1232 digits. Now a type alias of the
1583/// unified [`crate::D`] generic decimal type: `D1232<S>` is
1584/// `D<crate::wide_int::Int4096, S>`. Both spellings are interchangeable.
1585///
1586/// The `#[repr(transparent)]` layout over `Int4096` is preserved through
1587/// the alias because the underlying [`crate::D`] is itself
1588/// `#[repr(transparent)]` over its storage parameter.
1589///
1590/// Gated behind the `d1232` (or umbrella `xx-wide`) Cargo feature.
1591#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1592pub type D1232<const SCALE: u32> = crate::D<crate::wide_int::Int4096, SCALE>;
1593
1594/// `Default` returns `ZERO`, matching the all-zero limb pattern of
1595/// `Int4096`.
1596///
1597/// Implemented on the underlying `crate::D<crate::wide_int::Int4096, SCALE>`
1598/// because `D1232<SCALE>` is now an alias of that type. `ZERO` is emitted
1599/// by the basics macro further down in this file.
1600#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1601impl<const SCALE: u32> Default for crate::D<crate::wide_int::Int4096, SCALE> {
1602    #[inline]
1603    fn default() -> Self { Self::ZERO }
1604}
1605
1606#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1607crate::macros::full::decl_decimal_full!(
1608    wide D1232,
1609    crate::wide_int::I4096,
1610    crate::wide_int::U4096,
1611    crate::wide_int::I8192,
1612    crate::wide_int::Int8192,
1613    crate::wide_int::Int16384,
1614    crate::wide_int::Int16384,
1615    wide_trig_d1232,
1616    1231
1617);
1618#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1619pub type D1232s0 = D1232<0>;
1620#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1621pub type D1232s1 = D1232<1>;
1622#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1623pub type D1232s75 = D1232<75>;
1624#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1625pub type D1232s153 = D1232<153>;
1626#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1627pub type D1232s230 = D1232<230>;
1628#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1629pub type D1232s307 = D1232<307>;
1630#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1631pub type D1232s461 = D1232<461>;
1632#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1633pub type D1232s616 = D1232<616>;
1634#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1635pub type D1232s700 = D1232<700>;
1636#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1637pub type D1232s800 = D1232<800>;
1638#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1639pub type D1232s900 = D1232<900>;
1640#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1641pub type D1232s924 = D1232<924>;
1642#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1643pub type D1232s1000 = D1232<1000>;
1644#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1645pub type D1232s1100 = D1232<1100>;
1646#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1647pub type D1232s1180 = D1232<1180>;
1648#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1649pub type D1232s1220 = D1232<1220>;
1650#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1651pub type D1232s1230 = D1232<1230>;
1652/// Scale alias: `D1232<1231>`. 1 LSB = 10^-1231. Maximum supported scale
1653/// (v0.4.0 cap: `MAX_SCALE = name - 1`).
1654#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1655pub type D1232s1231 = D1232<1231>;
1656
1657// ─── Cross-tier next-neighbour widen/narrow chain ─────────────────────
1658//
1659// The historical .widen() / .narrow() methods on D38/D76/D153/D307
1660// follow the power-of-two storage sequence (D38→D76→D153→D307). The
1661// 0.2.6 tier ladder fills in half-widths between each pair plus
1662// extends to D1232; the complete ladder is:
1663//
1664//   D9 → D18 → D38 → D57 → D76 → D115 → D153 → D230 → D307 →
1665//   D462 → D616 → D924 → D1232
1666//
1667// The next-neighbour .widen() / .narrow() methods on the new tiers go
1668// to the immediate adjacent rung (D57.widen() → D76, D76.widen()
1669// already returns D153 which is the existing power-of-two next-up,
1670// etc.). The cross-tier From / TryFrom impls below cover the
1671// neighbour pairs that weren't already declared by the legacy
1672// D38/D76/D153/D307 blocks.
1673//
1674// Coverage strategy: declare every NEW adjacent pair both ways. The
1675// existing legacy declarations (D9↔D18, D9/D18/D38↔D76, D38/D76↔D153,
1676// D76/D153↔D307) stay where they are; this block adds the conversions
1677// that hop through the new tiers (D38↔D57, D57↔D76, D76↔D115, etc.).
1678
1679// D38 ↔ D57
1680#[cfg(any(feature = "d57", feature = "wide"))]
1681crate::macros::conversions::decl_cross_width_widening!(wide D57, crate::wide_int::I192, D38, i128);
1682#[cfg(any(feature = "d57", feature = "wide"))]
1683crate::macros::conversions::decl_cross_width_narrowing!(wide D38, i128, D57, crate::wide_int::I192);
1684
1685// D57 ↔ D76
1686#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d76", feature = "wide")))]
1687crate::macros::conversions::decl_cross_width_widening!(wide D76, crate::wide_int::I256, D57, crate::wide_int::I192);
1688#[cfg(all(any(feature = "d57", feature = "wide"), any(feature = "d76", feature = "wide")))]
1689crate::macros::conversions::decl_cross_width_narrowing!(wide D57, crate::wide_int::I192, D76, crate::wide_int::I256);
1690
1691// D76 ↔ D115
1692#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d115", feature = "wide")))]
1693crate::macros::conversions::decl_cross_width_widening!(wide D115, crate::wide_int::I384, D76, crate::wide_int::I256);
1694#[cfg(all(any(feature = "d76", feature = "wide"), any(feature = "d115", feature = "wide")))]
1695crate::macros::conversions::decl_cross_width_narrowing!(wide D76, crate::wide_int::I256, D115, crate::wide_int::I384);
1696
1697// D115 ↔ D153
1698#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d153", feature = "wide")))]
1699crate::macros::conversions::decl_cross_width_widening!(wide D153, crate::wide_int::I512, D115, crate::wide_int::I384);
1700#[cfg(all(any(feature = "d115", feature = "wide"), any(feature = "d153", feature = "wide")))]
1701crate::macros::conversions::decl_cross_width_narrowing!(wide D115, crate::wide_int::I384, D153, crate::wide_int::I512);
1702
1703// D153 ↔ D230
1704#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d230", feature = "wide")))]
1705crate::macros::conversions::decl_cross_width_widening!(wide D230, crate::wide_int::I768, D153, crate::wide_int::I512);
1706#[cfg(all(any(feature = "d153", feature = "wide"), any(feature = "d230", feature = "wide")))]
1707crate::macros::conversions::decl_cross_width_narrowing!(wide D153, crate::wide_int::I512, D230, crate::wide_int::I768);
1708
1709// D230 ↔ D307
1710#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d307", feature = "wide")))]
1711crate::macros::conversions::decl_cross_width_widening!(wide D307, crate::wide_int::I1024, D230, crate::wide_int::I768);
1712#[cfg(all(any(feature = "d230", feature = "wide"), any(feature = "d307", feature = "wide")))]
1713crate::macros::conversions::decl_cross_width_narrowing!(wide D230, crate::wide_int::I768, D307, crate::wide_int::I1024);
1714
1715// D307 ↔ D462
1716#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
1717crate::macros::conversions::decl_cross_width_widening!(wide D462, crate::wide_int::I1536, D307, crate::wide_int::I1024);
1718#[cfg(all(any(feature = "d307", feature = "wide"), any(feature = "d462", feature = "x-wide")))]
1719crate::macros::conversions::decl_cross_width_narrowing!(wide D307, crate::wide_int::I1024, D462, crate::wide_int::I1536);
1720
1721// D462 ↔ D616
1722#[cfg(all(any(feature = "d462", feature = "x-wide"), any(feature = "d616", feature = "x-wide")))]
1723crate::macros::conversions::decl_cross_width_widening!(wide D616, crate::wide_int::I2048, D462, crate::wide_int::I1536);
1724#[cfg(all(any(feature = "d462", feature = "x-wide"), any(feature = "d616", feature = "x-wide")))]
1725crate::macros::conversions::decl_cross_width_narrowing!(wide D462, crate::wide_int::I1536, D616, crate::wide_int::I2048);
1726
1727// D616 ↔ D924
1728#[cfg(all(any(feature = "d616", feature = "x-wide"), any(feature = "d924", feature = "xx-wide")))]
1729crate::macros::conversions::decl_cross_width_widening!(wide D924, crate::wide_int::I3072, D616, crate::wide_int::I2048);
1730#[cfg(all(any(feature = "d616", feature = "x-wide"), any(feature = "d924", feature = "xx-wide")))]
1731crate::macros::conversions::decl_cross_width_narrowing!(wide D616, crate::wide_int::I2048, D924, crate::wide_int::I3072);
1732
1733// D924 ↔ D1232
1734#[cfg(all(any(feature = "d924", feature = "xx-wide"), any(feature = "d1232", feature = "xx-wide")))]
1735crate::macros::conversions::decl_cross_width_widening!(wide D1232, crate::wide_int::I4096, D924, crate::wide_int::I3072);
1736#[cfg(all(any(feature = "d924", feature = "xx-wide"), any(feature = "d1232", feature = "xx-wide")))]
1737crate::macros::conversions::decl_cross_width_narrowing!(wide D924, crate::wide_int::I3072, D1232, crate::wide_int::I4096);
1738
1739// .widen() / .narrow() methods on the new tiers — each points at the
1740// IMMEDIATE neighbour in the comprehensive ladder above. The legacy
1741// .widen() / .narrow() on D38/D76/D153/D307 are unchanged (still go
1742// to the power-of-two next-up) for source compatibility; users who
1743// want to traverse through the half-widths should use the methods
1744// declared here, or the From / TryFrom impls directly.
1745
1746#[cfg(any(feature = "d57", feature = "wide"))]
1747impl<const SCALE: u32> D57<SCALE> {
1748    /// Demote to the immediate previous tier ([`D38`]) at the same `SCALE`.
1749    /// Returns `Err(ConvertError::Overflow)` if the value exceeds `i128` range.
1750    #[inline]
1751    pub fn narrow(self) -> Result<D38<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1752    /// Promote to the next storage tier ([`D76`]) at the same `SCALE`. Lossless.
1753    #[inline] #[must_use]
1754    pub fn widen(self) -> D76<SCALE> { self.into() }
1755}
1756
1757#[cfg(any(feature = "d115", feature = "wide"))]
1758impl<const SCALE: u32> D115<SCALE> {
1759    /// Demote to the immediate previous tier ([`D76`]) at the same `SCALE`.
1760    #[inline]
1761    pub fn narrow(self) -> Result<D76<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1762    /// Promote to the next storage tier ([`D153`]) at the same `SCALE`. Lossless.
1763    #[inline] #[must_use]
1764    pub fn widen(self) -> D153<SCALE> { self.into() }
1765}
1766
1767#[cfg(any(feature = "d230", feature = "wide"))]
1768impl<const SCALE: u32> D230<SCALE> {
1769    /// Demote to the immediate previous tier ([`D153`]) at the same `SCALE`.
1770    #[inline]
1771    pub fn narrow(self) -> Result<D153<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1772    /// Promote to the next storage tier ([`D307`]) at the same `SCALE`. Lossless.
1773    #[inline] #[must_use]
1774    pub fn widen(self) -> D307<SCALE> { self.into() }
1775}
1776
1777#[cfg(any(feature = "d462", feature = "x-wide"))]
1778impl<const SCALE: u32> D462<SCALE> {
1779    /// Demote to the immediate previous tier ([`D307`]) at the same `SCALE`.
1780    #[inline]
1781    pub fn narrow(self) -> Result<D307<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1782    /// Promote to the next storage tier ([`D616`]) at the same `SCALE`. Lossless.
1783    #[inline] #[must_use]
1784    pub fn widen(self) -> D616<SCALE> { self.into() }
1785}
1786
1787#[cfg(any(feature = "d616", feature = "x-wide"))]
1788impl<const SCALE: u32> D616<SCALE> {
1789    /// Demote to the immediate previous tier ([`D462`]) at the same `SCALE`.
1790    #[inline]
1791    pub fn narrow(self) -> Result<D462<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1792}
1793
1794// `widen` lives in a second impl gated on D924's feature — D616 can
1795// be enabled without xx-wide (docs.rs builds this case), in which
1796// case D924 doesn't exist as a type and the unconditional `widen`
1797// method above breaks the doc build.
1798#[cfg(all(
1799    any(feature = "d616", feature = "x-wide"),
1800    any(feature = "d924", feature = "xx-wide"),
1801))]
1802impl<const SCALE: u32> D616<SCALE> {
1803    /// Promote to the next storage tier ([`D924`]) at the same `SCALE`. Lossless.
1804    #[inline] #[must_use]
1805    pub fn widen(self) -> D924<SCALE> { self.into() }
1806}
1807
1808#[cfg(any(feature = "d924", feature = "xx-wide"))]
1809impl<const SCALE: u32> D924<SCALE> {
1810    /// Demote to the immediate previous tier ([`D616`]) at the same `SCALE`.
1811    #[inline]
1812    pub fn narrow(self) -> Result<D616<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1813    /// Promote to the next storage tier ([`D1232`]) at the same `SCALE`. Lossless.
1814    #[inline] #[must_use]
1815    pub fn widen(self) -> D1232<SCALE> { self.into() }
1816}
1817
1818#[cfg(any(feature = "d1232", feature = "xx-wide"))]
1819impl<const SCALE: u32> D1232<SCALE> {
1820    /// Demote to the immediate previous tier ([`D924`]) at the same `SCALE`.
1821    /// D1232 is the widest shipped tier, so there is no `.widen()` method.
1822    #[inline]
1823    pub fn narrow(self) -> Result<D924<SCALE>, crate::support::error::ConvertError> { self.try_into() }
1824}
1825
1826#[cfg(test)]
1827mod tests {
1828    use super::*;
1829
1830    /// `from_bits` / `to_bits` round-trip is exact.
1831    #[test]
1832    fn from_bits_to_bits_round_trip() {
1833        let raw: i128 = 1_500_000_000_000;
1834        let v: D38s12 = D38s12::from_bits(raw);
1835        assert_eq!(v.to_bits(), raw);
1836    }
1837
1838    /// `ZERO` has raw bit value 0.
1839    #[test]
1840    fn zero_is_zero_bits() {
1841        assert_eq!(D38s12::ZERO.to_bits(), 0);
1842    }
1843
1844    /// Two instances with identical raw bits compare equal.
1845    #[test]
1846    fn equal_by_underlying_bits() {
1847        assert_eq!(
1848            D38s12::from_bits(42_000_000_000_000),
1849            D38s12::from_bits(42_000_000_000_000)
1850        );
1851        assert_ne!(D38s12::from_bits(42), D38s12::from_bits(43));
1852    }
1853
1854    /// Ord is derived from i128: smaller bits compare less.
1855    #[test]
1856    fn ord_by_underlying_bits() {
1857        assert!(D38s12::from_bits(1) < D38s12::from_bits(2));
1858        assert!(D38s12::from_bits(-1) < D38s12::from_bits(0));
1859    }
1860
1861    /// `multiplier()` returns 10^SCALE. At SCALE = 12 that is 10^12.
1862    #[test]
1863    fn multiplier_is_ten_to_scale() {
1864        assert_eq!(D38s12::multiplier(), 1_000_000_000_000_i128);
1865    }
1866
1867    /// `SCALE` associated const returns the const-generic scale.
1868    #[test]
1869    fn scale_const_matches_type_parameter() {
1870        assert_eq!(D38s12::SCALE, 12);
1871        const N: u32 = D38s12::SCALE;
1872        assert_eq!(N, 12);
1873    }
1874
1875    /// `scale()` method returns the const-generic scale and is
1876    /// independent of the instance's value.
1877    #[test]
1878    fn scale_method_matches_type_parameter() {
1879        assert_eq!(D38s12::ZERO.scale(), 12);
1880        assert_eq!(D38s12::ONE.scale(), 12);
1881        assert_eq!(D38s12::from_bits(i128::MAX).scale(), 12);
1882        assert_eq!(D38s12::from_bits(-7).scale(), 12);
1883    }
1884
1885    /// Both forms agree at non-default scales.
1886    #[test]
1887    fn scale_at_other_scales() {
1888        type D6 = super::D38<6>;
1889        type D0 = super::D38<0>;
1890        type D38 = super::D38<38>;
1891        assert_eq!(D6::SCALE, 6);
1892        assert_eq!(D0::SCALE, 0);
1893        assert_eq!(D38::SCALE, 38);
1894        assert_eq!(D6::ZERO.scale(), 6);
1895        assert_eq!(D0::ZERO.scale(), 0);
1896        assert_eq!(D38::ZERO.scale(), 38);
1897    }
1898
1899    /// `ONE` has bit pattern 10^SCALE so that the logical value is 1.
1900    #[test]
1901    fn one_has_scaled_bit_pattern() {
1902        assert_eq!(D38s12::ONE.to_bits(), 1_000_000_000_000_i128);
1903    }
1904
1905    /// `MAX` is `i128::MAX`.
1906    #[test]
1907    fn max_is_i128_max() {
1908        assert_eq!(D38s12::MAX.to_bits(), i128::MAX);
1909    }
1910
1911    /// `MIN` is `i128::MIN`.
1912    #[test]
1913    fn min_is_i128_min() {
1914        assert_eq!(D38s12::MIN.to_bits(), i128::MIN);
1915    }
1916
1917    /// `ONE` is not equal to `ZERO`.
1918    #[test]
1919    fn one_is_not_zero() {
1920        assert_ne!(D38s12::ONE, D38s12::ZERO);
1921        assert!(D38s12::ONE.is_positive());
1922    }
1923
1924    /// `multiplier()` works correctly at non-default scales.
1925    #[test]
1926    fn multiplier_at_other_scales() {
1927        type D6 = super::D38<6>;
1928        assert_eq!(D6::multiplier(), 1_000_000_i128);
1929        assert_eq!(D6::ONE.to_bits(), 1_000_000_i128);
1930
1931        type D0 = super::D38<0>;
1932        assert_eq!(D0::multiplier(), 1_i128);
1933        assert_eq!(D0::ONE.to_bits(), 1_i128);
1934    }
1935
1936    // ----- D9 / D18 sanity tests -----
1937
1938    #[test]
1939    fn d9_basics() {
1940        assert_eq!(super::D9s2::ZERO.to_bits(), 0_i32);
1941        assert_eq!(super::D9s2::ONE.to_bits(), 100_i32);
1942        assert_eq!(super::D9s2::MAX.to_bits(), i32::MAX);
1943        assert_eq!(super::D9s2::MIN.to_bits(), i32::MIN);
1944        assert_eq!(super::D9s2::multiplier(), 100_i32);
1945        assert_eq!(super::D9s2::SCALE, 2);
1946    }
1947
1948    #[test]
1949    fn d18_basics() {
1950        assert_eq!(super::D18s9::ZERO.to_bits(), 0_i64);
1951        assert_eq!(super::D18s9::ONE.to_bits(), 1_000_000_000_i64);
1952        assert_eq!(super::D18s9::multiplier(), 1_000_000_000_i64);
1953        assert_eq!(super::D18s9::SCALE, 9);
1954    }
1955
1956    #[test]
1957    fn d9_arithmetic() {
1958        let a = super::D9s2::from_bits(150); // 1.50
1959        let b = super::D9s2::from_bits(250); // 2.50
1960        assert_eq!((a + b).to_bits(), 400);
1961        assert_eq!((b - a).to_bits(), 100);
1962        assert_eq!((-a).to_bits(), -150);
1963
1964        let x = super::D9s2::from_bits(200); // 2.00
1965        let y = super::D9s2::from_bits(300); // 3.00
1966        assert_eq!((x * y).to_bits(), 600); // 6.00
1967        assert_eq!((y / x).to_bits(), 150); // 1.50
1968        assert_eq!((y % x).to_bits(), 100); // 1.00
1969    }
1970
1971    #[test]
1972    fn d18_arithmetic() {
1973        let a = super::D18s9::from_bits(1_500_000_000); // 1.5
1974        let b = super::D18s9::from_bits(2_500_000_000); // 2.5
1975        assert_eq!((a + b).to_bits(), 4_000_000_000);
1976        assert_eq!((b - a).to_bits(), 1_000_000_000);
1977        assert_eq!((-a).to_bits(), -1_500_000_000);
1978
1979        let x = super::D18s9::from_bits(2_000_000_000); // 2.0
1980        let y = super::D18s9::from_bits(3_000_000_000); // 3.0
1981        assert_eq!((x * y).to_bits(), 6_000_000_000);
1982        assert_eq!((y / x).to_bits(), 1_500_000_000);
1983        assert_eq!((y % x).to_bits(), 1_000_000_000);
1984    }
1985
1986    #[test]
1987    fn d9_display() {
1988        let v: super::D9s2 = super::D9s2::from_bits(150); // 1.50
1989        let s = alloc::format!("{}", v);
1990        assert_eq!(s, "1.50");
1991        let neg: super::D9s2 = super::D9s2::from_bits(-2050); // -20.50
1992        assert_eq!(alloc::format!("{}", neg), "-20.50");
1993        let zero: super::D9s2 = super::D9s2::ZERO;
1994        assert_eq!(alloc::format!("{}", zero), "0.00");
1995        let int_only: super::D9s0 = super::D9s0::from_bits(42);
1996        assert_eq!(alloc::format!("{}", int_only), "42");
1997    }
1998
1999    #[test]
2000    fn d18_display() {
2001        let v: super::D18s9 = super::D18s9::from_bits(1_500_000_000); // 1.500000000
2002        assert_eq!(alloc::format!("{}", v), "1.500000000");
2003        let neg: super::D18s9 = super::D18s9::from_bits(-1_500_000_000);
2004        assert_eq!(alloc::format!("{}", neg), "-1.500000000");
2005    }
2006
2007    #[test]
2008    fn d9_debug() {
2009        let v: super::D9s2 = super::D9s2::from_bits(150);
2010        let s = alloc::format!("{:?}", v);
2011        assert_eq!(s, "D9<2>(1.50)");
2012    }
2013
2014    #[test]
2015    fn cross_width_widening_d9_to_d18() {
2016        let small: super::D9s2 = super::D9s2::from_bits(150);
2017        let wider: super::D18s2 = small.into();
2018        assert_eq!(wider.to_bits(), 150_i64);
2019    }
2020
2021    #[test]
2022    fn cross_width_widening_d9_to_d38() {
2023        let small: super::D9s2 = super::D9s2::from_bits(-150);
2024        let wider: super::D38s2 = small.into();
2025        assert_eq!(wider.to_bits(), -150_i128);
2026    }
2027
2028    #[test]
2029    fn cross_width_widening_d18_to_d38() {
2030        let mid: super::D18s9 = super::D18s9::from_bits(i64::MAX);
2031        let wider: super::D38s9 = mid.into();
2032        assert_eq!(wider.to_bits(), i64::MAX as i128);
2033    }
2034
2035    #[test]
2036    fn cross_width_narrowing_d38_to_d18_in_range() {
2037        let wide: super::D38s9 = super::D38s9::from_bits(1_500_000_000);
2038        let narrow: super::D18s9 = wide.try_into().unwrap();
2039        assert_eq!(narrow.to_bits(), 1_500_000_000);
2040    }
2041
2042    #[test]
2043    fn cross_width_narrowing_d38_to_d18_out_of_range() {
2044        let wide: super::D38s9 = super::D38s9::from_bits(i128::MAX);
2045        let narrow: Result<super::D18s9, _> = wide.try_into();
2046        assert!(narrow.is_err());
2047    }
2048
2049    #[test]
2050    fn cross_width_narrowing_d18_to_d9_in_range() {
2051        let mid: super::D18s2 = super::D18s2::from_bits(150);
2052        let narrow: super::D9s2 = mid.try_into().unwrap();
2053        assert_eq!(narrow.to_bits(), 150);
2054    }
2055
2056    #[test]
2057    fn cross_width_narrowing_d18_to_d9_out_of_range() {
2058        let mid: super::D18s2 = super::D18s2::from_bits(i64::MAX);
2059        let narrow: Result<super::D9s2, _> = mid.try_into();
2060        assert!(narrow.is_err());
2061    }
2062
2063    #[test]
2064    fn d9_consts() {
2065        if !crate::support::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
2066        use crate::types::consts::DecimalConstants;
2067        type D9s4 = super::D9<4>;
2068        // pi at scale 4 = 3.1416 -> bits = 31416.
2069        assert_eq!(D9s4::pi().to_bits(), 31416);
2070        // e at scale 4 = 2.7183 -> bits = 27183.
2071        assert_eq!(D9s4::e().to_bits(), 27183);
2072    }
2073
2074    #[test]
2075    fn d9_from_str() {
2076        use core::str::FromStr;
2077        let v = super::D9s2::from_str("1.50").unwrap();
2078        assert_eq!(v.to_bits(), 150);
2079        let neg = super::D9s2::from_str("-20.50").unwrap();
2080        assert_eq!(neg.to_bits(), -2050);
2081        // Out of range for D9s2 (i32::MAX is ~2.1e9).
2082        assert!(super::D9s2::from_str("1000000000000.00").is_err());
2083    }
2084
2085    #[test]
2086    fn d18_from_str() {
2087        use core::str::FromStr;
2088        let v = super::D18s9::from_str("1.500000000").unwrap();
2089        assert_eq!(v.to_bits(), 1_500_000_000);
2090        let neg = super::D18s9::from_str("-1.500000000").unwrap();
2091        assert_eq!(neg.to_bits(), -1_500_000_000);
2092    }
2093
2094    #[test]
2095    fn d18_consts() {
2096        if !crate::support::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
2097        use crate::types::consts::DecimalConstants;
2098        type D18s12 = super::D18<12>;
2099        // pi at scale 12 = 3.141592653590 (matches D38s12).
2100        assert_eq!(D18s12::pi().to_bits(), 3_141_592_653_590);
2101        // tau at scale 12 = 6.283185307180.
2102        assert_eq!(D18s12::tau().to_bits(), 6_283_185_307_180);
2103    }
2104
2105    #[cfg(any(feature = "d76", feature = "wide"))]
2106    #[test]
2107    fn d76_basics() {
2108        
2109        use crate::types::traits::arithmetic::DecimalArithmetic;
2110        use crate::wide_int::I256;
2111        assert_eq!(super::D76s2::ZERO.to_bits(), I256::from_str_radix("0", 10).unwrap());
2112        assert_eq!(super::D76s2::ONE.to_bits(), I256::from_str_radix("100", 10).unwrap());
2113        assert_eq!(super::D76s2::MAX.to_bits(), I256::MAX);
2114        assert_eq!(super::D76s2::MIN.to_bits(), I256::MIN);
2115        assert_eq!(super::D76s2::multiplier(), I256::from_str_radix("100", 10).unwrap());
2116        assert_eq!(super::D76s2::SCALE, 2);
2117        assert_eq!(super::D76s2::ZERO.scale(), 2);
2118        // SCALE = 75 (new MAX_SCALE) multiplier is 10^75, well within 256-bit range.
2119        let m75 = super::D76s75::multiplier();
2120        assert_eq!(
2121            m75,
2122            I256::from_str_radix("1000000000000000000000000000000000000000000000000000000000000000000000000000", 10).unwrap()
2123        );
2124        assert_eq!(<super::D76s12 as DecimalArithmetic>::MAX_SCALE, 75);
2125        // round-trip
2126        let raw = I256::from_str_radix("123456789012345678901234567890", 10).unwrap();
2127        assert_eq!(super::D76s12::from_bits(raw).to_bits(), raw);
2128    }
2129
2130    #[cfg(any(feature = "d76", feature = "wide"))]
2131    #[test]
2132    fn d76_arithmetic() {
2133        type D = super::D76<12>;
2134        let one = D::ONE;
2135        let two = D::from_bits(D::multiplier() + D::multiplier());
2136        let three = D::from_bits(D::multiplier() * crate::wide_int::I256::from_str_radix("3", 10).unwrap());
2137        // add / sub / neg
2138        assert_eq!((one + two), three);
2139        assert_eq!((three - one), two);
2140        assert_eq!((-one).to_bits(), -D::multiplier());
2141        // mul: 2 * 3 == 6
2142        let six = D::from_bits(D::multiplier() * crate::wide_int::I256::from_str_radix("6", 10).unwrap());
2143        assert_eq!((two * three), six);
2144        // div: 6 / 2 == 3
2145        assert_eq!((six / two), three);
2146        // rem: 6 % 2 == 0 (storage-level remainder)
2147        assert_eq!((six % two), D::ZERO);
2148        // assign forms
2149        let mut v = one;
2150        v += two;
2151        assert_eq!(v, three);
2152        v *= two;
2153        assert_eq!(v, six);
2154        v /= two;
2155        assert_eq!(v, three);
2156        v -= one;
2157        assert_eq!(v, two);
2158        v %= two;
2159        assert_eq!(v, D::ZERO);
2160        // fractional: 1.5 * 1.5 == 2.25 at scale 12
2161        let half = D::from_bits(D::multiplier() / crate::wide_int::I256::from_str_radix("2", 10).unwrap());
2162        let one_and_half = one + half;
2163        let product = one_and_half * one_and_half;
2164        let expected = D::from_bits(
2165            D::multiplier() * crate::wide_int::I256::from_str_radix("2", 10).unwrap()
2166                + D::multiplier() / crate::wide_int::I256::from_str_radix("4", 10).unwrap(),
2167        );
2168        assert_eq!(product, expected);
2169    }
2170
2171    #[cfg(any(feature = "d76", feature = "wide"))]
2172    #[test]
2173    fn d76_display() {
2174        type D = super::D76<12>;
2175        let one = D::ONE;
2176        assert_eq!(alloc::format!("{}", one), "1.000000000000");
2177        assert_eq!(alloc::format!("{}", -one), "-1.000000000000");
2178        assert_eq!(alloc::format!("{}", D::ZERO), "0.000000000000");
2179        let half = D::from_bits(D::multiplier() / crate::wide_int::I256::from_str_radix("2", 10).unwrap());
2180        assert_eq!(alloc::format!("{}", half), "0.500000000000");
2181        assert_eq!(alloc::format!("{:?}", one), "D76<12>(1.000000000000)");
2182        // scale 0 prints no fractional part
2183        let int_only: super::D76<0> = super::D76::<0>::ONE;
2184        assert_eq!(alloc::format!("{}", int_only), "1");
2185        // very large magnitude near the 75-digit ceiling (new MAX_SCALE)
2186        let big = super::D76s75::MAX;
2187        let s = alloc::format!("{}", big);
2188        assert!(s.starts_with("57.8960446"));
2189        assert_eq!(s.len(), "57.".len() + 75);
2190    }
2191
2192    #[cfg(any(feature = "d76", feature = "wide"))]
2193    #[test]
2194    fn d76_sign_and_helpers() {
2195        type D = super::D76<6>;
2196        let neg = -D::ONE;
2197        assert!(neg.is_negative());
2198        assert!(D::ONE.is_positive());
2199        assert!(!D::ZERO.is_positive());
2200        assert_eq!(neg.abs(), D::ONE);
2201        assert_eq!(D::ONE.signum(), D::ONE);
2202        assert_eq!(neg.signum(), neg);
2203        assert_eq!(D::ZERO.signum(), D::ZERO);
2204        // min / max / clamp
2205        let two = D::ONE + D::ONE;
2206        assert_eq!(D::ONE.min(two), D::ONE);
2207        assert_eq!(D::ONE.max(two), two);
2208        assert_eq!(two.clamp(D::ZERO, D::ONE), D::ONE);
2209        // copysign
2210        assert_eq!(D::ONE.copysign(neg), neg);
2211        assert_eq!(neg.copysign(D::ONE), D::ONE);
2212        // recip: 1/2 at scale 6
2213        let half = D::from_bits(D::multiplier() / crate::wide_int::I256::from_str_radix("2", 10).unwrap());
2214        assert_eq!(two.recip(), half);
2215    }
2216
2217    #[cfg(any(feature = "d76", feature = "wide"))]
2218    #[test]
2219    fn d76_overflow_variants() {
2220        type D = super::D76<2>;
2221        // checked_add overflow at MAX
2222        assert_eq!(D::MAX.checked_add(D::ONE), None);
2223        assert_eq!(D::ONE.checked_add(D::ONE), Some(D::ONE + D::ONE));
2224        // saturating
2225        assert_eq!(D::MAX.saturating_add(D::ONE), D::MAX);
2226        assert_eq!(D::MIN.saturating_sub(D::ONE), D::MIN);
2227        // checked_neg of MIN overflows
2228        assert_eq!(D::MIN.checked_neg(), None);
2229        assert_eq!(D::ONE.checked_neg(), Some(-D::ONE));
2230        // checked_mul / checked_div
2231        let two = D::ONE + D::ONE;
2232        let three = two + D::ONE;
2233        assert_eq!(two.checked_mul(three), Some(D::from_bits(D::multiplier() * crate::wide_int::I256::from_str_radix("6", 10).unwrap())));
2234        assert_eq!(D::ONE.checked_div(D::ZERO), None);
2235        assert_eq!((three).checked_div(D::ONE), Some(three));
2236        // wrapping_add of one storage LSB at MAX wraps around to MIN.
2237        let one_lsb = D::from_bits(crate::wide_int::I256::from_str_radix("1", 10).unwrap());
2238        assert_eq!(D::MAX.wrapping_add(one_lsb), D::MIN);
2239        // overflowing
2240        assert_eq!(D::ONE.overflowing_add(D::ONE), (two, false));
2241        assert_eq!(D::MAX.overflowing_add(D::ONE).1, true);
2242    }
2243
2244    #[cfg(any(feature = "d76", feature = "wide"))]
2245    #[test]
2246    fn d76_consts_and_from_str() {
2247        use crate::types::consts::DecimalConstants;
2248        use core::str::FromStr;
2249        // pi at scale 12 matches the D38 reference.
2250        assert_eq!(
2251            super::D76::<12>::pi().to_bits(),
2252            crate::wide_int::I256::from_str_radix("3141592653590", 10).unwrap()
2253        );
2254        assert_eq!(
2255            super::D76::<4>::e().to_bits(),
2256            crate::wide_int::I256::from_str_radix("27183", 10).unwrap()
2257        );
2258        // FromStr within i128 range
2259        let v = super::D76::<2>::from_str("1.50").unwrap();
2260        assert_eq!(v.to_bits(), crate::wide_int::I256::from_str_radix("150", 10).unwrap());
2261        let neg = super::D76::<2>::from_str("-20.50").unwrap();
2262        assert_eq!(neg.to_bits(), crate::wide_int::I256::from_str_radix("-2050", 10).unwrap());
2263        // num_traits Zero / One
2264        use ::num_traits::{One, Zero};
2265        assert!(super::D76::<6>::zero().is_zero());
2266        assert!(super::D76::<6>::one().is_one());
2267    }
2268
2269    #[cfg(any(feature = "d76", feature = "wide"))]
2270    #[test]
2271    fn d76_conversions() {
2272        use crate::wide_int::I256;
2273        type D = super::D76<6>;
2274        // From<primitive int>
2275        let from_i32: D = 5i32.into();
2276        assert_eq!(from_i32.to_bits(), I256::from_str_radix("5000000", 10).unwrap());
2277        let from_u64: D = 7u64.into();
2278        assert_eq!(from_u64.to_bits(), I256::from_str_radix("7000000", 10).unwrap());
2279        let from_neg: D = (-3i16).into();
2280        assert_eq!(from_neg.to_bits(), I256::from_str_radix("-3000000", 10).unwrap());
2281        // TryFrom<i128> / TryFrom<u128>
2282        let from_i128 = D::try_from(123i128).unwrap();
2283        assert_eq!(from_i128.to_bits(), I256::from_str_radix("123000000", 10).unwrap());
2284        let from_u128 = D::try_from(u128::MAX).unwrap();
2285        assert_eq!(
2286            from_u128.to_bits(),
2287            I256::from_str_radix("340282366920938463463374607431768211455", 10).unwrap()
2288                * I256::from_str_radix("1000000", 10).unwrap()
2289        );
2290        // TryFrom<f64>
2291        let from_f64 = D::try_from(2.5f64).unwrap();
2292        assert_eq!(from_f64.to_bits(), I256::from_str_radix("2500000", 10).unwrap());
2293        assert!(D::try_from(f64::NAN).is_err());
2294        // from_int / from_i32
2295        assert_eq!(D::from_int(9i128), D::from(9i32));
2296        assert_eq!(D::from_i32(-4), D::from(-4i32));
2297        // to_int: 2.5 with HalfToEven -> 2
2298        use crate::support::rounding::RoundingMode;
2299        let two_and_half = D::from_bits(I256::from_str_radix("2500000", 10).unwrap());
2300        assert_eq!(two_and_half.to_int_with(RoundingMode::HalfToEven), 2);
2301        assert_eq!(two_and_half.to_int_with(RoundingMode::HalfAwayFromZero), 3);
2302        assert_eq!(two_and_half.to_int_with(RoundingMode::Ceiling), 3);
2303        assert_eq!(two_and_half.to_int_with(RoundingMode::Floor), 2);
2304        let neg_two_and_half = -two_and_half;
2305        assert_eq!(neg_two_and_half.to_int_with(RoundingMode::Floor), -3);
2306        assert_eq!(neg_two_and_half.to_int_with(RoundingMode::Trunc), -2);
2307        // cross-width widening D38 -> D76 (lossless)
2308        let d38: super::D38s6 = super::D38s6::from_bits(-150);
2309        let widened: super::D76<6> = d38.into();
2310        assert_eq!(widened.to_bits(), I256::from_str_radix("-150", 10).unwrap());
2311        // cross-width narrowing D76 -> D38 in range
2312        let in_range: super::D76<6> = super::D76::<6>::from_bits(I256::from_str_radix("999", 10).unwrap());
2313        let narrowed: super::D38s6 = in_range.try_into().unwrap();
2314        assert_eq!(narrowed.to_bits(), 999i128);
2315        // cross-width narrowing D76 -> D38 out of range
2316        let out_of_range = super::D76s75::MAX;
2317        let narrow_fail: Result<super::D38<75>, _> = out_of_range.try_into();
2318        assert!(narrow_fail.is_err());
2319    }
2320
2321    #[cfg(any(feature = "d76", feature = "wide"))]
2322    #[test]
2323    fn d76_rescale_rounding_floats() {
2324        use crate::support::rounding::RoundingMode;
2325        use crate::wide_int::I256;
2326        type D6 = super::D76<6>;
2327        // rescale up (lossless): scale 6 -> scale 9
2328        let v = D6::from_bits(I256::from_str_radix("1500000", 10).unwrap()); // 1.5
2329        let up: super::D76<9> = v.rescale::<9>();
2330        assert_eq!(up.to_bits(), I256::from_str_radix("1500000000", 10).unwrap());
2331        // rescale down (lossy, HalfToEven): scale 6 -> scale 2
2332        let down: super::D76<2> = v.rescale::<2>();
2333        assert_eq!(down.to_bits(), I256::from_str_radix("150", 10).unwrap());
2334        // rescale down with explicit mode: 2.5 (scale 0 representation) ...
2335        let two_p_five = super::D76::<1>::from_bits(I256::from_str_radix("25", 10).unwrap());
2336        let r0: super::D76<0> = two_p_five.rescale_with::<0>(RoundingMode::HalfToEven);
2337        assert_eq!(r0.to_bits(), I256::from_str_radix("2", 10).unwrap());
2338        let r0b: super::D76<0> = two_p_five.rescale_with::<0>(RoundingMode::HalfAwayFromZero);
2339        assert_eq!(r0b.to_bits(), I256::from_str_radix("3", 10).unwrap());
2340        // floor / ceil / round / trunc / fract on 1.5 at scale 6
2341        assert_eq!(v.floor(), D6::ONE);
2342        assert_eq!(v.ceil(), D6::ONE + D6::ONE);
2343        assert_eq!(v.round(), D6::ONE + D6::ONE); // half away from zero
2344        assert_eq!(v.trunc(), D6::ONE);
2345        assert_eq!(v.fract(), D6::from_bits(I256::from_str_radix("500000", 10).unwrap()));
2346        // negative: -1.5
2347        let neg = -v;
2348        assert_eq!(neg.floor(), -(D6::ONE + D6::ONE));
2349        assert_eq!(neg.ceil(), -D6::ONE);
2350        assert_eq!(neg.round(), -(D6::ONE + D6::ONE));
2351        // float bridge
2352        let from_f = D6::from_f64(2.5);
2353        assert_eq!(from_f.to_bits(), I256::from_str_radix("2500000", 10).unwrap());
2354        assert_eq!(D6::from_f64(f64::NAN), D6::ZERO);
2355        assert_eq!(D6::from_f64(f64::INFINITY), D6::MAX);
2356        let round_trip = D6::ONE.to_f64();
2357        assert!((round_trip - 1.0).abs() < 1e-9);
2358    }
2359
2360    #[cfg(any(feature = "d153", feature = "wide"))]
2361    #[test]
2362    fn d153_smoke() {
2363        
2364        use crate::types::traits::arithmetic::DecimalArithmetic;
2365        use crate::wide_int::I512;
2366        type D = super::D153<35>;
2367        assert_eq!(<D as DecimalArithmetic>::MAX_SCALE, 152);
2368        assert_eq!(D::ZERO.to_bits(), I512::from_str_radix("0", 10).unwrap());
2369        let one = D::ONE;
2370        let two = one + one;
2371        let three = two + one;
2372        assert_eq!(two * three, D::from_int(6i128));
2373        assert_eq!((three * two) / two, three);
2374        assert_eq!(alloc::format!("{}", one).len(), "1.".len() + 35);
2375        assert_eq!(D::from_int(5i128).to_int(), 5);
2376        // rescale across the wide range
2377        let up: super::D153<150> = one.rescale::<150>();
2378        assert_eq!(up, super::D153::<150>::ONE);
2379        // 152-digit ceiling multiplier fits in I512 (new MAX_SCALE)
2380        let _ = super::D153s152::multiplier();
2381    }
2382
2383    #[cfg(any(feature = "d307", feature = "wide"))]
2384    #[test]
2385    fn d307_smoke() {
2386        
2387        use crate::types::traits::arithmetic::DecimalArithmetic;
2388        use crate::wide_int::I1024;
2389        type D = super::D307<35>;
2390        assert_eq!(<D as DecimalArithmetic>::MAX_SCALE, 306);
2391        let one = D::ONE;
2392        let two = one + one;
2393        let three = two + one;
2394        assert_eq!(two * three, D::from_int(6i128));
2395        assert_eq!((three * two) / two, three);
2396        assert_eq!(D::ZERO.to_bits(), I1024::from_str_radix("0", 10).unwrap());
2397        assert_eq!(alloc::format!("{}", one).len(), "1.".len() + 35);
2398        // cross-width: D76 -> D307 widening, D307 -> D76 narrowing
2399        #[cfg(any(feature = "d76", feature = "wide"))]
2400        {
2401            let small: super::D76<35> = super::D76::<35>::ONE;
2402            let widened: super::D307<35> = small.into();
2403            assert_eq!(widened, D::ONE);
2404            let narrowed: super::D76<35> = widened.try_into().unwrap();
2405            assert_eq!(narrowed, super::D76::<35>::ONE);
2406        }
2407        // 306-digit ceiling multiplier fits in I1024 (new MAX_SCALE)
2408        let _ = super::D307s306::multiplier();
2409    }
2410
2411    #[test]
2412    fn d9_op_assign() {
2413        let mut v = super::D9s2::from_bits(100);
2414        v += super::D9s2::from_bits(50);
2415        assert_eq!(v.to_bits(), 150);
2416        v -= super::D9s2::from_bits(25);
2417        assert_eq!(v.to_bits(), 125);
2418        v *= super::D9s2::from_bits(200); // *2.00
2419        assert_eq!(v.to_bits(), 250);
2420        v /= super::D9s2::from_bits(200); // /2.00
2421        assert_eq!(v.to_bits(), 125);
2422        v %= super::D9s2::from_bits(100);
2423        assert_eq!(v.to_bits(), 25);
2424    }
2425}