Skip to content

feat: add a public extended Euclidean algorithm for natural numbers - #15160

Draft
kim-em wants to merge 4 commits into
leanprover:masterfrom
kim-em:feat/nat-extended-gcd
Draft

kim-em wants to merge 4 commits into
leanprover:masterfrom
kim-em:feat/nat-extended-gcd

Conversation

@kim-em

@kim-em kim-em commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

This PR adds Nat.extendedGcd, returning a gcd and signed Bézout coefficients together with correctness and simplification lemmas.

Use a tail-recursive Lean definition and a GMP-backed extern with borrowed inputs and small-input fast paths. Preserve exact coefficient agreement, including zero and equal inputs, and provide a native fallback without GMP. Add test_extern and compiled-caller coverage for arbitrary-precision inputs, coefficient ties, allocation boundaries, and long Euclidean chains. Keep shared bignum allocation changes in #15162.

Follow up on the review discussion on #15077; leave UInt64 inverse integration and mathlib's existing APIs unchanged.

🤖 prepared with codex

This PR adds `Nat.extendedGcd` to compute the greatest common divisor of two natural numbers together with signed Bézout coefficients, and provides correctness lemmas for using those coefficients in proofs.

Return the gcd and both coefficients in a single `Nat.ExtendedGcdResult`, with named fields `gcd`, `coeffA`, and `coeffB`. Compute all three values in one tail-recursive Euclidean pass using arbitrary-precision `Nat` and `Int` arithmetic. Prove `extendedGcd_gcd`, `extendedGcd_bezout`, divisibility, and the zero-input and equal-input simplification lemmas. Use mathlib's initialization and zero-input conventions: `extendedGcd 0 b = ⟨b, 0, 1⟩`, including at `(0, 0)`, and `extendedGcd a 0 = ⟨a, 1, 0⟩` for `a ≠ 0`. Specify no coefficient minimality or symmetry.

This is the standalone public API follow-up to [the review discussion on leanprover#15077](leanprover#15077 (comment)). Keep the UInt64 inverse integration out of this PR; it can use this API in a subsequent change. Leave mathlib's existing `xgcd`, `gcdA`, and `gcdB` names untouched.

Add regression coverage for kernel and compiled evaluation, signed coefficient examples, zero inputs, a curated cross-product of arbitrary-precision inputs, long Fibonacci-driven Euclidean chains, and use of the public lemmas.
@kim-em kim-em added the changelog-library Library label Sep 15, 2026
@github-actions github-actions Bot added the toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN label Sep 15, 2026
This PR accelerates compiled extended greatest-common-divisor computations while preserving the Lean definition’s exact coefficient choices.

Borrow bignum inputs, use read-only stack views for scalar inputs, and move heap-sized outputs into Lean objects. Handle zero, equal, and 16-bit inputs directly, and provide an arbitrary-precision native fallback when GMP is disabled. Add exact extern/reference comparisons and compiled-caller coverage for coefficient ties, scalar and signed-integer boundaries, heap-sized outputs, long Euclidean chains, and retained operands.
Keep the existing copying allocation and result-conversion paths. Limit the extended-GCD extern to borrowed inputs, stack views, and fast paths; allocate its outputs without changing other arithmetic operations.
@TwoFX

TwoFX commented Sep 15, 2026

Copy link
Copy Markdown
Member

Do you have numbers and a use case showing that having the extern implementation is really worth it? This PR would be much easier to merge if it didn't contain all the C code whose correctness is quite non-trivial.

@kim-em

kim-em commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

I measured the extern against the exact Lean body (Nat.extendedGcd.go a 1 0 b 0 1) in the same compiled executable. On an AMD EPYC 9455 with GMP 6.3.0 and an optimized build of 3c93b48cf6, median times over nine runs were:

Random operand size Lean Extern Speedup
16 bits 0.071 µs 0.072 µs 1.0×
32 bits 0.218 µs 0.180 µs 1.2×
64 bits 4.046 µs 0.314 µs 12.9×
256 bits 44.350 µs 1.568 µs 28.3×
1024 bits 218.692 µs 5.687 µs 38.5×
4096 bits 1238.250 µs 29.572 µs 41.9×

A concrete use case is modular inversion, as in mathlib’s ZMod.inv. For inversion modulo 2^255 − 19, including coefficient reduction, I measured 41.26 µs with the Lean body versus 1.39 µs with the extern (29.8×). A Lean loop computing only the needed coefficient took 27.34 µs, still 19.7× slower.

This PR simplifies runtime support for Nat.extendedGcd by reusing its Lean implementation for small and exceptional inputs and for builds without GMP.

Replace handwritten Euclidean loops with an exported Lean fallback, use ordinary mpz conversions for the GMP call, and test the fallback ownership convention with retained operands.
@kim-em

kim-em commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

Do you have numbers and a use case showing that having the extern implementation is really worth it? This PR would be much easier to merge if it didn't contain all the C code whose correctness is quite non-trivial.

I've reduced the new C code from about 70 lines to 30, with ~1% slowdowns on large GMP workloads and larger losses on small inputs and some no-GMP cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog-library Library toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants