Skip to content

Commit 4c0e4e9

Browse files
authored
Add blockcheckindex and blockcheckbounds_indices (#269)
* Add blockcheckindex and blockcheckbounds_indices * Int to Integer in BlockArray constructor * remove type-assertions in blockcheckbounds
1 parent 8fa17c7 commit 4c0e4e9

File tree

3 files changed

+92
-18
lines changed

3 files changed

+92
-18
lines changed

docs/src/lib/internals.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@ BlockIndexRange
2727
BlockSlice
2828
unblock
2929
SubBlockIterator
30+
blockcheckbounds_indices
31+
blockcheckindex
3032
```

src/abstractblockarray.jl

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -87,32 +87,77 @@ ERROR: BlockBoundsError: attempt to access 2×2-blocked 2×3 BlockMatrix{Float64
8787
[...]
8888
```
8989
"""
90-
@inline function blockcheckbounds(A::AbstractArray{T, N}, i::Vararg{Integer, N}) where {T,N}
91-
if blockcheckbounds(Bool, A, i...)
92-
return
93-
else
94-
throw(BlockBoundsError(A, i))
95-
end
90+
@inline function blockcheckbounds(A::AbstractArray, i...)
91+
blockcheckbounds(Bool, A, i...) || throw(BlockBoundsError(A, i))
9692
end
9793

98-
@inline function blockcheckbounds(::Type{Bool}, A::AbstractArray{T, N}, i::Vararg{Integer, N}) where {T,N}
99-
n = blockaxes(A)
100-
k = 0
101-
for idx in 1:N # using enumerate here will allocate
102-
k += 1
103-
@inbounds _i = i[idx]
104-
Block(_i) in n[k] || return false
105-
end
106-
for idx = N+1:length(i)
107-
isone(i[idx]) || return false
108-
end
109-
return true
94+
# linear block indexing
95+
@inline function blockcheckbounds(::Type{Bool}, A::AbstractArray, i)
96+
blockcheckindex(Bool, BlockRange(blocklength(A)), i)
97+
end
98+
# cartesian block indexing
99+
@inline function blockcheckbounds(::Type{Bool}, A::AbstractArray, i...)
100+
blockcheckbounds_indices(Bool, blockaxes(A), i)
110101
end
111102

112103
blockcheckbounds(A::AbstractArray{T, N}, i::Block{N}) where {T,N} = blockcheckbounds(A, i.n...)
113104
blockcheckbounds(A::AbstractArray{T, N}, i::Vararg{Block{1},N}) where {T,N} = blockcheckbounds(A, Int.(i)...)
114105
blockcheckbounds(A::AbstractVector{T}, i::Block{1}) where {T} = blockcheckbounds(A, Int(i))
115106

107+
"""
108+
blockcheckbounds_indices(Bool, IA::Tuple{Vararg{BlockRange{1}}}, I::Tuple{Vararg{Integer}})
109+
110+
Return true if the "requested" indices in the tuple `Block.(I)` fall within the bounds of the "permitted"
111+
indices specified by the tuple `IA`. This function recursively consumes elements of these tuples
112+
in a 1-for-1 fashion.
113+
114+
The actual bounds-checking is performed by [`blockcheckindex`](@ref).
115+
116+
# Examples
117+
```jldoctest
118+
julia> B = BlockArray(zeros(6,6), 1:3, 1:3);
119+
120+
julia> blockaxes(B)
121+
(BlockRange(Base.OneTo(3)), BlockRange(Base.OneTo(3)))
122+
123+
julia> BlockArrays.blockcheckbounds_indices(Bool, blockaxes(B), (1,2))
124+
true
125+
126+
julia> BlockArrays.blockcheckbounds_indices(Bool, blockaxes(B), (4,1))
127+
false
128+
```
129+
"""
130+
@inline blockcheckbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true
131+
@inline function blockcheckbounds_indices(::Type{Bool}, blockaxes::Tuple{Vararg{BlockRange{1}}}, i::Tuple{})
132+
# the trailing blocks must be Block(1)
133+
b = first(blockaxes)
134+
length(b) == 1 && Int(b[]) == 1 && blockcheckbounds_indices(Bool, blockaxes[2:end], i)
135+
end
136+
@inline function blockcheckbounds_indices(::Type{Bool}, blockaxes::Tuple{}, i::Tuple)
137+
# the trailing indices must be 1
138+
first(i) == 1 && blockcheckbounds_indices(Bool, blockaxes, i[2:end])
139+
end
140+
@inline function blockcheckbounds_indices(::Type{Bool}, blockaxes::Tuple{Vararg{BlockRange{1}}}, i::Tuple)
141+
blockcheckindex(Bool, first(blockaxes), first(i)) &&
142+
blockcheckbounds_indices(Bool, blockaxes[2:end], i[2:end])
143+
end
144+
145+
"""
146+
blockcheckindex(Bool, inds::BlockRange{1}, index::Integer)
147+
148+
Return `true` if `Block(index)` is within the bounds of `inds`.
149+
150+
# Examples
151+
```jldoctest
152+
julia> BlockArrays.blockcheckindex(Bool, BlockRange(1:2), 1)
153+
true
154+
155+
julia> BlockArrays.blockcheckindex(Bool, BlockRange(1:2), 3)
156+
false
157+
```
158+
"""
159+
@inline blockcheckindex(::Type{Bool}, inds::BlockRange{1}, i::Integer) = Block(i) in inds
160+
116161
@propagate_inbounds Base.setindex!(block_arr::AbstractBlockArray{T,N}, v, block::Block{N}) where {T,N} =
117162
setindex!(block_arr, v, Block.(block.n)...)
118163
@inline @propagate_inbounds function Base.setindex!(block_arr::AbstractBlockArray{T,N}, v, block::Vararg{Block{1}, N}) where {T,N}

test/test_blockarrays.jl

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,13 @@ end
283283
@test_throws DimensionMismatch (BA_1[Block(3)] = rand(4))
284284
@test_throws BlockBoundsError blockcheckbounds(BA_1, 4)
285285
@test_throws BlockBoundsError BA_1[Block(4)]
286+
@test_throws BlockBoundsError blockcheckbounds(BA_1)
287+
@test_throws BlockBoundsError blockcheckbounds(BA_1, 4, 1)
288+
289+
Bv = BlockArray(zeros(1))
290+
@test Bv[Block()] == Bv[] == 0
291+
@test Bv[Block(1)] == Bv
292+
@test Bv[Block(1,1)] == zeros(1,1)
286293

287294
BA_2 = BlockArray(undef_blocks, Matrix{Float64}, [1,2], [3,4])
288295
a_2 = rand(1,4)
@@ -292,6 +299,26 @@ end
292299

293300
@test BA_2[1,5] == a_2[2]
294301
@test_throws DimensionMismatch BA_2[Block(1,2)] = rand(1,5)
302+
303+
# linear block indexing
304+
@test blockcheckbounds(Bool, BA_2, 3)
305+
@test_throws BlockBoundsError blockcheckbounds(BA_2, 5)
306+
307+
# trailing Block(1) indices
308+
BA_3 = BlockArray(undef_blocks, Matrix{Float64}, [1,3], [4])
309+
@test blockcheckbounds(Bool, BA_3, 1, 1)
310+
@test blockcheckbounds(Bool, BA_3, 2, 1)
311+
@test_throws BlockBoundsError blockcheckbounds(BA_3, 3, 1)
312+
@test_throws BlockBoundsError blockcheckbounds(BA_3, 1, 2)
313+
@test_throws BlockBoundsError blockcheckbounds(BA_3, 3, 2)
314+
315+
BA_4 = BlockArray(zeros(2,2,1))
316+
@test blockcheckbounds(Bool, BA_4)
317+
@test blockcheckbounds(Bool, BA_4, 1)
318+
@test blockcheckbounds(Bool, BA_4, 1, 1)
319+
@test blockcheckbounds(Bool, BA_4, 1, 1, 1)
320+
@test blockcheckbounds(Bool, BA_4, 1, 1, 1, 1)
321+
@test_throws BlockBoundsError blockcheckbounds(BA_3, 1, 2)
295322
end
296323

297324
@testset "misc block tests" begin

0 commit comments

Comments
 (0)