Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* DeterministicSkipOctree.scala
* (LucreData)
*
* Copyright (c) 2011-2014 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
package lucre
package data
import collection.immutable.{IndexedSeq => Vec}
import collection.mutable.{PriorityQueue => MPriorityQueue, Queue => MQueue}
import scala.annotation.{elidable, switch, tailrec}
import geom.{QueryShape, DistanceMeasure, Space}
import stm.{Identifiable, Sys, Mutable}
import serial.{Writable, DataInput, DataOutput, Serializer}
import java.io.PrintStream
import de.sciss.serial.impl.ByteArrayOutputStream
/** A transactional deterministic skip octree as outlined in the paper by Eppstein et al.
* It is constructed from a given space (dimensions) and a skip-gap parameter
* which determines the kind of skip list which is used to govern the
* level decimation.
*
* The tree is a mutable data structure which supports lookup, insertion and removal
* in O(log n), as well as efficient range queries and nearest neighbour search.
*
* The current implementation, backed by `impl.SkipOctreeImpl`, uses the types of
* the `geom` package, assuming that coordinates are integers, with the maximum
* root hyper-cube given by a span from `0` to `0x7FFFFFFF` (e.g. in `Space.IntTwoDim`,
* this is `IntSquare( 0x40000000, 0x40000000, 0x40000000 )`.
*/
object DeterministicSkipOctree {
private final val SER_VERSION = 79 // 'O'
private var stat_rounds = 0
private var stat_pq_add = 0
private var stat_pq_rem = 0
private val stat_print = false
@volatile private var sanitizing = false
@elidable(elidable.CONFIG) private def stat_reset(): Unit = {
stat_rounds = 0
stat_pq_add = 0
stat_pq_rem = 0
}
@elidable(elidable.CONFIG) private def stat_debug(what: => String): Unit =
if (stat_print) println(s" $what")
@elidable(elidable.CONFIG) private def stat_report() = ()
// println(s"NN took $stat_rounds rounds, adding $stat_pq_add and removing $stat_pq_rem times to/from PQ")
@elidable(elidable.CONFIG) private def stat_rounds1(obj: Any): Unit = {
stat_rounds += 1
if (stat_print) println(s" round max: $obj")
}
@elidable(elidable.CONFIG) private def stat_pq_add1(obj: Any): Unit = {
stat_pq_add += 1
if (stat_print) println(s" add pq: $obj")
}
@elidable(elidable.CONFIG) private def stat_pq_rem1(obj: Any): Unit = {
stat_pq_rem += 1
if (stat_print) println(s" remove pq: $obj")
}
def empty[S <: Sys[S], D <: Space[D], A](hyperCube: D#HyperCube, skipGap: Int = 2)
(implicit view: (A, S#Tx) => D#PointLike, tx: S#Tx, space: D,
keySerializer: Serializer[S#Tx, S#Acc, A]): DeterministicSkipOctree[S, D, A] =
new ImplNew[S, D, A](skipGap, tx.newID(), hyperCube, view, tx)
def read[S <: Sys[S], D <: Space[D], A](in: DataInput, access: S#Acc)(
implicit tx: S#Tx, view: (A, S#Tx) => D#PointLike, space: D,
keySerializer: Serializer[S#Tx, S#Acc, A]): DeterministicSkipOctree[S, D, A] =
new ImplRead[S, D, A](view, in, access, tx)
implicit def serializer[S <: Sys[S], D <: Space[D], A](
implicit view: (A, S#Tx) => D#PointLike, space: D,
keySerializer: Serializer[S#Tx, S#Acc, A]): Serializer[S#Tx, S#Acc, DeterministicSkipOctree[S, D, A]] =
new OctreeSerializer[S, D, A]
private final class OctreeSerializer[S <: Sys[S], D <: Space[D], A](
implicit view: (A, S#Tx) => D#PointLike, space: D, keySerializer: Serializer[S#Tx, S#Acc, A])
extends Serializer[S#Tx, S#Acc, DeterministicSkipOctree[S, D, A]] {
def read(in: DataInput, access: S#Acc)(implicit tx: S#Tx): DeterministicSkipOctree[S, D, A] = {
new ImplRead[S, D, A](view, in, access, tx)
}
override def toString = "DeterministicSkipOctree.serializer"
def write(v: DeterministicSkipOctree[S, D, A], out: DataOutput): Unit = v.write(out)
}
private final class ImplRead[S <: Sys[S], D <: Space[D], A](val pointView: (A, S#Tx) => D#PointLike, in: DataInput,
access: S#Acc, tx0: S#Tx)
(implicit val space: D,
val keySerializer: Serializer[S#Tx, S#Acc, A])
extends DeterministicSkipOctree[S, D, A] {
{
val version = in.readByte()
require(version == SER_VERSION,
s"Incompatible serialized version (found $version, required $SER_VERSION).")
}
val id = tx0.readID(in, access)
val hyperCube = space.hyperCubeSerializer.read(in, access)(tx0)
val skipList = {
implicit val ord = LeafOrdering
implicit val r1 = LeafSerializer
HASkipList.Set.serializer[S, LeafImpl](KeyObserver).read(in, access)(tx0)
}
val head = LeftTopBranchSerializer.read(in, access)(tx0)
val lastTreeRef = {
implicit val r4 = TopBranchSerializer
tx0.readVar[TopBranch](id, in)
}
}
private final class ImplNew[S <: Sys[S], D <: Space[D], A](skipGap: Int, val id: S#ID, val hyperCube: D#HyperCube,
val pointView: (A, S#Tx) => D#PointLike, tx0: S#Tx)
(implicit val space: D,
val keySerializer: Serializer[S#Tx, S#Acc, A])
extends DeterministicSkipOctree[S, D, A] {
val skipList = HASkipList.Set.empty[S, LeafImpl](skipGap, KeyObserver)(tx0, LeafOrdering, LeafSerializer)
val head = {
val sz = numOrthants
val ch = tx0.newVarArray[LeftChildOption](sz)
val cid = tx0.newID()
implicit val r1 = LeftChildOptionSerializer
var i = 0
while (i < sz) {
ch(i) = tx0.newVar[LeftChildOption](cid, EmptyValue)
i += 1
}
implicit val r2 = RightOptionReader
val headRight = tx0.newVar[NextOption](cid, EmptyValue)
new LeftTopBranch(cid, children = ch, nextRef = headRight)
}
val lastTreeRef = {
implicit val r3 = TopBranchSerializer
tx0.newVar[TopBranch](id, head)
}
}
private def opNotSupported : Nothing = sys.error( "Operation not supported" )
/* A debugging facility to inpect an octree only (not the skip list) for internal structural consistency.
*
* @param tree the tree to inspect.
* @param reportOnly if `true`, merely reports anomalies but does not try to resolve them. If `false` attempts to
* fix corrupt entries.
* @return empty if there were no inconsistencies found, otherwise a list of textual descriptions
* of the problems found
*/
private def verifyOctreeConsistency[S <: Sys[S], D <: Space[D], A](tree: DeterministicSkipOctree[S, D, A],
reportOnly: Boolean)
(implicit tx: S#Tx): Vec[String] = {
val q = tree.hyperCube
var level = tree.numLevels
var h: tree.Branch = tree.lastTreeImpl
var currUnlinkedOcs = Set.empty[D#HyperCube]
var currPoints = Set.empty[tree.LeafImpl]
var errors = Vec.empty[String]
val repair = !reportOnly
do {
if (h.hyperCube != q) {
errors :+= s"Root level quad is ${h.hyperCube} while it should be $q in level $level"
}
val nextUnlinkedOcs = currUnlinkedOcs
val nextPoints = currPoints
currUnlinkedOcs = Set.empty
currPoints = Set.empty
def checkChildren(n: tree.Branch, depth: Int): Unit = {
def assertInfo = s"in level $level / depth $depth"
var i = 0
while (i < tree.numOrthants) {
n.child(i) match {
case cb: tree.ChildBranch =>
if (cb.parent != n) {
errors :+= s"Child branch $cb has invalid parent ${cb.parent}, expected: $n $assertInfo"
if (repair) {
(n, cb) match {
case (pl: tree.LeftBranch , cbl: tree.LeftChildBranch ) => cbl.parent = pl
case (pr: tree.RightBranch, cbr: tree.RightChildBranch) => cbr.parent = pr
}
}
}
val nq = n.hyperCube.orthant(i)
val cq = cb.hyperCube
if (!nq.contains(cq)) {
errors :+= s"Node has invalid hyper-cube ($cq), expected: $nq $assertInfo"
}
if (n.hyperCube.indexOf(cq) != i) {
errors :+= s"Mismatch between index-of and used orthant ($i), with parent ${n.hyperCube} and $cq"
}
cb.nextOption match {
case Some(next) =>
if (next.prevOption != Some(cb)) {
errors :+= s"Asymmetric next link $cq $assertInfo"
}
if (next.hyperCube != cq) {
errors :+= s"Next hyper-cube does not match ($cq vs. ${next.hyperCube}) $assertInfo"
}
case None =>
if (nextUnlinkedOcs.contains(cq)) {
errors :+= s"Double missing link for $cq $assertInfo"
}
}
cb.prevOption match {
case Some(prev) =>
if (prev.nextOption != Some(cb)) {
errors :+= s"Asymmetric prev link $cq $assertInfo"
}
if (prev.hyperCube != cq) {
errors :+= s"Next hyper-cube do not match ($cq vs. ${prev.hyperCube}) $assertInfo"
}
case None => currUnlinkedOcs += cq
}
checkChildren(cb, depth + 1)
case l: tree.LeafImpl =>
currPoints += l // .value
case _ =>
}
i += 1
}
}
checkChildren(h, 0)
val pointsOnlyInNext = nextPoints.filterNot(currPoints.contains)
if (pointsOnlyInNext.nonEmpty) {
errors :+= s"Points in next which aren't in current (${pointsOnlyInNext.take(10).map(_.value)}); in level $level"
if (repair && level == 1) {
assert(h.prevOption.isEmpty)
def newNode(b: tree.LeftBranch, qidx: Int, iq: D#HyperCube)(implicit tx: S#Tx): tree.LeftChildBranch = {
val sz = tree.numOrthants // b.children.length
val ch = tx.newVarArray[tree.LeftChildOption](sz)
val cid = tx.newID()
var i = 0
while (i < sz) {
ch(i) = tx.newVar[tree.LeftChildOption](cid, tree.EmptyValue)(tree.LeftChildOptionSerializer)
i += 1
}
val parentRef = tx.newVar[tree.LeftBranch](cid, b)(tree.LeftBranchSerializer)
val rightRef = tx.newVar[tree.NextOption](cid, tree.EmptyValue)(tree.RightOptionReader)
val n = new tree.LeftChildBranch(
cid, parentRef, iq, children = ch, nextRef = rightRef
)
b.updateChild(qidx, n)
n
}
def insert(b: tree.LeftBranch, point: D#PointLike, leaf: tree.LeafImpl)(implicit tx: S#Tx): Unit = {
val qidx = b.hyperCube.indexOf(point)
b.child(qidx) match {
case tree.EmptyValue =>
b.updateChild(qidx, leaf)
case old: tree.LeftNonEmptyChild =>
// define the greatest interesting square for the new node to insert
// in this node at qidx:
val qn2 = old.union(b.hyperCube.orthant(qidx), point)
// create the new node (this adds it to the children!)
val n2 = newNode(b, qidx, qn2)
val oidx = old.orthantIndexIn(qn2)
n2.updateChild(oidx, old)
val lidx = qn2.indexOf(point)
assert(oidx != lidx)
// This is a tricky bit! And a reason
// why should eventually try to do without
// parent pointers at all. Since `old`
// may be a leaf whose parent points
// to a higher level tree, we need to
// check first if the parent is `this`,
// and if so, adjust the parent to point
// to the new intermediate node `ne`!
if (old.parent == this) old.updateParentLeft(n2)
n2.updateChild(lidx, leaf)
}
}
h match {
case lb: tree.LeftBranch =>
pointsOnlyInNext.foreach { leaf =>
val point = tree.pointView(leaf.value, tx)
def goDown(b: tree.LeftBranch): Unit = {
val idx = b.hyperCube.indexOf(point)
if (idx < 0) {
errors :+= s"Can't repair because $point is not in $lb"
} else {
b.child(idx) match {
case lb1: tree.LeftBranch => goDown(lb1)
case _ =>
insert(b, point, leaf)
}
}
}
goDown(lb)
}
case _ =>
errors +:= "Can't repair because not in left branch !?"
}
}
}
h = h.prevOption.orNull
level -= 1
} while (h != null)
errors
}
/** Checks the tree for correctness.
*
* @param reportOnly if `true` simply scans the tree, if `false` it will apply corrections if necessary
* @return empty if no problems were found, otherwise a list of strings describing the problems found
*/
def verifyConsistency[S <: Sys[S], D <: Space[D], A](tree: DeterministicSkipOctree[S, D, A], reportOnly: Boolean)
(implicit tx: S#Tx): Vec[String] = {
var errors = Vec.empty[String]
var repair = !reportOnly
val treeOnlyErrors = verifyOctreeConsistency(tree, reportOnly = reportOnly)
errors ++= treeOnlyErrors
if (treeOnlyErrors.nonEmpty) {
repair = false // stay on the safe side; require that consistency within tree is repaired first
}
// Take skip list as reference. Find if octree levels do not match skip list levels,
// or whether points in the skip list are not found in the octree.
tree.skipList.iterator.foreach { leaf =>
val pv = tree.pointView(leaf.value, tx)
@tailrec def findLeaf(b: tree.BranchLike = tree.lastTreeImpl,
lvl: Int = tree.numLevels, doPrint: Boolean = false): Option[(tree.BranchLike, Int)] = {
if (doPrint) errors :+= s"...checking $b in level $lvl"
val idx = b.hyperCube.indexOf(pv)
b.child(idx) match {
case `leaf` => Some(b -> lvl)
case cb: tree.BranchLike if cb.hyperCube.contains(pv) =>
findLeaf(cb, lvl = lvl, doPrint = doPrint)
case _ =>
b.prevOption match {
case Some(pb: tree.BranchLike) =>
findLeaf(pb, lvl = lvl - 1, doPrint = doPrint)
case _ => None
}
}
}
findLeaf() match {
case None =>
val foundLevelSkip = HASkipList.debugFindLevel(tree.skipList, leaf)
errors :+= s"Severe problem with $leaf - in skip list (level $foundLevelSkip) but octree does not find it"
if (repair && foundLevelSkip == 1) { // this one is fixable
try {
sanitizing = true
tree.skipList.remove(leaf)
} finally {
sanitizing = false
return errors // hacky!!! skipList iterator possibly invalid, thus abort straight after removal
}
}
case Some((foundParent, foundLevel)) =>
val foundLevelSkip = HASkipList.debugFindLevel(tree.skipList, leaf)
if (foundLevel != foundLevelSkip) {
errors :+= s"Severe problem with $leaf - is in skip list level $foundLevelSkip versus octree level $foundLevel"
}
val parent = leaf.parent
val idx = parent.hyperCube.indexOf(pv)
if (idx < 0) {
errors :+= s"Severe problem with $leaf - reported parent is $parent which doesn't contain the point $pv"
} else {
val saw = parent.child(idx)
if (saw != leaf) {
errors :+= s"$leaf with point $pv reported parent $parent but in orthant $idx we see $saw"
findLeaf(doPrint = true) match {
case Some((b, _)) =>
errors :+= s"...that is the correct parent!"
if (repair) {
leaf.parent = b
}
case None => errors :+= s"...this is bad. can't locate leaf!"
}
}
}
}
}
// Take octree as reference and see if it contains any points not in the skip list.
val inSkipList = tree.skipList.toSet
def checkInTreeLevel(b: tree.BranchLike, lvl: Int): Unit = {
val sz = tree.numOrthants
var i = 0
while (i < sz) {
b.child(i) match {
case l: tree.LeafImpl if !inSkipList(l) =>
errors :+= s"Only in octree level $lvl but not skip list: $l"
if (repair) {
println(s"\n============== BEFORE REMOVING $l ==============")
println(tree.debugPrint())
b.demoteLeaf(tree.pointView(l.value, tx), l)
println(s"\n============== AFTER REMOVING $l ==============")
println(tree.debugPrint())
return // XXX dirty - but if there is more than one wrong leaf, continuing may reinstall a lonley parent
}
case cb: tree.BranchLike =>
checkInTreeLevel(cb, lvl)
case _ =>
}
i += 1
}
}
def checkInTree(t: tree.BranchLike, lvl: Int): Unit = {
checkInTreeLevel(t, lvl)
t.prevOption.foreach {
case p: tree.BranchLike => checkInTree(p, lvl - 1)
}
}
checkInTree(tree.lastTreeImpl, tree.numLevels)
errors
}
}
sealed trait DeterministicSkipOctree[S <: Sys[S], D <: Space[D], A]
extends SkipOctree[S, D, A] {
octree =>
import DeterministicSkipOctree.{SER_VERSION, opNotSupported,
stat_reset, stat_rounds1, stat_pq_add1, stat_pq_rem1, stat_report, stat_debug}
private type Order = TotalOrder.Set.Entry[S]
// ---- abstract types and methods ----
implicit def space: D
implicit def keySerializer: Serializer[S#Tx, S#Acc, A]
protected def skipList: HASkipList.Set[S, LeafImpl]
protected def head: LeftTopBranch
protected def lastTreeRef: S#Var[TopBranch]
// ----
override def toString = s"Octree-${space.dim}d$id"
protected object LeafOrdering extends Ordering[S#Tx, LeafImpl] {
/** Leafs are ordered by the tree's in-order traversal,
* where the quadrants I+II and III+IV can be thought
* of as dummy nodes to binarize the octree. That is
* to say, in a node, the child order corresponds to
* their quadrant indices (I < II < III < IV).
*/
def compare(a: LeafImpl, b: LeafImpl)(implicit tx: S#Tx): Int = {
val pa = pointView(a.value, tx)
val pb = pointView(b.value, tx)
space.lexicalOrder.compare(pa, pb)
}
}
implicit protected object RightBranchSerializer extends Serializer[S#Tx, S#Acc, RightBranch] {
def read(in: DataInput, access: S#Acc)(implicit tx: S#Tx): RightBranch = {
val cookie = in.readByte()
val id = tx.readID(in, access)
(cookie: @switch) match {
case 4 => readRightTopBranch(in, access, id)
case 5 => readRightChildBranch(in, access, id)
case _ => sys.error("Unexpected cookie " + cookie)
}
}
def write(v: RightBranch, out: DataOutput): Unit = v.write(out)
}
implicit protected object BranchSerializer extends Serializer[S#Tx, S#Acc, BranchLike] {
def read(in: DataInput, access: S#Acc)(implicit tx: S#Tx): BranchLike = {
val cookie = in.readByte()
val id = tx.readID(in, access)
(cookie: @switch) match {
case 2 => readLeftTopBranch (in, access, id)
case 3 => readLeftChildBranch (in, access, id)
case 4 => readRightTopBranch (in, access, id)
case 5 => readRightChildBranch(in, access, id)
case _ => sys.error("Unexpected cookie " + cookie)
}
}
def write(v: BranchLike, out: DataOutput): Unit = v.write(out)
}
protected object TopBranchSerializer extends Serializer[S#Tx, S#Acc, TopBranch] {
def read(in: DataInput, access: S#Acc)(implicit tx: S#Tx): TopBranch = {
val cookie = in.readByte()
val id = tx.readID(in, access)
(cookie: @switch) match {
case 2 => readLeftTopBranch (in, access, id)
case 4 => readRightTopBranch(in, access, id)
case _ => sys.error("Unexpected cookie " + cookie)
}
}
def write(v: TopBranch, out: DataOutput): Unit = v.write(out)
}
protected object LeftChildOptionSerializer extends Serializer[S#Tx, S#Acc, LeftChildOption] {
// def empty = EmptyValue
def read(in: DataInput, access: S#Acc)(implicit tx: S#Tx): LeftChildOption = {
val cookie = in.readByte()
if (cookie == 0) return EmptyValue
val id = tx.readID(in, access)
(cookie: @switch) match {
case 1 => readLeaf(in, access, id)
case 3 => readLeftChildBranch(in, access, id)
case _ => sys.error("Unexpected cookie " + cookie)
}
}
def write(v: LeftChildOption, out: DataOutput): Unit = v.write(out)
}
implicit protected object LeftBranchSerializer extends Serializer[S#Tx, S#Acc, LeftBranch] {
def read(in: DataInput, access: S#Acc)(implicit tx: S#Tx): LeftBranch = {
val cookie = in.readByte()
val id = tx.readID(in, access)
(cookie: @switch) match {
case 2 => readLeftTopBranch (in, access, id)
case 3 => readLeftChildBranch(in, access, id)
case _ => sys.error("Unexpected cookie " + cookie)
}
}
def write(v: LeftBranch, out: DataOutput): Unit = v.write(out)
}
implicit protected object RightChildOptionSerializer extends Serializer[S#Tx, S#Acc, RightChildOption] {
// def empty = EmptyValue
def read(in: DataInput, access: S#Acc)(implicit tx: S#Tx): RightChildOption = {
val cookie = in.readByte()
if (cookie == 0) return EmptyValue
val id = tx.readID(in, access)
(cookie: @switch) match {
case 1 => readLeaf(in, access, id)
case 5 => readRightChildBranch(in, access, id)
case _ => sys.error("Unexpected cookie " + cookie)
}
}
def write(v: RightChildOption, out: DataOutput): Unit = v.write(out)
}
implicit protected object LeftTopBranchSerializer extends Serializer[S#Tx, S#Acc, LeftTopBranch] {
def read(in: DataInput, access: S#Acc)(implicit tx: S#Tx): LeftTopBranch = {
val cookie = in.readByte()
require(cookie == 2, "Unexpected cookie " + cookie)
val id = tx.readID(in, access)
readLeftTopBranch(in, access, id)
}
def write(v: LeftTopBranch, out: DataOutput): Unit = v.write(out)
}
protected object RightOptionReader extends Serializer[S#Tx, S#Acc, NextOption] {
// def empty = EmptyValue
def read(in: DataInput, access: S#Acc)(implicit tx: S#Tx): NextOption = {
val cookie = in.readByte()
if (cookie == 0) return EmptyValue
val id = tx.readID(in, access)
(cookie: @switch) match {
case 4 => readRightTopBranch(in, access, id)
case 5 => readRightChildBranch(in, access, id)
case _ => sys.error("Unexpected cookie " + cookie)
}
}
def write(v: NextOption, out: DataOutput): Unit = v.write(out)
}
protected object LeafSerializer extends Serializer[S#Tx, S#Acc, LeafImpl] {
def read(in: DataInput, access: S#Acc)(implicit tx: S#Tx): LeafImpl = {
val cookie = in.readByte()
require(cookie == 1, "Unexpected cookie " + cookie)
val id = tx.readID(in, access)
readLeaf(in, access, id)
}
def write(l: LeafImpl, out: DataOutput): Unit = l.write(out)
}
implicit protected object KeyObserver extends SkipList.KeyObserver[S#Tx, LeafImpl] {
def keyUp(l: LeafImpl)(implicit tx: S#Tx): Unit = {
//println( "up : " + l )
// "To insert x into Qi+1 we go from xi to pi(x) in Qi,
// then traverse upwards in Qi until we find the lowest
// ancestor q of x which is also interesting in Qi+1.
// (This is the reversed process of searching x in Qi
// with q = pi,start = pi+1,end so it takes at most 6
// steps by Lemma 5.) Then we go to the same square q
// in Qi+1 and insert x."
/** The reverse process of `findP0`: Finds the lowest
* common ancestor interesting node of this node
* which is also contained in Qi+1. Returns this node
* in Qi+1, or empty if no such node exists.
*/
@tailrec def findPN(b: BranchLike): NextOption = b match {
case tb: TopBranch => tb.next
case cb: ChildBranch => cb.next match {
case nb: BranchLike => nb
case EmptyValue => findPN(cb.parent)
}
}
val pNext = findPN(l.parent) match {
case EmptyValue => // create new level
val sz = numOrthants
val ch = tx.newVarArray[RightChildOption](sz)
val cid = tx.newID()
var i = 0
while (i < sz) {
ch(i) = tx.newVar[RightChildOption](cid, EmptyValue)
i += 1
}
val nextRef = tx.newVar[NextOption](cid, EmptyValue)(RightOptionReader)
val prev = lastTreeImpl
val res = new RightTopBranch(cid, prev, ch, nextRef)
prev.next = res
lastTreeImpl = res
res
case r: RightBranch => r
}
pNext.insert(pointView(l.value, tx), l)
}
def keyDown(l: LeafImpl)(implicit tx: S#Tx): Unit = {
//println( "down : " + l )
// "To delete x from Qi we go from xi to the smallest interesting
// square pi(x) containing x in Qi following the pointers. Then
// the deletion given pi(x) is as described in Section 2.3."
l.parent.demoteLeaf(pointView(l.value, tx), l)
}
}
final def numOrthants: Int = 1 << space.dim
// 4 for R2, 8 for R3, 16 for R4, etc.
sealed trait Child
sealed trait Branch extends Child {
def hyperCube: D#HyperCube
def nextOption(implicit tx: S#Tx): Option[Branch]
def prevOption: Option[Branch]
def child(idx: Int)(implicit tx: S#Tx): Child
}
sealed trait Leaf extends Child {
def value: A
}
sealed trait Empty extends Child
final def headTree: Branch = head
final def lastTree(implicit tx: S#Tx): Branch = lastTreeImpl
final def write(out: DataOutput): Unit = {
out.writeByte(SER_VERSION)
id .write(out)
space.hyperCubeSerializer.write(hyperCube, out)
skipList .write(out)
head .write(out)
lastTreeRef .write(out)
}
final def clear()(implicit tx: S#Tx): Unit = {
val sz = numOrthants
@tailrec def removeAllLeaves(b: BranchLike): Unit = {
@tailrec def stepB(down: BranchLike, i: Int): ChildOption = {
if (i == sz) down
else b.child(i) match {
case l: LeafImpl =>
removeLeaf(pointView(l.value, tx), l)
lastTreeImpl
case _ => stepB(down, i + 1)
}
}
@tailrec def step(i: Int): ChildOption = {
if (i == sz) EmptyValue
else b.child(i) match {
case cb: BranchLike => stepB(cb, i + 1)
case l: LeafImpl =>
removeLeaf(pointView(l.value, tx), l)
lastTreeImpl
case _ => step(i + 1)
}
}
step(0) match {
case _: LeafOrEmpty =>
case next: BranchLike => removeAllLeaves(next)
}
}
removeAllLeaves(lastTreeImpl)
}
final def dispose()(implicit tx: S#Tx): Unit = {
// val sz = numOrthants
//
// def disposeBranch( b: BranchLike ): Unit = {
// var i = 0; while( i < sz ) {
// b.child( i ) match {
// case l: LeafImpl => l.remove()
// case b: BranchLike => disposeBranch( b )
// case _ =>
// }
// i += 1 }
// b.remove()
// }
//
// @tailrec def disposeTree( b: BranchLike ): Unit = {
// disposeBranch( b )
// b match {
// case _: LeftBranch =>
// case rb: RightBranch => disposeTree( rb.prev )
// }
// }
// disposeTree( lastTreeImpl )
id .dispose()
lastTreeRef .dispose()
head .dispose()
skipList .dispose()
}
final def lastTreeImpl (implicit tx: S#Tx): TopBranch = lastTreeRef()
final def lastTreeImpl_=(node: TopBranch)(implicit tx: S#Tx): Unit = lastTreeRef() = node
final def size(implicit tx: S#Tx): Int = skipList.size
final def add(elem: A)(implicit tx: S#Tx): Boolean =
insertLeaf(elem) match {
case EmptyValue => true
case oldLeaf: LeafImpl => oldLeaf.value != elem
}
final def update(elem: A)(implicit tx: S#Tx): Option[A] =
insertLeaf(elem) match {
case EmptyValue => None
case oldLeaf: LeafImpl => Some(oldLeaf.value)
}
final def remove(elem: A)(implicit tx: S#Tx): Boolean =
removeLeafAt(pointView(elem, tx)) != EmptyValue
final def removeAt(point: D#PointLike)(implicit tx: S#Tx): Option[A] =
removeLeafAt(point) match {
case EmptyValue => None
case oldLeaf: LeafImpl => Some(oldLeaf.value)
}
final def contains(elem: A)(implicit tx: S#Tx): Boolean = {
val point = pointView(elem, tx)
if (!hyperCube.contains(point)) return false
findAt(point) match {
case l: LeafImpl => l.value == elem
case _ => false
}
}
final def isDefinedAt(point: D#PointLike)(implicit tx: S#Tx): Boolean = {
if (!hyperCube.contains(point)) return false
findAt(point) != EmptyValue
}
final def get(point: D#PointLike)(implicit tx: S#Tx): Option[A] = {
if (!hyperCube.contains(point)) return None
findAt(point) match {
case l: LeafImpl => Some(l.value)
case _ => None
}
}
final def nearestNeighbor[M](point: D#PointLike, metric: DistanceMeasure[M, D])
(implicit tx: S#Tx): A = {
val nn = new NN(point, metric).find()
stat_report()
nn match {
case EmptyValue => throw new NoSuchElementException("nearestNeighbor on an empty tree")
case l: LeafImpl => l.value
}
}
final def nearestNeighborOption[M](point: D#PointLike, metric: DistanceMeasure[M, D])
(implicit tx: S#Tx): Option[A] = {
val nn = new NN(point, metric).find()
stat_report()
nn match {
case EmptyValue => None
case l: LeafImpl => Some(l.value)
}
}
final def isEmpty(implicit tx: S#Tx): Boolean = {
val n = head
val sz = numOrthants
@tailrec def step(i: Int): Boolean = if (i == sz) true
else n.child(i) match {
case n: NonEmptyChild => false
case _ => step(i + 1)
}
step(0)
}
final def numLevels(implicit tx: S#Tx): Int = {
@tailrec def step(b: BranchLike, num: Int): Int = {
b.next match {
case EmptyValue => num
case n: BranchLike => step(n, num + 1)
}
}
step(head, 1)
}
final def +=(elem: A)(implicit tx: S#Tx): this.type = {
insertLeaf(elem)
// match {
// case oldLeaf: LeafImpl => oldLeaf.dispose()
// case _ =>
// }
this
}
final def -=(elem: A)(implicit tx: S#Tx): this.type = {
removeLeafAt(pointView(elem, tx))
// match {
// case oldLeaf: LeafImpl => oldLeaf.dispose()
// case _ =>
// }
this
}
final def rangeQuery[Area](qs: QueryShape[Area, D])(implicit tx: S#Tx): Iterator[S#Tx, A] = {
val q = new RangeQuery(qs)
q.findNextValue()
q
}
final def toIndexedSeq(implicit tx: S#Tx): Vec[A] = iterator.toIndexedSeq
final def toList(implicit tx: S#Tx): List[A] = iterator.toList
// note that `iterator.toSeq` produces a `Stream` !!
final def toSeq(implicit tx: S#Tx): Seq[A] = iterator.toIndexedSeq
final def toSet(implicit tx: S#Tx): Set[A] = iterator.toSet
private def findAt(point: D#PointLike)(implicit tx: S#Tx): LeafOrEmpty = {
val p0 = findP0(point) // lastTreeImpl.findP0( point )
findLeafInP0(p0, point) // p0.findImmediateLeaf( point )
}
// OBSOLETE: the caller _must not call dispose_
//
// (( WARNING: if the returned oldLeaf is defined, the caller is
// responsible for disposing it (after extracting useful information such as its value) ))
private def insertLeaf(elem: A)(implicit tx: S#Tx): LeafOrEmpty = {
val point = pointView(elem, tx)
require(hyperCube.contains(point), point.toString + " lies out of root hyper-cube " + hyperCube)
val p0 = findP0(point) // lastTreeImpl.findP0( point )
val res = findLeafInP0(p0, point)
res match {
case EmptyValue =>
val leaf = p0.insert(point, elem)
skipList.add(leaf)
case oldLeaf: LeafImpl =>
// remove previous leaf
removeLeaf(point, oldLeaf)
// search anew
val p0b = findP0(point) // lastTreeImpl.findP0( point )
assert(findLeafInP0(p0b, point) == EmptyValue)
val leaf = p0b.insert(point, elem)
skipList.add(leaf)
}
res
}
// WARNING: if the returned oldLeaf is defined, the caller is
// responsible for disposing it (after extracting useful information such as its value)
private def removeLeafAt(point: D#PointLike)(implicit tx: S#Tx): LeafOrEmpty = {
if (!hyperCube.contains(point)) return EmptyValue
// "To insert or delete a point y into or from S, we first search the
// quadtree structure to locate y in each Qi ..."
val p0 = findP0(point) // lastTreeImpl.findP0( point )
// "... Then we insert or delete y
// in the binary Q0 and update our total order."
val res = findLeafInP0(p0, point) // p0.findImmediateLeaf( point )
res match {
case l: LeafImpl => removeLeaf(point, l)
case _ =>
}
res
}
def transformAt(point: D#PointLike)(fun: Option[A] => Option[A])(implicit tx: S#Tx): Option[A] = {
require(hyperCube.contains(point), s"$point lies out of root hyper-cube $hyperCube")
val p0 = findP0(point)
findLeafInP0(p0, point) match {
case EmptyValue =>
val res = None
fun(res).foreach { elem =>
val leaf = p0.insert(point, elem)
skipList.add(leaf)
}
res
case oldLeaf: LeafImpl =>
// it's not possible currently to update a leaf's value...
// remove previous leaf
val res = Some(oldLeaf.value)
removeLeaf(point, oldLeaf)
fun(res).foreach {
elem =>
// search anew
val p0b = findP0(point)
assert(findLeafInP0(p0b, point) == EmptyValue)
val leaf = p0b.insert(point, elem)
skipList.add(leaf)
}
res
}
}
/*
* After arriving at this node from a `findP0` call, this resolves
* the given point to an actual leaf.
*
* @return the `Leaf` child in this node associated with the given
* `point`, or `empty` if no such leaf exists.
*/
private def findLeafInP0(b: LeftBranch, point: D#PointLike)(implicit tx: S#Tx): LeafOrEmpty = {
val qidx = b.hyperCube.indexOf(point)
b.child(qidx) match {
case l: LeafImpl if pointView(l.value, tx) == point => l
case _ => EmptyValue
}
}
/*
* Finds to smallest interesting hyper-cube
* in Q0, containing a given point. This method
* traverses downwards into its children, or,
* if the "bottom" has been reached, tries to
* continue in Qi-1.
*
* @return the node defined by the given search `point`, or `empty`
* if no such node exists.
*/
private def findP0(point: D#PointLike)(implicit tx: S#Tx): LeftBranch = {
@tailrec def stepLeft(lb: LeftBranch): LeftBranch = {
val qidx = lb.hyperCube.indexOf(point)
lb.child(qidx) match {
case _: LeafOrEmpty => lb
case cb: LeftBranch =>
if (!cb.hyperCube.contains(point)) lb else stepLeft(cb)
}
}
@tailrec def step(b: BranchLike): LeftBranch = b match {
case lb: LeftBranch => stepLeft(lb)
case rb: RightBranch =>
val qidx = rb.hyperCube.indexOf(point)
val n = rb.child(qidx) match {
case cb: BranchLike if cb.hyperCube.contains(point) => cb
case _ => rb.prev
}
step(n)
}
step(lastTreeImpl)
}
private def removeLeaf(point: D#PointLike, l: LeafImpl)(implicit tx: S#Tx): Unit = {
// this will trigger removals from upper levels
val skipOk = skipList.remove(l)
assert(skipOk, s"Leaf $l with point $point was not found in skip list")
// now l is in P0. demote it once more (this will dispose the leaf)
l.parent.demoteLeaf(point /* pointView( l.value ) */ , l)
}
final def iterator(implicit tx: S#Tx): Iterator[S#Tx, A] = skipList.iterator.map(_.value)
private final class NNIter[M](val bestLeaf: LeafOrEmpty, val bestDist: M, val rmax: M)
private final class NN[M](point: D#PointLike, metric: DistanceMeasure[M, D])
extends scala.math.Ordering[VisitedNode[M]] {
stat_reset()
// NOTE: `sz` must be protected and not private, otherwise
// scala's specialization blows up
protected val sz = numOrthants
private val acceptedChildren = new Array[Branch](sz)
// private val acceptedDists = {
// implicit val mf = metric.manifest
// new Array[ M ]( sz )
// }
private val acceptedMinDists = metric.newArray(sz)
// private val acceptedMaxDists = metric.newArray(sz)
/* @tailrec */ private def findNNTailOLD(n0: /* Left */ Branch, pri: MPriorityQueue[VisitedNode[M]],
_bestLeaf: LeafOrEmpty, _bestDist: M, _rmax: M)
(implicit tx: S#Tx): NNIter[M] = {
stat_rounds1(_rmax)
var numAccepted = 0
var acceptedQidx = 0
var bestLeaf = _bestLeaf
var bestDist = _bestDist
var rmax = _rmax
var i = 0
while (i < sz) {
n0.child(i) match {
case l: LeafImpl =>
val ldist = metric.distance(point, pointView(l.value, tx))
if (metric.isMeasureGreater(bestDist, ldist)) { // found a point that is closer than previously known best result
bestDist = ldist
bestLeaf = l
if (metric.isMeasureGreater(rmax, bestDist)) { // update minimum required distance if necessary
rmax = bestDist // note: we'll re-check acceptedChildren at the end of the loop
}
}
case c: LeftBranch =>
val cq = c.hyperCube
val cMinDist = metric.minDistance(point, cq)
if (!metric.isMeasureGreater(cMinDist, rmax)) { // is less than or equal to minimum required distance
// (otherwise we're out already)
val cMaxDist = metric.maxDistance(point, cq)
if (metric.isMeasureGreater(rmax, cMaxDist)) {
rmax = cMaxDist // found a new minimum required distance
}
acceptedChildren(numAccepted) = c
acceptedMinDists(numAccepted) = cMinDist
// acceptedMaxDists(numAccepted) = cMaxDist
numAccepted += 1
acceptedQidx = i // this will be used only if numAccepted == 1
}
case _ => // ignore empty orthants
}
i += 1
}
if (rmax != _rmax) {
// recheck
var j = 0
while (j < numAccepted) {
if (metric.isMeasureGreater(acceptedMinDists(j), rmax)) {
// immediately kick it out
numAccepted -= 1
var k = j
while (k < numAccepted) {
val k1 = k + 1
acceptedChildren(k) = acceptedChildren(k1)
acceptedMinDists(k) = acceptedMinDists(k1)
// acceptedMaxDists(k) = acceptedMaxDists(k1)
k = k1
}
}
j += 1
}
}
// Unless exactly one child is accepted, round is over
// if (numAccepted != 1) {
/* var */ i = 0
while (i < numAccepted) { // ...and the children are added to the priority queue
val vn = new VisitedNode[M](acceptedChildren(i), acceptedMinDists(i) /*, acceptedMaxDists(i) */)
stat_pq_add1(vn)
pri += vn
i += 1
}
new NNIter[M](bestLeaf, bestDist, rmax)
// } else {
// Otherwise find corresponding node in highest level, and descend
// val dn0 = acceptedChildren(0)
// val qdn = dn0.hyperCube
//
// @tailrec def findRight(cand: BranchLike, prev: BranchLike): BranchLike = {
// prev.next match {
// case EmptyValue => cand
// case next: BranchLike =>
// next.child(acceptedQidx) match {
// case _: LeafOrEmpty => cand
// case cb: BranchLike =>
// if (cb.hyperCube != qdn) cand else findRight(cb, next)
// }
// }
// }
//
// val dn = findRight(dn0, n0)
//
// // now go left
// @tailrec def findLeft(n: BranchLike): LeftBranch = n match {
// case lb: LeftBranch => lb
// case rb: RightBranch => findLeft(rb.prev)
// }
//
// val dnl = findLeft(dn)
// assert(dnl == dn0) // if this assertion holds, that would make the whole process of going right/left useless
// findNNTailOLD(dn0 /* dnl */, pri, bestLeaf, bestDist, rmax)
// }
}
private def findNNTail(p0: Branch, pMinDist: M, pri: MPriorityQueue[VisitedNode[M]],
_bestLeaf: LeafOrEmpty, _bestDist: M, _rmax: M)
(implicit tx: S#Tx): NNIter[M] = {
stat_rounds1(_rmax)
var bestLeaf = _bestLeaf
var bestDist = _bestDist
var rmax = _rmax
def inspectLeaf(l: LeafImpl): Unit = {
val ldist = metric.distance(point, pointView(l.value, tx))
if (metric.isMeasureGreater(bestDist, ldist)) { // found a point that is closer than previously known best result
bestDist = ldist
bestLeaf = l
if (metric.isMeasureGreater(rmax, bestDist)) { // update minimum required distance if necessary
stat_debug(s"better NN candidate ${l.value}")
rmax = bestDist // note: we'll re-check acceptedChildren at the end of the loop
}
}
}
// pMinDist = parent's minDist
def scanChildren(parent: Branch): Int = {
var numAccepted = 0
var i = 0
while (i < sz) {
parent.child(i) match {
case l: LeafImpl => inspectLeaf(l)
case c: Branch =>
val cq = c.hyperCube
val cMinDist = metric.minDistance(point, cq)
if (cMinDist == pMinDist) { // equistabbing
stat_debug(s"... scanChild $cq has minDist $cMinDist == pMinDist ($pMinDist)")
// val cMaxDist = metric.maxDistance(point, cq)
acceptedChildren(numAccepted) = c
acceptedMinDists(numAccepted) = cMinDist
// acceptedMaxDists(numAccepted) = cMaxDist
numAccepted += 1
}
case _ =>
}
i += 1
}
numAccepted
}
// TODO: this needs to use the skip structure
def ancestorOf(b: Branch): Branch = {
val h = b.hyperCube
@tailrec def loop(q: Branch): Branch = {
val idx = q.hyperCube.indexOf(h)
q.child(idx) match {
case `b` => q
case c: Branch => loop(c)
case other => sys.error(s"Unexpected child $other")
}
}
loop(p0)
}
def pushChildren(b: Branch, skip: Int): Unit = {
stat_debug(s"pushing child nodes of ${b.hyperCube} to priority queue")
assert(b.isInstanceOf[LeftBranch])
var i = 0
while (i < sz) {
if (i != skip) b.child(i) match {
case l: LeafImpl => inspectLeaf(l)
case c: Branch =>
// val cq = c.hyperCube
// val cMinDist = metric.minDistance(point, cq)
// val cMinDist = {
var j = 0
// var min = metric.maxValue
while (j < sz) {
c.child(j) match {
case l: LeafImpl => inspectLeaf(l)
case _ =>
}
j += 1
}
j = 0
while (j < sz) {
c.child(j) match {
case cc: Branch =>
val cch = cc.hyperCube
val cmin = metric.minDistance(point, cch)
// if (!metric.isMeasureGreater(cmin, rmax) &&
// metric.isMeasureGreater( min, cmin)) min = cmin
if (!metric.isMeasureGreater(cmin, rmax)) {
// val cmax = metric.maxDistance(point, cch)
val vn = new VisitedNode(cc, cmin /* , cmax */)
pri += vn
stat_pq_add1(cch)
}
case _ =>
}
j += 1
}
// min
// }
// // ---- added filter ----
// // if (!metric.isMeasureGreater(cMinDist, rmax)) {
// if (cMinDist != metric.maxValue) {
// val cMaxDist = metric.maxDistance(point, cq) // ---- added ----
// val vn = new VisitedNode(c, cMinDist, cMaxDist)
// pri += vn
// stat_pq_add1(cq)
// }
case _ =>
}
i += 1
}
}
def finish(b: Branch): NNIter[M] = {
val b0 = inLowestLevel(b)
val numAnc = 1 // XXX TODO needs to be retrieved from metric and numDim
@tailrec def checkendorfer(b: Branch, si: Int, _anc: Int): Unit = {
val a = ancestorOf(b)
val ai = a.hyperCube.indexOf(b.hyperCube)
var anc = _anc
if (ai == si) {
// pushChildren(a, si)
var i = 0
while (i < sz) {
a.child(i) match {
case l: LeafImpl => inspectLeaf(l)
case c: Branch => if (i != si) pushChildren(c, -1)
case _ =>
}
i += 1
}
anc -= 1
}
if (anc > 0 && a != p0) {
checkendorfer(a, si, anc)
}
}
pushChildren(b0, -1) // TODO: do not need to inspect leaves again!
if (b0 != p0) metric.stabbingDirections(point, p0.hyperCube, b0.hyperCube).foreach { si =>
stat_debug(s"searching for ancestor in ${if (si == 0) "SW" else if (si == 1) "SE" else if (si == 2) "NE" else if (si == 3) "NW"} direction")
checkendorfer(b0, si, numAnc)
}
// val cMinDist = metric.minDistance(point, c.hyperCube)
// val cv = new VisitedNode(c, cMinDist, null.asInstanceOf[M])
// pri += cv
new NNIter(bestLeaf, bestDist, rmax)
}
@tailrec def loop(b: Branch): NNIter[M] = {
val num = scanChildren(b)
if (num >= 2) {
stat_debug(s"found $num equistabbing children. stop here")
finish(b)
} else if (num == 1) {
stat_debug(s"found 1 equistabbing child ${acceptedChildren(0).hyperCube}, minDist = ${acceptedMinDists(0)}. descend")
loop(acceptedChildren(0))
} else {
b.prevOption match {
case Some(prev) =>
stat_debug(s"found no equistabbing children. go to previous tree ${prev.hyperCube}")
loop(prev)
case _ =>
stat_debug("found no equistabbing children and ended in Q0. stop here")
finish(b)
}
}
}
val ph = inHighestLevel(p0)
stat_debug(s"begin round in ${ph.hyperCube}, minDist = $pMinDist")
loop(ph)
}
@tailrec private def inHighestLevel(b: Branch)(implicit tx: S#Tx): Branch = b.nextOption match {
case Some(n) => inHighestLevel(n)
case _ => b
}
@tailrec private def inLowestLevel(b: Branch)(implicit tx: S#Tx): Branch = b.prevOption match {
case Some(n) => inLowestLevel(n)
case _ => b
}
// private def findNNTailEquiPotency(n0: Branch, pri: MPriorityQueue[VisitedNode[M]],
// _bestLeaf: LeafOrEmpty, _bestDist: M, _rmax: M)
// (implicit tx: S#Tx): NNIter[M] = {
// stat_rounds1(_rmax)
//
// var bestLeaf = _bestLeaf
// var bestDist = _bestDist
// var rmax = _rmax
//
// def inspectLeaf(l: LeafImpl): Unit = {
// val ldist = metric.distance(point, pointView(l.value, tx))
// if (metric.isMeasureGreater(bestDist, ldist)) { // found a point that is closer than previously known best result
// bestDist = ldist
// bestLeaf = l
// if (metric.isMeasureGreater(rmax, bestDist)) { // update minimum required distance if necessary
// rmax = bestDist // note: we'll re-check acceptedChildren at the end of the loop
// }
// }
// }
//
// def scanChildren(b: Branch): Int = {
// var numAccepted = 0
// val oldRMax = rmax
// var i = 0
// while (i < sz) {
// b.child(i) match {
// case l: LeafImpl => inspectLeaf(l)
//
// case c: Branch =>
// val cq = c.hyperCube
// val cMinDist = metric.minDistance(point, cq)
// if (!metric.isMeasureGreater(cMinDist, rmax)) { // is less than or equal to minimum required distance
// // (otherwise we're out already)
// val cMaxDist = metric.maxDistance(point, cq)
// if (metric.isMeasureGreater(rmax, cMaxDist)) {
// rmax = cMaxDist // found a new minimum required distance
// }
// acceptedChildren(numAccepted) = c
// acceptedMinDists(numAccepted) = cMinDist
// acceptedMaxDists(numAccepted) = cMaxDist
// numAccepted += 1
// // acceptedQidx = i // this will be used only if numAccepted == 1
// }
//
// case _ =>
// }
// i += 1
// }
//
// if (rmax != oldRMax) {
// // recheck
// var j = 0
// while (j < numAccepted) {
// if (metric.isMeasureGreater(acceptedMinDists(j), rmax)) {
// // immediately kick it out
// numAccepted -= 1
// var k = j
// while (k < numAccepted) {
// val k1 = k + 1
// acceptedChildren(k) = acceptedChildren(k1)
// acceptedMinDists(k) = acceptedMinDists(k1)
// acceptedMaxDists(k) = acceptedMaxDists(k1)
// k = k1
// }
// }
// j += 1
// }
// }
//
// numAccepted
// }
//
// @tailrec def loop(b: Branch): NNIter[M] = {
// val num = scanChildren(b)
//
// @tailrec def findEquipotent(i: Int): Int = {
// if (i == num) -1 else {
// val res = metric.isEquipotent(v = point, rmax = rmax, parent = b.hyperCube,
// child = acceptedChildren(i).hyperCube)
// if (res) i else findEquipotent(i + 1)
// }
// }
//
// val eqi = findEquipotent(0)
// if (eqi >= 0) loop(acceptedChildren(eqi))
// else b.prevOption match {
// case Some(prev) => loop(prev)
// case _ =>
// var i = 0
// while (i < num) {
// val vn = new VisitedNode[M](acceptedChildren(i), acceptedMinDists(i), acceptedMaxDists(i))
// stat_pq_add1(vn)
// pri += vn
// i += 1
// }
// new NNIter(bestLeaf, bestDist, rmax)
// }
// }
//
// val nh = inHighestLevel(n0)
// loop(nh)
// }
// private def findNNTailNO(n0: Branch, pri: MPriorityQueue[VisitedNode[M]],
// _bestLeaf: LeafOrEmpty, _bestDist: M, _rmax: M)
// (implicit tx: S#Tx): NNIter[M] = {
// stat_rounds1()
//
// var numAccepted = 0
// // var acceptedQidx = 0
//
// var bestLeaf = _bestLeaf
// var bestDist = _bestDist
// var rmax = _rmax
//
// def fall(parent: Branch, qidx: Int): Unit = {
// parent.child(qidx) match {
// case l: LeafImpl =>
// val ldist = metric.distance(point, pointView(l.value, tx))
// if (metric.isMeasureGreater(bestDist, ldist)) { // found a point that is closer than previously known best result
// bestDist = ldist
// bestLeaf = l
// if (metric.isMeasureGreater(rmax, bestDist)) { // update minimum required distance if necessary
// rmax = bestDist // note: we'll re-check acceptedChildren at the end of the loop
// }
// }
// parent.prevOption match {
// case Some(prev) => fall(prev, qidx)
// case _ =>
// }
//
// case EmptyValue =>
// parent.prevOption match {
// case Some(prev) => fall(prev, qidx)
// case _ =>
// }
//
// case c: Branch =>
// val cq = c.hyperCube
// val cMinDist = metric.minDistance(point, cq)
// if (!metric.isMeasureGreater(cMinDist, rmax)) { // is less than or equal to minimum required distance
// // (otherwise we're out already)
// val cMaxDist = metric.maxDistance(point, cq)
// if (metric.isMeasureGreater(rmax, cMaxDist)) {
// rmax = cMaxDist // found a new minimum required distance
// }
// acceptedChildren(numAccepted) = c
// acceptedMinDists(numAccepted) = cMinDist
// acceptedMaxDists(numAccepted) = cMaxDist
// numAccepted += 1
// // acceptedQidx = i // this will be used only if numAccepted == 1
// }
// }
// }
//
// var i = 0
// while (i < sz) {
// fall(n0, i)
// i += 1
// }
//
// if (rmax != _rmax) {
// // recheck
// var j = 0
// while (j < numAccepted) {
// if (metric.isMeasureGreater(acceptedMinDists(j), rmax)) {
// // immediately kick it out
// numAccepted -= 1
// var k = j
// while (k < numAccepted) {
// val k1 = k + 1
// acceptedChildren(k) = acceptedChildren(k1)
// acceptedMinDists(k) = acceptedMinDists(k1)
// acceptedMaxDists(k) = acceptedMaxDists(k1)
// k = k1
// }
// }
// j += 1
// }
// }
//
// i = 0
// while (i < numAccepted) { // ...and the children are added to the priority queue
// val vn = new VisitedNode[M](acceptedChildren(i), acceptedMinDists(i), acceptedMaxDists(i))
// stat_pq_add1(vn)
// pri += vn
// i += 1
// }
// new NNIter[M](bestLeaf, bestDist, rmax)
// }
def find()(implicit tx: S#Tx): LeafOrEmpty = {
val pri = MPriorityQueue.empty[VisitedNode[M]](this)
@tailrec def step(p0: Branch, pMinDist: M, bestLeaf: LeafOrEmpty, bestDist: M, rmax: M): LeafOrEmpty = {
val res = findNNTailOLD(p0, /* pMinDist, */ pri, bestLeaf, bestDist, rmax)
if (metric.isMeasureZero(res.bestDist)) {
res.bestLeaf // found a point exactly at the query position, so stop right away
} else {
if (pri.isEmpty) res.bestLeaf
else {
val vis = pri.dequeue()
stat_pq_rem1(vis.n.hyperCube)
// if (!metric.isMeasureGreater(vis.minDist, res.rmax)) vis.n else pop()
// because the queue is sorted by smallest minDist, if we find an element
// whose minimum distance is greater than the maximum distance allowed,
// we are done and do not need to process the remainder of the priority queue.
if (metric.isMeasureGreater(vis.minDist, res.rmax)) res.bestLeaf else {
val lb = vis.n
step(lb, vis.minDist, res.bestLeaf, res.bestDist, res.rmax)
}
}
}
}
val mmax = metric.maxValue
val p = headTree // lastTree
val pMinDist = metric.minDistance(point, hyperCube) // XXX could have metric.zero
step(p, pMinDist, EmptyValue, mmax, mmax)
}
def compare(a: VisitedNode[M], b: VisitedNode[M]) = {
val min = metric.compareMeasure(b.minDist, a.minDist)
min // if (min != 0) min else metric.compareMeasure(b.maxDist, a.maxDist)
}
}
private final class VisitedNode[M](val n: Branch, val minDist: M /*, val maxDist: M */) {
override def toString = s"($n, min = $minDist" // , max = $maxDist)"
}
// note: Iterator is not specialized, hence we can safe use the effort to specialize in A anyway
private final class RangeQuery[Area](qs: QueryShape[Area, D]) extends Iterator[S#Tx, A] {
val sz = numOrthants
val stabbing = MQueue.empty[(BranchLike, Area)]
// Tuple2 is specialized for Long, too!
val in = MQueue.empty[NonEmptyChild]
var current: A = _
// overwritten by initial run of `findNextValue`
var hasNextVar = true // eventually set to `false` by `findNextValue`
stabbing += head -> qs.overlapArea(head.hyperCube)
// findNextValue()
override def toString = octree.toString + ".rangeQuery(" + qs + ")"
def hasNext(implicit tx: S#Tx): Boolean = hasNextVar
// search downwards:
// "At each square q ∈ Qi we either go to a child square in Qi
// that covers the same area of R ∪ A as p does, if such a child
// square exists, or jump to the next level q ∈ Qi−1."
@tailrec private def findEquiStabbingTail(node: BranchLike, area: Area)(implicit tx: S#Tx): LeftBranch = {
var pi = node
var i = 0
while (i < sz) {
pi.child(i) match {
case pic: BranchLike =>
val a2 = qs.overlapArea(pic.hyperCube)
if (a2 == area) {
pi = pic
i = 0 // start over in child
} else {
i += 1
}
case _ => i += 1
}
}
// ... or jump to the next (previous) level
pi match {
case lb: LeftBranch => lb
case rb: RightBranch => findEquiStabbingTail(rb.prev, area)
}
}
// the movement from Q0 to Qj
// "assuming that p is not critical in Q0, we promote to Qj where Qj is the highest
// level in which p is not a critical square"
//
// definition of critical square:
// "a stabbing node of Qi whose child nodes are either not stabbing, or still
// stabbing but cover less volume of R than p does."
// ; bzw. umgedreht: eine unkritische node ist eine, in der es mindestens eine stabbing node
// mit derselben ueberlappungsflaeche gibt!
//
// definition stabbing: 0 < overlap-area < area-of-p
@tailrec def findHighestUncritical(p0: BranchLike, area: Area)(implicit tx: S#Tx): BranchLike = {
@tailrec def isCritical(b: BranchLike, i: Int): Boolean = {
i < sz && (b.child(i) match {
// if there is any child which has the same overlap area, it means the node is uncritical
case ci: BranchLike if qs.overlapArea(ci.hyperCube) == area => true
case _ => isCritical(b, i + 1)
})
}
p0.next match {
case EmptyValue => p0
case pi: BranchLike => if (isCritical(pi, 0)) p0 else findHighestUncritical(pi, area)
}
}
def next()(implicit tx: S#Tx): A = {
if (!hasNextVar) endReached()
val res = current
findNextValue()
res
}
def findNextValue()(implicit tx: S#Tx): Unit = {
while (true) {
if (in.isEmpty) {
if (stabbing.isEmpty) {
hasNextVar = false
return
}
val tup = stabbing.dequeue()
val ns = tup._1 // stabbing node
val as = tup._2 // overlapping area with query shape
val hi = findHighestUncritical(ns, as) // find highest uncritical hyper-cube of the stabbing node
val nc = findEquiStabbingTail (hi, as) // now traverse towards Q0 to find the critical square
var i = 0
while (i < sz) {
nc.child(i) match {
case cl: LeafImpl =>
if (qs.contains(pointView(cl.value, tx))) in += cl
case cn: ChildBranch =>
val q = cn.hyperCube
val ao = qs.overlapArea(q)
// test for stabbing or inclusion:
// inclusion: overlap-area == area-of-p
// stabbing: 0 < overlap-area < area-of-p
if (qs.isAreaNonEmpty(ao)) {
// q is _not_ out
if (qs.isAreaGreater(q, ao)) {
// q is stabbing
stabbing += cn -> ao
} else {
// q is in
in += cn
}
}
case _ =>
}
i += 1
}
} else {
// XXX scalac currently complains that this match doesn't account
// for LeftChildBranch and RightChildBranch. but both are
// captured by BranchLike, so this seems to be a bug.
(in.dequeue(): @unchecked) match {
case l: LeafImpl =>
current = l.value
return
case n: BranchLike =>
var i = 0
while (i < sz) {
n.child(i) match {
case cc: NonEmptyChild => in += cc // sucky `enqueue` creates intermediate Seq because of varargs
case _ =>
}
i += 1
}
}
}
}
}
}
final protected type ChildOption = Child with Writable
/** A node is an object that can be stored in a orthant of a branch. */
protected sealed trait NonEmpty extends Identifiable[S#ID] /* extends Down with Child */ {
protected def shortString: String
override def toString = shortString + id
override def equals(that: Any): Boolean = that match {
case n: Identifiable[_] => id == n.id // do _not_ match against n: NonEmpty because that's an inner class!!!
case _ => super.equals(that)
}
override def hashCode = id.hashCode()
/** Computes the greatest interesting hyper-cube within
* a given hyper-cube `mq` so that this (leaf's or node's)
* hyper-cube and the given point will be placed in
* separated orthants of this resulting hyper-cube.
*/
def union(mq: D#HyperCube, point: D#PointLike)(implicit tx: S#Tx): D#HyperCube
/**
* Queries the orthant index for this (leaf's or node's) hyper-cube
* with respect to a given outer hyper-cube `iq`.
*/
def orthantIndexIn(iq: D#HyperCube)(implicit tx: S#Tx): Int
}
/** A tree element in Q0 has markers for the
* in-order traversal. NOT ANYMORE
*/
protected sealed trait LeftNonEmpty extends Left with NonEmpty
protected sealed trait Left
protected sealed trait LeftChild extends Left with Child
protected type LeftChildOption = LeftChild with Writable
/** A common trait used in pattern matching, comprised of `Leaf` and `LeftChildBranch`. */
protected sealed trait LeftNonEmptyChild extends LeftNonEmpty with NonEmptyChild with LeftChild with Writable /* Mutable[ S ] */ {
def updateParentLeft(p: LeftBranch)(implicit tx: S#Tx): Unit
}
protected sealed trait RightChild extends Child
protected type RightChildOption = RightChild with Writable
/** A common trait used in pattern matching, comprised of `Leaf` and `RightChildBranch`. */
protected sealed trait RightNonEmptyChild extends RightChild with NonEmptyChild with Writable {
def updateParentRight(p: RightBranch)(implicit tx: S#Tx): Unit
}
/*
* Serialization-id: 1
*/
private def readLeaf(in: DataInput, access: S#Acc, id: S#ID)(implicit tx: S#Tx): LeafImpl = {
val value = keySerializer.read(in, access)
val parentRef = tx.readVar[BranchLike](id, in)
new LeafImpl(id, value, parentRef)
}
/** A leaf in the octree, carrying a map entry
* in the form of a point and associated value.
* Note that a single instance of a leaf is used
* across the levels of the octree! That means
* that multiple child pointers may go to the
* same leaf, while the parent of a leaf always
* points into the highest level octree that
* the leaf resides in, according to the skiplist.
*/
protected final class LeafImpl(val id: S#ID, val value: A, /* val point: D#PointLike, */ parentRef: S#Var[BranchLike])
extends LeftNonEmptyChild with RightNonEmptyChild with LeafOrEmpty with Leaf {
def updateParentLeft (p: LeftBranch )(implicit tx: S#Tx): Unit = parent_=(p)
def updateParentRight(p: RightBranch)(implicit tx: S#Tx): Unit = parent_=(p)
def parent (implicit tx: S#Tx): BranchLike = parentRef()
def parent_=(p: BranchLike)(implicit tx: S#Tx): Unit = parentRef() = p
def isLeaf = true
def isBranch = false
def asLeaf: LeafImpl = this
def asBranch: BranchLike = opNotSupported
def dispose()(implicit tx: S#Tx): Unit = {
id.dispose()
parentRef.dispose()
}
def write(out: DataOutput): Unit = {
out.writeByte(1)
id.write(out)
keySerializer.write(value, out)
parentRef.write(out)
}
def union(mq: D#HyperCube, point2: D#PointLike)(implicit tx: S#Tx): D#HyperCube =
mq.greatestInteresting(pointView(value, tx), point2)
def orthantIndexIn(iq: D#HyperCube)(implicit tx: S#Tx): Int =
iq.indexOf(pointView(value, tx))
def shortString = s"Leaf($value)"
def remove()(implicit tx: S#Tx): Unit = dispose()
}
/** Nodes are defined by a hyperCube area as well as a list of children,
* as well as a pointer `next` to the corresponding node in the
* next highest tree. A `Branch` also provides various search methods.
*/
protected sealed trait BranchLike extends NonEmpty with Writable /* Mutable[ S ] */ with Branch {
/** Returns the child for a given orthant index. */
def child(idx: Int)(implicit tx: S#Tx): ChildOption
/** Assuming that the given `leaf` is a child of this node,
* removes the child from this node's children. This method
* will perform further clean-up such as merging this node
* with its parent if it becomes uninteresting as part of the
* removal.
*/
def demoteLeaf(point: D#PointLike, leaf: LeafImpl)(implicit tx: S#Tx): Unit
/** Returns the hyper-cube covered by this node. */
def hyperCube: D#HyperCube
/** Returns the corresponding interesting
* node in Qi+1, or `empty` if no such
* node exists.
*/
final def next(implicit tx: S#Tx): NextOption = nextRef()
final def nextOption(implicit tx: S#Tx): Option[BranchLike] = next match {
case EmptyValue => None
case b: BranchLike => Some(b)
}
/** Sets the corresponding interesting
* node in Qi+1.
*/
final def next_=(node: NextOption)(implicit tx: S#Tx): Unit = nextRef() = node
protected def nextRef: S#Var[NextOption]
final def union(mq: D#HyperCube, point2: D#PointLike)(implicit tx: S#Tx): D#HyperCube = {
val q = hyperCube
mq.greatestInteresting(q, point2)
}
final def orthantIndexIn(iq: D#HyperCube)(implicit tx: S#Tx): Int = iq.indexOf(hyperCube)
/** Called when a leaf has been removed from the node.
* The node may need to cleanup after this, e.g. promote
* an under-full node upwards.
*/
protected def leafRemoved()(implicit tx: S#Tx): Unit
protected def nodeName: String
final protected def shortString = s"$nodeName($hyperCube)"
final def isLeaf = false
final def isBranch = true
final def asBranch: BranchLike = this
final def asLeaf: LeafImpl = opNotSupported
}
/** An inner non empty tree element has a mutable parent node. */
sealed trait NonEmptyChild extends NonEmpty with Child {
def parent(implicit tx: S#Tx): BranchLike
}
protected sealed trait LeafOrEmpty extends LeftChild
// fix for deserialization equality problem thanks to
// Eugene Yokota
// (http://stackoverflow.com/questions/9893522/fixing-case-object-identity-pattern-matching-under-serialization/9894036#9894036)
case object EmptyValue extends LeftChild with RightChild with Next with LeafOrEmpty with Empty with Writable /* EmptyMutable */ {
override def toString = ""
def write(out: DataOutput): Unit = out.writeByte(0)
override def hashCode: Int = 0
// grmpff....
import language.existentials
override def equals(that: Any): Boolean =
that.isInstanceOf[x.EmptyValue.type forSome {val x: DeterministicSkipOctree[_, _, _]}]
}
/** Utility trait which elements the rightward search `findPN`. */
protected sealed trait ChildBranch extends BranchLike with NonEmptyChild
protected sealed trait Next
final protected type NextOption = Next with Writable
/** A right tree node implementation provides more specialized child nodes
* of type `RightChild`. It furthermore defines the node in Qi-1 via the
* `prev` method.
*/
protected sealed trait RightBranch extends Next with BranchLike {
protected def children: Array[S#Var[RightChildOption]]
final def prevOption: Option[Branch] = Some(prev: Branch)
def prev: BranchLike
final def child (idx: Int) (implicit tx: S#Tx): RightChildOption = children(idx)()
final def updateChild(idx: Int, c: RightChildOption)(implicit tx: S#Tx): Unit = children(idx)() = c
/** Promotes a leaf that exists in Qi-1 to this
* tree, by inserting it into this node which
* is its interesting node in Qi.
*
* If the result of insertion is a new child node
* below this node, this intermediate node will
* be connected to Qi by looking for the corresponding
* hyper-cube in the given search path that led here
* (i.e. that was constructed in `findPN`).
*
* This method also sets the parent of the leaf
* accordingly.
*/
final def insert(point: D#PointLike, leaf: LeafImpl)(implicit tx: S#Tx): Unit = {
// val point = pointView( leaf.value )
val qidx = hyperCube.indexOf(point)
child(qidx) match {
case EmptyValue =>
updateChild(qidx, leaf)
leaf.parent = this
case old: RightNonEmptyChild =>
// determine the greatest interesting square for the new
// intermediate node to create
val qn2 = old.union(hyperCube.orthant(qidx), point)
// find the corresponding node in the lower tree
@tailrec def findInPrev(b: BranchLike): BranchLike = {
if (b.hyperCube == qn2) b
else {
val idx = b.hyperCube.indexOf(point)
b.child(idx) match {
case _: LeafOrEmpty => sys.error("Internal error - cannot find sub-cube in prev")
case cb: BranchLike => findInPrev(cb)
}
}
}
val pPrev = findInPrev(prev)
val n2 = newNode(qidx, pPrev, qn2)
val oidx = old.orthantIndexIn(qn2)
n2.updateChild(oidx, old)
// This is a tricky bit! And a reason
// why should eventually try to do without
// parent pointers at all. Since `old`
// may be a leaf whose parent points
// to a higher level tree, we need to
// check first if the parent is `this`,
// and if so, adjust the parent to point
// to the new intermediate node `ne`!
if (old.parent == this) old.updateParentRight(n2)
val lIdx = qn2.indexOf(point)
n2.updateChild(lIdx, leaf)
leaf.parent = n2
}
}
/*
* Instantiates an appropriate
* sub-node whose parent is this node, and whose predecessor
* in the lower octree is given.
*
* @param qidx the orthant index in this node where the node belongs
* @param prev the new node's prev field, i.e. its correspondant in
* Qi-1
* @param iq the hyper-cube for the new node
* @return the new node which has already been inserted into this node's
* children at index `qidx`.
*/
@inline private def newNode(qidx: Int, prev: BranchLike, iq: D#HyperCube)
(implicit tx: S#Tx): RightChildBranch = {
val sz = children.length
val ch = tx.newVarArray[RightChildOption](sz)
val cid = tx.newID()
var i = 0
while (i < sz) {
ch(i) = tx.newVar[RightChildOption](cid, EmptyValue)
i += 1
}
val parentRef = tx.newVar[RightBranch](cid, this)
val rightRef = tx.newVar[NextOption](cid, EmptyValue)(RightOptionReader)
val n = new RightChildBranch(cid, parentRef, prev, iq, ch, rightRef)
prev.next = n
updateChild(qidx, n)
n
}
final def demoteLeaf(point: D#PointLike, leaf: LeafImpl)(implicit tx: S#Tx): Unit = {
val qIdx = hyperCube.indexOf(point)
assert(child(qIdx) == leaf)
updateChild(qIdx, EmptyValue)
@tailrec def findParent(b: BranchLike, idx: Int): BranchLike = b.child(idx) match {
case sl: LeafImpl => assert(sl == leaf); b
case cb: BranchLike => findParent(cb, cb.hyperCube.indexOf(point))
}
val newParent = findParent(prev, qIdx)
leafRemoved()
leaf.parent = newParent
}
}
/** A left tree node implementation provides more specialized child nodes
* of type `LeftChild`. It furthermore defines a resolution method
* `findImmediateLeaf` which is typically called after arriving here
* from a `findP0` call.
*/
protected sealed trait LeftBranch extends /* LeftBranchOption with */ BranchLike with LeftNonEmpty {
/** For a `LeftBranch`, all its children are more specific
* -- they are instances of `LeftChild` and thus support
* order intervals.
*/
protected def children: Array[S#Var[LeftChildOption]]
final def prevOption: Option[Branch] = None
final def child (idx: Int) (implicit tx: S#Tx): LeftChildOption = children(idx)()
final def updateChild(idx: Int, c: LeftChildOption)(implicit tx: S#Tx): Unit = children(idx)() = c
final def demoteLeaf(point: D#PointLike, leaf: LeafImpl)(implicit tx: S#Tx): Unit = {
val qIdx = hyperCube.indexOf(point)
val ok = child(qIdx) == leaf
if (ok) {
updateChild(qIdx, EmptyValue)
leafRemoved()
leaf.remove() // dispose()
} else {
if (!DeterministicSkipOctree.sanitizing)
assert(assertion = false, s"Internal error - expected $leaf not found in $this")
}
}
final def insert(point: D#PointLike, value: A)(implicit tx: S#Tx): LeafImpl = {
val qIdx = hyperCube.indexOf(point)
child(qIdx) match {
case EmptyValue =>
newLeaf(qIdx, /* point, */ value) // (this adds it to the children!)
case old: LeftNonEmptyChild =>
// define the greatest interesting square for the new node to insert
// in this node at qidx:
val qn2 = old.union(hyperCube.orthant(qIdx), point)
// create the new node (this adds it to the children!)
val n2 = newNode(qIdx, qn2)
val oIdx = old.orthantIndexIn(qn2)
n2.updateChild(oIdx, old)
val lIdx = qn2.indexOf(point)
assert(oIdx != lIdx)
// This is a tricky bit! And a reason
// why should eventually try to do without
// parent pointers at all. Since `old`
// may be a leaf whose parent points
// to a higher level tree, we need to
// check first if the parent is `this`,
// and if so, adjust the parent to point
// to the new intermediate node `ne`!
if (old.parent == this) old.updateParentLeft(n2)
(n2: LeftBranch).newLeaf(lIdx, value) // cf. SI-8432
}
}
/** Instantiates an appropriate
* leaf whose parent is this node, and which should be
* ordered according to its position in this node.
*
* @param qIdx the orthant index of the new leaf in this node
* @param value the value associated with the new leaf
* @return the new leaf which has already assigned this node as
* parent and is already stored in this node's children
* at index `qIdx`
*/
private def newLeaf(qIdx: Int, value: A)(implicit tx: S#Tx): LeafImpl = {
val leafID = tx.newID()
val parentRef = tx.newVar[BranchLike](leafID, this)
val l = new LeafImpl(leafID, value, parentRef)
updateChild(qIdx, l)
l
}
/*
* Instantiates an appropriate
* sub-node whose parent is this node, and which should be
* ordered according to its position in this node.
*
* @param qidx the orthant index of the new node in this (parent) node
* @param iq the hyper-cube of the new node
* @return the new node which has already assigned this node as
* parent and is already stored in this node's children
* at index `qIdx`
*/
private def newNode(qIdx: Int, iq: D#HyperCube)(implicit tx: S#Tx): LeftChildBranch = {
val sz = children.length
val ch = tx.newVarArray[LeftChildOption](sz)
val cid = tx.newID()
var i = 0
while (i < sz) {
ch(i) = tx.newVar[LeftChildOption](cid, EmptyValue)(LeftChildOptionSerializer)
i += 1
}
val parentRef = tx.newVar[LeftBranch](cid, this)
val rightRef = tx.newVar[NextOption](cid, EmptyValue)(RightOptionReader)
val n = new LeftChildBranch(
cid, parentRef, iq, children = ch, nextRef = rightRef
)
updateChild(qIdx, n)
n
}
}
protected sealed trait TopBranch extends BranchLike {
final def hyperCube: D#HyperCube = octree.hyperCube
}
/*
* Serialization-id: 2
*/
private def readLeftTopBranch(in: DataInput, access: S#Acc, id: S#ID)(implicit tx: S#Tx): LeftTopBranch = {
val sz = numOrthants
val ch = tx.newVarArray[LeftChildOption](sz)
var i = 0
while (i < sz) {
ch(i) = tx.readVar[LeftChildOption](id, in)(LeftChildOptionSerializer)
i += 1
}
val nextRef = tx.readVar[NextOption](id, in)(RightOptionReader)
new LeftTopBranch(id, children = ch, nextRef = nextRef)
}
protected final class LeftTopBranch(val id: S#ID,
protected val children: Array[S#Var[LeftChildOption]],
protected val nextRef: S#Var[NextOption])
extends LeftBranch with TopBranch with Mutable[S#ID, S#Tx] {
// that's alright, we don't need to do anything special here
protected def leafRemoved()(implicit tx: S#Tx) = ()
def dispose()(implicit tx: S#Tx): Unit = {
id.dispose()
var i = 0
val sz = children.length
while (i < sz) {
children(i).dispose()
i += 1
}
nextRef.dispose()
}
def write(out: DataOutput): Unit = {
out.writeByte(2)
id.write(out)
// no need to write the hyperCube?
var i = 0
val sz = children.length
while (i < sz) {
children(i).write(out)
i += 1
}
nextRef.write(out)
}
protected def nodeName = "LeftTop"
}
/*
* Serialization-id: 3
*/
private def readLeftChildBranch(in: DataInput, access: S#Acc, id: S#ID)(implicit tx: S#Tx): LeftChildBranch = {
val parentRef = tx.readVar[LeftBranch](id, in)
val hyperCube = space.hyperCubeSerializer.read(in)
val sz = numOrthants
val ch = tx.newVarArray[LeftChildOption](sz)
var i = 0
while (i < sz) {
ch(i) = tx.readVar[LeftChildOption](id, in)(LeftChildOptionSerializer)
i += 1
}
val nextRef = tx.readVar[NextOption](id, in)(RightOptionReader)
new LeftChildBranch(id, parentRef, hyperCube, children = ch, nextRef = nextRef)
}
protected final class LeftChildBranch(val id: S#ID, parentRef: S#Var[LeftBranch], val hyperCube: D#HyperCube,
protected val children: Array[S#Var[LeftChildOption]],
protected val nextRef: S#Var[NextOption])
extends LeftBranch with ChildBranch with LeftNonEmptyChild {
protected def nodeName = "LeftInner"
def updateParentLeft(p: LeftBranch)(implicit tx: S#Tx): Unit = parent = p
def parent (implicit tx: S#Tx): LeftBranch = parentRef()
def parent_=(node: LeftBranch)(implicit tx: S#Tx): Unit = parentRef() = node
def dispose()(implicit tx: S#Tx): Unit = {
id .dispose()
parentRef .dispose()
var i = 0
val sz = children.length
while (i < sz) {
children(i).dispose()
i += 1
}
nextRef.dispose()
}
def write(out: DataOutput): Unit = {
out.writeByte(3)
id.write(out)
parentRef.write(out)
space.hyperCubeSerializer.write(hyperCube, out)
var i = 0
val sz = children.length
while (i < sz) {
children(i).write(out)
i += 1
}
nextRef.write(out)
}
private def remove()(implicit tx: S#Tx): Unit = dispose()
// make sure the node is not becoming uninteresting, in which case
// we need to merge upwards
protected def leafRemoved()(implicit tx: S#Tx): Unit = {
val sz = children.length
@tailrec def removeIfLonely(i: Int): Unit =
if (i < sz) child(i) match {
case lonely: LeftNonEmptyChild =>
@tailrec def isLonely(j: Int): Boolean = {
j == sz || (child(j) match {
case _: LeftNonEmptyChild => false
case _ => isLonely(j + 1)
})
}
if (isLonely(i + 1)) {
val p = parent
val myIdx = p.hyperCube.indexOf(hyperCube)
p.updateChild(myIdx, lonely)
if (lonely.parent == this) lonely.updateParentLeft(p)
remove() // dispose() // removeAndDispose()
}
case _ => removeIfLonely(i + 1)
}
removeIfLonely(0)
}
}
/*
* Serialization-id: 4
*/
private def readRightTopBranch(in: DataInput, access: S#Acc, id: S#ID)(implicit tx: S#Tx): RightTopBranch = {
val prev = TopBranchSerializer.read(in, access)
val sz = numOrthants
val ch = tx.newVarArray[RightChildOption](sz)
var i = 0
while (i < sz) {
ch(i) = tx.readVar[RightChildOption](id, in)
i += 1
}
val nextRef = tx.readVar[NextOption](id, in)(RightOptionReader)
new RightTopBranch(id, prev, ch, nextRef)
}
protected final class RightTopBranch(val id: S#ID, val prev: TopBranch,
protected val children: Array[S#Var[RightChildOption]],
protected val nextRef: S#Var[NextOption])
extends RightBranch with TopBranch {
protected def nodeName = "RightTop"
// def hyperCube = impl.hyperCube
private def remove()(implicit tx: S#Tx): Unit = {
// first unlink
assert(lastTreeImpl == this)
lastTreeImpl = prev
prev.next = EmptyValue
dispose()
}
def dispose()(implicit tx: S#Tx): Unit = {
id.dispose()
// // first unlink
// assert( lastTreeImpl == this )
// lastTreeImpl= prev
// prev.next = EmptyValue
// then dispose refs
var i = 0
val sz = children.length
while (i < sz) {
children(i).dispose()
i += 1
}
nextRef.dispose()
}
def write(out: DataOutput): Unit = {
out.writeByte(4)
id.write(out)
// no need to write the hypercube!
prev.write(out)
var i = 0
val sz = children.length
while (i < sz) {
children(i).write(out)
i += 1
}
nextRef.write(out)
}
// remove this node if it empty now and right-node tree
protected def leafRemoved()(implicit tx: S#Tx): Unit = {
if (next != EmptyValue) return
val sz = children.length
var i = 0
while (i < sz) {
val c = child(i)
if (c != EmptyValue) return // node not empty, abort the check
i += 1
}
// ok, we are the right most tree and the node is empty...
remove()
}
// private def removeAndDispose()( implicit tx: S#Tx ) {
// assert( lastTreeImpl == this )
// lastTreeImpl= prev
// prev.next = EmptyValue
// dispose()
// }
}
/*
* Serialization-id: 5
*/
private def readRightChildBranch(in: DataInput, access: S#Acc, id: S#ID)(implicit tx: S#Tx): RightChildBranch = {
val parentRef = tx.readVar[RightBranch](id, in)
val prev = BranchSerializer.read(in, access)
val hyperCube = space.hyperCubeSerializer.read(in)
val sz = numOrthants
val ch = tx.newVarArray[RightChildOption](sz)
var i = 0
while (i < sz) {
ch(i) = tx.readVar[RightChildOption](id, in)
i += 1
}
val nextRef = tx.readVar[NextOption](id, in)(RightOptionReader)
new RightChildBranch(id, parentRef, prev, hyperCube, ch, nextRef)
}
private final class RightChildBranch(val id: S#ID, parentRef: S#Var[RightBranch],
val prev: BranchLike, val hyperCube: D#HyperCube,
protected val children: Array[S#Var[RightChildOption]],
protected val nextRef: S#Var[NextOption])
extends RightBranch with ChildBranch with RightNonEmptyChild {
protected def nodeName = "RightInner"
def updateParentRight(p: RightBranch)(implicit tx: S#Tx): Unit = parent = p
private def remove()(implicit tx: S#Tx): Unit = {
// first unlink
prev.next = EmptyValue
dispose()
}
def dispose()(implicit tx: S#Tx): Unit = {
id.dispose()
// // first unlink
// prev.next = EmptyValue
// then dispose refs
parentRef.dispose()
var i = 0
val sz = children.length
while (i < sz) {
children(i).dispose()
i += 1
}
nextRef.dispose()
}
def write(out: DataOutput): Unit = {
out.writeByte(5)
id.write(out)
parentRef.write(out)
prev.write(out)
space.hyperCubeSerializer.write(hyperCube, out)
var i = 0
val sz = children.length
while (i < sz) {
children(i).write(out)
i += 1
}
nextRef.write(out)
}
// private def removeAndDispose()( implicit tx: S#Tx ): Unit = {
// prev.next = EmptyValue
// dispose()
// }
def parent (implicit tx: S#Tx): RightBranch = parentRef()
def parent_=(node: RightBranch)(implicit tx: S#Tx): Unit = parentRef() = node
// make sure the node is not becoming uninteresting, in which case
// we need to merge upwards
protected def leafRemoved()(implicit tx: S#Tx): Unit = {
val sz = children.length
@tailrec def removeIfLonely(i: Int): Unit =
if (i < sz) child(i) match {
case lonely: RightNonEmptyChild =>
@tailrec def isLonely(j: Int): Boolean = {
j == sz || (child(j) match {
case _: RightNonEmptyChild => false
case _ => isLonely(j + 1)
})
}
if (isLonely(i + 1)) {
val p = parent
val myIdx = p.hyperCube.indexOf(hyperCube)
p.updateChild(myIdx, lonely)
if (lonely.parent == this) lonely.updateParentRight(p)
remove()
}
case _ => removeIfLonely(i + 1)
}
removeIfLonely(0)
}
}
def debugPrint()(implicit tx: S#Tx): String = {
val baos = new ByteArrayOutputStream()
val ps = new PrintStream(baos)
import ps._
println(s"Debug print for $this")
println("Skip list of leaves:")
println(skipList.debugPrint())
println("Octree structure:")
def dumpTree(b: BranchLike, indent: Int): Unit = {
val iStr = " " * indent
b match {
case lb: LeftBranch =>
println(s"${iStr}LeftBranch${lb.id} with ${b.hyperCube}")
case _ =>
println(s"${iStr}RightBranch${b.id} with ${b.hyperCube}")
}
for(i <- 0 until numOrthants) {
print(s"$iStr Child #${i+1} = ")
b.child(i) match {
case cb: BranchLike =>
println("Branch:")
dumpTree(cb, indent + 4)
case l: LeafImpl =>
println(s"Leaf${l.id} ${l.value}")
case empty => println(empty)
}
}
}
def dumpTrees(b: BranchLike, level: Int): Unit = {
println(s"\n---level $level----")
dumpTree(b, 0)
b.nextOption.foreach(b => dumpTrees(b, level + 1))
}
dumpTrees(head, 0)
ps.close()
new String(baos.toByteArray, "UTF-8")
}
}