Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: rework TileIterator #27

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.jl.cov
*.jl.*.cov
*.jl.mem
Manifest.toml
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ OffsetArrays = "0.8, 0.9, 0.10, 0.11, 1"
julia = "1"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test", "Documenter"]
5 changes: 4 additions & 1 deletion src/TiledIteration.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ else
_inc(state, iter) = inc(state, iter.indices)
end

export TileIterator, EdgeIterator, padded_tilesize, TileBuffer, RelaxStride, RelaxLastTile
export TileIterator, FixedTileRange, FixedTile, TileIndices, EdgeIterator, padded_tilesize, TileBuffer, RelaxStride, RelaxLastTile

include("compat.jl")
include("tileiterator.jl")
include("tilerange.jl")
include("tileindices.jl")

const L1cachesize = 2^15
const cachelinesize = 64
Expand Down
8 changes: 8 additions & 0 deletions src/compat.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# https://github.com/JuliaLang/julia/pull/29442
if VERSION <= v"1.0.5"
_oneunit(x) = Base.oneunit(x)
_oneunit(::CartesianIndex{N}) where {N} = _oneunit(CartesianIndex{N})
_oneunit(::Type{CartesianIndex{N}}) where {N} = CartesianIndex(ntuple(x -> 1, Val(N)))
else
_oneunit = Base.oneunit
end
86 changes: 86 additions & 0 deletions src/tileindices.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
### TileIndices

struct TileIndices{T, N, R<:AbstractTileRange} <: AbstractArray{T, N}
indices::NTuple{N, R}
function TileIndices(indices::NTuple{N, <:AbstractTileRange{T}}) where {N, T}
new{NTuple{N, T}, N, eltype(indices)}(indices)
end
end

"""
TileIndices(indices, sz, Δ=sz; keep_last=true)

Construct a sliding tile along axes `indices` with fixed sliding strides `Δ` and tile size `sz`.

# Arguments

- `indices`: tuple of ranges, or `CartesianIndices`.
- `sz`: The size of each tile. If keyword `keep_last=true`, the last tile size might be smaller than
`sz`.
- `Δ=sz`: For each dimension `i` and `r = indices[i]`, the sliding stride `Δ[i]` is defined as
`first(r[n]) - first(r[n-1])`. Using a stride `Δ[i] < sz[i]` means there are overlaps between each
adjacent tile along this dimension.
- `keep_last=true` (keyword): this keyword affects the cases when the last tile size is smaller
than `sz`, in which case, `true`/`false` tells `TileIndices` to keep/discard the last tile.

# Examples

```jldoctest; setup=:(using TiledIteration)
julia> TileIndices((1:4, 0:5), (3, 4), (2, 3))
2×2 TileIndices{Tuple{UnitRange{$Int},UnitRange{$Int}},2,FixedTileRange{UnitRange{$Int},$Int,UnitRange{$Int}}}:
(1:3, 0:3) (1:3, 3:5)
(3:4, 0:3) (3:4, 3:5)

julia> TileIndices((1:4, 0:5), (3, 4), (2, 3); keep_last=false)
1×1 TileIndices{Tuple{UnitRange{$Int},UnitRange{$Int}},2,FixedTileRange{UnitRange{$Int},$Int,UnitRange{$Int}}}:
(1:3, 0:3)
```

When `sz` and `Δ` are scalars, it affects each dimension equivalently.

```jldoctest; setup=:(using TiledIteration)
julia> TileIndices((1:4, 0:5), 3, 2)
2×3 TileIndices{Tuple{UnitRange{$Int},UnitRange{$Int}},2,FixedTileRange{UnitRange{$Int},$Int,UnitRange{$Int}}}:
(1:3, 0:2) (1:3, 2:4) (1:3, 4:5)
(3:4, 0:2) (3:4, 2:4) (3:4, 4:5)

julia> TileIndices((1:4, 0:5), 3, 2; keep_last=false)
1×2 TileIndices{Tuple{UnitRange{$Int},UnitRange{$Int}},2,FixedTileRange{UnitRange{$Int},$Int,UnitRange{$Int}}}:
(1:3, 0:2) (1:3, 2:4)
```

!!! note
This method is equivalent to `TileIndices(indices, FixedTile(sz, Δ; keep_last=keep_last))`.
"""
TileIndices(indices, n, Δ=n; kwargs...) = TileIndices(indices, FixedTile(n, Δ; kwargs...))


"""
TileIndices(indices, s::AbstractTileStrategy)

Construct a sliding tile along axes `indices` using provided tile strategy `s`.

`indices` are tuple of ranges, or `CartesianIndices`.

Currently available strategies are:

- [`FixedTile`](@ref): each tile is of the same fixed size (except the last one).

For usage examples, please refer to the docstring of each tile strategy.
"""
TileIndices(indices, s::AbstractTileStrategy) = TileIndices(s(indices))

Base.size(iter::TileIndices) = map(length, iter.indices)

Base.@propagate_inbounds function Base.getindex(
iter::TileIndices{T, N},
inds::Vararg{Int, N}) where {N, T}
map(getindex, iter.indices, inds)
end
Base.@propagate_inbounds function Base.getindex(
iter::TileIndices{T, N},
inds::Vararg{Int, N}) where {N, T<:NTuple{N, CartesianIndices}}
tile = map(getindex, iter.indices, inds)
# reformulate into CartesianIndices{N}
CartesianIndices(mapreduce(I->I.indices, (i,j)->(i..., j...), tile))
end
222 changes: 222 additions & 0 deletions src/tilerange.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
# AbstractTileRange Protocols
#
# The parent range `R` is supposed to be an array of scalar indices, including:
# - ranges
# - vectors of integers
# - CartesianIndices{1}
#
# A tile range generated from `R` is a iterator whose elements are also an array of scalar indices.
# It's an indices-specific block array.
#
# Reference: https://docs.julialang.org/en/v1.5/manual/arrays/#man-supported-index-types
#
#
# | scalar index | indices(tile) |
# |-------------------|----------------- |
# | range/vector | AbstractTileRange |
# | CartesianIndices | TileIndices |
#
# AbstractTileStrategy serves as an adapter for `AbstractTileRange` and `TileIndices` to give a more
# flexible API specification.


abstract type AbstractTileRange{R} <: AbstractArray{R, 1} end
abstract type AbstractTileStrategy end

const Range1 = Union{OrdinalRange{<:Integer}, AbstractVector{<:Integer}, CartesianIndices{1}}

### FixedTileRange and FixedTile

"""
FixedTileRange(r, n, [Δ=n]; keep_last=true)

Construct a sliding tile along range `r` with fixed sliding stride `Δ` and tile length `n`.

# Arguments

- `r`: a range, `CartesianIndices` or `Vector`
- `n::Integer`: The length of each tile. If keyword `keep_last=true`, the last tile length might be
less than `n`.
- `Δ::Union{Integer, CartesianIndex{1}}=n`: The sliding stride `Δ` is defined as `first(r[n]) -
first(r[n-1])`. Using a stride `Δ<n` means there are overlaps between each adjacent tile. `Δ` can
also be `CartesianIndex{1}`.
- `keep_last=true` (keyword): this keyword affects the cases when the last tile has few elements
than `n`, in which case, `true`/`false` tells `FixedTileRange` to keep/discard the last tile.

# Examples

```jldoctest; setup=:(using TiledIteration)
julia> FixedTileRange(2:10, 3)
3-element FixedTileRange{UnitRange{Int64},Int64,UnitRange{Int64}}:
2:4
5:7
8:10

julia> FixedTileRange(1:10, 4)
3-element FixedTileRange{UnitRange{Int64},Int64,UnitRange{Int64}}:
1:4
5:8
9:10

julia> FixedTileRange(1:10, 4, 2)
4-element FixedTileRange{UnitRange{Int64},Int64,UnitRange{Int64}}:
1:4
3:6
5:8
7:10

julia> FixedTileRange(1:10, 4; keep_last=false)
2-element FixedTileRange{UnitRange{Int64},Int64,UnitRange{Int64}}:
1:4
5:8

julia> FixedTileRange(1:2:10, 4)
2-element FixedTileRange{StepRange{Int64,Int64},Int64,StepRange{Int64,Int64}}:
1:2:7
5:2:9
```

Besides an `AbstractUnitRange`, the input range `r` can also be a `CartesianIndices{1}` or more
generally, an `AbstractVector{<:Integer}`:

```jldoctest; setup=:(using TiledIteration)
julia> FixedTileRange(CartesianIndices((1:10, )), 4)
3-element FixedTileRange{CartesianIndices{1,Tuple{UnitRange{Int64}}},Int64,CartesianIndices{1,Tuple{UnitRange{Int64}}}}:
[CartesianIndex(1,), CartesianIndex(2,), CartesianIndex(3,), CartesianIndex(4,)]
[CartesianIndex(5,), CartesianIndex(6,), CartesianIndex(7,), CartesianIndex(8,)]
[CartesianIndex(9,), CartesianIndex(10,)]
```
Comment on lines +82 to +88
Copy link
Member Author

@johnnychen94 johnnychen94 Dec 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's even more ugly and unstable when it comes to N-d case.

julia> TileIndices(CartesianIndices((1:20, 1:20)), (7, 7))
3×3 TileIndices{Tuple{CartesianIndices{1, Tuple{UnitRange{Int64}}}, CartesianIndices{1, Tuple{UnitRange{Int64}}}}, 2, FixedTileRange{CartesianIndices{1, Tuple{UnitRange{Int64}}}, Int64, CartesianIndices{1, Tuple{UnitRange{Int64}}}}}:
 CartesianIndex{2}[CartesianIndex(1, 1) CartesianIndex(1, 2)  CartesianIndex(1, 6) CartesianIndex(1, 7); CartesianIndex(2, 1) CartesianIndex(2, 2)  CartesianIndex(2, 6) CartesianIndex(2, 7);  ; CartesianIndex(6, 1) CartesianIndex(6, 2)  CartesianIndex(6, 6) CartesianIndex(6, 7); CartesianIndex(7, 1) CartesianIndex(7, 2)  CartesianIndex(7, 6) CartesianIndex(7, 7)]                    CartesianIndex{2}[CartesianIndex(1, 15) CartesianIndex(1, 16)  CartesianIndex(1, 19) CartesianIndex(1, 20); CartesianIndex(2, 15) CartesianIndex(2, 16)  CartesianIndex(2, 19) CartesianIndex(2, 20);  ; CartesianIndex(6, 15) CartesianIndex(6, 16)  CartesianIndex(6, 19) CartesianIndex(6, 20); CartesianIndex(7, 15) CartesianIndex(7, 16)  CartesianIndex(7, 19) CartesianIndex(7, 20)]
 CartesianIndex{2}[CartesianIndex(8, 1) CartesianIndex(8, 2)  CartesianIndex(8, 6) CartesianIndex(8, 7); CartesianIndex(9, 1) CartesianIndex(9, 2)  CartesianIndex(9, 6) CartesianIndex(9, 7);  ; CartesianIndex(13, 1) CartesianIndex(13, 2)  CartesianIndex(13, 6) CartesianIndex(13, 7); CartesianIndex(14, 1) CartesianIndex(14, 2)  CartesianIndex(14, 6) CartesianIndex(14, 7)]             CartesianIndex{2}[CartesianIndex(8, 15) CartesianIndex(8, 16)  CartesianIndex(8, 19) CartesianIndex(8, 20); CartesianIndex(9, 15) CartesianIndex(9, 16)  CartesianIndex(9, 19) CartesianIndex(9, 20);  ; CartesianIndex(13, 15) CartesianIndex(13, 16)  CartesianIndex(13, 19) CartesianIndex(13, 20); CartesianIndex(14, 15) CartesianIndex(14, 16)  CartesianIndex(14, 19) CartesianIndex(14, 20)]
 CartesianIndex{2}[CartesianIndex(15, 1) CartesianIndex(15, 2)  CartesianIndex(15, 6) CartesianIndex(15, 7); CartesianIndex(16, 1) CartesianIndex(16, 2)  CartesianIndex(16, 6) CartesianIndex(16, 7);  ; CartesianIndex(19, 1) CartesianIndex(19, 2)  CartesianIndex(19, 6) CartesianIndex(19, 7); CartesianIndex(20, 1) CartesianIndex(20, 2)  CartesianIndex(20, 6) CartesianIndex(20, 7)]     CartesianIndex{2}[CartesianIndex(15, 15) CartesianIndex(15, 16)  CartesianIndex(15, 19) CartesianIndex(15, 20); CartesianIndex(16, 15) CartesianIndex(16, 16)  CartesianIndex(16, 19) CartesianIndex(16, 20);  ; CartesianIndex(19, 15) CartesianIndex(19, 16)  CartesianIndex(19, 19) CartesianIndex(19, 20); CartesianIndex(20, 15) CartesianIndex(20, 16)  CartesianIndex(20, 19) CartesianIndex(20, 20)]

and it simply crashes during the display if I use CartesianIndices((1:512, 1:512))

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to display those CartesianIndices as

[CartesianIndex(1, 1):CartesianIndex(1, 7), CartesianIndex(1, 1):CartesianIndex(8, 15), CartesianIndex(1, 1):CartesianIndex(16, 20)]

while still keep the current show behavior?

I vaguely remember that we have a top_level keyword for display related function, but can't find the name of it now.


!!! warning
It usually has bad indexing performance if `r` is not lazily evaluated. For example,
`FixedTileRange(collect(1:10), 4)` creates a new `Vector` of length `4` everytime when
`getindex` is called.
"""
struct FixedTileRange{R, T, RP} <: AbstractTileRange{R}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameters could use some clarificiation, maybe just by subtyping, e.g., R<:AbstractVector{<:Integer} or something?

parent::RP
n::T
Δ::T
keep_last::Bool

# keep `length` information to avoid unnecessary calculation and thus is more performant
length::T

function FixedTileRange(parent::R, n::T, Δ; keep_last::Bool=true) where {R<:Range1, T}
_length = _fixedtile_length(parent, n, Δ, keep_last)
new{_eltype(R), T, R}(parent, n, Δ, keep_last, _length)
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's interesting that this is conceptually equivalent to StepRange(1:5, BroadcastInt(2), 7:11) (a range-of-ranges). Should we consider the FixedTileRange in terms of the range API?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, not sure if I understand it right, do you mean StepRange(1:5, BroadcastInt(2), 7:11) is equivalent to

julia> TileIndices((1:5, 7:11), 2)
3×3 TileIndices{Tuple{UnitRange{Int64},UnitRange{Int64}},2,FixedTileRange{UnitRange{Int64},Int64,UnitRange{Int64}}}:
 (1:2, 7:8)  (1:2, 9:10)  (1:2, 11:11)
 (3:4, 7:8)  (3:4, 9:10)  (3:4, 11:11)
 (5:5, 7:8)  (5:5, 9:10)  (5:5, 11:11)

? I couldn't find what BroadcastInt is.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider the FixedTileRange in terms of the range API?

I would be largely relieved if we could separate the indices and tile indices world, just like how we are doing with normal array and fancy block arrays. If that's what you were thinking about.

or are you saying to could make something like FixedTileRange(start, stop; length, Δ, n) only to mimic range's usage? That would be a little bit hard because I do plan to support all indices types, including the vectors and CartesianIndices. Mimicking the range API won't improve the useability much IMO.

FixedTileRange(r::Range1, n::Integer; kwargs...) = FixedTileRange(r, n, n; kwargs...)

_eltype(::Type{R}) where R<:OrdinalRange = StepRange{eltype(R), eltype(R)}
johnnychen94 marked this conversation as resolved.
Show resolved Hide resolved
_eltype(::Type{R}) where R<:AbstractUnitRange = UnitRange{eltype(R)}
_eltype(::Type{R}) where R<:AbstractVector = R # this includes CartesianIndices{1}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're standardizing OrdinalRange and AbstractUnitRange but allowing any other type of AbstractVector? Does Vector{Int} count?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, Vector{Int} counts even though it is not memory-efficient. I choose to support that because Vector{Int} is a valid indices type.

Supporting this enables us to pass lazy evaluated vectors, even though I'm not sure if there's practical usage right now. Currently, I'm mostly concerned about CartesianIndices and step range.


_int(x::Integer) = x
_int(x::CartesianIndex{1}) = first(x.I)

function _fixedtile_length(r::OrdinalRange{T}, n, Δ, keep_last) where T<:Integer
_round = keep_last ? ceil : floor
start, step, stop = first(r), Base.step(r), last(r)
return _round(T, (stop - step*n - start + 1)/_int(Δ)) + 1
end
function _fixedtile_length(r::CartesianIndices{1}, n, Δ, keep_last)
_fixedtile_length(r.indices[1], n, Δ, keep_last)
end
function _fixedtile_length(r::AbstractVector{T}, n, Δ, keep_last) where T<:Integer
_fixedtile_length(UnitRange{T}(first(r), last(r)), n, Δ, keep_last)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe convert(UnitRange{T}, r) just to make sure this is consistent with what is being passed in? Or are you intending to support r = 1:3:10?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, StepRange and Vector{Int} are designed to be supported.

julia> FixedTileRange(1:2:10, 2)
4-element FixedTileRange{StepRange{Int64,Int64},Int64,StepRange{Int64,Int64}}:
 1:2:3
 3:2:5
 5:2:7
 7:2:9

julia> FixedTileRange([2, 3, 4, 5, 6], 2)
3-element FixedTileRange{Array{Int64,1},Int64,Array{Int64,1}}:
 [2, 3]
 [4, 5]
 [6]

I still need to figure out how this should be done for arbitrary vectors and step ranges; they're not working perfectly right now.

julia> FixedTileRange(collect(1:2:10), 2) # this is wrong
5-element FixedTileRange{Array{Int64,1},Int64,Array{Int64,1}}:
 [1, 2]
 [3, 4]
 [5, 6]
 [7, 8]
 [9]

end

Base.size(r::FixedTileRange) = (r.length, )
Base.length(r::FixedTileRange) = r.length
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need to define this (the fallbacks do it).


Base.@propagate_inbounds function Base.getindex(r::FixedTileRange{R, T}, i::Int) where {R, T}
convert(R, _getindex(r.parent, r.n, r.Δ, i))
end
Base.@propagate_inbounds function Base.getindex(r::FixedTileRange{R, T}, i::Int) where {R<:CartesianIndices, T}
# inter-operation between CartesianIndex and Integer is not very well defined
# for this reason, we deconstruct CartesianIndex into range, index, and reconstruct it.
R((_getindex(r.parent.indices[1], r.n, r.Δ, i), ))
end

Base.@propagate_inbounds function _getindex(r::R, n::T, Δ::T, i::Int) where {R, T}
@boundscheck checkbounds(r, i)
start = first(r) + (i-1)*Δ
stop = start + n - _oneunit(eltype(R))
return start:min(stop, last(r))
end
Base.@propagate_inbounds function _getindex(r::R, n::T, Δ::T, i::Int) where {R<:StepRange, T}
@boundscheck checkbounds(r, i)
start = first(r) + (i-1)*Δ
stop = start + step(r)*n - _oneunit(eltype(R))
return start:step(r):min(stop, last(r))
end


"""
FixedTile(sz, Δ; keep_last=true)

A tile strategy that you can use to construct [`TileIndices`](@ref) using [`FixedTileRange`](@ref).

# Arguments

- `sz`: The size of each tile. If keyword `keep_last=true`, the last tile size might be smaller than
`sz`.
- `Δ=sz`: For each dimension `i` and `r = indices[i]`, the sliding stride `Δ[i]` is defined as
`first(r[n]) - first(r[n-1])`. Using a stride `Δ[i] < sz[i]` means there are overlaps between each
adjacent tile along this dimension.
- `keep_last=true` (keyword): this keyword affects the cases when the last tile size is smaller
than `sz`, in which case, `true`/`false` tells `TileIndices` to keep/discard the last tile.

# Examples

```jldoctest; setup=:(using TiledIteration)
julia> TileIndices((1:4, 0:5), FixedTile((3, 4), (2, 3)))
2×2 TileIndices{Tuple{UnitRange{Int64},UnitRange{Int64}},2,FixedTileRange{UnitRange{Int64},Int64,UnitRange{Int64}}}:
(1:3, 0:3) (1:3, 3:5)
(3:4, 0:3) (3:4, 3:5)

julia> TileIndices((1:4, 0:5), FixedTile((3, 4), (2, 3); keep_last=false))
1×1 TileIndices{Tuple{UnitRange{Int64},UnitRange{Int64}},2,FixedTileRange{UnitRange{Int64},Int64,UnitRange{Int64}}}:
(1:3, 0:3)

julia> TileIndices((1:2:10, 0:1:5), FixedTile((3, 4), (2, 3)))
3×2 TileIndices{Tuple{StepRange{Int64,Int64},StepRange{Int64,Int64}},2,FixedTileRange{StepRange{Int64,Int64},Int64,StepRange{Int64,Int64}}}:
(1:2:5, 0:1:3) (1:2:5, 3:1:5)
(3:2:7, 0:1:3) (3:2:7, 3:1:5)
(5:2:9, 0:1:3) (5:2:9, 3:1:5)
```

When `sz` and `Δ` are scalars, it affects each dimension equivalently.

```jldoctest; setup=:(using TiledIteration)
julia> TileIndices((1:4, 0:5), FixedTile(3, 2))
2×3 TileIndices{Tuple{UnitRange{Int64},UnitRange{Int64}},2,FixedTileRange{UnitRange{Int64},Int64,UnitRange{Int64}}}:
(1:3, 0:2) (1:3, 2:4) (1:3, 4:5)
(3:4, 0:2) (3:4, 2:4) (3:4, 4:5)

julia> TileIndices((1:4, 0:5), FixedTile(3, 2; keep_last=false))
1×2 TileIndices{Tuple{UnitRange{Int64},UnitRange{Int64}},2,FixedTileRange{UnitRange{Int64},Int64,UnitRange{Int64}}}:
(1:3, 0:2) (1:3, 2:4)
```
"""
struct FixedTile{N, T} <: AbstractTileStrategy
size::T
Δ::T
keep_last::Bool
end

FixedTile(sz::T, Δ=sz; keep_last=true) where T<:Integer = FixedTile{0, T}(sz, Δ, keep_last)
FixedTile(sz::T, Δ=sz; keep_last=true) where T = FixedTile{length(sz), T}(sz, Δ, keep_last)

(S::FixedTile{0})(r::Range1) = FixedTileRange(r, S.size, S.Δ; keep_last=S.keep_last)
(S::FixedTile{0})(r::CartesianIndices{1}) = FixedTileRange(r, S.size, S.Δ; keep_last=S.keep_last)
(S::FixedTile{0})(indices) = map(r->FixedTileRange(r, S.size, S.Δ; keep_last=S.keep_last), indices)

(S::FixedTile{N})(indices) where N =
map((args...)->FixedTileRange(args...; keep_last=S.keep_last), indices, S.size, S.Δ)
# ambiguity patch
(S::FixedTile{0})(indices::CartesianIndices{N}) where N =
S(map(x->CartesianIndices((x, )), indices.indices))
(S::FixedTile{N})(indices::CartesianIndices{N}) where N =
S(map(x->CartesianIndices((x, )), indices.indices))
5 changes: 5 additions & 0 deletions test/compat.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# https://github.com/JuliaLang/julia/pull/29440
if VERSION < v"1.1.0-DEV.389"
Base.:(:)(I::CartesianIndex{N}, J::CartesianIndex{N}) where N =
CartesianIndices(map((i,j) -> i:j, Tuple(I), Tuple(J)))
end
5 changes: 4 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ using Test
using Documenter
using OffsetArrays: IdentityUnitRange

if VERSION < v"1.6-"
if v"1.1" <= VERSION < v"1.6-"
Documenter.doctest(TiledIteration)
# Version restriction can be lifted, when
# filters can be passed to `doctest`
Expand All @@ -17,6 +17,9 @@ if VERSION < v"1.6-"
# Documenter.doctest(TiledIteration, doctestfilters = doctestfilters)
end

include("compat.jl")
include("tilerange.jl")
include("tileindices.jl")

@testset "TileIterator small examples" begin
titr = @inferred TileIterator((1:10,), RelaxLastTile((3,)))
Expand Down
Loading