/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jul 5, 2007 */
package com.trifork.clj_ds;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
//import jsr166y.ForkJoinPool;
//import jsr166y.RecursiveTask;
public class PersistentVector<T> extends APersistentVector<T> implements IObj, IEditableCollection<T>{
static class Node implements Serializable {
transient final AtomicReference<Thread> edit;
final Object[] array;
Node(AtomicReference<Thread> edit, Object[] array){
this.edit = edit;
this.array = array;
}
Node(AtomicReference<Thread> edit){
this.edit = edit;
this.array = new Object[32];
}
}
final static AtomicReference<Thread> NOEDIT = new AtomicReference<Thread>(null);
final static Node EMPTY_NODE = new Node(NOEDIT, new Object[32]);
final int cnt;
final int shift;
final Node root;
final Object[] tail;
final IPersistentMap _meta;
@SuppressWarnings("unchecked")
public final static PersistentVector EMPTY = new PersistentVector(0, 5, EMPTY_NODE, new Object[]{});
@SuppressWarnings("unchecked")
static public <T> PersistentVector<T> emptyVector() {
return EMPTY;
}
@SuppressWarnings("unchecked")
static public <T> PersistentVector<T> create(ISeq<? extends T> items){
TransientVector<T> ret = EMPTY.asTransient();
for(; items != null; items = items.next())
ret = ret.conj(items.first());
return ret.persistentMap();
}
@SuppressWarnings("unchecked")
static public <T> PersistentVector<T> create(List<? extends T> items){
TransientVector<T> ret = EMPTY.asTransient();
for(T item : items)
ret = ret.conj(item);
return ret.persistentMap();
}
@SuppressWarnings("unchecked")
static public <T> PersistentVector<T> create(T ... items){
TransientVector<T> ret = EMPTY.asTransient();
for(T item : items)
ret = ret.conj(item);
return ret.persistentMap();
}
PersistentVector(int cnt, int shift, Node root, Object[] tail){
this._meta = null;
this.cnt = cnt;
this.shift = shift;
this.root = root;
this.tail = tail;
}
PersistentVector(IPersistentMap meta, int cnt, int shift, Node root, Object[] tail){
this._meta = meta;
this.cnt = cnt;
this.shift = shift;
this.root = root;
this.tail = tail;
}
public PersistentVector(IPersistentMap meta, int cnt, int shift, Node root, Object[] tail, IFn f) {
this._meta = meta();
this.cnt = cnt;
this.shift = shift;
this.tail = mapArray(f,Util.ret1(tail,tail=null));
this.root = mapNode(f,Util.ret1(root, root=null), this.shift);
}
public TransientVector<T> asTransient(){
return new TransientVector<T>(this);
}
final int tailoff(){
if(cnt < 32)
return 0;
return ((cnt - 1) >>> 5) << 5;
}
public Object[] arrayFor(int i){
if(i >= 0 && i < cnt)
{
if(i >= tailoff())
return tail;
Node node = root;
for(int level = shift; level > 0; level -= 5)
node = (Node) node.array[(i >>> level) & 0x01f];
return node.array;
}
throw new IndexOutOfBoundsException();
}
public T nth(int i){
Object[] node = arrayFor(i);
return (T) node[i & 0x01f];
}
public T nth(int i, T notFound){
if(i >= 0 && i < cnt)
return nth(i);
return notFound;
}
public PersistentVector<T> assocN(int i, T val){
if(i >= 0 && i < cnt)
{
if(i >= tailoff())
{
Object[] newTail = new Object[tail.length];
System.arraycopy(tail, 0, newTail, 0, tail.length);
newTail[i & 0x01f] = val;
return new PersistentVector<T>(meta(), cnt, shift, root, newTail);
}
return new PersistentVector<T>(meta(), cnt, shift, doAssoc(shift, root, i, val), tail);
}
if(i == cnt)
return cons(val);
throw new IndexOutOfBoundsException();
}
private static Node doAssoc(int level, Node node, int i, Object val){
Node ret = new Node(node.edit,node.array.clone());
if(level == 0)
{
ret.array[i & 0x01f] = val;
}
else
{
int subidx = (i >>> level) & 0x01f;
ret.array[subidx] = doAssoc(level - 5, (Node) node.array[subidx], i, val);
}
return ret;
}
public int count(){
return cnt;
}
public PersistentVector<T> withMeta(IPersistentMap meta){
return new PersistentVector<T>(meta, cnt, shift, root, tail);
}
public IPersistentMap meta(){
return _meta;
}
public PersistentVector<T> cons(T val){
int i = cnt;
//room in tail?
// if(tail.length < 32)
if(cnt - tailoff() < 32)
{
Object[] newTail = new Object[tail.length + 1];
System.arraycopy(tail, 0, newTail, 0, tail.length);
newTail[tail.length] = val;
return new PersistentVector<T>(meta(), cnt + 1, shift, root, newTail);
}
//full tail, push into tree
Node newroot;
Node tailnode = new Node(root.edit,tail);
int newshift = shift;
//overflow root?
if((cnt >>> 5) > (1 << shift))
{
newroot = new Node(root.edit);
newroot.array[0] = root;
newroot.array[1] = newPath(root.edit,shift, tailnode);
newshift += 5;
}
else
newroot = pushTail(shift, root, tailnode);
return new PersistentVector<T>(meta(), cnt + 1, newshift, newroot, new Object[]{val});
}
private Node pushTail(int level, Node parent, Node tailnode){
//if parent is leaf, insert node,
// else does it map to an existing child? -> nodeToInsert = pushNode one more level
// else alloc new path
//return nodeToInsert placed in copy of parent
int subidx = ((cnt - 1) >>> level) & 0x01f;
Node ret = new Node(parent.edit, parent.array.clone());
Node nodeToInsert;
if(level == 5)
{
nodeToInsert = tailnode;
}
else
{
Node child = (Node) parent.array[subidx];
nodeToInsert = (child != null)?
pushTail(level-5,child, tailnode)
:newPath(root.edit,level-5, tailnode);
}
ret.array[subidx] = nodeToInsert;
return ret;
}
private static Node newPath(AtomicReference<Thread> edit,int level, Node node){
if(level == 0)
return node;
Node ret = new Node(edit);
ret.array[0] = newPath(edit, level - 5, node);
return ret;
}
public IChunkedSeq<T> chunkedSeq(){
if(count() == 0)
return null;
return new ChunkedSeq<T>(this,0,0);
}
public ISeq<T> seq(){
return chunkedSeq();
}
final static class PersistentVectorIterator<T> implements Iterator<T> {
PersistentVector<T> vec;
int sft;
Stack<MapEntry<Integer, Object[]>> path;
Object[] current;
int currentIndex;
public PersistentVectorIterator(PersistentVector<T> vec) {
this.vec = vec;
sft = vec.shift;
path = initialPath();
MapEntry<Integer,Object[]> el = path.peek();
if (el.key() == -1) {
current = el.val();
} else {
current = ((Node) el.val()[el.key()]).array;
}
}
@Override
public boolean hasNext() {
ensureCurrentReady();
return (current != null && currentIndex < current.length);
}
private void ensureCurrentReady() {
if (current != null && currentIndex < current.length) {
return;
}//else current is null or exhausted. Find next
Object[] last = current;
current = findNextArray();
if (current != last) {
currentIndex = 0;
}
}
private Object[] findNextArray() {
if (path.isEmpty()) {return null;}
while(path.peek().getKey() != -1) {
MapEntry<Integer, Object[]> loc = path.pop();
int idx = loc.key();
Object[] arr = loc.val();
idx += 1;
if (idx < arr.length) {
Node next = (Node) arr[idx];
if (next == null) {
continue;
}
path.push(new MapEntry(idx, arr));
for(int level = sft - (path.size() - 1) * 5; level > 0; level -= 5) {
path.push(new MapEntry<Integer, Object[]>(0,next.array));
next = (Node) next.array[0];
}
return next.array;
}
}
return path.pop().val();
}
private Stack<MapEntry<Integer, Object[]>> initialPath() {
Stack<MapEntry<Integer, Object[]>> res = new Stack<MapEntry<Integer, Object[]>>();
res.push(new MapEntry<Integer, Object[]>(-1, vec.tail));
Node node = vec.root;
for(int level = sft; level > 0; level -= 5) {
res.push(new MapEntry<Integer, Object[]>(0,node.array));
node = (Node) node.array[0];
if (node == null) {
res.pop();
break;
}
}
return res;
}
@Override
public T next() {
return (T) current[currentIndex++];
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
public Iterator<T> iterator(){
return new PersistentVectorIterator(this);
}
static public final class ChunkedSeq<T> extends ASeq<T> implements IChunkedSeq<T>{
public final PersistentVector<T> vec;
final Object[] node;
final int i;
public final int offset;
public ChunkedSeq(PersistentVector<T> vec, int i, int offset){
this.vec = vec;
this.i = i;
this.offset = offset;
this.node = vec.arrayFor(i);
}
ChunkedSeq(IPersistentMap meta, PersistentVector<T> vec, Object[] node, int i, int offset){
super(meta);
this.vec = vec;
this.node = node;
this.i = i;
this.offset = offset;
}
ChunkedSeq(PersistentVector<T> vec, Object[] node, int i, int offset){
this.vec = vec;
this.node = node;
this.i = i;
this.offset = offset;
}
public IChunk<T> chunkedFirst() throws Exception{
return new ArrayChunk<T>(node, offset);
}
public ISeq<T> chunkedNext(){
if(i + node.length < vec.cnt)
return new ChunkedSeq<T>(vec,i+ node.length,0);
return null;
}
public ISeq<T> chunkedMore(){
ISeq<T> s = chunkedNext();
if(s == null)
return (ISeq<T>) PersistentList.emptyList();
return s;
}
public Obj withMeta(IPersistentMap meta){
if(meta == this._meta)
return this;
return new ChunkedSeq(meta, vec, node, i, offset);
}
public T first(){
return (T) node[offset];
}
public ISeq<T> next(){
if(offset + 1 < node.length)
return new ChunkedSeq<T>(vec, node, i, offset + 1);
return chunkedNext();
}
}
public IPersistentCollection<T> empty(){
return EMPTY.withMeta(meta());
}
//private Node pushTail(int level, Node node, Object[] tailNode, Box expansion){
// Object newchild;
// if(level == 0)
// {
// newchild = tailNode;
// }
// else
// {
// newchild = pushTail(level - 5, (Object[]) arr[arr.length - 1], tailNode, expansion);
// if(expansion.val == null)
// {
// Object[] ret = arr.clone();
// ret[arr.length - 1] = newchild;
// return ret;
// }
// else
// newchild = expansion.val;
// }
// //expansion
// if(arr.length == 32)
// {
// expansion.val = new Object[]{newchild};
// return arr;
// }
// Object[] ret = new Object[arr.length + 1];
// System.arraycopy(arr, 0, ret, 0, arr.length);
// ret[arr.length] = newchild;
// expansion.val = null;
// return ret;
//}
public PersistentVector<T> pop(){
if(cnt == 0)
throw new IllegalStateException("Can't pop empty vector");
if(cnt == 1)
return EMPTY.withMeta(meta());
//if(tail.length > 1)
if(cnt-tailoff() > 1)
{
Object[] newTail = new Object[tail.length - 1];
System.arraycopy(tail, 0, newTail, 0, newTail.length);
return new PersistentVector<T>(meta(), cnt - 1, shift, root, newTail);
}
Object[] newtail = arrayFor(cnt - 2);
Node newroot = popTail(shift, root);
int newshift = shift;
if(newroot == null)
{
newroot = EMPTY_NODE;
}
if(shift > 5 && newroot.array[1] == null)
{
newroot = (Node) newroot.array[0];
newshift -= 5;
}
return new PersistentVector<T>(meta(), cnt - 1, newshift, newroot, newtail);
}
private Node popTail(int level, Node node){
int subidx = ((cnt-2) >>> level) & 0x01f;
if(level > 5)
{
Node newchild = popTail(level - 5, (Node) node.array[subidx]);
if(newchild == null && subidx == 0)
return null;
else
{
Node ret = new Node(root.edit, node.array.clone());
ret.array[subidx] = newchild;
return ret;
}
}
else if(subidx == 0)
return null;
else
{
Node ret = new Node(root.edit, node.array.clone());
ret.array[subidx] = null;
return ret;
}
}
static final class TransientVector<T> extends AFn implements ITransientVector<T>, Counted{
int cnt;
int shift;
Node root;
Object[] tail;
TransientVector(int cnt, int shift, Node root, Object[] tail){
this.cnt = cnt;
this.shift = shift;
this.root = root;
this.tail = tail;
}
TransientVector(PersistentVector<? extends T> v){
this(v.cnt, v.shift, editableRoot(v.root), editableTail(v.tail));
}
public int count(){
ensureEditable();
return cnt;
}
Node ensureEditable(Node node){
if(node.edit == root.edit)
return node;
return new Node(root.edit, node.array.clone());
}
void ensureEditable(){
Thread owner = root.edit.get();
if(owner == Thread.currentThread())
return;
if(owner != null)
throw new IllegalAccessError("Transient used by non-owner thread");
throw new IllegalAccessError("Transient used after persistent! call");
// root = editableRoot(root);
// tail = editableTail(tail);
}
static Node editableRoot(Node node){
return new Node(new AtomicReference<Thread>(Thread.currentThread()), node.array.clone());
}
public PersistentVector<T> persistentMap(){
ensureEditable();
// Thread owner = root.edit.get();
// if(owner != null && owner != Thread.currentThread())
// {
// throw new IllegalAccessError("Mutation release by non-owner thread");
// }
root.edit.set(null);
Object[] trimmedTail = new Object[cnt-tailoff()];
System.arraycopy(tail,0,trimmedTail,0,trimmedTail.length);
return new PersistentVector<T>(cnt, shift, root, trimmedTail);
}
static Object[] editableTail(Object[] tl){
Object[] ret = new Object[32];
System.arraycopy(tl,0,ret,0,tl.length);
return ret;
}
public TransientVector<T> conj(Object val){
T t = (T) val;
ensureEditable();
int i = cnt;
//room in tail?
if(i - tailoff() < 32)
{
tail[i & 0x01f] = val;
++cnt;
return this;
}
//full tail, push into tree
Node newroot;
Node tailnode = new Node(root.edit, tail);
tail = new Object[32];
tail[0] = val;
int newshift = shift;
//overflow root?
if((cnt >>> 5) > (1 << shift))
{
newroot = new Node(root.edit);
newroot.array[0] = root;
newroot.array[1] = newPath(root.edit,shift, tailnode);
newshift += 5;
}
else
newroot = pushTail(shift, root, tailnode);
root = newroot;
shift = newshift;
++cnt;
return this;
}
private Node pushTail(int level, Node parent, Node tailnode){
//if parent is leaf, insert node,
// else does it map to an existing child? -> nodeToInsert = pushNode one more level
// else alloc new path
//return nodeToInsert placed in parent
parent = ensureEditable(parent);
int subidx = ((cnt - 1) >>> level) & 0x01f;
Node ret = parent;
Node nodeToInsert;
if(level == 5)
{
nodeToInsert = tailnode;
}
else
{
Node child = (Node) parent.array[subidx];
nodeToInsert = (child != null) ?
pushTail(level - 5, child, tailnode)
: newPath(root.edit, level - 5, tailnode);
}
ret.array[subidx] = nodeToInsert;
return ret;
}
final private int tailoff(){
if(cnt < 32)
return 0;
return ((cnt-1) >>> 5) << 5;
}
private Object[] arrayFor(int i){
if(i >= 0 && i < cnt)
{
if(i >= tailoff())
return tail;
Node node = root;
for(int level = shift; level > 0; level -= 5)
node = (Node) node.array[(i >>> level) & 0x01f];
return node.array;
}
throw new IndexOutOfBoundsException();
}
public Object valAt(Object key){
//note - relies on ensureEditable in 2-arg valAt
return valAt(key, null);
}
public Object valAt(Object key, Object notFound){
ensureEditable();
if(Util.isInteger(key))
{
int i = ((Number) key).intValue();
if(i >= 0 && i < cnt)
return nth(i);
}
return notFound;
}
public Object invoke(Object arg1) throws Exception{
//note - relies on ensureEditable in nth
if(Util.isInteger(arg1))
return nth(((Number) arg1).intValue());
throw new IllegalArgumentException("Key must be integer");
}
public T nth(int i){
ensureEditable();
Object[] node = arrayFor(i);
return (T) node[i & 0x01f];
}
public T nth(int i, T notFound){
if(i >= 0 && i < count())
return nth(i);
return notFound;
}
public TransientVector<T> assocN(int i, T val){
ensureEditable();
if(i >= 0 && i < cnt)
{
if(i >= tailoff())
{
tail[i & 0x01f] = val;
return this;
}
root = doAssoc(shift, root, i, val);
return this;
}
if(i == cnt)
return conj(val);
throw new IndexOutOfBoundsException();
}
public TransientVector<T> assoc(Object key, Object val){
//note - relies on ensureEditable in assocN
if(Util.isInteger(key))
{
int i = ((Number) key).intValue();
return assocN(i, (T) val);
}
throw new IllegalArgumentException("Key must be integer");
}
private Node doAssoc(int level, Node node, int i, Object val){
node = ensureEditable(node);
Node ret = node;
if(level == 0)
{
ret.array[i & 0x01f] = val;
}
else
{
int subidx = (i >>> level) & 0x01f;
ret.array[subidx] = doAssoc(level - 5, (Node) node.array[subidx], i, val);
}
return ret;
}
public TransientVector<T> pop(){
ensureEditable();
if(cnt == 0)
throw new IllegalStateException("Can't pop empty vector");
if(cnt == 1)
{
cnt = 0;
return this;
}
int i = cnt - 1;
//pop in tail?
if((i & 0x01f) > 0)
{
--cnt;
return this;
}
Object[] newtail = arrayFor(cnt - 2);
Node newroot = popTail(shift, root);
int newshift = shift;
if(newroot == null)
{
newroot = EMPTY_NODE;
}
if(shift > 5 && newroot.array[1] == null)
{
newroot = (Node) newroot.array[0];
newshift -= 5;
}
root = newroot;
shift = newshift;
--cnt;
tail = newtail;
return this;
}
private Node popTail(int level, Node node){
node = ensureEditable(node);
int subidx = ((cnt - 2) >>> level) & 0x01f;
if(level > 5)
{
Node newchild = popTail(level - 5, (Node) node.array[subidx]);
if(newchild == null && subidx == 0)
return null;
else
{
Node ret = node;
ret.array[subidx] = newchild;
return ret;
}
}
else if(subidx == 0)
return null;
else
{
Node ret = node;
ret.array[subidx] = null;
return ret;
}
}
@Override
public IPersistentCollection persistent() {
return persistentMap();
}
}
/*
static public void main(String[] args){
if(args.length != 3)
{
System.err.println("Usage: PersistentVector size writes reads");
return;
}
int size = Integer.parseInt(args[0]);
int writes = Integer.parseInt(args[1]);
int reads = Integer.parseInt(args[2]);
// Vector v = new Vector(size);
ArrayList v = new ArrayList(size);
// v.setSize(size);
//PersistentArray p = new PersistentArray(size);
PersistentVector p = PersistentVector.EMPTY;
// MutableVector mp = p.mutable();
for(int i = 0; i < size; i++)
{
v.add(i);
// v.set(i, i);
//p = p.set(i, 0);
p = p.cons(i);
// mp = mp.conj(i);
}
Random rand;
rand = new Random(42);
long tv = 0;
System.out.println("ArrayList");
long startTime = System.nanoTime();
for(int i = 0; i < writes; i++)
{
v.set(rand.nextInt(size), i);
}
for(int i = 0; i < reads; i++)
{
tv += (Integer) v.get(rand.nextInt(size));
}
long estimatedTime = System.nanoTime() - startTime;
System.out.println("time: " + estimatedTime / 1000000);
System.out.println("PersistentVector");
rand = new Random(42);
startTime = System.nanoTime();
long tp = 0;
// PersistentVector oldp = p;
//Random rand2 = new Random(42);
MutableVector mp = p.mutable();
for(int i = 0; i < writes; i++)
{
// p = p.assocN(rand.nextInt(size), i);
mp = mp.assocN(rand.nextInt(size), i);
// mp = mp.assoc(rand.nextInt(size), i);
//dummy set to force perverse branching
//oldp = oldp.assocN(rand2.nextInt(size), i);
}
for(int i = 0; i < reads; i++)
{
// tp += (Integer) p.nth(rand.nextInt(size));
tp += (Integer) mp.nth(rand.nextInt(size));
}
// p = mp.immutable();
//mp.cons(42);
estimatedTime = System.nanoTime() - startTime;
System.out.println("time: " + estimatedTime / 1000000);
for(int i = 0; i < size / 2; i++)
{
mp = mp.pop();
// p = p.pop();
v.remove(v.size() - 1);
}
p = (PersistentVector) mp.immutable();
//mp.pop(); //should fail
for(int i = 0; i < size / 2; i++)
{
tp += (Integer) p.nth(i);
tv += (Integer) v.get(i);
}
System.out.println("Done: " + tv + ", " + tp);
}
// */
public static IPersistentVector vectormap(IFn f, PersistentVector v) {
return new PersistentVector(v._meta,v.cnt,v.shift,v.root,Util.ret1(v.tail,v=null),f);
}
//static final ForkJoinPool mainPool = new ForkJoinPool();
//
//public static IPersistentVector pvectormap(IFn f, PersistentVector v) {
// Node invoke = mainPool.invoke(new PMapTask(f, v.shift,v.root));
// return new PersistentVector(v._meta,v.cnt,v.shift,invoke, mapArray(f,v.tail));
//}
//
//static final class PMapTask extends RecursiveTask<Node> {
//
// private IFn f;
// private int shift;
// private Node node;
//
// public PMapTask(IFn f, int shift, Node node) {
// this.f = f;
// this.shift = shift;
// this.node = node;
// }
//
// public Node compute() {
// if (node == null) {
// return null;
// }
// if (this.shift <= 5) {
// return mapNode(f,node,shift);
// }
//
// PMapTask[] tasks = new PMapTask[node.array.length];
// shift -= 5;
// for (int i=0;i<tasks.length;i++) {
// tasks[i] = new PMapTask(f,shift,(Node) node.array[i]);
// }
// invokeAll(tasks);
// Node[] nodes = new Node[node.array.length];
// try {
// for (int i=0;i<tasks.length;i++) {
// nodes[i] = tasks[i].get();
// }
// return new Node(null,nodes);
// } catch (InterruptedException e) {
// Thread.currentThread().interrupt();
// throw new RuntimeException(e);
// } catch (ExecutionException e) {
// throw new RuntimeException(e);
// }
// }
//}
private static Object[] mapArray(IFn f, Object[] arr) {
Object[] res = new Object[arr.length];
System.arraycopy(arr, 0, res, 0, arr.length);
arr = null;
try {
for(int i=0;i<res.length;i++) {
res[i] = f.invoke(res[i]);
}
return res;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Node mapNode(IFn f, Node node, int level) {
if (node == null) {return null;}
if (level == 0) {
return new Node(null,mapArray(f, Util.ret1(node.array, node=null)));
}
Object[] newArr = new Object[node.array.length];
System.arraycopy(node.array, 0, newArr, 0, node.array.length);
node=null;
level -= 5;
for (int i=0;i<newArr.length;i++) {
newArr[i] = mapNode(f,Util.ret1((Node) newArr[i], newArr[i]=null),level);
}
return new Node(null,newArr);
}
}