Skip to content

Commit

Permalink
base/digits: allow negative bases
Browse files Browse the repository at this point in the history
  • Loading branch information
rfourquet committed May 18, 2017
1 parent 259daca commit 71201d7
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 12 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
-----------------------------
Expand Down
2 changes: 2 additions & 0 deletions base/gmp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
38 changes: 26 additions & 12 deletions base/intfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
35 changes: 35 additions & 0 deletions test/intfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 71201d7

Please sign in to comment.