From fe1c7cd62bdfb5b1fd5422cc5be6fbe805cd0a51 Mon Sep 17 00:00:00 2001 From: Yuto Horikawa Date: Mon, 12 Feb 2024 11:30:58 +0900 Subject: [PATCH] Update docs (#186) * add docstring to `@iv_str` * update autodocs * update docs with `iv_str` macro * update docs * update docstrings * add docs about `plot` visualization --- docs/Project.toml | 1 + docs/src/api.md | 2 +- docs/src/index.md | 46 +++++++++- src/IntervalSets.jl | 217 +++++++++++++++++++++++++++++++++++++++++--- src/interval.jl | 37 +++++++- 5 files changed, 282 insertions(+), 21 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index 2c69d71..6abef9f 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,6 +1,7 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" [compat] Documenter = "1" diff --git a/docs/src/api.md b/docs/src/api.md index cc0494f..c24af73 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -2,5 +2,5 @@ ```@autodocs Modules = [IntervalSets] -Order = [:type, :function] +Order = [:type, :function, :macro] ``` diff --git a/docs/src/index.md b/docs/src/index.md index 5f929cb..f847f96 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -38,7 +38,7 @@ Optionally, `Interval{L,R}` can represent open and half-open intervals. The type parameters `L` and `R` correspond to the left and right endpoint respectively. The notation [`ClosedInterval`](@ref) is short for `Interval{:closed,:closed}`, while [`OpenInterval`](@ref) is short for `Interval{:open,:open}`. -For example, the interval `Interval{:open,:closed}` corresponds to the set ``\{x \ | \ a < x ≤ b\}``. +For example, the interval `Interval{:open,:closed}` corresponds to the set ``(a,b] = \{x \ | \ a < x ≤ b\}``. ## More examples @@ -51,13 +51,24 @@ using IntervalSets ClosedInterval{Float64}(1,3) OpenInterval{Float64}(1,3) Interval{:open, :closed}(1,3) -0.5..2.5 -1.5 ± 1 -Interval{:open,:closed}(1,3) OpenInterval(0.5..2.5) # construct `OpenInterval` from `ClosedInterval` ``` -The [`±`](@ref) operator may be typed as `\pm` (using Julia's LaTeX syntax tab-completion). +The [`±`](@ref) operator and [`..`](@ref) creates [`ClosedInterval`](@ref) instance. + +```@repl more +0.5..2.5 +1.5 ± 1 # \pm +``` + +There is also a useful string macro [`@iv_str`](@ref) to define an interval with mathematical notations such as ``(a,b]``. + +```@repl more +iv"[1,2]" +iv"[1,2)" +iv"(1,2]" +iv"(1,2)" +``` ### Set operations @@ -74,6 +85,31 @@ isleftopen(2..3) (0.25..5) ∪ (6..7.4) # union of interval must be an interval ``` +### Visualization +`Interval`s can be visulalized with `Plots.plot` function. + +```@example plot +using IntervalSets, Plots +plot(iv"(1,2)") +plot!(iv"[3,6)") +plot!(iv"[5,7)") +savefig("plot-intervals.png") # hide +nothing # hide +``` + +![](plot-intervals.png) + +The `offset` keyword argument is useful for avoid duplication. + +```@example plot +plot(iv"[1,3]") +plot!(iv"(2,4)"; offset=-0.1, ylims=(-1,1)) +savefig("plot-intervals-offset.png") # hide +nothing # hide +``` + +![](plot-intervals-offset.png) + ### Importing the `..` operator To import the [`..`](@ref) operator, use `import IntervalSets: (..)`. diff --git a/src/IntervalSets.jl b/src/IntervalSets.jl index 9ff0045..b7374aa 100644 --- a/src/IntervalSets.jl +++ b/src/IntervalSets.jl @@ -25,37 +25,184 @@ A subtype of `AbstractInterval{T}` represents an interval subset of type `T`, th """ abstract type AbstractInterval{T} <: Domain{T} end +""" + endpoints(d::AI) where AI<:AbstractInterval + +A tuple containing the left and right endpoints of the interval. -"A tuple containing the left and right endpoints of the interval." +# Examples +```jldoctest +julia> endpoints(iv"[1,2]") +(1, 2) + +julia> endpoints(iv"(2,1)") +(2, 1) +``` +""" endpoints(d::AI) where AI<:AbstractInterval = error("Override endpoints(::$(AI))") -"The left endpoint of the interval." +""" + leftendpoint(d::AbstractInterval) + +The left endpoint of the interval. + +# Examples +```jldoctest +julia> leftendpoint(iv"[1,2]") +1 + +julia> leftendpoint(iv"(2,1)") +2 +``` +""" leftendpoint(d::AbstractInterval) = endpoints(d)[1] -"The right endpoint of the interval." +""" + rightendpoint(d::AbstractInterval) + +The right endpoint of the interval. + +# Examples +```jldoctest +julia> rightendpoint(iv"[1,2]") +2 + +julia> rightendpoint(iv"(2,1)") +1 +``` +""" rightendpoint(d::AbstractInterval) = endpoints(d)[2] -"A tuple of `Bool`'s encoding whether the left/right endpoints are closed." +""" + closedendpoints(d::AI) where AI<:AbstractInterval + +A tuple of `Bool`'s encoding whether the left/right endpoints are closed. + +# Examples +```jldoctest +julia> closedendpoints(iv"[1,2]") +(true, true) + +julia> closedendpoints(iv"(1,2]") +(false, true) + +julia> closedendpoints(iv"[2,1)") +(true, false) + +julia> closedendpoints(iv"(2,1)") +(false, false) +``` +""" closedendpoints(d::AI) where AI<:AbstractInterval = error("Override closedendpoints(::$(AI))") -"Is the interval closed at the left endpoint?" +""" + isleftclosed(d::AbstractInterval) + +Is the interval closed at the left endpoint? + +# Examples +```jldoctest +julia> isleftclosed(iv"[1,2]") +true + +julia> isleftclosed(iv"(2,1)") +false +``` +""" isleftclosed(d::AbstractInterval) = closedendpoints(d)[1] -"Is the interval closed at the right endpoint?" +""" + isrightclosed(d::AbstractInterval) + +Is the interval closed at the right endpoint? + +# Examples +```jldoctest +julia> isrightclosed(iv"[1,2]") +true + +julia> isrightclosed(iv"(2,1)") +false +``` +""" isrightclosed(d::AbstractInterval) = closedendpoints(d)[2] # open_left and open_right are implemented in terms of closed_* above, so those # are the only ones that should be implemented for specific intervals -"Is the interval open at the left endpoint?" +""" + isleftopen(d::AbstractInterval) + +Is the interval open at the left endpoint? + +# Examples +```jldoctest +julia> isleftopen(iv"[1,2]") +false + +julia> isleftopen(iv"(2,1)") +true +``` +""" isleftopen(d::AbstractInterval) = !isleftclosed(d) -"Is the interval open at the right endpoint?" +""" + isrightopen(d::AbstractInterval) + +Is the interval open at the right endpoint? + +# Examples +```jldoctest +julia> isrightopen(iv"[1,2]") +false + +julia> isrightopen(iv"(2,1)") +true +``` +""" isrightopen(d::AbstractInterval) = !isrightclosed(d) -# Only closed if closed at both endpoints, and similar for open +""" + isclosedset(d::AbstractInterval) + +Is the interval closed set? + +# Examples +```jldoctest +julia> isclosedset(iv"[1,2]") +true + +julia> isclosedset(iv"(1,2]") +false + +julia> isclosedset(iv"[1,2)") +false + +julia> isclosedset(iv"(1,2)") +false +``` +""" isclosedset(d::AbstractInterval) = isleftclosed(d) && isrightclosed(d) -"Is the interval open?" +""" + isopenset(d::AbstractInterval) + +Is the interval open set? + +# Examples +```jldoctest +julia> isopenset(iv"[1,2]") +false + +julia> isopenset(iv"(1,2]") +false + +julia> isopenset(iv"[1,2)") +false + +julia> isopenset(iv"(1,2)") +true +``` +""" isopenset(d::AbstractInterval) = isleftopen(d) && isrightopen(d) eltype(::Type{AbstractInterval{T}}) where {T} = T @@ -94,6 +241,15 @@ mean(d::AbstractInterval) = (leftendpoint(d) + rightendpoint(d))/2 Calculate the width (max-min) of interval `iv`. Note that for integers `l` and `r`, `width(l..r) = length(l:r) - 1`. + +# Examples +```jldoctest +julia> width(2..7) +5 + +julia> length(2:7) +6 +``` """ function width(A::AbstractInterval) _width = rightendpoint(A) - leftendpoint(A) @@ -206,28 +362,61 @@ range(i::TypedEndpointsInterval{:closed,:closed,I}) where {I<:Integer} = UnitRan """ range(i::ClosedInterval; step, length) - range(i::ClosedInterval, len::Integer) + range(i::ClosedInterval, length::Integer) Constructs a range of a specified step or length. + +# Examples +```jldoctest +julia> range(1..2, 8) +1.0:0.14285714285714285:2.0 + +julia> range(1, 2, 8) +1.0:0.14285714285714285:2.0 + +julia> range(1..2, step=0.2) +1.0:0.2:2.0 + +julia> range(1, 2, step=0.2) +1.0:0.2:2.0 +``` """ range(i::TypedEndpointsInterval{:closed,:closed}; step=nothing, length=nothing) = range(leftendpoint(i); stop=rightendpoint(i), step=step, length=length) -range(i::TypedEndpointsInterval{:closed,:closed}, len::Integer) = range(i; length=len) +range(i::TypedEndpointsInterval{:closed,:closed}, length::Integer) = range(i; length=length) """ range(i::Interval{:closed,:open}; length) - range(i::Interval{:closed,:open}, len::Integer) + range(i::Interval{:closed,:open}, length::Integer) Constructs a range of a specified length with `step=width(i)/length`. + +# Examples +```jldoctest +julia> range(iv"[1, 2)", 7) # Does not contain right endpoint +1.0:0.14285714285714285:1.8571428571428572 + +julia> range(1, 2, 8) +1.0:0.14285714285714285:2.0 +``` """ range(i::TypedEndpointsInterval{:closed,:open}; length::Integer) = range(leftendpoint(i); step=width(i)/length, length=length) -range(i::TypedEndpointsInterval{:closed,:open}, len::Integer) = range(i; length=len) +range(i::TypedEndpointsInterval{:closed,:open}, length::Integer) = range(i; length=length) """ clamp(t, i::ClosedInterval) Clamp the scalar `t` such that the result is in the interval `i`. + +# Examples +```jldoctest +julia> clamp(1.2, 1..2) +1.2 + +julia> clamp(2.2, 1..2) +2.0 +``` """ clamp(t, i::TypedEndpointsInterval{:closed,:closed}) = clamp(t, leftendpoint(i), rightendpoint(i)) diff --git a/src/interval.jl b/src/interval.jl index b63ffb4..a35bf39 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -38,6 +38,26 @@ Interval(i::AbstractInterval) = Interval{isleftclosed(i) ? (:closed) : (:open), isrightclosed(i) ? (:closed) : (:open)}(i) Interval(i::TypedEndpointsInterval{L,R}) where {L,R} = Interval{L,R}(i) +""" + @iv_str -> Interval + +Construct an interval with mathematical notation such as `iv"(1,2]"`. + +# Examples +```jldoctest +julia> iv"[1,2]" +1 .. 2 + +julia> iv"[1,2)" +1 .. 2 (closed-open) + +julia> iv"(1,2]" +1 .. 2 (open-closed) + +julia> iv"(1,2)" +1 .. 2 (open) +``` +""" macro iv_str(s) msg = "Invalid expresson `$s`" for (reg, f) ∈ ( @@ -108,15 +128,30 @@ convert(::Type{TypedEndpointsInterval{L,R}}, d::Interval{L,R}) where {L,R} = d iv = l..r Construct a ClosedInterval `iv` spanning the region from `l` to `r`. + +# Examples +```jldoctest +julia> 1..2 +1 .. 2 + +julia> 3..1 # Empty interval set can be defined +3 .. 1 +``` """ ..(x, y) = ClosedInterval(x, y) """ - iv = center±halfwidth + iv = center ± halfwidth Construct a ClosedInterval `iv` spanning the region from `center - halfwidth` to `center + halfwidth`. + +# Examples +```jldoctest +julia> 3 ± 2 +1 .. 5 +``` """ ±(x, y) = ClosedInterval(x - y, x + y) ±(x::CartesianIndex, y::CartesianIndex) = ClosedInterval(x-y, x+y)