mollusk 0e4acfb8f2 fix incorrect folder name for julia-0.6.x
Former-commit-id: ef2c7401e0876f22d2f7762d182cfbcd5a7d9c70
2018-06-11 03:28:36 -07:00

2021 lines
50 KiB
Julia
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# This file is a part of Julia. License is MIT: https://julialang.org/license
## BitArray
# notes: bits are stored in contiguous chunks
# unused bits must always be set to 0
mutable struct BitArray{N} <: DenseArray{Bool, N}
chunks::Vector{UInt64}
len::Int
dims::NTuple{N,Int}
function BitArray{N}(dims::Vararg{Int,N}) where N
n = 1
i = 1
for d in dims
d >= 0 || throw(ArgumentError("dimension size must be ≥ 0, got $d for dimension $i"))
n *= d
i += 1
end
nc = num_bit_chunks(n)
chunks = Vector{UInt64}(nc)
nc > 0 && (chunks[end] = UInt64(0))
b = new(chunks, n)
N != 1 && (b.dims = dims)
return b
end
end
# note: the docs for the two signatures are unified, but only
# the first one is recognized by the help system; it would be nice
# to fix this.
"""
BitArray(dims::Integer...)
BitArray{N}(dims::NTuple{N,Int})
Construct an uninitialized `BitArray` with the given dimensions.
Behaves identically to the [`Array`](@ref) constructor.
```julia-repl
julia> BitArray(2, 2)
2×2 BitArray{2}:
false false
false true
julia> BitArray((3, 1))
3×1 BitArray{2}:
false
true
false
```
"""
BitArray(dims::Integer...) = BitArray(map(Int,dims))
BitArray(dims::NTuple{N,Int}) where {N} = BitArray{N}(dims...)
const BitVector = BitArray{1}
const BitMatrix = BitArray{2}
BitVector() = BitArray{1}(0)
## utility functions ##
length(B::BitArray) = B.len
size(B::BitVector) = (B.len,)
size(B::BitArray) = B.dims
@inline function size(B::BitVector, d)
d < 1 && throw_boundserror(size(B), d)
ifelse(d == 1, B.len, 1)
end
isassigned(B::BitArray, i::Int) = 1 <= i <= length(B)
IndexStyle(::Type{<:BitArray}) = IndexLinear()
## aux functions ##
const _msk64 = ~UInt64(0)
@inline _div64(l) = l >>> 6
@inline _mod64(l) = l & 63
@inline _msk_end(l::Integer) = _msk64 >>> _mod64(-l)
@inline _msk_end(B::BitArray) = _msk_end(length(B))
num_bit_chunks(n::Int) = _div64(n+63)
function _check_bitarray_consistency{N}(B::BitArray{N})
n = length(B)
if N 1
all(d 0 for d in B.dims) || (warn("negative d in dims: $(B.dims)"); return false)
prod(B.dims) n && (warn("inconsistent dims/len: prod(dims)=$(prod(B.dims)) len=$n"); return false)
end
Bc = B.chunks
nc = length(Bc)
nc == num_bit_chunks(n) || (warn("incorrect chunks length for length $n: expected=$(num_bit_chunks(n)) actual=$nc"); return false)
n == 0 && return true
Bc[end] & _msk_end(n) == Bc[end] || (warn("nonzero bits in chunk after BitArray end"); return false)
return true
end
@inline get_chunks_id(i::Integer) = _div64(Int(i)-1)+1, _mod64(Int(i)-1)
function glue_src_bitchunks(src::Vector{UInt64}, k::Int, ks1::Int, msk_s0::UInt64, ls0::Int)
@inbounds begin
chunk = ((src[k] & msk_s0) >>> ls0)
if ks1 > k && ls0 > 0
chunk_n = (src[k + 1] & ~msk_s0)
chunk |= (chunk_n << (64 - ls0))
end
end
return chunk
end
function copy_chunks!(dest::Vector{UInt64}, pos_d::Integer, src::Vector{UInt64}, pos_s::Integer, numbits::Integer)
numbits == 0 && return
if dest === src && pos_d > pos_s
return copy_chunks_rtol!(dest, pos_d, pos_s, numbits)
end
kd0, ld0 = get_chunks_id(pos_d)
kd1, ld1 = get_chunks_id(pos_d + numbits - 1)
ks0, ls0 = get_chunks_id(pos_s)
ks1, ls1 = get_chunks_id(pos_s + numbits - 1)
delta_kd = kd1 - kd0
delta_ks = ks1 - ks0
u = _msk64
if delta_kd == 0
msk_d0 = ~(u << ld0) | (u << (ld1+1))
else
msk_d0 = ~(u << ld0)
msk_d1 = (u << (ld1+1))
end
if delta_ks == 0
msk_s0 = (u << ls0) & ~(u << (ls1+1))
else
msk_s0 = (u << ls0)
end
chunk_s0 = glue_src_bitchunks(src, ks0, ks1, msk_s0, ls0)
dest[kd0] = (dest[kd0] & msk_d0) | ((chunk_s0 << ld0) & ~msk_d0)
delta_kd == 0 && return
for i = 1 : kd1 - kd0 - 1
chunk_s1 = glue_src_bitchunks(src, ks0 + i, ks1, msk_s0, ls0)
chunk_s = (chunk_s0 >>> (64 - ld0)) | (chunk_s1 << ld0)
dest[kd0 + i] = chunk_s
chunk_s0 = chunk_s1
end
if ks1 >= ks0 + delta_kd
chunk_s1 = glue_src_bitchunks(src, ks0 + delta_kd, ks1, msk_s0, ls0)
else
chunk_s1 = UInt64(0)
end
chunk_s = (chunk_s0 >>> (64 - ld0)) | (chunk_s1 << ld0)
dest[kd1] = (dest[kd1] & msk_d1) | (chunk_s & ~msk_d1)
return
end
function copy_chunks_rtol!(chunks::Vector{UInt64}, pos_d::Integer, pos_s::Integer, numbits::Integer)
pos_d == pos_s && return
pos_d < pos_s && return copy_chunks!(chunks, pos_d, chunks, pos_s, numbits)
left = numbits
s = min(left, 64)
b = left - s
ps = pos_s + b
pd = pos_d + b
u = _msk64
while left > 0
kd0, ld0 = get_chunks_id(pd)
kd1, ld1 = get_chunks_id(pd + s - 1)
ks0, ls0 = get_chunks_id(ps)
ks1, ls1 = get_chunks_id(ps + s - 1)
delta_kd = kd1 - kd0
delta_ks = ks1 - ks0
if delta_kd == 0
msk_d0 = ~(u << ld0) | (u << (ld1+1))
else
msk_d0 = ~(u << ld0)
msk_d1 = (u << (ld1+1))
end
if delta_ks == 0
msk_s0 = (u << ls0) & ~(u << (ls1+1))
else
msk_s0 = (u << ls0)
end
chunk_s0 = glue_src_bitchunks(chunks, ks0, ks1, msk_s0, ls0) & ~(u << s)
chunks[kd0] = (chunks[kd0] & msk_d0) | ((chunk_s0 << ld0) & ~msk_d0)
if delta_kd != 0
chunk_s = (chunk_s0 >>> (64 - ld0))
chunks[kd1] = (chunks[kd1] & msk_d1) | (chunk_s & ~msk_d1)
end
left -= s
s = min(left, 64)
b = left - s
ps = pos_s + b
pd = pos_d + b
end
end
function fill_chunks!(Bc::Array{UInt64}, x::Bool, pos::Integer, numbits::Integer)
numbits <= 0 && return
k0, l0 = get_chunks_id(pos)
k1, l1 = get_chunks_id(pos+numbits-1)
u = _msk64
if k1 == k0
msk0 = (u << l0) & ~(u << (l1+1))
else
msk0 = (u << l0)
msk1 = ~(u << (l1+1))
end
@inbounds if x
Bc[k0] |= msk0
for k = k0+1:k1-1
Bc[k] = u
end
k1 > k0 && (Bc[k1] |= msk1)
else
Bc[k0] &= ~msk0
for k = k0+1:k1-1
Bc[k] = 0
end
k1 > k0 && (Bc[k1] &= ~msk1)
end
end
copy_to_bitarray_chunks!(dest::Vector{UInt64}, pos_d::Int, src::BitArray, pos_s::Int, numbits::Int) =
copy_chunks!(dest, pos_d, src.chunks, pos_s, numbits)
# pack 8 Bools encoded as one contiguous UIn64 into a single byte, e.g.:
# 0000001:0000001:00000000:00000000:00000001:00000000:00000000:00000001 → 11001001 → 0xc9
function pack8bools(z::UInt64)
z |= z >>> 7
z |= z >>> 14
z |= z >>> 28
z &= 0xFF
return z
end
function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::Array{Bool}, pos_s::Int, numbits::Int)
kd0, ld0 = get_chunks_id(pos_d)
kd1, ld1 = get_chunks_id(pos_d + numbits - 1)
delta_kd = kd1 - kd0
u = _msk64
if delta_kd == 0
msk_d0 = msk_d1 = ~(u << ld0) | (u << (ld1+1))
lt0 = ld1
else
msk_d0 = ~(u << ld0)
msk_d1 = (u << (ld1+1))
lt0 = 63
end
bind = kd0
ind = pos_s
@inbounds if ld0 > 0
c = UInt64(0)
for j = ld0:lt0
c |= (UInt64(C[ind]) << j)
ind += 1
end
Bc[kd0] = (Bc[kd0] & msk_d0) | (c & ~msk_d0)
bind += 1
end
nc = _div64(numbits - ind + pos_s)
nc8 = (nc >>> 3) << 3
if nc8 > 0
ind8 = 1
P8 = Ptr{UInt64}(pointer(C, ind)) # unaligned i64 pointer
@inbounds for i = 1:nc8
c = UInt64(0)
for j = 0:7
# unaligned load
c |= (pack8bools(unsafe_load(P8, ind8)) << (j<<3))
ind8 += 1
end
Bc[bind] = c
bind += 1
end
ind += (ind8-1) << 3
end
@inbounds for i = (nc8+1):nc
c = UInt64(0)
for j = 0:63
c |= (UInt64(C[ind]) << j)
ind += 1
end
Bc[bind] = c
bind += 1
end
@inbounds if bind kd1
@assert bind == kd1
c = UInt64(0)
for j = 0:ld1
c |= (UInt64(C[ind]) << j)
ind += 1
end
Bc[kd1] = (Bc[kd1] & msk_d1) | (c & ~msk_d1)
end
end
## More definitions in multidimensional.jl
# auxiliary definitions used when filling a BitArray via a Vector{Bool} cache
# (e.g. when constructing from an iterable, or in broadcast!)
const bitcache_chunks = 64 # this can be changed
const bitcache_size = 64 * bitcache_chunks # do not change this
dumpbitcache(Bc::Vector{UInt64}, bind::Int, C::Vector{Bool}) =
copy_to_bitarray_chunks!(Bc, ((bind - 1) << 6) + 1, C, 1, min(bitcache_size, (length(Bc)-bind+1) << 6))
## custom iterator ##
start(B::BitArray) = 0
next(B::BitArray, i::Int) = (B.chunks[_div64(i)+1] & (UInt64(1)<<_mod64(i)) != 0, i+1)
done(B::BitArray, i::Int) = i >= length(B)
## similar, fill!, copy! etc ##
similar(B::BitArray) = BitArray(size(B))
similar(B::BitArray, dims::Int...) = BitArray(dims)
similar(B::BitArray, dims::Dims) = BitArray(dims...)
similar(B::BitArray, T::Type{Bool}, dims::Dims) = BitArray(dims)
# changing type to a non-Bool returns an Array
# (this triggers conversions like float(bitvector) etc.)
similar(B::BitArray, T::Type, dims::Dims) = Array{T}(dims)
function fill!(B::BitArray, x)
y = convert(Bool, x)
isempty(B) && return B
Bc = B.chunks
if !y
fill!(Bc, 0)
else
fill!(Bc, _msk64)
Bc[end] &= _msk_end(B)
end
return B
end
"""
falses(dims)
Create a `BitArray` with all values set to `false`.
```jldoctest
julia> falses(2,3)
2×3 BitArray{2}:
false false false
false false false
```
"""
falses(dims::Dims) = fill!(BitArray(dims), false)
falses(dims::Integer...) = falses(map(Int,dims))
"""
falses(A)
Create a `BitArray` with all values set to `false` of the same shape as `A`.
```jldoctest
julia> A = [1 2; 3 4]
2×2 Array{Int64,2}:
1 2
3 4
julia> falses(A)
2×2 BitArray{2}:
false false
false false
```
"""
falses(A::AbstractArray) = falses(size(A))
"""
trues(dims)
Create a `BitArray` with all values set to `true`.
```jldoctest
julia> trues(2,3)
2×3 BitArray{2}:
true true true
true true true
```
"""
trues(dims::Dims) = fill!(BitArray(dims), true)
trues(dims::Integer...) = trues(map(Int,dims))
"""
trues(A)
Create a `BitArray` with all values set to `true` of the same shape as `A`.
```jldoctest
julia> A = [1 2; 3 4]
2×2 Array{Int64,2}:
1 2
3 4
julia> trues(A)
2×2 BitArray{2}:
true true
true true
```
"""
trues(A::AbstractArray) = trues(size(A))
function one(x::BitMatrix)
m, n = size(x)
m == n || throw(DimensionMismatch("multiplicative identity defined only for square matrices"))
a = falses(n, n)
for i = 1:n
a[i,i] = true
end
return a
end
function copy!(dest::BitArray, src::BitArray)
length(src) > length(dest) && throw(BoundsError(dest, length(dest)+1))
destc = dest.chunks; srcc = src.chunks
nc = min(length(destc), length(srcc))
nc == 0 && return dest
@inbounds begin
for i = 1 : nc - 1
destc[i] = srcc[i]
end
if length(src) == length(dest)
destc[nc] = srcc[nc]
else
msk_s = _msk_end(src)
msk_d = ~msk_s
destc[nc] = (msk_d & destc[nc]) | (msk_s & srcc[nc])
end
end
return dest
end
function unsafe_copy!(dest::BitArray, doffs::Integer, src::Union{BitArray,Array}, soffs::Integer, n::Integer)
copy_to_bitarray_chunks!(dest.chunks, doffs, src, soffs, n)
return dest
end
function copy!(dest::BitArray, doffs::Integer, src::Array, soffs::Integer, n::Integer)
n == 0 && return dest
soffs < 1 && throw(BoundsError(src, soffs))
doffs < 1 && throw(BoundsError(dest, doffs))
soffs+n-1 > length(src) && throw(BoundsError(src, length(src)+1))
doffs+n-1 > length(dest) && throw(BoundsError(dest, length(dest)+1))
return unsafe_copy!(dest, doffs, src, soffs, n)
end
function copy!(dest::BitArray, src::Array)
length(src) > length(dest) && throw(BoundsError(dest, length(dest)+1))
length(src) == 0 && return det
return unsafe_copy!(dest, 1, src, 1, length(src))
end
function reshape(B::BitArray{N}, dims::NTuple{N,Int}) where N
return dims == size(B) ? B : _bitreshape(B, dims)
end
reshape(B::BitArray, dims::Tuple{Vararg{Int}}) = _bitreshape(B, dims)
function _bitreshape(B::BitArray, dims::NTuple{N,Int}) where N
prod(dims) == length(B) ||
throw(DimensionMismatch("new dimensions $(dims) must be consistent with array size $(length(B))"))
Br = BitArray{N}(ntuple(i->0,Val{N})...)
Br.chunks = B.chunks
Br.len = prod(dims)
N != 1 && (Br.dims = dims)
return Br
end
## Conversions ##
convert(::Type{Array{T}}, B::BitArray{N}) where {T,N} = convert(Array{T,N}, B)
convert(::Type{Array{T,N}}, B::BitArray{N}) where {T,N} = _convert(Array{T,N}, B) # see #15801
function _convert(::Type{Array{T,N}}, B::BitArray{N}) where {T,N}
A = Array{T}(size(B))
Bc = B.chunks
@inbounds for i = 1:length(A)
A[i] = unsafe_bitgetindex(Bc, i)
end
return A
end
convert(::Type{BitArray}, A::AbstractArray{T,N}) where {T,N} = convert(BitArray{N}, A)
function convert(::Type{BitArray{N}}, A::AbstractArray{T,N}) where N where T
B = BitArray(size(A))
Bc = B.chunks
l = length(B)
l == 0 && return B
ind = 1
@inbounds begin
for i = 1:length(Bc)-1
c = UInt64(0)
for j = 0:63
c |= (UInt64(A[ind] != 0) << j)
ind += 1
end
Bc[i] = c
end
c = UInt64(0)
for j = 0:_mod64(l-1)
c |= (UInt64(A[ind] != 0) << j)
ind += 1
end
Bc[end] = c
end
return B
end
function convert(::Type{BitArray{N}}, A::Array{Bool,N}) where N
B = BitArray(size(A))
Bc = B.chunks
l = length(B)
l == 0 && return B
copy_to_bitarray_chunks!(Bc, 1, A, 1, l)
return B
end
convert(::Type{BitArray{N}}, B::BitArray{N}) where {N} = B
convert(::Type{AbstractArray{T,N}}, B::BitArray{N}) where {T,N} = convert(Array{T,N}, B)
reinterpret(::Type{Bool}, B::BitArray, dims::NTuple{N,Int}) where {N} = reinterpret(B, dims)
reinterpret(B::BitArray, dims::NTuple{N,Int}) where {N} = reshape(B, dims)
## Constructors from generic iterables ##
BitArray{T,N}(A::AbstractArray{T,N}) = convert(BitArray{N}, A)
"""
BitArray(itr)
Construct a `BitArray` generated by the given iterable object. The shape is inferred from
the `itr` object.
```jldoctest
julia> BitArray([1 0; 0 1])
2×2 BitArray{2}:
true false
false true
julia> BitArray(x+y == 3 for x = 1:2, y = 1:3)
2×3 BitArray{2}:
false true false
true false false
julia> BitArray(x+y == 3 for x = 1:2 for y = 1:3)
6-element BitArray{1}:
false
true
false
true
false
false
```
"""
BitArray(itr) = gen_bitarray(iteratorsize(itr), itr)
# generic constructor from an iterable without compile-time info
# (we pass start(itr) explicitly to avoid a type-instability with filters)
gen_bitarray(isz::IteratorSize, itr) = gen_bitarray_from_itr(itr, start(itr))
# generic iterable with known shape
function gen_bitarray(::HasShape, itr)
B = BitArray(size(itr))
for (I,x) in zip(CartesianRange(indices(itr)), itr)
B[I] = x
end
return B
end
# generator with known shape or length
function gen_bitarray(::HasShape, itr::Generator)
B = BitArray(size(itr))
return fill_bitarray_from_itr!(B, itr, start(itr))
end
function gen_bitarray(::HasLength, itr)
n = length(itr)
B = BitArray(n)
return fill_bitarray_from_itr!(B, itr, start(itr))
end
gen_bitarray(::IsInfinite, itr) = throw(ArgumentError("infinite-size iterable used in BitArray constructor"))
# The aux functions gen_bitarray_from_itr and fill_bitarray_from_itr! both
# use a Vector{Bool} cache for performance reasons
function gen_bitarray_from_itr(itr, st)
B = empty!(BitArray(bitcache_size))
C = Vector{Bool}(bitcache_size)
Bc = B.chunks
ind = 1
cind = 1
while !done(itr, st)
x, st = next(itr, st)
@inbounds C[ind] = x
ind += 1
if ind > bitcache_size
resize!(B, length(B) + bitcache_size)
dumpbitcache(Bc, cind, C)
cind += bitcache_chunks
ind = 1
end
end
if ind > 1
@inbounds C[ind:bitcache_size] = false
resize!(B, length(B) + ind - 1)
dumpbitcache(Bc, cind, C)
end
return B
end
function fill_bitarray_from_itr!(B::BitArray, itr, st)
n = length(B)
C = Vector{Bool}(bitcache_size)
Bc = B.chunks
ind = 1
cind = 1
while !done(itr, st)
x, st = next(itr, st)
@inbounds C[ind] = x
ind += 1
if ind > bitcache_size
dumpbitcache(Bc, cind, C)
cind += bitcache_chunks
ind = 1
end
end
if ind > 1
@inbounds C[ind:bitcache_size] = false
dumpbitcache(Bc, cind, C)
end
return B
end
## Indexing: getindex ##
@inline function unsafe_bitgetindex(Bc::Vector{UInt64}, i::Int)
i1, i2 = get_chunks_id(i)
u = UInt64(1) << i2
@inbounds r = (Bc[i1] & u) != 0
return r
end
@inline function getindex(B::BitArray, i::Int)
@boundscheck checkbounds(B, i)
unsafe_bitgetindex(B.chunks, i)
end
## Indexing: setindex! ##
@inline function unsafe_bitsetindex!(Bc::Array{UInt64}, x::Bool, i::Int)
i1, i2 = get_chunks_id(i)
u = UInt64(1) << i2
@inbounds begin
c = Bc[i1]
Bc[i1] = ifelse(x, c | u, c & ~u)
end
end
@inline function setindex!(B::BitArray, x, i::Int)
@boundscheck checkbounds(B, i)
unsafe_bitsetindex!(B.chunks, convert(Bool, x), i)
return B
end
indexoffset(i) = first(i)-1
indexoffset(::Colon) = 0
@inline function setindex!(B::BitArray, x, J0::Union{Colon,UnitRange{Int}})
I0 = to_indices(B, (J0,))[1]
@boundscheck checkbounds(B, I0)
y = Bool(x)
l0 = length(I0)
l0 == 0 && return B
f0 = indexoffset(I0)+1
fill_chunks!(B.chunks, y, f0, l0)
return B
end
# logical indexing
# When indexing with a BitArray, we can operate whole chunks at a time for a ~100x gain
@inline function setindex!(B::BitArray, x, I::BitArray)
@boundscheck checkbounds(B, I)
_unsafe_setindex!(B, x, I)
end
function _unsafe_setindex!(B::BitArray, x, I::BitArray)
y = convert(Bool, x)
Bc = B.chunks
Ic = I.chunks
length(Bc) == length(Ic) || throw_boundserror(B, I)
@inbounds if y
for i = 1:length(Bc)
Bc[i] |= Ic[i]
end
else
for i = 1:length(Bc)
Bc[i] &= ~Ic[i]
end
end
return B
end
# Assigning an array of bools is more complicated, but we can still do some
# work on chunks by combining X and I 64 bits at a time to improve perf by ~40%
@inline function setindex!(B::BitArray, X::AbstractArray, I::BitArray)
@boundscheck checkbounds(B, I)
_unsafe_setindex!(B, X, I)
end
function _unsafe_setindex!(B::BitArray, X::AbstractArray, I::BitArray)
Bc = B.chunks
Ic = I.chunks
length(Bc) == length(Ic) || throw_boundserror(B, I)
lc = length(Bc)
lx = length(X)
last_chunk_len = _mod64(length(B)-1)+1
c = 1
for i = 1:lc
@inbounds Imsk = Ic[i]
@inbounds C = Bc[i]
u = UInt64(1)
for j = 1:(i < lc ? 64 : last_chunk_len)
if Imsk & u != 0
lx < c && throw_setindex_mismatch(X, c)
@inbounds x = convert(Bool, X[c])
C = ifelse(x, C | u, C & ~u)
c += 1
end
u <<= 1
end
@inbounds Bc[i] = C
end
if length(X) != c-1
throw_setindex_mismatch(X, c-1)
end
return B
end
## Dequeue functionality ##
function push!(B::BitVector, item)
# convert first so we don't grow the bitarray if the assignment won't work
item = convert(Bool, item)
Bc = B.chunks
l = _mod64(length(B))
if l == 0
ccall(:jl_array_grow_end, Void, (Any, UInt), Bc, 1)
Bc[end] = UInt64(0)
end
B.len += 1
if item
B[end] = true
end
return B
end
function append!(B::BitVector, items::BitVector)
n0 = length(B)
n1 = length(items)
n1 == 0 && return B
Bc = B.chunks
k0 = length(Bc)
k1 = num_bit_chunks(n0 + n1)
if k1 > k0
ccall(:jl_array_grow_end, Void, (Any, UInt), Bc, k1 - k0)
Bc[end] = UInt64(0)
end
B.len += n1
copy_chunks!(Bc, n0+1, items.chunks, 1, n1)
return B
end
append!(B::BitVector, items::AbstractVector{Bool}) = append!(B, BitArray(items))
append!(A::Vector{Bool}, items::BitVector) = append!(A, Array(items))
function prepend!(B::BitVector, items::BitVector)
n0 = length(B)
n1 = length(items)
n1 == 0 && return B
Bc = B.chunks
k0 = length(Bc)
k1 = num_bit_chunks(n0 + n1)
if k1 > k0
ccall(:jl_array_grow_end, Void, (Any, UInt), Bc, k1 - k0)
Bc[end] = UInt64(0)
end
B.len += n1
copy_chunks!(Bc, 1 + n1, Bc, 1, n0)
copy_chunks!(Bc, 1, items.chunks, 1, n1)
return B
end
prepend!(B::BitVector, items::AbstractVector{Bool}) = prepend!(B, BitArray(items))
prepend!(A::Vector{Bool}, items::BitVector) = prepend!(A, Array(items))
function sizehint!(B::BitVector, sz::Integer)
ccall(:jl_array_sizehint, Void, (Any, UInt), B.chunks, num_bit_chunks(sz))
return B
end
function resize!(B::BitVector, n::Integer)
n0 = length(B)
n == n0 && return B
n >= 0 || throw(BoundsError(B, n))
if n < n0
deleteat!(B, n+1:n0)
return B
end
Bc = B.chunks
k0 = length(Bc)
k1 = num_bit_chunks(Int(n))
if k1 > k0
ccall(:jl_array_grow_end, Void, (Any, UInt), Bc, k1 - k0)
Bc[end] = UInt64(0)
end
B.len = n
return B
end
function pop!(B::BitVector)
isempty(B) && throw(ArgumentError("argument must not be empty"))
item = B[end]
B[end] = false
l = _mod64(length(B))
l == 1 && ccall(:jl_array_del_end, Void, (Any, UInt), B.chunks, 1)
B.len -= 1
return item
end
function unshift!(B::BitVector, item)
item = convert(Bool, item)
Bc = B.chunks
l = _mod64(length(B))
if l == 0
ccall(:jl_array_grow_end, Void, (Any, UInt), Bc, 1)
Bc[end] = UInt64(0)
end
B.len += 1
if B.len == 1
Bc[1] = item
return B
end
for i = length(Bc) : -1 : 2
Bc[i] = (Bc[i] << 1) | (Bc[i-1] >>> 63)
end
Bc[1] = UInt64(item) | (Bc[1] << 1)
return B
end
function shift!(B::BitVector)
isempty(B) && throw(ArgumentError("argument must not be empty"))
@inbounds begin
item = B[1]
Bc = B.chunks
for i = 1 : length(Bc) - 1
Bc[i] = (Bc[i] >>> 1) | (Bc[i+1] << 63)
end
l = _mod64(length(B))
if l == 1
ccall(:jl_array_del_end, Void, (Any, UInt), Bc, 1)
else
Bc[end] >>>= 1
end
B.len -= 1
end
return item
end
function insert!(B::BitVector, i::Integer, item)
n = length(B)
1 <= i <= n+1 || throw(BoundsError(B, i))
item = convert(Bool, item)
Bc = B.chunks
k, j = get_chunks_id(i)
l = _mod64(length(B))
if l == 0
ccall(:jl_array_grow_end, Void, (Any, UInt), Bc, 1)
Bc[end] = UInt64(0)
end
B.len += 1
for t = length(Bc) : -1 : k + 1
Bc[t] = (Bc[t] << 1) | (Bc[t - 1] >>> 63)
end
msk_aft = (_msk64 << j)
msk_bef = ~msk_aft
Bc[k] = (msk_bef & Bc[k]) | ((msk_aft & Bc[k]) << 1)
B[i] = item
B
end
function _deleteat!(B::BitVector, i::Integer)
k, j = get_chunks_id(i)
msk_bef = _msk64 >>> (63 - j)
msk_aft = ~msk_bef
msk_bef >>>= 1
Bc = B.chunks
@inbounds begin
Bc[k] = (msk_bef & Bc[k]) | ((msk_aft & Bc[k]) >> 1)
if length(Bc) > k
Bc[k] |= (Bc[k + 1] << 63)
end
for t = k + 1 : length(Bc) - 1
Bc[t] = (Bc[t] >>> 1) | (Bc[t + 1] << 63)
end
l = _mod64(length(B))
if l == 1
ccall(:jl_array_del_end, Void, (Any, UInt), Bc, 1)
elseif length(Bc) > k
Bc[end] >>>= 1
end
end
B.len -= 1
return B
end
function deleteat!(B::BitVector, i::Integer)
n = length(B)
1 <= i <= n || throw(BoundsError(B, i))
return _deleteat!(B, i)
end
function deleteat!(B::BitVector, r::UnitRange{Int})
n = length(B)
i_f = first(r)
i_l = last(r)
1 <= i_f || throw(BoundsError(B, i_f))
i_l <= n || throw(BoundsError(B, n+1))
Bc = B.chunks
new_l = length(B) - length(r)
delta_k = num_bit_chunks(new_l) - length(Bc)
copy_chunks!(Bc, i_f, Bc, i_l+1, n-i_l)
delta_k < 0 && ccall(:jl_array_del_end, Void, (Any, UInt), Bc, -delta_k)
B.len = new_l
if new_l > 0
Bc[end] &= _msk_end(new_l)
end
return B
end
function deleteat!(B::BitVector, inds)
n = new_l = length(B)
s = start(inds)
done(inds, s) && return B
Bc = B.chunks
(p, s) = next(inds, s)
q = p+1
new_l -= 1
while !done(inds, s)
(i,s) = next(inds, s)
if !(q <= i <= n)
i < q && throw(ArgumentError("indices must be unique and sorted"))
throw(BoundsError(B, i))
end
new_l -= 1
if i > q
copy_chunks!(Bc, p, Bc, q, i-q)
p += i-q
end
q = i+1
end
q <= n && copy_chunks!(Bc, p, Bc, q, n-q+1)
delta_k = num_bit_chunks(new_l) - length(Bc)
delta_k < 0 && ccall(:jl_array_del_end, Void, (Any, UInt), Bc, -delta_k)
B.len = new_l
if new_l > 0
Bc[end] &= _msk_end(new_l)
end
return B
end
function splice!(B::BitVector, i::Integer)
n = length(B)
1 <= i <= n || throw(BoundsError(B, i))
v = B[i] # TODO: change to a copy if/when subscripting becomes an ArrayView
_deleteat!(B, i)
return v
end
const _default_bit_splice = BitVector(0)
function splice!(B::BitVector, r::Union{UnitRange{Int}, Integer}, ins::AbstractArray = _default_bit_splice)
n = length(B)
i_f = first(r)
i_l = last(r)
1 <= i_f <= n+1 || throw(BoundsError(B, i_f))
i_l <= n || throw(BoundsError(B, n+1))
Bins = convert(BitArray, ins)
if (i_f > n)
append!(B, Bins)
return BitVector(0)
end
v = B[r] # TODO: change to a copy if/when subscripting becomes an ArrayView
Bc = B.chunks
lins = length(Bins)
ldel = length(r)
new_l = length(B) + lins - ldel
delta_k = num_bit_chunks(new_l) - length(Bc)
delta_k > 0 && ccall(:jl_array_grow_end, Void, (Any, UInt), Bc, delta_k)
copy_chunks!(Bc, i_f+lins, Bc, i_l+1, n-i_l)
copy_chunks!(Bc, i_f, Bins.chunks, 1, lins)
delta_k < 0 && ccall(:jl_array_del_end, Void, (Any, UInt), Bc, -delta_k)
B.len = new_l
if new_l > 0
Bc[end] &= _msk_end(new_l)
end
return v
end
function splice!(B::BitVector, r::Union{UnitRange{Int}, Integer}, ins)
Bins = BitArray(length(ins))
i = 1
for x in ins
Bins[i] = Bool(x)
i += 1
end
return splice!(B, r, Bins)
end
function empty!(B::BitVector)
ccall(:jl_array_del_end, Void, (Any, UInt), B.chunks, length(B.chunks))
B.len = 0
return B
end
## Unary operators ##
function (-)(B::BitArray)
A = zeros(Int, size(B))
l = length(B)
l == 0 && return A
Bc = B.chunks
ind = 1
for i = 1:length(Bc)-1
u = UInt64(1)
c = Bc[i]
for j = 1:64
if c & u != 0
A[ind] = -1
end
ind += 1
u <<= 1
end
end
u = UInt64(1)
c = Bc[end]
for j = 0:_mod64(l-1)
if c & u != 0
A[ind] = -1
end
ind += 1
u <<= 1
end
return A
end
broadcast(::typeof(sign), B::BitArray) = copy(B)
function broadcast(::typeof(~), B::BitArray)
C = similar(B)
Bc = B.chunks
if !isempty(Bc)
Cc = C.chunks
for i = 1:length(Bc)
Cc[i] = ~Bc[i]
end
Cc[end] &= _msk_end(B)
end
return C
end
"""
flipbits!(B::BitArray{N}) -> BitArray{N}
Performs a bitwise not operation on `B`. See [`~`](@ref).
# Example
```jldoctest
julia> A = trues(2,2)
2×2 BitArray{2}:
true true
true true
julia> flipbits!(A)
2×2 BitArray{2}:
false false
false false
```
"""
function flipbits!(B::BitArray)
Bc = B.chunks
@inbounds if !isempty(Bc)
for i = 1:length(Bc)
Bc[i] = ~Bc[i]
end
Bc[end] &= _msk_end(B)
end
return B
end
## Binary arithmetic operators ##
for f in (:+, :-)
@eval function ($f)(A::BitArray, B::BitArray)
r = Array{Int}(promote_shape(size(A), size(B)))
ai = start(A)
bi = start(B)
ri = 1
while !done(A, ai)
a, ai = next(A, ai)
b, bi = next(B, bi)
@inbounds r[ri] = ($f)(a, b)
ri += 1
end
return r
end
end
for f in (:/, :\)
@eval begin
($f)(A::Union{BitMatrix,BitVector}, B::Union{BitMatrix,BitVector}) = ($f)(Array(A), Array(B))
end
end
(/)(B::BitArray, x::Number) = (/)(Array(B), x)
(/)(x::Number, B::BitArray) = (/)(x, Array(B))
# broadcast specializations for &, |, and xor/⊻
broadcast(::typeof(&), B::BitArray, x::Bool) = x ? copy(B) : falses(size(B))
broadcast(::typeof(&), x::Bool, B::BitArray) = broadcast(&, B, x)
broadcast(::typeof(|), B::BitArray, x::Bool) = x ? trues(size(B)) : copy(B)
broadcast(::typeof(|), x::Bool, B::BitArray) = broadcast(|, B, x)
broadcast(::typeof(xor), B::BitArray, x::Bool) = x ? .~B : copy(B)
broadcast(::typeof(xor), x::Bool, B::BitArray) = broadcast(xor, B, x)
for f in (:&, :|, :xor)
@eval begin
function broadcast(::typeof($f), A::BitArray, B::BitArray)
F = BitArray(promote_shape(size(A),size(B))...)
Fc = F.chunks
Ac = A.chunks
Bc = B.chunks
(isempty(Ac) || isempty(Bc)) && return F
for i = 1:length(Fc)
Fc[i] = ($f)(Ac[i], Bc[i])
end
Fc[end] &= _msk_end(F)
return F
end
broadcast(::typeof($f), A::DenseArray{Bool}, B::BitArray) = broadcast($f, BitArray(A), B)
broadcast(::typeof($f), B::BitArray, A::DenseArray{Bool}) = broadcast($f, B, BitArray(A))
end
end
## promotion to complex ##
# TODO?
## comparison operators ##
function (==)(A::BitArray, B::BitArray)
size(A) != size(B) && return false
return A.chunks == B.chunks
end
## Data movement ##
# preserve some special behavior
function slicedim(A::BitVector, d::Integer, i::Integer)
d >= 1 || throw(ArgumentError("dimension must be ≥ 1"))
if d > 1
i == 1 || throw_boundserror(A, (:, ntuple(k->1,d-2)..., i))
A[:]
else
fill!(BitArray{0}(), A[i]) # generic slicedim would return A[i] here
end
end
# TODO some of this could be optimized
function flipdim(A::BitArray, d::Integer)
nd = ndims(A)
1 d nd || throw(ArgumentError("dimension $d is not 1 ≤ $d$nd"))
sd = size(A, d)
sd == 1 && return copy(A)
B = similar(A)
nnd = 0
for i = 1:nd
nnd += Int(size(A,i)==1 || i==d)
end
if nnd == nd
# flip along the only non-singleton dimension
for i = 1:sd
B[i] = A[sd+1-i]
end
return B
end
d_in = size(A)
leading = d_in[1:(d-1)]
M = prod(leading)
N = length(A)
stride = M * sd
if M == 1
for j = 0:stride:(N-stride)
for i = 1:sd
ri = sd+1-i
B[j + ri] = A[j + i]
end
end
else
for i = 1:sd
ri = sd+1-i
for j=0:stride:(N-stride)
offs = j + 1 + (i-1)*M
boffs = j + 1 + (ri-1)*M
copy_chunks!(B.chunks, boffs, A.chunks, offs, M)
end
end
end
return B
end
function reverse_bits(src::UInt64)
z = src
z = ((z >>> 1) & 0x5555555555555555) | ((z << 1) & 0xaaaaaaaaaaaaaaaa)
z = ((z >>> 2) & 0x3333333333333333) | ((z << 2) & 0xcccccccccccccccc)
z = ((z >>> 4) & 0x0f0f0f0f0f0f0f0f) | ((z << 4) & 0xf0f0f0f0f0f0f0f0)
z = ((z >>> 8) & 0x00ff00ff00ff00ff) | ((z << 8) & 0xff00ff00ff00ff00)
z = ((z >>> 16) & 0x0000ffff0000ffff) | ((z << 16) & 0xffff0000ffff0000)
return ((z >>> 32) & 0x00000000ffffffff) | ((z << 32) & 0xffffffff00000000)
end
function reverse!(B::BitVector)
# Basic idea: each chunk is divided into two blocks of size k = n % 64, and
# h = 64 - k. Walk from either end (with indexes i and j) reversing chunks
# and separately ORing their two blocks into place.
#
# chunk 3 chunk 2 chunk 1
# ┌───────────────┬───────┐┌───────────────┬───────┐┌───────────────┬───────┐
# │000000000000000│ E ││ D │ C ││ B │ A │
# └───────────────┴───────┘└───────────────┴───────┘└───────────────┴───────┘
# k h k h k
# yielding;
# ┌───────────────┬───────┐┌───────────────┬───────┐┌───────────────┬───────┐
# │000000000000000│ A' ││ B' │ C' ││ D' │ E' │
# └───────────────┴───────┘└───────────────┴───────┘└───────────────┴───────┘
n = length(B)
n == 0 && return B
k = _mod64(n+63) + 1
h = 64 - k
i, j = 0, length(B.chunks)
u = UInt64(0)
v = reverse_bits(B.chunks[j])
B.chunks[j] = 0
@inbounds while true
i += 1
if i == j
break
end
u = reverse_bits(B.chunks[i])
B.chunks[i] = 0
B.chunks[j] |= u >>> h
B.chunks[i] |= v >>> h
j -= 1
if i == j
break
end
v = reverse_bits(B.chunks[j])
B.chunks[j] = 0
B.chunks[i] |= v << k
B.chunks[j] |= u << k
end
if isodd(length(B.chunks))
B.chunks[i] |= v >>> h
else
B.chunks[i] |= u << k
end
return B
end
reverse(v::BitVector) = reverse!(copy(v))
function (<<)(B::BitVector, i::UInt)
n = length(B)
i == 0 && return copy(B)
A = falses(n)
i < n && copy_chunks!(A.chunks, 1, B.chunks, i+1, n-i)
return A
end
function (>>>)(B::BitVector, i::UInt)
n = length(B)
i == 0 && return copy(B)
A = falses(n)
i < n && copy_chunks!(A.chunks, i+1, B.chunks, 1, n-i)
return A
end
"""
>>(B::BitVector, n) -> BitVector
Right bit shift operator, `B >> n`. For `n >= 0`, the result is `B`
with elements shifted `n` positions forward, filling with `false`
values. If `n < 0`, elements are shifted backwards. Equivalent to
`B << -n`.
## Example
```jldoctest
julia> B = BitVector([true, false, true, false, false])
5-element BitArray{1}:
true
false
true
false
false
julia> B >> 1
5-element BitArray{1}:
false
true
false
true
false
julia> B >> -1
5-element BitArray{1}:
false
true
false
false
false
```
"""
(>>)(B::BitVector, i::Union{Int, UInt}) = B >>> i
# signed integer version of shift operators with handling of negative values
"""
<<(B::BitVector, n) -> BitVector
Left bit shift operator, `B << n`. For `n >= 0`, the result is `B`
with elements shifted `n` positions backwards, filling with `false`
values. If `n < 0`, elements are shifted forwards. Equivalent to
`B >> -n`.
## Examples
```jldoctest
julia> B = BitVector([true, false, true, false, false])
5-element BitArray{1}:
true
false
true
false
false
julia> B << 1
5-element BitArray{1}:
false
true
false
false
false
julia> B << -1
5-element BitArray{1}:
false
true
false
true
false
```
"""
(<<)(B::BitVector, i::Int) = (i >=0 ? B << unsigned(i) : B >> unsigned(-i))
"""
>>>(B::BitVector, n) -> BitVector
Unsigned right bitshift operator, `B >>> n`. Equivalent to `B >> n`. See [`>>`](@ref) for
details and examples.
"""
(>>>)(B::BitVector, i::Int) = (i >=0 ? B >> unsigned(i) : B << unsigned(-i))
"""
rol!(dest::BitVector, src::BitVector, i::Integer) -> BitVector
Performs a left rotation operation on `src` and puts the result into `dest`.
`i` controls how far to rotate the bits.
"""
function rol!(dest::BitVector, src::BitVector, i::Integer)
length(dest) == length(src) || throw(ArgumentError("destination and source should be of same size"))
n = length(dest)
i %= n
i == 0 && return (src === dest ? src : copy!(dest, src))
i < 0 && return ror!(dest, src, -i)
Bc = (src === dest ? copy(src.chunks) : src.chunks)
copy_chunks!(dest.chunks, 1, Bc, i+1, n-i)
copy_chunks!(dest.chunks, n-i+1, Bc, 1, i)
return dest
end
"""
rol!(B::BitVector, i::Integer) -> BitVector
Performs a left rotation operation in-place on `B`.
`i` controls how far to rotate the bits.
"""
rol!(B::BitVector, i::Integer) = rol!(B, B, i)
"""
rol(B::BitVector, i::Integer) -> BitVector
Performs a left rotation operation, returning a new `BitVector`.
`i` controls how far to rotate the bits.
See also [`rol!`](@ref).
# Examples
```jldoctest
julia> A = BitArray([true, true, false, false, true])
5-element BitArray{1}:
true
true
false
false
true
julia> rol(A,1)
5-element BitArray{1}:
true
false
false
true
true
julia> rol(A,2)
5-element BitArray{1}:
false
false
true
true
true
julia> rol(A,5)
5-element BitArray{1}:
true
true
false
false
true
```
"""
rol(B::BitVector, i::Integer) = rol!(similar(B), B, i)
"""
ror!(dest::BitVector, src::BitVector, i::Integer) -> BitVector
Performs a right rotation operation on `src` and puts the result into `dest`.
`i` controls how far to rotate the bits.
"""
function ror!(dest::BitVector, src::BitVector, i::Integer)
length(dest) == length(src) || throw(ArgumentError("destination and source should be of same size"))
n = length(dest)
i %= n
i == 0 && return (src === dest ? src : copy!(dest, src))
i < 0 && return rol!(dest, src, -i)
Bc = (src === dest ? copy(src.chunks) : src.chunks)
copy_chunks!(dest.chunks, i+1, Bc, 1, n-i)
copy_chunks!(dest.chunks, 1, Bc, n-i+1, i)
return dest
end
"""
ror!(B::BitVector, i::Integer) -> BitVector
Performs a right rotation operation in-place on `B`.
`i` controls how far to rotate the bits.
"""
ror!(B::BitVector, i::Integer) = ror!(B, B, i)
"""
ror(B::BitVector, i::Integer) -> BitVector
Performs a right rotation operation on `B`, returning a new `BitVector`.
`i` controls how far to rotate the bits.
See also [`ror!`](@ref).
# Examples
```jldoctest
julia> A = BitArray([true, true, false, false, true])
5-element BitArray{1}:
true
true
false
false
true
julia> ror(A,1)
5-element BitArray{1}:
true
true
true
false
false
julia> ror(A,2)
5-element BitArray{1}:
false
true
true
true
false
julia> ror(A,5)
5-element BitArray{1}:
true
true
false
false
true
```
"""
ror(B::BitVector, i::Integer) = ror!(similar(B), B, i)
## countnz & find ##
function countnz(B::BitArray)
n = 0
Bc = B.chunks
@inbounds for i = 1:length(Bc)
n += count_ones(Bc[i])
end
return n
end
count(B::BitArray) = countnz(B)
# returns the index of the next non-zero element, or 0 if all zeros
function findnext(B::BitArray, start::Integer)
start > 0 || throw(BoundsError(B, start))
start > length(B) && return 0
Bc = B.chunks
chunk_start = _div64(start-1)+1
within_chunk_start = _mod64(start-1)
mask = _msk64 << within_chunk_start
@inbounds begin
if Bc[chunk_start] & mask != 0
return (chunk_start-1) << 6 + trailing_zeros(Bc[chunk_start] & mask) + 1
end
for i = chunk_start+1:length(Bc)
if Bc[i] != 0
return (i-1) << 6 + trailing_zeros(Bc[i]) + 1
end
end
end
return 0
end
#findfirst(B::BitArray) = findnext(B, 1) ## defined in array.jl
# aux function: same as findnext(~B, start), but performed without temporaries
function findnextnot(B::BitArray, start::Integer)
start > 0 || throw(BoundsError(B, start))
start > length(B) && return 0
Bc = B.chunks
l = length(Bc)
l == 0 && return 0
chunk_start = _div64(start-1)+1
within_chunk_start = _mod64(start-1)
mask = ~(_msk64 << within_chunk_start)
@inbounds if chunk_start < l
if Bc[chunk_start] | mask != _msk64
return (chunk_start-1) << 6 + trailing_ones(Bc[chunk_start] | mask) + 1
end
for i = chunk_start+1:l-1
if Bc[i] != _msk64
return (i-1) << 6 + trailing_ones(Bc[i]) + 1
end
end
if Bc[l] != _msk_end(B)
return (l-1) << 6 + trailing_ones(Bc[l]) + 1
end
elseif Bc[l] | mask != _msk_end(B)
return (l-1) << 6 + trailing_ones(Bc[l] | mask) + 1
end
return 0
end
findfirstnot(B::BitArray) = findnextnot(B,1)
# returns the index of the first matching element
function findnext(B::BitArray, v, start::Integer)
v == false && return findnextnot(B, start)
v == true && return findnext(B, start)
return 0
end
#findfirst(B::BitArray, v) = findnext(B, 1, v) ## defined in array.jl
# returns the index of the first element for which the function returns true
function findnext(testf::Function, B::BitArray, start::Integer)
f0::Bool = testf(false)
f1::Bool = testf(true)
!f0 && f1 && return findnext(B, start)
f0 && !f1 && return findnextnot(B, start)
start > 0 || throw(BoundsError(B, start))
start > length(B) && return 0
f0 && f1 && return Int(start)
return 0 # last case: !f0 && !f1
end
#findfirst(testf::Function, B::BitArray) = findnext(testf, B, 1) ## defined in array.jl
# returns the index of the previous non-zero element, or 0 if all zeros
function findprev(B::BitArray, start::Integer)
start > 0 || return 0
start > length(B) && throw(BoundsError(B, start))
Bc = B.chunks
chunk_start = _div64(start-1)+1
mask = _msk_end(start)
@inbounds begin
if Bc[chunk_start] & mask != 0
return (chunk_start-1) << 6 + (64 - leading_zeros(Bc[chunk_start] & mask))
end
for i = (chunk_start-1):-1:1
if Bc[i] != 0
return (i-1) << 6 + (64 - leading_zeros(Bc[i]))
end
end
end
return 0
end
function findprevnot(B::BitArray, start::Integer)
start > 0 || return 0
start > length(B) && throw(BoundsError(B, start))
Bc = B.chunks
chunk_start = _div64(start-1)+1
mask = ~_msk_end(start)
@inbounds begin
if Bc[chunk_start] | mask != _msk64
return (chunk_start-1) << 6 + (64 - leading_ones(Bc[chunk_start] | mask))
end
for i = chunk_start-1:-1:1
if Bc[i] != _msk64
return (i-1) << 6 + (64 - leading_ones(Bc[i]))
end
end
end
return 0
end
findlastnot(B::BitArray) = findprevnot(B, length(B))
# returns the index of the previous matching element
function findprev(B::BitArray, v, start::Integer)
v == false && return findprevnot(B, start)
v == true && return findprev(B, start)
return 0
end
#findlast(B::BitArray, v) = findprev(B, 1, v) ## defined in array.jl
# returns the index of the previous element for which the function returns true
function findprev(testf::Function, B::BitArray, start::Integer)
f0::Bool = testf(false)
f1::Bool = testf(true)
!f0 && f1 && return findprev(B, start)
f0 && !f1 && return findprevnot(B, start)
start > 0 || return 0
start > length(B) && throw(BoundsError(B, start))
f0 && f1 && return Int(start)
return 0 # last case: !f0 && !f1
end
#findlast(testf::Function, B::BitArray) = findprev(testf, B, 1) ## defined in array.jl
function find(B::BitArray)
l = length(B)
nnzB = countnz(B)
I = Vector{Int}(nnzB)
nnzB == 0 && return I
Bc = B.chunks
Bcount = 1
Icount = 1
for i = 1:length(Bc)-1
u = UInt64(1)
c = Bc[i]
for j = 1:64
if c & u != 0
I[Icount] = Bcount
Icount += 1
end
Bcount += 1
u <<= 1
end
end
u = UInt64(1)
c = Bc[end]
for j = 0:_mod64(l-1)
if c & u != 0
I[Icount] = Bcount
Icount += 1
end
Bcount += 1
u <<= 1
end
return I
end
findn(B::BitVector) = find(B)
function findn(B::BitMatrix)
nnzB = countnz(B)
I = Vector{Int}(nnzB)
J = Vector{Int}(nnzB)
count = 1
for j = 1:size(B,2), i = 1:size(B,1)
if B[i,j]
I[count] = i
J[count] = j
count += 1
end
end
return I, J
end
function findnz(B::BitMatrix)
I, J = findn(B)
return I, J, trues(length(I))
end
## Reductions ##
sum(A::BitArray, region) = reducedim(+, A, region)
sum(B::BitArray) = countnz(B)
function all(B::BitArray)
isempty(B) && return true
Bc = B.chunks
@inbounds begin
for i = 1:length(Bc)-1
Bc[i] == _msk64 || return false
end
Bc[end] == _msk_end(B) || return false
end
return true
end
function any(B::BitArray)
isempty(B) && return false
Bc = B.chunks
@inbounds begin
for i = 1:length(Bc)
Bc[i] == 0 || return true
end
end
return false
end
minimum(B::BitArray) = isempty(B) ? throw(ArgumentError("argument must be non-empty")) : all(B)
maximum(B::BitArray) = isempty(B) ? throw(ArgumentError("argument must be non-empty")) : any(B)
## map over bitarrays ##
# Specializing map is even more important for bitarrays than it is for generic
# arrays since there can be a 64x speedup by working at the level of Int64
# instead of looping bit-by-bit.
map(::Union{typeof(~), typeof(!)}, A::BitArray) = bit_map!(~, similar(A), A)
map(::typeof(zero), A::BitArray) = fill!(similar(A), false)
map(::typeof(one), A::BitArray) = fill!(similar(A), true)
map(::typeof(identity), A::BitArray) = copy(A)
map!(::Union{typeof(~), typeof(!)}, dest::BitArray, A::BitArray) = bit_map!(~, dest, A)
map!(::typeof(zero), dest::BitArray, A::BitArray) = fill!(dest, false)
map!(::typeof(one), dest::BitArray, A::BitArray) = fill!(dest, true)
map!(::typeof(identity), dest::BitArray, A::BitArray) = copy!(dest, A)
for (T, f) in ((:(Union{typeof(&), typeof(*), typeof(min)}), :(&)),
(:(Union{typeof(|), typeof(max)}), :(|)),
(:(Union{typeof(xor), typeof(!=)}), :xor),
(:(Union{typeof(>=), typeof(^)}), :((p, q) -> p | ~q)),
(:(typeof(<=)), :((p, q) -> ~p | q)),
(:(typeof(==)), :((p, q) -> ~xor(p, q))),
(:(typeof(<)), :((p, q) -> ~p & q)),
(:(typeof(>)), :((p, q) -> p & ~q)))
@eval map(::$T, A::BitArray, B::BitArray) = bit_map!($f, similar(A), A, B)
@eval map!(::$T, dest::BitArray, A::BitArray, B::BitArray) = bit_map!($f, dest, A, B)
end
# If we were able to specialize the function to a known bitwise operation,
# map across the chunks. Otherwise, fall-back to the AbstractArray method that
# iterates bit-by-bit.
function bit_map!{F}(f::F, dest::BitArray, A::BitArray)
size(A) == size(dest) || throw(DimensionMismatch("sizes of dest and A must match"))
isempty(A) && return dest
destc = dest.chunks
Ac = A.chunks
for i = 1:(length(Ac)-1)
destc[i] = f(Ac[i])
end
destc[end] = f(Ac[end]) & _msk_end(A)
dest
end
function bit_map!{F}(f::F, dest::BitArray, A::BitArray, B::BitArray)
size(A) == size(B) == size(dest) || throw(DimensionMismatch("sizes of dest, A, and B must all match"))
isempty(A) && return dest
destc = dest.chunks
Ac = A.chunks
Bc = B.chunks
for i = 1:(length(Ac)-1)
destc[i] = f(Ac[i], Bc[i])
end
destc[end] = f(Ac[end], Bc[end]) & _msk_end(A)
dest
end
## Filter ##
function filter(f, Bs::BitArray)
boolmap::Array{Bool} = map(f, Bs)
Bs[boolmap]
end
## Concatenation ##
function hcat(B::BitVector...)
height = length(B[1])
for j = 2:length(B)
length(B[j]) == height ||
throw(DimensionMismatch("dimensions must match"))
end
M = BitArray(height, length(B))
for j = 1:length(B)
copy_chunks!(M.chunks, (height*(j-1))+1, B[j].chunks, 1, height)
end
return M
end
function vcat(V::BitVector...)
n = 0
for Vk in V
n += length(Vk)
end
B = BitArray(n)
j = 1
for Vk in V
copy_chunks!(B.chunks, j, Vk.chunks, 1, length(Vk))
j += length(Vk)
end
return B
end
function hcat(A::Union{BitMatrix,BitVector}...)
nargs = length(A)
nrows = size(A[1], 1)
ncols = 0
dense = true
for j = 1:nargs
Aj = A[j]
nd = ndims(Aj)
ncols += (nd==2 ? size(Aj,2) : 1)
size(Aj, 1) == nrows ||
throw(DimensionMismatch("row lengths must match"))
end
B = BitArray(nrows, ncols)
pos = 1
for k = 1:nargs
Ak = A[k]
n = length(Ak)
copy_chunks!(B.chunks, pos, Ak.chunks, 1, n)
pos += n
end
return B
end
function vcat(A::BitMatrix...)
nargs = length(A)
nrows = sum(a->size(a, 1), A)::Int
ncols = size(A[1], 2)
for j = 2:nargs
size(A[j], 2) == ncols ||
throw(DimensionMismatch("column lengths must match"))
end
B = BitArray(nrows, ncols)
Bc = B.chunks
nrowsA = [size(a, 1) for a in A]
Ac = [a.chunks for a in A]
pos_d = 1
pos_s = ones(Int, nargs)
for j = 1:ncols, k = 1:nargs
copy_chunks!(Bc, pos_d, Ac[k], pos_s[k], nrowsA[k])
pos_s[k] += nrowsA[k]
pos_d += nrowsA[k]
end
return B
end
# general case, specialized for BitArrays and Integers
function cat(dims::Integer, X::Union{BitArray, Bool}...)
catdims = dims2cat(dims)
shape = cat_shape(catdims, (), map(cat_size, X)...)
A = falses(shape)
return _cat(A, shape, catdims, X...)
end
# hvcat -> use fallbacks in abstractarray.jl
# BitArray I/O
write(s::IO, B::BitArray) = write(s, B.chunks)
function read!(s::IO, B::BitArray)
n = length(B)
Bc = B.chunks
nc = length(read!(s, Bc))
if length(Bc) > 0 && Bc[end] & _msk_end(n) Bc[end]
Bc[end] &= _msk_end(n) # ensure that the BitArray is not broken
throw(DimensionMismatch("read mismatch, found non-zero bits after BitArray length"))
end
return B
end
sizeof(B::BitArray) = sizeof(B.chunks)