All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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