├── .gitignore ├── LICENSE.md ├── README.md ├── build.sbt ├── lib └── Thyme.jar ├── project └── build.properties └── src ├── main └── scala │ └── scalax │ └── collection │ └── immutable │ └── BTreeSet.scala └── test └── scala └── scalax └── collection └── immutable ├── BTreeSetSpec.scala └── BTreeSetThyme.scala /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | .classpath 3 | .project 4 | .settings/ 5 | .worksheet/ 6 | target/ 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Zilverline B.V. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Immutable, in-memory B-Trees for Scala 2 | ====================================== 3 | 4 | Current status: proof-of-concept. 5 | 6 | Performance is in the same ball-park as the standard Red-Black Tree 7 | used by Scala's TreeSet implementation, but gets better when there are 8 | many elements in the tree, due to higher memory efficiency. 9 | 10 | Overall the B-Tree uses about 1/5th the memory of a Red-Black Tree 11 | (assuming a minimum of 16 values in leaves). 12 | 13 | 14 | Benchmarks 15 | ---------- 16 | 17 | Start sbt with something like: 18 | 19 | ``` 20 | $ SBT_OPTS="-Xms1G -Xmx1G -XX:+UseCompressedOops -XX:+UseSerialGC -XX:+TieredCompilation" sbt test:console 21 | ``` 22 | 23 | Then run a benchmark: 24 | 25 | ``` 26 | scala> scalax.collection.immutable.BTreeSetThyme.insertShuffled() 27 | ``` 28 | 29 | The benchmark make use of the excellent [Thyme](https://github.com/Ichoran/thyme) library. 30 | 31 | 32 | Copyright 33 | --------- 34 | 35 | Copyright (c) 2013 Zilverline B.V. See 36 | [LICENSE](https://github.com/zilverline/scala-btree/blob/master/LICENSE.md) 37 | for details. 38 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | name := "btree" 2 | 3 | version := "0.1-SNAPSHOT" 4 | 5 | organization := "com.zilverline" 6 | 7 | description := "Memory efficient In-Memory B-Trees." 8 | 9 | scalaVersion := "2.10.2" 10 | 11 | scalacOptions := Seq("-deprecation", "-feature", "-unchecked", "-Xlint") 12 | 13 | testFrameworks in Test := Seq(TestFrameworks.Specs2) 14 | 15 | libraryDependencies := Seq( 16 | "org.scala-lang" % "scala-library" % "2.10.2", 17 | "org.specs2" %% "specs2" % "2.1.1" % "test", 18 | "org.scalacheck" %% "scalacheck" % "1.10.1" % "test") 19 | -------------------------------------------------------------------------------- /lib/Thyme.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zilverline/scala-btree/a34d93b715831022c8fd0873250e50543e49c962/lib/Thyme.jar -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.12.4 2 | -------------------------------------------------------------------------------- /src/main/scala/scalax/collection/immutable/BTreeSet.scala: -------------------------------------------------------------------------------- 1 | package scalax.collection.immutable 2 | 3 | import java.util.Arrays 4 | import java.util.Comparator 5 | import scala.collection.SortedSetLike 6 | import scala.collection.generic.ImmutableSortedSetFactory 7 | import scala.collection.immutable.SortedSet 8 | import scala.collection.mutable.{ Builder, SetBuilder } 9 | 10 | trait BTreeSet[A] extends SortedSet[A] with SortedSetLike[A, BTreeSet[A]] with Serializable { 11 | override def empty: BTreeSet[A] = BTreeSet.empty[A] 12 | 13 | // Should be an override in Scala 2.11 14 | def iteratorFrom(start: A): Iterator[A] 15 | } 16 | object BTreeSet extends ImmutableSortedSetFactory[BTreeSet] { 17 | import implementation._ 18 | 19 | case class Parameters(minLeafValues: Int = 16, minInternalValues: Int = 16) 20 | 21 | val DefaultParameters = Parameters() 22 | 23 | def empty[A: Ordering]: BTreeSet[A] = new Root[Leaf, A](Array.empty[AnyRef].asInstanceOf[Node[Leaf, A]])(implicitly, LeafOperations(implicitly, DefaultParameters)) 24 | def withParameters[A: Ordering](parameters: Parameters): BTreeSet[A] = new Root[Leaf, A](Array.empty[AnyRef].asInstanceOf[Node[Leaf, A]])(implicitly, LeafOperations(implicitly, parameters)) 25 | } 26 | 27 | private[immutable] object implementation { 28 | /* Track the node level in the type system to keep the tree balanced. */ 29 | sealed trait Level 30 | final class Leaf extends Level 31 | final class Next[L <: Level] extends Level 32 | 33 | /* Since nodes are represented by raw Array[AnyRef], tag nodes with the level and element type. */ 34 | type Tagged[U] = { type Tag = U } 35 | type @@[T, U] = T with Tagged[U] 36 | 37 | /* Marker type to indicate a value can be null. For documentation only. */ 38 | type Nullable[A >: Null] = A 39 | 40 | /* Nodes are raw arrays (for memory efficiency) but tagged for compile time checking. */ 41 | type Node[L <: Level, A] = Array[AnyRef] @@ (L, A) 42 | 43 | /* Helper to build new nodes. `NodeBuilder`s are mutable so be careful when using! */ 44 | class NodeBuilder[L <: Level, A] { 45 | private var node: Node[L, A] = _ 46 | private var index: Int = _ 47 | 48 | final def result(): Node[L, A] = { 49 | val r = node 50 | node = null 51 | index = -1 52 | r 53 | } 54 | final def leaf(n: Int)(implicit ev: L =:= Leaf): this.type = { 55 | node = new Array[AnyRef](n).asInstanceOf[Node[L, A]] 56 | index = 0 57 | this 58 | } 59 | final def internal(n: Int)(implicit ev: L <:< Next[_]): this.type = { 60 | node = new Array[AnyRef](1 + n + n + 1).asInstanceOf[Node[L, A]] 61 | index = 1 62 | this 63 | } 64 | final def allocCopy(n: Node[L, A]): this.type = allocCopy(n, 0, n.length) 65 | final def allocCopy(n: Node[L, A], from: Int, to: Int): this.type = { 66 | node = Arrays.copyOfRange(n, from, to).asInstanceOf[Node[L, A]] 67 | this 68 | } 69 | 70 | final def copy(source: Node[L, A]): this.type = copy(source, 0, source.length) 71 | final def copy(source: Node[L, A], from: Int, to: Int): this.type = { 72 | var i = 0 73 | while (i < to - from) { 74 | node(index + i) = source(from + i) 75 | i += 1 76 | } 77 | index += to - from 78 | this 79 | } 80 | 81 | final def copyDeleted(source: Node[L, A], index: Int): this.type = 82 | copy(source, 0, index).copy(source, index + 1, source.length) 83 | 84 | final def insertValue(v: A): this.type = { 85 | node(index) = v.asInstanceOf[AnyRef] 86 | index += 1 87 | this 88 | } 89 | final def insertChild[M <: Level](v: Node[M, A])(implicit ev: L =:= Next[M]): this.type = { 90 | node(index) = v.asInstanceOf[AnyRef] 91 | index += 1 92 | this 93 | } 94 | final def updateValue(index: Int, value: A): this.type = { 95 | node(index) = value.asInstanceOf[AnyRef] 96 | this 97 | } 98 | final def updateChild[M <: Level](index: Int, child: Node[M, A])(implicit ev: L =:= Next[M]): this.type = { 99 | node(index) = child.asInstanceOf[AnyRef] 100 | this 101 | } 102 | 103 | final def setSize(size: Int)(implicit ev: L <:< Next[_]): this.type = { 104 | node(0) = size.asInstanceOf[AnyRef] 105 | this 106 | } 107 | final def recalculateSize()(implicit ev: L <:< Next[_], ops: NodeOps[L, A]): this.type = { 108 | val children = ops.valueCount(node) 109 | var size = children 110 | var i = 1 + children 111 | while (i < node.length) { 112 | size += ops.childOps.size(ops.childAt(node, i)) 113 | i += 1 114 | } 115 | node(0) = size.asInstanceOf[AnyRef] 116 | this 117 | } 118 | 119 | /* Cast this instance to a builder for nodes of the parent level. */ 120 | final def up: NodeBuilder[Next[L], A] = this.asInstanceOf[NodeBuilder[Next[L], A]] 121 | /* Cast this instance to a builder for nodes of the child level. */ 122 | final def down[M <: Level](implicit ev: L =:= Next[M]): NodeBuilder[M, A] = this.asInstanceOf[NodeBuilder[M, A]] 123 | } 124 | 125 | sealed trait NodeWithOps[A] { 126 | type L <: Level 127 | def ops: NodeOps[L, A] 128 | def node: Node[L, A] 129 | } 130 | def NodeWithOps[L0 <: Level, A](node0: Node[L0, A])(implicit ops0: NodeOps[L0, A]): NodeWithOps[A] = new NodeWithOps[A] { 131 | type L = L0 132 | def ops = ops0 133 | def node = node0 134 | } 135 | 136 | /* 137 | * Since nodes are raw Java arrays we cannot extend them with the operations we need. 138 | * Instead, we keep a stack of NodeOps with one instance for each level in the tree. At the bottom of the 139 | * stack we keep an instance of `LeafOperations`, while all others are instances of `InternalOperations`. 140 | * This also lets us know the layout of the array we're working with. 141 | */ 142 | sealed trait NodeOps[L <: Level, A] { 143 | final type N = Node[L, A] 144 | 145 | type NextLevel = Next[L] 146 | type PreviousLevel <: Level 147 | 148 | type ChildNode = Node[PreviousLevel, A] 149 | type ChildOps = NodeOps[PreviousLevel, A] 150 | 151 | type Builder = NodeBuilder[L, A] 152 | 153 | def level: Int 154 | def parameters: BTreeSet.Parameters 155 | def ordering: Ordering[A] 156 | 157 | def newBuilder: Builder = new NodeBuilder[L, A] 158 | 159 | def valueCount(node: N): Int 160 | 161 | def childOps: ChildOps 162 | 163 | def valueAt(node: N, index: Int): A 164 | 165 | def childAt(node: N, index: Int): ChildNode 166 | def leftChild(node: N, index: Int): ChildNode 167 | def rightChild(node: N, index: Int): ChildNode 168 | 169 | def size(node: N): Int 170 | def isEmpty(node: N): Boolean 171 | 172 | def contains(node: N, a: A): Boolean 173 | def insert(node: N, a: A, overwrite: Boolean)(implicit builder: Builder): Nullable[Either[N, (N, A, N)]] 174 | 175 | def pathToHead(node: N, parent: Nullable[PathNode[A]] = null): Nullable[PathNode[A]] 176 | def pathToKey(node: N, key: A, parent: Nullable[PathNode[A]] = null): Nullable[PathNode[A]] 177 | 178 | def deleteFromRoot(node: N, a: A)(implicit builder: Builder): Nullable[Either[N, ChildNode]] 179 | def rebalance(left: N, right: N)(implicit builder: Builder): Either[N, (N, A, N)] 180 | def deleteAndMergeLeft(leftSibling: N, leftValue: A, node: N, a: A)(implicit builder: Builder): Nullable[Either[N, (N, A, N)]] 181 | def deleteAndMergeRight(node: N, rightValue: A, rightSibling: N, a: A)(implicit builder: Builder): Nullable[Either[N, (N, A, N)]] 182 | 183 | def splitAtIndex(node: N, index: Int)(implicit builder: Builder): (NodeWithOps[A], NodeWithOps[A]) 184 | def splitAtKey(node: N, key: A)(implicit builder: Builder): (NodeWithOps[A], NodeWithOps[A]) 185 | 186 | def prepend(smallerTree: NodeWithOps[A], value: A, node: N)(implicit builder: Builder): Either[N, (N, A, N)] 187 | def append(node: N, value: A, smallerTree: NodeWithOps[A])(implicit builder: Builder): Either[N, (N, A, N)] 188 | 189 | def head(node: N): A 190 | def last(node: N): A 191 | } 192 | 193 | private def valueComparator[A](implicit ordering: Ordering[A]): Comparator[AnyRef] = ordering.asInstanceOf[Comparator[AnyRef]] 194 | 195 | /* 196 | * Leaves are represented by an Array[AnyRef]. Each element in the array is a value. 197 | * The length of the array is equal to the number of stored values. 198 | * There is no additional overhead (counts, child pointers). 199 | */ 200 | implicit def LeafOperations[A: Ordering](implicit parameters: BTreeSet.Parameters) = new LeafOperations() 201 | final class LeafOperations[A]()(implicit val ordering: Ordering[A], val parameters: BTreeSet.Parameters) extends NodeOps[Leaf, A] { 202 | override type PreviousLevel = Nothing 203 | 204 | override def level = 0 205 | 206 | def childOps: ChildOps = throw new RuntimeException("no child ops for leaf node") 207 | def childAt(node: N, index: Int): ChildNode = throw new RuntimeException("no child for leaf node") 208 | def leftChild(node: N, index: Int): ChildNode = throw new RuntimeException("no child for leaf node") 209 | def rightChild(node: N, index: Int): ChildNode = throw new RuntimeException("no child for leaf node") 210 | 211 | val minValues = parameters.minLeafValues 212 | val maxValues = minValues * 2 213 | 214 | override def valueAt(node: N, index: Int): A = node(index).asInstanceOf[A] 215 | 216 | override def valueCount(node: N): Int = node.length 217 | override def size(node: N): Int = valueCount(node) 218 | override def isEmpty(node: N): Boolean = size(node) == 0 219 | override def contains(node: N, a: A): Boolean = search(node, a) >= 0 220 | 221 | override def insert(node: N, a: A, overwrite: Boolean)(implicit builder: Builder): Nullable[Either[N, (N, A, N)]] = { 222 | val index = search(node, a) 223 | if (index >= 0) { 224 | if (overwrite) Left(builder.allocCopy(node).updateValue(index, a).result()) else null 225 | } else { 226 | val insertionPoint = -index - 1 227 | if (valueCount(node) < maxValues) { 228 | Left(valueInserted(node, 0, node.length, insertionPoint, a)) 229 | } else { 230 | Right(if (insertionPoint < minValues) { 231 | val l: N = valueInserted(node, 0, minValues - 1, insertionPoint, a) 232 | val m: A = valueAt(node, minValues - 1) 233 | val r: N = copied(node, minValues, maxValues) 234 | (l, m, r) 235 | } else if (insertionPoint > minValues) { 236 | val l: N = copied(node, 0, minValues) 237 | val m: A = valueAt(node, minValues) 238 | val r: N = valueInserted(node, minValues + 1, maxValues, insertionPoint, a) 239 | (l, m, r) 240 | } else { 241 | val l: N = copied(node, 0, minValues) 242 | val m: A = a 243 | val r: N = copied(node, minValues, maxValues) 244 | (l, m, r) 245 | }) 246 | } 247 | } 248 | } 249 | 250 | override def deleteFromRoot(node: N, a: A)(implicit builder: Builder): Nullable[Either[N, ChildNode]] = { 251 | val index = search(node, a) 252 | if (index < 0) null 253 | else Left(valueDeleted(node, index)) 254 | } 255 | 256 | override def rebalance(left: N, right: N)(implicit builder: Builder): Either[N, (N, A, N)] = { 257 | if (left.length == minValues && right.length == minValues) 258 | Left(builder.leaf(left.length + right.length).copy(left).copy(right).result()) 259 | else 260 | Right( 261 | if (left.length > right.length) 262 | (valueDeleted(left, left.length - 1), valueAt(left, left.length - 1), right) 263 | else 264 | (left, valueAt(right, 0), valueDeleted(right, 0))) 265 | } 266 | 267 | override def deleteAndMergeLeft(leftSibling: N, leftValue: A, node: N, a: A)(implicit builder: Builder): Nullable[Either[N, (N, A, N)]] = { 268 | val index = search(node, a) 269 | if (index < 0) 270 | null 271 | else if (valueCount(node) > minValues) { 272 | Right((leftSibling, leftValue, valueDeleted(node, index))) 273 | } else if (valueCount(leftSibling) > minValues) { 274 | val left = valueDeleted(leftSibling, leftSibling.length - 1) 275 | val middle = valueAt(leftSibling, leftSibling.length - 1) 276 | val right = builder.leaf(node.length).insertValue(leftValue).copyDeleted(node, index).result() 277 | Right((left, middle, right)) 278 | } else { 279 | Left(builder.leaf(maxValues) 280 | .copy(leftSibling) 281 | .insertValue(leftValue) 282 | .copyDeleted(node, index).result()) 283 | } 284 | } 285 | 286 | override def deleteAndMergeRight(node: N, rightValue: A, rightSibling: N, a: A)(implicit builder: Builder): Nullable[Either[N, (N, A, N)]] = { 287 | val index = search(node, a) 288 | if (index < 0) 289 | null 290 | else if (valueCount(node) > minValues) { 291 | Right((valueDeleted(node, index), rightValue, rightSibling)) 292 | } else if (valueCount(rightSibling) > minValues) { 293 | val left = builder.leaf(node.length).copyDeleted(node, index).insertValue(rightValue).result() 294 | val middle = valueAt(rightSibling, 0) 295 | val right = valueDeleted(rightSibling, 0) 296 | Right((left, middle, right)) 297 | } else { 298 | Left(builder.leaf(maxValues) 299 | .copyDeleted(node, index) 300 | .insertValue(rightValue) 301 | .copy(rightSibling).result()) 302 | } 303 | } 304 | 305 | override def pathToHead(node: N, parent: Nullable[PathNode[A]]): Nullable[PathNode[A]] = 306 | if (isEmpty(node)) null else new PathLeafNode(node, 0, parent) 307 | 308 | override def pathToKey(node: N, key: A, parent: Nullable[PathNode[A]]): Nullable[PathNode[A]] = { 309 | val index = search(node, key) 310 | val startPoint = if (index >= 0) index else -(index + 1) 311 | if (startPoint >= valueCount(node)) null 312 | else new PathLeafNode(node, startPoint, parent) 313 | } 314 | 315 | override def splitAtIndex(node: N, index: Int)(implicit builder: Builder): (NodeWithOps[A], NodeWithOps[A]) = { 316 | val left = builder.allocCopy(node, 0, index).result() 317 | val right = builder.allocCopy(node, index, node.length).result() 318 | (NodeWithOps(left), NodeWithOps(right)) 319 | } 320 | 321 | override def splitAtKey(node: N, key: A)(implicit builder: Builder): (NodeWithOps[A], NodeWithOps[A]) = { 322 | val index = search(node, key) 323 | val splitPoint = if (index >= 0) index else (-index - 1) 324 | splitAtIndex(node, splitPoint) 325 | } 326 | 327 | override def prepend(prefix: NodeWithOps[A], value: A, right: N)(implicit builder: Builder): Either[N, (N, A, N)] = { 328 | assert(prefix.ops.level == 0, "prefix must be leaf") 329 | val left = prefix.node.asInstanceOf[N] 330 | concatenate(left, value, right) 331 | } 332 | 333 | override def append(left: N, value: A, suffix: NodeWithOps[A])(implicit builder: Builder): Either[N, (N, A, N)] = { 334 | assert(suffix.ops.level == 0, "suffix must be leaf") 335 | val right = suffix.node.asInstanceOf[N] 336 | concatenate(left, value, right) 337 | } 338 | 339 | override def head(node: N): A = { 340 | if (isEmpty(node)) throw new NoSuchElementException("empty set") 341 | valueAt(node, 0) 342 | } 343 | 344 | override def last(node: N): A = { 345 | if (isEmpty(node)) throw new NoSuchElementException("empty set") 346 | valueAt(node, node.length - 1) 347 | } 348 | 349 | private[this] def concatenate(left: N, value: A, right: N)(implicit builder: Builder): Either[N, (N, A, N)] = { 350 | val leftCount = valueCount(left) 351 | val rightCount = valueCount(right) 352 | val totalCount = leftCount + 1 + rightCount 353 | if (totalCount <= maxValues) { 354 | Left(builder.leaf(totalCount).copy(left).insertValue(value).copy(right).result()) 355 | } else { 356 | val newLeftCount = totalCount / 2 357 | val newRightCount = totalCount - 1 - newLeftCount 358 | Right(if (newLeftCount < leftCount) { 359 | val l: N = builder.leaf(newLeftCount).copy(left, 0, newLeftCount).result() 360 | val m: A = valueAt(left, newLeftCount) 361 | val r: N = builder.leaf(newRightCount).copy(left, newLeftCount + 1, leftCount).insertValue(value).copy(right).result() 362 | (l, m, r) 363 | } else if (newLeftCount > leftCount) { 364 | val l: N = builder.leaf(newLeftCount).copy(left).insertValue(value).copy(right, 0, newLeftCount - leftCount - 1).result() 365 | val m: A = valueAt(right, newLeftCount - leftCount - 1) 366 | val r: N = builder.leaf(newRightCount).copy(right, newLeftCount - leftCount, rightCount).result() 367 | (l, m, r) 368 | } else { 369 | (left, value, right) 370 | }) 371 | } 372 | } 373 | 374 | private def search(node: N, a: A): Int = Arrays.binarySearch(node, a.asInstanceOf[AnyRef], valueComparator[A]) 375 | 376 | private def copied(source: N, from: Int, to: Int)(implicit builder: Builder): N = 377 | builder.allocCopy(source, from, to).result() 378 | 379 | private def valueInserted(source: N, from: Int, to: Int, position: Int, value: A)(implicit builder: Builder): N = 380 | builder.leaf(to - from + 1) 381 | .copy(source, from, position) 382 | .insertValue(value) 383 | .copy(source, position, to) 384 | .result() 385 | 386 | private def valueDeleted(node: N, index: Int)(implicit builder: Builder): N = 387 | builder.leaf(node.length - 1).copyDeleted(node, index).result() 388 | } 389 | 390 | /* 391 | * Internal nodes are represented by an Array[AnyRef]. An internal node with C values has the following layout. 392 | * 393 | * node(0) - a java.lang.Integer containing the total size of this subtree. 394 | * node(1) .. node(C) - the values stored in this node. 395 | * node(1 + C) .. node(1 + 2*C) - the children of this internal node. 396 | */ 397 | implicit def InternalOps[L <: Level, A](implicit childOps: NodeOps[L, A]): NodeOps[Next[L], A] = new InternalOperations() 398 | final case class InternalOperations[L <: Level, A]()(implicit val childOps: NodeOps[L, A]) extends NodeOps[Next[L], A] { 399 | implicit val ordering = childOps.ordering 400 | val parameters = childOps.parameters 401 | val minValues = parameters.minInternalValues 402 | val maxValues = minValues * 2 403 | 404 | override type ChildNode = childOps.N 405 | override type PreviousLevel = L 406 | override val level = childOps.level + 1 407 | 408 | override def valueCount(node: N): Int = (node.length >> 1) - 1 409 | override def size(node: N): Int = node(0).asInstanceOf[Int] 410 | override def isEmpty(node: N): Boolean = false 411 | 412 | override def contains(node: N, a: A): Boolean = { 413 | val index = search(node, a) 414 | index >= 0 || { 415 | val children = valueCount(node) 416 | val insertionPoint = -index - 1 417 | val childIndex = insertionPoint + children 418 | val child = childAt(node, childIndex) 419 | childOps.contains(child, a) 420 | } 421 | } 422 | 423 | override def insert(node: N, a: A, overwrite: Boolean)(implicit builder: Builder): Nullable[Either[N, (N, A, N)]] = { 424 | val index = search(node, a) 425 | if (index >= 0) { 426 | if (overwrite) Left(builder.allocCopy(node).updateValue(index, a).result()) else null 427 | } else { 428 | val children = valueCount(node) 429 | val insertionPoint = -index - 1 430 | val childIndex = insertionPoint + children 431 | val child = childAt(node, childIndex) 432 | childUpdatedOrSplit(node, insertionPoint, childOps.insert(child, a, overwrite)(builder.down)) 433 | } 434 | } 435 | 436 | private def childUpdatedOrSplit(node: N, insertionPoint: Int, child: Nullable[Either[ChildNode, (ChildNode, A, ChildNode)]])(implicit builder: Builder): Either[N, (N, A, N)] = { 437 | val children = valueCount(node) 438 | val childIndex = insertionPoint + children 439 | child match { 440 | case null => 441 | null 442 | case Left(updatedChild) => 443 | val originalChild = childAt(node, childIndex) 444 | val sizeChange = childOps.size(updatedChild) - childOps.size(originalChild) 445 | builder.allocCopy(node) 446 | builder.setSize(size(node) + sizeChange) 447 | builder.updateChild(childIndex, updatedChild) 448 | Left(builder.result()) 449 | case Right((left, middle, right)) if children < maxValues => 450 | Left(valueInserted(node, 0, children, insertionPoint, left, middle, right)) 451 | case Right((left, middle, right)) => 452 | Right(split(node, insertionPoint, left, middle, right)) 453 | } 454 | } 455 | 456 | private def split(node: N, insertionPoint: Int, left: ChildNode, middle: A, right: ChildNode)(implicit builder: Builder): (N, A, N) = { 457 | val children = valueCount(node) 458 | if (insertionPoint < minValues + 1) { 459 | val l: N = valueInserted(node, 0, minValues - 1, insertionPoint, left, middle, right) 460 | val m: A = valueAt(node, minValues) 461 | val r: N = copied(node, minValues, children) 462 | (l, m, r) 463 | } else if (insertionPoint > minValues + 1) { 464 | val l: N = copied(node, 0, minValues) 465 | val m: A = valueAt(node, minValues + 1) 466 | val r: N = valueInserted(node, minValues + 1, children, insertionPoint, left, middle, right) 467 | (l, m, r) 468 | } else { 469 | val l: N = childUpdated(node, 0, minValues, minValues, left) 470 | val m: A = middle 471 | val r: N = childUpdated(node, minValues, children, 0, right) 472 | (l, m, r) 473 | } 474 | } 475 | 476 | private def valueInserted(node: N, from: Int, to: Int, insertionPoint: Int, left: ChildNode, middle: A, right: ChildNode)(implicit builder: Builder): N = { 477 | val children = valueCount(node) 478 | val childIndex = insertionPoint + children 479 | builder.internal(to - from + 1) 480 | builder.copy(node, 1 + from, insertionPoint) 481 | builder.insertValue(middle) 482 | builder.copy(node, insertionPoint, 1 + to) 483 | builder.copy(node, 1 + from + children, childIndex) 484 | builder.insertChild(left) 485 | builder.insertChild(right) 486 | builder.copy(node, childIndex + 1, 1 + to + children + 1) 487 | builder.recalculateSize().result() 488 | } 489 | 490 | private def copied(node: N, from: Int, to: Int)(implicit builder: Builder): N = { 491 | val children = valueCount(node) 492 | builder.internal(to - from) 493 | builder.copy(node, 1 + from, 1 + to) 494 | builder.copy(node, 1 + from + children, 1 + to + children + 1) 495 | builder.recalculateSize().result() 496 | } 497 | 498 | private def childUpdated(node: N, from: Int, to: Int, childIndex: Int, child: ChildNode)(implicit builder: Builder): N = { 499 | val children = valueCount(node) 500 | builder.internal(to - from) 501 | builder.copy(node, 1 + from, 1 + to) 502 | builder.copy(node, 1 + from + children, 1 + to + children + 1) 503 | builder.updateChild(1 + to - from + childIndex, child) 504 | builder.recalculateSize().result() 505 | } 506 | 507 | override def valueAt(node: N, index: Int): A = node(index).asInstanceOf[A] 508 | override def childAt(node: N, index: Int): ChildNode = node(index).asInstanceOf[ChildNode] 509 | override def leftChild(node: N, index: Int): ChildNode = childAt(node, 1 + valueCount(node) + index - 1) 510 | override def rightChild(node: N, index: Int): ChildNode = childAt(node, 1 + valueCount(node) + index) 511 | 512 | override def deleteFromRoot(node: N, a: A)(implicit builder: Builder): Nullable[Either[N, ChildNode]] = { 513 | val children = valueCount(node) 514 | val (index, result) = deleteValue(node, a) 515 | result match { 516 | case null => 517 | null 518 | case Left(merged) if children == 1 => 519 | Right(merged) 520 | case Left(merged) if children > 1 => 521 | Left(replaceMergedChildren(node, index, merged)) 522 | case Right((left, middle, right)) => 523 | Left(replaceUpdatedChildren(node, index, left, middle, right)) 524 | } 525 | } 526 | def deleteAndMergeLeft(leftSibling: N, leftValue: A, node: N, a: A)(implicit builder: Builder): Nullable[Either[N, (N, A, N)]] = { 527 | val children = valueCount(node) 528 | val (index, result) = deleteValue(node, a) 529 | result match { 530 | case null => 531 | null 532 | case Left(merged) if children == minValues && valueCount(leftSibling) == minValues => 533 | Left(replaceMergedChildAndMergeWithLeft(leftSibling, leftValue, node, index, merged)) 534 | case Left(merged) if children > minValues => 535 | Right((leftSibling, leftValue, replaceMergedChildren(node, index, merged))) 536 | case Left(merged) if valueCount(leftSibling) > minValues => 537 | Right(replaceMergedChildAndTakeFromLeft(leftSibling, leftValue, node, index, merged)) 538 | case Right((left, middle, right)) => 539 | Right((leftSibling, leftValue, replaceUpdatedChildren(node, index, left, middle, right))) 540 | } 541 | } 542 | def deleteAndMergeRight(node: N, rightValue: A, rightSibling: N, a: A)(implicit builder: Builder): Nullable[Either[N, (N, A, N)]] = { 543 | val children = valueCount(node) 544 | val (index, result) = deleteValue(node, a) 545 | result match { 546 | case null => 547 | null 548 | case Left(merged) if children == minValues && valueCount(rightSibling) == minValues => 549 | Left(replaceMergedChildAndMergeWithRight(node, rightValue, rightSibling, index, merged)) 550 | case Left(merged) if children > minValues => 551 | Right((replaceMergedChildren(node, index, merged), rightValue, rightSibling)) 552 | case Left(merged) if valueCount(rightSibling) > minValues => 553 | Right(replaceMergedChildAndTakeFromRight(node, rightValue, rightSibling, index, merged)) 554 | case Right((left, middle, right)) => 555 | Right((replaceUpdatedChildren(node, index, left, middle, right), rightValue, rightSibling)) 556 | } 557 | } 558 | override def rebalance(left: N, right: N)(implicit builder: Builder): Either[N, (N, A, N)] = { 559 | val leftCount = valueCount(left) 560 | val rightCount = valueCount(right) 561 | val highestLeftChild = childAt(left, 1 + leftCount + leftCount) 562 | val lowestRightChild = childAt(right, 1 + rightCount) 563 | childOps.rebalance(highestLeftChild, lowestRightChild)(builder.down) match { 564 | case Right((l, m, r)) => 565 | builder.internal(leftCount) 566 | builder.copy(left, 1, left.length - 1) 567 | builder.insertChild(l) 568 | val updatedLeft = builder.recalculateSize().result() 569 | builder.internal(rightCount) 570 | builder.copy(right, 1, 1 + rightCount) 571 | builder.insertChild(r) 572 | builder.copy(right, 1 + rightCount + 1, right.length) 573 | val updatedRight = builder.recalculateSize().result() 574 | Right((updatedLeft, m, updatedRight)) 575 | case Left(merged) if leftCount == minValues && rightCount == minValues => 576 | builder.internal(maxValues) 577 | builder.setSize(size(left) + size(right)) 578 | builder.copy(left, 1, 1 + leftCount) 579 | builder.copy(right, 1, 1 + rightCount) 580 | builder.copy(left, 1 + leftCount, left.length - 1) 581 | builder.insertChild(merged) 582 | builder.copy(right, 1 + rightCount + 1, right.length) 583 | Left(builder.result()) 584 | case Left(merged) if leftCount > rightCount => 585 | builder.internal(leftCount - 1) 586 | builder.copy(left, 1, 1 + leftCount - 1) 587 | builder.copy(left, 1 + leftCount, left.length - 1) 588 | val updatedLeft = builder.recalculateSize().result() 589 | val middle = valueAt(left, leftCount) 590 | builder.internal(rightCount) 591 | builder.copy(right, 1, 1 + rightCount) 592 | builder.insertChild(merged) 593 | builder.copy(right, 1 + rightCount + 1, right.length) 594 | val updatedRight = builder.recalculateSize().result() 595 | Right((updatedLeft, middle, updatedRight)) 596 | case Left(merged) => 597 | builder.internal(leftCount) 598 | builder.copy(left, 1, left.length - 1) 599 | builder.insertChild(merged) 600 | val updatedLeft = builder.recalculateSize().result() 601 | val middle = valueAt(right, 1) 602 | builder.internal(rightCount - 1) 603 | builder.copy(right, 2, 1 + rightCount) 604 | builder.copy(right, 1 + rightCount + 1, right.length) 605 | val updatedRight = builder.recalculateSize().result() 606 | Right((updatedLeft, middle, updatedRight)) 607 | } 608 | } 609 | 610 | override def pathToHead(node: N, parent: Nullable[PathNode[A]]): Nullable[PathNode[A]] = 611 | childOps.pathToHead(leftChild(node, 1), new PathInternalNode(node, 0, parent)) 612 | 613 | override def pathToKey(node: N, key: A, parent: Nullable[PathNode[A]]): Nullable[PathNode[A]] = { 614 | val children = valueCount(node) 615 | val index = search(node, key) 616 | if (index >= 0) new PathInternalNode(node, (index - 1) * 2 + 1, parent) 617 | else { 618 | val startPoint = -(index + 1) 619 | val child = childAt(node, startPoint + children) 620 | val parentPath = new PathInternalNode(node, (startPoint - 1) * 2, parent) 621 | val childPath = childOps.pathToKey(child, key, parentPath) 622 | if (childPath ne null) childPath else parentPath.next 623 | } 624 | } 625 | 626 | override def splitAtIndex(node: N, index: Int)(implicit builder: Builder): (NodeWithOps[A], NodeWithOps[A]) = { 627 | val children = valueCount(node) 628 | var splitPoint = 1 629 | var position = index 630 | while (splitPoint <= children + 1) { 631 | val childIndex = splitPoint + children 632 | val child = childAt(node, childIndex) 633 | val childSize = childOps.size(child) 634 | if (position <= childSize) { 635 | val (leftChildSplit, rightChildSplit) = childOps.splitAtIndex(child, position)(builder.down) 636 | return mergeAfterSplit(node, splitPoint, leftChildSplit, rightChildSplit) 637 | } else { 638 | position -= childSize + 1 639 | } 640 | splitPoint += 1 641 | } 642 | throw new ArrayIndexOutOfBoundsException(index) 643 | } 644 | 645 | override def splitAtKey(node: N, key: A)(implicit builder: Builder): (NodeWithOps[A], NodeWithOps[A]) = { 646 | val children = valueCount(node) 647 | val index = search(node, key) 648 | val splitPoint = if (index >= 0) index else -(index + 1) 649 | val child = childAt(node, splitPoint + children) 650 | val (leftChildSplit, rightChildSplit) = childOps.splitAtKey(child, key)(builder.down) 651 | mergeAfterSplit(node, splitPoint, leftChildSplit, rightChildSplit) 652 | } 653 | 654 | private[this] def mergeAfterSplit(node: N, splitPoint: Int, leftChildSplit: NodeWithOps[A], rightChildSplit: NodeWithOps[A])(implicit builder: Builder): (NodeWithOps[A], NodeWithOps[A]) = { 655 | val children = valueCount(node) 656 | val leftSplit = if (splitPoint == 1) leftChildSplit else { 657 | val leftChild = childAt(node, splitPoint + children - 1) 658 | childOps.append(leftChild, valueAt(node, splitPoint - 1), leftChildSplit)(builder.down) match { 659 | case Left(merged) if splitPoint == 2 => 660 | NodeWithOps(merged) 661 | case Left(merged) => 662 | builder.internal(splitPoint - 2) 663 | builder.copy(node, 1, splitPoint - 1) 664 | builder.copy(node, 1 + children, children + splitPoint - 1) 665 | builder.insertChild(merged) 666 | NodeWithOps(builder.recalculateSize().result()) 667 | case Right((left, middle, right)) => 668 | builder.internal(splitPoint - 1) 669 | builder.copy(node, 1, splitPoint - 1) 670 | builder.insertValue(middle) 671 | builder.copy(node, 1 + children, children + splitPoint - 1) 672 | builder.insertChild(left) 673 | builder.insertChild(right) 674 | NodeWithOps(builder.recalculateSize().result()) 675 | } 676 | } 677 | val rightSplit = if (splitPoint == 1 + children) rightChildSplit else { 678 | val rightChild = childAt(node, splitPoint + children + 1) 679 | childOps.prepend(rightChildSplit, valueAt(node, splitPoint), rightChild)(builder.down) match { 680 | case Left(merged) if splitPoint == children => 681 | NodeWithOps(merged) 682 | case Left(merged) => 683 | builder.internal(children - (splitPoint - 1) - 1) 684 | builder.copy(node, splitPoint + 1, children + 1) 685 | builder.insertChild(merged) 686 | builder.copy(node, children + splitPoint + 2, node.length) 687 | NodeWithOps(builder.recalculateSize().result()) 688 | case Right((left, value, right)) => 689 | builder.internal(children - (splitPoint - 1)) 690 | builder.insertValue(value) 691 | builder.copy(node, splitPoint + 1, children + 1) 692 | builder.insertChild(left) 693 | builder.insertChild(right) 694 | builder.copy(node, children + splitPoint + 2, node.length) 695 | NodeWithOps(builder.recalculateSize().result()) 696 | } 697 | } 698 | (leftSplit, rightSplit) 699 | } 700 | 701 | override def prepend(prefix: NodeWithOps[A], value: A, node: N)(implicit builder: Builder): Either[N, (N, A, N)] = { 702 | val children = valueCount(node) 703 | if (prefix.ops.level < level) { 704 | val leftMostChild = childAt(node, 1 + children) 705 | val updatedOrSplit = childOps.prepend(prefix, value, leftMostChild)(builder.down) 706 | childUpdatedOrSplit(node, 1, updatedOrSplit) 707 | } else if (prefix.ops.level == level) { 708 | val left = prefix.node.asInstanceOf[N] 709 | concatenate(left, value, node) 710 | } else { 711 | throw new AssertionError("prefix must be smaller") 712 | } 713 | } 714 | 715 | override def append(node: N, value: A, suffix: NodeWithOps[A])(implicit builder: Builder): Either[N, (N, A, N)] = { 716 | val children = valueCount(node) 717 | if (suffix.ops.level < level) { 718 | val rightMostChild = childAt(node, 1 + children + children) 719 | val updatedOrSplit = childOps.append(rightMostChild, value, suffix)(builder.down) 720 | childUpdatedOrSplit(node, 1 + children, updatedOrSplit) 721 | } else if (suffix.ops.level == level) { 722 | val right = suffix.node.asInstanceOf[N] 723 | concatenate(node, value, right) 724 | } else { 725 | throw new AssertionError("suffix must be smaller") 726 | } 727 | } 728 | 729 | override def head(node: N): A = childOps.head(childAt(node, 1 + valueCount(node))) 730 | override def last(node: N): A = childOps.last(childAt(node, node.length - 1)) 731 | 732 | private[this] def concatenate(left: N, value: A, right: N)(implicit builder: Builder): Either[N, (N, A, N)] = { 733 | val leftCount = valueCount(left) 734 | val rightCount = valueCount(right) 735 | val totalCount = leftCount + 1 + rightCount 736 | if (totalCount <= maxValues) { 737 | Left(builder.internal(totalCount) 738 | .copy(left, 1, 1 + leftCount).insertValue(value).copy(right, 1, 1 + rightCount) 739 | .copy(left, 1 + leftCount, left.length).copy(right, 1 + rightCount, right.length) 740 | .recalculateSize().result()) 741 | } else { 742 | val newLeftCount = totalCount / 2 743 | val newRightCount = totalCount - 1 - newLeftCount 744 | Right(if (newLeftCount < leftCount) { 745 | val l: N = builder.internal(newLeftCount) 746 | .copy(left, 1, 1 + newLeftCount) 747 | .copy(left, 1 + leftCount, 1 + leftCount + newLeftCount + 1) 748 | .recalculateSize().result() 749 | val m: A = valueAt(left, 1 + newLeftCount) 750 | val r: N = builder.internal(newRightCount) 751 | .copy(left, 1 + newLeftCount + 1, 1 + leftCount).insertValue(value).copy(right, 1, 1 + rightCount) 752 | .copy(left, 1 + leftCount + newLeftCount + 1, left.length).copy(right, 1 + rightCount, right.length) 753 | .recalculateSize().result() 754 | (l, m, r) 755 | } else if (newLeftCount > leftCount) { 756 | val l: N = builder.internal(newLeftCount) 757 | .copy(left, 1, 1 + leftCount).insertValue(value).copy(right, 1, 1 + newLeftCount - leftCount - 1) 758 | .copy(left, 1 + leftCount, left.length).copy(right, 1 + rightCount, 1 + rightCount + newLeftCount - leftCount) 759 | .recalculateSize().result() 760 | val m: A = valueAt(right, 1 + newLeftCount - leftCount - 1) 761 | val r: N = builder.internal(newRightCount) 762 | .copy(right, 1 + newLeftCount - leftCount, 1 + rightCount) 763 | .copy(right, 1 + rightCount + newLeftCount - leftCount, right.length) 764 | .recalculateSize().result() 765 | (l, m, r) 766 | } else { 767 | (left, value, right) 768 | }) 769 | } 770 | } 771 | 772 | private def deleteValue(node: N, a: A)(implicit builder: Builder): (Int, Nullable[Either[ChildNode, (ChildNode, A, ChildNode)]]) = { 773 | val index = search(node, a) 774 | if (index >= 0) { 775 | (index, childOps.rebalance(leftChild(node, index), rightChild(node, index))(builder.down)) 776 | } else { 777 | val insertionPoint = -index - 1 778 | val children = valueCount(node) 779 | val childIndex = insertionPoint + children 780 | val middle = childAt(node, childIndex) 781 | val left = if (childIndex > 1 + children) childAt(node, childIndex - 1) else null 782 | val right = if (childIndex + 1 < node.length) childAt(node, childIndex + 1) else null 783 | if (left == null || (right != null && childOps.valueCount(left) < childOps.valueCount(right))) { 784 | (insertionPoint, childOps.deleteAndMergeRight(middle, valueAt(node, insertionPoint), right, a)(builder.down)) 785 | } else { 786 | (insertionPoint - 1, childOps.deleteAndMergeLeft(left, valueAt(node, insertionPoint - 1), middle, a)(builder.down)) 787 | } 788 | } 789 | } 790 | 791 | private def replaceMergedChildren(node: N, insertionPoint: Int, merged: Node[L, A])(implicit builder: Builder): N = { 792 | val children = valueCount(node) 793 | val childIndex = insertionPoint + children 794 | builder.internal(children - 1) 795 | builder.setSize(size(node) - 1) 796 | builder.copy(node, 1, insertionPoint) 797 | builder.copy(node, insertionPoint + 1, childIndex) 798 | builder.insertChild(merged) 799 | builder.copy(node, childIndex + 2, node.length) 800 | builder.result() 801 | } 802 | private def replaceUpdatedChildren(node: N, insertionPoint: Int, left: Node[L, A], middle: A, right: Node[L, A])(implicit builder: Builder): N = { 803 | val children = valueCount(node) 804 | val childIndex = insertionPoint + children 805 | builder.internal(children) 806 | builder.setSize(size(node) - 1) 807 | builder.copy(node, 1, insertionPoint) 808 | builder.insertValue(middle) 809 | builder.copy(node, insertionPoint + 1, childIndex) 810 | builder.insertChild(left) 811 | builder.insertChild(right) 812 | builder.copy(node, childIndex + 2, node.length) 813 | builder.result() 814 | } 815 | private def replaceMergedChildAndMergeWithLeft(leftSibling: N, leftValue: A, node: N, index: Int, merged: Node[L, A])(implicit builder: Builder): N = { 816 | val children = valueCount(node) 817 | val childIndex = index + children 818 | builder.internal(maxValues) 819 | builder.setSize(size(leftSibling) + size(node)) 820 | builder.copy(leftSibling, 1, 1 + valueCount(leftSibling)) 821 | builder.insertValue(leftValue) 822 | builder.copy(node, 1, index) 823 | builder.copy(node, index + 1, 1 + children) 824 | builder.copy(leftSibling, 1 + valueCount(leftSibling), leftSibling.length) 825 | builder.copy(node, 1 + children, childIndex) 826 | builder.insertChild(merged) 827 | builder.copy(node, childIndex + 2, node.length) 828 | builder.result() 829 | } 830 | private def replaceMergedChildAndMergeWithRight(node: N, rightValue: A, rightSibling: N, index: Int, merged: Node[L, A])(implicit builder: Builder): N = { 831 | val children = valueCount(node) 832 | val childIndex = index + children 833 | builder.internal(maxValues) 834 | builder.setSize(size(node) + size(rightSibling)) 835 | builder.copy(node, 1, index) 836 | builder.copy(node, index + 1, 1 + children) 837 | builder.insertValue(rightValue) 838 | builder.copy(rightSibling, 1, 1 + valueCount(rightSibling)) 839 | builder.copy(node, 1 + children, childIndex) 840 | builder.insertChild(merged) 841 | builder.copy(node, childIndex + 2, node.length) 842 | builder.copy(rightSibling, 1 + valueCount(rightSibling), rightSibling.length) 843 | builder.result() 844 | } 845 | private def replaceMergedChildAndTakeFromLeft(leftSibling: N, leftValue: A, node: N, index: Int, merged: Node[L, A])(implicit builder: Builder): (N, A, N) = { 846 | val leftCount = valueCount(leftSibling) 847 | builder.internal(leftCount - 1) 848 | builder.copy(leftSibling, 1, 1 + leftCount - 1) 849 | builder.copy(leftSibling, 1 + leftCount, leftSibling.length - 1) 850 | builder.recalculateSize() 851 | val updatedLeft = builder.result() 852 | val updatedMiddle = valueAt(leftSibling, leftCount) 853 | val children = valueCount(node) 854 | val childIndex = index + children 855 | builder.internal(children) 856 | builder.insertValue(leftValue) 857 | builder.copy(node, 1, index) 858 | builder.copy(node, index + 1, 1 + children) 859 | builder.insertChild(rightChild(leftSibling, leftCount)) 860 | builder.copy(node, 1 + children, childIndex) 861 | builder.insertChild(merged) 862 | builder.copy(node, childIndex + 2, node.length) 863 | builder.recalculateSize() 864 | val updatedRight = builder.result() 865 | (updatedLeft, updatedMiddle, updatedRight) 866 | } 867 | private def replaceMergedChildAndTakeFromRight(node: N, rightValue: A, rightSibling: N, index: Int, merged: Node[L, A])(implicit builder: Builder): (N, A, N) = { 868 | val children = valueCount(node) 869 | val childIndex = index + children 870 | builder.internal(children) 871 | builder.copy(node, 1, index) 872 | builder.copy(node, index + 1, 1 + children) 873 | builder.insertValue(rightValue) 874 | builder.copy(node, 1 + children, childIndex) 875 | builder.insertChild(merged) 876 | builder.copy(node, childIndex + 2, node.length) 877 | builder.insertChild(leftChild(rightSibling, 1)) 878 | builder.recalculateSize() 879 | val updatedLeft = builder.result() 880 | val updatedMiddle = valueAt(rightSibling, 1) 881 | val rightCount = valueCount(rightSibling) 882 | builder.internal(rightCount - 1) 883 | builder.copy(rightSibling, 2, 1 + rightCount) 884 | builder.copy(rightSibling, 1 + rightCount + 1, rightSibling.length) 885 | builder.recalculateSize() 886 | val updatedRight = builder.result() 887 | (updatedLeft, updatedMiddle, updatedRight) 888 | } 889 | 890 | private def search(node: N, a: A): Int = Arrays.binarySearch(node, 1, valueCount(node) + 1, a.asInstanceOf[AnyRef], valueComparator[A]) 891 | } 892 | 893 | class Root[L <: Level, A](val root: Node[L, A])(implicit override val ordering: Ordering[A], val ops: NodeOps[L, A]) 894 | extends BTreeSet[A] with Serializable { 895 | override def +(a: A) = { 896 | val builder = ops.newBuilder 897 | ops.insert(root, a, overwrite = false)(builder) match { 898 | case null => this 899 | case Left(node) => new Root(node) 900 | case Right((left, middle, right)) => 901 | val rootBuilder = builder.up 902 | rootBuilder.internal(1) 903 | rootBuilder.setSize(ops.size(left) + ops.size(right) + 1) 904 | rootBuilder.insertValue(middle) 905 | rootBuilder.insertChild(left) 906 | rootBuilder.insertChild(right) 907 | new Root(rootBuilder.result()) 908 | } 909 | } 910 | 911 | override def -(a: A): BTreeSet[A] = { 912 | ops.deleteFromRoot(root, a)(ops.newBuilder) match { 913 | case null => this 914 | case Left(updated) => new Root(updated) 915 | case Right(child) => new Root(child)(ops.ordering, ops.childOps) 916 | } 917 | } 918 | override def isEmpty: Boolean = ops.isEmpty(root) 919 | override def size: Int = ops.size(root) 920 | override def contains(a: A): Boolean = ops.contains(root, a) 921 | 922 | override def iterator: Iterator[A] = new BTreeIterator(root, ops.pathToHead(root)) 923 | override def iteratorFrom(start: A): Iterator[A] = new BTreeIterator(root, ops.pathToKey(root, start)) 924 | 925 | override def rangeImpl(from: Option[A], until: Option[A]): BTreeSet[A] = (from, until) match { 926 | case (None, None) => 927 | this 928 | case (Some(key), None) => 929 | val nodeWithOps = ops.splitAtKey(root, key)(ops.newBuilder)._2 930 | new Root(nodeWithOps.node)(ordering, nodeWithOps.ops) 931 | case (None, Some(key)) => 932 | val nodeWithOps = ops.splitAtKey(root, key)(ops.newBuilder)._1 933 | new Root(nodeWithOps.node)(ordering, nodeWithOps.ops) 934 | case (from, until) => 935 | rangeImpl(from, None).rangeImpl(None, until) 936 | } 937 | override def stringPrefix: String = "BTreeSet" 938 | 939 | // Optimized implementations of various methods. 940 | override def head = ops.head(root) 941 | override def headOption = if (isEmpty) None else Some(head) 942 | override def last = ops.last(root) 943 | override def lastOption = if (isEmpty) None else Some(last) 944 | override def tail = this - head 945 | override def init = this - last 946 | 947 | override def take(n: Int) = 948 | if (n <= 0) empty 949 | else if (n >= size) this 950 | else { 951 | val (left, _) = ops.splitAtIndex(root, n)(ops.newBuilder) 952 | new Root(left.node)(ordering, left.ops) 953 | } 954 | 955 | override def drop(n: Int) = 956 | if (n <= 0) this 957 | else if (n >= size) empty 958 | else { 959 | val (_, right) = ops.splitAtIndex(root, n)(ops.newBuilder) 960 | new Root(right.node)(ordering, right.ops) 961 | } 962 | 963 | override def splitAt(n: Int) = 964 | if (n <= 0) (empty, this) 965 | else if (n >= size) (this, empty) 966 | else { 967 | val (left, right) = ops.splitAtIndex(root, n)(ops.newBuilder) 968 | (new Root(left.node)(ordering, left.ops), 969 | new Root(right.node)(ordering, right.ops)) 970 | } 971 | 972 | override def slice(from: Int, until: Int) = { 973 | if (from >= until || from >= size) empty 974 | else if (from <= 0) take(until) 975 | else if (until >= size) drop(from) 976 | else drop(from).take(until - from) 977 | } 978 | 979 | override def dropRight(n: Int) = take(size - n) 980 | override def takeRight(n: Int) = drop(size - n) 981 | 982 | private[this] def countWhile(p: A => Boolean): Int = { 983 | var result = 0 984 | val it = iterator 985 | while (it.hasNext && p(it.next())) result += 1 986 | result 987 | } 988 | 989 | override def dropWhile(p: A => Boolean) = drop(countWhile(p)) 990 | override def takeWhile(p: A => Boolean) = take(countWhile(p)) 991 | override def span(p: A => Boolean) = splitAt(countWhile(p)) 992 | } 993 | 994 | sealed trait PathNode[A] { 995 | def current: A 996 | def next: PathNode[A] 997 | } 998 | 999 | private final class PathLeafNode[A](node: Node[Leaf, A], initialPosition: Int, parent: PathNode[A])(implicit ops: NodeOps[Leaf, A]) extends PathNode[A] { 1000 | private[this] var position = initialPosition 1001 | private[this] val valueCount = ops.valueCount(node) 1002 | 1003 | def current: A = ops.valueAt(node, position) 1004 | 1005 | def next: PathNode[A] = 1006 | if (position + 1 < valueCount) { 1007 | position += 1 1008 | this 1009 | } else if (parent ne null) { 1010 | parent.next 1011 | } else { 1012 | null 1013 | } 1014 | } 1015 | 1016 | private final class PathInternalNode[L <: Next[_], A](node: Node[L, A], initialPosition: Int, parent: PathNode[A])(implicit ops: NodeOps[L, A]) extends PathNode[A] { 1017 | private[this] var position = initialPosition 1018 | private[this] val valueCount = ops.valueCount(node) 1019 | private[this] val valueAndChildCount = 2 * valueCount + 1 1020 | 1021 | def current: A = ops.valueAt(node, 1 + ((position - 1) >> 1)) 1022 | 1023 | def next: PathNode[A] = 1024 | if (position + 1 < valueAndChildCount) { 1025 | position += 1 1026 | if ((position & 1) == 0) 1027 | ops.childOps.pathToHead(ops.childAt(node, 1 + valueCount + (position >> 1)), this) 1028 | else 1029 | this 1030 | } else if (parent ne null) { 1031 | parent.next 1032 | } else { 1033 | null 1034 | } 1035 | } 1036 | 1037 | private final class BTreeIterator[L <: Level, A](root: Node[L, A], initialPath: PathNode[A] = null)(implicit ops: NodeOps[L, A]) extends Iterator[A] { 1038 | private[this] var path: PathNode[A] = _ 1039 | private[this] var nextAvailable: Boolean = _ 1040 | private[this] var next: AnyRef = _ 1041 | 1042 | tryNext(initialPath) 1043 | 1044 | override def hasNext: Boolean = nextAvailable 1045 | override def next(): A = { 1046 | if (!nextAvailable) throw new NoSuchElementException("next on empty iterator") 1047 | val result = next.asInstanceOf[A] 1048 | tryNext(path.next) 1049 | result 1050 | } 1051 | 1052 | private[this] def tryNext(nextPath: PathNode[A]): Unit = { 1053 | path = nextPath 1054 | nextAvailable = nextPath ne null 1055 | next = if (nextAvailable) path.current.asInstanceOf[AnyRef] else null 1056 | } 1057 | } 1058 | } 1059 | -------------------------------------------------------------------------------- /src/test/scala/scalax/collection/immutable/BTreeSetSpec.scala: -------------------------------------------------------------------------------- 1 | package scalax.collection.immutable 2 | 3 | import org.scalacheck._ 4 | import org.scalacheck.Arbitrary.arbitrary 5 | import org.specs2.matcher.Parameters 6 | import scala.collection.immutable.TreeSet 7 | 8 | class BTreeSetSpec extends org.specs2.Specification with org.specs2.ScalaCheck { 9 | implicit val parameters = BTreeSet.Parameters(minLeafValues = 2, minInternalValues = 2) 10 | 11 | private def tree[A: Ordering](a: A*): BTreeSet[A] = a.foldLeft(BTreeSet.withParameters[A](parameters))(_ + _) 12 | 13 | def is = s2""" 14 | In-memory B-Tree specification 15 | 16 | The empty B-Tree should 17 | be empty $beEmpty 18 | have length 0 $haveLength0 19 | 20 | Singleton B-Tree should 21 | not be empty $singletonNotEmpty 22 | have size 1 $singletonSize1 23 | contain single element $singletonContains1 24 | 25 | Two-element B-Tree should 26 | contain both elements in order $pairContainsElementsInOrder 27 | 28 | A B-Tree should 29 | contain all inserted elements $containsAllInsertedElements 30 | contain all distinct elements $containAllDistinctElements 31 | contain all elements in order $containsElementsInOrder 32 | iterate all elements in order $iterateElementsInOrder 33 | iterate from element in order $iterateFromInOrder 34 | support splitting $splittable 35 | head/tail identity $headTailIdentity 36 | init/last identity $initLastIdentity 37 | support take $take 38 | support drop $drop 39 | take/drop identity $takeDropIdentity 40 | support splitAt $splitAt 41 | support slice $slice 42 | 43 | A non-empty B-Tree should 44 | not contain deleted element $notContainDeletedElement 45 | not contain any elements after all are deleted $deleteAllElements 46 | """ 47 | 48 | implicit val params = Parameters(minTestsOk = 1000, minSize = 0, maxSize = 100, workers = Runtime.getRuntime().availableProcessors()) 49 | 50 | implicit def GenTree[T: Arbitrary: Ordering]: Arbitrary[BTreeSet[T]] = Arbitrary(for { 51 | elements <- Gen.listOf(arbitrary[T]) 52 | } yield tree(elements: _*)) 53 | 54 | def empty = BTreeSet.empty[Int] 55 | def singleton = tree(1) 56 | def pair = tree(2, 1) 57 | 58 | def beEmpty = empty.isEmpty must beTrue 59 | def haveLength0 = empty.size must_== 0 60 | 61 | def singletonNotEmpty = singleton.nonEmpty must beTrue 62 | def singletonSize1 = singleton must have size(1) 63 | def singletonContains1 = singleton must contain(1) 64 | 65 | def pairContainsElementsInOrder = pair.toVector must beEqualTo(Vector(1, 2)) 66 | 67 | def containsAllInsertedElements = Prop.forAll { elements: List[Int] => 68 | val subject = tree(elements: _*) 69 | elements.forall(subject.contains) 70 | } 71 | def ignoreAlreadyContainedElements = Prop.forAll { elements: List[Int] => 72 | val subject = tree(elements: _*) 73 | elements.foldLeft(subject)(_ + _) must beTheSameAs(subject) 74 | } 75 | def ignoreDeletionOfElementsNoInTreeSet = Prop.forAll { (elements: List[Int], toDelete: Int) => { 76 | val subject = tree(elements: _*) 77 | !subject.contains(toDelete) ==> ((subject - toDelete) must beTheSameAs(subject)) 78 | }} 79 | 80 | def containAllDistinctElements = Prop.forAll { elements: List[Int] => 81 | tree(elements: _*) must have size(elements.distinct.size) 82 | } 83 | def containsElementsInOrder = Prop.forAll { elements: List[Int] => 84 | tree(elements: _*).toVector must_== elements.sorted.distinct.toVector 85 | } 86 | def iterateElementsInOrder = Prop.forAll { elements: List[Int] => 87 | tree(elements: _*).iterator.toVector must_== elements.toVector.distinct.sorted 88 | } 89 | def iterateFromInOrder = Prop.forAll(GenListAndKey[Int]) { 90 | case (elements, key) => 91 | val subject = tree(elements: _*) 92 | subject.iteratorFrom(key).toVector must_== subject.from(key).iterator.toVector 93 | } 94 | def headTailIdentity = Prop.forAll { subject: BTreeSet[Int] => subject.nonEmpty ==> { 95 | subject must_== (subject.tail + subject.head) 96 | }} 97 | def initLastIdentity = Prop.forAll { subject: BTreeSet[Int] => subject.nonEmpty ==> { 98 | subject must_== (subject.init + subject.last) 99 | }} 100 | 101 | def GenNonEmptyTreeWithSelectedElement[T: Arbitrary: Ordering] = for { 102 | elements <- Gen.listOf(arbitrary[T]) 103 | if elements.nonEmpty 104 | toBeDeleted <- Gen.oneOf(elements) 105 | } yield (elements, toBeDeleted) 106 | 107 | def GenListAndKey[T: Arbitrary] = for { 108 | elements <- arbitrary[List[T]] 109 | key <- Gen.oneOf(arbitrary[T], Gen.oneOf(elements)) 110 | } yield (elements, key) 111 | 112 | def GenTreeWithSelectedIndex[T: Arbitrary: Ordering] = for { 113 | elements <- Gen.listOf(arbitrary[T]) 114 | index <- Gen.choose(-1, elements.size + 1) 115 | } yield (elements, index) 116 | 117 | def notContainDeletedElement = Prop.forAll(GenNonEmptyTreeWithSelectedElement[Int]) { 118 | case (elements, toBeDeleted) => 119 | val initial = tree(elements: _*) 120 | val deleted = initial - toBeDeleted 121 | (deleted.size must_== (initial.size - 1)) and (deleted.contains(toBeDeleted) aka "value still present" must beFalse) and (deleted.toVector must_== TreeSet(elements: _*).-(toBeDeleted).toVector) 122 | } 123 | def deleteAllElements = Prop.forAll(arbitrary[List[Int]]) { elements => 124 | elements.nonEmpty ==> { 125 | val subject = tree(elements: _*) 126 | elements.foldLeft(subject)(_ - _).toVector must_== Vector.empty 127 | } 128 | } 129 | 130 | def splittable = Prop.forAll(GenListAndKey[Int]) { 131 | case (elements, key) => 132 | val initial = tree(elements: _*) 133 | val init = initial.until(key) 134 | val tail = initial.from(key) 135 | (init must contain(be_<(key)).forall) and 136 | (tail must contain(be_>=(key)).forall) and 137 | ((init.toVector ++ tail.toVector) must (beSorted[Int] and containTheSameElementsAs(elements.distinct))) 138 | } 139 | 140 | def take = Prop.forAll(GenTreeWithSelectedIndex[Int]) { 141 | case (elements, n) => 142 | val subject = tree(elements: _*) 143 | subject.take(n).toVector must_== subject.toVector.take(n) 144 | } 145 | 146 | def drop = Prop.forAll(GenTreeWithSelectedIndex[Int]) { 147 | case (elements, n) => 148 | val subject = tree(elements: _*) 149 | subject.drop(n).toVector must_== subject.toVector.drop(n) 150 | } 151 | 152 | def takeDropIdentity = Prop.forAll(GenTreeWithSelectedIndex[Int]) { 153 | case (elements, n) => 154 | val subject = tree(elements: _*) 155 | (subject.take(n) ++ subject.drop(n)) must_== subject 156 | } 157 | 158 | def splitAt = Prop.forAll(GenTreeWithSelectedIndex[Int]) { 159 | case (elements, n) => 160 | val subject = tree(elements: _*) 161 | val (left, right) = subject.splitAt(n) 162 | (left must_== subject.take(n)) and (right must_== subject.drop(n)) 163 | } 164 | 165 | def slice = Prop.forAll(for { 166 | elements <- arbitrary[List[Int]] 167 | from <- Gen.choose(-1, elements.size + 1) 168 | until <- Gen.choose(-1, elements.size + 1) 169 | } yield (elements, from, until)) { 170 | case (elements, from, until) => 171 | tree(elements: _*).slice(from, until).toVector must_== TreeSet(elements: _*).slice(from, until).toVector 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/test/scala/scalax/collection/immutable/BTreeSetThyme.scala: -------------------------------------------------------------------------------- 1 | package scalax.collection.immutable 2 | 3 | import scala.collection.immutable.{ HashSet, SortedSet, TreeSet } 4 | 5 | object BTreeSetThyme { 6 | val random = new util.Random(1233312) 7 | val values = Vector.tabulate(4000000)(_.asInstanceOf[java.lang.Integer]) 8 | val shuffled = random.shuffle(values) 9 | 10 | lazy val th = new ichi.bench.Thyme() 11 | 12 | lazy val fib: Stream[Int] = 1 #:: 1 #:: fib.zip(fib.tail).map { case (a, b) => a + b } 13 | val DefaultSizes = 1000 +: fib.takeWhile(_ <= values.size).toVector //Vector(1000, 1, 10, 100, 1000, 10000, 100000, 1000000) 14 | val DefaultParameters = Vector( 15 | BTreeSet.Parameters(6, 5), 16 | BTreeSet.Parameters(8, 8), 17 | BTreeSet.Parameters(12, 12), 18 | BTreeSet.Parameters(14, 12), 19 | BTreeSet.Parameters(16, 16)) 20 | 21 | def containsSequential(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 22 | val xs = BTreeSetThyme.values.take(i) 23 | val btree = BTreeSet(xs: _*) 24 | val ts = TreeSet(xs: _*) 25 | th.pbenchOff(s"contains $i sequential values")(xs.forall(btree.contains), ftitle = "btree")(xs.forall(ts.contains), htitle = "treeset") 26 | } 27 | 28 | def containsShuffled(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 29 | val xs = BTreeSetThyme.shuffled.take(i) 30 | val btree = BTreeSet(xs: _*) 31 | val ts = TreeSet(xs: _*) 32 | th.pbenchOff(s"contains $i shuffled values")(xs.forall(btree.contains), ftitle = "btree")(xs.forall(ts.contains), htitle = "treeset") 33 | } 34 | 35 | def containsSomeShuffled(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 36 | val xs = shuffled.take(i) 37 | val toCheck = xs.take((i + 9) / 10) 38 | val btree = BTreeSet(xs: _*) 39 | val ts = TreeSet(xs: _*) 40 | th.pbenchOff(s"contains ${toCheck.size} shuffled values in tree of ${xs.size} elements")(toCheck.forall(btree.contains), ftitle = "btree")(toCheck.forall(ts.contains), htitle = "treeset") 41 | } 42 | def notContainsSomeShuffled(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 43 | val (toCheck, xs) = shuffled.take(i).splitAt((i + 9) / 10) 44 | val btree = BTreeSet(xs: _*) 45 | val ts = TreeSet(xs: _*) 46 | th.pbenchOff(s"not contains ${toCheck.size} shuffled values in tree of ${xs.size} elements")(toCheck.forall(btree.contains), ftitle = "btree")(toCheck.forall(ts.contains), htitle = "treeset") 47 | } 48 | def insertSequential(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 49 | val xs = BTreeSetThyme.values.take(i) 50 | th.pbenchOff(s"insert $i sequential values")(xs.foldLeft(BTreeSet.empty[Int])(_ + _).size, ftitle = "btree")(xs.foldLeft(TreeSet.empty[Int])(_ + _).size, htitle = "treeset") 51 | } 52 | 53 | def insertShuffled(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 54 | val xs = BTreeSetThyme.shuffled.take(i) 55 | th.pbenchOff(s"insert $i shuffled values")(xs.foldLeft(BTreeSet.empty[Int])(_ + _).size, ftitle = "btree")(xs.foldLeft(TreeSet.empty[Int])(_ + _).size, htitle = "treeset") 56 | } 57 | 58 | def insertSomeShuffled(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 59 | val (toInsert, xs) = shuffled.take(i).splitAt((i + 9) / 10) 60 | val btree = BTreeSet(xs: _*) 61 | val ts = TreeSet(xs: _*) 62 | th.pbenchOff(s"insert ${toInsert.size} shuffled values into tree of ${xs.size} elements")(toInsert.foldLeft(btree)(_ + _).size, ftitle = "btree")(toInsert.foldLeft(ts)(_ + _).size, htitle = "treeset") 63 | } 64 | 65 | def insertShuffled2(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 66 | val xs = BTreeSetThyme.shuffled.take(i) 67 | th.pbenchOff(s"insert $i shuffled values")(BTreeSet(xs: _*).size, ftitle = "btree")(HashSet(xs: _*).size, htitle = "hashset") 68 | } 69 | 70 | def deleteSequential(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 71 | val xs = BTreeSetThyme.values.take(i) 72 | val btree = BTreeSet(xs: _*) 73 | val ts = TreeSet(xs: _*) 74 | th.pbenchOff(s"delete $i sequential values")(xs.foldLeft(btree)(_ - _).isEmpty, ftitle = "btree")(xs.foldLeft(ts)(_ - _).isEmpty, htitle = "treeset") 75 | } 76 | 77 | def deleteShuffled(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 78 | val xs = BTreeSetThyme.shuffled.take(i) 79 | val btree = BTreeSet(xs: _*) 80 | val ts = TreeSet(xs: _*) 81 | th.pbenchOff(s"delete $i shuffled values")(xs.foldLeft(btree)(_ - _).isEmpty, ftitle = "btree")(xs.foldLeft(ts)(_ - _).isEmpty, htitle = "treeset") 82 | } 83 | 84 | def deleteSomeShuffled(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 85 | val xs = BTreeSetThyme.shuffled.take(i) 86 | val toDelete = xs.take((i + 9) / 10) 87 | val btree = BTreeSet(xs: _*) 88 | val ts = TreeSet(xs: _*) 89 | th.pbenchOff(s"delete ${toDelete.size} of ${xs.size} shuffled values")(toDelete.foldLeft(btree)(_ - _).size, ftitle = "btree")(toDelete.foldLeft(ts)(_ - _).size, htitle = "treeset") 90 | } 91 | 92 | def iterateShuffled(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 93 | val xs = BTreeSetThyme.shuffled.take(i) 94 | val btree = BTreeSet(xs: _*) 95 | val ts = TreeSet(xs: _*) 96 | th.pbenchOff(s"iterate $i shuffled values")(btree.iterator.size, ftitle = "btree")(ts.iterator.size, htitle = "treeset") 97 | } 98 | 99 | def iterateSequential(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 100 | val xs = BTreeSetThyme.values.take(i) 101 | val btree = BTreeSet(xs: _*) 102 | val ts = TreeSet(xs: _*) 103 | th.pbenchOff(s"iterate $i sequential values")(btree.iterator.size, ftitle = "btree")(ts.iterator.size, htitle = "treeset") 104 | } 105 | 106 | def splitShuffled(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 107 | val xs = BTreeSetThyme.shuffled.take(i) 108 | val splits = xs.take(1000) 109 | val btree = BTreeSet(xs: _*) 110 | val ts = TreeSet(xs: _*) 111 | th.pbenchOff(s"split $i shuffled values (${splits.size} times)")(splits.map(i => btree.from(i).size), ftitle = "btree")(splits.map(i => ts.from(i).size), htitle = "treeset") 112 | } 113 | 114 | def splitSequential(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 115 | val xs = BTreeSetThyme.values.take(i) 116 | val splits = random.shuffle(xs).take(1000) 117 | val btree = BTreeSet(xs: _*) 118 | val ts = TreeSet(xs: _*) 119 | th.pbenchOff(s"split $i sequential values (${splits.size} times)")(splits.map(i => btree.from(i).size), ftitle = "btree")(splits.map(i => ts.from(i).size), htitle = "treeset") 120 | } 121 | 122 | def splitAtIndexShuffled(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 123 | val xs = BTreeSetThyme.shuffled.take(i) 124 | val splits = Vector.fill(1000)(random.nextInt(xs.size)) 125 | val btree = BTreeSet(xs: _*) 126 | val ts = TreeSet(xs: _*) 127 | th.pbenchOff(s"split $i shuffled values at index (${splits.size} times)")(splits.map(i => btree.splitAt(i)._1.size), ftitle = "btree")(splits.map(i => ts.splitAt(i)._1.size), htitle = "treeset") 128 | } 129 | 130 | def takeShuffled(sizes: Seq[Int] = DefaultSizes): Unit = sizes foreach { i => 131 | val xs = BTreeSetThyme.shuffled.take(i) 132 | val splits = Vector.fill(1000)(random.nextInt(xs.size)) 133 | val btree = BTreeSet(xs: _*) 134 | val ts = TreeSet(xs: _*) 135 | th.pbenchOff(s"take $i shuffled values (${splits.size} times)")(splits.map(i => btree.take(i).size), ftitle = "btree")(splits.map(i => ts.take(i).size), htitle = "treeset") 136 | } 137 | 138 | def insertShuffledVaryingOrder(sizes: Seq[Int] = DefaultSizes, parameters: Seq[BTreeSet.Parameters] = DefaultParameters): Unit = for { 139 | size <- sizes 140 | (p1, p2) <- parameters.zip(parameters.tail) 141 | } { 142 | val xs = BTreeSetThyme.shuffled.take(size) 143 | th.pbenchOff(s"insert $size shuffled values")(BTreeSet.withParameters[Integer](p1).++(xs).size, ftitle = s"btree L=${p1.minLeafValues},I=${p1.minInternalValues}")(BTreeSet.withParameters[Integer](p2).++(xs).size, htitle = s"btree L=${p2.minLeafValues},I=${p2.minInternalValues}") 144 | } 145 | 146 | private def simpleBench[A](label: String, f: SortedSet[Integer] => A): Unit = DefaultSizes foreach { i => 147 | val xs = BTreeSetThyme.shuffled.take(i) 148 | val btree = BTreeSet(xs: _*) 149 | val ts = TreeSet(xs: _*) 150 | th.pbenchOff(s"$label of $i shuffled values")(f(btree), ftitle = "btree")(f(ts), htitle = "treeset") 151 | } 152 | 153 | def benchHead() = simpleBench("head", _.head) 154 | def benchTail() = simpleBench("tail", _.tail) 155 | def benchLast() = simpleBench("last", _.last) 156 | def benchInit() = simpleBench("init", _.init) 157 | 158 | def benchSlice() = simpleBench("slice", { set => 159 | val start = set.size / 3 160 | val end = 2 * start 161 | set.slice(start, end) 162 | }) 163 | } 164 | --------------------------------------------------------------------------------