Skip to main content

decimal_scaled/
lib.rs

1//! Const-generic base-10 fixed-point decimal types for deterministic arithmetic.
2//!
3//! # Overview
4//!
5//! `decimal-scaled` provides a family of fixed-point decimal types whose stored
6//! integer encodes `actual_value * 10^SCALE`. Decimal literals like `1.1`
7//! round-trip exactly without any binary approximation, and all core arithmetic
8//! is integer-only — identical bit-patterns on every platform.
9//!
10//! # Primary types
11//!
12//! Each width has a `D<digits><const SCALE: u32>` const-generic shape with the
13//! same method surface; pick the narrowest that fits your range. The number on
14//! every `D{N}` type is `MAX_SCALE` — the highest `SCALE` the storage can hold.
15//!
16//! | Type | Storage | `MAX_SCALE` | Feature gate |
17//! |------|---------|-------------|--------------|
18//! | [`D9<SCALE>`]    | `i32`     |    9 | always on |
19//! | [`D18<SCALE>`]   | `i64`     |   18 | always on |
20//! | [`D38<SCALE>`]   | `i128`    |   38 | always on |
21//! | [`D57<SCALE>`]   | 192-bit   |   57 | `d57` or `wide` |
22//! | [`D76<SCALE>`]   | 256-bit   |   76 | `d76` or `wide` |
23//! | [`D115<SCALE>`]  | 384-bit   |  115 | `d115` or `wide` |
24//! | [`D153<SCALE>`]  | 512-bit   |  153 | `d153` or `wide` |
25//! | [`D230<SCALE>`]  | 768-bit   |  230 | `d230` or `wide` |
26//! | [`D307<SCALE>`]  | 1024-bit  |  307 | `d307` or `wide` |
27//! | [`D462<SCALE>`]  | 1536-bit  |  462 | `d462` or `x-wide` |
28//! | [`D616<SCALE>`]  | 2048-bit  |  616 | `d616` or `x-wide` |
29//! | [`D924<SCALE>`]  | 3072-bit  |  924 | `d924` or `xx-wide` |
30//! | [`D1232<SCALE>`] | 4096-bit  | 1232 | `d1232` or `xx-wide` |
31//!
32//! Umbrellas: `wide` enables D57 / D76 / D115 / D153 / D230 / D307;
33//! `x-wide` adds D462 + D616; `xx-wide` adds D924 + D1232. Every
34//! adjacent pair has lossless `.widen()` / fallible `.narrow()`
35//! helpers plus `From` / `TryFrom` impls.
36//!
37//! Concrete scale aliases such as `D38s12 = D38<12>` are emitted for every
38//! supported `SCALE`. `SCALE = MAX_SCALE + 1` is rejected at compile time —
39//! `10^(MAX_SCALE+1)` overflows the storage type.
40//!
41//! The width-generic [`Decimal`] trait carries the surface that is identical
42//! across widths (constants, arithmetic operators, sign methods, integer
43//! variants, pow / checked / wrapping / saturating / overflowing, float bridge,
44//! Euclidean / floor / ceil division, etc.). Use it to write helpers that work
45//! across widths; reach for the concrete type for width-specific operations
46//! like `rescale::<TARGET>()` whose const-generic parameter cannot live on a
47//! trait method.
48//!
49//! # Equality and hashing
50//!
51//! Because each logical value has exactly one representation at a fixed scale,
52//! `Hash`, `Eq`, `PartialEq`, `PartialOrd`, and `Ord` are all derived from the
53//! underlying integer storage. Two `Dxx<S>` values compare equal if and only
54//! if their raw bit patterns are identical. This gives predictable behaviour
55//! when decimal values are used as `HashMap` keys, unlike variable-scale
56//! decimal types where `1.10` and `1.1` may hash differently.
57//!
58//! # `num-traits` compatibility
59//!
60//! Every width implements the standard `num-traits` 0.2 surface:
61//! `Zero`, `One`, `Num`, `Bounded`, `Signed`, `FromPrimitive`,
62//! `ToPrimitive`, and the `Checked{Add,Sub,Mul,Div,Rem,Neg}` family
63//! (see [`::num_traits`]). These impls are unconditional (not behind a
64//! feature flag) because generic numeric code in the wider ecosystem
65//! consumes this surface by default.
66//!
67//! # `no_std` support
68//!
69//! The crate compiles with `no_std + alloc` when default features are
70//! disabled. `alloc` is required for `Display::to_string` and
71//! `FromStr::from_str`. Targets without `alloc` are not supported.
72//!
73//! # Feature flags
74//!
75//! - `std` (default): enables the fast implementations of transcendental
76//! functions (trigonometry, logarithms, exponentials, square root, cube
77//! root, float power) that delegate to platform `f64` intrinsics.
78//! - `alloc`: pulled in automatically; required for string formatting and
79//! parsing.
80//! - `serde`: enables `serde_helpers` for serialisation and deserialisation.
81//! - `strict`: enables integer-only implementations of all transcendental
82//! functions. When `strict` is active each function that would otherwise
83//! route through `f64` is instead implemented using integer-only
84//! algorithms. Explicit float-conversion methods (`to_f64`,
85//! `from_f64`, etc.) remain available regardless; they are type
86//! conversions, not mathematical operations. `strict` does not require
87//! `std`; the integer transcendental implementations compile under
88//! `no_std + alloc`.
89
90#![cfg_attr(not(feature = "std"), no_std)]
91#![cfg_attr(feature = "experimental-floats", feature(f16, f128))]
92// ── Clippy allow-list ─────────────────────────────────────────────────
93//
94// These are pedantic lints whose patterns this crate uses
95// intentionally and pervasively. Each is justified inline; allowing
96// them at the crate level is preferable to spraying per-site
97// `#[allow]` attributes or rewriting against the crate's domain.
98#![allow(
99    // Decimal width names overlap with type prefixes; the lint adds no
100    // signal here.
101    clippy::module_name_repetitions,
102    // We use unindented Markdown continuation in module docs.
103    clippy::doc_lazy_continuation,
104    // We routinely place a blank line between a method's `#[cfg]`
105    // attribute and its doc/body for readability.
106    clippy::empty_line_after_outer_attr,
107    // Big-integer arithmetic regularly casts between signed/unsigned
108    // and between widths. The wraps / truncations / sign flips are
109    // intentional — `unsigned_abs` paths, two's-complement tricks,
110    // narrowing the final result back to storage after a widened mul.
111    clippy::cast_possible_truncation,
112    clippy::cast_possible_wrap,
113    clippy::cast_sign_loss,
114    // We prefer `as` casts over `T::from(x)` in arithmetic-heavy
115    // inner loops for readability and to match the surrounding
116    // big-integer idiom.
117    clippy::cast_lossless,
118    // Float bridges (`to_f64`, `to_f32`) are explicitly lossy by
119    // contract. The lint is a tautology here.
120    clippy::cast_precision_loss,
121    // Literals like `1_000_000_000_000` carry the scale visually and
122    // are kept unseparated when they encode `10^SCALE`.
123    clippy::unreadable_literal,
124    // `if cond { panic!(…) }` is the crate's canonical bounds-check
125    // shape; `assert!(…)` would lose the dynamic message.
126    clippy::manual_assert,
127    // `Result<_, ()>` is the only honest error type for `const fn`
128    // digit-validity checks where no allocator is available.
129    clippy::result_unit_err,
130    // `if …; if …` chains read more cleanly than `if … && …` in the
131    // const-fn limb-arithmetic helpers.
132    clippy::collapsible_if,
133    // Big-int / fixed-point inner loops use `i`, `j`, `k`, `n`, `m`
134    // as conventional names. Renaming to `outer_index` etc. hurts
135    // readability without payoff.
136    clippy::similar_names,
137    clippy::many_single_char_names,
138    // Strict-transcendental kernels exceed 100 lines because they
139    // unroll a series-evaluation loop; splitting them just to please
140    // the line-count lint would scatter the algorithm.
141    clippy::too_many_lines,
142    // `#[inline(always)]` is set deliberately on small hot-path
143    // helpers (`apply_rounding`, `panic_or_wrap_*`). The lint
144    // assumes the inliner knows better; here we override on purpose.
145    clippy::inline_always,
146    // Strict-vs-fast comparisons in `tests/` deliberately compare
147    // raw `f64` results bit-for-bit. The lint can't tell test code
148    // from production.
149    clippy::float_cmp,
150    // Some narrow helpers `let result = …; result + 1` are flagged
151    // as let-else candidates; the explicit form is clearer in the
152    // big-int helpers.
153    clippy::manual_let_else,
154    // `format!("{x}") + "y"` is fine when both pieces stay tiny.
155    clippy::format_push_string,
156    // `if-else-if` chains over disjoint conditions sometimes read
157    // more clearly than `match` (especially with `<` / `>=` arms).
158    clippy::comparison_chain,
159    // Macro-emitted methods that return `Self` are wrapped with
160    // `#[must_use]` where it would catch bugs; the lint's
161    // recommendation on tiny constructors is noise.
162    clippy::must_use_candidate,
163    clippy::return_self_not_must_use,
164    // `# Errors` / `# Panics` sections: every public function's
165    // behaviour on error / panic is described in its main doc
166    // paragraph (and matches the pattern of the std-library
167    // primitive it shadows). The lint's per-section requirement
168    // adds boilerplate without information.
169    clippy::missing_errors_doc,
170    clippy::missing_panics_doc,
171    // Doc-comment backticks are added where they matter (type and
172    // function names); the lint flags every identifier-looking
173    // word, including math symbols and abbreviations.
174    clippy::doc_markdown,
175)]
176
177#[cfg(feature = "alloc")]
178extern crate alloc;
179
180// Re-export `tracing` under the crate so the perf-trace cfg-gated
181// `info_span!` calls in macro-emitted modules can reach it via
182// `$crate::tracing::…`. Internal-only — gated by the same feature.
183#[cfg(feature = "perf-trace")]
184#[doc(hidden)]
185pub use ::tracing;
186
187mod algos;
188mod identity;
189mod support;
190mod types;
191#[cfg(feature = "bench-alt")]
192#[doc(hidden)]
193pub mod __bench_internals {
194    #[inline(never)]
195    pub fn limbs_mul(a: &[u128], b: &[u128], out: &mut [u128]) {
196        crate::wide_int::limbs_mul(a, b, out)
197    }
198    #[inline(never)]
199    pub fn limbs_mul_fast(a: &[u128], b: &[u128], out: &mut [u128]) {
200        crate::wide_int::limbs_mul_fast(a, b, out)
201    }
202    #[inline(never)]
203    pub fn mul_slice(a: &[u64], b: &[u64], out: &mut [u64]) {
204        crate::wide_int::limbs_mul_u64(a, b, out)
205    }
206    #[inline(never)]
207    pub fn mul_fixed<const L: usize, const D: usize>(
208        a: &[u64; L],
209        b: &[u64; L],
210        out: &mut [u64; D],
211    ) {
212        crate::wide_int::limbs_mul_u64_fixed::<L, D>(a, b, out)
213    }
214    #[inline(never)]
215    pub fn mul_u64_into<const L: usize, const LP1: usize>(
216        a: &[u64; L],
217        n: u64,
218        out: &mut [u64; LP1],
219    ) {
220        crate::wide_int::limbs_mul_u64_into::<L, LP1>(a, n, out)
221    }
222}
223mod macros;
224
225// `bitwise` and `num_traits_impls` used to live here as test-only
226// modules; their tests now run as Cargo integration tests under
227// `tests/`. The macro-generated impls themselves are emitted by
228// `decl_decimal_bitwise!` / `decl_decimal_num_traits_basics!` from
229// `types/widths.rs`, alongside every other surface.
230//
231// `wide_int` is unconditional. D38's strict transcendentals use
232// `Int512` as their guard-digit work integer (replacing the previous
233// `algos::fixed_d38::Fixed` 256-bit sign-magnitude type), so the wide-
234// integer family must be available in every feature configuration —
235// not just `feature = "wide"` builds. Compile-time impact is modest:
236// ~2k LOC of self-contained limb arithmetic plus the per-width
237// `decl_wide_int!` instantiations.
238mod wide_int;
239mod policy;
240
241#[cfg(feature = "serde")]
242pub use crate::support::serde_helpers;
243
244
245pub use crate::types::consts::DecimalConstants;
246pub use crate::types::traits::DecimalArithmetic;
247pub use crate::types::traits::DecimalConvert;
248pub use crate::types::unified::D;
249pub use crate::types::traits::Decimal;
250pub use crate::support::error::{ConvertError, ParseError};
251pub use crate::support::rounding::RoundingMode;
252pub use crate::types::traits::DecimalTranscendental;
253
254#[cfg(feature = "dyn")]
255pub use crate::types::traits::dyn_decimal::{DecimalWidth, DynDecimal, RawStorage};
256
257// D38 — the 128-bit foundation, plus every scale alias D38s0..=D38s37
258// (v0.4.0 cap: MAX_SCALE = name - 1).
259pub use crate::types::widths::{
260    D38, D38s0, D38s1, D38s2, D38s3, D38s4, D38s5, D38s6, D38s7, D38s8, D38s9, D38s10,
261    D38s11, D38s12, D38s13, D38s14, D38s15, D38s16, D38s17, D38s18, D38s19, D38s20,
262    D38s21, D38s22, D38s23, D38s24, D38s25, D38s26, D38s27, D38s28, D38s29, D38s30,
263    D38s31, D38s32, D38s33, D38s34, D38s35, D38s36, D38s37,
264};
265
266// D9 — 32-bit storage, scale 0..=9.
267pub use crate::types::widths::{
268    D9, D9s0, D9s1, D9s2, D9s3, D9s4, D9s5, D9s6, D9s7, D9s8,
269};
270
271// D18 — 64-bit storage, scale 0..=18.
272pub use crate::types::widths::{
273    D18, D18s0, D18s1, D18s2, D18s3, D18s4, D18s5, D18s6, D18s7, D18s8, D18s9, D18s10, D18s11,
274    D18s12, D18s13, D18s14, D18s15, D18s16, D18s17,
275};
276
277// D76 — 256-bit storage, behind the `d76` / `wide` features.
278#[cfg(any(feature = "d76", feature = "wide"))]
279pub use crate::types::widths::{
280    D76,
281    D76s0, D76s1, D76s2, D76s3, D76s4, D76s6, D76s9, D76s12, D76s15,
282    D76s18, D76s20, D76s24, D76s28, D76s32, D76s35, D76s38, D76s42,
283    D76s48, D76s50, D76s56, D76s64, D76s70, D76s75,
284};
285
286// The hand-rolled wide-integer types — the storage backend for the
287// wide decimal tiers, also useful on their own.
288#[cfg(any(feature = "d76", feature = "d153", feature = "d307", feature = "wide"))]
289pub use wide_int::{
290    Int256, Int512, Int1024, Int2048, Int4096, Uint256, Uint512, Uint1024, Uint2048, Uint4096,
291};
292
293// D153 — 512-bit storage, behind the `d153` / `wide` features.
294#[cfg(any(feature = "d153", feature = "wide"))]
295pub use crate::types::widths::{
296    D153,
297    D153s0, D153s1, D153s2, D153s4, D153s6, D153s9, D153s12, D153s15,
298    D153s18, D153s20, D153s24, D153s28, D153s32, D153s35, D153s38,
299    D153s50, D153s57, D153s75, D153s76, D153s100, D153s115, D153s140,
300    D153s150, D153s152,
301};
302
303// D307 — 1024-bit storage, behind the `d307` / `wide` features.
304#[cfg(any(feature = "d307", feature = "wide"))]
305pub use crate::types::widths::{
306    D307,
307    D307s0, D307s1, D307s2, D307s4, D307s6, D307s9, D307s12, D307s15,
308    D307s18, D307s20, D307s24, D307s28, D307s32, D307s35, D307s38,
309    D307s50, D307s75, D307s100, D307s115, D307s150, D307s153,
310    D307s200, D307s230, D307s275, D307s300, D307s306,
311};
312
313// ─── New half-width and wider tiers ───────────────────────────────────
314
315// D57 — 192-bit storage; half-width between D38 and D76.
316#[cfg(any(feature = "d57", feature = "wide"))]
317pub use crate::types::widths::{
318    D57,
319    D57s0, D57s1, D57s2, D57s4, D57s6, D57s9, D57s12, D57s18, D57s20, D57s24,
320    D57s28, D57s32, D57s38, D57s42, D57s48, D57s52, D57s56,
321};
322#[cfg(any(feature = "d57", feature = "wide"))]
323pub use wide_int::{Int192, Uint192};
324
325// D115 — 384-bit; half-width between D76 and D153.
326#[cfg(any(feature = "d115", feature = "wide"))]
327pub use crate::types::widths::{
328    D115,
329    D115s0, D115s1, D115s4, D115s8, D115s16, D115s24, D115s32, D115s38, D115s50,
330    D115s57, D115s64, D115s76, D115s90, D115s100, D115s110, D115s114,
331};
332#[cfg(any(feature = "d115", feature = "wide"))]
333pub use wide_int::{Int384, Uint384};
334
335// D230 — 768-bit; half-width between D153 and D307.
336#[cfg(any(feature = "d230", feature = "wide"))]
337pub use crate::types::widths::{
338    D230,
339    D230s0, D230s1, D230s6, D230s18, D230s38, D230s57, D230s75, D230s100, D230s115,
340    D230s140, D230s153, D230s175, D230s200, D230s215, D230s225, D230s229,
341};
342#[cfg(any(feature = "d230", feature = "wide"))]
343pub use wide_int::{Int768, Uint768};
344
345// D462 — 1536-bit; half-width between D307 and D616.
346#[cfg(any(feature = "d462", feature = "x-wide"))]
347pub use crate::types::widths::{
348    D462,
349    D462s0, D462s1, D462s18, D462s38, D462s75, D462s115, D462s153, D462s200, D462s230,
350    D462s275, D462s307, D462s350, D462s400, D462s440, D462s460, D462s461,
351};
352#[cfg(any(feature = "d462", feature = "x-wide"))]
353pub use wide_int::{Int1536, Uint1536};
354
355// D616 — 2048-bit; new top wide tier. Int2048 / Uint2048 are
356// already exported above for x-wide / d307 widening; no re-export
357// here.
358#[cfg(any(feature = "d616", feature = "x-wide"))]
359pub use crate::types::widths::{
360    D616,
361    D616s0, D616s1, D616s38, D616s75, D616s115, D616s153, D616s200, D616s230, D616s275,
362    D616s308, D616s380, D616s462, D616s500, D616s555, D616s600, D616s615,
363};
364
365// D924 — 3072-bit; half-width between D616 and D1232.
366#[cfg(any(feature = "d924", feature = "xx-wide"))]
367pub use crate::types::widths::{
368    D924,
369    D924s0, D924s1, D924s75, D924s153, D924s230, D924s307, D924s400, D924s461, D924s462,
370    D924s500, D924s616, D924s700, D924s800, D924s860, D924s900, D924s920, D924s923,
371};
372#[cfg(any(feature = "d924", feature = "xx-wide"))]
373pub use wide_int::{Int3072, Int6144, Int12288, Uint3072, Uint6144, Uint12288};
374
375// D1232 — 4096-bit; widest tier shipped.
376#[cfg(any(feature = "d1232", feature = "xx-wide"))]
377pub use crate::types::widths::{
378    D1232,
379    D1232s0, D1232s1, D1232s75, D1232s153, D1232s230, D1232s307, D1232s461, D1232s616,
380    D1232s700, D1232s800, D1232s900, D1232s924, D1232s1000, D1232s1100,
381    D1232s1180, D1232s1220, D1232s1230, D1232s1231,
382};
383#[cfg(any(feature = "d1232", feature = "xx-wide"))]
384pub use wide_int::{Int8192, Int16384, Uint8192, Uint16384};
385
386// ─── Construction macros (re-exports + per-scale wrappers) ────────────
387
388/// The narrow-tier proc-macros are always available with the
389/// `macros` feature; the wide-tier proc-macros are additionally
390/// feature-gated to match their target type's availability.
391#[cfg(feature = "macros")]
392pub use decimal_scaled_macros::{d9, d18, d38};
393
394#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))]
395pub use decimal_scaled_macros::d76;
396
397#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))]
398pub use decimal_scaled_macros::d153;
399
400#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))]
401pub use decimal_scaled_macros::d307;
402
403#[cfg(all(feature = "macros", any(feature = "d57", feature = "wide")))]
404pub use decimal_scaled_macros::d57;
405
406#[cfg(all(feature = "macros", any(feature = "d115", feature = "wide")))]
407pub use decimal_scaled_macros::d115;
408
409#[cfg(all(feature = "macros", any(feature = "d230", feature = "wide")))]
410pub use decimal_scaled_macros::d230;
411
412#[cfg(all(feature = "macros", any(feature = "d462", feature = "x-wide")))]
413pub use decimal_scaled_macros::d462;
414
415#[cfg(all(feature = "macros", any(feature = "d616", feature = "x-wide")))]
416pub use decimal_scaled_macros::d616;
417
418#[cfg(all(feature = "macros", any(feature = "d924", feature = "xx-wide")))]
419pub use decimal_scaled_macros::d924;
420
421#[cfg(all(feature = "macros", any(feature = "d1232", feature = "xx-wide")))]
422pub use decimal_scaled_macros::d1232;
423
424// Per-scale wrappers — curated subset of pre-baked
425// `<dN>s<SCALE>!` macros that forward to the corresponding
426// proc-macro with `scale N` added. Long-tail scales remain
427// reachable via the explicit `, scale N` qualifier.
428//
429// Each alias is a tiny `macro_rules!`. We don't generate them
430// through a nested macro because `macro_rules!` doesn't support
431// directly emitting another `macro_rules!` without `$$` escapes
432// that aren't available in stable Rust; explicit per-line
433// declarations keep things debuggable and only cost ~40 lines.
434
435// D9 curated scales.
436/// `d9s0!(value)` — equivalent to `d9!(value, scale 0)`.
437#[cfg(feature = "macros")] #[macro_export]
438macro_rules! d9s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 0  $(, $($rest)*)?) }; }
439/// `d9s2!(value)` — equivalent to `d9!(value, scale 2)`.
440#[cfg(feature = "macros")] #[macro_export]
441macro_rules! d9s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 2  $(, $($rest)*)?) }; }
442/// `d9s4!(value)` — equivalent to `d9!(value, scale 4)`.
443#[cfg(feature = "macros")] #[macro_export]
444macro_rules! d9s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 4  $(, $($rest)*)?) }; }
445/// `d9s6!(value)` — equivalent to `d9!(value, scale 6)`.
446#[cfg(feature = "macros")] #[macro_export]
447macro_rules! d9s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d9!($v, scale 6  $(, $($rest)*)?) }; }
448
449// D18 curated scales.
450/// `d18s0!(value)` — equivalent to `d18!(value, scale 0)`.
451#[cfg(feature = "macros")] #[macro_export]
452macro_rules! d18s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 0  $(, $($rest)*)?) }; }
453/// `d18s2!(value)` — equivalent to `d18!(value, scale 2)`.
454#[cfg(feature = "macros")] #[macro_export]
455macro_rules! d18s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 2  $(, $($rest)*)?) }; }
456/// `d18s4!(value)` — equivalent to `d18!(value, scale 4)`.
457#[cfg(feature = "macros")] #[macro_export]
458macro_rules! d18s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 4  $(, $($rest)*)?) }; }
459/// `d18s6!(value)` — equivalent to `d18!(value, scale 6)`.
460#[cfg(feature = "macros")] #[macro_export]
461macro_rules! d18s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 6  $(, $($rest)*)?) }; }
462/// `d18s9!(value)` — equivalent to `d18!(value, scale 9)`.
463#[cfg(feature = "macros")] #[macro_export]
464macro_rules! d18s9  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 9  $(, $($rest)*)?) }; }
465/// `d18s12!(value)` — equivalent to `d18!(value, scale 12)`.
466#[cfg(feature = "macros")] #[macro_export]
467macro_rules! d18s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d18!($v, scale 12 $(, $($rest)*)?) }; }
468
469// D38 curated scales.
470/// `d38s0!(value)` — equivalent to `d38!(value, scale 0)`.
471#[cfg(feature = "macros")] #[macro_export]
472macro_rules! d38s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 0  $(, $($rest)*)?) }; }
473/// `d38s2!(value)` — equivalent to `d38!(value, scale 2)`.
474#[cfg(feature = "macros")] #[macro_export]
475macro_rules! d38s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 2  $(, $($rest)*)?) }; }
476/// `d38s4!(value)` — equivalent to `d38!(value, scale 4)`.
477#[cfg(feature = "macros")] #[macro_export]
478macro_rules! d38s4  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 4  $(, $($rest)*)?) }; }
479/// `d38s6!(value)` — equivalent to `d38!(value, scale 6)`.
480#[cfg(feature = "macros")] #[macro_export]
481macro_rules! d38s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 6  $(, $($rest)*)?) }; }
482/// `d38s8!(value)` — equivalent to `d38!(value, scale 8)`.
483#[cfg(feature = "macros")] #[macro_export]
484macro_rules! d38s8  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 8  $(, $($rest)*)?) }; }
485/// `d38s9!(value)` — equivalent to `d38!(value, scale 9)`.
486#[cfg(feature = "macros")] #[macro_export]
487macro_rules! d38s9  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 9  $(, $($rest)*)?) }; }
488/// `d38s12!(value)` — equivalent to `d38!(value, scale 12)`.
489#[cfg(feature = "macros")] #[macro_export]
490macro_rules! d38s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 12 $(, $($rest)*)?) }; }
491/// `d38s15!(value)` — equivalent to `d38!(value, scale 15)`.
492#[cfg(feature = "macros")] #[macro_export]
493macro_rules! d38s15 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 15 $(, $($rest)*)?) }; }
494/// `d38s18!(value)` — equivalent to `d38!(value, scale 18)`.
495#[cfg(feature = "macros")] #[macro_export]
496macro_rules! d38s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 18 $(, $($rest)*)?) }; }
497/// `d38s24!(value)` — equivalent to `d38!(value, scale 24)`.
498#[cfg(feature = "macros")] #[macro_export]
499macro_rules! d38s24 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 24 $(, $($rest)*)?) }; }
500/// `d38s35!(value)` — equivalent to `d38!(value, scale 35)`.
501#[cfg(feature = "macros")] #[macro_export]
502macro_rules! d38s35 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d38!($v, scale 35 $(, $($rest)*)?) }; }
503
504// D76 curated scales.
505/// `d76s0!(value)` — equivalent to `d76!(value, scale 0)`.
506#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
507macro_rules! d76s0  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 0  $(, $($rest)*)?) }; }
508/// `d76s2!(value)` — equivalent to `d76!(value, scale 2)`.
509#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
510macro_rules! d76s2  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 2  $(, $($rest)*)?) }; }
511/// `d76s6!(value)` — equivalent to `d76!(value, scale 6)`.
512#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
513macro_rules! d76s6  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 6  $(, $($rest)*)?) }; }
514/// `d76s12!(value)` — equivalent to `d76!(value, scale 12)`.
515#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
516macro_rules! d76s12 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 12 $(, $($rest)*)?) }; }
517/// `d76s18!(value)` — equivalent to `d76!(value, scale 18)`.
518#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
519macro_rules! d76s18 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 18 $(, $($rest)*)?) }; }
520/// `d76s35!(value)` — equivalent to `d76!(value, scale 35)`.
521#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
522macro_rules! d76s35 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 35 $(, $($rest)*)?) }; }
523/// `d76s50!(value)` — equivalent to `d76!(value, scale 50)`.
524#[cfg(all(feature = "macros", any(feature = "d76", feature = "wide")))] #[macro_export]
525macro_rules! d76s50 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d76!($v, scale 50 $(, $($rest)*)?) }; }
526
527// D153 curated scales.
528/// `d153s0!(value)` — equivalent to `d153!(value, scale 0)`.
529#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
530macro_rules! d153s0   { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 0   $(, $($rest)*)?) }; }
531/// `d153s35!(value)` — equivalent to `d153!(value, scale 35)`.
532#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
533macro_rules! d153s35  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 35  $(, $($rest)*)?) }; }
534/// `d153s75!(value)` — equivalent to `d153!(value, scale 75)`.
535#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
536macro_rules! d153s75  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 75  $(, $($rest)*)?) }; }
537/// `d153s150!(value)` — equivalent to `d153!(value, scale 150)`.
538#[cfg(all(feature = "macros", any(feature = "d153", feature = "wide")))] #[macro_export]
539macro_rules! d153s150 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d153!($v, scale 150 $(, $($rest)*)?) }; }
540
541// D307 curated scales.
542/// `d307s0!(value)` — equivalent to `d307!(value, scale 0)`.
543#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
544macro_rules! d307s0   { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 0   $(, $($rest)*)?) }; }
545/// `d307s35!(value)` — equivalent to `d307!(value, scale 35)`.
546#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
547macro_rules! d307s35  { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 35  $(, $($rest)*)?) }; }
548/// `d307s150!(value)` — equivalent to `d307!(value, scale 150)`.
549#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
550macro_rules! d307s150 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 150 $(, $($rest)*)?) }; }
551/// `d307s300!(value)` — equivalent to `d307!(value, scale 300)`.
552#[cfg(all(feature = "macros", any(feature = "d307", feature = "wide", feature = "x-wide")))] #[macro_export]
553macro_rules! d307s300 { ($v:tt $(, $($rest:tt)*)?) => { $crate::d307!($v, scale 300 $(, $($rest)*)?) }; }