|
| 1 | +# Methods related to arrays. |
| 2 | + |
| 3 | +""" |
| 4 | + as_array_axes(args...) -> rngs::ArrayAxes |
| 5 | +
|
| 6 | +converts array dimensions or ranges `args...` to a canonical form of array axes, that is a |
| 7 | +tuple of `AbstractUnitRange{eltype(Dims)}`s. Any integer in `args...` is replaced by an |
| 8 | +instance of `Base.OneTo{eltype(Dims)}`. |
| 9 | +
|
| 10 | +The array dimensions or ranges may also be provided as a tuple. |
| 11 | +[`RelaxedArrayShape{N}`](@ref) is the union of types of `N`-tuples to which |
| 12 | +`as_array_axes` is applicable. |
| 13 | +
|
| 14 | +Also see [`as_array_shape`](@ref), [`as_array_size`](@ref), [`as_array_axis`](@ref), |
| 15 | +[`ArrayAxes`](@ref), `Dims`, and [`new_array`](@ref). |
| 16 | +
|
| 17 | +""" |
| 18 | +as_array_axes(::Tuple{}) = () |
| 19 | +as_array_axes(args::eltype(RelaxedArrayShape)...) = as_array_axes(args) |
| 20 | +as_array_axes(rngs::ArrayAxes) = rngs |
| 21 | +as_array_axes(args::RelaxedArrayShape) = map(as_array_axis, args) |
| 22 | + |
| 23 | +""" |
| 24 | + as_array_axis(arg) -> rng::ArrayAxis |
| 25 | +
|
| 26 | +converts array dimension or range `arg` to a canonical array axis, that is an instance of |
| 27 | +`AbstractUnitRange{eltype(Dims)}`. If `arg` is an integer, `Base.OneTo{eltype(Dims)}(arg)` |
| 28 | +is returned. [`eltype(RelaxedArrayShape)`](@ref RelaxedArrayShape) is the union of types |
| 29 | +to which `as_array_axis` is applicable. |
| 30 | +
|
| 31 | +Also see [`as_array_axes`](@ref), [`as_array_dim`](@ref), and `Dims`. |
| 32 | +
|
| 33 | +""" |
| 34 | +as_array_axis(dim::Integer) = Base.OneTo{Dim}(dim) |
| 35 | +as_array_axis(rng::ArrayAxis) = rng |
| 36 | +as_array_axis(rng::AbstractUnitRange{<:Integer}) = ArrayAxis(rng) |
| 37 | +as_array_axis(rng::AbstractRange{<:Integer}) = |
| 38 | + isone(step(rng)) ? UnitRange{Dim}(first(rng), last(rng)) : throw_non_unit_step(rng) |
| 39 | + |
| 40 | +""" |
| 41 | + as_array_dim(arg) -> dim::eltype(Dims) |
| 42 | +
|
| 43 | +converts array dimension or range `arg` to a canonical array dimension, that is an |
| 44 | +`eltype(Dims)`. If `arg` is a unit-step range, its length is returned. |
| 45 | +[`eltype(RelaxedArrayShape)`](@ref RelaxedArrayShape) is the union of types to which |
| 46 | +`as_array_dim` is applicable. |
| 47 | +
|
| 48 | +Also see [`as_array_size`](@ref), [`as_array_axis`](@ref), and `Dims`. |
| 49 | +
|
| 50 | +""" |
| 51 | +as_array_dim(dim::Dim) = dim |
| 52 | +as_array_dim(dim::Integer) = as(Dim, dim) |
| 53 | +as_array_dim(rng::AbstractUnitRange{<:Integer}) = as_array_dim(length(rng)) |
| 54 | +as_array_dim(rng::AbstractRange{<:Integer}) = |
| 55 | + isone(step(rng)) ? as_array_dim(length(rng)) : throw_non_unit_step(rng) |
| 56 | + |
| 57 | +@noinline throw_non_unit_step(rng::AbstractRange) = throw(ArgumentError( |
| 58 | + "invalid non-unit step ($(step(rng))) for array axis")) |
| 59 | + |
| 60 | +""" |
| 61 | + as_array_shape(args...) -> r::Union{Dims,ArraysAxes} |
| 62 | +
|
| 63 | +converts array dimensions or ranges `args...` to a canonical form of array shape, one of: |
| 64 | +
|
| 65 | +* array size, that is a tuple of `Int`s. This is the result if all of `args...` are |
| 66 | + integers or instances of `Base.OneTo`, the latter, if any, being replaced by their |
| 67 | + lengths. |
| 68 | +
|
| 69 | +* array axes, that is a tuple of `AbstractUnitRange{Int}`s. This is the result if any of |
| 70 | + `args...` are non-`Base.OneTo` ranges, the integers being converted to instances of |
| 71 | + `Base.OneTo{eltype(Dims)}`. |
| 72 | +
|
| 73 | +The array dimensions or ranges may also be provided as a tuple. |
| 74 | +[`RelaxedArrayShape{N}`](@ref) is the union of types of `N`-tuples to which |
| 75 | +`as_array_shape` is applicable. |
| 76 | +
|
| 77 | +Also see [`as_array_size`](@ref), [`as_array_axes`](@ref), [`ArrayAxes`](@ref), `Dims`, |
| 78 | +and [`new_array`](@ref). |
| 79 | +
|
| 80 | +""" |
| 81 | +as_array_shape(::Tuple{}) = () |
| 82 | +as_array_shape(args::eltype(RelaxedArrayShape)...) = as_array_shape(args) |
| 83 | +as_array_shape(args::RegularArrayShape) = as_array_size(args) |
| 84 | +as_array_shape(args::RelaxedArrayShape) = as_array_axes(args) |
| 85 | + |
| 86 | +""" |
| 87 | + as_array_size(args...) -> dims::Dims |
| 88 | +
|
| 89 | +converts array dimensions or ranges `args...` to a canonical form of array size, that is a |
| 90 | +tuple of `eltype(Dims)`s. Any range in `args...` is replaced by its length. |
| 91 | +
|
| 92 | +The array dimensions or ranges may also be provided as a tuple. |
| 93 | +[`RelaxedArrayShape{N}`](@ref) is the union of types of `N`-tuples to which |
| 94 | +`as_array_size` is applicable. |
| 95 | +
|
| 96 | +Also see [`as_array_shape`](@ref), [`as_array_axes`](@ref), [`as_array_dim`](@ref), |
| 97 | +`Dims`, and [`new_array`](@ref). |
| 98 | +
|
| 99 | +""" |
| 100 | +as_array_size(::Tuple{}) = () |
| 101 | +as_array_size(args::eltype(RelaxedArrayShape)...) = as_array_size(args) |
| 102 | +as_array_size(dims::Dims) = dims |
| 103 | +as_array_size(args::RelaxedArrayShape) = map(as_array_dim, args) |
| 104 | + |
| 105 | +""" |
| 106 | + new_array(T, inds...) -> A |
| 107 | + new_array(T, (inds...,)) -> A |
| 108 | +
|
| 109 | +create a new array with element type `T` and shape defined by `inds...`, a list of array |
| 110 | +dimension lengths and/or index ranges. The shape may also be specified as a tuple. |
| 111 | +
|
| 112 | +If `inds...` contains any index range other than `Base.OneTo`, an `OffsetArray{T}` is |
| 113 | +returned; otherwise an `Array{T}` is returned. In the former case, an exception is thrown |
| 114 | +if the package `OffsetArrays` has not been loaded. |
| 115 | +
|
| 116 | +Also see [`as_array_shape`](@ref), [`as_array_axes`](@ref), and [`as_array_size`](@ref). |
| 117 | +
|
| 118 | +""" |
| 119 | +new_array(::Type{T}, shape::eltype(RelaxedArrayShape)...) where {T} = new_array(T, shape) |
| 120 | +new_array(::Type{T}, shape::RelaxedArrayShape) where {T} = new_array(T, as_array_shape(shape)) |
| 121 | +new_array(::Type{T}, shape::RegularArrayShape) where {T} = new_array(T, as_array_size(shape)) |
| 122 | +new_array(::Type{T}, shape::Tuple{}) where {T} = Array{T}(undef) |
| 123 | +new_array(::Type{T}, shape::Dims{N}) where {T,N} = Array{T,N}(undef, shape) |
| 124 | +new_array(::Type, shape::Union{Unsupported,ArrayAxes}) = |
| 125 | + error("package `OffsetArrays` must be loaded for such array index ranges") |
| 126 | + |
| 127 | +""" |
| 128 | + promote_eltype(args...) |
| 129 | +
|
| 130 | +yields the promoted element type of its arguments. Arguments `args...` may be |
| 131 | +anything implementing the `eltype` method. |
| 132 | +
|
| 133 | +""" |
| 134 | +promote_eltype() = promote_type() |
| 135 | +promote_eltype(arg) = eltype(arg) |
| 136 | +@inline promote_eltype(args...) = promote_type(map(eltype, args)...) |
| 137 | + |
| 138 | +""" |
| 139 | + convert_eltype(T, A) -> B |
| 140 | +
|
| 141 | +converts the element type of object/type `A` to type `T`. The returned object/type is |
| 142 | +similar to `A` except maybe for the element type. For example, if `A` is a range, then `B` |
| 143 | +is also a range. If `T` is the element type of `A`, then `A` may be returned. |
| 144 | +
|
| 145 | +Consider using [`as_eltype(T, A)`](@ref as_eltype) to build an object that lazily performs |
| 146 | +the conversion. |
| 147 | +
|
| 148 | +To simplify extending `convert_eltype` for objects `A` of given type, the default behavior |
| 149 | +is: |
| 150 | +
|
| 151 | + convert_eltype(T, A) = as(convert_eltype(T, typeof(A)), A) |
| 152 | +
|
| 153 | +so that it may be sufficient to extend `convert_eltype` for the type of the objects. |
| 154 | +
|
| 155 | +""" |
| 156 | +convert_eltype(::Type{T}, x::X) where {T,X} = as(convert_eltype(T, X), x) |
| 157 | +convert_eltype(::Type{T}, ::Type{X}) where {T,X} = |
| 158 | + error("don't know how to convert the element type of type `$X` to `$T`") |
| 159 | + |
| 160 | +convert_eltype(::Type{T}, A::AbstractArray{T}) where {T} = A |
| 161 | +convert_eltype(::Type{T}, A::AbstractArray) where {T} = as(AbstractArray{T}, A) |
| 162 | +convert_eltype(::Type{T}, ::Type{<:Array{<:Any,N}}) where {T,N} = Array{T,N} |
| 163 | +convert_eltype(::Type{T}, ::Type{<:AbstractArray{<:Any,N}}) where {T,N} = AbstractArray{T,N} |
| 164 | + |
| 165 | +# Convert element type for numbers. |
| 166 | +convert_eltype(::Type{T}, ::Type{<:Number}) where {T} = T |
| 167 | + |
| 168 | +# Convert element type for tuples. See `_countuple` in `base/tuple.jl` for the best |
| 169 | +# way to extract the number of elements in a tuple given its type. |
| 170 | +convert_eltype(::Type{T}, ::Type{<:NTuple{N,Any}}) where {N,T} = NTuple{N,T} |
| 171 | +convert_eltype(::Type{T}, A::NTuple{N,T}) where {N,T} = A |
| 172 | +convert_eltype(::Type{T}, A::Tuple) where {T} = map(as(T), A) |
| 173 | + |
| 174 | +# Convert element type for `Base.OneTo` and `UnitRange`. For `T` non-integer, |
| 175 | +# a `Base.OneTo` instance becomes a `UnitRange` one (in a predictible way). |
| 176 | +convert_eltype(::Type{T}, ::Type{<:OneTo}) where {T<:Integer} = OneTo{T} |
| 177 | +convert_eltype(::Type{T}, ::Type{<:OneTo}) where {T} = UnitRange{T} |
| 178 | +convert_eltype(::Type{T}, ::Type{<:UnitRange}) where {T} = UnitRange{T} |
| 179 | +convert_eltype(::Type{T}, A::Union{OneTo,UnitRange}) where {T} = |
| 180 | + as(convert_eltype(T, typeof(A)), A) |
| 181 | + |
| 182 | +# Convert element type for AbstractUnitRange{T} <: OrdinalRange{T,T}. |
| 183 | +convert_eltype(::Type{T}, ::Type{<:AbstractUnitRange}) where {T} = AbstractUnitRange{T} |
| 184 | +convert_eltype(::Type{T}, A::AbstractUnitRange{T}) where {T} = A |
| 185 | +convert_eltype(::Type{T}, A::AbstractUnitRange) where {T} = |
| 186 | + as(T, first(A)):as(T, last(A)) |
| 187 | + |
| 188 | +# Convert element type for other range types. |
| 189 | +convert_eltype(::Type{T}, ::Type{<:AbstractRange}) where {T} = AbstractRange{T} |
| 190 | +convert_eltype(::Type{T}, A::AbstractRange{T}) where {T} = A |
| 191 | +convert_eltype(::Type{T}, A::AbstractRange) where {T} = |
| 192 | + as(T, first(A)):as(T, step(A)):as(T, last(A)) |
| 193 | + |
| 194 | +# Convert element type for LinRange{T,L<:Integer} <: AbstractRange{T}. |
| 195 | +convert_eltype(::Type{T}, A::LinRange{T}) where {T} = A |
| 196 | +convert_eltype(::Type{T}, A::LinRange) where {T} = |
| 197 | + LinRange(as(T, first(A)), as(T, last(A)), length(A)) |
| 198 | + |
| 199 | +""" |
| 200 | + convert_eltype(T) -> f |
| 201 | +
|
| 202 | +yields a callable object `f` such that `f(x)` yields `convert_eltype(T, x)` for any `x`. |
| 203 | +
|
| 204 | +""" |
| 205 | +convert_eltype(::Type{T}) where {T} = Converter(convert_eltype, T) |
| 206 | + |
| 207 | +""" |
| 208 | + as_eltype(T, A) -> B |
| 209 | +
|
| 210 | +yields an array which lazily converts its entries to type `T`. More specifically, a call |
| 211 | +like `B[i]` yields `as(T,A[i])`. |
| 212 | +
|
| 213 | +Consider using [`convert_eltype(T, A)`](@ref convert_eltype) to perform the conversion |
| 214 | +once and immediately. |
| 215 | +
|
| 216 | +""" |
| 217 | +as_eltype(::Type{T}, A::AbstractArray{T}) where {T} = A |
| 218 | +as_eltype(::Type{T}, A::AbstractArray) where {T} = AsEltype{T}(A) |
| 219 | + |
| 220 | +Base.parent(A::AsEltype) = getfield(A, :parent) |
| 221 | + |
| 222 | +# Implement abstract array API for `AsEltype` objects. |
| 223 | +for func in (:axes, :length, :size) |
| 224 | + @eval Base.$func(A::AsEltype) = $func(parent(A)) |
| 225 | +end |
| 226 | +for (L, S, Idecl, Icall) in ((false, :IndexCartesian, :(I::Vararg{Int,N}), :(I...)), |
| 227 | + (true, :IndexLinear, :(i::Int), :(i))) |
| 228 | + @eval begin |
| 229 | + Base.IndexStyle(::Type{<:AsEltype{T,N,$L}}) where {T,N} = $S() |
| 230 | + @inline function Base.getindex(A::AsEltype{T,N,$L}, $Idecl) where {T,N} |
| 231 | + @boundscheck checkbounds(A, $Icall) |
| 232 | + r = @inbounds getindex(parent(A), $Icall) |
| 233 | + return as(T, r) |
| 234 | + end |
| 235 | + @inline function Base.setindex!(A::AsEltype{T,N,$L}, x, $Idecl) where {T,N} |
| 236 | + @boundscheck checkbounds(A, $Icall) |
| 237 | + @inbounds setindex!(parent(A), x, $Icall) |
| 238 | + return A |
| 239 | + end |
| 240 | + end |
| 241 | +end |
| 242 | + |
| 243 | +Base.similar(A::AsEltype, ::Type{T}) where {T} = similar(parent(A), T) |
| 244 | +for shape in (:Dims, |
| 245 | + :(Tuple{Integer,Vararg{Integer}}), |
| 246 | + :(Tuple{Union{Integer,UnitRange{<:Integer}}, |
| 247 | + Vararg{Union{Integer,UnitRange{<:Integer}}}})) |
| 248 | + @eval Base.similar(A::AsEltype, ::Type{T}, shape::$shape) where {T} = |
| 249 | + similar(parent(A), T, shape) |
| 250 | +end |
0 commit comments