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

com.shapesecurity.functional.data.ConcatList Maven / Gradle / Ivy

There is a newer version: 3.1.0
Show newest version
/*
 * Copyright 2014 Shape Security, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.shapesecurity.functional.data;

import com.shapesecurity.functional.Effect;
import com.shapesecurity.functional.F;
import com.shapesecurity.functional.F2;
import com.shapesecurity.functional.Pair;

import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Spliterator;
import java.util.Stack;
import java.util.function.Consumer;

@CheckReturnValue
public abstract class ConcatList implements Iterable {
    @SuppressWarnings("StaticInitializerReferencesSubClass")
    private static final Empty EMPTY = new Empty<>();
    private static BinaryTreeMonoid MONOID = new BinaryTreeMonoid<>();
    public final int length;
    final boolean isBalanced;

    protected ConcatList(int length, boolean isBalanced) {
        this.length = length;
        this.isBalanced = isBalanced;
    }

    @SuppressWarnings("unchecked")
    @Nonnull
    public static  ConcatList empty() {
        return (ConcatList) EMPTY;
    }

    @SuppressWarnings("unchecked")
    @Nonnull
    @Deprecated
    public static  ConcatList nil() {
        return ConcatList.empty();
    }

    @SafeVarargs
    public static  ConcatList of(T... elements) {
        if (elements.length == 0) {
            return empty();
        }
        return ofInternal(elements, 0, elements.length);
    }

    public abstract boolean isEmpty();

    @Nonnull
    public static  ConcatList fromList(@Nonnull List list) {
        return fromListInternal(list.iterator(), 0, list.size());
    }

    @Nonnull
    private static  ConcatList ofInternal(@Nonnull T[] elements, int start, int end) {
        if (start == end) {
            return empty();
        } else if (start + 1 == end) {
            return single(elements[start]);
        } else {
            int mid = (start + end) / 2; // start < mid && mid < end
            return ofInternal(elements, start, mid).append(ofInternal(elements, mid, end));
        }
    }

    @Nonnull
    private static  ConcatList fromListInternal(@Nonnull Iterator elements, int start, int end) {
        if (start == end) {
            return empty();
        } else if (start + 1 == end) {
            return single(elements.next());
        } else {
            int mid = (start + end) / 2; // start < mid && mid < end
            return fromListInternal(elements, start, mid).append(fromListInternal(elements, mid, end));
        }
    }

    public abstract ConcatList balanced();

    @Nonnull
    public final Maybe, ConcatList>> split(int index) {
        if (index < 0 || index > this.length) {
            return Maybe.empty();
        }
        if (index == 0) {
            return Maybe.of(Pair.of(empty(), this));
        }
        if (index == this.length) {
            return Maybe.of(Pair.of(this, empty()));
        }
        return Maybe.of(this.splitInternal(index));
    }

    abstract Pair, ConcatList> splitInternal(int index);

    @Nonnull
    public static  ConcatList single(@Nonnull T scope) {
        return new Leaf<>(scope);
    }

    @SuppressWarnings("unchecked")
    public static  Monoid> monoid() {
        return (BinaryTreeMonoid) MONOID;
    }

    @Nonnull
    public abstract ImmutableList toList();

    @Nonnull
    public final  B foldLeft(@Nonnull F2 f, @Nonnull B init) {
        if (this.isEmpty()) {
            return init;
        }
        @SuppressWarnings("unchecked")
        B[] result = (B[]) new Object[]{init};
        this.forEach(n -> result[0] = f.apply(result[0], n));
        return result[0];
    }

    @Nonnull
    public final  B foldRight(@Nonnull F2 f, @Nonnull B init) {
        // Manually expanded recursion
        Deque> stack = new ArrayDeque<>(this.length);
        stack.add(this);
        while (!stack.isEmpty()) {
            ConcatList curr = stack.pop();
            if (curr instanceof Fork) {
                Fork fork = (Fork) curr;
                stack.push(fork.left);
                stack.push(fork.right);
            } else if (curr instanceof Leaf) {
                init = f.apply(((Leaf) curr).data, init);
            }
        }
        return init;
    }

    /**
     * @deprecated Use {@link #forEach(Consumer)} instead
     */
    @Deprecated
    public final void foreach(@Nonnull Effect f) {
        this.forEach(f::e);
    }

    @Override
    public final void forEach(@Nonnull Consumer action) {
        // Manually expanded recursion
        @SuppressWarnings("unchecked")
        ConcatList[] stack = new ConcatList[this.length];
        int i = 0;
        stack[i++] = this;
        while (i > 0) {
            ConcatList curr = stack[--i];
            if (curr instanceof Fork) {
                Fork fork = (Fork) curr;
                stack[i++] = fork.right;
                stack[i++] = fork.left;
            } else if (curr instanceof Leaf) {
                action.accept(((Leaf) curr).data);
            }
        }
    }

    @Nonnull
    public abstract ConcatList append(@Nonnull ConcatList rhs);

    @Nonnull
    public final ConcatList append1(@Nonnull T element) {
        return this.append(ConcatList.single(element));
    }

    public final boolean exists(@Nonnull F f) {
        return this.find(f).isJust();
    }

    @Nonnull
    public final Maybe find(@Nonnull F f) {
        // Manually expanded recursion
        Deque> stack = new ArrayDeque<>(this.length);
        stack.add(this);
        while (!stack.isEmpty()) {
            ConcatList curr = stack.pop();
            if (curr instanceof Fork) {
                Fork fork = (Fork) curr;
                stack.push(fork.right);
                stack.push(fork.left);
            } else if (curr instanceof Leaf) {
                if (f.apply(((Leaf) curr).data)) {
                    return Maybe.of(((Leaf) curr).data);
                }
            }
        }
        return Maybe.empty();
    }

    @Nonnull
    public final ConcatList reverse() {
        if (this instanceof Empty) {
            return this;
        }
        ArrayList list = new ArrayList<>(this.length);
        Deque> stack = new ArrayDeque<>(this.length);
        stack.add(this);
        while (!stack.isEmpty()) {
            ConcatList curr = stack.pop();
            if (curr instanceof Fork) {
                Fork fork = (Fork) curr;
                // reversed
                stack.push(fork.left);
                stack.push(fork.right);
            } else if (curr instanceof Leaf) {
                list.add(((Leaf) curr).data);
            }
        }
        return ConcatList.fromList(list);
    }

    @Nonnull
    public final Maybe index(int index) {
        ConcatList list = this;
        if (index >= this.length) {
            return Maybe.empty();
        }
        // list is not an Empty
        while (list instanceof Fork) {
            ConcatList left = ((Fork) list).left;
            if (index < left.length) {
                list = left;
            } else {
                index -= left.length;
                list = ((Fork) list).right;
            }
        }
        return Maybe.of(((Leaf) list).data);
    }

    @Nullable
    abstract ConcatList updateInternal(int index, @Nonnull T element);

    @Nonnull
    public final Maybe> update(int index, @Nonnull T element) {
        return Maybe.fromNullable(this.updateInternal(index, element));
    }

    private final static class Empty extends ConcatList {
        private Empty() {
            super(0, true);
        }

        @Nonnull
        @Override
        public ImmutableList toList() {
            return ImmutableList.empty();
        }

        @Override
        public boolean isEmpty() {
            return true;
        }

        @Override
        public ConcatList balanced() {
            return this;
        }

        @Override
        Pair, ConcatList> splitInternal(int index) {
            return Pair.of(this, this);
        }

        @SuppressWarnings("unchecked")
        @Nonnull
        @Override
        public ConcatList append(@Nonnull ConcatList rhs) {
            return (ConcatList) rhs;
        }

        @Nullable
        @Override
        public ConcatList updateInternal(int index, @Nonnull T element) {
            return null;
        }

        @Override
        public Iterator iterator() {
            return new Iterator() {
                @Override
                public boolean hasNext() {
                    return false;
                }

                @Override
                public T next() {
                    return null;
                }
            };
        }
    }

    private final static class Leaf extends ConcatList {
        @Nonnull
        public final T data;

        private Leaf(@Nonnull T data) {
            super(1, true);
            this.data = data;
        }

        @Nonnull
        @Override
        public ImmutableList toList() {
            return ImmutableList.of(this.data);
        }

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

        @Override
        public ConcatList balanced() {
            return this;
        }

        @Override
        Pair, ConcatList> splitInternal(int index) {
            if (index == 0) {
                return Pair.of(ConcatList.empty(), this);
            } else {
                return Pair.of(this, ConcatList.empty());
            }
        }

        @SuppressWarnings("unchecked")
        @Nonnull
        @Override
        public ConcatList append(@Nonnull ConcatList rhs) {
            if (rhs instanceof Empty) {
                return this;
            }
            return new Fork<>(this, (ConcatList) rhs);
        }

        @Nullable
        @Override
        ConcatList updateInternal(int index, @Nonnull T element) {
            return index == 0 ? single(element) : null;
        }

        @Override
        public Iterator iterator() {
            return Collections.singleton(this.data).iterator();
        }
    }

    private final static class Fork extends ConcatList {
        @Nonnull
        public final ConcatList left, right;

        private Fork(@Nonnull ConcatList left, @Nonnull ConcatList right) {
            super(left.length + right.length, left.isBalanced && right.isBalanced && (left.length + right.length < 16 || left.length > right.length / 2 && left.length < right.length * 2));
            this.left = left;
            this.right = right;
        }

        @Nonnull
        @Override
        public ImmutableList toList() {
            ImmutableList out = ImmutableList.empty();
            final Stack> stack = new Stack<>();
            stack.push(this.left);
            ConcatList next = this.right;
            while (true) {
                if (next instanceof Fork) {
                    stack.push(((Fork) next).left);
                    next = ((Fork) next).right;
                } else if (next instanceof Leaf) {
                    out = out.cons(((Leaf) next).data);
                    if (stack.empty()) break;
                    next = stack.pop();
                } else { // Empty
                    if (stack.empty()) break;
                    next = stack.pop();
                }
            }
            return out;
        }

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

        @Override
        public ConcatList balanced() {
            if (this.isBalanced) {
                return this;
            }
            return ConcatList.fromListInternal(this.iterator(), 0, this.length);
        }

        @Override
        @Nonnull
        Pair, ConcatList> splitInternal(int index) {
            // Manually expanded zipper access
            Stack> zippers1 = new Stack<>();
            Stack zippers2 = new Stack<>();
            ConcatList list = this;
            while (list instanceof Fork) {
                ConcatList left = ((Fork) list).left;
                if (index < left.length) {
                    zippers1.push(((Fork) list).right);
                    zippers2.push(false);
                    list = left;
                } else {
                    index -= left.length;
                    zippers1.push(left);
                    zippers2.push(true);
                    list = ((Fork) list).right;
                }
            }
            // list is the first of the right hand side
            Pair, ConcatList> result = Pair.of(empty(), list);
            while (!zippers1.isEmpty()) {
                if (zippers2.pop()) {
                    result = Pair.of(
                            zippers1.pop().append(result.left()),
                            result.right());
                } else {
                    result = Pair.of(
                            result.left(),
                            result.right().append(zippers1.pop()));
                }
            }
            return result;
        }

        @SuppressWarnings("unchecked")
        @Nonnull
        @Override
        public ConcatList append(@Nonnull ConcatList rhs) {
            if (rhs instanceof Empty) {
                return this;
            }
            return new Fork<>(this, (ConcatList) rhs);
        }

        @Nullable
        @Override
        ConcatList updateInternal(int index, @Nonnull T element) {
            // Manually expanded zipper access
            if (index >= this.length) {
                return null;
            }
            Stack> zippers1 = new Stack<>();
            Stack zippers2 = new Stack<>();
            ConcatList list = this;
            while (list instanceof Fork) {
                ConcatList left = ((Fork) list).left;
                if (index < left.length) {
                    zippers1.push(((Fork) list).right);
                    zippers2.push(false);
                    list = left;
                } else {
                    index -= left.length;
                    zippers1.push(left);
                    zippers2.push(true);
                    list = ((Fork) list).right;
                }
            }
            ConcatList l = ConcatList.single(element);
            while (!zippers1.isEmpty()) {
                l = zippers2.pop() ? zippers1.pop().append(l) : l.append(zippers1.pop());
            }
            return l;
        }

        @Override
        public Iterator iterator() {
            return new Iterator() {
                @SuppressWarnings("unchecked")
                private final ConcatList[] stack = new ConcatList[Fork.this.length];
                int i = 0;

                {
                    this.stack[this.i++] = Fork.this;
                }

                @Override
                public boolean hasNext() {
                    return this.i > 0;
                }

                @Override
                public T next() {
                    while (this.i > 0) {
                        ConcatList curr = this.stack[--this.i];
                        if (curr instanceof Fork) {
                            Fork fork = (Fork) curr;
                            this.stack[this.i++] = fork.right;
                            this.stack[this.i++] = fork.left;
                        } else if (curr instanceof Leaf) {
                            return ((Leaf) curr).data;
                        }
                    }
                    throw new NoSuchElementException();
                }
            };
        }
    }

    private final static class ConcatListSplitIterator implements Spliterator {
        @Nonnull
        private final Deque> stack;
        private int size;

        private ConcatListSplitIterator(@Nonnull Deque> stack, int size) {
            this.stack = stack;
            this.size = size;
        }


        private ConcatListSplitIterator(@Nonnull ConcatList list) {
            // Manually expanded recursion
            this.stack = new ArrayDeque<>(list.length);
            this.stack.add(list);
            this.size = list.length;
        }

        @Override
        public boolean tryAdvance(Consumer action) {
            while (!this.stack.isEmpty()) {
                ConcatList curr = this.stack.pop();
                if (curr instanceof Fork) {
                    Fork fork = (Fork) curr;
                    this.stack.push(fork.right);
                    this.stack.push(fork.left);
                } else if (curr instanceof Leaf) {
                    this.size--;
                    action.accept(((Leaf) curr).data);
                    return true;
                }
            }
            return false;
        }

        @Override
        public Spliterator trySplit() {
            Deque> newDequeue = new ArrayDeque<>();
            newDequeue.addAll(this.stack);
            return new ConcatListSplitIterator<>(newDequeue, this.size);
        }

        @Override
        public long estimateSize() {
            return this.size;
        }

        @Override
        public int characteristics() {
            return IMMUTABLE | ORDERED | SUBSIZED | SIZED;
        }

        @Override
        public void forEachRemaining(Consumer action) {
            while (!this.stack.isEmpty()) {
                ConcatList curr = this.stack.pop();
                if (curr instanceof Fork) {
                    Fork fork = (Fork) curr;
                    this.stack.push(fork.right);
                    this.stack.push(fork.left);
                } else if (curr instanceof Leaf) {
                    this.size--;
                    action.accept(((Leaf) curr).data);
                }
            }
        }
    }

    @Override
    public Spliterator spliterator() {
        return new ConcatListSplitIterator<>(this);
    }

    private static class BinaryTreeMonoid implements Monoid> {
        @Nonnull
        @Override
        public ConcatList identity() {
            return new Empty<>();
        }

        @Nonnull
        @Override
        public ConcatList append(ConcatList a, ConcatList b) {
            return a.append(b);
        }
    }
}