summaryrefslogblamecommitdiff
path: root/src/library/scala/collection/immutable/Range.scala
blob: f39b505be2f1e4a1a4ff778a969a203dcc15b9df (plain) (tree)




































































































































































































































                                                                                                      
/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2006-2009, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */

// $Id: Range.scala 18987 2009-10-08 18:31:44Z odersky $

package scala.collection.immutable

/** <p>
 *    The <code>Range</code> class represents integer values in range
 *    <code>[start;end)</code> with non-zero step value <code>step</code>.
 *    It's a special case of an indexed sequence.
 *    For example:
 *  </p><pre>
 *     <b>val</b> r1 = 0 until 10
 *     <b>val</b> r2 = r1.start until r1.end by r1.step + 1
 *     println(r2.length) // = 5
 *  </pre>
 *
 *  @author Martin Odersky
 *  @version 2.8
 *  @since   2.5
 */
class Range(val start: Int, val end: Int, val step: Int) extends Vector[Int] {

  require(step != 0)

  /** Create a new range with the start and end values of this range and
   *  a new <code>step</code>.
   *  @note should never be called after `inclusive'.
   */
  def by(step: Int): Range = Range(start, end, step)

  override def foreach[U](f: Int => U) {
    var i = start
    while (if (step > 0) i < end else i > end) {
      f(i)
      i += step
    }
  }

  lazy val length: Int = {
    def plen(start: Int, end: Int, step: Int) =
      if (end <= start) 0 else (end - start - 1) / step + 1
    if (step > 0) plen(start, end, step)
    else plen(end, start, -step)
  }

  final override def isEmpty =
    if (step > 0) start >= end else start <= end

  @inline
  final def apply(idx: Int): Int = {
    if (idx < 0 || idx >= length) throw new IndexOutOfBoundsException(idx.toString)
    start + idx * step
  }

  final override def init: Range =
    dropRight(1)

  final override def take(n: Int): Range = {
    val end1 = start + step * (n max 0)
    if (step > 0) Range(start, end1 min end, step)
    else Range(start, end1 max end, step)
  }

  final override def drop(n: Int): Range =
    if (step == 0) this
    else Range(start + step * n, end, step)

  final override def slice(from: Int, until: Int): Range =
    drop(from).take(until - from)

  final override def takeWhile(p: Int => Boolean): Range = {
    var s = start
    while (!isEmpty && p(s)) s += step
    Range(start, s, step)
  }

  final override def dropWhile(p: Int => Boolean): Range = {
    var s = start
    while (!isEmpty && p(s)) s += step
    Range(s, end, step)
  }

  final override def span(p: Int => Boolean): (Range, Range) = {
    var s = start
    while (!isEmpty && p(s)) s += step
    (Range(start, s, step), Range(s, end, step))
  }

  final override def splitAt(n: Int) = (take(n), drop(n))

  final override def takeRight(n: Int): Range = {
    val start1 = end - step * (n max 0)
    if (step > 0) Range(start1 max start, end, step)
    else Range(start1 min start, end, step)
  }

  final override def dropRight(n: Int): Range =
    Range(start, end - step * n, step)

  final override def reverse: Range =
    Range(end, start, -step)

  def contains(x: Int): Boolean =
    if (step > 0) start <= x && x < end && (x - start) % step == 0
    else start >= x && x > end && (start - x) % step == 0

  def inclusive = Range(start, end + Math.signum(step), step)

  override def equals(other: Any) = other match {
    case x: Range =>
      length == x.length &&
      (length == 0 ||
       start == x.start &&
       (length == 1 || step == x.step))
    case _ =>
      super.equals(other)
  }

  /* eliminated, so as to not break the hashcode/equals contract
  override def hashCode = start + end + step
  */

  override def toString() = {
    val end = if (length > Range.MAX_PRINT) ", ... )" else ")"
    take(Range.MAX_PRINT).mkString("Range(", ", ", end)
  }
}

object Range {
  private val MAX_PRINT = 512  // some arbitrary value

  @deprecated("use Range.inclusive instead")
  final class Inclusive(start: Int, end0: Int, step: Int)
      extends Range(start, if (step > 0) end0 + 1 else end0 - 1, step) { self =>
    override def by(step: Int): Range = new Inclusive(start, end0, step)
  }

  // The standard / Int-specific Range.
  def apply(start: Int, end: Int, step: Int): Range =
    if (step == 1) new ByOne(start, end)
    else if (step > 0) new ByPosStep(start, end, step)
    else new ByNegStep(start, end, step)

  def inclusive(start: Int, end: Int, step: Int): Range =
    apply(start, end + Math.signum(step), step)

  class ByOne(start: Int, end: Int) extends Range(start, end, 1) {
    override final def foreach[U](f: Int => U) {
      var i = start
      while (i < end) {
        f(i)
        i += 1
      }
    }
  }

  class ByPosStep(start: Int, end: Int, step: Int) extends Range(start, end, step) {
    override final def foreach[U](f: Int => U) {
      var i = start
      while (i < end) {
        f(i)
        i += step
      }
    }
  }

  class ByNegStep(start: Int, end: Int, step: Int) extends Range(start, end, step) {
    override final def foreach[U](f: Int => U) {
      var i = start
      while (i > end) {
        f(i)
        i += step
      }
    }
  }

  // BigInt and Long are straightforward generic ranges.
  object BigInt {
    def apply(start: BigInt, end: BigInt, step: BigInt) = GenericRange(start, end, step)
    def inclusive(start: BigInt, end: BigInt, step: BigInt) = GenericRange.inclusive(start, end, step)
  }

  object Long {
    def apply(start: Long, end: Long, step: Long) = GenericRange(start, end, step)
    def inclusive(start: Long, end: Long, step: Long) = GenericRange.inclusive(start, end, step)
  }

  // BigDecimal uses an alternative implementation of Numeric in which
  // it pretends to be Integral[T] instead of Fractional[T].  See Numeric for
  // details.  The intention is for it to throw an exception anytime
  // imprecision or surprises might result from anything, although this may
  // not yet be fully implemented.
  object BigDecimal {
    implicit val bigDecAsIntegral = scala.Numeric.BigDecimalAsIfIntegral

    def apply(start: BigDecimal, end: BigDecimal, step: BigDecimal) =
      GenericRange(start, end, step)
    def inclusive(start: BigDecimal, end: BigDecimal, step: BigDecimal) =
      GenericRange.inclusive(start, end, step)
  }

  // Double re-uses BigDecimal's range.
  object Double {
    def apply(start: Double, end: Double, step: Double) = scala.BigDecimal(start) until end by step
    def inclusive(start: Double, end: Double, step: Double) = scala.BigDecimal(start) to end by step
  }

  // As there is no appealing default step size for not-really-integral ranges,
  // we offer a partially constructed object.
  class Partial[T, U](f: T => U) {
    def by(x: T): U = f(x)
  }

  // Illustrating genericity with Int Range, which should have the same behavior
  // as the original Range class.  However we leave the original Range
  // indefinitely, for performance and because the compiler seems to bootstrap
  // off it and won't do so with our parameterized version without modifications.
  object Int {
    def apply(start: Int, end: Int, step: Int) = GenericRange(start, end, step)
    def inclusive(start: Int, end: Int, step: Int) = GenericRange.inclusive(start, end, step)
  }
}