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

codata.process1.scala Maven / Gradle / Ivy

package org.specs2.codata

import scala.annotation.tailrec
import scalaz.\/._
import scalaz._
import scalaz.syntax.equal._

import Cause._
import Process._
import Util._

object process1 {

  // nb: methods are in alphabetical order, there are going to be so many that
  // any other order will just going get confusing

  /** Await a single value, returning `None` if the input has been exhausted. */
  def awaitOption[I]: Process1[I, Option[I]] =
    receive1Or[I, Option[I]](emit(None))(i => emit(Some(i)))

  /** Behaves like the identity process, but requests `n` elements at a time from its input. */
  def buffer[I](n: Int): Process1[I, I] =
    chunk[I](n).flatMap(emitAll)

  /**
   * Behaves like the identity process, but requests elements from its
   * input in blocks that end whenever the predicate switches from true to false.
   */
  def bufferBy[I](f: I => Boolean): Process1[I, I] =
    chunkBy(f).flatMap(emitAll)

  /** Behaves like the identity process, but batches all output into a single `Emit`. */
  def bufferAll[I]: Process1[I, I] =
    chunkAll[I].flatMap(emitAll)

  /**
   * Groups inputs into chunks of size `n`. The last chunk may have size
   * less than `n`, depending on the number of elements in the input.
   *
   * @example {{{
   * scala> Process(1, 2, 3, 4, 5).chunk(2).toList
   * res0: List[Vector[Int]] = List(Vector(1, 2), Vector(3, 4), Vector(5))
   * }}}
   * @throws IllegalArgumentException if `n` <= 0
   */
  def chunk[I](n: Int): Process1[I, Vector[I]] = {
    require(n > 0, "chunk size must be > 0, was: " + n)
    def go(m: Int, acc: Vector[I]): Process1[I, Vector[I]] =
      if (m <= 0) emit(acc) ++ go(n, Vector())
      else receive1Or[I, Vector[I]](if (acc.nonEmpty) emit(acc) else halt) { i =>
        go(m - 1, acc :+ i)
      }
    go(n, Vector())
  }

  /** Collects up all output of this `Process1` into a single `Emit`. */
  def chunkAll[I]: Process1[I, Vector[I]] =
    chunkBy[I](_ => false)

  /**
   * Like `chunk`, but emits a chunk whenever the predicate switches from
   * true to false.
   * {{{
   * scala> Process(1, 2, -1, 3, 4).chunkBy(_ > 0).toList
   * res0: List[Vector[Int]] = List(Vector(1, 2, -1), Vector(3, 4))
   * }}}
   */
  def chunkBy[I](f: I => Boolean): Process1[I, Vector[I]] = {
    def go(acc: Vector[I], last: Boolean): Process1[I, Vector[I]] =
      receive1Or[I,Vector[I]](emit(acc)) { i =>
        val chunk = acc :+ i
        val cur = f(i)
        if (!cur && last) emit(chunk) ++ go(Vector(), false)
        else go(chunk, cur)
      }
    go(Vector(), false)
  }

  /**
   * Like `chunkBy`, but the predicate depends on the current and previous elements.
   */
  def chunkBy2[I](f: (I, I) => Boolean): Process1[I, Vector[I]] = {
    def go(acc: Vector[I], last: I): Process1[I, Vector[I]] =
      receive1Or[I,Vector[I]](emit(acc)) { i =>
        if (f(last, i)) go(acc :+ i, i)
        else emit(acc) ++ go(Vector(i), i)
      }
    receive1(i => go(Vector(i), i))
  }

  /**
   * Like `collect` on scala collection.
   * Builds a new process by applying a partial function
   * to all elements of this process on which the function is defined.
   *
   * Elements, for which the partial function is not defined are
   * filtered out from new process
   */
  def collect[I, I2](pf: PartialFunction[I, I2]): Process1[I, I2] =
    id[I].flatMap(pf andThen (emit) orElse { case _ => halt })

  /**
   * Like `collect`, but emits only the first element of this process on which
   * the partial function is defined.
   */
  def collectFirst[I, I2](pf: PartialFunction[I, I2]): Process1[I, I2] =
    collect(pf).once

  /**
   * Skips the first element that matches the predicate.
   *
   * @example {{{
   * scala> Process(3, 4, 5, 6).delete(_ % 2 == 0).toList
   * res0: List[Int] = List(3, 5, 6)
   * }}}
   */
  def delete[I](f: I => Boolean): Process1[I, I] =
    receive1(i => if (f(i)) id else emit(i) ++ delete(f))

  /**
   * Remove any leading emitted values that occur before the first successful
   * `Await`. That means that the returned `Process1` will produce output only
   * if it has consumed at least one input element.
   */
  def drainLeading[A, B](p: Process1[A, B]): Process1[A, B] =
    receive1(a => feed1(a)(p))

  /**
   * Emits only elements that are distinct from their immediate predecessors.
   *
   * @example {{{
   * scala> import scalaz.std.anyVal._
   * scala> Process(1, 2, 2, 1, 1, 3).distinctConsecutive.toList
   * res0: List[Int] = List(1, 2, 1, 3)
   * }}}
   */
  def distinctConsecutive[A: Equal]: Process1[A, A] =
    distinctConsecutiveBy(identity)

  /**
   * Emits only elements that are distinct from their immediate predecessors
   * according to `f`.
   *
   * @example {{{
   * scala> import scalaz.std.anyVal._
   * scala> Process("a", "ab", "bc", "c", "d").distinctConsecutiveBy(_.length).toList
   * res0: List[String] = List(a, ab, c)
   * }}}
   */
  def distinctConsecutiveBy[A, B: Equal](f: A => B): Process1[A, A] =
    filterBy2((a1, a2) => f(a1) =/= f(a2))

  /** Skips the first `n` elements of the input, then passes through the rest. */
  def drop[I](n: Int): Process1[I, I] =
    if (n <= 0) id
    else skip ++ drop(n - 1)

  /** Emits all but the last element of the input. */
  def dropLast[I]: Process1[I, I] =
    dropLastIf(_ => true)

  /** Emits all elements of the input but skips the last if the predicate is true. */
  def dropLastIf[I](p: I => Boolean): Process1[I, I] = {
    def go(prev: I): Process1[I, I] =
      receive1Or[I,I](if (p(prev)) halt else emit(prev)) { i =>
        emit(prev) ++ go(i)
      }
    receive1(go)
  }

  /** Emits all but the last `n` elements of the input. */
  def dropRight[I](n: Int): Process1[I, I] = {
    def go(acc: Vector[I]): Process1[I, I] =
      receive1(i => emit(acc.head) ++ go(acc.tail :+ i))
    if (n <= 0) id
    else chunk(n).once.flatMap(go)
  }

  /**
   * Skips elements of the input while the predicate is true,
   * then passes through the remaining inputs.
   */
  def dropWhile[I](f: I => Boolean): Process1[I, I] =
    receive1(i => if (f(i)) dropWhile(f) else emit(i) ++ id)

  /** Feed a single input to a `Process1`. */
  def feed1[I, O](i: I)(p: Process1[I, O]): Process1[I, O] =
    feed(Seq(i))(p)

  /** Feed a sequence of inputs to a `Process1`. */
  def feed[I, O](i: Seq[I])(p: Process1[I, O]): Process1[I, O] = {
    @tailrec
    def go(in: Seq[I], out: Vector[O] , cur: Process1[I, O]  ): Process1[I, O] = {
      if (in.nonEmpty) {
        cur.step match {
          case Step(Emit(os),cont) => go(in, out fast_++ os, cont.continue)
          case Step(Await1(rcv), cont) => go(in.tail,out,rcv(right(in.head)) +: cont)
          case Halt(rsn) =>  emitAll(out).causedBy(rsn)
        }
      } else cur.prepend(out)
    }
    go(i, Vector(), p)
  }

  /** Skips any elements of the input not matching the predicate. */
  def filter[I](f: I => Boolean): Process1[I, I] =
    id[I].flatMap(i => if (f(i)) emit(i) else halt)

  /**
   * Like `filter`, but the predicate `f` depends on the previously emitted and
   * current elements.
   *
   * @example {{{
   * scala> Process(2, 4, 1, 5, 3).filterBy2(_ < _).toList
   * res0: List[Int] = List(2, 4, 5)
   * }}}
   */
  def filterBy2[I](f: (I, I) => Boolean): Process1[I, I] = {
    def pass(i: I): Process1[I, I] =
      emit(i) ++ go(f(i, _))
    def go(g: I => Boolean): Process1[I, I] =
      receive1(i => if (g(i)) pass(i) else go(g))
    receive1(pass)
  }

  /**
   * Skips any elements not satisfying predicate and when found, will emit that
   * element and terminate
   */
  def find[I](f: I => Boolean): Process1[I, I] =
    filter(f).once

  /**
   * Halts with `true` as soon as a matching element is received.
   * Emits a single `false` if no input matches the predicate.
   */
  def exists[I](f: I => Boolean): Process1[I, Boolean] =
    forall[I](!f(_)).map(!_)

  /**
   * Emits a single `true` value if all input matches the predicate.
   * Halts with `false` as soon as a non-matching element is received.
   */
  def forall[I](f: I => Boolean): Process1[I, Boolean] =
    receive1Or[I,Boolean](emit(true)) { i =>
      if (f(i)) forall(f) else emit(false)
    }

  /**
   * `Process1` form of `List.fold`.
   * Folds the elements of this Process using the specified associative binary operator.
   *
   * Unlike List.fold the order is always from the `left` side, i.e. it will always
   * honor order of `A`.
   *
   * If Process of `A` is empty, it will just emit `z` and terminate
   * {{{
   * scala> Process(1, 2, 3, 4).fold(0)(_ + _).toList
   * res0: List[Int] = List(10)
   * }}}
   */
  def fold[A, B](z: B)(f: (B, A) => B): Process1[A, B] =
    scan(z)(f).last

  /** Alias for `[[reduce]](f)`. */
  def fold1[A](f: (A, A) => A): Process1[A, A] =
    reduce(f)

  /** Alias for `[[reduceMap]](f)(M)`. */
  def fold1Map[A, B](f: A => B)(implicit M: Monoid[B]): Process1[A, B] =
    reduceMap(f)(M)

  /** Alias for `[[reduceSemigroup]](M)`. */
  def fold1Monoid[A](implicit M: Monoid[A]): Process1[A, A] =
    reduceSemigroup(M)

  /**
   * Like `fold` only uses `f` to map `A` to `B` and uses Monoid `M` for associative operation
   */
  def foldMap[A, B](f: A => B)(implicit M: Monoid[B]): Process1[A, B] =
   lift(f).foldMonoid(M)

  /**
   * Like `fold` but uses Monoid for folding operation
   */
  def foldMonoid[A](implicit M: Monoid[A]): Process1[A, A] =
    fold(M.zero)(M.append(_, _))

  /** Alias for `[[reduceSemigroup]](M)`. */
  def foldSemigroup[A](implicit M: Semigroup[A]): Process1[A, A] =
    reduceSemigroup(M)

  /** Repeatedly echo the input; satisfies `x |> id == x` and `id |> x == x`. */
  def id[I]: Process1[I, I] =
    await1[I].repeat

  /**
   * Adds `separator` between elements of the input. For example,
   * {{{
   * scala> Process(1, 2, 3).intersperse(0).toList
   * res0: List[Int] = List(1, 0, 2, 0, 3)
   * }}}
   */
  def intersperse[A](separator: A): Process1[A, A] =
    await1[A] ++ id[A].flatMap(a => Process(separator, a))

  /** Skips all but the last element of the input. */
  def last[I]: Process1[I, I] = {
    def go(prev: I): Process1[I, I] = receive1Or[I,I](emit(prev))(go)
    receive1(go)
  }

  /**
   * Skips all but the last element of the input.
   * This `Process` will always emit exactly one value;
   * If the input is empty, `li` is emitted.
   */
  def lastOr[I](li: => I): Process1[I, I] =
    receive1Or[I,I](emit(li))(i => lastOr(i))

  /** Transform the input using the given function, `f`. */
  def lift[I, O](f: I => O): Process1[I, O] =
    id map f

  /**
   * Transform `p` to operate on the first element of a pair, passing
   * through the right value with no modifications. Note that this halts
   * whenever `p` halts.
   *
   * @param f function used to convert `B`s generated during cleanup of `p` to pairs
   */
  def liftFirst[A, B, C](f: B => Option[C])(p: Process1[A, B]): Process1[(A, C), (B, C)] = {
    def go(curr: Process1[A, B]): Process1[(A, C), (B, C)] = {
      val cleanup: Process1[(A, C), (B, C)] = curr.disconnect(Kill).flatMap(b => f(b) match {
        case Some(c) => emit((b, c))
        case None => halt
      })
      receive1Or[(A, C), (B, C)](cleanup) { case (a, c) =>
        val (emitted, next) = curr.feed1(a).unemit
        val out = emitAll(emitted).map((_, c))
        next match {
          case h @ Halt(_) => out fby h
          case other => out fby go(other)
        }
      }
    }
    go(p)
  }

  /**
   * Transform `p` to operate on the second element of a pair, passing
   * through the left value with no modifications. Note that this halts
   * whenever `p` halts.
   *
   * @param f function used to convert `B`s generated during cleanup of `p` to pairs
   */
  def liftSecond[A, B, C](f: B => Option[C])(p: Process1[A, B]): Process1[(C, A), (C, B)] =
    lift[(C, A), (A, C)](_.swap) |> liftFirst(f)(p).map(_.swap)

  /**
   * Transform `p` to operate on the left hand side of an `\/`, passing
   * through any values it receives on the right. Note that this halts
   * whenever `p` halts.
   */
  def liftL[A, B, C](p: Process1[A, B]): Process1[A \/ C, B \/ C] = {
    def go(curr: Process1[A,B]): Process1[A \/ C, B \/ C] = {
      receive1Or[A \/ C, B \/ C](curr.disconnect(Kill).map(-\/(_))) {
        case -\/(a) =>
          val (bs, next) = curr.feed1(a).unemit
          val out =  emitAll(bs).map(-\/(_))
          next match {
            case Halt(rsn) => out ++ Halt(rsn)
            case other => out ++ go(other)
          }
        case \/-(c) => emitO(c) ++ go(curr)
      }
    }
    go(p)
  }

  /**
   * Transform `p` to operate on the right hand side of an `\/`, passing
   * through any values it receives on the left. Note that this halts
   * whenever `p` halts.
   */
  def liftR[A, B, C](p: Process1[B, C]): Process1[A \/ B, A \/ C] =
    lift((e: A \/ B) => e.swap) |> liftL(p).map(_.swap)

  /**
   * Lifts Process1 to operate on Left side of `wye`, ignoring any right input.
   * Use `wye.flip` to convert it to right side
   */
  def liftY[I,O](p: Process1[I,O]) : Wye[I,Any,O] = {
    p.step match {
      case Step(Await(_,rcv, cln), cont) =>
        Await(L[I]: Env[I,Any]#Y[I],rcv, cln) onHalt(rsn=> liftY(Halt(rsn) +: cont))

      case Step(emt@Emit(os), cont) =>
        emt onHalt(rsn=> liftY(Halt(rsn) +: cont))

      case hlt@Halt(rsn) => hlt
    }
  }

  /**
   * Maps a running total according to `S` and the input with the function `f`.
   *
   * @example {{{
   * scala> Process("Hello", "World")
   *      |   .mapAccumulate(0)((l, s) => (l + s.length, s.head)).toList
   * res0: List[(Int, Char)] = List((5,H), (10,W))
   * }}}
   * @see [[zipWithScan1]]
   */
  def mapAccumulate[S, A, B](init: S)(f: (S, A) => (S, B)): Process1[A, (S, B)] =
    receive1 { a =>
      val sb = f(init, a)
      emit(sb) ++ mapAccumulate(sb._1)(f)
    }

  /** Emits the greatest element of the input. */
  def maximum[A](implicit A: Order[A]): Process1[A,A] =
    reduce((x, y) => if (A.greaterThan(x, y)) x else y)

  /** Emits the element `a` of the input which yields the greatest value of `f(a)`. */
  def maximumBy[A,B: Order](f: A => B): Process1[A,A] =
    reduce((x, y) => if (Order.orderBy(f).greaterThan(x, y)) x else y)

  /** Emits the greatest value of `f(a)` for each element `a` of the input. */
  def maximumOf[A,B: Order](f: A => B): Process1[A,B] =
    lift(f).maximum

  /** Emits the smallest element of the input. */
  def minimum[A](implicit A: Order[A]): Process1[A,A] =
    reduce((x, y) => if (A.lessThan(x, y)) x else y)

  /** Emits the element `a` of the input which yields the smallest value of `f(a)`. */
  def minimumBy[A,B: Order](f: A => B): Process1[A,A] =
    reduce((x, y) => if (Order.orderBy(f).lessThan(x, y)) x else y)

  /** Emits the smallest value of `f(a)` for each element `a` of the input. */
  def minimumOf[A,B: Order](f: A => B): Process1[A,B] =
    lift(f).minimum

  /**
   * Split the input and send to either `chanL` or `chanR`, halting when
   * either branch halts.
   *
   * @example {{{
   * scala> import scalaz.\/._
   * scala> import process1._
   * scala> Process(left(1), right('a'), left(2), right('b'))
   *      |   .pipe(multiplex(lift(_ * -1), lift(_.toInt))).toList
   * res0: List[Int] = List(-1, 97, -2, 98)
   * }}}
   */
  def multiplex[I, I2, O](chanL: Process1[I, O], chanR: Process1[I2, O]): Process1[I \/ I2, O] =
    (liftL(chanL) pipe liftR(chanR)).map(_.fold(identity, identity))

  /**
   * Emits the sums of prefixes (running totals) of the input elements.
   * The first value emitted will always be zero.
   *
   * @example {{{
   * scala> Process(1, 2, 3).prefixSums.toList
   * res0: List[Int] = List(0, 1, 3, 6)
   *
   * scala> Process[Int]().prefixSums.toList
   * res1: List[Int] = List(0)
   * }}}
   */
  def prefixSums[N](implicit N: Numeric[N]): Process1[N,N] =
    scan(N.zero)(N.plus)

  /**
   * `Process1` form of `List.reduce`.
   *
   * Reduces the elements of this Process using the specified associative binary operator.
   * {{{
   * scala> Process(1, 2, 3, 4).reduce(_ + _).toList
   * res0: List[Int] = List(10)
   *
   * scala> Process(1).reduce(_ + _).toList
   * res1: List[Int] = List(1)
   *
   * scala> Process[Int]().reduce(_ + _).toList
   * res2: List[Int] = List()
   * }}}
   *
   * Unlike `List.reduce` will not fail when Process is empty.
   */
  def reduce[A](f: (A, A) => A): Process1[A, A] =
    scan1(f).last

  /**
   * Like `reduce` only uses `f` to map `A` to `B` and uses Semigroup `M` for
   * associative operation.
   */
  def reduceMap[A, B](f: A => B)(implicit M: Semigroup[B]): Process1[A, B] =
    lift(f).reduceSemigroup(M)

  /** Alias for `[[reduceSemigroup]](M)`. */
  def reduceMonoid[A](implicit M: Monoid[A]): Process1[A, A] =
    reduceSemigroup(M)

  /** Like `reduce` but uses Semigroup `M` for associative operation. */
  def reduceSemigroup[A](implicit M: Semigroup[A]): Process1[A, A] =
    reduce(M.append(_, _))

  /**
   * Repartitions the input with the function `p`. On each step `p` is applied
   * to the input and all elements but the last of the resulting sequence
   * are emitted. The last element is then prepended to the next input using the
   * Semigroup `I`. For example,
   * {{{
   * scala> import scalaz.std.string._
   * scala> Process("Hel", "l", "o Wor", "ld").repartition(_.split(" ")).toList
   * res0: List[String] = List(Hello, World)
   * }}}
   */
  def repartition[I](p: I => IndexedSeq[I])(implicit I: Semigroup[I]): Process1[I, I] = {
    def go(carry: Option[I]): Process1[I, I] =
      receive1Or[I,I](emitAll(carry.toList)) { i =>
        val next = carry.fold(i)(c => I.append(c, i))
        val parts = p(next)
        parts.size match {
          case 0 => go(None)
          case 1 => go(Some(parts.head))
          case _ => emitAll(parts.init) ++ go(Some(parts.last))
        }
      }
    go(None)
  }

  /**
   * Repartitions the input with the function `p`. On each step `p` is applied
   * to the input and the first element of the resulting tuple is emitted if it
   * is `Some(x)`. The second element is then prepended to the next input using
   * the Semigroup `I`. In comparison to `repartition` this allows to emit
   * single inputs without prepending them to the next input.
   */
  def repartition2[I](p: I => (Option[I], Option[I]))(implicit I: Semigroup[I]): Process1[I,I] = {
    def go(carry: Option[I]): Process1[I,I] =
      receive1Or[I,I](emitAll(carry.toList)) { i =>
        val next = carry.fold(i)(c => I.append(c, i))
        val (fst, snd) = p(next)
        fst.fold(go(snd))(head => emit(head) ++ go(snd))
      }
    go(None)
  }

  /** Throws any input exceptions and passes along successful results. */
  def rethrow[A]: Process1[Throwable \/ A, A] =
    id[Throwable \/ A].flatMap {
      case -\/(err) => throw err
      case \/-(a)   => emit(a)
    }

  def stateScan[S, A, B](init: S)(f: A => State[S, B]): Process1[A, B] = {
    await1[A] flatMap { a =>
      val (s, b) = f(a) run init
      emit(b) ++ stateScan(s)(f)
    }
  }

  /**
   * Similar to List.scan.
   * Produces a process of `B` containing cumulative results of applying the operator to Process of `A`.
   * It will always emit `z`, even when the Process of `A` is empty
   */
  def scan[A, B](z: B)(f: (B, A) => B): Process1[A, B] =
    emit(z) ++ receive1(a => scan(f(z, a))(f))

  /**
   * Similar to `scan`, but unlike it it won't emit the `z` even when there is no input of `A`.
   * {{{
   * scala> Process(1, 2, 3, 4).scan1(_ + _).toList
   * res0: List[Int] = List(1, 3, 6, 10)
   *
   * scala> Process(1).scan1(_ + _).toList
   * res1: List[Int] = List(1)
   *
   * scala> Process[Int]().scan1(_ + _).toList
   * res2: List[Int] = List()
   * }}}
   */
  def scan1[A](f: (A, A) => A): Process1[A, A] =
    receive1(a => scan(a)(f))

  /**
   * Like `scan1` only uses `f` to map `A` to `B` and uses Semigroup `M` for
   * associative operation.
   */
  def scan1Map[A, B](f: A => B)(implicit M: Semigroup[B]): Process1[A, B] =
    lift(f).scanSemigroup(M)

  /** Alias for `[[scanSemigroup]](M)`. */
  def scan1Monoid[A](implicit M: Monoid[A]): Process1[A, A] =
    scanSemigroup(M)

  /**
   * Like `scan` only uses `f` to map `A` to `B` and uses Monoid `M` for associative operation
   */
  def scanMap[A, B](f: A => B)(implicit M: Monoid[B]): Process1[A, B] =
    lift(f).scanMonoid(M)

  /**
   * Like `scan` but uses Monoid for associative operation
   */
  def scanMonoid[A](implicit M: Monoid[A]): Process1[A, A] =
    scan(M.zero)(M.append(_, _))

  /** Like `scan1` but uses Semigroup `M` for associative operation. */
  def scanSemigroup[A](implicit M: Semigroup[A]): Process1[A, A] =
    scan1(M.append(_, _))

  /**
   * Emit the given values, then echo the rest of the input.
   *
   * @example {{{
   * scala> Process(3, 4).shiftRight(1, 2).toList
   * res0: List[Int] = List(1, 2, 3, 4)
   * }}}
   */
  def shiftRight[I](head: I*): Process1[I, I] =
    emitAll(head) ++ id

  /**
   * Reads a single element of the input, emits nothing, then halts.
   *
   * @example {{{
   * scala> import process1._
   * scala> Process(1, 2, 3).pipe(skip ++ id).toList
   * res0: List[Int] = List(2, 3)
   * }}}
   */
  def skip: Process1[Any, Nothing] =
    receive1(_ => halt)

  /**
   * Groups inputs in fixed size chunks by passing a "sliding window"
   * of size `n` over them. If the input contains less than or equal to
   * `n` elements, only one chunk of this size will be emitted.
   *
   * @example {{{
   * scala> Process(1, 2, 3, 4).sliding(2).toList
   * res0: List[Vector[Int]] = List(Vector(1, 2), Vector(2, 3), Vector(3, 4))
   * }}}
   * @throws IllegalArgumentException if `n` <= 0
   */
  def sliding[I](n: Int): Process1[I, Vector[I]] = {
    require(n > 0, "window size must be > 0, was: " + n)
    def go(window: Vector[I]): Process1[I, Vector[I]] =
      emit(window) ++ receive1(i => go(window.tail :+ i))
    chunk(n).once.flatMap(go)
  }

  /**
   * Break the input into chunks where the delimiter matches the predicate.
   * The delimiter does not appear in the output. Two adjacent delimiters in the
   * input result in an empty chunk in the output.
   */
  def split[I](f: I => Boolean): Process1[I, Vector[I]] = {
    def go(acc: Vector[I]): Process1[I, Vector[I]] =
      receive1Or[I, Vector[I]](emit(acc)) { i =>
        if (f(i)) emit(acc) ++ go(Vector())
        else go(acc :+ i)
      }
    go(Vector())
  }

  /**
   * Break the input into chunks where the input is equal to the given delimiter.
   * The delimiter does not appear in the output. Two adjacent delimiters in the
   * input result in an empty chunk in the output.
   */
  def splitOn[I: Equal](i: I): Process1[I, Vector[I]] =
    split(_ === i)

  /**
   * Breaks the input into chunks that alternatively satisfy and don't satisfy
   * the predicate `f`.
   *
   * @example {{{
   * scala> Process(1, 2, -3, -4, 5, 6).splitWith(_ < 0).toList
   * res0: List[Vector[Int]] = List(Vector(1, 2), Vector(-3, -4), Vector(5, 6))
   * }}}
   */
  def splitWith[I](f: I => Boolean): Process1[I, Vector[I]] = {
    def go(acc: Vector[I], last: Boolean): Process1[I, Vector[I]] =
      receive1Or[I, Vector[I]](emit(acc)) { i =>
         val cur = f(i)
         if (cur == last) go(acc :+ i, cur)
         else emit(acc) ++ go(Vector(i), cur)
      }
    receive1(i => go(Vector(i), f(i)))
  }

  /** Remove any `None` inputs. */
  def stripNone[A]: Process1[Option[A], A] =
    collect { case Some(a) => a }

  /**
   * Emits the sum of all input elements or zero if the input is empty.
   *
   * @example {{{
   * scala> Process(1, 2, 3).sum.toList
   * res0: List[Int] = List(6)
   *
   * scala> Process[Int]().sum.toList
   * res1: List[Int] = List(0)
   * }}}
   */
  def sum[N](implicit N: Numeric[N]): Process1[N,N] =
    fold(N.zero)(N.plus)

  /**
   * Emits all elements of the input except the first one.
   *
   * @example {{{
   * scala> Process(1, 2, 3).tail.toList
   * res0: List[Int] = List(2, 3)
   *
   * scala> Process[Int]().tail.toList
   * res1: List[Int] = List()
   * }}}
   */
  def tail[I]: Process1[I, I] =
    receive1(_ => id)

  /** Passes through `n` elements of the input, then halts. */
  def take[I](n: Int): Process1[I, I] =
    if (n <= 0) halt
    else await1[I] ++ take(n - 1)

  /** Emits the last `n` elements of the input. */
  def takeRight[I](n: Int): Process1[I, I] = {
    def go(acc: Vector[I]): Process1[I, I] =
      receive1Or[I, I](emitAll(acc))(i => go(acc.tail :+ i))
    if (n <= 0) halt
    else chunk(n).once.flatMap(go)
  }

  /** Passes through elements of the input as long as the predicate is true, then halts. */
  def takeWhile[I](f: I => Boolean): Process1[I, I] =
    receive1 (i => if (f(i)) emit(i) ++ takeWhile(f) else halt)

  /** Like `takeWhile`, but emits the first value which tests false. */
  def takeThrough[I](f: I => Boolean): Process1[I, I] =
    receive1 (i => if (f(i)) emit(i) ++ takeThrough(f) else emit(i))

  /** Wraps all inputs in `Some`, then outputs a single `None` before halting. */
  def terminated[A]: Process1[A, Option[A]] =
     lift[A, Option[A]](Some(_)) onComplete emit(None)

  /**
   * Ungroups chunked input.
   *
   * @example {{{
   * scala> Process(Seq(1, 2), Seq(3)).pipe(process1.unchunk).toList
   * res0: List[Int] = List(1, 2, 3)
   * }}}
   */
  def unchunk[I]: Process1[Seq[I], I] =
    id[Seq[I]].flatMap(emitAll)

  /** Zips the input with an index of type `Int`. */
  def zipWithIndex[A]: Process1[A,(A,Int)] =
    zipWithIndex[A,Int]

  /** Zips the input with an index of type `N`. */
  def zipWithIndex[A,N](implicit N: Numeric[N]): Process1[A,(A,N)] =
    zipWithState(N.zero)((_, n) => N.plus(n, N.one))

  /**
   * Zips every element with its previous element wrapped into `Some`.
   * The first element is zipped with `None`.
   */
  def zipWithPrevious[I]: Process1[I,(Option[I],I)] =
    zipWithState[I,Option[I]](None)((cur, _) => Some(cur)).map(_.swap)

  /**
   * Zips every element with its next element wrapped into `Some`.
   * The last element is zipped with `None`.
   */
  def zipWithNext[I]: Process1[I,(I,Option[I])] = {
    def go(prev: I): Process1[I,(I,Option[I])] =
      receive1Or[I,(I,Option[I])](emit((prev, None)))(i => emit((prev, Some(i))) ++ go(i))
    receive1(go)
  }

  /**
   * Zips every element with its previous and next elements wrapped into `Some`.
   * The first element is zipped with `None` as the previous element,
   * the last element is zipped with `None` as the next element.
   */
  def zipWithPreviousAndNext[I]: Process1[I,(Option[I],I,Option[I])] =
    zipWithPrevious.pipe(zipWithNext[(Option[I],I)]).map {
      case ((previous, current), None)            =>  (previous, current, None)
      case ((previous, current), Some((_, next))) =>  (previous, current, Some(next))
    }

  /**
   * Zips the input with a running total according to `B`, up to but not including the
   * current element. Thus the initial `z` value is the first emitted to the output:
   *
   * {{{
   * scala> Process("uno", "dos", "tres", "cuatro").zipWithScan(0)(_.length + _).toList
   * res0: List[(String,Int)] = List((uno,0), (dos,3), (tres,6), (cuatro,10))
   * }}}
   *
   * @see [[zipWithScan1]]
   */
  def zipWithScan[A,B](z: B)(f: (A,B) => B): Process1[A,(A,B)] =
   zipWithState(z)(f)

  /**
   * Zips the input with a running total according to `B`, up to and including the
   * current element. Thus the initial `z` value is not emitted to the output:
   *
   * {{{
   * scala> Process("uno", "dos", "tres", "cuatro").zipWithScan1(0)(_.length + _).toList
   * res0: List[(String,Int)] = List((uno,3), (dos,6), (tres,10), (cuatro,16))
   * }}}
   *
   * @see [[zipWithScan]]
   */
  def zipWithScan1[A,B](z: B)(f: (A,B) => B): Process1[A,(A,B)] =
    receive1 { a =>
      val z2 = f(a,z)
      emit((a,z2)) ++ zipWithScan1(z2)(f)
    }

  /** Zips the input with state that begins with `z` and is updated by `next`. */
  def zipWithState[A,B](z: B)(next: (A, B) => B): Process1[A,(A,B)] =
    receive1(a => emit((a, z)) ++ zipWithState(next(a, z))(next))

  object Await1 {
    /** deconstruct for `Await` directive of `Process1` */
    def unapply[I, O](self: Process1[I, O]): Option[EarlyCause \/ I => Process1[I, O]] = self match {
      case Await(_, rcv,_) => Some((r:EarlyCause\/ I) => Try(rcv(r).run))
      case _             => None
    }

  }



}

final class Process1Syntax[I, O](val self: Process1[I, O]) extends AnyVal {

  /** Apply this `Process` to an `Iterable`. */
  def apply(input: Iterable[I]): IndexedSeq[O] =
    Process(input.toSeq: _*).pipe(self).toIndexedSeq

  /**
   * Transform `self` to operate on the left hand side of an `\/`, passing
   * through any values it receives on the right. Note that this halts
   * whenever `self` halts.
   */
  def liftL[I2]: Process1[I \/ I2, O \/ I2] =
    process1.liftL(self)

  /**
   * Transform `self` to operate on the right hand side of an `\/`, passing
   * through any values it receives on the left. Note that this halts
   * whenever `self` halts.
   */
  def liftR[I0]: Process1[I0 \/ I, I0 \/ O] =
    process1.liftR(self)

  /**
   * Feed a single input to this `Process1`.
   */
  def feed1(i: I): Process1[I,O] =
    process1.feed1(i)(self)

  /** Transform the input of this `Process1`. */
  def contramap[I0](f: I0 => I): Process1[I0, O] =
    process1.lift(f).pipe(self)

}

private[codata] trait Process1Ops[+F[_],+O] {
  self: Process[F,O] =>

  /** Alias for `this |> [[process1.awaitOption]]`. */
  def awaitOption: Process[F,Option[O]] =
    this |> process1.awaitOption

  /** Alias for `this |> [[process1.buffer]](n)`. */
  def buffer(n: Int): Process[F,O] =
    this |> process1.buffer(n)

  /** Alias for `this |> [[process1.bufferAll]]`. */
  def bufferAll: Process[F,O] =
    this |> process1.bufferAll

  /** Alias for `this |> [[process1.bufferBy]](f)`. */
  def bufferBy(f: O => Boolean): Process[F,O] =
    this |> process1.bufferBy(f)

  /** Alias for `this |> [[process1.chunk]](n)`. */
  def chunk(n: Int): Process[F,Vector[O]] =
    this |> process1.chunk(n)

  /** Alias for `this |> [[process1.chunkAll]]`. */
  def chunkAll: Process[F,Vector[O]] =
    this |> process1.chunkAll

  /** Alias for `this |> [[process1.chunkBy]](f)`. */
  def chunkBy(f: O => Boolean): Process[F,Vector[O]] =
    this |> process1.chunkBy(f)

  /** Alias for `this |> [[process1.chunkBy2]](f)`. */
  def chunkBy2(f: (O, O) => Boolean): Process[F,Vector[O]] =
    this |> process1.chunkBy2(f)

  /** Alias for `this |> [[process1.collect]](pf)`. */
  def collect[O2](pf: PartialFunction[O,O2]): Process[F,O2] =
    this |> process1.collect(pf)

  /** Alias for `this |> [[process1.collectFirst]](pf)`. */
  def collectFirst[O2](pf: PartialFunction[O,O2]): Process[F,O2] =
    this |> process1.collectFirst(pf)

  /** Alias for `this |> [[process1.delete]](f)`. */
  def delete(f: O => Boolean): Process[F,O] =
    this |> process1.delete(f)

  /** Alias for `this |> [[process1.distinctConsecutive]]`. */
  def distinctConsecutive[O2 >: O](implicit O2: Equal[O2]): Process[F,O2] =
    this |> process1.distinctConsecutive(O2)

  /** Alias for `this |> [[process1.distinctConsecutiveBy]](f)`. */
  def distinctConsecutiveBy[B: Equal](f: O => B): Process[F,O] =
    this |> process1.distinctConsecutiveBy(f)

  /** Alias for `this |> [[process1.drop]](n)`. */
  def drop(n: Int): Process[F,O] =
    this |> process1.drop(n)

  /** Alias for `this |> [[process1.dropLast]]`. */
  def dropLast: Process[F,O] =
    this |> process1.dropLast

  /** Alias for `this |> [[process1.dropLastIf]](p)`. */
  def dropLastIf(p: O => Boolean): Process[F,O] =
    this |> process1.dropLastIf(p)

  /** Alias for `this |> [[process1.dropRight]](n)`. */
  def dropRight(n: Int): Process[F,O] =
    this |> process1.dropRight(n)

  /** Alias for `this |> [[process1.dropWhile]](f)`. */
  def dropWhile(f: O => Boolean): Process[F,O] =
    this |> process1.dropWhile(f)

  /** Alias for `this |> [[process1.exists]](f)` */
  def exists(f: O => Boolean): Process[F,Boolean] =
    this |> process1.exists(f)

  /** Alias for `this |> [[process1.filter]](f)`. */
  def filter(f: O => Boolean): Process[F,O] =
    this |> process1.filter(f)

  /** Alias for `this |> [[process1.filterBy2]](f)`. */
  def filterBy2(f: (O, O) => Boolean): Process[F,O] =
    this |> process1.filterBy2(f)

  /** Alias for `this |> [[process1.find]](f)` */
  def find(f: O => Boolean): Process[F,O] =
    this |> process1.find(f)

  /** Alias for `this |> [[process1.forall]](f)` */
  def forall(f: O => Boolean): Process[F,Boolean] =
    this |> process1.forall(f)

  /** Alias for `this |> [[process1.fold]](b)(f)`. */
  def fold[B](b: B)(f: (B, O) => B): Process[F, B] =
    this |> process1.fold(b)(f)

  /** Alias for `this |> [[process1.foldMap]](f)(M)`. */
  def foldMap[M](f: O => M)(implicit M: Monoid[M]): Process[F,M] =
    this |> process1.foldMap(f)(M)

  /** Alias for `this |> [[process1.foldMonoid]](M)` */
  def foldMonoid[O2 >: O](implicit M: Monoid[O2]): Process[F,O2] =
    this |> process1.foldMonoid(M)

  /** Alias for `this |> [[process1.foldSemigroup]](M)`. */
  def foldSemigroup[O2 >: O](implicit M: Semigroup[O2]): Process[F,O2] =
    this |> process1.foldSemigroup(M)

  /** Alias for `this |> [[process1.fold1]](f)`. */
  def fold1[O2 >: O](f: (O2,O2) => O2): Process[F,O2] =
    this |> process1.fold1(f)

  /** Alias for `this |> [[process1.fold1Map]](f)(M)`. */
  def fold1Map[M](f: O => M)(implicit M: Monoid[M]): Process[F,M] =
    this |> process1.fold1Map(f)(M)

  /** Alias for `this |> [[process1.fold1Monoid]](M)` */
  def fold1Monoid[O2 >: O](implicit M: Monoid[O2]): Process[F,O2] =
    this |> process1.fold1Monoid(M)

  /** Alias for `this |> [[process1.intersperse]](sep)`. */
  def intersperse[O2>:O](sep: O2): Process[F,O2] =
    this |> process1.intersperse(sep)

  /** Alias for `this |> [[process1.last]]`. */
  def last: Process[F,O] =
    this |> process1.last

  /** Alias for `this |> [[process1.last]]`. */
  def lastOr[O2 >: O](o: => O2): Process[F,O2] =
    this |> process1.lastOr(o)

  /** Alias for `this |> [[process1.mapAccumulate]](s)(f)`. */
  def mapAccumulate[S, B](s: S)(f: (S, O) => (S, B)): Process[F, (S, B)] =
    this |> process1.mapAccumulate(s)(f)

  /** Alias for `this |> [[process1.maximum]]`. */
  def maximum[O2 >: O](implicit O2: Order[O2]): Process[F,O2] =
    this |> process1.maximum(O2)

  /** Alias for `this |> [[process1.maximumBy]](f)`. */
  def maximumBy[B: Order](f: O => B): Process[F,O] =
    this |> process1.maximumBy(f)

  /** Alias for `this |> [[process1.maximumOf]](f)`. */
  def maximumOf[B: Order](f: O => B): Process[F,B] =
    this |> process1.maximumOf(f)

  /** Alias for `this |> [[process1.minimum]]`. */
  def minimum[O2 >: O](implicit O2: Order[O2]): Process[F,O2] =
    this |> process1.minimum(O2)

  /** Alias for `this |> [[process1.minimumBy]](f)`. */
  def minimumBy[B: Order](f: O => B): Process[F,O] =
    this |> process1.minimumBy(f)

  /** Alias for `this |> [[process1.minimumOf]](f)`. */
  def minimumOf[B: Order](f: O => B): Process[F,B] =
    this |> process1.minimumOf(f)

  /** Alias for `this |> [[Process.await1]]`. */
  def once: Process[F,O] =
    this |> Process.await1

  /** Alias for `this |> [[process1.prefixSums]]` */
  def prefixSums[O2 >: O](implicit N: Numeric[O2]): Process[F,O2] =
    this |> process1.prefixSums(N)

  /** Alias for `this |> [[process1.reduce]](f)`. */
  def reduce[O2 >: O](f: (O2,O2) => O2): Process[F,O2] =
    this |> process1.reduce(f)

  /** Alias for `this |> [[process1.reduceMap]](f)(M)`. */
  def reduceMap[M](f: O => M)(implicit M: Semigroup[M]): Process[F,M] =
    this |> process1.reduceMap(f)(M)

  /** Alias for `this |> [[process1.reduceMonoid]](M)`. */
  def reduceMonoid[O2 >: O](implicit M: Monoid[O2]): Process[F,O2] =
    this |> process1.reduceMonoid(M)

  /** Alias for `this |> [[process1.reduceSemigroup]](M)`. */
  def reduceSemigroup[O2 >: O](implicit M: Semigroup[O2]): Process[F,O2] =
    this |> process1.reduceSemigroup(M)

  /** Alias for `this |> [[process1.repartition]](p)(S)` */
  def repartition[O2 >: O](p: O2 => IndexedSeq[O2])(implicit S: Semigroup[O2]): Process[F,O2] =
    this |> process1.repartition(p)(S)

  /** Alias for `this |> [[process1.repartition2]](p)(S)` */
  def repartition2[O2 >: O](p: O2 => (Option[O2], Option[O2]))(implicit S: Semigroup[O2]): Process[F,O2] =
    this |> process1.repartition2(p)(S)

  /** Alias for `this |> [[process1.scan]](b)(f)`. */
  def scan[B](b: B)(f: (B,O) => B): Process[F,B] =
    this |> process1.scan(b)(f)

  /** Alias for `this |> [[process1.stateScan]](init)(f)`. */
  def stateScan[S, B](init: S)(f: O => State[S, B]): Process[F, B] =
    this |> process1.stateScan(init)(f)

  /** Alias for `this |> [[process1.scanMap]](f)(M)`. */
  def scanMap[M](f: O => M)(implicit M: Monoid[M]): Process[F,M] =
    this |> process1.scanMap(f)(M)

  /** Alias for `this |> [[process1.scanMonoid]](M)`. */
  def scanMonoid[O2 >: O](implicit M: Monoid[O2]): Process[F,O2] =
    this |> process1.scanMonoid(M)

  /** Alias for `this |> [[process1.scanSemigroup]](M)`. */
  def scanSemigroup[O2 >: O](implicit M: Semigroup[O2]): Process[F,O2] =
    this |> process1.scanSemigroup(M)

  /** Alias for `this |> [[process1.scan1]](f)`. */
  def scan1[O2 >: O](f: (O2,O2) => O2): Process[F,O2] =
    this |> process1.scan1(f)

  /** Alias for `this |> [[process1.scan1Map]](f)(M)`. */
  def scan1Map[M](f: O => M)(implicit M: Semigroup[M]): Process[F,M] =
    this |> process1.scan1Map(f)(M)

  /** Alias for `this |> [[process1.scan1Monoid]](M)`. */
  def scan1Monoid[O2 >: O](implicit M: Monoid[O2]): Process[F,O2] =
    this |> process1.scan1Monoid(M)

  /** Alias for `this |> [[process1.shiftRight]](head)` */
  def shiftRight[O2 >: O](head: O2*): Process[F,O2] =
    this |> process1.shiftRight(head: _*)

  /** Alias for `this |> [[process1.sliding]](n)`. */
  def sliding(n: Int): Process[F,Vector[O]] =
    this |> process1.sliding(n)

  /** Alias for `this |> [[process1.split]](f)` */
  def split(f: O => Boolean): Process[F,Vector[O]] =
    this |> process1.split(f)

  /** Alias for `this |> [[process1.splitOn]](p)` */
  def splitOn[P >: O](p: P)(implicit P: Equal[P]): Process[F,Vector[P]] =
    this |> process1.splitOn(p)

  /** Alias for `this |> [[process1.splitWith]](f)` */
  def splitWith(f: O => Boolean): Process[F,Vector[O]] =
    this |> process1.splitWith(f)

  /** Alias for `this |> [[process1.sum]]` */
  def sum[O2 >: O](implicit N: Numeric[O2]): Process[F,O2] =
    this |> process1.sum(N)

  /** Alias for `this |> [[process1.tail]]`. */
  def tail: Process[F,O] =
    this |> process1.tail

  /** Alias for `this |> [[process1.take]](n)`. */
  def take(n: Int): Process[F,O] =
    this |> process1.take(n)

  /** Alias for `this |> [[process1.takeRight]](n)`. */
  def takeRight(n: Int): Process[F,O] =
    this |> process1.takeRight(n)

  /** Alias for `this |> [[process1.takeThrough]](f)`. */
  def takeThrough(f: O => Boolean): Process[F,O] =
    this |> process1.takeThrough(f)

  /** Alias for `this |> [[process1.takeWhile]](f)`. */
  def takeWhile(f: O => Boolean): Process[F,O] =
    this |> process1.takeWhile(f)

  /** Alias for `this |> [[process1.terminated]]`. */
  def terminated: Process[F,Option[O]] =
    this |> process1.terminated

  /** Alias for `this |> [[process1.zipWithIndex[A]*]]`. */
  def zipWithIndex: Process[F,(O,Int)] =
    this |> process1.zipWithIndex

  /** Alias for `this |> [[process1.zipWithIndex[A,N]*]]`. */
  def zipWithIndex[N: Numeric]: Process[F,(O,N)] =
    this |> process1.zipWithIndex[O,N]

  /** Alias for `this |> [[process1.zipWithPrevious]]`. */
  def zipWithPrevious: Process[F,(Option[O],O)] =
    this |> process1.zipWithPrevious

  /** Alias for `this |> [[process1.zipWithNext]]`. */
  def zipWithNext: Process[F,(O,Option[O])] =
    this |> process1.zipWithNext

  /** Alias for `this |> [[process1.zipWithPreviousAndNext]]`. */
  def zipWithPreviousAndNext: Process[F,(Option[O],O,Option[O])] =
    this |> process1.zipWithPreviousAndNext

  /** Alias for `this |> [[process1.zipWithScan]](z)(next)`. */
  def zipWithScan[B](z: B)(next: (O, B) => B): Process[F,(O,B)] =
    this |> process1.zipWithScan(z)(next)

  /** Alias for `this |> [[process1.zipWithScan]](z)(next)`. */
  def zipWithScan1[B](z: B)(next: (O, B) => B): Process[F,(O,B)] =
    this |> process1.zipWithScan1(z)(next)

  /** Alias for `this |> [[process1.zipWithState]](z)(next)`. */
  def zipWithState[B](z: B)(next: (O, B) => B): Process[F,(O,B)] =
    this |> process1.zipWithState(z)(next)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy