From cc5b5050f733da9bc84a3a8aa0c0a4ebd4ba760c Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Mon, 21 Jan 2019 15:45:14 -0600 Subject: [PATCH 01/10] Implement rounding for Intervals --- src/anchoredinterval.jl | 32 ++++++++++++++++++++ src/endpoint.jl | 17 +++++++++++ src/interval.jl | 26 ++++++++++++++++ test/interval.jl | 66 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+) diff --git a/src/anchoredinterval.jl b/src/anchoredinterval.jl index aa62a7aa..f3c1e979 100644 --- a/src/anchoredinterval.jl +++ b/src/anchoredinterval.jl @@ -143,6 +143,7 @@ HourBeginning(anchor::T) where T = HourBeginning{T}(anchor) `HE` is a pseudoconstructor for [`HourEnding`](@ref) that rounds the anchor provided up to the nearest hour. """ +# TODO: update to ceil the interval HE(anchor) = HourEnding(ceil(anchor, Hour)) """ @@ -151,6 +152,7 @@ HE(anchor) = HourEnding(ceil(anchor, Hour)) `HB` is a pseudoconstructor for [`HourBeginning`](@ref) that rounds the anchor provided down to the nearest hour. """ +# TODO: update to floor the interval HB(anchor) = HourBeginning(floor(anchor, Hour)) function Base.copy(x::AnchoredInterval{P,T,L,R}) where {P,T,L,R} @@ -332,6 +334,36 @@ function Base.intersect(a::AnchoredInterval{P,T}, b::AnchoredInterval{Q,T}) wher return AnchoredInterval{new_P, T, L, R}(anchor) end +##### ROUNDING ##### + +for f in (:floor, :ceil, :round) + @eval function Base.$f( + interval::AnchoredInterval{P, T}, + args...; + on::Type{<:Endpoint}=AnchorEndpoint, + ) where {P, T} + anc = if on === AnchorEndpoint + $f(anchor(interval), args...) + elseif on === LeftEndpoint + if P ≤ zero(P) + $f(first(interval), args...) - P + else + $f(first(interval), args...) + end + elseif on === RightEndpoint + if P ≤ zero(P) + $f(last(interval), args...) + else + $f(last(interval), args...) - P + end + else + throw(ArgumentError("Unhandled `on` type: $on")) + end + + return AnchoredInterval{P, T}(anc, inclusivity(interval)) + end +end + ##### UTILITIES ##### function canonicalize(target_type::Type{<:Period}, p::P) where P <: Period diff --git a/src/endpoint.jl b/src/endpoint.jl index f8cad3d2..aba29632 100644 --- a/src/endpoint.jl +++ b/src/endpoint.jl @@ -24,6 +24,7 @@ struct Endpoint{T, D, B <: Bound} end end + Endpoint{T,D,B}(ep) where {T, D, B <: Bounded} = Endpoint{T,D,B}(convert(T, ep)) const LeftEndpoint{T,B} = Endpoint{T, Left, B} where {T,B} @@ -35,6 +36,10 @@ RightEndpoint{B}(ep::T) where {T,B} = RightEndpoint{T,B}(ep) LeftEndpoint(i::AbstractInterval{T,L,R}) where {T,L,R} = LeftEndpoint{T,L}(L !== Unbounded ? first(i) : nothing) RightEndpoint(i::AbstractInterval{T,L,R}) where {T,L,R} = RightEndpoint{T,R}(R !== Unbounded ? last(i) : nothing) +# Unconstructable Endpoint types used for rounding +const AnchorEndpoint{B} = Endpoint{Union{}, Direction{:Anchor}(), B} where {B} +const LeftAndRightEndpoint{B} = Endpoint{Union{}, Direction{:LeftAndRight}(), B} where {B} + endpoint(x::Endpoint) = isbounded(x) ? x.endpoint : nothing bound_type(x::Endpoint{T,D,B}) where {T,D,B} = B @@ -169,3 +174,15 @@ end Base.isless(a, b::RightEndpoint) = isunbounded(b) || a < b.endpoint Base.isless(a::LeftEndpoint, b) = isunbounded(a) || a.endpoint < b + +for f in (:floor, :ceil, :round) + @eval begin + function Base.$f(p::Endpoint{T,D,B}) where {T,D,B} + Endpoint{T,D,B}($f(p.endpoint), p.included) + end + + function Base.$f(p::Endpoint{T,D,B}, duration) where {T <: TimeType,D,B} + Endpoint{T,D,B}($f(p.endpoint, duration), p.included) + end + end +end diff --git a/src/interval.jl b/src/interval.jl index 281c9842..d0fc340d 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -478,6 +478,32 @@ function Base.merge(a::AbstractInterval, b::AbstractInterval) return Interval(left, right) end +##### ROUNDING ##### + +for f in (:floor, :ceil, :round) + @eval function Base.$f( + interval::Interval, + args...; + on::Type{<:Endpoint}=LeftAndRightEndpoint, + ) + left = LeftEndpoint(interval) + right = RightEndpoint(interval) + + if on === LeftAndRightEndpoint + left = $f(left, args...) + right = $f(right, args...) + elseif on === LeftEndpoint + left = $f(left, args...) + elseif on === RightEndpoint + right = $f(right, args...) + else + throw(ArgumentError("Unhandled `on` type: $on")) + end + + return Interval(left, right) + end +end + ##### TIME ZONES ##### function TimeZones.astimezone(i::Interval{ZonedDateTime, L, R}, tz::TimeZone) where {L,R} diff --git a/test/interval.jl b/test/interval.jl index d693c61e..56e47ff9 100644 --- a/test/interval.jl +++ b/test/interval.jl @@ -887,4 +887,70 @@ end end end + + @testset "floor" begin + @test floor(Interval(0.0, 1.0)) == Interval(0.0, 1.0) + @test floor(Interval(0.5, 1.0)) == Interval(0.0, 1.0) + @test floor(Interval(0.0, 1.5)) == Interval(0.0, 1.0) + @test floor(Interval(0.5, 1.5)) == Interval(0.0, 1.0) + + @test floor(Interval(0.0, 1.0), on=LeftEndpoint) == Interval(0.0, 1.0) + @test floor(Interval(0.5, 1.0), on=LeftEndpoint) == Interval(0.0, 1.0) + @test floor(Interval(0.0, 1.5), on=LeftEndpoint) == Interval(0.0, 1.5) + @test floor(Interval(0.5, 1.5), on=LeftEndpoint) == Interval(0.0, 1.5) + + @test floor(Interval(0.0, 1.0), on=RightEndpoint) == Interval(0.0, 1.0) + @test floor(Interval(0.5, 1.0), on=RightEndpoint) == Interval(0.5, 1.0) + @test floor(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.0, 1.0) + @test floor(Interval(0.5, 1.5), on=RightEndpoint) == Interval(0.5, 1.0) + + interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) + expected = Interval(DateTime(2011, 2, 1), DateTime(2011, 2, 2)) + @test floor(interval, Day) == expected + @test floor(interval, Day(1)) == expected + end + + @testset "ceil" begin + @test ceil(Interval(0.0, 1.0)) == Interval(0.0, 1.0) + @test ceil(Interval(0.5, 1.0)) == Interval(1.0, 1.0) + @test ceil(Interval(0.0, 1.5)) == Interval(0.0, 2.0) + @test ceil(Interval(0.5, 1.5)) == Interval(1.0, 2.0) + + @test ceil(Interval(0.0, 1.0), on=LeftEndpoint) == Interval(0.0, 1.0) + @test ceil(Interval(0.5, 1.0), on=LeftEndpoint) == Interval(1.0, 1.0) + @test ceil(Interval(0.0, 1.5), on=LeftEndpoint) == Interval(0.0, 1.5) + @test ceil(Interval(0.5, 1.5), on=LeftEndpoint) == Interval(1.0, 1.5) + + @test ceil(Interval(0.0, 1.0), on=RightEndpoint) == Interval(0.0, 1.0) + @test ceil(Interval(0.5, 1.0), on=RightEndpoint) == Interval(0.5, 1.0) + @test ceil(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.0, 2.0) + @test ceil(Interval(0.5, 1.5), on=RightEndpoint) == Interval(0.5, 2.0) + + interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) + expected = Interval(DateTime(2011, 2, 2), DateTime(2011, 2, 3)) + @test ceil(interval, Day) == expected + @test ceil(interval, Day(1)) == expected + end + + @testset "round" begin + @test round(Interval(0.0, 1.0)) == Interval(0.0, 1.0) + @test round(Interval(0.5, 1.0)) == Interval(0.0, 1.0) + @test round(Interval(0.0, 1.5)) == Interval(0.0, 2.0) + @test round(Interval(0.5, 1.5)) == Interval(0.0, 2.0) + + @test round(Interval(0.0, 1.0), on=LeftEndpoint) == Interval(0.0, 1.0) + @test round(Interval(0.5, 1.0), on=LeftEndpoint) == Interval(0.0, 1.0) + @test round(Interval(0.0, 1.5), on=LeftEndpoint) == Interval(0.0, 1.5) + @test round(Interval(0.5, 1.5), on=LeftEndpoint) == Interval(0.0, 1.5) + + @test round(Interval(0.0, 1.0), on=RightEndpoint) == Interval(0.0, 1.0) + @test round(Interval(0.5, 1.0), on=RightEndpoint) == Interval(0.5, 1.0) + @test round(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.0, 2.0) + @test round(Interval(0.5, 1.5), on=RightEndpoint) == Interval(0.5, 2.0) + + interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) + expected = Interval(DateTime(2011, 2, 1), DateTime(2011, 2, 3)) + @test round(interval, Day) == expected + @test round(interval, Day(1)) == expected + end end From 523be30f3755491181824fc82600d59d14fb9ce7 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Mon, 21 Jan 2019 16:18:24 -0600 Subject: [PATCH 02/10] Tests for AnchoredInterval rounding --- test/anchoredinterval.jl | 84 ++++++++++++++++++++++++++++++++++++++++ test/interval.jl | 3 ++ 2 files changed, 87 insertions(+) diff --git a/test/anchoredinterval.jl b/test/anchoredinterval.jl index dfe78875..d8f6c055 100644 --- a/test/anchoredinterval.jl +++ b/test/anchoredinterval.jl @@ -841,4 +841,88 @@ using Intervals: Bounded, Ending, Beginning, canonicalize, isunbounded @test interval isa AnchoredInterval @test interval == AnchoredInterval{-1,Int,Closed,Open}(2) end + + @testset "floor" begin + @test floor(AnchoredInterval{-0.5}(1.0)) == AnchoredInterval{-0.5}(1.0) + @test floor(AnchoredInterval{+0.5}(1.0)) == AnchoredInterval{+0.5}(1.0) + @test floor(AnchoredInterval{-0.5}(0.5)) == AnchoredInterval{-0.5}(0.0) + @test floor(AnchoredInterval{+0.5}(0.5)) == AnchoredInterval{+0.5}(0.0) + + @test floor(AnchoredInterval{-0.5}(1.0), on=LeftEndpoint) == AnchoredInterval{-0.5}(0.5) + @test floor(AnchoredInterval{+0.5}(1.0), on=LeftEndpoint) == AnchoredInterval{+0.5}(1.0) + @test floor(AnchoredInterval{-0.5}(0.5), on=LeftEndpoint) == AnchoredInterval{-0.5}(0.5) + @test floor(AnchoredInterval{+0.5}(0.5), on=LeftEndpoint) == AnchoredInterval{+0.5}(0.0) + + @test floor(AnchoredInterval{-0.5}(1.0), on=RightEndpoint) == AnchoredInterval{-0.5}(1.0) + @test floor(AnchoredInterval{+0.5}(1.0), on=RightEndpoint) == AnchoredInterval{+0.5}(0.5) + @test floor(AnchoredInterval{-0.5}(0.5), on=RightEndpoint) == AnchoredInterval{-0.5}(0.0) + @test floor(AnchoredInterval{+0.5}(0.5), on=RightEndpoint) == AnchoredInterval{+0.5}(0.5) + + # Test supplying a period to floor to + interval_ending = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 1, 12)) + expected = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 1)) + @test floor(interval_ending, Day) == expected + @test floor(interval_ending, Day(1)) == expected + + interval_beginning = AnchoredInterval{Day(1)}(DateTime(2011, 2, 1, 12)) + expected = AnchoredInterval{Day(1)}(DateTime(2011, 2, 1)) + @test floor(interval_beginning, Day) == expected + @test floor(interval_beginning, Day(1)) == expected + end + + @testset "ceil" begin + @test ceil(AnchoredInterval{-0.5}(1.0)) == AnchoredInterval{-0.5}(1.0) + @test ceil(AnchoredInterval{+0.5}(1.0)) == AnchoredInterval{+0.5}(1.0) + @test ceil(AnchoredInterval{-0.5}(0.5)) == AnchoredInterval{-0.5}(1.0) + @test ceil(AnchoredInterval{+0.5}(0.5)) == AnchoredInterval{+0.5}(1.0) + + @test ceil(AnchoredInterval{-0.5}(1.0), on=LeftEndpoint) == AnchoredInterval{-0.5}(1.5) + @test ceil(AnchoredInterval{+0.5}(1.0), on=LeftEndpoint) == AnchoredInterval{+0.5}(1.0) + @test ceil(AnchoredInterval{-0.5}(0.5), on=LeftEndpoint) == AnchoredInterval{-0.5}(0.5) + @test ceil(AnchoredInterval{+0.5}(0.5), on=LeftEndpoint) == AnchoredInterval{+0.5}(1.0) + + @test ceil(AnchoredInterval{-0.5}(1.0), on=RightEndpoint) == AnchoredInterval{-0.5}(1.0) + @test ceil(AnchoredInterval{+0.5}(1.0), on=RightEndpoint) == AnchoredInterval{+0.5}(1.5) + @test ceil(AnchoredInterval{-0.5}(0.5), on=RightEndpoint) == AnchoredInterval{-0.5}(1.0) + @test ceil(AnchoredInterval{+0.5}(0.5), on=RightEndpoint) == AnchoredInterval{+0.5}(0.5) + + # Test supplying a period to ceil to + interval_ending = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 1, 12)) + expected = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 2)) + @test ceil(interval_ending, Day) == expected + @test ceil(interval_ending, Day(1)) == expected + + interval_beginning = AnchoredInterval{Day(1)}(DateTime(2011, 2, 1, 12)) + expected = AnchoredInterval{Day(1)}(DateTime(2011, 2, 2)) + @test ceil(interval_beginning, Day) == expected + @test ceil(interval_beginning, Day(1)) == expected + end + + @testset "round" begin + @test round(AnchoredInterval{-0.5}(1.0)) == AnchoredInterval{-0.5}(1.0) + @test round(AnchoredInterval{+0.5}(1.0)) == AnchoredInterval{+0.5}(1.0) + @test round(AnchoredInterval{-0.5}(0.5)) == AnchoredInterval{-0.5}(0.0) + @test round(AnchoredInterval{+0.5}(0.5)) == AnchoredInterval{+0.5}(0.0) + + @test round(AnchoredInterval{-0.5}(1.0), on=LeftEndpoint) == AnchoredInterval{-0.5}(0.5) + @test round(AnchoredInterval{+0.5}(1.0), on=LeftEndpoint) == AnchoredInterval{+0.5}(1.0) + @test round(AnchoredInterval{-0.5}(0.5), on=LeftEndpoint) == AnchoredInterval{-0.5}(0.5) + @test round(AnchoredInterval{+0.5}(0.5), on=LeftEndpoint) == AnchoredInterval{+0.5}(0.0) + + @test round(AnchoredInterval{-0.5}(1.0), on=RightEndpoint) == AnchoredInterval{-0.5}(1.0) + @test round(AnchoredInterval{+0.5}(1.0), on=RightEndpoint) == AnchoredInterval{+0.5}(1.5) + @test round(AnchoredInterval{-0.5}(0.5), on=RightEndpoint) == AnchoredInterval{-0.5}(0.0) + @test round(AnchoredInterval{+0.5}(0.5), on=RightEndpoint) == AnchoredInterval{+0.5}(0.5) + + # Test supplying a period to round to + interval_ending = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 1, 12)) + expected = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 2)) + @test round(interval_ending, Day) == expected + @test round(interval_ending, Day(1)) == expected + + interval_beginning = AnchoredInterval{Day(1)}(DateTime(2011, 2, 1, 12)) + expected = AnchoredInterval{Day(1)}(DateTime(2011, 2, 2)) + @test round(interval_beginning, Day) == expected + @test round(interval_beginning, Day(1)) == expected + end end diff --git a/test/interval.jl b/test/interval.jl index 56e47ff9..d9bd84da 100644 --- a/test/interval.jl +++ b/test/interval.jl @@ -904,6 +904,7 @@ @test floor(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.0, 1.0) @test floor(Interval(0.5, 1.5), on=RightEndpoint) == Interval(0.5, 1.0) + # Test supplying a period to floor to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) expected = Interval(DateTime(2011, 2, 1), DateTime(2011, 2, 2)) @test floor(interval, Day) == expected @@ -926,6 +927,7 @@ @test ceil(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.0, 2.0) @test ceil(Interval(0.5, 1.5), on=RightEndpoint) == Interval(0.5, 2.0) + # Test supplying a period to ceil to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) expected = Interval(DateTime(2011, 2, 2), DateTime(2011, 2, 3)) @test ceil(interval, Day) == expected @@ -948,6 +950,7 @@ @test round(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.0, 2.0) @test round(Interval(0.5, 1.5), on=RightEndpoint) == Interval(0.5, 2.0) + # Test supplying a period to round to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) expected = Interval(DateTime(2011, 2, 1), DateTime(2011, 2, 3)) @test round(interval, Day) == expected From 2c3952de3eae719ca997d79a8ec3e840c66d6365 Mon Sep 17 00:00:00 2001 From: Nicole Epp Date: Fri, 9 Oct 2020 12:57:53 -0500 Subject: [PATCH 03/10] Require the on keyword for non-anchored intervals rounding --- src/Intervals.jl | 3 ++ src/anchoredinterval.jl | 14 +++-- src/endpoint.jl | 13 +++-- src/interval.jl | 28 ++++++---- test/anchoredinterval.jl | 15 ++++++ test/endpoint.jl | 5 ++ test/interval.jl | 112 +++++++++++++++++++++++++++------------ 7 files changed, 133 insertions(+), 57 deletions(-) diff --git a/src/Intervals.jl b/src/Intervals.jl index 0c606436..8815230d 100644 --- a/src/Intervals.jl +++ b/src/Intervals.jl @@ -42,6 +42,9 @@ export Bound, AbstractInterval, Interval, AnchoredInterval, + LeftEndpoint, + RightEndpoint, + AnchorEndpoint, HourEnding, HourBeginning, HE, diff --git a/src/anchoredinterval.jl b/src/anchoredinterval.jl index f3c1e979..7ed5d651 100644 --- a/src/anchoredinterval.jl +++ b/src/anchoredinterval.jl @@ -143,8 +143,7 @@ HourBeginning(anchor::T) where T = HourBeginning{T}(anchor) `HE` is a pseudoconstructor for [`HourEnding`](@ref) that rounds the anchor provided up to the nearest hour. """ -# TODO: update to ceil the interval -HE(anchor) = HourEnding(ceil(anchor, Hour)) +HE(anchor) = ceil(HourEnding(anchor), Hour) """ HB(anchor) -> HourBeginning @@ -152,8 +151,7 @@ HE(anchor) = HourEnding(ceil(anchor, Hour)) `HB` is a pseudoconstructor for [`HourBeginning`](@ref) that rounds the anchor provided down to the nearest hour. """ -# TODO: update to floor the interval -HB(anchor) = HourBeginning(floor(anchor, Hour)) +HB(anchor) = floor(HourBeginning(anchor), Hour) function Base.copy(x::AnchoredInterval{P,T,L,R}) where {P,T,L,R} return AnchoredInterval{P,T,L,R}(anchor(x)) @@ -331,17 +329,17 @@ function Base.intersect(a::AnchoredInterval{P,T}, b::AnchoredInterval{Q,T}) wher end L, R = bounds_types(interval) - return AnchoredInterval{new_P, T, L, R}(anchor) + return AnchoredInterval{new_P,T,L,R}(anchor) end ##### ROUNDING ##### for f in (:floor, :ceil, :round) @eval function Base.$f( - interval::AnchoredInterval{P, T}, + interval::AnchoredInterval{P,T,L,R}, args...; on::Type{<:Endpoint}=AnchorEndpoint, - ) where {P, T} + ) where {P,T,L,R} anc = if on === AnchorEndpoint $f(anchor(interval), args...) elseif on === LeftEndpoint @@ -360,7 +358,7 @@ for f in (:floor, :ceil, :round) throw(ArgumentError("Unhandled `on` type: $on")) end - return AnchoredInterval{P, T}(anc, inclusivity(interval)) + return AnchoredInterval{P,T,L,R}(anc) end end diff --git a/src/endpoint.jl b/src/endpoint.jl index aba29632..3666c47f 100644 --- a/src/endpoint.jl +++ b/src/endpoint.jl @@ -27,8 +27,8 @@ end Endpoint{T,D,B}(ep) where {T, D, B <: Bounded} = Endpoint{T,D,B}(convert(T, ep)) -const LeftEndpoint{T,B} = Endpoint{T, Left, B} where {T,B} -const RightEndpoint{T,B} = Endpoint{T, Right, B} where {T,B} +const LeftEndpoint{T,B} = Endpoint{T, Left, B} where {T,B <: Bound} +const RightEndpoint{T,B} = Endpoint{T, Right, B} where {T,B <: Bound} LeftEndpoint{B}(ep::T) where {T,B} = LeftEndpoint{T,B}(ep) RightEndpoint{B}(ep::T) where {T,B} = RightEndpoint{T,B}(ep) @@ -36,9 +36,8 @@ RightEndpoint{B}(ep::T) where {T,B} = RightEndpoint{T,B}(ep) LeftEndpoint(i::AbstractInterval{T,L,R}) where {T,L,R} = LeftEndpoint{T,L}(L !== Unbounded ? first(i) : nothing) RightEndpoint(i::AbstractInterval{T,L,R}) where {T,L,R} = RightEndpoint{T,R}(R !== Unbounded ? last(i) : nothing) -# Unconstructable Endpoint types used for rounding -const AnchorEndpoint{B} = Endpoint{Union{}, Direction{:Anchor}(), B} where {B} -const LeftAndRightEndpoint{B} = Endpoint{Union{}, Direction{:LeftAndRight}(), B} where {B} +# Unconstructable Endpoint type used for rounding AnchoredIntervals +const AnchorEndpoint{B} = Endpoint{Union{}, Direction{:Anchor}(), B} where {B <: Bounded} endpoint(x::Endpoint) = isbounded(x) ? x.endpoint : nothing bound_type(x::Endpoint{T,D,B}) where {T,D,B} = B @@ -178,11 +177,11 @@ Base.isless(a::LeftEndpoint, b) = isunbounded(a) || a.endpoint < b for f in (:floor, :ceil, :round) @eval begin function Base.$f(p::Endpoint{T,D,B}) where {T,D,B} - Endpoint{T,D,B}($f(p.endpoint), p.included) + Endpoint{T,D,B}($f(p.endpoint)) end function Base.$f(p::Endpoint{T,D,B}, duration) where {T <: TimeType,D,B} - Endpoint{T,D,B}($f(p.endpoint, duration), p.included) + Endpoint{T,D,B}($f(p.endpoint, duration)) end end end diff --git a/src/interval.jl b/src/interval.jl index d0fc340d..413accf5 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -482,20 +482,30 @@ end for f in (:floor, :ceil, :round) @eval function Base.$f( - interval::Interval, + interval::Interval{T,L,R}, args...; - on::Type{<:Endpoint}=LeftAndRightEndpoint, - ) + on::Type{<:Endpoint}, + ) where {T,L,R} left = LeftEndpoint(interval) right = RightEndpoint(interval) - if on === LeftAndRightEndpoint - left = $f(left, args...) - right = $f(right, args...) - elseif on === LeftEndpoint - left = $f(left, args...) + if on === LeftEndpoint + # Do nothing if the left endpoint is unbounded + if L <: Bounded + left = $f(left, args...) + # Shifts the interval, span remains the same + if R <: Bounded + right = RightEndpoint{T, R}(left.endpoint + span(interval)) + end + end elseif on === RightEndpoint - right = $f(right, args...) + # Do nothing if the right endpoint is unbounded + if R <: Bounded + right = $f(right, args...) + if L <: Bounded + left = RightEndpoint{T, L}(right.endpoint - span(interval)) + end + end else throw(ArgumentError("Unhandled `on` type: $on")) end diff --git a/test/anchoredinterval.jl b/test/anchoredinterval.jl index d8f6c055..ef97f4e7 100644 --- a/test/anchoredinterval.jl +++ b/test/anchoredinterval.jl @@ -858,6 +858,11 @@ using Intervals: Bounded, Ending, Beginning, canonicalize, isunbounded @test floor(AnchoredInterval{-0.5}(0.5), on=RightEndpoint) == AnchoredInterval{-0.5}(0.0) @test floor(AnchoredInterval{+0.5}(0.5), on=RightEndpoint) == AnchoredInterval{+0.5}(0.5) + @test floor(AnchoredInterval{-0.5}(1.0), on=AnchorEndpoint) == AnchoredInterval{-0.5}(1.0) + @test floor(AnchoredInterval{+0.5}(1.0), on=AnchorEndpoint) == AnchoredInterval{+0.5}(1.0) + @test floor(AnchoredInterval{-0.5}(0.5), on=AnchorEndpoint) == AnchoredInterval{-0.5}(0.0) + @test floor(AnchoredInterval{+0.5}(0.5), on=AnchorEndpoint) == AnchoredInterval{+0.5}(0.0) + # Test supplying a period to floor to interval_ending = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 1, 12)) expected = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 1)) @@ -886,6 +891,11 @@ using Intervals: Bounded, Ending, Beginning, canonicalize, isunbounded @test ceil(AnchoredInterval{-0.5}(0.5), on=RightEndpoint) == AnchoredInterval{-0.5}(1.0) @test ceil(AnchoredInterval{+0.5}(0.5), on=RightEndpoint) == AnchoredInterval{+0.5}(0.5) + @test ceil(AnchoredInterval{-0.5}(1.0), on=AnchorEndpoint) == AnchoredInterval{-0.5}(1.0) + @test ceil(AnchoredInterval{+0.5}(1.0), on=AnchorEndpoint) == AnchoredInterval{+0.5}(1.0) + @test ceil(AnchoredInterval{-0.5}(0.5), on=AnchorEndpoint) == AnchoredInterval{-0.5}(1.0) + @test ceil(AnchoredInterval{+0.5}(0.5), on=AnchorEndpoint) == AnchoredInterval{+0.5}(1.0) + # Test supplying a period to ceil to interval_ending = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 1, 12)) expected = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 2)) @@ -914,6 +924,11 @@ using Intervals: Bounded, Ending, Beginning, canonicalize, isunbounded @test round(AnchoredInterval{-0.5}(0.5), on=RightEndpoint) == AnchoredInterval{-0.5}(0.0) @test round(AnchoredInterval{+0.5}(0.5), on=RightEndpoint) == AnchoredInterval{+0.5}(0.5) + @test round(AnchoredInterval{-0.5}(1.0), on=AnchorEndpoint) == AnchoredInterval{-0.5}(1.0) + @test round(AnchoredInterval{+0.5}(1.0), on=AnchorEndpoint) == AnchoredInterval{+0.5}(1.0) + @test round(AnchoredInterval{-0.5}(0.5), on=AnchorEndpoint) == AnchoredInterval{-0.5}(0.0) + @test round(AnchoredInterval{+0.5}(0.5), on=AnchorEndpoint) == AnchoredInterval{+0.5}(0.0) + # Test supplying a period to round to interval_ending = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 1, 12)) expected = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 2)) diff --git a/test/endpoint.jl b/test/endpoint.jl index e56b8dab..6325812b 100644 --- a/test/endpoint.jl +++ b/test/endpoint.jl @@ -499,4 +499,9 @@ using Intervals: Endpoint, Left, Right, LeftEndpoint, RightEndpoint result = test .== 0 @test result == [false, true, false, false, true, false] end + + @testset "AnchorEndpoint" begin + # Unconstructable type used for rounding anchored intervals + @test_throws MethodError AnchorEndpoint() + end end diff --git a/test/interval.jl b/test/interval.jl index d9bd84da..4c59720d 100644 --- a/test/interval.jl +++ b/test/interval.jl @@ -889,71 +889,117 @@ end @testset "floor" begin - @test floor(Interval(0.0, 1.0)) == Interval(0.0, 1.0) - @test floor(Interval(0.5, 1.0)) == Interval(0.0, 1.0) - @test floor(Interval(0.0, 1.5)) == Interval(0.0, 1.0) - @test floor(Interval(0.5, 1.5)) == Interval(0.0, 1.0) + @test_throws UndefKeywordError floor(Interval(0.0, 1.0)) @test floor(Interval(0.0, 1.0), on=LeftEndpoint) == Interval(0.0, 1.0) - @test floor(Interval(0.5, 1.0), on=LeftEndpoint) == Interval(0.0, 1.0) + @test floor(Interval(0.5, 1.0), on=LeftEndpoint) == Interval(0.0, 0.5) @test floor(Interval(0.0, 1.5), on=LeftEndpoint) == Interval(0.0, 1.5) - @test floor(Interval(0.5, 1.5), on=LeftEndpoint) == Interval(0.0, 1.5) + @test floor(Interval(0.5, 1.5), on=LeftEndpoint) == Interval(0.0, 1.0) @test floor(Interval(0.0, 1.0), on=RightEndpoint) == Interval(0.0, 1.0) @test floor(Interval(0.5, 1.0), on=RightEndpoint) == Interval(0.5, 1.0) - @test floor(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.0, 1.0) - @test floor(Interval(0.5, 1.5), on=RightEndpoint) == Interval(0.5, 1.0) + @test floor(Interval(0.0, 1.5), on=RightEndpoint) == Interval(-0.5, 1.0) + @test floor(Interval(0.5, 1.5), on=RightEndpoint) == Interval(0.0, 1.0) # Test supplying a period to floor to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) - expected = Interval(DateTime(2011, 2, 1), DateTime(2011, 2, 2)) - @test floor(interval, Day) == expected - @test floor(interval, Day(1)) == expected + expected = Interval(DateTime(2011, 2, 1), DateTime(2011, 2, 2, 12)) + @test_throws UndefKeywordError floor(interval, Day) + @test floor(interval, Day; on=LeftEndpoint) == expected + @test floor(interval, Day(1); on=LeftEndpoint) == expected + + expected = Interval(DateTime(2011, 1, 31, 12), DateTime(2011, 2, 2)) + @test floor(interval, Day; on=RightEndpoint) == expected + @test floor(interval, Day(1); on=RightEndpoint) == expected + + # Test unbounded intervals + @test floor(Interval{Closed, Unbounded}(0.0, nothing), on=LeftEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) + @test floor(Interval{Closed, Unbounded}(0.5, nothing), on=LeftEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) + @test floor(Interval{Unbounded, Closed}(nothing, 1.0), on=LeftEndpoint) == Interval{Unbounded, Closed}(nothing, 1.0) + @test floor(Interval{Unbounded, Closed}(nothing, 1.5), on=LeftEndpoint) == Interval{Unbounded, Closed}(nothing, 1.5) + @test floor(Interval{Unbounded, Unbounded}(nothing, nothing), on=LeftEndpoint) == Interval{Unbounded, Unbounded}(nothing, nothing) + + @test floor(Interval{Closed, Unbounded}(0.0, nothing), on=RightEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) + @test floor(Interval{Closed, Unbounded}(0.5, nothing), on=RightEndpoint) == Interval{Closed, Unbounded}(0.5, nothing) + @test floor(Interval{Unbounded, Closed}(nothing, 1.0), on=RightEndpoint) == Interval{Unbounded, Closed}(nothing, 1.0) + @test floor(Interval{Unbounded, Closed}(nothing, 1.5), on=RightEndpoint) == Interval{Unbounded, Closed}(nothing, 1.0) + @test floor(Interval{Unbounded, Unbounded}(nothing, nothing), on=RightEndpoint) == Interval{Unbounded, Unbounded}(nothing, nothing) end @testset "ceil" begin - @test ceil(Interval(0.0, 1.0)) == Interval(0.0, 1.0) - @test ceil(Interval(0.5, 1.0)) == Interval(1.0, 1.0) - @test ceil(Interval(0.0, 1.5)) == Interval(0.0, 2.0) - @test ceil(Interval(0.5, 1.5)) == Interval(1.0, 2.0) + @test_throws UndefKeywordError ceil(Interval(0.0, 1.0)) @test ceil(Interval(0.0, 1.0), on=LeftEndpoint) == Interval(0.0, 1.0) - @test ceil(Interval(0.5, 1.0), on=LeftEndpoint) == Interval(1.0, 1.0) + @test ceil(Interval(0.5, 1.0), on=LeftEndpoint) == Interval(1.0, 1.5) @test ceil(Interval(0.0, 1.5), on=LeftEndpoint) == Interval(0.0, 1.5) - @test ceil(Interval(0.5, 1.5), on=LeftEndpoint) == Interval(1.0, 1.5) + @test ceil(Interval(0.5, 1.5), on=LeftEndpoint) == Interval(1.0, 2.0) @test ceil(Interval(0.0, 1.0), on=RightEndpoint) == Interval(0.0, 1.0) @test ceil(Interval(0.5, 1.0), on=RightEndpoint) == Interval(0.5, 1.0) - @test ceil(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.0, 2.0) - @test ceil(Interval(0.5, 1.5), on=RightEndpoint) == Interval(0.5, 2.0) + @test ceil(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.5, 2.0) + @test ceil(Interval(0.5, 1.5), on=RightEndpoint) == Interval(1.0, 2.0) # Test supplying a period to ceil to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) - expected = Interval(DateTime(2011, 2, 2), DateTime(2011, 2, 3)) - @test ceil(interval, Day) == expected - @test ceil(interval, Day(1)) == expected + expected = Interval(DateTime(2011, 2, 2), DateTime(2011, 2, 3, 12)) + @test_throws UndefKeywordError ceil(interval, Day) + @test ceil(interval, Day; on=LeftEndpoint) == expected + @test ceil(interval, Day(1); on=LeftEndpoint) == expected + + expected = Interval(DateTime(2011, 2, 1, 12), DateTime(2011, 2, 3)) + @test ceil(interval, Day; on=RightEndpoint) == expected + @test ceil(interval, Day(1); on=RightEndpoint) == expected + + # Test unbounded intervals + @test ceil(Interval{Closed, Unbounded}(0.0, nothing), on=LeftEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) + @test ceil(Interval{Closed, Unbounded}(0.5, nothing), on=LeftEndpoint) == Interval{Closed, Unbounded}(1.0, nothing) + @test ceil(Interval{Unbounded, Closed}(nothing, 1.0), on=LeftEndpoint) == Interval{Unbounded, Closed}(nothing, 1.0) + @test ceil(Interval{Unbounded, Closed}(nothing, 1.5), on=LeftEndpoint) == Interval{Unbounded, Closed}(nothing, 1.5) + @test ceil(Interval{Unbounded, Unbounded}(nothing, nothing), on=LeftEndpoint) == Interval{Unbounded, Unbounded}(nothing, nothing) + + @test ceil(Interval{Closed, Unbounded}(0.0, nothing), on=RightEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) + @test ceil(Interval{Closed, Unbounded}(0.5, nothing), on=RightEndpoint) == Interval{Closed, Unbounded}(0.5, nothing) + @test ceil(Interval{Unbounded, Closed}(nothing, 1.0), on=RightEndpoint) == Interval{Unbounded, Closed}(nothing, 1.0) + @test ceil(Interval{Unbounded, Closed}(nothing, 1.5), on=RightEndpoint) == Interval{Unbounded, Closed}(nothing, 2.0) + @test ceil(Interval{Unbounded, Unbounded}(nothing, nothing), on=RightEndpoint) == Interval{Unbounded, Unbounded}(nothing, nothing) end @testset "round" begin - @test round(Interval(0.0, 1.0)) == Interval(0.0, 1.0) - @test round(Interval(0.5, 1.0)) == Interval(0.0, 1.0) - @test round(Interval(0.0, 1.5)) == Interval(0.0, 2.0) - @test round(Interval(0.5, 1.5)) == Interval(0.0, 2.0) + @test_throws UndefKeywordError round(Interval(0.0, 1.0)) @test round(Interval(0.0, 1.0), on=LeftEndpoint) == Interval(0.0, 1.0) - @test round(Interval(0.5, 1.0), on=LeftEndpoint) == Interval(0.0, 1.0) + @test round(Interval(0.5, 1.0), on=LeftEndpoint) == Interval(0.0, 0.5) @test round(Interval(0.0, 1.5), on=LeftEndpoint) == Interval(0.0, 1.5) - @test round(Interval(0.5, 1.5), on=LeftEndpoint) == Interval(0.0, 1.5) + @test round(Interval(0.5, 1.5), on=LeftEndpoint) == Interval(0.0, 1.0) @test round(Interval(0.0, 1.0), on=RightEndpoint) == Interval(0.0, 1.0) @test round(Interval(0.5, 1.0), on=RightEndpoint) == Interval(0.5, 1.0) - @test round(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.0, 2.0) - @test round(Interval(0.5, 1.5), on=RightEndpoint) == Interval(0.5, 2.0) + @test round(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.5, 2.0) + @test round(Interval(0.5, 1.5), on=RightEndpoint) == Interval(1.0, 2.0) # Test supplying a period to round to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) - expected = Interval(DateTime(2011, 2, 1), DateTime(2011, 2, 3)) - @test round(interval, Day) == expected - @test round(interval, Day(1)) == expected + expected = Interval(DateTime(2011, 2, 1), DateTime(2011, 2, 2, 12)) + @test_throws UndefKeywordError round(interval, Day) + @test round(interval, Day; on=LeftEndpoint) == expected + @test round(interval, Day(1); on=LeftEndpoint) == expected + + expected = Interval(DateTime(2011, 2, 1, 12), DateTime(2011, 2, 3)) + @test_throws UndefKeywordError round(interval, Day) + @test round(interval, Day; on=RightEndpoint) == expected + @test round(interval, Day(1); on=RightEndpoint) == expected + + # Test unbounded intervals + @test round(Interval{Closed, Unbounded}(0.0, nothing), on=LeftEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) + @test round(Interval{Closed, Unbounded}(0.5, nothing), on=LeftEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) + @test round(Interval{Unbounded, Closed}(nothing, 1.0), on=LeftEndpoint) == Interval{Unbounded, Closed}(nothing, 1.0) + @test round(Interval{Unbounded, Closed}(nothing, 1.5), on=LeftEndpoint) == Interval{Unbounded, Closed}(nothing, 1.5) + @test round(Interval{Unbounded, Unbounded}(nothing, nothing), on=LeftEndpoint) == Interval{Unbounded, Unbounded}(nothing, nothing) + + @test round(Interval{Closed, Unbounded}(0.0, nothing), on=RightEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) + @test round(Interval{Closed, Unbounded}(0.5, nothing), on=RightEndpoint) == Interval{Closed, Unbounded}(0.5, nothing) + @test round(Interval{Unbounded, Closed}(nothing, 1.0), on=RightEndpoint) == Interval{Unbounded, Closed}(nothing, 1.0) + @test round(Interval{Unbounded, Closed}(nothing, 1.5), on=RightEndpoint) == Interval{Unbounded, Closed}(nothing, 2.0) + @test round(Interval{Unbounded, Unbounded}(nothing, nothing), on=RightEndpoint) == Interval{Unbounded, Unbounded}(nothing, nothing) end end From 3d6e7cef06070b7d72921231651ee696a907b815 Mon Sep 17 00:00:00 2001 From: Nicole Epp Date: Fri, 9 Oct 2020 14:09:18 -0500 Subject: [PATCH 04/10] Add docs for Endpoint types and rounding --- docs/src/index.md | 42 ++++++++++++++++++++++++++++++++++ src/Intervals.jl | 6 ++--- src/endpoint.jl | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 3 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index 32cd2bf0..23f4b6e4 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -209,6 +209,45 @@ julia> 0..10 ≪ 11..20 true ``` +### Rounding + +Interval rounding maintains the original span of the interval, shifting it according to +whichever endpoint is specified as the one to use for rounding. The operations `floor`, +`ceil`, and `round` are supported, as long as the `on` keyword is supplied to specify which +endpoint should be used for rounding. Valid options are `LeftEndpoint`, `RightEndpoint`, or +`AnchorEndpoint` if dealing with anchored intervals. + +```jldoctest +julia> floor(Interval(0.0, 1.0), on=LeftEndpoint) +Interval{Float64,Closed,Closed}(0.0, 1.0) + +julia> floor(Interval(0.5, 1.0), on=LeftEndpoint) +Interval{Float64,Closed,Closed}(0.0, 0.5) + +julia> floor(Interval(0.5, 1.5), on=RightEndpoint) +Interval{Intervals.Endpoint{Float64,Intervals.Direction{:Right}(),Closed},Closed,Closed}(Intervals.Endpoint{Float64,Intervals.Direction{:Right}(),Closed}(0.0), Intervals.Endpoint{Float64,Intervals.Direction{:Right}(),Closed}(1.0)) +``` + +Anchored intervals default to rounding using the anchor point. + +```jldoctest +julia> round(AnchoredInterval{-0.5}(1.0)) +AnchoredInterval{-0.5,Float64,Open,Closed}(1.0) + +julia> round(AnchoredInterval{+0.5}(0.5)) +AnchoredInterval{0.5,Float64,Closed,Open}(0.0) + +julia> round(AnchoredInterval{+0.5}(0.5), on=AnchorEndpoint) +AnchoredInterval{0.5,Float64,Closed,Open}(0.0) + +julia> round(AnchoredInterval{+0.5}(0.5), on=LeftEndpoint) +AnchoredInterval{0.5,Float64,Closed,Open}(0.0) + +# The right endpoint of this interval is 1.0 (0.5 + 0.5) so it remains the same +julia> round(AnchoredInterval{+0.5}(0.5), on=RightEndpoint) +AnchoredInterval{0.5,Float64,Closed,Open}(0.5) +``` + ## API ```@docs @@ -223,6 +262,9 @@ Intervals.Bounded Closed Open Unbounded +LeftEndpoint +RightEndpoint +AnchorEndpoint first last span diff --git a/src/Intervals.jl b/src/Intervals.jl index 8815230d..ceb0dc9e 100644 --- a/src/Intervals.jl +++ b/src/Intervals.jl @@ -42,13 +42,13 @@ export Bound, AbstractInterval, Interval, AnchoredInterval, - LeftEndpoint, - RightEndpoint, - AnchorEndpoint, HourEnding, HourBeginning, HE, HB, + LeftEndpoint, + RightEndpoint, + AnchorEndpoint, first, last, span, diff --git a/src/endpoint.jl b/src/endpoint.jl index 3666c47f..257d7719 100644 --- a/src/endpoint.jl +++ b/src/endpoint.jl @@ -27,7 +27,50 @@ end Endpoint{T,D,B}(ep) where {T, D, B <: Bounded} = Endpoint{T,D,B}(convert(T, ep)) +""" + LeftEndpoint + +`LeftEndpoint` represents the lesser endpoint of an `AbstractInterval`. Useful for comparing +two endpoints to each other or for specifying how to round intervals. + +### Examples + +```jldoctest; setup = :(using Intervals) +julia> LeftEndpoint(Interval(0.0, 1.0)) +Intervals.Endpoint{Float64,Intervals.Direction{:Left}(),Closed}(0.0) + +julia> LeftEndpoint{Closed}(1.0) +Intervals.Endpoint{Float64,Intervals.Direction{:Left}(),Closed}(1.0) + +julia> LeftEndpoint{Integer, Closed}(1.0) +Intervals.Endpoint{Integer,Intervals.Direction{:Left}(),Closed}(1) +``` + +See also: [`RightEndpoint`](@ref), [`AnchorEndpoint`](@ref) +""" const LeftEndpoint{T,B} = Endpoint{T, Left, B} where {T,B <: Bound} + +""" + RightEndpoint + +`RightEndpoint` represents the greater endpoint of an `AbstractInterval`. Useful for +comparing two endpoints to each other or for specifying how to round intervals. + +### Examples + +```jldoctest; setup = :(using Intervals) +julia> RightEndpoint(Interval(0.0, 1.0)) +Intervals.Endpoint{Float64,Intervals.Direction{:Right}(),Closed}(1.0) + +julia> RightEndpoint{Closed}(1.0) +Intervals.Endpoint{Float64,Intervals.Direction{:Right}(),Closed}(1.0) + +julia> RightEndpoint{Integer, Closed}(1.0) +Intervals.Endpoint{Integer,Intervals.Direction{:Right}(),Closed}(1) +``` + +See also: [`LeftEndpoint`](@ref), [`AnchorEndpoint`](@ref) +""" const RightEndpoint{T,B} = Endpoint{T, Right, B} where {T,B <: Bound} LeftEndpoint{B}(ep::T) where {T,B} = LeftEndpoint{T,B}(ep) @@ -37,6 +80,21 @@ LeftEndpoint(i::AbstractInterval{T,L,R}) where {T,L,R} = LeftEndpoint{T,L}(L !== RightEndpoint(i::AbstractInterval{T,L,R}) where {T,L,R} = RightEndpoint{T,R}(R !== Unbounded ? last(i) : nothing) # Unconstructable Endpoint type used for rounding AnchoredIntervals +""" + AnchorEndpoint + +Unconstructable Endpoint type used for rounding AnchoredIntervals. Specifies to use +whichever endpoint is the anchor. + +### Example + +```jldoctest; setup = :(using Intervals) +julia> floor(AnchoredInterval{-0.5}(1.0), on=AnchorEndpoint) +AnchoredInterval{-0.5,Float64,Open,Closed}(1.0) +``` + +See also: [`LeftEndpoint`](@ref), [`RightEndpoint`](@ref) +""" const AnchorEndpoint{B} = Endpoint{Union{}, Direction{:Anchor}(), B} where {B <: Bounded} endpoint(x::Endpoint) = isbounded(x) ? x.endpoint : nothing From 5d168dfd3499f0a0a94d3eeaf38ca46b91213354 Mon Sep 17 00:00:00 2001 From: Nicole Epp Date: Fri, 9 Oct 2020 15:30:30 -0500 Subject: [PATCH 05/10] Add tests attempting to use an AnchorEndpoint with an Interval --- test/interval.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/interval.jl b/test/interval.jl index 4c59720d..03d22d7e 100644 --- a/test/interval.jl +++ b/test/interval.jl @@ -901,6 +901,9 @@ @test floor(Interval(0.0, 1.5), on=RightEndpoint) == Interval(-0.5, 1.0) @test floor(Interval(0.5, 1.5), on=RightEndpoint) == Interval(0.0, 1.0) + # AnchorEndpoint is only usable with AnchoredIntervals + @test_throws ArgumentError floor(Interval(0.0, 1.0), on=AnchorEndpoint) + # Test supplying a period to floor to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) expected = Interval(DateTime(2011, 2, 1), DateTime(2011, 2, 2, 12)) @@ -939,6 +942,9 @@ @test ceil(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.5, 2.0) @test ceil(Interval(0.5, 1.5), on=RightEndpoint) == Interval(1.0, 2.0) + # AnchorEndpoint is only usable with AnchoredIntervals + @test_throws ArgumentError ceil(Interval(0.0, 1.0), on=AnchorEndpoint) + # Test supplying a period to ceil to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) expected = Interval(DateTime(2011, 2, 2), DateTime(2011, 2, 3, 12)) @@ -977,6 +983,9 @@ @test round(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.5, 2.0) @test round(Interval(0.5, 1.5), on=RightEndpoint) == Interval(1.0, 2.0) + # AnchorEndpoint is only usable with AnchoredIntervals + @test_throws ArgumentError round(Interval(0.0, 1.0), on=AnchorEndpoint) + # Test supplying a period to round to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) expected = Interval(DateTime(2011, 2, 1), DateTime(2011, 2, 2, 12)) From 02c3d854bcbb2f9f7e2d0c33825c556d27e5a42a Mon Sep 17 00:00:00 2001 From: Nicole Epp Date: Fri, 9 Oct 2020 15:59:08 -0500 Subject: [PATCH 06/10] Use dispatch for when rounding --- src/interval.jl | 74 ++++++++++++++++++++++++++++-------------------- test/interval.jl | 6 ++-- 2 files changed, 47 insertions(+), 33 deletions(-) diff --git a/src/interval.jl b/src/interval.jl index 413accf5..842b7112 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -479,41 +479,55 @@ function Base.merge(a::AbstractInterval, b::AbstractInterval) end ##### ROUNDING ##### +const RoundingFunctionTypes = Union{typeof(floor), typeof(ceil), typeof(round)} for f in (:floor, :ceil, :round) - @eval function Base.$f( - interval::Interval{T,L,R}, - args...; - on::Type{<:Endpoint}, - ) where {T,L,R} - left = LeftEndpoint(interval) - right = RightEndpoint(interval) - - if on === LeftEndpoint - # Do nothing if the left endpoint is unbounded - if L <: Bounded - left = $f(left, args...) - # Shifts the interval, span remains the same - if R <: Bounded - right = RightEndpoint{T, R}(left.endpoint + span(interval)) - end - end - elseif on === RightEndpoint - # Do nothing if the right endpoint is unbounded - if R <: Bounded - right = $f(right, args...) - if L <: Bounded - left = RightEndpoint{T, L}(right.endpoint - span(interval)) - end - end - else - throw(ArgumentError("Unhandled `on` type: $on")) - end - - return Interval(left, right) + @eval function Base.$f(interval::Interval, args...; on::Type{<:Endpoint}) + return _round($f, interval, on, args...) end end +function _round( + f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Type{LeftEndpoint}, args... +) where {T, L <: Bounded, R <: Bounded} + left_val = f(first(interval), args...) + return Interval{T,L,R}(left_val, left_val + span(interval)) +end + +function _round( + f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Type{LeftEndpoint}, args... +) where {T, L <: Bounded, R <: Unbounded} + left_val = f(first(interval), args...) + return Interval{T,L,R}(left_val, nothing) +end + +function _round( + f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Type{LeftEndpoint}, args... +) where {T, L <: Unbounded, R <: Bound} + return interval +end + +function _round( + f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Type{RightEndpoint}, args... +) where {T, L <: Bounded, R <: Bounded} + right_val = f(last(interval), args...) + return Interval{T,L,R}(right_val - span(interval), right_val) +end + +function _round( + f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Type{RightEndpoint}, args... +) where {T, L <: Unbounded, R <: Bounded} + right_val = f(last(interval), args...) + return Interval{T,L,R}(nothing, right_val) +end + +function _round( + f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Type{RightEndpoint}, args... +) where {T, L <: Bound, R <: Unbounded} + return interval +end + + ##### TIME ZONES ##### function TimeZones.astimezone(i::Interval{ZonedDateTime, L, R}, tz::TimeZone) where {L,R} diff --git a/test/interval.jl b/test/interval.jl index 03d22d7e..a07dedee 100644 --- a/test/interval.jl +++ b/test/interval.jl @@ -902,7 +902,7 @@ @test floor(Interval(0.5, 1.5), on=RightEndpoint) == Interval(0.0, 1.0) # AnchorEndpoint is only usable with AnchoredIntervals - @test_throws ArgumentError floor(Interval(0.0, 1.0), on=AnchorEndpoint) + @test_throws MethodError floor(Interval(0.0, 1.0), on=AnchorEndpoint) # Test supplying a period to floor to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) @@ -943,7 +943,7 @@ @test ceil(Interval(0.5, 1.5), on=RightEndpoint) == Interval(1.0, 2.0) # AnchorEndpoint is only usable with AnchoredIntervals - @test_throws ArgumentError ceil(Interval(0.0, 1.0), on=AnchorEndpoint) + @test_throws MethodError ceil(Interval(0.0, 1.0), on=AnchorEndpoint) # Test supplying a period to ceil to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) @@ -984,7 +984,7 @@ @test round(Interval(0.5, 1.5), on=RightEndpoint) == Interval(1.0, 2.0) # AnchorEndpoint is only usable with AnchoredIntervals - @test_throws ArgumentError round(Interval(0.0, 1.0), on=AnchorEndpoint) + @test_throws MethodError round(Interval(0.0, 1.0), on=AnchorEndpoint) # Test supplying a period to round to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) From 7823575f506c0a128a5e73ca819c3102c42fac58 Mon Sep 17 00:00:00 2001 From: Nicole Epp Date: Tue, 13 Oct 2020 15:49:48 -0500 Subject: [PATCH 07/10] Switch to using Symbols instead of Endpoints for on keyword --- docs/src/index.md | 22 +++--- src/Intervals.jl | 3 - src/anchoredinterval.jl | 8 +- src/endpoint.jl | 30 ++------ src/interval.jl | 16 ++-- test/anchoredinterval.jl | 81 +++++++++++--------- test/endpoint.jl | 5 -- test/interval.jl | 162 +++++++++++++++++++++------------------ 8 files changed, 159 insertions(+), 168 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index 23f4b6e4..0e75faf8 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -214,18 +214,18 @@ true Interval rounding maintains the original span of the interval, shifting it according to whichever endpoint is specified as the one to use for rounding. The operations `floor`, `ceil`, and `round` are supported, as long as the `on` keyword is supplied to specify which -endpoint should be used for rounding. Valid options are `LeftEndpoint`, `RightEndpoint`, or -`AnchorEndpoint` if dealing with anchored intervals. +endpoint should be used for rounding. Valid options are `:left`, `:right`, or +`:anchor` if dealing with anchored intervals. ```jldoctest -julia> floor(Interval(0.0, 1.0), on=LeftEndpoint) +julia> floor(Interval(0.0, 1.0), on=:left) Interval{Float64,Closed,Closed}(0.0, 1.0) -julia> floor(Interval(0.5, 1.0), on=LeftEndpoint) +julia> floor(Interval(0.5, 1.0), on=:left) Interval{Float64,Closed,Closed}(0.0, 0.5) -julia> floor(Interval(0.5, 1.5), on=RightEndpoint) -Interval{Intervals.Endpoint{Float64,Intervals.Direction{:Right}(),Closed},Closed,Closed}(Intervals.Endpoint{Float64,Intervals.Direction{:Right}(),Closed}(0.0), Intervals.Endpoint{Float64,Intervals.Direction{:Right}(),Closed}(1.0)) +julia> floor(Interval(0.5, 1.5), on=:right) +Interval{Float64,Closed,Closed}(0.0, 1.0) ``` Anchored intervals default to rounding using the anchor point. @@ -237,14 +237,13 @@ AnchoredInterval{-0.5,Float64,Open,Closed}(1.0) julia> round(AnchoredInterval{+0.5}(0.5)) AnchoredInterval{0.5,Float64,Closed,Open}(0.0) -julia> round(AnchoredInterval{+0.5}(0.5), on=AnchorEndpoint) +julia> round(AnchoredInterval{+0.5}(0.5), on=:anchor) AnchoredInterval{0.5,Float64,Closed,Open}(0.0) -julia> round(AnchoredInterval{+0.5}(0.5), on=LeftEndpoint) +julia> round(AnchoredInterval{+0.5}(0.5), on=:left) AnchoredInterval{0.5,Float64,Closed,Open}(0.0) -# The right endpoint of this interval is 1.0 (0.5 + 0.5) so it remains the same -julia> round(AnchoredInterval{+0.5}(0.5), on=RightEndpoint) +julia> round(AnchoredInterval{+0.5}(0.5), on=:right) AnchoredInterval{0.5,Float64,Closed,Open}(0.5) ``` @@ -262,9 +261,6 @@ Intervals.Bounded Closed Open Unbounded -LeftEndpoint -RightEndpoint -AnchorEndpoint first last span diff --git a/src/Intervals.jl b/src/Intervals.jl index ceb0dc9e..0c606436 100644 --- a/src/Intervals.jl +++ b/src/Intervals.jl @@ -46,9 +46,6 @@ export Bound, HourBeginning, HE, HB, - LeftEndpoint, - RightEndpoint, - AnchorEndpoint, first, last, span, diff --git a/src/anchoredinterval.jl b/src/anchoredinterval.jl index 7ed5d651..05474ce5 100644 --- a/src/anchoredinterval.jl +++ b/src/anchoredinterval.jl @@ -338,17 +338,17 @@ for f in (:floor, :ceil, :round) @eval function Base.$f( interval::AnchoredInterval{P,T,L,R}, args...; - on::Type{<:Endpoint}=AnchorEndpoint, + on::Symbol=:anchor, ) where {P,T,L,R} - anc = if on === AnchorEndpoint + anc = if on === :anchor $f(anchor(interval), args...) - elseif on === LeftEndpoint + elseif on === :left if P ≤ zero(P) $f(first(interval), args...) - P else $f(first(interval), args...) end - elseif on === RightEndpoint + elseif on === :right if P ≤ zero(P) $f(last(interval), args...) else diff --git a/src/endpoint.jl b/src/endpoint.jl index 257d7719..872d28a2 100644 --- a/src/endpoint.jl +++ b/src/endpoint.jl @@ -31,11 +31,11 @@ Endpoint{T,D,B}(ep) where {T, D, B <: Bounded} = Endpoint{T,D,B}(convert(T, ep)) LeftEndpoint `LeftEndpoint` represents the lesser endpoint of an `AbstractInterval`. Useful for comparing -two endpoints to each other or for specifying how to round intervals. +two endpoints to each other. ### Examples -```jldoctest; setup = :(using Intervals) +```jldoctest; setup = :(using Intervals; using Intervals: LeftEndpoint) julia> LeftEndpoint(Interval(0.0, 1.0)) Intervals.Endpoint{Float64,Intervals.Direction{:Left}(),Closed}(0.0) @@ -46,7 +46,7 @@ julia> LeftEndpoint{Integer, Closed}(1.0) Intervals.Endpoint{Integer,Intervals.Direction{:Left}(),Closed}(1) ``` -See also: [`RightEndpoint`](@ref), [`AnchorEndpoint`](@ref) +See also: [`RightEndpoint`](@ref) """ const LeftEndpoint{T,B} = Endpoint{T, Left, B} where {T,B <: Bound} @@ -54,11 +54,11 @@ const LeftEndpoint{T,B} = Endpoint{T, Left, B} where {T,B <: Bound} RightEndpoint `RightEndpoint` represents the greater endpoint of an `AbstractInterval`. Useful for -comparing two endpoints to each other or for specifying how to round intervals. +comparing two endpoints to each other. ### Examples -```jldoctest; setup = :(using Intervals) +```jldoctest; setup = :(using Intervals; using Intervals: RightEndpoint) julia> RightEndpoint(Interval(0.0, 1.0)) Intervals.Endpoint{Float64,Intervals.Direction{:Right}(),Closed}(1.0) @@ -69,7 +69,7 @@ julia> RightEndpoint{Integer, Closed}(1.0) Intervals.Endpoint{Integer,Intervals.Direction{:Right}(),Closed}(1) ``` -See also: [`LeftEndpoint`](@ref), [`AnchorEndpoint`](@ref) +See also: [`LeftEndpoint`](@ref) """ const RightEndpoint{T,B} = Endpoint{T, Right, B} where {T,B <: Bound} @@ -79,24 +79,6 @@ RightEndpoint{B}(ep::T) where {T,B} = RightEndpoint{T,B}(ep) LeftEndpoint(i::AbstractInterval{T,L,R}) where {T,L,R} = LeftEndpoint{T,L}(L !== Unbounded ? first(i) : nothing) RightEndpoint(i::AbstractInterval{T,L,R}) where {T,L,R} = RightEndpoint{T,R}(R !== Unbounded ? last(i) : nothing) -# Unconstructable Endpoint type used for rounding AnchoredIntervals -""" - AnchorEndpoint - -Unconstructable Endpoint type used for rounding AnchoredIntervals. Specifies to use -whichever endpoint is the anchor. - -### Example - -```jldoctest; setup = :(using Intervals) -julia> floor(AnchoredInterval{-0.5}(1.0), on=AnchorEndpoint) -AnchoredInterval{-0.5,Float64,Open,Closed}(1.0) -``` - -See also: [`LeftEndpoint`](@ref), [`RightEndpoint`](@ref) -""" -const AnchorEndpoint{B} = Endpoint{Union{}, Direction{:Anchor}(), B} where {B <: Bounded} - endpoint(x::Endpoint) = isbounded(x) ? x.endpoint : nothing bound_type(x::Endpoint{T,D,B}) where {T,D,B} = B diff --git a/src/interval.jl b/src/interval.jl index 842b7112..65b70c41 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -482,47 +482,47 @@ end const RoundingFunctionTypes = Union{typeof(floor), typeof(ceil), typeof(round)} for f in (:floor, :ceil, :round) - @eval function Base.$f(interval::Interval, args...; on::Type{<:Endpoint}) - return _round($f, interval, on, args...) + @eval function Base.$f(interval::Interval, args...; on::Symbol) + return _round($f, interval, Val(on), args...) end end function _round( - f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Type{LeftEndpoint}, args... + f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Val{:left}, args... ) where {T, L <: Bounded, R <: Bounded} left_val = f(first(interval), args...) return Interval{T,L,R}(left_val, left_val + span(interval)) end function _round( - f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Type{LeftEndpoint}, args... + f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Val{:left}, args... ) where {T, L <: Bounded, R <: Unbounded} left_val = f(first(interval), args...) return Interval{T,L,R}(left_val, nothing) end function _round( - f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Type{LeftEndpoint}, args... + f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Val{:left}, args... ) where {T, L <: Unbounded, R <: Bound} return interval end function _round( - f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Type{RightEndpoint}, args... + f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Val{:right}, args... ) where {T, L <: Bounded, R <: Bounded} right_val = f(last(interval), args...) return Interval{T,L,R}(right_val - span(interval), right_val) end function _round( - f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Type{RightEndpoint}, args... + f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Val{:right}, args... ) where {T, L <: Unbounded, R <: Bounded} right_val = f(last(interval), args...) return Interval{T,L,R}(nothing, right_val) end function _round( - f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Type{RightEndpoint}, args... + f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Val{:right}, args... ) where {T, L <: Bound, R <: Unbounded} return interval end diff --git a/test/anchoredinterval.jl b/test/anchoredinterval.jl index ef97f4e7..2dc513cf 100644 --- a/test/anchoredinterval.jl +++ b/test/anchoredinterval.jl @@ -843,25 +843,28 @@ using Intervals: Bounded, Ending, Beginning, canonicalize, isunbounded end @testset "floor" begin + # only :anchor, :left, and :right are supported + @test_throws ArgumentError floor(AnchoredInterval{-0.5}(1.0); on=:nothing) + @test floor(AnchoredInterval{-0.5}(1.0)) == AnchoredInterval{-0.5}(1.0) @test floor(AnchoredInterval{+0.5}(1.0)) == AnchoredInterval{+0.5}(1.0) @test floor(AnchoredInterval{-0.5}(0.5)) == AnchoredInterval{-0.5}(0.0) @test floor(AnchoredInterval{+0.5}(0.5)) == AnchoredInterval{+0.5}(0.0) - @test floor(AnchoredInterval{-0.5}(1.0), on=LeftEndpoint) == AnchoredInterval{-0.5}(0.5) - @test floor(AnchoredInterval{+0.5}(1.0), on=LeftEndpoint) == AnchoredInterval{+0.5}(1.0) - @test floor(AnchoredInterval{-0.5}(0.5), on=LeftEndpoint) == AnchoredInterval{-0.5}(0.5) - @test floor(AnchoredInterval{+0.5}(0.5), on=LeftEndpoint) == AnchoredInterval{+0.5}(0.0) + @test floor(AnchoredInterval{-0.5}(1.0); on=:left) == AnchoredInterval{-0.5}(0.5) + @test floor(AnchoredInterval{+0.5}(1.0); on=:left) == AnchoredInterval{+0.5}(1.0) + @test floor(AnchoredInterval{-0.5}(0.5); on=:left) == AnchoredInterval{-0.5}(0.5) + @test floor(AnchoredInterval{+0.5}(0.5); on=:left) == AnchoredInterval{+0.5}(0.0) - @test floor(AnchoredInterval{-0.5}(1.0), on=RightEndpoint) == AnchoredInterval{-0.5}(1.0) - @test floor(AnchoredInterval{+0.5}(1.0), on=RightEndpoint) == AnchoredInterval{+0.5}(0.5) - @test floor(AnchoredInterval{-0.5}(0.5), on=RightEndpoint) == AnchoredInterval{-0.5}(0.0) - @test floor(AnchoredInterval{+0.5}(0.5), on=RightEndpoint) == AnchoredInterval{+0.5}(0.5) + @test floor(AnchoredInterval{-0.5}(1.0); on=:right) == AnchoredInterval{-0.5}(1.0) + @test floor(AnchoredInterval{+0.5}(1.0); on=:right) == AnchoredInterval{+0.5}(0.5) + @test floor(AnchoredInterval{-0.5}(0.5); on=:right) == AnchoredInterval{-0.5}(0.0) + @test floor(AnchoredInterval{+0.5}(0.5); on=:right) == AnchoredInterval{+0.5}(0.5) - @test floor(AnchoredInterval{-0.5}(1.0), on=AnchorEndpoint) == AnchoredInterval{-0.5}(1.0) - @test floor(AnchoredInterval{+0.5}(1.0), on=AnchorEndpoint) == AnchoredInterval{+0.5}(1.0) - @test floor(AnchoredInterval{-0.5}(0.5), on=AnchorEndpoint) == AnchoredInterval{-0.5}(0.0) - @test floor(AnchoredInterval{+0.5}(0.5), on=AnchorEndpoint) == AnchoredInterval{+0.5}(0.0) + @test floor(AnchoredInterval{-0.5}(1.0); on=:anchor) == AnchoredInterval{-0.5}(1.0) + @test floor(AnchoredInterval{+0.5}(1.0); on=:anchor) == AnchoredInterval{+0.5}(1.0) + @test floor(AnchoredInterval{-0.5}(0.5); on=:anchor) == AnchoredInterval{-0.5}(0.0) + @test floor(AnchoredInterval{+0.5}(0.5); on=:anchor) == AnchoredInterval{+0.5}(0.0) # Test supplying a period to floor to interval_ending = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 1, 12)) @@ -876,25 +879,28 @@ using Intervals: Bounded, Ending, Beginning, canonicalize, isunbounded end @testset "ceil" begin + # only :anchor, :left, and :right are supported + @test_throws ArgumentError ceil(AnchoredInterval{-0.5}(1.0); on=:nothing) + @test ceil(AnchoredInterval{-0.5}(1.0)) == AnchoredInterval{-0.5}(1.0) @test ceil(AnchoredInterval{+0.5}(1.0)) == AnchoredInterval{+0.5}(1.0) @test ceil(AnchoredInterval{-0.5}(0.5)) == AnchoredInterval{-0.5}(1.0) @test ceil(AnchoredInterval{+0.5}(0.5)) == AnchoredInterval{+0.5}(1.0) - @test ceil(AnchoredInterval{-0.5}(1.0), on=LeftEndpoint) == AnchoredInterval{-0.5}(1.5) - @test ceil(AnchoredInterval{+0.5}(1.0), on=LeftEndpoint) == AnchoredInterval{+0.5}(1.0) - @test ceil(AnchoredInterval{-0.5}(0.5), on=LeftEndpoint) == AnchoredInterval{-0.5}(0.5) - @test ceil(AnchoredInterval{+0.5}(0.5), on=LeftEndpoint) == AnchoredInterval{+0.5}(1.0) + @test ceil(AnchoredInterval{-0.5}(1.0); on=:left) == AnchoredInterval{-0.5}(1.5) + @test ceil(AnchoredInterval{+0.5}(1.0); on=:left) == AnchoredInterval{+0.5}(1.0) + @test ceil(AnchoredInterval{-0.5}(0.5); on=:left) == AnchoredInterval{-0.5}(0.5) + @test ceil(AnchoredInterval{+0.5}(0.5); on=:left) == AnchoredInterval{+0.5}(1.0) - @test ceil(AnchoredInterval{-0.5}(1.0), on=RightEndpoint) == AnchoredInterval{-0.5}(1.0) - @test ceil(AnchoredInterval{+0.5}(1.0), on=RightEndpoint) == AnchoredInterval{+0.5}(1.5) - @test ceil(AnchoredInterval{-0.5}(0.5), on=RightEndpoint) == AnchoredInterval{-0.5}(1.0) - @test ceil(AnchoredInterval{+0.5}(0.5), on=RightEndpoint) == AnchoredInterval{+0.5}(0.5) + @test ceil(AnchoredInterval{-0.5}(1.0); on=:right) == AnchoredInterval{-0.5}(1.0) + @test ceil(AnchoredInterval{+0.5}(1.0); on=:right) == AnchoredInterval{+0.5}(1.5) + @test ceil(AnchoredInterval{-0.5}(0.5); on=:right) == AnchoredInterval{-0.5}(1.0) + @test ceil(AnchoredInterval{+0.5}(0.5); on=:right) == AnchoredInterval{+0.5}(0.5) - @test ceil(AnchoredInterval{-0.5}(1.0), on=AnchorEndpoint) == AnchoredInterval{-0.5}(1.0) - @test ceil(AnchoredInterval{+0.5}(1.0), on=AnchorEndpoint) == AnchoredInterval{+0.5}(1.0) - @test ceil(AnchoredInterval{-0.5}(0.5), on=AnchorEndpoint) == AnchoredInterval{-0.5}(1.0) - @test ceil(AnchoredInterval{+0.5}(0.5), on=AnchorEndpoint) == AnchoredInterval{+0.5}(1.0) + @test ceil(AnchoredInterval{-0.5}(1.0); on=:anchor) == AnchoredInterval{-0.5}(1.0) + @test ceil(AnchoredInterval{+0.5}(1.0); on=:anchor) == AnchoredInterval{+0.5}(1.0) + @test ceil(AnchoredInterval{-0.5}(0.5); on=:anchor) == AnchoredInterval{-0.5}(1.0) + @test ceil(AnchoredInterval{+0.5}(0.5); on=:anchor) == AnchoredInterval{+0.5}(1.0) # Test supplying a period to ceil to interval_ending = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 1, 12)) @@ -909,25 +915,28 @@ using Intervals: Bounded, Ending, Beginning, canonicalize, isunbounded end @testset "round" begin + # only :anchor, :left, and :right are supported + @test_throws ArgumentError round(AnchoredInterval{-0.5}(1.0); on=:nothing) + @test round(AnchoredInterval{-0.5}(1.0)) == AnchoredInterval{-0.5}(1.0) @test round(AnchoredInterval{+0.5}(1.0)) == AnchoredInterval{+0.5}(1.0) @test round(AnchoredInterval{-0.5}(0.5)) == AnchoredInterval{-0.5}(0.0) @test round(AnchoredInterval{+0.5}(0.5)) == AnchoredInterval{+0.5}(0.0) - @test round(AnchoredInterval{-0.5}(1.0), on=LeftEndpoint) == AnchoredInterval{-0.5}(0.5) - @test round(AnchoredInterval{+0.5}(1.0), on=LeftEndpoint) == AnchoredInterval{+0.5}(1.0) - @test round(AnchoredInterval{-0.5}(0.5), on=LeftEndpoint) == AnchoredInterval{-0.5}(0.5) - @test round(AnchoredInterval{+0.5}(0.5), on=LeftEndpoint) == AnchoredInterval{+0.5}(0.0) + @test round(AnchoredInterval{-0.5}(1.0); on=:left) == AnchoredInterval{-0.5}(0.5) + @test round(AnchoredInterval{+0.5}(1.0); on=:left) == AnchoredInterval{+0.5}(1.0) + @test round(AnchoredInterval{-0.5}(0.5); on=:left) == AnchoredInterval{-0.5}(0.5) + @test round(AnchoredInterval{+0.5}(0.5); on=:left) == AnchoredInterval{+0.5}(0.0) - @test round(AnchoredInterval{-0.5}(1.0), on=RightEndpoint) == AnchoredInterval{-0.5}(1.0) - @test round(AnchoredInterval{+0.5}(1.0), on=RightEndpoint) == AnchoredInterval{+0.5}(1.5) - @test round(AnchoredInterval{-0.5}(0.5), on=RightEndpoint) == AnchoredInterval{-0.5}(0.0) - @test round(AnchoredInterval{+0.5}(0.5), on=RightEndpoint) == AnchoredInterval{+0.5}(0.5) + @test round(AnchoredInterval{-0.5}(1.0); on=:right) == AnchoredInterval{-0.5}(1.0) + @test round(AnchoredInterval{+0.5}(1.0); on=:right) == AnchoredInterval{+0.5}(1.5) + @test round(AnchoredInterval{-0.5}(0.5); on=:right) == AnchoredInterval{-0.5}(0.0) + @test round(AnchoredInterval{+0.5}(0.5); on=:right) == AnchoredInterval{+0.5}(0.5) - @test round(AnchoredInterval{-0.5}(1.0), on=AnchorEndpoint) == AnchoredInterval{-0.5}(1.0) - @test round(AnchoredInterval{+0.5}(1.0), on=AnchorEndpoint) == AnchoredInterval{+0.5}(1.0) - @test round(AnchoredInterval{-0.5}(0.5), on=AnchorEndpoint) == AnchoredInterval{-0.5}(0.0) - @test round(AnchoredInterval{+0.5}(0.5), on=AnchorEndpoint) == AnchoredInterval{+0.5}(0.0) + @test round(AnchoredInterval{-0.5}(1.0); on=:anchor) == AnchoredInterval{-0.5}(1.0) + @test round(AnchoredInterval{+0.5}(1.0); on=:anchor) == AnchoredInterval{+0.5}(1.0) + @test round(AnchoredInterval{-0.5}(0.5); on=:anchor) == AnchoredInterval{-0.5}(0.0) + @test round(AnchoredInterval{+0.5}(0.5); on=:anchor) == AnchoredInterval{+0.5}(0.0) # Test supplying a period to round to interval_ending = AnchoredInterval{Day(-1)}(DateTime(2011, 2, 1, 12)) diff --git a/test/endpoint.jl b/test/endpoint.jl index 6325812b..e56b8dab 100644 --- a/test/endpoint.jl +++ b/test/endpoint.jl @@ -499,9 +499,4 @@ using Intervals: Endpoint, Left, Right, LeftEndpoint, RightEndpoint result = test .== 0 @test result == [false, true, false, false, true, false] end - - @testset "AnchorEndpoint" begin - # Unconstructable type used for rounding anchored intervals - @test_throws MethodError AnchorEndpoint() - end end diff --git a/test/interval.jl b/test/interval.jl index a07dedee..a5131f75 100644 --- a/test/interval.jl +++ b/test/interval.jl @@ -889,126 +889,138 @@ end @testset "floor" begin + # `on` keyword is required @test_throws UndefKeywordError floor(Interval(0.0, 1.0)) - @test floor(Interval(0.0, 1.0), on=LeftEndpoint) == Interval(0.0, 1.0) - @test floor(Interval(0.5, 1.0), on=LeftEndpoint) == Interval(0.0, 0.5) - @test floor(Interval(0.0, 1.5), on=LeftEndpoint) == Interval(0.0, 1.5) - @test floor(Interval(0.5, 1.5), on=LeftEndpoint) == Interval(0.0, 1.0) + # only :left and :right are supported + @test_throws MethodError floor(Interval(0.0, 1.0); on=:nothing) - @test floor(Interval(0.0, 1.0), on=RightEndpoint) == Interval(0.0, 1.0) - @test floor(Interval(0.5, 1.0), on=RightEndpoint) == Interval(0.5, 1.0) - @test floor(Interval(0.0, 1.5), on=RightEndpoint) == Interval(-0.5, 1.0) - @test floor(Interval(0.5, 1.5), on=RightEndpoint) == Interval(0.0, 1.0) + @test floor(Interval(0.0, 1.0); on=:left) == Interval(0.0, 1.0) + @test floor(Interval(0.5, 1.0); on=:left) == Interval(0.0, 0.5) + @test floor(Interval(0.0, 1.5); on=:left) == Interval(0.0, 1.5) + @test floor(Interval(0.5, 1.5); on=:left) == Interval(0.0, 1.0) - # AnchorEndpoint is only usable with AnchoredIntervals - @test_throws MethodError floor(Interval(0.0, 1.0), on=AnchorEndpoint) + @test floor(Interval(0.0, 1.0); on=:right) == Interval(0.0, 1.0) + @test floor(Interval(0.5, 1.0); on=:right) == Interval(0.5, 1.0) + @test floor(Interval(0.0, 1.5); on=:right) == Interval(-0.5, 1.0) + @test floor(Interval(0.5, 1.5); on=:right) == Interval(0.0, 1.0) + + # :anchor is only usable with AnchoredIntervals + @test_throws MethodError floor(Interval(0.0, 1.0); on=:anchor) # Test supplying a period to floor to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) expected = Interval(DateTime(2011, 2, 1), DateTime(2011, 2, 2, 12)) @test_throws UndefKeywordError floor(interval, Day) - @test floor(interval, Day; on=LeftEndpoint) == expected - @test floor(interval, Day(1); on=LeftEndpoint) == expected + @test floor(interval, Day; on=:left) == expected + @test floor(interval, Day(1); on=:left) == expected expected = Interval(DateTime(2011, 1, 31, 12), DateTime(2011, 2, 2)) - @test floor(interval, Day; on=RightEndpoint) == expected - @test floor(interval, Day(1); on=RightEndpoint) == expected + @test floor(interval, Day; on=:right) == expected + @test floor(interval, Day(1); on=:right) == expected # Test unbounded intervals - @test floor(Interval{Closed, Unbounded}(0.0, nothing), on=LeftEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) - @test floor(Interval{Closed, Unbounded}(0.5, nothing), on=LeftEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) - @test floor(Interval{Unbounded, Closed}(nothing, 1.0), on=LeftEndpoint) == Interval{Unbounded, Closed}(nothing, 1.0) - @test floor(Interval{Unbounded, Closed}(nothing, 1.5), on=LeftEndpoint) == Interval{Unbounded, Closed}(nothing, 1.5) - @test floor(Interval{Unbounded, Unbounded}(nothing, nothing), on=LeftEndpoint) == Interval{Unbounded, Unbounded}(nothing, nothing) - - @test floor(Interval{Closed, Unbounded}(0.0, nothing), on=RightEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) - @test floor(Interval{Closed, Unbounded}(0.5, nothing), on=RightEndpoint) == Interval{Closed, Unbounded}(0.5, nothing) - @test floor(Interval{Unbounded, Closed}(nothing, 1.0), on=RightEndpoint) == Interval{Unbounded, Closed}(nothing, 1.0) - @test floor(Interval{Unbounded, Closed}(nothing, 1.5), on=RightEndpoint) == Interval{Unbounded, Closed}(nothing, 1.0) - @test floor(Interval{Unbounded, Unbounded}(nothing, nothing), on=RightEndpoint) == Interval{Unbounded, Unbounded}(nothing, nothing) + @test floor(Interval{Closed, Unbounded}(0.0, nothing); on=:left) == Interval{Closed, Unbounded}(0.0, nothing) + @test floor(Interval{Closed, Unbounded}(0.5, nothing); on=:left) == Interval{Closed, Unbounded}(0.0, nothing) + @test floor(Interval{Unbounded, Closed}(nothing, 1.0); on=:left) == Interval{Unbounded, Closed}(nothing, 1.0) + @test floor(Interval{Unbounded, Closed}(nothing, 1.5); on=:left) == Interval{Unbounded, Closed}(nothing, 1.5) + @test floor(Interval{Unbounded, Unbounded}(nothing, nothing); on=:left) == Interval{Unbounded, Unbounded}(nothing, nothing) + + @test floor(Interval{Closed, Unbounded}(0.0, nothing); on=:right) == Interval{Closed, Unbounded}(0.0, nothing) + @test floor(Interval{Closed, Unbounded}(0.5, nothing); on=:right) == Interval{Closed, Unbounded}(0.5, nothing) + @test floor(Interval{Unbounded, Closed}(nothing, 1.0); on=:right) == Interval{Unbounded, Closed}(nothing, 1.0) + @test floor(Interval{Unbounded, Closed}(nothing, 1.5); on=:right) == Interval{Unbounded, Closed}(nothing, 1.0) + @test floor(Interval{Unbounded, Unbounded}(nothing, nothing); on=:right) == Interval{Unbounded, Unbounded}(nothing, nothing) end @testset "ceil" begin + # `on` keyword is required @test_throws UndefKeywordError ceil(Interval(0.0, 1.0)) - @test ceil(Interval(0.0, 1.0), on=LeftEndpoint) == Interval(0.0, 1.0) - @test ceil(Interval(0.5, 1.0), on=LeftEndpoint) == Interval(1.0, 1.5) - @test ceil(Interval(0.0, 1.5), on=LeftEndpoint) == Interval(0.0, 1.5) - @test ceil(Interval(0.5, 1.5), on=LeftEndpoint) == Interval(1.0, 2.0) + # only :left and :right are supported + @test_throws MethodError ceil(Interval(0.0, 1.0); on=:nothing) + + @test ceil(Interval(0.0, 1.0); on=:left) == Interval(0.0, 1.0) + @test ceil(Interval(0.5, 1.0); on=:left) == Interval(1.0, 1.5) + @test ceil(Interval(0.0, 1.5); on=:left) == Interval(0.0, 1.5) + @test ceil(Interval(0.5, 1.5); on=:left) == Interval(1.0, 2.0) - @test ceil(Interval(0.0, 1.0), on=RightEndpoint) == Interval(0.0, 1.0) - @test ceil(Interval(0.5, 1.0), on=RightEndpoint) == Interval(0.5, 1.0) - @test ceil(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.5, 2.0) - @test ceil(Interval(0.5, 1.5), on=RightEndpoint) == Interval(1.0, 2.0) + @test ceil(Interval(0.0, 1.0); on=:right) == Interval(0.0, 1.0) + @test ceil(Interval(0.5, 1.0); on=:right) == Interval(0.5, 1.0) + @test ceil(Interval(0.0, 1.5); on=:right) == Interval(0.5, 2.0) + @test ceil(Interval(0.5, 1.5); on=:right) == Interval(1.0, 2.0) - # AnchorEndpoint is only usable with AnchoredIntervals - @test_throws MethodError ceil(Interval(0.0, 1.0), on=AnchorEndpoint) + # :anchor is only usable with AnchoredIntervals + @test_throws MethodError ceil(Interval(0.0, 1.0); on=:anchor) # Test supplying a period to ceil to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) expected = Interval(DateTime(2011, 2, 2), DateTime(2011, 2, 3, 12)) @test_throws UndefKeywordError ceil(interval, Day) - @test ceil(interval, Day; on=LeftEndpoint) == expected - @test ceil(interval, Day(1); on=LeftEndpoint) == expected + @test ceil(interval, Day; on=:left) == expected + @test ceil(interval, Day(1); on=:left) == expected expected = Interval(DateTime(2011, 2, 1, 12), DateTime(2011, 2, 3)) - @test ceil(interval, Day; on=RightEndpoint) == expected - @test ceil(interval, Day(1); on=RightEndpoint) == expected + @test ceil(interval, Day; on=:right) == expected + @test ceil(interval, Day(1); on=:right) == expected # Test unbounded intervals - @test ceil(Interval{Closed, Unbounded}(0.0, nothing), on=LeftEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) - @test ceil(Interval{Closed, Unbounded}(0.5, nothing), on=LeftEndpoint) == Interval{Closed, Unbounded}(1.0, nothing) - @test ceil(Interval{Unbounded, Closed}(nothing, 1.0), on=LeftEndpoint) == Interval{Unbounded, Closed}(nothing, 1.0) - @test ceil(Interval{Unbounded, Closed}(nothing, 1.5), on=LeftEndpoint) == Interval{Unbounded, Closed}(nothing, 1.5) - @test ceil(Interval{Unbounded, Unbounded}(nothing, nothing), on=LeftEndpoint) == Interval{Unbounded, Unbounded}(nothing, nothing) - - @test ceil(Interval{Closed, Unbounded}(0.0, nothing), on=RightEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) - @test ceil(Interval{Closed, Unbounded}(0.5, nothing), on=RightEndpoint) == Interval{Closed, Unbounded}(0.5, nothing) - @test ceil(Interval{Unbounded, Closed}(nothing, 1.0), on=RightEndpoint) == Interval{Unbounded, Closed}(nothing, 1.0) - @test ceil(Interval{Unbounded, Closed}(nothing, 1.5), on=RightEndpoint) == Interval{Unbounded, Closed}(nothing, 2.0) - @test ceil(Interval{Unbounded, Unbounded}(nothing, nothing), on=RightEndpoint) == Interval{Unbounded, Unbounded}(nothing, nothing) + @test ceil(Interval{Closed, Unbounded}(0.0, nothing); on=:left) == Interval{Closed, Unbounded}(0.0, nothing) + @test ceil(Interval{Closed, Unbounded}(0.5, nothing); on=:left) == Interval{Closed, Unbounded}(1.0, nothing) + @test ceil(Interval{Unbounded, Closed}(nothing, 1.0); on=:left) == Interval{Unbounded, Closed}(nothing, 1.0) + @test ceil(Interval{Unbounded, Closed}(nothing, 1.5); on=:left) == Interval{Unbounded, Closed}(nothing, 1.5) + @test ceil(Interval{Unbounded, Unbounded}(nothing, nothing); on=:left) == Interval{Unbounded, Unbounded}(nothing, nothing) + + @test ceil(Interval{Closed, Unbounded}(0.0, nothing); on=:right) == Interval{Closed, Unbounded}(0.0, nothing) + @test ceil(Interval{Closed, Unbounded}(0.5, nothing); on=:right) == Interval{Closed, Unbounded}(0.5, nothing) + @test ceil(Interval{Unbounded, Closed}(nothing, 1.0); on=:right) == Interval{Unbounded, Closed}(nothing, 1.0) + @test ceil(Interval{Unbounded, Closed}(nothing, 1.5); on=:right) == Interval{Unbounded, Closed}(nothing, 2.0) + @test ceil(Interval{Unbounded, Unbounded}(nothing, nothing); on=:right) == Interval{Unbounded, Unbounded}(nothing, nothing) end @testset "round" begin + # `on` keyword is required @test_throws UndefKeywordError round(Interval(0.0, 1.0)) - @test round(Interval(0.0, 1.0), on=LeftEndpoint) == Interval(0.0, 1.0) - @test round(Interval(0.5, 1.0), on=LeftEndpoint) == Interval(0.0, 0.5) - @test round(Interval(0.0, 1.5), on=LeftEndpoint) == Interval(0.0, 1.5) - @test round(Interval(0.5, 1.5), on=LeftEndpoint) == Interval(0.0, 1.0) + # only :left and :right are supported + @test_throws MethodError round(Interval(0.0, 1.0); on=:nothing) + + @test round(Interval(0.0, 1.0); on=:left) == Interval(0.0, 1.0) + @test round(Interval(0.5, 1.0); on=:left) == Interval(0.0, 0.5) + @test round(Interval(0.0, 1.5); on=:left) == Interval(0.0, 1.5) + @test round(Interval(0.5, 1.5); on=:left) == Interval(0.0, 1.0) - @test round(Interval(0.0, 1.0), on=RightEndpoint) == Interval(0.0, 1.0) - @test round(Interval(0.5, 1.0), on=RightEndpoint) == Interval(0.5, 1.0) - @test round(Interval(0.0, 1.5), on=RightEndpoint) == Interval(0.5, 2.0) - @test round(Interval(0.5, 1.5), on=RightEndpoint) == Interval(1.0, 2.0) + @test round(Interval(0.0, 1.0); on=:right) == Interval(0.0, 1.0) + @test round(Interval(0.5, 1.0); on=:right) == Interval(0.5, 1.0) + @test round(Interval(0.0, 1.5); on=:right) == Interval(0.5, 2.0) + @test round(Interval(0.5, 1.5); on=:right) == Interval(1.0, 2.0) - # AnchorEndpoint is only usable with AnchoredIntervals - @test_throws MethodError round(Interval(0.0, 1.0), on=AnchorEndpoint) + # :anchor is only usable with AnchoredIntervals + @test_throws MethodError round(Interval(0.0, 1.0); on=:anchor) # Test supplying a period to round to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) expected = Interval(DateTime(2011, 2, 1), DateTime(2011, 2, 2, 12)) @test_throws UndefKeywordError round(interval, Day) - @test round(interval, Day; on=LeftEndpoint) == expected - @test round(interval, Day(1); on=LeftEndpoint) == expected + @test round(interval, Day; on=:left) == expected + @test round(interval, Day(1); on=:left) == expected expected = Interval(DateTime(2011, 2, 1, 12), DateTime(2011, 2, 3)) @test_throws UndefKeywordError round(interval, Day) - @test round(interval, Day; on=RightEndpoint) == expected - @test round(interval, Day(1); on=RightEndpoint) == expected + @test round(interval, Day; on=:right) == expected + @test round(interval, Day(1); on=:right) == expected # Test unbounded intervals - @test round(Interval{Closed, Unbounded}(0.0, nothing), on=LeftEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) - @test round(Interval{Closed, Unbounded}(0.5, nothing), on=LeftEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) - @test round(Interval{Unbounded, Closed}(nothing, 1.0), on=LeftEndpoint) == Interval{Unbounded, Closed}(nothing, 1.0) - @test round(Interval{Unbounded, Closed}(nothing, 1.5), on=LeftEndpoint) == Interval{Unbounded, Closed}(nothing, 1.5) - @test round(Interval{Unbounded, Unbounded}(nothing, nothing), on=LeftEndpoint) == Interval{Unbounded, Unbounded}(nothing, nothing) - - @test round(Interval{Closed, Unbounded}(0.0, nothing), on=RightEndpoint) == Interval{Closed, Unbounded}(0.0, nothing) - @test round(Interval{Closed, Unbounded}(0.5, nothing), on=RightEndpoint) == Interval{Closed, Unbounded}(0.5, nothing) - @test round(Interval{Unbounded, Closed}(nothing, 1.0), on=RightEndpoint) == Interval{Unbounded, Closed}(nothing, 1.0) - @test round(Interval{Unbounded, Closed}(nothing, 1.5), on=RightEndpoint) == Interval{Unbounded, Closed}(nothing, 2.0) - @test round(Interval{Unbounded, Unbounded}(nothing, nothing), on=RightEndpoint) == Interval{Unbounded, Unbounded}(nothing, nothing) + @test round(Interval{Closed, Unbounded}(0.0, nothing); on=:left) == Interval{Closed, Unbounded}(0.0, nothing) + @test round(Interval{Closed, Unbounded}(0.5, nothing); on=:left) == Interval{Closed, Unbounded}(0.0, nothing) + @test round(Interval{Unbounded, Closed}(nothing, 1.0); on=:left) == Interval{Unbounded, Closed}(nothing, 1.0) + @test round(Interval{Unbounded, Closed}(nothing, 1.5); on=:left) == Interval{Unbounded, Closed}(nothing, 1.5) + @test round(Interval{Unbounded, Unbounded}(nothing, nothing); on=:left) == Interval{Unbounded, Unbounded}(nothing, nothing) + + @test round(Interval{Closed, Unbounded}(0.0, nothing); on=:right) == Interval{Closed, Unbounded}(0.0, nothing) + @test round(Interval{Closed, Unbounded}(0.5, nothing); on=:right) == Interval{Closed, Unbounded}(0.5, nothing) + @test round(Interval{Unbounded, Closed}(nothing, 1.0); on=:right) == Interval{Unbounded, Closed}(nothing, 1.0) + @test round(Interval{Unbounded, Closed}(nothing, 1.5); on=:right) == Interval{Unbounded, Closed}(nothing, 2.0) + @test round(Interval{Unbounded, Unbounded}(nothing, nothing); on=:right) == Interval{Unbounded, Unbounded}(nothing, nothing) end end From 17f69fdcf92cb7703fa45495119412da6f25809f Mon Sep 17 00:00:00 2001 From: Nicole Epp Date: Tue, 13 Oct 2020 16:11:53 -0500 Subject: [PATCH 08/10] Add documentation for rounding functions and the on keyword --- src/anchoredinterval.jl | 52 ++++++++++++++++++++++++----------------- src/interval.jl | 13 +++++++++-- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/anchoredinterval.jl b/src/anchoredinterval.jl index 05474ce5..5553dcd7 100644 --- a/src/anchoredinterval.jl +++ b/src/anchoredinterval.jl @@ -335,30 +335,40 @@ end ##### ROUNDING ##### for f in (:floor, :ceil, :round) - @eval function Base.$f( - interval::AnchoredInterval{P,T,L,R}, - args...; - on::Symbol=:anchor, - ) where {P,T,L,R} - anc = if on === :anchor - $f(anchor(interval), args...) - elseif on === :left - if P ≤ zero(P) - $f(first(interval), args...) - P - else - $f(first(interval), args...) - end - elseif on === :right - if P ≤ zero(P) - $f(last(interval), args...) + @eval begin + """ + $($f)(interval::AnchoredInterval, args...; on::Symbol=:anchor) + + Round the anchored interval by applying `$($f)` to a single endpoint, then shifting + the interval so that the span remains the same. The `on` keyword determines which + endpoint the rounding will be applied to. Valid options are `:anchor`, `:left`, or + `:right`. Rounding defaults to using the anchor point. + """ + function Base.$f( + interval::AnchoredInterval{P,T,L,R}, + args...; + on::Symbol=:anchor, + ) where {P,T,L,R} + anc = if on === :anchor + $f(anchor(interval), args...) + elseif on === :left + if P ≤ zero(P) + $f(first(interval), args...) - P + else + $f(first(interval), args...) + end + elseif on === :right + if P ≤ zero(P) + $f(last(interval), args...) + else + $f(last(interval), args...) - P + end else - $f(last(interval), args...) - P + throw(ArgumentError("Unhandled `on` type: $on")) end - else - throw(ArgumentError("Unhandled `on` type: $on")) - end - return AnchoredInterval{P,T,L,R}(anc) + return AnchoredInterval{P,T,L,R}(anc) + end end end diff --git a/src/interval.jl b/src/interval.jl index 65b70c41..f3cbefb5 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -482,8 +482,17 @@ end const RoundingFunctionTypes = Union{typeof(floor), typeof(ceil), typeof(round)} for f in (:floor, :ceil, :round) - @eval function Base.$f(interval::Interval, args...; on::Symbol) - return _round($f, interval, Val(on), args...) + @eval begin + """ + $($f)(interval::Interval, args...; on::Symbol) + + Round the interval by applying `$($f)` to a single endpoint, then shifting the + interval so that the span remains the same. The `on` keyword determines which + endpoint the rounding will be applied to. Valid options are `:left` or `:right`. + """ + function Base.$f(interval::Interval, args...; on::Symbol) + return _round($f, interval, Val(on), args...) + end end end From a9e29275a2d93737526bc302a14854bba64bd2cb Mon Sep 17 00:00:00 2001 From: Nicole Epp Date: Wed, 14 Oct 2020 12:57:49 -0500 Subject: [PATCH 09/10] Delete Endpoint docstrings --- src/endpoint.jl | 55 ------------------------------------------------- 1 file changed, 55 deletions(-) diff --git a/src/endpoint.jl b/src/endpoint.jl index 872d28a2..dc3b83d9 100644 --- a/src/endpoint.jl +++ b/src/endpoint.jl @@ -27,50 +27,7 @@ end Endpoint{T,D,B}(ep) where {T, D, B <: Bounded} = Endpoint{T,D,B}(convert(T, ep)) -""" - LeftEndpoint - -`LeftEndpoint` represents the lesser endpoint of an `AbstractInterval`. Useful for comparing -two endpoints to each other. - -### Examples - -```jldoctest; setup = :(using Intervals; using Intervals: LeftEndpoint) -julia> LeftEndpoint(Interval(0.0, 1.0)) -Intervals.Endpoint{Float64,Intervals.Direction{:Left}(),Closed}(0.0) - -julia> LeftEndpoint{Closed}(1.0) -Intervals.Endpoint{Float64,Intervals.Direction{:Left}(),Closed}(1.0) - -julia> LeftEndpoint{Integer, Closed}(1.0) -Intervals.Endpoint{Integer,Intervals.Direction{:Left}(),Closed}(1) -``` - -See also: [`RightEndpoint`](@ref) -""" const LeftEndpoint{T,B} = Endpoint{T, Left, B} where {T,B <: Bound} - -""" - RightEndpoint - -`RightEndpoint` represents the greater endpoint of an `AbstractInterval`. Useful for -comparing two endpoints to each other. - -### Examples - -```jldoctest; setup = :(using Intervals; using Intervals: RightEndpoint) -julia> RightEndpoint(Interval(0.0, 1.0)) -Intervals.Endpoint{Float64,Intervals.Direction{:Right}(),Closed}(1.0) - -julia> RightEndpoint{Closed}(1.0) -Intervals.Endpoint{Float64,Intervals.Direction{:Right}(),Closed}(1.0) - -julia> RightEndpoint{Integer, Closed}(1.0) -Intervals.Endpoint{Integer,Intervals.Direction{:Right}(),Closed}(1) -``` - -See also: [`LeftEndpoint`](@ref) -""" const RightEndpoint{T,B} = Endpoint{T, Right, B} where {T,B <: Bound} LeftEndpoint{B}(ep::T) where {T,B} = LeftEndpoint{T,B}(ep) @@ -213,15 +170,3 @@ end Base.isless(a, b::RightEndpoint) = isunbounded(b) || a < b.endpoint Base.isless(a::LeftEndpoint, b) = isunbounded(a) || a.endpoint < b - -for f in (:floor, :ceil, :round) - @eval begin - function Base.$f(p::Endpoint{T,D,B}) where {T,D,B} - Endpoint{T,D,B}($f(p.endpoint)) - end - - function Base.$f(p::Endpoint{T,D,B}, duration) where {T <: TimeType,D,B} - Endpoint{T,D,B}($f(p.endpoint, duration)) - end - end -end From cfc2c13c70d1c46772bc1a7367ccd1e6552990a9 Mon Sep 17 00:00:00 2001 From: Nicole Epp Date: Fri, 16 Oct 2020 13:48:10 -0500 Subject: [PATCH 10/10] Add explicit error for :anchor on kwarg for non Anchored Intervals --- src/interval.jl | 4 ++++ test/interval.jl | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/interval.jl b/src/interval.jl index f3cbefb5..198d0847 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -496,6 +496,10 @@ for f in (:floor, :ceil, :round) end end +function _round(f::RoundingFunctionTypes, interval::Interval, on::Val{:anchor}, args...) + throw(ArgumentError(":anchor is only usable with an AnchoredInterval.")) +end + function _round( f::RoundingFunctionTypes, interval::Interval{T,L,R}, on::Val{:left}, args... ) where {T, L <: Bounded, R <: Bounded} diff --git a/test/interval.jl b/test/interval.jl index a5131f75..4c07449b 100644 --- a/test/interval.jl +++ b/test/interval.jl @@ -906,7 +906,7 @@ @test floor(Interval(0.5, 1.5); on=:right) == Interval(0.0, 1.0) # :anchor is only usable with AnchoredIntervals - @test_throws MethodError floor(Interval(0.0, 1.0); on=:anchor) + @test_throws ArgumentError floor(Interval(0.0, 1.0); on=:anchor) # Test supplying a period to floor to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) @@ -951,7 +951,7 @@ @test ceil(Interval(0.5, 1.5); on=:right) == Interval(1.0, 2.0) # :anchor is only usable with AnchoredIntervals - @test_throws MethodError ceil(Interval(0.0, 1.0); on=:anchor) + @test_throws ArgumentError ceil(Interval(0.0, 1.0); on=:anchor) # Test supplying a period to ceil to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18)) @@ -996,7 +996,7 @@ @test round(Interval(0.5, 1.5); on=:right) == Interval(1.0, 2.0) # :anchor is only usable with AnchoredIntervals - @test_throws MethodError round(Interval(0.0, 1.0); on=:anchor) + @test_throws ArgumentError round(Interval(0.0, 1.0); on=:anchor) # Test supplying a period to round to interval = Interval(DateTime(2011, 2, 1, 6), DateTime(2011, 2, 2, 18))