aiken/interval
In a eUTxO-based blockchain like Cardano, the management of time can be finicky.
Indeed, in order to maintain a complete determinism in the execution of scripts, it is impossible to introduce a notion of “current time” since the execution would then depend on factor that are external to the transaction itself: the ineluctable stream of time flowing in our universe.
Hence, to work around that, we typically define time intervals, which gives window – a.k.a intervals – within which the transaction can be executed. From within a script, it isn’t possible to know when exactly the script is executed, but we can reason about the interval bounds to validate pieces of logic.
Types
A type to represent intervals of values. Interval are inhabited by a type
a
which is useful for non-infinite intervals that have a finite
lower-bound and/or upper-bound.
This allows to represent all kind of mathematical intervals:
// [1; 10]
let i0: Interval<Int> = Interval
{ lower_bound:
IntervalBound { bound_type: Finite(1), is_inclusive: True }
, upper_bound:
IntervalBound { bound_type: Finite(10), is_inclusive: True }
}
// (20; infinity)
let i1: Interval<Int> = Interval
{ lower_bound:
IntervalBound { bound_type: Finite(20), is_inclusive: False }
, upper_bound:
IntervalBound { bound_type: PositiveInfinity, is_inclusive: False }
}
Constructors
-
Interval { lower_bound: IntervalBound<a>, upper_bound: IntervalBound<a> }
An interval bound, either inclusive or exclusive.
Constructors
-
IntervalBound { bound_type: IntervalBoundType<a>, is_inclusive: Bool }
A type of interval bound. Where finite, a value of type a
must be
provided. a
will typically be an Int
, representing a number of seconds or
milliseconds.
Constructors
-
NegativeInfinity
-
Finite(a)
-
PositiveInfinity
Constants
Create an empty interval that contains no value.
interval.contains(empty, 0) == False
interval.contains(empty, 1000) == False
Functions
Constructing
Create an interval that includes all values greater than the given bound. i.e [lower_bound, +INF)
interval.after(10) == Interval {
lower_bound: IntervalBound { bound_type: Finite(10), is_inclusive: True },
upper_bound: IntervalBound { bound_type: PositiveInfinity, is_inclusive: True },
}
Create an interval that includes all values after (and not including) the given bound. i.e (lower_bound, +INF)
interval.entirely_after(10) == Interval {
lower_bound: IntervalBound { bound_type: Finite(10), is_inclusive: False },
upper_bound: IntervalBound { bound_type: PositiveInfinity, is_inclusive: True },
}
Create an interval that includes all values before (and including) the given bound. i.e (-INF, upper_bound]
interval.before(100) == Interval {
lower_bound: IntervalBound { bound_type: NegativeInfinity, is_inclusive: True },
upper_bound: IntervalBound { bound_type: Finite(100), is_inclusive: True },
}
Create an interval that includes all values before (and not including) the given bound. i.e (-INF, upper_bound)
interval.entirely_before(10) == Interval {
lower_bound: IntervalBound { bound_type: NegativeInfinity, is_inclusive: True },
upper_bound: IntervalBound { bound_type: Finite(10), is_inclusive: False },
}
Create an interval that includes all values between two bounds, including the bounds. i.e. [lower_bound, upper_bound]
interval.between(10, 100) == Interval {
lower_bound: IntervalBound { bound_type: Finite(10), is_inclusive: True },
upper_bound: IntervalBound { bound_type: Finite(100), is_inclusive: True },
}
Create an interval that includes all values between two bounds, excluding the bounds. i.e. (lower_bound, upper_bound)
interval.entirely_between(10, 100) == Interval {
lower_bound: IntervalBound { bound_type: Finite(10), is_inclusive: False },
upper_bound: IntervalBound { bound_type: Finite(100), is_inclusive: False },
}
Inspecting
Checks whether an element is contained within the interval.
let iv =
Interval {
lower_bound: IntervalBound {
bound_type: Finite(14),
is_inclusive: True
},
upper_bound: IntervalBound {
bound_type: Finite(42),
is_inclusive: False
},
}
interval.contains(iv, 25) == True
interval.contains(iv, 0) == False
interval.contains(iv, 14) == True
interval.contains(iv, 42) == False
Tells whether an interval is empty; i.e. that is contains no value.
let iv1 = interval.empty
let iv2 = Interval {
lower_bound: IntervalBound { bound_type: Finite(0), is_inclusive: False },
upper_bound: IntervalBound { bound_type: Finite(0), is_inclusive: False },
}
let iv3 = Interval {
lower_bound: IntervalBound { bound_type: Finite(0), is_inclusive: False },
upper_bound: IntervalBound { bound_type: Finite(100), is_inclusive: False },
}
interval.is_empty(iv1) == True
interval.is_empty(iv2) == True
interval.is_empty(iv3) == False
// Note: Two empty intervals are not necessarily equal.
iv1 != iv2
Check whether the interval is entirely after the point “a”
interval.is_entirely_after(interval.after(10), 5) == True
interval.is_entirely_after(interval.after(10), 10) == False
interval.is_entirely_after(interval.after(10), 15) == False
interval.is_entirely_after(interval.between(10, 20), 30) == False
interval.is_entirely_after(interval.between(10, 20), 5) == True
Check whether the interval is entirely before the point “a”
interval.is_entirely_before(interval.before(10), 15) == True
interval.is_entirely_before(interval.before(10), 10) == False
interval.is_entirely_before(interval.before(10), 5) == False
interval.is_entirely_before(interval.between(10, 20), 30) == True
interval.is_entirely_before(interval.between(10, 20), 5) == False
Combining
Computes the smallest interval containing the two given intervals, if any
let iv1 = between(0, 10)
let iv2 = between(2, 14)
hull(iv1, iv2) == between(0, 14)
let iv1 = between(5, 10)
let iv2 = before(0)
hull(iv1, iv2) == before(10)
let iv1 = entirely_after(0)
let iv2 = between(10, 42)
hull(iv1, iv2) = entirely_after(0)
Computes the largest interval contains in the two given intervals, if any.
let iv1 = interval.between(0, 10)
let iv2 = interval.between(2, 14)
interval.intersection(iv1, iv2) == interval.between(2, 10)
let iv1 = interval.entirely_before(10)
let iv2 = interval.entirely_after(0)
interval.intersection(iv1, iv2) == interval.entirely_between(0, 10)
let iv1 = interval.between(0, 1)
let iv2 = interval.between(2, 3)
interval.intersection(iv1, iv2) |> interval.is_empty
Return the highest bound of the two.
let ib1 = IntervalBound { bound_type: Finite(0), is_inclusive: False }
let ib2 = IntervalBound { bound_type: Finite(1), is_inclusive: False }
interval.max(ib1, ib2) == ib2
Create an interval that includes all values greater than the given bound. i.e [lower_bound, +INF)
interval.after(10) == Interval {
lower_bound: IntervalBound { bound_type: Finite(10), is_inclusive: True },
upper_bound: IntervalBound { bound_type: PositiveInfinity, is_inclusive: True },
}
Create an interval that includes all values after (and not including) the given bound. i.e (lower_bound, +INF)
interval.entirely_after(10) == Interval {
lower_bound: IntervalBound { bound_type: Finite(10), is_inclusive: False },
upper_bound: IntervalBound { bound_type: PositiveInfinity, is_inclusive: True },
}
Create an interval that includes all values before (and including) the given bound. i.e (-INF, upper_bound]
interval.before(100) == Interval {
lower_bound: IntervalBound { bound_type: NegativeInfinity, is_inclusive: True },
upper_bound: IntervalBound { bound_type: Finite(100), is_inclusive: True },
}
Create an interval that includes all values before (and not including) the given bound. i.e (-INF, upper_bound)
interval.entirely_before(10) == Interval {
lower_bound: IntervalBound { bound_type: NegativeInfinity, is_inclusive: True },
upper_bound: IntervalBound { bound_type: Finite(10), is_inclusive: False },
}
Create an interval that includes all values between two bounds, including the bounds. i.e. [lower_bound, upper_bound]
interval.between(10, 100) == Interval {
lower_bound: IntervalBound { bound_type: Finite(10), is_inclusive: True },
upper_bound: IntervalBound { bound_type: Finite(100), is_inclusive: True },
}
Create an interval that includes all values between two bounds, excluding the bounds. i.e. (lower_bound, upper_bound)
interval.entirely_between(10, 100) == Interval {
lower_bound: IntervalBound { bound_type: Finite(10), is_inclusive: False },
upper_bound: IntervalBound { bound_type: Finite(100), is_inclusive: False },
}
Checks whether an element is contained within the interval.
let iv =
Interval {
lower_bound: IntervalBound {
bound_type: Finite(14),
is_inclusive: True
},
upper_bound: IntervalBound {
bound_type: Finite(42),
is_inclusive: False
},
}
interval.contains(iv, 25) == True
interval.contains(iv, 0) == False
interval.contains(iv, 14) == True
interval.contains(iv, 42) == False
Tells whether an interval is empty; i.e. that is contains no value.
let iv1 = interval.empty
let iv2 = Interval {
lower_bound: IntervalBound { bound_type: Finite(0), is_inclusive: False },
upper_bound: IntervalBound { bound_type: Finite(0), is_inclusive: False },
}
let iv3 = Interval {
lower_bound: IntervalBound { bound_type: Finite(0), is_inclusive: False },
upper_bound: IntervalBound { bound_type: Finite(100), is_inclusive: False },
}
interval.is_empty(iv1) == True
interval.is_empty(iv2) == True
interval.is_empty(iv3) == False
// Note: Two empty intervals are not necessarily equal.
iv1 != iv2
Check whether the interval is entirely after the point “a”
interval.is_entirely_after(interval.after(10), 5) == True
interval.is_entirely_after(interval.after(10), 10) == False
interval.is_entirely_after(interval.after(10), 15) == False
interval.is_entirely_after(interval.between(10, 20), 30) == False
interval.is_entirely_after(interval.between(10, 20), 5) == True
Check whether the interval is entirely before the point “a”
interval.is_entirely_before(interval.before(10), 15) == True
interval.is_entirely_before(interval.before(10), 10) == False
interval.is_entirely_before(interval.before(10), 5) == False
interval.is_entirely_before(interval.between(10, 20), 30) == True
interval.is_entirely_before(interval.between(10, 20), 5) == False
Combining
Computes the smallest interval containing the two given intervals, if any
let iv1 = between(0, 10)
let iv2 = between(2, 14)
hull(iv1, iv2) == between(0, 14)
let iv1 = between(5, 10)
let iv2 = before(0)
hull(iv1, iv2) == before(10)
let iv1 = entirely_after(0)
let iv2 = between(10, 42)
hull(iv1, iv2) = entirely_after(0)
Computes the largest interval contains in the two given intervals, if any.
let iv1 = interval.between(0, 10)
let iv2 = interval.between(2, 14)
interval.intersection(iv1, iv2) == interval.between(2, 10)
let iv1 = interval.entirely_before(10)
let iv2 = interval.entirely_after(0)
interval.intersection(iv1, iv2) == interval.entirely_between(0, 10)
let iv1 = interval.between(0, 1)
let iv2 = interval.between(2, 3)
interval.intersection(iv1, iv2) |> interval.is_empty
Return the highest bound of the two.
let ib1 = IntervalBound { bound_type: Finite(0), is_inclusive: False }
let ib2 = IntervalBound { bound_type: Finite(1), is_inclusive: False }
interval.max(ib1, ib2) == ib2
Computes the smallest interval containing the two given intervals, if any
let iv1 = between(0, 10)
let iv2 = between(2, 14)
hull(iv1, iv2) == between(0, 14)
let iv1 = between(5, 10)
let iv2 = before(0)
hull(iv1, iv2) == before(10)
let iv1 = entirely_after(0)
let iv2 = between(10, 42)
hull(iv1, iv2) = entirely_after(0)
Computes the largest interval contains in the two given intervals, if any.
let iv1 = interval.between(0, 10)
let iv2 = interval.between(2, 14)
interval.intersection(iv1, iv2) == interval.between(2, 10)
let iv1 = interval.entirely_before(10)
let iv2 = interval.entirely_after(0)
interval.intersection(iv1, iv2) == interval.entirely_between(0, 10)
let iv1 = interval.between(0, 1)
let iv2 = interval.between(2, 3)
interval.intersection(iv1, iv2) |> interval.is_empty
Return the highest bound of the two.
let ib1 = IntervalBound { bound_type: Finite(0), is_inclusive: False }
let ib2 = IntervalBound { bound_type: Finite(1), is_inclusive: False }
interval.max(ib1, ib2) == ib2