ianmackenzie / elm-interval / Interval


type Interval number

Represents a finite, closed interval with a minimum and maximum value, for example the interval from 3 to 5. An Interval Int represents a range of integers and an Interval Float represents a range of floating-point values.

Constructors

from : number -> number -> Interval number

Construct an interval containing the two given values (which can be provided in either order).

Interval.endpoints (Interval.from 2 5)
--> ( 2, 5 )

Interval.endpoints (Interval.from 5 2)
--> ( 2, 5 )

fromEndpoints : ( number, number ) -> Interval number

Construct an interval from its endpoints (the minimum and maximum values of the interval).

rgbRange =
    Interval.fromEndpoints ( 0, 255 )

alphaRange =
    Interval.fromEndpoints ( 0, 1 )

The two values should be given in order but will be swapped if necessary to ensure a valid interval is returned:

Interval.endpoints (Interval.fromEndpoints ( 3, 2 ))
--> ( 2, 3 )

singleton : number -> Interval number

Construct a zero-width interval containing a single value.

Interval.singleton 3
--> Interval.fromEndpoints ( 3, 3 )

Booleans

union : Interval number -> Interval number -> Interval number

Construct an interval containing both of the given intervals.

firstInterval =
    Interval.from 1 2

secondInterval =
    Interval.from 3 6

Interval.union firstInterval secondInterval
--> Interval.from 1 6

(Note that this is not strictly speaking a 'union' in the precise mathematical sense, since the result will contain values that are in between the two given intervals and not actually in either of them if those two intervals do not overlap.)

intersection : Interval number -> Interval number -> Maybe (Interval number)

Attempt to construct an interval containing all the values common to both given intervals. If the intervals do not intersect, returns Nothing.

Interval.intersection
    (Interval.from 1 3)
    (Interval.from 2 5)
--> Just (Interval.from 2 3)

Interval.intersection
    (Interval.from 1 3)
    (Interval.from 4 7)
--> Nothing

If the two intervals just touch, a singleton interval will be returned:

Interval.intersection
    (Interval.from 1 3)
    (Interval.from 3 5)
--> Just (Interval.singleton 3)

Hull

These functions let you construct an Interval containing one or more input numbers.

hull : number -> List number -> Interval number

Find the interval containing one or more input values:

Interval.hull 5 [ 3, 2, 4 ]
--> Interval.from 2 5

Often ends up being used within a case expression:

case values of
    [] ->
        -- some default behavior

    first :: rest ->
        let
            interval =
                Interval.hull first rest
        in
        -- normal behavior using 'interval'

See also hullN.

hullN : List number -> Maybe (Interval number)

Attempt to construct an interval containing all N values in the given list. If the list is empty, returns Nothing. If you know you have at least one value, you can use hull instead.

Interval.hullN [ 2, 1, 3 ]
--> Just (Interval.from 1 3)

Interval.hullN [ -3 ]
--> Just (Interval.singleton -3)

Interval.hullN []
--> Nothing

hullOf : (a -> number) -> a -> List a -> Interval number

Like hull, but lets you work on any kind of item as long as a number can be extracted from it. For example, if you had

type alias Person =
    { name : String
    , age : Float
    }

then given some people you could find their range of ages as an Interval using

Interval.hullOf .age
    firstPerson
    [ secondPerson
    , thirdPerson
    , fourthPerson
    ]

See also hullOfN.

hullOfN : (a -> number) -> List a -> Maybe (Interval number)

Combination of hullOf and hullN.

hull3 : number -> number -> number -> Interval number

Construct an interval containing the three given values;

Interval.hull3 a b c

is equivalent to

Interval.hull a [ b, c ]

but is more efficient. (If you're looking for a hull2 function, from should do what you want.)

Aggregation

These functions let you 'aggregate' one or more intervals into a single larger interval that contains all of them.

aggregate : Interval number -> List (Interval number) -> Interval number

Construct an interval containing one or more given intervals:

Interval.aggregate
    (Interval.singleton 2)
    [ Interval.singleton 4
    , Interval.singleton 3
    ]
--> Interval.from 2 4

Works much like hull. See also aggregateN.

aggregateN : List (Interval number) -> Maybe (Interval number)

Attemp to construct an interval containing all of the intervals in the given list. If the list is empty, returns Nothing. If you know you have at least one interval, you can use aggregate instead.

aggregateOf : (a -> Interval number) -> a -> List a -> Interval number

Like aggregate, but lets you work on any kind of item as long as an interval can be generated from it (similar to hullOf).

aggregateOfN : (a -> Interval number) -> List a -> Maybe (Interval number)

Combination of aggregateOf and aggregateN.

aggregate3 : Interval number -> Interval number -> Interval number -> Interval number

Special case of aggregate for the case of three intervals;

Interval.aggregate3 first second third

is equivalent to

Interval.aggregate first [ second, third ]

but is more efficient. (If you're looking for an aggregate2 function, union should do what you want.)

Properties

endpoints : Interval number -> ( number, number )

Get the endpoints of an interval (its minimum and maximum values) as a tuple. The first value will always be less than or equal to the second.

( minValue, maxValue ) =
    Interval.endpoints someInterval

For any interval,

Interval.endpoints interval

is equivalent to (but more efficient than)

( Interval.minValue interval
, Interval.maxValue interval
)

minValue : Interval number -> number

Get the minimum value of an interval.

Interval.minValue (Interval.from 1 3)
--> 1

maxValue : Interval number -> number

Get the maximum value of an interval.

Interval.maxValue (Interval.from 1 3)
--> 3

midpoint : Interval Basics.Float -> Basics.Float

Get the midpoint of an interval.

Interval.midpoint (Interval.from 1 4)
--> 2.5

width : Interval number -> number

Get the width of an interval.

Interval.width (Interval.from 1 5)
--> 4

Queries

contains : number -> Interval number -> Basics.Bool

Check if an interval contains a given value.

Interval.contains 0 (Interval.from -1 3)
--> True

Interval.contains 5 (Interval.from -1 3)
--> False

The minimum and maximum values of an interval are considered to be contained in the interval:

Interval.contains 3 (Interval.from -1 3)
--> True

isContainedIn : Interval number -> Interval number -> Basics.Bool

Check if the second interval is fully contained in the first.

Interval.from -5 5
    |> Interval.isContainedIn (Interval.from 0 10)
--> False

Interval.from -5 5
    |> Interval.isContainedIn (Interval.from -10 10)
--> True

Be careful with the argument order! If not using the |> operator, the second example would be written as:

Interval.isContainedIn (Interval.from -10 10)
    (Interval.from -5 5)
--> True

intersects : Interval number -> Interval number -> Basics.Bool

Check if two intervals touch or overlap (have any values in common).

Interval.from -5 5
    |> Interval.intersects (Interval.from 0 10)
--> True

Interval.from -5 5
    |> Interval.intersects (Interval.from 10 20)
--> False

Intervals that just touch each other are considered to intersect (this is consistent with intersection which will return a zero-width interval for the intersection of two just-touching intervals):

Interval.from -5 5
    |> Interval.intersects (Interval.from 5 10)
--> True

isSingleton : Interval number -> Basics.Bool

Check if the interval is a singleton (the minimum and maximum values are the same).

Interval.isSingleton (Interval.from 2 2)
--> True

Interval.isSingleton (Interval.from 2 3)
--> False

Interpolation

interpolate : Interval Basics.Float -> Basics.Float -> Basics.Float

Interpolate between an interval's endpoints based on a parameter value that will generally be between 0.0 and 1.0. A value of 0.0 corresponds to the minimum value of the interval, a value of 0.5 corresponds to its midpoint and a value of 1.0 corresponds to its maximum value:

Interval.interpolate (Interval.from 1 5) 0
--> 1

Interval.interpolate (Interval.from 1 5) 0.75
--> 4

Values less than 0.0 or greater than 1.0 can be used to extrapolate:

Interval.interpolate (Interval.from 1 5) 1.5
--> 7

Note that because of how Interval.from works, the interpolation is in fact from the minimum value to the maximum, not "from the first Interval.from argument to the second":

Interval.interpolate (Interval.from 0 10) 0.2
--> 2

Interval.interpolate (Interval.from 10 0) 0.2
--> 2 -- not 8!

If you want the interpolate from one number down to another, you can use Float.Extra.interpolateFrom from the elm-float-extra package.

interpolationParameter : Interval Basics.Float -> Basics.Float -> Basics.Float

Given an interval and a given value, determine the corresponding interpolation parameter (the parameter that you would pass to interpolate to get the given value):

Interval.interpolationParameter
    (Interval.from 10 15)
    12
--> 0.4

The result will be between 0 and 1 if (and only if) the given value is within the given interval:

Interval.interpolationParameter
    (Interval.from 10 15)
    18
--> 1.6

Interval.interpolationParameter
    (Interval.from 10 15)
    9
--> -0.2

This is the inverse of interpolate; for any non-zero-width interval,

Interval.interpolationParameter interval value
    |> Interval.interpolate interval

should be equal to the original value (within numerical roundoff).

Arithmetic

These functions let you do math with Interval values, following the rules of interval arithmetic.

negate : Interval number -> Interval number

Negate an interval. Note that this will flip the order of the endpoints.

Interval.negate (Interval.from 2 3)
--> Interval.from -3 -2

add : number -> Interval number -> Interval number

Add the given amount to an interval.

Interval.from -1 5 |> Interval.add 3
--> Interval.from 2 8

subtract : number -> Interval number -> Interval number

Subtract the given amount from an interval.

Interval.from -1 5 |> Interval.subtract 3
--> Interval.from -4 2

multiplyBy : number -> Interval number -> Interval number

Multiply an interval by a given value. Note that this will flip the order of the interval's endpoints if the given value is negative.

Interval.multiplyBy 5 (Interval.from 2 3)
--> Interval.from 10 15

Interval.multiplyBy -2 (Interval.from 2 3)
--> Interval.from -6 -4

divideBy : Basics.Float -> Interval Basics.Float -> Interval Basics.Float

Divide an interval by a given value. Note that this will flip the order of the interval's endpoints if the given value is negative.

Interval.divideBy 2 (Interval.from 2 3)
--> Interval.from 1 1.5

Interval.divideBy -2 (Interval.from 2 3)
--> Interval.from -1.5 -1

half : Interval Basics.Float -> Interval Basics.Float

Shorthand for multiplyBy 0.5.

twice : Interval number -> Interval number

Shorthand for multiplyBy 2.

plus : Interval number -> Interval number -> Interval number

Add two intervals together.

Interval.from 5 10
    |> Interval.plus (Interval.from 2 3)
--> Interval.from 7 13

minus : Interval number -> Interval number -> Interval number

Subtract the first interval from the second. This means that minus makes the most sense when using |>:

Interval.from 5 10
    |> Interval.minus (Interval.from 2 3)
--> Interval.from 2 8

Without the pipe operator, the above would be written as:

Interval.minus (Interval.from 2 3)
    (Interval.from 5 10)
--> Interval.from 2 8

times : Interval number -> Interval number -> Interval number

Multiply the two given intervals.

Interval.from 10 12
    |> Interval.times
        (Interval.from 5 6)
--> Interval.from 50 72

sin : Interval Basics.Float -> Interval Basics.Float

Get the image of sin(x) applied on the interval.

Interval.sin (Interval.from 0 (degrees 45))
--> Interval.from 0 0.7071

Interval.sin (Interval.from 0 pi)
--> Interval.from 0 1

cos : Interval Basics.Float -> Interval Basics.Float

Get the image of cos(x) applied on the interval.

Interval.cos (Interval.from 0 (degrees 45))
--> Interval.from 0.7071 1

Interval.cos (Interval.from 0 pi)
--> Interval.from -1 1