Skip to content

Commit 633dac8

Browse files
authored
minimum/maximum/issorted for RangeCumSum of AbstractUnitRange (#221)
* minimum/maximum/issorted for RangeCumSum of AbstractUnitRange * Restrict issorted to integer AbstractUnitRanges * Tests for infinite ranges
1 parent d3505c4 commit 633dac8

File tree

3 files changed

+92
-6
lines changed

3 files changed

+92
-6
lines changed

src/cumsum.jl

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,52 @@ union(a::RangeCumsum{<:Any,<:OneTo}, b::RangeCumsum{<:Any,<:OneTo}) =
6464

6565
sort!(a::RangeCumsum{<:Any,<:Base.OneTo}) = a
6666
sort(a::RangeCumsum{<:Any,<:Base.OneTo}) = a
67+
Base.issorted(a::RangeCumsum{<:Any,<:Base.OneTo}) = true
68+
function Base.issorted(a::RangeCumsum{<:Any,<:AbstractUnitRange{<:Integer}})
69+
r = parent(a)
70+
r2 = r[firstindex(r):searchsortedlast(r, zero(eltype(r)))]
71+
# at max one negative value is allowed
72+
length(r2) <= 1 + (last(r) >= 0)
73+
end
74+
75+
function Base.minimum(a::RangeCumsum{<:Any, <:OneTo})
76+
isempty(a) && throw(ArgumentError("RangeCumsum must be non-empty"))
77+
first(a)
78+
end
79+
function Base.maximum(a::RangeCumsum{<:Any, <:OneTo})
80+
isempty(a) && throw(ArgumentError("RangeCumsum must be non-empty"))
81+
last(a)
82+
end
83+
function Base.maximum(a::RangeCumsum{<:Any, <:AbstractUnitRange{<:Integer}})
84+
isempty(a) && throw(ArgumentError("RangeCumsum must be non-empty"))
85+
r = parent(a)
86+
if -first(r) in r
87+
r2 = r[searchsortedfirst(r, -first(r)+1):end]
88+
max(zero(eltype(r)), sum(r2))
89+
else
90+
max(first(r), sum(r))
91+
end
92+
end
93+
function Base.minimum(a::RangeCumsum{<:Any, <:AbstractUnitRange{<:Integer}})
94+
isempty(a) && throw(ArgumentError("RangeCumsum must be non-empty"))
95+
r = parent(a)
96+
if zero(eltype(r)) in r
97+
r2 = r[firstindex(r):searchsortedlast(r, zero(eltype(r)))]
98+
min(sum(r2), zero(eltype(r)))
99+
else
100+
min(first(r), sum(r))
101+
end
102+
end
67103

68104
convert(::Type{RangeCumsum{T,R}}, r::RangeCumsum) where {T,R} = RangeCumsum{T,R}(convert(R, r.range))
69105

70-
function Broadcast.broadcasted(::Broadcast.DefaultArrayStyle{1}, ::typeof(-), r::RangeCumsum)
106+
function Broadcast.broadcasted(::typeof(-), r::RangeCumsum)
71107
RangeCumsum(.-r.range)
72108
end
73-
function Broadcast.broadcasted(::Broadcast.DefaultArrayStyle{1}, ::typeof(*), x::Number, r::RangeCumsum)
109+
function Broadcast.broadcasted(::typeof(*), x::Number, r::RangeCumsum)
74110
RangeCumsum(x * r.range)
75111
end
76-
function Broadcast.broadcasted(::Broadcast.DefaultArrayStyle{1}, ::typeof(*), r::RangeCumsum, x::Number)
112+
function Broadcast.broadcasted(::typeof(*), r::RangeCumsum, x::Number)
77113
RangeCumsum(r.range * x)
78114
end
79115

test/infinitearrays.jl

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module InfiniteArrays
1111
Base.axes(r::AbstractInfUnitRange) = (OneToInf(),)
1212

1313
Base.IteratorSize(::Type{<:AbstractInfUnitRange}) = Base.IsInfinite()
14+
Base.sum(r::AbstractInfUnitRange) = last(r)
1415

1516
"""
1617
OneToInf(n)
@@ -26,6 +27,7 @@ module InfiniteArrays
2627
Base.first(r::OneToInf{T}) where {T} = oneunit(T)
2728
Base.oneto(::InfiniteCardinal{0}) = OneToInf()
2829

30+
2931
struct InfUnitRange{T<:Real} <: AbstractInfUnitRange{T}
3032
start::T
3133
end
@@ -34,8 +36,16 @@ module InfiniteArrays
3436
InfUnitRange{T}(a::AbstractInfUnitRange) where T<:Real = InfUnitRange{T}(first(a))
3537
InfUnitRange(a::AbstractInfUnitRange{T}) where T<:Real = InfUnitRange{T}(first(a))
3638
Base.:(:)(start::T, stop::InfiniteCardinal{0}) where {T<:Integer} = InfUnitRange{T}(start)
37-
function getindex(v::InfUnitRange{T}, i::Integer) where T
38-
@boundscheck i > 0 || Base.throw_boundserror(v, i)
39+
function Base.getindex(v::InfUnitRange{T}, i::Integer) where T
40+
@boundscheck i > 0 || throw(BoundsError(v, i))
3941
convert(T, first(v) + i - 1)
4042
end
43+
function Base.getindex(v::InfUnitRange{T}, i::AbstractUnitRange{<:Integer}) where T
44+
@boundscheck checkbounds(v, i)
45+
v[first(i)]:v[last(i)]
46+
end
47+
function Base.getindex(v::InfUnitRange{T}, i::AbstractInfUnitRange{<:Integer}) where T
48+
@boundscheck checkbounds(v, first(i))
49+
v[first(i)]:ℵ₀
50+
end
4151
end

test/test_cumsum.jl

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module TestCumsum
22

3-
using ArrayLayouts, Test
3+
using ArrayLayouts, Test, Infinities
44

55
include("infinitearrays.jl")
66

@@ -84,6 +84,46 @@ cmpop(p) = isinteger(real(first(p))) && isinteger(real(step(p))) ? (==) : (≈)
8484
test_broadcast(3.5 + 2im, r)
8585
end
8686
end
87+
88+
@testset "issorted" begin
89+
@testset "RangeCumsum($start:$stop)" for start in -15:15, stop in start-1:20
90+
a = RangeCumsum(start:stop)
91+
v = Vector(a)
92+
@test issorted(a) == issorted(v)
93+
end
94+
a = RangeCumsum(Base.OneTo(4))
95+
@test issorted(a)
96+
a = RangeCumsum(Base.OneTo(0))
97+
@test issorted(a)
98+
end
99+
100+
@testset "minimum/maximum" begin
101+
@testset "RangeCumsum($start:$stop)" for start in -15:15, stop in start:20
102+
a = RangeCumsum(start:stop)
103+
v = Vector(a)
104+
@test minimum(a) == minimum(v)
105+
@test maximum(a) == maximum(v)
106+
end
107+
a = RangeCumsum(2:1)
108+
@test_throws ArgumentError minimum(a)
109+
@test_throws ArgumentError maximum(a)
110+
111+
a = RangeCumsum(Base.OneTo(4))
112+
@test maximum(a) == 10
113+
@test minimum(a) == 1
114+
a = RangeCumsum(Base.OneTo(0))
115+
@test_throws ArgumentError minimum(a)
116+
@test_throws ArgumentError maximum(a)
117+
118+
@testset "infinite" begin
119+
r = RangeCumsum(-5:ℵ₀)
120+
@test maximum(r) == ℵ₀
121+
@test minimum(r) == minimum(RangeCumsum(-5:5))
122+
r = RangeCumsum(InfiniteArrays.OneToInf())
123+
@test maximum(r) == ℵ₀
124+
@test minimum(r) == 1
125+
end
126+
end
87127
end
88128

89129
end

0 commit comments

Comments
 (0)