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

JuliaLang / julia / #37770

05 May 2024 01:26AM UTC coverage: 85.802% (-1.6%) from 87.442%
#37770

push

local

web-flow
A better mechanism for coordinating internal breaking changes. (#53849)

This was origiginally supposed to be an issue, but I just started
writing out the whole code in the issue text to explain what I want all
the behavior to be, so instead, here's the actual implementation of it,
with the motativation in the commit message, and the details of the
actual behavior in the code change ;)

Sometimes packages rely on Julia internals. This is in general
discouraged, but of course for some packages, there isn't really any
other option. If your packages needs to hook the julia internals in a
deep way or is specifically about introspecting the way that julia
itself works, then some amount of reliance on internals is inevitable.
In general, we're happy to let people touch the internals, as long as
they (and their users) are aware that things will break and it's on them
to fix things.

That said, I think we've been a little bit too *caveat emptor* on this
entire business. There's a number of really key packages that rely on
internals (I'm thinking in particular of Revise, Cthulhu and its
dependency stacks) that if they're broken, it's really hard to even
develop julia itself. In particular, these packages have been broken on
Julia master for a more than a week now (following #52415) and there has
been much frustration.

I think one of the biggest issues is that we're generally relying on
`VERSION` checks for these kinds of things. This isn't really a problem
when updating a package between released major versions, but for closely
coupled packages like the above you run into two problems:

1. Since the VERSION number of a package is not known ahead of time,
some breaking changes cannot be made atomically, i.e. we need to merge
the base PR (which bumps people's nightly) in order to get the version
number, which we then need to plug into the various PRs in all the
various packages. If something goes wrong in this process (as it did... (continued)

0 of 3 new or added lines in 1 file covered. (0.0%)

1453 existing lines in 67 files now uncovered.

74896 of 87289 relevant lines covered (85.8%)

14448147.81 hits per line

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

93.8
/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,992✔
29
        n = 1
11,150✔
30
        i = 1
11,150✔
31
        for d in dims
4,427,131✔
32
            d >= 0 || throw(ArgumentError("dimension size must be ≥ 0, got $d for dimension $i"))
4,438,599✔
33
            n *= d
4,438,599✔
34
            i += 1
26,812✔
35
        end
4,449,573✔
36
        nc = num_bit_chunks(n)
4,427,131✔
37
        chunks = Vector{UInt64}(undef, nc)
8,842,106✔
38
        nc > 0 && (chunks[end] = UInt64(0))
4,427,131✔
39
        b = new(chunks, n)
4,427,394✔
40
        N != 1 && (b.dims = dims)
15,344✔
41
        return b
4,427,131✔
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}
1✔
71
BitArray(::UndefInitializer, dims::NTuple{N,Integer}) where {N} = BitArray{N}(undef, map(Int, dims)...)
4,332,032✔
72
BitArray{N}(::UndefInitializer, dims::NTuple{N,Integer}) where {N} = BitArray{N}(undef, map(Int, dims)...)
1✔
73

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

77
BitVector() = BitVector(undef, 0)
263✔
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}})
4✔
97
    bv = BitVector(undef, length(nt))
4✔
98
    bv .= nt
4✔
99
end
100

101
## utility functions ##
102

103
length(B::BitArray) = B.len
75,672,808✔
104
size(B::BitVector) = (B.len,)
76,435,523✔
105
size(B::BitArray) = B.dims
4,545,737✔
106

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

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

114
IndexStyle(::Type{<:BitArray}) = IndexLinear()
3,779,192✔
115

116
## aux functions ##
117

118
const _msk64 = ~UInt64(0)
119
@inline _div64(l) = l >> 6
1,257,841,822✔
120
@inline _mod64(l) = l & 63
1,081,456,812✔
121
@inline _blsr(x)= x & (x-1) #zeros the last set bit. Has native instruction on many archs. needed in multidimensional.jl
212,869,955✔
122
@inline _msk_end(l::Int) = _msk64 >>> _mod64(-l)
729,533✔
123
@inline _msk_end(B::BitArray) = _msk_end(length(B))
211,881✔
124
num_bit_chunks(n::Int) = _div64(n+63)
4,895,671✔
125

126
@inline get_chunks_id(i::Int) = _div64(i-1)+1, _mod64(i-1)
182,180,451✔
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)
1,617,681✔
131
        if ks1 > k && ls0 > 0
1,617,681✔
132
            chunk_n = (src[k + 1] & ~msk_s0)
462,773✔
133
            chunk |= (chunk_n << (64 - ls0))
462,773✔
134
        end
135
    end
136
    return chunk
1,617,681✔
137
end
138

139
function copy_chunks!(dest::Vector{UInt64}, pos_d::Int, src::Vector{UInt64}, pos_s::Int, numbits::Int)
1,182,160✔
140
    numbits == 0 && return
1,182,160✔
141
    if dest === src && pos_d > pos_s
1,141,439✔
142
        return copy_chunks_rtol!(dest, pos_d, pos_s, numbits)
30,319✔
143
    end
144

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

150
    delta_kd = kd1 - kd0
1,111,120✔
151
    delta_ks = ks1 - ks0
1,111,120✔
152

153
    u = _msk64
×
154
    if delta_kd == 0
1,111,120✔
155
        msk_d0 = ~(u << ld0) | (u << (ld1+1))
830,511✔
156
    else
157
        msk_d0 = ~(u << ld0)
280,609✔
158
        msk_d1 = (u << (ld1+1))
280,609✔
159
    end
160
    if delta_ks == 0
1,111,120✔
161
        msk_s0 = (u << ls0) & ~(u << (ls1+1))
814,586✔
162
    else
163
        msk_s0 = (u << ls0)
296,534✔
164
    end
165

166
    chunk_s0 = glue_src_bitchunks(src, ks0, ks1, msk_s0, ls0)
1,111,120✔
167

168
    dest[kd0] = (dest[kd0] & msk_d0) | ((chunk_s0 << ld0) & ~msk_d0)
1,111,120✔
169

170
    delta_kd == 0 && return
1,111,120✔
171

172
    for i = 1 : kd1 - kd0 - 1
280,609✔
173
        chunk_s1 = glue_src_bitchunks(src, ks0 + i, ks1, msk_s0, ls0)
262,588✔
174

175
        chunk_s = (chunk_s0 >>> (64 - ld0)) | (chunk_s1 << ld0)
262,588✔
176

177
        dest[kd0 + i] = chunk_s
262,588✔
178

179
        chunk_s0 = chunk_s1
×
180
    end
385,279✔
181

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

188
    chunk_s = (chunk_s0 >>> (64 - ld0)) | (chunk_s1 << ld0)
280,609✔
189

190
    dest[kd1] = (dest[kd1] & msk_d1) | (chunk_s & ~msk_d1)
280,609✔
191

192
    return
280,609✔
193
end
194

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

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

211
        delta_kd = kd1 - kd0
67,567✔
212
        delta_ks = ks1 - ks0
67,567✔
213

214
        if delta_kd == 0
67,567✔
215
            msk_d0 = ~(u << ld0) | (u << (ld1+1))
16,916✔
216
        else
217
            msk_d0 = ~(u << ld0)
50,651✔
218
            msk_d1 = (u << (ld1+1))
50,651✔
219
        end
220
        if delta_ks == 0
67,567✔
221
            msk_s0 = (u << ls0) & ~(u << (ls1+1))
6,986✔
222
        else
223
            msk_s0 = (u << ls0)
60,581✔
224
        end
225

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

229
        if delta_kd != 0
67,567✔
230
            chunk_s = (chunk_s0 >>> (64 - ld0))
50,651✔
231

232
            chunks[kd1] = (chunks[kd1] & msk_d1) | (chunk_s & ~msk_d1)
50,651✔
233
        end
234

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

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

248
    u = _msk64
×
249
    if k1 == k0
2,061✔
250
        msk0 = (u << l0) & ~(u << (l1+1))
1,958✔
251
    else
252
        msk0 = (u << l0)
103✔
253
        msk1 = ~(u << (l1+1))
103✔
254
    end
255
    @inbounds if x
2,061✔
256
        Bc[k0] |= msk0
1,755✔
257
        for k = k0+1:k1-1
3,477✔
258
            Bc[k] = u
146✔
259
        end
259✔
260
        k1 > k0 && (Bc[k1] |= msk1)
1,755✔
261
    else
262
        Bc[k0] &= ~msk0
306✔
263
        for k = k0+1:k1-1
582✔
264
            Bc[k] = 0
148✔
265
        end
266✔
266
        k1 > k0 && (Bc[k1] &= ~msk1)
2,367✔
267
    end
268
end
269

270
copy_to_bitarray_chunks!(dest::Vector{UInt64}, pos_d::Int, src::BitArray, pos_s::Int, numbits::Int) =
27,585✔
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
66,496✔
277
    z |= z >>> 14
66,496✔
278
    z |= z >>> 28
66,496✔
279
    z &= 0xFF
66,496✔
280
    return z
66,496✔
281
end
282

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

287
    delta_kd = kd1 - kd0
114,630✔
288

289
    u = _msk64
114,541✔
290
    if delta_kd == 0
114,630✔
291
        msk_d0 = msk_d1 = ~(u << ld0) | (u << (ld1+1))
16,579✔
292
        lt0 = ld1
16,495✔
293
    else
294
        msk_d0 = ~(u << ld0)
98,051✔
295
        msk_d1 = (u << (ld1+1))
98,051✔
296
        lt0 = 63
98,046✔
297
    end
298

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

311
    nc = _div64(numbits - ind + pos_s)
114,630✔
312
    nc8 = (nc >>> 3) << 3
114,630✔
313
    if nc8 > 0
114,630✔
314
        ind8 = 1
1,018✔
315
        P8 = Ptr{UInt64}(pointer(C, ind)) # unaligned i64 pointer
1,018✔
316
        @inbounds for i = 1:nc8
1,018✔
317
            c = UInt64(0)
8,312✔
318
            for j = 0:7
8,312✔
319
                # unaligned load
320
                c |= (pack8bools(unsafe_load(P8, ind8)) << (j<<3))
66,496✔
321
                ind8 += 1
66,496✔
322
            end
124,680✔
323
            Bc[bind] = c
8,312✔
324
            bind += 1
8,312✔
325
        end
15,606✔
326
        ind += (ind8-1) << 3
1,018✔
327
    end
328
    @inbounds for i = (nc8+1):nc
130,347✔
329
        c = UInt64(0)
234,378✔
330
        for j = 0:63
240,394✔
331
            c |= (UInt64(C[ind]) << j)
15,006,208✔
332
            ind += 1
15,006,208✔
333
        end
29,777,944✔
334
        Bc[bind] = c
234,472✔
335
        bind += 1
234,472✔
336
    end
370,031✔
337
    @inbounds if bind ≤ kd1
114,630✔
338
        @assert bind == kd1
13,200✔
339
        c = UInt64(0)
13,200✔
340
        for j = 0:ld1
13,200✔
341
            c |= (UInt64(C[ind]) << j)
458,453✔
342
            ind += 1
458,453✔
343
        end
903,706✔
344
        Bc[kd1] = (Bc[kd1] & msk_d1) | (c & ~msk_d1)
13,200✔
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}) =
90,810✔
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)
4,998✔
362
    i >= length(B) && return nothing
64,443,403✔
363
    (B.chunks[_div64(i)+1] & (UInt64(1)<<_mod64(i)) != 0, i+1)
63,945,740✔
364
end
365

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

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

372
similar(B::BitArray, T::Type{Bool}, dims::Dims) = BitArray(undef, dims)
86,110✔
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)
24,209✔
376

377
function fill!(B::BitArray, x)
3,922,116✔
378
    y = convert(Bool, x)
50,985✔
379
    isempty(B) && return B
3,922,116✔
380
    Bc = B.chunks
3,921,999✔
381
    if !y
3,921,999✔
382
        fill!(Bc, 0)
4,928,568✔
383
    else
384
        fill!(Bc, _msk64)
11,766✔
385
        Bc[end] &= _msk_end(B)
1,854✔
386
    end
387
    return B
3,921,999✔
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,817,572✔
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)
3,818,341✔
406
falses(dims::Tuple{}) = fill!(BitArray(undef, dims), false)
10✔
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)
2,005✔
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)
2,017✔
425
trues(dims::Tuple{}) = fill!(BitArray(undef, dims), true)
6✔
UNCOV
426
trues(dims::NTuple{N, DimOrInd}) where {N} = fill!(similar(BitArray, dims), true)
×
427

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

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

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

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

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

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

495
## Constructors ##
496

497
function Array{T,N}(B::BitArray{N}) where {T,N}
3,995✔
498
    A = Array{T,N}(undef, size(B))
548,293✔
499
    Bc = B.chunks
275,636✔
500
    @inbounds for i = 1:length(A)
275,636✔
501
        A[i] = unsafe_bitgetindex(Bc, i)
97,487,159✔
502
    end
194,701,641✔
503
    return A
275,636✔
504
end
505

506
BitArray(A::AbstractArray{<:Any,N}) where {N} = BitArray{N}(A)
14,687✔
507

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

515
function _copyto_bitarray!(B::BitArray, A::AbstractArray)
20,967✔
516
    l = length(A)
20,970✔
517
    l == 0 && return B
20,970✔
518
    l > length(B) && throw(BoundsError(B, length(B)+1))
19,401✔
519
    Bc = B.chunks
19,401✔
520
    nc = num_bit_chunks(l)
19,401✔
521
    Ai = first(eachindex(A))
11,542✔
522
    @inbounds begin
11,542✔
523
        for i = 1:nc-1
19,401✔
524
            c = UInt64(0)
14,391✔
525
            for j = 0:63
183,671✔
526
                c |= (UInt64(convert(Bool, A[Ai])::Bool) << j)
1,543,042✔
527
                Ai = nextind(A, Ai)
1,090,304✔
528
            end
2,163,572✔
529
            Bc[i] = c
17,036✔
530
        end
23,852✔
531
        c = UInt64(0)
19,401✔
532
        tail = _mod64(l - 1) + 1
19,401✔
533
        for j = 0:tail-1
19,401✔
534
            c |= (UInt64(convert(Bool, A[Ai])::Bool) << j)
837,845✔
535
            Ai = nextind(A, Ai)
619,505✔
536
        end
1,219,611✔
537
        msk = _msk_end(tail)
19,399✔
538
        Bc[nc] = (c & msk) | (Bc[nc] & ~msk)
19,399✔
539
    end
540
    return B
19,399✔
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
if nameof(@__MODULE__) === :Base  # avoid method overwrite
547
(::Type{T})(x::T) where {T<:BitArray} = copy(x)::T
88✔
548
BitArray(x::BitArray) = copy(x)
73✔
549
end
550

551
"""
552
    BitArray(itr)
553

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

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

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

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

582
convert(::Type{T}, a::AbstractArray) where {T<:BitArray} = a isa T ? a : T(a)::T
74,894✔
583

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

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

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

607
gen_bitarray(::IsInfinite, itr) =  throw(ArgumentError("infinite-size iterable used in BitArray constructor"))
1✔
608

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

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

620
function gen_bitarray_from_itr(itr)
4,178✔
621
    B = empty!(BitVector(undef, bitcache_size))
267,392✔
622
    C = Vector{Bool}(undef, bitcache_size)
4,178✔
623
    Bc = B.chunks
4,178✔
624
    ind = 1
4,178✔
625
    cind = 1
4,178✔
626
    y = iterate(itr)
7,834✔
627
    while y !== nothing
305,967✔
628
        x, st = y
301,789✔
629
        @inbounds C[ind] = x
301,789✔
630
        ind += 1
301,789✔
631
        if ind > bitcache_size
301,789✔
632
            resize!(B, length(B) + bitcache_size)
×
633
            dumpbitcache(Bc, cind, C)
×
634
            cind += bitcache_chunks
×
635
            ind = 1
×
636
        end
637
        y = iterate(itr, st)
599,583✔
638
    end
301,789✔
639
    if ind > 1
4,178✔
640
        @inbounds C[ind:bitcache_size] .= false
14,673,187✔
641
        resize!(B, length(B) + ind - 1)
3,656✔
642
        dumpbitcache(Bc, cind, C)
3,656✔
643
    end
644
    return B
4,178✔
645
end
646

647
function fill_bitarray_from_itr!(B::BitArray, itr)
87,676✔
648
    n = length(B)
87,587✔
649
    C = Vector{Bool}(undef, bitcache_size)
87,676✔
650
    Bc = B.chunks
87,676✔
651
    ind = 1
87,587✔
652
    cind = 1
87,587✔
653
    y = iterate(itr)
174,830✔
654
    while y !== nothing
8,579,527✔
655
        x, st = y
8,490,353✔
656
        @inbounds C[ind] = x
8,491,851✔
657
        ind += 1
8,491,851✔
658
        if ind > bitcache_size
8,491,851✔
659
            dumpbitcache(Bc, cind, C)
×
660
            cind += bitcache_chunks
×
661
            ind = 1
×
662
        end
663
        y = iterate(itr, st)
16,896,548✔
664
    end
8,491,851✔
665
    if ind > 1
87,676✔
666
        @inbounds C[ind:bitcache_size] .= false
348,490,933✔
667
        dumpbitcache(Bc, cind, C)
87,154✔
668
    end
669
    return B
87,676✔
670
end
671

672

673
## Indexing: getindex ##
674

675
@inline function unsafe_bitgetindex(Bc::Vector{UInt64}, i::Int)
676
    i1, i2 = get_chunks_id(i)
163,564,926✔
677
    u = UInt64(1) << i2
163,522,196✔
678
    @inbounds r = (Bc[i1] & u) != 0
163,564,926✔
679
    return r
163,564,926✔
680
end
681

682
@inline function getindex(B::BitArray, i::Int)
1,440✔
683
    @boundscheck checkbounds(B, i)
65,395,246✔
684
    unsafe_bitgetindex(B.chunks, i)
65,395,246✔
685
end
686

687
## Indexing: setindex! ##
688

689
@inline function unsafe_bitsetindex!(Bc::Array{UInt64}, x::Bool, i::Int)
690
    i1, i2 = get_chunks_id(i)
13,614,787✔
691
    _unsafe_bitsetindex!(Bc, x, i1, i2)
13,614,787✔
692
end
693

694
@inline function _unsafe_bitsetindex!(Bc::Array{UInt64}, x::Bool, i1::Int, i2::Int)
695
    u = UInt64(1) << i2
558,802,224✔
696
    @inbounds begin
10✔
697
        c = Bc[i1]
605,364,445✔
698
        Bc[i1] = ifelse(x, c | u, c & ~u)
605,364,444✔
699
    end
700
end
701

702
@inline function setindex!(B::BitArray, x, i::Int)
5,882✔
703
    @boundscheck checkbounds(B, i)
12,927,296✔
704
    unsafe_bitsetindex!(B.chunks, convert(Bool, x), i)
12,927,296✔
705
    return B
12,927,296✔
706
end
707

708
indexoffset(i) = first(i)-1
85,948✔
709
indexoffset(::Colon) = 0
×
710

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

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

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

752
## Dequeue functionality ##
753

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

758
    Bc = B.chunks
260✔
759

760
    l = _mod64(length(B))
260✔
761
    if l == 0
260✔
762
        _growend!(Bc, 1)
5✔
763
        Bc[end] = UInt64(0)
5✔
764
    end
765
    B.len += 1
260✔
766
    if item
260✔
767
        B[end] = true
133✔
768
    end
769
    return B
260✔
770
end
771

772
function append!(B::BitVector, items::BitVector)
2,088✔
773
    n0 = length(B)
12,564✔
774
    n1 = length(items)
12,564✔
775
    n1 == 0 && return B
12,564✔
776
    Bc = B.chunks
10,995✔
777
    k0 = length(Bc)
10,995✔
778
    k1 = num_bit_chunks(n0 + n1)
10,995✔
779
    if k1 > k0
10,995✔
780
        _growend!(Bc, k1 - k0)
9,423✔
781
        Bc[end] = UInt64(0)
9,423✔
782
    end
783
    B.len += n1
10,995✔
784
    copy_chunks!(Bc, n0+1, items.chunks, 1, n1)
10,995✔
785
    return B
10,995✔
786
end
787

788
append!(B::BitVector, items) = append!(B, BitVector(items))
10,440✔
789
append!(A::Vector{Bool}, items::BitVector) = append!(A, Array(items))
6,264✔
790

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

808
prepend!(B::BitVector, items) = prepend!(B, BitArray(items))
2,088✔
809
prepend!(A::Vector{Bool}, items::BitVector) = prepend!(A, Array(items))
2,088✔
810

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

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

836
function pop!(B::BitVector)
260✔
837
    isempty(B) && throw(ArgumentError("argument must not be empty"))
260✔
838
    item = B[end]
260✔
839
    B[end] = false
260✔
840

841
    l = _mod64(length(B))
260✔
842
    l == 1 && _deleteend!(B.chunks, 1)
260✔
843
    B.len -= 1
260✔
844

845
    return item
260✔
846
end
847

848
function pushfirst!(B::BitVector, item)
260✔
849
    item = convert(Bool, item)
260✔
850

851
    Bc = B.chunks
260✔
852

853
    l = _mod64(length(B))
260✔
854
    if l == 0
260✔
855
        _growend!(Bc, 1)
5✔
856
        Bc[end] = UInt64(0)
5✔
857
    end
858
    B.len += 1
260✔
859
    if B.len == 1
260✔
860
        Bc[1] = item
1✔
861
        return B
1✔
862
    end
863
    for i = length(Bc) : -1 : 2
454✔
864
        Bc[i] = (Bc[i] << 1) | (Bc[i-1] >>> 63)
400✔
865
    end
604✔
866
    Bc[1] = UInt64(item) | (Bc[1] << 1)
259✔
867
    return B
259✔
868
end
869

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

875
        Bc = B.chunks
260✔
876

877
        for i = 1 : length(Bc) - 1
260✔
878
            Bc[i] = (Bc[i] >>> 1) | (Bc[i+1] << 63)
400✔
879
        end
604✔
880

881
        l = _mod64(length(B))
260✔
882
        if l == 1
260✔
883
            _deleteend!(Bc, 1)
5✔
884
        else
885
            Bc[end] >>>= 1
255✔
886
        end
887
        B.len -= 1
260✔
888
    end
889

890
    return item
260✔
891
end
892

893
insert!(B::BitVector, i::Integer, item) = _insert_int!(B, Int(i), item)
271✔
894
function _insert_int!(B::BitVector, i::Int, item)
271✔
895
    i = Int(i)
271✔
896
    n = length(B)
271✔
897
    1 <= i <= n+1 || throw(BoundsError(B, i))
273✔
898
    item = convert(Bool, item)
276✔
899

900
    Bc = B.chunks
269✔
901

902
    k, j = get_chunks_id(i)
269✔
903

904
    l = _mod64(length(B))
269✔
905
    if l == 0
269✔
906
        _growend!(Bc, 1)
5✔
907
        Bc[end] = UInt64(0)
5✔
908
    end
909
    B.len += 1
269✔
910

911
    for t = length(Bc) : -1 : k + 1
461✔
912
        Bc[t] = (Bc[t] << 1) | (Bc[t - 1] >>> 63)
258✔
913
    end
362✔
914

915
    msk_aft = (_msk64 << j)
269✔
916
    msk_bef = ~msk_aft
269✔
917
    Bc[k] = (msk_bef & Bc[k]) | ((msk_aft & Bc[k]) << 1)
269✔
918
    B[i] = item
269✔
919
    B
269✔
920
end
921

922
function _deleteat!(B::BitVector, i::Int)
538✔
923
    k, j = get_chunks_id(i)
538✔
924

925
    msk_bef = _msk64 >>> (63 - j)
538✔
926
    msk_aft = ~msk_bef
538✔
927
    msk_bef >>>= 1
538✔
928

929
    Bc = B.chunks
538✔
930

931
    @inbounds begin
538✔
932
        Bc[k] = (msk_bef & Bc[k]) | ((msk_aft & Bc[k]) >> 1)
538✔
933
        if length(Bc) > k
538✔
934
            Bc[k] |= (Bc[k + 1] << 63)
324✔
935
        end
936

937
        for t = k + 1 : length(Bc) - 1
931✔
938
            Bc[t] = (Bc[t] >>> 1) | (Bc[t + 1] << 63)
209✔
939
        end
273✔
940

941
        l = _mod64(length(B))
538✔
942

943
        if l == 1
538✔
944
            _deleteend!(Bc, 1)
12✔
945
        elseif length(Bc) > k
526✔
946
            Bc[end] >>>= 1
314✔
947
        end
948
    end
949

950
    B.len -= 1
538✔
951

952
    return B
538✔
953
end
954

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

961
    return _deleteat!(B, i)
269✔
962
end
963

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

971
    Bc = B.chunks
33,935✔
972
    new_l = length(B) - length(r)
33,935✔
973
    delta_k = num_bit_chunks(new_l) - length(Bc)
33,935✔
974

975
    copy_chunks!(Bc, i_f, Bc, i_l+1, n-i_l)
33,935✔
976

977
    delta_k < 0 && _deleteend!(Bc, -delta_k)
63,188✔
978

979
    B.len = new_l
33,935✔
980

981
    if new_l > 0
33,935✔
982
        Bc[end] &= _msk_end(new_l)
33,934✔
983
    end
984

985
    return B
33,935✔
986
end
987

988
function deleteat!(B::BitVector, inds)
33,683✔
989
    n = new_l = length(B)
33,683✔
990
    y = iterate(inds)
33,683✔
991
    y === nothing && return B
33,683✔
992

993
    Bc = B.chunks
33,541✔
994

995
    (p, s) = y
33,541✔
996
    checkbounds(B, p)
33,546✔
997
    p isa Bool && throw(ArgumentError("invalid index $p of type Bool"))
33,536✔
998
    q = p+1
33,535✔
999
    new_l -= 1
33,535✔
1000
    y = iterate(inds, s)
33,535✔
1001
    while y !== nothing
1,480,731✔
1002
        (i, s) = y
1,447,201✔
1003
        if !(q <= i <= n)
1,447,201✔
1004
            i isa Bool && throw(ArgumentError("invalid index $i of type Bool"))
5✔
1005
            i < q && throw(ArgumentError("indices must be unique and sorted"))
4✔
1006
            throw(BoundsError(B, i))
2✔
1007
        end
1008
        new_l -= 1
1,447,196✔
1009
        if i > q
1,447,196✔
1010
            copy_chunks!(Bc, Int(p), Bc, Int(q), Int(i-q))
715,769✔
1011
            p += i-q
715,769✔
1012
        end
1013
        q = i+1
1,447,196✔
1014
        y = iterate(inds, s)
1,447,196✔
1015
    end
1,447,196✔
1016

1017
    q <= n && copy_chunks!(Bc, Int(p), Bc, Int(q), Int(n-q+1))
33,531✔
1018

1019
    delta_k = num_bit_chunks(new_l) - length(Bc)
33,530✔
1020
    delta_k < 0 && _deleteend!(Bc, -delta_k)
41,502✔
1021

1022
    B.len = new_l
33,530✔
1023

1024
    if new_l > 0
33,530✔
1025
        Bc[end] &= _msk_end(new_l)
33,530✔
1026
    end
1027

1028
    return B
33,530✔
1029
end
1030

1031
function deleteat!(B::BitVector, inds::AbstractVector{Bool})
4,004✔
1032
    length(inds) == length(B) || throw(BoundsError(B, inds))
4,005✔
1033

1034
    n = new_l = length(B)
4,003✔
1035
    y = findfirst(inds)
9,740✔
1036
    y === nothing && return B
4,003✔
1037

1038
    Bc = B.chunks
3,809✔
1039

1040
    p = y
3,809✔
1041
    s = y + 1
3,809✔
1042
    checkbounds(B, p)
3,809✔
1043
    q = p + 1
3,809✔
1044
    new_l -= 1
3,809✔
1045
    y = findnext(inds, s)
9,004✔
1046
    while y !== nothing
21,127✔
1047
        i = y
17,318✔
1048
        s = y + 1
17,318✔
1049
        new_l -= 1
17,318✔
1050
        if i > q
17,318✔
1051
            copy_chunks!(Bc, Int(p), Bc, Int(q), Int(i-q))
7,726✔
1052
            p += i - q
7,726✔
1053
        end
1054
        q = i + 1
17,318✔
1055
        y = findnext(inds, s)
38,949✔
1056
    end
17,318✔
1057

1058
    q <= n && copy_chunks!(Bc, Int(p), Bc, Int(q), Int(n - q + 1))
3,809✔
1059

1060
    delta_k = num_bit_chunks(new_l) - length(Bc)
3,809✔
1061
    delta_k < 0 && _deleteend!(Bc, -delta_k)
3,809✔
1062

1063
    B.len = new_l
3,809✔
1064

1065
    if new_l > 0
3,809✔
1066
        Bc[end] &= _msk_end(new_l)
3,596✔
1067
    end
1068

1069
    return B
3,809✔
1070
end
1071

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

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

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

1089
const _default_bit_splice = BitVector()
1090

1091
function splice!(B::BitVector, r::Union{AbstractUnitRange{Int}, Integer}, ins::AbstractArray = _default_bit_splice)
62,154✔
1092
    r isa Bool && depwarn("passing Bool as an index is deprecated", :splice!)
108,636✔
1093
    _splice_int!(B, isa(r, AbstractUnitRange{Int}) ? r : Int(r), ins)
74,706✔
1094
end
1095

1096
function _splice_int!(B::BitVector, r, ins)
74,706✔
1097
    n = length(B)
74,706✔
1098
    i_f, i_l = first(r), last(r)
74,706✔
1099
    1 <= i_f <= n+1 || throw(BoundsError(B, i_f))
74,706✔
1100
    i_l <= n || throw(BoundsError(B, n+1))
74,706✔
1101

1102
    Bins = convert(BitArray, ins)
74,706✔
1103

1104
    if (i_f > n)
74,706✔
1105
        append!(B, Bins)
69✔
1106
        return BitVector()
36✔
1107
    end
1108

1109
    v = B[r]  # TODO: change to a copy if/when subscripting becomes an ArrayView
74,670✔
1110

1111
    Bc = B.chunks
74,670✔
1112

1113
    lins = length(Bins)
74,670✔
1114
    ldel = length(r)
74,670✔
1115

1116
    new_l = length(B) + lins - ldel
74,670✔
1117
    delta_k = num_bit_chunks(new_l) - length(Bc)
74,670✔
1118

1119
    delta_k > 0 && _growend!(Bc, delta_k)
74,670✔
1120

1121
    copy_chunks!(Bc, i_f+lins, Bc, i_l+1, n-i_l)
74,670✔
1122
    copy_chunks!(Bc, i_f, Bins.chunks, 1, lins)
74,670✔
1123

1124
    delta_k < 0 && _deleteend!(Bc, -delta_k)
113,684✔
1125

1126
    B.len = new_l
74,670✔
1127

1128
    if new_l > 0
74,670✔
1129
        Bc[end] &= _msk_end(new_l)
74,666✔
1130
    end
1131

1132
    return v
74,670✔
1133
end
1134

1135
function splice!(B::BitVector, r::Union{AbstractUnitRange{Int}, Integer}, ins)
12,552✔
1136
    Bins = BitVector(undef, length(ins))
12,552✔
1137
    i = 1
12,552✔
1138
    for x in ins
25,104✔
1139
        Bins[i] = Bool(x)
1,341,274✔
1140
        i += 1
1,341,274✔
1141
    end
2,682,548✔
1142
    return splice!(B, r, Bins)
12,552✔
1143
end
1144

1145

1146
function empty!(B::BitVector)
1✔
1147
    _deleteend!(B.chunks, length(B.chunks))
267,397✔
1148
    B.len = 0
4,179✔
1149
    return B
4,179✔
1150
end
1151

1152
## Unary operators ##
1153

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

1183
## Binary arithmetic operators ##
1184

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

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

1209
## promotion to complex ##
1210

1211
# TODO?
1212

1213
## comparison operators ##
1214

1215
function (==)(A::BitArray, B::BitArray)
4,026✔
1216
    size(A) != size(B) && return false
32,278✔
1217
    return A.chunks == B.chunks
16,140✔
1218
end
1219

1220

1221
## Data movement ##
1222

1223
# TODO some of this could be optimized
1224

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

1232
    B = similar(A)
4✔
1233

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

1246
    d_in = size(A)
4✔
1247
    leading = d_in[1:(d-1)]
7✔
1248
    M = prod(leading)
7✔
1249
    N = length(A)
4✔
1250
    stride = M * sd
4✔
1251

1252
    if M == 1
4✔
1253
        for j = 0:stride:(N-stride)
2✔
1254
            for i = 1:sd
168✔
1255
                ri = sd+1-i
840✔
1256
                B[j + ri] = A[j + i]
840✔
1257
            end
1,512✔
1258
        end
335✔
1259
    else
1260
        for i = 1:sd
3✔
1261
            ri = sd+1-i
18✔
1262
            for j=0:stride:(N-stride)
29✔
1263
                offs = j + 1 + (i-1)*M
196✔
1264
                boffs = j + 1 + (ri-1)*M
196✔
1265
                copy_chunks!(B.chunks, boffs, A.chunks, offs, M)
196✔
1266
            end
374✔
1267
        end
18✔
1268
    end
1269
    return B
4✔
1270
end
1271

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

1287
    n = length(B)
×
1288
    n == 0 && return B
×
1289

1290
    k = _mod64(n+63) + 1
×
1291
    h = 64 - k
×
1292

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

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

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

1323
    return B
×
1324
end
1325

1326
function (<<)(B::BitVector, i::UInt)
33,440✔
1327
    n = length(B)
33,440✔
1328
    i == 0 && return copy(B)
33,440✔
1329
    A = falses(n)
33,437✔
1330
    i < n && copy_chunks!(A.chunks, 1, B.chunks, Int(i+1), Int(n-i))
33,437✔
1331
    return A
33,437✔
1332
end
1333

1334
function (>>>)(B::BitVector, i::UInt)
67,370✔
1335
    n = length(B)
67,370✔
1336
    i == 0 && return copy(B)
67,370✔
1337
    A = falses(n)
67,365✔
1338
    i < n && copy_chunks!(A.chunks, Int(i+1), B.chunks, 1, Int(n-i))
67,365✔
1339
    return A
67,365✔
1340
end
1341

1342
"""
1343
    >>(B::BitVector, n) -> BitVector
1344

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

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

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

1368
julia> B >> -1
1369
5-element BitVector:
1370
 0
1371
 1
1372
 0
1373
 0
1374
 0
1375
```
1376
"""
1377
(>>)(B::BitVector, i::Union{Int, UInt}) = B >>> i
134,720✔
1378

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

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

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

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

1406
julia> B << -1
1407
5-element BitVector:
1408
 0
1409
 1
1410
 0
1411
 1
1412
 0
1413
```
1414
"""
1415
(<<)(B::BitVector, i::Int) = (i >=0 ? B << unsigned(i) : B >> unsigned(-i))
33,440✔
1416

1417
"""
1418
    >>>(B::BitVector, n) -> BitVector
1419

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

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

1444
circshift!(B::BitVector, i::Integer) = circshift!(B, B, i)
16✔
1445

1446
## count & find ##
1447

1448
function bitcount(Bc::Vector{UInt64}; init::T=0) where {T}
52,312,463✔
1449
    n::T = init
50,701✔
1450
    @inbounds for i = 1:length(Bc)
45,154,837✔
1451
        n = (n + count_ones(Bc[i])) % T
37,215,053✔
1452
    end
44,374,243✔
1453
    return n
45,154,837✔
1454
end
1455

1456
_count(::typeof(identity), B::BitArray, ::Colon, init) = bitcount(B.chunks; init)
148,566✔
1457

1458
function unsafe_bitfindnext(Bc::Vector{UInt64}, start::Int)
1459
    chunk_start = _div64(start-1)+1
2,230,205✔
1460
    within_chunk_start = _mod64(start-1)
2,230,205✔
1461
    mask = _msk64 << within_chunk_start
2,230,205✔
1462

1463
    @inbounds begin
155,702✔
1464
        if Bc[chunk_start] & mask != 0
43,640,875✔
1465
            return (chunk_start-1) << 6 + trailing_zeros(Bc[chunk_start] & mask) + 1
35,172,374✔
1466
        end
1467

1468
        for i = chunk_start+1:length(Bc)
8,479,480✔
1469
            if Bc[i] != 0
11,192,816✔
1470
                return (i-1) << 6 + trailing_zeros(Bc[i]) + 1
2,270,170✔
1471
            end
1472
        end
17,801,608✔
1473
    end
1474
    return nothing
6,198,331✔
1475
end
1476

1477
# returns the index of the next true element, or nothing if all false
1478
function findnext(B::BitArray, start::Integer)
41,739✔
1479
    start = Int(start)
60,940✔
1480
    start > 0 || throw(BoundsError(B, start))
60,938✔
1481
    start > length(B) && return nothing
60,971✔
1482
    unsafe_bitfindnext(B.chunks, start)
59,958✔
1483
end
1484

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

1487
# aux function: same as findnext(~B, start), but performed without temporaries
1488
function findnextnot(B::BitArray, start::Int)
55,903✔
1489
    start > 0 || throw(BoundsError(B, start))
55,905✔
1490
    start > length(B) && return nothing
55,901✔
1491

1492
    Bc = B.chunks
55,893✔
1493
    l = length(Bc)
55,893✔
1494
    l == 0 && return nothing
55,893✔
1495

1496
    chunk_start = _div64(start-1)+1
55,893✔
1497
    within_chunk_start = _mod64(start-1)
55,893✔
1498
    mask = ~(_msk64 << within_chunk_start)
55,893✔
1499

1500
    @inbounds if chunk_start < l
55,893✔
1501
        if Bc[chunk_start] | mask != _msk64
55,467✔
1502
            return (chunk_start-1) << 6 + trailing_ones(Bc[chunk_start] | mask) + 1
25,275✔
1503
        end
1504
        for i = chunk_start+1:l-1
30,705✔
1505
            if Bc[i] != _msk64
46,997✔
1506
                return (i-1) << 6 + trailing_ones(Bc[i]) + 1
28,645✔
1507
            end
1508
        end
35,670✔
1509
        if Bc[l] != _msk_end(B)
1,547✔
1510
            return (l-1) << 6 + trailing_ones(Bc[l]) + 1
1,153✔
1511
        end
1512
    elseif Bc[l] | mask != _msk_end(B)
426✔
1513
        return (l-1) << 6 + trailing_ones(Bc[l] | mask) + 1
295✔
1514
    end
1515
    return nothing
525✔
1516
end
1517
findfirstnot(B::BitArray) = findnextnot(B,1)
261✔
1518

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

1529
# returns the index of the first element for which the function returns true
1530
findnext(testf::Function, B::BitArray, start::Integer) = _findnext_int(testf, B, Int(start))
13,826✔
1531
function _findnext_int(testf::Function, B::BitArray, start::Int)
13,819✔
1532
    f0::Bool = testf(false)
13,827✔
1533
    f1::Bool = testf(true)
13,827✔
1534
    !f0 && f1 && return findnext(B, start)
13,827✔
1535
    f0 && !f1 && return findnextnot(B, start)
12,259✔
1536

1537
    start > 0 || throw(BoundsError(B, start))
2,364✔
1538
    start > length(B) && return nothing
2,356✔
1539
    f0 && f1 && return start
2,342✔
1540
    return nothing # last case: !f0 && !f1
1,561✔
1541
end
1542
#findfirst(testf::Function, B::BitArray) = findnext(testf, B, 1)  ## defined in array.jl
1543

1544
function unsafe_bitfindprev(Bc::Vector{UInt64}, start::Int)
41,155✔
1545
    chunk_start = _div64(start-1)+1
41,155✔
1546
    mask = _msk_end(start)
41,155✔
1547

1548
    @inbounds begin
39,378✔
1549
        if Bc[chunk_start] & mask != 0
41,155✔
1550
            return (chunk_start-1) << 6 + (top_set_bit(Bc[chunk_start] & mask))
38,952✔
1551
        end
1552

1553
        for i = (chunk_start-1):-1:1
4,326✔
1554
            if Bc[i] != 0
2,211✔
1555
                return (i-1) << 6 + (top_set_bit(Bc[i]))
2,191✔
1556
            end
1557
        end
35✔
1558
    end
1559
    return nothing
12✔
1560
end
1561

1562
# returns the index of the previous true element, or nothing if all false
1563
function findprev(B::BitArray, start::Integer)
8,588✔
1564
    start = Int(start)
13,166✔
1565
    start > 0 || return nothing
13,408✔
1566
    start > length(B) && throw(BoundsError(B, start))
13,406✔
1567
    unsafe_bitfindprev(B.chunks, start)
13,404✔
1568
end
1569

1570
function findprevnot(B::BitArray, start::Int)
21,227✔
1571
    start = Int(start)
21,227✔
1572
    start > 0 || return nothing
21,229✔
1573
    start > length(B) && throw(BoundsError(B, start))
21,225✔
1574

1575
    Bc = B.chunks
21,223✔
1576

1577
    chunk_start = _div64(start-1)+1
21,223✔
1578
    mask = ~_msk_end(start)
21,223✔
1579

1580
    @inbounds begin
21,223✔
1581
        if Bc[chunk_start] | mask != _msk64
21,223✔
1582
            return (chunk_start-1) << 6 + (64 - leading_ones(Bc[chunk_start] | mask))
17,008✔
1583
        end
1584

1585
        for i = chunk_start-1:-1:1
8,420✔
1586
            if Bc[i] != _msk64
4,235✔
1587
                return (i-1) << 6 + (64 - leading_ones(Bc[i]))
4,202✔
1588
            end
1589
        end
60✔
1590
    end
1591
    return nothing
13✔
1592
end
1593
findlastnot(B::BitArray) = findprevnot(B, length(B))
1✔
1594

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

1605
# returns the index of the previous element for which the function returns true
1606
findprev(testf::Function, B::BitArray, start::Integer) = _findprev_int(testf, B, Int(start))
8,866✔
1607
function _findprev_int(testf::Function, B::BitArray, start::Int)
8,846✔
1608
    f0::Bool = testf(false)
8,866✔
1609
    f1::Bool = testf(true)
8,866✔
1610
    !f0 && f1 && return findprev(B, start)
8,866✔
1611
    f0 && !f1 && return findprevnot(B, start)
8,604✔
1612

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

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

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

1650
# findall helper functions
1651
# Generic case (>2 dimensions)
1652
function allindices!(I, B::BitArray)
1653
    ind = first(keys(B))
7✔
1654
    for k = 1:length(B)
7✔
1655
        I[k] = ind
505✔
1656
        ind = nextind(B, ind)
757✔
1657
    end
1,003✔
1658
end
1659

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

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

1674
@inline _overflowind(i1, irest::Tuple{}, size) = (i1, irest)
5,653✔
1675
@inline function _overflowind(i1, irest, size)
1676
    i2 = irest[1]
3,800✔
1677
    while i1 > size[1]
5,869✔
1678
        i1 -= size[1]
2,069✔
1679
        i2 += 1
2,069✔
1680
    end
2,069✔
1681
    i2, irest = _overflowind(i2, tail(irest), tail(size))
3,800✔
1682
    return (i1, (i2, irest...))
3,800✔
1683
end
1684

1685
@inline _toind(i1, irest::Tuple{}) = i1
164,795✔
1686
@inline _toind(i1, irest) = CartesianIndex(i1, irest...)
1,313✔
1687

1688
function findall(B::BitArray)
4,144✔
1689
    nnzB = count(B)
9,543✔
1690
    I = Vector{eltype(keys(B))}(undef, nnzB)
7,967✔
1691
    nnzB == 0 && return I
4,144✔
1692
    nnzB == length(B) && (allindices!(I, B); return I)
3,823✔
1693
    Bc = B.chunks
2,471✔
1694
    Bs = size(B)
2,394✔
1695
    Bi = i1 = i = 1
2,394✔
1696
    irest = ntuple(one, ndims(B) - 1)
2,394✔
1697
    c = Bc[1]
2,471✔
1698
    @inbounds while true
3,484✔
1699
        while c == 0
171,012✔
1700
            Bi == length(Bc) && return I
6,204✔
1701
            i1 += 64
3,733✔
1702
            Bi += 1
3,733✔
1703
            c = Bc[Bi]
3,733✔
1704
        end
3,733✔
1705

1706
        tz = trailing_zeros(c)
167,121✔
1707
        c = _blsr(c)
167,121✔
1708

1709
        i1, irest = _overflowind(i1 + tz, irest, Bs)
167,121✔
1710
        I[i] = _toind(i1, irest)
167,121✔
1711
        i += 1
167,121✔
1712
        i1 -= tz
167,121✔
1713
    end
167,121✔
1714
end
1715

1716
# For performance
1717
findall(::typeof(!iszero), B::BitArray) = findall(B)
1✔
1718

1719
## Reductions ##
1720

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

1724
function all(B::BitArray)
516✔
1725
    isempty(B) && return true
2,168✔
1726
    Bc = B.chunks
2,165✔
1727
    @inbounds begin
980✔
1728
        for i = 1:length(Bc)-1
2,165✔
1729
            Bc[i] == _msk64 || return false
2,642✔
1730
        end
4,733✔
1731
        Bc[end] == _msk_end(B) || return false
2,596✔
1732
    end
1733
    return true
1,602✔
1734
end
1735

1736
function any(B::BitArray)
4,559✔
1737
    isempty(B) && return false
3,545,456✔
1738
    Bc = B.chunks
3,545,455✔
1739
    @inbounds begin
5,961✔
1740
        for i = 1:length(Bc)
3,545,455✔
1741
            Bc[i] == 0 || return true
7,067,608✔
1742
        end
24,112✔
1743
    end
1744
    return false
23,572✔
1745
end
1746

1747
minimum(B::BitArray) = isempty(B) ? throw(ArgumentError("argument must be non-empty")) : all(B)
1✔
1748
maximum(B::BitArray) = isempty(B) ? throw(ArgumentError("argument must be non-empty")) : any(B)
1✔
1749

1750
## map over bitarrays ##
1751

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

1756
map(::Union{typeof(~), typeof(!)}, A::BitArray) = bit_map!(~, similar(A), A)
380✔
1757
map(::typeof(zero), A::BitArray) = fill!(similar(A), false)
14✔
1758
map(::typeof(one), A::BitArray) = fill!(similar(A), true)
14✔
1759
map(::typeof(identity), A::BitArray) = copy(A)
14✔
1760

1761
map!(::Union{typeof(~), typeof(!)}, dest::BitArray, A::BitArray) = bit_map!(~, dest, A)
393✔
1762
map!(::typeof(zero), dest::BitArray, A::BitArray) = fill!(dest, false)
14✔
1763
map!(::typeof(one), dest::BitArray, A::BitArray) = fill!(dest, true)
14✔
1764
map!(::typeof(identity), dest::BitArray, A::BitArray) = copyto!(dest, A)
14✔
1765

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

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

1825
## Filter ##
1826

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

1832

1833
## Concatenation ##
1834

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

1848
function vcat(V::BitVector...)
515✔
1849
    n = 0
515✔
1850
    for Vk in V
515✔
1851
        n += length(Vk)
1,902✔
1852
    end
2,417✔
1853
    B = BitVector(undef, n)
515✔
1854
    j = 1
515✔
1855
    for Vk in V
515✔
1856
        copy_chunks!(B.chunks, j, Vk.chunks, 1, length(Vk))
1,902✔
1857
        j += length(Vk)
1,902✔
1858
    end
2,417✔
1859
    return B
515✔
1860
end
1861

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

1875
    B = BitMatrix(undef, nrows, ncols)
1✔
1876

1877
    pos = 1
1✔
1878
    for k = 1:nargs
1✔
1879
        Ak = A[k]
3✔
1880
        n = length(Ak)
4✔
1881
        copy_chunks!(B.chunks, pos, Ak.chunks, 1, n)
4✔
1882
        pos += n
3✔
1883
    end
5✔
1884
    return B
1✔
1885
end
1886

1887
function vcat(A::BitMatrix...)
89✔
1888
    nargs = length(A)
89✔
1889
    nrows, nrowsA = 0, sizehint!(Int[], nargs)
89✔
1890
    for a in A
89✔
1891
        sz1 = size(a, 1)
553✔
1892
        nrows += sz1
553✔
1893
        push!(nrowsA, sz1)
553✔
1894
    end
642✔
1895
    ncols = size(A[1], 2)
89✔
1896
    for j = 2:nargs
89✔
1897
        size(A[j], 2) == ncols ||
465✔
1898
        throw(DimensionMismatch("column lengths must match: $j-th element has second dim $(size(A[j], 2)), should have $ncols"))
1899
    end
838✔
1900
    B = BitMatrix(undef, nrows, ncols)
88✔
1901
    Bc = B.chunks
88✔
1902
    pos_d = 1
88✔
1903
    pos_s = fill(1, nargs)
550✔
1904
    for j = 1:ncols, k = 1:nargs
88✔
1905
        copy_chunks!(Bc, pos_d, A[k].chunks, pos_s[k], nrowsA[k])
12,217✔
1906
        pos_s[k] += nrowsA[k]
12,217✔
1907
        pos_d += nrowsA[k]
12,217✔
1908
    end
13,473✔
1909
    return B
88✔
1910
end
1911

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

1922
# hvcat -> use fallbacks in abstractarray.jl
1923

1924

1925
# BitArray I/O
1926

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

1939
sizeof(B::BitArray) = sizeof(B.chunks)
2✔
1940

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