Skip to content

Commit

Permalink
Expose constructors for interval types
Browse files Browse the repository at this point in the history
  • Loading branch information
LaurenceWarne committed Mar 17, 2024
1 parent ac844d3 commit effe0b3
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 13 deletions.
24 changes: 17 additions & 7 deletions core/src/main/scala/spire/math/Interval.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand All @@ -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()
}
Expand Down Expand Up @@ -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
Expand Down
20 changes: 14 additions & 6 deletions docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit effe0b3

Please sign in to comment.