|
123 | 123 | for (a, b, _) in test_values |
124 | 124 | for (L, R) in BOUND_PERMUTATIONS |
125 | 125 | interval = Interval{L, R}(a, b) |
126 | | - |
127 | 126 | @test first(interval) == a |
128 | 127 | @test last(interval) == b |
129 | 128 | @test span(interval) == b - a |
|
144 | 143 | @test span(interval) == Hour(3) |
145 | 144 | end |
146 | 145 |
|
| 146 | + @testset "maximum/minimum" begin |
| 147 | + # Helper functions that manage the value we should be expecting from min and max. |
| 148 | + function _min_val_helper(interval, a, unit) |
| 149 | + t = eltype(interval) |
| 150 | + # If the interal is empty, min is nothing |
| 151 | + isempty(interval) && return nothing |
| 152 | + |
| 153 | + # If a is in the interval, it is closed/unbounded and min is the first value. |
| 154 | + # If a is nothing then it is unbounded and min is typemin(T) |
| 155 | + a === nothing && return typemin(t) |
| 156 | + a ∈ interval && return first(interval) |
| 157 | + |
| 158 | + # From this point on, b ∉ interval so the bound is Open |
| 159 | + # Also, if a is infinite we return typemin |
| 160 | + # If it's an abstractfloat, we can't return just typemin since typemin IS Inf and |
| 161 | + # since the bound is open at this point, Inf ∉ interval So we return the one after INF |
| 162 | + !Intervals.isfinite(a) && t <: AbstractFloat && return nextfloat(a) |
| 163 | + !Intervals.isfinite(a) && return typemin(t) |
| 164 | + |
| 165 | + f = first(interval) |
| 166 | + nv = if t <: AbstractFloat && unit === nothing |
| 167 | + nextfloat(f) |
| 168 | + else |
| 169 | + f + unit |
| 170 | + end |
| 171 | + |
| 172 | + nv ∈ interval && return nv |
| 173 | + |
| 174 | + # If we get to this point, the min/max functions throw a DomainError |
| 175 | + # Since we want our tests to be predictable, we will not throw an error in this helper. |
| 176 | + end |
| 177 | + |
| 178 | + function _max_val_helper(interval, b, unit) |
| 179 | + t = eltype(interval) |
| 180 | + # If the interal is empty, min is nothing |
| 181 | + isempty(interval) && return nothing |
| 182 | + |
| 183 | + # If a is in the interval, it is closed/unbounded and min is the first value. |
| 184 | + # If a is nothing then it is unbounded and min is typemin(T) |
| 185 | + b === nothing && return typemax(t) |
| 186 | + b ∈ interval && return last(interval) |
| 187 | + |
| 188 | + # From this point on, b ∉ interval so the bound is Open |
| 189 | + # Also, if a is infinite we return typemin |
| 190 | + # If it's an abstractfloat, we can't return just typemin since typemin IS Inf and |
| 191 | + # since the bound is open at this point, Inf ∉ interval So we return the one after INF |
| 192 | + !isfinite(b) && t <: AbstractFloat && return prevfloat(b) |
| 193 | + !isfinite(b) && return typemax(t) |
| 194 | + |
| 195 | + l = last(interval) |
| 196 | + nv = if t <: AbstractFloat && unit === nothing |
| 197 | + prevfloat(l) |
| 198 | + else |
| 199 | + l - unit |
| 200 | + end |
| 201 | + |
| 202 | + nv ∈ interval && return nv |
| 203 | + |
| 204 | + # If we get to this point, the min/max functions throw a DomainError |
| 205 | + # Since we want our tests to be predictable, we will not throw an error in this helper. |
| 206 | + end |
| 207 | + @testset "bounded intervals" begin |
| 208 | + bounded_test_vals = [ |
| 209 | + #test nextfloat and prevfloat |
| 210 | + (-10.0, 10.0, nothing), |
| 211 | + (-Inf, Inf, nothing), |
| 212 | + |
| 213 | + ('c', 'x', 2), |
| 214 | + (Date(2004, 2, 13), Date(2020, 3, 13), Day(1)), |
| 215 | + ] |
| 216 | + for (a, b, unit) in append!(bounded_test_vals, test_values) |
| 217 | + for (L, R) in BOUND_PERMUTATIONS |
| 218 | + interval = Interval{L, R}(a, b) |
| 219 | + |
| 220 | + mi = _min_val_helper(interval, a, unit) |
| 221 | + ma = _max_val_helper(interval, b, unit) |
| 222 | + |
| 223 | + @test minimum(interval; increment=unit) == mi |
| 224 | + @test maximum(interval; increment=unit) == ma |
| 225 | + end |
| 226 | + end |
| 227 | + end |
| 228 | + |
| 229 | + @testset "unbounded intervals" begin |
| 230 | + unbounded_test_values = [ |
| 231 | + # one side unbounded with different types |
| 232 | + (Interval{Open,Unbounded}(-10, nothing), 1), |
| 233 | + (Interval{Unbounded,Closed}(nothing, 1.0), 0.01), |
| 234 | + (Interval{Unbounded,Open}(nothing, 'z'), 1), |
| 235 | + (Interval{Closed,Unbounded}(Date(2013, 2, 13), nothing), Day(1)), |
| 236 | + (Interval{Open,Unbounded}(DateTime(2016, 8, 11, 0, 30), nothing), Millisecond(1)), |
| 237 | + # both sides unbounded different types |
| 238 | + (Interval{Int}(nothing, nothing), 1), |
| 239 | + (Interval{Float64}(nothing, nothing), 0.01), |
| 240 | + (Interval{Char}(nothing , nothing), 1), |
| 241 | + (Interval{Day}(nothing, nothing), Day(1)), |
| 242 | + (Interval{DateTime}(nothing, nothing), Millisecond(1)), |
| 243 | + # test adding eps() with unbounded |
| 244 | + (Interval{Open,Unbounded}(-10.0, nothing), nothing), |
| 245 | + (Interval{Unbounded,Open}(nothing, 10.0), nothing), |
| 246 | + # test infinity |
| 247 | + (Interval{Open,Unbounded}(-Inf, nothing), nothing), |
| 248 | + (Interval{Unbounded,Open}(nothing, Inf), nothing), |
| 249 | + ] |
| 250 | + for (interval, unit) in unbounded_test_values |
| 251 | + a, b = first(interval), last(interval) |
| 252 | + |
| 253 | + mi = _min_val_helper(interval, a, unit) |
| 254 | + ma = _max_val_helper(interval, b, unit) |
| 255 | + |
| 256 | + @test minimum(interval; increment=unit) == mi |
| 257 | + @test maximum(interval; increment=unit) == ma |
| 258 | + @test_throws DomainError span(interval) |
| 259 | + |
| 260 | + end |
| 261 | + end |
| 262 | + @testset "bounds errors in min/max" begin |
| 263 | + error_test_vals = [ |
| 264 | + # empty intervals |
| 265 | + (Interval{Open,Open}(-10, -10), 1), |
| 266 | + (Interval{Open,Open}(0.0, 0.0), 60), |
| 267 | + (Interval{Open,Open}(Date(2013, 2, 13), Date(2013, 2, 13)), Day(1)), |
| 268 | + (Interval{Open,Open}(DateTime(2016, 8, 11, 0, 30), DateTime(2016, 8, 11, 0, 30)), Day(1)), |
| 269 | + # increment too large |
| 270 | + (Interval{Open,Open}(-10, 15), 60), |
| 271 | + (Interval{Open,Open}(0.0, 25), 60.0), |
| 272 | + (Interval{Open,Open}(Date(2013, 2, 13), Date(2013, 2, 14)), Day(5)), |
| 273 | + (Interval{Open,Open}(DateTime(2016, 8, 11, 0, 30), DateTime(2016, 8, 11, 5, 30)), Day(5)), |
| 274 | + ] |
| 275 | + for (interval, unit) in error_test_vals |
| 276 | + @test_throws BoundsError minimum(interval; increment=unit) |
| 277 | + @test_throws BoundsError maximum(interval; increment=unit) |
| 278 | + end |
| 279 | + end |
| 280 | + end |
147 | 281 | @testset "display" begin |
148 | 282 | interval = Interval{Open, Open}(1, 2) |
149 | 283 | @test string(interval) == "(1 .. 2)" |
|
0 commit comments