diff --git a/ext/OffsetArraysAdaptExt.jl b/ext/OffsetArraysAdaptExt.jl index 55969fd..4ac45d5 100644 --- a/ext/OffsetArraysAdaptExt.jl +++ b/ext/OffsetArraysAdaptExt.jl @@ -10,7 +10,7 @@ Adapt.adapt_structure(to, O::OffsetArray) = OffsetArrays.parent_call(x -> Adapt. @static if isdefined(Adapt, :parent_type) # To support Adapt 3.0 which doesn't have parent_type defined - Adapt.parent_type(::Type{OffsetArray{T,N,AA}}) where {T,N,AA} = AA + Adapt.parent_type(::Type{OffsetArray{T,N,AA,I}}) where {T,N,AA,I} = AA Adapt.unwrap_type(W::Type{<:OffsetArray}) = unwrap_type(parent_type(W)) end diff --git a/src/OffsetArrays.jl b/src/OffsetArrays.jl index 9aa9f58..20c6bd5 100644 --- a/src/OffsetArrays.jl +++ b/src/OffsetArrays.jl @@ -109,14 +109,33 @@ julia> OffsetArray(a, OffsetArrays.Origin(0)) # set the origin to zero along eac """ -struct OffsetArray{T,N,AA<:AbstractArray{T,N}} <: AbstractArray{T,N} +struct OffsetArray{T, N, AA<:AbstractArray{T,N}, I<:Integer} <: AbstractArray{T, N} parent::AA - offsets::NTuple{N,Int} - @inline function OffsetArray{T, N, AA}(parent::AA, offsets::NTuple{N, Int}; checkoverflow = true) where {T, N, AA<:AbstractArray{T,N}} + offsets::NTuple{N,I} + # Inner constructor with all 4 type parameters bound + @inline function OffsetArray{T, N, AA, I}(parent::AA, offsets::NTuple{N, I}; checkoverflow=true) where {T, N, AA<:AbstractArray{T,N}, I<:Integer} # allocation of `map` on tuple is optimized away checkoverflow && map(overflow_check, axes(parent), offsets) - new{T, N, AA}(parent, offsets) + new{T, N, AA, I}(parent, offsets) end + # Special case of a 0-dimensional array, offsets do not make sense here + @inline function OffsetArray{T, N, AA, I}(parent::AA, offsets::Tuple{}; checkoverflow=true) where {T, N, AA<:AbstractArray{T,0}, I<:Integer} + new{T, N, AA, I}(parent, offsets) + end +end + +# Helper to get the element type of an offset tuple +_offset_eltype(::Tuple{I, Vararg{I}}) where {I<:Integer} = I +_offset_eltype(::Tuple{}) = Int + +# Outer constructor that infers I from the offsets - for backwards compatibility with 3-param calls +@inline function OffsetArray{T, N, AA}(parent::AA, offsets::NTuple{N, <:Integer}; checkoverflow=true) where {T, N, AA<:AbstractArray{T,N}} + I = _offset_eltype(offsets) + OffsetArray{T, N, AA, I}(parent, offsets; checkoverflow=checkoverflow) +end +# Special case for 0-dimensional arrays with 3-param call +@inline function OffsetArray{T, N, AA}(parent::AA, offsets::Tuple{}; checkoverflow=true) where {T, N, AA<:AbstractArray{T,0}} + OffsetArray{T, N, AA, _offset_eltype(offsets)}(parent, offsets; checkoverflow=checkoverflow) end """ @@ -124,17 +143,17 @@ end Type alias and convenience constructor for one-dimensional [`OffsetArray`](@ref)s. """ -const OffsetVector{T,AA<:AbstractVector{T}} = OffsetArray{T,1,AA} +const OffsetVector{T,AA<:AbstractVector{T},I<:Integer} = OffsetArray{T,1,AA,I} """ OffsetMatrix(A, index1, index2) Type alias and convenience constructor for two-dimensional [`OffsetArray`](@ref)s. """ -const OffsetMatrix{T,AA<:AbstractMatrix{T}} = OffsetArray{T,2,AA} +const OffsetMatrix{T,AA<:AbstractMatrix{T},I<:Integer} = OffsetArray{T,2,AA,I} # checks if the offset may be added to the range without overflowing -function overflow_check(r::AbstractUnitRange, offset::Integer) +function overflow_check(r::AbstractUnitRange, offset::Integer) Base.hastypemax(eltype(r)) || return nothing # This gives some performance boost https://github.com/JuliaLang/julia/issues/33273 throw_upper_overflow_error(val) = throw(OverflowError("offset should be <= $(typemax(Int) - val) corresponding to the axis $r, received an offset $offset")) @@ -176,16 +195,13 @@ end for FT in (:OffsetArray, :OffsetVector, :OffsetMatrix) # Nested OffsetArrays may strip off the wrapper and collate the offsets # empty tuples are handled here - @eval @inline function $FT(A::OffsetArray, offsets::Tuple{Vararg{Int}}; checkoverflow = true) + @eval @inline function $FT(A::OffsetArray, offsets::Tuple{Vararg{Integer}}; checkoverflow = true) _checkindices(A, offsets, "offsets") # ensure that the offsets may be added together without an overflow checkoverflow && map(overflow_check, axes(A), offsets) I = map(+, _offsets(A, parent(A)), offsets) $FT(parent(A), I, checkoverflow = false) end - @eval @inline function $FT(A::OffsetArray, offsets::Tuple{Integer,Vararg{Integer}}; kw...) - $FT(A, map(Int, offsets); kw...) - end # In general, indices get converted to AbstractUnitRanges. # CartesianIndices{N} get converted to N ranges @@ -224,16 +240,13 @@ end @inline OffsetArray{T,N}(M::AbstractArray{T,N}, I...; kw...) where {T,N} = OffsetArray{T,N,typeof(M)}(M, I...; kw...) @inline OffsetArray{T,N,A}(M::AbstractArray{<:Any,N}, I...; kw...) where {T,N,A<:AbstractArray{T,N}} = OffsetArray{T,N,A}(M, I; kw...) -@inline function OffsetArray{T,N,A}(M::AbstractArray{<:Any,N}, I::NTuple{N,Int}; checkoverflow = true) where {T,N,A<:AbstractArray{T,N}} +@inline function OffsetArray{T,N,A}(M::AbstractArray{<:Any,N}, I::NTuple{N,Integer}; checkoverflow = true) where {T,N,A<:AbstractArray{T,N}} checkoverflow && map(overflow_check, axes(M), I) Mv = no_offset_view(M) MvA = convert(A, Mv)::A Iof = map(+, _offsets(M), I) OffsetArray{T,N,A}(MvA, Iof, checkoverflow = false) end -@inline function OffsetArray{T, N, AA}(parent::AbstractArray{<:Any,N}, offsets::NTuple{N, Integer}; kw...) where {T, N, AA<:AbstractArray{T,N}} - OffsetArray{T, N, AA}(parent, map(Int, offsets)::NTuple{N,Int}; kw...) -end @inline function OffsetArray{T,N,A}(M::AbstractArray{<:Any,N}, I::Tuple{AbstractUnitRange,Vararg{AbstractUnitRange}}; kw...) where {T,N,A<:AbstractArray{T,N}} _checkindices(M, I, "indices") # Performance gain by wrapping the error in a function: see https://github.com/JuliaLang/julia/issues/37558 @@ -275,7 +288,7 @@ end @inline OffsetArray{T}(init::ArrayInitializer, inds...; kw...) where {T} = OffsetArray{T}(init, inds; kw...) Base.IndexStyle(::Type{OA}) where {OA<:OffsetArray} = IndexStyle(parenttype(OA)) -parenttype(::Type{OffsetArray{T,N,AA}}) where {T,N,AA} = AA +parenttype(::Type{OffsetArray{T,N,AA,I}}) where {T,N,AA,I} = AA parenttype(A::OffsetArray) = parenttype(typeof(A)) Base.parent(A::OffsetArray) = A.parent @@ -333,6 +346,7 @@ function Base.similar(::Type{T}, shape::Tuple{OffsetAxisKnownLength,Vararg{Offse P = _similar_axes_or_length(T, new_shape, shape) OffsetArray(P, map(_offset, axes(P), shape)) end + # Try to use the axes to generate the parent array type # This is useful if the axes have special meanings, such as with static arrays # This method is hit if at least one axis provided to similar(A, T, axes) is an IdOffsetRange @@ -381,6 +395,7 @@ Base.reshape(A::OffsetArray, inds::Dims) = _reshape_nov(A, inds) if VERSION < v"1.10.7" # the specialized reshape(parent::AbstractVector, ::Tuple{Colon}) is available in Base at least on this version Base.reshape(A::OffsetVector, ::Tuple{Colon}) = A + # Keep Int here (not Integer) to avoid method ambiguity with Base on older Julia versions Base.reshape(A::OffsetArray, inds::Tuple{Vararg{Union{Int,Colon}}}) = _reshape_nov(A, inds) end @@ -430,13 +445,13 @@ end # but that case is handled by getindex(::OffsetArray{<:Any,N}, ::Vararg{Colon,N}) @propagate_inbounds Base.getindex(A::OffsetArray, c::Colon) = A.parent[:] -@inline function Base.getindex(A::OffsetVector, i::Int) +@inline function Base.getindex(A::OffsetVector{T,AA,<:Integer}, i::Int) where {T,AA<:AbstractVector{T}} @boundscheck checkbounds(A, i) @inbounds parent(A)[parentindex(Base.axes1(A), i)] end @propagate_inbounds Base.getindex(A::OffsetArray, i::Int) = parent(A)[i] -@inline function Base.setindex!(A::OffsetArray{T,N}, val, I::Vararg{Int,N}) where {T,N} +@inline function Base.setindex!(A::OffsetArray{T,N,AA,<:Any}, val, I::Vararg{Int,N}) where {T,N,AA} @boundscheck checkbounds(A, I...) J = map(parentindex, axes(A), I) @inbounds parent(A)[J...] = val @@ -459,7 +474,7 @@ Base.in(x, A::OffsetArray) = in(x, parent(A)) Base.copy(A::OffsetArray) = parent_call(copy, A) Base.strides(A::OffsetArray) = strides(parent(A)) -Base.elsize(::Type{OffsetArray{T,N,A}}) where {T,N,A} = Base.elsize(A) +Base.elsize(::Type{OffsetArray{T,N,A,I}}) where {T,N,A,I} = Base.elsize(A) Base.cconvert(P::Type{Ptr{T}}, A::OffsetArray{T}) where {T} = Base.cconvert(P, parent(A)) if VERSION < v"1.11-" @inline Base.unsafe_convert(::Type{Ptr{T}}, A::OffsetArray{T}) where {T} = Base.unsafe_convert(Ptr{T}, parent(A)) @@ -491,7 +506,7 @@ end end # An OffsetUnitRange might use the rapid getindex(::Array, ::AbstractUnitRange{Int}) for contiguous indexing -@propagate_inbounds function Base.getindex(A::Array, r::OffsetUnitRange{Int}) +@propagate_inbounds function Base.getindex(A::Array, r::OffsetUnitRange{Integer}) B = A[_contiguousindexingtype(parent(r))] OffsetArray(B, axes(r), checkoverflow = false) end @@ -530,7 +545,7 @@ end # An OffsetUnitRange{Int} has an equivalent IdOffsetRange with the same values and axes, # something similar also holds for OffsetUnitRange{BigInt} # We may replace the former with the latter in an indexing operation to obtain a performance boost -@inline function Base.to_index(r::OffsetUnitRange{<:Union{Int,BigInt}}) +@inline function Base.to_index(r::OffsetUnitRange{<:Integer}) of = first(axes(r,1)) - 1 IdOffsetRange(_subtractoffset(parent(r), of), of) end diff --git a/src/axes.jl b/src/axes.jl index 519f0da..e96c0dd 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -104,20 +104,20 @@ end _bool_check(::Type, r, offset) = nothing # Construction/coercion from arbitrary AbstractUnitRanges -function IdOffsetRange{T,I}(r::AbstractUnitRange, offset::Integer = 0) where {T<:Integer,I<:AbstractUnitRange{T}} +function IdOffsetRange{T,I}(r::AbstractUnitRange, offset::Integer=Int8(0)) where {T<:Integer,I<:AbstractUnitRange{T}} rc, o = offset_coerce(I, r) return IdOffsetRange{T,I}(rc, convert(T, o+offset)::T) end -function IdOffsetRange{T}(r::AbstractUnitRange, offset::Integer = 0) where T<:Integer +function IdOffsetRange{T}(r::AbstractUnitRange, offset::Integer=Int8(0)) where T<:Integer rc = convert(AbstractUnitRange{T}, r)::AbstractUnitRange{T} return IdOffsetRange{T,typeof(rc)}(rc, convert(T, offset)::T) end -IdOffsetRange(r::AbstractUnitRange{T}, offset::Integer = 0) where T<:Integer = +IdOffsetRange(r::AbstractUnitRange{T}, offset::Integer=Int8(0)) where T<:Integer = IdOffsetRange{T,typeof(r)}(r, convert(T, offset)::T) # Coercion from other IdOffsetRanges IdOffsetRange{T,I}(r::IdOffsetRange{T,I}) where {T<:Integer,I<:AbstractUnitRange{T}} = r -function IdOffsetRange{T,I}(r::IdOffsetRange, offset::Integer = 0) where {T<:Integer,I<:AbstractUnitRange{T}} +function IdOffsetRange{T,I}(r::IdOffsetRange, offset::Integer=Int8(0)) where {T<:Integer,I<:AbstractUnitRange{T}} rc, offset_rc = offset_coerce(I, r.parent) return IdOffsetRange{T,I}(rc, convert(T, r.offset + offset + offset_rc)::T) end @@ -134,7 +134,7 @@ _subtractindexoffset(values, indices, offset) = _subtractoffset(values, offset) function IdOffsetRange(; values::AbstractUnitRange{<:Integer}, indices::AbstractUnitRange{<:Integer}) length(values) == length(indices) || throw(ArgumentError("values and indices must have the same length")) values_nooffset = no_offset_view(values) - offset = first(indices) - 1 + offset = first(indices) - Int8(1) values_minus_offset = _subtractindexoffset(values_nooffset, indices, offset) return IdOffsetRange(values_minus_offset, offset) end diff --git a/src/utils.jl b/src/utils.jl index 0d232c1..7bf05bb 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -3,7 +3,7 @@ _indexoffset(r::AbstractRange) = first(r) - 1 _indexoffset(i::Integer) = 0 _indexlength(r::AbstractRange) = length(r) -_indexlength(i::Integer) = Int(i) +_indexlength(i::Integer) = Int(i) # Convert to Int for Base compatibility (reshape, etc.) _indexlength(i::Colon) = Colon() # utility methods used in reshape @@ -17,7 +17,10 @@ _toaxis(i) = i _strip_IdOffsetRange(r::IdOffsetRange) = parent(r) _strip_IdOffsetRange(r) = r -_offset(axparent::AbstractUnitRange, ax::AbstractUnitRange) = first(ax) - first(axparent) +_offset(axparent::AbstractUnitRange, ax::AbstractUnitRange{Int}) = first(ax) - first(axparent) + +# Keep the provided integer if possible +_offset(axparent::AbstractUnitRange, ax::AbstractUnitRange{I}) where {I<:Integer} = first(ax) - convert(I, first(axparent)) _offset(axparent::AbstractUnitRange, ::Union{Integer, Colon}) = 1 - first(axparent) _offsets(A::AbstractArray) = map(ax -> first(ax) - 1, axes(A)) diff --git a/test/runtests.jl b/test/runtests.jl index 5e95311..292c378 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -13,7 +13,9 @@ using OffsetArrays: IdentityUnitRange, no_offset_view, IIUR, Origin, IdOffsetRan using StaticArrays using Test -const SliceIntUR = Slice{<:AbstractUnitRange{<:Integer}} +if !isdefined(Main, :SliceIntUR) + const SliceIntUR = Slice{<:AbstractUnitRange{<:Integer}} +end DocMeta.setdocmeta!(OffsetArrays, :DocTestSetup, :(using OffsetArrays); recursive=true) @@ -46,7 +48,7 @@ end @testset "Project meta quality checks" begin Aqua.test_all(OffsetArrays, piracies=false) if VERSION >= v"1.2" - doctest(OffsetArrays, manual = false) + doctest(OffsetArrays, manual=false) end end @@ -433,6 +435,10 @@ Base.Int(a::WeirdInteger) = a @test a === OffsetArray(a, ()) @test_throws ArgumentError OffsetArray(a, 0) @test_throws ArgumentError OffsetArray(a0, 0) + # Test 0-dimensional array with explicit type parameters (coverage for 0-dim constructors) + b = OffsetArray{Int, 0, typeof(a0)}(a0, ()) + @test b[] == 3 + @test axes(b) == () end @testset "OffsetVector" begin @@ -457,15 +463,23 @@ Base.Int(a::WeirdInteger) = a ] offsets = size.(one_based_axes[1], 1) - offsets_big = map(big, offsets) + # Test with different integer offset types (Int8, Int16, Int32, Int64, Int128, BigInt) + offsets_all = [map(T, offsets) for T in (Int8, Int16, Int32, Int64, Int128, BigInt)] - for inds in Any[offsets, offsets_big, one_based_axes...] + for inds in Any[offsets_all..., one_based_axes...] # test indices API a = OffsetVector{Float64}(undef, inds) @test eltype(a) === Float64 @test axes(a) === axes(OffsetVector{Float64}(undef, inds...)) === axes(OffsetArray{Float64, 1}(undef, inds)) === axes(OffsetArray{Float64}(undef, inds)) @test axes(a) === (IdOffsetRange(Base.OneTo(4), 0), ) - @test a.offsets === (0, ) + + OffsetType = eltype(a.offsets) + + if OffsetType == BigInt # Note BigInt.((0, )) === BigInt.((0, )) is false + @test a.offsets == (0, ) + else + @test a.offsets === map(OffsetType, (0, )) + end @test axes(a.parent) == (Base.OneTo(4), ) a = OffsetVector{Nothing}(nothing, inds) @@ -480,7 +494,7 @@ Base.Int(a::WeirdInteger) = a end # nested OffsetVectors - for inds in Any[offsets, offsets_big] + for inds in offsets_all a = OffsetVector{Float64}(undef, inds) b = OffsetVector(a, inds); b2 = OffsetVector(a, inds...); @test eltype(b) === eltype(b2) === Float64 @@ -492,7 +506,12 @@ Base.Int(a::WeirdInteger) = a # test offsets a = OffsetVector{Float64}(undef, inds) ax = (IdOffsetRange(Base.OneTo(4), -2), ) - @test a.offsets === (-2, ) + OffsetType = eltype(a.offsets) + if OffsetType == BigInt # Note BigInt.((-2, )) === BigInt.((-2, )) is false + @test a.offsets == (-2, ) + else + @test a.offsets === map(OffsetType, (-2, )) + end @test axes(a.parent) == (Base.OneTo(4), ) @test axes(a) === ax a = OffsetVector{Nothing}(nothing, inds) @@ -519,10 +538,15 @@ Base.Int(a::WeirdInteger) = a oa2 = OffsetVector(a, inds) oa3 = OffsetArray(a, inds...) oa4 = OffsetArray(a, inds) - @test oa1 === oa2 === oa3 === oa4 + if eltype(oa1.offsets) == BigInt # Note: BigInt(1) === BigInt(1) is false + @test oa1 == oa2 == oa3 == oa4 + @test oa1.offsets == (-2, ) + else + @test oa1 === oa2 === oa3 === oa4 + @test oa1.offsets === (-2, ) + end @test axes(oa1) === (IdOffsetRange(Base.OneTo(4), -2), ) @test parent(oa1) === a - @test oa1.offsets === (-2, ) end oa = OffsetArray(a, :) @@ -537,7 +561,11 @@ Base.Int(a::WeirdInteger) = a for inds in Any[.-oa.offsets, one_based_axes...] ooa = OffsetArray(oa, inds) @test typeof(parent(ooa)) <: Vector - @test ooa === OffsetArray(oa, inds...) === OffsetVector(oa, inds) === OffsetVector(oa, inds...) + if eltype(ooa.offsets) == BigInt # Note: BigInt(1) === BigInt(1) is false + @test ooa == OffsetArray(oa, inds...) == OffsetVector(oa, inds) == OffsetVector(oa, inds...) + else + @test ooa === OffsetArray(oa, inds...) === OffsetVector(oa, inds) === OffsetVector(oa, inds...) + end @test ooa == a @test axes(ooa) == axes(a) @test axes(ooa) !== axes(a) @@ -630,16 +658,21 @@ Base.Int(a::WeirdInteger) = a ] offsets = size.(one_based_axes[1], 1) - offsets_big = map(big, offsets) + # Test with different integer offset types (Int8, Int16, Int32, Int64, Int128, BigInt) + offsets_all = [map(T, offsets) for T in (Int8, Int16, Int32, Int64, Int128, BigInt)] - for inds in Any[offsets, offsets_big, one_based_axes...] + for inds in Any[offsets_all..., one_based_axes...] # test API a = OffsetMatrix{Float64}(undef, inds) ax = (IdOffsetRange(Base.OneTo(4), 0), IdOffsetRange(Base.OneTo(3), 0)) @test eltype(a) === Float64 @test axes(a) === axes(OffsetMatrix{Float64}(undef, inds...)) === axes(OffsetArray{Float64, 2}(undef, inds)) === axes(OffsetArray{Float64, 2}(undef, inds...)) === axes(OffsetArray{Float64}(undef, inds)) @test axes(a) === ax - @test a.offsets === (0, 0) + if eltype(a.offsets) == BigInt # Note BigInt.((0, 0)) === BigInt.((0, 0)) is false + @test a.offsets == (0, 0) + else + @test a.offsets === map(eltype(a.offsets), (0, 0)) + end @test axes(a.parent) == (Base.OneTo(4), Base.OneTo(3)) a = OffsetMatrix{Nothing}(nothing, inds) @@ -655,7 +688,7 @@ Base.Int(a::WeirdInteger) = a @test_throws Union{ArgumentError, ErrorException} OffsetMatrix{Float64}(undef, 2, -2) # only positive numbers works # nested OffsetMatrices - for inds in Any[offsets, offsets_big] + for inds in offsets_all a = OffsetMatrix{Float64}(undef, inds) b = OffsetMatrix(a, inds); b2 = OffsetMatrix(a, inds...); @test eltype(b) === eltype(b2) === Float64 @@ -667,7 +700,11 @@ Base.Int(a::WeirdInteger) = a # test offsets a = OffsetMatrix{Float64}(undef, inds) ax = (IdOffsetRange(Base.OneTo(4), -2), IdOffsetRange(Base.OneTo(3), -1)) - @test a.offsets === (-2, -1) + if eltype(a.offsets) == BigInt # Note BigInt.((0, 0)) === BigInt.((0, 0)) is false + @test a.offsets == (-2, -1) + else + @test a.offsets === (-2, -1) + end @test axes(a.parent) == (Base.OneTo(4), Base.OneTo(3)) @test axes(a) === ax a = OffsetMatrix{Nothing}(nothing, inds) @@ -694,13 +731,22 @@ Base.Int(a::WeirdInteger) = a oa2 = OffsetMatrix(a, inds) oa3 = OffsetArray(a, inds...) oa4 = OffsetArray(a, inds) - @test oa1 === oa2 === oa3 === oa4 + if eltype(oa1.offsets) == BigInt # Note BigInt.((0, 0)) === BigInt.((0, 0)) is false + @test oa1 == oa2 == oa3 == oa4 + @test oa1.offsets == (-2, -1) + else + @test oa1 === oa2 === oa3 === oa4 + @test oa1.offsets === (-2, -1) + end @test axes(oa1) === ax @test parent(oa1) === a - @test oa1.offsets === (-2, -1) end oa = OffsetArray(a, :, axes(a, 2)) - @test oa === OffsetArray(a, (axes(oa, 1), :)) === OffsetArray(a, axes(a)) === OffsetMatrix(a, (axes(oa, 1), :)) === OffsetMatrix(a, axes(a)) + if eltype(oa.offsets) == BigInt # + @test oa == OffsetArray(a, (:, axes(a, 2))) == OffsetArray(a, axes(a)) == OffsetMatrix(a, (IdOffsetRange(axes(a)[1], 0), axes(a, 2))) + else + @test oa === OffsetArray(a, (:, axes(a, 2))) === OffsetArray(a, axes(a)) === OffsetMatrix(a, (IdOffsetRange(axes(a)[1], 0), axes(a, 2))) + end @test oa == a @test axes(oa) == axes(a) @test axes(oa) !== axes(a) @@ -713,7 +759,11 @@ Base.Int(a::WeirdInteger) = a oa = OffsetArray(a, -1, -2) for inds in Any[.-oa.offsets, one_based_axes...] ooa = OffsetArray(oa, inds) - @test ooa === OffsetArray(oa, inds...) === OffsetMatrix(oa, inds) === OffsetMatrix(oa, inds...) + if eltype(ooa.offsets) == BigInt # Note BigInt.((-1, -2)) === BigInt.((-1, -2)) is false + @test ooa == OffsetArray(oa, inds...) == OffsetMatrix(oa, inds) == OffsetMatrix(oa, inds...) + else + @test ooa === OffsetArray(oa, inds...) === OffsetMatrix(oa, inds) === OffsetMatrix(oa, inds...) + end @test typeof(parent(ooa)) <: Matrix @test ooa == a @test axes(ooa) == axes(a) @@ -813,8 +863,9 @@ Base.Int(a::WeirdInteger) = a # ndim of an OffsetArray should match that of the parent @test_throws TypeError OffsetArray{Float64,3,Matrix{Float64}} - # should throw a TypeError if the offsets can not be converted to Ints - @test_throws TypeError OffsetVector{Int,Vector{Int}}(zeros(Int,2), (WeirdInteger(1),)) + # should throw an error if the offset type doesn't support proper arithmetic operations + # (previously threw TypeError when converting to Int, now throws during overflow_check) + @test_throws Exception OffsetVector{Int,Vector{Int}}(zeros(Int,2), (WeirdInteger(1),)) end @testset "custom range types" begin