This modules contains most of the core functionality for creating and
working with Interval
values.
Note: most examples assume that you have imported this module as
import Quantity.Interval as Interval
Represents a finite, closed interval with a minimum and maximum value.
The two type parameters match those of the Quantity
type from elm-units
. For example, an Interval Int Pixels
might represent a
value between 200 and 300 pixels, and an Interval Float Meters
might represent
a value between 2.5 and 3.7 centimeters. (As with the Quantity
type, the
units
type parameter refers to the base unit of a particular quantity type;
lengths in meters, centimeters, feet, miles etc. are all internally stored in
meters.)
fromEndpoints : ( Quantity number units, Quantity number units ) -> Interval number units
Construct an interval from its endpoints (the minimum and maximum values of the interval).
highwayCarSpeeds =
Interval.fromEndpoints
( Speed.kilometersPerHour 90
, Speed.kilometersPerHour 130
)
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
( Speed.kilometersPerHour 130
, Speed.kilometersPerHour 90
)
--> ( Speed.kilometersPerHour 90
--> , Speed.kilometersPerHour 130
--> )
singleton : Quantity number units -> Interval number units
Construct a zero-width interval containing a single value.
Interval.singleton (Length.meters 3)
--> Interval.fromEndpoints
--> ( Length.meters 3
--> , Length.meters 3
--> )
These functions let you construct an Interval
containing one or more input
values.
hull2 : Quantity number units -> Quantity number units -> Interval number units
Construct an interval containing the two given values (which can be provided
in either order). hull2 a b
is equivalent to fromEndpoints ( a, b )
.
-- "The heights of people participating in the study
-- ranged from 1.2 to 1.9 meters"
heightRange =
Interval.hull2
(Length.meters 1.2)
(Length.meters 1.9)
-- "Please allow 4 to 6 weeks for delivery"
estimatedShippingTime =
Interval.hull2
(Duration.weeks 4)
(Duration.weeks 6)
hull3 : Quantity number units -> Quantity number units -> Quantity number units -> Interval number units
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.
hull4 : Quantity number units -> Quantity number units -> Quantity number units -> Quantity number units -> Interval number units
Construct an interval containing the four given values.
hull : Quantity number units -> List (Quantity number units) -> Interval number units
Find the interval containing one or more input values, by passing the first value as the first argument and then a list of all other values as the second argument. For example, to find the interval containing the values 5 cm, 2 cm, 3 cm and 4 cm:
Interval.hull
(Length.centimeters 5)
[ Length.centimeters 3
, Length.centimeters 2
, Length.centimeters 4
]
--> Interval.fromEndpoints
--> ( Length.centimeters 2
--> , Length.centimeters 5
--> )
Why pass the first and all other values as separate arguments? It lets this
function return an Interval
instead of a Maybe Interval
. If there was just a
single list as an argument, then this function would have to handle the case of
an empty list being passed by returning Nothing
. As a result, when using this
function you often end up using it within a case
expression:
case values of
[] ->
-- some default behavior
first :: rest ->
let
interval =
Interval.hull first rest
in
-- normal behavior using 'interval'
If you do want the simpler behavior of taking a single list and returning a
Maybe Interval
, check out hullN
.
hullN : List (Quantity number units) -> Maybe (Interval number units)
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
[ Duration.hours 2
, Duration.hours 1
, Duration.hours 3
]
--> Just <|
--> Interval.fromEndpoints
--> ( Duration.hours 1
--> , Duration.hours 3
--> )
Interval.hullN [ Duration.hours 5]
--> Just (Interval.singleton (Duration.hours 5))
Interval.hullN []
--> Nothing
hullOf : (a -> Quantity number units) -> a -> List a -> Interval number units
Like hull
, but lets you work on any kind of item as long as a
Quantity
of some kind can be extracted from it. For example, if you had
type alias Person =
{ name : String
, age : Duration
}
then given some people you could find their range of ages as an Interval Float
Seconds
using
Interval.hullOf .age
firstPerson
[ secondPerson
, thirdPerson
, fourthPerson
]
See also hullOfN
.
hullOfN : (a -> Quantity number units) -> List a -> Maybe (Interval number units)
Combination of hullOf
and hullN
.
These functions let you 'aggregate' one or more intervals into a single larger interval that contains all of them.
aggregate2 : Interval number units -> Interval number units -> Interval number units
Construct an interval containing both of the given intervals.
firstInterval =
Interval.fromEndpoints
( Length.feet 1, Length.feet 2 )
secondInterval =
Interval.fromEndpoints
( Length.feet 3, Length.feet 6 )
Interval.aggregate2 firstInterval secondInterval
--> Interval.fromEndpoints
--> ( Length.feet 1, Length.feet 6 )
aggregate3 : Interval number units -> Interval number units -> Interval number units -> Interval number units
Construct an interval containing all three of the given intervals;
Interval.aggregate3 first second third
is equivalent to
Interval.aggregate first [ second, third ]
but is more efficient.
aggregate4 : Interval number units -> Interval number units -> Interval number units -> Interval number units -> Interval number units
Construct an interval containing all four of the given intervals.
aggregate : Interval number units -> List (Interval number units) -> Interval number units
Construct an interval containing one or more given intervals:
Interval.aggregate
(Interval.singleton (Length.feet 2))
[ Interval.fromEndpoints
( Length.feet 3
, Length.feet 4
)
]
--> Interval.fromEndpoints
--> ( Length.feet 2, Length.feet 4 )
Works much like hull
. See also aggregateN
.
aggregateN : List (Interval number units) -> Maybe (Interval number units)
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 units) -> a -> List a -> Interval number units
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 units) -> List a -> Maybe (Interval number units)
Combination of aggregateOf
and aggregateN
.
endpoints : Interval number units -> ( Quantity number units, Quantity number units )
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 units -> Quantity number units
Get the minimum value of an interval.
maxValue : Interval number units -> Quantity number units
Get the maximum value of an interval.
midpoint : Interval Basics.Float units -> Quantity Basics.Float units
Get the midpoint of an interval.
Interval.midpoint <|
Interval.fromEndpoints
( Duration.hours 2
, Duration.hours 3
)
--> Duration.hours 2.5
width : Interval number units -> Quantity number units
Get the width of an interval.
Interval.width <|
Interval.fromEndpoints
( Length.meters 1.2
, Length.meters 1.35
)
--> Length.centimeters 15
contains : Quantity number units -> Interval number units -> Basics.Bool
Check if an interval contains a given value:
angleInterval =
Interval.fromEndpoints
( Angle.degrees -10
, Angle.degrees 30
)
angleInterval |> Interval.contains (Angle.degrees 0)
--> True
angleInterval |> Interval.contains (Angle.degrees 45)
--> False
The minimum and maximum values of an interval are considered to be contained in the interval (but be careful of numerical roundoff):
angleInterval |> Interval.contains (Angle.degrees 30)
--> True
isContainedIn : Interval number units -> Interval number units -> Basics.Bool
Check if the second interval is fully contained in the first.
angleInterval =
Interval.fromEndpoints
( Angle.degrees -30, Angle.degrees 30 )
Interval.fromEndpoints
( Angle.degrees -5, Angle.degrees 15 )
|> Interval.isContainedIn angleInterval
--> True
Interval.fromEndpoints
( Angle.degrees 15, Angle.degrees 45 )
|> Interval.isContainedIn angleInterval
--> False
Be careful with the argument order! If not using the |>
operator, the first
example would be written as:
Interval.isContainedIn angleInterval
(Interval.fromEndpoints
( Angle.degrees -5
, Angle.degrees 15
)
)
--> True
intersects : Interval number units -> Interval number units -> Basics.Bool
Check if two intervals touch or overlap (have any values in common).
distanceInterval =
Interval.fromEndpoints
( Length.kilometers 5
, Length.kilometers 10
)
distanceInterval
|> Interval.intersects
(Interval.fromEndpoints
( Length.kilometers 8
, Length.kilometers 12
)
)
--> True
distanceInterval
|> Interval.intersects
(Interval.fromEndpoints
( Length.kilometers 12
, Length.kilometers 15
)
)
--> 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):
distanceInterval
|> Interval.intersects
(Interval.fromEndpoints
( Length.kilometers 10
, Length.kilometers 15
)
)
--> True
intersection : Interval number units -> Interval number units -> Maybe (Interval number units)
Attempt to construct an interval containing all the values common to both given intervals:
Interval.intersection
(Interval.fromEndpoints
( Mass.grams 1, Mass.grams 3 )
)
(Interval.fromEndpoints
( Mass.grams 2, Mass.grams 5 )
)
--> Just <|
--> Interval.fromEndpoints
--> ( Mass.grams 2, Mass.grams 3 )
If the intervals do not intersect, returns Nothing
:
Interval.intersection
(Interval.fromEndpoints
( Mass.grams 1, Mass.grams 3 )
)
(Interval.fromEndpoints
( Mass.grams 4, Mass.grams 7 )
)
--> Nothing
If the two intervals just touch, a singleton interval will be returned:
Interval.intersection
(Interval.fromEndpoints
( Mass.grams 1, Mass.grams 3 )
)
(Interval.fromEndpoints
( Mass.grams 3, Mass.grams 5 )
)
--> Just (Interval.singleton (Mass.grams 3))
isSingleton : Interval number units -> Basics.Bool
Check if the interval is a singleton (the minimum and maximum values are the same).
Interval.isSingleton <|
Interval.fromEndpoints
( Length.meters 2, Length.meters 2 )
--> True
Interval.isSingleton <|
Interval.fromEndpoints
( Length.meters 2, Length.meters 3 )
--> False
interpolate : Interval Basics.Float units -> Basics.Float -> Quantity Basics.Float units
Interpolate between an interval's endpoints based on a parameter value. 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:
lengthInterval =
Interval.fromEndpoints
( Length.meters 1, Length.meters 5 )
Interval.interpolate lengthInterval 0
--> Length.meters 1
Interval.interpolate lengthInterval 0.75
--> Length.meters 4
Values less than 0.0 or greater than 1.0 can be used to extrapolate:
Interval.interpolate lengthInterval 1.5
--> Length.meters 7
Note that because of how Interval.fromEndpoints
works, the
interpolation is in fact from the minimum value to the maximum, not "from the
first argument to the second":
Interval.interpolate
(Interval.fromEndpoints
( Length.meters 0
, Length.meters 10
)
)
0.2
--> 2
Interval.interpolate
(Interval.fromEndpoints
( Length.meters 10
, Length.meters 0
)
)
0.2
--> 2 -- not 8!
If you want to interpolate from one number down to another, you can use
Quantity.interpolateFrom
from the elm-units
package.
interpolationParameter : Interval Basics.Float units -> Quantity Basics.Float units -> 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.fromEndpoints
( Duration.minutes 10
, Duration.minutes 15
)
)
(Duration.minutes 12)
--> 0.4
The result will be between 0 and 1 if (and only if) the given value is contained in the given interval:
Interval.interpolationParameter
(Interval.fromEndpoints
( Duration.minutes 10
, Duration.minutes 15
)
)
(Duration.minutes 18)
--> 1.6
Interval.interpolationParameter
(Interval.fromEndpoints
( Duration.minutes 10
, Duration.minutes 15
)
)
(Duration.minutes 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).
These functions let you do math with Interval
values, following the rules of
interval arithmetic.
negate : Interval number units -> Interval number units
Negate an interval. Note that this will flip the order of the endpoints.
Interval.negate <|
Interval.fromEndpoints
( Angle.degrees 20
, Angle.degrees 30
)
--> Interval.fromEndpoints
--> ( Angle.degrees -30
--> , Angle.degrees -20
--> )
multiplyBy : number -> Interval number units -> Interval number units
Multiply an interval by a given value:
Interval.multiplyBy 5 <|
Interval.fromEndpoints
( Duration.minutes 20
, Duration.minutes 30
)
--> Interval.fromEndpoints
--> ( Duration.minutes 100
--> , Duration.minutes 150
--> )
Note that this will flip the order of the interval's endpoints if the given value is negative:
Interval.multiplyBy -2 <|
Interval.fromEndpoints
( Angle.degrees 20
, Angle.degrees 30
)
--> Interval.fromEndpoints
--> ( Angle.degrees -60
--> , Angle.degrees -40
--> )
divideBy : Basics.Float -> Interval Basics.Float units -> Interval Basics.Float units
Divide an interval by a given value:
Interval.divideBy 2 <|
Interval.fromEndpoints
( Length.feet 2, Length.feet 3 )
--> Interval.fromEndpoints
--> ( Length.feet 1, Length.feet 1.5 )
Note that this will flip the order of the interval's endpoints if the given value is negative:
Interval.divideBy -2 <|
Interval.fromEndpoints
( Angle.degrees 20
, Angle.degrees 30
)
--> Interval.fromEndpoints
--> ( Angle.degrees -15
--> , Angle.degrees -10
--> )
half : Interval Basics.Float units -> Interval Basics.Float units
Shorthand for multiplyBy 0.5
.
twice : Interval number units -> Interval number units
Shorthand for multiplyBy 2
.
plus : Quantity number units -> Interval number units -> Interval number units
Add the given amount to an interval:
lengthInterval =
Interval.fromEndpoints
( Length.meters 2, Length.meters 3 )
lengthInterval |> Interval.plus (Length.centinmeters 20)
--> Interval.fromEndpoints
--> ( Length.meters 2.2, Length.meters 3.2 )
plusInterval : Interval number units -> Interval number units -> Interval number units
Add two intervals together.
firstInterval =
Interval.fromEndpoints
( Length.feet 5, Length.feet 10 )
secondInterval =
Interval.fromEndpoints
( Length.feet 2, Length.feet 3 )
firstInterval |> Interval.plus secondInterval
--> Interval.fromEndpoints
--> ( Length.feet 7, Length.feet 13 )
minus : Quantity number units -> Interval number units -> Interval number units
Subtract the given amount from an interval.
angleInterval =
Interval.fromEndpoints
( Angle.degrees -10
, Angle.degrees 50
)
angleInterval |> Interval.minus (Angle.degrees 30)
--> Interval.fromEndpoints
--> ( Angle.degrees -40
--> , Angle.degrees 20
--> )
difference : Quantity number units -> Interval number units -> Interval number units
Subtract an interval from the given amount. So if you wanted to compute
interval - quantity
you would write
interval |> Interval.minus quantity
but if you wanted to compute quantity - interval
then you would write
Interval.difference quantity interval
minusInterval : Interval number units -> Interval number units -> Interval number units
Subtract the first interval from the second. This means that minusInterval
makes the most sense when using |>
:
firstInterval =
Interval.fromEndpoints
( Length.feet 5, Length.feet 10 )
secondInterval =
Interval.fromEndpoints
( Length.feet 2, Length.feet 3 )
firstInterval |> Interval.minusInterval secondInterval
--> Interval.fromEndpoints
--> ( Length.feet 2, Length.feet 8 )
Without the pipe operator, the above would be written as:
Interval.minusInterval secondInterval firstInterval
times : Quantity number quantityUnits -> Interval number intervalUnits -> Interval number (Quantity.Product intervalUnits quantityUnits)
Multiply an Interval
by a Quantity
, for example
interval |> Interval.times quantity
product : Quantity number quantityUnits -> Interval number intervalUnits -> Interval number (Quantity.Product quantityUnits intervalUnits)
Multiply an Interval
by a Quantity
, for example
Interval.product quantity interval
Note that unlike times
, the units of the result will be Product
quantityUnits intervalUnits
, not Product intervalUnits quantityUnits
.
timesUnitless : Quantity number Quantity.Unitless -> Interval number units -> Interval number units
Multiply an Interval
by a unitless Quantity
. See the documentation for
Quantity.timesUnitless
for more details.
timesInterval : Interval number units2 -> Interval number units1 -> Interval number (Quantity.Product units1 units2)
Multiply the second interval by the first. The order only matters if the
two intervals have different units (since it will affect the units of the
result) but, like other functions in this module, times
is generally designed
to be used with |>
:
width =
Interval.fromEndpoints
( Length.centimeters 10, Length.centimeters 12 )
height =
Interval.fromEndpoints
( Length.centimeters 5, Length.centimeters 6 )
width |> Interval.times height
--> Interval.fromEndpoints
--> ( Area.squareCentimeters 50
--> , Area.squareCentimeters 72
--> )
timesUnitlessInterval : Interval number Quantity.Unitless -> Interval number units -> Interval number units
Combination of timesInterval
and timesUnitless
for when one of the intervals in a product is unitless.
reciprocal : Interval Basics.Float Quantity.Unitless -> Interval Basics.Float Quantity.Unitless
Find the inverse of a unitless interval:
Interval.reciprocal <|
Interval.fromEndpoints
( Quantity.float 2
, Quantity.float 3
)
--> Interval.fromEndpoints
--> ( Quantity.float 0.333
--> , Quantity.flaot 0.500
--> )
Avoid using this function whenever possible, since it's very easy to get infinite intervals as a result:
Interval.reciprocal <|
Interval.fromEndpoints
( Quantity.float -1
, Quantity.float 2
)
--> Interval.fromEndpoints
--> ( Quantity.negativeInfinity
--> , Quantity.negativeInfinity
--> )
Since zero is contained in the above interval, the range of possible reciprocals ranges from negative to positive infinity!
abs : Interval number units -> Interval number units
Get the absolute value of an interval.
Interval.abs <|
Interval.fromEndpoints
( Length.meters -3 Length.meters 2 )
--> Interval.fromEndpoints
--> (Length.meters 0) (Length.meters 3)
squared : Interval number units -> Interval number (Quantity.Squared units)
Get the square of an interval.
squaredUnitless : Interval number Quantity.Unitless -> Interval number Quantity.Unitless
Specialized version of squared
for unitless intervals.
cubed : Interval number units -> Interval number (Quantity.Cubed units)
Get the cube of an interval.
cubedUnitless : Interval number Quantity.Unitless -> Interval number Quantity.Unitless
Specialized version of cubed
for unitless intervals.
randomValue : Interval Basics.Float units -> Random.Generator (Quantity Basics.Float units)
Create a random generator for quantities within a given interval. For example,
Interval.randomValue <|
Interval.fromEndpoints
( Length.meters 5, Length.meters 10 )
is a Generator
that will produce random lengths between 5 and 10 meters.
These functions are currently deprecated and will be removed in the next major release.
from : Quantity number units -> Quantity number units -> Interval number units
Deprecated alias for hull2
.
union : Interval number units -> Interval number units -> Interval number units
Deprecated alias for aggregate2
.