• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37527

pending completion
#37527

push

local

web-flow
make `IRShow.method_name` inferrable (#49607)

18 of 18 new or added lines in 3 files covered. (100.0%)

68710 of 81829 relevant lines covered (83.97%)

33068903.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

35.68
/base/bitarray.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
## BitArray
4

5
# notes: bits are stored in contiguous chunks
6
#        unused bits must always be set to 0
7
"""
8
    BitArray{N} <: AbstractArray{Bool, N}
9

10
Space-efficient `N`-dimensional boolean array, using just one bit for each boolean value.
11

12
`BitArray`s pack up to 64 values into every 8 bytes, resulting in an 8x space efficiency
13
over `Array{Bool, N}` and allowing some operations to work on 64 values at once.
14

15
By default, Julia returns `BitArrays` from [broadcasting](@ref Broadcasting) operations
16
that generate boolean elements (including dotted-comparisons like `.==`) as well as from
17
the functions [`trues`](@ref) and [`falses`](@ref).
18

19
!!! note
20
    Due to its packed storage format, concurrent access to the elements of a `BitArray`
21
    where at least one of them is a write is not thread-safe.
22

23
"""
24
mutable struct BitArray{N} <: AbstractArray{Bool, N}
25
    chunks::Vector{UInt64}
26
    len::Int
27
    dims::NTuple{N,Int}
28
    function BitArray{N}(::UndefInitializer, dims::Vararg{Int,N}) where N
10,967✔
29
        n = 1
357✔
30
        i = 1
357✔
31
        for d in dims
20,180,889✔
32
            d >= 0 || throw(ArgumentError("dimension size must be ≥ 0, got $d for dimension $i"))
20,184,890✔
33
            n *= d
20,184,890✔
34
            i += 1
7,809✔
35
        end
20,188,584✔
36
        nc = num_bit_chunks(n)
20,180,889✔
37
        chunks = Vector{UInt64}(undef, nc)
20,180,889✔
38
        nc > 0 && (chunks[end] = UInt64(0))
20,180,866✔
39
        b = new(chunks, n)
20,180,889✔
40
        N != 1 && (b.dims = dims)
3,785✔
41
        return b
20,180,889✔
42
    end
43
end
44

45
# note: the docs for the two signatures are unified, but only
46
# the first one is recognized by the help system; it would be nice
47
# to fix this.
48
"""
49
    BitArray(undef, dims::Integer...)
50
    BitArray{N}(undef, dims::NTuple{N,Int})
51

52
Construct an undef [`BitArray`](@ref) with the given dimensions.
53
Behaves identically to the [`Array`](@ref) constructor. See [`undef`](@ref).
54

55
# Examples
56
```julia-repl
57
julia> BitArray(undef, 2, 2)
58
2×2 BitMatrix:
59
 0  0
60
 0  0
61

62
julia> BitArray(undef, (3, 1))
63
3×1 BitMatrix:
64
 0
65
 0
66
 0
67
```
68
"""
69
BitArray(::UndefInitializer, dims::Integer...) = BitArray(undef, map(Int,dims))
3✔
70
BitArray{N}(::UndefInitializer, dims::Integer...) where {N} = BitArray{N}(undef, map(Int,dims))::BitArray{N}
×
71
BitArray(::UndefInitializer, dims::NTuple{N,Integer}) where {N} = BitArray{N}(undef, map(Int, dims)...)
20,178,584✔
72
BitArray{N}(::UndefInitializer, dims::NTuple{N,Integer}) where {N} = BitArray{N}(undef, map(Int, dims)...)
×
73

74
const BitVector = BitArray{1}
75
const BitMatrix = BitArray{2}
76

77
BitVector() = BitVector(undef, 0)
23✔
78

79
"""
80
    BitVector(nt::Tuple{Vararg{Bool}})
81

82
Construct a `BitVector` from a tuple of `Bool`.
83
# Examples
84
```julia-repl
85
julia> nt = (true, false, true, false)
86
(true, false, true, false)
87

88
julia> BitVector(nt)
89
4-element BitVector:
90
 1
91
 0
92
 1
93
 0
94
```
95
"""
96
function BitVector(nt::Tuple{Vararg{Bool}})
×
97
    bv = BitVector(undef, length(nt))
×
98
    bv .= nt
×
99
end
100

101
## utility functions ##
102

103
length(B::BitArray) = B.len
27,532,130✔
104
size(B::BitVector) = (B.len,)
36,506,113✔
105
size(B::BitArray) = B.dims
3,295,436✔
106

107
@inline function size(B::BitVector, d::Integer)
2✔
108
    d < 1 && throw_boundserror(size(B), d)
2✔
109
    ifelse(d == 1, B.len, 1)
2✔
110
end
111

112
isassigned(B::BitArray, i::Int) = 1 <= i <= length(B)
2✔
113

114
IndexStyle(::Type{<:BitArray}) = IndexLinear()
1,012,113✔
115

116
## aux functions ##
117

118
const _msk64 = ~UInt64(0)
119
@inline _div64(l) = l >> 6
605,242,423✔
120
@inline _mod64(l) = l & 63
503,363,468✔
121
@inline _blsr(x)= x & (x-1) #zeros the last set bit. Has native instruction on many archs. needed in multidimensional.jl
146,762,909✔
122
@inline _msk_end(l::Int) = _msk64 >>> _mod64(-l)
20,504✔
123
@inline _msk_end(B::BitArray) = _msk_end(length(B))
3,534✔
124
num_bit_chunks(n::Int) = _div64(n+63)
20,180,884✔
125

126
@inline get_chunks_id(i::Int) = _div64(i-1)+1, _mod64(i-1)
41,424,437✔
127

128
function glue_src_bitchunks(src::Vector{UInt64}, k::Int, ks1::Int, msk_s0::UInt64, ls0::Int)
×
129
    @inbounds begin
×
130
        chunk = ((src[k] & msk_s0) >>> ls0)
5,447✔
131
        if ks1 > k && ls0 > 0
5,447✔
132
            chunk_n = (src[k + 1] & ~msk_s0)
206✔
133
            chunk |= (chunk_n << (64 - ls0))
206✔
134
        end
135
    end
136
    return chunk
5,447✔
137
end
138

139
function copy_chunks!(dest::Vector{UInt64}, pos_d::Int, src::Vector{UInt64}, pos_s::Int, numbits::Int)
5,433✔
140
    numbits == 0 && return
5,433✔
141
    if dest === src && pos_d > pos_s
5,406✔
142
        return copy_chunks_rtol!(dest, pos_d, pos_s, numbits)
×
143
    end
144

145
    kd0, ld0 = get_chunks_id(pos_d)
5,406✔
146
    kd1, ld1 = get_chunks_id(pos_d + numbits - 1)
5,406✔
147
    ks0, ls0 = get_chunks_id(pos_s)
5,406✔
148
    ks1, ls1 = get_chunks_id(pos_s + numbits - 1)
5,406✔
149

150
    delta_kd = kd1 - kd0
5,406✔
151
    delta_ks = ks1 - ks0
5,406✔
152

153
    u = _msk64
×
154
    if delta_kd == 0
5,406✔
155
        msk_d0 = ~(u << ld0) | (u << (ld1+1))
5,313✔
156
    else
157
        msk_d0 = ~(u << ld0)
93✔
158
        msk_d1 = (u << (ld1+1))
93✔
159
    end
160
    if delta_ks == 0
5,406✔
161
        msk_s0 = (u << ls0) & ~(u << (ls1+1))
5,192✔
162
    else
163
        msk_s0 = (u << ls0)
214✔
164
    end
165

166
    chunk_s0 = glue_src_bitchunks(src, ks0, ks1, msk_s0, ls0)
5,406✔
167

168
    dest[kd0] = (dest[kd0] & msk_d0) | ((chunk_s0 << ld0) & ~msk_d0)
5,406✔
169

170
    delta_kd == 0 && return
5,406✔
171

172
    for i = 1 : kd1 - kd0 - 1
93✔
173
        chunk_s1 = glue_src_bitchunks(src, ks0 + i, ks1, msk_s0, ls0)
×
174

175
        chunk_s = (chunk_s0 >>> (64 - ld0)) | (chunk_s1 << ld0)
×
176

177
        dest[kd0 + i] = chunk_s
×
178

179
        chunk_s0 = chunk_s1
×
180
    end
×
181

182
    if ks1 >= ks0 + delta_kd
93✔
183
        chunk_s1 = glue_src_bitchunks(src, ks0 + delta_kd, ks1, msk_s0, ls0)
41✔
184
    else
185
        chunk_s1 = UInt64(0)
×
186
    end
187

188
    chunk_s = (chunk_s0 >>> (64 - ld0)) | (chunk_s1 << ld0)
93✔
189

190
    dest[kd1] = (dest[kd1] & msk_d1) | (chunk_s & ~msk_d1)
93✔
191

192
    return
93✔
193
end
194

195
function copy_chunks_rtol!(chunks::Vector{UInt64}, pos_d::Int, pos_s::Int, numbits::Int)
×
196
    pos_d == pos_s && return
×
197
    pos_d < pos_s && return copy_chunks!(chunks, pos_d, chunks, pos_s, numbits)
×
198

199
    left = numbits
×
200
    s = min(left, 64)
×
201
    b = left - s
×
202
    ps = pos_s + b
×
203
    pd = pos_d + b
×
204
    u = _msk64
×
205
    while left > 0
×
206
        kd0, ld0 = get_chunks_id(pd)
×
207
        kd1, ld1 = get_chunks_id(pd + s - 1)
×
208
        ks0, ls0 = get_chunks_id(ps)
×
209
        ks1, ls1 = get_chunks_id(ps + s - 1)
×
210

211
        delta_kd = kd1 - kd0
×
212
        delta_ks = ks1 - ks0
×
213

214
        if delta_kd == 0
×
215
            msk_d0 = ~(u << ld0) | (u << (ld1+1))
×
216
        else
217
            msk_d0 = ~(u << ld0)
×
218
            msk_d1 = (u << (ld1+1))
×
219
        end
220
        if delta_ks == 0
×
221
            msk_s0 = (u << ls0) & ~(u << (ls1+1))
×
222
        else
223
            msk_s0 = (u << ls0)
×
224
        end
225

226
        chunk_s0 = glue_src_bitchunks(chunks, ks0, ks1, msk_s0, ls0) & ~(u << s)
×
227
        chunks[kd0] = (chunks[kd0] & msk_d0) | ((chunk_s0 << ld0) & ~msk_d0)
×
228

229
        if delta_kd != 0
×
230
            chunk_s = (chunk_s0 >>> (64 - ld0))
×
231

232
            chunks[kd1] = (chunks[kd1] & msk_d1) | (chunk_s & ~msk_d1)
×
233
        end
234

235
        left -= s
×
236
        s = min(left, 64)
×
237
        b = left - s
×
238
        ps = pos_s + b
×
239
        pd = pos_d + b
×
240
    end
×
241
end
242

243
function fill_chunks!(Bc::Array{UInt64}, x::Bool, pos::Int, numbits::Int)
1,719✔
244
    numbits <= 0 && return
1,719✔
245
    k0, l0 = get_chunks_id(pos)
1,719✔
246
    k1, l1 = get_chunks_id(pos+numbits-1)
1,719✔
247

248
    u = _msk64
×
249
    if k1 == k0
1,719✔
250
        msk0 = (u << l0) & ~(u << (l1+1))
1,717✔
251
    else
252
        msk0 = (u << l0)
2✔
253
        msk1 = ~(u << (l1+1))
2✔
254
    end
255
    @inbounds if x
1,719✔
256
        Bc[k0] |= msk0
1,661✔
257
        for k = k0+1:k1-1
1,663✔
258
            Bc[k] = u
22✔
259
        end
42✔
260
        k1 > k0 && (Bc[k1] |= msk1)
1,661✔
261
    else
262
        Bc[k0] &= ~msk0
58✔
263
        for k = k0+1:k1-1
58✔
264
            Bc[k] = 0
×
265
        end
×
266
        k1 > k0 && (Bc[k1] &= ~msk1)
1,777✔
267
    end
268
end
269

270
copy_to_bitarray_chunks!(dest::Vector{UInt64}, pos_d::Int, src::BitArray, pos_s::Int, numbits::Int) =
7✔
271
    copy_chunks!(dest, pos_d, src.chunks, pos_s, numbits)
272

273
# pack 8 Bools encoded as one contiguous UIn64 into a single byte, e.g.:
274
# 0000001:0000001:00000000:00000000:00000001:00000000:00000000:00000001 → 11001001 → 0xc9
275
function pack8bools(z::UInt64)
×
276
    z |= z >>> 7
188,224✔
277
    z |= z >>> 14
188,224✔
278
    z |= z >>> 28
188,224✔
279
    z &= 0xFF
188,224✔
280
    return z
×
281
end
282

283
function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::Array{Bool}, pos_s::Int, numbits::Int)
1,581✔
284
    kd0, ld0 = get_chunks_id(pos_d)
1,581✔
285
    kd1, ld1 = get_chunks_id(pos_d + numbits - 1)
1,581✔
286

287
    delta_kd = kd1 - kd0
1,581✔
288

289
    u = _msk64
×
290
    if delta_kd == 0
1,581✔
291
        msk_d0 = msk_d1 = ~(u << ld0) | (u << (ld1+1))
36✔
292
        lt0 = ld1
36✔
293
    else
294
        msk_d0 = ~(u << ld0)
1,545✔
295
        msk_d1 = (u << (ld1+1))
1,545✔
296
        lt0 = 63
×
297
    end
298

299
    bind = kd0
1,581✔
300
    ind = pos_s
×
301
    @inbounds if ld0 > 0
1,581✔
302
        c = UInt64(0)
×
303
        for j = ld0:lt0
×
304
            c |= (UInt64(C[ind]) << j)
×
305
            ind += 1
×
306
        end
×
307
        Bc[kd0] = (Bc[kd0] & msk_d0) | (c & ~msk_d0)
×
308
        bind += 1
×
309
    end
310

311
    nc = _div64(numbits - ind + pos_s)
1,581✔
312
    nc8 = (nc >>> 3) << 3
1,581✔
313
    if nc8 > 0
1,581✔
314
        ind8 = 1
×
315
        P8 = Ptr{UInt64}(pointer(C, ind)) # unaligned i64 pointer
1,482✔
316
        @inbounds for i = 1:nc8
2,964✔
317
            c = UInt64(0)
×
318
            for j = 0:7
23,528✔
319
                # unaligned load
320
                c |= (pack8bools(unsafe_load(P8, ind8)) << (j<<3))
188,224✔
321
                ind8 += 1
188,224✔
322
            end
352,920✔
323
            Bc[bind] = c
23,528✔
324
            bind += 1
23,528✔
325
        end
45,574✔
326
        ind += (ind8-1) << 3
1,482✔
327
    end
328
    @inbounds for i = (nc8+1):nc
2,872✔
329
        c = UInt64(0)
×
330
        for j = 0:63
3,209✔
331
            c |= (UInt64(C[ind]) << j)
205,376✔
332
            ind += 1
205,376✔
333
        end
407,543✔
334
        Bc[bind] = c
3,209✔
335
        bind += 1
3,209✔
336
    end
5,127✔
337
    @inbounds if bind ≤ kd1
1,581✔
338
        @assert bind == kd1
×
339
        c = UInt64(0)
×
340
        for j = 0:ld1
×
341
            c |= (UInt64(C[ind]) << j)
×
342
            ind += 1
×
343
        end
×
344
        Bc[kd1] = (Bc[kd1] & msk_d1) | (c & ~msk_d1)
×
345
    end
346
end
347

348
## More definitions in multidimensional.jl
349

350
# auxiliary definitions used when filling a BitArray via a Vector{Bool} cache
351
# (e.g. when constructing from an iterable, or in broadcast!)
352

353
const bitcache_chunks = 64 # this can be changed
354
const bitcache_size = 64 * bitcache_chunks # do not change this
355

356
dumpbitcache(Bc::Vector{UInt64}, bind::Int, C::Vector{Bool}) =
1,581✔
357
    copy_to_bitarray_chunks!(Bc, ((bind - 1) << 6) + 1, C, 1, min(bitcache_size, (length(Bc)-bind+1) << 6))
358

359

360
## custom iterator ##
361
function iterate(B::BitArray, i::Int=0)
10,606✔
362
    i >= length(B) && return nothing
26,919✔
363
    (B.chunks[_div64(i)+1] & (UInt64(1)<<_mod64(i)) != 0, i+1)
22,194✔
364
end
365

366
## similar, fill!, copy! etc ##
367

368
similar(B::BitArray) = BitArray(undef, size(B))
574✔
369
similar(B::BitArray, dims::Int...) = BitArray(undef, dims)
1✔
370
similar(B::BitArray, dims::Dims) = BitArray(undef, dims...)
1✔
371

372
similar(B::BitArray, T::Type{Bool}, dims::Dims) = BitArray(undef, dims)
3,221✔
373
# changing type to a non-Bool returns an Array
374
# (this triggers conversions like float(bitvector) etc.)
375
similar(B::BitArray, T::Type, dims::Dims) = Array{T}(undef, dims)
73✔
376

377
function fill!(B::BitArray, x)
20,163,822✔
378
    y = convert(Bool, x)
8✔
379
    isempty(B) && return B
20,163,822✔
380
    Bc = B.chunks
20,162,777✔
381
    if !y
20,162,777✔
382
        fill!(Bc, 0)
20,162,841✔
383
    else
384
        fill!(Bc, _msk64)
873✔
385
        Bc[end] &= _msk_end(B)
711✔
386
    end
387
    return B
20,162,777✔
388
end
389

390
"""
391
    falses(dims)
392

393
Create a `BitArray` with all values set to `false`.
394

395
# Examples
396
```jldoctest
397
julia> falses(2,3)
398
2×3 BitMatrix:
399
 0  0  0
400
 0  0  0
401
```
402
"""
403
falses(dims::DimOrInd...) = falses(dims)
20,162,676✔
404
falses(dims::NTuple{N, Union{Integer, OneTo}}) where {N} = falses(map(to_dim, dims))
9✔
405
falses(dims::NTuple{N, Integer}) where {N} = fill!(BitArray(undef, dims), false)
20,162,675✔
406
falses(dims::Tuple{}) = fill!(BitArray(undef, dims), false)
×
407

408
"""
409
    trues(dims)
410

411
Create a `BitArray` with all values set to `true`.
412

413
# Examples
414
```jldoctest
415
julia> trues(2,3)
416
2×3 BitMatrix:
417
 1  1  1
418
 1  1  1
419
```
420
"""
421
trues(dims::DimOrInd...) = trues(dims)
712✔
422
trues(dims::NTuple{N, Union{Integer, OneTo}}) where {N} = trues(map(to_dim, dims))
×
423
trues(dims::NTuple{N, Integer}) where {N} = fill!(BitArray(undef, dims), true)
712✔
424
trues(dims::Tuple{}) = fill!(BitArray(undef, dims), true)
×
425

426
function one(x::BitMatrix)
×
427
    m, n = size(x)
×
428
    m == n || throw(DimensionMismatch("multiplicative identity defined only for square matrices"))
×
429
    a = falses(n, n)
×
430
    for i = 1:n
×
431
        a[i,i] = true
×
432
    end
×
433
    return a
×
434
end
435

436
function copyto!(dest::BitArray, src::BitArray)
576✔
437
    length(src) > length(dest) && throw(BoundsError(dest, length(dest)+1))
576✔
438
    destc = dest.chunks; srcc = src.chunks
1,150✔
439
    nc = min(length(destc), length(srcc))
575✔
440
    nc == 0 && return dest
575✔
441
    @inbounds begin
32✔
442
        for i = 1 : nc - 1
636✔
443
            destc[i] = srcc[i]
5,729✔
444
        end
11,397✔
445
        if length(src) == length(dest)
575✔
446
            destc[nc] = srcc[nc]
574✔
447
        else
448
            msk_s = _msk_end(src)
1✔
449
            msk_d = ~msk_s
1✔
450
            destc[nc] = (msk_d & destc[nc]) | (msk_s & srcc[nc])
1✔
451
        end
452
    end
453
    return dest
575✔
454
end
455

456
function unsafe_copyto!(dest::BitArray, doffs::Integer, src::Union{BitArray,Array}, soffs::Integer, n::Integer)
3✔
457
    copy_to_bitarray_chunks!(dest.chunks, Int(doffs), src, Int(soffs), Int(n))
3✔
458
    return dest
3✔
459
end
460

461
copyto!(dest::BitArray, doffs::Integer, src::Union{BitArray,Array}, soffs::Integer, n::Integer) =
18✔
462
    _copyto_int!(dest, Int(doffs), src, Int(soffs), Int(n))
463
function _copyto_int!(dest::BitArray, doffs::Int, src::Union{BitArray,Array}, soffs::Int, n::Int)
18✔
464
    n == 0 && return dest
18✔
465
    n < 0 && throw(ArgumentError("Number of elements to copy must be nonnegative."))
16✔
466
    soffs < 1 && throw(BoundsError(src, soffs))
15✔
467
    doffs < 1 && throw(BoundsError(dest, doffs))
14✔
468
    soffs+n-1 > length(src) && throw(BoundsError(src, length(src)+1))
11✔
469
    doffs+n-1 > length(dest) && throw(BoundsError(dest, length(dest)+1))
9✔
470
    return unsafe_copyto!(dest, doffs, src, soffs, n)
3✔
471
end
472

473
function copyto!(dest::BitArray, src::Array)
×
474
    length(src) > length(dest) && throw(BoundsError(dest, length(dest)+1))
×
475
    length(src) == 0 && return dest
×
476
    return unsafe_copyto!(dest, 1, src, 1, length(src))
×
477
end
478

479
function reshape(B::BitArray{N}, dims::NTuple{N,Int}) where N
×
480
    return dims == size(B) ? B : _bitreshape(B, dims)
×
481
end
482
reshape(B::BitArray, dims::Tuple{Vararg{Int}}) = _bitreshape(B, dims)
57✔
483
function _bitreshape(B::BitArray, dims::NTuple{N,Int}) where N
20✔
484
    prod(dims) == length(B) ||
57✔
485
        throw(DimensionMismatch("new dimensions $(dims) must be consistent with array size $(length(B))"))
486
    Br = BitArray{N}(undef, ntuple(i->0,Val(N))...)
57✔
487
    Br.chunks = B.chunks
57✔
488
    Br.len = prod(dims)
57✔
489
    N != 1 && (Br.dims = dims)
57✔
490
    return Br
57✔
491
end
492

493
## Constructors ##
494

495
function Array{T,N}(B::BitArray{N}) where {T,N}
73✔
496
    A = Array{T,N}(undef, size(B))
73✔
497
    Bc = B.chunks
73✔
498
    @inbounds for i = 1:length(A)
146✔
499
        A[i] = unsafe_bitgetindex(Bc, i)
1,923✔
500
    end
3,773✔
501
    return A
73✔
502
end
503

504
BitArray(A::AbstractArray{<:Any,N}) where {N} = BitArray{N}(A)
23✔
505

506
function BitArray{N}(A::AbstractArray{T,N}) where N where T
27✔
507
    B = BitArray(undef, convert(Dims{N}, size(A)::Dims{N}))
27✔
508
    _checkaxs(axes(B), axes(A))
27✔
509
    _copyto_bitarray!(B, A)
27✔
510
    return B::BitArray{N}
27✔
511
end
512

513
function _copyto_bitarray!(B::BitArray, A::AbstractArray)
27✔
514
    l = length(A)
27✔
515
    l == 0 && return B
27✔
516
    l > length(B) && throw(BoundsError(B, length(B)+1))
27✔
517
    Bc = B.chunks
27✔
518
    nc = num_bit_chunks(l)
27✔
519
    Ai = first(eachindex(A))
27✔
520
    @inbounds begin
27✔
521
        for i = 1:nc-1
29✔
522
            c = UInt64(0)
2✔
523
            for j = 0:63
2✔
524
                c |= (UInt64(convert(Bool, A[Ai])::Bool) << j)
128✔
525
                Ai = nextind(A, Ai)
128✔
526
            end
254✔
527
            Bc[i] = c
2✔
528
        end
2✔
529
        c = UInt64(0)
27✔
530
        tail = _mod64(l - 1) + 1
27✔
531
        for j = 0:tail-1
54✔
532
            c |= (UInt64(convert(Bool, A[Ai])::Bool) << j)
164✔
533
            Ai = nextind(A, Ai)
152✔
534
        end
277✔
535
        msk = _msk_end(tail)
27✔
536
        Bc[nc] = (c & msk) | (Bc[nc] & ~msk)
27✔
537
    end
538
    return B
27✔
539
end
540

541
reinterpret(::Type{Bool}, B::BitArray, dims::NTuple{N,Int}) where {N} = reinterpret(B, dims)
×
542
reinterpret(B::BitArray, dims::NTuple{N,Int}) where {N} = reshape(B, dims)
×
543

544
if nameof(@__MODULE__) === :Base  # avoid method overwrite
545
(::Type{T})(x::T) where {T<:BitArray} = copy(x)::T
36✔
546
BitArray(x::BitArray) = copy(x)
72✔
547
end
548

549
"""
550
    BitArray(itr)
551

552
Construct a [`BitArray`](@ref) generated by the given iterable object.
553
The shape is inferred from the `itr` object.
554

555
# Examples
556
```jldoctest
557
julia> BitArray([1 0; 0 1])
558
2×2 BitMatrix:
559
 1  0
560
 0  1
561

562
julia> BitArray(x+y == 3 for x = 1:2, y = 1:3)
563
2×3 BitMatrix:
564
 0  1  0
565
 1  0  0
566

567
julia> BitArray(x+y == 3 for x = 1:2 for y = 1:3)
568
6-element BitVector:
569
 0
570
 1
571
 0
572
 1
573
 0
574
 0
575
```
576
"""
577
BitArray(itr) = gen_bitarray(IteratorSize(itr), itr)
4✔
578
BitArray{N}(itr) where N = gen_bitarrayN(BitArray{N}, IteratorSize(itr), itr)
37✔
579

580
convert(::Type{T}, a::AbstractArray) where {T<:BitArray} = a isa T ? a : T(a)::T
×
581

582
# generic constructor from an iterable without compile-time info
583
# (we pass start(itr) explicitly to avoid a type-instability with filters)
584
gen_bitarray(isz::IteratorSize, itr) = gen_bitarray_from_itr(itr)
×
585

586
# generic iterable with known shape
587
function gen_bitarray(::HasShape, itr)
4✔
588
    B = BitArray(undef, size(itr))
4✔
589
    for (I,x) in zip(CartesianIndices(axes(itr)), itr)
4✔
590
        B[I] = x
4✔
591
    end
4✔
592
    return B
4✔
593
end
594

595
# generator with known shape or length
596
function gen_bitarray(::HasShape, itr::Generator)
37✔
597
    B = BitArray(undef, size(itr))
37✔
598
    return fill_bitarray_from_itr!(B, itr)
37✔
599
end
600
function gen_bitarray(::HasLength, itr)
×
601
    b = BitVector(undef, length(itr))
×
602
    return fill_bitarray_from_itr!(b, itr)
×
603
end
604

605
gen_bitarray(::IsInfinite, itr) =  throw(ArgumentError("infinite-size iterable used in BitArray constructor"))
×
606

607
gen_bitarrayN(::Type{BitVector}, itsz, itr)                        = gen_bitarray(itsz, itr)
×
608
gen_bitarrayN(::Type{BitVector}, itsz::HasShape{1}, itr)           = gen_bitarray(itsz, itr)
37✔
609
gen_bitarrayN(::Type{BitArray{N}}, itsz::HasShape{N}, itr) where N = gen_bitarray(itsz, itr)
×
610
# The first of these is just for ambiguity resolution
611
gen_bitarrayN(::Type{BitVector}, itsz::HasShape{N}, itr) where N      = throw(DimensionMismatch("cannot create a BitVector from a $N-dimensional iterator"))
×
612
gen_bitarrayN(@nospecialize(T::Type), itsz::HasShape{N}, itr) where N = throw(DimensionMismatch("cannot create a $T from a $N-dimensional iterator"))
×
613
gen_bitarrayN(@nospecialize(T::Type), itsz, itr) = throw(DimensionMismatch("cannot create a $T from a generic iterator"))
×
614

615
# The aux functions gen_bitarray_from_itr and fill_bitarray_from_itr! both
616
# use a Vector{Bool} cache for performance reasons
617

618
function gen_bitarray_from_itr(itr)
×
619
    B = empty!(BitVector(undef, bitcache_size))
×
620
    C = Vector{Bool}(undef, bitcache_size)
×
621
    Bc = B.chunks
×
622
    ind = 1
×
623
    cind = 1
×
624
    y = iterate(itr)
×
625
    while y !== nothing
×
626
        x, st = y
×
627
        @inbounds C[ind] = x
×
628
        ind += 1
×
629
        if ind > bitcache_size
×
630
            resize!(B, length(B) + bitcache_size)
×
631
            dumpbitcache(Bc, cind, C)
×
632
            cind += bitcache_chunks
×
633
            ind = 1
×
634
        end
635
        y = iterate(itr, st)
×
636
    end
×
637
    if ind > 1
×
638
        @inbounds C[ind:bitcache_size] .= false
×
639
        resize!(B, length(B) + ind - 1)
×
640
        dumpbitcache(Bc, cind, C)
×
641
    end
642
    return B
×
643
end
644

645
function fill_bitarray_from_itr!(B::BitArray, itr)
37✔
646
    n = length(B)
×
647
    C = Vector{Bool}(undef, bitcache_size)
37✔
648
    Bc = B.chunks
37✔
649
    ind = 1
×
650
    cind = 1
×
651
    y = iterate(itr)
74✔
652
    while y !== nothing
250✔
653
        x, st = y
×
654
        @inbounds C[ind] = x
213✔
655
        ind += 1
213✔
656
        if ind > bitcache_size
213✔
657
            dumpbitcache(Bc, cind, C)
×
658
            cind += bitcache_chunks
×
659
            ind = 1
×
660
        end
661
        y = iterate(itr, st)
389✔
662
    end
213✔
663
    if ind > 1
37✔
664
        @inbounds C[ind:bitcache_size] .= false
151,339✔
665
        dumpbitcache(Bc, cind, C)
37✔
666
    end
667
    return B
37✔
668
end
669

670

671
## Indexing: getindex ##
672

673
@inline function unsafe_bitgetindex(Bc::Vector{UInt64}, i::Int)
425,071✔
674
    i1, i2 = get_chunks_id(i)
35,240,082✔
675
    u = UInt64(1) << i2
35,240,037✔
676
    @inbounds r = (Bc[i1] & u) != 0
35,240,082✔
677
    return r
×
678
end
679

680
@inline function getindex(B::BitArray, i::Int)
3,655,553✔
681
    @boundscheck checkbounds(B, i)
35,205,684✔
682
    unsafe_bitgetindex(B.chunks, i)
35,205,684✔
683
end
684

685
## Indexing: setindex! ##
686

687
@inline function unsafe_bitsetindex!(Bc::Array{UInt64}, x::Bool, i::Int)
101,524✔
688
    i1, i2 = get_chunks_id(i)
6,156,122✔
689
    _unsafe_bitsetindex!(Bc, x, i1, i2)
6,156,122✔
690
end
691

692
@inline function _unsafe_bitsetindex!(Bc::Array{UInt64}, x::Bool, i1::Int, i2::Int)
38✔
693
    u = UInt64(1) << i2
312,860,398✔
694
    @inbounds begin
38✔
695
        c = Bc[i1]
363,191,922✔
696
        Bc[i1] = ifelse(x, c | u, c & ~u)
363,191,922✔
697
    end
698
end
699

700
@inline function setindex!(B::BitArray, x, i::Int)
2,110,786✔
701
    @boundscheck checkbounds(B, i)
6,123,647✔
702
    unsafe_bitsetindex!(B.chunks, convert(Bool, x), i)
6,123,647✔
703
    return B
6,123,647✔
704
end
705

706
indexoffset(i) = first(i)-1
2,458✔
707
indexoffset(::Colon) = 0
×
708

709
@propagate_inbounds function setindex!(B::BitArray, X::AbstractArray, J0::Union{Colon,AbstractUnitRange{Int}})
×
710
    _setindex!(IndexStyle(B), B, X, to_indices(B, (J0,))[1])
×
711
end
712

713
# Assigning an array of bools is more complicated, but we can still do some
714
# work on chunks by combining X and I 64 bits at a time to improve perf by ~40%
715
@inline function setindex!(B::BitArray, X::AbstractArray, I::BitArray)
×
716
    @boundscheck checkbounds(B, I)
×
717
    _unsafe_setindex!(B, X, I)
×
718
end
719
function _unsafe_setindex!(B::BitArray, X::AbstractArray, I::BitArray)
×
720
    Bc = B.chunks
×
721
    Ic = I.chunks
×
722
    length(Bc) == length(Ic) || throw_boundserror(B, I)
×
723
    lc = length(Bc)
×
724
    lx = length(X)
×
725
    last_chunk_len = _mod64(length(B)-1)+1
×
726

727
    Xi = first(eachindex(X))
×
728
    lastXi = last(eachindex(X))
×
729
    for i = 1:lc
×
730
        @inbounds Imsk = Ic[i]
×
731
        @inbounds C = Bc[i]
×
732
        u = UInt64(1)
×
733
        for j = 1:(i < lc ? 64 : last_chunk_len)
×
734
            if Imsk & u != 0
×
735
                Xi > lastXi && throw_setindex_mismatch(X, count(I))
×
736
                @inbounds x = convert(Bool, X[Xi])
×
737
                C = ifelse(x, C | u, C & ~u)
×
738
                Xi = nextind(X, Xi)
×
739
            end
740
            u <<= 1
×
741
        end
×
742
        @inbounds Bc[i] = C
×
743
    end
×
744
    if Xi != nextind(X, lastXi)
×
745
        throw_setindex_mismatch(X, count(I))
×
746
    end
747
    return B
×
748
end
749

750
## Dequeue functionality ##
751

752
function push!(B::BitVector, item)
×
753
    # convert first so we don't grow the bitarray if the assignment won't work
754
    item = convert(Bool, item)
×
755

756
    Bc = B.chunks
×
757

758
    l = _mod64(length(B))
×
759
    if l == 0
×
760
        _growend!(Bc, 1)
×
761
        Bc[end] = UInt64(0)
×
762
    end
763
    B.len += 1
×
764
    if item
×
765
        B[end] = true
×
766
    end
767
    return B
×
768
end
769

770
function append!(B::BitVector, items::BitVector)
×
771
    n0 = length(B)
×
772
    n1 = length(items)
×
773
    n1 == 0 && return B
×
774
    Bc = B.chunks
×
775
    k0 = length(Bc)
×
776
    k1 = num_bit_chunks(n0 + n1)
×
777
    if k1 > k0
×
778
        _growend!(Bc, k1 - k0)
×
779
        Bc[end] = UInt64(0)
×
780
    end
781
    B.len += n1
×
782
    copy_chunks!(Bc, n0+1, items.chunks, 1, n1)
×
783
    return B
×
784
end
785

786
append!(B::BitVector, items) = append!(B, BitVector(items))
×
787
append!(A::Vector{Bool}, items::BitVector) = append!(A, Array(items))
×
788

789
function prepend!(B::BitVector, items::BitVector)
×
790
    n0 = length(B)
×
791
    n1 = length(items)
×
792
    n1 == 0 && return B
×
793
    Bc = B.chunks
×
794
    k0 = length(Bc)
×
795
    k1 = num_bit_chunks(n0 + n1)
×
796
    if k1 > k0
×
797
        _growend!(Bc, k1 - k0)
×
798
        Bc[end] = UInt64(0)
×
799
    end
800
    B.len += n1
×
801
    copy_chunks!(Bc, 1 + n1, Bc, 1, n0)
×
802
    copy_chunks!(Bc, 1, items.chunks, 1, n1)
×
803
    return B
×
804
end
805

806
prepend!(B::BitVector, items) = prepend!(B, BitArray(items))
×
807
prepend!(A::Vector{Bool}, items::BitVector) = prepend!(A, Array(items))
×
808

809
function sizehint!(B::BitVector, sz::Integer)
×
810
    ccall(:jl_array_sizehint, Cvoid, (Any, UInt), B.chunks, num_bit_chunks(sz))
×
811
    return B
×
812
end
813

814
resize!(B::BitVector, n::Integer) = _resize_int!(B, Int(n))
×
815
function _resize_int!(B::BitVector, n::Int)
×
816
    n0 = length(B)
×
817
    n == n0 && return B
×
818
    n >= 0 || throw(BoundsError(B, n))
×
819
    if n < n0
×
820
        deleteat!(B, n+1:n0)
×
821
        return B
×
822
    end
823
    Bc = B.chunks
×
824
    k0 = length(Bc)
×
825
    k1 = num_bit_chunks(n)
×
826
    if k1 > k0
×
827
        _growend!(Bc, k1 - k0)
×
828
        Bc[end] = UInt64(0)
×
829
    end
830
    B.len = n
×
831
    return B
×
832
end
833

834
function pop!(B::BitVector)
×
835
    isempty(B) && throw(ArgumentError("argument must not be empty"))
×
836
    item = B[end]
×
837
    B[end] = false
×
838

839
    l = _mod64(length(B))
×
840
    l == 1 && _deleteend!(B.chunks, 1)
×
841
    B.len -= 1
×
842

843
    return item
×
844
end
845

846
function pushfirst!(B::BitVector, item)
×
847
    item = convert(Bool, item)
×
848

849
    Bc = B.chunks
×
850

851
    l = _mod64(length(B))
×
852
    if l == 0
×
853
        _growend!(Bc, 1)
×
854
        Bc[end] = UInt64(0)
×
855
    end
856
    B.len += 1
×
857
    if B.len == 1
×
858
        Bc[1] = item
×
859
        return B
×
860
    end
861
    for i = length(Bc) : -1 : 2
×
862
        Bc[i] = (Bc[i] << 1) | (Bc[i-1] >>> 63)
×
863
    end
×
864
    Bc[1] = UInt64(item) | (Bc[1] << 1)
×
865
    return B
×
866
end
867

868
function popfirst!(B::BitVector)
×
869
    isempty(B) && throw(ArgumentError("argument must not be empty"))
×
870
    @inbounds begin
×
871
        item = B[1]
×
872

873
        Bc = B.chunks
×
874

875
        for i = 1 : length(Bc) - 1
×
876
            Bc[i] = (Bc[i] >>> 1) | (Bc[i+1] << 63)
×
877
        end
×
878

879
        l = _mod64(length(B))
×
880
        if l == 1
×
881
            _deleteend!(Bc, 1)
×
882
        else
883
            Bc[end] >>>= 1
×
884
        end
885
        B.len -= 1
×
886
    end
887

888
    return item
×
889
end
890

891
insert!(B::BitVector, i::Integer, item) = _insert_int!(B, Int(i), item)
×
892
function _insert_int!(B::BitVector, i::Int, item)
×
893
    i = Int(i)
×
894
    n = length(B)
×
895
    1 <= i <= n+1 || throw(BoundsError(B, i))
×
896
    item = convert(Bool, item)
×
897

898
    Bc = B.chunks
×
899

900
    k, j = get_chunks_id(i)
×
901

902
    l = _mod64(length(B))
×
903
    if l == 0
×
904
        _growend!(Bc, 1)
×
905
        Bc[end] = UInt64(0)
×
906
    end
907
    B.len += 1
×
908

909
    for t = length(Bc) : -1 : k + 1
×
910
        Bc[t] = (Bc[t] << 1) | (Bc[t - 1] >>> 63)
×
911
    end
×
912

913
    msk_aft = (_msk64 << j)
×
914
    msk_bef = ~msk_aft
×
915
    Bc[k] = (msk_bef & Bc[k]) | ((msk_aft & Bc[k]) << 1)
×
916
    B[i] = item
×
917
    B
×
918
end
919

920
function _deleteat!(B::BitVector, i::Int)
×
921
    k, j = get_chunks_id(i)
×
922

923
    msk_bef = _msk64 >>> (63 - j)
×
924
    msk_aft = ~msk_bef
×
925
    msk_bef >>>= 1
×
926

927
    Bc = B.chunks
×
928

929
    @inbounds begin
×
930
        Bc[k] = (msk_bef & Bc[k]) | ((msk_aft & Bc[k]) >> 1)
×
931
        if length(Bc) > k
×
932
            Bc[k] |= (Bc[k + 1] << 63)
×
933
        end
934

935
        for t = k + 1 : length(Bc) - 1
×
936
            Bc[t] = (Bc[t] >>> 1) | (Bc[t + 1] << 63)
×
937
        end
×
938

939
        l = _mod64(length(B))
×
940

941
        if l == 1
×
942
            _deleteend!(Bc, 1)
×
943
        elseif length(Bc) > k
×
944
            Bc[end] >>>= 1
×
945
        end
946
    end
947

948
    B.len -= 1
×
949

950
    return B
×
951
end
952

953
function deleteat!(B::BitVector, i::Integer)
×
954
    i isa Bool && depwarn("passing Bool as an index is deprecated", :deleteat!)
×
955
    i = Int(i)
×
956
    n = length(B)
×
957
    1 <= i <= n || throw(BoundsError(B, i))
×
958

959
    return _deleteat!(B, i)
×
960
end
961

962
function deleteat!(B::BitVector, r::AbstractUnitRange{Int})
4✔
963
    n = length(B)
4✔
964
    i_f = first(r)
4✔
965
    i_l = last(r)
4✔
966
    1 <= i_f || throw(BoundsError(B, i_f))
4✔
967
    i_l <= n || throw(BoundsError(B, n+1))
4✔
968

969
    Bc = B.chunks
4✔
970
    new_l = length(B) - length(r)
4✔
971
    delta_k = num_bit_chunks(new_l) - length(Bc)
4✔
972

973
    copy_chunks!(Bc, i_f, Bc, i_l+1, n-i_l)
4✔
974

975
    delta_k < 0 && _deleteend!(Bc, -delta_k)
4✔
976

977
    B.len = new_l
4✔
978

979
    if new_l > 0
4✔
980
        Bc[end] &= _msk_end(new_l)
4✔
981
    end
982

983
    return B
4✔
984
end
985

986
function deleteat!(B::BitVector, inds)
×
987
    n = new_l = length(B)
×
988
    y = iterate(inds)
×
989
    y === nothing && return B
×
990

991
    Bc = B.chunks
×
992

993
    (p, s) = y
×
994
    checkbounds(B, p)
×
995
    p isa Bool && throw(ArgumentError("invalid index $p of type Bool"))
×
996
    q = p+1
×
997
    new_l -= 1
×
998
    y = iterate(inds, s)
×
999
    while y !== nothing
×
1000
        (i, s) = y
×
1001
        if !(q <= i <= n)
×
1002
            i isa Bool && throw(ArgumentError("invalid index $i of type Bool"))
×
1003
            i < q && throw(ArgumentError("indices must be unique and sorted"))
×
1004
            throw(BoundsError(B, i))
×
1005
        end
1006
        new_l -= 1
×
1007
        if i > q
×
1008
            copy_chunks!(Bc, Int(p), Bc, Int(q), Int(i-q))
×
1009
            p += i-q
×
1010
        end
1011
        q = i+1
×
1012
        y = iterate(inds, s)
×
1013
    end
×
1014

1015
    q <= n && copy_chunks!(Bc, Int(p), Bc, Int(q), Int(n-q+1))
×
1016

1017
    delta_k = num_bit_chunks(new_l) - length(Bc)
×
1018
    delta_k < 0 && _deleteend!(Bc, -delta_k)
×
1019

1020
    B.len = new_l
×
1021

1022
    if new_l > 0
×
1023
        Bc[end] &= _msk_end(new_l)
×
1024
    end
1025

1026
    return B
×
1027
end
1028

1029
function deleteat!(B::BitVector, inds::AbstractVector{Bool})
×
1030
    length(inds) == length(B) || throw(BoundsError(B, inds))
×
1031

1032
    n = new_l = length(B)
×
1033
    y = findfirst(inds)
×
1034
    y === nothing && return B
×
1035

1036
    Bc = B.chunks
×
1037

1038
    p = y
×
1039
    s = y + 1
×
1040
    checkbounds(B, p)
×
1041
    q = p + 1
×
1042
    new_l -= 1
×
1043
    y = findnext(inds, s)
×
1044
    while y !== nothing
×
1045
        i = y
×
1046
        s = y + 1
×
1047
        new_l -= 1
×
1048
        if i > q
×
1049
            copy_chunks!(Bc, Int(p), Bc, Int(q), Int(i-q))
×
1050
            p += i - q
×
1051
        end
1052
        q = i + 1
×
1053
        y = findnext(inds, s)
×
1054
    end
×
1055

1056
    q <= n && copy_chunks!(Bc, Int(p), Bc, Int(q), Int(n - q + 1))
×
1057

1058
    delta_k = num_bit_chunks(new_l) - length(Bc)
×
1059
    delta_k < 0 && _deleteend!(Bc, -delta_k)
×
1060

1061
    B.len = new_l
×
1062

1063
    if new_l > 0
×
1064
        Bc[end] &= _msk_end(new_l)
×
1065
    end
1066

1067
    return B
×
1068
end
1069

1070
keepat!(B::BitVector, inds) = _keepat!(B, inds)
2✔
1071
keepat!(B::BitVector, inds::AbstractVector{Bool}) = _keepat!(B, inds)
1✔
1072

1073
function splice!(B::BitVector, i::Integer)
×
1074
    # TODO: after deprecation remove the four lines below
1075
    #       as v = B[i] is enough to do both bounds checking
1076
    #       and Bool check then just pass Int(i) to _deleteat!
1077
    i isa Bool && depwarn("passing Bool as an index is deprecated", :splice!)
×
1078
    i = Int(i)
×
1079
    n = length(B)
×
1080
    1 <= i <= n || throw(BoundsError(B, i))
×
1081

1082
    v = B[i]   # TODO: change to a copy if/when subscripting becomes an ArrayView
×
1083
    _deleteat!(B, i)
×
1084
    return v
×
1085
end
1086

1087
const _default_bit_splice = BitVector()
1088

1089
function splice!(B::BitVector, r::Union{AbstractUnitRange{Int}, Integer}, ins::AbstractArray = _default_bit_splice)
×
1090
    r isa Bool && depwarn("passing Bool as an index is deprecated", :splice!)
×
1091
    _splice_int!(B, isa(r, AbstractUnitRange{Int}) ? r : Int(r), ins)
×
1092
end
1093

1094
function _splice_int!(B::BitVector, r, ins)
×
1095
    n = length(B)
×
1096
    i_f, i_l = first(r), last(r)
×
1097
    1 <= i_f <= n+1 || throw(BoundsError(B, i_f))
×
1098
    i_l <= n || throw(BoundsError(B, n+1))
×
1099

1100
    Bins = convert(BitArray, ins)
×
1101

1102
    if (i_f > n)
×
1103
        append!(B, Bins)
×
1104
        return BitVector()
×
1105
    end
1106

1107
    v = B[r]  # TODO: change to a copy if/when subscripting becomes an ArrayView
×
1108

1109
    Bc = B.chunks
×
1110

1111
    lins = length(Bins)
×
1112
    ldel = length(r)
×
1113

1114
    new_l = length(B) + lins - ldel
×
1115
    delta_k = num_bit_chunks(new_l) - length(Bc)
×
1116

1117
    delta_k > 0 && _growend!(Bc, delta_k)
×
1118

1119
    copy_chunks!(Bc, i_f+lins, Bc, i_l+1, n-i_l)
×
1120
    copy_chunks!(Bc, i_f, Bins.chunks, 1, lins)
×
1121

1122
    delta_k < 0 && _deleteend!(Bc, -delta_k)
×
1123

1124
    B.len = new_l
×
1125

1126
    if new_l > 0
×
1127
        Bc[end] &= _msk_end(new_l)
×
1128
    end
1129

1130
    return v
×
1131
end
1132

1133
function splice!(B::BitVector, r::Union{AbstractUnitRange{Int}, Integer}, ins)
×
1134
    Bins = BitVector(undef, length(ins))
×
1135
    i = 1
×
1136
    for x in ins
×
1137
        Bins[i] = Bool(x)
×
1138
        i += 1
×
1139
    end
×
1140
    return splice!(B, r, Bins)
×
1141
end
1142

1143

1144
function empty!(B::BitVector)
×
1145
    _deleteend!(B.chunks, length(B.chunks))
×
1146
    B.len = 0
×
1147
    return B
×
1148
end
1149

1150
## Unary operators ##
1151

1152
function (-)(B::BitArray)
×
1153
    A = zeros(Int, size(B))
×
1154
    l = length(B)
×
1155
    l == 0 && return A
×
1156
    Bc = B.chunks
×
1157
    ind = 1
×
1158
    for i = 1:length(Bc)-1
×
1159
        u = UInt64(1)
×
1160
        c = Bc[i]
×
1161
        for j = 1:64
×
1162
            if c & u != 0
×
1163
                A[ind] = -1
×
1164
            end
1165
            ind += 1
×
1166
            u <<= 1
×
1167
        end
×
1168
    end
×
1169
    u = UInt64(1)
×
1170
    c = Bc[end]
×
1171
    for j = 0:_mod64(l-1)
×
1172
        if c & u != 0
×
1173
            A[ind] = -1
×
1174
        end
1175
        ind += 1
×
1176
        u <<= 1
×
1177
    end
×
1178
    return A
×
1179
end
1180

1181
## Binary arithmetic operators ##
1182

1183
for f in (:+, :-)
1184
    @eval function ($f)(A::BitArray, B::BitArray)
64✔
1185
        r = Array{Int}(undef, promote_shape(size(A), size(B)))
64✔
1186
        ay, by = iterate(A), iterate(B)
128✔
1187
        ri = 1
64✔
1188
        # promote_shape guarantees that A and B have the
1189
        # same iteration space
1190
        while ay !== nothing
1,344✔
1191
            @inbounds r[ri] = ($f)(ay[1], by[1])
1,280✔
1192
            ri += 1
1,280✔
1193
            ay, by = iterate(A, ay[2]), iterate(B, by[2])
2,496✔
1194
        end
1,280✔
1195
        return r
64✔
1196
    end
1197
end
1198

1199
for f in (:/, :\)
1200
    @eval begin
1201
        ($f)(A::Union{BitMatrix,BitVector}, B::Union{BitMatrix,BitVector}) = ($f)(Array(A), Array(B))
×
1202
    end
1203
end
1204
(/)(B::BitArray, x::Number) = (/)(Array(B), x)
×
1205
(/)(x::Number, B::BitArray) = (/)(x, Array(B))
×
1206

1207
## promotion to complex ##
1208

1209
# TODO?
1210

1211
## comparison operators ##
1212

1213
function (==)(A::BitArray, B::BitArray)
136✔
1214
    size(A) != size(B) && return false
2,039✔
1215
    return A.chunks == B.chunks
1,020✔
1216
end
1217

1218

1219
## Data movement ##
1220

1221
# TODO some of this could be optimized
1222

1223
_reverse(A::BitArray, d::Tuple{Integer}) = _reverse(A, d[1])
×
1224
function _reverse(A::BitArray, d::Int)
×
1225
    nd = ndims(A)
×
1226
    1 ≤ d ≤ nd || throw(ArgumentError("dimension $d is not 1 ≤ $d ≤ $nd"))
×
1227
    sd = size(A, d)
×
1228
    sd == 1 && return copy(A)
×
1229

1230
    B = similar(A)
×
1231

1232
    nnd = 0
×
1233
    for i = 1:nd
×
1234
        nnd += size(A,i)==1 || i==d
×
1235
    end
×
1236
    if nnd == nd
×
1237
        # reverse along the only non-singleton dimension
1238
        for i = 1:sd
×
1239
            B[i] = A[sd+1-i]
×
1240
        end
×
1241
        return B
×
1242
    end
1243

1244
    d_in = size(A)
×
1245
    leading = d_in[1:(d-1)]
×
1246
    M = prod(leading)
×
1247
    N = length(A)
×
1248
    stride = M * sd
×
1249

1250
    if M == 1
×
1251
        for j = 0:stride:(N-stride)
×
1252
            for i = 1:sd
×
1253
                ri = sd+1-i
×
1254
                B[j + ri] = A[j + i]
×
1255
            end
×
1256
        end
×
1257
    else
1258
        for i = 1:sd
×
1259
            ri = sd+1-i
×
1260
            for j=0:stride:(N-stride)
×
1261
                offs = j + 1 + (i-1)*M
×
1262
                boffs = j + 1 + (ri-1)*M
×
1263
                copy_chunks!(B.chunks, boffs, A.chunks, offs, M)
×
1264
            end
×
1265
        end
×
1266
    end
1267
    return B
×
1268
end
1269

1270
function _reverse!(B::BitVector, ::Colon)
×
1271
    # Basic idea: each chunk is divided into two blocks of size k = n % 64, and
1272
    # h = 64 - k. Walk from either end (with indices i and j) reversing chunks
1273
    # and separately ORing their two blocks into place.
1274
    #
1275
    #           chunk 3                  chunk 2                  chunk 1
1276
    # ┌───────────────┬───────┐┌───────────────┬───────┐┌───────────────┬───────┐
1277
    # │000000000000000│   E   ││       D       │   C   ││       B       │   A   │
1278
    # └───────────────┴───────┘└───────────────┴───────┘└───────────────┴───────┘
1279
    #                     k            h           k            h            k
1280
    # yielding;
1281
    # ┌───────────────┬───────┐┌───────────────┬───────┐┌───────────────┬───────┐
1282
    # │000000000000000│  A'   ││      B'       │  C'   ││      D'       │  E'   │
1283
    # └───────────────┴───────┘└───────────────┴───────┘└───────────────┴───────┘
1284

1285
    n = length(B)
×
1286
    n == 0 && return B
×
1287

1288
    k = _mod64(n+63) + 1
×
1289
    h = 64 - k
×
1290

1291
    i, j = 0, length(B.chunks)
×
1292
    u = UInt64(0)
×
1293
    v = bitreverse(B.chunks[j])
×
1294
    B.chunks[j] = 0
×
1295
    @inbounds while true
×
1296
        i += 1
×
1297
        if i == j
×
1298
            break
×
1299
        end
1300
        u = bitreverse(B.chunks[i])
×
1301
        B.chunks[i] = 0
×
1302
        B.chunks[j] |= u >>> h
×
1303
        B.chunks[i] |= v >>> h
×
1304

1305
        j -= 1
×
1306
        if i == j
×
1307
            break
×
1308
        end
1309
        v = bitreverse(B.chunks[j])
×
1310
        B.chunks[j] = 0
×
1311
        B.chunks[i] |= v << k
×
1312
        B.chunks[j] |= u << k
×
1313
    end
×
1314

1315
    if isodd(length(B.chunks))
×
1316
        B.chunks[i] |= v >>> h
×
1317
    else
1318
        B.chunks[i] |= u << k
×
1319
    end
1320

1321
    return B
×
1322
end
1323

1324
function (<<)(B::BitVector, i::UInt)
×
1325
    n = length(B)
×
1326
    i == 0 && return copy(B)
×
1327
    A = falses(n)
×
1328
    i < n && copy_chunks!(A.chunks, 1, B.chunks, Int(i+1), Int(n-i))
×
1329
    return A
×
1330
end
1331

1332
function (>>>)(B::BitVector, i::UInt)
×
1333
    n = length(B)
×
1334
    i == 0 && return copy(B)
×
1335
    A = falses(n)
×
1336
    i < n && copy_chunks!(A.chunks, Int(i+1), B.chunks, 1, Int(n-i))
×
1337
    return A
×
1338
end
1339

1340
"""
1341
    >>(B::BitVector, n) -> BitVector
1342

1343
Right bit shift operator, `B >> n`. For `n >= 0`, the result is `B`
1344
with elements shifted `n` positions forward, filling with `false`
1345
values. If `n < 0`, elements are shifted backwards. Equivalent to
1346
`B << -n`.
1347

1348
# Examples
1349
```jldoctest
1350
julia> B = BitVector([true, false, true, false, false])
1351
5-element BitVector:
1352
 1
1353
 0
1354
 1
1355
 0
1356
 0
1357

1358
julia> B >> 1
1359
5-element BitVector:
1360
 0
1361
 1
1362
 0
1363
 1
1364
 0
1365

1366
julia> B >> -1
1367
5-element BitVector:
1368
 0
1369
 1
1370
 0
1371
 0
1372
 0
1373
```
1374
"""
1375
(>>)(B::BitVector, i::Union{Int, UInt}) = B >>> i
×
1376

1377
# signed integer version of shift operators with handling of negative values
1378
"""
1379
    <<(B::BitVector, n) -> BitVector
1380

1381
Left bit shift operator, `B << n`. For `n >= 0`, the result is `B`
1382
with elements shifted `n` positions backwards, filling with `false`
1383
values. If `n < 0`, elements are shifted forwards. Equivalent to
1384
`B >> -n`.
1385

1386
# Examples
1387
```jldoctest
1388
julia> B = BitVector([true, false, true, false, false])
1389
5-element BitVector:
1390
 1
1391
 0
1392
 1
1393
 0
1394
 0
1395

1396
julia> B << 1
1397
5-element BitVector:
1398
 0
1399
 1
1400
 0
1401
 0
1402
 0
1403

1404
julia> B << -1
1405
5-element BitVector:
1406
 0
1407
 1
1408
 0
1409
 1
1410
 0
1411
```
1412
"""
1413
(<<)(B::BitVector, i::Int) = (i >=0 ? B << unsigned(i) : B >> unsigned(-i))
×
1414

1415
"""
1416
    >>>(B::BitVector, n) -> BitVector
1417

1418
Unsigned right bitshift operator, `B >>> n`. Equivalent to `B >> n`. See [`>>`](@ref) for
1419
details and examples.
1420
"""
1421
(>>>)(B::BitVector, i::Int) = (i >=0 ? B >> unsigned(i) : B << unsigned(-i))
×
1422

1423
circshift!(dest::BitVector, src::BitVector, i::Integer) = _circshift_int!(dest, src, Int(i))
×
1424
function _circshift_int!(dest::BitVector, src::BitVector, i::Int)
×
1425
    i = Int(i)
×
1426
    length(dest) == length(src) || throw(ArgumentError("destination and source should be of same size"))
×
1427
    n = length(dest)
×
1428
    i %= n
×
1429
    i == 0 && return (src === dest ? src : copyto!(dest, src))
×
1430
    Bc = (src === dest ? copy(src.chunks) : src.chunks)
×
1431
    if i > 0 # right
×
1432
        copy_chunks!(dest.chunks, i+1, Bc, 1, n-i)
×
1433
        copy_chunks!(dest.chunks, 1, Bc, n-i+1, i)
×
1434
    else # left
1435
        i = -i
×
1436
        copy_chunks!(dest.chunks, 1, Bc, i+1, n-i)
×
1437
        copy_chunks!(dest.chunks, n-i+1, Bc, 1, i)
×
1438
    end
1439
    return dest
×
1440
end
1441

1442
circshift!(B::BitVector, i::Integer) = circshift!(B, B, i)
×
1443

1444
## count & find ##
1445

1446
function bitcount(Bc::Vector{UInt64}; init::T=0) where {T}
45,915,988✔
1447
    n::T = init
636✔
1448
    @inbounds for i = 1:length(Bc)
73,558,505✔
1449
        n = (n + count_ones(Bc[i])) % T
33,258,351✔
1450
    end
36,065,950✔
1451
    return n
43,107,753✔
1452
end
1453

1454
_count(::typeof(identity), B::BitArray, ::Colon, init) = bitcount(B.chunks; init)
19,037✔
1455

1456
function unsafe_bitfindnext(Bc::Vector{UInt64}, start::Int)
25,119,980✔
1457
    chunk_start = _div64(start-1)+1
25,119,980✔
1458
    within_chunk_start = _mod64(start-1)
25,119,980✔
1459
    mask = _msk64 << within_chunk_start
25,119,980✔
1460

1461
    @inbounds begin
×
1462
        if Bc[chunk_start] & mask != 0
25,119,980✔
1463
            return (chunk_start-1) << 6 + trailing_zeros(Bc[chunk_start] & mask) + 1
18,615,775✔
1464
        end
1465

1466
        for i = chunk_start+1:length(Bc)
7,804,000✔
1467
            if Bc[i] != 0
5,173,318✔
1468
                return (i-1) << 6 + trailing_zeros(Bc[i]) + 1
1,289,419✔
1469
            end
1470
        end
7,757,422✔
1471
    end
1472
    return nothing
5,214,786✔
1473
end
1474

1475
# returns the index of the next true element, or nothing if all false
1476
function findnext(B::BitArray, start::Integer)
×
1477
    start = Int(start)
×
1478
    start > 0 || throw(BoundsError(B, start))
×
1479
    start > length(B) && return nothing
23✔
1480
    unsafe_bitfindnext(B.chunks, start)
23✔
1481
end
1482

1483
#findfirst(B::BitArray) = findnext(B, 1)  ## defined in array.jl
1484

1485
# aux function: same as findnext(~B, start), but performed without temporaries
1486
function findnextnot(B::BitArray, start::Int)
×
1487
    start > 0 || throw(BoundsError(B, start))
×
1488
    start > length(B) && return nothing
×
1489

1490
    Bc = B.chunks
×
1491
    l = length(Bc)
×
1492
    l == 0 && return nothing
×
1493

1494
    chunk_start = _div64(start-1)+1
×
1495
    within_chunk_start = _mod64(start-1)
×
1496
    mask = ~(_msk64 << within_chunk_start)
×
1497

1498
    @inbounds if chunk_start < l
×
1499
        if Bc[chunk_start] | mask != _msk64
×
1500
            return (chunk_start-1) << 6 + trailing_ones(Bc[chunk_start] | mask) + 1
×
1501
        end
1502
        for i = chunk_start+1:l-1
×
1503
            if Bc[i] != _msk64
×
1504
                return (i-1) << 6 + trailing_ones(Bc[i]) + 1
×
1505
            end
1506
        end
×
1507
        if Bc[l] != _msk_end(B)
×
1508
            return (l-1) << 6 + trailing_ones(Bc[l]) + 1
×
1509
        end
1510
    elseif Bc[l] | mask != _msk_end(B)
×
1511
        return (l-1) << 6 + trailing_ones(Bc[l] | mask) + 1
×
1512
    end
1513
    return nothing
×
1514
end
1515
findfirstnot(B::BitArray) = findnextnot(B,1)
×
1516

1517
# returns the index of the first matching element
1518
function findnext(pred::Fix2{<:Union{typeof(isequal),typeof(==)},Bool},
×
1519
                  B::BitArray, start::Integer)
1520
    v = pred.x
×
1521
    v == false && return findnextnot(B, Int(start))
×
1522
    v == true && return findnext(B, start)
×
1523
    return nothing
×
1524
end
1525
#findfirst(B::BitArray, v) = findnext(B, 1, v)  ## defined in array.jl
1526

1527
# returns the index of the first element for which the function returns true
1528
findnext(testf::Function, B::BitArray, start::Integer) = _findnext_int(testf, B, Int(start))
×
1529
function _findnext_int(testf::Function, B::BitArray, start::Int)
×
1530
    f0::Bool = testf(false)
×
1531
    f1::Bool = testf(true)
×
1532
    !f0 && f1 && return findnext(B, start)
×
1533
    f0 && !f1 && return findnextnot(B, start)
×
1534

1535
    start > 0 || throw(BoundsError(B, start))
×
1536
    start > length(B) && return nothing
×
1537
    f0 && f1 && return start
×
1538
    return nothing # last case: !f0 && !f1
×
1539
end
1540
#findfirst(testf::Function, B::BitArray) = findnext(testf, B, 1)  ## defined in array.jl
1541

1542
function unsafe_bitfindprev(Bc::Vector{UInt64}, start::Int)
16,922✔
1543
    chunk_start = _div64(start-1)+1
16,922✔
1544
    mask = _msk_end(start)
16,922✔
1545

1546
    @inbounds begin
×
1547
        if Bc[chunk_start] & mask != 0
16,922✔
1548
            return (chunk_start-1) << 6 + (top_set_bit(Bc[chunk_start] & mask))
16,849✔
1549
        end
1550

1551
        for i = (chunk_start-1):-1:1
145✔
1552
            if Bc[i] != 0
72✔
1553
                return (i-1) << 6 + (top_set_bit(Bc[i]))
72✔
1554
            end
1555
        end
×
1556
    end
1557
    return nothing
1✔
1558
end
1559

1560
# returns the index of the previous true element, or nothing if all false
1561
function findprev(B::BitArray, start::Integer)
×
1562
    start = Int(start)
×
1563
    start > 0 || return nothing
125✔
1564
    start > length(B) && throw(BoundsError(B, start))
125✔
1565
    unsafe_bitfindprev(B.chunks, start)
125✔
1566
end
1567

1568
function findprevnot(B::BitArray, start::Int)
×
1569
    start = Int(start)
×
1570
    start > 0 || return nothing
×
1571
    start > length(B) && throw(BoundsError(B, start))
×
1572

1573
    Bc = B.chunks
×
1574

1575
    chunk_start = _div64(start-1)+1
×
1576
    mask = ~_msk_end(start)
×
1577

1578
    @inbounds begin
×
1579
        if Bc[chunk_start] | mask != _msk64
×
1580
            return (chunk_start-1) << 6 + (64 - leading_ones(Bc[chunk_start] | mask))
×
1581
        end
1582

1583
        for i = chunk_start-1:-1:1
×
1584
            if Bc[i] != _msk64
×
1585
                return (i-1) << 6 + (64 - leading_ones(Bc[i]))
×
1586
            end
1587
        end
×
1588
    end
1589
    return nothing
×
1590
end
1591
findlastnot(B::BitArray) = findprevnot(B, length(B))
×
1592

1593
# returns the index of the previous matching element
1594
function findprev(pred::Fix2{<:Union{typeof(isequal),typeof(==)},Bool},
×
1595
                  B::BitArray, start::Integer)
1596
    v = pred.x
×
1597
    v == false && return findprevnot(B, Int(start))
×
1598
    v == true && return findprev(B, start)
×
1599
    return nothing
×
1600
end
1601
#findlast(B::BitArray, v) = findprev(B, 1, v)  ## defined in array.jl
1602

1603
# returns the index of the previous element for which the function returns true
1604
findprev(testf::Function, B::BitArray, start::Integer) = _findprev_int(testf, B, Int(start))
×
1605
function _findprev_int(testf::Function, B::BitArray, start::Int)
×
1606
    f0::Bool = testf(false)
×
1607
    f1::Bool = testf(true)
×
1608
    !f0 && f1 && return findprev(B, start)
×
1609
    f0 && !f1 && return findprevnot(B, start)
×
1610

1611
    start > 0 || return nothing
×
1612
    start > length(B) && throw(BoundsError(B, start))
×
1613
    f0 && f1 && return start
×
1614
    return nothing # last case: !f0 && !f1
×
1615
end
1616
#findlast(testf::Function, B::BitArray) = findprev(testf, B, 1)  ## defined in array.jl
1617

1618
function findmax(a::BitArray)
×
1619
    isempty(a) && throw(ArgumentError("BitArray must be non-empty"))
×
1620
    m, mi = false, 1
×
1621
    ti = 1
×
1622
    ac = a.chunks
×
1623
    for i = 1:length(ac)
×
1624
        @inbounds k = trailing_zeros(ac[i])
×
1625
        ti += k
×
1626
        k == 64 || return (true, @inbounds keys(a)[ti])
×
1627
    end
×
1628
    return m, @inbounds keys(a)[mi]
×
1629
end
1630

1631
function findmin(a::BitArray)
×
1632
    isempty(a) && throw(ArgumentError("BitArray must be non-empty"))
×
1633
    m, mi = true, 1
×
1634
    ti = 1
×
1635
    ac = a.chunks
×
1636
    for i = 1:length(ac)-1
×
1637
        @inbounds k = trailing_ones(ac[i])
×
1638
        ti += k
×
1639
        k == 64 || return (false, @inbounds keys(a)[ti])
×
1640
    end
×
1641
    l = Base._mod64(length(a)-1) + 1
×
1642
    @inbounds k = trailing_ones(ac[end] & Base._msk_end(l))
×
1643
    ti += k
×
1644
    k == l || return (false, @inbounds keys(a)[ti])
×
1645
    return (m, @inbounds keys(a)[mi])
×
1646
end
1647

1648
# findall helper functions
1649
# Generic case (>2 dimensions)
1650
function allindices!(I, B::BitArray)
×
1651
    ind = first(keys(B))
×
1652
    for k = 1:length(B)
×
1653
        I[k] = ind
×
1654
        ind = nextind(B, ind)
×
1655
    end
×
1656
end
1657

1658
# Optimized case for vector
1659
function allindices!(I, B::BitVector)
87✔
1660
    I[:] .= 1:length(B)
87✔
1661
end
1662

1663
# Optimized case for matrix
1664
function allindices!(I, B::BitMatrix)
×
1665
    k = 1
×
1666
    for c = 1:size(B,2), r = 1:size(B,1)
×
1667
        I[k] = CartesianIndex(r, c)
×
1668
        k += 1
×
1669
    end
×
1670
end
1671

1672
@inline _overflowind(i1, irest::Tuple{}, size) = (i1, irest)
110✔
1673
@inline function _overflowind(i1, irest, size)
813✔
1674
    i2 = irest[1]
813✔
1675
    while i1 > size[1]
1,513✔
1676
        i1 -= size[1]
700✔
1677
        i2 += 1
700✔
1678
    end
700✔
1679
    i2, irest = _overflowind(i2, tail(irest), tail(size))
813✔
1680
    return (i1, (i2, irest...))
813✔
1681
end
1682

1683
@inline _toind(i1, irest::Tuple{}) = i1
×
1684
@inline _toind(i1, irest) = CartesianIndex(i1, irest...)
304✔
1685

1686
function findall(B::BitArray)
517✔
1687
    nnzB = count(B)
616✔
1688
    I = Vector{eltype(keys(B))}(undef, nnzB)
517✔
1689
    nnzB == 0 && return I
517✔
1690
    nnzB == length(B) && (allindices!(I, B); return I)
571✔
1691
    Bc = B.chunks
397✔
1692
    Bs = size(B)
36✔
1693
    Bi = i1 = i = 1
36✔
1694
    irest = ntuple(one, ndims(B) - 1)
36✔
1695
    c = Bc[1]
397✔
1696
    @inbounds while true
1,569✔
1697
        while c == 0
1,966✔
1698
            Bi == length(Bc) && return I
491✔
1699
            i1 += 64
94✔
1700
            Bi += 1
94✔
1701
            c = Bc[Bi]
94✔
1702
        end
94✔
1703

1704
        tz = trailing_zeros(c)
1,476✔
1705
        c = _blsr(c)
1,476✔
1706

1707
        i1, irest = _overflowind(i1 + tz, irest, Bs)
1,476✔
1708
        I[i] = _toind(i1, irest)
1,476✔
1709
        i += 1
1,476✔
1710
        i1 -= tz
1,476✔
1711
    end
1,476✔
1712
end
1713

1714
# For performance
1715
findall(::typeof(!iszero), B::BitArray) = findall(B)
×
1716

1717
## Reductions ##
1718

1719
_sum(A::BitArray, dims)    = reduce(+, A, dims=dims)
×
1720
_sum(B::BitArray, ::Colon) = count(B)
×
1721

1722
function all(B::BitArray)
1,837✔
1723
    isempty(B) && return true
2,075✔
1724
    Bc = B.chunks
2,074✔
1725
    @inbounds begin
187✔
1726
        for i = 1:length(Bc)-1
2,305✔
1727
            Bc[i] == _msk64 || return false
2,264✔
1728
        end
4,294✔
1729
        Bc[end] == _msk_end(B) || return false
2,187✔
1730
    end
1731
    return true
1,959✔
1732
end
1733

1734
function any(B::BitArray)
8,009✔
1735
    isempty(B) && return false
2,966,044✔
1736
    Bc = B.chunks
2,966,044✔
1737
    @inbounds begin
1,532✔
1738
        for i = 1:length(Bc)
5,932,088✔
1739
            Bc[i] == 0 || return true
5,925,220✔
1740
        end
7,480✔
1741
    end
1742
    return false
7,072✔
1743
end
1744

1745
minimum(B::BitArray) = isempty(B) ? throw(ArgumentError("argument must be non-empty")) : all(B)
×
1746
maximum(B::BitArray) = isempty(B) ? throw(ArgumentError("argument must be non-empty")) : any(B)
×
1747

1748
## map over bitarrays ##
1749

1750
# Specializing map is even more important for bitarrays than it is for generic
1751
# arrays since there can be a 64x speedup by working at the level of Int64
1752
# instead of looping bit-by-bit.
1753

1754
map(::Union{typeof(~), typeof(!)}, A::BitArray) = bit_map!(~, similar(A), A)
×
1755
map(::typeof(zero), A::BitArray) = fill!(similar(A), false)
×
1756
map(::typeof(one), A::BitArray) = fill!(similar(A), true)
×
1757
map(::typeof(identity), A::BitArray) = copy(A)
×
1758

1759
map!(::Union{typeof(~), typeof(!)}, dest::BitArray, A::BitArray) = bit_map!(~, dest, A)
×
1760
map!(::typeof(zero), dest::BitArray, A::BitArray) = fill!(dest, false)
×
1761
map!(::typeof(one), dest::BitArray, A::BitArray) = fill!(dest, true)
×
1762
map!(::typeof(identity), dest::BitArray, A::BitArray) = copyto!(dest, A)
×
1763

1764
for (T, f) in ((:(Union{typeof(&), typeof(*), typeof(min)}), :(&)),
1765
               (:(Union{typeof(|), typeof(max)}),            :(|)),
1766
               (:(Union{typeof(xor), typeof(!=)}),           :xor),
1767
               (:(typeof(nand)),                             :nand),
1768
               (:(typeof(nor)),                              :nor),
1769
               (:(Union{typeof(>=), typeof(^)}),             :((p, q) -> p | ~q)),
×
1770
               (:(typeof(<=)),                               :((p, q) -> ~p | q)),
×
1771
               (:(typeof(==)),                               :((p, q) -> ~xor(p, q))),
×
1772
               (:(typeof(<)),                                :((p, q) -> ~p & q)),
×
1773
               (:(typeof(>)),                                :((p, q) -> p & ~q)))
×
1774
    @eval map(::$T, A::BitArray, B::BitArray) = bit_map!($f, similar(A), A, B)
×
1775
    @eval map!(::$T, dest::BitArray, A::BitArray, B::BitArray) = bit_map!($f, dest, A, B)
×
1776
end
1777

1778
# If we were able to specialize the function to a known bitwise operation,
1779
# map across the chunks. Otherwise, fall-back to the AbstractArray method that
1780
# iterates bit-by-bit.
1781
function bit_map!(f::F, dest::BitArray, A::BitArray) where F
×
1782
    length(A) <= length(dest) || throw(DimensionMismatch("length of destination must be >= length of collection"))
×
1783
    isempty(A) && return dest
×
1784
    destc = dest.chunks
×
1785
    Ac = A.chunks
×
1786
    len_Ac = length(Ac)
×
1787
    for i = 1:(len_Ac-1)
×
1788
        destc[i] = f(Ac[i])
×
1789
    end
×
1790
    # the last effected UInt64's original content
1791
    dest_last = destc[len_Ac]
×
1792
    _msk = _msk_end(A)
×
1793
    # first zero out the bits mask is going to change
1794
    destc[len_Ac] = (dest_last & (~_msk))
×
1795
    # then update bits by `or`ing with a masked RHS
1796
    destc[len_Ac] |= f(Ac[len_Ac]) & _msk
×
1797
    dest
×
1798
end
1799
function bit_map!(f::F, dest::BitArray, A::BitArray, B::BitArray) where F
×
1800
    min_bitlen = min(length(A), length(B))
×
1801
    min_bitlen <= length(dest) || throw(DimensionMismatch("length of destination must be >= length of smallest input collection"))
×
1802
    isempty(A) && return dest
×
1803
    isempty(B) && return dest
×
1804
    destc = dest.chunks
×
1805
    Ac = A.chunks
×
1806
    Bc = B.chunks
×
1807
    len_Ac = min(length(Ac), length(Bc))
×
1808
    for i = 1:len_Ac-1
×
1809
        destc[i] = f(Ac[i], Bc[i])
×
1810
    end
×
1811
    # the last effected UInt64's original content
1812
    dest_last = destc[len_Ac]
×
1813
    _msk = _msk_end(min_bitlen)
×
1814
    # first zero out the bits mask is going to change
1815
    destc[len_Ac] = (dest_last & ~(_msk))
×
1816
    # then update bits by `or`ing with a masked RHS
1817
    destc[len_Ac] |= f(Ac[end], Bc[end]) & _msk
×
1818
    dest
×
1819
end
1820

1821
## Filter ##
1822

1823
function filter(f, Bs::BitArray)
×
1824
    boolmap::Array{Bool} = map(f, Bs)
×
1825
    Bs[boolmap]
×
1826
end
1827

1828

1829
## Concatenation ##
1830

1831
function hcat(B::BitVector...)
×
1832
    height = length(B[1])
×
1833
    for j = 2:length(B)
×
1834
        length(B[j]) == height ||
×
1835
            throw(DimensionMismatch("dimensions must match: $j-th argument has length $(length(B[j])), should have $height"))
1836
    end
×
1837
    M = BitMatrix(undef, height, length(B))
×
1838
    for j = 1:length(B)
×
1839
        copy_chunks!(M.chunks, (height*(j-1))+1, B[j].chunks, 1, height)
×
1840
    end
×
1841
    return M
×
1842
end
1843

1844
function vcat(V::BitVector...)
26✔
1845
    n = 0
26✔
1846
    for Vk in V
26✔
1847
        n += length(Vk)
100✔
1848
    end
126✔
1849
    B = BitVector(undef, n)
26✔
1850
    j = 1
26✔
1851
    for Vk in V
26✔
1852
        copy_chunks!(B.chunks, j, Vk.chunks, 1, length(Vk))
100✔
1853
        j += length(Vk)
100✔
1854
    end
126✔
1855
    return B
26✔
1856
end
1857

1858
function hcat(A::Union{BitMatrix,BitVector}...)
×
1859
    nargs = length(A)
×
1860
    nrows = size(A[1], 1)
×
1861
    ncols = 0
×
1862
    dense = true
×
1863
    for j = 1:nargs
×
1864
        Aj = A[j]
×
1865
        nd = ndims(Aj)
×
1866
        ncols += (nd==2 ? size(Aj,2) : 1)
×
1867
        size(Aj, 1) == nrows ||
×
1868
            throw(DimensionMismatch("row lengths must match: $j-th element has first dim $(size(Aj, 1)), should have $nrows"))
1869
    end
×
1870

1871
    B = BitMatrix(undef, nrows, ncols)
×
1872

1873
    pos = 1
×
1874
    for k = 1:nargs
×
1875
        Ak = A[k]
×
1876
        n = length(Ak)
×
1877
        copy_chunks!(B.chunks, pos, Ak.chunks, 1, n)
×
1878
        pos += n
×
1879
    end
×
1880
    return B
×
1881
end
1882

1883
function vcat(A::BitMatrix...)
36✔
1884
    nargs = length(A)
36✔
1885
    nrows, nrowsA = 0, sizehint!(Int[], nargs)
36✔
1886
    for a in A
36✔
1887
        sz1 = size(a, 1)
192✔
1888
        nrows += sz1
192✔
1889
        push!(nrowsA, sz1)
192✔
1890
    end
228✔
1891
    ncols = size(A[1], 2)
36✔
1892
    for j = 2:nargs
70✔
1893
        size(A[j], 2) == ncols ||
156✔
1894
        throw(DimensionMismatch("column lengths must match: $j-th element has second dim $(size(A[j], 2)), should have $ncols"))
1895
    end
276✔
1896
    B = BitMatrix(undef, nrows, ncols)
36✔
1897
    Bc = B.chunks
36✔
1898
    pos_d = 1
36✔
1899
    pos_s = fill(1, nargs)
192✔
1900
    for j = 1:ncols, k = 1:nargs
194✔
1901
        copy_chunks!(Bc, pos_d, A[k].chunks, pos_s[k], nrowsA[k])
1,578✔
1902
        pos_s[k] += nrowsA[k]
1,578✔
1903
        pos_d += nrowsA[k]
1,578✔
1904
    end
1,702✔
1905
    return B
36✔
1906
end
1907

1908
# general case, specialized for BitArrays and Integers
1909
_cat(dims::Integer, X::Union{BitArray, Bool}...) = _cat(Int(dims)::Int, X...)
×
1910
function _cat(dims::Int, X::Union{BitArray, Bool}...)
×
1911
    dims = Int(dims)
×
1912
    catdims = dims2cat(dims)
×
1913
    shape = cat_shape(catdims, map(cat_size, X))
×
1914
    A = falses(shape)
×
1915
    return __cat(A, shape, catdims, X...)
×
1916
end
1917

1918
# hvcat -> use fallbacks in abstractarray.jl
1919

1920

1921
# BitArray I/O
1922

1923
write(s::IO, B::BitArray) = write(s, B.chunks)
1✔
1924
function read!(s::IO, B::BitArray)
1925
    n = length(B)
1926
    Bc = B.chunks
1927
    nc = length(read!(s, Bc))
1928
    if length(Bc) > 0 && Bc[end] & _msk_end(n) ≠ Bc[end]
1929
        Bc[end] &= _msk_end(n) # ensure that the BitArray is not broken
1930
        throw(DimensionMismatch("read mismatch, found non-zero bits after BitArray length"))
1931
    end
1932
    return B
1933
end
1934

1935
sizeof(B::BitArray) = sizeof(B.chunks)
1936

1937
function _split_rest(a::Union{Vector, BitVector}, n::Int)
6✔
1938
    _check_length_split_rest(length(a), n)
6✔
1939
    last_n = a[end-n+1:end]
5✔
1940
    resize!(a, length(a) - n)
10✔
1941
    return a, last_n
5✔
1942
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc