From effe0b3ecfcf014157a2ab7aacb8af55643ed892 Mon Sep 17 00:00:00 2001 From: Laurence Warne Date: Sun, 17 Mar 2024 14:52:19 +0000 Subject: [PATCH] Expose constructors for interval types --- core/src/main/scala/spire/math/Interval.scala | 24 +++++++++++++------ docs/guide.md | 20 +++++++++++----- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/core/src/main/scala/spire/math/Interval.scala b/core/src/main/scala/spire/math/Interval.scala index 3c0c52830..61dea4c65 100644 --- a/core/src/main/scala/spire/math/Interval.scala +++ b/core/src/main/scala/spire/math/Interval.scala @@ -798,7 +798,7 @@ sealed abstract class Interval[A] extends Serializable { lhs => def overlap(rhs: Interval[A])(implicit o: Order[A]): Overlap[A] = Overlap(lhs, rhs) } -case class All[A] private[spire] () extends Interval[A] { +case class All[A]() extends Interval[A] { def lowerBound: Unbound[A] = Unbound() def upperBound: Unbound[A] = Unbound() } @@ -808,23 +808,33 @@ case class Above[A] private[spire] (lower: A, flags: Int) extends Interval[A] { def upperBound: Unbound[A] = Unbound() } +object Above { + def above[A: Order](a: A): Above[A] = Above(a, 1) + def atOrAbove[A: Order](a: A): Above[A] = Above(a, 0) +} + case class Below[A] private[spire] (upper: A, flags: Int) extends Interval[A] { def lowerBound: Unbound[A] = Unbound() def upperBound: ValueBound[A] = if (isOpenUpper(flags)) Open(upper) else Closed(upper) } +object Below { + def below[A: Order](a: A): Below[A] = Below(a, 2) + def atOrBelow[A: Order](a: A): Below[A] = Below(a, 0) +} + // Bounded, non-empty interval with lower < upper case class Bounded[A] private[spire] (lower: A, upper: A, flags: Int) extends Interval[A] { def lowerBound: ValueBound[A] = if (isOpenLower(flags)) Open(lower) else Closed(lower) def upperBound: ValueBound[A] = if (isOpenUpper(flags)) Open(upper) else Closed(upper) } -case class Point[A] private[spire] (value: A) extends Interval[A] { +case class Point[A](value: A) extends Interval[A] { def lowerBound: Closed[A] = Closed(value) def upperBound: Closed[A] = Closed(value) } -case class Empty[A] private[spire] () extends Interval[A] { +case class Empty[A]() extends Interval[A] { def lowerBound: EmptyBound[A] = EmptyBound() def upperBound: EmptyBound[A] = EmptyBound() } @@ -944,10 +954,10 @@ object Interval { if (lower < upper) Bounded(lower, upper, 1) else Interval.empty[A] def openUpper[A: Order](lower: A, upper: A): Interval[A] = if (lower < upper) Bounded(lower, upper, 2) else Interval.empty[A] - def above[A: Order](a: A): Interval[A] = Above(a, 1) - def below[A: Order](a: A): Interval[A] = Below(a, 2) - def atOrAbove[A: Order](a: A): Interval[A] = Above(a, 0) - def atOrBelow[A: Order](a: A): Interval[A] = Below(a, 0) + def above[A: Order](a: A): Interval[A] = Above.above(a) + def below[A: Order](a: A): Interval[A] = Below.below(a) + def atOrAbove[A: Order](a: A): Interval[A] = Above.atOrAbove(a) + def atOrBelow[A: Order](a: A): Interval[A] = Below.atOrBelow(a) private val NullRe = "^ *\\( *Ø *\\) *$".r private val SingleRe = "^ *\\[ *([^,]+) *\\] *$".r diff --git a/docs/guide.md b/docs/guide.md index 2948a36cc..1ff01ae2e 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -666,12 +666,20 @@ type classes (ranging from `AdditiveSemigroup[A]` for `+` to Intervals may be unbounded on either side, and bounds can be open or closed. (An interval includes closed boundaries, but not open boundaries). Here are some string representations of various -intervals: - - * `[3, 6]` the set of values between 3 and 6 (including both). - * `(2, 4)` the set of values between 2 and 4 (excluding both). - * `[1, 2)` half-open set, including 1 but not 2. - * `(-∞, 5)` the set of values less than 5. +intervals, and how to create their equivalents in `spire`: + + * `[3, 6]` the set of values between 3 and 6 (including both) - `Interval.fromBounds(Closed(3), Closed(6))` + * `(2, 4)` the set of values between 2 and 4 (excluding both) - `Interval.fromBounds(Open(2), Open(4))` + * `[1, 2)` half-open set, including 1 but not 2 - `Interval.fromBounds(Closed(1), Open(2))` + * `(-∞, 5)` the set of values less than 5 - `Below.below(5)` + * `[1, ∞]` the set of values greater than or equal to 1 - `Above.atOrAbove(1)` + * `Ø` the empty set - `Empty()` + * `(-∞, ∞)` the set of all values - `All()` + +Note that `Interval` may be used in place of `Below`/`Above`/ +`Empty`/`All`, though these methods share a return value of the +`Interval` supertype from which lower/upper bound values which are +known can't be extracted. Intervals model continuous spaces, even if the type A is discrete. So for instance when `(3, 4)` is an `Interval[Int]` it is not considered