@@ -436,3 +436,236 @@ unsafe_setindex!(a, v, i::Vararg{Any}) = unsafe_set_collection!(a, v, i)
436436Sets `inds` of `A` to `val`. `inds` is assumed to have been bounds-checked.
437437=#
438438unsafe_set_collection! (A, v, i) = Base. _unsafe_setindex! (IndexStyle (A), A, v, i... )
439+
440+ # # Index Information
441+
442+ """
443+ known_first(::Type{T}) -> Union{Int,Nothing}
444+
445+ If `first` of an instance of type `T` is known at compile time, return it.
446+ Otherwise, return `nothing`.
447+
448+ ```julia
449+ julia> ArrayInterface.known_first(typeof(1:4))
450+ nothing
451+
452+ julia> ArrayInterface.known_first(typeof(Base.OneTo(4)))
453+ 1
454+ ```
455+ """
456+ known_first (x) = known_first (typeof (x))
457+ known_first (T:: Type ) = is_forwarding_wrapper (T) ? known_first (parent_type (T)) : nothing
458+ known_first (:: Type{<:Base.OneTo} ) = 1
459+ known_first (@nospecialize T:: Type{<:LinearIndices} ) = 1
460+ known_first (@nospecialize T:: Type{<:Base.IdentityUnitRange} ) = known_first (parent_type (T))
461+ function known_first (:: Type{<:CartesianIndices{N, R}} ) where {N, R}
462+ _cartesian_index (ntuple (i -> known_first (R. parameters[i]), Val (N)))
463+ end
464+
465+ """
466+ known_last(::Type{T}) -> Union{Int,Nothing}
467+
468+ If `last` of an instance of type `T` is known at compile time, return it.
469+ Otherwise, return `nothing`.
470+
471+ ```julia
472+ julia> ArrayInterface.known_last(typeof(1:4))
473+ nothing
474+
475+ julia> ArrayInterface.known_first(typeof(static(1):static(4)))
476+ 4
477+
478+ ```
479+ """
480+ known_last (x) = known_last (typeof (x))
481+ known_last (T:: Type ) = is_forwarding_wrapper (T) ? known_last (parent_type (T)) : nothing
482+ function known_last (:: Type{<:CartesianIndices{N, R}} ) where {N, R}
483+ _cartesian_index (ntuple (i -> known_last (R. parameters[i]), Val (N)))
484+ end
485+
486+ """
487+ known_step(::Type{T}) -> Union{Int,Nothing}
488+
489+ If `step` of an instance of type `T` is known at compile time, return it.
490+ Otherwise, return `nothing`.
491+
492+ ```julia
493+ julia> StaticArrayInterface.known_step(typeof(1:2:8))
494+ nothing
495+
496+ julia> StaticArrayInterface.known_step(typeof(1:4))
497+ 1
498+
499+ ```
500+ """
501+ known_step (x) = known_step (typeof (x))
502+ known_step (T:: Type ) = is_forwarding_wrapper (T) ? known_step (parent_type (T)) : nothing
503+ known_step (@nospecialize T:: Type{<:AbstractUnitRange} ) = 1
504+
505+ """
506+ is_splat_index(::Type{T}) -> Bool
507+
508+ Returns `static(true)` if `T` is a type that splats across multiple dimensions.
509+ """
510+ is_splat_index (T:: Type ) = false
511+ is_splat_index (@nospecialize (x)) = is_splat_index (typeof (x))
512+
513+ _add1 (@nospecialize x) = x + oneunit (x)
514+ _sub1 (@nospecialize x) = x - oneunit (x)
515+
516+ """
517+ IndicesInfo{N}(inds::Tuple) -> IndicesInfo{N}(typeof(inds))
518+ IndicesInfo{N}(T::Type{<:Tuple}) -> IndicesInfo{N,pdims,cdims}()
519+ IndicesInfo(inds::Tuple) -> IndicesInfo(typeof(inds))
520+ IndicesInfo(T::Type{<:Tuple}) -> IndicesInfo{maximum(pdims),pdims,cdims}()
521+
522+
523+ Maps a tuple of indices to `N` dimensions. The resulting `pdims` is a tuple where each
524+ field in `inds` (or field type in `T`) corresponds to the parent dimensions accessed.
525+ `cdims` similarly maps indices to the resulting child array produced after indexing with
526+ `inds`. If `N` is not provided then it is assumed that all indices are represented by parent
527+ dimensions and there are no trailing dimensions accessed. These may be accessed by through
528+ `parentdims(info::IndicesInfo)` and `childdims(info::IndicesInfo)`. If `N` is not provided,
529+ it is assumed that no indices are accessing trailing dimensions (which are represented as
530+ `0` in `parentdims(info)[index_position]`).
531+
532+ The the fields and types of `IndicesInfo` should not be accessed directly.
533+ Instead [`parentdims`](@ref), [`childdims`](@ref), [`ndims_index`](@ref), and
534+ [`ndims_shape`](@ref) should be used to extract relevant information.
535+
536+ # Examples
537+
538+ ```julia
539+ julia> using StaticArrayInterface: IndicesInfo, parentdims, childdims, ndims_index, ndims_shape
540+
541+ julia> info = IndicesInfo{5}(typeof((:,[CartesianIndex(1,1),CartesianIndex(1,1)], 1, ones(Int, 2, 2), :, 1)));
542+
543+ julia> parentdims(info) # the last two indices access trailing dimensions
544+ (1, (2, 3), 4, 5, 0, 0)
545+
546+ julia> childdims(info)
547+ (1, 2, 0, (3, 4), 5, 0)
548+
549+ julia> childdims(info)[3] # index 3 accesses a parent dimension but is dropped in the child array
550+ 0
551+
552+ julia> ndims_index(info)
553+ 5
554+
555+ julia> ndims_shape(info)
556+ 5
557+
558+ julia> info = IndicesInfo(typeof((:,[CartesianIndex(1,1),CartesianIndex(1,1)], 1, ones(Int, 2, 2), :, 1)));
559+
560+ julia> parentdims(info) # assumed no trailing dimensions
561+ (1, (2, 3), 4, 5, 6, 7)
562+
563+ julia> ndims_index(info) # assumed no trailing dimensions
564+ 7
565+
566+ ```
567+ """
568+ struct IndicesInfo{Np, pdims, cdims, Nc}
569+ function IndicesInfo {N} (@nospecialize (T:: Type{<:Tuple} )) where {N}
570+ SI = _find_first_true (map_tuple_type (is_splat_index, T))
571+ NI = map_tuple_type (ndims_index, T)
572+ NS = map_tuple_type (ndims_shape, T)
573+ if SI === nothing
574+ ndi = NI
575+ nds = NS
576+ else
577+ nsplat = N - sum (NI)
578+ if nsplat === 0
579+ ndi = NI
580+ nds = NS
581+ else
582+ splatmul = max (0 , nsplat + 1 )
583+ ndi = _map_splats (splatmul, SI, NI)
584+ nds = _map_splats (splatmul, SI, NS)
585+ end
586+ end
587+ if ndi === (1 ,) && N != = 1
588+ ns1 = getfield (nds, 1 )
589+ new {N, (:,), (ns1 > 1 ? ntuple(identity, ns1) : ns1,), ns1} ()
590+ else
591+ nds_cumsum = cumsum (nds)
592+ if sum (ndi) > N
593+ init_pdims = _accum_dims (cumsum (ndi), ndi)
594+ pdims = ntuple (nfields (init_pdims)) do i
595+ dim_i = getfield (init_pdims, i)
596+ if dim_i isa Tuple
597+ ntuple (length (dim_i)) do j
598+ dim_i_j = getfield (dim_i, j)
599+ dim_i_j > N ? 0 : dim_i_j
600+ end
601+ else
602+ dim_i > N ? 0 : dim_i
603+ end
604+ end
605+ new {N, pdims, _accum_dims(nds_cumsum, nds), last(nds_cumsum)} ()
606+ else
607+ new{N, _accum_dims (cumsum (ndi), ndi), _accum_dims (nds_cumsum, nds),
608+ last (nds_cumsum)}()
609+ end
610+ end
611+ end
612+ IndicesInfo {N} (@nospecialize (t:: Tuple )) where {N} = IndicesInfo {N} (typeof (t))
613+ function IndicesInfo (@nospecialize (T:: Type{<:Tuple} ))
614+ ndi = map_tuple_type (ndims_index, T)
615+ nds = map_tuple_type (ndims_shape, T)
616+ ndi_sum = cumsum (ndi)
617+ nds_sum = cumsum (nds)
618+ nf = nfields (ndi_sum)
619+ pdims = _accum_dims (ndi_sum, ndi)
620+ cdims = _accum_dims (nds_sum, nds)
621+ new {getfield(ndi_sum, nf), pdims, cdims, getfield(nds_sum, nf)} ()
622+ end
623+ IndicesInfo (@nospecialize t:: Tuple ) = IndicesInfo (typeof (t))
624+ @inline function IndicesInfo (@nospecialize T:: Type{<:SubArray} )
625+ IndicesInfo {ndims(parent_type(T))} (fieldtype (T, :indices ))
626+ end
627+ IndicesInfo (x:: SubArray ) = IndicesInfo {ndims(parent(x))} (typeof (x. indices))
628+ end
629+
630+ @inline function _map_splats (nsplat:: Int , splat_index:: Int , dims:: Tuple{Vararg{Int}} )
631+ ntuple (length (dims)) do i
632+ i === splat_index ? (nsplat * getfield (dims, i)) : getfield (dims, i)
633+ end
634+ end
635+ @inline function _accum_dims (csdims:: NTuple{N, Int} , nd:: NTuple{N, Int} ) where {N}
636+ ntuple (N) do i
637+ nd_i = getfield (nd, i)
638+ if nd_i === 0
639+ 0
640+ elseif nd_i === 1
641+ getfield (csdims, i)
642+ else
643+ ntuple (Base. Fix1 (+ , getfield (csdims, i) - nd_i), nd_i)
644+ end
645+ end
646+ end
647+
648+ function _lower_info (:: IndicesInfo{Np, pdims, cdims, Nc} ) where {Np, pdims, cdims, Nc}
649+ Np, pdims, cdims, Nc
650+ end
651+
652+ ndims_index (@nospecialize (info:: IndicesInfo )) = getfield (_lower_info (info), 1 )
653+ ndims_shape (@nospecialize (info:: IndicesInfo )) = getfield (_lower_info (info), 4 )
654+
655+ """
656+ parentdims(::IndicesInfo) -> Tuple
657+
658+ Returns the parent dimension mapping from `IndicesInfo`.
659+
660+ See also: [`IndicesInfo`](@ref), [`childdims`](@ref)
661+ """
662+ parentdims (@nospecialize info:: IndicesInfo ) = getfield (_lower_info (info), 2 )
663+
664+ """
665+ childdims(::IndicesInfo) -> Tuple
666+
667+ Returns the child dimension mapping from `IndicesInfo`.
668+
669+ See also: [`IndicesInfo`](@ref), [`parentdims`](@ref)
670+ """
671+ childdims (@nospecialize info:: IndicesInfo ) = getfield (_lower_info (info), 3 )
0 commit comments