-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add utilities for modular arithmetic #320
Conversation
These utilities all operate on unsigned 64-bit integers. The main design goal is to produce correct answers for all inputs while avoiding overflow. Most operations are standard: addition, subtraction, multiplication, powers. The one non-standard one is "half_mod_odd". This operation is `(x/2) % n`, but only if `n` is odd. If `x` is also odd, we add `n` before dividing by 2, which gives us an integer result. We'll need this operation for the strong Lucas probable prime checking later on. These are Au-internal functions, so we use unchecked preconditions: as long as the caller makes sure the inputs are already reduced-mod-n, we'll keep them that way. Helps #217.
The new test would fail without this change!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implementation looks good. Tests look good. Thanks for the comments.
// | ||
// Precondition: (a < n). | ||
// Precondition: (n is odd). | ||
constexpr uint64_t half_mod_odd(uint64_t a, uint64_t n) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we document this a little more? If a < n
, then a / 2 < n
. So (a / 2) % n
would just be a
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
// | ||
// Precondition: (a < n). | ||
// Precondition: (b < n). | ||
constexpr uint64_t mul_mod(uint64_t a, uint64_t b, uint64_t n) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation seems right. It gets the right answer for the JUST_UNDER_HALF, 10u, MAX
case as opposed to https://www.geeksforgeeks.org/how-to-avoid-overflow-in-modular-multiplication/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Geeksforgeeks has such terrible quality control. I also stumbled on this supposed Baillie-PSW implementation, which is very clearly only the Miller-Rabin step! (Not to mention: their modPow
implementation runs in O(exp) rather than O(log(exp)). Bring a pillow!)
These utilities all operate on unsigned 64-bit integers. The main
design goal is to produce correct answers for all inputs while avoiding
overflow.
Most operations are standard: addition, subtraction, multiplication,
powers. The one non-standard one is "half_mod_odd". This operation is
(x/2) % n
, but only ifn
is odd. Ifx
is also odd, we addn
before dividing by 2, which gives us an integer result. We'll need this
operation for the strong Lucas probable prime checking later on.
These are Au-internal functions, so we use unchecked preconditions: as
long as the caller makes sure the inputs are already reduced-mod-n,
we'll keep them that way.
Helps #217.