Skip to content

feat: expose LibTomMath modular arithmetic as primitives#6026

Open
ggreif wants to merge 16 commits intomasterfrom
gabor/modular-arithmetic
Open

feat: expose LibTomMath modular arithmetic as primitives#6026
ggreif wants to merge 16 commits intomasterfrom
gabor/modular-arithmetic

Conversation

@ggreif
Copy link
Copy Markdown
Contributor

@ggreif ggreif commented Apr 16, 2026

Summary

Expose LibTomMath modular arithmetic operations as Motoko primitives:

  • Prim.intAddMod(a, b, m)(a + b) mod m via mp_addmod
  • Prim.intSubMod(a, b, m)(a - b) mod m via mp_submod
  • Prim.intMulMod(a, b, m)(a * b) mod m via mp_mulmod
  • Prim.intPowMod(base, exp, m)base^exp mod m via mp_exptmod

All operate on Int (arbitrary precision). intPowMod is particularly useful for cryptographic applications (RSA, Diffie-Hellman, etc.) where modular exponentiation with large integers is needed.

Changes

Layer File What
RTS/C rts/Makefile Add libtommath source files + bindgen allowlist
RTS/Rust rts/motoko-rts/src/bigint.rs Wrapper functions calling mp_*
Compiler compile_enhanced.ml, compile_classical.ml Wasm imports + OtherPrim codegen
Interpreter mo_values/prim.ml OCaml reference implementation
Prelude src/prelude/prim.mo Public Prim declarations
Test test/run/modular-arith.mo Happy-path tests incl. Fermat's little theorem

Test plan

  • nix build .#tests.unit passes
  • nix build .#tests.release passes
  • CI green

🤖 Generated with Claude Code

TODO

  • Amend Changelog.md

Add intAddMod, intSubMod, intMulMod, intPowMod primitives backed
by mp_addmod, mp_submod, mp_mulmod, mp_exptmod from LibTomMath.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ggreif ggreif requested a review from a team as a code owner April 16, 2026 12:11
@ggreif
Copy link
Copy Markdown
Contributor Author

ggreif commented Apr 16, 2026

cc @timohanke — this was your request 🎯

Required by s_mp_exptmod_fast at link time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ggreif ggreif changed the title feat: expose LibTomMath modular arithmetic via Prim feat: expose LibTomMath modular arithmetic via Prim Apr 16, 2026
@ggreif ggreif changed the title feat: expose LibTomMath modular arithmetic via Prim feat: expose LibTomMath modular arithmetic via Prim Apr 16, 2026
Comment thread rts/Makefile Outdated
Required transitively by mp_invmod (called from mp_exptmod for
negative exponents) and mp_reduce/mp_dr_reduce.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ggreif ggreif self-assigned this Apr 16, 2026
@ggreif ggreif added the build_artifacts Upload moc binary as workflow artifacts label Apr 16, 2026
ggreif and others added 9 commits April 16, 2026 16:21
…gets

- GADT constructors: AddMod, SubMod, MulMod, PowMod on MOTerm Integer
- Evaluatable, unparseMO, Arbitrary, shrinking for all four ops
- modularProps test group wired into main test tree
- nix targets: tests.qc-{arith,modular,conversions,utf8,matching}
- fix: add missing s_mp_invmod_fast, s_mp_invmod_slow to TOMMATHFILES

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… test size

- prop_verifies takes TestCases, not MOTerm Integer
- Shadow pattern vars in eval (cleaner: do a <- eval a)
- Reduce modulus to ≤1002 and exponent to ≤9 for speed
- withMaxSuccess 50 for modular suite

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Required by s_mp_exptmod_fast for fast modular exponentiation path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Prim type listing now includes intAddMod, intMulMod, intPowMod,
intSubMod — update expected output for 4 fail tests × 2 modes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
(3 - 7) mod 10 = 6, not -4. mp_submod always returns non-negative.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…antics

- Remove #[cfg(feature = "ic")] from bigint_{addmod,submod,mulmod,exptmod}
  so they're available in wasmtime/WASI test mode (matching bigint_pow)
- Fix OCaml interpreter to return non-negative results for positive modulus
  (matching LibTomMath's mp_*mod behavior, not OCaml's rem)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ggreif ggreif force-pushed the gabor/modular-arithmetic branch from 7d9284a to 873abcc Compare April 17, 2026 00:01
Small integers in Motoko are stored as tagged scalars, not heap-
allocated BigInts. The OtherPrim codegen was calling bigint_addmod
etc. directly, passing tagged scalars where the RTS expects BigInt
pointers — causing wrong results or traps.

Fix: add slow_only3 to MakeCompact (both classical and enhanced
backends) that boxes any tagged scalar arguments before calling the
RTS, and compacts the result back. This mirrors try_unbox2 used by
the existing binary arithmetic ops.

Also bump QC modular suite to 100 samples.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 17, 2026

Comparing from 9412bb5 to 8992c79:
In terms of gas, no changes are observed in 5 tests.
In terms of size, 5 tests regressed and the mean change is +3.9%.

@ggreif
Copy link
Copy Markdown
Contributor Author

ggreif commented Apr 17, 2026

@timohanke — your modular arithmetic primitives are ready! 🎉

Prim.intAddMod, intSubMod, intMulMod, intPowMod backed by LibTomMath's mp_addmod, mp_submod, mp_mulmod, mp_exptmod. Build artifacts attached to the CI run.

Getting here was a bit of a Grothendieck experience — rather than cracking the nut with a hammer, we let the rising sea of understanding slowly submerge the problem. From missing libtommath transitive dependencies, through WASI allocator incompatibilities, to discovering that Motoko's compact BigNum representation requires proper unboxing for ternary operations — each wrong theory peeled back a layer until the fix emerged naturally from the architecture.

Build artifacts (macOS, Ubuntu, ARM) are available from the CI run linked above.

E.g. macOS ARM64 is here: https://github.com/caffeinelabs/motoko/actions/runs/24552026361/artifacts/6490288624

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@timohanke
Copy link
Copy Markdown
Contributor

Thanks @ggreif . This is awesome. Looking forward to try it out.

We should also consider exposing mp_sqr. And additionally, make the compiler detect x ** 2 or x * x in the source code and call it. (Off-topic for the feature, which is about modular arithmetic and mp_sqr isn't modular, but on-topic application-wise because it is useful for the same cryptographic applications.)

Further down the road we can also consider exposing

mp_montgomery_setup
mp_montgomery_calc_normalization
mp_montgomery_reduce

but let's see first how the ones already added perform in practice.

@ggreif
Copy link
Copy Markdown
Contributor Author

ggreif commented Apr 17, 2026

We should also consider exposing mp_sqr. And additionally, make the compiler detect x ** 2 or x * x in the source code and call it. (Off-topic for the feature, which is about modular arithmetic and mp_sqr isn't modular, but on-topic application-wise because it is useful for the same cryptographic applications.)

Good point. We could do
We sure could:

  • spot both args VarE and same -> call mp_sqr
  • spot literal ** exponent 2 and call mp_sqr
  • popcnt exponent and if =1 do iterated mp_sqr
    Both of the latter are possible, but does TomMath already do the second? Still it would be a win, because ** 2 is internally 0b100 and ctz gives 2 then iterate mp_sqr only once. For 0b1000 (a.k.a. 4) iterate twice. Etc. Maybe worth it. When heap pointer popcnt = 1 will never trigger.

@ggreif ggreif added opportunity More optimisation opportunities inside and removed build_artifacts Upload moc binary as workflow artifacts labels Apr 17, 2026
@timohanke
Copy link
Copy Markdown
Contributor

Also mp_invmod should be exposed as well. That fits right into this PR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ggreif ggreif changed the title feat: expose LibTomMath modular arithmetic via Prim feat: expose LibTomMath modular arithmetic as primitives Apr 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

opportunity More optimisation opportunities inside

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants