From 71201d772bdb60d784c4bf7fb01043f002cb92b2 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 9 Jun 2016 21:16:28 +0530 Subject: [PATCH] base/digits: allow negative bases --- NEWS.md | 2 ++ base/gmp.jl | 2 ++ base/intfuncs.jl | 38 ++++++++++++++++++++++++++------------ test/intfuncs.jl | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/NEWS.md b/NEWS.md index 00b98f4144526..ef56f334e7757 100644 --- a/NEWS.md +++ b/NEWS.md @@ -28,6 +28,8 @@ This section lists changes that do not have deprecation warnings. Library improvements -------------------- + * the functions `base` and `digits` digits now accept a negative + base (like `ndigits` did). Compiler/Runtime improvements ----------------------------- diff --git a/base/gmp.jl b/base/gmp.jl index af0ebdc0d763c..57e68c62a001e 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -558,6 +558,7 @@ dec(n::BigInt, pad::Int) = base(10, n, pad) hex(n::BigInt, pad::Int) = base(16, n, pad) function base(b::Integer, n::BigInt) + b < 0 && return base(Int(b), n, 1, (b>0) & (n.size<0)) 2 <= b <= 62 || throw(ArgumentError("base must be 2 ≤ base ≤ 62, got $b")) nd = ndigits(n, b) str = Base._string_n(n < 0 ? nd+1 : nd) @@ -566,6 +567,7 @@ function base(b::Integer, n::BigInt) end function base(b::Integer, n::BigInt, pad::Integer) + b < 0 && return base(Int(b), n, pad, (b>0) & (n.size<0)) s = base(b, n) buf = IOBuffer() if n < 0 diff --git a/base/intfuncs.jl b/base/intfuncs.jl index fca9fc16a1dc8..c9026270107be 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -486,14 +486,21 @@ num2hex(n::Integer) = hex(n, sizeof(n)*2) const base36digits = ['0':'9';'a':'z'] const base62digits = ['0':'9';'A':'Z';'a':'z'] -function base(b::Int, x::Unsigned, pad::Int, neg::Bool) - 2 <= b <= 62 || throw(ArgumentError("base must be 2 ≤ base ≤ 62, got $b")) - digits = b <= 36 ? base36digits : base62digits + +function base(b::Int, x::Integer, pad::Int, neg::Bool) + (x >= 0) | (b < 0) || throw(DomainError()) + 2 <= abs(b) <= 62 || throw(ArgumentError("base must satisfy 2 ≤ abs(base) ≤ 62, got $b")) + digits = abs(b) <= 36 ? base36digits : base62digits i = neg + ndigits(x, b, pad) a = StringVector(i) - while i > neg - a[i] = digits[1+rem(x,b)] - x = div(x,b) + @inbounds while i > neg + if b > 0 + a[i] = digits[1+rem(x,b)] + x = div(x,b) + else + a[i] = digits[1+mod(x,-b)] + x = cld(x,b) + end i -= 1 end if neg; a[1]='-'; end @@ -514,7 +521,8 @@ julia> base(5,13,4) "0023" ``` """ -base(b::Integer, n::Integer, pad::Integer=1) = base(Int(b), unsigned(abs(n)), pad, n<0) +base(b::Integer, n::Integer, pad::Integer=1) = + base(Int(b), b > 0 ? unsigned(abs(n)) : convert(Signed, n), Int(pad), (b>0) & (n<0)) for sym in (:bin, :oct, :dec, :hex) @eval begin @@ -589,12 +597,18 @@ Fills an array of the digits of `n` in the given base. More significant digits a indexes. If the array length is insufficient, the least significant digits are filled up to the array length. If the array length is excessive, the excess portion is filled with zeros. """ -function digits!(a::AbstractArray{T,1}, n::Integer, base::Integer=10) where T<:Integer - 2 <= base || throw(ArgumentError("base must be ≥ 2, got $base")) - base - 1 <= typemax(T) || throw(ArgumentError("type $T too small for base $base")) +function digits!(a::AbstractVector{T}, n::Integer, base::Integer=10) where T<:Integer + base < 0 && isa(n, Unsigned) && return digits!(a, convert(Signed, n), base) + 2 <= abs(base) || throw(ArgumentError("base must be ≥ 2 or ≤ -2, got $base")) + abs(base) - 1 <= typemax(T) || throw(ArgumentError("type $T too small for base $base")) for i in eachindex(a) - a[i] = rem(n, base) - n = div(n, base) + if base > 0 + a[i] = rem(n, base) + n = div(n, base) + else + a[i] = mod(n, -base) + n = cld(n, base) + end end return a end diff --git a/test/intfuncs.jl b/test/intfuncs.jl index 7a445307e197d..ca0f5a426f083 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -131,6 +131,41 @@ end @test digits(4, 2) == [0, 0, 1] @test digits(5, 3) == [2, 1] +# digits with negative bases +@testset "digits/base with negative bases" begin + @testset "digits(n::$T, b)" for T in (Int, UInt, BigInt, Int32) + @test digits(T(8163), -10) == [3, 4, 2, 2, 1] + if !(T<:Unsigned) + @test digits(T(-8163), -10) == [7, 7, 9, 9] + end + end + @test [base(b, n) + for n = [-10^9, -10^5, -2^20, -2^10, -100, -83, -50, -34, -27, -16, -7, -3, -2, -1, + 0, 1, 2, 3, 4, 7, 16, 27, 34, 50, 83, 100, 2^10, 2^20, 10^5, 10^9] + for b = [-2, -3, -7, -10, -60]] == + ["11000101101001010100101000000000", "11211100201202120012", + "144246601121", "1000000000", "2hANlK", "111000111010100000", + "122011122112", "615462", "100000", "1XlK", "1100000000000000000000", + "11000202101022", "25055043", "19169584", "59Hi", "110000000000", + "12102002", "3005", "1036", "Iu", "11101100", "121112", "1515", + "1900", "2K", "11111101", "120011", "1651", "97", "2b", "11010010", + "2121", "1616", "50", "1A", "100010", "2202", "51", "46", "1Q", + "100101", "1000", "41", "33", "1X", "110000", "1102", "35", "24", + "1i", "1001", "1202", "10", "13", "1r", "1101", "10", "14", "17", + "1v", "10", "11", "15", "18", "1w", "11", "12", "16", "19", "1x", "0", + "0", "0", "0", "0", "1", "1", "1", "1", "1", "110", "2", "2", "2", + "2", "111", "120", "3", "3", "3", "100", "121", "4", "4", "4", + "11011", "111", "160", "7", "7", "10000", "211", "152", "196", "G", + "1101111", "12000", "146", "187", "R", "1100110", "12111", "136", + "174", "Y", "1110110", "11022", "101", "150", "o", "1010111", "10002", + "236", "123", "1xN", "110100100", "10201", "202", "100", "1xe", + "10000000000", "2211011", "14012", "19184", "1h4", + "100000000000000000000", "2001112212121", "162132144", "1052636", + "1uqiG", "1101001101111100000", "21002022201", "1103425", "1900000", + "SEe", "1001100111011111101111000000000", "120220201100111010001", + "44642116066", "19000000000", "1xIpcEe"] +end + @test leading_ones(UInt32(Int64(2) ^ 32 - 2)) == 31 @test leading_ones(1) == 0 @test leading_zeros(Int32(1)) == 31