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

de.sciss.lucre.data.HASkipList.scala Maven / Gradle / Ivy

Go to download

Extension of Scala-STM, adding optional durability layer, and providing API for confluent and reactive event layers

The newest version!
/*
 *  HASkipList.scala
 *  (Lucre)
 *
 *  Copyright (c) 2009-2019 Hanns Holger Rutz. All rights reserved.
 *
 *  This software is published under the GNU Lesser General Public License v2.1+
 *
 *
 *  For further information, please contact Hanns Holger Rutz at
 *  [email protected]
 */

package de.sciss.lucre.data

import de.sciss.lucre.stm.impl.MutableImpl
import de.sciss.lucre.stm.{Base, Sink}
import de.sciss.serial.{DataInput, DataOutput, Serializer}

import scala.annotation.{switch, tailrec}
import scala.collection.immutable.{IndexedSeq => Vec, Set => ISet}
import scala.collection.mutable

/** A transactional version of the deterministic k-(2k+1) top-down operated skip list
  * as described in T. Papadakis, Skip Lists and Probabilistic Analysis of
  * Algorithms. Ch. 4 (Deterministic Skip Lists), pp. 55--78. Waterloo (CA) 1993
  *
  * It uses the horizontal array technique with a parameter for k (minimum gap size).
  * It uses a modified top-down removal algorithm that avoids the need for a second
  * pass as in the original algorithm, and is careful about object creations, so that
  * it will be able to persist the data structure without any unnecessary reads or
  * writes to the store.
  *
  * Three implementation notes: (1) We treat the nodes as immutable at the moment, storing them
  * directly in the S#Val child pointers of their parents. While this currently seems to
  * have a performance advantage (?), we could try to avoid this by using S#Refs for
  * the child pointers, making the nodes become mutable. We could avoid copying the
  * arrays for each insertion or deletion, at the cost of more space, but maybe better
  * performance.
  *
  * (2) The special treatment of `isRight` kind of sucks. Since now that information is
  * also persisted, we might just have two types of branches and leaves, and avoid passing
  * around this flag.
  *
  * (3) Since there is a bug with the top-down one-pass removal, we might end up removing
  * the creation of instances of virtual branches altogether again when replacing the
  * current algorithm by a two-pass one.
  *
  * TODO: nodes or at least leaves should be horizontally connected for a faster iterator
  *       and fast pair (interval) search
  */
object HASkipList {
  private def opNotSupported: Nothing = sys.error("Operation not supported")

  private final val SER_VERSION = 76

  private final class SetSer[S <: Base[S], A](keyObserver: SkipList.KeyObserver[S#Tx, A])
                                            (implicit ordering: Ordering[S#Tx, A],
                                             keySerializer: Serializer[S#Tx, S#Acc, A])
    extends Serializer[S#Tx, S#Acc, HASkipList.Set[S, A]] {

    def read(in: DataInput, access: S#Acc)(implicit tx: S#Tx): HASkipList.Set[S, A] =
      HASkipList.Set.read[S, A](in, access, keyObserver)

    def write(list: HASkipList.Set[S, A], out: DataOutput): Unit = list.write(out)

    override def toString = "HASkipList.Set.serializer"
  }

  private final class MapSer[S <: Base[S], A, B](keyObserver: SkipList.KeyObserver[S#Tx, A])
                                               (implicit ordering: Ordering[S#Tx, A],
                                                keySerializer: Serializer[S#Tx, S#Acc, A],
                                                valueSerializer: Serializer[S#Tx, S#Acc, B])
    extends Serializer[S#Tx, S#Acc, HASkipList.Map[S, A, B]] {
    def read(in: DataInput, access: S#Acc)(implicit tx: S#Tx): HASkipList.Map[S, A, B] =
      HASkipList.Map.read[S, A, B](in, access, keyObserver)

    def write(list: HASkipList.Map[S, A, B], out: DataOutput): Unit = list.write(out)

    override def toString = "HASkipList.Map.serializer"
  }

  def debugFindLevel[S <: Base[S], A](list: SkipList[S, A, _], key: A)(implicit tx: S#Tx): Int = list match {
    case impl0: Impl[S, A, _] =>
      val h = impl0.height

      @tailrec
      def stepRight[E](impl: Impl[S, A, E], n: Node[S, A, E], lvl: Int): Int = {
        val idx = impl.indexInNodeR(key, n)
        if (idx < 0) lvl
        else if (n.isLeaf) -1
        else {
          val c = n.asBranch.down(idx)
          if (idx < n.size - 1) stepLeft(impl, c, lvl - 1) else stepRight(impl, c, lvl - 1)
        }
      }

      @tailrec
      def stepLeft[E](impl: Impl[S, A, E], n: Node[S, A, E], lvl: Int): Int = {
        val idx = impl.indexInNodeL(key, n)
        if (idx < 0) lvl else if (n.isLeaf) -1 else stepLeft(impl, n.asBranch.down(idx), lvl - 1)
      }

      val c = impl0.top.orNull // topN
      if (c eq null) -1 else stepRight(impl0, c, h)

    case _ => sys.error(s"Not a HA Skip List: $list")
  }

  private final class SetImpl[S <: Base[S], A](val id: S#Id, val minGap: Int,
                                              protected val keyObserver: SkipList.KeyObserver[S#Tx, A],
                                              _downNode: SetImpl[S, A] => S#Var[Node[S, A, A]])
                                             (implicit val ordering: Ordering[S#Tx, A],
                                              val keySerializer: Serializer[S#Tx, S#Acc, A])
    extends Impl[S, A, A] with HASkipList.Set[S, A] {

    protected val downNode: S#Var[Node[S, A, A]] = _downNode(this)

    override def toString = s"SkipList.Set$id"

    def add   (key: A)(implicit tx: S#Tx): Boolean = addEntry(key, key).isEmpty
    def remove(key: A)(implicit tx: S#Tx): Boolean = removeEntry(key).isDefined

    def firstKey(implicit tx: S#Tx): A = head
    def lastKey (implicit tx: S#Tx): A = last

    def +=(key: A)(implicit tx: S#Tx): this.type = {
      addEntry(key, key)
      this
    }

    protected def newLeaf(key: A): Leaf[S, A, A] = {
      val lKeys = Vector[A](key, null.asInstanceOf[A])
      new SetLeaf[S, A](lKeys)
    }

    def writeEntry(key: A, out: DataOutput): Unit = keySerializer.write(key, out)

    protected def readLeaf(in: DataInput, access: S#Acc, isRight: Boolean)
                          (implicit tx: S#Tx): Leaf[S, A, A] = {
      val sz    = in.readByte()
      val szi   = if (isRight) sz - 1 else sz
      val keys  = Vector.tabulate[A](sz) { i =>
        if (i < szi) keySerializer.read(in, access) else null.asInstanceOf[A]
      }
      new SetLeaf[S, A](keys)
    }
  }

  private final class MapImpl[S <: Base[S], A, B](val id: S#Id, val minGap: Int,
                                                  protected val keyObserver: SkipList.KeyObserver[S#Tx, A],
                                                  _downNode: MapImpl[S, A, B] => S#Var[Map.Node[S, A, B]])
                                                 (implicit val ordering : Ordering  [S#Tx, A],
                                                  val keySerializer     : Serializer[S#Tx, S#Acc, A],
                                                  val valueSerializer   : Serializer[S#Tx, S#Acc, B])
    extends Impl[S, A, (A, B)] with HASkipList.Map[S, A, B] {

    protected val downNode: S#Var[Map.Node[S, A, B]] = _downNode(this)

    override def toString = s"SkipList.Map$id"

    def put(key: A, value: B)(implicit tx: S#Tx): Option[B] = addEntry(key, (key, value)).map(_._2)

    def remove(key: A)(implicit tx: S#Tx): Option[B] = removeEntry(key).map(_._2)

    def firstKey(implicit tx: S#Tx): A = head._1
    def lastKey (implicit tx: S#Tx): A = last._1

    def +=(entry: (A, B))(implicit tx: S#Tx): this.type = {
      addEntry(entry._1, entry)
      this
    }

    def writeEntry(entry: (A, B), out: DataOutput): Unit = {
      keySerializer  .write(entry._1, out)
      valueSerializer.write(entry._2, out)
    }

    protected def newLeaf(entry: (A, B)): Leaf[S, A, (A, B)] = {
      val en = Vector(entry, null.asInstanceOf[(A, B)])
      new MapLeaf[S, A, B](en)
    }

    def keysIterator(implicit tx: S#Tx): Iterator[A] = {
      val i = new KeyIteratorImpl
      i.init()
      i
    }

    def valuesIterator(implicit tx: S#Tx): Iterator[B] = {
      val i = new ValueIteratorImpl
      i.init()
      i
    }

    def get(key: A)(implicit tx: S#Tx): Option[B] = {
      @tailrec
      def stepRight(n: Node[S, A, (A, B)]): Option[B] = {
        val idx   = indexInNodeR(key, n)
        val idxP  = if (idx < 0) -(idx + 1) else idx
        if (n.isLeaf) {
          if (idx < 0)
            Some(n.asLeaf.entry(idxP)._2)
          else
            None
        } else {
          val c = n.asBranch.down(idxP)
          if (idxP < n.size - 1) stepLeft(c) else stepRight(c)
        }
      }

      @tailrec
      def stepLeft(n: Node[S, A, (A, B)]): Option[B] = {
        val idx  = indexInNodeL(key, n)
        val idxP = if (idx < 0) -(idx + 1) else idx
        if (n.isLeaf) {
          if (idx < 0)
            Some(n.asLeaf.entry(idxP)._2)
          else
            None
        } else {
          stepLeft(n.asBranch.down(idxP))
        }
      }

      val c = topN
      if (c eq null) None else stepRight(c)
    }

    def getOrElse[B1 >: B](key: A, default: => B1)(implicit tx: S#Tx): B1 =
      get(key).getOrElse(default) // XXX TODO --- could optimize this at some point

    def getOrElseUpdate(key: A, op: => B)(implicit tx: S#Tx): B =
      get(key).getOrElse { // XXX TODO --- could optimize this at some point
        val value = op
        put(key, value)
        value
      }

    private final class KeyIteratorImpl(implicit tx: S#Tx) extends IteratorImpl[A] {
      protected def getValue(l: Leaf[S, A, (A, B)], idx: Int): A = l.key(idx)

      override def toString = "KeyIterator"
    }

    private final class ValueIteratorImpl(implicit tx: S#Tx) extends IteratorImpl[B] {
      protected def getValue(l: Leaf[S, A, (A, B)], idx: Int): B = l.entry(idx)._2

      override def toString = "ValueIterator"
    }

    protected def readLeaf(in: DataInput, access: S#Acc, isRight: Boolean)
                          (implicit tx: S#Tx): Leaf[S, A, (A, B)] = {
      val sz  = in.readByte()
      val szi = if (isRight) sz - 1 else sz
      val en  = Vector.tabulate(sz) { i =>
        if (i < szi) {
          val key   = keySerializer  .read(in, access)
          val value = valueSerializer.read(in, access)
          (key, value)
        } else {
          null.asInstanceOf[(A, B)]
        }
      }
      new MapLeaf[S, A, B](en)
    }
  }

  private sealed trait Impl[S <: Base[S], /* @spec(KeySpec) */ A, E]
    extends HeadOrBranch[S, A, E] with Serializer[S#Tx, S#Acc, Node[S, A, E]] with MutableImpl[S] {
    impl =>

    // ---- abstract ----

    protected def downNode: S#Var[Node[S, A, E]]
    protected def minGap: Int
    protected def ordering: Ordering[S#Tx, A]
    protected def keyObserver: SkipList.KeyObserver[S#Tx, A]

    def keySerializer: Serializer[S#Tx, S#Acc, A]

    def id: S#Id

    def writeEntry(entry: E, out: DataOutput): Unit

    protected def newLeaf(entry: E): Leaf[S, A, E]
    protected def readLeaf(in: DataInput, access: S#Acc, isRight: Boolean)(implicit tx: S#Tx): Leaf[S, A, E]

    // ---- impl ----

    implicit private[this] def head: Impl[S, A, E] = this

    final def         arrMinSz: Int = minGap + 1
    private[this] def arrMaxSz: Int = (minGap + 1) << 1 // aka arrMinSz << 1

    private[this] val hasObserver = keyObserver != SkipList.NoKeyObserver

    protected final def writeData(out: DataOutput): Unit = {
      out.writeByte(SER_VERSION)
      out.writeByte(minGap)
      downNode.write(out)
    }

    final def clear()(implicit tx: S#Tx): Unit = {
      // we just replace `downNode`. for confluent,
      // the updates wouldn't make any difference,
      // anyway. for durable, perhaps we have GC some day...

//      def step(n: Node[S, A, E]): Unit = {
//        if (n.isBranch) {
//          val b   = n.asBranch
//          val bsz = b.size
//          var i = 0; while (i < bsz) {
//            step(b.down(i))
//            b.downRef(i).dispose()
//            i += 1
//          }
//        }
//      }
//
//      val c = topN
//      if (c ne null) {
//        step(c)
        downNode() = null
//      }
    }

    protected final def disposeData()(implicit tx: S#Tx): Unit =
      downNode.dispose()

    def size(implicit tx: S#Tx): Int = {
      val c = topN
      if (c eq null) 0 else c.leafSizeSum - 1
    }

    final def maxGap: Int = (minGap << 1) + 1 // aka arrMaxSz - 1

    final def isEmpty (implicit tx: S#Tx): Boolean = topN eq null
    final def nonEmpty(implicit tx: S#Tx): Boolean = !isEmpty

    final def height(implicit tx: S#Tx): Int = {
      var n = topN
      if (n eq null) 0
      else {
        var h = 1
        while (n.isBranch) {
          n = n.asBranch.down(0)
          h += 1
        }
        h
      }
    }

    final def top(implicit tx: S#Tx): Option[Node[S, A, E]] = Option(topN)

    @inline protected final def topN(implicit tx: S#Tx): Node[S, A, E] = downNode()

    final def debugPrint()(implicit tx: S#Tx): String = topN.printNode(isRight = true).mkString("\n")

    final def toIndexedSeq(implicit tx: S#Tx): Vec [E]  = fillBuilder(Vector.newBuilder)
    final def toList      (implicit tx: S#Tx): List[E]  = fillBuilder(List  .newBuilder)
    final def toSeq       (implicit tx: S#Tx): Seq [E]  = fillBuilder(Seq   .newBuilder)
    final def toSet       (implicit tx: S#Tx): ISet[E]  = fillBuilder(ISet  .newBuilder)

    private[this] def fillBuilder[Res](b: mutable.Builder[E, Res])(implicit tx: S#Tx): Res = {
      val i = iterator
      while (i.hasNext) {
        b += i.next() // Txn
      }
      b.result()
    }

    @tailrec
    private[this] def headImpl(n: Node[S, A, E])(implicit tx: S#Tx): E = {
      if (n.isLeaf) {
        n.asLeaf.entry(0)
      } else {
        headImpl(n.asBranch.down(0))
      }
    }

    final def head(implicit tx: S#Tx): E = {
      val n0 = topN
      if (n0 eq null) throw new NoSuchElementException("head of empty list")
      else headImpl(n0)
    }

    final def headOption(implicit tx: S#Tx): Option[E] = {
      val n0 = topN
      if (n0 eq null) None
      else Some(headImpl(n0))
    }

    @tailrec
    private[this] def lastImpl(n: Node[S, A, E])(implicit tx: S#Tx): E = {
      if (n.isLeaf) {
        n.asLeaf.entry(n.size - 2)  // N.B. we have `null` as terminator
      } else {
        lastImpl(n.asBranch.down(n.size - 1))
      }
    }

    final def last(implicit tx: S#Tx): E = {
      val n0 = topN
      if (n0 eq null) throw new NoSuchElementException("last of empty list")
      else lastImpl(n0)
    }

    final def lastOption(implicit tx: S#Tx): Option[E] = {
      val n0 = topN
      if (n0 eq null) None
      else Some(lastImpl(n0))
    }

  /** Finds the leaf and index in the leaf corresponding to the entry that holds either the given
      * search key or the greatest key in the set smaller than the search key.
      *
      * @param key  the search key
      * @param tx   the current transaction
      * @return     if `Some`, holds the leaf and index for the floor element (whose key is <= the search key),
      *             if `None`, there is no key smaller than or equal to the search key in the list
      */
    final def floor(key: A)(implicit tx: S#Tx): Option[E] = {
      // the algorithm is as follows: find the index of the search key in the current level.
      // if the key was found, just go down straight to the leaf. if not:
      //  - the index points to an element greater than the search key
      //  - if the index is >0, let the key at index-1 be the backup-key, the node is the backup-node
      //  - if a leaf is reached and the index is 0
      //       - if no backup-node exists, return None
      //       - if a backup-node exists, follow that node straight down along the backup-key to the leaf
      // In the worst case, we descend the list twice, still giving O(log n) performance, while
      // saving the effort to horizontally connect the leaves.

      @tailrec
      def straight(n: Node[S, A, E], idx: Int): E = {
        if (n.isLeaf) {
          n.asLeaf.entry(idx)
        } else {
          val c = n.asBranch.down(idx)
          straight(c, c.size - 1)
        }
      }

      @tailrec
      def step(n: Node[S, A, E], _bckNode: Node[S, A, E], _bckIdx: Int, isRight: Boolean): Option[E] = {

        val idx = if (isRight) indexInNodeR(key, n) else indexInNodeL(key, n)

        if (idx < 0) {
          // found
          Some(straight(n, -(idx + 1)))
        } else {
          // not found
          var bckNode = _bckNode
          var bckIdx  = _bckIdx
          if (idx > 0) {
            // new backup exists, because there is an entry smaller than the search key
            bckNode = n
            bckIdx  = idx - 1
          }
          if (n.isLeaf) {
            if (bckNode eq null) None else Some(straight(bckNode, bckIdx))
          } else {
            step(n.asBranch.down(idx), bckNode, bckIdx, isRight && idx == n.size - 1)
          }
        }
      }

      val n0 = topN
      if (n0 eq null) None else step(n0, null, 0, isRight = true)
    }

    /** Finds the leaf and index in the leaf corresponding to the entry that holds either the given
      * search key or the smallest key in the set greater than the search key.
      *
      * @param key  the search key
      * @param tx   the current transaction
      * @return     if `Some`, holds the leaf and index for the ceiling element (whose key is >= the search key),
      *             if `None`, there is no key greater than or equal to the search key in the list
      */
    final def ceil(key: A)(implicit tx: S#Tx): Option[E] = {
      @tailrec
      def step(n: Node[S, A, E], isRight: Boolean): Option[E] = {
        val idx = if (isRight) indexInNodeR(key, n) else indexInNodeL(key, n)
        val idxP = if (idx < 0) -(idx + 1) else idx
        val newRight = isRight && idxP == n.size - 1
        if (n.isLeaf) {
          if (newRight) None else Some(n.asLeaf.entry(idxP))
        } else {
          step(n.asBranch.down(idxP), newRight)
        }
      }
      val c = topN
      if (c eq null) None else step(c, isRight = true)
    }

    final def isomorphicQuery(ord: Ordered[S#Tx, A])(implicit tx: S#Tx): (E, Int) = {
      def isoIndexR(n: Node[S, A, E]): Int = {
        var idx = 0
        val sz = n.size - 1
        do {
          val cmp = ord.compare(n.key(idx))
          if (cmp == 0) return -(idx + 1) else if (cmp < 0) return idx
          idx += 1
        } while (idx < sz)
        sz
      }

      def isoIndexL(n: Node[S, A, E])(implicit tx: S#Tx): Int = {
        @tailrec
        def step(idx: Int): Int = {
          val cmp = ord.compare(n.key(idx))
          if (cmp == 0) -(idx + 1) else if (cmp < 0) idx else step(idx + 1)
        }
        step(0)
      }

      @tailrec
      def stepRight(n: Node[S, A, E]): (E, Int) = {
        val idx = isoIndexR(n)
        val found = idx < 0
        val idxP = if (found) -(idx + 1) else idx
        if (n.isLeaf) {
          val l = n.asLeaf
          if (found) {
            (l.entry(idxP), 0)
          } else if (idxP == l.size - 1) {
            (l.entry(idxP - 1), 1)
          } else {
            (l.entry(idxP), -1)
          }
        } else {
          val c = n.asBranch.down(idxP)
          if (idxP < n.size - 1) stepLeft(c) else stepRight(c)
        }
      }

      @tailrec
      def stepLeft(n: Node[S, A, E]): (E, Int) = {
        val idx = isoIndexL(n)
        val found = idx < 0
        val idxP = if (found) -(idx + 1) else idx
        if (n.isLeaf) {
          val l = n.asLeaf
          (l.entry(idxP), if (found) 0 else -1)
        } else {
          stepLeft(n.asBranch.down(idxP))
        }
      }

      val c = topN
      if (c eq null) {
        throw new NoSuchElementException("isomorphicQuery on an empty list")
      } else {
        stepRight(c)
      }
    }

    // ---- set support ----

    final def contains(v: A)(implicit tx: S#Tx): Boolean = {
      @tailrec
      def stepRight(n: Node[S, A, E]): Boolean = {
        val idx = indexInNodeR(v, n)
        if (idx < 0) true
        else if (n.isLeaf) false
        else {
          val c = n.asBranch.down(idx)
          if (idx < n.size - 1) stepLeft(c) else stepRight(c)
        }
      }

      @tailrec
      def stepLeft(n: Node[S, A, E]): Boolean = {
        val idx = indexInNodeL(v, n)
        if (idx < 0) true else if (n.isLeaf) false else stepLeft(n.asBranch.down(idx))
      }

      val c = topN
      if (c eq null) false else stepRight(c)
    }

    /** Finds the right-most key which
      * is greater than or equal to the query key.
      *
      * @param   key  the key to search for
      * @param   n the branch or leaf from which to go down
      *
      * @return  the index to go down (a node whose key is greater than `key`),
      *         or `-(index+1)` if `key` was found at `index`
      */
    final /*protected */ def indexInNodeR(key: A, n: Node[S, A, E])(implicit tx: S#Tx): Int = {
      var idx = 0
      val sz = n.size - 1
      do {
        val cmp = ordering.compare(key, n.key(idx))
        if (cmp == 0) return -(idx + 1) else if (cmp < 0) return idx
        idx += 1
      } while (idx < sz)
      sz
    }

    final /* protected */ def indexInNodeL(key: A, n: Node[S, A, E])(implicit tx: S#Tx): Int = {
      @tailrec
      def step(idx: Int): Int = {
        val cmp = ordering.compare(key, n.key(idx))
        if (cmp == 0) -(idx + 1) else if (cmp < 0) idx else step(idx + 1)
      }
      step(0)
    }

    protected final def addEntry(key: A, entry: E)(implicit tx: S#Tx): Option[E] = {
      val c = topN
      if (c eq null) {
        val l = newLeaf(entry)
        downNode() = l
        None
      } else if (c.isLeaf) {
        addToLeaf(key, entry, head, 0, head, 0, c.asLeaf, isRight = true)
      } else {
        addToBranch(key, entry, head, 0, head, 0, c.asBranch, isRight = true)
      }
    }

    private[this] def addToLeaf(key: A, entry: E, pp: HeadOrBranch[S, A, E], ppIdx: Int, p: HeadOrBranch[S, A, E],
                                pIdx: Int, l: Leaf[S, A, E], isRight: Boolean)
                               (implicit tx: S#Tx): Option[E] = {
      val idx = if (isRight) indexInNodeR(key, l) else indexInNodeL(key, l)
      if (idx < 0) {
        val idxP = -(idx + 1)
        val oldEntry = l.entry(idxP)
        if (entry != oldEntry) {
          val lNew = l.update(idxP, entry)
          p.updateDown(pIdx, lNew)
        }
        Some(oldEntry)

      } else {
        if (l.size == arrMaxSz) {
          val splitKey  = l.key(minGap)
          val tup       = l.splitAndInsert(idx, entry)
          val left      = tup._1
          val right     = tup._2
          val pNew      = p.insertAfterSplit(pIdx, splitKey, left, right)
          pp.updateDown(ppIdx, pNew)
          if (hasObserver) keyObserver.keyUp(splitKey)
        } else {
          val lNew = l.insert(idx, entry)
          // and overwrite down entry in pn's parent
          p.updateDown(pIdx, lNew)
        }
        None
      }
    }

    @tailrec
    private[this] def addToBranch(key: A, entry: E, pp: HeadOrBranch[S, A, E], ppIdx: Int,
                                  p: HeadOrBranch[S, A, E], pIdx: Int, b: Branch[S, A, E], isRight: Boolean)
                                 (implicit tx: S#Tx): Option[E] = {
      val idx         = if (isRight) indexInNodeR(key, b) else indexInNodeL(key, b)
      val found       = idx < 0
      val idxP        = if (found) -(idx + 1) else idx
      var bNew        = b
      var idxNew      = idxP
      var pNew        = p
      var pIdxNew     = pIdx
      val bsz         = b.size
      val isRightNew  = isRight && idxP == bsz - 1

      if (!found && bsz == arrMaxSz) {
        val splitKey  = b.key(minGap)
        val tup       = b.split
        val left      = tup._1
        val right     = tup._2
        val pbNew     = p.insertAfterSplit(pIdx, splitKey, left, right)
        pNew          = pbNew
        pp.updateDown(ppIdx, pbNew)
        val mns       = arrMinSz
        if (idx < mns) {
          bNew        = left
        } else {
          bNew        = right
          pIdxNew    += 1
          idxNew     -= mns
        }
        if (hasObserver) keyObserver.keyUp(splitKey)
      }
      val c = bNew.down(idxNew)
      if (c.isLeaf) {
        addToLeaf  (key, entry, pNew, pIdxNew, bNew, idxNew, c.asLeaf,   isRightNew)
      } else {
        addToBranch(key, entry, pNew, pIdxNew, bNew, idxNew, c.asBranch, isRightNew)
      }
    }

    final def -=(key: A)(implicit tx: S#Tx): this.type = {
      removeEntry(key); this
    }

    protected final def removeEntry(key: A)(implicit tx: S#Tx): Option[E] = {
      val c = topN
      if (c eq null) {
        None
      } else if (c.isLeaf) {
        removeFromLeaf  (key, downNode, c.asLeaf  , isRight = true, lDirty = false)
      } else {
        removeFromBranch(key, downNode, c.asBranch, isRight = true, bDirty = false)
      }
    }

    private[this] def removeFromLeaf(key: A, pDown: Sink[S#Tx, Node[S, A, E]], l: Leaf[S, A, E],
                                     isRight: Boolean, lDirty: Boolean)(implicit tx: S#Tx): Option[E] = {
      val idx   = if (isRight) indexInNodeR(key, l) else indexInNodeL(key, l)
      val found = idx < 0
      if (found) {
        val idxP = -(idx + 1)
        val lNew = l.removeColumn(idxP)
        pDown()  = if (lNew.size > 1) lNew else null
        Some(l.entry(idxP))
      } else {
        if (lDirty) {
          pDown() = if (l.size > 1) l else null
        }
        None
      }
    }

    @tailrec
    private[this] def removeFromBranchAndBubble(key: A, pDown: Sink[S#Tx, Node[S, A, E]], b: Branch[S, A, E],
                                                leafUpKey: A)(implicit tx: S#Tx): Option[E] = {
      val bsz       = b.size
      val idxP      = bsz - 1 // that we know
      val mns       = arrMinSz
      val c         = b.down(idxP)
      val cSz       = c.size

      var bNew      = null: Branch[S, A, E]
      var bDownIdx  = idxP
      var cNew      = c

      if (hasObserver) keyObserver.keyDown(key)

      // a merge or borrow is necessary either when we descend
      // to a minimally filled child (because that child might
      // need to shrink in the next step)
      if (cSz == mns) {
        // merge with or borrow from the left
        val idxPM1  = idxP - 1
        val cSib    = b.down(idxPM1)
        val cSibSz  = cSib.size

        val downKey = b.key(idxPM1)
        if (hasObserver) keyObserver.keyDown(downKey)

        if (cSibSz == mns) {
          // merge with the left
          // The parent needs to remove the
          // entry of the left sibling.
          val bNew0   = b.removeColumn(idxPM1)
          bNew        = bNew0.updateKey(idxPM1, leafUpKey) // XXX optimise by merging with previous operation
          b.downRef(idxPM1).dispose()
          bDownIdx    = idxPM1
          cNew        = c.mergeLeft(cSib)
        } else {
          // borrow from the left
          // the parent needs to update the key for the
          // left sibling to match the before-last key in
          // the left sibling.
          val upKey   = cSib.key(cSibSz - 2)
          val bNew0   = b.updateKey(idxPM1, upKey)
          bNew        = bNew0.updateKey(idxP, leafUpKey) // XXX optimise by merging with previous operation
          if (hasObserver) keyObserver.keyUp(upKey)
          val bDown1  = b.downRef(idxPM1)
          bDown1()    = cSib.removeColumn(cSibSz - 1)
          cNew        = c.borrowLeft(cSib)
        }
      } else {
        bNew = b.updateKey(idxP, leafUpKey)
      }

      if (hasObserver) keyObserver.keyUp(leafUpKey)

      // branch changed
      val bDown = if (bNew.size > 1) {
        pDown() = bNew // update down ref from which it came
        bNew.downRef(bDownIdx)
      } else {
        // unfortunately we do not have `p`
        //               assert( p == Head )
        bNew.downRef(0).dispose()
        pDown
      }

      if (cNew.isLeaf) {
        removeFromLeaf           (key, bDown, cNew.asLeaf, isRight = false, cNew ne c)
      } else {
        removeFromBranchAndBubble(key, bDown, cNew.asBranch, leafUpKey)
      }
    }

    @tailrec
    private[this] def removeFromBranch(key: A, pDown: Sink[S#Tx, Node[S, A, E]], b: Branch[S, A, E],
                                       isRight: Boolean, bDirty: Boolean)(implicit tx: S#Tx): Option[E] = {
      val idx   = if (isRight) indexInNodeR(key, b) else indexInNodeL(key, b)
      val found = idx < 0
      val idxP  = if (found) -(idx + 1) else idx
      val bsz   = b.size
      val mns   = arrMinSz
      val c     = b.down(idxP)
      val cSz   = /* if( cFound ) c.size - 1 else */ c.size

      // if v is found, it will appear in right-most position in all following children.
      // there are two possibilities:
      // (1) a borrow-from-right or merge-with-right is performed. in this case,
      //     v is overwritten in the current branch, thus keep going normally
      //     (no need to specially treat the branch).
      // (2) none of these two operations are performed (because either the child size
      //     is greater than minimum, or v appears in right-most position in b (causing a left op).
      //     -- is this second case possible? no, because we would have encountered
      //     case (1) in the previous iteration, that is, a borrow-from-right or merge-
      //     with-right would have been performed, and thus v cannot appear in right-most
      //     position, and there cannot be a left op.
      // Therefore, we only need to specially treat case (2), that is `cSz > mns`!
      if (found && cSz > mns) {
        // we are here, because the key was found and it would appear in the right-most position
        // in the child, unless we treat it specially here, by finding the key that will bubble
        // up!
        @tailrec def findUpKey(n: Node[S, A, E]): A = {
          if (n.isLeaf) {
            n.key(n.size - 2)
          } else {
            findUpKey(n.asBranch.down(n.size - 1))
          }
        }
        val leafUpKey = findUpKey(c)
        if (hasObserver) keyObserver.keyDown(key)
        val bNew      = b.updateKey(idxP, leafUpKey)
        if (hasObserver) keyObserver.keyUp(leafUpKey)

        pDown()       = bNew // update down ref from which we came
        val bDown     = bNew.downRef(idxP)
        return if (c.isLeaf) {
          removeFromLeaf           (key, bDown, c.asLeaf, isRight = false, lDirty = false)
        } else {
          removeFromBranchAndBubble(key, bDown, c.asBranch, leafUpKey)
        }
      }

      var isRightNew  = isRight && idxP == bsz - 1
      var bNew        = b
      var bDownIdx    = idxP
      var cNew        = c

      // a merge or borrow is necessary either when we descend
      // to a minimally filled child (because that child might
      // need to shrink in the next step)
      if (cSz == mns) {
        val idxP1     = idxP + 1
        val bHasRight = idxP1 < bsz
        if (bHasRight) {
          // merge with or borrow from/to the right
          val cSib      = b.down(idxP1)
          val cSibSz    = cSib.size
          val mergedSz  = cSz + cSibSz

          val downKey   = b.key(idxP)
          if (hasObserver) keyObserver.keyDown(downKey)

          if (mergedSz <= arrMaxSz) {
            // merge with the right
            // remove the entry at idxP from the branch,
            // and actualise b with virtual sibling. the key
            // at bNew's index idxP is now the one formerly at
            // idxP1, hence the right-most key in cSib.
            bNew        = b.removeColumn(idxP)
            b.downRef(idxP).dispose()
            cNew        = c.mergeRight(cSib)
            isRightNew  = isRight && idxP == bsz - 2 // ! we might be in the right-most branch now
          } else {
            // borrow from the right
            assert(cSibSz > mns)
            // update the key index idxP of the
            // originating sibling to match the first key in
            // the right sibling
            val upKey   = cSib.key(0)
            bNew        = b.updateKey(idxP, upKey)
            if (hasObserver) keyObserver.keyUp(upKey)
            val bDown1  = b.downRef(idxP1)
            bDown1()    = cSib.removeColumn(0)
            cNew        = c.borrowRight(cSib)
          }

        } else {    // merge with or borrow from the left
          // it implies that if cFound is true, cIdx is < c.size - 1
          // that is, the key is not in the last element of c
          // (because otherwise, b would have already in its
          // virtualization be merged to or have borrowed from its right sibling)

          val idxPM1  = idxP - 1
          val cSib    = b.down(idxPM1)
          val cSibSz  = cSib.size

          val downKey = b.key(idxPM1)
          if (hasObserver) keyObserver.keyDown(downKey)

          if (cSibSz == mns) {    // merge with the left
            // The parent needs to remove the
            // entry of the left sibling.
            bNew      = b.removeColumn(idxPM1)
            b.downRef(idxPM1).dispose()
            bDownIdx  = idxPM1
            cNew      = c.mergeLeft(cSib)
          } else {                // borrow from the left
            // the parent needs to update the key for the
            // left sibling to match the before-last key in
            // the left sibling.
            val upKey   = cSib.key(cSibSz - 2)
            bNew        = b.updateKey(idxPM1, upKey)
            if (hasObserver) keyObserver.keyUp(upKey)
            val bDown1  = b.downRef(idxPM1)
            bDown1()    = cSib.removeColumn(cSibSz - 1)
            cNew        = c.borrowLeft(cSib)
          }
        }
      }

      val bDown = if (bDirty || (bNew ne b)) {  // branch changed
        if (bNew.size > 1) {
          pDown() = bNew // update down ref from which it came
          bNew.downRef(bDownIdx)
        } else {
          // unfortunately we do not have `p`
          //               assert( p == Head )
          bNew.downRef(0).dispose()
          pDown
        }
      } else {
        bNew.downRef(bDownIdx)
      }

      //         val bDown = bNew.downRef( bDownIdx )
      val cDirty = cNew ne c
      if (cNew.isLeaf) {
        removeFromLeaf  (key, bDown, cNew.asLeaf  , isRight = isRightNew, lDirty = cDirty)
      } else {
        removeFromBranch(key, bDown, cNew.asBranch, isRight = isRightNew, bDirty = cDirty)
      }
    }

    final def iterator(implicit tx: S#Tx): Iterator[E] = {
      val i = new EntryIteratorImpl
      i.init()
      i
    }

    // ---- Serializer[ S#Tx, S#Acc, Node[ S, A ]] ----
    def write(v: Node[S, A, E], out: DataOutput): Unit =
      if (v eq null) {
        out.writeByte(0) // Bottom
      } else {
        v.write(out)
      }

    def read(in: DataInput, access: S#Acc)(implicit tx: S#Tx): Node[S, A, E] = {
      (in.readByte(): @switch) match {
        case 0 => null // .asInstanceOf[ Branch[ S, A ]]
        case 1 => Branch.read(in, access, isRight = false)
        case 2 => readLeaf   (in, access, isRight = false)
        case 5 => Branch.read(in, access, isRight = true )
        case 6 => readLeaf   (in, access, isRight = true )
      }
    }

    private[this] final class EntryIteratorImpl(implicit tx: S#Tx) extends IteratorImpl[E] {
      protected def getValue(l: Leaf[S, A, E], idx: Int): E = l.entry(idx)

      override def toString = "Iterator"
    }

    protected sealed abstract class IteratorImpl[C](implicit tx: S#Tx) extends Iterator[C] {
      private[this] var l: Leaf[S, A, E]  = _
      private[this] var nextValue: C      = _
      private[this] var isRight           = true
      private[this] var idx               = 0
      private[this] var stack             = List.empty[(Branch[S, A, E], Int, Boolean)]

      override def toString = s"$impl.iterator"

      protected def getValue(l: Leaf[S, A, E], idx: Int): C

      @tailrec
      private[this] def pushDown(n: Node[S, A, E], idx0: Int, r: Boolean)(implicit tx: S#Tx): Unit =
        if (n.isLeaf) {
          val l2    = n.asLeaf
          l         = l2
          idx       = 0
          isRight   = r
          nextValue = getValue(l2, 0) // l2.key( 0 )
        } else {
          val b     = n.asBranch
          stack ::= ((b, idx0 + 1, r))
          pushDown(b.down(idx0), 0, r && idx0 == b.size - 1)
        }

      def init()(implicit tx: S#Tx): Unit = {
        val c = topN
        if (c ne null) pushDown(c, 0, r = true)
      }

      def hasNext: Boolean = l ne null // ordering.nequiv( nextKey, maxKey )

      def next(): C = {
        if (!hasNext) throw new java.util.NoSuchElementException("next on empty iterator")
        val res = nextValue
        idx += 1
        if (idx == (if (isRight) l.size - 1 else l.size) /* || ordering.equiv( l.key( idx ), maxKey ) */ ) {
          @tailrec def popUp(): Unit =
            if (stack.isEmpty) {
              l = null
              nextValue = null.asInstanceOf[C] // maxKey
            } else {
              val (b, i, r) :: tail = stack
              stack = tail
              if (i < b.size) {
                pushDown(b, i, r)
              } else {
                popUp()
              }
            }

          popUp()

        } else {
          nextValue = getValue(l, idx) // l.key( idx )
        }
        res
      }
    }

    def updateDown(i: Int, n: Node[S, A, E])(implicit tx: S#Tx): Unit = {
      if (i != 0) throw new IndexOutOfBoundsException(i.toString)
      downNode() = n
    }

    def insertAfterSplit(pIdx: Int, splitKey: A, left: Node[S, A, E], right: Node[S, A, E])
                        (implicit tx: S#Tx, head: Impl[S, A, E]): Branch[S, A, E] = {
      val bKeys  = Vector[A](splitKey, null.asInstanceOf[A])
      val bDowns = Vector[S#Var[Node[S, A, E]]](
        tx.newVar(head.id, left),
        tx.newVar(head.id, right)
      )
      new Branch[S, A, E](bKeys, bDowns) // new parent branch
    }
  }

  sealed trait HeadOrBranch[S <: Base[S], A, E] /* extends Branch */ {
    private[HASkipList] def updateDown(i: Int, n: Node[S, A, E])(implicit tx: S#Tx): Unit

    private[HASkipList] def insertAfterSplit(pIdx: Int, splitKey: A, left: Node[S, A, E], right: Node[S, A, E])
                                            (implicit tx: S#Tx, list: Impl[S, A, E]): Branch[S, A, E]
  }

  sealed trait Node[S <: Base[S], A, E] {
    private[HASkipList] def removeColumn(idx: Int)(implicit tx: S#Tx, list: Impl[S, A, E]): Node[S, A, E]

    def size: Int
    def key(i: Int): A

    private[HASkipList] def write(out: DataOutput)(implicit list: Impl[S, A, E]): Unit

    private[HASkipList] def leafSizeSum(implicit tx: S#Tx): Int

    private[HASkipList] def printNode(isRight: Boolean)(implicit tx: S#Tx): Vec[String]

    /*
     * In merge-with-right, the right sibling's
     * identifier is re-used for the merged node.
     * Thus after the merge, the originating sibling
     * should be disposed (when using an ephemeral
     * data store). The parent needs to remove the
     * entry of the originating sibling.
     *
     * (thus the disposal corresponds with the ref
     * removed from the `downs` array)
     */
    private[HASkipList] def mergeRight(sib: Node[S, A, E])(implicit tx: S#Tx): Node[S, A, E]

    /*
     * In borrow-from-right, both parents' downs need
     * update, but identifiers are kept.
     * the parent needs to update the key for the
     * originating sibling to match the first key in
     * the right sibling (or the new last key in the
     * originating sibling).
     */
    private[HASkipList] def borrowRight(sib: Node[S, A, E])(implicit tx: S#Tx): Node[S, A, E]

    /*
     * In merge-with-left, the originating sibling's
     * identifier is re-used for the merged node.
     * Thus after the merge, the left sibling
     * should be disposed (when using an ephemeral
     * data store). The parent needs to remove the
     * entry of the left sibling.
     *
     * (thus the disposal corresponds with the ref
     * removed from the `downs` array)
     */
    private[HASkipList] def mergeLeft(sib: Node[S, A, E])(implicit tx: S#Tx): Node[S, A, E]

    /*
     * In borrow-from-left, both parents' downs need
     * update, but identifiers are kept.
     * the parent needs to update the key for the
     * left sibling to match the before-last key in
     * the left sibling.
     */
    private[HASkipList] def borrowLeft(sib: Node[S, A, E])(implicit tx: S#Tx): Node[S, A, E]

    def isLeaf:   Boolean
    def isBranch: Boolean

    def asLeaf:   Leaf  [S, A, E]
    def asBranch: Branch[S, A, E]
  }

  private final class SetLeaf[S <: Base[S], A](private[HASkipList] val entries: Vector[A])
    extends Leaf[S, A, A] {

    protected def copy(newEntries: Vector[A]): Leaf[S, A, A] = new SetLeaf(newEntries)

    def key(idx: Int): A = entries(idx)
  }

  private final class MapLeaf[S <: Base[S], A, B](private[HASkipList] val entries: Vector[(A, B)])
    extends Leaf[S, A, (A, B)] {

    protected def copy(newEntries: Vector[(A, B)]): Leaf[S, A, (A, B)] = new MapLeaf(newEntries)

    def key(idx: Int): A = entries(idx)._1
  }

  sealed trait Leaf[S <: Base[S], A, E] extends Node[S, A, E] {
    override def toString: String = entries.mkString("Leaf(", ",", ")")

    private[HASkipList] def entries: Vector[E]
    final def entry(idx: Int): E = entries(idx)

    protected def copy(newEntries: Vector[E]): Leaf[S, A, E]

    final def size: Int = entries.size

    final def isLeaf:   Boolean = true
    final def isBranch: Boolean = false

    final def asLeaf:   Leaf[S, A, E] = this
    final def asBranch: Branch[S, A, E] = opNotSupported

    private[HASkipList] final def leafSizeSum(implicit tx: S#Tx): Int = size

    private[HASkipList] final def printNode(isRight: Boolean)(implicit tx: S#Tx): Vec[String] = {
      val sz      = size
      val szm     = sz - 1
      val strings = Seq.tabulate(sz)(idx => if (!isRight || idx < szm) entry(idx).toString else "M")
      Vector(strings.mkString("--"))
    }

    private[HASkipList] final def mergeRight(sib: Node[S, A, E])(implicit tx: S#Tx): Node[S, A, E] = {
      val lSib = sib.asLeaf
      copy(entries ++ lSib.entries)
    }

    private[HASkipList] final def borrowRight(sib: Node[S, A, E])(implicit tx: S#Tx): Node[S, A, E] = {
      val lSib = sib.asLeaf
      copy(entries :+ lSib.entries.head)
    }

    private[HASkipList] final def mergeLeft(sib: Node[S, A, E])(implicit tx: S#Tx): Node[S, A, E] = {
      val lSib = sib.asLeaf
      copy(lSib.entries ++ entries)
    }

    private[HASkipList] final def borrowLeft(sib: Node[S, A, E])(implicit tx: S#Tx): Node[S, A, E] = {
      val lSib = sib.asLeaf
      copy(lSib.entries.last +: entries)
    }

    private[HASkipList] final def insert(idx: Int, entry: E)(implicit list: Impl[S, A, E]): Leaf[S, A, E] = {
      val newEntries = entries.patch(idx, Vector(entry), 0)
      copy(newEntries)
    }

    private[HASkipList] final def update(idx: Int, entry: E)(implicit list: Impl[S, A, E]): Leaf[S, A, E] = {
      val newEntries = entries.patch(idx, Vector(entry), 1)
      copy(newEntries)
    }

    private[HASkipList] final def splitAndInsert(idx: Int, entry: E)
                                                (implicit list: Impl[S, A, E]): (Leaf[S, A, E], Leaf[S, A, E]) = {
      //         assert( size == arrMaxSz )
      val arrMinSz = list.arrMinSz
      val (len0, ren0) = entries.splitAt(arrMinSz)
      if (idx < arrMinSz) {
        // split and add `v` to left leaf
        val len   = len0.patch(idx, Vector(entry), 0)
        val left  = copy(len)
        val right = copy(ren0)

        (left, right)

      } else {
        // split and add `v` to right leaf
        val numL  = idx - arrMinSz
        val ren   = ren0.patch(numL, Vector(entry), 0)
        val left  = copy(len0)
        val right = copy(ren)

        (left, right)
      }
    }

    private[HASkipList] final def removeColumn(idx: Int)(implicit tx: S#Tx, list: Impl[S, A, E]): Leaf[S, A, E] = {
      val newEntries = entries.patch(idx, Vector.empty, 1)
      copy(newEntries)
    }

    private[HASkipList] final def write(out: DataOutput)(implicit list: Impl[S, A, E]): Unit = {
      val sz      = size
      val sz1     = sz - 1
      val isRight = entries(sz1) == null // XXX XXX
      val szi     = if (isRight) sz1 else sz
      out.writeByte(if (isRight) 6 else 2)
      out.writeByte(sz)
      var i = 0
      while (i < szi) {
        list.writeEntry(entries(i), out)
        i += 1
      }
    }
  }

  object Branch {
    private[HASkipList] def read[S <: Base[S], A, B](in: DataInput, access: S#Acc,
                                                    isRight: Boolean)
                                                   (implicit tx: S#Tx,
                                                    list: Impl[S, A, B]): Branch[S, A, B] = {
      import list.keySerializer
      val sz    = in.readByte()
      val szi   = if (isRight) sz - 1 else sz
      val keys  = Vector.tabulate(sz) { i =>
        if (i < szi) keySerializer.read(in, access) else null.asInstanceOf[A]
      }
      val downs = Vector.fill(sz)(tx.readVar[Node[S, A, B]](list.id, in))
      new Branch[S, A, B](keys, downs)
    }
  }

  final class Branch[S <: Base[S], A, B](private[HASkipList] val keys : Vector[A],
                                        private[HASkipList] val downs: Vector[S#Var[Node[S, A, B]]])
    extends HeadOrBranch[S, A, B] with Node[S, A, B] {

    override def toString: String = keys.mkString("Branch(", ",", ")")

    def isLeaf:   Boolean = false
    def isBranch: Boolean = true

    def asLeaf:   Leaf  [S, A, B] = opNotSupported
    def asBranch: Branch[S, A, B] = this

    private[HASkipList] def mergeRight(sib: Node[S, A, B])(implicit tx: S#Tx): Node[S, A, B] = {
      val bSib = sib.asBranch
      new Branch[S, A, B](keys ++ bSib.keys, downs ++ bSib.downs)
    }

    private[HASkipList] def borrowRight(sib: Node[S, A, B])(implicit tx: S#Tx): Node[S, A, B] = {
      val bSib = sib.asBranch
      new Branch[S, A, B](keys :+ bSib.keys.head, downs :+ bSib.downs.head)
    }

    private[HASkipList] def mergeLeft(sib: Node[S, A, B])(implicit tx: S#Tx): Node[S, A, B] = {
      val bSib = sib.asBranch
      new Branch[S, A, B](bSib.keys ++ keys, bSib.downs ++ downs)
    }

    private[HASkipList] def borrowLeft(sib: Node[S, A, B])(implicit tx: S#Tx): Node[S, A, B] = {
      val bSib = sib.asBranch
      new Branch[S, A, B](bSib.keys.last +: keys, bSib.downs.last +: downs)
    }

    private[HASkipList] def leafSizeSum(implicit tx: S#Tx): Int = {
      var res = 0
      val sz = size
      var i = 0; while (i < sz) {
        res += down(i).leafSizeSum
        i += 1
      }
      res
    }

     private[HASkipList] def printNode(isRight: Boolean)(implicit tx: S#Tx): Vec[String] = {
       val sz   = size
       val szm  = sz - 1
       val columns = Vector.tabulate(sz) { idx =>
         val rr       = isRight && idx == szm
         val child    = down(idx).printNode(rr)
         val childSz  = child.head.length()
         val ks       = if (rr) "M" else key(idx).toString
         val keySz    = ks.length()
         val colSz    = math.max(keySz, childSz) + 2
         val keyAdd   = (if (idx == size - 1) " " else "-") * (colSz - keySz)
         val bar      = s"|${" " * (colSz - 1)}"
         val childAdd = " " * (colSz - childSz)
         Vector(ks + keyAdd, bar) ++ child.map(_ + childAdd)
       }
       Vector.tabulate(columns.map(_.size).max) { row =>
         columns.map(_.apply(row)).mkString("")
       }
     }

     def key(idx: Int): A = keys(idx)

     def size: Int = keys.length

     private[HASkipList] def downRef(i: Int): S#Var[Node[S, A, B]] = downs(i)

     def down(i: Int)(implicit tx: S#Tx): Node[S, A, B] = downs(i)()

     private[HASkipList] def split(implicit tx: S#Tx, list: Impl[S, A, B]): (Branch[S, A, B], Branch[S, A, B]) = {
       val lsz = list.arrMinSz
       val (lKeys , rKeys ) = keys .splitAt(lsz)
       val (lDowns, rDowns) = downs.splitAt(lsz)
       val left  = new Branch[S, A, B](lKeys, lDowns)
       val right = new Branch[S, A, B](rKeys, rDowns)

       (left, right)
     }

     private[HASkipList] def updateDown(i: Int, n: Node[S, A, B])(implicit tx: S#Tx): Unit = downs(i)() = n

     private[HASkipList] def removeColumn(idx: Int)(implicit tx: S#Tx, list: Impl[S, A, B]): Branch[S, A, B] = {
       val newKeys  = keys .patch(idx, Vector.empty, 1)
       val newDowns = downs.patch(idx, Vector.empty, 1)
       new Branch[S, A, B](newKeys, newDowns)
     }

     private[HASkipList] def updateKey(idx: Int, key: A)(implicit tx: S#Tx, list: Impl[S, A, B]): Branch[S, A, B] = {
       val newKeys = keys.updated(idx, key)
       new Branch[S, A, B](newKeys, downs)
     }

     private[HASkipList] def insertAfterSplit(idx: Int, splitKey: A, left: Node[S, A, B], right: Node[S, A, B])
                                             (implicit tx: S#Tx, list: Impl[S, A, B]): Branch[S, A, B] = {
       // we must make a copy of this branch with the
       // size increased by one. the new key is `splitKey`
       // which gets inserted at the index where we went
       // down, `idx`.
       val bKeys  = keys.patch (idx, Vector(splitKey), 0)
       val bDowns = downs.patch(idx, Vector(tx.newVar(list.id, left)), 0)

       // copy entries right to split index
       val rightOff       = idx + 1
       bDowns(rightOff)() = right

       new Branch[S, A, B](bKeys, bDowns)
     }

     private[HASkipList] def write(out: DataOutput)(implicit list: Impl[S, A, B]): Unit = {
       import list.keySerializer
       val sz       = size
       val sz1      = sz - 1
       val isRight  = keys(sz1) == null
       val szi      = if (isRight) sz1 else sz
       out.writeByte(if (isRight) 5 else 1)
       out.writeByte(sz)
       var i = 0; while (i < szi) {
         keySerializer.write(keys(i), out)
         i += 1
       }
       i = 0; while (i < sz) {
         downs(i).write(out)
         i += 1
       }
     }
   }

  object Set {
    type Node  [S <: Base[S], A] = HASkipList.Node  [S, A, A]
    type Branch[S <: Base[S], A] = HASkipList.Branch[S, A, A]
    type Leaf  [S <: Base[S], A] = HASkipList.Leaf  [S, A, A]

    /** Creates a new empty skip list with default minimum gap parameter of `2` and no key observer.
      * Type parameter `S` specifies the STM system to use. Type parameter `A`
      * specifies the type of the keys stored in the list.
      *
      * @param   tx          the transaction in which to initialize the structure
      * @param   ord         the ordering of the keys. This is an instance of `txn.Ordering` to allow
      *                      for specialized versions and transactional restrictions.
      * @param   keySerializer      the serializer for the elements, in case a persistent STM is used.
      */
    def empty[S <: Base[S], A](implicit tx: S#Tx, ord: Ordering[S#Tx, A],
                              keySerializer: Serializer[S#Tx, S#Acc, A]): HASkipList.Set[S, A] =
      empty()

    /** Creates a new empty skip list. Type parameter `S` specifies the STM system to use. Type parameter `A`
      * specifies the type of the keys stored in the list.
      *
      * @param   minGap      the minimum gap-size used for the skip list. This value must be between 1 and 126 inclusive.
      * @param   keyObserver an object which observes key promotions and demotions. Use `NoKeyObserver` (default) if
      *                      key motions do not need to be monitored. The monitoring allows the use of the skip list
      *                      for synchronized decimation of related data structures, such as the deterministic
      *                      skip quadtree.
      * @param   tx          the transaction in which to initialize the structure
      * @param   ord         the ordering of the keys. This is an instance of `txn.Ordering` to allow
      *                      for specialized versions and transactional restrictions.
      * @param   keySerializer  the serializer for the elements, in case a persistent STM is used.
      */
    def empty[S <: Base[S], A](minGap: Int = 2,
                              keyObserver: SkipList.KeyObserver[S#Tx, A] = SkipList.NoKeyObserver)
                             (implicit tx: S#Tx, ord: Ordering[S#Tx, A],
                              keySerializer: Serializer[S#Tx, S#Acc, A]): HASkipList.Set[S, A] = {

      // 255 <= arrMaxSz = (minGap + 1) << 1
      // ; this is, so we can write a node's size as signed byte, and
      // no reasonable app would use a node size > 255
      if (minGap < 1 || minGap > 126) sys.error(s"Minimum gap ($minGap) cannot be less than 1 or greater than 126")

      val implId = tx.newId()
      new SetImpl[S, A](implId, minGap, keyObserver, list => {
        tx.newVar[Node[S, A]](implId, null)(list)
      })
    }

    def read[S <: Base[S], A](in: DataInput, access: S#Acc,
                             keyObserver: SkipList.KeyObserver[S#Tx, A] = SkipList.NoKeyObserver)
                            (implicit tx: S#Tx, ordering: Ordering[S#Tx, A],
                             keySerializer: Serializer[S#Tx, S#Acc, A]): HASkipList.Set[S, A] = {

      val id      = tx.readId(in, access)
      val version = in.readByte()
      if (version != SER_VERSION)
        sys.error(s"Incompatible serialized version (found $version, required $SER_VERSION).")

      val minGap = in.readByte()
      new SetImpl[S, A](id, minGap, keyObserver, list => tx.readVar[Node[S, A]](id, in)(list))
    }

    def serializer[S <: Base[S], A](keyObserver: SkipList.KeyObserver[S#Tx, A] = SkipList.NoKeyObserver)
                                  (implicit ordering: Ordering[S#Tx, A],
                                   keySerializer: Serializer[S#Tx, S#Acc, A]): Serializer[S#Tx, S#Acc, HASkipList.Set[S, A]] =
      new SetSer[S, A](keyObserver)
  }

  sealed trait Set[S <: Base[S], A] extends SkipList.Set[S, A] {
    def top(implicit tx: S#Tx): Option[HASkipList.Set.Node[S, A]]
  }

  object Map {
    type Node  [S <: Base[S], A, B] = HASkipList.Node  [S, A, (A, B)]
    type Branch[S <: Base[S], A, B] = HASkipList.Branch[S, A, (A, B)]
    type Leaf  [S <: Base[S], A, B] = HASkipList.Leaf  [S, A, (A, B)]

    /** Creates a new empty skip list with default minimum gap parameter of `2` and no key observer.
      * Type parameter `S` specifies the STM system to use. Type parameter `A`
      * specifies the type of the keys stored in the list.
      *
      * @param   tx          the transaction in which to initialize the structure
      * @param   ord         the ordering of the keys. This is an instance of `txn.Ordering` to allow
      *                      for specialized versions and transactional restrictions.
      * @param   keySerializer      the serializer for the elements, in case a persistent STM is used.
      */
    def empty[S <: Base[S], A, B](implicit tx: S#Tx, ord: Ordering[S#Tx, A],
                                 keySerializer: Serializer[S#Tx, S#Acc, A],
                                 valueSerializer: Serializer[S#Tx, S#Acc, B]): HASkipList.Map[S, A, B] =
      empty()

    /** Creates a new empty skip list. Type parameter `S` specifies the STM system to use. Type parameter `A`
      * specifies the type of the keys stored in the list.
      *
      * @param   minGap      the minimum gap-size used for the skip list. This value must be between 1 and 126 inclusive.
      * @param   keyObserver an object which observes key promotions and demotions. Use `NoKeyObserver` (default) if
      *                      key motions do not need to be monitored. The monitoring allows the use of the skip list
      *                      for synchronized decimation of related data structures, such as the deterministic
      *                      skip quadtree.
      * @param   tx          the transaction in which to initialize the structure
      * @param   ord         the ordering of the keys. This is an instance of `txn.Ordering` to allow
      *                      for specialized versions and transactional restrictions.
      * @param   keySerializer  the serializer for the elements, in case a persistent STM is used.
      */
    def empty[S <: Base[S], A, B](minGap: Int = 2,
                                 keyObserver: SkipList.KeyObserver[S#Tx, A] = SkipList.NoKeyObserver)
                                (implicit tx: S#Tx, ord: Ordering[S#Tx, A],
                                 keySerializer: Serializer[S#Tx, S#Acc, A],
                                 valueSerializer: Serializer[S#Tx, S#Acc, B]): HASkipList.Map[S, A, B] = {

      // 255 <= arrMaxSz = (minGap + 1) << 1
      // ; this is, so we can write a node's size as signed byte, and
      // no reasonable app would use a node size > 255
      if (minGap < 1 || minGap > 126) sys.error(s"Minimum gap ($minGap) cannot be less than 1 or greater than 126")

      val implId = tx.newId()
      new MapImpl[S, A, B](implId, minGap, keyObserver, list => {
        tx.newVar[Node[S, A, B]](implId, null)(list)
      })
    }

    def read[S <: Base[S], A, B](in: DataInput, access: S#Acc,
                                keyObserver: SkipList.KeyObserver[S#Tx, A] = SkipList.NoKeyObserver)
                               (implicit tx: S#Tx, ordering: Ordering[S#Tx, A],
                                keySerializer: Serializer[S#Tx, S#Acc, A],
                                valueSerializer: Serializer[S#Tx, S#Acc, B]): HASkipList.Map[S, A, B] = {

      val id      = tx.readId(in, access)
      val version = in.readByte()
      if (version != SER_VERSION) sys.error(s"Incompatible serialized version (found $version, required $SER_VERSION).")

      val minGap = in.readByte()
      new MapImpl[S, A, B](id, minGap, keyObserver, list => tx.readVar[Node[S, A, B]](id, in)(list))
    }

    def serializer[S <: Base[S], A, B](keyObserver: SkipList.KeyObserver[S#Tx, A] = SkipList.NoKeyObserver)
                                     (implicit ordering: Ordering[S#Tx, A],
                                      keySerializer: Serializer[S#Tx, S#Acc, A],
                                      valueSerializer: Serializer[S#Tx, S#Acc, B]): Serializer[S#Tx, S#Acc, HASkipList.Map[S, A, B]] =
      new MapSer[S, A, B](keyObserver)
  }

  sealed trait Map[S <: Base[S], /* @spec(KeySpec) */ A, /* @spec(ValueSpec) */ B] extends SkipList.Map[S, A, B] {
    def top(implicit tx: S#Tx): Option[HASkipList.Node[S, A, (A, B)]]
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy