All Downloads are FREE. Search and download functionalities are using the official Maven repository.

spire.math.interval.Bound.scala Maven / Gradle / Ivy

The newest version!
package spire
package math
package interval

import spire.syntax.order._
import spire.syntax.field._
import spire.algebra._

sealed trait Bound[A] { lhs =>
  def map[B](f: A => B): Bound[B] = this match {
    case Open(a) => Open(f(a))
    case Closed(a) => Closed(f(a))
    case Unbound() => Unbound()
    case EmptyBound() => EmptyBound()
  }
  def combine[B](rhs: Bound[A])(f: (A, A) => A): Bound[A] = (lhs, rhs) match {
    case (EmptyBound(), _) => lhs
    case (_, EmptyBound()) => rhs
    case (Unbound(), _) => lhs
    case (_, Unbound()) => rhs
    case (Closed(a), Closed(b)) => Closed(f(a, b))
    case (Closed(a), Open(b)) => Open(f(a, b))
    case (Open(a), Closed(b)) => Open(f(a, b))
    case (Open(a), Open(b)) => Open(f(a, b))
  }

  def unary_-()(implicit ev: AdditiveGroup[A]): Bound[A] =
    lhs.map(-_)
  def reciprocal()(implicit ev: MultiplicativeGroup[A]): Bound[A] =
    lhs.map(_.reciprocal)

  def +(a: A)(implicit ev: AdditiveSemigroup[A]): Bound[A] = map(_ + a)
  def -(a: A)(implicit ev: AdditiveGroup[A]): Bound[A] = map(_ - a)
  def *(a: A)(implicit ev: MultiplicativeSemigroup[A]): Bound[A] = map(_ * a)
  def /(a: A)(implicit ev: MultiplicativeGroup[A]): Bound[A] = map(_ / a)

  def +(rhs: Bound[A])(implicit ev: AdditiveSemigroup[A]): Bound[A] =
    lhs.combine(rhs)(_ + _)
  def -(rhs: Bound[A])(implicit ev: AdditiveGroup[A]): Bound[A] =
    lhs.combine(rhs)(_ - _)
  def *(rhs: Bound[A])(implicit ev: MultiplicativeSemigroup[A]): Bound[A] =
    lhs.combine(rhs)(_ * _)
  def /(rhs: Bound[A])(implicit ev: MultiplicativeGroup[A]): Bound[A] =
    lhs.combine(rhs)(_ / _)
}

object Bound {
  private[spire] def minLower[A: Order](lhs: Bound[A], rhs: Bound[A], emptyIsMin: Boolean): Bound[A] =
    (lhs, rhs) match {
      case (EmptyBound(), _) => if (emptyIsMin) lhs else rhs
      case (_, EmptyBound()) => if (emptyIsMin) rhs else lhs
      case (Unbound(), _) | (_, Unbound()) => Unbound()
      case (Closed(lv), Closed(rv)) if lv <= rv => lhs
      case (Closed(_), Closed(_)) => rhs
      case (Open(lv), Open(rv)) if lv <= rv => lhs
      case (Open(_), Open(_)) => rhs
      case (Closed(lv), Open(rv)) if lv <= rv => lhs
      case (Closed(_), Open(_)) => rhs
      case (Open(lv), Closed(rv)) if rv <= lv => rhs
      case (Open(_), Closed(_)) => lhs
    }

  private[spire] def maxLower[A: Order](lhs: Bound[A], rhs: Bound[A], emptyIsMax: Boolean): Bound[A] =
    (lhs, rhs) match {
      case (EmptyBound(), _) => if (emptyIsMax) lhs else rhs
      case (_, EmptyBound()) => if (emptyIsMax) rhs else lhs
      case (Unbound(), _) => rhs
      case (_, Unbound()) => lhs
      case (Closed(lv), Closed(rv)) if lv >= rv => lhs
      case (Closed(_), Closed(_)) => rhs
      case (Open(lv), Open(rv)) if lv >= rv => lhs
      case (Open(_), Open(_)) => rhs
      case (Closed(lv), Open(rv)) if rv >= lv => rhs
      case (Closed(_), Open(_)) => lhs
      case (Open(lv), Closed(rv)) if lv >= rv => lhs
      case (Open(_), Closed(_)) => rhs
    }

  private[spire] def minUpper[A: Order](lhs: Bound[A], rhs: Bound[A], emptyIsMin: Boolean): Bound[A] =
    (lhs, rhs) match {
      case (EmptyBound(), _) => if (emptyIsMin) lhs else rhs
      case (_, EmptyBound()) => if (emptyIsMin) rhs else lhs
      case (Unbound(), _) => rhs
      case (_, Unbound()) => lhs
      case (Closed(lv), Closed(rv)) if lv <= rv => lhs
      case (Closed(_), Closed(_)) => rhs
      case (Open(lv), Open(rv)) if lv <= rv => lhs
      case (Open(_), Open(_)) => rhs
      case (Closed(lv), Open(rv)) if rv <= lv => rhs
      case (Closed(_), Open(_)) => lhs
      case (Open(lv), Closed(rv)) if lv <= rv => lhs
      case (Open(_), Closed(_)) => rhs
    }

  private[spire] def maxUpper[A: Order](lhs: Bound[A], rhs: Bound[A], emptyIsMax: Boolean): Bound[A] =
    (lhs, rhs) match {
      case (EmptyBound(), _) => if (emptyIsMax) lhs else rhs
      case (_, EmptyBound()) => if (emptyIsMax) rhs else lhs
      case (Unbound(), _) | (_, Unbound()) => Unbound()
      case (Closed(lv), Closed(rv)) if lv >= rv => lhs
      case (Closed(_), Closed(_)) => rhs
      case (Open(lv), Open(rv)) if lv >= rv => lhs
      case (Open(_), Open(_)) => rhs
      case (Closed(lv), Open(rv)) if lv >= rv => lhs
      case (Closed(_), Open(_)) => rhs
      case (Open(lv), Closed(rv)) if rv >= lv => rhs
      case (Open(_), Closed(_)) => lhs
    }

  implicit def eq[A](implicit ev: Eq[A]): Eq[Bound[A]] =
    new Eq[Bound[A]] {
      def eqv(x: Bound[A], y: Bound[A]): Boolean = (x, y) match {
        case (EmptyBound(), EmptyBound()) => true
        case (Unbound(), Unbound()) => true
        case (Closed(a), Closed(b)) => ev.eqv(a, b)
        case (Open(a), Open(b)) => ev.eqv(a, b)
        case _ => false
      }
    }
}

case class EmptyBound[A]() extends Bound[A]

case class Unbound[A]() extends Bound[A]

sealed trait ValueBound[A] extends Bound[A] { lhs =>
  def a: A
  def isClosed: Boolean

  override def unary_-()(implicit ev: AdditiveGroup[A]): ValueBound[A] =
    if (isClosed) Closed(-a) else Open(-a)

  override def reciprocal()(implicit ev: MultiplicativeGroup[A]): ValueBound[A] =
    if (isClosed) Closed(a.reciprocal) else Open(a.reciprocal)

  def +~(rhs: ValueBound[A])(implicit ev: AdditiveSemigroup[A]): ValueBound[A] = {
    val m = lhs.a + rhs.a
    if (lhs.isClosed && rhs.isClosed) Closed(m) else Open(m)
  }

  def -~(rhs: ValueBound[A])(implicit ev: AdditiveGroup[A]): ValueBound[A] = {
    val m = lhs.a - rhs.a
    if (lhs.isClosed && rhs.isClosed) Closed(m) else Open(m)
  }

  def *~(rhs: ValueBound[A])(implicit ev: MultiplicativeSemigroup[A]): ValueBound[A] = {
    val m = lhs.a * rhs.a
    if (lhs.isClosed && rhs.isClosed) Closed(m) else Open(m)
  }

  def /~(rhs: ValueBound[A])(implicit ev: MultiplicativeGroup[A]): ValueBound[A] = {
    val m = lhs.a / rhs.a
    if (lhs.isClosed && rhs.isClosed) Closed(m) else Open(m)
  }
}

case class Open[A](a: A) extends ValueBound[A] {
  def isClosed: Boolean = false
}

case class Closed[A](a: A) extends ValueBound[A] {
  def isClosed: Boolean = true
}

/** Companion object for open/closed bounds, used to construct intervals from
  * a set of bounds.
  *
  * In the comments, we write
  *
  * - [v, (v or ?v when the bound is interpreted as a lower bound,
  * - v], v), v? when the bound is interpreted as an upper bound.
  *
  * The symbols [], (), ? correspond to closed, open or unknown bounds.
  */
object ValueBound {
  def unapply[A](b: Bound[A]): Option[A] = b match {
    case Open(a) => Some(a)
    case Closed(a) => Some(a)
    case _ => None
  }

  /** Returns the interval containing the two given bounds. */
  @inline def union2[A: Order](v1: ValueBound[A], v2: ValueBound[A]): Interval[A] =
    v1.a.compare(v2.a).signum match {
      case -1 => // ?v1 < v2?
        Interval.fromOrderedBounds(v1, v2)
      case 1 => // ?v2 < v1?
        Interval.fromOrderedBounds(v2, v1)
      case 0 if v1.isClosed || v2.isClosed => // v1 ~ v2, including a closed bound
        Interval.point(v1.a)
      case 0 => // both bounds are Open(x)
        Interval.empty[A]
    }

  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a === v2.a < v3.a.
    */
  @inline def union3_1approx2_2less3[A: Order](v1: ValueBound[A],
    v2: ValueBound[A], v3: ValueBound[A]): Interval[A] =
    if (v1.isClosed) // [v1 ~ ?v2 < v3?
      Interval.fromOrderedBounds(v1, v3)
    else // (v1 ~ ?v2 < v3?
      Interval.fromOrderedBounds(v2, v3)

  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a < v2.a === v3.a.
    */
  @inline def union3_1less2_2approx3[A: Order](v1: ValueBound[A],
    v2: ValueBound[A], v3: ValueBound[A]): Interval[A] =
    if (v2.isClosed) // ?v1 < v2] ~ v3?
      Interval.fromOrderedBounds(v1, v2)
    else // ?v1 < v2) ~ v3?
      Interval.fromOrderedBounds(v1, v3)

  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a === v2.a === v3.a.
    */
  @inline def union3_1approx2_2approx3[A: Order](v1: ValueBound[A],
    v2: ValueBound[A], v3: ValueBound[A]): Interval[A] =
    if (v1.isClosed || v2.isClosed || v3.isClosed)
      Interval.point(v1.a)
    else
      Interval.empty[A]

  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a === v2.a.
    */
  @inline def union3_1approx2[A: Order](v1: ValueBound[A],
    v2: ValueBound[A], v3: ValueBound[A]): Interval[A] =
    v2.a.compare(v3.a).signum match {
      case -1 => // v1 ~ v2 < v3
        union3_1approx2_2less3(v1, v2, v3)
      case 0 => // v1 ~ v2 ~ v3
        union3_1approx2_2approx3(v1, v2, v3)
      case 1 => // v3 < v1 ~ v2
        union3_1less2_2approx3(v3, v1, v2)
    }

  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a < v2.a.
    */
  @inline def union3_1less2[A: Order](v1: ValueBound[A],
    v2: ValueBound[A], v3: ValueBound[A]): Interval[A] =
    v2.a.compare(v3.a).signum match {
      case -1 => // v1 < v2 < v3
        Interval.fromOrderedBounds(v1, v3)
      case 1 => // v1 < v2 and v3 < v2
        v1.a.compare(v3.a).signum match {
          case -1 => // v1 < v3 < v2
            Interval.fromOrderedBounds(v1, v2)
          case 1 => // v3 < v1 < v2
            Interval.fromOrderedBounds(v3, v2)
          case 0 => // v1 ~ v3 < v2
            union3_1approx2_2less3(v1, v3, v2)
        }
      case 0 => // v1 < v2 ~ v3
        union3_1less2_2approx3(v1, v2, v3)
    }

  /** Returns the interval which contains all the given bounds. */
  @inline def union3[A: Order](v1: ValueBound[A],
    v2: ValueBound[A], v3: ValueBound[A]): Interval[A] =
    v1.a.compare(v2.a).signum match {
      case -1 => // v1 < v2
        union3_1less2(v1, v2, v3)
      case 1 => // v1 > v2
        union3_1less2(v2, v1, v3)
      case 0 => // v1 ~ v2
        union3_1approx2(v1, v2, v3)
    }

  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a === v2.a === v3.a < v4.a.
    */
  @inline def union4_1approx2_2approx3_3less4[A: Order](v1: ValueBound[A], v2: ValueBound[A],
    v3: ValueBound[A], v4: ValueBound[A]): Interval[A] =
    if (v1.isClosed) // [v1 ~ ?v2 ~ ?v3 < v4
      Interval.fromOrderedBounds(v1, v4)
    else if (v2.isClosed) // (v1 ~ [v2 ~ ?v3 < v4
      Interval.fromOrderedBounds(v2, v4)
    else // (v1 ~ (v2 ~ ?v3 < v4
      Interval.fromOrderedBounds(v3, v4)

  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a === v2.a < v3.a === v4.a.
    */
  @inline def union4_1approx2_2less3_3approx4[A: Order](v1: ValueBound[A], v2: ValueBound[A],
    v3: ValueBound[A], v4: ValueBound[A]): Interval[A] =
    (v1.isClosed, v3.isClosed) match {
      case (true, true) => //  [v1 ~ ?v2  <  v3] ~ v4?
        Interval.fromOrderedBounds(v1, v3)
      case (false, true) => //  (v1 ~ ?v2 <  v3] ~ v4?
        Interval.fromOrderedBounds(v2, v3)
      case (true, false) => //  [v1 ~ ?v2 <  v3) ~ v4?
        Interval.fromOrderedBounds(v1, v4)
      case (false, false) => // (v1 ~ ?v2 <  v3) ~ v4?
        Interval.fromOrderedBounds(v2, v4)
    }

  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a < v2.a === v3.a === v4.a.
    */
  @inline def union4_1less2_2approx3_3approx4[A: Order](v1: ValueBound[A], v2: ValueBound[A],
    v3: ValueBound[A], v4: ValueBound[A]): Interval[A] =
    if (v2.isClosed) // ?v1 < v2] ~ v3? ~ v4?
      Interval.fromOrderedBounds(v1, v2)
    else if (v3.isClosed) // ?v1 < v2) ~ v3] ~ v4?
      Interval.fromOrderedBounds(v1, v3)
    else // ?v1 < v2) ~ v3) ~ v4?
      Interval.fromOrderedBounds(v1, v4)


  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a < v3.a === v4.a and v2.a < v3.a === v4.a.
    */
  @inline def union4_1less3_2less3_3approx4[A: Order](v1: ValueBound[A], v2: ValueBound[A],
    v3: ValueBound[A], v4: ValueBound[A]): Interval[A] =
    v1.a.compare(v2.a).signum match {
      case -1 => // v1 < v2 < v3 ~ v4
        union3_1less2_2approx3(v1, v3, v4)
      case 0 => // v1 ~ v2 < v3 ~ v4
        union4_1approx2_2less3_3approx4(v1, v2, v3, v4)
      case 1 => // v2 < v1 < v3 ~ v4
        union3_1less2_2approx3(v2, v3, v4)
    }

  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a < v2.a === v3.a.
    */
  @inline def union4_1less2_2approx3[A: Order](v1: ValueBound[A], v2: ValueBound[A],
    v3: ValueBound[A], v4: ValueBound[A]): Interval[A] =
    v3.a.compare(v4.a).signum match {
      case -1 => // v1 < v2 ~ v3 < v4
        Interval.fromOrderedBounds(v1, v4)
      case 0 => // v1 < v2 ~ v3 ~ v4
        union4_1less2_2approx3_3approx4(v1, v2, v3, v4)
      case 1 => // v1 < v2 ~ v3 and v4 < v2 ~ v3
        union4_1less3_2less3_3approx4(v1, v4, v2, v3)
    }

  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a === v2.a < v3.a.
    */
  @inline def union4_1approx2_2less3[A: Order](v1: ValueBound[A], v2: ValueBound[A],
    v3: ValueBound[A], v4: ValueBound[A]): Interval[A] =
    v3.a.compare(v4.a).signum match {
      case -1 => // v1 ~ v2 < v3 < v4
        union3_1approx2_2less3(v1, v2, v4)
      case 0 => // v1 ~ v2 < v3 ~ v4
        union4_1approx2_2less3_3approx4(v1, v2, v3, v4)
      case 1 => // v1 ~ v2 < v3 and v4 < v3
        v1.a.compare(v4.a).signum match {
          case -1 => // v1 ~ v2 < v4 < v3
            union3_1approx2_2less3(v1, v2, v3)
          case 0 => // v1 ~ v2 ~ v4 < v3
            union4_1approx2_2approx3_3less4(v1, v2, v4, v3)
          case 1 => // v4 < v1 ~ v2 < v3
            Interval.fromOrderedBounds(v4, v3)
        }
    }

  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a === v2.a == v3.a.
    */
  @inline def union4_1approx2_2approx3[A: Order](v1: ValueBound[A], v2: ValueBound[A],
    v3: ValueBound[A], v4: ValueBound[A]): Interval[A] =
    if (v1.isClosed == v2.isClosed) // [v1] ~ [v2] ~ ?v3? or (v1) ~ (v2) ~ ?v3?
      union3_1approx2(v2, v3, v4)
    else // [v1] ~ (v2) ~ ?v3? or (v1) ~ [v2] ~ ?v3?
      union3_1approx2(v1, v2, v4)

  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a === v2.a.
    */
  @inline def union4_1approx2[A: Order](v1: ValueBound[A], v2: ValueBound[A],
    v3: ValueBound[A], v4: ValueBound[A]): Interval[A] =
    v2.a.compare(v3.a).signum match {
      case -1 => // v1 ~ v2 < v3
        union4_1approx2_2less3(v1, v2, v3, v4)
      case 0 => // v1 ~ v2 ~ v3
        union4_1approx2_2approx3(v1, v2, v3, v4)
      case 1 => // v3 < v1 ~ v2
        union4_1less2_2approx3(v3, v1, v2, v4)
    }

  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a < v2.a and v1.a < v3.a (i.e. v1 is the minimum of all).
    */
  @inline def union4_1less2_1less3[A: Order](v1: ValueBound[A], v2: ValueBound[A],
    v3: ValueBound[A], v4: ValueBound[A]): Interval[A] =
    v2.a.compare(v3.a).signum match {
      case -1 => // v1 < v2 < v3
        union3_1less2(v1, v3, v4)
      case 0 => // v1 < v2 ~ v3
        union4_1less2_2approx3(v1, v2, v3, v4)
      case 1 => // v1 < v3 < v2
        union3_1less2(v1, v2, v4)
    }

  /** Returns the interval which contains all the given bounds.
    *
    * Assumption: v1.a < v3.a and v2.a < v3.a (i.e. v3 is the maximum of all).
    */
  @inline def union4_1less3_2less3[A: Order](v1: ValueBound[A], v2: ValueBound[A],
    v3: ValueBound[A], v4: ValueBound[A]): Interval[A] =
    v1.a.compare(v2.a).signum match {
      case -1 => // v1 < v2 < v3
        union3_1less2(v1, v3, v4)
      case 0 => // v1 ~ v2 < v3
        union4_1approx2_2less3(v1, v2, v3, v4)
      case 1 => // v2 < v1 < v3
        union3_1less2(v2, v3, v4)
    }

  /** Returns the interval which contains all the given bounds.
    * *
    * Assumption: v1.a < v2.a.
    */
  @inline def union4_1less2[A: Order](v1: ValueBound[A], v2: ValueBound[A],
    v3: ValueBound[A], v4: ValueBound[A]): Interval[A] =
    v2.a.compare(v3.a).signum match {
      case -1 => // v1 < v2 < v3
        union3_1less2(v1, v3, v4)
      case 0 => // v1 < v2 ~ v3
        union4_1less2_2approx3(v1, v2, v3, v4)
      case 1 => // v1 < v2 and v3 < v2
        union4_1less3_2less3(v1, v3, v2, v4)
    }

  /** Returns the interval which contains all the given bounds. */
  @inline def union4[A: Order](v1: ValueBound[A], v2: ValueBound[A],
    v3: ValueBound[A], v4: ValueBound[A]): Interval[A] =
    v1.a.compare(v2.a).signum match {
      case -1 => // v1 < v2
        union4_1less2(v1, v2, v3, v4)
      case 1 => // v1 > v2
        union4_1less2(v2, v1, v3, v4)
      case 0 => // v1 ~ v2
        union4_1approx2(v1, v2, v3, v4)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy