Skip to content
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "InfiniteArrays"
uuid = "4858937d-0d70-526a-a4dd-2d5cb5dd786c"
version = "0.15.10"
version = "0.15.11"

[deps]
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
Expand All @@ -26,7 +26,7 @@ InfiniteArraysStatisticsExt = "Statistics"
[compat]
Aqua = "0.8"
ArrayLayouts = "1.8"
BandedMatrices = "1.7.6"
BandedMatrices = "1.11"
Base64 = "1"
BlockArrays = "1.0"
BlockBandedMatrices = "0.13"
Expand Down
5 changes: 3 additions & 2 deletions src/InfiniteArrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import Base: *, +, -, /, <, ==, >, \, ≤, ≥, (:), @propagate_inbounds,
searchsortedfirst, searchsortedlast, setindex!, show, show_circular, show_delim_array, sign,
signbit, similar, size, sort, sort!, step, sum, tail,
to_shape, transpose, unaliascopy, union, unitrange_last, unsafe_convert, unsafe_indices, unsafe_length,
vcat, zeros, copyto!, range_start_step_length
vcat, zeros, copyto!, range_start_step_length, undef_ref_str, alignment, IndexStyle, IndexCartesian

if VERSION ≥ v"1.11.0-DEV.21"
using LinearAlgebra: UpperOrLowerTriangular
Expand All @@ -41,7 +41,7 @@ import LazyArrays: AbstractLazyLayout, AbstractCachedVector, ApplyLayout, Cached
LazyArrayStyle, LazyLayout, LazyMatrix, PaddedColumns, _padded_sub_materialize, sub_paddeddata,
ApplyBandedLayout, BroadcastBandedLayout, islazy_layout

import LinearAlgebra: AdjOrTrans, HermOrSym, diag, norm, norm1, norm2, normp
import LinearAlgebra: AdjOrTrans, HermOrSym, diag, norm, norm1, norm2, normp, AdjOrTransAbsVec

import LazyArrays: AbstractPaddedLayout

Expand Down Expand Up @@ -204,6 +204,7 @@ end
return LazyArrays.searchsortedlast_recursive(n, x, args...)
end

include("biinfrange.jl")

collect(G::Base.Generator{<:InfRanges}) = BroadcastArray(G.f, G.iter)

Expand Down
230 changes: 230 additions & 0 deletions src/biinfrange.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
"""
BiInfUnitRange()

Represent -∞:∞ with offset indexing
"""
struct BiInfUnitRange{T<:Real} <: AbstractInfUnitRange{T} end

BiInfUnitRange() = BiInfUnitRange{Int}()

AbstractArray{T}(::BiInfUnitRange) where T<:Real = BiInfUnitRange{T}()
AbstractVector{T}(::BiInfUnitRange) where T<:Real = BiInfUnitRange{T}()

unitrange(a::BiInfUnitRange) = a
Base.has_offset_axes(::BiInfUnitRange) = true

getindex(v::BiInfUnitRange{T}, i::Integer) where T = convert(T, i)
function getindex(x::BiInfUnitRange, i::PosInfinity)
isinf(length(x)) || throw(BoundsError(x,y))
ℵ₀
end
function getindex(x::BiInfUnitRange, i::Infinity)
isinf(length(x)) || throw(BoundsError(x,y))
ℵ₀
end

getindex(v::BiInfUnitRange{T}, i::RealInfinity) where T = i
axes(::BiInfUnitRange) = (BiInfUnitRange(),)
first(::BiInfUnitRange) = -∞
show(io::IO, ::BiInfUnitRange{Int}) = print(io, "BiInfUnitRange()")

getindex(r::BiInfUnitRange{T}, s::AbstractUnitRange{<:Integer}) where T = convert(AbstractVector{T}, s)

IndexStyle(::Type{<:AdjOrTransAbsVec{<:Any,<:BiInfUnitRange}}) = IndexCartesian()

Base.Broadcast.axistype(a::BiInfUnitRange, ::OneTo) = a

function Base._print_matrix(io, @nospecialize(X::AbstractVecOrMat), pre, sep, post, hdots, vdots, ddots, hmod, vmod, ::BiInfUnitRange, colsA)
hmod, vmod = Int(hmod)::Int, Int(vmod)::Int
ncols, idxlast = length(colsA), last(colsA)
if !(get(io, :limit, false)::Bool)
screenheight = screenwidth = typemax(Int)
else
sz = displaysize(io)::Tuple{Int,Int}
screenheight, screenwidth = sz[1] - 4, sz[2]
end
screenwidth -= length(pre)::Int + length(post)::Int
presp = repeat(" ", length(pre)::Int) # indent each row to match pre string
postsp = ""
@assert textwidth(hdots) == textwidth(ddots)
sepsize = length(sep)::Int
n = length(colsA)
# To figure out alignments, only need to look at as many rows as could
# fit down screen. If screen has at least as many rows as A, look at A.
# If not, then we only need to look at the first and last chunks of A,
# each half a screen height in size.
quarterheight = div(screenheight,4)
halfheight = 2quarterheight+1
rowsA = -quarterheight:quarterheight
# Similarly for columns, only necessary to get alignments for as many
# columns as could conceivably fit across the screen
maxpossiblecols = div(screenwidth, 1+sepsize)
if n > maxpossiblecols
colsA = [colsA[(0:maxpossiblecols-1) .+ firstindex(colsA)]; colsA[(end-maxpossiblecols+1):end]]
else
colsA = [colsA;]
end
A = alignment(io, X, rowsA, colsA, screenwidth, screenwidth, sepsize, ncols)
# Nine-slicing is accomplished using print_matrix_row repeatedly
if n <= length(A) # rows don't fit, cols do, so only vertical ellipsis
print_matrix_vdots(io, vdots, A, sep, vmod, 1, false)
print(io, postsp * '\n')
for i in rowsA
print(io, i == first(rowsA) ? pre : presp)
i == 0 && print(io, "\e[1m")
print_matrix_row(io, X,A,i,colsA,sep,idxlast)
i == 0 && print(io, "\e[0m")
print(io, i == last(rowsA) ? post : postsp)
if i != rowsA[end] || i == rowsA[halfheight]; println(io); end
if i == rowsA[halfheight]
print(io, i == first(rowsA) ? pre : presp)
print_matrix_vdots(io, vdots, A, sep, vmod, 1, false)
print(io, i == last(rowsA) ? post : postsp * '\n')
end
end
else # neither rows nor cols fit, so use all 3 kinds of dots
c = div(screenwidth-length(hdots)::Int+1,2)+1
Ralign = reverse(alignment(io, X, rowsA, reverse(colsA), c, c, sepsize, ncols))
c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots)::Int
Lalign = alignment(io, X, rowsA, colsA, c, c, sepsize, ncols)
r = mod((length(Ralign)-n+1),vmod) # where to put dots on right half
for i in rowsA
print(io, i == first(rowsA) ? pre : presp)
print_matrix_row(io, X,Lalign,i,colsA[1:length(Lalign)],sep,idxlast)
print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots)::Int))
print_matrix_row(io, X,Ralign,i,(n-length(Ralign)).+colsA,sep,idxlast)
print(io, i == last(rowsA) ? post : postsp)
if i != rowsA[end] || i == rowsA[halfheight]; println(io); end
if i == rowsA[halfheight]
print(io, i == first(rowsA) ? pre : presp)
print_matrix_vdots(io, vdots, Lalign, sep, vmod, 1, true)
print(io, ddots)
print_matrix_vdots(io, vdots, Ralign, sep, vmod, r, false)
print(io, i == last(rowsA) ? post : postsp * '\n')
end
end
end
if isempty(rowsA)
print(io, pre)
print(io, vdots)
length(colsA) > 1 && print(io, " ", ddots)
print(io, post)
end
end


function Base._print_matrix(io, @nospecialize(X::AbstractVecOrMat), pre, sep, post, hdots, vdots, ddots, hmod, vmod, rowsA, ::BiInfUnitRange)
hmod, vmod = Int(hmod)::Int, Int(vmod)::Int
if !(get(io, :limit, false)::Bool)
screenheight = screenwidth = typemax(Int)
else
sz = displaysize(io)::Tuple{Int,Int}
screenheight, screenwidth = sz[1] - 4, sz[2]
end
screenwidth -= length(pre)::Int + length(post)::Int
presp = repeat(" ", length(pre)::Int) # indent each row to match pre string
postsp = ""
@assert textwidth(hdots) == textwidth(ddots)
sepsize = length(sep)::Int
m = length(rowsA)

# To figure out alignments, only need to look at as many rows as could
# fit down screen. If screen has at least as many rows as A, look at A.
# If not, then we only need to look at the first and last chunks of A,
# each half a screen height in size.
halfheight = div(screenheight,2)
if m > screenheight
rowsA = [rowsA[(0:halfheight-1) .+ firstindex(rowsA)]; rowsA[(end-div(screenheight-1,2)+1):end]]
else
rowsA = [rowsA;]
end

# Similarly for columns, only necessary to get alignments for as many
# columns as could conceivably fit across the screen

halfmaxpossiblecols = div(screenwidth, 4*(1+sepsize))
maxpossiblecols = 2halfmaxpossiblecols + 1
colsA = -halfmaxpossiblecols:halfmaxpossiblecols

A = alignment(io, X, rowsA, colsA, screenwidth, screenwidth, sepsize, ℵ₀)
c = div(screenwidth-length(hdots)::Int+1,2)+1 # what goes to right of ellipsis
Ralign = reverse(alignment(io, X, rowsA, reverse(colsA), c, c, sepsize, ℵ₀)) # alignments for right
c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots)::Int
Lalign = alignment(io, X, rowsA, colsA, c, c, sepsize, ℵ₀) # alignments for left of ellipsis

print(io, hdots)
for i in rowsA
print(io, i == first(rowsA) ? pre : presp)
print_matrix_row(io, X,Lalign,i,colsA[1:length(Lalign)],sep,ℵ₀)
print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots)::Int))
print_matrix_row(io, X, Ralign, i, ℵ₀ .+ colsA, sep, ℵ₀)
print(io, i == last(rowsA) ? post : postsp)
if i != last(rowsA); println(io); end
end
end



function Base._print_matrix(io, @nospecialize(X::AbstractVecOrMat), pre, sep, post, hdots, vdots, ddots, hmod, vmod, ::BiInfUnitRange, ::BiInfUnitRange)
hmod, vmod = Int(hmod)::Int, Int(vmod)::Int
if !(get(io, :limit, false)::Bool)
screenheight = screenwidth = typemax(Int)
else
sz = displaysize(io)::Tuple{Int,Int}
screenheight, screenwidth = sz[1] - 4, sz[2]
end
screenwidth -= length(pre)::Int + length(post)::Int
presp = repeat(" ", length(pre)::Int) # indent each row to match pre string
postsp = ""
@assert textwidth(hdots) == textwidth(ddots)
sepsize = length(sep)::Int

# To figure out alignments, only need to look at as many rows as could
# fit down screen. If screen has at least as many rows as A, look at A.
# If not, then we only need to look at the first and last chunks of A,
# each half a screen height in size.
quarterheight = div(screenheight,4)
halfheight = 2quarterheight+1
rowsA = -quarterheight:quarterheight
# Similarly for columns, only necessary to get alignments for as many
# columns as could conceivably fit across the screen

halfmaxpossiblecols = div(screenwidth, 4*(1+sepsize))
maxpossiblecols = 2halfmaxpossiblecols + 1
colsA = -halfmaxpossiblecols:halfmaxpossiblecols

A = alignment(io, X, rowsA, colsA, screenwidth, screenwidth, sepsize, ℵ₀)
c = div(screenwidth-length(hdots)::Int+1,2)+1
Ralign = reverse(alignment(io, X, rowsA, reverse(colsA), c, c, sepsize, ℵ₀))
c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots)::Int
Lalign = alignment(io, X, rowsA, colsA, c, c, sepsize, ℵ₀)
r = 1 # where to put dots on right half

for i in rowsA
if i == rowsA[1]
print(io, i == first(rowsA) ? pre : presp)
print_matrix_vdots(io, vdots, Lalign, sep, vmod, 1, true)
print(io, ddots)
print(io, i == last(rowsA) ? post : postsp * '\n')
end

print(io, i == first(rowsA) ? pre : presp)
print_matrix_row(io, X,Lalign,i,colsA,sep,ℵ₀)
print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots)::Int))
# print_matrix_row(io, X,Ralign,i,colsA,sep,ℵ₀)
print(io, i == last(rowsA) ? post : postsp)
if i != rowsA[end] || i == rowsA[halfheight]; println(io); end
if i == rowsA[halfheight]
print(io, i == first(rowsA) ? pre : presp)
print_matrix_vdots(io, vdots, Lalign, sep, vmod, 1, true)
print(io, ddots)
print(io, i == last(rowsA) ? post : postsp * '\n')
end
end
if isempty(rowsA)
print(io, pre)
print(io, vdots)
length(colsA) > 1 && print(io, " ", ddots)
print(io, post)
end
end
2 changes: 2 additions & 0 deletions src/infrange.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ _range(a::Real, ::Nothing, ::Nothing, len::InfiniteCardinal{0})
_range(a::AbstractFloat, ::Nothing, ::Nothing, len::InfiniteCardinal{0}) = _range(a, oftype(a, 1), nothing, len)
_range(a::T, st::T, ::Nothing, ::InfiniteCardinal{0}) where T<:IEEEFloat = InfStepRange{T,T}(a, st)
_range(a::T, st::T, ::Nothing, ::InfiniteCardinal{0}) where T<:AbstractFloat = InfStepRange{T,T}(a, st)
_range(a::Infinities.AllInfinities, ::Nothing, ::Nothing, length::Int) = Fill(a, length)
range_start_step_length(a, st, ::InfiniteCardinal{0}) = InfStepRange(a, st)
range_start_step_length(a::Real, st::IEEEFloat, len::InfiniteCardinal{0}) = range_start_step_length(promote(a, st)..., len)
range_start_step_length(a::IEEEFloat, st::Real, len::InfiniteCardinal{0}) = range_start_step_length(promote(a, st)..., len)
Expand Down Expand Up @@ -442,6 +443,7 @@ sum(r::InfRanges{<:Real}) = last(r)
in(x::Union{Infinity,RealInfinity}, r::InfRanges) = false # never reach it...
in(x::Infinity, r::InfRanges{<:Integer}) = false # never reach it...
in(x::Real, r::InfRanges{<:Real}) = _in_range(x, r)
in(x::Integer, r::InfUnitRange) = x ≥ first(r)
# This method needs to be defined separately since -(::T, ::T) can be implemented
# even if -(::T, ::Real) is not
in(x::T, r::InfRanges{T}) where {T} = _in_range(x, r)
Expand Down
5 changes: 5 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,10 @@ end
@test_throws ArgumentError [1:∞; [1.0]; 1:∞]
@test_throws ArgumentError [1:∞; 1; [1]]
end

@testset "range from ∞" begin
@test range(ℵ₀; length=5) ≡ Fill(ℵ₀, 5)
end
end

@testset "fill" begin
Expand Down Expand Up @@ -1300,3 +1304,4 @@ include("test_infconv.jl")
include("test_infblock.jl")
include("test_infbanded.jl")
include("test_infblockbanded.jl")
include("test_biinfrange.jl")
43 changes: 43 additions & 0 deletions test/test_biinfrange.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using InfiniteArrays, Base64, Test
using InfiniteArrays: BiInfUnitRange

@testset "-∞:∞" begin
r = BiInfUnitRange()

@test AbstractArray{Int}(r) ≡ AbstractVector{Int}(r) ≡ r
@test Base.has_offset_axes(r)

@test r[-∞] ≡ -∞
@test r[∞] ≡ r[ℵ₀] ≡ ℵ₀
@test r[-3:3] ≡ -3:3



@test stringmime("text/plain", r) == "BiInfUnitRange()"

@test Fill(1, (r,))[-5] == 1
@test exp.(r)[-3] == exp(-3)


@test stringmime("text/plain", r * (1:10)'; context= IOContext(IOBuffer(), :limit=>true)) == "(ℵ₀-element BiInfUnitRange{$Int} with indices BiInfUnitRange()) .* (1×10 adjoint(::UnitRange{$Int}) with eltype $Int) with indices BiInfUnitRange()×Base.OneTo(10):\n ⋮ ⋮ \n -5 -10 -15 -20 -25 -30 -35 -40 -45 -50\n -4 -8 -12 -16 -20 -24 -28 -32 -36 -40\n -3 -6 -9 -12 -15 -18 -21 -24 -27 -30\n -2 -4 -6 -8 -10 -12 -14 -16 -18 -20\n -1 -2 -3 -4 -5 -6 -7 -8 -9 -10\n \e[1m 0 0 0 0 0 0 0 0 0 0\e[0m\n 1 2 3 4 5 6 7 8 9 10\n 2 4 6 8 10 12 14 16 18 20\n 3 6 9 12 15 18 21 24 27 30\n 4 8 12 16 20 24 28 32 36 40\n 5 10 15 20 25 30 35 40 45 50\n ⋮ ⋮ "
@test stringmime("text/plain", r * (1:1000)'; context= IOContext(IOBuffer(), :limit=>true)) == "(ℵ₀-element BiInfUnitRange{$Int} with indices BiInfUnitRange()) .* (1×1000 adjoint(::UnitRange{$Int}) with eltype $Int) with indices BiInfUnitRange()×Base.OneTo(1000):
-5 -10 -15 -20 -25 -30 -35 -40 … -4980 -4985 -4990 -4995 -5000
-4 -8 -12 -16 -20 -24 -28 -32 -3984 -3988 -3992 -3996 -4000
-3 -6 -9 -12 -15 -18 -21 -24 -2988 -2991 -2994 -2997 -3000
-2 -4 -6 -8 -10 -12 -14 -16 -1992 -1994 -1996 -1998 -2000
-1 -2 -3 -4 -5 -6 -7 -8 -996 -997 -998 -999 -1000
0 0 0 0 0 0 0 0 … 0 0 0 0 0
1 2 3 4 5 6 7 8 996 997 998 999 1000
2 4 6 8 10 12 14 16 1992 1994 1996 1998 2000
3 6 9 12 15 18 21 24 2988 2991 2994 2997 3000
4 8 12 16 20 24 28 32 3984 3988 3992 3996 4000
5 10 15 20 25 30 35 40 … 4980 4985 4990 4995 5000
⋮ ⋮ ⋱ ⋮ "

@test stringmime("text/plain", r.^2; context= IOContext(IOBuffer(), :limit=>true)) == "(ℵ₀-element BiInfUnitRange{$Int} with indices BiInfUnitRange()) .^ 2 with indices BiInfUnitRange():\n ⋮\n 25\n 16\n 9\n 4\n 1\n \e[1m 0\e[0m\n 1\n 4\n 9\n 16\n 25\n ⋮"

@test isassigned(r', 1,-12)
@test stringmime("text/plain", r'; context= IOContext(IOBuffer(), :limit=>true)) == "1×ℵ₀ adjoint(::BiInfUnitRange{$Int}) with eltype $Int with indices Base.OneTo(1)×BiInfUnitRange():\n … -6 -5 -4 -3 -2 -1 0 1 2 3 … "

@test stringmime("text/plain", r * r'; context= IOContext(IOBuffer(), :limit=>true)) == "(ℵ₀-element BiInfUnitRange{$Int} with indices BiInfUnitRange()) .* (1×ℵ₀ adjoint(::BiInfUnitRange{$Int}) with eltype $Int with indices Base.OneTo(1)×BiInfUnitRange()) with indices BiInfUnitRange()×BiInfUnitRange():\n ⋮ ⋮ ⋱ \n 30 25 20 15 10 5 0 -5 … \n 24 20 16 12 8 4 0 -4 \n 18 15 12 9 6 3 0 -3 \n 12 10 8 6 4 2 0 -2 \n 6 5 4 3 2 1 0 -1 \n 0 0 0 0 0 0 0 0 … \n -6 -5 -4 -3 -2 -1 0 1 \n -12 -10 -8 -6 -4 -2 0 2 \n -18 -15 -12 -9 -6 -3 0 3 \n -24 -20 -16 -12 -8 -4 0 4 \n -30 -25 -20 -15 -10 -5 0 5 … \n ⋮ ⋮ ⋱ "
end
Loading