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

JuliaLang / julia / #37997

29 Jan 2025 02:08AM UTC coverage: 17.283% (-68.7%) from 85.981%
#37997

push

local

web-flow
bpart: Start enforcing min_world for global variable definitions (#57150)

This is the analog of #57102 for global variables. Unlike for consants,
there is no automatic global backdate mechanism. The reasoning for this
is that global variables can be declared at any time, unlike constants
which can only be decalared once their value is available. As a result
code patterns using `Core.eval` to declare globals are rarer and likely
incorrect.

1 of 22 new or added lines in 3 files covered. (4.55%)

31430 existing lines in 188 files now uncovered.

7903 of 45728 relevant lines covered (17.28%)

98663.7 hits per line

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

12.57
/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
29
        n = 1
122✔
30
        i = 1
122✔
31
        for d in dims
35,527✔
32
            d >= 0 || throw(ArgumentError("dimension size must be ≥ 0, got $d for dimension $i"))
44,003✔
33
            n *= d
44,003✔
34
            i += 1
16,952✔
35
        end
52,479✔
36
        nc = num_bit_chunks(n)
35,527✔
37
        chunks = Vector{UInt64}(undef, nc)
35,735✔
38
        nc > 0 && (chunks[end] = UInt64(0))
35,527✔
39
        b = new(chunks, n)
35,735✔
40
        N != 1 && (b.dims = dims)
8,476✔
41
        return b
35,527✔
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
"""
UNCOV
69
BitArray(::UndefInitializer, dims::Integer...) = BitArray(undef, map(Int,dims))
×
UNCOV
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)...)
19,344✔
UNCOV
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)
208✔
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
"""
UNCOV
96
function BitVector(nt::Tuple{Vararg{Bool}})
×
UNCOV
97
    bv = BitVector(undef, length(nt))
×
UNCOV
98
    bv .= nt
×
99
end
100

101
## utility functions ##
102

103
length(B::BitArray) = B.len
601,718✔
104
size(B::BitVector) = (B.len,)
229,872✔
105
size(B::BitArray) = B.dims
834,645✔
106

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

UNCOV
112
isassigned(B::BitArray, i::Int) = 1 <= i <= length(B)
×
113

UNCOV
114
IndexStyle(::Type{<:BitArray}) = IndexLinear()
×
115

116
## aux functions ##
117

118
const _msk64 = ~UInt64(0)
119
@inline _div64(l) = l >> 6
3,835,648✔
120
@inline _mod64(l) = l & 63
3,806,872✔
121
@inline _blsr(x)= x & (x-1) #zeros the last set bit. Has native instruction on many archs. needed in multidimensional.jl
68,788✔
122
@inline _msk_end(l::Int) = _msk64 >>> _mod64(-l)
6,751✔
123
@inline _msk_end(B::BitArray) = _msk_end(length(B))
6,751✔
124
num_bit_chunks(n::Int) = _div64(n+63)
35,527✔
125

126
@inline get_chunks_id(i::Int) = _div64(i-1)+1, _mod64(i-1)
3,236,790✔
127

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

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

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

UNCOV
150
    delta_kd = kd1 - kd0
×
UNCOV
151
    delta_ks = ks1 - ks0
×
152

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

UNCOV
166
    chunk_s0 = glue_src_bitchunks(src, ks0, ks1, msk_s0, ls0)
×
167

UNCOV
168
    dest[kd0] = (dest[kd0] & msk_d0) | ((chunk_s0 << ld0) & ~msk_d0)
×
169

UNCOV
170
    delta_kd == 0 && return
×
171

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

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

UNCOV
177
        dest[kd0 + i] = chunk_s
×
178

UNCOV
179
        chunk_s0 = chunk_s1
×
UNCOV
180
    end
×
181

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

UNCOV
188
    chunk_s = (chunk_s0 >>> (64 - ld0)) | (chunk_s1 << ld0)
×
189

UNCOV
190
    dest[kd1] = (dest[kd1] & msk_d1) | (chunk_s & ~msk_d1)
×
191

UNCOV
192
    return
×
193
end
194

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

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

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

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

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

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

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

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

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

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

UNCOV
270
copy_to_bitarray_chunks!(dest::Vector{UInt64}, pos_d::Int, src::BitArray, pos_s::Int, numbits::Int) =
×
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
UNCOV
275
function pack8bools(z::UInt64)
×
UNCOV
276
    z |= z >>> 7
×
UNCOV
277
    z |= z >>> 14
×
UNCOV
278
    z |= z >>> 28
×
UNCOV
279
    z &= 0xFF
×
280
    return z
×
281
end
282

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

UNCOV
287
    delta_kd = kd1 - kd0
×
288

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

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

UNCOV
311
    nc = _div64(numbits - ind + pos_s)
×
UNCOV
312
    nc8 = (nc >>> 3) << 3
×
UNCOV
313
    if nc8 > 0
×
UNCOV
314
        ind8 = 1
×
UNCOV
315
        P8 = Ptr{UInt64}(pointer(C, ind)) # unaligned i64 pointer
×
UNCOV
316
        @inbounds for i = 1:nc8
×
UNCOV
317
            c = UInt64(0)
×
UNCOV
318
            for j = 0:7
×
319
                # unaligned load
UNCOV
320
                c |= (pack8bools(unsafe_load(P8, ind8)) << (j<<3))
×
UNCOV
321
                ind8 += 1
×
UNCOV
322
            end
×
UNCOV
323
            Bc[bind] = c
×
UNCOV
324
            bind += 1
×
UNCOV
325
        end
×
UNCOV
326
        ind += (ind8-1) << 3
×
327
    end
UNCOV
328
    @inbounds for i = (nc8+1):nc
×
UNCOV
329
        c = UInt64(0)
×
UNCOV
330
        for j = 0:63
×
UNCOV
331
            c |= (UInt64(C[ind]) << j)
×
UNCOV
332
            ind += 1
×
UNCOV
333
        end
×
UNCOV
334
        Bc[bind] = c
×
UNCOV
335
        bind += 1
×
UNCOV
336
    end
×
UNCOV
337
    @inbounds if bind ≤ kd1
×
UNCOV
338
        @assert bind == kd1
×
UNCOV
339
        c = UInt64(0)
×
UNCOV
340
        for j = 0:ld1
×
UNCOV
341
            c |= (UInt64(C[ind]) << j)
×
UNCOV
342
            ind += 1
×
UNCOV
343
        end
×
UNCOV
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}) =
86✔
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)
362
    i >= length(B) && return nothing
579,447✔
363
    (B.chunks[_div64(i)+1] & (UInt64(1)<<_mod64(i)) != 0, i+1)
567,715✔
364
end
365

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

368
similar(B::BitArray) = BitArray(undef, size(B))
6,281✔
UNCOV
369
similar(B::BitArray, dims::Int...) = BitArray(undef, dims)
×
UNCOV
370
similar(B::BitArray, dims::Dims) = BitArray(undef, dims...)
×
371

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

UNCOV
377
function fill!(B::BitArray, x)
×
UNCOV
378
    y = convert(Bool, x)
×
UNCOV
379
    isempty(B) && return B
×
UNCOV
380
    Bc = B.chunks
×
UNCOV
381
    if !y
×
UNCOV
382
        fill!(Bc, 0)
×
383
    else
UNCOV
384
        fill!(Bc, _msk64)
×
UNCOV
385
        Bc[end] &= _msk_end(B)
×
386
    end
UNCOV
387
    return B
×
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)
3✔
UNCOV
404
falses(dims::NTuple{N, Union{Integer, OneTo}}) where {N} = falses(map(to_dim, dims))
×
405
falses(dims::NTuple{N, Integer}) where {N} = fill!(BitArray(undef, dims), false)
3✔
UNCOV
406
falses(dims::Tuple{}) = fill!(BitArray(undef, dims), false)
×
UNCOV
407
falses(dims::NTuple{N, DimOrInd}) where {N} = fill!(similar(BitArray, dims), false)
×
408

409
"""
410
    trues(dims)
411

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

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

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

UNCOV
438
function copyto!(dest::BitArray, src::BitArray)
×
UNCOV
439
    length(src) > length(dest) && throw(BoundsError(dest, length(dest)+1))
×
UNCOV
440
    destc = dest.chunks; srcc = src.chunks
×
UNCOV
441
    nc = min(length(destc), length(srcc))
×
UNCOV
442
    nc == 0 && return dest
×
UNCOV
443
    @inbounds begin
×
UNCOV
444
        for i = 1 : nc - 1
×
UNCOV
445
            destc[i] = srcc[i]
×
UNCOV
446
        end
×
UNCOV
447
        if length(src) == length(dest)
×
UNCOV
448
            destc[nc] = srcc[nc]
×
449
        else
UNCOV
450
            msk_s = _msk_end(src)
×
UNCOV
451
            msk_d = ~msk_s
×
UNCOV
452
            destc[nc] = (msk_d & destc[nc]) | (msk_s & srcc[nc])
×
453
        end
454
    end
UNCOV
455
    return dest
×
456
end
457

UNCOV
458
function unsafe_copyto!(dest::BitArray, doffs::Integer, src::Union{BitArray,Array}, soffs::Integer, n::Integer)
×
UNCOV
459
    copy_to_bitarray_chunks!(dest.chunks, Int(doffs), src, Int(soffs), Int(n))
×
UNCOV
460
    return dest
×
461
end
462

UNCOV
463
copyto!(dest::BitArray, doffs::Integer, src::Union{BitArray,Array}, soffs::Integer, n::Integer) =
×
464
    _copyto_int!(dest, Int(doffs), src, Int(soffs), Int(n))
UNCOV
465
function _copyto_int!(dest::BitArray, doffs::Int, src::Union{BitArray,Array}, soffs::Int, n::Int)
×
UNCOV
466
    n == 0 && return dest
×
UNCOV
467
    n < 0 && throw(ArgumentError("Number of elements to copy must be non-negative."))
×
UNCOV
468
    soffs < 1 && throw(BoundsError(src, soffs))
×
UNCOV
469
    doffs < 1 && throw(BoundsError(dest, doffs))
×
UNCOV
470
    soffs+n-1 > length(src) && throw(BoundsError(src, length(src)+1))
×
UNCOV
471
    doffs+n-1 > length(dest) && throw(BoundsError(dest, length(dest)+1))
×
UNCOV
472
    return unsafe_copyto!(dest, doffs, src, soffs, n)
×
473
end
474

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

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

495
## Constructors ##
496

UNCOV
497
function Array{T,N}(B::BitArray{N}) where {T,N}
×
UNCOV
498
    A = Array{T,N}(undef, size(B))
×
UNCOV
499
    Bc = B.chunks
×
UNCOV
500
    @inbounds for i = 1:length(A)
×
UNCOV
501
        A[i] = unsafe_bitgetindex(Bc, i)
×
UNCOV
502
    end
×
UNCOV
503
    return A
×
504
end
505

UNCOV
506
BitArray(A::AbstractArray{<:Any,N}) where {N} = BitArray{N}(A)
×
507

UNCOV
508
function BitArray{N}(A::AbstractArray{T,N}) where N where T
×
UNCOV
509
    B = BitArray(undef, convert(Dims{N}, size(A)::Dims{N}))
×
UNCOV
510
    _checkaxs(axes(B), axes(A))
×
UNCOV
511
    _copyto_bitarray!(B, A)
×
UNCOV
512
    return B::BitArray{N}
×
513
end
514

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

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

546
(::Type{T})(x::T) where {T<:BitArray} = copy(x)::T
122✔
UNCOV
547
BitArray(x::BitArray) = copy(x)
×
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
"""
UNCOV
577
BitArray(itr) = gen_bitarray(IteratorSize(itr), itr)
×
578
BitArray{N}(itr) where N = gen_bitarrayN(BitArray{N}, IteratorSize(itr), itr)
86✔
579

UNCOV
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)
UNCOV
584
gen_bitarray(isz::IteratorSize, itr) = gen_bitarray_from_itr(itr)
×
585

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

595
# generator with known shape or length
596
function gen_bitarray(::HasShape, itr::Generator)
597
    B = BitArray(undef, size(itr))
86✔
598
    return fill_bitarray_from_itr!(B, itr)
86✔
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

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

UNCOV
607
gen_bitarrayN(::Type{BitVector}, itsz, itr)                        = gen_bitarray(itsz, itr)
×
608
gen_bitarrayN(::Type{BitVector}, itsz::HasShape{1}, itr)           = gen_bitarray(itsz, itr)
86✔
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
UNCOV
611
gen_bitarrayN(::Type{BitVector}, itsz::HasShape{N}, itr) where N      = throw(DimensionMismatch("cannot create a BitVector from a $N-dimensional iterator"))
×
UNCOV
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

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

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

670

671
## Indexing: getindex ##
672

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

680
@inline function getindex(B::BitArray, i::Int)
681
    @boundscheck checkbounds(B, i)
1,144,384✔
682
    unsafe_bitgetindex(B.chunks, i)
1,144,384✔
683
end
684

685
## Indexing: setindex! ##
686

687
@inline function unsafe_bitsetindex!(Bc::Array{UInt64}, x::Bool, i::Int)
688
    i1, i2 = get_chunks_id(i)
1,413,595✔
689
    _unsafe_bitsetindex!(Bc, x, i1, i2)
1,413,595✔
690
end
691

692
@inline function _unsafe_bitsetindex!(Bc::Array{UInt64}, x::Bool, i1::Int, i2::Int)
693
    u = UInt64(1) << i2
1,413,604✔
UNCOV
694
    @inbounds begin
×
695
        c = Bc[i1]
1,413,604✔
696
        Bc[i1] = ifelse(x, c | u, c & ~u)
1,413,604✔
697
    end
698
end
699

700
@inline function setindex!(B::BitArray, x, i::Int)
701
    @boundscheck checkbounds(B, i)
734,784✔
702
    unsafe_bitsetindex!(B.chunks, convert(Bool, x), i)
734,784✔
703
    return B
734,784✔
704
end
705

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

UNCOV
709
@propagate_inbounds function setindex!(B::BitArray, X::AbstractArray, J0::Union{Colon,AbstractUnitRange{Int}})
×
UNCOV
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%
UNCOV
715
@inline function setindex!(B::BitArray, X::AbstractArray, I::BitArray)
×
UNCOV
716
    @boundscheck checkbounds(B, I)
×
UNCOV
717
    _unsafe_setindex!(B, X, I)
×
718
end
UNCOV
719
function _unsafe_setindex!(B::BitArray, X::AbstractArray, I::BitArray)
×
UNCOV
720
    Bc = B.chunks
×
UNCOV
721
    Ic = I.chunks
×
UNCOV
722
    length(Bc) == length(Ic) || throw_boundserror(B, I)
×
UNCOV
723
    lc = length(Bc)
×
UNCOV
724
    lx = length(X)
×
UNCOV
725
    last_chunk_len = _mod64(length(B)-1)+1
×
726

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

750
## Dequeue functionality ##
751

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

UNCOV
756
    Bc = B.chunks
×
757

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

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

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

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

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

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

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

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

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

UNCOV
843
    return item
×
844
end
845

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

UNCOV
849
    Bc = B.chunks
×
850

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

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

UNCOV
873
        Bc = B.chunks
×
874

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

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

UNCOV
888
    return item
×
889
end
890

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

UNCOV
898
    Bc = B.chunks
×
899

UNCOV
900
    k, j = get_chunks_id(i)
×
901

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

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

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

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

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

UNCOV
927
    Bc = B.chunks
×
928

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

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

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

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

UNCOV
948
    B.len -= 1
×
949

UNCOV
950
    return B
×
951
end
952

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

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

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

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

UNCOV
973
    copy_chunks!(Bc, i_f, Bc, i_l+1, n-i_l)
×
974

UNCOV
975
    delta_k < 0 && _deleteend!(Bc, -delta_k)
×
976

UNCOV
977
    B.len = new_l
×
978

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

UNCOV
983
    return B
×
984
end
985

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

UNCOV
991
    Bc = B.chunks
×
992

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

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

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

UNCOV
1020
    B.len = new_l
×
1021

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

UNCOV
1026
    return B
×
1027
end
1028

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

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

UNCOV
1036
    Bc = B.chunks
×
1037

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

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

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

UNCOV
1061
    B.len = new_l
×
1062

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

UNCOV
1067
    return B
×
1068
end
1069

UNCOV
1070
keepat!(B::BitVector, inds) = _keepat!(B, inds)
×
UNCOV
1071
keepat!(B::BitVector, inds::AbstractVector{Bool}) = _keepat!(B, inds)
×
1072

UNCOV
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!
UNCOV
1077
    i isa Bool && depwarn("passing Bool as an index is deprecated", :splice!)
×
UNCOV
1078
    i = Int(i)
×
UNCOV
1079
    n = length(B)
×
UNCOV
1080
    1 <= i <= n || throw(BoundsError(B, i))
×
1081

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

1087
const _default_bit_splice = BitVector()
1088

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

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

UNCOV
1100
    Bins = convert(BitArray, ins)
×
1101

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

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

UNCOV
1109
    Bc = B.chunks
×
1110

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

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

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

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

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

UNCOV
1124
    B.len = new_l
×
1125

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

UNCOV
1130
    return v
×
1131
end
1132

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

1143

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

1150
## Unary operators ##
1151

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

1181
## Binary arithmetic operators ##
1182

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

1199
for f in (:/, :\)
1200
    @eval begin
UNCOV
1201
        ($f)(A::Union{BitMatrix,BitVector}, B::Union{BitMatrix,BitVector}) = ($f)(Array(A), Array(B))
×
1202
    end
1203
end
UNCOV
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)
1214
    size(A) != size(B) && return false
39,748✔
1215
    return A.chunks == B.chunks
19,874✔
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])
×
UNCOV
1224
function _reverse(A::BitArray, d::Int)
×
UNCOV
1225
    nd = ndims(A)
×
UNCOV
1226
    1 ≤ d ≤ nd || throw(ArgumentError("dimension $d is not 1 ≤ $d ≤ $nd"))
×
UNCOV
1227
    sd = size(A, d)
×
UNCOV
1228
    sd == 1 && return copy(A)
×
1229

UNCOV
1230
    B = similar(A)
×
1231

UNCOV
1232
    nnd = 0
×
UNCOV
1233
    for i = 1:nd
×
UNCOV
1234
        nnd += size(A,i)==1 || i==d
×
UNCOV
1235
    end
×
UNCOV
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

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

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

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

UNCOV
1332
function (>>>)(B::BitVector, i::UInt)
×
UNCOV
1333
    n = length(B)
×
UNCOV
1334
    i == 0 && return copy(B)
×
UNCOV
1335
    A = falses(n)
×
UNCOV
1336
    i < n && copy_chunks!(A.chunks, Int(i+1), B.chunks, 1, Int(n-i))
×
UNCOV
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
"""
UNCOV
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
"""
UNCOV
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
"""
UNCOV
1421
(>>>)(B::BitVector, i::Int) = (i >=0 ? B >> unsigned(i) : B << unsigned(-i))
×
1422

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

UNCOV
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}
8,319✔
UNCOV
1447
    n::T = init
×
1448
    @inbounds for i = 1:length(Bc)
8,214✔
1449
        n = (n + count_ones(Bc[i])) % T
8,319✔
1450
    end
8,424✔
1451
    return n
8,214✔
1452
end
1453

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

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

UNCOV
1461
    @inbounds begin
×
1462
        if Bc[chunk_start] & mask != 0
37✔
1463
            return (chunk_start-1) << 6 + trailing_zeros(Bc[chunk_start] & mask) + 1
37✔
1464
        end
1465

UNCOV
1466
        for i = chunk_start+1:length(Bc)
×
UNCOV
1467
            if Bc[i] != 0
×
UNCOV
1468
                return (i-1) << 6 + trailing_zeros(Bc[i]) + 1
×
1469
            end
UNCOV
1470
        end
×
1471
    end
UNCOV
1472
    return nothing
×
1473
end
1474

1475
# returns the index of the next true element, or nothing if all false
1476
function findnext(B::BitArray, start::Integer)
UNCOV
1477
    start = Int(start)
×
UNCOV
1478
    start > 0 || throw(BoundsError(B, start))
×
1479
    start > length(B) && return nothing
37✔
1480
    unsafe_bitfindnext(B.chunks, start)
37✔
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
UNCOV
1486
function findnextnot(B::BitArray, start::Int)
×
UNCOV
1487
    start > 0 || throw(BoundsError(B, start))
×
UNCOV
1488
    start > length(B) && return nothing
×
1489

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

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

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

1517
# returns the index of the first matching element
UNCOV
1518
function findnext(pred::Fix2{<:Union{typeof(isequal),typeof(==)},Bool},
×
1519
                  B::BitArray, start::Integer)
UNCOV
1520
    v = pred.x
×
UNCOV
1521
    v == false && return findnextnot(B, Int(start))
×
UNCOV
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
UNCOV
1528
findnext(testf::Function, B::BitArray, start::Integer) = _findnext_int(testf, B, Int(start))
×
UNCOV
1529
function _findnext_int(testf::Function, B::BitArray, start::Int)
×
UNCOV
1530
    f0::Bool = testf(false)
×
UNCOV
1531
    f1::Bool = testf(true)
×
UNCOV
1532
    !f0 && f1 && return findnext(B, start)
×
UNCOV
1533
    f0 && !f1 && return findnextnot(B, start)
×
1534

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

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

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

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

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

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

UNCOV
1573
    Bc = B.chunks
×
1574

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

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

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

1593
# returns the index of the previous matching element
UNCOV
1594
function findprev(pred::Fix2{<:Union{typeof(isequal),typeof(==)},Bool},
×
1595
                  B::BitArray, start::Integer)
UNCOV
1596
    v = pred.x
×
UNCOV
1597
    v == false && return findprevnot(B, Int(start))
×
UNCOV
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
UNCOV
1604
findprev(testf::Function, B::BitArray, start::Integer) = _findprev_int(testf, B, Int(start))
×
1605
function _findprev_int(testf::Function, B::BitArray, start::Int)
UNCOV
1606
    f0::Bool = testf(false)
×
UNCOV
1607
    f1::Bool = testf(true)
×
UNCOV
1608
    !f0 && f1 && return findprev(B, start)
×
UNCOV
1609
    f0 && !f1 && return findprevnot(B, start)
×
1610

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

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

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

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

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

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

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

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

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

UNCOV
1704
        tz = trailing_zeros(c)
×
UNCOV
1705
        c = _blsr(c)
×
1706

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

1714
# For performance
UNCOV
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✔
1723
    isempty(B) && return true
1,202✔
1724
    Bc = B.chunks
1,202✔
1725
    @inbounds begin
1✔
1726
        for i = 1:length(Bc)-1
1,202✔
1727
            Bc[i] == _msk64 || return false
163✔
1728
        end
35✔
1729
        Bc[end] == _msk_end(B) || return false
1,617✔
1730
    end
1731
    return true
659✔
1732
end
1733

1734
function any(B::BitArray)
1735
    isempty(B) && return false
5,642✔
1736
    Bc = B.chunks
5,642✔
UNCOV
1737
    @inbounds begin
×
1738
        for i = 1:length(Bc)
5,642✔
1739
            Bc[i] == 0 || return true
11,281✔
1740
        end
3✔
1741
    end
1742
    return false
3✔
1743
end
1744

UNCOV
1745
minimum(B::BitArray) = isempty(B) ? throw(ArgumentError("argument must be non-empty")) : all(B)
×
UNCOV
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

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

UNCOV
1759
map!(::Union{typeof(~), typeof(!)}, dest::BitArray, A::BitArray) = bit_map!(~, dest, A)
×
UNCOV
1760
map!(::typeof(zero), dest::BitArray, A::BitArray) = fill!(dest, false)
×
UNCOV
1761
map!(::typeof(one), dest::BitArray, A::BitArray) = fill!(dest, true)
×
UNCOV
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),
UNCOV
1769
               (:(Union{typeof(>=), typeof(^)}),             :((p, q) -> p | ~q)),
×
UNCOV
1770
               (:(typeof(<=)),                               :((p, q) -> ~p | q)),
×
UNCOV
1771
               (:(typeof(==)),                               :((p, q) -> ~xor(p, q))),
×
UNCOV
1772
               (:(typeof(<)),                                :((p, q) -> ~p & q)),
×
UNCOV
1773
               (:(typeof(>)),                                :((p, q) -> p & ~q)))
×
UNCOV
1774
    @eval map(::$T, A::BitArray, B::BitArray) = bit_map!($f, similar(A), A, B)
×
UNCOV
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.
UNCOV
1781
function bit_map!(f::F, dest::BitArray, A::BitArray) where F
×
UNCOV
1782
    length(A) <= length(dest) || throw(DimensionMismatch("length of destination must be >= length of collection"))
×
UNCOV
1783
    isempty(A) && return dest
×
UNCOV
1784
    destc = dest.chunks
×
UNCOV
1785
    Ac = A.chunks
×
UNCOV
1786
    len_Ac = length(Ac)
×
UNCOV
1787
    for i = 1:(len_Ac-1)
×
UNCOV
1788
        destc[i] = f(Ac[i])
×
UNCOV
1789
    end
×
1790
    # the last effected UInt64's original content
UNCOV
1791
    dest_last = destc[len_Ac]
×
UNCOV
1792
    _msk = _msk_end(A)
×
1793
    # first zero out the bits mask is going to change
1794
    # then update bits by `or`ing with a masked RHS
1795
    # DO NOT SEPARATE ONTO TO LINES.
1796
    # Otherwise there will be bugs when Ac aliases destc
UNCOV
1797
    destc[len_Ac] = (dest_last & (~_msk)) | f(Ac[len_Ac]) & _msk
×
UNCOV
1798
    dest
×
1799
end
UNCOV
1800
function bit_map!(f::F, dest::BitArray, A::BitArray, B::BitArray) where F
×
UNCOV
1801
    min_bitlen = min(length(A), length(B))
×
UNCOV
1802
    min_bitlen <= length(dest) || throw(DimensionMismatch("length of destination must be >= length of smallest input collection"))
×
UNCOV
1803
    isempty(A) && return dest
×
UNCOV
1804
    isempty(B) && return dest
×
UNCOV
1805
    destc = dest.chunks
×
UNCOV
1806
    Ac = A.chunks
×
UNCOV
1807
    Bc = B.chunks
×
UNCOV
1808
    len_Ac = min(length(Ac), length(Bc))
×
UNCOV
1809
    for i = 1:len_Ac-1
×
UNCOV
1810
        destc[i] = f(Ac[i], Bc[i])
×
UNCOV
1811
    end
×
1812
    # the last effected UInt64's original content
UNCOV
1813
    dest_last = destc[len_Ac]
×
UNCOV
1814
    _msk = _msk_end(min_bitlen)
×
1815
    # first zero out the bits mask is going to change
1816
    # then update bits by `or`ing with a masked RHS
1817
    # DO NOT SEPARATE ONTO TO LINES.
1818
    # Otherwise there will be bugs when Ac or Bc aliases destc
UNCOV
1819
    destc[len_Ac] = (dest_last & ~(_msk)) | f(Ac[end], Bc[end]) & _msk
×
UNCOV
1820
    dest
×
1821
end
1822

1823
## Filter ##
1824

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

1830

1831
## Concatenation ##
1832

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

1846
function vcat(V::BitVector...)
208✔
1847
    n = 0
208✔
1848
    for Vk in V
208✔
1849
        n += length(Vk)
1,212✔
1850
    end
1,402✔
1851
    B = BitVector(undef, n)
208✔
1852
    j = 1
208✔
1853
    for Vk in V
208✔
1854
        copy_chunks!(B.chunks, j, Vk.chunks, 1, length(Vk))
1,212✔
1855
        j += length(Vk)
1,212✔
1856
    end
1,402✔
1857
    return B
208✔
1858
end
1859

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

UNCOV
1873
    B = BitMatrix(undef, nrows, ncols)
×
1874

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

1885
function vcat(A::BitMatrix...)
122✔
1886
    nargs = length(A)
122✔
1887
    nrows, nrowsA = 0, sizehint!(Int[], nargs)
122✔
1888
    for a in A
122✔
1889
        sz1 = size(a, 1)
1,260✔
1890
        nrows += sz1
1,260✔
1891
        push!(nrowsA, sz1)
1,260✔
1892
    end
1,381✔
1893
    ncols = size(A[1], 2)
122✔
1894
    for j = 2:nargs
122✔
1895
        size(A[j], 2) == ncols ||
1,138✔
1896
        throw(DimensionMismatch("column lengths must match: $j-th element has second dim $(size(A[j], 2)), should have $ncols"))
1897
    end
2,155✔
1898
    B = BitMatrix(undef, nrows, ncols)
122✔
1899
    Bc = B.chunks
122✔
1900
    pos_d = 1
122✔
1901
    pos_s = fill(1, nargs)
1,260✔
1902
    for j = 1:ncols, k = 1:nargs
122✔
1903
        copy_chunks!(Bc, pos_d, A[k].chunks, pos_s[k], nrowsA[k])
21,698✔
1904
        pos_s[k] += nrowsA[k]
21,698✔
1905
        pos_d += nrowsA[k]
21,698✔
1906
    end
23,111✔
1907
    return B
122✔
1908
end
1909

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

1920
# hvcat -> use fallbacks in abstractarray.jl
1921

1922

1923
# BitArray I/O
1924

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

1937
sizeof(B::BitArray) = sizeof(B.chunks)
1938

1939
function _split_rest(a::Union{Vector, BitVector}, n::Int)
1940
    _check_length_split_rest(length(a), n)
1941
    last_n = a[end-n+1:end]
1942
    resize!(a, length(a) - n)
1943
    return a, last_n
1944
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

© 2026 Coveralls, Inc