-
Notifications
You must be signed in to change notification settings - Fork 60
Binary XGCD #761
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
base: master
Are you sure you want to change the base?
Binary XGCD #761
Conversation
I was experimenting with this (understanding it still is to-be-merged and not expecting it to be final) when my code calling stopped working. I was reviewing the exact issue for a good bit before I noticed this 😅 Since I didn't see it discussed, I wanted to ensure it was raised. I'll post the pair in a moment. |
This is on a 64-bit platform with the latest commit on your branch (from 13 hours ago). My snippet is to my wrapper fn (as I prior had defined a wrapper around |
Sorry, I think that may be the wrong pair. If so, please try:
Those serialization are their 1024-bit form yet it has the same issue even with just a 512-bit Uint. |
The first pair you sent is already producing an incorrect output on my end. Still, thanks for the second pair! |
Happy to hear I was able to help! Really excited for this as someone who spends 50% of my runtime on safegcd 😅 |
src/modular/bingcd/xgcd.rs
Outdated
// TODO: this is sketchy! | ||
matrix = update_matrix.wrapping_mul_right(&matrix); | ||
matrix.conditional_negate_top_row(a_sgn); | ||
matrix.conditional_negate_bottom_row(b_sgn); | ||
matrix.conditional_negate(a_sgn); |
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.
@kayabaNerve I think I found the culprit 🙈 As it turns out, in rare circumstances (like yours) this does not work 😅
I knew this was sketchy, and did it anyway. Next time, I'll think twice before committing something I know is sketchy.
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.
FYI: the inputs you provided trigger a particular situation somewhere along the way where a > b
but a.compact() < b.compact
. As a result, this matrix multiplication should be doing something different than it is now.
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.
Fun!
Is the fix as simple as reverting back to the top/bottom row negation, instead of the entire matrix negation, or is it unfortunately a bit more annoying to navigate?
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.
Sadly, solving this problem is more complicated than that
Reverting to the old version is not really an option, since that version was not capable of computing the xgcd
for Uint
s that had their msb set: I needed that top bit to represent the sign of the matrix elements. I solved this using an interesting trick I previously described here. It turns out that the input you presented is an exception to "the trick" I described there.
We can, however, use the fact that a > b
and a.compact() < b.compact()
to conclude that the top K-1
bits of both a
and b
are the same. This implies that just subtracting b
from a
already allows the latter to shrink by K-1
bits. We can, therefore, replace the outcome of the (..., update_matrix) = partial_xgcd(...)
with a simple ((1, -1),(0,1))
matrix and still be guaranteed that the algorithm is done after all steps.
Sadly, there are some intricate details I skipped over that make it a tad more complicated 😅
- The mirrored situation can also take place:
a < b
anda.compact() > b.compact()
- Subtracting
b
froma
can make the latter even, which means we have to swap them. - We want the solution to be fast to execute.
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.
@kayabaNerve, the new commits I added should fix the bug. Please try it out and check whether it works on your end as well!
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.
I'm happy to report the patch has a negligible impact on performance: ~1% 👌
Also update the bug detection signal
The new commit, "Fix bug", which I have not personally reviewed, passes my personal code! Thank you! |
This is from the "Fix bug" commit I prior commented worked for my prior noted issues. I'm actually using it in the same context, solely running a different test suite, so I'm unsure why this current test suite is managing to trip this low-level bug when my prior one didn't. I'll try to find a reproducible test case, but at least having noted this ideally lets some review be started on. EDIT:
|
@kayabaNerve, thank you for being a persistent tester! I hope to find some time later this week to debug the issue you presented. I appreciate your patience! (I'm delighted I added in those |
Also just hit |
@kayabaNerve I had a quick look today. Your counterexample demonstrates there are more annoying ways in which the |
I did try poking at this myself to see if I could fix it, but it really wasn't immediately familiar/obvious to me. I ended up just making a fork which never uses the optimized algorithm as to not block my work. Pros: Works. I may try to work on this again if I myself have free time before the deadline, but any further help from you would truly be appreciated! This truly is a massive improvement for crypto-bigint on this topic and it's fundamentally what makes my current research topic go from 'initial impl for reference which isn't discussable' to 'initial impl which yields feasible results for practical deployment'. EDIT: The Bezout coefficients are correct or their additive inverse modulo the other value divided by their GCD, for this specific test case. From these incorrect values, it's trivial to calculate the proper values with a post-processing run. I tried to do so on every result, to restore functioning despite not having a proper fix, but that yields other errors so this isn't a universal fact. |
I've worked on this here-and-there for the past couple days, without much luck. The problemThe failing input presented by @kayabaNerve illustrates that the trick does not always apply. SolutionsI've thought of a couple solutions: 1) Using
|
@erik-3milabs if it's just a single extra limb, with some effort you could always make a struct that adds that extra limb which avoids type-level size calculations |
Continuation of the work started in #755