added immutable persistent Map and Vector
This commit is contained in:
parent
0f6ca61cb2
commit
7abd91785c
2 changed files with 712 additions and 0 deletions
361
kernel/src/main/scala/collection/HashTrie.scala
Executable file
361
kernel/src/main/scala/collection/HashTrie.scala
Executable file
|
|
@ -0,0 +1,361 @@
|
||||||
|
/**
|
||||||
|
Copyright (c) 2007-2008, Rich Hickey
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials provided
|
||||||
|
with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of Clojure nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this
|
||||||
|
software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**/
|
||||||
|
|
||||||
|
package se.scalablesolutions.akka.collection
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A clean-room port of Rich Hickey's persistent hash trie implementation from
|
||||||
|
* Clojure (http://clojure.org). Originally presented as a mutable structure in
|
||||||
|
* a paper by Phil Bagwell.
|
||||||
|
*
|
||||||
|
* @author Daniel Spiewak
|
||||||
|
* @author Rich Hickey
|
||||||
|
*/
|
||||||
|
final class HashTrie[K, +V] private (root: Node[K, V]) extends Map[K, V] {
|
||||||
|
lazy val size = root.size
|
||||||
|
|
||||||
|
def this() = this(new EmptyNode[K])
|
||||||
|
|
||||||
|
def get(key: K) = root(key, key.hashCode)
|
||||||
|
|
||||||
|
override def +[A >: V](pair: (K, A)) = pair match {
|
||||||
|
case (k, v) => update(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
def update[A >: V](key: K, value: A) = new HashTrie(root(0, key, key.hashCode) = value)
|
||||||
|
|
||||||
|
def -(key: K) = new HashTrie(root.remove(key, key.hashCode))
|
||||||
|
|
||||||
|
def elements = root.elements
|
||||||
|
|
||||||
|
def empty[A]: HashTrie[K, A] = new HashTrie(new EmptyNode[K])
|
||||||
|
|
||||||
|
def diagnose = root.toString
|
||||||
|
}
|
||||||
|
|
||||||
|
object HashTrie {
|
||||||
|
def apply[K, V](pairs: (K, V)*) = pairs.foldLeft(new HashTrie[K, V]) { _ + _ }
|
||||||
|
|
||||||
|
def unapplySeq[K, V](map: HashTrie[K, V]) = map.toSeq
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// nodes
|
||||||
|
|
||||||
|
private[collection] sealed trait Node[K, +V] {
|
||||||
|
val size: Int
|
||||||
|
|
||||||
|
def apply(key: K, hash: Int): Option[V]
|
||||||
|
|
||||||
|
def update[A >: V](shift: Int, key: K, hash: Int, value: A): Node[K, A]
|
||||||
|
|
||||||
|
def remove(key: K, hash: Int): Node[K, V]
|
||||||
|
|
||||||
|
def elements: Iterator[(K, V)]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private[collection] class EmptyNode[K] extends Node[K, Nothing] {
|
||||||
|
val size = 0
|
||||||
|
|
||||||
|
def apply(key: K, hash: Int) = None
|
||||||
|
|
||||||
|
def update[V](shift: Int, key: K, hash: Int, value: V) = new LeafNode(key, hash, value)
|
||||||
|
|
||||||
|
def remove(key: K, hash: Int) = this
|
||||||
|
|
||||||
|
lazy val elements = new Iterator[(K, Nothing)] {
|
||||||
|
val hasNext = false
|
||||||
|
|
||||||
|
val next = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private[collection] abstract class SingleNode[K, +V] extends Node[K, V] {
|
||||||
|
val hash: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private[collection] class LeafNode[K, +V](key: K, val hash: Int, value: V) extends SingleNode[K, V] {
|
||||||
|
val size = 1
|
||||||
|
|
||||||
|
def apply(key: K, hash: Int) = if (this.key == key) Some(value) else None
|
||||||
|
|
||||||
|
def update[A >: V](shift: Int, key: K, hash: Int, value: A) = {
|
||||||
|
if (this.key == key) {
|
||||||
|
if (this.value == value) this else new LeafNode(key, hash, value)
|
||||||
|
} else if (this.hash == hash) {
|
||||||
|
new CollisionNode(hash, this.key -> this.value, key -> value)
|
||||||
|
} else {
|
||||||
|
BitmappedNode(shift)(this, key, hash, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def remove(key: K, hash: Int) = if (this.key == key) new EmptyNode[K] else this
|
||||||
|
|
||||||
|
def elements = new Iterator[(K, V)] {
|
||||||
|
var hasNext = true
|
||||||
|
|
||||||
|
def next = {
|
||||||
|
hasNext = false
|
||||||
|
(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def toString = "LeafNode(" + key + " -> " + value + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private[collection] class CollisionNode[K, +V](val hash: Int, bucket: List[(K, V)]) extends SingleNode[K, V] {
|
||||||
|
lazy val size = bucket.length
|
||||||
|
|
||||||
|
def this(hash: Int, pairs: (K, V)*) = this(hash, pairs.toList)
|
||||||
|
|
||||||
|
def apply(key: K, hash: Int) = {
|
||||||
|
for {
|
||||||
|
(_, v) <- bucket find { case (k, _) => k == key }
|
||||||
|
} yield v
|
||||||
|
}
|
||||||
|
|
||||||
|
def update[A >: V](shift: Int, key: K, hash: Int, value: A): Node[K, A] = {
|
||||||
|
if (this.hash == hash) {
|
||||||
|
var found = false
|
||||||
|
|
||||||
|
val newBucket = for ((k, v) <- bucket) yield {
|
||||||
|
if (k == key) {
|
||||||
|
found = true
|
||||||
|
(key, value)
|
||||||
|
} else (k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
new CollisionNode(hash, if (found) newBucket else (key, value) :: bucket)
|
||||||
|
} else {
|
||||||
|
BitmappedNode(shift)(this, key, hash, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def remove(key: K, hash: Int) = {
|
||||||
|
val newBucket = bucket filter { case (k, _) => k != key }
|
||||||
|
|
||||||
|
if (newBucket.length == bucket.length) this else {
|
||||||
|
if (newBucket.length == 1) {
|
||||||
|
val (key, value) = newBucket.head
|
||||||
|
new LeafNode(key, hash, value)
|
||||||
|
} else new CollisionNode(hash, newBucket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def elements = bucket.elements
|
||||||
|
|
||||||
|
override def toString = "CollisionNode(" + bucket.toString + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private[collection] class BitmappedNode[K, +V](shift: Int)(table: Array[Node[K, V]], bits: Int) extends Node[K, V] {
|
||||||
|
lazy val size = {
|
||||||
|
val sizes = for {
|
||||||
|
n <- table
|
||||||
|
if n != null
|
||||||
|
} yield n.size
|
||||||
|
|
||||||
|
sizes.foldLeft(0) { _ + _ }
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply(key: K, hash: Int) = {
|
||||||
|
val i = (hash >>> shift) & 0x01f
|
||||||
|
val mask = 1 << i
|
||||||
|
|
||||||
|
if ((bits & mask) == mask) table(i)(key, hash) else None
|
||||||
|
}
|
||||||
|
|
||||||
|
def update[A >: V](levelShift: Int, key: K, hash: Int, value: A): Node[K, A] = {
|
||||||
|
val i = (hash >>> shift) & 0x01f
|
||||||
|
val mask = 1 << i
|
||||||
|
|
||||||
|
if ((bits & mask) == mask) {
|
||||||
|
val node = (table(i)(shift + 5, key, hash) = value)
|
||||||
|
|
||||||
|
if (node == table(i)) this else {
|
||||||
|
val newTable = new Array[Node[K, A]](table.length)
|
||||||
|
Array.copy(table, 0, newTable, 0, table.length)
|
||||||
|
|
||||||
|
newTable(i) = node
|
||||||
|
|
||||||
|
new BitmappedNode(shift)(newTable, bits)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val newTable = new Array[Node[K, A]](Math.max(table.length, i + 1))
|
||||||
|
Array.copy(table, 0, newTable, 0, table.length)
|
||||||
|
|
||||||
|
newTable(i) = new LeafNode(key, hash, value)
|
||||||
|
|
||||||
|
val newBits = bits | mask
|
||||||
|
if (newBits == ~0) {
|
||||||
|
new FullNode(shift)(newTable)
|
||||||
|
} else {
|
||||||
|
new BitmappedNode(shift)(newTable, newBits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def remove(key: K, hash: Int) = {
|
||||||
|
val i = (hash >>> shift) & 0x01f
|
||||||
|
val mask = 1 << i
|
||||||
|
|
||||||
|
if ((bits & mask) == mask) {
|
||||||
|
val node = table(i).remove(key, hash)
|
||||||
|
|
||||||
|
if (node == table(i)) {
|
||||||
|
this
|
||||||
|
} else if (node.isInstanceOf[EmptyNode[_]]) {
|
||||||
|
if (size == 1) new EmptyNode[K] else {
|
||||||
|
val adjustedBits = bits ^ mask
|
||||||
|
val log = Math.log(adjustedBits) / Math.log(2)
|
||||||
|
|
||||||
|
if (log.toInt.toDouble == log) { // last one
|
||||||
|
table(log.toInt)
|
||||||
|
} else {
|
||||||
|
val newTable = new Array[Node[K, V]](table.length)
|
||||||
|
Array.copy(table, 0, newTable, 0, newTable.length)
|
||||||
|
|
||||||
|
newTable(i) = null
|
||||||
|
|
||||||
|
new BitmappedNode(shift)(newTable, adjustedBits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val newTable = new Array[Node[K, V]](table.length)
|
||||||
|
Array.copy(table, 0, newTable, 0, table.length)
|
||||||
|
|
||||||
|
newTable(i) = node
|
||||||
|
|
||||||
|
new BitmappedNode(shift)(newTable, bits)
|
||||||
|
}
|
||||||
|
} else this
|
||||||
|
}
|
||||||
|
|
||||||
|
def elements = {
|
||||||
|
table.foldLeft(emptyElements) { (it, e) =>
|
||||||
|
if (e == null) it else it ++ e.elements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def toString = "BitmappedNode(" + size + "," + table.filter(_ != null).toList.toString + ")"
|
||||||
|
|
||||||
|
private lazy val emptyElements: Iterator[(K, V)] = new Iterator[(K, V)] {
|
||||||
|
val hasNext = false
|
||||||
|
|
||||||
|
val next = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private[collection] object BitmappedNode {
|
||||||
|
def apply[K, V](shift: Int)(node: SingleNode[K, V], key: K, hash: Int, value: V) = {
|
||||||
|
val table = new Array[Node[K, V]](Math.max((hash >>> shift) & 0x01f, (node.hash >>> shift) & 0x01f) + 1)
|
||||||
|
|
||||||
|
val preBits = {
|
||||||
|
val i = (node.hash >>> shift) & 0x01f
|
||||||
|
table(i) = node
|
||||||
|
1 << i
|
||||||
|
}
|
||||||
|
|
||||||
|
val bits = {
|
||||||
|
val i = (hash >>> shift) & 0x01f
|
||||||
|
val mask = 1 << i
|
||||||
|
|
||||||
|
if ((preBits & mask) == mask) {
|
||||||
|
table(i) = (table(i)(shift + 5, key, hash) = value)
|
||||||
|
} else {
|
||||||
|
table(i) = new LeafNode(key, hash, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
preBits | mask
|
||||||
|
}
|
||||||
|
|
||||||
|
new BitmappedNode(shift)(table, bits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private[collection] class FullNode[K, +V](shift: Int)(table: Array[Node[K, V]]) extends Node[K, V] {
|
||||||
|
lazy val size = table.foldLeft(0) { _ + _.size }
|
||||||
|
|
||||||
|
def apply(key: K, hash: Int) = table((hash >>> shift) & 0x01f)(key, hash)
|
||||||
|
|
||||||
|
def update[A >: V](levelShift: Int, key: K, hash: Int, value: A) = {
|
||||||
|
val i = (hash >>> shift) & 0x01f
|
||||||
|
|
||||||
|
val node = (table(i)(shift + 5, key, hash) = value)
|
||||||
|
|
||||||
|
if (node == table(i)) this else {
|
||||||
|
val newTable = new Array[Node[K, A]](32)
|
||||||
|
Array.copy(table, 0, newTable, 0, 32)
|
||||||
|
|
||||||
|
newTable(i) = node
|
||||||
|
|
||||||
|
new FullNode(shift)(newTable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def remove(key: K, hash: Int) = {
|
||||||
|
val i = (hash >>> shift) & 0x01f
|
||||||
|
val mask = 1 << i
|
||||||
|
|
||||||
|
val node = table(i).remove(key, hash)
|
||||||
|
|
||||||
|
if (node == table(i)) this else {
|
||||||
|
val newTable = new Array[Node[K, V]](32)
|
||||||
|
Array.copy(table, 0, newTable, 0, 32)
|
||||||
|
|
||||||
|
if (node.isInstanceOf[EmptyNode[_]]) {
|
||||||
|
newTable(i) = null
|
||||||
|
new BitmappedNode(shift)(newTable, ~mask)
|
||||||
|
} else {
|
||||||
|
newTable(i) = node
|
||||||
|
new FullNode(shift)(newTable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def elements = table.foldLeft(emptyElements) { _ ++ _.elements }
|
||||||
|
|
||||||
|
override def toString = "FullNode(" + table.foldLeft("") { _.toString + ", " + _.toString } + ")"
|
||||||
|
|
||||||
|
private lazy val emptyElements: Iterator[(K, V)] = new Iterator[(K, V)] {
|
||||||
|
val hasNext = false
|
||||||
|
|
||||||
|
val next = null
|
||||||
|
}
|
||||||
|
}
|
||||||
351
kernel/src/main/scala/collection/Vector.scala
Executable file
351
kernel/src/main/scala/collection/Vector.scala
Executable file
|
|
@ -0,0 +1,351 @@
|
||||||
|
/**
|
||||||
|
Copyright (c) 2007-2008, Rich Hickey
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials provided
|
||||||
|
with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of Clojure nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this
|
||||||
|
software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**/
|
||||||
|
|
||||||
|
package se.scalablesolutions.akka.collection
|
||||||
|
|
||||||
|
import Vector._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A straight port of Clojure's <code>PersistentVector</code> class.
|
||||||
|
*
|
||||||
|
* @author Daniel Spiewak
|
||||||
|
* @author Rich Hickey
|
||||||
|
*/
|
||||||
|
class Vector[+T] private (val length: Int, shift: Int, root: Array[AnyRef], tail: Array[AnyRef]) extends RandomAccessSeq[T] { outer =>
|
||||||
|
private val tailOff = length - tail.length
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The design of this data structure inherantly requires heterogenous arrays.
|
||||||
|
* It is *possible* to design around this, but the result is comparatively
|
||||||
|
* quite inefficient. With respect to this fact, I have left the original
|
||||||
|
* (somewhat dynamically-typed) implementation in place.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private[collection] def this() = this(0, 5, EmptyArray, EmptyArray)
|
||||||
|
|
||||||
|
def apply(i: Int): T = {
|
||||||
|
if (i >= 0 && i < length) {
|
||||||
|
if (i >= tailOff) {
|
||||||
|
tail(i & 0x01f).asInstanceOf[T]
|
||||||
|
} else {
|
||||||
|
var arr = root
|
||||||
|
var level = shift
|
||||||
|
|
||||||
|
while (level > 0) {
|
||||||
|
arr = arr((i >>> level) & 0x01f).asInstanceOf[Array[AnyRef]]
|
||||||
|
level -= 5
|
||||||
|
}
|
||||||
|
|
||||||
|
arr(i & 0x01f).asInstanceOf[T]
|
||||||
|
}
|
||||||
|
} else throw new IndexOutOfBoundsException(i.toString)
|
||||||
|
}
|
||||||
|
|
||||||
|
def update[A >: T](i: Int, obj: A): Vector[A] = {
|
||||||
|
if (i >= 0 && i < length) {
|
||||||
|
if (i >= tailOff) {
|
||||||
|
val newTail = new Array[AnyRef](tail.length)
|
||||||
|
Array.copy(tail, 0, newTail, 0, tail.length)
|
||||||
|
newTail(i & 0x01f) = obj.asInstanceOf[AnyRef]
|
||||||
|
|
||||||
|
new Vector[A](length, shift, root, newTail)
|
||||||
|
} else {
|
||||||
|
new Vector[A](length, shift, doAssoc(shift, root, i, obj), tail)
|
||||||
|
}
|
||||||
|
} else if (i == length) {
|
||||||
|
this + obj
|
||||||
|
} else throw new IndexOutOfBoundsException(i.toString)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def doAssoc[A >: T](level: Int, arr: Array[AnyRef], i: Int, obj: A): Array[AnyRef] = {
|
||||||
|
val ret = new Array[AnyRef](arr.length)
|
||||||
|
Array.copy(arr, 0, ret, 0, arr.length)
|
||||||
|
|
||||||
|
if (level == 0) {
|
||||||
|
ret(i & 0x01f) = obj.asInstanceOf[AnyRef]
|
||||||
|
} else {
|
||||||
|
val subidx = (i >>> level) & 0x01f
|
||||||
|
ret(subidx) = doAssoc(level - 5, arr(subidx).asInstanceOf[Array[AnyRef]], i, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
override def ++[A >: T](other: Iterable[A]) = other.foldLeft(this:Vector[A]) { _ + _ }
|
||||||
|
|
||||||
|
def +[A >: T](obj: A): Vector[A] = {
|
||||||
|
if (tail.length < 32) {
|
||||||
|
val newTail = new Array[AnyRef](tail.length + 1)
|
||||||
|
Array.copy(tail, 0, newTail, 0, tail.length)
|
||||||
|
newTail(tail.length) = obj.asInstanceOf[AnyRef]
|
||||||
|
|
||||||
|
new Vector[A](length + 1, shift, root, newTail)
|
||||||
|
} else {
|
||||||
|
var (newRoot, expansion) = pushTail(shift - 5, root, tail, null)
|
||||||
|
var newShift = shift
|
||||||
|
|
||||||
|
if (expansion != null) {
|
||||||
|
newRoot = array(newRoot, expansion)
|
||||||
|
newShift += 5
|
||||||
|
}
|
||||||
|
|
||||||
|
new Vector[A](length + 1, newShift, newRoot, array(obj.asInstanceOf[AnyRef]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def pushTail(level: Int, arr: Array[AnyRef], tailNode: Array[AnyRef], expansion: AnyRef): (Array[AnyRef], AnyRef) = {
|
||||||
|
val newChild = if (level == 0) tailNode else {
|
||||||
|
val (newChild, subExpansion) = pushTail(level - 5, arr(arr.length - 1).asInstanceOf[Array[AnyRef]], tailNode, expansion)
|
||||||
|
|
||||||
|
if (subExpansion == null) {
|
||||||
|
val ret = new Array[AnyRef](arr.length)
|
||||||
|
Array.copy(arr, 0, ret, 0, arr.length)
|
||||||
|
|
||||||
|
ret(arr.length - 1) = newChild
|
||||||
|
|
||||||
|
return (ret, null)
|
||||||
|
} else subExpansion
|
||||||
|
}
|
||||||
|
|
||||||
|
// expansion
|
||||||
|
if (arr.length == 32) {
|
||||||
|
(arr, array(newChild))
|
||||||
|
} else {
|
||||||
|
val ret = new Array[AnyRef](arr.length + 1)
|
||||||
|
Array.copy(arr, 0, ret, 0, arr.length)
|
||||||
|
ret(arr.length) = newChild
|
||||||
|
|
||||||
|
(ret, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the <i>tail</i> element of this vector.
|
||||||
|
*/
|
||||||
|
def pop: Vector[T] = {
|
||||||
|
if (length == 0) {
|
||||||
|
throw new IllegalStateException("Can't pop empty vector")
|
||||||
|
} else if (length == 1) {
|
||||||
|
EmptyVector
|
||||||
|
} else if (tail.length > 1) {
|
||||||
|
val newTail = new Array[AnyRef](tail.length - 1)
|
||||||
|
Array.copy(tail, 0, newTail, 0, newTail.length)
|
||||||
|
|
||||||
|
new Vector[T](length - 1, shift, root, newTail)
|
||||||
|
} else {
|
||||||
|
var (newRoot, pTail) = popTail(shift - 5, root, null)
|
||||||
|
var newShift = shift
|
||||||
|
|
||||||
|
if (newRoot == null) {
|
||||||
|
newRoot = EmptyArray
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shift > 5 && newRoot.length == 1) {
|
||||||
|
newRoot = newRoot(0).asInstanceOf[Array[AnyRef]]
|
||||||
|
newShift -= 5
|
||||||
|
}
|
||||||
|
|
||||||
|
new Vector[T](length - 1, newShift, newRoot, pTail.asInstanceOf[Array[AnyRef]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def popTail(shift: Int, arr: Array[AnyRef], pTail: AnyRef): (Array[AnyRef], AnyRef) = {
|
||||||
|
val newPTail = if (shift > 0) {
|
||||||
|
val (newChild, subPTail) = popTail(shift - 5, arr(arr.length - 1).asInstanceOf[Array[AnyRef]], pTail)
|
||||||
|
|
||||||
|
if (newChild != null) {
|
||||||
|
val ret = new Array[AnyRef](arr.length)
|
||||||
|
Array.copy(arr, 0, ret, 0, arr.length)
|
||||||
|
|
||||||
|
ret(arr.length - 1) = newChild
|
||||||
|
|
||||||
|
return (ret, subPTail)
|
||||||
|
}
|
||||||
|
subPTail
|
||||||
|
} else if (shift == 0) {
|
||||||
|
arr(arr.length - 1)
|
||||||
|
} else pTail
|
||||||
|
|
||||||
|
// contraction
|
||||||
|
if (arr.length == 1) {
|
||||||
|
(null, newPTail)
|
||||||
|
} else {
|
||||||
|
val ret = new Array[AnyRef](arr.length - 1)
|
||||||
|
Array.copy(arr, 0, ret, 0, ret.length)
|
||||||
|
|
||||||
|
(ret, newPTail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def filter(p: (T)=>Boolean) = {
|
||||||
|
var back = new Vector[T]
|
||||||
|
var i = 0
|
||||||
|
|
||||||
|
while (i < length) {
|
||||||
|
val e = apply(i)
|
||||||
|
if (p(e)) back += e
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
back
|
||||||
|
}
|
||||||
|
|
||||||
|
override def flatMap[A](f: (T)=>Iterable[A]) = {
|
||||||
|
var back = new Vector[A]
|
||||||
|
var i = 0
|
||||||
|
|
||||||
|
while (i < length) {
|
||||||
|
f(apply(i)) foreach { back += _ }
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
back
|
||||||
|
}
|
||||||
|
|
||||||
|
override def map[A](f: (T)=>A) = {
|
||||||
|
var back = new Vector[A]
|
||||||
|
var i = 0
|
||||||
|
|
||||||
|
while (i < length) {
|
||||||
|
back += f(apply(i))
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
back
|
||||||
|
}
|
||||||
|
|
||||||
|
override def reverse: Vector[T] = new VectorProjection[T] {
|
||||||
|
override val length = outer.length
|
||||||
|
|
||||||
|
override def apply(i: Int) = outer.apply(length - i - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def subseq(from: Int, end: Int) = subVector(from, end)
|
||||||
|
|
||||||
|
def subVector(from: Int, end: Int): Vector[T] = {
|
||||||
|
if (from < 0) {
|
||||||
|
throw new IndexOutOfBoundsException(from.toString)
|
||||||
|
} else if (end >= length) {
|
||||||
|
throw new IndexOutOfBoundsException(end.toString)
|
||||||
|
} else if (end <= from) {
|
||||||
|
throw new IllegalArgumentException("Invalid range: " + from + ".." + end)
|
||||||
|
} else {
|
||||||
|
new VectorProjection[T] {
|
||||||
|
override val length = end - from
|
||||||
|
|
||||||
|
override def apply(i: Int) = outer.apply(i + from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def zip[A](that: Vector[A]) = {
|
||||||
|
var back = new Vector[(T, A)]
|
||||||
|
var i = 0
|
||||||
|
|
||||||
|
val limit = Math.min(length, that.length)
|
||||||
|
while (i < limit) {
|
||||||
|
back += (apply(i), that(i))
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
back
|
||||||
|
}
|
||||||
|
|
||||||
|
def zipWithIndex = {
|
||||||
|
var back = new Vector[(T, Int)]
|
||||||
|
var i = 0
|
||||||
|
|
||||||
|
while (i < length) {
|
||||||
|
back += (apply(i), i)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
back
|
||||||
|
}
|
||||||
|
|
||||||
|
override def equals(other: Any) = other match {
|
||||||
|
case vec:Vector[T] => {
|
||||||
|
var back = length == vec.length
|
||||||
|
var i = 0
|
||||||
|
|
||||||
|
while (i < length) {
|
||||||
|
back &&= apply(i) == vec.apply(i)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
back
|
||||||
|
}
|
||||||
|
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
|
||||||
|
override def hashCode = foldLeft(0) { _ ^ _.hashCode }
|
||||||
|
}
|
||||||
|
|
||||||
|
object Vector {
|
||||||
|
private[collection] val EmptyArray = new Array[AnyRef](0)
|
||||||
|
|
||||||
|
def apply[T](elems: T*) = elems.foldLeft(EmptyVector:Vector[T]) { _ + _ }
|
||||||
|
|
||||||
|
def unapplySeq[T](vec: Vector[T]): Option[Seq[T]] = Some(vec)
|
||||||
|
|
||||||
|
@inline
|
||||||
|
private[collection] def array(elems: AnyRef*) = {
|
||||||
|
val back = new Array[AnyRef](elems.length)
|
||||||
|
Array.copy(elems, 0, back, 0, back.length)
|
||||||
|
|
||||||
|
back
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object EmptyVector extends Vector[Nothing]
|
||||||
|
|
||||||
|
private[collection] abstract class VectorProjection[+T] extends Vector[T] {
|
||||||
|
override val length: Int
|
||||||
|
override def apply(i: Int): T
|
||||||
|
|
||||||
|
override def +[A >: T](e: A) = innerCopy + e
|
||||||
|
|
||||||
|
override def update[A >: T](i: Int, e: A) = {
|
||||||
|
if (i < 0) {
|
||||||
|
throw new IndexOutOfBoundsException(i.toString)
|
||||||
|
} else if (i > length) {
|
||||||
|
throw new IndexOutOfBoundsException(i.toString)
|
||||||
|
} else innerCopy(i) = e
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy val innerCopy = foldLeft(EmptyVector:Vector[T]) { _ + _ }
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue