org.javimmutable.collections.array.trie32.MultiBranchTrieNode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javimmutable-collections Show documentation
Show all versions of javimmutable-collections Show documentation
Library providing immutable/persistent collection classes for
Java. While collections are immutable they provide methods for
adding and removing values by creating new modified copies of
themselves. Each copy shares almost all of its structure with
other copies to minimize memory consumption.
///###////////////////////////////////////////////////////////////////////////
//
// Burton Computer Corporation
// http://www.burton-computer.com
//
// Copyright (c) 2014, Burton Computer Corporation
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the
// distribution.
//
// Neither the name of the Burton Computer Corporation nor the names
// of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package org.javimmutable.collections.array.trie32;
import org.javimmutable.collections.Cursor;
import org.javimmutable.collections.Func1;
import org.javimmutable.collections.Holder;
import org.javimmutable.collections.Holders;
import org.javimmutable.collections.Indexed;
import org.javimmutable.collections.JImmutableMap;
import org.javimmutable.collections.common.MutableDelta;
import org.javimmutable.collections.cursors.MultiTransformCursor;
import org.javimmutable.collections.cursors.StandardCursor;
import javax.annotation.concurrent.Immutable;
@Immutable
public class MultiBranchTrieNode
extends TrieNode
{
// used by SignedOrderCursorSource to determine which index to use next
private static final IndexList SIGNED_INDEX_LIST = new IndexList(2, new IndexList(3, new IndexList(0, new IndexList(1, null))));
private final int shift;
private final int bitmask;
private final TrieNode[] entries;
private MultiBranchTrieNode(int shift,
int bitmask,
TrieNode[] entries)
{
assert shift >= 0;
this.shift = shift;
this.bitmask = bitmask;
this.entries = entries;
}
static MultiBranchTrieNode forTesting(int shift)
{
TrieNode[] entries = allocate(0);
return new MultiBranchTrieNode(shift, 0, entries);
}
static MultiBranchTrieNode forIndex(int shift,
int index,
TrieNode child)
{
int branchIndex = ((index >>> shift) & 0x1f);
return forBranchIndex(shift, branchIndex, child);
}
static MultiBranchTrieNode forBranchIndex(int shift,
int branchIndex,
TrieNode child)
{
assert branchIndex >= 0 && branchIndex < 32;
TrieNode[] entries = allocate(1);
entries[0] = child;
return new MultiBranchTrieNode(shift, 1 << branchIndex, entries);
}
static MultiBranchTrieNode forEntries(int shift,
TrieNode[] entries)
{
final int length = entries.length;
final int bitmask = (length == 32) ? -1 : ((1 << length) - 1);
return new MultiBranchTrieNode(shift, bitmask, entries.clone());
}
static MultiBranchTrieNode forSource(int index,
int size,
Indexed extends T> source,
int offset)
{
final TrieNode[] entries = allocate(size);
for (int i = 0; i < size; ++i) {
entries[i] = LeafTrieNode.of(index++, source.get(offset++));
}
final int bitmask = (size == 32) ? -1 : ((1 << size) - 1);
return new MultiBranchTrieNode(0, bitmask, entries);
}
static MultiBranchTrieNode fullWithout(int shift,
TrieNode[] entries,
int withoutIndex)
{
assert entries.length == 32;
final TrieNode[] newEntries = allocate(31);
System.arraycopy(entries, 0, newEntries, 0, withoutIndex);
System.arraycopy(entries, withoutIndex + 1, newEntries, withoutIndex, 31 - withoutIndex);
final int newMask = ~(1 << withoutIndex);
return new MultiBranchTrieNode(shift, newMask, newEntries);
}
@Override
public boolean isEmpty()
{
return entries.length == 0;
}
@Override
public T getValueOr(int shift,
int index,
T defaultValue)
{
assert this.shift == shift;
final int bit = 1 << ((index >>> shift) & 0x1f);
final int bitmask = this.bitmask;
if ((bitmask & bit) == 0) {
return defaultValue;
} else {
final int childIndex = realIndex(bitmask, bit);
return entries[childIndex].getValueOr(shift - 5, index, defaultValue);
}
}
@Override
public V getValueOr(int shift,
int index,
K key,
Transforms transforms,
V defaultValue)
{
assert this.shift == shift;
final int bit = 1 << ((index >>> shift) & 0x1f);
final int bitmask = this.bitmask;
if ((bitmask & bit) == 0) {
return defaultValue;
} else {
final int childIndex = realIndex(bitmask, bit);
return entries[childIndex].getValueOr(shift - 5, index, key, transforms, defaultValue);
}
}
@Override
public Holder find(int shift,
int index)
{
assert this.shift == shift;
final int bit = 1 << ((index >>> shift) & 0x1f);
final int bitmask = this.bitmask;
if ((bitmask & bit) == 0) {
return Holders.of();
} else {
final int childIndex = realIndex(bitmask, bit);
return entries[childIndex].find(shift - 5, index);
}
}
@Override
public Holder find(int shift,
int index,
K key,
Transforms transforms)
{
assert this.shift == shift;
final int bit = 1 << ((index >>> shift) & 0x1f);
final int bitmask = this.bitmask;
if ((bitmask & bit) == 0) {
return Holders.of();
} else {
final int childIndex = realIndex(bitmask, bit);
return entries[childIndex].find(shift - 5, index, key, transforms);
}
}
@Override
public TrieNode assign(int shift,
int index,
T value,
MutableDelta sizeDelta)
{
assert this.shift == shift;
final int bit = 1 << ((index >>> shift) & 0x1f);
final int bitmask = this.bitmask;
final int childIndex = realIndex(bitmask, bit);
final TrieNode[] entries = this.entries;
if ((bitmask & bit) == 0) {
final TrieNode newChild = LeafTrieNode.of(index, value);
sizeDelta.add(1);
return selectNodeForInsertResult(shift, bit, bitmask, childIndex, entries, newChild);
} else {
final TrieNode child = entries[childIndex];
final TrieNode newChild = child.assign(shift - 5, index, value, sizeDelta);
return selectNodeForUpdateResult(shift, bitmask, childIndex, entries, child, newChild);
}
}
@Override
public TrieNode assign(int shift,
int index,
K key,
V value,
Transforms transforms,
MutableDelta sizeDelta)
{
assert this.shift == shift;
final int bit = 1 << ((index >>> shift) & 0x1f);
final int bitmask = this.bitmask;
final int childIndex = realIndex(bitmask, bit);
final TrieNode[] entries = this.entries;
if ((bitmask & bit) == 0) {
final TrieNode newChild = LeafTrieNode.of(index, transforms.update(Holders.of(), key, value, sizeDelta));
return selectNodeForInsertResult(shift, bit, bitmask, childIndex, entries, newChild);
} else {
final TrieNode child = entries[childIndex];
final TrieNode newChild = child.assign(shift - 5, index, key, value, transforms, sizeDelta);
return selectNodeForUpdateResult(shift, bitmask, childIndex, entries, child, newChild);
}
}
@Override
public TrieNode delete(int shift,
int index,
MutableDelta sizeDelta)
{
assert this.shift == shift;
final int bit = 1 << ((index >>> shift) & 0x1f);
final int bitmask = this.bitmask;
final TrieNode[] entries = this.entries;
if ((bitmask & bit) == 0) {
return this;
} else {
final int childIndex = realIndex(bitmask, bit);
final TrieNode child = entries[childIndex];
final TrieNode newChild = child.delete(shift - 5, index, sizeDelta);
return selectNodeForDeleteResult(shift, bit, bitmask, entries, childIndex, child, newChild);
}
}
@Override
public TrieNode delete(int shift,
int index,
K key,
Transforms transforms,
MutableDelta sizeDelta)
{
assert this.shift == shift;
final int bit = 1 << ((index >>> shift) & 0x1f);
final int bitmask = this.bitmask;
final TrieNode[] entries = this.entries;
if ((bitmask & bit) == 0) {
return this;
} else {
final int childIndex = realIndex(bitmask, bit);
final TrieNode child = entries[childIndex];
TrieNode newChild = child.delete(shift - 5, index, key, transforms, sizeDelta);
return selectNodeForDeleteResult(shift, bit, bitmask, entries, childIndex, child, newChild);
}
}
@Override
public int getShift()
{
return shift;
}
@Override
public boolean isLeaf()
{
return false;
}
@Override
public TrieNode trimmedToMinimumDepth()
{
return bitmask == 1 ? entries[0].trimmedToMinimumDepth() : this;
}
@Override
public Cursor> anyOrderEntryCursor()
{
return entryCursor(new AnyOrderCursorSource());
}
@Override
public Cursor> signedOrderEntryCursor()
{
if (shift != ROOT_SHIFT) {
return anyOrderEntryCursor();
} else {
return entryCursor(new SignedOrderCursorSource());
}
}
@Override
public Cursor> anyOrderEntryCursor(final Transforms transforms)
{
return entryCursor(new AnyOrderCursorSource(), transforms);
}
@Override
public Cursor> signedOrderEntryCursor(Transforms transforms)
{
if (shift != ROOT_SHIFT) {
return anyOrderEntryCursor(transforms);
} else {
return entryCursor(new SignedOrderCursorSource(), transforms);
}
}
@Override
public Cursor anyOrderValueCursor()
{
return valueCursor(new AnyOrderCursorSource());
}
@Override
public Cursor signedOrderValueCursor()
{
if (shift != ROOT_SHIFT) {
return anyOrderValueCursor();
} else {
return valueCursor(new SignedOrderCursorSource());
}
}
// for use by unit tests
int getBitmask()
{
return bitmask;
}
// for use by unit tests
TrieNode[] getEntries()
{
return entries.clone();
}
private TrieNode selectNodeForUpdateResult(int shift,
int bitmask,
int childIndex,
TrieNode[] entries,
TrieNode child,
TrieNode newChild)
{
if (newChild == child) {
return this;
} else {
assert newChild.isLeaf() || (newChild.getShift() == shift - 5);
final TrieNode[] newEntries = entries.clone();
newEntries[childIndex] = newChild;
return new MultiBranchTrieNode(shift, bitmask, newEntries);
}
}
private TrieNode selectNodeForInsertResult(int shift,
int bit,
int bitmask,
int childIndex,
TrieNode[] entries,
TrieNode newChild)
{
final int oldLength = entries.length;
final TrieNode[] newEntries = allocate(oldLength + 1);
if (bitmask != 0) {
System.arraycopy(entries, 0, newEntries, 0, childIndex);
System.arraycopy(entries, childIndex, newEntries, childIndex + 1, oldLength - childIndex);
}
newEntries[childIndex] = newChild;
if (newEntries.length == 32) {
return new FullBranchTrieNode(shift, newEntries);
} else {
return new MultiBranchTrieNode(shift, bitmask | bit, newEntries);
}
}
private TrieNode selectNodeForDeleteResult(int shift,
int bit,
int bitmask,
TrieNode[] entries,
int childIndex,
TrieNode child,
TrieNode newChild)
{
if (newChild.isEmpty()) {
switch (entries.length) {
case 1:
return of();
case 2: {
final int newBitmask = bitmask & ~bit;
final int remainingIndex = Integer.numberOfTrailingZeros(newBitmask);
final TrieNode remainingChild = entries[realIndex(bitmask, 1 << remainingIndex)];
if (remainingChild.isLeaf()) {
return remainingChild;
} else {
return SingleBranchTrieNode.forBranchIndex(shift, remainingIndex, remainingChild);
}
}
default: {
final int newLength = entries.length - 1;
final TrieNode[] newArray = allocate(newLength);
System.arraycopy(entries, 0, newArray, 0, childIndex);
System.arraycopy(entries, childIndex + 1, newArray, childIndex, newLength - childIndex);
return new MultiBranchTrieNode(shift, bitmask & ~bit, newArray);
}
}
} else {
return selectNodeForUpdateResult(shift, bitmask, childIndex, entries, child, newChild);
}
}
private Cursor> entryCursor(StandardCursor.Source> source)
{
return MultiTransformCursor.of(StandardCursor.of(source), new Func1, Cursor>>()
{
@Override
public Cursor> apply(TrieNode node)
{
return node.anyOrderEntryCursor();
}
});
}
private Cursor> entryCursor(StandardCursor.Source> source,
final Transforms transforms)
{
return MultiTransformCursor.of(StandardCursor.of(source), new Func1, Cursor>>()
{
@Override
public Cursor> apply(TrieNode node)
{
return node.anyOrderEntryCursor(transforms);
}
});
}
private Cursor valueCursor(StandardCursor.Source> source)
{
return MultiTransformCursor.of(StandardCursor.of(source), new Func1, Cursor>()
{
@Override
public Cursor apply(TrieNode node)
{
return node.anyOrderValueCursor();
}
});
}
private class AnyOrderCursorSource
implements StandardCursor.Source>
{
private final int index;
private AnyOrderCursorSource()
{
this(0);
}
private AnyOrderCursorSource(int index)
{
this.index = index;
}
@Override
public boolean atEnd()
{
return index >= entries.length;
}
@Override
public TrieNode currentValue()
{
return entries[index];
}
@Override
public StandardCursor.Source> advance()
{
return new AnyOrderCursorSource(index + 1);
}
}
private class SignedOrderCursorSource
implements StandardCursor.Source>
{
private final IndexList indexList;
private SignedOrderCursorSource()
{
this(SIGNED_INDEX_LIST);
}
private SignedOrderCursorSource(IndexList indexList)
{
assert shift == TrieNode.ROOT_SHIFT;
this.indexList = findFirstIndex(indexList);
}
@Override
public boolean atEnd()
{
return indexList == null;
}
@Override
public TrieNode currentValue()
{
return entries[realIndex(bitmask, indexList.bit)];
}
@Override
public StandardCursor.Source> advance()
{
if (indexList == null) {
return this;
} else {
return new SignedOrderCursorSource(findFirstIndex(indexList.next));
}
}
}
private IndexList findFirstIndex(IndexList next)
{
while (next != null && (bitmask & next.bit) == 0) {
next = next.next;
}
return next;
}
private static int realIndex(int bitmask,
int bit)
{
return Integer.bitCount(bitmask & (bit - 1));
}
@SuppressWarnings("unchecked")
static TrieNode[] allocate(int size)
{
return (TrieNode[])new TrieNode[size];
}
private static class IndexList
{
private final int bit;
private final IndexList next;
private IndexList(int index,
IndexList next)
{
this.bit = 1 << index;
this.next = next;
}
}
}