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

org.javimmutable.collections.hash.map.MapBranchNode 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.map;

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.Proc2;
import org.javimmutable.collections.Proc2Throws;
import org.javimmutable.collections.Sum2;
import org.javimmutable.collections.Sum2Throws;
import org.javimmutable.collections.common.ArrayHelper;
import org.javimmutable.collections.common.CollisionMap;
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 MapBranchNode
    implements ArrayHelper.Allocator>,
               MapNode
{
    private static final MapBranchNode[] EMPTY_NODES = new MapBranchNode[0];

    static final int SHIFT = 5;
    static final int MASK = 0x1f;

    private final int bitmask;
    @Nonnull
    private final CollisionMap.Node value;
    @Nonnull
    private final MapNode[] children;
    private final int size;

    MapBranchNode(int bitmask,
                  @Nonnull CollisionMap.Node value,
                  @Nonnull MapNode[] children,
                  int size)
    {
        this.bitmask = bitmask;
        this.value = value;
        this.children = children;
        this.size = size;
    }

    @SuppressWarnings("unchecked")
    static  MapNode forLeafExpansion(@Nonnull CollisionMap collisionMap,
                                                 int hashCode,
                                                 @Nonnull K key,
                                                 @Nullable V value)
    {
        if (hashCode == 0) {
            return new MapBranchNode(0, collisionMap.single(key, value), EMPTY_NODES, 1);
        } else {
            final int index = hashCode & MASK;
            final int remainder = hashCode >>> SHIFT;
            final int bit = 1 << index;
            final MapNode[] children = new MapNode[1];
            children[0] = new MapSingleKeyLeafNode<>(remainder, key, value);
            return new MapBranchNode<>(bit, collisionMap.empty(), children, 1);
        }
    }

    @SuppressWarnings("unchecked")
    static  MapNode forLeafExpansion(@Nonnull CollisionMap collisionMap,
                                                 int hashCode,
                                                 @Nonnull CollisionMap.Node value)
    {
        if (hashCode == 0) {
            return new MapBranchNode(0, value, EMPTY_NODES, collisionMap.size(value));
        } else {
            final int index = hashCode & MASK;
            final int remainder = hashCode >>> SHIFT;
            final int bit = 1 << index;
            final MapNode[] children = new MapNode[1];
            children[0] = MapMultiKeyLeafNode.createLeaf(collisionMap, remainder, value);
            return new MapBranchNode<>(bit, collisionMap.empty(), children, collisionMap.size(value));
        }
    }

    @Override
    public boolean isLeaf()
    {
        return false;
    }

    @Override
    public int size(@Nonnull CollisionMap collisionMap)
    {
        return size;
    }

    @Override
    public Holder find(@Nonnull CollisionMap collisionMap,
                          int hashCode,
                          @Nonnull K hashKey)
    {
        if (hashCode == 0) {
            return collisionMap.findValue(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 Holders.of();
        } else {
            final int childIndex = realIndex(bitmask, bit);
            return children[childIndex].find(collisionMap, remainder, hashKey);
        }
    }

    @Override
    public V getValueOr(@Nonnull CollisionMap collisionMap,
                        int hashCode,
                        @Nonnull K hashKey,
                        V defaultValue)
    {
        if (hashCode == 0) {
            return collisionMap.getValueOr(value, hashKey, defaultValue);
        }
        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 defaultValue;
        } else {
            final int childIndex = realIndex(bitmask, bit);
            return children[childIndex].getValueOr(collisionMap, remainder, hashKey, defaultValue);
        }
    }

    @Override
    @Nonnull
    public MapNode assign(@Nonnull CollisionMap collisionMap,
                                int hashCode,
                                @Nonnull K hashKey,
                                @Nullable V value)
    {
        final MapNode[] children = this.children;
        final int bitmask = this.bitmask;
        final CollisionMap.Node thisValue = this.value;
        if (hashCode == 0) {
            final CollisionMap.Node newValue = collisionMap.update(thisValue, hashKey, value);
            if (thisValue == newValue) {
                return this;
            } else {
                return new MapBranchNode<>(bitmask, newValue, children, size - collisionMap.size(thisValue) + collisionMap.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 MapNode newChild = new MapSingleKeyLeafNode<>(remainder, hashKey, value);
            final MapNode[] newChildren = ArrayHelper.insert(this, children, childIndex, newChild);
            return new MapBranchNode<>(bitmask | bit, thisValue, newChildren, size + 1);
        } else {
            final MapNode child = children[childIndex];
            final MapNode newChild = child.assign(collisionMap, remainder, hashKey, value);
            if (newChild == child) {
                return this;
            } else {
                final MapNode[] newChildren = ArrayHelper.assign(children, childIndex, newChild);
                return new MapBranchNode<>(bitmask, thisValue, newChildren, size - child.size(collisionMap) + newChild.size(collisionMap));
            }
        }
    }

    @Nonnull
    @Override
    public MapNode update(@Nonnull CollisionMap collisionMap,
                                int hashCode,
                                @Nonnull K hashKey,
                                @Nonnull Func1, V> generator)
    {
        final MapNode[] children = this.children;
        final int bitmask = this.bitmask;
        final CollisionMap.Node thisValue = this.value;
        if (hashCode == 0) {
            final CollisionMap.Node newValue = collisionMap.update(thisValue, hashKey, generator);
            if (thisValue == newValue) {
                return this;
            } else {
                return new MapBranchNode<>(bitmask, newValue, children, size - collisionMap.size(thisValue) + collisionMap.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 MapNode newChild = new MapSingleKeyLeafNode<>(remainder, hashKey, generator.apply(Holders.of()));
            final MapNode[] newChildren = ArrayHelper.insert(this, children, childIndex, newChild);
            return new MapBranchNode<>(bitmask | bit, thisValue, newChildren, size + 1);
        } else {
            final MapNode child = children[childIndex];
            final MapNode newChild = child.update(collisionMap, remainder, hashKey, generator);
            if (newChild == child) {
                return this;
            } else {
                final MapNode[] newChildren = ArrayHelper.assign(children, childIndex, newChild);
                return new MapBranchNode<>(bitmask, thisValue, newChildren, size - child.size(collisionMap) + newChild.size(collisionMap));
            }
        }
    }

    @Override
    @Nonnull
    public MapNode delete(@Nonnull CollisionMap collisionMap,
                                int hashCode,
                                @Nonnull K hashKey)
    {
        final int bitmask = this.bitmask;
        final MapNode[] children = this.children;
        final CollisionMap.Node value = this.value;
        if (hashCode == 0) {
            final CollisionMap.Node newValue = collisionMap.delete(value, hashKey);
            final int newSize = this.size - collisionMap.size(value) + collisionMap.size(newValue);
            if (newValue == value) {
                return this;
            } else if (collisionMap.size(newValue) == 0) {
                if (bitmask == 0) {
                    return MapEmptyNode.of();
                } else {
                    return createForDelete(collisionMap, bitmask, newValue, children, newSize);
                }
            } else {
                return new MapBranchNode<>(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 MapNode child = children[childIndex];
            final MapNode newChild = child.delete(collisionMap, remainder, hashKey);
            final int newSize = size - child.size(collisionMap) + newChild.size(collisionMap);
            if (newChild == child) {
                return this;
            } else if (newChild.isEmpty(collisionMap)) {
                if (children.length == 1) {
                    if (collisionMap.size(value) == 0) {
                        return MapEmptyNode.of();
                    } else {
                        return MapMultiKeyLeafNode.createLeaf(collisionMap, 0, value);
                    }
                } else {
                    final MapNode[] newChildren = ArrayHelper.delete(this, children, childIndex);
                    return createForDelete(collisionMap, bitmask & ~bit, value, newChildren, newSize);
                }
            } else {
                final MapNode[] newChildren = ArrayHelper.assign(children, childIndex, newChild);
                return createForDelete(collisionMap, bitmask, value, newChildren, newSize);
            }
        }
    }

    private MapNode createForDelete(@Nonnull CollisionMap collisionMap,
                                          int bitmask,
                                          CollisionMap.Node value,
                                          @Nonnull MapNode[] children,
                                          int newSize)
    {
        if (collisionMap.size(value) == 0 && children.length == 1) {
            final MapNode child = children[0];
            if (child.isLeaf()) {
                assert newSize == child.size(collisionMap);
                return child.liftNode(Integer.numberOfTrailingZeros(bitmask));
            }
            if (child instanceof MapBranchNode) {
                final MapBranchNode branch = (MapBranchNode)child;
                if (collisionMap.size(branch.value) > 0 && branch.children.length == 0) {
                    assert newSize == collisionMap.size(branch.value);
                    return MapMultiKeyLeafNode.createLeaf(collisionMap, Integer.numberOfTrailingZeros(bitmask), branch.value);
                }
            }
        }
        return new MapBranchNode<>(bitmask, value, children, newSize);
    }

    @Override
    public boolean isEmpty(@Nonnull CollisionMap collisionMap)
    {
        return bitmask == 0 && collisionMap.size(value) == 0;
    }

    @Nonnull
    @Override
    public MapNode 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 MapNode[] allocate(int size)
    {
        return new MapNode[size];
    }

    @Nullable
    @Override
    public GenericIterator.State> iterateOverRange(@Nonnull CollisionMap collisionMap,
                                                                             @Nullable GenericIterator.State> parent,
                                                                             int offset,
                                                                             int limit)
    {
        assert offset >= 0 && offset <= limit && limit <= size;
        return GenericIterator.indexedState(parent, indexedForIterator(collisionMap), offset, limit);
    }

    @Override
    public void forEach(@Nonnull CollisionMap collisionMap,
                        @Nonnull Proc2 proc)
    {
        collisionMap.forEach(value, proc);
        for (MapNode child : children) {
            child.forEach(collisionMap, proc);
        }
    }

    @Override
    public  void forEachThrows(@Nonnull CollisionMap collisionMap,
                                                    @Nonnull Proc2Throws proc)
        throws E
    {
        collisionMap.forEachThrows(value, proc);
        for (MapNode child : children) {
            child.forEachThrows(collisionMap, proc);
        }
    }

    @Override
    public  R reduce(@Nonnull CollisionMap collisionMap,
                        R sum,
                        @Nonnull Sum2 proc)
    {
        sum = collisionMap.reduce(value, sum, proc);
        for (MapNode child : children) {
            sum = child.reduce(collisionMap, sum, proc);
        }
        return sum;
    }

    @Override
    public  R reduceThrows(@Nonnull CollisionMap collisionMap,
                                                   R sum,
                                                   @Nonnull Sum2Throws proc)
        throws E
    {
        sum = collisionMap.reduceThrows(value, sum, proc);
        for (MapNode child : children) {
            sum = child.reduceThrows(collisionMap, sum, proc);
        }
        return sum;
    }

    @Override
    public boolean equals(Object o)
    {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        MapBranchNode that = (MapBranchNode)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 CollisionMap collisionMap)
    {
        int answer = collisionMap.size(value);
        for (MapNode child : children) {
            answer += child.size(collisionMap);
        }
        return answer;
    }

    @Override
    public void checkInvariants(@Nonnull CollisionMap collisionMap)
    {
        if (size != computeSize(collisionMap)) {
            throw new IllegalStateException(String.format("incorrect size: expected=%d actual=%d", computeSize(collisionMap), size));
        }
        if (collisionMap.size(value) == 0 && children.length == 1) {
            if (children[0] instanceof MapMultiKeyLeafNode || children[0] instanceof MapSingleKeyLeafNode) {
                // we should have replaced ourselves with a leaf
                throw new IllegalStateException(String.format("expected leaf but was %s", children[0].getClass().getName()));
            }
        }
        for (MapNode child : children) {
            child.checkInvariants(collisionMap);
        }
    }

    @Nonnull
    private Indexed>> indexedForIterator(@Nonnull CollisionMap collisionMap)
    {
        return new Indexed>>()
        {
            @Override
            public GenericIterator.Iterable> get(int index)
            {
                if (index == 0) {
                    return collisionMap.genericIterable(value);
                } else {
                    return children[index - 1].genericIterable(collisionMap);
                }
            }

            @Override
            public int size()
            {
                return 1 + children.length;
            }
        };
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy