Skip to content

Take a remainder of a whole power or factorial as it is built - #2330

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
a-remainder-of-a-power-or-factorial-is-taken-as-it-is-built
Nov 2, 2026
Merged

Rafael-SOWNet merged 1 commit into
masterfrom
a-remainder-of-a-power-or-factorial-is-taken-as-it-is-built

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member

The second half of the follow-up to Happypig375's comment on #2327, after #2329.

Before this PR, b ^ e mod m and n! mod m, for whole numbers, wrote out the whole power or factorial and then divided. Now they are reduced modulo m as they are built:

  • a power uses modular exponentiation;
  • a factorial is multiplied out factor by factor, in machine words when |m| < 2^31;
  • n! mod m is 0 outright once n >= |m|, since |m| then divides it.

The remainder is the floored one, as for the operator.

master this PR
2^(10^8) mod 1000 29 s, 13.9 GB allocated 2 ms, 0.8 MB
2^(10^7) mod 1000 0.68 s, 772 MB 44 ms, 0.8 MB
10^(10^9) mod 7 no answer in 60 s 35 ms
(10^5)! mod 1000003 21 s, 13.7 GB 4 ms, 0.8 MB

0 ^ 0, a negative or symbolic exponent, and a divisor that is not a whole number go to the evaluation as before. No answer changes, apart from the ones that never finished.

Tests: ARemainderOfALargeWholeTest checks 16 remainders against Python's pow and math.factorial, including negative bases and divisors, 0 ^ 0 and a reciprocal. It also asserts that 2 ^ (10 ^ 7) mod 1000 allocates under 20 MB; that fails on master, at 810 MB.

🤖 Generated with Claude Code

b ^ e mod m and n! mod m, for whole b, e >= 0, n >= 0 and m not 0, wrote
the power or the factorial out first and then divided. They are now
reduced modulo m as they are built: the power by modular exponentiation,
the factorial factor by factor (in machine words under 2^31), and n! mod m
is 0 outright once n >= |m|, since |m| then divides it. The remainder is
the floored one, as the operator's.

| | before | after |
|---|---|---|
| 2^(10^8) mod 1000 | 29 s, 13.9 GB | 2 ms, 0.8 MB |
| 2^(10^7) mod 1000 | 0.68 s, 772 MB | 44 ms, 0.8 MB |
| 10^(10^9) mod 7 | no answer in 60 s | 35 ms |
| (10^5)! mod 1000003 | 21 s, 13.7 GB | 4 ms, 0.8 MB |
| (10^7)! mod 1000000007 | -- | 62 ms |

0 ^ 0, a negative or symbolic exponent, and a divisor that is not a whole
number are left to the evaluation as before. No answer changes; the ones
that change are those that never finished.

#2327 (comment)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Rafael-SOWNet Rafael-SOWNet added this to the 2.7.0 milestone Nov 2, 2026
@Rafael-SOWNet Rafael-SOWNet moved this from Todo to In review in AngouriMath Nov 2, 2026
@codecov

codecov Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.75%. Comparing base (93a52f0) to head (d3d8f70).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #2330   +/-   ##
=======================================
  Coverage   86.75%   86.75%           
=======================================
  Files         312      312           
  Lines       68447    68477   +30     
  Branches    19909    19921   +12     
=======================================
+ Hits        59379    59406   +27     
- Misses       4908     4913    +5     
+ Partials     4160     4158    -2     
Flag Coverage Δ
cpp 37.77% <ø> (ø)
csharp 87.29% <100.00%> (+<0.01%) ⬆️
fsharp 32.97% <ø> (ø)
interactive 40.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...nuous/Evaluation.Continuous.Arithmetics.Classes.cs 87.98% <100.00%> (+0.41%) ⬆️

... and 5 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Happypig375

Copy link
Copy Markdown
Member

This shows the importance of separating numerical evaluation from all symbolic operations in v3, there are a lot of special forms that we can take advantage of that would result in huge numbers if evaluated directly

@Rafael-SOWNet

Copy link
Copy Markdown
Member Author

Agreed. Here it's a shortcut in Modf's own evaluation, which spots the shape before the children are evaluated. In v3 it belongs in the evaluator, which would see b^e mod m as one operation instead of building b^e first.

@Rafael-SOWNet
Rafael-SOWNet merged commit 5c725f2 into master Nov 2, 2026
35 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in AngouriMath Nov 2, 2026
@Rafael-SOWNet
Rafael-SOWNet deleted the a-remainder-of-a-power-or-factorial-is-taken-as-it-is-built branch November 2, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants