Skip to content

Commit ba4c908

Browse files
committed
AnchoredInterval refactored to use span as field
1 parent 24ea040 commit ba4c908

File tree

6 files changed

+208
-126
lines changed

6 files changed

+208
-126
lines changed

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
This package defines:
1010
* `AbstractInterval`, along with its subtypes:
1111
* `Interval{T}`, which represents a non-iterable range between two endpoints of type `T`
12-
* `AnchoredInterval{T, P}`, which represents a non-iterable range defined by a single
13-
value `anchor::T` and the value type `P` which represents the size of the range
14-
* `HourEnding`, a type alias for `AnchoredInterval{T, Hour(-1)}`
15-
* `HourBeginning`, a type alias for `AnchoredInterval{T, Hour(1)}`
12+
* `AnchoredInterval{T, S, E}`, which represents a non-iterable range defined by a single
13+
value `anchor::T` and `span::S` which represents the size of the range. The type
14+
parameter `E` is an instance of `Direction` and indicate whether the anchor is a
15+
left-endpoint (`Beginning`) or a right-endpoing (`Ending`).
16+
* `HourEnding`, a type alias for `AnchoredInterval{T, Hour, Ending}`
17+
* `HourBeginning`, a type alias for `AnchoredInterval{T, Hour, Beginning}`
1618
* `HE` and `HB`, pseudoconstructors for `HourEnding` and `HourBeginning` that round the
1719
anchor up (`HE`) or down (`HB`) to the nearest hour
1820
* `Inclusivity`, which represents whether an `AbstractInterval` is open, half-open, or

docs/src/index.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
This package defines:
44
* `AbstractInterval`, along with its subtypes:
55
* `Interval{T}`, which represents a non-iterable range between two endpoints of type `T`
6-
* `AnchoredInterval{T, P}`, which represents a non-iterable range defined by a single
7-
value `anchor::T` and the value type `P` which represents the size of the range
8-
* `HourEnding`, a type alias for `AnchoredInterval{T, Hour(-1)}`
9-
* `HourBeginning`, a type alias for `AnchoredInterval{T, Hour(1)}`
6+
* `AnchoredInterval{T, S, E}`, which represents a non-iterable range defined by a single
7+
value `anchor::T` and `span::S` which represents the size of the range. The type
8+
parameter `E` is an instance of `Direction` and indicate whether the anchor is a
9+
left-endpoint (`Beginning`) or a right-endpoing (`Ending`).
10+
* `HourEnding`, a type alias for `AnchoredInterval{T, Hour, Ending}`
11+
* `HourBeginning`, a type alias for `AnchoredInterval{T, Hour, Beginning}`
1012
* `HE` and `HB`, pseudoconstructors for `HourEnding` and `HourBeginning` that round the
1113
anchor up (`HE`) or down (`HB`) to the nearest hour
1214
* `Inclusivity`, which represents whether an `AbstractInterval` is open, half-open, or

src/anchoredinterval.jl

Lines changed: 108 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
using Base.Dates: value, coarserperiod
22

33
"""
4-
AnchoredInterval{T, P}(anchor::T, [inclusivity::Inclusivity]) -> AnchoredInterval{T, P}
5-
AnchoredInterval{T, P}(anchor::T, [closed_left::Bool, closed_right::Bool]) -> AnchoredInterval{T, P}
4+
AnchoredInterval{T, S, E}(anchor::T, [span::S, inclusivity::Inclusivity])
5+
AnchoredInterval{T, S, E}(anchor::T, [span::S, closed_left::Bool, closed_right::Bool])
66
7-
AnchoredInterval(anchor::T, P, [inclusivity::Inclusivity]) -> AnchoredInterval{T, P}
8-
AnchoredInterval(anchor::T, P, [closed_left::Bool, closed_right::Bool]) -> AnchoredInterval{T, P}
7+
AnchoredInterval{T, S, E}(anchor::T, [inclusivity::Inclusivity])
8+
AnchoredInterval{T, S, E}(anchor::T, [closed_left::Bool, closed_right::Bool])
99
1010
`AnchoredInterval` is a subtype of `AbstractInterval` that represents a non-iterable range
1111
or span of values defined not by two endpoints but instead by a single `anchor` point and
@@ -24,8 +24,9 @@ included for positive values of `P` and the greater endpoint included for negati
2424
range of values. This happens most often with dates and times, where "HE15" is often used as
2525
shorthand for (14:00..15:00].
2626
27-
To this end, `HourEnding` is a type alias for `AnchoredInterval{T, Hour(-1)} where T`.
28-
Similarly, `HourBeginning` is a type alias for `AnchoredInterval{T, Hour(1)} where T`.
27+
To this end, `HourEnding` is a type alias for `AnchoredInterval{T, Hour, Ending} where T`.
28+
Similarly, `HourBeginning` is a type alias for
29+
`AnchoredInterval{T, Hour, Beginning} where T`.
2930
3031
### Rounding
3132
@@ -52,41 +53,80 @@ HourBeginning{DateTime}(2016-08-11T02:00:00, Inclusivity(true, false))
5253
5354
```julia
5455
julia> AnchoredInterval(DateTime(2016, 8, 11, 12), Hour(-1))
55-
HourEnding{DateTime}(2016-08-11T12:00:00, Inclusivity(false, true))
56+
HourEnding{DateTime}(2016-08-11T12:00:00, -1 hour, Inclusivity(false, true))
5657
5758
julia> AnchoredInterval(DateTime(2016, 8, 11), Day(1))
58-
AnchoredInterval{DateTime, 1 day}(2016-08-11T00:00:00, Inclusivity(true, false))
59+
AnchoredInterval{DateTime, Day, Beginning}(2016-08-11T00:00:00, 1 day, Inclusivity(true, false))
5960
6061
julia> AnchoredInterval(DateTime(2016, 8, 11, 12, 30), Minute(5), true, true)
61-
AnchoredInterval{DateTime, 5 minutes}(2016-08-11T12:30:00, Inclusivity(true, true))
62+
AnchoredInterval{DateTime, Minute, Beginning}(2016-08-11T12:30:00, 5 minutes, Inclusivity(true, true))
6263
```
6364
6465
See also: [`Interval`](@ref), [`Inclusivity`](@ref), [`HE`](@ref), [`HB`](@ref)
6566
"""
66-
struct AnchoredInterval{T, P} <: AbstractInterval{T}
67+
struct AnchoredInterval{T, S, E} <: AbstractInterval{T}
6768
anchor::T
69+
span::S
6870
inclusivity::Inclusivity
71+
72+
function AnchoredInterval{T, S, E}(anchor::T, span::S, inc::Inclusivity) where {T, S, E}
73+
@assert E isa Direction
74+
if span < zero(S)
75+
@assert E == Ending
76+
elseif span > zero(S)
77+
@assert E == Beginning
78+
else
79+
@assert E isa Direction
80+
end
81+
@assert typeof(anchor + span) == T
82+
new{T, S, E}(anchor, span, inc)
83+
end
84+
end
85+
86+
function AnchoredInterval{T, S, E}(anchor::T, span::S, x::Bool, y::Bool) where {T, S, E}
87+
AnchoredInterval{T, S, E}(anchor, span, Inclusivity(x, y))
88+
end
89+
function AnchoredInterval{T, S, E}(anchor::T, span::S) where {T, S, E}
90+
# If an interval is anchored to the lesser endpoint, default to Inclusivity(false, true)
91+
# If an interval is anchored to the greater endpoint, default to Inclusivity(true, false)
92+
AnchoredInterval{T, S, E}(anchor, span, Inclusivity(span zero(S), span zero(S)))
93+
end
94+
function AnchoredInterval{T, S, E}(anchor::T, inc::Inclusivity) where {T, S, E}
95+
span = E == Ending ? -oneunit(S) : oneunit(S)
96+
AnchoredInterval{T, S, E}(anchor, span, inc)
97+
end
98+
function AnchoredInterval{T, S, E}(anchor::T, x::Bool, y::Bool) where {T, S, E}
99+
AnchoredInterval{T, S, E}(anchor, Inclusivity(x, y))
100+
end
101+
function AnchoredInterval{T, S, E}(anchor::T) where {T, S, E}
102+
span = E == Ending ? -oneunit(S) : oneunit(S)
103+
AnchoredInterval{T, S, E}(anchor, span)
69104
end
70105

71-
# When an interval is anchored to the lesser endpoint, default to Inclusivity(false, true)
72-
# When an interval is anchored to the greater endpoint, default to Inclusivity(true, false)
73-
function AnchoredInterval{T, P}(i::T) where {T, P}
74-
return AnchoredInterval{T, P}(i::T, Inclusivity(P zero(P), P zero(P)))
106+
function AnchoredInterval{T, S}(anchor::T, span::S, args...) where {T, S}
107+
E = if span < zero(S)
108+
Ending
109+
elseif span > zero(S)
110+
Beginning
111+
else
112+
throw(ArgumentError("Must specify endpoint type when span is zero"))
113+
end
114+
AnchoredInterval{T, S, E}(anchor, span, args...)
75115
end
76116

77-
function AnchoredInterval{T, P}(i::T, x::Bool, y::Bool) where {T, P}
78-
return AnchoredInterval{T, P}(i, Inclusivity(x, y))
117+
function AnchoredInterval{T}(anchor::T, span::S, args...) where {T, S}
118+
AnchoredInterval{T, S}(anchor, span, args...)
79119
end
80120

81-
AnchoredInterval(i::T, span, inc::Inclusivity) where T = AnchoredInterval{T, span}(i, inc)
82-
AnchoredInterval(i::T, span, x::Bool, y::Bool) where T = AnchoredInterval{T, span}(i, x, y)
83-
AnchoredInterval(i::T, span) where T = AnchoredInterval{T, span}(i)
121+
function AnchoredInterval(anchor::T, span::S, args...) where {T, S}
122+
AnchoredInterval{T, S}(anchor, span, args...)
123+
end
84124

85125

86-
const HourEnding{T} = AnchoredInterval{T, Hour(-1)} where T <: TimeType
126+
const HourEnding{T} = AnchoredInterval{T, Hour, Ending} where T <: TimeType
87127
HourEnding(a::T, args...) where T = HourEnding{T}(a, args...)
88128

89-
const HourBeginning{T} = AnchoredInterval{T, Hour(1)} where T <: TimeType
129+
const HourBeginning{T} = AnchoredInterval{T, Hour, Beginning} where T <: TimeType
90130
HourBeginning(a::T, args...) where T = HourBeginning{T}(a, args...)
91131

92132
"""
@@ -105,22 +145,22 @@ nearest hour.
105145
"""
106146
HB(a, args...) = HourBeginning(floor(a, Hour), args...)
107147

108-
function Base.copy(x::AnchoredInterval{T, P}) where {T, P}
109-
return AnchoredInterval{T, P}(anchor(x), inclusivity(x))
148+
function Base.copy(x::AnchoredInterval{T, S, E}) where {T, S, E}
149+
return AnchoredInterval{T, S, E}(anchor(x), inclusivity(x))
110150
end
111151

112152
##### ACCESSORS #####
113153

114-
function Base.first(interval::AnchoredInterval{T, P}) where {T, P}
115-
min(interval.anchor, interval.anchor + P)
154+
function Base.first(interval::AnchoredInterval)
155+
min(interval.anchor, interval.anchor + interval.span)
116156
end
117157

118-
function Base.last(interval::AnchoredInterval{T, P}) where {T, P}
119-
max(interval.anchor, interval.anchor + P)
158+
function Base.last(interval::AnchoredInterval)
159+
max(interval.anchor, interval.anchor + interval.span)
120160
end
121161

122162
anchor(interval::AnchoredInterval) = interval.anchor
123-
span(interval::AnchoredInterval{T, P}) where {T, P} = abs(P)
163+
span(interval::AnchoredInterval) = abs(interval.span)
124164

125165
##### CONVERSION #####
126166

@@ -132,6 +172,19 @@ function Base.convert(::Type{Interval{T}}, interval::AnchoredInterval{T}) where
132172
return Interval{T}(first(interval), last(interval), inclusivity(interval))
133173
end
134174

175+
# Conversion methods which currently aren't needed but could prove useful. Commented out
176+
# since these are untested.
177+
178+
#=
179+
function Base.convert(::Type{AnchoredInterval{Ending}}, interval::Interval{T}) where T
180+
AnchoredInterval{T}(last(interval), -span(interval), inclusivity(interval))
181+
end
182+
183+
function Base.convert(::Type{AnchoredInterval{Beginning}}, interval::Interval{T}) where T
184+
AnchoredInterval{T}(first(interval), span(interval), inclusivity(interval))
185+
end
186+
=#
187+
135188
Base.convert(::Type{T}, interval::AnchoredInterval{T}) where T = anchor(interval)
136189

137190
# Date/DateTime attempt to convert to Int64 instead of falling back to convert(T, ...)
@@ -146,17 +199,21 @@ Base.show(io::IO, ::Type{HourBeginning}) = print(io, "HourBeginning{T}")
146199
Base.show(io::IO, ::Type{HourEnding{T}}) where T <: TimeType = print(io, "HourEnding{$T}")
147200
Base.show(io::IO, ::Type{HourBeginning{T}}) where T <: TimeType = print(io, "HourBeginning{$T}")
148201

149-
function Base.show(io::IO, ::Type{AnchoredInterval{T, P}}) where {T, P}
150-
print(io, "AnchoredInterval{$T, $P}")
202+
function Base.show(io::IO, ::Type{AnchoredInterval{T, S, E}}) where {T, S, E}
203+
d = E == Beginning ? "Beginning" : "Ending"
204+
print(io, "AnchoredInterval{$T, $S, $d}")
151205
end
152206

153-
function Base.show(io::IO, interval::T) where T <: AnchoredInterval
207+
function Base.show(io::IO, interval::AnchoredInterval)
154208
if get(io, :compact, false)
155209
print(io, interval)
156210
else
157-
print(io, "$T(")
211+
show(io, typeof(interval))
212+
print(io, "(")
158213
show(io, anchor(interval))
159214
print(io, ", ")
215+
show(io, interval.span)
216+
print(io, ", ")
160217
show(io, inclusivity(interval))
161218
print(io, ")")
162219
end
@@ -173,7 +230,7 @@ end
173230

174231
##### ARITHMETIC #####
175232

176-
Base.:+(a::T, b) where {T <: AnchoredInterval} = T(anchor(a) + b, inclusivity(a))
233+
Base.:+(a::T, b) where {T <: AnchoredInterval} = T(anchor(a) + b, a.span, inclusivity(a))
177234

178235
Base.:+(a, b::AnchoredInterval) = b + a
179236
Base.:-(a::AnchoredInterval, b) = a + -b
@@ -183,15 +240,20 @@ Base.:-(a::AnchoredInterval, b::AnchoredInterval) = anchor(a) - anchor(b)
183240

184241
Base.:-(a::T, b::AnchoredInterval{T}) where {T <: Number} = a + -b
185242

186-
function Base.:-(a::AnchoredInterval{T, P}) where {T <: Number, P}
243+
function Base.:-(a::AnchoredInterval{T, S, Ending}) where {T <: Number, S}
244+
inc = inclusivity(a)
245+
AnchoredInterval{T, S, Beginning}(-anchor(a), -a.span, Inclusivity(last(inc), first(inc)))
246+
end
247+
248+
function Base.:-(a::AnchoredInterval{T, S, Beginning}) where {T <: Number, S}
187249
inc = inclusivity(a)
188-
AnchoredInterval{T, -P}(-anchor(a), Inclusivity(last(inc), first(inc)))
250+
AnchoredInterval{T, S, Ending}(-anchor(a), -a.span, Inclusivity(last(inc), first(inc)))
189251
end
190252

191253
##### EQUALITY #####
192254

193255
# Required for min/max of AnchoredInterval{LaxZonedDateTime} when the anchor is AMB or DNE
194-
function Base.:<(a::AnchoredInterval{T, P}, b::AnchoredInterval{T, P}) where {T, P}
256+
function Base.:<(a::AnchoredInterval{T, S, E}, b::AnchoredInterval{T, S, E}) where {T, S, E}
195257
return anchor(a) < anchor(b)
196258
end
197259

@@ -203,8 +265,8 @@ function Base.steprem(a::T, b::T, c) where {T <: AnchoredInterval}
203265
end
204266

205267
# Infer step for two-argument StepRange{<:AnchoredInterval}
206-
function Base.colon(start::AnchoredInterval{T, P}, stop::AnchoredInterval{T, P}) where {T,P}
207-
return colon(start, abs(P), stop)
268+
function Base.colon(start::AnchoredInterval{T, S}, stop::AnchoredInterval{T, S}) where {T,S}
269+
return colon(start, oneunit(S), stop)
208270
end
209271

210272
function Base.length(r::StepRange{<:AnchoredInterval})
@@ -213,23 +275,23 @@ end
213275

214276
##### SET OPERATIONS #####
215277

216-
function Base.isempty(interval::AnchoredInterval{T, P}) where {T, P}
217-
return P == zero(P) && !isclosed(interval)
278+
function Base.isempty(interval::AnchoredInterval{T, S}) where {T, S}
279+
return span(interval) == zero(S) && !isclosed(interval)
218280
end
219281

220-
function Base.intersect(a::AnchoredInterval{T, P}, b::AnchoredInterval{T, Q}) where {T,P,Q}
282+
function Base.intersect(a::AnchoredInterval{T, S, E}, b::AnchoredInterval{T, S, E}) where {T, S, E}
221283
interval = invoke(intersect, Tuple{AbstractInterval{T}, AbstractInterval{T}}, a, b)
222284

223-
sp = isa(P, Period) ? canonicalize(typeof(P), span(interval)) : span(interval)
224-
if P zero(P)
285+
sp = S <: Period ? canonicalize(S, span(interval)) : span(interval)
286+
if E == Ending
225287
anchor = last(interval)
226-
new_P = -sp
288+
sp = -sp
227289
else
228290
anchor = first(interval)
229-
new_P = sp
291+
sp = sp
230292
end
231293

232-
return AnchoredInterval{T, new_P}(anchor, inclusivity(interval))
294+
return AnchoredInterval{T, S, E}(anchor, sp, inclusivity(interval))
233295
end
234296

235297
##### UTILITIES #####

src/description.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
using Base.Dates: value, coarserperiod
22

3-
function description(interval::AnchoredInterval{T, P}) where {T, P}
4-
description(interval, P > zero(P) ? "B" : "E")
3+
function description(interval::AnchoredInterval{T, S, E}) where {T, S, E}
4+
description(interval, E == Beginning ? "B" : "E")
55
end
66

7-
function description(interval::AnchoredInterval{T, P}, s::String) where {T, P}
7+
function description(interval::AnchoredInterval, s::String)
88
return string(
99
first(inclusivity(interval)) ? '[' : '(',
10-
description(anchor(interval), abs(P), s),
10+
description(anchor(interval), span(interval), s),
1111
last(inclusivity(interval)) ? ']' : ')',
1212
)
1313
end
1414

15-
function description(interval::AnchoredInterval{ZonedDateTime, P}, s::String) where P
15+
function description(interval::AnchoredInterval{ZonedDateTime}, s::String)
1616
return string(
1717
first(inclusivity(interval)) ? '[' : '(',
18-
description(anchor(interval), abs(P), s),
18+
description(anchor(interval), span(interval), s),
1919
anchor(interval).zone.offset,
2020
last(inclusivity(interval)) ? ']' : ')',
2121
)

src/endpoint.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ struct Direction{T} end
22
const Left = Direction{:Left}()
33
const Right = Direction{:Right}()
44

5+
const Beginning = Left
6+
const Ending = Right
7+
58
struct Endpoint{T, D}
69
endpoint::T
710
included::Bool

0 commit comments

Comments
 (0)