/*
 * Scala (https://www.scala-lang.org)
 *
 * Copyright EPFL and Lightbend, Inc.
 *
 * Licensed under Apache License 2.0
 * (http://www.apache.org/licenses/LICENSE-2.0).
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package scala
package collection

import scala.annotation.tailrec
import scala.annotation.unchecked.uncheckedVariance
import scala.collection.mutable.StringBuilder
import scala.language.implicitConversions
import scala.math.{Numeric, Ordering}
import scala.reflect.ClassTag
import scala.runtime.{AbstractFunction1, AbstractFunction2}

/**
  * A template trait for collections which can be traversed either once only
  * or one or more times.
  *
  * Note: `IterableOnce` does not extend [[IterableOnceOps]]. This is different than the general
  * design of the collections library, which uses the following pattern:
  * {{{
  *   trait Seq extends Iterable with SeqOps
  *   trait SeqOps extends IterableOps
  *
  *   trait IndexedSeq extends Seq with IndexedSeqOps
  *   trait IndexedSeqOps extends SeqOps
  * }}}
  *
  * The goal is to provide a minimal interface without any sequential operations. This allows
  * third-party extension like Scala parallel collections to integrate at the level of IterableOnce
  * without inheriting unwanted implementations.
  *
  * @define coll collection
  */
trait IterableOnce[+A] extends Any {

  /** An [[scala.collection.Iterator]] over the elements of this $coll.
    *
    * If an `IterableOnce` object is in fact an [[scala.collection.Iterator]], this method always returns itself,
    * in its current state, but if it is an [[scala.collection.Iterable]], this method always returns a new
    * [[scala.collection.Iterator]].
    */
  def iterator: Iterator[A]

  /** Returns a [[scala.collection.Stepper]] for the elements of this collection.
    *
    * The Stepper enables creating a Java stream to operate on the collection, see
    * [[scala.jdk.StreamConverters]]. For collections holding primitive values, the Stepper can be
    * used as an iterator which doesn't box the elements.
    *
    * The implicit [[scala.collection.StepperShape]] parameter defines the resulting Stepper type according to the
    * element type of this collection.
    *
    *   - For collections of `Int`, `Short`, `Byte` or `Char`, an [[scala.collection.IntStepper]] is returned
    *   - For collections of `Double` or `Float`, a [[scala.collection.DoubleStepper]] is returned
    *   - For collections of `Long` a [[scala.collection.LongStepper]] is returned
    *   - For any other element type, an [[scala.collection.AnyStepper]] is returned
    *
    * Note that this method is overridden in subclasses and the return type is refined to
    * `S with EfficientSplit`, for example [[scala.collection.IndexedSeqOps.stepper]]. For Steppers marked with
    * [[scala.collection.Stepper.EfficientSplit]], the converters in [[scala.jdk.StreamConverters]]
    * allow creating parallel streams, whereas bare Steppers can be converted only to sequential
    * streams.
    */
  def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S = {
    import convert.impl._
    val s = shape.shape match {
      case StepperShape.IntShape    => new IntIteratorStepper   (iterator.asInstanceOf[Iterator[Int]])
      case StepperShape.LongShape   => new LongIteratorStepper  (iterator.asInstanceOf[Iterator[Long]])
      case StepperShape.DoubleShape => new DoubleIteratorStepper(iterator.asInstanceOf[Iterator[Double]])
      case _                        => shape.seqUnbox(new AnyIteratorStepper[A](iterator))
    }
    s.asInstanceOf[S]
  }

  /** The number of elements in this $coll, if it can be cheaply computed,
   *  -1 otherwise. Cheaply usually means: Not requiring a collection traversal.
   */
  def knownSize: Int = -1
}

final class IterableOnceExtensionMethods[A](private val it: IterableOnce[A]) extends AnyVal {
  @deprecated("Use .iterator.withFilter(...) instead", "2.13.0")
  def withFilter(f: A => Boolean): Iterator[A] = it.iterator.withFilter(f)

  @deprecated("Use .iterator.reduceLeftOption(...) instead", "2.13.0")
  def reduceLeftOption(f: (A, A) => A): Option[A] = it.iterator.reduceLeftOption(f)

  @deprecated("Use .iterator.min instead", "2.13.0")
  def min(implicit ord: Ordering[A]): A = it.iterator.min

  @deprecated("Use .iterator.nonEmpty instead", "2.13.0")
  def nonEmpty: Boolean = it.iterator.nonEmpty

  @deprecated("Use .iterator.max instead", "2.13.0")
  def max(implicit ord: Ordering[A]): A = it.iterator.max

  @deprecated("Use .iterator.reduceRight(...) instead", "2.13.0")
  def reduceRight(f: (A, A) => A): A = it.iterator.reduceRight(f)

  @deprecated("Use .iterator.maxBy(...) instead", "2.13.0")
  def maxBy[B](f: A => B)(implicit cmp: Ordering[B]): A = it.iterator.maxBy(f)

  @deprecated("Use .iterator.reduceLeft(...) instead", "2.13.0")
  def reduceLeft(f: (A, A) => A): A = it.iterator.reduceLeft(f)

  @deprecated("Use .iterator.sum instead", "2.13.0")
  def sum(implicit num: Numeric[A]): A = it.iterator.sum

  @deprecated("Use .iterator.product instead", "2.13.0")
  def product(implicit num: Numeric[A]): A = it.iterator.product

  @deprecated("Use .iterator.count(...) instead", "2.13.0")
  def count(f: A => Boolean): Int = it.iterator.count(f)

  @deprecated("Use .iterator.reduceOption(...) instead", "2.13.0")
  def reduceOption(f: (A, A) => A): Option[A] = it.iterator.reduceOption(f)

  @deprecated("Use .iterator.minBy(...) instead", "2.13.0")
  def minBy[B](f: A => B)(implicit cmp: Ordering[B]): A = it.iterator.minBy(f)

  @deprecated("Use .iterator.size instead", "2.13.0")
  def size: Int = it.iterator.size

  @deprecated("Use .iterator.forall(...) instead", "2.13.0")
  def forall(f: A => Boolean): Boolean = it.iterator.forall(f)

  @deprecated("Use .iterator.collectFirst(...) instead", "2.13.0")
  def collectFirst[B](f: PartialFunction[A, B]): Option[B] = it.iterator.collectFirst(f)

  @deprecated("Use .iterator.filter(...) instead", "2.13.0")
  def filter(f: A => Boolean): Iterator[A] = it.iterator.filter(f)

  @deprecated("Use .iterator.exists(...) instead", "2.13.0")
  def exists(f: A => Boolean): Boolean = it.iterator.exists(f)

  @deprecated("Use .iterator.copyToBuffer(...) instead", "2.13.0")
  def copyToBuffer(dest: mutable.Buffer[A]): Unit = it.iterator.copyToBuffer(dest)

  @deprecated("Use .iterator.reduce(...) instead", "2.13.0")
  def reduce(f: (A, A) => A): A = it.iterator.reduce(f)

  @deprecated("Use .iterator.reduceRightOption(...) instead", "2.13.0")
  def reduceRightOption(f: (A, A) => A): Option[A] = it.iterator.reduceRightOption(f)

  @deprecated("Use .iterator.toIndexedSeq instead", "2.13.0")
  def toIndexedSeq: IndexedSeq[A] = it.iterator.toIndexedSeq

  @deprecated("Use .iterator.foreach(...) instead", "2.13.0")
  @`inline` def foreach[U](f: A => U): Unit = it match {
    case it: Iterable[A] => it.foreach(f)
    case _ => it.iterator.foreach(f)
  }

  @deprecated("Use .iterator.to(factory) instead", "2.13.0")
  def to[C1](factory: Factory[A, C1]): C1 = factory.fromSpecific(it)

  @deprecated("Use .iterator.to(ArrayBuffer) instead", "2.13.0")
  def toBuffer[B >: A]: mutable.Buffer[B] = mutable.ArrayBuffer.from(it)

  @deprecated("Use .iterator.toArray", "2.13.0")
  def toArray[B >: A: ClassTag]: Array[B] = it match {
    case it: Iterable[B] => it.toArray[B]
    case _ => it.iterator.toArray[B]
  }

  @deprecated("Use .iterator.to(List) instead", "2.13.0")
  def toList: immutable.List[A] = immutable.List.from(it)

  @deprecated("Use .iterator.to(Set) instead", "2.13.0")
  @`inline` def toSet[B >: A]: immutable.Set[B] = immutable.Set.from(it)

  @deprecated("Use .iterator.to(Iterable) instead", "2.13.0")
  @`inline` final def toTraversable: Traversable[A] = toIterable

  @deprecated("Use .iterator.to(Iterable) instead", "2.13.0")
  @`inline` final def toIterable: Iterable[A] = Iterable.from(it)

  @deprecated("Use .iterator.to(Seq) instead", "2.13.0")
  @`inline` def toSeq: immutable.Seq[A] = immutable.Seq.from(it)

  @deprecated("Use .iterator.to(LazyList) instead", "2.13.0")
  @`inline` def toStream: immutable.Stream[A] = immutable.Stream.from(it)

  @deprecated("Use .iterator.to(Vector) instead", "2.13.0")
  @`inline` def toVector: immutable.Vector[A] = immutable.Vector.from(it)

  @deprecated("Use .iterator.to(Map) instead", "2.13.0")
  def toMap[K, V](implicit ev: A <:< (K, V)): immutable.Map[K, V] =
    immutable.Map.from(it.asInstanceOf[IterableOnce[(K, V)]])

  @deprecated("Use .iterator instead", "2.13.0")
  @`inline` def toIterator: Iterator[A] = it.iterator

  @deprecated("Use .iterator.isEmpty instead", "2.13.0")
  def isEmpty: Boolean = it match {
    case it: Iterable[A] => it.isEmpty
    case _ => it.iterator.isEmpty
  }

  @deprecated("Use .iterator.mkString instead", "2.13.0")
  def mkString(start: String, sep: String, end: String): String = it match {
    case it: Iterable[A] => it.mkString(start, sep, end)
    case _ => it.iterator.mkString(start, sep, end)
  }

  @deprecated("Use .iterator.mkString instead", "2.13.0")
  def mkString(sep: String): String = it match {
    case it: Iterable[A] => it.mkString(sep)
    case _ => it.iterator.mkString(sep)
  }

  @deprecated("Use .iterator.mkString instead", "2.13.0")
  def mkString: String = it match {
    case it: Iterable[A] => it.mkString
    case _ => it.iterator.mkString
  }

  @deprecated("Use .iterator.find instead", "2.13.0")
  def find(p: A => Boolean): Option[A] = it.iterator.find(p)

  @deprecated("Use .iterator.foldLeft instead", "2.13.0")
  @`inline` def foldLeft[B](z: B)(op: (B, A) => B): B = it.iterator.foldLeft(z)(op)

  @deprecated("Use .iterator.foldRight instead", "2.13.0")
  @`inline` def foldRight[B](z: B)(op: (A, B) => B): B = it.iterator.foldRight(z)(op)

  @deprecated("Use .iterator.fold instead", "2.13.0")
  def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = it.iterator.fold(z)(op)

  @deprecated("Use .iterator.foldLeft instead", "2.13.0")
  @`inline` def /: [B](z: B)(op: (B, A) => B): B = foldLeft[B](z)(op)

  @deprecated("Use .iterator.foldRight instead", "2.13.0")
  @`inline` def :\ [B](z: B)(op: (A, B) => B): B = foldRight[B](z)(op)

  @deprecated("Use .iterator.map instead or consider requiring an Iterable", "2.13.0")
  def map[B](f: A => B): IterableOnce[B] = it match {
    case it: Iterable[A] => it.map(f)
    case _ => it.iterator.map(f)
  }

  @deprecated("Use .iterator.flatMap instead or consider requiring an Iterable", "2.13.0")
  def flatMap[B](f: A => IterableOnce[B]): IterableOnce[B] = it match {
    case it: Iterable[A] => it.flatMap(f)
    case _ => it.iterator.flatMap(f)
  }

  @deprecated("Use .iterator.sameElements instead", "2.13.0")
  def sameElements[B >: A](that: IterableOnce[B]): Boolean = it.iterator.sameElements(that)
}

object IterableOnce {
  @inline implicit def iterableOnceExtensionMethods[A](it: IterableOnce[A]): IterableOnceExtensionMethods[A] =
    new IterableOnceExtensionMethods[A](it)

  /** Computes the number of elements to copy to an array from a source IterableOnce
    *
    * @param srcLen the length of the source collection
    * @param destLen the length of the destination array
    * @param start the index in the destination array at which to start copying elements to
    * @param len the requested number of elements to copy (we may only be able to copy less than this)
    * @return the number of elements that will be copied to the destination array
    */
  @inline private[collection] def elemsToCopyToArray(srcLen: Int, destLen: Int, start: Int, len: Int): Int =
    math.max(math.min(math.min(len, srcLen), destLen - start), 0)

  /** Calls `copyToArray` on the given collection, regardless of whether or not it is an `Iterable`. */
  @inline private[collection] def copyElemsToArray[A, B >: A](elems: IterableOnce[A],
                                                              xs: Array[B],
                                                              start: Int = 0,
                                                              len: Int = Int.MaxValue): Int =
    elems match {
      case src: Iterable[A] => src.copyToArray[B](xs, start, len)
      case src              => src.iterator.copyToArray[B](xs, start, len)
    }
}

/** This implementation trait can be mixed into an `IterableOnce` to get the basic methods that are shared between
  * `Iterator` and `Iterable`. The `IterableOnce` must support multiple calls to `iterator` but may or may not
  * return the same `Iterator` every time.
  *
  * @define orderDependent
  *
  *              Note: might return different results for different runs, unless the underlying collection type is ordered.
  * @define orderDependentReduce
  *
  *              Note: might return different results for different runs, unless the
  *              underlying collection type is ordered or the operator is associative
  *              and commutative.
  * @define orderIndependentReduce
  *
  *              Note: might return different results for different runs, unless either
  *              of the following conditions is met: (1) the operator is associative,
  *              and the underlying collection type is ordered; or (2) the operator is
  *              associative and commutative.
  * @define mayNotTerminateInf
  *
  *              Note: may not terminate for infinite-sized collections.
  * @define willNotTerminateInf
  *
  *              Note: will not terminate for infinite-sized collections.
  * @define willForceEvaluation
  *              Note: Even when applied to a view or a lazy collection it will always force the elements.
  * @define consumesIterator
  *              After calling this method, one should discard the iterator it was called
  * on. Using it is undefined and subject to change.
  * @define undefinedOrder
  *              The order of applications of the operator is unspecified and may be nondeterministic.
  * @define exactlyOnce
  *              Each element appears exactly once in the computation.
  * @define coll collection
  *
  */
trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] =>
  /////////////////////////////////////////////////////////////// Abstract methods that must be implemented

  /** Produces a $coll containing cumulative results of applying the
   *  operator going left to right, including the initial value.
   *
   *  $willNotTerminateInf
   *  $orderDependent
   *
   *  @tparam B      the type of the elements in the resulting collection
   *  @param z       the initial value
   *  @param op      the binary operator applied to the intermediate result and the element
   *  @return        collection with intermediate results
   */
  def scanLeft[B](z: B)(op: (B, A) => B): CC[B]

  /** Selects all elements of this $coll which satisfy a predicate.
   *
   *  @param p     the predicate used to test elements.
   *  @return      a new $coll consisting of all elements of this $coll that satisfy the given
   *               predicate `p`. The order of the elements is preserved.
   */
  def filter(p: A => Boolean): C

  /** Selects all elements of this $coll which do not satisfy a predicate.
   *
   *  @param pred  the predicate used to test elements.
   *  @return      a new $coll consisting of all elements of this $coll that do not satisfy the given
   *               predicate `pred`. Their order may not be preserved.
   */
  def filterNot(pred: A => Boolean): C

  /** Selects the first `n` elements.
   *  $orderDependent
   *  @param  n    the number of elements to take from this $coll.
   *  @return a $coll consisting only of the first `n` elements of this $coll,
   *          or else the whole $coll, if it has less than `n` elements.
   *          If `n` is negative, returns an empty $coll.
   */
  def take(n: Int): C

  /** Selects the longest prefix of elements that satisfy a predicate.
   *
   *  The matching prefix starts with the first element of this $coll,
   *  and the element following the prefix is the first element that
   *  does not satisfy the predicate. The matching prefix may empty,
   *  so that this method returns an empty $coll.
   *
   *  Example:
   *
   *  {{{
   *      scala> List(1, 2, 3, 100, 4).takeWhile(n => n < 10)
   *      val res0: List[Int] = List(1, 2, 3)
   *
   *      scala> List(1, 2, 3, 100, 4).takeWhile(n => n == 0)
   *      val res1: List[Int] = List()
   *  }}}
   *
   *  Use [[span]] to obtain both the prefix and suffix.
   *  Use [[filter]] to retain only those elements from the entire $coll that satisfy the predicate.
   *  $orderDependent
   *  @param   p  The predicate used to test elements.
   *  @return  the longest prefix of this $coll whose elements all satisfy
   *           the predicate `p`.
   */
  def takeWhile(p: A => Boolean): C

  /** Selects all elements except the first `n` ones.
   *  $orderDependent
   *  @param  n    the number of elements to drop from this $coll.
   *  @return a $coll consisting of all elements of this $coll except the first `n` ones, or else the
   *          empty $coll, if this $coll has less than `n` elements.
   *          If `n` is negative, don't drop any elements.
   */
  def drop(n: Int): C

  /** Selects all elements except the longest prefix that satisfies a predicate.
   *
   *  The matching prefix starts with the first element of this $coll,
   *  and the element following the prefix is the first element that
   *  does not satisfy the predicate. The matching prefix may be empty,
   *  so that this method returns the entire $coll.
   *
   *  Example:
   *
   *  {{{
   *      scala> List(1, 2, 3, 100, 4).dropWhile(n => n < 10)
   *      val res0: List[Int] = List(100, 4)
   *
   *      scala> List(1, 2, 3, 100, 4).dropWhile(n => n == 0)
   *      val res1: List[Int] = List(1, 2, 3, 100, 4)
   *  }}}
   *
   *  Use [[span]] to obtain both the prefix and suffix.
   *  Use [[filterNot]] to drop all elements that satisfy the predicate.
   *
   *  $orderDependent
   *  @param   p  The predicate used to test elements.
   *  @return  the longest suffix of this $coll whose first element
   *           does not satisfy the predicate `p`.
   */
  def dropWhile(p: A => Boolean): C

  /** Selects an interval of elements.  The returned $coll is made up
   *  of all elements `x` which satisfy the invariant:
   *  {{{
   *    from <= indexOf(x) < until
   *  }}}
   *  $orderDependent
   *
   *  @param from   the lowest index to include from this $coll.
   *  @param until  the lowest index to EXCLUDE from this $coll.
   *  @return  a $coll containing the elements greater than or equal to
   *           index `from` extending up to (but not including) index `until`
   *           of this $coll.
   */
  def slice(from: Int, until: Int): C

  /** Builds a new $coll by applying a function to all elements of this $coll.
   *
   *  @param f      the function to apply to each element.
   *  @tparam B     the element type of the returned $coll.
   *  @return       a new $coll resulting from applying the given function
   *                `f` to each element of this $coll and collecting the results.
   */
  def map[B](f: A => B): CC[B]

  /** Builds a new $coll by applying a function to all elements of this $coll
   *  and using the elements of the resulting collections.
   *
   *    For example:
   *
   *    {{{
   *      def getWords(lines: Seq[String]): Seq[String] = lines flatMap (line => line split "\\W+")
   *    }}}
   *
   *    The type of the resulting collection is guided by the static type of $coll. This might
   *    cause unexpected results sometimes. For example:
   *
   *    {{{
   *      // lettersOf will return a Seq[Char] of likely repeated letters, instead of a Set
   *      def lettersOf(words: Seq[String]) = words flatMap (word => word.toSet)
   *
   *      // lettersOf will return a Set[Char], not a Seq
   *      def lettersOf(words: Seq[String]) = words.toSet flatMap ((word: String) => word.toSeq)
   *
   *      // xs will be an Iterable[Int]
   *      val xs = Map("a" -> List(11,111), "b" -> List(22,222)).flatMap(_._2)
   *
   *      // ys will be a Map[Int, Int]
   *      val ys = Map("a" -> List(1 -> 11,1 -> 111), "b" -> List(2 -> 22,2 -> 222)).flatMap(_._2)
   *    }}}
   *
   *  @param f      the function to apply to each element.
   *  @tparam B     the element type of the returned collection.
   *  @return       a new $coll resulting from applying the given collection-valued function
   *                `f` to each element of this $coll and concatenating the results.
   */
  def flatMap[B](f: A => IterableOnce[B]): CC[B]

  /** Converts this $coll of iterable collections into
   *  a $coll formed by the elements of these iterable
   *  collections.
   *
   *    The resulting collection's type will be guided by the
   *    type of $coll. For example:
   *
   *    {{{
   *    val xs = List(
   *               Set(1, 2, 3),
   *               Set(1, 2, 3)
   *             ).flatten
   *    // xs == List(1, 2, 3, 1, 2, 3)
   *
   *    val ys = Set(
   *               List(1, 2, 3),
   *               List(3, 2, 1)
   *             ).flatten
   *    // ys == Set(1, 2, 3)
   *    }}}
   *
   *  @tparam B the type of the elements of each iterable collection.
   *  @param asIterable an implicit conversion which asserts that the element
   *          type of this $coll is an `Iterable`.
   *  @return a new $coll resulting from concatenating all element ${coll}s.
   */
  def flatten[B](implicit asIterable: A => IterableOnce[B]): CC[B]

  /** Builds a new $coll by applying a partial function to all elements of this $coll
   *  on which the function is defined.
   *
   *  @param pf     the partial function which filters and maps the $coll.
   *  @tparam B     the element type of the returned $coll.
   *  @return       a new $coll resulting from applying the given partial function
   *                `pf` to each element on which it is defined and collecting the results.
   *                The order of the elements is preserved.
   */
  def collect[B](pf: PartialFunction[A, B]): CC[B]

  /** Zips this $coll with its indices.
   *
   *  @return        A new $coll containing pairs consisting of all elements of this $coll paired with their index.
   *                 Indices start at `0`.
   *  @example
   *    `List("a", "b", "c").zipWithIndex == List(("a", 0), ("b", 1), ("c", 2))`
   */
  def zipWithIndex: CC[(A @uncheckedVariance, Int)]

  /** Splits this $coll into a prefix/suffix pair according to a predicate.
   *
   *  Note: `c span p`  is equivalent to (but possibly more efficient than)
   *  `(c takeWhile p, c dropWhile p)`, provided the evaluation of the
   *  predicate `p` does not cause any side-effects.
   *  $orderDependent
   *
   *  @param p the test predicate
   *  @return  a pair consisting of the longest prefix of this $coll whose
   *           elements all satisfy `p`, and the rest of this $coll.
   */
  def span(p: A => Boolean): (C, C)

  /** Splits this $coll into a prefix/suffix pair at a given position.
   *
   *  Note: `c splitAt n` is equivalent to (but possibly more efficient than)
   *         `(c take n, c drop n)`.
   *  $orderDependent
   *
   *  @param n the position at which to split.
   *  @return  a pair of ${coll}s consisting of the first `n`
   *           elements of this $coll, and the other elements.
   */
  def splitAt(n: Int): (C, C) = {
    class Spanner extends runtime.AbstractFunction1[A, Boolean] {
      var i = 0
      def apply(a: A) = i < n && { i += 1 ; true }
    }
    val spanner = new Spanner
    span(spanner)
  }

  /** Applies a side-effecting function to each element in this collection.
    * Strict collections will apply `f` to their elements immediately, while lazy collections
    * like Views and LazyLists will only apply `f` on each element if and when that element
    * is evaluated, and each time that element is evaluated.
    *
    * @param f a function to apply to each element in this $coll
    * @tparam U the return type of f
    * @return The same logical collection as this
    */
  def tapEach[U](f: A => U): C

  /////////////////////////////////////////////////////////////// Concrete methods based on iterator

  /** Tests whether this $coll is known to have a finite size.
   *  All strict collections are known to have finite size. For a non-strict
   *  collection such as `Stream`, the predicate returns `'''true'''` if all
   *  elements have been computed. It returns `'''false'''` if the stream is
   *  not yet evaluated to the end. Non-empty Iterators usually return
   *  `'''false'''` even if they were created from a collection with a known
   *  finite size.
   *
   *  Note: many collection methods will not work on collections of infinite sizes.
   *  The typical failure mode is an infinite loop. These methods always attempt a
   *  traversal without checking first that `hasDefiniteSize` returns `'''true'''`.
   *  However, checking `hasDefiniteSize` can provide an assurance that size is
   *  well-defined and non-termination is not a concern.
   *
   *  @deprecated This method is deprecated in 2.13 because it does not provide any
   *    actionable information. As noted above, even the collection library itself
   *    does not use it. When there is no guarantee that a collection is finite, it
   *    is generally best to attempt a computation anyway and document that it will
   *    not terminate for infinite collections rather than backing out because this
   *    would prevent performing the computation on collections that are in fact
   *    finite even though `hasDefiniteSize` returns `false`.
   *
   *  @see method `knownSize` for a more useful alternative
   *
   *  @return  `'''true'''` if this collection is known to have finite size,
   *           `'''false'''` otherwise.
   */
  @deprecated("Check .knownSize instead of .hasDefiniteSize for more actionable information (see scaladoc for details)", "2.13.0")
  def hasDefiniteSize: Boolean = true

  /** Tests whether this $coll can be repeatedly traversed.  Always
   *  true for Iterables and false for Iterators unless overridden.
   *
   *  @return   `true` if it is repeatedly traversable, `false` otherwise.
   */
  def isTraversableAgain: Boolean = false

  /** Applies `f` to each element for its side effects.
   *  Note: `U` parameter needed to help scalac's type inference.
   */
  def foreach[U](f: A => U): Unit = {
    val it = iterator
    while(it.hasNext) f(it.next())
  }

  /** Tests whether a predicate holds for all elements of this $coll.
   *
   *  $mayNotTerminateInf
   *
   *  @param   p     the predicate used to test elements.
   *  @return        `true` if this $coll is empty or the given predicate `p`
   *                 holds for all elements of this $coll, otherwise `false`.
   */
  def forall(p: A => Boolean): Boolean = {
    var res = true
    val it = iterator
    while (res && it.hasNext) res = p(it.next())
    res
  }

  /** Tests whether a predicate holds for at least one element of this $coll.
   *
   *  $mayNotTerminateInf
   *
   *  @param   p     the predicate used to test elements.
   *  @return        `true` if the given predicate `p` is satisfied by at least one element of this $coll, otherwise `false`
   */
  def exists(p: A => Boolean): Boolean = {
    var res = false
    val it = iterator
    while (!res && it.hasNext) res = p(it.next())
    res
  }

  /** Counts the number of elements in the $coll which satisfy a predicate.
   *
   *  $willNotTerminateInf
   *
   *  @param p     the predicate  used to test elements.
   *  @return      the number of elements satisfying the predicate `p`.
   */
  def count(p: A => Boolean): Int = {
    var res = 0
    val it = iterator
    while (it.hasNext) if (p(it.next())) res += 1
    res
  }

  /** Finds the first element of the $coll satisfying a predicate, if any.
   *
   *  $mayNotTerminateInf
   *  $orderDependent
   *
   *  @param p       the predicate used to test elements.
   *  @return        an option value containing the first element in the $coll
   *                 that satisfies `p`, or `None` if none exists.
   */
  def find(p: A => Boolean): Option[A] = {
    val it = iterator
    while (it.hasNext) {
      val a = it.next()
      if (p(a)) return Some(a)
    }
    None
  }

  // in future, move to IndexedSeqOps
  private def foldl[X >: A, B](seq: IndexedSeq[X], start: Int, z: B, op: (B, X) => B): B = {
    @tailrec def loop(at: Int, end: Int, acc: B): B =
      if (at == end) acc
      else loop(at + 1, end, op(acc, seq(at)))
    loop(start, seq.length, z)
  }

  private def foldr[X >: A, B >: X](seq: IndexedSeq[X], op: (X, B) => B): B = {
    @tailrec def loop(at: Int, acc: B): B =
      if (at == 0) acc
      else loop(at - 1, op(seq(at - 1), acc))
    loop(seq.length - 1, seq(seq.length - 1))
  }

  /** Applies the given binary operator `op` to the given initial value `z` and all
   *  elements of this $coll, going left to right. Returns the initial value if this $coll
   *  is empty.
   *
   *  "Going left to right" only makes sense if this collection is ordered: then if
   *  `x,,1,,`, `x,,2,,`, ..., `x,,n,,` are the elements of this $coll, the result is
   *  `op( op( ... op( op(z, x,,1,,), x,,2,,) ... ), x,,n,,)`.
   *
   *  If this collection is not ordered, then for each application of the operator, each
   *  right operand is an element. In addition, the leftmost operand is the initial
   *  value, and each other left operand is itself an application of the operator. The
   *  elements of this $coll and the initial value all appear exactly once in the
   *  computation.
   *
   *  $orderDependent
   *  $willNotTerminateInf
   *
   *  @param    z       An initial value.
   *  @param    op      A binary operator.
   *  @tparam   B       The result type of the binary operator.
   *  @return           The result of applying `op` to `z` and all elements of this $coll,
   *                    going left to right. Returns `z` if this $coll is empty.
   */
  def foldLeft[B](z: B)(op: (B, A) => B): B = this match {
    case seq: IndexedSeq[A @unchecked] => foldl[A, B](seq, 0, z, op)
    case _ =>
      var result = z
      val it = iterator
      while (it.hasNext) {
        result = op(result, it.next())
      }
      result
  }

  /** Applies the given binary operator `op` to all elements of this $coll and the given
   *  initial value `z`, going right to left. Returns the initial value if this $coll is
   *  empty.
   *
   *  "Going right to left" only makes sense if this collection is ordered: then if
   *  `x,,1,,`, `x,,2,,`, ..., `x,,n,,` are the elements of this $coll, the result is
   *  `op(x,,1,,, op(x,,2,,, op( ... op(x,,n,,, z) ... )))`.
   *
   *  If this collection is not ordered, then for each application of the operator, each
   *  left operand is an element. In addition, the rightmost operand is the initial
   *  value, and each other right operand is itself an application of the operator. The
   *  elements of this $coll and the initial value all appear exactly once in the
   *  computation.
   *
   *  $orderDependent
   *  $willNotTerminateInf
   *
   *  @param    z       An initial value.
   *  @param    op      A binary operator.
   *  @tparam   B       The result type of the binary operator.
   *  @return           The result of applying `op` to all elements of this $coll and `z`,
   *                    going right to left. Returns `z` if this $coll is empty.
   */
  def foldRight[B](z: B)(op: (A, B) => B): B = reversed.foldLeft(z)((b, a) => op(a, b))

  @deprecated("Use foldLeft instead of /:", "2.13.0")
  @`inline` final def /: [B](z: B)(op: (B, A) => B): B = foldLeft[B](z)(op)

  @deprecated("Use foldRight instead of :\\", "2.13.0")
  @`inline` final def :\ [B](z: B)(op: (A, B) => B): B = foldRight[B](z)(op)

  /** Applies the given binary operator `op` to the given initial value `z` and all
   *  elements of this $coll.
   *
   *  For each application of the operator, each operand is either an element of this
   *  $coll, the initial value, or another such application of the operator.
   *  $undefinedOrder $exactlyOnce The initial value may be used an arbitrary number of
   *  times, but at least once.
   *
   *  If this collection is ordered, then for any application of the operator, the
   *  element(s) appearing in the left operand will precede those in the right.
   *
   *  $orderIndependentReduce In either case, it is also necessary that the initial value
   *  be a neutral value for the operator, e.g. `Nil` for `List` concatenation or `1` for
   *  multiplication.
   *
   *  The default implementation in `IterableOnce` is equivalent to `foldLeft` but may be
   *  overridden for more efficient traversal orders.
   *
   *  $willNotTerminateInf
   *
   *  @tparam A1     The type parameter for the binary operator, a supertype of `A`.
   *  @param z       An initial value; may be used an arbitrary number of times in the
   *                 computation of the result; must be a neutral value for `op` for the
   *                 result to always be the same across runs.
   *  @param op      A binary operator; must be associative for the result to always be the
   *                 same across runs.
   *  @return        The result of applying `op` between all the elements and `z`, or `z`
   *                 if this $coll is empty.
   */
  def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)

  /** Applies the given binary operator `op` to all elements of this $coll.
   *
   *  For each application of the operator, each operand is either an element of this
   *  $coll or another such application of the operator. $undefinedOrder $exactlyOnce
   *
   *  If this collection is ordered, then for any application of the operator, the
   *  element(s) appearing in the left operand will precede those in the right.
   *
   *  $orderIndependentReduce
   *  $willNotTerminateInf
   *
   *  @tparam B      The type parameter for the binary operator, a supertype of `A`.
   *  @param op      A binary operator; must be associative for the result to always be the
   *                 same across runs.
   *  @return        The result of applying `op` between all the elements if the $coll is
   *                 nonempty.
   *  @throws        UnsupportedOperationException if this $coll is empty.
   */
  def reduce[B >: A](op: (B, B) => B): B = reduceLeft(op)

  /** If this $coll is nonempty, reduces it with the given binary operator `op`.
   *
   *  The behavior is the same as [[reduce]] except that the value is `None` if the $coll
   *  is empty. $undefinedOrder $exactlyOnce
   *
   *  $orderIndependentReduce
   *  $willNotTerminateInf
   *
   *  @tparam B      A type parameter for the binary operator, a supertype of `A`.
   *  @param op      A binary operator; must be associative for the result to always be the
   *                 same across runs.
   *  @return        The result of reducing this $coll with `op` if the $coll is nonempty,
   *                 inside a `Some`, and `None` otherwise.
   */
  def reduceOption[B >: A](op: (B, B) => B): Option[B] = reduceLeftOption(op)

  /** Applies the given binary operator `op` to all elements of this $coll, going left to
   *  right.
   *
   *  "Going left to right" only makes sense if this collection is ordered: then if
   *  `x,,1,,`, `x,,2,,`, ..., `x,,n,,` are the elements of this $coll, the result is
   *  `op( op( op( ... op(x,,1,,, x,,2,,) ... ), x,,n-1,,), x,,n,,)`.
   *
   *  If this collection is not ordered, then for each application of the operator, each
   *  right operand is an element. In addition, the leftmost operand is the first element
   *  of this $coll and each other left operand is itself an application of the
   *  operator. $exactlyOnce
   *
   *  $orderDependentReduce
   *  $willNotTerminateInf
   *
   *  @param    op      A binary operator.
   *  @tparam   B       The result type of the binary operator, a supertype of `A`.
   *  @return           The result of applying `op` to all elements of this $coll, going
   *                    left to right.
   *  @throws UnsupportedOperationException if this $coll is empty.
   */
  def reduceLeft[B >: A](op: (B, A) => B): B = this match {
    case seq: IndexedSeq[A @unchecked] if seq.length > 0 => foldl(seq, 1, seq(0), op)
    case _ if knownSize == 0 => throw new UnsupportedOperationException("empty.reduceLeft")
    case _ => reduceLeftIterator[B](throw new UnsupportedOperationException("empty.reduceLeft"))(op)
  }
  private final def reduceLeftIterator[B >: A](onEmpty: => B)(op: (B, A) => B): B = {
    val it = iterator
    if (it.hasNext) {
      var acc: B = it.next()
      while (it.hasNext)
        acc = op(acc, it.next())
      acc
    }
    else onEmpty
  }

  /** Applies the given binary operator `op` to all elements of this $coll, going right to
   *  left.
   *
   *  "Going right to left" only makes sense if this collection is ordered: then if
   *  `x,,1,,`, `x,,2,,`, ..., `x,,n,,` are the elements of this $coll, the result is
   *  `op(x,,1,,, op(x,,2,,, op( ... op(x,,n-1,,, x,,n,,) ... )))`.
   *
   *  If this collection is not ordered, then for each application of the operator, each
   *  left operand is an element. In addition, the rightmost operand is the last element
   *  of this $coll and each other right operand is itself an application of the
   *  operator. $exactlyOnce
   *
   *  $orderDependentReduce
   *  $willNotTerminateInf
   *
   *  @param    op      A binary operator.
   *  @tparam   B       The result type of the binary operator, a supertype of `A`.
   *  @return           The result of applying `op` to all elements of this $coll, going
   *                    right to left.
   *  @throws UnsupportedOperationException if this $coll is empty.
   */
  def reduceRight[B >: A](op: (A, B) => B): B = this match {
    case seq: IndexedSeq[A @unchecked] if seq.length > 0 => foldr[A, B](seq, op)
    case _ if knownSize == 0 => throw new UnsupportedOperationException("empty.reduceRight")
    case _ => reversed.reduceLeft[B]((x, y) => op(y, x)) // reduceLeftIterator
  }

  /** If this $coll is nonempty, reduces it with the given binary operator `op`, going
    * left to right.
   *
   *  The behavior is the same as [[reduceLeft]] except that the value is `None` if the
   *  $coll is empty. $exactlyOnce
   *
   *  $orderDependentReduce
   *  $willNotTerminateInf
   *
   *  @param    op      A binary operator.
   *  @tparam   B       The result type of the binary operator, a supertype of `A`.
   *  @return           The result of reducing this $coll with `op` going left to right if
   *                    the $coll is nonempty, inside a `Some`, and `None` otherwise.
   */
  def reduceLeftOption[B >: A](op: (B, A) => B): Option[B] =
    knownSize match {
      case -1 => reduceLeftOptionIterator[B](op)
      case  0 => None
      case  _ => Some(reduceLeft(op))
    }
  private final def reduceLeftOptionIterator[B >: A](op: (B, A) => B): Option[B] = reduceOptionIterator[A, B](iterator)(op)
  private final def reduceOptionIterator[X >: A, B >: X](it: Iterator[X])(op: (B, X) => B): Option[B] = {
    if (it.hasNext) {
      var acc: B = it.next()
      while (it.hasNext)
        acc = op(acc, it.next())
      Some(acc)
    }
    else None
  }

  /** If this $coll is nonempty, reduces it with the given binary operator `op`, going
   *  right to left.
   *
   *  The behavior is the same as [[reduceRight]] except that the value is `None` if the
   *  $coll is empty. $exactlyOnce
   *
   *  $orderDependentReduce
   *  $willNotTerminateInf
   *
   *  @param    op      A binary operator.
   *  @tparam   B       The result type of the binary operator, a supertype of `A`.
   *  @return           The result of reducing this $coll with `op` going right to left if
   *                    the $coll is nonempty, inside a `Some`, and `None` otherwise.
   */
  def reduceRightOption[B >: A](op: (A, B) => B): Option[B] =
    knownSize match {
      case -1 => reduceOptionIterator[A, B](reversed.iterator)((x, y) => op(y, x))
      case  0 => None
      case  _ => Some(reduceRight(op))
    }

  /** Tests whether the $coll is empty.
   *
   *  Note: The default implementation creates and discards an iterator.
   *
   *  Note: Implementations in subclasses that are not repeatedly iterable must take
   *  care not to consume any elements when `isEmpty` is called.
   *
   *  @return    `true` if the $coll contains no elements, `false` otherwise.
   */
  def isEmpty: Boolean =
    knownSize match {
      case -1 => !iterator.hasNext
      case  0 => true
      case  _ => false
    }

  /** Tests whether the $coll is not empty.
   *
   *  @return    `true` if the $coll contains at least one element, `false` otherwise.
   */
  @deprecatedOverriding("nonEmpty is defined as !isEmpty; override isEmpty instead", "2.13.0")
  def nonEmpty: Boolean = !isEmpty

  /** The size of this $coll.
   *
   *  $willNotTerminateInf
   *
   *  @return    the number of elements in this $coll.
   */
  def size: Int =
    if (knownSize >= 0) knownSize
    else {
      val it = iterator
      var len = 0
      while (it.hasNext) { len += 1; it.next() }
      len
    }

  @deprecated("Use `dest ++= coll` instead", "2.13.0")
  @inline final def copyToBuffer[B >: A](dest: mutable.Buffer[B]): Unit = dest ++= this

  /** Copies elements to an array, returning the number of elements written.
   *
   *  Fills the given array `xs` starting at index `start` with values of this $coll.
   *
   *  Copying will stop once either all the elements of this $coll have been copied,
   *  or the end of the array is reached.
   *
   *  @param  xs     the array to fill.
   *  @tparam B      the type of the elements of the array.
   *  @return        the number of elements written to the array
   *
   *  @note    Reuse: $consumesIterator
   */
  @deprecatedOverriding("This should always forward to the 3-arg version of this method", since = "2.13.4")
  def copyToArray[B >: A](xs: Array[B]): Int = copyToArray(xs, 0, Int.MaxValue)

  /** Copies elements to an array, returning the number of elements written.
   *
   *  Fills the given array `xs` starting at index `start` with values of this $coll.
   *
   *  Copying will stop once either all the elements of this $coll have been copied,
   *  or the end of the array is reached.
   *
   *  @param  xs     the array to fill.
   *  @param  start  the starting index of xs.
   *  @tparam B      the type of the elements of the array.
   *  @return        the number of elements written to the array
   *
   *  @note    Reuse: $consumesIterator
   */
  @deprecatedOverriding("This should always forward to the 3-arg version of this method", since = "2.13.4")
  def copyToArray[B >: A](xs: Array[B], start: Int): Int = copyToArray(xs, start, Int.MaxValue)

  /** Copy elements to an array, returning the number of elements written.
   *
   *  Fills the given array `xs` starting at index `start` with at most `len` elements of this $coll.
   *
   *  Copying will stop once either all the elements of this $coll have been copied,
   *  or the end of the array is reached, or `len` elements have been copied.
   *
   *  @param  xs     the array to fill.
   *  @param  start  the starting index of xs.
   *  @param  len    the maximal number of elements to copy.
   *  @tparam B      the type of the elements of the array.
   *  @return        the number of elements written to the array
   *
   *  @note    Reuse: $consumesIterator
   */
  def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = {
    val it = iterator
    var i = start
    val end = start + math.min(len, xs.length - start)
    while (i < end && it.hasNext) {
      xs(i) = it.next()
      i += 1
    }
    i - start
  }

  /** Sums the elements of this collection.
   *
   *  The default implementation uses `reduce` for a known non-empty collection, `foldLeft` otherwise.
   *
   *   $willNotTerminateInf
   *
   *   @param   num  an implicit parameter defining a set of numeric operations
   *                 which includes the `+` operator to be used in forming the sum.
   *   @tparam  B    the result type of the `+` operator.
   *   @return       the sum of all elements of this $coll with respect to the `+` operator in `num`.
   */
  def sum[B >: A](implicit num: Numeric[B]): B =
    knownSize match {
      case -1 => foldLeft(num.zero)(num.plus)
      case  0 => num.zero
      case  _ => reduce(num.plus)
    }

  /** Multiplies together the elements of this collection.
   *
   *  The default implementation uses `reduce` for a known non-empty collection, `foldLeft` otherwise.
   *
   *  $willNotTerminateInf
   *
   *   @param   num  an implicit parameter defining a set of numeric operations
   *                 which includes the `*` operator to be used in forming the product.
   *   @tparam  B   the result type of the `*` operator.
   *   @return       the product of all elements of this $coll with respect to the `*` operator in `num`.
   */
  def product[B >: A](implicit num: Numeric[B]): B =
    knownSize match {
      case -1 => foldLeft(num.one)(num.times)
      case  0 => num.one
      case  _ => reduce(num.times)
    }

  /** Finds the smallest element.
   *
   *  $willNotTerminateInf
   *
   *  @param    ord   An ordering to be used for comparing elements.
   *  @tparam   B    The type over which the ordering is defined.
   *  @throws   UnsupportedOperationException if this $coll is empty.
   *  @return   the smallest element of this $coll with respect to the ordering `ord`.
   *
   */
  def min[B >: A](implicit ord: Ordering[B]): A =
    knownSize match {
      case -1 => reduceLeftIterator[A](throw new UnsupportedOperationException("empty.min"))(ord.min)
      case  0 => throw new UnsupportedOperationException("empty.min")
      case  _ => reduceLeft(ord.min)
    }

  /** Finds the smallest element.
   *
   *  $willNotTerminateInf
   *
   *  @param    ord   An ordering to be used for comparing elements.
   *  @tparam   B    The type over which the ordering is defined.
   *  @return   an option value containing the smallest element of this $coll
   *            with respect to the ordering `ord`.
   */
  def minOption[B >: A](implicit ord: Ordering[B]): Option[A] =
    knownSize match {
      case -1 => reduceLeftOptionIterator[A](ord.min)
      case  0 => None
      case  _ => Some(reduceLeft(ord.min))
    }

  /** Finds the largest element.
   *
   *  $willNotTerminateInf
   *
   *  @param    ord   An ordering to be used for comparing elements.
   *  @tparam   B    The type over which the ordering is defined.
   *  @throws   UnsupportedOperationException if this $coll is empty.
   *  @return   the largest element of this $coll with respect to the ordering `ord`.
   */
  def max[B >: A](implicit ord: Ordering[B]): A =
    knownSize match {
      case -1 => reduceLeftIterator[A](throw new UnsupportedOperationException("empty.max"))(ord.max)
      case  0 => throw new UnsupportedOperationException("empty.max")
      case  _ => reduceLeft(ord.max)
    }

  /** Finds the largest element.
   *
   *  $willNotTerminateInf
   *
   *  @param    ord   An ordering to be used for comparing elements.
   *  @tparam   B    The type over which the ordering is defined.
   *  @return   an option value containing the largest element of this $coll with
   *            respect to the ordering `ord`.
   */
  def maxOption[B >: A](implicit ord: Ordering[B]): Option[A] =
    knownSize match {
      case -1 => reduceLeftOptionIterator[A](ord.max)
      case  0 => None
      case  _ => Some(reduceLeft(ord.max))
    }

  /** Finds the first element which yields the largest value measured by function `f`.
   *
   *  $willNotTerminateInf
   *
   *  @param    cmp   An ordering to be used for comparing elements.
   *  @tparam   B     The result type of the function `f`.
   *  @param    f     The measuring function.
   *  @throws   UnsupportedOperationException if this $coll is empty.
   *  @return   the first element of this $coll with the largest value measured by function `f`
   *            with respect to the ordering `cmp`.
   */
  def maxBy[B](f: A => B)(implicit ord: Ordering[B]): A =
    knownSize match {
      case  0 => throw new UnsupportedOperationException("empty.maxBy")
      case  _ => foldLeft(new Maximized[A, B]("maxBy")(f)(ord.gt))((m, a) => m(m, a)).result
    }

  private class Maximized[X, B](descriptor: String)(f: X => B)(cmp: (B, B) => Boolean) extends AbstractFunction2[Maximized[X, B], X, Maximized[X, B]] {
    var maxElem: X = null.asInstanceOf[X]
    var maxF: B = null.asInstanceOf[B]
    var nonEmpty = false
    def toOption: Option[X] = if (nonEmpty) Some(maxElem) else None
    def result: X = if (nonEmpty) maxElem else throw new UnsupportedOperationException(s"empty.$descriptor")
    def apply(m: Maximized[X, B], a: X): Maximized[X, B] =
      if (m.nonEmpty) {
        val fa = f(a)
        if (cmp(fa, maxF)) {
          maxF = fa
          maxElem = a
        }
        m
      }
      else {
        m.nonEmpty = true
        m.maxElem = a
        m.maxF = f(a)
        m
      }
  }

  /** Finds the first element which yields the largest value measured by function `f`.
   *
   *  $willNotTerminateInf
   *
   *  @param    cmp   An ordering to be used for comparing elements.
   *  @tparam   B     The result type of the function `f`.
   *  @param    f     The measuring function.
   *  @return   an option value containing the first element of this $coll with the
   *            largest value measured by function `f` with respect to the ordering `cmp`.
   */
  def maxByOption[B](f: A => B)(implicit ord: Ordering[B]): Option[A] =
    knownSize match {
      case  0 => None
      case  _ => foldLeft(new Maximized[A, B]("maxBy")(f)(ord.gt))((m, a) => m(m, a)).toOption
    }

  /** Finds the first element which yields the smallest value measured by function `f`.
   *
   *  $willNotTerminateInf
   *
   *  @param    cmp   An ordering to be used for comparing elements.
   *  @tparam   B     The result type of the function `f`.
   *  @param    f     The measuring function.
   *  @throws   UnsupportedOperationException if this $coll is empty.
   *  @return   the first element of this $coll with the smallest value measured by function `f`
   *            with respect to the ordering `cmp`.
   */
  def minBy[B](f: A => B)(implicit ord: Ordering[B]): A =
    knownSize match {
      case  0 => throw new UnsupportedOperationException("empty.minBy")
      case  _ => foldLeft(new Maximized[A, B]("minBy")(f)(ord.lt))((m, a) => m(m, a)).result
    }

  /** Finds the first element which yields the smallest value measured by function `f`.
   *
   *  $willNotTerminateInf
   *
   *  @param    cmp   An ordering to be used for comparing elements.
   *  @tparam   B     The result type of the function `f`.
   *  @param    f     The measuring function.
   *  @return   an option value containing the first element of this $coll
   *            with the smallest value measured by function `f`
   *            with respect to the ordering `cmp`.
   */
  def minByOption[B](f: A => B)(implicit ord: Ordering[B]): Option[A] =
    knownSize match {
      case  0 => None
      case  _ => foldLeft(new Maximized[A, B]("minBy")(f)(ord.lt))((m, a) => m(m, a)).toOption
    }

  /** Finds the first element of the $coll for which the given partial
   *  function is defined, and applies the partial function to it.
   *
   *  $mayNotTerminateInf
   *  $orderDependent
   *
   *  @param pf   the partial function
   *  @return     an option value containing pf applied to the first
   *              value for which it is defined, or `None` if none exists.
   *  @example    `Seq("a", 1, 5L).collectFirst({ case x: Int => x*10 }) = Some(10)`
   */
  def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = {
    // Presumably the fastest way to get in and out of a partial function is for a sentinel function to return itself
    // (Tested to be lower-overhead than runWith.  Would be better yet to not need to (formally) allocate it)
    val sentinel: scala.Function1[A, Any] = new AbstractFunction1[A, Any] {
      def apply(a: A): AbstractFunction1[A, Any] = this
    }
    val it = iterator
    while (it.hasNext) {
      val x = pf.applyOrElse(it.next(), sentinel)
      if (x.asInstanceOf[AnyRef] ne sentinel) return Some(x.asInstanceOf[B])
    }
    None
  }

  /** Aggregates the results of applying an operator to subsequent elements.
   *
   *  Since this method degenerates to `foldLeft` for sequential (non-parallel) collections,
   *  where the combining operation is ignored, it is advisable to prefer `foldLeft` for that case.
   *
   *  For [[https://github.com/scala/scala-parallel-collections parallel collections]],
   *  use the `aggregate` method specified by `scala.collection.parallel.ParIterableLike`.
   *
   *  @param   z      the start value, a neutral element for `seqop`.
   *  @param   seqop  the binary operator used to accumulate the result.
   *  @param   combop an associative operator for combining sequential results, unused for sequential collections.
   *  @tparam  B      the result type, produced by `seqop`, `combop`, and by this function as a final result.
   */
  @deprecated("For sequential collections, prefer `foldLeft(z)(seqop)`. For parallel collections, use `ParIterableLike#aggregate`.", "2.13.0")
  def aggregate[B](z: => B)(seqop: (B, A) => B, combop: (B, B) => B): B = foldLeft(z)(seqop)

  /** Tests whether every element of this collection's iterator relates to the
   *  corresponding element of another collection by satisfying a test predicate.
   *
   *  $willNotTerminateInf
   *
   *  @param   that    the other collection
   *  @param   p       the test predicate, which relates elements from both collections
   *  @tparam  B       the type of the elements of `that`
   *  @return          `true` if both collections have the same length and
   *                   `p(x, y)` is `true` for all corresponding elements `x` of this iterator
   *                   and `y` of `that`, otherwise `false`
   */
  def corresponds[B](that: IterableOnce[B])(p: (A, B) => Boolean): Boolean = {
    val a = iterator
    val b = that.iterator

    while (a.hasNext && b.hasNext) {
      if (!p(a.next(), b.next())) return false
    }

    a.hasNext == b.hasNext
  }

  /** Displays all elements of this $coll in a string using start, end, and separator strings.
   *
   *  Delegates to addString, which can be overridden.
   *
   *  @param start the starting string.
   *  @param sep   the separator string.
   *  @param end   the ending string.
   *  @return      a string representation of this $coll. The resulting string
   *               begins with the string `start` and ends with the string
   *               `end`. Inside, the string representations (w.r.t. the method
   *               `toString`) of all elements of this $coll are separated by
   *               the string `sep`.
   *
   *  @example  `List(1, 2, 3).mkString("(", "; ", ")") = "(1; 2; 3)"`
   */
  final def mkString(start: String, sep: String, end: String): String =
    if (knownSize == 0) start + end
    else addString(new StringBuilder(), start, sep, end).result()

  /** Displays all elements of this $coll in a string using a separator string.
   *
   *  Delegates to addString, which can be overridden.
   *
   *  @param sep   the separator string.
   *  @return      a string representation of this $coll. In the resulting string
   *               the string representations (w.r.t. the method `toString`)
   *               of all elements of this $coll are separated by the string `sep`.
   *
   *  @example  `List(1, 2, 3).mkString("|") = "1|2|3"`
   */
  @inline final def mkString(sep: String): String = mkString("", sep, "")

  /** Displays all elements of this $coll in a string.
   *
   *  Delegates to addString, which can be overridden.
   *
   *  @return a string representation of this $coll. In the resulting string
   *          the string representations (w.r.t. the method `toString`)
   *          of all elements of this $coll follow each other without any
   *          separator string.
   */
  @inline final def mkString: String = mkString("")

  /** Appends all elements of this $coll to a string builder using start, end, and separator strings.
   *  The written text begins with the string `start` and ends with the string `end`.
   *  Inside, the string representations (w.r.t. the method `toString`)
   *  of all elements of this $coll are separated by the string `sep`.
   *
   *  Example:
   *
   *  {{{
   *      scala> val a = List(1,2,3,4)
   *      a: List[Int] = List(1, 2, 3, 4)
   *
   *      scala> val b = new StringBuilder()
   *      b: StringBuilder =
   *
   *      scala> a.addString(b , "List(" , ", " , ")")
   *      res5: StringBuilder = List(1, 2, 3, 4)
   *  }}}
   *
   *  @param  b    the string builder to which elements are appended.
   *  @param start the starting string.
   *  @param sep   the separator string.
   *  @param end   the ending string.
   *  @return      the string builder `b` to which elements were appended.
   */
  def addString(b: StringBuilder, start: String, sep: String, end: String): b.type = {
    val jsb = b.underlying
    if (start.length != 0) jsb.append(start)
    val it = iterator
    if (it.hasNext) {
      jsb.append(it.next())
      while (it.hasNext) {
        jsb.append(sep)
        jsb.append(it.next())
      }
    }
    if (end.length != 0) jsb.append(end)
    b
  }

  /** Appends all elements of this $coll to a string builder using a separator string.
   *  The written text consists of the string representations (w.r.t. the method `toString`)
   *  of all elements of this $coll, separated by the string `sep`.
   *
   *  Example:
   *
   *  {{{
   *      scala> val a = List(1,2,3,4)
   *      a: List[Int] = List(1, 2, 3, 4)
   *
   *      scala> val b = new StringBuilder()
   *      b: StringBuilder =
   *
   *      scala> a.addString(b, ", ")
   *      res0: StringBuilder = 1, 2, 3, 4
   *  }}}
   *
   *  @param  b    the string builder to which elements are appended.
   *  @param sep   the separator string.
   *  @return      the string builder `b` to which elements were appended.
   */
  @inline final def addString(b: StringBuilder, sep: String): b.type = addString(b, "", sep, "")

  /** Appends all elements of this $coll to a string builder.
   *  The written text consists of the string representations (w.r.t. the method
   * `toString`) of all elements of this $coll without any separator string.
   *
   *  Example:
   *
   *  {{{
   *      scala> val a = List(1,2,3,4)
   *      a: List[Int] = List(1, 2, 3, 4)
   *
   *      scala> val b = new StringBuilder()
   *      b: StringBuilder =
   *
   *      scala> val h = a.addString(b)
   *      h: StringBuilder = 1234
   *  }}}
   *
   *  @param  b    the string builder to which elements are appended.
   *  @return      the string builder `b` to which elements were appended.
   */
  @inline final def addString(b: StringBuilder): b.type = addString(b, "")

  /** Given a collection factory `factory`, converts this $coll to the appropriate
   *  representation for the current element type `A`. Example uses:
   *
   *  {{{
   *      xs.to(List)
   *      xs.to(ArrayBuffer)
   *      xs.to(BitSet) // for xs: Iterable[Int]
   *  }}}
   */
  def to[C1](factory: Factory[A, C1]): C1 = factory.fromSpecific(this)

  @deprecated("Use .iterator instead of .toIterator", "2.13.0")
  @`inline` final def toIterator: Iterator[A] = iterator

  /** Converts this $coll to a `List`.
    *
    * @return This $coll as a `List[A]`.
    */
  def toList: immutable.List[A] = immutable.List.from(this)

  /** Converts this $coll to a `Vector`.
    *
    * @return This $coll as a `Vector[A]`.
    */
  def toVector: immutable.Vector[A] = immutable.Vector.from(this)

  /** Converts this $coll to a `Map`, given an implicit coercion from the $coll's type to a key-value tuple.
    *
    * @tparam K The key type for the resulting map.
    * @tparam V The value type for the resulting map.
    * @param ev An implicit coercion from `A` to `[K, V]`.
    * @return This $coll as a `Map[K, V]`.
    */
  def toMap[K, V](implicit ev: A <:< (K, V)): immutable.Map[K, V] =
    immutable.Map.from(this.asInstanceOf[IterableOnce[(K, V)]])

  /** Converts this $coll to a `Set`.
    *
    * @tparam B The type of elements of the result, a supertype of `A`.
    * @return This $coll as a `Set[B]`.
    */
  def toSet[B >: A]: immutable.Set[B] = immutable.Set.from(this)

  /** @return This $coll as a `Seq[A]`. This is equivalent to `to(Seq)` but might be faster.
   */
  def toSeq: immutable.Seq[A] = immutable.Seq.from(this)

  /** Converts this $coll to an `IndexedSeq`.
    *
    * @return This $coll as an `IndexedSeq[A]`.
    */
  def toIndexedSeq: immutable.IndexedSeq[A] = immutable.IndexedSeq.from(this)

  @deprecated("Use .to(LazyList) instead of .toStream", "2.13.0")
  @inline final def toStream: immutable.Stream[A] = to(immutable.Stream)

  /** Converts this $coll to a `Buffer`.
    *
    * @tparam B The type of elements of the result, a supertype of `A`.
    * @return This $coll as a `Buffer[B]`.
    */
  @inline final def toBuffer[B >: A]: mutable.Buffer[B] = mutable.Buffer.from(this)

  /** Converts this $coll to an `Array`.
    *
    * Implementation note: DO NOT call [[Array.from]] from this method.
    *
    * @tparam B The type of elements of the result, a supertype of `A`.
    * @return This $coll as an `Array[B]`.
    */
  def toArray[B >: A: ClassTag]: Array[B] =
    if (knownSize >= 0) {
      val destination = new Array[B](knownSize)
      @annotation.unused val copied = copyToArray(destination, 0)
      //assert(copied == destination.length)
      destination
    }
    else mutable.ArrayBuilder.make[B].addAll(this).result()

  // For internal use
  protected def reversed: Iterable[A] = {
    var xs: immutable.List[A] = immutable.Nil
    val it = iterator
    while (it.hasNext) xs = it.next() :: xs
    xs
  }
}
