Skip to content
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 lcm and gcd functions #15

Closed
kunerd opened this issue Jul 17, 2015 · 8 comments
Closed

Add lcm and gcd functions #15

kunerd opened this issue Jul 17, 2015 · 8 comments

Comments

@kunerd
Copy link
Contributor

kunerd commented Jul 17, 2015

No description provided.

@vks
Copy link
Contributor

vks commented Jul 23, 2015

An implementation:

extern crate ramp;

use std::cmp::Ordering;
use ramp::Int;

#[derive(Debug,Copy,Clone,PartialEq,Eq)]
struct GcdResult<T> {
    /// Greatest common divisor.
    gcd: T,
    /// Coefficients such that: gcd(a, b) = c1*a + c2*b
    c1: T, c2: T,
}

/// Calculate greatest common divisor and the corresponding coefficients.
fn extended_gcd(a: Int, b: Int) -> GcdResult<Int> {
    // Euclid's extended algorithm
    let (mut s, mut old_s) = (Int::zero(), Int::one());
    let (mut t, mut old_t) = (Int::one(), Int::zero());
    let (mut r, mut old_r) = (b, a);

    while r != 0 {
        let quotient = &old_r / &r;
        let mut tmp;
        tmp = r.clone(); r = &old_r - &quotient * r; old_r = tmp;
        tmp = s.clone(); s = &old_s - &quotient * s; old_s = tmp;
        tmp = t.clone(); t = &old_t - &quotient * t; old_t = tmp;
    }

    let _quotients = (t, s); // == (a, b) / gcd

    GcdResult { gcd: old_r, c1: old_s, c2: old_t }
}

fn main() {
    println!("{:?}", extended_gcd(Int::from(6), Int::from(3)));
}

From this it is possible to calculate a few other number theoretical functions.

Where should this functionality go? int.rs? A new file? Should the interface rather be a.gcd(b)? Should it be destructive?

(By the way, I think everything should be destructive, adding a .clone() is easy. We could get rid of square and only have dsquare, because .clone().dsquare() is equivalent to .square().)

@Aatch
Copy link
Owner

Aatch commented Jul 24, 2015

@vks I'd really like to see this implemented in the low-level section to avoid as much copying. Probably in a new file ll/gcd.rs with the signature unsafe fn extended_gcd(gp: *mut Limb, ap: *mut Limb, an: i32, bp: *mut Limb, bn: i32) -> i32 with {ap, an} and {bp,bn} being modified in-place, and the gcd being in gp and the return value being the number of limbs in the GCD.

Since GCD is integral to implementing rationals, I want to make sure it's reasonably efficient in it's first implementation. Doesn't have to be super-tuned like GMP's right away, but just implementing it in the unsafe portion of the library should be enough.

As for what the higher-level API should be, I think that a static method on Int is probably the best option, so calling it would be Int::gcd(a, b), and have it take a and b by-value, with that returning an Int too, then an extended_gcd method which could either take a and b by-value and return the GcdResult you have there or take a and b as mutable-references and modify them in-place. I'm leaning towards the first option as I agree that pretty much everything that might have to modify the value passed should take it by-value.

@kunerd
Copy link
Contributor Author

kunerd commented Aug 3, 2015

Euclid's extended algorithm is not a good fit for multiple-precision integers, see http://cacr.uwaterloo.ca/hac/about/chap14.pdf Section 14.4 for more details. I think it would be a good idea to implement one or more of the algorithms, which are described in this section.

@kunerd
Copy link
Contributor Author

kunerd commented Aug 21, 2015

I have implemented the binary gcd and the extended binary gcd algorithms, as described in the "Handbook of applied cryptography". Both run two times faster as Euclid's extended algorithm.
@Aatch: I have no idea how to move my current safe implementation into the ll part. Should I create a pull-request for the save implementation, so that we can move it into ll together?

@kunerd
Copy link
Contributor Author

kunerd commented Oct 1, 2015

I have seen, that other libraries (like GMP) are using different algorithms for the various input sizes.
Therefore, @vks It would be very nice to have your extended euclidean algorithm implemented in the low-level part of ramp also - as aatch already said. After that we can do some benchmarks that orientate on the thresholds used by GMP.

See also:
GMP - gcd()
https://gmplib.org/manual/Greatest-Common-Divisor-Algorithms.html
For really large inputs maybe also:
https://hal.archives-ouvertes.fr/file/index/docid/71533/filename/RR-5050.pdf#page=22&zoom=auto,-74,716

@vks
Copy link
Contributor

vks commented Oct 1, 2015

I don't know yet when I'll find the time to implement the low-level version. (I've had a quick look at low-level ramp, and frankly it made the task at hand seem a bit daunting.)

vks added a commit to vks/ramp that referenced this issue Oct 1, 2015
@vks
Copy link
Contributor

vks commented Jan 8, 2016

The only thing missing to close this issue is a safe interface to gcd and lcm, right?

Edit: Nevermind, those are implemented. It would be nice to have extended Euclid algorithm though, to get the coefficients.

@rozbb
Copy link
Collaborator

rozbb commented Jun 18, 2018

I like the idea of a low-level extended gcd function. I may try to implement it in the future.

@rozbb rozbb closed this as completed Jun 18, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants