Skip to content

Commit fcbe6a6

Browse files
aplavinhyrodium
andauthored
implement Base.mod (#114)
* implement Base.mod * docstring Co-authored-by: Yuto Horikawa <[email protected]> * mod() for open and half-open intervals Co-authored-by: Yuto Horikawa <[email protected]>
1 parent 3ce0217 commit fcbe6a6

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

src/IntervalSets.jl

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module IntervalSets
22

33
using Base: @pure
44
import Base: eltype, convert, show, in, length, isempty, isequal, issubset, ==, hash,
5-
union, intersect, minimum, maximum, extrema, range, clamp, float, , ,
5+
union, intersect, minimum, maximum, extrema, range, clamp, mod, float, , ,
66

77
using Statistics
88
import Statistics: mean
@@ -229,6 +229,46 @@ Clamp the scalar `t` such that the result is in the interval `i`.
229229
clamp(t, i::TypedEndpointsInterval{:closed,:closed}) =
230230
clamp(t, leftendpoint(i), rightendpoint(i))
231231

232+
"""
233+
mod(x, i::AbstractInterval)
234+
235+
Find `y` in the `i` interval such that ``x ≡ y (mod w)``, where `w = width(i)`.
236+
237+
# Examples
238+
239+
```jldoctest
240+
julia> I = 2.5..4.5;
241+
242+
julia> mod(3.0, I)
243+
3.0
244+
245+
julia> mod(5.0, I)
246+
3.0
247+
248+
julia> mod(2.5, I)
249+
2.5
250+
251+
julia> mod(4.5, I) # (a in I) does not imply (a == mod(a, I)) for closed intervals
252+
2.5
253+
254+
julia> mod(4.5, Interval{:open, :closed}(2.5, 4.5))
255+
4.5
256+
```
257+
"""
258+
mod(x, i::TypedEndpointsInterval{:closed,:closed}) = mod(x - leftendpoint(i), width(i)) + leftendpoint(i)
259+
260+
function mod(x, i::AbstractInterval)
261+
res = mod(x - leftendpoint(i), width(i)) + leftendpoint(i)
262+
if res == rightendpoint(i) && isrightopen(i)
263+
isleftclosed(i) && return oftype(res, leftendpoint(i))
264+
elseif res == leftendpoint(i) && isleftopen(i)
265+
isrightclosed(i) && return oftype(res, rightendpoint(i))
266+
else
267+
return res
268+
end
269+
throw(DomainError(x, "mod() result is an endpoint of the open interval $i"))
270+
end
271+
232272
include("interval.jl")
233273
include("findall.jl")
234274

test/runtests.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,21 @@ struct IncompleteInterval <: AbstractInterval{Int} end
728728
@test clamp.([pi, 1.0, big(10.)], Ref(2..9.)) == [big(pi), 2, 9]
729729
end
730730

731+
@testset "mod" begin
732+
@test mod(10, 0..3) === 1
733+
@test mod(-10, 0..3) === 2
734+
@test mod(10.5, 0..3) == 1.5
735+
@test mod(10.5, 1..1) |> isnan
736+
@test mod(10.5, Interval{:open, :open}(0, 3)) == 1.5
737+
@test mod(10.5, Interval{:open, :open}(1, 1)) |> isnan
738+
739+
@test_throws DomainError mod(0, Interval{:open, :open}(0, 3))
740+
for x in (0, 3, 0.0, -0.0, 3.0, -eps())
741+
@test mod(x, Interval{:closed, :open}(0, 3))::typeof(x) == 0
742+
@test mod(x, Interval{:open, :closed}(0, 3))::typeof(x) == 3
743+
end
744+
end
745+
731746
@testset "rand" begin
732747
@test rand(1..2) isa Float64
733748
@test rand(1..2.) isa Float64

0 commit comments

Comments
 (0)