Skip to content

Implement a sharable const cache for FBig#83

Merged
cmpute merged 29 commits into
developfrom
float-cache
Jun 26, 2026
Merged

Implement a sharable const cache for FBig#83
cmpute merged 29 commits into
developfrom
float-cache

Conversation

@cmpute

@cmpute cmpute commented Jun 21, 2026

Copy link
Copy Markdown
Owner

No description provided.

Jacob Zhong and others added 21 commits June 16, 2026 23:20
Add an opt-in MathCache<B> type to dashu-float that caches exact
binary-splitting tree state for mathematical constants, so repeated calls
at increasing precision *extend* prior work instead of recomputing from
scratch (e.g. π at 100 digits then 1000 digits pays only for the extra
work). Context and FBig are untouched and remain Copy + Send + Sync; the
cache is purely additive (Send + !Sync, wrappable in Arc<Mutex<..>>).

Implementation (FLOAT-CACHE.md):
- math/cache.rs: MathCache<B>, ConstCache (one Option<CachedState> field
  per series — pi, iacoth_6/9/99), CachedState (exact (P,Q,T,num_terms)),
  extend_or_compute (reuse / extend / cold-compute), manual Debug that
  reports term counts and bit lengths rather than MB-sized integers.
- math/consts.rs: factor out a shared universal merge() and add iacoth_bs()
  (ratio-form binary splitting for L(n), keeping Q at O(p) digits); give
  chudnovsky_bs an a>=b identity guard and make the helpers pub(crate).
- math::pi/ln2/ln10/ln_base: finalize from the cached exact integers;
  ln2/ln10/ln_base combine sub-series at an elevated work precision so the
  linear combination survives the final round.
- Switch Context::iacoth from its iterative loop to iacoth_bs (behavior
  pinned by the existing test_iacoth / test_ln2_ln10 fixtures).

Works in no_std (uses core::cell::RefCell + EstimatedLog2; no std-only
transcendentals). All float unit tests, doctests, cargo check
--all-features --tests, clippy -D warnings, and rustfmt pass.

Co-Authored-By: Claude <noreply@anthropic.com>
The design plan has been fully reflected in float/src/math/cache.rs and
the supporting helpers; the document no longer adds value beyond the
code, tests, and changelog. Only the explicitly-deferred Phase 4
roadmap items (benchmark/leaf-threshold tuning, optional sqrt(10005)
caching, future sin/cos) were not implemented.

Co-Authored-By: Claude <noreply@anthropic.com>
The cache Debug test used format!, which is not in scope when the crate
is built without std (the CI runs `cargo test --no-default-features
--features rand`). Import it from alloc, matching how other no_std test
modules in the workspace pull in alloc items.

Co-Authored-By: Claude <noreply@anthropic.com>
The first several leaves of the L(n) = acoth(n) binary splitting are
cheap but redundant to recompute on every fresh evaluation. Precompute
their merged (P, Q, T) state as a compile-time constant for the
sub-series that back ln2 / ln10 (n = 6, 9, 99), and use it as the
basecase of iacoth_bs: when the range starts at the series origin it
returns the constant triple instead of recursing into those leaves.

K is chosen per n so that P, Q and |T| each fit in a DoubleWord, which
keeps the triples portable consts on both 32- and 64-bit Word (verified
by the CI `--cfg force_bits="32"` clippy/test gate). Because the merge
is associative, the constant equals the recursively computed state
regardless of split order — pinned by a new test that re-derives each
block independently and cross-checks iacoth_bs.

pi cannot use this trick: its 2-term T (~1.5e23) already overflows a
DoubleWord, so Chudnovsky keeps its existing single-term k=0 const leaf.

Co-Authored-By: Claude <noreply@anthropic.com>
The previous basecase constants fit only u64, so they failed to compile
on Word = u16 (where DoubleWord = u32), which the CI exercises via
--cfg force_bits="16". dashu-int's Word width is not detectable from the
float crate, so instead keep every precomputed P/Q/|T| within u32: a u32
literal is accepted by from_dword / from_parts_const on Word = u16/32/64
alike, giving a single portable set of constants with no width detection.

This reduces the precomputed depth (4/3/2 leaves for n = 6/9/99, down
from 7/6/4) in exchange for portability. Verified on force_bits = 16, 32
and 64 (clippy -D warnings + tests), no_std, and the default 64-bit build.

Co-Authored-By: Claude <noreply@anthropic.com>
Introduce `CachedFBig`, an FBig carrying a shared `Rc<RefCell<ConstCache>>`
handle whose transcendental operations (ln, exp, trig, pi, base conversion)
thread the handle through the `Context` methods, reusing/extending the cached
exact binary-splitting state instead of recomputing constants from scratch.

The cache lives outside `Context`, which stays `Copy + Send + Sync + no_std`
(so `static_fbig!`/`static_dbig!` keep working); only `CachedFBig` is
`!Send + !Sync` (sharing state via `Rc<RefCell<..>>`). `ConstCache` replaces
the earlier `MathCache` wrapper as the sole public cache type (`Send + Sync`,
`&mut self` methods). Since `Context` accepts `Option<&mut ConstCache>`, users
can also build `Arc<Mutex<ConstCache>>`-based variants.

- The constant-source `Context` methods (ln, ln_1p, exp, exp_m1, powf, pi,
  sin/cos/sin_cos/tan/asin/acos/atan/atan2, and internal ln2/ln10/ln_base/
  convert_base) gain a `cache: Option<&mut ConstCache>` parameter — a breaking
  change to the low-level `Context` API. The high-level `FBig` API is unchanged
  (it passes `None`).
- CachedFBig mirrors FBig's surface explicitly; every value-producing op
  preserves the handle, so `(a + b).ln().exp()` stays cached.
- Fix a pre-existing no_std bug: `f64::ceil()` in ConstCache's precision
  helpers is std-only on the MSRV (1.68) and broke the workspace
  `--all-features --tests` build (dashu-float is built without std as a
  dependency of dashu-ratio); replaced with an integer `ceil_usize`.

Co-Authored-By: Claude <noreply@anthropic.com>
Re-encode +inf/-inf as exponent isize::MAX/isize::MIN (was 1/-1) and add
the Repr::neg_zero() constructor at exponent -1, per the repr.rs:125-126
plan. Update is_infinite() to match the new sentinel exponents and add
is_neg_zero(). normalize() now preserves infinity sentinels instead of
clobbering them (the prior documented bug); -0 is not yet produced by any
operation, so existing behavior is unchanged.

NumHash short-circuits zero-significand values to avoid overflow when
negating the isize::MIN exponent of -inf.

No user-visible behavior change; infinity still panics in arithmetic.
First milestone of the signed-zero / FpResult-reshape refactor (single PR).

Co-Authored-By: Claude <noreply@anthropic.com>
Operations now produce the IEEE-mandated sign of zero, and -0 is a first-class
value distinct from +0 only where the sign matters (1/-0 = -inf etc., landing
in M3). Highlights:

- Repr: manual PartialEq/Eq so +0 == -0; normalize() preserves the -0 encoding.
- Neg/Abs/signum: correctly toggle ±0 and ±inf by flipping the sentinel
  exponent (negating IBig::ZERO is a no-op). Sign-mul (Sign*FBig) delegates
  to Neg.
- Comparisons (cmp.rs): ±0 compare and order equal.
- Arithmetic: mul attaches XOR sign to zero products; div/rem attach the
  dividend/XOR sign to zero results; add/sub cancellation yields -0 only under
  roundTowardNegative (Down), +0 otherwise (new Round::IS_ROUND_TOWARD_NEGATIVE).
  repr_round preserves input sign when rounding to zero.
- Transcendentals: sqrt/cbrt/nth_root preserve ±0; sin/tan/atan/sin_cos carry
  the sign (cos(±0)=+1); ln_1p(±0)=±0; pow(-0,n) sign via sqr/mul.
- Rounding ops: trunc/round/fract yield signed zero; ceil/floor pass -0 through.
- Conversions: -0.0 round-trips through f32/f64 (TryFrom checks is_sign_negative;
  into_f*_internal preserves -0). num_traits is_positive/is_negative follow the
  sign bit (matching Rust's f64::is_sign_*).
- shift.rs skips exponent modification for any zero-significand value.

Infinity-as-value (1/0 -> +inf, ln(0) -> -inf, exp(huge) -> +inf) and the
FpResult reshape remain for M3.

Co-Authored-By: Claude <noreply@anthropic.com>
Reshape the result model so that infinite *inputs* are errors and infinite
*outputs* are legitimate values, unifying the old FpResult enum with Rounded.

- FpError { InfiniteInput, OutOfDomain, Indeterminate } (Display + std Error),
  modeled on ConversionError; FpResult<T> = Result<Rounded<T>, FpError>.
- unwrap_fp() maps FpError variants to granular panics for the FBig/CachedFBig
  convenience layer, which now uniformly panics on error (including trig).
- Deleted the old FpResult enum.
- Context arithmetic/transcendental/trig methods return FpResult:
  * inf input -> Err(InfiniteInput), except atan(±inf)=±π/2 and the atan2
    signed-∞ quadrant table (preserved, well-defined finite results).
  * inf output as a value: 1/0 -> ±inf, inv(0) -> ±inf, ln(0) -> -inf,
    tan(odd·π/2) -> ±inf (repr_div produces inf for finite/0).
  * 0/0 -> Err(Indeterminate); sqrt/ln of negative, asin(|x|>1), pow(neg base),
    even root of negative -> Err(OutOfDomain).
- FBig/CachedFBig layers panic-on-error via unwrap_fp; new
  forward_to_context_unwrap! macro unifies sin/cos/tan/asin/acos/atan (which
  finally fit the macro now that trig returns the uniform Result shape).
- Internal call sites updated (cache/consts/exp/log/convert/rand); removed
  unused panic helpers; tests and doctests updated to the Result shape.

New tests/fpresult.rs covers the inf-as-value / inf-input-as-error / domain
contract. The old dead Overflow/Underflow variants are gone; inf flows as a
value, errors flow as Err.

Co-Authored-By: Claude <noreply@anthropic.com>
- Rewrite the IEEE-754 compliance section in fbig.rs to describe the new model:
  NaN → FpError/panic, signed zero, and infinities as terminal values (producible
  and comparable, but inf inputs error/panic; atan/atan2 inf cases preserved).
- Replace the stale lib.rs TODO about inf arithmetic with a note describing the
  implemented terminal-value behavior.
- CHANGELOG (Unreleased, → 0.5.0): document the breaking encoding change
  (±inf sentinel exponents, -0), the FpResult = Result<Rounded<T>, FpError>
  reshape and old-enum removal, and the FBig trig methods returning Self.

rational::to_float and dashu-python are unchanged (they only use the
still-Rounded conversions); the workspace compiles and all 66 test binaries pass.

Co-Authored-By: Claude <noreply@anthropic.com>
- FBig +/- operators: produce -0 on exact cancellation under round-toward-negative
  (Down), matching Context::add/sub. The equal-exponent fast paths now route the
  summed significand through cancel_zero (previously they yielded +0).
- powf(±0, y): return the *positive* result (+0 for y>0, +inf for y<0), matching the
  common float-pow convention (a float exponent doesn't track parity). powi remains
  the sign-correct variant (pow(-0, odd) = -0). Fixes powf(0, negative) which
  previously returned +0.
- exp(huge)/exp_m1(huge)/powi: return +inf (or 0 / -1 for huge negative exp args)
  instead of panicking when the result exponent overflows isize.
- Fix pre-existing bug: FBig's ShrAssign (>>=) subtracted the shift twice; now once.
- CHANGELOG: document the powf convention, overflow-to-inf, and the shr_assign fix.

New tests cover operator cancellation under Down, exp overflow, powf zero base,
and shr_assign.

Co-Authored-By: Claude <noreply@anthropic.com>
…iacoth

Adds bench_iacoth::bench_ln2_iter_vs_binary_splitting (#[ignore], run via
`cargo test -- --ignored --nocapture`) that times ln(2) via master's iterative
Maclaurin-series iacoth against the current binary-splitting path.

Measured speedup of binary splitting over the iterative method grows with
precision (decimal digits): ~9x@50, ~26x@200, ~92x@1000, ~420x@5000, ~791x@10000
— consistent with the asymptotic gap (iterative O(p·M(p)) vs binary splitting
O(M(p)·log p)).

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread float/src/math/consts.rs Outdated
let sqrt_10005 = work_context
.sqrt(&work_context.convert_int::<B>(10005.into()).value().repr)
.value();
let sqrt_10005 =

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. The code of calculation of pi are duplicate in consts.ts and cache.rs
  2. the constant 10005 here don't need rounding (unnecessary)

Comment thread float/src/math/trig.rs Outdated
} else {
Repr::zero()
};
Ok(FBig::<R, B>::new(zero, *ctx).with_precision(ctx.precision))

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last with_precision call is unnecessary. new() already ensures this

bench_pi_sqrt::bench_pi_vs_sqrt10005 measures how much of the Chudnovsky π
computation is spent on its sqrt(10005) sub-computation. Result: sqrt(10005)
(Karatsuba square root) is ~1–10% of π's time across 50–5000 digits, so caching
it would be a modest win; the dominant cost remains the binary-splitting series,
which ConstCache already amortizes.

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread float/src/math/trig.rs
/// Calculate the cosine of the floating point representation.
#[must_use]
pub fn cos<const B: Word>(&self, x: &Repr<B>) -> FpResult<B> {
pub fn cos<const B: Word>(

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still need #[must_use] tag for functions using the cache.

Jacob Zhong and others added 2 commits June 22, 2026 18:07
ConstCache now caches floor(√10005 · 2^bits) as a base-free integer (computed
via Karatsuba UBig::sqrt, O(M(n))), reused by π. Newton refinement would be no
faster since UBig::sqrt is already optimal, so extension just recomputes.

The isqrt is folded into the π integer ratio
  π = (426880 · isqrt_val · Q) / (T · 2^isqrt_bits),
which reuses the fast convert_int path for Q and T and avoids any cross-base
conversion of √10005 (an earlier with_base approach was ~O(p²) and thousands of
times slower). When a higher-precision isqrt is already cached, a lower-precision
π request reuses it (the extra bits act as guard digits); the exponent tracks
the isqrt's own scaling.

- ConstCache gains sqrt_10005 / sqrt_10005_bits fields; total_words() counts the
  isqrt; total_terms() is unchanged (it isn't a series); clear() resets it.
- New tests cover caching/reuse, total_words accounting, and clear().
- Ignored benches (bench_pi_vs_sqrt10005, bench_pi_repeat_call) measure the
  √10005 share of π and the warm-call speedup (~20× at 500 digits).

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread float/src/convert.rs
impl<R: Round> Context<R> {
// Convert the [Repr] from base B to base NewB, with the precision under the target base from this context.
#[allow(non_upper_case_globals)]
fn convert_base<const B: Word, const NewB: Word>(&self, repr: Repr<B>) -> Rounded<Repr<NewB>> {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we also return FpResult in convert_base, since overflow and underflow can happen

Comment thread float/src/div.rs Outdated
/// Attach the dividend/divisor XOR sign to a zero quotient: the raw quotient significand is
/// `+0`, so the sign of a zero result (`0/finite`, or a finite/finite that rounds to zero) is
/// `sign(lhs) XOR sign(rhs)`.
fn div_repr<const B: Word>(sign_negative: bool, significand: IBig, exponent: isize) -> Repr<B> {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need a better name, make_div_repr?

Comment thread float/src/error.rs Outdated
/// `ln(-x)`, `asin(|x| > 1)`, `pow(negative, non-integer)`, an even root of a negative value.
OutOfDomain,

/// An indeterminate form, e.g. `0 / 0`.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to check whether x / 0 all goes to indeterminate. If so, document here

Comment thread float/src/fbig_cached_ops.rs Outdated
forward_to_context!(exp);
forward_to_context!(exp_m1);

/// Square root (see [`Context::sqrt`]).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we wrap sqrt, and inv?

Comment thread float/src/log.rs
// TODO: test if we can use log_B(p/2log_B(n)) directly
let guard_digits = (self.precision.log2_est() / B.log2_est()) as usize;
let work_context = Self::new(self.precision + guard_digits + 2);
let n: u32 = (&n).try_into().expect("iacoth argument must fit in u32");

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We lost the nice graphic comment

Comment thread float/src/mul.rs Outdated

/// Raw product of two finite reprs, attaching the XOR sign of the operands to a zero product
/// (the significand product alone is `+0`, losing the sign).
fn mul_finite_reprs<const B: Word>(lhs: &Repr<B>, rhs: &Repr<B>) -> Repr<B> {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make naming consistent: make_mul_repr?

Comment thread float/src/round_ops.rs Outdated
impl<R: Round, const B: Word> FBig<R, B> {
/// Return `±0` carrying the same sign as `self` (used by trunc/round/fract where a zero
/// result inherits the sign of the input). The result has precision 0, matching `FBig::ZERO`.
fn signed_zero(&self) -> Self {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two helper functions seems very redundant, can we have something like UBig::with_sign()?

Comment thread float/tests/fpresult.rs Outdated
@@ -0,0 +1,187 @@
//! Tests for the `FpResult` contract: infinite inputs → `Err`, infinite outputs → `Ok(±inf)`,

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the testing cases should be moved to the test cases per operations.

Comment thread float/CHANGELOG.md Outdated
- Fix rounding issues in `to_f32()` and `to_f64()` ([#53](https://github.com/cmpute/dashu/issues/53), [#56](https://github.com/cmpute/dashu/issues/56)).
- Fix several rounding bugs in `FBig`/`Context` addition and subtraction: severe-cancellation collapse, spurious-ULP errors from negligible operands, the window-edge boundary, and `Context::sub` with a zero left operand under directed rounding modes.
- Fix `FBig::fract()` inflating context precision and `split_at_point_internal` using an incorrect fractional scale for values smaller than one.
- Fix rounding issues in `to_32()` and `to_f64()` (fixes [#53](https://github.com/cmpute/dashu/issues/53) and [#56](https://github.com/cmpute/dashu/issues/56)).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't touch the existing Changelog items

@cmpute
cmpute merged commit 9c55a61 into develop Jun 26, 2026
13 checks passed
@cmpute
cmpute deleted the float-cache branch June 26, 2026 03:45
cmpute pushed a commit that referenced this pull request Jun 27, 2026
…MPC oracle

- `tests/special_values.rs`: expanded Annex-G coverage — `proj` (Riemann collapse),
  `conj` with infinities, `arg` of imaginary infinities, the signed-zero
  `log(-r ± i0) = ln r ± iπ` branch cut, and `sqrt(-∞) = 0 + i·∞`.
- `benches/` (criterion, `required-features = ["rand"]`, not run in CI): `arith`
  (`mul`/`div`/`sqr` over precisions {53,113,256,1024}), `transcendental`
  (`exp`/`log`/`sin`/`cos`/`sqrt`/`abs`/`arg`), and `io` (`Display`/`FromStr`),
  each with a `random_cbig` helper (log-scale sizing, `StdRng::seed_from_u64(1)`).
- `fuzz/tests/cmplx_random.rs`: a `rug::Complex` (GNU MPC) differential oracle for
  field arithmetic at 53-bit precision — `mul`/`div`/`sqr` match MPC within a few
  ulps over 8192 random cases (manual: `--ignored`). `dashu-cmplx` added to fuzz deps.

The pre-existing fuzz tests (`add_random`, `trig_random`) are stale against the
post-#83 `FpResult` (unchanged here); the new oracle compiles and passes standalone.

Co-Authored-By: Claude <noreply@anthropic.com>
cmpute pushed a commit that referenced this pull request Jun 28, 2026
…d.yml

The workspace-excluded `fuzz/` crate had not compiled since the #83 FpResult
change (its tests targeted the pre-#83 `Context::sin(f)`/`FpResult::Normal`
API). Restore it and convert the differentials to proptest.

Fuzz crate (step 1):
- Port every test to the current `Context`/`FpResult` API (`ctx.sin::<B>(&x,
  None).unwrap().value()`; `FpResult` is `Result<Rounded<_>, FpError>`).
- Replace the `for _ in 0..N { rng… }` loops with proptest strategies
  (`fuzz_config` = 1024 cases, overridable via `PROPTEST_CASES`) so a mismatch
  shrinks to a minimal counterexample. Shared strategies live in `fuzz/src/lib.rs`.
- `add_random`: dispatches all six rounding modes per case, precision biased
  toward 1/2/3. `trig_random`: five broad differentials (sin/cos/tan/atan2/
  inv-trig) in proptest + three deterministic sweeps (pi/asin-near-1/large-exp
  tan) ported; `atan2(0,0)` indeterminate handled by skipping. `cmplx_random`:
  MPC mul/div/sqr over f64 pairs. Trig tests use `HalfAway` to match `DBig`.
  All remain `#[ignore]`d (manual, release-time — they link `rug` and run long).

CI (step 2):
- New `.github/workflows/build.yml` for compile-only checks: a `fuzz-check` job
  (cargo check the fuzz crate so it can't silently rot again — tests stay
  manual), plus `build-benchmark` and `build-aarch64` moved out of `tests.yml`,
  plus the `check` (cargo check, stable/1.85/1.68 MSRV matrix) job.
- `tests.yml` now runs only the test matrix + x86/x86_64/no-std tests + fmt +
  clippy.

Co-Authored-By: Claude <noreply@anthropic.com>
cmpute added a commit that referenced this pull request Jun 30, 2026
* Add complex crate design

* Update the CBig design

* Update TODO

* Further update TODO

* Update design

* Update AbsEq note in TODO-cmplx to reflect Phase 1 removal

AbsEq was folded into AbsOrd and removed in Phase 1 (not merely
deprecated); reword the §5.3 parenthetical to match.

Co-Authored-By: Claude <noreply@anthropic.com>

* Refine v0.5/cmplx design docs

TODO-v05.md:
- Note the full C <tgmath.h> type-generic math surface as a long-term
  goal, explicitly out of scope for 0.5 and 0.5.x.

TODO-cmplx.md:
- unwrap_cfp: define dashu-cmplx's own Context::unwrap_cfp (the float
  unwrap_fp is pub but typed for FpResult<FBig>, unusable for CfpResult).
- Conversions: replace from_real/from_int with From<FBig>/From<UBig>/
  From<IBig> and add TryFrom<CBig> for FBig/IBig, mirroring FBig's
  "Convert from/to UBig/IBig" pattern.
- hypot: make FBig::hypot (overflow-safe scaled sum-of-squares) a
  prerequisite dashu-float step; CBig::abs becomes a thin composition.

Co-Authored-By: Claude <noreply@anthropic.com>

* Minor improvement

* dashu-cmplx (M1): skeleton, type model, and easy operations

Add the new `dashu-cmplx` crate (the headline 0.5 feature) with the `CBig`
arbitrary-precision complex type built on `dashu-float`'s `FBig`.

Type & context model (§2):
- `CBig<R: Round = Zero, const B: Word = 2>` — two `Repr<B>` parts over a single
  shared `dashu_cmplx::Context<R>` newtype, mirroring `FBig`'s `Repr`+`Context`
  layout (the uniform-precision invariant is structural).
- `Context<R>` wraps `dashu_float::Context<R>` and hosts the context-layer ops;
  `CRounded`/`CfpResult` carry per-axis `(Rounding, Rounding)` inexactness.

Construction/conversion (§5.1): `from_parts`/`re`/`imag`/`into_parts`, the
`ZERO`/`ONE`/`I` constants, `From<FBig>`/`From<UBig>`/`From<IBig>`, and the lossy
`TryFrom<CBig> for FBig`/`IBig` (composing CBig → FBig → IBig).

Easy ops (single-pass rounding): add/sub (operators only, calling the context
layer — matching `FBig`'s no-inherent-add pattern), `neg`/`conj`/`proj`/`mul_i`
(exact), `norm` (squared modulus) and `arg` (Annex-G `atan2`).

Comparison (§5.3): componentwise `Eq`, lexicographic `Ord`, `AbsOrd` via `|z|²`,
and `NumOrd`/`NumHash` (behind `num-order`).

I/O (§5.6): algebraic `a+bi` `Display`/`FromStr` (MPC's parenthesized form is
rejected) and structured `Debug`.

Workspace + meta-crate wiring: `complex/` added to the workspace, the meta-crate
gains `dashu::complex` and the `dashu::Complex = CBig` alias, and `dashu-cmplx`
is wired into the shared feature-forwarding lines.

Co-Authored-By: Claude <noreply@anthropic.com>

* dashu-cmplx (M2): mul/div/sqr/inv + abs, and FBig::hypot

dashu-float prerequisite:
- `FBig::hypot` / `Context::hypot`: overflow-safe `sqrt(a²+b²)` via the scaled
  sum-of-squares (the larger operand is never squared; `m·sqrt(1+r²)` with
  `r=min/max ∈ [0,1]`). `hypot(±inf,·)=+inf`, `hypot(0,0)=+0`. This is the kernel
  `CBig::abs` composes, landed on FBig so it is a first-class real op too.

CBig field arithmetic (near-correctly rounded via the §6.1 guard-digit recipe —
evaluate each component at `p+g`, re-round to `p`; no Ziv retry):
- `sqr` `(x²-y²)+i(2xy)`, `mul` `(xu-yv)+i(xv+yu)` (naive 4-mul),
  `div` (Smith's overflow-safe method, `|u|≥|v|` branch), `inv` `conj(z)/|z|²`.
- `abs` = `ctx.hypot(re, im)` at guard precision.
- Scalar mul/div by a real `FBig` via mixed-type operators (`z*r`, `z/r`, `r*z`,
  `r/z`) — no named methods (Decision 9).

Annex-G special-value short-circuits: `0·∞`/`0/0`/`∞/∞` → `Indeterminate`;
`z/0`/`∞·finite` → the Riemann point at infinity (`+∞+i·0`); `finite/∞`/`0/finite`
→ 0. (dashu's convention: complex infinity is the single Riemann point produced
by `proj`.)

Tests:
- `tests/special_values.rs` — exact Annex-G vectors for mul/div/inv.
- `tests/arith_prop.rs` — exact identity proptests (commutativity, z+0=z, z-z=0,
  z·1=z, z·0=0, mul_i⁴=id, conj²=id, proj idempotent, z+(-z)=0).
- `tests/rounding_prop.rs` — correct-rounding self-oracle (mul/sqr/div/abs at `p`
  vs `2p` re-rounded, ≤1 ulp/component) and `z·conj(z)=norm`.

Co-Authored-By: Claude <noreply@anthropic.com>

* dashu-cmplx (M3): sqrt, exp, log, and powers

- `sqrt` (principal branch, cut `]−∞, 0]`): cancellation-free form — for
  `x ≥ 0`, `a = sqrt((r+x)/2)` and `b = y/(2a)`; for `x < 0`, `b` from `(r-x)/2`
  and `a = y/(2b)`. Avoids the `(r−x)` catastrophic cancellation when `|y|≪|x|`.
  Annex-G specials: `sqrt(±0)=±0`, `sqrt(+∞+iy)=+∞+i·0`, `sqrt(-∞+iy)=0+i·sign(y)∞`.
- `exp(x+iy) = e^x·(cos y + i sin y)` reusing FBig `exp`/`sin_cos`; `exp(0)=1`,
  `exp(+∞+ifinite)=+∞`, `exp(-∞+ifinite)=0`, infinite imaginary → `Indeterminate`.
- `log z = ln|z| + i·arg z` (Im ∈ `]−π,π]`); `log(0)=-∞`, `log(∞)=+∞`, cut `]−∞,0]`.
- `powi` (integer exponent, repeated squaring, branch-cut-free) and `powf`
  (`exp(w·log z)` at `p+POWF_GUARD`, re-rounded); `powf(z,0)=1` incl. `powf(0,0)=1`.

The transcendental context ops (`exp`/`log`/`powf`/`arg`) now carry
`cache: Option<&mut ConstCache>` (threaded via a centralized `reborrow_cache`
helper), matching the spec so the deferred `CachedCBig` needs no signature change.

Tests:
- `tests/transcendental_prop.rs` — sqrt/exp/log self-oracles (p vs 2p, ≤2 ulp),
  `sqrt(conj z)=conj(sqrt z)`, and `log z = ln|z| + i·arg z`.
- `tests/special_values.rs` — extended with sqrt/exp/log special-value vectors.

Co-Authored-By: Claude <noreply@anthropic.com>

* dashu-cmplx (M4): trig and inverse trig

Forward trig via the real–imaginary decomposition, reusing `dashu-float`'s real
`sin`/`cos` and cancellation-free `sinh`/`cosh` (avoids the `exp(±iz)` blow-up for
large `|Im z|`):
- `sin(x+iy) = sin x·cosh y + i·cos x·sinh y`
- `cos(x+iy) = cos x·cosh y − i·sin x·sinh y`
- `sin_cos` (shared reduction), `tan = sin/cos`. Infinite input → `Indeterminate`.

Inverse trig (Kahan log forms; the inner `log` argument always has positive real
part, so the cut comes from the `sqrt`):
- `asin z = -i·log(iz + sqrt(1-z²))`, `acos z = -i·log(z + i·sqrt(1-z²))`,
  `atan z = (i/2)·(log(1-iz) - log(1+iz))`.

dashu-float: `FBig::from_repr`'s debug assert now accepts the documented single
guard digit (`precision + 1`), so `CBig::into_parts` doesn't panic on a guard-digit
part produced by an inexact add.

Tests: `transcendental_prop.rs` extended with sin/cos/asin/atan self-oracles
(p vs 2p) and the pythagorean identity (modest-range strategy, since `cosh²y`/
`sinh²y` cancel for large `|Im z|`).

Co-Authored-By: Claude <noreply@anthropic.com>

* dashu-cmplx (M5): hardening — Annex-G specials, benches, and the rug/MPC oracle

- `tests/special_values.rs`: expanded Annex-G coverage — `proj` (Riemann collapse),
  `conj` with infinities, `arg` of imaginary infinities, the signed-zero
  `log(-r ± i0) = ln r ± iπ` branch cut, and `sqrt(-∞) = 0 + i·∞`.
- `benches/` (criterion, `required-features = ["rand"]`, not run in CI): `arith`
  (`mul`/`div`/`sqr` over precisions {53,113,256,1024}), `transcendental`
  (`exp`/`log`/`sin`/`cos`/`sqrt`/`abs`/`arg`), and `io` (`Display`/`FromStr`),
  each with a `random_cbig` helper (log-scale sizing, `StdRng::seed_from_u64(1)`).
- `fuzz/tests/cmplx_random.rs`: a `rug::Complex` (GNU MPC) differential oracle for
  field arithmetic at 53-bit precision — `mul`/`div`/`sqr` match MPC within a few
  ulps over 8192 random cases (manual: `--ignored`). `dashu-cmplx` added to fuzz deps.

The pre-existing fuzz tests (`add_random`, `trig_random`) are stale against the
post-#83 `FpResult` (unchanged here); the new oracle compiles and passes standalone.

Co-Authored-By: Claude <noreply@anthropic.com>

* dashu-cmplx (M6): cbig! macro, guide chapter, and 0.5.0 version sync

cbig! literal macro (dashu-macros):
- `cbig!` / `static_cbig!` (+ `cbig_embedded` / `static_cbig_embedded`) accepting the
  algebraic `a+bi` form or a `re, im` pair; each coefficient reuses the `fbig!` base-2
  parser. The static variant builds via a new `CBig::from_repr_parts` const constructor
  (each Repr via `from_static_words`/`Repr::zero()`), gated on Rust 1.64+ like the other
  static macros. Exposed as `dashu::cbig!` in the meta-crate.

Guide: a "Complex Numbers" chapter (construction, two-layer API, no-NaN special-value
model, near-correct rounding), linked from SUMMARY.md.

Version sync (Phase 5): all crates bumped to 0.5.0 (base/int/float/ratio/macros 0.4.x →
0.5.0; complex already 0.5.0) and every inter-crate dependency version string updated;
changelels consolidated (Unreleased → 0.5.0).

Co-Authored-By: Claude <noreply@anthropic.com>

* ci: extend the MSRV dep-drop script for dashu-cmplx

The MSRV (1.68) job strips rand 0.9/0.10 (which need newer Rust) from the
manifests before `cargo check --features rand`. The new `dashu-cmplx` crate
wasn't in that list, and it forwards `rand_v09 → dashu-float/rand_v09`, while
the meta-crate's `rand` feature still pointed at `dashu-cmplx/rand` — so after
the strip, resolution failed with "dashu-float does not have feature rand_v09".

Mirror the existing handling: strip `rand_v09`/`rand_v010` (and complex's
`rand` alias + the `required-features = ["rand"]` benches) for the MSRV build,
and drop the `dashu-cmplx/rand` forwarding from the meta-crate's `rand` line.
Stable/1.85 still exercise the full rand surface via `--all-features`.

Verified by running the script + the exact MSRV check command locally
(`cargo check --workspace --exclude dashu-python --features
"std,num-order,serde,zeroize,rand,num-traits_v02"` builds cleanly).

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(cmplx): import alloc::format in the no_std test modules

The inline Display/FromStr unit tests use format!, which is not in the prelude
under no_std. The fmt/parse test modules now `use alloc::format;` (the same fix
dashu-ratio uses), so `cargo test --no-default-features --features rand` compiles.

Verified: `cargo test --no-default-features --features rand --workspace
--exclude dashu-python` passes; the all-features (std) build and clippy are
unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(cmplx): rand support for CBig (rand = rand_v08 default)

Add random generation for CBig, mirroring dashu-float/ratio:

- Features: `rand = ["rand_v08"]` (default alias), with `rand_v09`/`rand_v010`
  opt-in — matching integer/float/rational, and MSRV-compatible (rand 0.8 runs
  on 1.68, so the MSRV `rand` build no longer needs a complex-specific carve-out).
- `third_party/rand.rs`: `UniformCBig<R,B>` samples the box `[low, high)` (each
  part an independent `UniformFBig` range). `rand_v08`/`rand_v09`/`rand_v010`
  add the `Distribution<CBig>` impls; `Standard`/`StandardUniform`/`Open01`/
  `OpenClosed01` sample the unit square `[0,1)²` (each part uniform in `[0,1)`).
  No bespoke sampling algorithm — it reuses `dashu_float::rand::UniformFBig`.
- No single-axis `Uniform`/`SampleUniform` (complex has no interval order); the
  box `UniformCBig::new(low, high, precision)` is the ranged sampler.

The MSRV dep-drop script is simplified back to just adding `complex/Cargo.toml`
to the existing rand_v09/rand_v010 strip loop (complex now keeps `rand = rand_v08`).

Tests: `tests/random.rs` (the `Standard` unit square, the `UniformCBig` box, and
`Open01` excludes zero). Verified across rand 0.8/0.9/0.10, `no_std`, and the
MSRV check command.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(cmplx): CBig NumHash consistent with num-complex + num-order

The old CBig NumHash hashed re and im sequentially (tuple-style), inconsistent
with num-order's native Complex<f64> hashing (algebraic combination of the
per-part field elements).

CBig NumHash now mirrors num-order's "Case 4": treat z = a + b·i and write
hash(a + bterm) where bterm = ∓PROOT²·b² (sign of b), with a, b the per-part
residues. This keeps a CBig and a num-complex Complex<f64> of the same value
in sync.

dashu-float changes (behind `num-order`):
- `Repr::num_hash_residue(&self) -> i128`: the numeric-hash field element
  (mod 2¹²⁷−1), extracted from `Repr::num_hash` so composite types can reuse
  it. For base-2 uses the same `absm(&127)` exponent reduction as num-order's
  f64 fhash.
- ±∞ now map to `HASH_INF`/`HASH_NEGINF` (= ±M127), matching num-order's
  f64 fhash. The subsequent `i128::num_hash` still reduces these to 0 (since
  M127 ≡ 0 mod M127), so the *final* hash of a real ±∞ is unchanged — but the
  *residue* now distinguishes them so CBig's algebraic combination is correct
  for complex numbers with infinite parts. ±0 still maps to 0.
- `test_fbig_num_hash_matches_f64` verifies FBig↔f64 consistency including
  ±∞ and -0.

dashu-cmplx changes:
- `_num-modular` added to the `num-order` feature.
- `CBig::num_hash` rewritten to the algebraic formula.
- `cbig_num_hash_matches_num_complex` cross-checks against the transcribed
  num-order Complex<f64> formula for 7 sample values.

Co-Authored-By: Claude <noreply@anthropic.com>

* Restructure dashu-cmplx modules to mirror dashu-float

Align dashu-cmplx's module layout and operator-impl style with dashu-float.
Pure restructuring; no behavioral change.

- Merge `sub.rs` into `add.rs` and `power.rs` into `exp.rs`, matching float's
  single-file grouping: add+sub share one sign-parameterized kernel
  (`signed_add`, sub = add with negated rhs), and `exp` hosts the power family.
- Rewrite the `CBig·CBig` `Mul`/`MulAssign` impls (and `add.rs`'s
  `AddAssign`/`SubAssign`) as explicit ref/val impls plus a new
  `impl_binop_assign_by_taking!` macro, mirroring float's `mul.rs`/`add.rs`
  instead of the one-shot `impl_cbig_binop!` mega-macro.
- Promote `trig.rs` to a `math/` submodule (`math/{mod,trig}`), matching
  float's `pub mod math`.
- Rename `context.rs` to `repr.rs` (float keeps `Context` in `repr.rs`),
  updating every `crate::context::` path to `crate::repr::`.

Co-Authored-By: Claude <noreply@anthropic.com>

* Tighten TODO-v05.md: mark dashu-cmplx done, prune resolved items

- Phase 3 (dashu-cmplx): mark ✅ Implemented (M1–M6); fold the detailed
  TODO-cmplx.md design doc into a consolidated §3.4 deferred-to-0.5.x list
  (Ziv, hyperbolics, fma, CachedCBig forward-compat note, CRound, etc.).
  TODO-cmplx.md is removed.
- Phase 0: drop the "record baseline benchmark" item — results are
  hardware-dependent and won't be committed; note the decision inline.
- Phase 1 §1.5: replace vague prose with four concrete checkboxes (guide
  prose migration + the pow/div/exp algorithm TODOs), line refs updated.
- Remove the "Open Decisions" section — all four (CBig scope, serde
  padding, proptest, MSRV) are resolved and documented in their phases.

Co-Authored-By: Claude <noreply@anthropic.com>

* Promote the sin many-digit rounding reproducer to CI tests

The `Context::sin` 49-digit-significand rounding regression (found during
fuzzing) lived only in the workspace-excluded `fuzz/` crate, so it never ran in
CI. Move it into float's inline trig tests as
`test_sin_many_digit_rounding_no_panic` (rewritten to the current
`Context::sin` API), and consolidate the pythagorean identity proptest to sweep
precisions {20, 50, 100} — a strict superset of the former single-precision
version, replacing the complementary fuzz sweep that was deleted.

Co-Authored-By: Claude <noreply@anthropic.com>

* Rewrite fuzz differentials in proptest; split build-only CI into build.yml

The workspace-excluded `fuzz/` crate had not compiled since the #83 FpResult
change (its tests targeted the pre-#83 `Context::sin(f)`/`FpResult::Normal`
API). Restore it and convert the differentials to proptest.

Fuzz crate (step 1):
- Port every test to the current `Context`/`FpResult` API (`ctx.sin::<B>(&x,
  None).unwrap().value()`; `FpResult` is `Result<Rounded<_>, FpError>`).
- Replace the `for _ in 0..N { rng… }` loops with proptest strategies
  (`fuzz_config` = 1024 cases, overridable via `PROPTEST_CASES`) so a mismatch
  shrinks to a minimal counterexample. Shared strategies live in `fuzz/src/lib.rs`.
- `add_random`: dispatches all six rounding modes per case, precision biased
  toward 1/2/3. `trig_random`: five broad differentials (sin/cos/tan/atan2/
  inv-trig) in proptest + three deterministic sweeps (pi/asin-near-1/large-exp
  tan) ported; `atan2(0,0)` indeterminate handled by skipping. `cmplx_random`:
  MPC mul/div/sqr over f64 pairs. Trig tests use `HalfAway` to match `DBig`.
  All remain `#[ignore]`d (manual, release-time — they link `rug` and run long).

CI (step 2):
- New `.github/workflows/build.yml` for compile-only checks: a `fuzz-check` job
  (cargo check the fuzz crate so it can't silently rot again — tests stay
  manual), plus `build-benchmark` and `build-aarch64` moved out of `tests.yml`,
  plus the `check` (cargo check, stable/1.85/1.68 MSRV matrix) job.
- `tests.yml` now runs only the test matrix + x86/x86_64/no-std tests + fmt +
  clippy.

Co-Authored-By: Claude <noreply@anthropic.com>

* Note SIMD-optimized FFT multiplication as a v1.0 item

Co-Authored-By: Claude <noreply@anthropic.com>

* Add comprehensive fuzz oracles vs GMP/MPFR/MPC (step 3)

Four new proptest-driven test files in the workspace-excluded `fuzz/` crate,
differential against `rug` (GMP/MPFR/MPC) to complete the correctness-oracle
coverage:

- `transcendental.rs` — all 17 non-trig FBig transcendetals (exp/exp_m1,
  ln/ln_1p/sqrt/cbrt/nth_root/hypot/atan/powf/powi + the hyperbolic family),
  compared vs `rug::Float` (MPFR) with a `within_k_ulps(k=2)` tolerance
  (dashu near-correct, MPFR Ziv-correct → ≤1 ulp is legitimate).
- `cmplx_transcendental.rs` — 10 CBig transcendetals (exp/log/sqrt/
  sin/cos/tan/asin/acos/atan/powf) vs `rug::Complex` (MPC) at 53-bit,
  reusing the shared `close()` helpers.
- `integer.rs` — 15 UBig/IBig ops (mul/sqr/gcd/div_rem/pow/sqrt/nth_root/
  bit-ops/shifts) vs `rug::Integer` (GMP) via exact decimal-string round-trip.
- `ratio.rs` — 8 RBig ops (add/sub/mul/div/sqr/pow/inv/reduce) vs
  `rug::Rational` (GMP mpq) via exact canonical-form comparison.

Shared infra: `fuzz/src/lib.rs` gains `ubig_strategy`, `pos_dbig_strategy`,
`unit_dbig` (promoted from trig_random), and a `cmplx` module holding the
53-bit build/compare helpers (moved from cmplx_random). The fuzz crate now
depends on `dashu-ratio`.

Bug fix discovered by the integer oracle:
- `UBig/IBig::nth_root(0, n)` (and `cbrt(0)`) returned 1 instead of 0:
  the `bits <= n` shortcut in `integer/src/root_ops.rs` fired for the zero
  input (bit_len == 0). Fixed with a zero guard. Regression test added.

Co-Authored-By: Claude <noreply@anthropic.com>

* Revert version changes

* Add FBig::sinh_cosh for combined sinh+cosh computation

- Add Context::sinh_cosh(x, cache) -> (FpResult, FpResult) in float/src/math/hyper.rs,
  sharing the exp_m1(±x) sub-computations to roughly halve the cost vs separate calls.
- Add FBig::sinh_cosh() -> (Self, Self) convenience method with doc example.
- Add CachedFBig::sinh_cosh forwarding (mirrors sin_cos pattern, threads ConstCache).
- Use sinh_cosh in complex CBig::sin_cos kernel instead of separate sinh+cosh calls.
- Add sinh_cosh_fuzz proptest oracle vs rug's independent sinh/cosh (within 2 ulps).

Co-Authored-By: Claude <noreply@anthropic.com>

* Address review feedback: renames, ownership patterns, and cleanup

- log() → ln() on CBig, matching FBig's naming; context-layer stays log()
- imag() → im() across all call sites
- Remove from_repr_parts; make new() pub (identical code)
- Inline is_numeric_zero helper at call sites
- Inline trig.rs helper fns (cbig_one, cbig_real, reround, …)
- Add #[inline] to Context::sin/cos
- Replace CBig::inv() with Inverse trait impl (matching FBig)
- Split add.rs signed_add into per-ownership kernel functions
- Use impl_cbig_binop! macro for Mul (matching Div)
- Unroll parameterless impl_scalar_mul!/impl_scalar_div! macros
- Fix abs_ge to avoid cloning Repr parts
- Fix overflow: both parts +∞ instead of ±∞ + i·0
- Extract bterm helper; test shares it instead of duplicating formula
- Add Uniform01<BASE> struct in rand.rs for complex unit-square sampling
- Fix copyright year (2026), use IBig::ONE/NEG_ONE constants
- Update CHANGELOG with all review-driven changes

Co-Authored-By: Claude <noreply@anthropic.com>

* Address additional review feedback: inline abs_repr, delete guide, rename cbig→cmplx

- Inline abs_repr helper in float/src/root.rs (clone avoidance)
- Delete premature guide/src/complex.md (deferred to post-merge)
- Rename macros/src/parse/cbig.rs → cmplx.rs for naming consistency

Co-Authored-By: Claude <noreply@anthropic.com>

* Address second batch of review feedback

- Simplify trig constant construction (CBig::ONE and IBig::from(2).into())
- Use impl_cbig_binop! for Add/Sub (replace kernel functions)
- Add impl_cbig_scalar_binop! macro for CBig/FBig scalar ops
- Remove unused impl_binop_assign_by_taking macro
- Replace abs_ge with inline FBig::abs_cmp at call sites
- Restore signed-zero preservation in underflow
- Fix fuzz: rug::Complex::imag() accidentally renamed to im()
- Fix formatting in float/src/root.rs
- Update CHANGELOG to reflect current state

Co-Authored-By: Claude <noreply@anthropic.com>

* Note: expose ownership-aware kernel fns from dashu-float for dashu-cmplx

Add a deferred item under Phase 3.4 to make float's add_val_val / add_val_ref /
add_ref_val / add_ref_ref kernel functions (and equivalents for sub/mul/div)
public, so dashu-cmplx can call them directly in its own per-ownership operator
impls rather than immediately borrowing through the complex Context.

Co-Authored-By: Claude <noreply@anthropic.com>

* Use live num_complex::Complex64 NumHash in test instead of manual formula

Make num-complex_v04 a non-optional regular dependency and enable
num-order/num-complex so Complex<f64>: NumHash is available in unit
tests. The cbig_num_hash_matches_num_complex test now calls
residue(&Complex64::new(re, im)) directly against num-order's actual
implementation — no duplicated bterm transcription.

Co-Authored-By: Claude <noreply@anthropic.com>

* Add num-complex conversions; decouple num-complex from num-order

- New `num-complex` feature providing TryFrom conversions between CBig and
  num-complex's Complex<f32>/Complex<f64> (base-2, composing through FBig):
  exact lift with NaN -> OutOfBounds, infinities/signed zeros preserved;
  rounding back errors on overflow or inexactness — mirroring FBig's
  primitive-float TryFrom pair.
- The `num-order` feature no longer enables `num-order/num-complex`, and
  num-complex is no longer an unconditional dependency: it is now an opt-in
  normal dep behind `num-complex`, plus a dev-dependency (the num-order
  NumHash test compares against num-complex's live Complex<f64> reference,
  and the new conversion tests use it). Enabling only `num-order` (the
  default) therefore no longer drags num-complex into the dependency tree.

Co-Authored-By: Claude <noreply@anthropic.com>

* Add cargo doc CI guard; fix workspace intra-doc links

- New `rustdoc` job in build.yml runs `cargo doc --workspace` with
  `-D warnings` (stable, --all-features). The meta-crate `dashu` and
  `dashu-python` both name their lib `dashu`, so they collide on the output
  path and are documented in two invocations (both are checked).
- Fix 9 broken intra-doc links surfaced by the check: dashu-cmplx
  (Context::guard pub(crate) link, dashu_float::Rounded path), dashu-float
  (Exact/Inexact variant paths, FpError::InfiniteInput crate path,
  static_fbig! external macro -> plain code), dashu-int (MontgomeryRepr
  self link), dashu-ratio (Display path).

Co-Authored-By: Claude <noreply@anthropic.com>

* Rename guide ieee754.md to compliance.md; add C99 Annex G section

The compliance page now covers two aspects of standards conformance:
dashu-float's FBig vs IEEE 754-2008 (the former ieee754.md content,
demoted one heading level) and dashu-cmplx's CBig vs C99 Annex G
(IEC 60559 complex) — data model, arithmetic, branch cuts, and the
no-NaN / Riemann-point policy, sourced from the crate's actual behavior.

Also updates the SUMMARY.md link and a stale pointer in TODO-v05.md.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Jacob Zhong <jacob@rimbot.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant