package scala.collection
package immutable
import mutable.{ Builder, ListBuffer }
import generic._
abstract class NumericRange[T]
(val start: T, val end: T, val step: T, val isInclusive: Boolean)
(implicit num: Integral[T])
extends IndexedSeq[T] with Serializable {
import num._
private lazy val numRangeElements: Int =
NumericRange.count(start, end, step, isInclusive)
override def length = numRangeElements
override def isEmpty = length == 0
override lazy val last: T =
if (length == 0) Nil.last
else locationAfterN(length - 1)
def by(newStep: T): NumericRange[T] = copy(start, end, newStep)
def copy(start: T, end: T, step: T): NumericRange[T]
override def foreach[U](f: T => U) {
var count = 0
var current = start
while (count < length) {
f(current)
current += step
count += 1
}
}
private def skipCount(p: T => Boolean): Int = {
var current = start
var counted = 0
while (counted < length && p(current)) {
counted += 1
current += step
}
counted
}
private def isWithinBoundaries(elem: T) = !isEmpty && (
(step > zero && start <= elem && elem <= last ) ||
(step < zero && last <= elem && elem <= start)
)
private def locationAfterN(n: Int): T = start + (step * fromInt(n))
private def newEmptyRange(value: T) = NumericRange(value, value, step)
final override def take(n: Int): NumericRange[T] = (
if (n <= 0 || length == 0) newEmptyRange(start)
else if (n >= length) this
else new NumericRange.Inclusive(start, locationAfterN(n - 1), step)
)
final override def drop(n: Int): NumericRange[T] = (
if (n <= 0 || length == 0) this
else if (n >= length) newEmptyRange(end)
else copy(locationAfterN(n), end, step)
)
def apply(idx: Int): T = {
if (idx < 0 || idx >= length) throw new IndexOutOfBoundsException(idx.toString)
else locationAfterN(idx)
}
private[immutable] def mapRange[A](fm: T => A)(implicit unum: Integral[A]): NumericRange[A] = {
val self = this
new NumericRange[A](fm(start), fm(end), fm(step), isInclusive) {
def copy(start: A, end: A, step: A): NumericRange[A] =
if (isInclusive) NumericRange.inclusive(start, end, step)
else NumericRange(start, end, step)
private lazy val underlyingRange: NumericRange[T] = self
override def foreach[U](f: A => U) { underlyingRange foreach (x => f(fm(x))) }
override def isEmpty = underlyingRange.isEmpty
override def apply(idx: Int): A = fm(underlyingRange(idx))
override def containsTyped(el: A) = underlyingRange exists (x => fm(x) == el)
}
}
def containsTyped(x: T): Boolean =
isWithinBoundaries(x) && (((x - start) % step) == zero)
override def contains(x: Any): Boolean =
try containsTyped(x.asInstanceOf[T])
catch { case _: ClassCastException => false }
override lazy val hashCode = super.hashCode()
override def equals(other: Any) = other match {
case x: NumericRange[_] =>
(x canEqual this) && (length == x.length) && (
(length == 0) ||
(start == x.start && last == x.last)
)
case _ =>
super.equals(other)
}
override def toString() = {
val endStr = if (length > Range.MAX_PRINT) ", ... )" else ")"
take(Range.MAX_PRINT).mkString("NumericRange(", ", ", endStr)
}
}
object NumericRange {
def count[T](start: T, end: T, step: T, isInclusive: Boolean)(implicit num: Integral[T]): Int = {
val zero = num.zero
val upward = num.lt(start, end)
val posStep = num.gt(step, zero)
if (step == zero) throw new IllegalArgumentException("step cannot be 0.")
else if (start == end) if (isInclusive) 1 else 0
else if (upward != posStep) 0
else {
val diff = num.minus(end, start)
val jumps = num.toLong(num.quot(diff, step))
val remainder = num.toLong(num.rem(diff, step))
val longCount = jumps + (
if (!isInclusive && zero == remainder) 0 else 1
)
val isOverflow = longCount == 0 && num.lt(num.plus(start, step), end) == upward
if (longCount > scala.Int.MaxValue || longCount < 0L || isOverflow) {
val word = if (isInclusive) "to" else "until"
val descr = List(start, word, end, "by", step) mkString " "
throw new IllegalArgumentException(descr + ": seqs cannot contain more than Int.MaxValue elements.")
}
longCount.toInt
}
}
class Inclusive[T](start: T, end: T, step: T)(implicit num: Integral[T])
extends NumericRange(start, end, step, true) {
def copy(start: T, end: T, step: T): Inclusive[T] =
NumericRange.inclusive(start, end, step)
def exclusive: Exclusive[T] = NumericRange(start, end, step)
}
class Exclusive[T](start: T, end: T, step: T)(implicit num: Integral[T])
extends NumericRange(start, end, step, false) {
def copy(start: T, end: T, step: T): Exclusive[T] =
NumericRange(start, end, step)
def inclusive: Inclusive[T] = NumericRange.inclusive(start, end, step)
}
def apply[T](start: T, end: T, step: T)(implicit num: Integral[T]): Exclusive[T] =
new Exclusive(start, end, step)
def inclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]): Inclusive[T] =
new Inclusive(start, end, step)
}