From dd738f9ee8dc5875934b22e9f635158b755654ae Mon Sep 17 00:00:00 2001 From: Ravishankar Joshi <21024229+ravibitsgoa@users.noreply.github.com> Date: Sun, 19 Apr 2020 21:58:52 +0530 Subject: [PATCH] Added tests for gcd and lcm for other integer datatypes. (#35451) --- base/intfuncs.jl | 39 +++++++++-- test/intfuncs.jl | 167 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 182 insertions(+), 24 deletions(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 6209828d239f0..22aaecfa28158 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -18,6 +18,21 @@ julia> gcd(6,9) julia> gcd(6,-9) 3 + +julia> gcd(6,0) +6 + +julia> gcd(0,0) +0 + +julia> gcd(1//3,2//3) +1//3 + +julia> gcd(1//3,-2//3) +1//3 + +julia> gcd(1//3,2) +1//3 ``` """ function gcd(a::T, b::T) where T<:Integer @@ -32,8 +47,8 @@ end # binary GCD (aka Stein's) algorithm # about 1.7x (2.1x) faster for random Int64s (Int128s) function gcd(a::T, b::T) where T<:Union{Int8,UInt8,Int16,UInt16,Int32,UInt32,Int64,UInt64,Int128,UInt128} - a == 0 && return abs(b) - b == 0 && return abs(a) + a == 0 && return checked_abs(b) + b == 0 && return checked_abs(a) za = trailing_zeros(a) zb = trailing_zeros(b) k = min(za, zb) @@ -69,12 +84,28 @@ julia> lcm(2,3) julia> lcm(-2,3) 6 + +julia> lcm(0,3) +0 + +julia> lcm(0,0) +0 + +julia> lcm(1//3,2//3) +2//3 + +julia> lcm(1//3,-2//3) +2//3 + +julia> lcm(1//3,2) +2//1 ``` """ function lcm(a::T, b::T) where T<:Integer # explicit a==0 test is to handle case of lcm(0,0) correctly - if a == 0 - return a + # explicit b==0 test is to handle case of lcm(typemin(T),0) correctly + if a == 0 || b == 0 + return zero(a) else return checked_abs(checked_mul(a, div(b, gcd(b,a)))) end diff --git a/test/intfuncs.jl b/test/intfuncs.jl index 24d3c1f9aca87..de4fd21060294 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -3,51 +3,158 @@ using Random @testset "gcd/lcm" begin - # Int32 and Int64 take different code paths -- test both - for T in (Int32, Int64) + # All Integer data types take different code paths -- test all + # TODO: Test gcd and lcm for BigInt. + for T in (Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128) @test gcd(T(3)) === T(3) @test gcd(T(3), T(5)) === T(1) @test gcd(T(3), T(15)) === T(3) @test gcd(T(0), T(15)) === T(15) - @test gcd(T(3), T(-15)) === T(3) - @test gcd(T(-3), T(-15)) === T(3) + @test gcd(T(15), T(0)) === T(15) + if T <: Signed + @test gcd(T(0), T(-15)) === T(15) + @test gcd(T(-15), T(0)) === T(15) + @test gcd(T(3), T(-15)) === T(3) + @test gcd(T(-3), T(-15)) === T(3) + end @test gcd(T(0), T(0)) === T(0) @test gcd(T(2), T(4), T(6)) === T(2) + if T <: Signed + @test gcd(T(2), T(4), T(-6)) === T(2) + @test gcd(T(2), T(-4), T(-6)) === T(2) + @test gcd(T(-2), T(4), T(-6)) === T(2) + @test gcd(T(-2), T(-4), T(-6)) === T(2) + end @test gcd(typemax(T), T(1)) === T(1) - @test gcd(-typemax(T), T(1)) === T(1) - @test gcd(typemin(T), T(1)) === T(1) - @test_throws OverflowError gcd(typemin(T), typemin(T)) + @test gcd(T(1), typemax(T)) === T(1) + @test gcd(typemax(T), T(0)) === typemax(T) + @test gcd(T(0), typemax(T)) === typemax(T) + @test gcd(typemax(T), typemax(T)) === typemax(T) + @test gcd(typemax(T), typemax(T)-T(1)) === T(1) # gcd(n, n-1) = 1. n and n-1 are always coprime. + + if T <: Signed + @test gcd(-typemax(T), T(1)) === T(1) + @test gcd(T(1), -typemax(T)) === T(1) + @test gcd(-typemax(T), T(0)) === typemax(T) + @test gcd(T(0), -typemax(T)) === typemax(T) + @test gcd(-typemax(T), -typemax(T)) === typemax(T) + @test gcd(typemax(T), -typemax(T)) === typemax(T) + @test gcd(-typemax(T), typemax(T)) === typemax(T) + + @test gcd(typemin(T), T(1)) === T(1) + @test gcd(T(1), typemin(T)) === T(1) + @test gcd(typemin(T), typemin(T)+T(1)) === T(1) # gcd(n, n+1) = 1. n and n+1 are always coprime. + @test_throws OverflowError gcd(typemin(T), typemin(T)) + @test_throws OverflowError gcd(typemin(T), T(0)) + @test_throws OverflowError gcd(T(0), typemin(T)) + else + # For Unsigned Integer types, -typemax(T) == 1. + @test gcd(-typemax(T), T(1)) === T(1) + @test gcd(T(1), -typemax(T)) === T(1) + @test gcd(-typemax(T), T(0)) === T(1) + @test gcd(T(0), -typemax(T)) === T(1) + @test gcd(-typemax(T), -typemax(T)) === T(1) + @test gcd(-typemax(T), typemax(T)) === T(1) + @test gcd(typemax(T), -typemax(T)) === T(1) + + # For Unsigned Integer types, typemin(T) == 0. + @test gcd(typemin(T), T(1)) === T(1) + @test gcd(T(1), typemin(T)) === T(1) + @test gcd(typemin(T), typemin(T)+T(1)) === T(1) # gcd(n, n+1) = 1. n and n+1 are always coprime. + @test gcd(typemin(T), typemin(T)) === T(0) + @test gcd(typemin(T), T(0)) === T(0) + @test gcd(T(0), typemin(T)) === T(0) + end @test lcm(T(0)) === T(0) @test lcm(T(2)) === T(2) @test lcm(T(2), T(3)) === T(6) + @test lcm(T(3), T(2)) === T(6) @test lcm(T(4), T(6)) === T(12) + @test lcm(T(6), T(4)) === T(12) @test lcm(T(3), T(0)) === T(0) + @test lcm(T(0), T(3)) === T(0) @test lcm(T(0), T(0)) === T(0) - @test lcm(T(4), T(-6)) === T(12) - @test lcm(T(-4), T(-6)) === T(12) + if T <: Signed + @test lcm(T(0), T(-4)) === T(0) + @test lcm(T(-4), T(0)) === T(0) + @test lcm(T(4), T(-6)) === T(12) + @test lcm(T(-4), T(-6)) === T(12) + end @test lcm(T(2), T(4), T(6)) === T(12) + @test lcm(T(2), T(4), T(0)) === T(0) + if T <: Signed + @test lcm(T(2), T(4), T(-6)) === T(12) + @test lcm(T(2), T(-4), T(-6)) === T(12) + @test lcm(T(-2), T(-4), T(-6)) === T(12) + @test lcm(T(-2), T(0), T(-6)) === T(0) + end @test lcm(typemax(T), T(1)) === typemax(T) - @test lcm(-typemax(T), T(1)) === typemax(T) - @test_throws OverflowError lcm(typemin(T), T(1)) - @test_throws OverflowError lcm(typemin(T), typemin(T)) + @test lcm(T(1), typemax(T)) === typemax(T) + @test lcm(typemax(T), T(0)) === T(0) + @test lcm(T(0), typemax(T)) === T(0) + @test lcm(typemax(T), typemax(T)) === typemax(T) + @test_throws OverflowError lcm(typemax(T), typemax(T)-T(1)) # lcm(n, n-1) = n*(n-1). Since n and n-1 are always coprime. @test_throws OverflowError lcm(typemax(T), T(2)) + + let x = isqrt(typemax(T))+T(1) # smallest number x such that x^2 > typemax(T) + @test lcm(x, x) === x + @test_throws OverflowError lcm(x, x+T(1)) # lcm(n, n+1) = n*(n+1). Since n and n+1 are always coprime. + end + + if T <: Signed + @test lcm(-typemax(T), T(1)) === typemax(T) + @test lcm(T(1), -typemax(T)) === typemax(T) + @test lcm(-typemax(T), T(0)) === T(0) + @test lcm(T(0), -typemax(T)) === T(0) + @test lcm(-typemax(T), -typemax(T)) === typemax(T) + @test lcm(typemax(T), -typemax(T)) === typemax(T) + @test lcm(-typemax(T), typemax(T)) === typemax(T) + + @test_throws OverflowError lcm(typemin(T), T(1)) + @test_throws OverflowError lcm(T(1), typemin(T)) + @test lcm(typemin(T), T(0)) === T(0) + @test lcm(T(0), typemin(T)) === T(0) + @test_throws OverflowError lcm(typemin(T), typemin(T)+T(1)) # lcm(n, n+1) = n*(n+1). + @test_throws OverflowError lcm(typemin(T), typemin(T)) + else + # For Unsigned Integer types, -typemax(T) == 1. + @test lcm(-typemax(T), T(1)) === T(1) + @test lcm(T(1), -typemax(T)) === T(1) + @test lcm(-typemax(T), T(0)) === T(0) + @test lcm(T(0), -typemax(T)) === T(0) + @test lcm(-typemax(T), -typemax(T)) === T(1) + @test lcm(-typemax(T), typemax(T)) === typemax(T) + @test lcm(typemax(T), -typemax(T)) === typemax(T) + + # For Unsigned Integer types, typemin(T) == 0. + @test lcm(typemin(T), T(1)) === lcm(T(0), T(1)) === T(0) + @test lcm(T(1), typemin(T)) === T(0) + @test lcm(typemin(T), T(0)) === T(0) + @test lcm(T(0), typemin(T)) === T(0) + @test lcm(typemin(T), typemin(T)) === T(0) + @test lcm(typemin(T), typemin(T)+T(1)) === T(0) + end end @test lcm(0x5, 3) == 15 @test gcd(0xf, 20) == 5 end + @testset "gcd/lcm for arrays" begin - for T in (Int32, Int64) + # TODO: Test gcd and lcm for BigInt arrays. + for T in (Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128) @test gcd(T[]) === T(0) @test gcd(T[3, 5]) === T(1) @test gcd(T[3, 15]) === T(3) @test gcd(T[0, 15]) === T(15) - @test gcd(T[3,-15]) === T(3) - @test gcd(T[-3,-15]) === T(3) + if T <: Signed + @test gcd(T[3,-15]) === T(3) + @test gcd(T[-3,-15]) === T(3) + end @test gcd(T[0, 0]) === T(0) @test gcd(T[2, 4, 6]) === T(2) @@ -59,17 +166,26 @@ end @test lcm(T[4, 6]) === T(12) @test lcm(T[3, 0]) === T(0) @test lcm(T[0, 0]) === T(0) - @test lcm(T[4, -6]) === T(12) - @test lcm(T[-4, -6]) === T(12) + if T <: Signed + @test lcm(T[4, -6]) === T(12) + @test lcm(T[-4, -6]) === T(12) + end @test lcm(T[2, 4, 6]) === T(12) end end + @testset "gcdx" begin - @test gcdx(5, 12) == (1, 5, -2) - @test gcdx(5, -12) == (1, 5, 2) - @test gcdx(-25, -4) == (1, -1, 6) + # TODO: Test gcdx for BigInt. + for T in (Int8, Int16, Int32, Int64, Int128) + @test gcdx(T(5), T(12)) === (T(1), T(5), T(-2)) + @test gcdx(T(5), T(-12)) === (T(1), T(5), T(2)) + @test gcdx(T(-5), T(12)) === (T(1), T(-5), T(-2)) + @test gcdx(T(-5), T(-12)) === (T(1), T(-5), T(2)) + @test gcdx(T(-25), T(-4)) === (T(1), T(-1), T(6)) + end end + @testset "gcd/lcm/gcdx for custom types" begin struct MyRational <: Real val::Rational{Int} @@ -81,6 +197,7 @@ end @test lcm(MyRational(2//3), 3) == lcm(2//3, 3) == lcm(Real[MyRational(2//3), 3]) @test gcdx(MyRational(2//3), 3) == gcdx(2//3, 3) end + @testset "invmod" begin @test invmod(6, 31) === 26 @test invmod(-1, 3) === 2 @@ -90,6 +207,7 @@ end @test invmod(2, 0x3) === 2 @test_throws DomainError invmod(0, 3) end + @testset "powermod" begin @test powermod(2, 3, 5) == 3 @test powermod(2, 3, -5) == -2 @@ -102,6 +220,7 @@ end @test powermod(2, -1, -5) == -2 @test powermod(2, -2, -5) == -1 end + @testset "nextpow/prevpow" begin @test nextpow(2, 3) == 4 @test nextpow(2, 4) == 4 @@ -115,6 +234,7 @@ end @test_throws DomainError prevpow(0, 3) @test_throws DomainError prevpow(0, 3) end + @testset "ndigits/ndigits0z" begin @testset "issue #8266" begin @test ndigits(-15, base=10) == 2 @@ -171,6 +291,7 @@ end end end + @testset "bin/oct/dec/hex/bits" begin @test string(UInt32('3'), base = 2) == "110011" @test string(UInt32('3'), pad = 7, base = 2) == "0110011" @@ -202,6 +323,7 @@ end "0000000000000000000000000000000000000000000000000000010000001011") @test bitstring(Int128(3)) == "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011" end + @testset "digits/base" begin @test digits(5, base = 3) == [2, 1] @test digits(5, pad = 3) == [5, 0, 0] @@ -261,6 +383,7 @@ end "44642116066", "19000000000", "1xIpcEe"] end end + @testset "leading_ones and count_zeros" begin @test leading_ones(UInt32(Int64(2) ^ 32 - 2)) == 31 @test leading_ones(1) == 0 @@ -269,18 +392,21 @@ end @test count_zeros(Int64(1)) == 63 end + @testset "factorial" begin @test factorial(3) == 6 @test factorial(Int8(3)) === 6 @test_throws DomainError factorial(-3) @test_throws DomainError factorial(Int8(-3)) end + @testset "isqrt" begin @test isqrt(4) == 2 @test isqrt(5) == 2 @test isqrt(Int8(4)) === Int8(2) @test isqrt(Int8(5)) === Int8(2) end + @testset "issue #4884" begin @test isqrt(9223372030926249000) == 3037000498 @test isqrt(typemax(Int128)) == parse(Int128,"13043817825332782212") @@ -297,6 +423,7 @@ end @test (s+1)*(s+1) > n end end + # issue #9786 let ptr = Ptr{Cvoid}(typemax(UInt)) for T in (Int, Cssize_t)