diff --git a/src/main/java/speiger/src/builder/example/GlobalVariables.java b/src/main/java/speiger/src/builder/example/GlobalVariables.java index 4fe30c25..91f39a5d 100644 --- a/src/main/java/speiger/src/builder/example/GlobalVariables.java +++ b/src/main/java/speiger/src/builder/example/GlobalVariables.java @@ -73,13 +73,16 @@ public class GlobalVariables //Final Classes addClassMapper("ARRAY_LIST", "ArrayList"); + addClassMapper("LINKED_CUSTOM_HASH_SET", "LinkedOpenCustomHashSet"); addClassMapper("LINKED_HASH_SET", "LinkedOpenHashSet"); + addClassMapper("CUSTOM_HASH_SET", "OpenCustomHashSet"); addClassMapper("HASH_SET", "OpenHashSet"); + addClassMapper("ARRAY_SET", "ArraySet"); //Abstract Classes - addClassMapper("ABSTRACT_COLLECTION", "AbstractCollection"); - addClassMapper("ABSTRACT_SET", "AbstractSet"); - addClassMapper("ABSTRACT_LIST", "AbstractList"); + addAbstractMapper("ABSTRACT_COLLECTION", "Abstract%sCollection"); + addAbstractMapper("ABSTRACT_SET", "Abstract%sSet"); + addAbstractMapper("ABSTRACT_LIST", "Abstract%sList"); addClassMapper("SUB_LIST", "SubList"); //Helper Classes @@ -94,9 +97,11 @@ public class GlobalVariables addClassMapper("ITERATOR", "Iterator"); addClassMapper("ITERABLE", "Iterable"); addClassMapper("COLLECTION", "Collection"); + addClassMapper("LIST_ITER", "ListIter"); addClassMapper("LIST", "List"); addClassMapper("SORTED_SET", "SortedSet"); addClassMapper("SET", "Set"); + addClassMapper("STRATEGY", "Strategy"); addClassMapper("STACK", "Stack"); if(type.isObject()) { @@ -111,7 +116,6 @@ public class GlobalVariables addFunctionMappers("IARRAY", "I%sArray"); } //Dependency - addClassMapper("LIST_ITER", "ListIter"); return this; } @@ -169,6 +173,11 @@ public class GlobalVariables operators.add(new SimpleMapper(pattern, type.getFileType()+replacement)); } + private void addAbstractMapper(String pattern, String replacement) + { + operators.add(new SimpleMapper(pattern, String.format(replacement, type.getFileType()))); + } + private void addFunctionMapper(String pattern, String replacement) { operators.add(new SimpleMapper(pattern, replacement+type.getNonFileType())); diff --git a/src/main/java/speiger/src/builder/example/TestBuilder.java b/src/main/java/speiger/src/builder/example/TestBuilder.java index fcae1e0a..88dbb265 100644 --- a/src/main/java/speiger/src/builder/example/TestBuilder.java +++ b/src/main/java/speiger/src/builder/example/TestBuilder.java @@ -53,6 +53,9 @@ public class TestBuilder extends TemplateProcessor varibles.add(type); } nameRemapper.put("IArray", "I%sArray"); + nameRemapper.put("AbstractCollection", "Abstract%sCollection"); + nameRemapper.put("AbstractSet", "Abstract%sSet"); + nameRemapper.put("AbstractList", "Abstract%sList"); blocked.put("Consumer", EnumSet.of(ClassType.OBJECT)); blocked.put("Comparator", EnumSet.of(ClassType.OBJECT)); blocked.put("Stack", EnumSet.of(ClassType.OBJECT)); diff --git a/src/main/resources/speiger/assets/collections/templates/lists/AbstractList.template b/src/main/resources/speiger/assets/collections/templates/lists/AbstractList.template index 35319475..dce61f00 100644 --- a/src/main/resources/speiger/assets/collections/templates/lists/AbstractList.template +++ b/src/main/resources/speiger/assets/collections/templates/lists/AbstractList.template @@ -366,6 +366,7 @@ public abstract class ABSTRACT_LIST KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION @Override public KEY_TYPE NEXT() { + lastReturned = index; return GET_KEY(index++); } @@ -376,6 +377,7 @@ public abstract class ABSTRACT_LIST KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION @Override public KEY_TYPE PREVIOUS() { + lastReturned = index; return GET_KEY(index--); } diff --git a/src/main/resources/speiger/assets/collections/templates/sets/ArraySet.template b/src/main/resources/speiger/assets/collections/templates/sets/ArraySet.template new file mode 100644 index 00000000..7501d1eb --- /dev/null +++ b/src/main/resources/speiger/assets/collections/templates/sets/ArraySet.template @@ -0,0 +1,567 @@ +package speiger.src.collections.PACKAGE.sets; + +import java.util.Arrays; +import java.util.Collection; +#if TYPE_OBJECT +import java.util.Comparator; +import java.util.NoSuchElementException; +import java.util.Objects; +#else +import java.util.NoSuchElementException; +#endif +import java.util.Set; + +import speiger.src.collections.PACKAGE.collections.BI_ITERATOR; +import speiger.src.collections.PACKAGE.collections.COLLECTION; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.functions.COMPARATOR; +#endif +import speiger.src.collections.PACKAGE.lists.LIST_ITERATOR; +import speiger.src.collections.PACKAGE.utils.ARRAYS; + +public class ARRAY_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE implements SORTED_SET KEY_GENERIC_TYPE +{ + protected transient KEY_TYPE[] data; + protected int size = 0; + + public ARRAY_SET() { +#if TYPE_OBJECT + data = (KEY_TYPE[])ARRAYS.EMPTY_ARRAY; +#else + data = ARRAYS.EMPTY_ARRAY; +#endif + } + + public ARRAY_SET(int capacity) { +#if TYPE_OBJECT + data = (KEY_TYPE[])new Object[capacity]; +#else + data = new KEY_TYPE[capacity]; +#endif + } + + public ARRAY_SET(KEY_TYPE[] array) { + this(array, array.length); + } + + public ARRAY_SET(KEY_TYPE[] array, int length) { + data = array; + size = length; + } + + @Deprecated + public ARRAY_SET(Collection c) { + this(c.size()); + addAll(c); + } + + public ARRAY_SET(COLLECTION KEY_GENERIC_TYPE c) { + this(c.size()); + addAll(c); + } + + @Deprecated + public ARRAY_SET(Set s) { + this(s.size()); + for(CLASS_TYPE e : s) + data[size++] = OBJ_TO_KEY(e); + } + + public ARRAY_SET(SET KEY_GENERIC_TYPE s) { + this(s.size()); + for(KEY_TYPE e : s) + data[size++] = e; + } + + @Override + public boolean add(KEY_TYPE o) { + int index = findIndex(o); + if(index == -1) { + if(data.length == size) data = Arrays.copyOf(data, size == 0 ? 2 : size * 2); + data[size++] = o; + return true; + } + return false; + } + + @Override + public boolean addAndMoveToFirst(KEY_TYPE o) { + int index = findIndex(o); + if(index == -1) { + if(data.length == size) data = Arrays.copyOf(data, size == 0 ? 2 : size * 2); + System.arraycopy(data, 0, data, 1, size++); + data[0] = o; + return true; + } + else if(index != 0) { + o = data[index]; + System.arraycopy(data, 0, data, 1, index); + data[index] = o; + } + return false; + } + + @Override + public boolean addAndMoveToLast(KEY_TYPE o) { + int index = findIndex(o); + if(index == -1) { + if(data.length == size) data = Arrays.copyOf(data, size == 0 ? 2 : size * 2); + data[size++] = o; + return true; + } + else if(index != size - 1) { + o = data[index]; + System.arraycopy(data, index+1, data, index, size - index); + data[size] = o; + } + return false; + } + + @Override + public boolean moveToFirst(KEY_TYPE o) { + int index = findIndex(o); + if(index > 0) { + o = data[index]; + System.arraycopy(data, 0, data, 1, index); + data[index] = o; + return true; + } + return false; + } + + @Override + public boolean moveToLast(KEY_TYPE o) { + int index = findIndex(o); + if(index != -1 && index != size - 1) { + o = data[index]; + System.arraycopy(data, index+1, data, index, size - index); + data[size] = o; + return true; + } + return false; + } + +#if !TYPE_OBJECT + @Override + public boolean contains(KEY_TYPE e) { + return findIndex(e) != -1; + } + +#else + @Override + public boolean contains(Object e) { + return findIndex((KEY_TYPE)e) != -1; + } + +#endif + @Override + public KEY_TYPE FIRST_KEY() { + if(size == 0) throw new NoSuchElementException(); + return data[0]; + } + + @Override + public KEY_TYPE LAST_KEY() { + if(size == 0) throw new NoSuchElementException(); + return data[size - 1]; + } + +#if !TYPE_OBJECT + @Override + public boolean remove(KEY_TYPE o) { + int index = findIndex(o); + if(index != -1) { + System.arraycopy(data, index+1, data, index, size - index); + return true; + } + return false; + } + +#else + @Override + public boolean remove(Object o) { + int index = findIndex((KEY_TYPE)o); + if(index != -1) { + System.arraycopy(data, index+1, data, index, size - index); + data[size-1] = EMPTY_VALUE; + return true; + } + return false; + } + +#endif + @Override + public KEY_TYPE POLL_FIRST_KEY() { + if(size == 0) throw new NoSuchElementException(); + KEY_TYPE result = data[0]; + System.arraycopy(data, 1, data, 0, size); +#if TYPE_OBJECT + data[size-1] = EMPTY_VALUE; +#endif + return result; + } + + @Override + public KEY_TYPE POLL_LAST_KEY() { + if(size == 0) throw new NoSuchElementException(); +#if TYPE_OBJECT + KEY_TYPE result = data[size-1]; + data[size-1] = EMPTY_VALUE; + return result; +#else + return data[--size]; +#endif + } + + protected int findIndex(KEY_TYPE o) { + for(int i = size-1;i>=0;i--) + if(EQUALS(data[i], o)) return i; + return -1; + } + + @Override + public BI_ITERATOR KEY_GENERIC_TYPE iterator() { + return new SetIterator(0); + } + + @Override + public BI_ITERATOR KEY_GENERIC_TYPE iterator(KEY_TYPE fromElement) { + int index = findIndex(fromElement); + if(index != -1) return new SetIterator(index); + throw new NoSuchElementException(); + } + + @Override + public SORTED_SET KEY_GENERIC_TYPE subSet(KEY_TYPE fromElement, KEY_TYPE toElement) { + int fromIndex = findIndex(fromElement); + int toIndex = findIndex(toElement); + if(fromIndex == -1 || toIndex == -1) throw new NoSuchElementException(); + return new SubSet(fromIndex, toIndex - fromIndex); + } + + @Override + public SORTED_SET KEY_GENERIC_TYPE headSet(KEY_TYPE toElement) { + int toIndex = findIndex(toElement); + if(toIndex == -1) throw new NoSuchElementException(); + return new SubSet(0, toIndex); + } + + @Override + public SORTED_SET KEY_GENERIC_TYPE tailSet(KEY_TYPE fromElement) { + int fromIndex = findIndex(fromElement); + if(fromIndex == -1) throw new NoSuchElementException(); + return new SubSet(fromIndex, size - fromIndex); + } + + @Override + public COMPARATOR KEY_GENERIC_TYPE comparator() { + return null; + } + + @Override + public void clear() { + size = 0; + } + + @Override + public int size() { + return size; + } + + private class SubSet extends ABSTRACT_SET KEY_GENERIC_TYPE implements SORTED_SET KEY_GENERIC_TYPE { + int offset; + int length; + + SubSet(int offset, int length) { + this.offset = offset; + this.length = length; + } + + @Override + public boolean add(KEY_TYPE o) { + int index = findIndex(o); + if(index != -1) { + if(data.length == size) data = Arrays.copyOf(data, size == 0 ? 2 : size * 2); + System.arraycopy(data, (offset+length), data, (offset+length)+1, size-(offset+length)); + data[offset+length] = o; + size++; + length++; + return true; + } + return false; + } + + @Override + public boolean addAndMoveToFirst(KEY_TYPE o) { + int index = findIndex(o); + if(index == -1) { + if(data.length == size) data = Arrays.copyOf(data, size == 0 ? 2 : size * 2); + System.arraycopy(data, offset, data, offset+1, size-offset); + data[offset] = o; + size++; + length++; + return true; + } + else if(index != 0) { + o = data[index]; + System.arraycopy(data, offset, data, offset+1, index-offset); + data[offset] = o; + } + return false; + } + + @Override + public boolean addAndMoveToLast(KEY_TYPE o) { + int index = findIndex(o); + if(index == -1) { + if(data.length == size) data = Arrays.copyOf(data, size == 0 ? 2 : size * 2); + System.arraycopy(data, (offset+length)+1, data, (offset+length), size-(offset+length)); + data[offset+length] = o; + size++; + length++; + return true; + } + else if(index != 0) { + o = data[index]; + System.arraycopy(data, offset+1, data, offset, index-offset); + data[offset+length] = o; + } + return false; + } + + @Override + public boolean moveToFirst(KEY_TYPE o) { + int index = findIndex(o); + if(index != -1 && index != offset) { + o = data[index]; + System.arraycopy(data, offset, data, offset+1, index-offset); + data[offset] = o; + return true; + } + return false; + } + + @Override + public boolean moveToLast(KEY_TYPE o) { + int index = findIndex(o); + if(index != -1 && index != (offset+length) - 1) { + o = data[index]; + System.arraycopy(data, offset+1, data, offset, index-offset); + data[offset+length] = o; + return true; + } + return false; + } + +#if !TYPE_OBJECT + @Override + public boolean contains(KEY_TYPE e) { + return findIndex(e) != -1; + } + +#else + @Override + public boolean contains(Object e) { + return findIndex((KEY_TYPE)e) != -1; + } + +#endif + @Override + public KEY_TYPE FIRST_KEY() { + if(length == 0) throw new NoSuchElementException(); + return data[offset]; + } + + @Override + public KEY_TYPE LAST_KEY() { + if(length == 0) throw new NoSuchElementException(); + return data[(offset + length) - 1]; + } + +#if !TYPE_OBJECT + @Override + public boolean remove(KEY_TYPE o) { + int index = findIndex(o); + if(index != -1) { + System.arraycopy(data, index+1, data, index, size - index); + size--; + length--; + return true; + } + return false; + } + +#else + @Override + public boolean remove(Object o) { + int index = findIndex((KEY_TYPE)o); + if(index != -1) { + System.arraycopy(data, index+1, data, index, size - index); + data[size-1] = EMPTY_VALUE; + size--; + length--; + return true; + } + return false; + } + +#endif + @Override + public KEY_TYPE POLL_FIRST_KEY() { + if(length == 0) throw new NoSuchElementException(); + KEY_TYPE result = data[offset]; + System.arraycopy(data, offset+1, data, offset, size-offset); +#if TYPE_OBJECT + data[size-1] = EMPTY_VALUE; +#endif + size--; + length--; + return result; + } + + @Override + public KEY_TYPE POLL_LAST_KEY() { + if(length == 0) throw new NoSuchElementException(); + KEY_TYPE result = data[offset+length]; + System.arraycopy(data, (offset+length)+1, data, (offset+length), size-(offset+length)); +#if TYPE_OBJECT + data[size-1] = EMPTY_VALUE; +#endif + size--; + length--; + return result; + } + + @Override + public COMPARATOR KEY_GENERIC_TYPE comparator() { + return null; + } + + @Override + public BI_ITERATOR KEY_GENERIC_TYPE iterator() { + return new SetIterator(offset); + } + + @Override + public BI_ITERATOR KEY_GENERIC_TYPE iterator(KEY_TYPE fromElement) { + int index = findIndex(fromElement); + if(index != -1) return new SetIterator(index); + throw new NoSuchElementException(); + } + + @Override + public SORTED_SET KEY_GENERIC_TYPE subSet(KEY_TYPE fromElement, KEY_TYPE toElement) { + int fromIndex = findIndex(fromElement); + int toIndex = findIndex(toElement); + if(fromIndex == -1 || toIndex == -1) throw new NoSuchElementException(); + return new SubSet(fromIndex, toIndex - fromIndex); + } + + @Override + public SORTED_SET KEY_GENERIC_TYPE headSet(KEY_TYPE toElement) { + int toIndex = findIndex(toElement); + if(toIndex == -1) throw new NoSuchElementException(); + return new SubSet(0, toIndex); + } + + @Override + public SORTED_SET KEY_GENERIC_TYPE tailSet(KEY_TYPE fromElement) { + int fromIndex = findIndex(fromElement); + if(fromIndex == -1) throw new NoSuchElementException(); + return new SubSet(fromIndex, size - fromIndex); + } + + @Override + public int size() { + return length; + } + + private int findIndex(KEY_TYPE o) { + for(int i = length-1;i>=0;i--) + if(data[offset+i] == o) return i + offset; + return -1; + } + } + + private class SetIterator implements LIST_ITERATOR KEY_GENERIC_TYPE { + int index; + int lastReturned = -1; + + public SetIterator(int index) { + this.index = index; + } + + @Override + public boolean hasNext() { + return index < size(); + } + + @Override + public KEY_TYPE NEXT() { + lastReturned = index; + return data[index++]; + } + + @Override + public boolean hasPrevious() { + return index > 0; + } + + @Override + public KEY_TYPE PREVIOUS() { + lastReturned = index; + return data[index--]; + } + + @Override + public int nextIndex() { + return index; + } + + @Override + public int previousIndex() { + return index-1; + } + + @Override + public void remove() { + if(lastReturned == -1) + throw new IllegalStateException(); + ARRAY_SET.this.remove(data[lastReturned]); + if(lastReturned < index) + index--; + lastReturned = -1; + } + +#if TYPE_OBJECT + @Override + public void set(Object e) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object e) { throw new UnsupportedOperationException(); } + +#else + @Override + public void set(KEY_TYPE e) { throw new UnsupportedOperationException(); } + + @Override + public void add(KEY_TYPE e) { throw new UnsupportedOperationException(); } + +#endif + @Override + public int skip(int amount) { + if(amount < 0) throw new IllegalStateException("Negative Numbers are not allowed"); + int steps = Math.min(amount, (size() - 1) - index); + index += steps; + return steps; + } + + @Override + public int back(int amount) { + if(amount < 0) throw new IllegalStateException("Negative Numbers are not allowed"); + int steps = Math.min(amount, index); + index -= steps; + return steps; + } + } +} diff --git a/src/main/resources/speiger/assets/collections/templates/sets/LinkedOpenCustomHashSet.template b/src/main/resources/speiger/assets/collections/templates/sets/LinkedOpenCustomHashSet.template new file mode 100644 index 00000000..8381f0e3 --- /dev/null +++ b/src/main/resources/speiger/assets/collections/templates/sets/LinkedOpenCustomHashSet.template @@ -0,0 +1,560 @@ +package speiger.src.collections.PACKAGE.sets; + +#if TYPE_OBJECT +import java.util.Comparator; +#endif +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import speiger.src.collections.PACKAGE.collections.COLLECTION; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.collections.ITERATOR; +#endif +import speiger.src.collections.PACKAGE.collections.BI_ITERATOR; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.functions.COMPARATOR; +#endif +import speiger.src.collections.PACKAGE.lists.LIST_ITERATOR; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.utils.ITERATORS; +#endif +import speiger.src.collections.PACKAGE.utils.STRATEGY; +import speiger.src.collections.utils.HashUtil; +import speiger.src.collections.utils.SanityChecks; + +public class LINKED_CUSTOM_HASH_SET KEY_GENERIC_TYPE extends CUSTOM_HASH_SET KEY_GENERIC_TYPE implements SORTED_SET KEY_GENERIC_TYPE +{ + protected long[] links; + protected int firstIndex = -1; + protected int lastIndex = -1; + + public LINKED_CUSTOM_HASH_SET(STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(HashUtil.DEFAULT_MIN_CAPACITY, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + public LINKED_CUSTOM_HASH_SET(int minCapacity, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + public LINKED_CUSTOM_HASH_SET(int minCapacity, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + super(minCapacity, loadFactor, strategy); + links = new long[nullIndex + 1]; + } + + public LINKED_CUSTOM_HASH_SET(KEY_TYPE[] array, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(array, 0, array.length, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + public LINKED_CUSTOM_HASH_SET(KEY_TYPE[] array, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(array, 0, array.length, loadFactor, strategy); + } + + public LINKED_CUSTOM_HASH_SET(KEY_TYPE[] array, int offset, int length, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(array, offset, length, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + public LINKED_CUSTOM_HASH_SET(KEY_TYPE[] array, int offset, int length, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(length < 0 ? 0 : length, strategy); + SanityChecks.checkArrayCapacity(array.length, offset, length); + for(int i = 0;i collection, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + @Deprecated + public LINKED_CUSTOM_HASH_SET(Collection collection, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection.size(), loadFactor, strategy); + addAll(collection); + } + + public LINKED_CUSTOM_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + public LINKED_CUSTOM_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection.size(), strategy); + addAll(collection); + } + + public LINKED_CUSTOM_HASH_SET(Iterator iterator, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(iterator, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + public LINKED_CUSTOM_HASH_SET(Iterator iterator, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { +#if !TYPE_OBJECT + this(ITERATORS.wrap(iterator), loadFactor, strategy); +#else + this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor, strategy); + while(iterator.hasNext()) add(iterator.next()); +#endif + } + +#if !TYPE_OBJECT + public LINKED_CUSTOM_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(iterator, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + public LINKED_CUSTOM_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor, strategy); + while(iterator.hasNext()) add(iterator.NEXT()); + } + +#endif + @Override + public boolean addAndMoveToFirst(KEY_TYPE o) { + if(strategy.equals(o, EMPTY_VALUE)) { + if(containsNull) { + moveToFirstIndex(nullIndex); + return false; + } + containsNull = true; + onNodeAdded(nullIndex); + } + else { + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + while(!strategy.equals(keys[pos], EMPTY_VALUE)) { + if(strategy.equals(keys[pos], o)) { + moveToFirstIndex(pos); + return false; + } + pos = ++pos & mask; + } + keys[pos] = o; + onNodeAdded(pos); + } + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return true; + } + + @Override + public boolean addAndMoveToLast(KEY_TYPE o) { + if(strategy.equals(o, EMPTY_VALUE)) { + if(containsNull) { + moveToLastIndex(nullIndex); + return false; + } + containsNull = true; + onNodeAdded(nullIndex); + } + else { + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + while(!strategy.equals(keys[pos], EMPTY_VALUE)) { + if(strategy.equals(keys[pos], o)) { + moveToLastIndex(pos); + return false; + } + pos = ++pos & mask; + } + keys[pos] = o; + onNodeAdded(pos); + } + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return true; + } + + @Override + public boolean moveToFirst(KEY_TYPE o) { + if(strategy.equals(o, EMPTY_VALUE)) { + if(containsNull) { + moveToFirstIndex(nullIndex); + return true; + } + } + else { + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + while(!strategy.equals(keys[pos], EMPTY_VALUE)) { + if(strategy.equals(keys[pos], o)) { + moveToFirstIndex(pos); + return true; + } + pos = ++pos & mask; + } + keys[pos] = o; + } + return false; + } + + @Override + public boolean moveToLast(KEY_TYPE o) { + if(strategy.equals(o, EMPTY_VALUE)) { + if(containsNull) { + moveToLastIndex(nullIndex); + return true; + } + } + else { + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + while(!strategy.equals(keys[pos], EMPTY_VALUE)) { + if(strategy.equals(keys[pos], o)) { + moveToLastIndex(pos); + return true; + } + pos = ++pos & mask; + } + keys[pos] = o; + } + return false; + } + + protected void moveToFirstIndex(int startPos) { + if(size == 1 || firstIndex == startPos) return; + if(lastIndex == startPos) { + lastIndex = (int)(links[startPos] >>> 32); + links[lastIndex] |= 0xFFFFFFFFL; + } + else { + long link = links[startPos]; + int prev = (int)(link >>> 32); + int next = (int)link; + links[prev] ^= ((links[prev] ^ (link & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + links[next] ^= ((links[next] ^ (link & 0xFFFFFFFF00000000L)) & 0xFFFFFFFF00000000L); + } + links[firstIndex] ^= ((links[firstIndex] ^ ((startPos & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); + links[startPos] = 0xFFFFFFFF00000000L | (firstIndex & 0xFFFFFFFFL); + firstIndex = startPos; + } + + protected void moveToLastIndex(int startPos) { + if(size == 1 || lastIndex == startPos) return; + if(firstIndex == startPos) { + firstIndex = (int)links[startPos]; + links[lastIndex] |= 0xFFFFFFFF00000000L; + } + else { + long link = links[startPos]; + int prev = (int)(link >>> 32); + int next = (int)link; + links[prev] ^= ((links[prev] ^ (link & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + links[next] ^= ((links[next] ^ (link & 0xFFFFFFFF00000000L)) & 0xFFFFFFFF00000000L); + } + links[lastIndex] ^= ((links[lastIndex] ^ (startPos & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + links[startPos] = ((lastIndex & 0xFFFFFFFFL) << 32) | 0xFFFFFFFFL; + lastIndex = startPos; + } + + @Override + public KEY_TYPE FIRST_KEY() { + if(size == 0) throw new NoSuchElementException(); + return keys[firstIndex]; + } + + @Override + public KEY_TYPE POLL_FIRST_KEY() { + if(size == 0) throw new NoSuchElementException(); + int pos = firstIndex; + firstIndex = (int)links[pos]; + if(0 <= firstIndex) links[firstIndex] |= 0xFFFFFFFF00000000L; + KEY_TYPE result = keys[pos]; + size--; + if(strategy.equals(result, EMPTY_VALUE)) { + containsNull = false; + keys[nullIndex] = EMPTY_VALUE; + } + else shiftKeys(pos); + if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); + return result; + } + + @Override + public KEY_TYPE LAST_KEY() { + if(size == 0) throw new NoSuchElementException(); + return keys[lastIndex]; + } + + @Override + public KEY_TYPE POLL_LAST_KEY() { + if(size == 0) throw new NoSuchElementException(); + int pos = lastIndex; + lastIndex = (int)(links[pos] >>> 32); + if(0 <= lastIndex) links[lastIndex] |= 0xFFFFFFFFL; + KEY_TYPE result = keys[pos]; + size--; + if(strategy.equals(result, EMPTY_VALUE)) { + containsNull = false; + keys[nullIndex] = EMPTY_VALUE; + } + else shiftKeys(pos); + if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); + return result; + } + + @Override + protected void onNodeAdded(int pos) { + if(size == 0) { + firstIndex = lastIndex = pos; + links[pos] = -1L; + } + else { + links[lastIndex] ^= ((links[lastIndex] ^ (pos & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + links[pos] = ((lastIndex & 0xFFFFFFFFL) << 32) | (-1 & 0xFFFFFFFFL); + lastIndex = pos; + } + } + + @Override + protected void onNodeRemoved(int pos) { + if(size == 0) firstIndex = lastIndex = 0; + else if(firstIndex == pos) { + firstIndex = (int)links[pos]; + if(0 <= firstIndex) links[firstIndex] |= 0xFFFFFFFF00000000L; + } + else if(lastIndex == pos) { + lastIndex = (int)(links[pos] >>> 32); + if(0 <= lastIndex) links[pos] |= 0xFFFFFFFFL; + } + else { + long link = links[pos]; + int prev = (int)(link >>> 32); + int next = (int)link; + links[prev] ^= ((links[prev] ^ (link & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + links[next] ^= ((links[next] ^ (link & 0xFFFFFFFF00000000L)) & 0xFFFFFFFF00000000L); + } + } + + @Override + protected void onNodeMoved(int from, int to) { + if(size == 1) { + firstIndex = lastIndex = to; + links[to] = -1L; + } + else if(firstIndex == from) { + firstIndex = to; + links[(int)links[from]] ^= ((links[(int)links[from]] ^ ((to & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); + links[to] = links[from]; + } + else if(lastIndex == from) { + lastIndex = to; + links[(int)(links[from] >>> 32)] ^= ((links[(int)(links[from] >>> 32)] ^ (to & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + links[to] = links[from]; + } + else { + long link = links[from]; + int prev = (int)(link >>> 32); + int next = (int)link; + links[prev] ^= ((links[prev] ^ (to & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + links[next] ^= ((links[next] ^ ((to & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); + links[to] = link; + } + } + + @Override + protected void rehash(int newSize) { + int newMask = newSize - 1; +#if TYPE_OBJECT + KEY_TYPE[] newKeys = (KEY_TYPE[])new Object[newSize + 1]; +#else + KEY_TYPE[] newKeys = new KEY_TYPE[newSize + 1]; +#endif + long[] newLinks = new long[newSize + 1]; + int newPrev = -1; + for(int j = size, i = firstIndex, pos = 0, prev = -1;j != 0;) { + if(strategy.equals(keys[i], EMPTY_VALUE)) pos = newSize; + else { + pos = HashUtil.mix(strategy.hashCode(keys[i])) & newMask; + while(!strategy.equals(newKeys[pos], EMPTY_VALUE)) pos = ++pos & newMask; + } + newKeys[pos] = keys[i]; + if(prev != -1) { + newLinks[newPrev] ^= ((newLinks[newPrev] ^ (pos & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + newLinks[pos] ^= ((newLinks[pos] ^ ((newPrev & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); + newPrev = pos; + } + else { + newPrev = firstIndex = pos; + newLinks[pos] = -1L; + } + i = (int)links[prev = i]; + } + links = newLinks; + lastIndex = newPrev; + if(newPrev != -1) newLinks[newPrev] |= 0xFFFFFFFFL; + nullIndex = newSize; + mask = newMask; + maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); + keys = newKeys; + } + + @Override + public void clear() { + super.clear(); + firstIndex = lastIndex = -1; + } + + @Override + public LIST_ITERATOR KEY_GENERIC_TYPE iterator() { + return new SetIterator(); + } + + @Override + public BI_ITERATOR KEY_GENERIC_TYPE iterator(KEY_TYPE fromElement) { + return new SetIterator(fromElement); + } + + @Override + public COMPARATOR KEY_GENERIC_TYPE comparator() { return null; } + + @Override + public SORTED_SET KEY_GENERIC_TYPE subSet(KEY_TYPE fromElement, KEY_TYPE toElement) { throw new UnsupportedOperationException(); } + + @Override + public SORTED_SET KEY_GENERIC_TYPE headSet(KEY_TYPE toElement) { throw new UnsupportedOperationException(); } + + @Override + public SORTED_SET KEY_GENERIC_TYPE tailSet(KEY_TYPE fromElement) { throw new UnsupportedOperationException(); } + + private class SetIterator implements LIST_ITERATOR KEY_GENERIC_TYPE { + int previous = -1; + int next = -1; + int current = -1; + int index = 0; + + SetIterator() { + next = firstIndex; + } + + SetIterator(KEY_TYPE from) { + if(strategy.equals(from, EMPTY_VALUE)) { + if(containsNull) { + next = (int) links[nullIndex]; + previous = nullIndex; + } + else throw new NoSuchElementException("The null element is not in the set"); + } + else if(strategy.equals(keys[lastIndex], from)) { + previous = lastIndex; + index = size; + } + else { + int pos = HashUtil.mix(strategy.hashCode(from)) & mask; + while(!strategy.equals(keys[pos], EMPTY_VALUE)) { + if(strategy.equals(keys[pos], from)) { + next = (int)links[pos]; + previous = pos; + break; + } + pos = ++pos & mask; + } + if(previous == -1 && next == -1) + throw new NoSuchElementException("The element was not found"); + } + } + + @Override + public boolean hasNext() { + return next != -1; + } + + @Override + public boolean hasPrevious() { + return previous != -1; + } + + @Override + public int nextIndex() { + ensureIndexKnown(); + return index; + } + + @Override + public int previousIndex() { + ensureIndexKnown(); + return index - 1; + } + + @Override + public void remove() { + if(current == -1) throw new IllegalStateException(); + ensureIndexKnown(); + if(current == previous) { + index--; + previous = (int)(links[current] >> 32); + } + else next = (int)links[current]; + size--; + if(previous == -1) firstIndex = next; + else links[previous] ^= ((links[previous] ^ (next & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + + if (next == -1) lastIndex = previous; + else links[next] ^= ((links[next] ^ ((previous & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); + if(current == nullIndex) { + current = -1; + containsNull = false; + keys[nullIndex] = EMPTY_VALUE; + } + else { + int slot, last, startPos = current; + current = -1; + KEY_TYPE current; + while(true) { + last = ((last = startPos) + 1) & mask; + while(true){ + if(strategy.equals((current = keys[startPos]), EMPTY_VALUE)) { + keys[last] = EMPTY_VALUE; + return; + } + slot = HashUtil.mix(strategy.hashCode(current)) & mask; + if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; + startPos = ++startPos & mask; + } + keys[last] = current; + if(next == startPos) next = last; + if(previous == startPos) previous = last; + onNodeMoved(startPos, last); + } + } + } + + @Override + public KEY_TYPE PREVIOUS() { + if(!hasPrevious()) throw new NoSuchElementException(); + current = previous; + previous = (int)(links[current] >> 32); + next = current; + if(index >= 0) index--; + return keys[current]; + } + + @Override + public KEY_TYPE NEXT() { + if(!hasPrevious()) throw new NoSuchElementException(); + current = next; + next = (int)(links[current]); + previous = current; + if(index >= 0) index++; + return keys[current]; + } + + private void ensureIndexKnown() { + if(index == -1) { + if(previous == -1) { + index = 0; + } + else if(next == -1) { + index = size; + } + else { + index = 1; + for(int pos = firstIndex;pos != previous;pos = (int)links[pos], index++); + } + } + } + +#if TYPE_OBJECT + @Override + public void set(Object e) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object e) { throw new UnsupportedOperationException(); } +#else + @Override + public void set(KEY_TYPE e) { throw new UnsupportedOperationException(); } + + @Override + public void add(KEY_TYPE e) { throw new UnsupportedOperationException(); } +#endif + } +} diff --git a/src/main/resources/speiger/assets/collections/templates/sets/LinkedOpenHashSet.template b/src/main/resources/speiger/assets/collections/templates/sets/LinkedOpenHashSet.template index cac5381e..e212a4a8 100644 --- a/src/main/resources/speiger/assets/collections/templates/sets/LinkedOpenHashSet.template +++ b/src/main/resources/speiger/assets/collections/templates/sets/LinkedOpenHashSet.template @@ -210,13 +210,13 @@ public class LINKED_HASH_SET KEY_GENERIC_TYPE extends HASH_SET KEY_GENERIC_TYPE } else { long link = links[startPos]; - int prev = (int) ( link >>> 32 ); - int next = (int) link; + int prev = (int)(link >>> 32); + int next = (int)link; links[prev] ^= ((links[prev] ^ (link & 0xFFFFFFFFL)) & 0xFFFFFFFFL); links[next] ^= ((links[next] ^ (link & 0xFFFFFFFF00000000L)) & 0xFFFFFFFF00000000L); } links[firstIndex] ^= ((links[firstIndex] ^ ((startPos & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); - links[startPos] = ((-1 & 0xFFFFFFFFL) << 32) | (firstIndex & 0xFFFFFFFFL); + links[startPos] = 0xFFFFFFFF00000000L | (firstIndex & 0xFFFFFFFFL); firstIndex = startPos; } @@ -234,7 +234,7 @@ public class LINKED_HASH_SET KEY_GENERIC_TYPE extends HASH_SET KEY_GENERIC_TYPE links[next] ^= ((links[next] ^ (link & 0xFFFFFFFF00000000L)) & 0xFFFFFFFF00000000L); } links[lastIndex] ^= ((links[lastIndex] ^ (startPos & 0xFFFFFFFFL)) & 0xFFFFFFFFL); - links[startPos] = ((lastIndex & 0xFFFFFFFFL) << 32) | (-1 & 0xFFFFFFFFL); + links[startPos] = ((lastIndex & 0xFFFFFFFFL) << 32) | 0xFFFFFFFFL; lastIndex = startPos; } diff --git a/src/main/resources/speiger/assets/collections/templates/sets/OpenCustomHashSet.template b/src/main/resources/speiger/assets/collections/templates/sets/OpenCustomHashSet.template new file mode 100644 index 00000000..ad15878b --- /dev/null +++ b/src/main/resources/speiger/assets/collections/templates/sets/OpenCustomHashSet.template @@ -0,0 +1,384 @@ +package speiger.src.collections.PACKAGE.sets; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import speiger.src.collections.PACKAGE.collections.COLLECTION; +import speiger.src.collections.PACKAGE.collections.ITERATOR; +import speiger.src.collections.PACKAGE.lists.ARRAY_LIST; +import speiger.src.collections.PACKAGE.lists.LIST; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.utils.ITERATORS; +#endif +import speiger.src.collections.PACKAGE.utils.STRATEGY; + +import speiger.src.collections.utils.HashUtil; +import speiger.src.collections.utils.ITrimmable; +import speiger.src.collections.utils.SanityChecks; + +public class CUSTOM_HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE implements ITrimmable +{ + protected transient KEY_TYPE[] keys; + protected transient boolean containsNull; + protected transient int minCapacity; + protected transient int nullIndex; + protected transient int maxFill; + protected transient int mask; + + protected int size; + protected final float loadFactor; + protected final STRATEGY KEY_SUPER_GENERIC_TYPE strategy; + + public CUSTOM_HASH_SET(STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(HashUtil.DEFAULT_MIN_CAPACITY, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + public CUSTOM_HASH_SET(int minCapacity, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + public CUSTOM_HASH_SET(int minCapacity, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + if(minCapacity < 0) throw new IllegalStateException("Minimum Capacity is negative. This is not allowed"); + if(loadFactor <= 0 || loadFactor >= 1F) throw new IllegalStateException("Load Factor is not between 0 and 1"); + this.loadFactor = loadFactor; + this.minCapacity = nullIndex = HashUtil.arraySize(minCapacity, loadFactor); + mask = nullIndex - 1; + maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); +#if TYPE_OBJECT + keys = (KEY_TYPE[])new Object[nullIndex + 1]; +#else + keys = new KEY_TYPE[nullIndex + 1]; +#endif + this.strategy = strategy; + } + + public CUSTOM_HASH_SET(KEY_TYPE[] array, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(array, 0, array.length, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + public CUSTOM_HASH_SET(KEY_TYPE[] array, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(array, 0, array.length, loadFactor, strategy); + } + + public CUSTOM_HASH_SET(KEY_TYPE[] array, int offset, int length, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(array, offset, length, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + public CUSTOM_HASH_SET(KEY_TYPE[] array, int offset, int length, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(length < 0 ? 0 : length, strategy); + SanityChecks.checkArrayCapacity(array.length, offset, length); + for(int i = 0;i collection, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + @Deprecated + public CUSTOM_HASH_SET(Collection collection, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection.size(), loadFactor, strategy); + addAll(collection); + } + + public CUSTOM_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + public CUSTOM_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection.size(), strategy); + addAll(collection); + } + + public CUSTOM_HASH_SET(Iterator iterator, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(iterator, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + public CUSTOM_HASH_SET(Iterator iterator, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { +#if !TYPE_OBJECT + this(ITERATORS.wrap(iterator), loadFactor, strategy); +#else + this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor, strategy); + while(iterator.hasNext()) add(iterator.next()); +#endif + } + +#if !TYPE_OBJECT + public CUSTOM_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(iterator, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + public CUSTOM_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor, strategy); + while(iterator.hasNext()) add(iterator.NEXT()); + } + +#endif + public STRATEGY KEY_SUPER_GENERIC_TYPE getStrategy() { + return strategy; + } + + @Override + public boolean add(KEY_TYPE o) { + if(strategy.equals(o, EMPTY_VALUE)) { + if(containsNull) return false; + containsNull = true; + onNodeAdded(nullIndex); + } + else { + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + KEY_TYPE current = keys[pos]; + if(!strategy.equals(current, EMPTY_VALUE)) { + if(strategy.equals(current, o)) return false; + while(!strategy.equals((current = keys[++pos & mask]), EMPTY_VALUE)) + if(strategy.equals(current, o)) return false; + } + keys[pos] = o; + onNodeAdded(pos); + } + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return true; + } + + @Override + @Deprecated + public boolean addAll(Collection c) { + if(loadFactor <= 0.5F) ensureCapacity(c.size()); + else ensureCapacity(c.size() + size()); + return super.addAll(c); + } + + @Override + public boolean addAll(COLLECTION KEY_GENERIC_TYPE c) { + if(loadFactor <= 0.5F) ensureCapacity(c.size()); + else ensureCapacity(c.size() + size()); + return super.addAll(c); + } + +#if TYPE_OBJECT + @Override + public boolean contains(Object o) { + if(strategy.equals((KEY_TYPE)o, EMPTY_VALUE)) return containsNull; + int pos = HashUtil.mix(strategy.hashCode((KEY_TYPE)o)) & mask; + KEY_TYPE current = keys[pos]; + if(!strategy.equals(current, EMPTY_VALUE)) return false; + if(strategy.equals(current, (KEY_TYPE)o)) return true; + while(true) { + if(strategy.equals((current = keys[++pos & mask]), EMPTY_VALUE)) return false; + else if(strategy.equals(current, (KEY_TYPE)o)) return true; + } + } + + @Override + public boolean remove(Object o) { + if(strategy.equals((KEY_TYPE)o, EMPTY_VALUE)) return (containsNull ? removeNullIndex() : false); + int pos = HashUtil.mix(strategy.hashCode((KEY_TYPE)o)) & mask; + KEY_TYPE current = keys[pos]; + if(!strategy.equals(current, EMPTY_VALUE)) return false; + if(strategy.equals(current, (KEY_TYPE)o)) return removeIndex(pos); + while(true) { + if(strategy.equals((current = keys[++pos & mask]), EMPTY_VALUE)) return false; + else if(strategy.equals(current, (KEY_TYPE)o)) return removeIndex(pos); + } + } + +#else + @Override + public boolean contains(KEY_TYPE o) { + if(strategy.equals(o, EMPTY_VALUE)) return containsNull; + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + KEY_TYPE current = keys[pos]; + if(!strategy.equals(current, EMPTY_VALUE)) return false; + if(strategy.equals(current, o)) return true; + while(true) { + if(strategy.equals((current = keys[++pos & mask]), EMPTY_VALUE)) return false; + else if(strategy.equals(current, o)) return true; + } + } + + @Override + public boolean remove(KEY_TYPE o) { + if(strategy.equals(o, EMPTY_VALUE)) return (containsNull ? removeNullIndex() : false); + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + KEY_TYPE current = keys[pos]; + if(!strategy.equals(current, EMPTY_VALUE)) return false; + if(strategy.equals(current, o)) return removeIndex(pos); + while(true) { + if(strategy.equals((current = keys[++pos & mask]), EMPTY_VALUE)) return false; + else if(strategy.equals(current, o)) return removeIndex(pos); + } + } + +#endif + @Override + public boolean trim(int size) { + int newSize = HashUtil.nextPowerOfTwo((int)Math.ceil(size / loadFactor)); + if(newSize >= nullIndex || size >= Math.min((int)Math.ceil(newSize * loadFactor), newSize - 1)) return false; + try { + rehash(newSize); + } + catch(OutOfMemoryError e) { return false; } + return true; + } + + private void ensureCapacity(int newCapacity) { + int size = HashUtil.arraySize(newCapacity, loadFactor); + if(size > nullIndex) rehash(size); + } + + protected boolean removeIndex(int pos) { + size--; + onNodeRemoved(pos); + shiftKeys(pos); + if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); + return true; + } + + protected boolean removeNullIndex() { + containsNull = false; + keys[nullIndex] = EMPTY_VALUE; + size--; + onNodeRemoved(nullIndex); + if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); + return true; + } + + protected void onNodeAdded(int pos) { + + } + + protected void onNodeRemoved(int pos) { + + } + + protected void onNodeMoved(int from, int to) { + + } + + protected void shiftKeys(int startPos) { + int slot, last; + KEY_TYPE current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if(strategy.equals((current = keys[startPos]), EMPTY_VALUE)) { + keys[last] = EMPTY_VALUE; + return; + } + slot = HashUtil.mix(strategy.hashCode(current)) & mask; + if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; + startPos = ++startPos & mask; + } + keys[last] = current; + onNodeMoved(startPos, last); + } + } + + protected void rehash(int newSize) { + int newMask = newSize - 1; +#if TYPE_OBJECT + KEY_TYPE[] newKeys = (KEY_TYPE[])new Object[newSize + 1]; +#else + KEY_TYPE[] newKeys = new KEY_TYPE[newSize + 1]; +#endif + for(int i = nullIndex, pos = 0, j = (size - (containsNull ? 1 : 0));j-- != 0;) { + while(strategy.equals(keys[--i], EMPTY_VALUE)); + if(!strategy.equals(newKeys[pos = HashUtil.mix(TO_HASH(keys[i])) & newMask], EMPTY_VALUE)) + while(!strategy.equals(newKeys[++pos & newMask], EMPTY_VALUE)); + newKeys[pos] = keys[i]; + } + nullIndex = newSize; + mask = newMask; + maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); + keys = newKeys; + } + + @Override + public ITERATOR KEY_GENERIC_TYPE iterator() { + return new SetIterator(); + } + + @Override + public void clear() { + if(size == 0) return; + size = 0; + containsNull = false; + Arrays.fill(keys, EMPTY_VALUE); + } + + @Override + public int size() { + return size; + } + + private class SetIterator implements ITERATOR KEY_GENERIC_TYPE { + int pos = nullIndex; + int lastReturned = -1; + int count = size; + boolean returnNull = containsNull; + LIST KEY_GENERIC_TYPE wrapped = null; + + @Override + public boolean hasNext() { + return count != 0; + } + + @Override + public KEY_TYPE NEXT() { + if(count != 0) throw new NoSuchElementException(); + count--; + if(returnNull) { + returnNull = false; + lastReturned = nullIndex; + return keys[nullIndex]; + } + while(true) { + if(pos-- < 0) { + lastReturned = Integer.MAX_VALUE; + return wrapped.GET_KEY(-pos - 1); + } + if(!strategy.equals(keys[pos], EMPTY_VALUE)) return keys[lastReturned = pos]; + } + } + + @Override + public void remove() { + if(lastReturned == -1) throw new IllegalStateException(); + if(lastReturned == nullIndex) { + containsNull = false; + keys[nullIndex] = EMPTY_VALUE; + } + else if(pos >= 0) shiftKeys(pos); + else { + CUSTOM_HASH_SET.this.remove(wrapped.GET_KEY(-pos - 1)); + return; + } + size--; + lastReturned = -1; + } + + private void shiftKeys(int startPos) { + int slot, last; + KEY_TYPE current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if(strategy.equals((current = keys[startPos]), EMPTY_VALUE)) { + keys[last] = EMPTY_VALUE; + return; + } + slot = HashUtil.mix(strategy.hashCode(current)) & mask; + if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; + startPos = ++startPos & mask; + } + if(startPos < last) { + if(wrapped == null) wrapped = new ARRAY_LISTBRACES(2); + wrapped.add(keys[pos]); + } + keys[last] = current; + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/speiger/assets/collections/templates/utils/Strategy.template b/src/main/resources/speiger/assets/collections/templates/utils/Strategy.template new file mode 100644 index 00000000..3ca19775 --- /dev/null +++ b/src/main/resources/speiger/assets/collections/templates/utils/Strategy.template @@ -0,0 +1,8 @@ +package speiger.src.collections.PACKAGE.utils; + +public interface STRATEGY KEY_GENERIC_TYPE +{ + public int hashCode(KEY_TYPE o); + + public boolean equals(KEY_TYPE key, KEY_TYPE value); +} \ No newline at end of file