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

JuliaLang / julia / #38011

17 Feb 2025 06:24AM UTC coverage: 20.248% (-5.6%) from 25.839%
#38011

push

local

web-flow
bpart: Track whether any binding replacement has happened in image modules (#57433)

This implements the optimization proposed in #57426 by keeping track of
whether any bindings were replaced in image modules (excluding `Main` as
facilitated by #57426). In addition, we augment serialization to keep
track of whether a method body contains any GlobalRefs that point to a
loaded (system or package) image. If both of these flags are true, we
can skip scanning the body of the method, since we know that we neither
need to add any additional backedges nor were any of the referenced
bindings invalidated. The performance impact on end-to-end load time is
small, but measurable. Overall `@time using ModelingToolkit`
consistently improves about 5% using this PR. However, I should note
that using time is still about 40% slower than 1.11. This is not
necessarily an Apples-to-Apples comparison as there were substantial
other changes on 1.12 (as well as current load-time-tunings targeting
older versions), but I wanted to put the number context.

2 of 15 new or added lines in 5 files covered. (13.33%)

2655 existing lines in 108 files now uncovered.

9867 of 48731 relevant lines covered (20.25%)

107722.08 hits per line

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

16.49
/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
37,054✔
32
            d >= 0 || throw(ArgumentError("dimension size must be ≥ 0, got $d for dimension $i"))
45,647✔
33
            n *= d
45,647✔
34
            i += 1
17,186✔
35
        end
54,240✔
36
        nc = num_bit_chunks(n)
37,054✔
37
        chunks = Vector{UInt64}(undef, nc)
37,272✔
38
        nc > 0 && (chunks[end] = UInt64(0))
37,054✔
39
        b = new(chunks, n)
37,272✔
40
        N != 1 && (b.dims = dims)
8,593✔
41
        return b
37,054✔
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))
×
70
BitArray{N}(::UndefInitializer, dims::Integer...) where {N} = BitArray{N}(undef, map(Int,dims))::BitArray{N}
×
71
BitArray(::UndefInitializer, dims::NTuple{N,Integer}) where {N} = BitArray{N}(undef, map(Int, dims)...)
20,612✔
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)
218✔
78

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

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

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

101
## utility functions ##
102

103
length(B::BitArray) = B.len
609,147✔
104
size(B::BitVector) = (B.len,)
253,448✔
105
size(B::BitArray) = B.dims
846,873✔
106

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

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

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

116
## aux functions ##
117

118
const _msk64 = ~UInt64(0)
119
@inline _div64(l) = l >> 6
4,494,025✔
120
@inline _mod64(l) = l & 63
4,428,474✔
121
@inline _blsr(x)= x & (x-1) #zeros the last set bit. Has native instruction on many archs. needed in multidimensional.jl
184,297✔
122
@inline _msk_end(l::Int) = _msk64 >>> _mod64(-l)
6,975✔
123
@inline _msk_end(B::BitArray) = _msk_end(length(B))
6,871✔
124
num_bit_chunks(n::Int) = _div64(n+63)
37,056✔
125

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

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

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

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

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

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

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

170
    delta_kd == 0 && return
×
171

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

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

177
        dest[kd0 + i] = chunk_s
×
178

179
        chunk_s0 = chunk_s1
×
180
    end
×
181

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

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

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

192
    return
×
193
end
194

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

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

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

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

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

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

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

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

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

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
275
function pack8bools(z::UInt64)
×
276
    z |= z >>> 7
×
277
    z |= z >>> 14
×
278
    z |= z >>> 28
×
279
    z &= 0xFF
×
280
    return z
×
281
end
282

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

287
    delta_kd = kd1 - kd0
×
288

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

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

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

348
## More definitions in multidimensional.jl
349

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

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

356
dumpbitcache(Bc::Vector{UInt64}, bind::Int, C::Vector{Bool}) =
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
584,392✔
363
    (B.chunks[_div64(i)+1] & (UInt64(1)<<_mod64(i)) != 0, i+1)
572,502✔
364
end
365

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

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

372
similar(B::BitArray, T::Type{Bool}, dims::Dims) = BitArray(undef, dims)
11,546✔
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)
×
376

377
function fill!(B::BitArray, x)
986✔
378
    y = convert(Bool, x)
×
379
    isempty(B) && return B
986✔
380
    Bc = B.chunks
986✔
381
    if !y
986✔
382
        fill!(Bc, 0)
984✔
383
    else
384
        fill!(Bc, _msk64)
2✔
385
        Bc[end] &= _msk_end(B)
2✔
386
    end
387
    return B
986✔
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)
987✔
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)
987✔
406
falses(dims::Tuple{}) = fill!(BitArray(undef, dims), false)
×
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,348✔
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,348✔
425
trues(dims::Tuple{}) = fill!(BitArray(undef, dims), true)
×
426
trues(dims::NTuple{N, DimOrInd}) where {N} = fill!(similar(BitArray, dims), true)
×
427

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

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

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

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

481
function reshape(B::BitArray{N}, dims::NTuple{N,Int}) where N
×
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

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

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

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

515
function _copyto_bitarray!(B::BitArray, A::AbstractArray)
2✔
516
    l = length(A)
2✔
517
    l == 0 && return B
2✔
518
    l > length(B) && throw(BoundsError(B, length(B)+1))
2✔
519
    Bc = B.chunks
2✔
520
    nc = num_bit_chunks(l)
2✔
521
    Ai = first(eachindex(A))
×
522
    @inbounds begin
×
523
        for i = 1:nc-1
2✔
524
            c = UInt64(0)
×
525
            for j = 0:63
×
526
                c |= (UInt64(convert(Bool, A[Ai])::Bool) << j)
×
527
                Ai = nextind(A, Ai)
×
528
            end
×
529
            Bc[i] = c
×
530
        end
×
531
        c = UInt64(0)
2✔
532
        tail = _mod64(l - 1) + 1
2✔
533
        for j = 0:tail-1
2✔
534
            c |= (UInt64(convert(Bool, A[Ai])::Bool) << j)
76✔
535
            Ai = nextind(A, Ai)
76✔
536
        end
150✔
537
        msk = _msk_end(tail)
2✔
538
        Bc[nc] = (c & msk) | (Bc[nc] & ~msk)
2✔
539
    end
540
    return B
2✔
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✔
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
"""
577
BitArray(itr) = gen_bitarray(IteratorSize(itr), itr)
×
578
BitArray{N}(itr) where N = gen_bitarrayN(BitArray{N}, IteratorSize(itr), itr)
86✔
579

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

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

586
# generic iterable with known shape
587
function gen_bitarray(::HasShape, itr)
×
588
    B = BitArray(undef, size(itr))
×
589
    for (I,x) in zip(CartesianIndices(axes(itr)), itr)
×
590
        B[I] = x
×
591
    end
×
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

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

607
gen_bitarrayN(::Type{BitVector}, itsz, itr)                        = gen_bitarray(itsz, itr)
×
608
gen_bitarrayN(::Type{BitVector}, itsz::HasShape{1}, itr)           = gen_bitarray(itsz, itr)
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
611
gen_bitarrayN(::Type{BitVector}, itsz::HasShape{N}, itr) where N      = throw(DimensionMismatch("cannot create a BitVector from a $N-dimensional iterator"))
×
612
gen_bitarrayN(@nospecialize(T::Type), itsz::HasShape{N}, itr) where N = throw(DimensionMismatch("cannot create a $T from a $N-dimensional iterator"))
×
613
gen_bitarrayN(@nospecialize(T::Type), itsz, itr) = throw(DimensionMismatch("cannot create a $T from a generic iterator"))
×
614

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

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

645
function fill_bitarray_from_itr!(B::BitArray, itr)
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
666✔
653
        x, st = y
580✔
654
        @inbounds C[ind] = x
580✔
655
        ind += 1
580✔
656
        if ind > bitcache_size
580✔
657
            dumpbitcache(Bc, cind, C)
×
658
            cind += bitcache_chunks
×
659
            ind = 1
×
660
        end
661
        y = iterate(itr, st)
1,074✔
662
    end
580✔
663
    if ind > 1
86✔
664
        @inbounds C[ind:bitcache_size] .= false
351,676✔
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,872,832✔
675
    u = UInt64(1) << i2
1,872,832✔
676
    @inbounds r = (Bc[i1] & u) != 0
1,872,832✔
677
    return r
1,872,832✔
678
end
679

680
@inline function getindex(B::BitArray, i::Int)
681
    @boundscheck checkbounds(B, i)
1,182,944✔
682
    unsafe_bitgetindex(B.chunks, i)
1,182,944✔
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,438,543✔
689
    _unsafe_bitsetindex!(Bc, x, i1, i2)
1,438,543✔
690
end
691

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

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

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

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

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

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

750
## Dequeue functionality ##
751

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

756
    Bc = B.chunks
×
757

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

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

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

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

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

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

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

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

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

843
    return item
×
844
end
845

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

849
    Bc = B.chunks
×
850

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

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

873
        Bc = B.chunks
×
874

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

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

888
    return item
×
889
end
890

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

898
    Bc = B.chunks
×
899

900
    k, j = get_chunks_id(i)
×
901

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

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

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

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

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

927
    Bc = B.chunks
×
928

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

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

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

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

948
    B.len -= 1
×
949

950
    return B
×
951
end
952

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

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

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

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

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

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

977
    B.len = new_l
×
978

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

983
    return B
×
984
end
985

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

991
    Bc = B.chunks
×
992

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

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

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

1020
    B.len = new_l
×
1021

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

1026
    return B
×
1027
end
1028

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

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

1036
    Bc = B.chunks
×
1037

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

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

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

1061
    B.len = new_l
×
1062

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

1067
    return B
×
1068
end
1069

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

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

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

1087
const _default_bit_splice = BitVector()
1088

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

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

1100
    Bins = convert(BitArray, ins)
×
1101

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

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

1109
    Bc = B.chunks
×
1110

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

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

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

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

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

1124
    B.len = new_l
×
1125

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

1130
    return v
×
1131
end
1132

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

1143

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

1150
## Unary operators ##
1151

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

1181
## Binary arithmetic operators ##
1182

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

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

1207
## promotion to complex ##
1208

1209
# TODO?
1210

1211
## comparison operators ##
1212

1213
function (==)(A::BitArray, B::BitArray)
1214
    size(A) != size(B) && return false
40,258✔
1215
    return A.chunks == B.chunks
20,129✔
1216
end
1217

1218

1219
## Data movement ##
1220

1221
# TODO some of this could be optimized
1222

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

1230
    B = similar(A)
×
1231

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

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

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

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

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

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

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

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

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

1321
    return B
×
1322
end
1323

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1444
## count & find ##
1445

1446
function bitcount(Bc::Vector{UInt64}; init::T=0) where {T}
26,148✔
1447
    n::T = init
×
1448
    @inbounds for i = 1:length(Bc)
24,287✔
1449
        n = (n + count_ones(Bc[i])) % T
22,086✔
1450
    end
23,947✔
1451
    return n
24,287✔
1452
end
1453

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

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

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

1466
        for i = chunk_start+1:length(Bc)
4,075✔
1467
            if Bc[i] != 0
4,435✔
1468
                return (i-1) << 6 + trailing_zeros(Bc[i]) + 1
2,579✔
1469
            end
1470
        end
3,688✔
1471
    end
1472
    return nothing
1,427✔
1473
end
1474

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

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

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

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

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

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

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

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

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

1551
        for i = (chunk_start-1):-1:1
103✔
1552
            if Bc[i] != 0
56✔
1553
                return (i-1) << 6 + (top_set_bit(Bc[i]))
56✔
1554
            end
1555
        end
×
1556
    end
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)
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

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

1573
    Bc = B.chunks
×
1574

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

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

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

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

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

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

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

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

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

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

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

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

1683
@inline _toind(i1, irest::Tuple{}) = i1
×
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
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,203✔
1724
    Bc = B.chunks
1,203✔
1725
    @inbounds begin
1✔
1726
        for i = 1:length(Bc)-1
1,203✔
1727
            Bc[i] == _msk64 || return false
163✔
1728
        end
35✔
1729
        Bc[end] == _msk_end(B) || return false
1,618✔
1730
    end
1731
    return true
660✔
1732
end
1733

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

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

1748
## map over bitarrays ##
1749

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

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

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

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

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

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

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

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

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

1875
    pos = 1
×
1876
    for k = 1:nargs
×
1877
        Ak = A[k]
×
1878
        n = length(Ak)
×
1879
        copy_chunks!(B.chunks, pos, Ak.chunks, 1, n)
×
1880
        pos += n
×
1881
    end
×
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,792✔
1904
        pos_s[k] += nrowsA[k]
21,792✔
1905
        pos_d += nrowsA[k]
21,792✔
1906
    end
23,215✔
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...)
×
1912
function _cat(dims::Int, X::Union{BitArray, Bool}...)
×
1913
    dims = Int(dims)
×
1914
    catdims = dims2cat(dims)
×
1915
    shape = cat_shape(catdims, map(cat_size, X))
×
1916
    A = falses(shape)
×
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