@@ -6,27 +6,65 @@ using Base: @pure
6
6
import Base: eltype, convert, show, in, length, isempty, isequal, issubset, == , hash,
7
7
union, intersect, minimum, maximum, extrema, range, ⊇
8
8
9
+ using Compat. Statistics
10
+ import Compat. Statistics: mean
11
+
12
+
9
13
using Compat
10
14
using Compat. Dates
11
15
12
- export AbstractInterval, ClosedInterval, ⊇ , .. , ± , ordered, width
16
+ export AbstractInterval, Interval, OpenInterval, ClosedInterval,
17
+ ⊇ , .. , ± , ordered, width, duration, leftendpoint, rightendpoint, endpoints,
18
+ isclosed, isleftclosed, isrightclosed, isleftopen, isrightopen, closedendpoints,
19
+ infimum, supremum
20
+
21
+ """
22
+ A subtype of `Domain{T}` represents a subset of type `T`, that provides `in`.
23
+ """
24
+ abstract type Domain{T} end
25
+ """
26
+ A subtype of `AbstractInterval{T}` represents an interval subset of type `T`, that provides
27
+ `endpoints`, `closedendpoints`.
28
+ """
29
+ abstract type AbstractInterval{T} <: Domain{T} end
30
+
31
+
32
+ " A tuple containing the left and right endpoints of the interval."
33
+ endpoints (d:: AI ) where AI<: AbstractInterval = error (" Override endpoints(::$(AI) )" )
34
+
35
+ " The left endpoint of the interval."
36
+ leftendpoint (d:: AbstractInterval ) = endpoints (d)[1 ]
37
+
38
+ " The right endpoint of the interval."
39
+ rightendpoint (d:: AbstractInterval ) = endpoints (d)[2 ]
40
+
41
+ " A tuple of `Bool`'s encoding whether the left/right endpoints are closed."
42
+ closedendpoints (d:: AI ) where AI<: AbstractInterval = error (" Override closedendpoints(::$(AI) )" )
43
+
44
+ " Is the interval closed at the left endpoint?"
45
+ isleftclosed (d:: AbstractInterval ) = closedendpoints (d)[1 ]
46
+
47
+ " Is the interval closed at the right endpoint?"
48
+ isrightclosed (d:: AbstractInterval ) = closedendpoints (d)[2 ]
49
+
50
+ # open_left and open_right are implemented in terms of closed_* above, so those
51
+ # are the only ones that should be implemented for specific intervals
52
+ " Is the interval open at the left endpoint?"
53
+ isleftopen (d:: AbstractInterval ) = ! isleftclosed (d)
13
54
14
- abstract type AbstractInterval{T} end
55
+ " Is the interval open at the right endpoint?"
56
+ isrightopen (d:: AbstractInterval ) = ! isrightclosed (d)
15
57
16
- include (" closed.jl" )
58
+ # Only closed if closed at both endpoints, and similar for open
59
+ isclosed (d:: AbstractInterval ) = isleftclosed (d) && isrightclosed (d)
60
+ isopen (d:: AbstractInterval ) = isleftopen (d) && isrightopen (d)
17
61
18
62
eltype (:: Type{AbstractInterval{T}} ) where {T} = T
19
63
@pure eltype (:: Type{I} ) where {I<: AbstractInterval } = eltype (supertype (I))
20
64
21
- convert (:: Type{I} , i:: I ) where {I<: AbstractInterval } = i
22
- function convert (:: Type{I} , i:: AbstractInterval ) where I<: AbstractInterval
23
- T = eltype (I)
24
- I (convert (T, i. left), convert (T, i. right))
25
- end
26
- function convert (:: Type{I} , r:: AbstractRange ) where I<: AbstractInterval
27
- T = eltype (I)
28
- I (convert (T, minimum (r)), convert (T, maximum (r)))
29
- end
65
+ convert (:: Type{AbstractInterval} , i:: AbstractInterval ) = i
66
+ convert (:: Type{AbstractInterval{T}} , i:: AbstractInterval{T} ) where T = i
67
+
30
68
31
69
ordered (a:: T , b:: T ) where {T} = ifelse (a < b, (a, b), (b, a))
32
70
ordered (a, b) = ordered (promote (a, b)... )
@@ -36,4 +74,133 @@ _checked_conversion(::Type{T}, a::T, b::T) where {T} = a, b
36
74
_checked_conversion (:: Type{Any} , a, b) = throw (ArgumentError (" $a and $b promoted to type Any" ))
37
75
_checked_conversion (:: Type{T} , a, b) where {T} = throw (ArgumentError (" $a and $b are not both of type $T " ))
38
76
77
+ function infimum (d:: AbstractInterval{T} ) where T
78
+ a = leftendpoint (d)
79
+ b = rightendpoint (d)
80
+ a > b && throw (ArgumentError (" Infimum not defined for empty intervals" ))
81
+ a
82
+ end
83
+
84
+ function supremum (d:: AbstractInterval{T} ) where T
85
+ a = leftendpoint (d)
86
+ b = rightendpoint (d)
87
+ a > b && throw (ArgumentError (" Supremum not defined for empty intervals" ))
88
+ b
89
+ end
90
+
91
+ mean (d:: AbstractInterval ) = (leftendpoint (d) + rightendpoint (d))/ 2
92
+
93
+ issubset (A:: AbstractInterval , B:: AbstractInterval ) = ((leftendpoint (A) in B) && (rightendpoint (A) in B)) || isempty (A)
94
+ ⊇ (A:: AbstractInterval , B:: AbstractInterval ) = issubset (B, A)
95
+ if VERSION < v " 1.1.0-DEV.123"
96
+ issubset (x, B:: AbstractInterval ) = issubset (convert (AbstractInterval, x), B)
97
+ end
98
+
99
+ """
100
+ w = width(iv)
101
+
102
+ Calculate the width (max-min) of interval `iv`. Note that for integers
103
+ `l` and `r`, `width(l..r) = length(l:r) - 1`.
104
+ """
105
+ function width (A:: AbstractInterval )
106
+ _width = rightendpoint (A) - leftendpoint (A)
107
+ max (zero (_width), _width) # this works when T is a Date
108
+ end
109
+
110
+ """
111
+ A subtype of `TypedEndpointsInterval{L,R,T}` where `L` and `R` are `:open` or `:closed`,
112
+ that represents an interval subset of type `T`, and provides `endpoints`.
113
+ """
114
+ abstract type TypedEndpointsInterval{L,R,T} <: AbstractInterval{T} end
115
+
116
+ closedendpoints (d:: TypedEndpointsInterval{:closed,:closed} ) = (true ,true )
117
+ closedendpoints (d:: TypedEndpointsInterval{:closed,:open} ) = (true ,false )
118
+ closedendpoints (d:: TypedEndpointsInterval{:open,:closed} ) = (false ,true )
119
+ closedendpoints (d:: TypedEndpointsInterval{:open,:open} ) = (false ,false )
120
+
121
+
122
+ in (v, I:: TypedEndpointsInterval{:closed,:closed} ) = leftendpoint (I) ≤ v ≤ rightendpoint (I)
123
+ in (v, I:: TypedEndpointsInterval{:open,:open} ) = leftendpoint (I) < v < rightendpoint (I)
124
+ in (v, I:: TypedEndpointsInterval{:closed,:open} ) = leftendpoint (I) ≤ v < rightendpoint (I)
125
+ in (v, I:: TypedEndpointsInterval{:open,:closed} ) = leftendpoint (I) < v ≤ rightendpoint (I)
126
+
127
+ in (a:: AbstractInterval , b:: TypedEndpointsInterval{:closed,:closed} ) =
128
+ (leftendpoint (a) ≥ leftendpoint (b)) & (rightendpoint (a) ≤ rightendpoint (b))
129
+ in (a:: TypedEndpointsInterval{:open,:open} , b:: TypedEndpointsInterval{:open,:open} ) =
130
+ (leftendpoint (a) ≥ leftendpoint (b)) & (rightendpoint (a) ≤ rightendpoint (b))
131
+ in (a:: TypedEndpointsInterval{:closed,:open} , b:: TypedEndpointsInterval{:open,:open} ) =
132
+ (leftendpoint (a) > leftendpoint (b)) & (rightendpoint (a) ≤ rightendpoint (b))
133
+ in (a:: TypedEndpointsInterval{:open,:closed} , b:: TypedEndpointsInterval{:open,:open} ) =
134
+ (leftendpoint (a) ≥ leftendpoint (b)) & (rightendpoint (a) < rightendpoint (b))
135
+ in (a:: TypedEndpointsInterval{:closed,:closed} , b:: TypedEndpointsInterval{:open,:open} ) =
136
+ (leftendpoint (a) > leftendpoint (b)) & (rightendpoint (a) < rightendpoint (b))
137
+ in (a:: TypedEndpointsInterval{:closed} , b:: TypedEndpointsInterval{:open,:closed} ) =
138
+ (leftendpoint (a) > leftendpoint (b)) & (rightendpoint (a) ≤ rightendpoint (b))
139
+ in (a:: TypedEndpointsInterval{:open} , b:: TypedEndpointsInterval{:open,:closed} ) =
140
+ (leftendpoint (a) ≥ leftendpoint (b)) & (rightendpoint (a) ≤ rightendpoint (b))
141
+ in (a:: TypedEndpointsInterval{L,:closed} , b:: TypedEndpointsInterval{:closed,:open} ) where L = (leftendpoint (a) ≥ leftendpoint (b)) & (rightendpoint (a) < rightendpoint (b))
142
+ in (a:: TypedEndpointsInterval{L,:open} , b:: TypedEndpointsInterval{:closed,:open} ) where L = (leftendpoint (a) ≥ leftendpoint (b)) & (rightendpoint (a) ≤ rightendpoint (b))
143
+
144
+ isempty (A:: TypedEndpointsInterval{:closed,:closed} ) = leftendpoint (A) > rightendpoint (A)
145
+ isempty (A:: TypedEndpointsInterval ) = leftendpoint (A) ≥ rightendpoint (A)
146
+
147
+ isequal (A:: TypedEndpointsInterval{L,R} , B:: TypedEndpointsInterval{L,R} ) where {L,R} = (isequal (leftendpoint (A), leftendpoint (B)) & isequal (rightendpoint (A), rightendpoint (B))) | (isempty (A) & isempty (B))
148
+ isequal (A:: TypedEndpointsInterval , B:: TypedEndpointsInterval ) = isempty (A) & isempty (B)
149
+
150
+ == (A:: TypedEndpointsInterval{L,R} , B:: TypedEndpointsInterval{L,R} ) where {L,R} = (leftendpoint (A) == leftendpoint (B) && rightendpoint (A) == rightendpoint (B)) || (isempty (A) && isempty (B))
151
+ == (A:: TypedEndpointsInterval , B:: TypedEndpointsInterval ) = isempty (A) && isempty (B)
152
+
153
+ const _interval_hash = UInt == UInt64 ? 0x1588c274e0a33ad4 : 0x1e3f7252
154
+
155
+ hash (I:: TypedEndpointsInterval , h:: UInt ) = hash (leftendpoint (I), hash (rightendpoint (I), hash (_interval_hash, h)))
156
+
157
+ minimum (d:: TypedEndpointsInterval{:closed} ) = infimum (d)
158
+ minimum (d:: TypedEndpointsInterval{:open} ) = throw (ArgumentError (" $d is open on the left. Use infimum." ))
159
+ maximum (d:: TypedEndpointsInterval{L,:closed} ) where L = supremum (d)
160
+ maximum (d:: TypedEndpointsInterval{L,:open} ) where L = throw (ArgumentError (" $d is open on the right. Use supremum." ))
161
+
162
+ extrema (I:: TypedEndpointsInterval ) = (infimum (I), supremum (I))
163
+
164
+ # Open and closed at endpoints
165
+ isleftclosed (d:: TypedEndpointsInterval{:closed} ) = true
166
+ isleftclosed (d:: TypedEndpointsInterval{:open} ) = false
167
+ isrightclosed (d:: TypedEndpointsInterval{L,:closed} ) where {L} = true
168
+ isrightclosed (d:: TypedEndpointsInterval{L,:open} ) where {L} = false
169
+
170
+ # UnitRange construction
171
+ # The third is the one we want, but the first two are needed to resolve ambiguities
172
+ Base. Slice {T} (i:: TypedEndpointsInterval{:closed,:closed,I} ) where {T<: AbstractUnitRange ,I<: Integer } =
173
+ Base. Slice {T} (minimum (i): maximum (i))
174
+ function Base. OneTo {T} (i:: TypedEndpointsInterval{:closed,:closed,I} ) where {T<: Integer ,I<: Integer }
175
+ @noinline throwstart (i) = throw (ArgumentError (" smallest element must be 1, got $(minimum (i)) " ))
176
+ minimum (i) == 1 || throwstart (i)
177
+ Base. OneTo {T} (maximum (i))
178
+ end
179
+ UnitRange {T} (i:: TypedEndpointsInterval{:closed,:closed,I} ) where {T<: Integer ,I<: Integer } = UnitRange {T} (minimum (i), maximum (i))
180
+ UnitRange (i:: TypedEndpointsInterval{:closed,:closed,I} ) where {I<: Integer } = UnitRange {I} (i)
181
+ range (i:: TypedEndpointsInterval{:closed,:closed,I} ) where {I<: Integer } = UnitRange {I} (i)
182
+
183
+ """
184
+ duration(iv)
185
+
186
+ calculates the the total number of integers or dates of an integer or date
187
+ valued interval. For example, `duration(0..1)` is 2, while `width(0..1)` is 1.
188
+ """
189
+ duration (A:: TypedEndpointsInterval{:closed,:closed,T} ) where {T<: Integer } = max (0 , Int (A. right - A. left) + 1 )
190
+ duration (A:: TypedEndpointsInterval{:closed,:closed,Date} ) = max (0 , Dates. days (A. right - A. left) + 1 )
191
+
192
+ include (" interval.jl" )
193
+
194
+ # convert numbers to intervals
195
+ convert (:: Type{AbstractInterval} , x:: Number ) = x.. x
196
+ convert (:: Type{AbstractInterval{T}} , x:: Number ) where T =
197
+ convert (AbstractInterval{T}, convert (AbstractInterval, x))
198
+ convert (:: Type{TypedEndpointsInterval{:closed,:closed}} , x:: Number ) = x.. x
199
+ convert (:: Type{TypedEndpointsInterval{:closed,:closed,T}} , x:: Number ) where T =
200
+ convert (AbstractInterval{T}, convert (AbstractInterval, x))
201
+ convert (:: Type{ClosedInterval} , x:: Number ) = x.. x
202
+ convert (:: Type{ClosedInterval{T}} , x:: Number ) where T =
203
+ convert (AbstractInterval{T}, convert (AbstractInterval, x))
204
+
205
+
39
206
end # module
0 commit comments