Skip to content

Commit 2fb9bad

Browse files
committed
bitarray indexing: sync with array + refactor
* Use setindex_shape_check and DimensionMismatch instead of generic errors; this (and a couple of extra minor fixes) makes BitArrays behave like Arrays (again) * Move portions of the indexing code to multidimensional.jl * Restrict signatures so as to avoid to_index and convert, then create very generic methods which do the conversion and the dispatch: this greatly simplifies the logic and removes most of the need for disambiguation. Should also improve code generation.
1 parent 793d769 commit 2fb9bad

File tree

3 files changed

+142
-285
lines changed

3 files changed

+142
-285
lines changed

base/bitarray.jl

+39-228
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,10 @@ falses(args...) = fill!(BitArray(args...), false)
217217
trues(args...) = fill!(BitArray(args...), true)
218218

219219
function one(x::BitMatrix)
220-
m, n = size(x)
221-
a = falses(size(x))
222-
for i = 1 : min(m,n)
220+
m,n = size(x)
221+
m == n || throw(DimensionMismatch("multiplicative identity defined only for square matrices"))
222+
a = falses(n, n)
223+
for i = 1:n
223224
a[i,i] = true
224225
end
225226
return a
@@ -258,7 +259,7 @@ end
258259

259260
function reshape{N}(B::BitArray, dims::NTuple{N,Int})
260261
if prod(dims) != length(B)
261-
error("new dimensions $(dims) inconsistent with the array length $(length(B))")
262+
throw(DimensionMismatch("new dimensions $(dims) must be consistent with array size $(length(B))"))
262263
end
263264
Br = BitArray{N}(ntuple(N,i->0)...)
264265
Br.chunks = B.chunks
@@ -320,18 +321,7 @@ end
320321
convert{N}(::Type{BitArray{N}}, B::BitArray{N}) = B
321322

322323
reinterpret{N}(::Type{Bool}, B::BitArray, dims::NTuple{N,Int}) = reinterpret(B, dims)
323-
function reinterpret{N}(B::BitArray, dims::NTuple{N,Int})
324-
if prod(dims) != length(B)
325-
error("new dimensions $(dims) are inconsistent with array length $(length(B))")
326-
end
327-
A = BitArray{N}(ntuple(N,i->0)...)
328-
A.chunks = B.chunks
329-
A.len = prod(dims)
330-
if N != 1
331-
A.dims = dims
332-
end
333-
return A
334-
end
324+
reinterpret{N}(B::BitArray, dims::NTuple{N,Int}) = reshape(B, dims)
335325

336326
# shorthand forms BitArray <-> Array
337327
bitunpack{N}(B::BitArray{N}) = convert(Array{Bool,N}, B)
@@ -359,9 +349,7 @@ function getindex_unchecked(Bc::Vector{Uint64}, i::Int)
359349
end
360350

361351
function getindex(B::BitArray, i::Int)
362-
if i < 1 || i > length(B)
363-
throw(BoundsError())
364-
end
352+
1 <= i <= length(B) || throw(BoundsError())
365353
return getindex_unchecked(B.chunks, i)
366354
end
367355

@@ -372,58 +360,6 @@ getindex(B::BitArray) = getindex(B, 1)
372360
# 0d bitarray
373361
getindex(B::BitArray{0}) = getindex_unchecked(B.chunks, 1)
374362

375-
function getindex(B::BitArray, i1::Real, i2::Real)
376-
#checkbounds(B, i0, i1) # manually inlined for performance
377-
i1, i2 = to_index(i1, i2)
378-
l1 = size(B,1)
379-
1 <= i1 <= l1 || throw(BoundsError())
380-
return B[i1 + l1*(i2-1)]
381-
end
382-
function getindex(B::BitArray, i1::Real, i2::Real, i3::Real)
383-
#checkbounds(B, i0, i1, i2) # manually inlined for performance
384-
i1, i2, i3 = to_index(i1, i2, i3)
385-
l1 = size(B,1)
386-
1 <= i1 <= l1 || throw(BoundsError())
387-
l2 = size(B,2)
388-
1 <= i2 <= l2 || throw(BoundsError())
389-
return B[i1 + l1*((i2-1) + l2*(i3-1))]
390-
end
391-
function getindex(B::BitArray, i1::Real, i2::Real, i3::Real, i4::Real)
392-
#checkbounds(B, i1, i2, i3, i4)
393-
i1, i2, i3, i4 = to_index(i1, i2, i3, i4)
394-
l1 = size(B,1)
395-
1 <= i1 <= l1 || throw(BoundsError())
396-
l2 = size(B,2)
397-
1 <= i2 <= l2 || throw(BoundsError())
398-
l3 = size(B,3)
399-
1 <= i3 <= l3 || throw(BoundsError())
400-
return B[i1 + l1*((i2-1) + l2*((i3-1) + l3*(i4-1)))]
401-
end
402-
403-
function getindex(B::BitArray, I::Real...)
404-
#checkbounds(B, I...) # inlined for performance
405-
#I = to_index(I) # inlined for performance
406-
ndims = length(I)
407-
i = to_index(I[1])
408-
l = size(B,1)
409-
1 <= i <= l || throw(BoundsError())
410-
index = i
411-
stride = 1
412-
for k = 2:ndims-1
413-
stride *= l
414-
i = to_index(I[k])
415-
l = size(B,k)
416-
1 <= i <= l || throw(BoundsError())
417-
index += (i-1) * stride
418-
end
419-
stride *= l
420-
i = to_index(I[ndims])
421-
index += (i-1) * stride
422-
return B[index]
423-
end
424-
425-
# note: the Range1{Int} case is still handled by the version above
426-
# (which is fine)
427363
function getindex{T<:Real}(B::BitArray, I::AbstractVector{T})
428364
X = BitArray(length(I))
429365
lB = length(B)
@@ -432,40 +368,34 @@ function getindex{T<:Real}(B::BitArray, I::AbstractVector{T})
432368
ind = 1
433369
for i in I
434370
# faster X[ind] = B[i]
435-
i = to_index(i)
436-
1 <= i <= lB || throw(BoundsError())
437-
setindex_unchecked(Xc, getindex_unchecked(Bc, i), ind)
371+
j = to_index(i)
372+
1 <= j <= lB || throw(BoundsError())
373+
setindex_unchecked(Xc, getindex_unchecked(Bc, j), ind)
438374
ind += 1
439375
end
440376
return X
441377
end
442378

443379
# logical indexing
444380

445-
function getindex_bool_1d(B::BitArray, I::AbstractArray{Bool})
446-
n = sum(I)
447-
X = BitArray(n)
448-
lI = length(I)
449-
if lI != length(B)
450-
throw(BoundsError())
451-
end
452-
Xc = X.chunks
453-
Bc = B.chunks
454-
ind = 1
455-
for i = 1:length(I)
456-
if I[i]
457-
# faster X[ind] = B[i]
458-
setindex_unchecked(Xc, getindex_unchecked(Bc, i), ind)
459-
ind += 1
381+
# (multiple signatures for disambiguation)
382+
for IT in [AbstractVector{Bool}, AbstractArray{Bool}]
383+
@eval function getindex(B::BitArray, I::$IT)
384+
checkbounds(B, I)
385+
n = sum(I)
386+
X = BitArray(n)
387+
Xc = X.chunks
388+
Bc = B.chunks
389+
ind = 1
390+
for i = 1:length(I)
391+
if I[i]
392+
# faster X[ind] = B[i]
393+
setindex_unchecked(Xc, getindex_unchecked(Bc, i), ind)
394+
ind += 1
395+
end
460396
end
397+
return X
461398
end
462-
return X
463-
end
464-
465-
# multiple signatures required for disambiguation
466-
# (see also getindex in multidimensional.jl)
467-
for BT in [BitVector, BitArray], IT in [Range1{Bool}, AbstractVector{Bool}, AbstractArray{Bool}]
468-
@eval getindex(B::$BT, I::$IT) = getindex_bool_1d(B, I)
469399
end
470400

471401
## Indexing: setindex! ##
@@ -482,133 +412,31 @@ function setindex_unchecked(Bc::Array{Uint64}, x::Bool, i::Int)
482412
end
483413
end
484414

415+
setindex!(B::BitArray, x::Bool) = setindex!(B, x, 1)
416+
485417
function setindex!(B::BitArray, x::Bool, i::Int)
486-
if i < 1 || i > length(B)
487-
throw(BoundsError())
488-
end
418+
1 <= i <= length(B) || throw(BoundsError())
489419
setindex_unchecked(B.chunks, x, i)
490420
return B
491421
end
492422

493-
setindex!(B::BitArray, x) = setindex!(B, x, 1)
494-
495-
setindex!(B::BitArray, x, i::Real) = setindex!(B, convert(Bool,x), to_index(i))
496-
497-
function setindex!(B::BitArray, x, i1::Real, i2::Real)
498-
#checkbounds(B, i0, i1) # manually inlined for performance
499-
i1, i2 = to_index(i1, i2)
500-
l1 = size(B,1)
501-
1 <= i1 <= l1 || throw(BoundsError())
502-
B[i1 + l1*(i2-1)] = x
503-
return B
504-
end
505-
506-
function setindex!(B::BitArray, x, i1::Real, i2::Real, i3::Real)
507-
#checkbounds(B, i1, i2, i3) # manually inlined for performance
508-
i1, i2, i3 = to_index(i1, i2, i3)
509-
l1 = size(B,1)
510-
1 <= i1 <= l1 || throw(BoundsError())
511-
l2 = size(B,2)
512-
1 <= i2 <= l2 || throw(BoundsError())
513-
B[i1 + l1*((i2-1) + l2*(i3-1))] = x
514-
return B
515-
end
516-
517-
function setindex!(B::BitArray, x, i1::Real, i2::Real, i3::Real, i4::Real)
518-
#checkbounds(B, i1, i2, i3, i4) # manually inlined for performance
519-
i1, i2, i3, i4 = to_index(i1, i2, i3, i4)
520-
l1 = size(B,1)
521-
1 <= i1 <= l1 || throw(BoundsError())
522-
l2 = size(B,2)
523-
1 <= i2 <= l2 || throw(BoundsError())
524-
l3 = size(B,3)
525-
1 <= i3 <= l3 || throw(BoundsError())
526-
B[i1 + l1*((i2-1) + l2*((i3-1) + l3*(i4-1)))] = x
527-
return B
528-
end
529-
530-
function setindex!(B::BitArray, x, i::Real, I::Real...)
531-
#checkbounds(B, I...) # inlined for performance
532-
#I = to_index(I) # inlined for performance
533-
ndims = length(I) + 1
534-
i = to_index(i)
535-
l = size(B,1)
536-
1 <= i <= l || throw(BoundsError())
537-
index = i
538-
stride = 1
539-
for k = 2:ndims-1
540-
stride *= l
541-
l = size(B,k)
542-
i = to_index(I[k-1])
543-
1 <= i <= l || throw(BoundsError())
544-
index += (i-1) * stride
545-
end
546-
stride *= l
547-
i = to_index(I[ndims-1])
548-
index += (i-1) * stride
549-
B[index] = x
550-
return B
551-
end
552-
553-
function setindex!{T<:Real}(B::BitArray, X::AbstractArray, I::AbstractVector{T})
554-
if length(X) != length(I); error("argument dimensions must match"); end
555-
count = 1
556-
for i in I
557-
B[i] = X[count]
558-
count += 1
559-
end
560-
return B
561-
end
562-
563-
function setindex!(B::BitArray, X::AbstractArray, i0::Real)
564-
if length(X) != 1
565-
error("argument dimensions must match")
566-
end
567-
return setindex!(B, X[1], i0)
568-
end
569-
570-
function setindex!(B::BitArray, X::AbstractArray, i0::Real, i1::Real)
571-
if length(X) != 1
572-
error("argument dimensions must match")
573-
end
574-
return setindex!(B, X[1], i0, i1)
575-
end
576-
577-
function setindex!(B::BitArray, X::AbstractArray, I0::Real, I::Real...)
578-
if length(X) != 1
579-
error("argument dimensions must match")
580-
end
581-
return setindex!(B, X[1], i0, I...)
582-
end
583-
584-
function setindex!{T<:Real}(B::BitArray, x, I::AbstractVector{T})
585-
x = convert(Bool, x)
586-
for i in I
587-
B[i] = x
588-
end
589-
return B
590-
end
591-
592423
# logical indexing
593424

594-
function setindex_bool_1d(A::BitArray, x, I::AbstractArray{Bool})
595-
if length(I) > length(A)
596-
throw(BoundsError())
597-
end
425+
function setindex!(A::BitArray, x, I::AbstractArray{Bool})
426+
checkbounds(A, I)
427+
y = convert(Bool, x)
598428
Ac = A.chunks
599429
for i = 1:length(I)
600430
if I[i]
601-
# faster A[i] = x
602-
setindex_unchecked(Ac, convert(Bool, x), i)
431+
# faster A[i] = y
432+
setindex_unchecked(Ac, y, i)
603433
end
604434
end
605435
A
606436
end
607437

608-
function setindex_bool_1d(A::BitArray, X::AbstractArray, I::AbstractArray{Bool})
609-
if length(I) > length(A)
610-
throw(BoundsError())
611-
end
438+
function setindex!(A::BitArray, X::AbstractArray, I::AbstractArray{Bool})
439+
checkbounds(A, I)
612440
Ac = A.chunks
613441
c = 1
614442
for i = 1:length(I)
@@ -618,27 +446,10 @@ function setindex_bool_1d(A::BitArray, X::AbstractArray, I::AbstractArray{Bool})
618446
c += 1
619447
end
620448
end
621-
A
622-
end
623-
624-
# lots of definitions here are required just for disambiguation
625-
# (see also setindex! in multidimensional.jl)
626-
for XT in [BitArray, AbstractArray, Any]
627-
for IT in [AbstractVector{Bool}, AbstractArray{Bool}]
628-
@eval setindex!(A::BitArray, X::$XT, I::$IT) = setindex_bool_1d(A, X, I)
629-
end
630-
631-
for IT in [Range1{Bool}, AbstractVector{Bool}], JT in [Range1{Bool}, AbstractVector{Bool}]
632-
@eval setindex!(A::BitMatrix, x::$XT, I::$IT, J::$JT) = (A[find(I),find(J)] = x; A)
449+
if length(X) != c-1
450+
throw(DimensionMismatch("assigned $(length(X)) elements to length $(c-1) destination"))
633451
end
634-
635-
for IT in [Range1{Bool}, AbstractVector{Bool}], JT in [Real, Range1]
636-
@eval setindex!(A::BitMatrix, x::$XT, I::$IT, J::$JT) = (A[find(I),J] = x; A)
637-
end
638-
@eval setindex!{T<:Real}(A::BitMatrix, x::$XT, I::AbstractVector{Bool}, J::AbstractVector{T}) = (A[find(I),J] = x; A)
639-
640-
@eval setindex!(A::BitMatrix, x::$XT, I::Real, J::AbstractVector{Bool}) = (A[I,find(J)] = x; A)
641-
@eval setindex!{T<:Real}(A::BitMatrix, x::$XT, I::AbstractVector{T}, J::AbstractVector{Bool}) = (A[I,find(J)] = x; A)
452+
A
642453
end
643454

644455
## Dequeue functionality ##

0 commit comments

Comments
 (0)