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

axle.Permutations.scala Maven / Gradle / Ivy

The newest version!
package axle

import scala.Stream.cons
import scala.Stream.empty

import spire.implicits.IntAlgebra
import spire.implicits.eqOps

/**
 * Based on Python's itertools.permutations function
 *
 * http://docs.python.org/library/itertools.html#itertools.permutations
 *
 * Permutations("ABCD".toIndexedSeq, 2)
 * Permutations(0 until 3)
 *
 */

object Permutations {

  def apply[E: Manifest](pool: Seq[E], r: Int): Permutations[E] = new Permutations(pool, r)
}

class Permutations[E: Manifest](_pool: Seq[E], r: Int) extends Iterable[IndexedSeq[E]] {

  val pool = _pool.toArray
  val n = pool.length
  val untilN = (0 until n).toArray
  val untilR = (0 until r).toArray

  override def size: Int = if (r >= 0 && r <= n) (n.factorial / (n - r).factorial) else 0

  private[this] def loop2branchTrue(
    indices0: Array[Int],
    cycles0: Array[Int],
    i0: Int): (Array[Int], Array[Int]) = {
    (indices0(0 until i0) ++ indices0(i0 + 1 until n) ++ indices0(i0 until i0 + 1),
      cycles0.updated(i0, n - i0)
    )
  }

  private[this] def loop2branchFalse(
    indices0: Array[Int],
    cycles0: Array[Int],
    i0: Int): (Array[E], Array[Int]) = {
    val indices1 = indices0.swap(indices0((n - cycles0(i0)) % n), indices0(i0))
    (untilR.map(indices1).map(pool), indices1)
  }

  private[this] def loop2branch(
    indices0: Array[Int],
    cycles0: Array[Int],
    i0: Int): (Option[IndexedSeq[E]], Array[Int], Array[Int], Int, Boolean) =
    if (cycles0(i0) === 0) {
      val (indices1, cycles1) = loop2branchTrue(indices0, cycles0, i0)
      (None, indices1, cycles1, i0 - 1, false)
    } else {
      val (result2, indices2) = loop2branchFalse(indices0, cycles0, i0)
      (Some(result2), indices2, cycles0, i0, true)
    }

  private[this] def loop2(
    indices0: Array[Int],
    cycles0: Array[Int],
    i0: Int,
    broken0: Boolean): (Stream[IndexedSeq[E]], Array[Int], Array[Int], Boolean) =
    if (i0 >= 0 && !broken0) {
      val cycles1 = cycles0.updated(i0, cycles0(i0) - 1)
      val (result, indices2, cycles2, i2, broken2) = loop2branch(indices0, cycles1, i0)
      val (subStream, indices3, cycles3, broken3) = loop2(indices2, cycles2, i2, broken2)
      (if (result.isDefined) cons(result.get, subStream) else subStream, indices3, cycles3, broken3)
    } else {
      (empty, indices0, cycles0, broken0)
    }

  private[this] def loop1(indices: Array[Int], cycles: Array[Int]): Stream[IndexedSeq[E]] = {
    val (subStream, indicesOut, cyclesOut, broken) = loop2(indices, cycles, r - 1, false)
    subStream ++ (if (broken) loop1(indicesOut, cyclesOut) else empty)
  }

  lazy val result: Stream[IndexedSeq[E]] = if (r <= n && n > 0) {
    val indices = untilN
    val head = untilR.map(indices(_)).map(pool(_))
    cons(head, loop1(indices, n.until(n - r, -1).toArray))
  } else {
    empty
  }

  def iterator: Iterator[IndexedSeq[E]] = result.iterator

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy