Remove unneeded code and beautify HashPMap and its dependencies

This commit is contained in:
Alexander Udalov
2014-06-09 22:38:58 +04:00
parent 58bc611e3a
commit d83df541b7
10 changed files with 547 additions and 1126 deletions
@@ -1,86 +1,58 @@
package kotlin.reflect.jvm.internal.pcollections; package kotlin.reflect.jvm.internal.pcollections;
import java.util.AbstractSequentialList;
import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.NoSuchElementException;
/** /**
*
* A simple persistent stack of non-null values. * A simple persistent stack of non-null values.
* <p> * <p/>
* This implementation is thread-safe (assuming Java's AbstractSequentialList is thread-safe), * This implementation is thread-safe, although its iterators may not be.
* although its iterators may not be.
* *
* @author harold * @author harold
*
* @param <E>
*/ */
public final class ConsPStack<E> extends AbstractSequentialList<E> implements PStack<E> { public final class ConsPStack<E> implements PStack<E> {
//// STATIC FACTORY METHODS ////
private static final ConsPStack<Object> EMPTY = new ConsPStack<Object>(); private static final ConsPStack<Object> EMPTY = new ConsPStack<Object>();
/**
* @param <E>
* @return an empty stack
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <E> ConsPStack<E> empty() { public static <E> ConsPStack<E> empty() {
return (ConsPStack<E>)EMPTY; } return (ConsPStack<E>) EMPTY;
/**
* @param <E>
* @param e
* @return empty().plus(e)
*/
public static <E> ConsPStack<E> singleton(final E e) {
return ConsPStack.<E>empty().plus(e); }
/**
* @param <E>
* @param list
* @return a stack consisting of the elements of list in the order of list.iterator()
*/
@SuppressWarnings("unchecked")
public static <E> ConsPStack<E> from(final Collection<? extends E> list) {
if(list instanceof ConsPStack)
return (ConsPStack<E>)list; //(actually we only know it's ConsPStack<? extends E>)
// but that's good enough for an immutable
// (i.e. we can't mess someone else up by adding the wrong type to it)
return from(list.iterator());
} }
private static <E> ConsPStack<E> from(final Iterator<? extends E> i) { private final E first;
if(!i.hasNext()) return empty(); private final ConsPStack<E> rest;
E e = i.next();
return from(i).plus(e);
}
//// PRIVATE CONSTRUCTORS ////
private final E first; private final ConsPStack<E> rest;
private final int size; private final int size;
// not externally instantiable (or subclassable):
private ConsPStack() { // EMPTY constructor private ConsPStack() { // EMPTY constructor
if(EMPTY!=null) size = 0;
throw new RuntimeException("empty constructor should only be used once"); first = null;
size = 0; first=null; rest=null; rest = null;
}
private ConsPStack(final E first, final ConsPStack<E> rest) {
this.first = first; this.rest = rest;
size = 1 + rest.size;
} }
private ConsPStack(E first, ConsPStack<E> rest) {
this.first = first;
this.rest = rest;
this.size = 1 + rest.size;
}
public E get(int index) {
try {
return listIterator(index).next();
} catch (NoSuchElementException e) {
throw new IndexOutOfBoundsException("Index: " + index);
}
}
@Override
public Iterator<E> iterator() {
return listIterator(0);
}
//// REQUIRED METHODS FROM AbstractSequentialList ////
@Override @Override
public int size() { public int size() {
return size; } return size;
}
@Override
public ListIterator<E> listIterator(final int index) { public ListIterator<E> listIterator(final int index) {
if (index < 0 || index > size) throw new IndexOutOfBoundsException(); if (index < 0 || index > size) throw new IndexOutOfBoundsException();
@@ -88,83 +60,64 @@ public final class ConsPStack<E> extends AbstractSequentialList<E> implements PS
int i = index; int i = index;
ConsPStack<E> next = subList(index); ConsPStack<E> next = subList(index);
@Override
public boolean hasNext() { public boolean hasNext() {
return next.size>0; } return next.size > 0;
}
@Override
public boolean hasPrevious() { public boolean hasPrevious() {
return i>0; } return i > 0;
}
@Override
public int nextIndex() { public int nextIndex() {
return index; } return index;
}
@Override
public int previousIndex() { public int previousIndex() {
return index-1; } return index - 1;
}
@Override
public E next() { public E next() {
E e = next.first; E e = next.first;
next = next.rest; next = next.rest;
return e; return e;
} }
@Override
public E previous() { public E previous() {
System.err.println("ConsPStack.listIterator().previous() is inefficient, don't use it!"); System.err.println("ConsPStack.listIterator().previous() is inefficient, don't use it!");
next = subList(index - 1); // go from beginning... next = subList(index - 1); // go from beginning...
return next.first; return next.first;
} }
public void add(final E o) { @Override
throw new UnsupportedOperationException(); } public void add(E o) {
throw new UnsupportedOperationException();
}
@Override
public void remove() { public void remove() {
throw new UnsupportedOperationException(); } throw new UnsupportedOperationException();
public void set(final E o) { }
throw new UnsupportedOperationException(); }
@Override
public void set(E o) {
throw new UnsupportedOperationException();
}
}; };
} }
//// OVERRIDDEN METHODS FROM AbstractSequentialList ////
@Override @Override
public ConsPStack<E> subList(final int start, final int end) { public ConsPStack<E> plus(E e) {
if(start<0 || end>size || start>end)
throw new IndexOutOfBoundsException();
if(end==size) // want a substack
return subList(start); // this is faster
if(start==end) // want nothing
return empty();
if(start==0) // want the current element
return new ConsPStack<E>(first, rest.subList(0, end-1));
// otherwise, don't want the current element:
return rest.subList(start-1, end-1);
}
//// IMPLEMENTED METHODS OF PStack ////
public ConsPStack<E> plus(final E e) {
return new ConsPStack<E>(e, this); return new ConsPStack<E>(e, this);
} }
public ConsPStack<E> plusAll(final Collection<? extends E> list) { public ConsPStack<E> minus(Object e) {
ConsPStack<E> result = this; if (size == 0) return this;
for(E e : list)
result = result.plus(e);
return result;
}
public ConsPStack<E> plus(final int i, final E e) {
if(i<0 || i>size)
throw new IndexOutOfBoundsException();
if(i==0) // insert at beginning
return plus(e);
return new ConsPStack<E>(first, rest.plus(i-1, e));
}
public ConsPStack<E> plusAll(final int i, final Collection<? extends E> list) {
// TODO inefficient if list.isEmpty()
if(i<0 || i>size)
throw new IndexOutOfBoundsException();
if(i==0)
return plusAll(list);
return new ConsPStack<E>(first, rest.plusAll(i-1, list));
}
public ConsPStack<E> minus(final Object e) {
if(size==0)
return this;
if (first.equals(e)) // found it if (first.equals(e)) // found it
return rest; // don't recurse (only remove one) return rest; // don't recurse (only remove one)
// otherwise keep looking: // otherwise keep looking:
@@ -173,34 +126,12 @@ public final class ConsPStack<E> extends AbstractSequentialList<E> implements PS
return new ConsPStack<E>(first, newRest); return new ConsPStack<E>(first, newRest);
} }
public ConsPStack<E> minus(final int i) { @Override
public ConsPStack<E> minus(int i) {
return minus(get(i)); return minus(get(i));
} }
public ConsPStack<E> minusAll(final Collection<?> list) { public ConsPStack<E> subList(int start) {
if(size==0)
return this;
if(list.contains(first)) // get rid of current element
return rest.minusAll(list); // recursively delete all
// either way keep looking:
ConsPStack<E> newRest = rest.minusAll(list);
if(newRest==rest) return this;
return new ConsPStack<E>(first, newRest);
}
public ConsPStack<E> with(final int i, final E e) {
if(i<0 || i>=size)
throw new IndexOutOfBoundsException();
if(i==0) {
if(first.equals(e)) return this;
return new ConsPStack<E>(e, rest);
}
ConsPStack<E> newRest = rest.with(i-1, e);
if(newRest==rest) return this;
return new ConsPStack<E>(first, newRest);
}
public ConsPStack<E> subList(final int start) {
if (start < 0 || start > size) if (start < 0 || start > size)
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
if (start == 0) if (start == 0)
@@ -1,148 +1,81 @@
package kotlin.reflect.jvm.internal.pcollections; package kotlin.reflect.jvm.internal.pcollections;
import java.util.AbstractMap; import static java.util.Map.Entry;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/** /**
*
* A persistent map from non-null keys to non-null values. * A persistent map from non-null keys to non-null values.
* <p> * <p/>
* This map uses a given integer map to map hashcodes to lists of elements * This map uses a given integer map to map hashcodes to lists of elements
* with the same hashcode. Thus if all elements have the same hashcode, performance * with the same hashcode. Thus if all elements have the same hashcode, performance
* is reduced to that of an association list. * is reduced to that of an association list.
* <p> * <p/>
* This implementation is thread-safe (assuming Java's AbstractMap and AbstractSet are thread-safe), * This implementation is thread-safe, although its iterators may not be.
* although its iterators may not be.
* *
* @author harold * @author harold
*
* @param <K>
* @param <V>
*/ */
public final class HashPMap<K,V> extends AbstractMap<K,V> implements PMap<K,V> { public final class HashPMap<K, V> implements PMap<K, V> {
//// STATIC FACTORY METHODS //// public static final HashPMap<Object, Object> EMPTY = new HashPMap<Object, Object>(IntTreePMap.<PStack<Entry<Object, Object>>>empty(), 0);
/**
* @param <K>
* @param <V>
* @param intMap
* @return a map backed by an empty version of intMap,
* i.e. backed by intMap.minusAll(intMap.keySet())
*/
public static <K,V> HashPMap<K,V> empty(final PMap<Integer,PSequence<Entry<K,V>>> intMap) {
return new HashPMap<K,V>(intMap.minusAll(intMap.keySet()), 0); }
@SuppressWarnings("unchecked")
public static <K, V> HashPMap<K, V> empty() {
return (HashPMap<K, V>) HashPMap.EMPTY;
}
//// PRIVATE CONSTRUCTORS //// private final IntTreePMap<PStack<Entry<K, V>>> intMap;
private final PMap<Integer,PSequence<Entry<K,V>>> intMap;
private final int size; private final int size;
// not externally instantiable (or subclassable):
private HashPMap(final PMap<Integer,PSequence<Entry<K,V>>> intMap, final int size) {
this.intMap = intMap; this.size = size; }
private HashPMap(IntTreePMap<PStack<Entry<K, V>>> intMap, int size) {
//// REQUIRED METHODS FROM AbstractMap //// this.intMap = intMap;
// this cache variable is thread-safe since assignment in Java is atomic: this.size = size;
private Set<Entry<K,V>> entrySet = null;
@Override
public Set<Entry<K,V>> entrySet() {
if(entrySet==null)
entrySet = new AbstractSet<Entry<K,V>>() {
// REQUIRED METHODS OF AbstractSet //
@Override
public int size() {
return size; }
@Override
public Iterator<Entry<K,V>> iterator() {
return new SequenceIterator<Entry<K,V>>(intMap.values().iterator()); }
// OVERRIDDEN METHODS OF AbstractSet //
@Override
public boolean contains(final Object e) {
if(!(e instanceof Entry))
return false;
V value = get(((Entry<?,?>)e).getKey());
return value!=null && value.equals(((Entry<?,?>)e).getValue());
}
};
return entrySet;
} }
//// OVERRIDDEN METHODS FROM AbstractMap ////
@Override
public int size() { public int size() {
return size; } return size;
}
public boolean containsKey(Object key) {
return keyIndexIn(getEntries(key.hashCode()), key) != -1;
}
@Override @Override
public boolean containsKey(final Object key) { public V get(Object key) {
return keyIndexIn(getEntries(key.hashCode()), key) != -1; } PStack<Entry<K, V>> entries = getEntries(key.hashCode());
@Override
public V get(final Object key) {
PSequence<Entry<K,V>> entries = getEntries(key.hashCode());
for (Entry<K, V> entry : entries) for (Entry<K, V> entry : entries)
if (entry.getKey().equals(key)) if (entry.getKey().equals(key))
return entry.getValue(); return entry.getValue();
return null; return null;
} }
@Override
//// IMPLEMENTED METHODS OF PMap//// public HashPMap<K, V> plus(K key, V value) {
public HashPMap<K,V> plusAll(final Map<? extends K, ? extends V> map) { PStack<Entry<K, V>> entries = getEntries(key.hashCode());
HashPMap<K,V> result = this; int size0 = entries.size();
for(Entry<? extends K,? extends V> entry : map.entrySet()) int i = keyIndexIn(entries, key);
result = result.plus(entry.getKey(), entry.getValue());
return result;
}
public HashPMap<K,V> minusAll(final Collection<?> keys) {
HashPMap<K,V> result = this;
for(Object key : keys)
result = result.minus(key);
return result;
}
public HashPMap<K,V> plus(final K key, final V value) {
PSequence<Entry<K,V>> entries = getEntries(key.hashCode());
int size0 = entries.size(),
i = keyIndexIn(entries, key);
if (i != -1) entries = entries.minus(i); if (i != -1) entries = entries.minus(i);
entries = entries.plus(new SimpleImmutableEntry<K, V>(key, value)); entries = entries.plus(new SimpleImmutableEntry<K, V>(key, value));
return new HashPMap<K,V>(intMap.plus(key.hashCode(), entries), return new HashPMap<K, V>(intMap.plus(key.hashCode(), entries), size - size0 + entries.size());
size-size0+entries.size());
} }
public HashPMap<K,V> minus(final Object key) { @Override
PSequence<Entry<K,V>> entries = getEntries(key.hashCode()); public HashPMap<K, V> minus(Object key) {
PStack<Entry<K, V>> entries = getEntries(key.hashCode());
int i = keyIndexIn(entries, key); int i = keyIndexIn(entries, key);
if (i == -1) // key not in this if (i == -1) // key not in this
return this; return this;
entries = entries.minus(i); entries = entries.minus(i);
if (entries.size() == 0) // get rid of the entire hash entry if (entries.size() == 0) // get rid of the entire hash entry
return new HashPMap<K,V>(intMap.minus(key.hashCode()), return new HashPMap<K, V>(intMap.minus(key.hashCode()), size - 1);
size-1);
// otherwise replace hash entry with new smaller one: // otherwise replace hash entry with new smaller one:
return new HashPMap<K,V>(intMap.plus(key.hashCode(), entries), return new HashPMap<K, V>(intMap.plus(key.hashCode(), entries), size - 1);
size-1);
} }
private PStack<Entry<K, V>> getEntries(int hash) {
//// PRIVATE UTILITIES //// PStack<Entry<K, V>> entries = intMap.get(hash);
private PSequence<Entry<K,V>> getEntries(final int hash) {
PSequence<Entry<K,V>> entries = intMap.get(hash);
if (entries == null) return ConsPStack.empty(); if (entries == null) return ConsPStack.empty();
return entries; return entries;
} }
private static <K, V> int keyIndexIn(PStack<Entry<K, V>> entries, Object key) {
//// PRIVATE STATIC UTILITIES ////
private static <K,V> int keyIndexIn(final PSequence<Entry<K,V>> entries, final Object key) {
int i = 0; int i = 0;
for (Entry<K, V> entry : entries) { for (Entry<K, V> entry : entries) {
if (entry.getKey().equals(key)) if (entry.getKey().equals(key))
@@ -151,25 +84,4 @@ public final class HashPMap<K,V> extends AbstractMap<K,V> implements PMap<K,V> {
} }
return -1; return -1;
} }
static class SequenceIterator<E> implements Iterator<E> {
private final Iterator<PSequence<E>> i;
private PSequence<E> seq = ConsPStack.empty();
SequenceIterator(Iterator<PSequence<E>> i) {
this.i = i; }
public boolean hasNext() {
return seq.size()>0 || i.hasNext(); }
public E next() {
if(seq.size()==0)
seq = i.next();
final E result = seq.get(0);
seq = seq.subList(1, seq.size());
return result;
}
public void remove() {
throw new UnsupportedOperationException(); }
}
} }
@@ -1,51 +0,0 @@
package kotlin.reflect.jvm.internal.pcollections;
import java.util.Map;
import java.util.Map.Entry;
/**
*
* A static convenience class for creating efficient persistent maps.
* <p>
* This class simply creates HashPMaps backed by IntTreePMaps.
*
* @author harold
*/
public final class HashTreePMap {
// not instantiable (or subclassable):
private HashTreePMap() {}
private static final HashPMap<Object,Object> EMPTY
= HashPMap.empty(IntTreePMap.<PSequence<Entry<Object,Object>>>empty());
/**
* @param <K>
* @param <V>
* @return an empty map
*/
@SuppressWarnings("unchecked")
public static <K,V> HashPMap<K,V> empty() {
return (HashPMap<K,V>)EMPTY; }
/**
* @param <K>
* @param <V>
* @param key
* @param value
* @return empty().plus(key, value)
*/
public static <K,V> HashPMap<K,V> singleton(final K key, final V value) {
return HashTreePMap.<K,V>empty().plus(key, value); }
/**
* @param <K>
* @param <V>
* @param map
* @return empty().plusAll(map)
*/
public static <K,V> HashPMap<K,V> from(final Map<? extends K, ? extends V> map) {
return HashTreePMap.<K,V>empty().plusAll(map); }
}
@@ -1,72 +1,63 @@
package kotlin.reflect.jvm.internal.pcollections; package kotlin.reflect.jvm.internal.pcollections;
import java.util.Iterator;
import java.util.Map.Entry;
/** /**
*
* A non-public utility class for persistent balanced tree maps with integer keys. * A non-public utility class for persistent balanced tree maps with integer keys.
* <p> * <p/>
* To allow for efficiently increasing all keys above a certain value or decreasing * To allow for efficiently increasing all keys above a certain value or decreasing
* all keys below a certain value, the keys values are stored relative to their parent. * all keys below a certain value, the keys values are stored relative to their parent.
* This makes this map a good backing for fast insertion and removal of indices in a * This makes this map a good backing for fast insertion and removal of indices in a
* vector. * vector.
* <p> * <p/>
* This implementation is thread-safe except for its iterators. * This implementation is thread-safe except for its iterators.
* <p> * <p/>
* Other than that, this tree is based on the Glasgow Haskell Compiler's Data.Map implementation, * Other than that, this tree is based on the Glasgow Haskell Compiler's Data.Map implementation,
* which in turn is based on "size balanced binary trees" as described by: * which in turn is based on "size balanced binary trees" as described by:
* <p> * <p/>
* Stephen Adams, "Efficient sets: a balancing act", * Stephen Adams, "Efficient sets: a balancing act",
* Journal of Functional Programming 3(4):553-562, October 1993, * Journal of Functional Programming 3(4):553-562, October 1993,
* http://www.swiss.ai.mit.edu/~adams/BB/. * http://www.swiss.ai.mit.edu/~adams/BB/.
* <p> * <p/>
* J. Nievergelt and E.M. Reingold, "Binary search trees of bounded balance", * J. Nievergelt and E.M. Reingold, "Binary search trees of bounded balance",
* SIAM journal of computing 2(1), March 1973. * SIAM journal of computing 2(1), March 1973.
* *
* @author harold * @author harold
*
* @param <V>
*/ */
class IntTree<V> { final class IntTree<V> {
// marker value: // marker value:
static final IntTree<Object> EMPTYNODE = new IntTree<Object>(); static final IntTree<Object> EMPTYNODE = new IntTree<Object>();
private final long key; // we use longs so relative keys can express all ints // we use longs so relative keys can express all ints
// (e.g. if this has key -10 and right has 'absolute' key MAXINT, // (e.g. if this has key -10 and right has 'absolute' key MAXINT,
// then its relative key is MAXINT+10 which overflows) // then its relative key is MAXINT+10 which overflows)
// there might be some way to deal with this based on left-verse-right logic, // there might be some way to deal with this based on left-verse-right logic,
// but that sounds like a mess. // but that sounds like a mess.
private final long key;
private final V value; // null value means this is empty node private final V value; // null value means this is empty node
private final IntTree<V> left, right; private final IntTree<V> left, right;
private final int size; private final int size;
private IntTree() {
if(EMPTYNODE!=null)
throw new RuntimeException("empty constructor should only be used once");
size = 0;
key=0; value=null; left=null; right=null; private IntTree() {
size = 0;
key = 0;
value = null;
left = null;
right = null;
} }
private IntTree(final long key, final V value, final IntTree<V> left, final IntTree<V> right) {
this.key = key; this.value = value; private IntTree(long key, V value, IntTree<V> left, IntTree<V> right) {
this.left = left; this.right = right; this.key = key;
this.value = value;
this.left = left;
this.right = right;
size = 1 + left.size + right.size; size = 1 + left.size + right.size;
} }
private IntTree<V> withKey(final long newKey) { private IntTree<V> withKey(long newKey) {
if (size == 0 || newKey == key) return this; if (size == 0 || newKey == key) return this;
return new IntTree<V>(newKey, value, left, right); } return new IntTree<V>(newKey, value, left, right);
}
Iterator<Entry<Integer,V>> iterator() { boolean containsKey(long key) {
return new EntryIterator<V>(this); }
int size() {
return size; }
boolean containsKey(final long key) {
if (size == 0) if (size == 0)
return false; return false;
if (key < this.key) if (key < this.key)
@@ -77,7 +68,7 @@ class IntTree<V> {
return true; return true;
} }
V get(final long key) { V get(long key) {
if (size == 0) if (size == 0)
return null; return null;
if (key < this.key) if (key < this.key)
@@ -88,7 +79,7 @@ class IntTree<V> {
return value; return value;
} }
IntTree<V> plus(final long key, final V value) { IntTree<V> plus(long key, V value) {
if (size == 0) if (size == 0)
return new IntTree<V>(key, value, this, this); return new IntTree<V>(key, value, this, this);
if (key < this.key) if (key < this.key)
@@ -101,7 +92,7 @@ class IntTree<V> {
return new IntTree<V>(key, value, left, right); return new IntTree<V>(key, value, left, right);
} }
IntTree<V> minus(final long key) { IntTree<V> minus(long key) {
if (size == 0) if (size == 0)
return this; return this;
if (key < this.key) if (key < this.key)
@@ -141,16 +132,16 @@ class IntTree<V> {
/** /**
* Changes every key k>=key to k+delta. * Changes every key k>=key to k+delta.
* * <p/>
* This method will create an _invalid_ tree if delta<0 * This method will create an _invalid_ tree if delta<0
* and the distance between the smallest k>=key in this * and the distance between the smallest k>=key in this
* and the largest j<key in this is |delta| or less. * and the largest j<key in this is |delta| or less.
* * <p/>
* In other words, this method must not result in any change * In other words, this method must not result in any change
* in the order of the keys in this, since the tree structure is * in the order of the keys in this, since the tree structure is
* not being changed at all. * not being changed at all.
*/ */
IntTree<V> changeKeysAbove(final long key, final int delta) { IntTree<V> changeKeysAbove(long key, int delta) {
if (size == 0 || delta == 0) if (size == 0 || delta == 0)
return this; return this;
@@ -168,16 +159,16 @@ class IntTree<V> {
/** /**
* Changes every key k<key to k+delta. * Changes every key k<key to k+delta.
* * <p/>
* This method will create an _invalid_ tree if delta>0 * This method will create an _invalid_ tree if delta>0
* and the distance between the largest k<key in this * and the distance between the largest k<key in this
* and the smallest j>=key in this is delta or less. * and the smallest j>=key in this is delta or less.
* * <p/>
* In other words, this method must not result in any overlap or change * In other words, this method must not result in any overlap or change
* in the order of the keys in this, since the tree _structure_ is * in the order of the keys in this, since the tree _structure_ is
* not being changed at all. * not being changed at all.
*/ */
IntTree<V> changeKeysBelow(final long key, final int delta) { IntTree<V> changeKeysBelow(long key, int delta) {
if (size == 0 || delta == 0) if (size == 0 || delta == 0)
return this; return this;
@@ -201,7 +192,7 @@ class IntTree<V> {
return left.minKey() + this.key; return left.minKey() + this.key;
} }
private IntTree<V> rebalanced(final IntTree<V> newLeft, final IntTree<V> newRight) { private IntTree<V> rebalanced(IntTree<V> newLeft, IntTree<V> newRight) {
if (newLeft == left && newRight == right) if (newLeft == left && newRight == right)
return this; // already balanced return this; // already balanced
return rebalanced(key, value, newLeft, newRight); return rebalanced(key, value, newLeft, newRight);
@@ -209,9 +200,9 @@ class IntTree<V> {
private static final int OMEGA = 5; private static final int OMEGA = 5;
private static final int ALPHA = 2; private static final int ALPHA = 2;
// rebalance a tree that is off-balance by at most 1: // rebalance a tree that is off-balance by at most 1:
private static <V> IntTree<V> rebalanced(final long key, final V value, private static <V> IntTree<V> rebalanced(long key, V value, IntTree<V> left, IntTree<V> right) {
final IntTree<V> left, final IntTree<V> right) {
if (left.size + right.size > 1) { if (left.size + right.size > 1) {
if (left.size >= OMEGA * right.size) { // rotate to the right if (left.size >= OMEGA * right.size) { // rotate to the right
IntTree<V> ll = left.left, lr = left.right; IntTree<V> ll = left.left, lr = left.right;
@@ -231,8 +222,7 @@ class IntTree<V> {
lrr.withKey(lrr.key + lr.key + left.key), lrr.withKey(lrr.key + lr.key + left.key),
right)); right));
} }
} } else if (right.size >= OMEGA * left.size) { // rotate to the left
else if(right.size >= OMEGA*left.size) { // rotate to the left
IntTree<V> rl = right.left, rr = right.right; IntTree<V> rl = right.left, rr = right.right;
if (rl.size < ALPHA * rr.size) // single rotation if (rl.size < ALPHA * rr.size) // single rotation
return new IntTree<V>(right.key + key, right.value, return new IntTree<V>(right.key + key, right.value,
@@ -255,55 +245,4 @@ class IntTree<V> {
// otherwise already balanced enough: // otherwise already balanced enough:
return new IntTree<V>(key, value, left, right); return new IntTree<V>(key, value, left, right);
} }
////entrySet().iterator() IMPLEMENTATION ////
// TODO make this a ListIterator?
private static final class EntryIterator<V> implements Iterator<Entry<Integer,V>> {
private PStack<IntTree<V>> stack = ConsPStack.empty(); //path of nonempty nodes
private int key = 0; // note we use _int_ here since this is a truly absolute key
EntryIterator(final IntTree<V> root) {
gotoMinOf(root); }
public boolean hasNext() {
return stack.size()>0; }
public Entry<Integer,V> next() {
IntTree<V> node = stack.get(0);
final Entry<Integer,V> result = new SimpleImmutableEntry<Integer,V>(key, node.value);
// find next node.
// we've already done everything smaller,
// so try least larger node:
if(node.right.size>0) // we can descend to the right
gotoMinOf(node.right);
else // can't descend to the right -- try ascending to the right
while (true) { // find current node's least larger ancestor, if any
key -= node.key; // revert to parent's key
stack = stack.subList(1); // climb up to parent
// if parent was larger than child or there was no parent, we're done:
if(node.key<0 || stack.size()==0)
break;
// otherwise parent was smaller -- try its parent:
node = stack.get(0);
}
return result;
}
public void remove() {
throw new UnsupportedOperationException(); }
// extend the stack to its least non-empty node:
private void gotoMinOf(IntTree<V> node) {
while(node.size>0) {
stack = stack.plus(node);
key += node.key;
node = node.left;
}
}
}
} }
@@ -1,163 +1,52 @@
package kotlin.reflect.jvm.internal.pcollections; package kotlin.reflect.jvm.internal.pcollections;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/** /**
*
* An efficient persistent map from integer keys to non-null values. * An efficient persistent map from integer keys to non-null values.
* <p> * <p/>
* Iteration occurs in the integer order of the keys. * Iteration occurs in the integer order of the keys.
* <p> * <p/>
* This implementation is thread-safe (assuming Java's AbstractMap and AbstractSet are thread-safe), * This implementation is thread-safe, although its iterators may not be.
* although its iterators may not be. * <p/>
* <p>
* The balanced tree is based on the Glasgow Haskell Compiler's Data.Map implementation, * The balanced tree is based on the Glasgow Haskell Compiler's Data.Map implementation,
* which in turn is based on "size balanced binary trees" as described by: * which in turn is based on "size balanced binary trees" as described by:
* <p> * <p/>
* Stephen Adams, "Efficient sets: a balancing act", * Stephen Adams, "Efficient sets: a balancing act",
* Journal of Functional Programming 3(4):553-562, October 1993, * Journal of Functional Programming 3(4):553-562, October 1993,
* http://www.swiss.ai.mit.edu/~adams/BB/. * http://www.swiss.ai.mit.edu/~adams/BB/.
* <p> * <p/>
* J. Nievergelt and E.M. Reingold, "Binary search trees of bounded balance", * J. Nievergelt and E.M. Reingold, "Binary search trees of bounded balance",
* SIAM journal of computing 2(1), March 1973. * SIAM journal of computing 2(1), March 1973.
* *
* @author harold * @author harold
*
* @param <V>
*/ */
public final class IntTreePMap<V> extends AbstractMap<Integer,V> implements PMap<Integer,V> { public final class IntTreePMap<V> {
//// STATIC FACTORY METHODS ////
private static final IntTreePMap<Object> EMPTY = new IntTreePMap<Object>(IntTree.EMPTYNODE); private static final IntTreePMap<Object> EMPTY = new IntTreePMap<Object>(IntTree.EMPTYNODE);
/**
* @param <V>
* @return an empty map
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <V> IntTreePMap<V> empty() { public static <V> IntTreePMap<V> empty() {
return (IntTreePMap<V>)EMPTY; } return (IntTreePMap<V>) EMPTY;
}
/**
* @param <V>
* @param key
* @param value
* @return empty().plus(key, value)
*/
public static <V> IntTreePMap<V> singleton(final Integer key, final V value) {
return IntTreePMap.<V>empty().plus(key, value); }
/**
* @param <V>
* @param map
* @return empty().plusAll(map)
*/
@SuppressWarnings("unchecked")
public static <V> IntTreePMap<V> from(final Map<? extends Integer, ? extends V> map) {
if(map instanceof IntTreePMap)
return (IntTreePMap<V>)map; //(actually we only know it's IntTreePMap<? extends V>)
// but that's good enough for an immutable
// (i.e. we can't mess someone else up by adding the wrong type to it)
return IntTreePMap.<V>empty().plusAll(map); }
//// PRIVATE CONSTRUCTORS ////
private final IntTree<V> root; private final IntTree<V> root;
// not externally instantiable (or subclassable):
private IntTreePMap(final IntTree<V> root) { private IntTreePMap(IntTree<V> root) {
this.root = root; } this.root = root;
private IntTreePMap<V> withRoot(final IntTree<V> root) { }
private IntTreePMap<V> withRoot(IntTree<V> root) {
if (root == this.root) return this; if (root == this.root) return this;
return new IntTreePMap<V>(root); } return new IntTreePMap<V>(root);
//// UNINHERITED METHODS OF IntTreePMap ////
IntTreePMap<V> withKeysChangedAbove(final int key, final int delta) {
// TODO check preconditions of changeKeysAbove()
// TODO make public?
return withRoot( root.changeKeysAbove(key, delta) );
} }
IntTreePMap<V> withKeysChangedBelow(final int key, final int delta) { public V get(int key) {
// TODO check preconditions of changeKeysAbove() return root.get(key);
// TODO make public?
return withRoot( root.changeKeysBelow(key, delta) );
} }
//// REQUIRED METHODS FROM AbstractMap //// public IntTreePMap<V> plus(int key, V value) {
// this cache variable is thread-safe, since assignment in Java is atomic: return withRoot(root.plus(key, value));
private Set<Entry<Integer,V>> entrySet = null;
@Override
public Set<Entry<Integer,V>> entrySet() {
if(entrySet==null)
entrySet = new AbstractSet<Entry<Integer,V>>() {
// REQUIRED METHODS OF AbstractSet //
@Override
public int size() { // same as Map
return IntTreePMap.this.size(); }
@Override
public Iterator<Entry<Integer,V>> iterator() {
return root.iterator(); }
// OVERRIDDEN METHODS OF AbstractSet //
@Override
public boolean contains(final Object e) {
if(!(e instanceof Entry))
return false;
V value = get(((Entry<?,?>)e).getKey());
return value!=null && value.equals(((Entry<?,?>)e).getValue());
}
};
return entrySet;
} }
public IntTreePMap<V> minus(int key) {
//// OVERRIDDEN METHODS FROM AbstractMap //// return withRoot(root.minus(key));
@Override
public int size() {
return root.size(); }
@Override
public boolean containsKey(final Object key) {
if(!(key instanceof Integer))
return false;
return root.containsKey((Integer)key);
}
@Override
public V get(final Object key) {
if(!(key instanceof Integer))
return null;
return root.get((Integer)key);
}
//// IMPLEMENTED METHODS OF PMap////
public IntTreePMap<V> plus(final Integer key, final V value) {
return withRoot( root.plus(key, value) ); }
public IntTreePMap<V> minus(final Object key) {
if(!(key instanceof Integer)) return this;
return withRoot( root.minus((Integer)key) ); }
public IntTreePMap<V> plusAll(final Map<? extends Integer, ? extends V> map) {
IntTree<V> root = this.root;
for(Entry<? extends Integer,? extends V> entry : map.entrySet())
root = root.plus(entry.getKey(), entry.getValue());
return withRoot(root);
}
public IntTreePMap<V> minusAll(final Collection<?> keys) {
IntTree<V> root = this.root;
for(Object key : keys)
if(key instanceof Integer)
root = root.minus((Integer)key);
return withRoot(root);
} }
} }
@@ -1,47 +0,0 @@
package kotlin.reflect.jvm.internal.pcollections;
import java.util.Collection;
/**
*
* An immutable, persistent collection of non-null elements of type E.
*
* @author harold
*
* @param <E>
*/
public interface PCollection<E> extends Collection<E> {
/**
* @param e non-null
* @return a collection which contains e and all of the elements of this
*/
public PCollection<E> plus(E e);
/**
* @param list contains no null elements
* @return a collection which contains all of the elements of list and this
*/
public PCollection<E> plusAll(Collection<? extends E> list);
/**
* @param e
* @return this with a single instance of e removed, if e is in this
*/
public PCollection<E> minus(Object e);
/**
* @param list
* @return this with all elements of list completely removed
*/
public PCollection<E> minusAll(Collection<?> list);
// TODO public PCollection<E> retainingAll(Collection<?> list);
@Deprecated boolean add(E o);
@Deprecated boolean remove(Object o);
@Deprecated boolean addAll(Collection<? extends E> c);
@Deprecated boolean removeAll(Collection<?> c);
@Deprecated boolean retainAll(Collection<?> c);
@Deprecated void clear();
}
@@ -1,45 +1,14 @@
package kotlin.reflect.jvm.internal.pcollections; package kotlin.reflect.jvm.internal.pcollections;
import java.util.Collection;
import java.util.Map;
/** /**
*
* An immutable, persistent map from non-null keys of type K to non-null values of type V. * An immutable, persistent map from non-null keys of type K to non-null values of type V.
* *
* @author harold * @author harold
*
* @param <K>
* @param <V>
*/
public interface PMap<K,V> extends Map<K,V> {
/**
* @param key non-null
* @param value non-null
* @return a map with the mappings of this but with key mapped to value
*/ */
public interface PMap<K, V> {
public PMap<K, V> plus(K key, V value); public PMap<K, V> plus(K key, V value);
/**
* @param map
* @return this combined with map, with map's mappings used for any keys in both map and this
*/
public PMap<K,V> plusAll(Map<? extends K, ? extends V> map);
/**
* @param key
* @return a map with the mappings of this but with no value for key
*/
public PMap<K, V> minus(Object key); public PMap<K, V> minus(Object key);
/** V get(Object key);
* @param keys
* @return a map with the mappings of this but with no value for any element of keys
*/
public PMap<K,V> minusAll(Collection<?> keys);
@Deprecated V put(K k, V v);
@Deprecated V remove(Object k);
@Deprecated void putAll(Map<? extends K, ? extends V> m);
@Deprecated void clear();
} }
@@ -1,69 +0,0 @@
package kotlin.reflect.jvm.internal.pcollections;
import java.util.Collection;
import java.util.List;
/**
*
* An immutable, persistent indexed collection.
*
* @author harold
*
* @param <E>
*/
public interface PSequence<E> extends PCollection<E>, List<E> {
//@Override
public PSequence<E> plus(E e);
//@Override
public PSequence<E> plusAll(Collection<? extends E> list);
/**
* @param i
* @param e
* @return a sequence consisting of the elements of this with e replacing the element at index i.
* @throws IndexOutOfBOundsException if i&lt;0 || i&gt;=this.size()
*/
public PSequence<E> with(int i, E e);
/**
* @param i
* @param e non-null
* @return a sequence consisting of the elements of this with e inserted at index i.
* @throws IndexOutOfBOundsException if i&lt;0 || i&gt;this.size()
*/
public PSequence<E> plus(int i, E e);
/**
* @param i
* @param list
* @return a sequence consisting of the elements of this with list inserted at index i.
* @throws IndexOutOfBOundsException if i&lt;0 || i&gt;this.size()
*/
public PSequence<E> plusAll(int i, Collection<? extends E> list);
/**
* Returns a sequence consisting of the elements of this without the first occurrence of e.
*/
//@Override
public PSequence<E> minus(Object e);
//@Override
public PSequence<E> minusAll(Collection<?> list);
/**
* @param i
* @return a sequence consisting of the elements of this with the element at index i removed.
* @throws IndexOutOfBOundsException if i&lt;0 || i&gt;=this.size()
*/
public PSequence<E> minus(int i);
//@Override
public PSequence<E> subList(int start, int end);
@Deprecated boolean addAll(int index, Collection<? extends E> c);
@Deprecated E set(int index, E element);
@Deprecated void add(int index, E element);
@Deprecated E remove(int index);
}
@@ -1,53 +1,14 @@
package kotlin.reflect.jvm.internal.pcollections; package kotlin.reflect.jvm.internal.pcollections;
import java.util.Collection;
/** /**
*
* An immutable, persistent stack. * An immutable, persistent stack.
* *
* @author harold * @author harold
*
* @param <E>
*/ */
public interface PStack<E> extends PSequence<E> { public interface PStack<E> extends Iterable<E> {
PStack<E> plus(E e);
/** PStack<E> minus(int i);
* Returns a stack consisting of the elements of this with e prepended.
*/
//@Override
public PStack<E> plus(E e);
/** int size();
* Returns a stack consisting of the elements of this with list prepended in reverse.
*/
//@Override
public PStack<E> plusAll(Collection<? extends E> list);
//@Override
public PStack<E> with(int i, E e);
//@Override
public PStack<E> plus(int i, E e);
//@Override
public PStack<E> plusAll(int i, Collection<? extends E> list);
//@Override
public PStack<E> minus(Object e);
//@Override
public PStack<E> minusAll(Collection<?> list);
//@Override
public PStack<E> minus(int i);
//@Override
public PStack<E> subList(int start, int end);
/**
* @param start
* @return subList(start,this.size())
*/
public PStack<E> subList(int start);
} }
@@ -1,12 +1,9 @@
package kotlin.reflect.jvm.internal.pcollections; package kotlin.reflect.jvm.internal.pcollections;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
/** /**
* (Taken from Java 6 code, so pcollections can be Java 5-compatible.) * <p/>
* <p>
* An Entry maintaining an immutable key and value. This class * An Entry maintaining an immutable key and value. This class
* does not support method <tt>setValue</tt>. This class may be * does not support method <tt>setValue</tt>. This class may be
* convenient in methods that return thread-safe snapshots of * convenient in methods that return thread-safe snapshots of
@@ -14,9 +11,7 @@ import java.util.Map.Entry;
* *
* @since 1.6 * @since 1.6
*/ */
/*public*/ final class SimpleImmutableEntry<K,V> final class SimpleImmutableEntry<K, V> implements Map.Entry<K, V>, java.io.Serializable {
implements Map.Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = 7138329143949025153L; private static final long serialVersionUID = 7138329143949025153L;
private final K key; private final K key;
@@ -34,22 +29,12 @@ import java.util.Map.Entry;
this.value = value; this.value = value;
} }
/**
* Creates an entry representing the same mapping as the
* specified entry.
*
* @param entry the entry to copy
*/
public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.getValue();
}
/** /**
* Returns the key corresponding to this entry. * Returns the key corresponding to this entry.
* *
* @return the key corresponding to this entry * @return the key corresponding to this entry
*/ */
@Override
public K getKey() { public K getKey() {
return key; return key;
} }
@@ -59,6 +44,7 @@ import java.util.Map.Entry;
* *
* @return the value corresponding to this entry * @return the value corresponding to this entry
*/ */
@Override
public V getValue() { public V getValue() {
return value; return value;
} }
@@ -73,6 +59,7 @@ import java.util.Map.Entry;
* @return (Does not return) * @return (Does not return)
* @throws UnsupportedOperationException always * @throws UnsupportedOperationException always
*/ */
@Override
public V setValue(V value) { public V setValue(V value) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }