Skip to content

Commit dda7460

Browse files
committedNov 14, 2013
Merge pull request #4811 from stevengj/invmod
fix gcd & lcm sign and optimize gcdx and invmod
2 parents 629e615 + ef40cd9 commit dda7460

File tree

6 files changed

+41
-23
lines changed

6 files changed

+41
-23
lines changed
 

‎base/gmp.jl

+5-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Base: *, +, -, /, <, <<, >>, >>>, <=, ==, >, >=, ^, (~), (&), (|), ($),
66
binomial, cmp, convert, div, divrem, factorial, fld, gcd, gcdx, lcm, mod,
77
ndigits, promote_rule, rem, show, isqrt, string, isprime, powermod,
88
widemul, sum, trailing_zeros, trailing_ones, count_ones, base, parseint,
9-
serialize, deserialize, bin, oct, dec, hex, isequal
9+
serialize, deserialize, bin, oct, dec, hex, isequal, invmod
1010

1111
type BigInt <: Integer
1212
alloc::Cint
@@ -152,7 +152,7 @@ deserialize(s, ::Type{BigInt}) = parseint(BigInt, deserialize(s), 62)
152152
# Binary ops
153153
for (fJ, fC) in ((:+, :add), (:-,:sub), (:*, :mul),
154154
(:fld, :fdiv_q), (:div, :tdiv_q), (:mod, :fdiv_r), (:rem, :tdiv_r),
155-
(:gcd, :gcd), (:lcm, :lcm),
155+
(:gcd, :gcd), (:lcm, :lcm), (:invmod, :invert),
156156
(:&, :and), (:|, :ior), (:$, :xor))
157157
@eval begin
158158
function ($fJ)(x::BigInt, y::BigInt)
@@ -314,6 +314,9 @@ powermod(x::BigInt, p::Integer, m::BigInt) = powermod(x, BigInt(p), m)
314314
powermod(x::BigInt, p::Integer, m::Integer) = powermod(x, BigInt(p), BigInt(m))
315315

316316
function gcdx(a::BigInt, b::BigInt)
317+
if b == 0 # shortcut this to ensure consistent results with gcdx(a,b)
318+
return a < 0 ? (-a,-one(BigInt),zero(BigInt)) : (a,one(BigInt),zero(BigInt))
319+
end
317320
g = BigInt()
318321
s = BigInt()
319322
t = BigInt()

‎base/intfuncs.jl

+16-14
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,16 @@ abs(x::Signed) = flipsign(x,x)
3333
## number-theoretic functions ##
3434

3535
function gcd{T<:Integer}(a::T, b::T)
36-
neg = a < 0
3736
while b != 0
3837
t = b
3938
b = rem(a, b)
4039
a = t
4140
end
42-
g = abs(a)
43-
neg ? -g : g
41+
abs(a)
4442
end
45-
lcm{T<:Integer}(a::T, b::T) = a * div(b, gcd(b,a))
43+
44+
# explicit a==0 test is to handle case of lcm(0,0) correctly
45+
lcm{T<:Integer}(a::T, b::T) = a == 0 ? a : abs(a * div(b, gcd(b,a)))
4646

4747
gcd(a::Integer) = a
4848
lcm(a::Integer) = a
@@ -52,21 +52,23 @@ gcd(a::Integer, b::Integer...) = gcd(a, gcd(b...))
5252
lcm(a::Integer, b::Integer...) = lcm(a, lcm(b...))
5353

5454
# return (gcd(a,b),x,y) such that ax+by == gcd(a,b)
55-
function gcdx(a, b)
56-
if b == 0
57-
(a, 1, 0)
58-
else
59-
m = rem(a, b)
60-
k = div((a-m), b)
61-
(g, x, y) = gcdx(b, m)
62-
(g, y, x-k*y)
55+
function gcdx{T<:Integer}(a::T, b::T)
56+
s0, s1 = one(T), zero(T)
57+
t0, t1 = s1, s0
58+
while b != 0
59+
q = div(a, b)
60+
a, b = b, rem(a, b)
61+
s0, s1 = s1, s0 - q*s1
62+
t0, t1 = t1, t0 - q*t1
6363
end
64+
a < 0 ? (-a, -s0, -t0) : (a, s0, t0)
6465
end
66+
gcdx(a::Integer, b::Integer) = gcdx(promote(a,b)...)
6567

66-
# multiplicative inverse of x mod m, error if none
68+
# multiplicative inverse of n mod m, error if none
6769
function invmod(n, m)
6870
g, x, y = gcdx(n, m)
69-
g != 1 ? error("no inverse exists") : (x < 0 ? m + x : x)
71+
g == 1 ? (x < 0 ? abs(m) + x : x) : error("no inverse exists")
7072
end
7173

7274
# ^ for any x supporting *

‎base/rational.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ immutable Rational{T<:Integer} <: Real
66
if num == 0 && den == 0
77
error("invalid rational: 0//0")
88
end
9-
g = gcd(den, num)
9+
g = den < 0 ? -gcd(den,num) : gcd(den, num)
1010
new(div(num, g), div(den, g))
1111
end
1212
end

‎doc/helpdb.jl

+3-3
Original file line numberDiff line numberDiff line change
@@ -3885,19 +3885,19 @@ popdisplay(d::Display)
38853885

38863886
("Mathematical Functions","Base","gcd","gcd(x, y)
38873887
3888-
Greatest common divisor
3888+
Greatest common (positive) divisor (or zero if x and y are both zero).
38893889
38903890
"),
38913891

38923892
("Mathematical Functions","Base","lcm","lcm(x, y)
38933893
3894-
Least common multiple
3894+
Least common (non-negative) multiple.
38953895
38963896
"),
38973897

38983898
("Mathematical Functions","Base","gcdx","gcdx(x, y)
38993899
3900-
Greatest common divisor, also returning integer coefficients \"u\"
3900+
Greatest common (positive) divisor, also returning integer coefficients \"u\"
39013901
and \"v\" that solve \"ux+vy == gcd(x,y)\"
39023902
39033903
"),

‎doc/stdlib/base.rst

+3-3
Original file line numberDiff line numberDiff line change
@@ -2632,15 +2632,15 @@ Mathematical Functions
26322632

26332633
.. function:: gcd(x,y)
26342634

2635-
Greatest common divisor
2635+
Greatest common (positive) divisor (or zero if x and y are both zero).
26362636

26372637
.. function:: lcm(x,y)
26382638

2639-
Least common multiple
2639+
Least common (non-negative) multiple.
26402640

26412641
.. function:: gcdx(x,y)
26422642

2643-
Greatest common divisor, also returning integer coefficients ``u`` and ``v`` that solve ``ux+vy == gcd(x,y)``
2643+
Greatest common (positive) divisor, also returning integer coefficients ``u`` and ``v`` that solve ``ux+vy == gcd(x,y)``
26442644

26452645
.. function:: ispow2(n)
26462646

‎test/numbers.jl

+13
Original file line numberDiff line numberDiff line change
@@ -1511,3 +1511,16 @@ end
15111511
# overflow in rational comparison
15121512
@test 3//2 < typemax(Int)
15131513
@test 3//2 <= typemax(Int)
1514+
1515+
# check gcd and related functions against GMP
1516+
for i = -20:20, j = -20:20
1517+
local d = gcd(i,j)
1518+
@test d >= 0
1519+
@test lcm(i,j) >= 0
1520+
local ib = big(i)
1521+
local jb = big(j)
1522+
@test d == gcd(ib,jb)
1523+
@test lcm(i,j) == lcm(ib,jb)
1524+
@test gcdx(i,j) == gcdx(ib,jb)
1525+
@test d != 1 || invmod(i,j) == invmod(ib,jb)
1526+
end

0 commit comments

Comments
 (0)
Please sign in to comment.