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

io.reactors.common.Conc.scala Maven / Gradle / Ivy

package io.reactors.common



import io.reactors.Arrayable
import scala.annotation.unchecked
import scala.annotation.tailrec
import scala.annotation.switch
import scala.reflect.ClassTag
import scala.runtime.ObjectRef



sealed trait Conc[@specialized(Int, Long, Float, Double) +T] {
  def level: Int
  def size: Int
  def left: Conc[T]
  def right: Conc[T]
  def normalized = this
}


case class <>[+T](left: Conc[T], right: Conc[T]) extends Conc[T] {
  val level = 1 + math.max(left.level, right.level)
  val size = left.size + right.size
}


object Conc {

  sealed trait Leaf[T] extends Conc[T] {
    def left = unsupported("Leaves do not have children.")
    def right = unsupported("Leaves do not have children.")
  }
  
  case object Empty extends Leaf[Nothing] {
    def level = 0
    def size = 0
  }
  
  class Single[@specialized(Int, Long, Float, Double) T](val x: T)
  extends Leaf[T] {
    def level = 0
    def size = 1
    override def toString = s"Single($x)"
  }
  
  class Chunk[@specialized(Int, Long, Float, Double) T](
    val array: Array[T], val size: Int, val k: Int
  ) extends Leaf[T] {
    def level = 0
    override def toString = s"Chunk(${array.mkString("", ", ", "")}; $size; $k)"
  }

  def reverseQueueForeach[
    @specialized(Int, Long, Float, Double) T,
    @specialized(Int, Long, Float, Double, Unit) U
  ](xs: Conc[T], f: T => U): Unit = (xs: @unchecked) match {
    case left <> right =>
      reverseQueueForeach(right, f)
      reverseQueueForeach(left, f)
    case s: Single[T] @unchecked =>
      f(s.x)
    case Empty =>
    case ConcRope.Append(left, right) =>
      reverseQueueForeach(right, f)
      reverseQueueForeach(left, f)
    case ConcRope.Prepend(left, right) =>
      reverseQueueForeach(right, f)
      reverseQueueForeach(left, f)
    case _ =>
      invalid("All cases should have been covered: " + xs.getClass)
  }

  def reverseQueueApply[@specialized(Int, Long, Float, Double) T](
    xs: Conc[T], i: Int
  ): T = {
    (xs: @unchecked) match {
      case _ <> right if i < right.size =>
        reverseQueueApply(right, i)
      case left <> right =>
        reverseQueueApply(left, i - right.size)
      case s: Single[T] @unchecked => s.x
      case ConcRope.Append(_, right) if i < right.size =>
        reverseQueueApply(right, i)
      case ConcRope.Append(left, right) =>
        reverseQueueApply(left, i - right.size)
      case ConcRope.Prepend(_, right) if i < right.size =>
        reverseQueueApply(right, i)
      case ConcRope.Prepend(left, right) =>
        reverseQueueApply(left, i - right.size)
      case _ =>
        invalid("All cases should have been covered: " + xs.getClass)
    }
  }

  class Queue[@specialized(Int, Long, Float, Double) T](
    private[reactors] val left: Conc[T],
    private[reactors] val right: Conc[T]
  ) {
    import ConcRope.Append
    import ConcRope.Prepend

    def this() = this(Empty, Empty)

    def size: Int = left.size + right.size

    def isEmpty: Boolean = size == 0

    def head: T = {
      @tailrec def find(c: Conc[T]): T = c match {
        case Append(l, r) => find(r)
        case Prepend(l, r) => find(r)
        case l <> r => find(r)
        case s: Single[T] @unchecked => s.x
      }
      if (size == 0) throw new IllegalStateException(".head")
      if (right.size > 0) find(right)
      else find(left)
    }

    def foreach[@specialized(Int, Long, Float, Double, Unit) U](f: T => U): Unit = {
      reverseQueueForeach(right, f)
      reverseQueueForeach(left, f)
    }

    def last: T = {
      @tailrec def find(c: Conc[T]): T = c match {
        case Append(l, r) => find(l)
        case Prepend(l, r) => find(l)
        case l <> r => find(l)
        case s: Single[T] @unchecked => s.x
      }
      if (size == 0) throw new IllegalStateException(".last")
      if (left.size > 0) find(left)
      else find(right)
    }

    def apply(idx: Int): T = {
      if (idx < 0 || idx >= size) throw new IndexOutOfBoundsException(s"$idx")
      if (idx < right.size) reverseQueueApply(right, idx)
      else reverseQueueApply(left, idx - right.size)
    }

    def enqueue(x: T): Queue[T] = {
      new Queue(left, ConcRope.append(right, new Single(x)))
    }

    def dequeue(): Queue[T] = {
      if (size == 0) throw new IllegalStateException(".dequeue")
      if (left.size > 0) new Queue(ConcRope.unprependDirect(left), right)
      else {
        right.normalized match {
          case l <> r =>
            new Queue(ConcRope.unprependDirect(l), r)
          case s: Single[T] @unchecked =>
            new Queue(Empty, Empty)
        }
      }
    }

    def toArray(implicit a: Arrayable[T]): Array[T] = {
      val array = a.newRawArray(size)
      var i = 0
      for (x <- this) {
        array(i) = x
        i += 1
      }
      array
    }
  }

}


sealed abstract class ConcRope[+T] extends Conc[T]


object ConcRope {
  import Conc._

  case class Append[+T](left: Conc[T], right: Conc[T]) extends ConcRope[T] {
    val level = 1 + math.max(left.level, right.level)
    val size = left.size + right.size
    override def normalized = wrapAppend(this, Conc.Empty)
  }

  @tailrec
  def wrapAppend[T](xs: Conc[T], ys: Conc[T]): Conc[T] = {
    (xs: @unchecked) match {
      case Append(ws, zs) => wrapAppend(ws, zs <> ys)
      case xs => xs <> ys
    }
  }

  def append[T](xs: Conc[T], ys: Leaf[T]): Conc[T] = (xs: @unchecked) match {
    case xs: Append[T] => appendRec(xs, ys)
    case _ <> _ => new Append(xs, ys)
    case Empty => ys
    case xs: Leaf[T] => new <>(xs, ys)
    case xs: Prepend[T] => append(xs.normalized, ys)
  }

  @tailrec
  private def appendRec[T](xs: Append[T], ys: Conc[T]): Conc[T] = {
    if (xs.right.level > ys.level) new Append(xs, ys)
    else {
      val zs = new <>(xs.right, ys)
      xs.left match {
        case ws @ Append(_, _) => appendRec(ws, zs)
        case ws if ws.level <= zs.level => ws <> zs // note: probably can be just new <>
        case ws => new Append(ws, zs)
      }
    }
  }

  case class Prepend[+T](left: Conc[T], right: Conc[T]) extends ConcRope[T] {
    val level = 1 + math.max(left.level, right.level)
    val size = left.size + right.size
    override def normalized = wrapPrepend(Conc.Empty, this)
  }

  @tailrec
  def wrapPrepend[T](xs: Conc[T], ys: Conc[T]): Conc[T] = {
    (ys: @unchecked) match {
      case Prepend(zs, ws) => wrapPrepend(xs <> zs, ws)
      case ys => xs <> ys
    }
  }

  def unprependDirect[T](xs: Conc[T]): Conc[T] = {
    def unwind(left: Conc[T], acc: Conc[T]): Conc[T] = {
      (left: @unchecked) match {
        case zs: Single[T] =>
          acc
        case zs: Chunk[T] =>
          if (zs.size > 1) {
            val nxs = Prepend(new Chunk(zs.array.tail, zs.size - 1, zs.k), acc)
            nxs
          } else {
            acc
          }
        case zs: <>[T] =>
          val left <> right = ConcUtils.shakeRight(zs)
          unwind(left, Prepend(right, acc))
      }
    }
    (xs: @unchecked) match {
      case Prepend(zs, ws) =>
        unwind(zs, ws)
      case xs: <>[T] =>
        val left <> acc = ConcUtils.shakeRight(xs)
        unwind(left, acc)
      case Empty =>
        throw new UnsupportedOperationException("Cannot unprepend on Empty.")
      case xs: Single[T] =>
        Empty
      case xs: Chunk[T] =>
        if (xs.size > 1) {
          val nxs = new Chunk(xs.array.tail, xs.size - 1, xs.k)
          nxs
        } else {
          Empty
        }
      case xs: Append[T] =>
        unprependDirect(xs.normalized)
    }
  }

  def unprepend[T](xs: Conc[T]): (T, Conc[T]) = {
    def unwind(left: Conc[T], acc: Conc[T]): (T, Conc[T]) = {
      (left: @unchecked) match {
        case zs: Single[T] =>
          (zs.x, acc)
        case zs: Chunk[T] =>
          if (zs.size > 1) {
            val nxs = Prepend(new Chunk(zs.array.tail, zs.size - 1, zs.k), acc)
            (zs.array(0), nxs)
          } else {
            (zs.array(0), acc)
          }
        case zs: <>[T] =>
          val left <> right = ConcUtils.shakeRight(zs)
          unwind(left, Prepend(right, acc))
      }
    }
    (xs: @unchecked) match {
      case Prepend(zs, ws) =>
        unwind(zs, ws)
      case xs: <>[T] =>
        val left <> acc = ConcUtils.shakeRight(xs)
        unwind(left, acc)
      case Empty =>
        throw new UnsupportedOperationException("Cannot unprepend on Empty.")
      case xs: Single[T] =>
        (xs.x, Empty)
      case xs: Chunk[T] =>
        if (xs.size > 1) {
          val nxs = new Chunk(xs.array.tail, xs.size - 1, xs.k)
          (xs.array(0), nxs)
        } else {
          (xs.array(0), Empty)
        }
      case xs: Append[T] =>
        unprepend(xs.normalized)
    }
  }

  private def isNormalized[T](xs: Conc[T]): Boolean = (xs: @unchecked) match {
    case left <> right => isNormalized(left) && isNormalized(right)
    case xs: Leaf[T] => true
    case _ => false
  }

  @tailrec
  private def isAppendList[T](xs: Conc[T]): Boolean = (xs: @unchecked) match {
    case Prepend(_, _) =>
      false
    case Append(zs @ Append(_, ws), ys) =>
      ys.level < ws.level && isNormalized(ys) && isAppendList(zs)
    case Append(zs, ys) =>
      ys.level < zs.level && isNormalized(ys) && isNormalized(zs)
  }

  @tailrec
  private def isPrependList[T](xs: Conc[T]): Boolean = (xs: @unchecked) match {
    case Append(_, _) =>
      false
    case Prepend(ys, zs @ Prepend(ws, _)) =>
      ys.level < ws.level && isNormalized(ys) && isPrependList(zs)
    case Prepend(ys, zs) =>
      ys.level < zs.level && isNormalized(ys) && isNormalized(zs)
  }

  def invariant[T](xs: Conc[T]) = (xs: @unchecked) match {
    case Append(_, _) => isAppendList(xs)
    case Prepend(_, _) => isPrependList(xs)
    case _ => isNormalized(xs)
  }

}


sealed abstract class Conqueue[+T] extends Conc[T] {
  def evaluated: Boolean
  def rear: Conqueue[T]
  def addIfUnevaluated[U >: T](stack: List[Conqueue.Spine[U]]) = stack
}


object Conqueue {

  def empty[T]: Conqueue[T] = Tip(Zero)

  case class Lazy[+T](
    lstack: List[Spine[T]], queue: Conqueue[T], rstack: List[Spine[T]]
  ) extends Conqueue[T] {
    def left = queue.left
    def right = queue.right
    def level = queue.level
    def size = queue.size
    def evaluated = unsupported("Undefined for lazy conqueue.")
    def rear = unsupported("Undefined for lazy conqueue.")
    override def normalized = queue.normalized
  }

  class Spine[+T](
    val lwing: Num[T], val rwing: Num[T], @volatile var evaluateTail: AnyRef
  ) extends Conqueue[T] {
    lazy val rear: Conqueue[T] = {
      val t = (evaluateTail: @unchecked) match {
        case eager: Conqueue[T] => eager
        case suspension: Function0[_] => suspension().asInstanceOf[Conqueue[T]]
      }
      evaluateTail = null
      t
    }
    def evaluated = evaluateTail == null
    override def addIfUnevaluated[U >: T](stack: List[Conqueue.Spine[U]]) =
      if (!evaluated) this :: stack else stack
    def left = lwing
    def right = new <>(rear, rwing)
    lazy val level: Int = 1 + math.max(lwing.level, math.max(rear.level, rwing.level))
    lazy val size: Int = lwing.size + rear.size + rwing.size
    override def normalized =
      ConcUtils.normalizeLeftWingsAndTip(
        this, Conc.Empty) <> ConcUtils.normalizeRightWings(this, Conc.Empty)
  }

  object Spine {
    def withSameTail[T](s: Spine[T], lwing: Num[T], rwing: Num[T]): Spine[T] = {
      var tail = s.evaluateTail
      if (tail eq null) tail = s.rear
      new Spine(lwing, rwing, tail)
    }
  }

  case class Tip[+T](tip: Num[T]) extends Conqueue[T] {
    def left = tip.left
    def right = tip.right
    def level = tip.level
    def size = tip.size
    def evaluated = true
    def rear = unsupported("Undefined for the tip.")
    override def normalized = tip.normalized
  }

  sealed abstract class Num[+T] extends Conc[T] {
    def leftmost: Conc[T]
    def rightmost: Conc[T]
    def digit: Int
  }

  case object Zero extends Num[Nothing] {
    def left = unsupported("Zero does not have children.")
    def right = unsupported("Zero does not have children.")
    def leftmost = unsupported("empty")
    def rightmost = unsupported("empty")
    def level: Int = 0
    def size: Int = 0
    def digit = 0
    override def normalized = Conc.Empty
  }

  case class One[+T](_1: Conc[T]) extends Num[T] {
    def left = _1
    def right = Conc.Empty
    def leftmost = _1
    def rightmost = _1
    def level: Int = 1 + _1.level
    def size: Int = _1.size
    def digit = 1
    override def normalized = _1
  }

  case class Two[+T](_1: Conc[T], _2: Conc[T]) extends Num[T] {
    def left = _1
    def right = _2
    def leftmost = _1
    def rightmost = _2
    def level: Int = 1 + math.max(_1.level, _2.level)
    def size: Int = _1.size + _2.size
    def digit = 2
    override def normalized = _1 <> _2
  }

  case class Three[+T](_1: Conc[T], _2: Conc[T], _3: Conc[T]) extends Num[T] {
    def left = _1
    def right = new <>(_2, _3)
    def leftmost = _1
    def rightmost = _3
    def level: Int = 1 + math.max(math.max(_1.level, _2.level), _3.level)
    def size: Int = _1.size + _2.size + _3.size
    def digit = 3
    override def normalized = _1 <> _2 <> _3
  }

  case class Four[+T](_1: Conc[T], _2: Conc[T], _3: Conc[T], _4: Conc[T])
  extends Num[T] {
    def left = new <>(_1, _2)
    def right = new <>(_3, _4)
    def leftmost = _1
    def rightmost = _4
    def level: Int =
      1 + math.max(math.max(_1.level, _2.level), math.max(_3.level, _4.level))
    def size: Int = _1.size + _2.size + _3.size + _4.size
    def digit = 4
    override def normalized = _1 <> _2 <> _3 <> _4
  }
}


object ConcUtils {

  import Conc._
  import ConcRope._
  import Conqueue._

  private[common] def toSeq[T](xs: Conc[T]): Seq[T] = {
    val buffer = collection.mutable.Buffer[T]()
    for (x <- xs) {
      buffer += x
    }
    buffer
  }

  def levelFormatter[T](num: Num[T]): String = num match {
    case Zero => "Zero"
    case One(_1) if _1.level == 0 || (_1.left.level == _1.right.level) =>
      s"One*(${_1.level})"
    case One(_1) => s"One(${_1.level})"
    case Two(_1, _2) => s"Two(${_1.level}, ${_2.level})"
    case Three(_1, _2, _3) => s"Three(${_1.level}, ${_2.level}, ${_3.level})"
    case Four(_1, _2, _3, _4) =>
      s"Four(${_1.level}, ${_2.level}, ${_3.level}, ${_4.level})"
  }

  private def mkstr[T](c: Conc[T]) = toSeq(c).mkString(s"l${c.level}:[", ", ", "]")

  def contentsFormatter[T](num: Num[T]): String = num match {
    case Zero => s"Zero"
    case One(_1) => s"One(${mkstr(_1)})"
    case Two(_1, _2) => s"Two(${mkstr(_1)}, ${mkstr(_2)})"
    case Three(_1, _2, _3) => s"Three(${mkstr(_1)}, ${mkstr(_2)}, ${mkstr(_3)}})"
    case Four(_, _, _, _) => invalid("never four.")
  }

  private def mkstrn[T](c: Conc[Conc[T]]) = toSeq(c).map(mkstr).mkString("[", ", ", "]")

  def nestedContentsFormatter[T](num: Num[Conc[T]]): String = num match {
    case Zero => s"Zero"
    case One(_1) => s"One(${mkstrn(_1)})"
    case Two(_1, _2) => s"Two(${mkstrn(_1)}, ${mkstrn(_2)})"
    case Three(_1, _2, _3) => s"Three(${mkstrn(_1)}, ${mkstrn(_2)}, ${mkstrn(_3)}})"
    case Four(_, _, _, _) => invalid("never four.")
  }

  def queueString[T](
    conq: Conqueue[T], showNum: Num[T] => String = levelFormatter _, spacing: Int = 80
  ): String = {
    val buffer = new StringBuffer

    def traverse(
      rank: Int, indent: Int, conq: Conqueue[T]
    ): Unit = (conq: @unchecked) match {
      case s: Spine[T] =>
        val lefts = showNum(s.lwing)
        val rights = showNum(s.rwing)
        val spines = "Spine(+)"
        buffer.append(
          " " * (indent - lefts.length) + lefts + " " + spines + " " + rights)
        buffer.append("\n")
        traverse(rank + 1, indent, s.rear)
      case Tip(tip) =>
        val tips = s"Tip(${showNum(tip)})"
        buffer.append(" " * (indent) + tips)
      case Lazy(_, conq, _) =>
        buffer.append(" " * (indent) + "Lazy(+)")
        buffer.append("\n")
        traverse(rank, indent, conq)
    }

    traverse(0, spacing, conq)
    buffer.toString
  }

  def foreach[
    @specialized(Int, Long, Float, Double) T,
    @specialized(Int, Long, Float, Double) U
  ](xs: Conc[T], f: T => U): Unit = (xs: @unchecked) match {
    case left <> right =>
      foreach(left, f)
      foreach(right, f)
    case s: Single[T] =>
      f(s.x)
    case c: Chunk[T] =>
      val a = c.array
      val sz = c.size
      var i = 0
      while (i < sz) {
        f(a(i))
        i += 1
      }
    case Empty =>
    case Append(left, right) =>
      foreach(left, f)
      foreach(right, f)
    case Prepend(left, right) =>
      foreach(left, f)
      foreach(right, f)
    case Zero =>
    case One(_1) =>
      foreach(_1, f)
    case Two(_1, _2) =>
      foreach(_1, f)
      foreach(_2, f)
    case Three(_1, _2, _3) =>
      foreach(_1, f)
      foreach(_2, f)
      foreach(_3, f) 
    case Tip(Zero) =>
    case Tip(num) =>
      foreach(num, f)
    case Lazy(_, conq, _) =>
      foreach(conq, f)
    case st: Spine[T] =>
      foreach(st.lwing, f)
      foreach(st.rear, f)
      foreach(st.rwing, f)
    case _ =>
      invalid("All cases should have been covered: " + xs + ", " + xs.getClass)
  }

  def foreachLeafLeft[T](xs: Conc[T])(
    f: Leaf[T] => Unit
  ): Unit = (xs: @unchecked) match {
    case left <> right =>
      foreachLeafLeft(left)(f)
      foreachLeafLeft(right)(f)
    case Empty =>
    case leaf: Leaf[T] =>
      f(leaf)
    case Append(left, right) =>
      // TODO: investigate if this case is necessary here.
      foreachLeafLeft(left)(f)
      foreachLeafLeft(right)(f)
    case Zero =>
    case One(_1) =>
      foreachLeafLeft(_1)(f)
    case Two(_1, _2) =>
      foreachLeafLeft(_1)(f)
      foreachLeafLeft(_2)(f)
    case Three(_1, _2, _3) =>
      foreachLeafLeft(_1)(f)
      foreachLeafLeft(_2)(f)
      foreachLeafLeft(_3)(f) 
    case Tip(Zero) =>
    case Tip(num) =>
      foreachLeafLeft(num)(f)
    case Lazy(_, conq, _) =>
      foreachLeafLeft(conq)(f)
    case st: Spine[T] =>
      foreachLeafLeft(st.lwing)(f)
      foreachLeafLeft(st.rear)(f)
      foreachLeafLeft(st.rwing)(f)
    case _ =>
      invalid("All cases should have been covered: " + xs + ", " + xs.getClass)
  }

  def foreachLeafRight[T](xs: Conc[T])(
    f: Leaf[T] => Unit
  ): Unit = (xs: @unchecked) match {
    case left <> right =>
      foreachLeafRight(right)(f)
      foreachLeafRight(left)(f)
    case Empty =>
    case leaf: Leaf[T] =>
      f(leaf)
    case Append(left, right) =>
      // TODO: investigate if this case is necessary here.
      foreachLeafRight(right)(f)
      foreachLeafRight(left)(f)
    case Zero =>
    case One(_1) =>
      foreachLeafRight(_1)(f)
    case Two(_1, _2) =>
      foreachLeafRight(_2)(f)
      foreachLeafRight(_1)(f)
    case Three(_1, _2, _3) =>
      foreachLeafRight(_3)(f) 
      foreachLeafRight(_2)(f)
      foreachLeafRight(_1)(f)
    case Tip(Zero) =>
    case Tip(num) =>
      foreachLeafRight(num)(f)
    case Lazy(_, conq, _) =>
      foreachLeafRight(conq)(f)
    case st: Spine[T] =>
      foreachLeafRight(st.rwing)(f)
      foreachLeafRight(st.rear)(f)
      foreachLeafRight(st.lwing)(f)
    case _ =>
      invalid("All cases should have been covered: " + xs + ", " + xs.getClass)
  }

  def apply[@specialized(Int, Long, Float, Double) T](
    xs: Conc[T], i: Int
  ): T = (xs: @unchecked) match {
    case left <> _ if i < left.size =>
      apply(left, i)
    case left <> right =>
      apply(right, i - left.size)
    case st: Spine[T] =>
      if (i < st.lwing.size) apply(st.lwing, i)
      else if (i < st.lwing.size + st.rear.size) apply(st.rear, i - st.lwing.size)
      else apply(st.rwing, i - st.lwing.size - st.rear.size)
    case s: Single[T] => s.x
    case c: Chunk[T] => c.array(i)
    case Append(left, _) if i < left.size =>
      apply(left, i)
    case Append(left, right) =>
      apply(right, i - left.size)
    case Prepend(left, right) if i < left.size =>
      apply(left, i)
    case Prepend(left, right) =>
      apply(right, i - left.size)
    case One(_1) =>
      apply(_1, i)
    case Two(_1, _2) =>
      if (i < _1.size) apply(_1, i)
      else apply(_2, i - _1.size)
    case Three(_1, _2, _3) =>
      if (i < _1.size) apply(_1, i)
      else if (i < _1.size + _2.size) apply(_2, i - _1.size)
      else apply(_3, i - _1.size - _2.size)
    case Tip(num) =>
      apply(num, i)
    case Lazy(_, conq, _) =>
      apply(conq, i)
  }

  private def updatedArray[
    @specialized(Int, Long, Float, Double) T: ClassTag
  ](a: Array[T], i: Int, y: T, sz: Int): Array[T] = {
    val na = new Array[T](a.length)
    System.arraycopy(a, 0, na, 0, sz)
    na(i) = y
    na
  }

  def asConqueue[T](xs: Conc[T]) = xs.asInstanceOf[Conqueue[T]]

  def asNum[T](xs: Conc[T]) = xs.asInstanceOf[Num[T]]

  def update[
    @specialized(Int, Long, Float, Double) T: ClassTag
  ](xs: Conc[T], i: Int, y: T): Conc[T] = (xs: @unchecked) match {
    case left <> right if i < left.size =>
      new <>(update(left, i, y), right)
    case left <> right =>
      val ni = i - left.size
      new <>(left, update(right, ni, y))
    case s: Single[T] =>
      new Single(y)
    case c: Chunk[T] =>
      new Chunk(updatedArray(c.array, i, y, c.size), c.size, c.k)
    case Append(left, right) if i < left.size => // TODO: see if this should be removed.
      new Append(update(left, i, y), right)
    case Append(left, right) =>
      new Append(left, update(right, i - left.size, y))
    case st: Spine[T] =>
      if (i < st.lwing.size) {
        val nlwing = asNum(update(st.lwing, i, y))
        Spine.withSameTail(st, nlwing, st.rwing)
      } else {
        val lwingrearsize = st.lwing.size + st.rear.size
        if (i >= lwingrearsize) {
          val nrwing = asNum(update(st.rwing, i - lwingrearsize, y))
          new Spine(st.lwing, nrwing, st.rear)
        } else {
          val nrear = asConqueue(update(st.rear, i - st.lwing.size, y))
          new Spine(st.lwing, st.rwing, nrear)
        }
      }
    case Tip(n) =>
      Tip(asNum(update(n, i, y)))
    case One(_1) =>
      One(update(_1, i, y))
    case Two(_1, _2) =>
      if (i < _1.size) Two(update(_1, i, y), _2)
      else Two(_1, update(_2, i - _1.size, y))
    case Three(_1, _2, _3) =>
      if (i < _1.size) Three(update(_1, i, y), _2, _3)
      else if (i < _1.size + _2.size) Three(_1, update(_2, i - _1.size, y), _3)
      else Three(_1, _2, update(_3, i - _1.size - _2.size, y))
    case _ =>
      invalid("All cases should have been covered: " + xs + ", " + xs.getClass)
  }

  def concatConqueueTop[T](xs: Conqueue[T], ys: Conqueue[T]): Conqueue[T] = {
    if (xs.level < 32 && (1 << xs.level) <= ys.level) {
      var nys = ys
      foreachLeafRight(xs)(leaf => nys = pushHeadTop(nys, leaf))
      nys
    } else if (ys.level < 32 && (1 << ys.level) <= xs.level) {
      var nxs = xs
      foreachLeafLeft(ys)(leaf => nxs = pushLastTop(nxs,leaf))
      nxs
    } else {
      toConqueue(concat(xs.normalized, ys.normalized))
    }
  }

  def concat[T](xs: Conc[T], ys: Conc[T]) = {
    if (xs == Empty) ys
    else if (ys == Empty) xs
    else concatRec(xs, ys)
  }

  private def concatRec[T](xs: Conc[T], ys: Conc[T]): Conc[T] = {
    val diff = ys.level - xs.level
    if (diff >= -1 && diff <= 1) new <>(xs, ys)
    else if (diff < -1) {
      if (xs.left.level >= xs.right.level) {
        val nr = concatRec(xs.right, ys)
        new <>(xs.left, nr)
      } else {
        val nrr = concatRec(xs.right.right, ys)
        if (nrr.level == xs.level - 3) {
          val nl = xs.left
          val nr = new <>(xs.right.left, nrr)
          new <>(nl, nr)
        } else {
          val nl = new <>(xs.left, xs.right.left)
          val nr = nrr
          new <>(nl, nr)
        }
      }
    } else {
      if (ys.right.level >= ys.left.level) {
        val nl = concatRec(xs, ys.left)
        new <>(nl, ys.right)
      } else {
        val nll = concatRec(xs, ys.left.left)
        if (nll.level == ys.level - 3) {
          val nl = new <>(nll, ys.left.right)
          val nr = ys.right
          new <>(nl, nr)
        } else {
          val nl = nll
          val nr = new <>(ys.left.right, ys.right)
          new <>(nl, nr)
        }
      }
    }
  }

  private[common] def insertedArray[
    @specialized(Int, Long, Float, Double) T: ClassTag
  ](a: Array[T], from: Int, i: Int, y: T, sz: Int): Array[T] = {
    val na = new Array[T](sz + 1)
    System.arraycopy(a, from, na, 0, i)
    na(i) = y
    System.arraycopy(a, from + i, na, i + 1, sz - i)
    na
  }

  private[common] def removedArray[T: ClassTag](
    a: Array[T], from: Int, at: Int, sz: Int
  ): Array[T] = {
    val na = new Array[T](sz - 1)
    System.arraycopy(a, from, na, 0, at)
    System.arraycopy(a, from + at + 1, na, at, sz - at - 1)
    na
  }

  private[common] def copiedArray[T: ClassTag](
    a: Array[T], from: Int, sz: Int
  ): Array[T] = {
    val na = new Array[T](sz)
    System.arraycopy(a, from, na, 0, sz)
    na
  }

  def insert[@specialized(Int, Long, Float, Double) T: ClassTag](
    xs: Conc[T], i: Int, y: T
  ): Conc[T] = (xs.normalized: @unchecked) match {
    case left <> right if i < left.size =>
      insert(left, i, y) <> right
    case left <> right =>
      left <> insert(right, i - left.size, y)
    case s: Single[T] =>
      if (i == 0) new <>(new Single(y), xs)
      else new <>(xs, new Single(y))
    case c: Chunk[T] if c.size == c.k =>
      val a = c.array
      val sz = c.size
      val k = c.k
      if (i < k / 2) {
        val la = insertedArray(a, 0, i, y, k / 2)
        val ra = copiedArray(a, k / 2, k - k / 2)
        new <>(new Chunk(la, k / 2 + 1, k), new Chunk(ra, k - k / 2, k))
      } else {
        val la = copiedArray(a, 0, k / 2)
        val ra = insertedArray(a, k / 2, i - k / 2, y, k - k / 2 + 1)
        new <>(new Chunk(la, k / 2, k), new Chunk(ra, k - k / 2 + 1, k))
      }
    case c: Chunk[T] =>
      val a = c.array
      val sz = c.size
      val k = c.k
      new Chunk(insertedArray(a, 0, i, y, sz), sz + 1, k)
    case Empty =>
      new Single(y)
    case _ =>
      invalid("undefined for conqueues.")
  }

  def shakeLeft[T](xs: Conc[T]): Conc[T] = {
    if (xs.level <= 1) {
      //
      //       1       
      //    +--+--+    
      //    0     0    
      //
      xs
    } else if (xs.left.level >= xs.right.level) {
      //
      //                 n             
      //           +-----+-----+       
      //         n - 1       n - 1     
      //       +---+---+    (n - 2)    
      //     n - 2   n - 2             
      //    (n - 3) (n - 2)            
      //    (n - 2) (n - 3)            
      //
      xs
    } else if (xs.right.right.level >= xs.right.left.level) {
      //
      //            n                              n         
      //      +-----+-----+                  +-----+-----+   
      //    n - 2       n - 1      =>      n - 1       n - 2 
      //              +---+---+          +---+---+    (n - 2)
      //            n - 2   n - 2      n - 2   n - 2         
      //           (n - 3) (n - 2)            (n - 3)        
      //
      val nl = new <>(xs.left, xs.right.left)
      val nr = xs.right.right
      new <>(nl, nr)
    } else if (xs.left.left.level >= xs.left.right.level) {
      //
      //                    n                                      n                
      //          +---------+---------+                  +---------+---------+      
      //        n - 2               n - 1      =>      n - 1               n - 2    
      //      +---+---+           +---+---+          +---+---+           +---+---+  
      //    n - 3   n - 3       n - 2   n - 3      n - 3   n - 2       n - 3   n - 3
      //                      +---+---+                  +---+---+    (n - 4)       
      //                    n - 3   n - 3              n - 3   n - 3  (n - 3)       
      //                   (n - 3) (n - 4)                    (n - 3)               
      //                   (n - 4) (n - 3)                    (n - 4)               
      //
      //  OR:
      //
      //                    n                                      n                
      //          +---------+---------+                  +---------+---------+      
      //        n - 2               n - 1      =>      n - 1               n - 2    
      //      +---+---+           +---+---+          +---+---+           +---+---+  
      //    n - 3   n - 4       n - 2   n - 3      n - 3   n - 2       n - 3   n - 3
      //                      +---+---+                  +---+---+    (n - 4)       
      //                    n - 3   n - 3              n - 4   n - 3                
      //                   (n - 3) (n - 4)                    (n - 3)               
      //
      //  OR:
      //
      //                    n                                    n - 1              
      //          +---------+---------+                  +---------+---------+      
      //        n - 2               n - 1      =>      n - 2               n - 2    
      //      +---+---+           +---+---+          +---+---+           +---+---+  
      //    n - 3   n - 4       n - 2   n - 3      n - 3   n - 3       n - 3   n - 3
      //                      +---+---+                  +---+---+                  
      //                    n - 4   n - 3              n - 4   n - 4                
      //
      val nll = xs.left.left
      val nlr = new <>(xs.left.right, xs.right.left.left)
      val nl = new <>(nll, nlr)
      val nr = new <>(xs.right.left.right, xs.right.right)
      new <>(nl, nr)
    } else if (xs.right.left.left.level >= xs.right.left.right.level) {
      //
      //                    n                    
      //          +---------+---------+          
      //        n - 2               n - 1      =>
      //      +---+---+           +---+---+      
      //    n - 4   n - 3       n - 2   n - 3    
      //                      +---+---+          
      //                    n - 3   n - 3        
      //                   (n - 3) (n - 4)       
      //
      //                           n                
      //                 +---------+---------+      
      //               n - 1               n - 2    
      //          +------+------+        +---+---+  
      //        n - 2         n - 3    n - 3   n - 3
      //      +---+---+      (n - 3)  (n - 4)       
      //    n - 4   n - 3                           
      //
      val nl = new <>(xs.left, xs.right.left.left)
      val nr = new <>(xs.right.left.right, xs.right.right)
      new <>(nl, nr)
    } else {
      //
      //                       n                       
      //          +------------+------------+          
      //        n - 2                     n - 1      =>
      //      +---+---+                 +---+---+      
      //    n - 4   n - 3             n - 2   n - 3    
      //          +---+---+         +---+---+          
      //        n - 4   n - 4     n - 4   n - 3        
      //       (n - 4) (n - 5)                         
      //       (n - 5) (n - 4)                         
      //
      //                             n - 1                 
      //                  +------------+------------+      
      //                n - 2                     n - 2    
      //        +---------+---------+           +---+---+  
      //      n - 3               n - 3       n - 3   n - 3
      //    +---+---+           +---+---+                  
      //  n - 4   n - 4       n - 4   n - 4                
      //         (n - 4)     (n - 5)                       
      //         (n - 5)     (n - 4)                       
      //
      val nll = new <>(xs.left.left, xs.left.right.left)
      val nlr = new <>(xs.left.right.right, xs.right.left.left)
      val nl = new <>(nll, nlr)
      val nr = new <>(xs.right.left.right, xs.right.right)
      new <>(nl, nr)
    }
  }

  def shakeRight[T](xs: Conc[T]): Conc[T] = {
    if (xs.level <= 1) {
      //
      //       1       
      //    +--+--+    
      //    0     0    
      //
      xs
    } else if (xs.left.level <= xs.right.level) {
      //
      //             n                 
      //       +-----+-----+           
      //     n - 1       n - 1         
      //    (n - 2)    +---+---+       
      //             n - 2   n - 2     
      //            (n - 3) (n - 2)    
      //            (n - 2) (n - 3)    
      //
      xs
    } else if (xs.left.left.level >= xs.left.right.level) {
      //
      //                 n                      n            
      //           +-----+-----+          +-----+-----+      
      //         n - 1       n - 2  =>  n - 2       n - 1    
      //       +---+---+               (n - 2)    +---+---+  
      //     n - 2   n - 2                      n - 2   n - 2
      //    (n - 2) (n - 3)                    (n - 3)       
      //
      val nl = xs.left.left
      val nr = new <>(xs.left.right, xs.right)
      new <>(nl, nr)
    } else if (xs.right.right.level >= xs.right.left.level) {
      //
      //                    n                                      n                
      //          +---------+---------+                  +---------+---------+      
      //        n - 1               n - 2      =>      n - 2               n - 1    
      //      +---+---+           +---+---+          +---+---+           +---+---+  
      //    n - 3   n - 2       n - 3   n - 3      n - 3   n - 3       n - 2   n - 3
      //          +---+---+                               (n - 4)    +---+---+      
      //        n - 3   n - 3                             (n - 3)  n - 3   n - 3    
      //       (n - 4) (n - 3)                                    (n - 3)           
      //       (n - 3) (n - 4)                                    (n - 4)           
      //
      //  OR:
      //
      //                    n                                      n                
      //          +---------+---------+                  +---------+---------+      
      //        n - 1               n - 2      =>      n - 2               n - 1    
      //      +---+---+           +---+---+          +---+---+           +---+---+  
      //    n - 3   n - 2       n - 4   n - 3      n - 3   n - 3       n - 2   n - 3
      //          +---+---+                               (n - 4)    +---+---+      
      //        n - 3   n - 3                                      n - 3   n - 4    
      //       (n - 4) (n - 3)                                    (n - 3)           
      //
      //  OR:
      //
      //                    n                                    n - 1              
      //          +---------+---------+                  +---------+---------+      
      //        n - 1               n - 2      =>      n - 2               n - 2    
      //      +---+---+           +---+---+          +---+---+           +---+---+  
      //    n - 3   n - 2       n - 4   n - 3      n - 3   n - 3       n - 3   n - 3
      //          +---+---+                                          +---+---+      
      //        n - 3   n - 4                                      n - 4   n - 4    
      //
      val nl = new <>(xs.left.left, xs.left.right.left)
      val nrl = new <>(xs.left.right.right, xs.right.left)
      val nrr = xs.right.right
      val nr = new <>(nrl, nrr)
      new <>(nl, nr)
    } else if (xs.left.right.right.level >= xs.left.right.left.level) {
      //
      //                    n                    
      //          +---------+---------+          
      //        n - 1               n - 2      =>
      //      +---+---+           +---+---+      
      //    n - 3   n - 2       n - 3   n - 4    
      //          +---+---+                      
      //        n - 3   n - 3                    
      //       (n - 4) (n - 3)                   
      //
      //                  n                       
      //        +---------+---------+             
      //      n - 2               n - 1           
      //    +---+---+        +------+------+      
      //  n - 3   n - 3    n - 3         n - 2    
      //         (n - 4)  (n - 3)      +---+---+  
      //                             n - 3   n - 4
      //                                          
      //
      val nl = new <>(xs.left.left, xs.left.right.left)
      val nr = new <>(xs.left.right.right, xs.right)
      new <>(nl, nr)
    } else {
      //
      //                       n                       
      //          +------------+------------+          
      //        n - 1                     n - 2      =>
      //      +---+---+                 +---+---+      
      //    n - 3   n - 2             n - 3   n - 4    
      //          +---+---+         +---+---+          
      //        n - 3   n - 4     n - 4   n - 4        
      //                         (n - 5) (n - 4)       
      //                         (n - 4) (n - 5)       
      //
      //                   n - 1                           
      //        +------------+------------+                
      //      n - 2                     n - 2              
      //    +---+---+           +---------+---------+      
      //  n - 3   n - 3       n - 3               n - 3    
      //                    +---+---+           +---+---+  
      //                  n - 4   n - 4       n - 4   n - 4
      //                         (n - 5)     (n - 4)       
      //                         (n - 4)     (n - 5)       
      //
      val nl = new <>(xs.left.left, xs.left.right.left)
      val nrl = new <>(xs.left.right.right, xs.right.left.left)
      val nrr = new <>(xs.right.left.right, xs.right.right)
      val nr = new <>(nrl, nrr)
      new <>(nl, nr)
    }
  }

  def pay[T](work: List[Spine[T]], n: Int): List[Spine[T]] =
    if (n == 0) work else work match {
      case head :: rest =>
        // do 2 units of work
        val tail = head.rear
        pay(tail.addIfUnevaluated(rest), n - 1)
      case Nil =>
        // hoorah - nothing to do
        Nil
    }

  val doNothing = () => {}

  def noCarryPushHead[T](num: Num[T], c: Conc[T]): Num[T] =
    (num.digit: @switch) match {
      case 0 =>
        One(c)
      case 1 =>
        val One(_1) = num
        Two(c, _1)
      case 2 =>
        val Two(_1, _2) = num
        Three(c, _1, _2)
      case _ =>
        invalid("Causes a carry.")
    }

  def noCarryPushLast[T](num: Num[T], c: Conc[T]): Num[T] =
    (num.digit: @switch) match {
      case 0 =>
        One(c)
      case 1 =>
        val One(_1) = num
        Two(_1, c)
      case 2 =>
        val Two(_1, _2) = num
        Three(_1, _2, c)
      case _ =>
        invalid("Causes a carry.")
    }

  def noCarryAdd[T](n: Num[T], m: Num[T]): Num[T] = (n.digit: @switch) match {
    case 0 =>
      m
    case 1 =>
      val One(n1) = n
      (m.digit: @switch) match {
        case 0 =>
          n
        case 1 =>
          val One(m1) = m
          Two(n1, m1)
        case 2 =>
          val Two(m1, m2) = m
          Three(n1, m1, m2)
        case 3 =>
          val Three(m1, m2, m3) = m
          Four(n1, m1, m2, m3)
        case _ =>
          invalid("Causes a carry.")
      }
    case 2 =>
      val Two(n1, n2) = n
      (m.digit: @switch) match {
        case 0 =>
          n
        case 1 =>
          val One(m1) = m
          Three(n1, n2, m1)
        case 2 =>
          val Two(m1, m2) = m
          Four(n1, n2, m1, m2)
        case _ =>
          invalid("Causes a carry.")
      }
    case 3 =>
      val Three(n1, n2, n3) = n
      (m.digit: @switch) match {
        case 0 =>
          n
        case 1 =>
          val One(m1) = m
          Four(n1, n2, n3, m1)
        case _ =>
          invalid("Causes a carry.")
      }
    case 4 =>
      (m.digit: @switch) match {
        case 0 =>
          n
        case _ =>
          invalid("Causes a carry.")
      }
  }

  def noBorrowPopHead[T](num: Num[T]): Num[T] = (num.digit: @switch) match {
    case 0 =>
      unsupported("empty")
    case 1 =>
      Zero
    case 2 =>
      val Two(_1, _2) = num
      One(_2)
    case 3 =>
      val Three(_1, _2, _3) = num
      Two(_2, _3)
    case 4 =>
      invalid("Four should never happen.")
  }

  def noBorrowPopLast[T](num: Num[T]): Num[T] = (num.digit: @switch) match {
    case 0 =>
      unsupported("empty")
    case 1 =>
      Zero
    case 2 =>
      val Two(_1, _2) = num
      One(_1)
    case 3 =>
      val Three(_1, _2, _3) = num
      Two(_1, _2)
    case 4 =>
      invalid("Four should never happen.")
  }

  def pushHead[T](
    conq: Conqueue[T], c: Conc[T], onPush: () => Unit = doNothing
  ): Conqueue[T] = {
    onPush()

    (conq: @unchecked) match {
      case s: Spine[T] =>
        if (s.lwing.digit < 3) {
          Spine.withSameTail(s, noCarryPushHead(s.lwing, c), s.rwing)
        } else {
          val Three(_1, _2, _3) = s.lwing
          val nlwing = Two(c, _1)
          val carry = _2 <> _3
          val ntail = (s.rear: @unchecked) match {
            case st: Spine[T] if st.lwing.digit == 3 =>
              () => pushHead(s.rear, carry, onPush)
            case _ =>
              pushHead(s.rear, carry, onPush)
          }
          new Spine(nlwing, s.rwing, ntail)
        }
      case Tip(tip) =>
        if (tip.digit < 3) {
          Tip(noCarryPushHead(tip, c))
        } else {
          val Three(_1, _2, _3) = tip
          new Spine(Two(c, _1), Two(_2, _3), Tip(Zero))
        }
    }
  }

  def pushHeadTop[T](
    conq: Conqueue[T], leaf: Leaf[T], onPush: () => Unit = doNothing
  ): Conqueue[T] = conq match {
    case Conqueue.Lazy(lstack, queue, rstack) =>
      val nqueue = pushHead(queue, leaf, onPush)
      val nlstack = pay(nqueue.addIfUnevaluated(lstack), 2)
      val nrstack = pay(rstack, 2)
      Conqueue.Lazy(nlstack, nqueue, nrstack)
    case _ =>
      pushHead(conq, leaf, onPush)
  }

  def fixLeft[T](s: Spine[T], onFix: () => Unit = doNothing): Spine[T] = {
    def spreadBorrow(
      b: Conc[T], otail: Spine[T], nttail: Conqueue[T], continue: Boolean
    ): Spine[T] = {
      val bshaken = shakeRight(b)
      if (bshaken.level == b.level) {
        if (bshaken.left.level == b.level - 1) {
          // regular Two in position n - 1
          val ntlwing = Two(bshaken.left, bshaken.right)
          val ntspine = new Spine(ntlwing, otail.rwing, nttail)
          val ntail = if (continue) ntspine else () => fixLeft(ntspine, onFix)
          new Spine(s.lwing, s.rwing, ntail)
        } else {
          // regular One in position n - 1, regular One in position n - 2
          val ntlwing = One(bshaken.right)
          val ntspine = new Spine(ntlwing, otail.rwing, nttail)
          val ntail = if (continue) ntspine else () => fixLeft(ntspine, onFix)
          val nlwing = noCarryPushLast(s.lwing, bshaken.left)
          new Spine(nlwing, s.rwing, ntail)
        }
      } else {
        // excited One in position n - 1
        val ntlwing = One(bshaken)
        val ntspine = new Spine(ntlwing, otail.rwing, nttail)
        val ntail = if (continue) ntspine else () => fixLeft(ntspine, onFix)
        new Spine(s.lwing, s.rwing, ntail)
      }
    }

    onFix()

    (s.rear: @unchecked) match {
      case st: Spine[T] if st.lwing.digit == 0 =>
        (st.rear: @unchecked) match {
          case stt: Spine[T] =>
            val nttlwing = noBorrowPopHead(stt.lwing)
            val nttail = Spine.withSameTail(stt, nttlwing, stt.rwing)
            spreadBorrow(stt.lwing.leftmost, st, nttail, nttlwing.digit > 0)
          case Tip(Zero) =>
            new Spine(s.lwing, s.rwing, Tip(st.rwing))
          case Tip(tip) =>
            spreadBorrow(tip.leftmost, st, Tip(noBorrowPopHead(tip)), false)
        }
      case _ =>
        s
    }
  }

  def popHead[T](conq: Conqueue[T], onFix: () => Unit = doNothing): Conqueue[T] = {
    (conq: @unchecked) match {
      case s: Spine[T] =>
        if (s.lwing.digit > 1) {
          Spine.withSameTail(s, noBorrowPopHead(s.lwing), s.rwing)
        } else {
          (s.rear: @unchecked) match {
            case st: Spine[T] => // note: s is at rank 0
              val tleftmost = st.lwing.leftmost
              val nlwing = Two(tleftmost.left, tleftmost.right)
              val ntlwing = noBorrowPopHead(st.lwing)
              val ntail = Spine.withSameTail(st, ntlwing, st.rwing)
              val nspine = new Spine(nlwing, s.rwing, ntail)
              if (ntlwing.digit > 0) nspine else fixLeft(nspine, onFix)
            case Tip(Zero) =>
              Tip(s.rwing)
            case Tip(tip) =>
              val leftmost = tip.leftmost
              val nlwing = Two(leftmost.left, leftmost.right)
              val ntip = Tip(noBorrowPopHead(tip))
              new Spine(nlwing, s.rwing, ntip)
          }
        }
      case Tip(tip) =>
        Tip(noBorrowPopHead(tip))
    }
  }

  def popHeadTop[T](conq: Conqueue[T], onFix: () => Unit = doNothing): Conqueue[T] =
    conq match {
      case Conqueue.Lazy(lstack, queue, rstack) =>
        val nqueue = popHead(queue, onFix)
        val nlstack = pay(nqueue.addIfUnevaluated(lstack), 2)
        val nrstack = pay(rstack, 2)
        Conqueue.Lazy(nlstack, nqueue, nrstack)
      case _ =>
        popHead(conq, onFix)
    }

  def head[T](conq: Conqueue[T]): Leaf[T] = {
    @tailrec def leftmost(c: Conc[T]): Leaf[T] = c match {
      case Empty => invalid("Num should never have a Zero.")
      case l: Leaf[T] => l
      case _ <> _ => leftmost(c.left)
      case _ => invalid("Invalid conqueue state.")
    }

    (conq: @unchecked) match {
      case s: Spine[T] =>
        leftmost(s.lwing.leftmost)
      case Tip(Zero) =>
        null
      case Tip(tip) =>
        leftmost(tip.leftmost)
      case Lazy(_, queue, _) =>
        head(queue)
    }
  }

  def pushLast[T](
    conq: Conqueue[T], c: Conc[T], onPush: () => Unit = doNothing
  ): Conqueue[T] = {
    onPush()

    (conq: @unchecked) match {
      case s: Spine[T] =>
        if (s.rwing.digit < 3) {
          Spine.withSameTail(s, s.lwing, noCarryPushLast(s.rwing, c))
        } else {
          val Three(_1, _2, _3) = s.rwing
          val nrwing = Two(_3, c)
          val carry = _1 <> _2
          val ntail = (s.rear: @unchecked) match {
            case st: Spine[T] =>
              () => pushLast(s.rear, carry, onPush)
            case Tip(_) =>
              pushLast(s.rear, carry, onPush)
          }
          new Spine(s.lwing, nrwing, ntail)
        }
      case Tip(tip) =>
        if (tip.digit < 3) {
          Tip(noCarryPushLast(tip, c))
        } else {
          val Three(_1, _2, _3) = tip
          new Spine(Two(_1, _2), Two(_3, c), Tip(Zero))
        }
    }
  }

  def pushLastTop[T](
    conq: Conqueue[T], leaf: Leaf[T], onPush: () => Unit = doNothing
  ): Conqueue[T] = conq match {
    case Conqueue.Lazy(lstack, queue, rstack) =>
      val nqueue = pushLast(queue, leaf, onPush)
      val nlstack = pay(lstack, 2)
      val nrstack = pay(nqueue.addIfUnevaluated(rstack), 2)
      Conqueue.Lazy(nlstack, nqueue, nrstack)
    case _ =>
      pushLast(conq, leaf, onPush)
  }

  def fixRight[T](s: Spine[T], onFix: () => Unit = doNothing): Spine[T] = {
    onFix()
    def spreadBorrow(
      b: Conc[T], otail: Spine[T], nttail: Conqueue[T], continued: Boolean
    ): Spine[T] = {
      val bshaken = shakeLeft(b)
      if (bshaken.level == b.level) {
        if (bshaken.right.level == b.level - 1) {
          // regular Two in position n - 1
          val ntrwing = Two(bshaken.left, bshaken.right)
          val ntspine = new Spine(otail.lwing, ntrwing, nttail)
          val ntail = if (continued) ntspine else () => fixRight(ntspine, onFix)
          new Spine(s.lwing, s.rwing, ntail)
        } else {
          // regular One in position n - 1, regular One in position n - 2
          val ntrwing = One(bshaken.left)
          val ntspine = new Spine(otail.lwing, ntrwing, nttail)
          val ntail = if (continued) ntspine else () => fixRight(ntspine, onFix)
          val nrwing = noCarryPushHead(s.rwing, bshaken.right)
          new Spine(s.lwing, nrwing, ntail)
        }
      } else {
        // excited One in position n - 1
        val ntrwing = One(bshaken)
        val ntspine = new Spine(otail.lwing, ntrwing, nttail)
        val ntail = if (continued) ntspine else () => fixRight(ntspine, onFix)
        new Spine(s.lwing, s.rwing, ntail)
      }
    }
    (s.rear: @unchecked) match {
      case st: Spine[T] if st.rwing.digit == 0 =>
        (st.rear: @unchecked) match {
          case stt: Spine[T] =>
            val nttrwing = noBorrowPopLast(stt.rwing)
            val nttail = Spine.withSameTail(stt, stt.lwing, nttrwing)
            spreadBorrow(stt.rwing.rightmost, st, nttail, nttrwing.digit > 0)
          case Tip(Zero) =>
            new Spine(s.lwing, s.rwing, Tip(st.lwing))
          case Tip(tip) =>
            spreadBorrow(tip.rightmost, st, Tip(noBorrowPopLast(tip)), false)
        }
      case _ =>
        s
    }
  }

  def popLast[T](conq: Conqueue[T], onFix: () => Unit = doNothing): Conqueue[T] = {
    (conq: @unchecked) match {
      case s: Spine[T] =>
        if (s.rwing.digit > 1) {
          Spine.withSameTail(s, s.lwing, noBorrowPopLast(s.rwing))
        } else {
          (s.rear: @unchecked) match {
            case st: Spine[T] => // note: s is at rank 0
              val trightmost = st.rwing.rightmost
              val nrwing = Two(trightmost.left, trightmost.right)
              val ntrwing = noBorrowPopLast(st.rwing)
              val ntail = Spine.withSameTail(st, st.lwing, ntrwing)
              val nspine = new Spine(s.lwing, nrwing, ntail)
              if (ntrwing.digit > 0) nspine else fixRight(nspine, onFix)
            case Tip(Zero) =>
              Tip(s.lwing)
            case Tip(tip) =>
              val rightmost = tip.rightmost
              val nrwing = Two(rightmost.left, rightmost.right)
              val ntip = Tip(noBorrowPopLast(tip))
              new Spine(s.lwing, nrwing, ntip)
          }
        }
      case Tip(tip) =>
        Tip(noBorrowPopLast(tip))
    }
  }

  def popLastTop[T](
    conq: Conqueue[T], onFix: () => Unit = doNothing
  ): Conqueue[T] = conq match {
    case Conqueue.Lazy(lstack, queue, rstack) =>
      val nqueue = popLast(queue, onFix)
      val nlstack = pay(lstack, 2)
      val nrstack = pay(nqueue.addIfUnevaluated(rstack), 2)
      Conqueue.Lazy(nlstack, nqueue, nrstack)
    case _ =>
      popLast(conq, onFix)
  }

  def last[T](conq: Conqueue[T]): Leaf[T] = {
    @tailrec def rightmost(c: Conc[T]): Leaf[T] = c match {
      case Empty => invalid("Num should never have a Zero.")
      case l: Leaf[T] => l
      case _ <> _ => rightmost(c.right)
      case _ => invalid("Invalid conqueue state: " + c.getClass.getSimpleName)
    }

    (conq: @unchecked) match {
      case s: Spine[T] =>
        rightmost(s.rwing.rightmost)
      case Tip(Zero) =>
        null
      case Tip(tip) =>
        rightmost(tip.rightmost)
      case Lazy(_, queue, _) =>
        last(queue)
    }
  }

  @tailrec def normalizeLeftWingsAndTip[T](
    conq: Conqueue[T], front: Conc[T]
  ): Conc[T] = {
    @tailrec def wrapUntil(
      s: Spine[T], wrapped: Conc[T], level: Int
    ): (Conc[T], Conqueue[T]) = {
      if (wrapped.level >= level) (wrapped, s)
      else {
        val nwrapped = wrapped <> s.lwing.normalized
        (s.rear: @unchecked) match {
          case st: Spine[T] => wrapUntil(st, nwrapped, level)
          case Tip(tip) => (nwrapped, s.rear)
        }
      }
    }

    (conq: @unchecked) match {
      case s: Spine[T] =>
        val (wrapped, remaining) = wrapUntil(s, Conc.Empty, math.max(1, front.level))
        normalizeLeftWingsAndTip(remaining, front <> wrapped)
      case Tip(tip) =>
        front <> tip.normalized
    }
  }

  @tailrec def normalizeRightWings[T](conq: Conqueue[T], back: Conc[T]): Conc[T] = {
    @tailrec def wrapUntil(
      s: Spine[T], wrapped: Conc[T], level: Int
    ): (Conc[T], Conqueue[T]) = {
      if (wrapped.level >= level) (wrapped, s)
      else {
        val nwrapped = s.rwing.normalized <> wrapped
        (s.rear: @unchecked) match {
          case st: Spine[T] => wrapUntil(st, nwrapped, level)
          case Tip(tip) => (nwrapped, Tip(Zero))
        }
      }
    }

    (conq: @unchecked) match {
      case s: Spine[T] =>
        val (wrapped, remaining) = wrapUntil(s, Conc.Empty, math.max(1, back.level))
        normalizeRightWings(remaining, wrapped <> back)
      case Tip(tip) =>
        back
    }
  }

  def toLazyConqueue[T](xs: Conc[T]): Conqueue.Lazy[T] = Lazy(Nil, toConqueue(xs), Nil)

  def toConqueue[T](xs: Conc[T], log: Log = noLog): Conqueue[T] = xs match {
    case conq: Conqueue[T] => conq
    case Append(_, _) => toConqueue(xs.normalized)
    case num: Num[T] => toConqueue(num.normalized)
    case Empty => Tip(Zero)
    case leaf: Leaf[T] => Tip(One(leaf))
    case xs @ _ <> _ => unwrap(xs, log)
  }

  case class Partial[T](rank: Int, bucket: List[Conc[T]], stack: List[Num[T]]) {
    def lborrow(): Partial[T] = {
      if (stack.head.digit == 1) copy(stack = stack.tail, rank = rank - 1)
      else copy(stack = noBorrowPopLast(stack.head) :: stack.tail)
    }
    def rborrow(): Partial[T] = {
      if (stack.head.digit == 1) copy(stack = stack.tail, rank = rank - 1)
      else copy(stack = noBorrowPopHead(stack.head) :: stack.tail)
    }
  }

  private def unwrap[T](xs: <>[T], log: Log = noLog): Conqueue[T] = {
    def zip(rank: Int, lstack: List[Num[T]], rstack: List[Num[T]]): Conqueue[T] =
      ((lstack, rstack): @unchecked) match {
        case (lwing :: Nil, Nil) =>
          assert(lwing.leftmost.level == rank)
          Tip(lwing)
        case (Nil, rwing :: Nil) =>
          assert(rwing.rightmost.level == rank)
          Tip(rwing)
        case (lwing :: Nil, rwing :: Nil) =>
          assert(lwing.leftmost.level == rank)
          assert(rwing.rightmost.level == rank)
          new Spine(lwing, rwing, Tip(Zero))
        case (lwing :: ltail, rwing :: rtail) =>
          new Spine(lwing, rwing, zip(rank + 1, ltail, rtail))
      }

    //def unwrap = unwrap1 _
    @tailrec
    def unwrap(
      lstack: List[Num[T]], rstack: List[Num[T]], rem: Conqueue[Conc[T]]
    ): (List[Num[T]], List[Num[T]]) = {
      if (rem.isEmpty) (lstack.reverse, rstack.reverse)
      else if (lstack.length < rstack.length) {
        val remhead = rem.head
        if (
          (lstack.nonEmpty && lstack.head.rightmost.level < remhead.level) ||
          (lstack.isEmpty && remhead.level > 0)
        ) {
          val nrem = remhead.left +: remhead.right +: rem.tail
          unwrap(lstack, rstack, nrem)
        } else (lstack: @unchecked) match {
          case Three(_1, _2, _3) :: ltail =>
            val added = _3 <> remhead
            if (added.level == _3.level)
              unwrap(Three(_1, _2, added) :: ltail, rstack, rem.tail)
            else unwrap(One(added) :: Two(_1, _2) :: ltail, rstack, rem.tail)
          case Two(_1, _2) :: ltail =>
            val added = _2 <> remhead
            if (added.level == _2.level)
              unwrap(Two(_1, added) :: ltail, rstack, rem.tail)
            else unwrap(One(added) :: One(_1) :: ltail, rstack, rem.tail)
          case One(_1) :: Nil =>
            val added = _1 <> remhead
            unwrap(Two(added.left, added.right) :: Nil, rstack, rem.tail)
          case One(_1) :: num :: ltail =>
            val added = _1 <> remhead
            val shaken = if (added.level == _1.level) added else shakeRight(added)
            if (shaken.level == _1.level)
              unwrap(One(shaken) :: num :: ltail, rstack, rem.tail)
            else if (shaken.left.level == shaken.right.level)
              unwrap(Two(shaken.left, shaken.right) :: num :: ltail, rstack, rem.tail)
            else num match {
              case Three(n1, n2, n3) =>
                unwrap(
                  Two(n3 <> shaken.left, shaken.right) :: Two(n1, n2) :: ltail,
                  rstack, rem.tail)
              case num =>
                unwrap(
                  One(shaken.right) :: noCarryPushLast(num, shaken.left) :: ltail,
                  rstack, rem.tail)
            }
          case Nil =>
            unwrap(One(remhead) :: Nil, rstack, rem.tail)
        }
      } else {
        val remlast = rem.last
        if (
          (rstack.nonEmpty && rstack.head.leftmost.level < remlast.level) ||
          (rstack.isEmpty && remlast.level > 0)
        ) {
          val nrem = rem.init :+ remlast.left :+ remlast.right
          unwrap(lstack, rstack, nrem)
        } else (rstack: @unchecked) match {
          case Three(_1, _2, _3) :: rtail =>
            val added = remlast <> _1
            if (added.level == _1.level)
              unwrap(lstack, Three(added, _2, _3) :: rtail, rem.init)
            else unwrap(lstack, One(added) :: Two(_2, _3) :: rtail, rem.init)
          case Two(_1, _2) :: rtail =>
            val added = remlast <> _1
            if (added.level == _1.level)
              unwrap(lstack, Two(added, _2) :: rtail, rem.init)
            else unwrap(lstack, One(added) :: One(_2) :: rtail, rem.init)
          case One(_1) :: Nil =>
            val added = remlast <> _1
            unwrap(lstack, Two(added.left, added.right) :: Nil, rem.init)
          case One(_1) :: num :: ltail =>
            val added = remlast <> _1
            val shaken = if (added.level == _1.level) added else shakeLeft(added)
            if (shaken.level == _1.level)
              unwrap(lstack, One(shaken) :: num :: ltail, rem.init)
            else if (shaken.left.level == shaken.right.level)
              unwrap(lstack, Two(shaken.left, shaken.right) :: num :: ltail, rem.init)
            else num match {
              case Three(n1, n2, n3) =>
                unwrap(lstack,
                  Two(shaken.left, shaken.right <> n1) :: Two(n2, n3) :: ltail,
                  rem.init)
              case num =>
                unwrap(lstack,
                  One(shaken.left) :: noCarryPushHead(num, shaken.right) :: ltail,
                  rem.init)
            }
          case Nil =>
            unwrap(lstack, One(remlast) :: Nil, rem.init)
        }
      }
    }

    val (lwings, rwings) = unwrap(Nil, Nil, Tip(One(new Single(xs))))
    zip(0, lwings, rwings)
  }

  def split[@specialized(Int, Long, Float, Double) T: ClassTag](
    xs: Conc[T], n: Int, rref: ObjectRef[Conc[T]]
  ): Conc[T] = (xs.normalized: @unchecked) match {
    case left <> right =>
      if (n < left.size) {
        val ll = split(left, n, rref)
        val lr = rref.elem
        rref.elem = lr <> right
        ll
      } else if (n > left.size) {
        val rl = split(right, n - left.size, rref)
        val rr = rref.elem
        rref.elem = rr
        left <> rl
      } else {
        rref.elem = right
        left
      }
    case s: Single[T] =>
      if (n == 0) {
        rref.elem = s
        Empty
      } else {
        rref.elem = Empty
        s
      }
    case c: Chunk[T] =>
      def subchunk(from: Int, sz: Int) = {
        if (sz == 0) Empty
        else new Chunk(copiedArray(c.array, from, sz), sz, c.k)
      }
      val lelems = n
      val relems = c.size - n
      val ltree = subchunk(0, n)
      val rtree = subchunk(n, c.size - n)
      rref.elem = rtree
      ltree
    case Empty =>
      rref.elem = Empty
      Empty
    case _ =>
      invalid("All cases should have been covered: " + xs + ", " + xs.getClass)
  }

  def isEmptyConqueue[T](conqueue: Conqueue[T]): Boolean = conqueue match {
    case Lazy(_, Tip(Zero), _) => true
    case Tip(Zero) => true
    case _ => false
  }

  trait Log {
    def apply(x: AnyRef): Unit
    def on: Boolean
    def clear() {}
    def flush() {}
  }

  object noLog extends Log {
    def apply(x: AnyRef) {}
    def on = false
  }

  object printLog extends Log {
    def apply(x: AnyRef) = println(x.toString)
    def on = true
  }

  def bufferedLog(proxy: Log) = new Log {
    val buffer = collection.mutable.Buffer[String]()
    def apply(x: AnyRef) = buffer += x.toString
    def on = true
    override def clear() = buffer.clear()
    override def flush() {
      proxy(buffer.mkString("\n"))
      clear()
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy