
org.javimmutable.collections.hash.set.SetBranchNode Maven / Gradle / Ivy
///###////////////////////////////////////////////////////////////////////////
//
// Burton Computer Corporation
// http://www.burton-computer.com
//
// Copyright (c) 2019, 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.hash.set;
import org.javimmutable.collections.Indexed;
import org.javimmutable.collections.Proc1;
import org.javimmutable.collections.Proc1Throws;
import org.javimmutable.collections.Sum1;
import org.javimmutable.collections.Sum1Throws;
import org.javimmutable.collections.common.ArrayHelper;
import org.javimmutable.collections.common.CollisionSet;
import org.javimmutable.collections.common.ToStringHelper;
import org.javimmutable.collections.iterators.GenericIterator;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import java.util.Arrays;
import java.util.Objects;
@Immutable
public class SetBranchNode
implements ArrayHelper.Allocator>,
SetNode
{
@SuppressWarnings("rawtypes")
private static final SetBranchNode[] EMPTY_NODES = new SetBranchNode[0];
static final int SHIFT = 5;
static final int MASK = 0x1f;
private final int bitmask;
@Nonnull
private final CollisionSet.Node value;
@Nonnull
private final SetNode[] children;
private final int size;
SetBranchNode(int bitmask,
@Nonnull CollisionSet.Node value,
@Nonnull SetNode[] children,
int size)
{
this.bitmask = bitmask;
this.value = value;
this.children = children;
this.size = size;
}
@SuppressWarnings("unchecked")
static SetNode forLeafExpansion(@Nonnull CollisionSet collisionSet,
int hashCode,
@Nonnull T key)
{
if (hashCode == 0) {
return new SetBranchNode(0, collisionSet.single(key), EMPTY_NODES, 1);
} else {
final int index = hashCode & MASK;
final int remainder = hashCode >>> SHIFT;
final int bit = 1 << index;
final SetNode[] children = new SetNode[1];
children[0] = new SetSingleValueLeafNode<>(remainder, key);
return new SetBranchNode<>(bit, collisionSet.empty(), children, 1);
}
}
@SuppressWarnings("unchecked")
static SetNode forLeafExpansion(@Nonnull CollisionSet collisionSet,
int hashCode,
@Nonnull CollisionSet.Node value)
{
if (hashCode == 0) {
return new SetBranchNode(0, value, EMPTY_NODES, collisionSet.size(value));
} else {
final int index = hashCode & MASK;
final int remainder = hashCode >>> SHIFT;
final int bit = 1 << index;
final SetNode[] children = new SetNode[1];
children[0] = SetMultiValueLeafNode.createLeaf(collisionSet, remainder, value);
return new SetBranchNode<>(bit, collisionSet.empty(), children, collisionSet.size(value));
}
}
@Override
public boolean isLeaf()
{
return false;
}
@Override
public int size(@Nonnull CollisionSet collisionSet)
{
return size;
}
@Override
public boolean contains(@Nonnull CollisionSet collisionSet,
int hashCode,
@Nonnull T hashKey)
{
if (hashCode == 0) {
return collisionSet.contains(value, hashKey);
}
final int index = hashCode & MASK;
final int remainder = hashCode >>> SHIFT;
final int bit = 1 << index;
final int bitmask = this.bitmask;
if ((bitmask & bit) == 0) {
return false;
} else {
final int childIndex = realIndex(bitmask, bit);
return children[childIndex].contains(collisionSet, remainder, hashKey);
}
}
@Override
@Nonnull
public SetNode insert(@Nonnull CollisionSet collisionSet,
int hashCode,
@Nonnull T hashKey)
{
final SetNode[] children = this.children;
final int bitmask = this.bitmask;
final CollisionSet.Node thisValue = this.value;
if (hashCode == 0) {
final CollisionSet.Node newValue = collisionSet.insert(thisValue, hashKey);
if (thisValue == newValue) {
return this;
} else {
return new SetBranchNode<>(bitmask, newValue, children, size - collisionSet.size(thisValue) + collisionSet.size(newValue));
}
}
final int index = hashCode & MASK;
final int remainder = hashCode >>> SHIFT;
final int bit = 1 << index;
final int childIndex = realIndex(bitmask, bit);
if ((bitmask & bit) == 0) {
final SetNode newChild = new SetSingleValueLeafNode<>(remainder, hashKey);
final SetNode[] newChildren = ArrayHelper.insert(this, children, childIndex, newChild);
return new SetBranchNode<>(bitmask | bit, thisValue, newChildren, size + 1);
} else {
final SetNode child = children[childIndex];
final SetNode newChild = child.insert(collisionSet, remainder, hashKey);
if (newChild == child) {
return this;
} else {
final SetNode[] newChildren = ArrayHelper.assign(children, childIndex, newChild);
return new SetBranchNode<>(bitmask, thisValue, newChildren, size - child.size(collisionSet) + newChild.size(collisionSet));
}
}
}
@Override
@Nonnull
public SetNode delete(@Nonnull CollisionSet collisionSet,
int hashCode,
@Nonnull T hashKey)
{
final int bitmask = this.bitmask;
final SetNode[] children = this.children;
final CollisionSet.Node value = this.value;
if (hashCode == 0) {
final CollisionSet.Node newValue = collisionSet.delete(value, hashKey);
final int newSize = this.size - collisionSet.size(value) + collisionSet.size(newValue);
if (newValue == value) {
return this;
} else if (collisionSet.size(newValue) == 0) {
if (bitmask == 0) {
return SetEmptyNode.of();
} else {
return createForDelete(collisionSet, bitmask, newValue, children, newSize);
}
} else {
return new SetBranchNode<>(bitmask, newValue, children, newSize);
}
}
final int index = hashCode & MASK;
final int remainder = hashCode >>> SHIFT;
final int bit = 1 << index;
final int childIndex = realIndex(bitmask, bit);
if ((bitmask & bit) == 0) {
return this;
} else {
final SetNode child = children[childIndex];
final SetNode newChild = child.delete(collisionSet, remainder, hashKey);
final int newSize = size - child.size(collisionSet) + newChild.size(collisionSet);
if (newChild == child) {
return this;
} else if (newChild.isEmpty(collisionSet)) {
if (children.length == 1) {
if (collisionSet.size(value) == 0) {
return SetEmptyNode.of();
} else {
return SetMultiValueLeafNode.createLeaf(collisionSet, 0, value);
}
} else {
final SetNode[] newChildren = ArrayHelper.delete(this, children, childIndex);
return createForDelete(collisionSet, bitmask & ~bit, value, newChildren, newSize);
}
} else {
final SetNode[] newChildren = ArrayHelper.assign(children, childIndex, newChild);
return createForDelete(collisionSet, bitmask, value, newChildren, newSize);
}
}
}
private SetNode createForDelete(@Nonnull CollisionSet collisionSet,
int bitmask,
CollisionSet.Node value,
@Nonnull SetNode[] children,
int newSize)
{
if (collisionSet.size(value) == 0 && children.length == 1) {
final SetNode child = children[0];
if (child.isLeaf()) {
assert newSize == child.size(collisionSet);
return child.liftNode(Integer.numberOfTrailingZeros(bitmask));
}
if (child instanceof SetBranchNode) {
final SetBranchNode branch = (SetBranchNode)child;
if (collisionSet.size(branch.value) > 0 && branch.children.length == 0) {
assert newSize == collisionSet.size(branch.value);
return SetMultiValueLeafNode.createLeaf(collisionSet, Integer.numberOfTrailingZeros(bitmask), branch.value);
}
}
}
return new SetBranchNode<>(bitmask, value, children, newSize);
}
@Override
public boolean isEmpty(@Nonnull CollisionSet collisionSet)
{
return bitmask == 0 && collisionSet.size(value) == 0;
}
@Nonnull
@Override
public SetNode liftNode(int index)
{
throw new UnsupportedOperationException();
}
private static int realIndex(int bitmask,
int bit)
{
return Integer.bitCount(bitmask & (bit - 1));
}
@SuppressWarnings("unchecked")
@Nonnull
@Override
public SetNode[] allocate(int size)
{
return new SetNode[size];
}
@Nullable
@Override
public GenericIterator.State iterateOverRange(@Nonnull CollisionSet collisionSet,
@Nullable GenericIterator.State parent,
int offset,
int limit)
{
assert offset >= 0 && offset <= limit && limit <= size;
return GenericIterator.indexedState(parent, indexedForIterator(collisionSet), offset, limit);
}
@Override
public void forEach(@Nonnull CollisionSet collisionSet,
@Nonnull Proc1 proc)
{
collisionSet.forEach(value, proc);
for (SetNode child : children) {
child.forEach(collisionSet, proc);
}
}
@Override
public void forEachThrows(@Nonnull CollisionSet collisionSet,
@Nonnull Proc1Throws proc)
throws E
{
collisionSet.forEachThrows(value, proc);
for (SetNode child : children) {
child.forEachThrows(collisionSet, proc);
}
}
@Override
public R reduce(@Nonnull CollisionSet collisionSet,
R sum,
@Nonnull Sum1 proc)
{
sum = collisionSet.reduce(value, sum, proc);
for (SetNode child : children) {
sum = child.reduce(collisionSet, sum, proc);
}
return sum;
}
@Override
public R reduceThrows(@Nonnull CollisionSet collisionSet,
R sum,
@Nonnull Sum1Throws proc)
throws E
{
sum = collisionSet.reduceThrows(value, sum, proc);
for (SetNode child : children) {
sum = child.reduceThrows(collisionSet, sum, proc);
}
return sum;
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SetBranchNode> that = (SetBranchNode>)o;
return bitmask == that.bitmask &&
size == that.size &&
value.equals(that.value) &&
Arrays.equals(children, that.children);
}
@Override
public int hashCode()
{
int result = Objects.hash(bitmask, value, size);
result = 31 * result + Arrays.hashCode(children);
return result;
}
@Override
public String toString()
{
return "(" + size + ",0x" + Integer.toHexString(bitmask) + "," + children.length + "," + value + "," + ToStringHelper.arrayToString(children) + ")";
}
private int computeSize(@Nonnull CollisionSet collisionSet)
{
int answer = collisionSet.size(value);
for (SetNode child : children) {
answer += child.size(collisionSet);
}
return answer;
}
@Override
public void checkInvariants(@Nonnull CollisionSet collisionSet)
{
if (size != computeSize(collisionSet)) {
throw new IllegalStateException(String.format("incorrect size: expected=%d actual=%d", computeSize(collisionSet), size));
}
if (collisionSet.size(value) == 0 && children.length == 1) {
if (children[0] instanceof SetMultiValueLeafNode || children[0] instanceof SetSingleValueLeafNode) {
// we should have replaced ourselves with a leaf
throw new IllegalStateException(String.format("expected leaf but was %s", children[0].getClass().getName()));
}
}
for (SetNode child : children) {
child.checkInvariants(collisionSet);
}
}
@Nonnull
private Indexed> indexedForIterator(@Nonnull CollisionSet collisionSet)
{
return new Indexed>()
{
@Override
public GenericIterator.Iterable get(int index)
{
if (index == 0) {
return collisionSet.genericIterable(value);
} else {
return children[index - 1].genericIterable(collisionSet);
}
}
@Override
public int size()
{
return 1 + children.length;
}
};
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy