[package]
name = "decimal-scaled"
version = "0.2.1"
edition = "2024"
rust-version = "1.85"
license = "MIT OR Apache-2.0"
description = "Const-generic base-10 fixed-point decimals (D9/D18/D38/D76/D153/D307) 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://docs.rs/decimal-scaled/"
readme = "README.md"
keywords = ["decimal", "fixed-point", "deterministic", "no-std", "precision"]
categories = ["mathematics", "no-std", "science", "finance"]
exclude = [
    ".github/",
    "bash.exe.stackdump",
    "research/",
    "tarpaulin.toml",
    "scripts/",
    "docs/benchmarks*.draft",
]

# 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
# (D9/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"]
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. To opt
# in to the faster f64-bridge ("fast") path instead, build with
# `default-features = false, features = ["std", "serde"]` and the
# plain `ln` / `exp` / `sin` / … methods will dispatch through f64
# (giving ≈ 16 decimal digits of platform-libm precision rather
# than 0.5 ULP).
default = ["std", "serde", "strict"]
std = ["alloc"]
alloc = []
serde = ["dep:serde"]
macros = ["dep:decimal_scaled_macros"]
# Plain transcendentals (`sin`, `ln`, …) dispatch to the integer-only,
# 0.5-ULP, platform-deterministic `*_strict` path. On by default
# (see `default` above). The `*_strict` and `*_fast` named methods
# are both always available regardless of this feature; `strict` only
# controls what the plain `*` form resolves to. Does not require
# `std`; the integer-only transcendentals compile under `no_std`.
strict = []
# Forces plain transcendentals to dispatch to the f64 bridge instead
# of `*_strict`, even when `strict` is also on. Same caveat as above:
# `*_strict` and `*_fast` named methods stay available either way.
fast = []
# 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 OVERRIDE_POLICY.md). 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 = []
# Wide decimal tiers (D76 / D153 / D307). Each width is independently
# opt-in; `wide` is the umbrella for the 256-bit / 512-bit tiers and
# `x-wide` adds the 1024-bit tier. They are backed by the in-tree
# hand-rolled wide-integer types in `crate::wide_int` — no external
# big-integer dependency. D9 / D18 / D38 use primitive storage and
# need none of this.
wide = ["d76", "d153"]
d76 = []
d153 = []
# Very-wide tier (D307). Gated separately because its strict-
# transcendental work integers run into thousands of bits and inflate
# build time vs the narrower wide tiers.
x-wide = ["d307"]
d307 = []

[dependencies]
serde = { version = "1", features = ["derive"], optional = true }
num-traits = { version = "0.2", default-features = false }
decimal_scaled_macros = { path = "macros", version = "0.2.1", default-features = false, optional = true }

[dev-dependencies]
criterion = { version = "0.8", default-features = false, features = ["cargo_bench_support", "html_reports"] }
i256 = { version = "0.2", default-features = false }
serde_json = "1"
postcard = { version = "1", default-features = false, features = ["alloc"] }
decimal_scaled_macros = { path = "macros" }
# 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"

[[bench]]
name = "mul_div_candidates"
harness = false

[[bench]]
name = "all_functions"
harness = false

# Wide-integer backend comparison: native hand-rolled `Int256` vs
# `bnum` / `ruint`. Needs the wide tier.
[[bench]]
name = "wide_int_backends"
harness = false
required-features = ["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"
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"
harness = false
required-features = ["wide"]

# 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"
harness = false
required-features = ["x-wide"]

# Comprehensive matrix: every decimal width × three scales (smallest /
# midpoint / largest) × arithmetic + strict + fast transcendentals,
# plus cross-crate baselines. Drives the `docs/benchmarks.md` rewrite.
[[bench]]
name = "full_matrix"
harness = false
required-features = ["wide", "x-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"
harness = false
required-features = ["wide"]

[lints.rust]
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",
]
resolver = "3"