[package]
name = "decimal-scaled"
version = "0.5.0"
edition = "2024"
rust-version = "1.89"
license = "MIT OR Apache-2.0"
description = "Const-generic base-10 fixed-point decimals (D18/D38/D76/D153/D307 and the half-width tiers up to D1232) with integer-only transcendentals correctly rounded to within 0.5 ULP — exact at the type's last representable place. Deterministic across every platform; no_std-friendly."
repository = "https://github.com/mootable/decimal-scaled"
homepage = "https://github.com/mootable/decimal-scaled"
documentation = "https://mootable.github.io/decimal-scaled/"
readme = "README.md"
keywords = ["decimal", "fixed-point", "deterministic", "no-std", "precision"]
categories = ["mathematics", "no-std", "science", "finance"]
include = [
    "src/**",
    "/README.md",
    "/CHANGELOG.md",
    "/CONTRIBUTORS.md",
    "/LICENSE*",
    "/LICENSES/**",
]

# docs.rs renders the doc build with whichever features are enabled
# here, not the crate's default-feature set. Turning on the wide
# tiers, the macros, and serde means every public type and macro
# (D18/D38/D76/D153/D307, all `dNN!` proc-macros, `serde_helpers`)
# is visible on the rendered page. Experimental-floats and the
# mutually-exclusive `rounding-*` flags are deliberately omitted so
# the docs build doesn't require nightly or pick a non-default
# rounding mode.
[package.metadata.docs.rs]
features = ["std", "serde", "strict", "macros", "wide", "x-wide", "xx-wide", "dyn"]
rustdoc-args = ["--cfg", "docsrs"]

[features]
# `strict` is on by default. The crate's headline feature is the
# correctly-rounded (<=0.5 ULP) integer-only transcendentals;
# plain `ln` / `sin` / `sqrt` / etc. dispatch to that path.
#
# Strict is ALSO the dispatch when no feature is set at all; the
# explicit `strict` feature mostly signals intent and survives a
# transitive `fast` flip from a downstream crate (which still
# resolves to strict — strict wins when both are present).
#
# To opt out and use the f64 bridge instead you have to take all
# three steps: (a) `default-features = false`, (b) explicitly add
# `fast` + `std`, and (c) NOT re-add `strict`:
#   decimal-scaled = { version = "0.5.0", default-features = false,
#                      features = ["std", "serde", "fast"] }
# That gives ~16 decimal digits of platform-libm precision rather
# than the deterministic 0.5 ULP strict path.
default = ["std", "serde", "strict", "exact-scratch"]
# `std` requires `exact-scratch`: the std build's kernels assume the exact
# per-N scratch sizing; a std build without it is NOT a supported
# configuration (working-scale arithmetic can outgrow the blanket scratch).
std = ["alloc", "exact-scratch"]
alloc = []
serde = ["dep:serde"]
macros = ["dep:decimal_scaled_macros"]
# Marks the build as on the strict path: plain `sin` / `ln` / etc.
# dispatch to the integer-only, <=0.5 ULP, platform-deterministic
# `*_strict` methods. On by default. Does NOT require `std` — the
# integer-only transcendentals compile under `no_std`.
strict = []
# Runtime-polymorphic façade. Adds the `DynDecimal` object-safe trait
# and `DecimalWidth` enum, with a blanket impl bridging every concrete
# `Dxx<S>` type. Off by default — typed `Decimal` users pay nothing.
# Boxing cost (heap allocation per binary op) is the price of erasure,
# so the façade pulls in `alloc` (its `Box`/`String` returns are the
# crate's only sanctioned heap path; the typed `Decimal` core stays
# heap-free).
dyn = ["alloc"]
# Opt out of strict dispatch: plain transcendentals forward to the
# f64 bridge. Only takes effect when `strict` is NOT enabled — if
# both are present, strict wins. Both `*_strict` and `*_fast`
# named methods are emitted regardless of which feature is on; this
# feature only chooses what plain `*` resolves to.
#
# Implies `std`: the f64 bridge uses the inherent `f64` transcendental
# intrinsics (`sin`/`cos`/`exp`/…), which are std-only under the crate's
# float policy (std owns floats; no_std is integer-only, never libm). So
# `fast` is a std-build convenience and cannot combine with `no_std`.
fast = ["std"]
# Compiles BOTH the macro-default and the per-type override
# implementations for every method that has them. Default builds compile
# only the chosen winner (see the `src/support/bench_alt.rs` module
# docs). With `bench-alt` on
# benchmarks can call `*_strict_default` and `*_strict_override` side by
# side without recompiling. Off by default — adds build time and binary
# size for no runtime benefit unless you're benching.
bench-alt = []
# Compile-time selector for the default RoundingMode used by the
# no-arg `rescale` family (and any future default-rounding methods).
# If none of these is enabled, the default is HalfToEven (IEEE-754
# default; banker's rounding). Mutually exclusive in intent — if more
# than one is enabled, the priority order in `src/rounding.rs` picks
# canonically.
rounding-half-away-from-zero = []
rounding-half-toward-zero = []
rounding-trunc = []
rounding-floor = []
rounding-ceiling = []
# Nightly-only: expose `from_f16` / `to_f16` /
# `from_f128` / `to_f128` on each decimal type. f16 and
# f128 are experimental in Rust; this feature requires the nightly
# toolchain. On stable, leave the feature off.
experimental-floats = []
# Nightly-only: auto-inferred cross-scale operations via
# `generic_const_exprs`. Adds a `decimal_scaled::cross` module with
# free functions `cross::mul(a, b)` / `cross::add(a, b)` / …
# returning a decimal at `max(width_a, width_b)`, `max(SCALE_a,
# SCALE_b)`. Without this feature the stable `D{N}<S>::mul_of(a, b)`
# explicit-target form is the only cross-scale entry point.
# Requires the nightly toolchain.
cross-scale-ops = []
# Root-kernel work-scratch sizing. Default (neither feature) sizes the
# scratch buffers to the build-max width and applies it uniformly. With
# `exact-scratch` (stable) each storage width gets a per-`Int<N>` impl
# sized exactly `mult·N + ceil(N/2)` — smaller frames, no nightly.
# `exact-scratch-nightly` does the same per-`N` sizing with one blanket
# impl via `generic_const_exprs` (the const-expr stays in that impl block).
# See `crate::int::types::compute_limbs`.
exact-scratch = []
exact-scratch-nightly = ["exact-scratch"]
# Wide decimal tiers (D57 … D1232). Each width is independently opt-in
# (`d57` … `d1232`), and the umbrellas NEST: `wide` = D57/D76/D115/D153/
# D230/D307, `x-wide` = `wide` + D462 + D616, `xx-wide` = `x-wide` + D924 +
# D1232 — so a wider umbrella enables every tier up to it. All are backed by
# the in-tree const-generic `Int<N>` integers in `crate::int` — no external
# big-integer dependency. D18 / D38 use primitive i64 / i128 storage and need
# none of this.
# Internal marker: enabled by EVERY wide-family tier (d57 … d1232) so
# the shared wide-tier support code (the `wide_roots` /
# `wide_transcendental` macro modules, the `decl_wide_serde` macro, the
# `DecimalConstants` / `Int` imports in `consts/wide.rs`, etc.) is
# compiled in whenever any single wide tier is on. Not meant to be set
# directly by downstreams — turn on a concrete tier (or an umbrella)
# instead. Without this marker each shared-support `cfg` would have to
# enumerate all twelve tier features by hand and a single-tier build
# (`--features d57`) would miss the support it depends on.
#
# Heap-free (Phase 9): the wide-tier transcendental lookup tables used to
# be heap-cached (`thread_local!` + `alloc::vec::Vec`); those runtime
# caches were removed in favour of stateless on-stack recompute, so the
# wide-support path no longer needs `alloc`. This marker pulls in NO
# features — `_wide-support = []` keeps it as the heap-free backstop: a
# `no_std` single-tier build (`--no-default-features --features d57`) gets
# no `alloc`, so any reintroduced `alloc::vec::Vec` on the wide path is a
# hard compile error rather than a silent heap allocation. (`std` still
# pulls `alloc` for the std build; that is the std backend's own affair.)
_wide-support = []
wide = ["d57", "d76", "d115", "d153", "d230", "d307"]
d57 = ["_wide-support"]
d76 = ["_wide-support"]
d115 = ["_wide-support"]
d153 = ["_wide-support"]
d230 = ["_wide-support"]
# Very-wide tiers (D462 + D616), nesting `wide`. Gated separately because
# their strict-transcendental work integers run into thousands of bits and
# inflate build time vs the narrower wide tiers.
x-wide = ["wide", "d462", "d616"]
d307 = ["_wide-support"]
d462 = ["_wide-support"]
d616 = ["_wide-support"]
# Extra-wide tiers (D924 + D1232), nesting `x-wide`. Pulls in the Int12288 /
# Int16384 work integers — multi-second compile-time cost for the macro emission.
xx-wide = ["x-wide", "d924", "d1232"]
d924 = ["_wide-support"]
d1232 = ["_wide-support"]
# Internal perf-investigation feature: emits `tracing::info_span!`
# blocks at the section boundaries of the wide-tier strict
# transcendental cores (`exp_fixed`, `ln_fixed`, …). With
# `tracing-chrome` set up in an example, the resulting JSON loads in
# https://ui.perfetto.dev for a per-section flame view. Default-off;
# the spans compile to nothing without the feature, so production
# users see zero overhead.
perf-trace = ["dep:tracing"]

[dependencies]
serde = { version = "1", features = ["derive"], optional = true }
# No `libm`: the crate's float policy is std-owns-floats / no_std-is-
# integer-only, so we never use `num_traits::Float` (which is the only
# part needing libm under no_std). num-traits is kept solely for the
# `FromPrimitive`/`ToPrimitive`/`NumCast` bridge used by `from_num`/
# `to_num`, none of which require libm.
num-traits = { version = "0.2", default-features = false }
decimal_scaled_macros = { path = "macros", version = "0.5.0", default-features = false, optional = true }
tracing = { version = "0.1", default-features = false, features = ["std"], optional = true }

[dev-dependencies]
criterion = { version = "0.8", default-features = false, features = ["cargo_bench_support"] }
i256 = { version = "0.2", default-features = false }
serde_json = "1"
postcard = { version = "1", default-features = false, features = ["alloc"] }
decimal_scaled_macros = { path = "macros" }
# Perf-trace examples wire tracing-chrome to write Perfetto/Chrome JSON.
tracing-chrome = "0.7"
tracing-subscriber = { version = "0.3", default-features = false, features = ["std"] }
# Benchmark-only baselines for the backend comparisons (see benches/).
# None of these are normal dependencies.
bnum = { version = "0.14", default-features = false }
ruint = { version = "1", default-features = false }
rust_decimal = { version = "1", default-features = false, features = ["maths"] }
fixed = "1"
# g_math: another fixed-point arithmetic crate that claims "0 ULP
# transcendentals". Used as a benchmark baseline + accuracy comparison
# (see benches/g_math_comparison.rs).
g_math = "0.4"
# Library-comparison comparators for the §5 chapter in docs/benchmarks.md.
# All bench-only; never compiled into a normal build.
fastnum = { version = "0.7", default-features = false, features = ["std"] }
bigdecimal = "0.4"
dashu-float = "0.4"
# dashu-int provides IBig for exact base-10 integer scaling of dashu-float
# results in the comparison benches.
dashu-int = { version = "0.4", default-features = false, features = ["std"] }
decimal-rs = "0.1"
scientific = "0.5"
# Chart generation for docs/figures/library_comparison/*.png — pure
# Rust, no Python dep. Driven by examples/chart_gen.rs.
plotters = { version = "0.3", default-features = false, features = ["bitmap_backend", "bitmap_encoder", "line_series", "ttf"] }
# Pin the bench process to a single core (int_ops_micro + the
# precision-fix impact microbench) so the criterion timing loop doesn't
# migrate cross-core mid-measurement. Cross-platform, MIT/Apache. Bench-only.
core_affinity = "0.8"

# Profiling / internal-benchmark examples need extra features; gating them
# with required-features means a plain `cargo test` (without these features)
# skips them rather than failing to build.
[[example]]
name = "karabench"
required-features = ["bench-alt"]

[[example]]
name = "karabench_u64"
required-features = ["bench-alt"]

[[example]]
name = "int_div_probe"
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

[[example]]
name = "ln_probe"
required-features = ["wide", "x-wide", "xx-wide"]

[[example]]
name = "atan_probe"
required-features = ["wide", "x-wide", "xx-wide"]

[[example]]
name = "profile_atan_perfetto"
required-features = ["perf-trace"]

[[example]]
name = "profile_exp_perfetto"
required-features = ["perf-trace"]

# Examples/tests that reference the wide-only decimal tiers (D57…D1232)
# must declare the tier features they use, so a default `cargo build
# --all-targets` (narrow tiers only) skips them instead of failing to
# build with E0432 unresolved-import on the wide types.
[[example]]
name = "agm_mpmath_validate"
required-features = ["x-wide", "xx-wide"]

[[example]]
name = "divbench"
required-features = ["wide", "x-wide"]

[[example]]
name = "expbench"
required-features = ["wide", "x-wide"]

[[example]]
name = "hyper_recover"
required-features = ["wide", "x-wide", "xx-wide"]

[[example]]
name = "profile_exp"
required-features = ["x-wide"]

[[example]]
name = "rounding_mode_probe"
required-features = ["wide"]

[[example]]
name = "mul_toom3_probe"
required-features = ["wide", "bench-alt"]

[[example]]
name = "profile_rescale"
required-features = ["wide", "x-wide", "xx-wide", "macros", "bench-alt"]

[[example]]
name = "sqrtbench"
required-features = ["wide", "x-wide"]

[[example]]
name = "test_pi"
required-features = ["wide", "x-wide", "xx-wide"]

[[example]]
name = "ulp_report"
required-features = ["wide"]

[[bench]]
name = "mul_div_candidates"
path = "benches/micro/mul_div_candidates.rs"
harness = false

[[bench]]
name = "dec_mul_wide_ab"
path = "benches/micro/dec_mul_wide_ab.rs"
harness = false
required-features = ["wide", "x-wide"]

# Focused micro-bench for the wide-int decimal `to_string` base case
# (#38): base-10^19 19-digit chunking vs the historic one-divide-per-
# digit loop, across Int256 / Int512 / Int1024 / Int4096.
[[bench]]
name = "int_to_string_base1e19"
path = "benches/micro/int_to_string_base1e19.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide"]

# Per-PR perf-regression micro-bench gate, run with plain criterion
# (a small, focused set). Bench file: benches/micro/pr_gate.rs.
[[bench]]
name = "pr_gate"
path = "benches/micro/pr_gate.rs"
harness = false
required-features = ["wide"]

[[bench]]
name = "all_functions"
path = "benches/support/all_functions.rs"
harness = false

# Focused micro-bench for the powf_strict integer-exponent fast path
# (perf/powf-integer-fastpath-0.4.3). Compares the integer-valued
# exponents 2.0 / 3.0 (fast path) against the non-integer 0.5 / 2.5
# (slow exp(y · ln x) path) across D38<19>, D76<35>, D307<150>.
[[bench]]
name = "powf_integer"
path = "benches/micro/powf_integer.rs"
harness = false
required-features = ["wide", "x-wide"]

# Focused micro-bench for D38<33>::log10_strict / log2_strict / etc.
# Used to validate the borrow-D57 migration of the secondary log/exp
# entry points (ln/exp were borrowed by commit 09e7ea9).
[[bench]]
name = "d38_s33_log10_micro"
path = "benches/lookup/d38_s33_log10_micro.rs"
harness = false
required-features = ["wide"]

# Wide-integer backend comparison: native hand-rolled `Int256` vs
# `bnum` / `ruint`. Needs the wide tier.
[[bench]]
name = "wide_int_backends"
path = "benches/backends/wide_int_backends.rs"
harness = false
required-features = ["wide"]

# Per-width baseline for the in-tree wide signed integers
# (`Int256` … `Int4096`): add/sub/mul/div/rem, pow, square, cube.
# Microbench-scale snapshot for tracking integer-algorithm changes.
[[bench]]
name = "int_ops_micro"
path = "benches/micro/int_ops_micro.rs"
harness = false
required-features = ["wide"]

[[bench]]
name = "wide_round_shell"
path = "benches/micro/wide_round_shell.rs"
harness = false
required-features = ["wide", "x-wide"]

# Decimal / fixed-point backend comparison: native `D38` / `D76` vs
# `bnum`-backed `D76`, `rust_decimal`, and `fixed` — arithmetic and
# transcendentals (including the fast vs strict variants). Needs the
# wide tier.
[[bench]]
name = "decimal_backends"
path = "benches/backends/decimal_backends.rs"
harness = false
required-features = ["wide"]

# D38 mul / div: native Moller-Granlund path vs the wide-arm
# (Int256-widening) algorithm head-to-head.
[[bench]]
name = "d_w128_mul_div_paths"
path = "benches/micro/d_w128_mul_div_paths.rs"
harness = false
required-features = ["wide"]

# Newton-Raphson reciprocal divide vs MG chain divide at D307/D616/D1232.
# Research bench — informs ALGORITHMS.md "Evaluated and not used".
[[bench]]
name = "newton_vs_mg"
path = "benches/micro/newton_vs_mg.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# AGM vs artanh / Taylor strict transcendentals at the deepest tier
# (D307<300>). Tracks the AGM path's throughput so a future bench-
# driven promotion has a data point.
[[bench]]
name = "agm_vs_taylor"
path = "benches/agm/agm_vs_taylor.rs"
harness = false
required-features = ["x-wide"]

# AGM vs artanh / Taylor across the D616 scale band — 250 / 300 / 400 /
# 500. Brent's AGM ln crossover is around ~300 decimal digits, so D616
# straddles the transition. Captures the data for a future width-
# override decision on the deep mid-storage band.
[[bench]]
name = "agm_vs_taylor_d616"
path = "benches/agm/agm_vs_taylor_d616.rs"
harness = false
required-features = ["x-wide"]

# AGM vs artanh / Taylor at D924 — the first half-width tier whose
# upper SCALE band (700+) might tip past the Brent AGM crossover after
# D616 measurements (SCALE 250/300/400/500) all showed AGM LOSING.
[[bench]]
name = "agm_vs_taylor_d924"
path = "benches/agm/agm_vs_taylor_d924.rs"
harness = false
required-features = ["xx-wide"]

# AGM vs artanh / Taylor at D230<115>. Mid-storage crossover probe:
# the AGM ln crossover (Brent 1976) is around ~300 decimal digits;
# D230<115> sits at ~131 working-bit decimal digits and is expected
# to LOSE to Brent-reduced artanh — the bench captures the data
# point for completeness.
[[bench]]
name = "agm_vs_taylor_d230"
path = "benches/agm/agm_vs_taylor_d230.rs"
harness = false
required-features = ["wide"]

# Like-for-like comparison vs `g_math` (another fixed-point crate that
# claims "0 ULP transcendentals"). Runs at decimal precision matched
# to g_math's default Q64.64 (~19 decimal digits = D38<19>) and reports
# both timing and an accuracy delta against the higher-precision
# D76<35> baseline.
[[bench]]
name = "g_math_comparison"
path = "benches/backends/g_math_comparison.rs"
harness = false
required-features = ["wide"]


# Focused microbench for wide-tier div/mul during tuning passes.
# Runs in ~30s, used to validate per-change wins before kicking
# the full lib_cmp_d{N} sweep.
[[bench]]
name = "quick_div"
path = "benches/micro/quick_div.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide"]

# Focused D57<20> per-op microbench — the primary production scale
# for the wide-tier. ~30 s wall, used for tight iteration loops on
# SCALE 20 kernel tuning before kicking the full sweeps.
[[bench]]
name = "d57_s20"
path = "benches/lookup/d57_s20.rs"
harness = false
required-features = ["wide"]

# Focused D76<6> per-op microbench — narrow-SCALE wide-tier cell
# where the wide_transcendental working scale (w = 36) is inside
# the MG (magic-multiply) base-2^128 long-divide kernel's
# `1..=38` window. Used to probe MG-routing wins on hot loops.
[[bench]]
name = "d76_s6"
path = "benches/lookup/d76_s6.rs"
harness = false
required-features = ["wide"]

# Focused D153<76> per-op microbench — mid-storage popular scale on
# the 512-bit storage / Int1024 working-integer tier. Used to probe
# narrow-GUARD and Tang-lookup wins on the D153 hot paths.
[[bench]]
name = "d153_s76"
path = "benches/lookup/d153_s76.rs"
harness = false
required-features = ["wide"]

# Focused D616<308> per-op microbench — mid-storage popular scale on
# the 2048-bit storage / Int8192 working-integer tier. Used to probe
# narrow-GUARD Tang-lookup wins (and the AGM crossover for ln/exp) at
# the D616 hot paths.
[[bench]]
name = "d616_s308"
path = "benches/lookup/d616_s308.rs"
harness = false
required-features = ["x-wide"]

# Side-by-side D616 ln/exp/sinh/cosh/tanh at SCALE 295 (wide_kernel)
# vs SCALE 308 (Tang lookup). Isolates the lookup-vs-kernel speedup
# from the underlying Int8192 primitive cost.
[[bench]]
name = "d616_lookup_baseline"
path = "benches/lookup/d616_lookup_baseline.rs"
harness = false
required-features = ["x-wide"]

# Focused D924<460> per-op microbench — mid-storage popular scale on
# the 3072-bit storage / Int12288 working-integer tier. Used to probe
# narrow-GUARD Tang-lookup wins (and the AGM crossover for ln/exp) at
# the D924 hot paths.
[[bench]]
name = "d924_s460"
path = "benches/lookup/d924_s460.rs"
harness = false
required-features = ["xx-wide"]

# Side-by-side D924 ln at SCALE 440 (wide_kernel) vs SCALE 460 (Tang
# lookup). Isolates the lookup-vs-kernel speedup from the underlying
# Int12288 primitive cost.
[[bench]]
name = "d924_lookup_baseline"
path = "benches/lookup/d924_lookup_baseline.rs"
# Side-by-side D1232 ln at SCALE 600 (wide_kernel) vs SCALE 615 (Tang
# lookup). Isolates the lookup-vs-kernel speedup from the underlying
# Int16384 primitive cost.
[[bench]]
name = "d1232_lookup_baseline"
path = "benches/lookup/d1232_lookup_baseline.rs"
harness = false
required-features = ["xx-wide"]

# AGM vs canonical artanh ln at D1232 across SCALE 615 / 800 / 1000 /
# 1200 — Brent's AGM ln crossover sits around ~300 decimal digits, so
# D1232 is the first width where the canonical artanh path may finally
# lose to AGM at the deep end of the scale range.
[[bench]]
name = "agm_vs_taylor_d1232"
path = "benches/agm/agm_vs_taylor_d1232.rs"
harness = false
required-features = ["xx-wide"]

# Gate bench for the per-L fixed-array mul specialisation.
[[bench]]
name = "m2_mul_fixed"
path = "benches/micro/m2_mul_fixed.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# Gate bench for the single-u64-multiplier specialisation
# `limbs_mul_u64_into` vs `limbs_mul_u64_fixed` with a single-limb
# operand. Validates the n×1 schoolbook win across every wide tier.
[[bench]]
name = "m2_mul_u64_into"
path = "benches/micro/m2_mul_u64_into.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# Multi-input atan comparison across decimal-scaled / fastnum /
# dashu-float at D38<19>. Times 8 input classes to expose
# per-library fast-path coverage (fastnum's NaN-out for |x|>1,
# our 3 new fast paths, etc.).
[[bench]]
name = "atan_inputs"
path = "benches/support/atan_inputs.rs"
harness = false
required-features = ["wide"]

# Gate for ln_approx(working_digits) — caller-controlled precision.
[[bench]]
name = "m2_ln_approx"
path = "benches/micro/m2_ln_approx.rs"
harness = false
required-features = ["wide"]

[[bench]]
name = "prec_fix_impact"
path = "benches/micro/prec_fix_impact.rs"
harness = false
required-features = ["wide", "x-wide", "macros"]

[[bench]]
name = "div_recover_ab"
path = "benches/micro/div_recover_ab.rs"
harness = false
required-features = ["wide", "x-wide", "macros", "bench-alt"]

[lints.rust]
# The crate is 100% safe Rust; keep it that way.
unsafe_code = "forbid"
unused_variables = "allow"
dead_code = "allow"

# Crate-wide clippy policy — applies to lib code, tests, benches,
# and the build script. Mirrors the `#![allow(...)]` block at the
# top of `src/lib.rs` (which is still needed because `lints.clippy`
# was introduced in Cargo 1.74 and the per-file directive supports
# Rust toolchains that don't yet have the manifest-level feature).
[lints.clippy]
module_name_repetitions = "allow"
doc_lazy_continuation = "allow"
empty_line_after_outer_attr = "allow"
cast_possible_truncation = "allow"
cast_possible_wrap = "allow"
cast_sign_loss = "allow"
cast_lossless = "allow"
cast_precision_loss = "allow"
unreadable_literal = "allow"
manual_assert = "allow"
result_unit_err = "allow"
collapsible_if = "allow"
similar_names = "allow"
many_single_char_names = "allow"
too_many_lines = "allow"
inline_always = "allow"
float_cmp = "allow"
manual_let_else = "allow"
format_push_string = "allow"
comparison_chain = "allow"
must_use_candidate = "allow"
return_self_not_must_use = "allow"
missing_errors_doc = "allow"
missing_panics_doc = "allow"
doc_markdown = "allow"
# Lints relevant only to tests / benches.
items_after_statements = "allow"
uninlined_format_args = "allow"
semicolon_if_nothing_returned = "allow"
should_panic_without_expect = "allow"
nonminimal_bool = "allow"
assertions_on_constants = "allow"
bool_assert_comparison = "allow"
bool_to_int_with_if = "allow"

[workspace]
members = [
    ".",
    "./macros",
    "./decimal-scaled-golden",
    "./decimal-scaled-cells",
    "./decimal-scale-test",
    "./golden-competitors",
]
resolver = "3"

# `debug = true` emits a full PDB (function names + line info) on
# Windows MSVC and full DWARF on Linux/Mac. Required for sample-based
# profilers (samply, cargo-flamegraph) to resolve frames to symbol
# names — `line-tables-only` only carries file:line and leaves the
# function-name table empty, which makes flame graphs unsymbolicated.
# Doesn't affect optimisation level; ~30% binary growth from debug
# info, but the PDB is a sibling file on Windows (not in the .exe)
# so runtime perf and shipped binary size are unchanged.
[profile.release]
debug = true

# A/B microbench worked example: schoolbook vs forced-Karatsuba multiply
# kernel at two widths, driven by the reusable `ab_microbench` support
# module (benches/ab_microbench.rs). The fast pre-check for a multiply
# policy `Select` arm. Kernels come from `__bench_internals` (bench-alt).
[[bench]]
name = "mul_kernel_ab"
path = "benches/micro/mul_kernel_ab.rs"
harness = false
required-features = ["wide", "bench-alt"]

# hypot_ab: dispatch-seam A/B for the integer hypot policy — the production
# Pythagoras path vs the native-u128 narrow fast path (the D38 regression).
[[bench]]
name = "hypot_ab"
path = "benches/micro/hypot_ab.rs"
harness = false
required-features = ["wide", "bench-alt"]

# isqrt_ab: N-way dispatch-seam A/B for the integer isqrt policy — native
# (hardware u64/u128, N<=2) vs newton (f64-seeded + Knuth div) vs schoolbook
# (bitwise reference), across the full width sweep × magnitude classes
# (incl. perfect-square ±1 floor-correction edges).
[[bench]]
name = "isqrt_ab"
path = "benches/micro/isqrt_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# icbrt_ab: N-way dispatch-seam A/B for the integer icbrt policy — newton
# (f64-cbrt-seeded Brent–Zimmermann, the wired kernel for every N) vs
# schoolbook (bitwise restoring reference), across the full width sweep ×
# radicand magnitude classes (incl. perfect-cube ±1 floor-correction edges).
[[bench]]
name = "icbrt_ab"
path = "benches/micro/icbrt_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# int_cube_eq_ab: dispatch-seam A/B for the integer cube and eq policies —
# cube schoolbook (sqr-then-multiply, wired) vs comba (fused product-scanning
# candidate); eq limbwise (cmp-kernel reuse, wired) vs xor_fold (branchless
# OR-fold candidate), across the storage-width sweep N.
[[bench]]
name = "int_cube_eq_ab"
path = "benches/micro/int_cube_eq_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# sum_sq_ab: N-way dispatch-seam A/B for the integer sum-of-squares policy —
# schoolbook (the wired kernel, each square a general x*x product) vs comba
# (a kept alternative, symmetric product-scanning square ~L^2/2 mults),
# across the full width sweep x operand significant-limb magnitude classes.
[[bench]]
name = "sum_sq_ab"
path = "benches/micro/sum_sq_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# pow_ab: N-way dispatch-seam A/B for the integer exponentiation policy —
# square_and_multiply (O(log exp), the wired kernel) vs schoolbook (O(exp)
# repeated multiply, the reference), across the full width sweep x exponent
# magnitude classes (e2..e31). Confirms the square-and-multiply routing.
[[bench]]
name = "pow_ab"
path = "benches/micro/pow_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# to_radians_ab: dispatch-seam A/B for the wide-tier to_radians angle
# conversion — the public method path (through the double dispatch +
# resize_to round-trips) vs the MulPiRatio kernel called directly. Isolates
# the routing overhead behind the to_radians regression vs 0.4.4.
[[bench]]
name = "to_radians_ab"
path = "benches/micro/to_radians_ab.rs"
harness = false
required-features = ["wide", "bench-alt"]

# to_radians_baseline: per-call public-method timing matched to the
# v0.4.4 to_radians_v044 baseline (same tiers/scales/inputs) for a direct
# 0.4.4-vs-0.5.0 comparison.
[[bench]]
name = "to_radians_baseline"
path = "benches/micro/to_radians_baseline.rs"
harness = false
required-features = ["wide"]

# {exp,ln,trig}_series_tang_ab: dispatch-seam A/Bs for the transcendental
# Series-vs-Tang policy choice. The `policy::{exp,ln,trig}::select` Tang bands
# were tuned via kernel-ISOLATION benches; these A/B the production Tang kernel
# vs the generic Series kernel at the band edges (in-band + just-out probes),
# bit-identity-asserted before timing, on the `wide`-buildable narrow-wide
# tiers (D57/D115/D153).
[[bench]]
name = "exp_series_tang_ab"
path = "benches/micro/exp_series_tang_ab.rs"
harness = false
required-features = ["wide", "bench-alt"]

[[bench]]
name = "ln_series_tang_ab"
path = "benches/micro/ln_series_tang_ab.rs"
harness = false
required-features = ["wide", "bench-alt"]

[[bench]]
name = "trig_series_tang_ab"
path = "benches/micro/trig_series_tang_ab.rs"
harness = false
required-features = ["wide", "bench-alt"]

# mul_low_u128_ab: pilot A/B for the wide-tier exp/powf Taylor
# work-multiply -- the truncated-low base-2^64 `mul_low_fixed` vs the
# u128-packed `mul_low_fixed_u128` candidate at the D616/D924/D1232 work
# integer widths (N = 128/192/256) plus the storage widths for reference.
[[bench]]
name = "mul_low_u128_ab"
path = "benches/micro/mul_low_u128_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# mul_full_ab: N-way A/B for the int::policy::mul FULL equal-length product
# -- slice schoolbook (reference) vs the fixed-width mul_full_limb::<N,u64>,
# the u128-packed mul_full_limb::<N,u128> (even N), and one-level Karatsuba,
# across N = 2..64. Localizes the u64/u128 LimbSize crossover and the
# schoolbook/Karatsuba threshold; bit-identicality asserted before timing.
[[bench]]
name = "mul_full_ab"
path = "benches/micro/mul_full_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# mul_toom3_ab: N-way A/B for the Toom-Cook 3-way integer multiply (step-1 bench)
# The int-mul policy map: every fixed-array candidate (schoolbook/Karatsuba/
# Toom-3 x u64/u128) raced per width across 24..256 (step-2 measure).
[[bench]]
name = "mul_toom3_ab"
path = "benches/micro/mul_toom3_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# Karatsuba-u128 recursion-threshold sweep at the crossover region (step-2 tuning).
[[bench]]
name = "mul_kara_thresh_ab"
path = "benches/micro/mul_kara_thresh_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# sqr_low_u128_ab: pilot A/B for the wide-tier exp/powf Smith-squaring
# work-square -- the truncated-low base-2^64 `sqr_low_limb::<N, u64>` vs the
# u128-packed `sqr_low_limb::<N, u128>` candidate at the D616/D924/D1232 work
# integer widths (N = 128/192/256) plus the storage widths for reference.
[[bench]]
name = "sqr_low_u128_ab"
path = "benches/micro/sqr_low_u128_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# sqr_full_ab: N-way A/B for the int::policy::sqr truncated-low square across
# the full width axis N = 2..64 -- the const half_product (production arm) vs
# the const schoolbook (route-through-mul baseline) vs the non-const
# sqr_low_limb u64/u128 LimbSize arms (the sqr_low policy's axis), all gated
# bit-identical before timing. Maps which const algorithm sqr must route to
# and confirms the u128 win lives only in the non-const sqr_low policy.
[[bench]]
name = "sqr_full_ab"
path = "benches/micro/sqr_full_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# exp_wide_series_tang_ab: the N-way width x scale x algorithm map for wide
# exp -- Series (with the u128 square) vs single-shot Tang at D307..D1232,
# at the bbc SCALE 30 and the tiers' narrow bands, validity-gated (Tang ==
# Series across all six modes) before timing. Decides which wide Tang arms
# (if any) to wire; supersedes the stale wide-exp research framing.
[[bench]]
name = "exp_wide_series_tang_ab"
path = "benches/micro/exp_wide_series_tang_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# exp_wide_tang_bisect: targeted Tang/Series crossover bisection for D307 and
# D462, supplementing the 5-point coarse map. D307: probes s90/s100/s115/s130
# to localize the s76..s153 crossover. D462: probes s58 (s0..s115 midpoint) +
# s115 (the ~tie boundary) to confirm whether Tang has a winning sub-region.
[[bench]]
name = "exp_wide_tang_bisect"
path = "benches/micro/exp_wide_tang_bisect.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# trig_wide_tang_bisect: targeted narrow-Taylor / Series crossover bisection for
# D462 forward-trig (sin/cos/tan/atan), the audit Finding #2 untested band
# (24, 225..=235). Probes inside (s225/s230/s235) + outward (s210/s218 below,
# s240/s260/s290 above) — same shape as `exp_wide_tang_bisect`.
[[bench]]
name = "trig_wide_tang_bisect"
path = "benches/micro/trig_wide_tang_bisect.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# trig_rung_ab: forward-trig SCALE-derived work-rung A/B -- tier-width Series
# vs the rung-routed production path (gate + const-folded rung match) at
# D307/D1232 across the scale axis, plus an out-of-budget large-|x| probe.
# Validity-walled (rung == tier bit-identity across six modes) before timing.
[[bench]]
name = "trig_rung_ab"
path = "benches/micro/trig_rung_ab.rs"
harness = false
required-features = ["x-wide", "xx-wide", "bench-alt"]

# hyper_wide_tang_ab: tests the 2026-05-28 audit Finding #4 "Pattern A" lead --
# does Tang-composed (e^x, e^-x) hyperbolic beat the current schoolbook (Series-
# composed) at D462/D616/D924/D1232? Five-point coarse-scale sweep x {sinh, cosh,
# tanh}, three Tang configs, validity-walled against schoolbook bit-identity.
[[bench]]
name = "hyper_wide_tang_ab"
path = "benches/micro/hyper_wide_tang_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# ln_wide_series_tang_ab: the N-way width x scale x algorithm map for wide
# ln -- Series (multi-level-sqrt argument reduction) vs Tang (table-driven,
# no sqrts) across every wide tier D76..D1232 at the 5-point scale set
# {0, S/4, S/2, 3S/4, S-1}, validity-gated (Tang == Series across all six
# modes) before timing. Establishes the continuous Tang win-region per tier
# so the policy/ln Tang gates can be widened off their bbc-cell point ranges.
# The wide-transcendental BAND regression task.
[[bench]]
name = "ln_wide_series_tang_ab"
path = "benches/micro/ln_wide_series_tang_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# rem_kernel_ab: dispatch-seam A/B for the remainder policy `select` arm.
# div_kernel_ab: dispatch-seam A/B for the integer division ENGINE
# (Knuth base-2⁶⁴ vs Burnikel-Ziegler chunking vs Knuth base-2¹²⁸) at the
# wide limb counts, full-storage-width `balanced` and `wide_num` shapes.
[[bench]]
name = "div_kernel_ab"
path = "benches/micro/div_kernel_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# div_pow10_ab: the REAL per-term `÷10^w` divide shape the wide-tier
# transcendentals run (divisor = power of ten, ~w/19 limbs), ranking the
# production routing, base-2⁶⁴ Knuth, our base-2¹²⁸ Knuth, and a faithful
# port of v0.4.4's u128-limb Knuth (MG 2-by-1 q̂) — the 0.4.4-vs-ours
# comparison for the wide-divide regression.
[[bench]]
name = "div_pow10_ab"
path = "benches/micro/div_pow10_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# dec_mul_rescale_ab: whole-op decimal `mul` at the bbc regression-cluster
# cells (D57/D76/D115/D153/D230 at high scale), exercising the production
# `mul_widen_divide` + `÷10^SCALE` rescale chain — the lever for the cluster.
[[bench]]
name = "dec_mul_rescale_ab"
path = "benches/micro/dec_mul_rescale_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# div_bz_validity_probe: establishes whether a multiply-based recursive
# Burnikel-Ziegler divide can beat Knuth at division-reachable widths by
# timing one n-by-n schoolbook multiply against a full div_knuth(2n, n).
[[bench]]
name = "div_bz_validity_probe"
path = "benches/micro/div_bz_validity_probe.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

[[bench]]
name = "rem_kernel_ab"
path = "benches/micro/rem_kernel_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# int_unary_kernel_ab: dispatch-seam A/B for the INT unary policies with a
# registered reference arm -- sqr (half_product vs schoolbook), pow
# (square_and_multiply vs schoolbook), isqrt / icbrt (f64-seeded Newton vs
# the bitwise schoolbook reference). Confirms each `select` arm routes to
# the benched winner across narrow + wide widths.
[[bench]]
name = "int_unary_kernel_ab"
path = "benches/micro/int_unary_kernel_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# neg_kernel_ab: dispatch-seam A/B for the INT-tier `neg` policy -- the
# wide-tier `neg_D462`/`D616`/`D924`/`D1232` regression recovery.
# fused_split (limb-0 special-cased, the routed kernel) vs two_pass
# (NOT loop + full-width add_assign_fixed of [1,0,...,0], the previous
# routed shape) vs fused_open (single-pass dependent-chain reference).
# Bit-identical; A/B confirms fused_split is the wide-tier winner
# (D462/D616/D924/D1232 by 1.40x-1.83x vs two_pass).
[[bench]]
name = "neg_kernel_ab"
path = "benches/micro/neg_kernel_ab.rs"
harness = false
required-features = ["wide", "x-wide", "xx-wide", "bench-alt"]

# mul_div_native_ab: dispatch-seam A/B for the narrow decimal mul/div
# policies (native hardware-i128 vs the generic widen kernels at N <= 2).
[[bench]]
name = "mul_div_native_ab"
path = "benches/micro/mul_div_native_ab.rs"
harness = false
required-features = ["wide", "bench-alt"]

# root_kernel_ab: dispatch-seam A/B for the root policies -- D38 hypot /
# sqrt (256-bit mg_divide vs the slice path) and D57<20> cbrt (f64-seeded
# Int<6> Newton vs the int-icbrt table-seed arm vs the generic slice).
[[bench]]
name = "root_kernel_ab"
path = "benches/micro/root_kernel_ab.rs"
harness = false
required-features = ["wide", "bench-alt"]

# The wide-tier Tang/Series benchmark example uses the wide decimal types
# (D307..D1232), which only exist under the wide feature umbrellas. Gate it so a
# default / `--all-targets` build does not try to compile it without them.
[[example]]
name = "wide_tang_bench"
required-features = ["wide", "x-wide", "xx-wide"]
