/*
 Copyright (c) Rich Hickey. All rights reserved. The use and distribution terms for this software
 are covered by the Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
 which can be found in the file epl-v10.html at the root of this distribution. By using this
 software in any fashion, you are agreeing to be bound by the terms of this license. You must not
 remove this notice, or any other, from this software.
 */

/* rich May 20, 2006 */
package org.organicdesign.fp.collections;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Stack;

import org.organicdesign.fp.function.Fn1;
import org.organicdesign.fp.oneOf.Option;
import org.organicdesign.fp.tuple.Tuple2;

import static org.organicdesign.fp.FunctionUtils.stringify;

/**
 Persistent Red Black Tree. Note that instances of this class are constant values
 i.e. add/remove etc return new values.

 See Okasaki, Kahrs, Larsen et al

 This file is a derivative work based on a Clojure collection licensed under the Eclipse Public
 License 1.0 Copyright Rich Hickey

 @author Rich Hickey: Original author
 @author Glen Peterson: Added generic types, static factories, custom serialization, and made Nodes
         extend Tuple2.  All errors are Glen's.
 */

public class PersistentTreeMap<K,V> extends AbstractUnmodMap<K,V>
        implements ImSortedMap<K,V>, Serializable {

    /**
     This is a throw-away class used internally by PersistentTreeMap and PersistentHashMap like
     a mutable Option class to hold either null, or some value.  I don't want to remove this
     without checking the effect on performance.
     */
    static class Box<E> {
        E val;
        Box(E val) { this.val = val; }
    }

    /**
     Returns a new PersistentTreeMap of the given comparable keys and their paired values, skipping
     any null Entries.
     */
    public static <K extends Comparable<K>,V> PersistentTreeMap<K,V>
    of(Iterable<Map.Entry<K,V>> es) {
        if (es == null) { return empty(); }
        PersistentTreeMap<K,V> map = new PersistentTreeMap<>(Equator.defaultComparator(), null, 0);
        for (Map.Entry<K,V> entry : es) {
            if (entry != null) {
                map = map.assoc(entry.getKey(), entry.getValue());
            }
        }
        return map;
    }

    /**
     Returns a new PersistentTreeMap of the specified comparator and the given key/value pairs.

     @param comp A comparator (on the keys) that defines the sort order inside the new map.  This
     becomes a permanent part of the map and all sub-maps or appended maps derived from it.  If you
     want to use a null key, make sure the comparator treats nulls correctly in all circumstances!

     @param kvPairs Key/value pairs (to go into the map).  In the case of a duplicate key, later
     values in the input list overwrite the earlier ones.  The resulting map can contain zero or
     one null key (if your comparator knows how to sort nulls) and any number of null values.  Null
     k/v pairs will be silently ignored.

     @return a new PersistentTreeMap of the specified comparator and the given key/value pairs
     */
    public static <K,V> PersistentTreeMap<K,V>
    ofComp(Comparator<? super K> comp, Iterable<Map.Entry<K,V>> kvPairs) {
        if (kvPairs == null) { return new PersistentTreeMap<>(comp, null, 0); }
        PersistentTreeMap<K,V> map = new PersistentTreeMap<>(comp, null, 0);
        for (Map.Entry<K,V> entry : kvPairs) {
            if (entry != null) {
                map = map.assoc(entry.getKey(), entry.getValue());
            }
        }
        return map;
    }

    /**
     Be extremely careful with this because it uses the default comparator, which only works for
     items that implement Comparable (have a "natural ordering").  An attempt to use it with other
     items will blow up at runtime.  Either a withComparator() method will be added, or this will
     be removed.
     */
    final static public PersistentTreeMap EMPTY =
            new PersistentTreeMap<>(Equator.defaultComparator(), null, 0);

    /**
     Be extremely careful with this because it uses the default comparator, which only works for
     items that implement Comparable (have a "natural ordering").  An attempt to use it with other
     items will blow up at runtime.  Either a withComparator() method will be added, or this will
     be removed.
     */
    @SuppressWarnings("unchecked")
    public static <K extends Comparable<K>, V> PersistentTreeMap<K,V> empty() {
        return (PersistentTreeMap<K,V>) EMPTY;
    }

    /** Returns a new empty PersistentTreeMap that will use the specified comparator. */
    public static <K,V> PersistentTreeMap<K,V> empty(Comparator<? super K> c) {
        return new PersistentTreeMap<>(c, null, 0);
    }

    /**
     This would be private, except that PersistentTreeSet needs to check that the wrapped
     comparator is serializable.
     */
    static final class KeyComparator<T> implements Comparator<Map.Entry<T,?>>, Serializable {
        private static final long serialVersionUID = 20160827174100L;

        private final Comparator<? super T> wrappedComparator;

        private KeyComparator(Comparator<? super T> c) { wrappedComparator = c; }

        @Override public int compare(Map.Entry<T,?> a, Map.Entry<T,?> b) {
            return wrappedComparator.compare(a.getKey(), b.getKey());
        }

        @Override public String toString() {
            return "KeyComparator(" + wrappedComparator + ")";
        }

        /**
         This would be private, except that PersistentTreeSet needs to check that the wrapped
         comparator is serializable.
         */
        Comparator<? super T> unwrap() { return wrappedComparator; }
    }

    // ==================================== Instance Variables ====================================
    private final Comparator<? super K> comp;
    private final transient Node<K,V> tree;
    private final int size;

    // ======================================== Constructor ========================================
    private PersistentTreeMap(Comparator<? super K> c, Node<K,V> t, int n) {
        if (c == null) {
            throw new IllegalArgumentException("Comparator can't be null.");
        }
        comp = c; tree = t; size = n;
    }

//    /** Returns a new PersistentTreeMap of the given comparable keys and their paired values. */
//    public static <K extends Comparable<K>,V> PersistentTreeMap<K,V> of() {
//        return empty();
//    }

    // ======================================= Serialization =======================================
    // This class has a custom serialized form designed to be as small as possible.  It does not
    // have the same internal structure as an instance of this class.

    // For serializable.  Make sure to change whenever internal data format changes.
    private static final long serialVersionUID = 20160904095000L;

    // Check out Josh Bloch Item 78, p. 312 for an explanation of what's going on here.
    private static class SerializationProxy<K,V> implements Serializable {
        // For serializable.  Make sure to change whenever internal data format changes.
        private static final long serialVersionUID = 20160904095000L;

        private final Comparator<? super K> comparator;
        private final int size;
        private transient PersistentTreeMap<K,V> theMap;
        SerializationProxy(PersistentTreeMap<K,V> phm) {
            comparator = phm.comp;
            if ( !(comparator instanceof Serializable) ) {
                throw new IllegalStateException("Comparator must equal serializable." +
                                                "  Instead it was " + comparator);
            }
            size = phm.size;
            theMap = phm;
        }

        // Taken from Josh Bloch Item 75, p. 298
        private void writeObject(ObjectOutputStream s) throws IOException {
            s.defaultWriteObject();

            // Serializing in iteration-order yields a worst-case deserialization because
            // without re-balancing (rotating nodes) such an order yields an completely unbalanced
            // linked list internal structure.
            //       4
            //      /
            //     3
            //    /
            //   2
            //  /
            // 1
            //
            // That seems unnecessary since before Serialization we might have something like this
            // which, while not perfect, requires no re-balancing:
            //
            //                    11
            //            ,------'  `----.
            //           8                14
            //        ,-' `-.            /  \
            //       4       9         13    15
            //    ,-' `-.     \       /        \
            //   2       6     10   12          16
            //  / \     / \
            // 1   3   5   7
            //
            // If we serialize the middle value (n/2) first.  Then the n/4 and 3n/4,
            // followed by n/8, 3n/8, 5n/8, 7n/8, then n/16, 3n/16, etc.  Finally, the odd-numbered
            // values last.  That gives us the order:
            // 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15
            //
            // Deserializing in that order yields an ideally balanced tree without any shuffling:
            //               8
            //        ,-----' `-------.
            //       4                 12
            //    ,-' `-.          ,--'  `--.
            //   2       6       10          14
            //  / \     / \     /  \        /  \
            // 1   3   5   7   9    11    13    15
            //
            // That would be ideal, but I don't see how to do that without a significant
            // intermediate data structure.
            //
            // A good improvement could be made by serializing breadth-first instead of depth first
            // to at least yield a tree no worse than the original without requiring shuffling.
            //
            // This improvement does not change the serialized form, or break compatibility.
            // But it has a superior ordering for deserialization without (or with minimal)
            // rotations.

//            System.out.println("Serializing tree map...");
            if (theMap.tree != null) {
                Queue<Node<K,V>> queue = new ArrayDeque<>();
                queue.add(theMap.tree);
                while (queue.peek() != null) {
                    Node<K,V> node = queue.remove();
//                    System.out.println("Node: " + node);
                    s.writeObject(node.getKey());
                    s.writeObject(node.getValue());
                    Node<K,V> child = node.left();
                    if (child != null) {
                        queue.add(child);
                    }
                    child = node.right();
                    if (child != null) {
                        queue.add(child);
                    }
                }
            }
//            for (UnEntry<K,V> entry : theMap) {
//                s.writeObject(entry.getKey());
//                s.writeObject(entry.getValue());
//            }
        }

        @SuppressWarnings("unchecked")
        private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
            s.defaultReadObject();
            theMap = new PersistentTreeMap<>(comparator, null, 0);
            for (int i = 0; i < size; i++) {
                theMap = theMap.assoc((K) s.readObject(), (V) s.readObject());
            }
        }

        private Object readResolve() { return theMap; }
    }

    private Object writeReplace() { return new SerializationProxy<>(this); }

    private void readObject(java.io.ObjectInputStream in) throws IOException,
            ClassNotFoundException {
        throw new InvalidObjectException("Proxy required");
    }

    // ===================================== Instance Methods =====================================
    /**
     Returns a view of the mappings contained in this map.  The set should actually contain
     UnmodMap.UnEntry items, but that return signature is illegal in Java, so you'll just have to
     remember.
     */
    @Override public ImSortedSet<Entry<K,V>> entrySet() {
        // This is the pretty way to do it.
        return this.fold(PersistentTreeSet.ofComp(new KeyComparator<>(comp)),
                         PersistentTreeSet::put);
    }

//    public static final Equator<SortedMap> EQUATOR = new Equator<SortedMap>() {
//        @Override
//        public int hash(SortedMap kvSortedMap) {
//            return UnmodIterable.hashCode(kvSortedMap.entrySet());
//        }
//
//        @Override
//        public boolean eq(SortedMap o1, SortedMap o2) {
//            if (o1 == o2) { return true; }
//            if ( o1.size() != o2.size() ) { return false; }
//            return UnmodSortedIterable.equals(UnmodSortedIterable.castFromSortedMap(o1),
//                                              UnmodSortedIterable.castFromSortedMap(o2));
//        }
//    };

//    /** Returns a view of the keys contained in this map. */
//    @Override public ImSet<K> keySet() { return PersistentTreeSet.ofMap(this); }

    /** {@inheritDoc} */
    @Override public ImSortedMap<K,V> subMap(K fromKey, K toKey) {
        int diff = comp.compare(fromKey, toKey);

        if (diff > 0) {
            throw new IllegalArgumentException("fromKey is greater than toKey");
        }
        UnEntry<K,V> last = last();
        K lastKey = last.getKey();
        int compFromKeyLastKey = comp.compare(fromKey, lastKey);

        // If no intersect, return empty. We aren't checking the toKey vs. the firstKey() because
        // that's a single pass through the iterator loop which is probably as cheap as checking
        // here.
        if ( (diff == 0) || (compFromKeyLastKey > 0) )  {
            return new PersistentTreeMap<>(comp, null, 0);
        }
        // If map is entirely contained, just return it.
        if ( (comp.compare(fromKey, firstKey()) <= 0) &&
                    (comp.compare(toKey, lastKey) > 0) ) {
            return this;
        }
        // Don't iterate through entire map for only the last item.
        if (compFromKeyLastKey == 0) {
            return ofComp(comp, Collections.singletonList(last));
        }

        ImSortedMap<K,V> ret = new PersistentTreeMap<>(comp, null, 0);
        UnmodIterator<UnEntry<K,V>> iter = this.iterator();
        while (iter.hasNext()) {
            UnEntry<K,V> next = iter.next();
            K key = next.getKey();
            if (comp.compare(toKey, key) <= 0) {
                break;
            }
            if (comp.compare(fromKey, key) > 0) {
                continue;
            }
            ret = ret.assoc(key, next.getValue());
        }
        return ret;
    }

//    String debugStr() {
//        return "PersistentTreeMap(size=" + size +
//               " comp=" + comp +
//               " tree=" + tree + ")";
//    }

//    /** {@inheritDoc} */
//    @Override public UnmodCollection<V> values() {
//        class ValueColl<B,Z> implements UnmodCollection<B>, UnmodSortedIterable<B> {
//            private final Fn0<UnmodSortedIterator<UnEntry<Z,B>>> iterFactory;
//            private ValueColl(Fn0<UnmodSortedIterator<UnEntry<Z, B>>> f) { iterFactory = f; }
//
//            @Override public int size() { return size; }
//
//            @Override public UnmodSortedIterator<B> iterator() {
//                final UnmodSortedIterator<UnmodMap.UnEntry<Z,B>> iter = iterFactory.apply();
//                return new UnmodSortedIterator<B>() {
//                    @Override public boolean hasNext() { return iter.hasNext(); }
//                    @Override public B next() { return iter.next().getValue(); }
//                };
//            }
//            @Override public int hashCode() { return UnmodIterable.hashCode(this); }
//            @Override public boolean equals(Object o) {
//                if (this == o) { return true; }
//                if ( !(o instanceof UnmodSortedIterable) ) { return false; }
//                return UnmodSortedIterable.equals(this, (UnmodSortedIterable) o);
//            }
//            @Override public String toString() {
//                return UnmodSortedIterable.toString("ValueColl", this);
//            }
//        }
//        return new ValueColl<>(() -> this.iterator());
//    }

    /** {@inheritDoc} */
    @Override public Option<UnEntry<K,V>> head() {
        Node<K,V> t = tree;
        if (t != null) {
            while (t.left() != null) {
                t = t.left();
            }
        }
        return Option.some(t);
    }

    /** {@inheritDoc} */
    @Override public ImSortedMap<K,V> tailMap(K fromKey) {
        UnEntry<K,V> last = last();
        K lastKey = last.getKey();
        int compFromKeyLastKey = comp.compare(fromKey, lastKey);

        // If no intersect, return empty. We aren't checking the toKey vs. the firstKey() because
        // that's a single pass through the iterator loop which is probably as cheap as checking
        // here.
        if (compFromKeyLastKey > 0) {
            return new PersistentTreeMap<>(comp, null, 0);
        }
        // If map is entirely contained, just return it.
        if (comp.compare(fromKey, firstKey()) <= 0) {
            return this;
        }
        // Don't iterate through entire map for only the last item.
        if (compFromKeyLastKey == 0) {
            return ofComp(comp, Collections.singletonList(last));
        }

        ImSortedMap<K,V> ret = new PersistentTreeMap<>(comp, null, 0);
        UnmodIterator<UnEntry<K,V>> iter = this.iterator();
        while (iter.hasNext()) {
            UnEntry<K,V> next = iter.next();
            K key = next.getKey();
            if (comp.compare(fromKey, key) > 0) {
                continue;
            }
            ret = ret.assoc(key, next.getValue());
        }
        return ret;
    }

//    /** {@inheritDoc} */
//    @Override public Sequence<UnEntry<K,V>> tail() {
//        if (size() > 1) {
//            return without(firstKey());
////            // The iterator is designed to do this quickly.  It also prevents an infinite loop.
////            UnmodIterator<UnEntry<K,V>> iter = this.iterator();
////            // Drop the head
////            iter.next();
////            return tailMap(iter.next().getKey());
//        }
//        return Sequence.emptySequence();
//    }

//    @SuppressWarnings("unchecked")
//    static public <S, K extends S, V extends S> PersistentTreeMap<K,V> create(ISeq<S> items) {
//        PersistentTreeMap<K,V> ret = empty();
//        for (; items != null; items = items.next().next()) {
//            if (items.next() == null)
//                throw new IllegalArgumentException(String.format("No value supplied for key: %s",
//                                                                 items.head()));
//            ret = ret.assoc((K) items.head(), (V) RT.second(items));
//        }
//        return ret;
//    }

//    @SuppressWarnings("unchecked")
//    static public <S, K extends S, V extends S>
//    PersistentTreeMap<K,V> create(Comparator<? super K> comp, ISeq<S> items) {
//        PersistentTreeMap<K,V> ret = new PersistentTreeMap<>(comp);
//        for (; items != null; items = items.next().next()) {
//            if (items.next() == null)
//                throw new IllegalArgumentException(String.format("No value supplied for key: %s",
//                                                                 items.head()));
//            ret = ret.assoc((K) items.head(), (V) RT.second(items));
//        }
//        return ret;
//    }

    /**
     Returns the comparator used to order the keys in this map, or null if it uses
     Fn2.DEFAULT_COMPARATOR (for compatibility with java.util.SortedMap).
     */
    @Override public Comparator<? super K> comparator() {
        return (comp == Equator.Comp.DEFAULT) ? null : comp;
    }

//    /** Returns true if the map contains the given key. */
//    @SuppressWarnings("unchecked")
//    @Override public boolean containsKey(Object key) {
//        return entryAt((K) key) != null;
//    }

//    /** Returns the value associated with the given key. */
//    @SuppressWarnings("unchecked")
//    @Override
//    public V get(Object key) {
//        if (key == null) { return null; }
//        Entry<K,V> entry = entryAt((K) key);
//        if (entry == null) { return null; }
//        return entry.getValue();
//    }

// public PersistentTreeMap<K,V> assocEx(K key, V val) {
// Inherits default implementation of assocEx from IPersistentMap

    /** {@inheritDoc} */
    @Override public PersistentTreeMap<K,V> assoc(K key, V val) {
        Box<Node<K,V>> found = new Box<>(null);
        Node<K,V> t = add(tree, key, val, found);
        //null == already contains key
        if (t == null) {
            Node<K,V> foundNode = found.val;

            //note only get same collection on identity of val, not equals()
            if (foundNode.getValue() == val) {
                return this;
            }
            return new PersistentTreeMap<>(comp, replace(tree, key, val), size);
        }
        return new PersistentTreeMap<>(comp, t.blacken(), size + 1);
    }

    /** {@inheritDoc} */
    @Override public PersistentTreeMap<K,V> without(K key) {
        Box<Node<K,V>> found = new Box<>(null);
        Node<K,V> t = remove(tree, key, found);
        if (t == null) {
            //null == doesn't contain key
            if (found.val == null) {
                return this;
            }
            //empty
            return new PersistentTreeMap<>(comp, null, 0);
        }
        return new PersistentTreeMap<>(comp, t.blacken(), size - 1);
    }

//    @Override
//    public ISeq<Map.Entry<K,V>> seq() {
//        if (size > 0)
//            return Iter.create(tree, true, size);
//        return null;
//    }
//
//    @Override
//    public ISeq<Map.Entry<K,V>> rseq() {
//        if (size > 0)
//            return Iter.create(tree, false, size);
//        return null;
//    }

//    @Override
//    public Object entryKey(Map.Entry<K,V> entry) {
//        return entry.getKey();
//    }

//    // This lets you make a sequence of map entries from this HashMap.
//// The other methods on Sorted seem to care only about the key, and the implementations of them
//// here work that way.  This one, however, returns a sequence of Map.Entry<K,V> or Node<K,V>
//// If I understood why, maybe I could do better.
//    @SuppressWarnings("unchecked")
//    @Override
//    public ISeq<Map.Entry<K,V>> seq(boolean ascending) {
//        if (size > 0)
//            return Iter.create(tree, ascending, size);
//        return null;
//    }

//    @SuppressWarnings("unchecked")
//    @Override
//    public ISeq<Map.Entry<K,V>> seqFrom(Object key, boolean ascending) {
//        if (size > 0) {
//            ISeq<Node<K,V>> stack = null;
//            Node<K,V> t = tree;
//            while (t != null) {
//                int c = doCompare((K) key, t.key);
//                if (c == 0) {
//                    stack = RT.cons(t, stack);
//                    return new Iter<>(stack, ascending);
//                } else if (ascending) {
//                    if (c < 0) {
//                        stack = RT.cons(t, stack);
//                        t = t.left();
//                    } else
//                        t = t.right();
//                } else {
//                    if (c > 0) {
//                        stack = RT.cons(t, stack);
//                        t = t.right();
//                    } else
//                        t = t.left();
//                }
//            }
//            if (stack != null)
//                return new Iter<>(stack, ascending);
//        }
//        return null;
//    }

    /** {@inheritDoc} */
    @Override
    public UnmodSortedIterator<UnEntry<K,V>> iterator() { return iterator(Tuple2::of); }

    @Override
    public UnmodSortedIterator<K> keyIterator() { return iterator(Node::getKey); }

    @Override
    public UnmodSortedIterator<V> valIterator() { return iterator(Node::getValue); }

    public <R> UnmodSortedIterator<R> iterator(Fn1<Node<K,V>,R> aFn) { return new NodeIterator<>(tree, true, aFn); }

//    public NodeIterator<K,V> reverseIterator() { return new NodeIterator<>(tree, false); }

    /** Returns the first key in this map or throws a NoSuchElementException if the map is empty. */
    @Override public K firstKey() {
        if (size() < 1) { throw new NoSuchElementException("this map is empty"); }
        return head().get().getKey();
    }

    /** Returns the last key in this map or throws a NoSuchElementException if the map is empty. */
    @Override public K lastKey() {
        UnEntry<K,V> max = last();
        if (max == null) {
            throw new NoSuchElementException("this map is empty");
        }
        return max.getKey();
    }

    /** Returns the last key/value pair in this map, or null if the map is empty. */
    public UnEntry<K,V> last() {
        Node<K,V> t = tree;
        if (t != null) {
            while (t.right() != null)
                t = t.right();
        }
        return t;
    }

//    public int depth() {
//        return depth(tree);
//    }

//    int depth(Node<K,V> t) {
//        if (t == null)
//            return 0;
//        return 1 + Math.max(depth(t.left()), depth(t.right()));
//    }

// public Object valAt(Object key){
// Default implementation now inherited from ILookup

    /** Returns the number of key/value mappings in this map. */
    @Override public int size() { return size; }

    /**
     Returns an Option of the key/value pair matching the given key, or Option.none() if the key is
     not found.
     */
    @Override public Option<UnmodMap.UnEntry<K,V>> entry(K key) {
        Node<K,V> t = tree;
        while (t != null) {
            int c = comp.compare(key, t.getKey());
            if (c == 0)
                return Option.some(t);
            else if (c < 0)
                t = t.left();
            else
                t = t.right();
        }
        return Option.none(); // t; // t is always null
    }

//    // In TreeMap, this is final Entry<K,V> getEntry(Object key)
//    /** Returns the key/value pair matching the given key, or null if the key is not found. */
//    public UnEntry<K,V> entryAt(K key) {
//        Node<K,V> t = tree;
//        while (t != null) {
//            int c = comp.compare(key, t.key);
//            if (c == 0)
//                return t;
//            else if (c < 0)
//                t = t.left();
//            else
//                t = t.right();
//        }
//        return null; // t; // t is always null
//    }

    private Node<K,V> add(Node<K,V> t, K key, V val, Box<Node<K,V>> found) {
        if (t == null) {
//            if (val == null)
//                return new Red<>(key);
            return new Red<>(key, val);
        }
        int c = comp.compare(key, t.getKey());
        if (c == 0) {
            found.val = t;
            return null;
        }
        Node<K,V> ins = add(c < 0 ? t.left() : t.right(),
                            key, val, found);
        if (ins == null) //found below
            return null;
        if (c < 0)
            return t.addLeft(ins);
        return t.addRight(ins);
    }

    private Node<K,V> remove(Node<K,V> t, K key, Box<Node<K,V>> found) {
        if (t == null)
            return null; //not found indicator
        int c = comp.compare(key, t.getKey());
        if (c == 0) {
            found.val = t;
            return append(t.left(), t.right());
        }
        Node<K,V> del = remove(c < 0 ? t.left() : t.right(),
                               key, found);
        if (del == null && found.val == null) //not found below
            return null;
        if (c < 0) {
            if (t.left() instanceof PersistentTreeMap.Black)
                return balanceLeftDel(t.getKey(), t.getValue(), del, t.right());
            else
                return red(t.getKey(), t.getValue(), del, t.right());
        }
        if (t.right() instanceof PersistentTreeMap.Black)
            return balanceRightDel(t.getKey(), t.getValue(), t.left(), del);
        return red(t.getKey(), t.getValue(), t.left(), del);
//		return t.removeLeft(del);
//	return t.removeRight(del);
    }

    //static <K,V, K1 extends K, K2 extends K, V1 extends V, V2 extends V>
//Node<K,V> concat(Node<K1,V1> left, Node<K2,V2> right){
    @SuppressWarnings("unchecked")
    private static <K, V> Node<K,V> append(Node<? extends K,? extends V> left,
                                   Node<? extends K,? extends V> right) {
        if (left == null)
            return (Node<K,V>) right;
        else if (right == null)
            return (Node<K,V>) left;
        else if (left instanceof PersistentTreeMap.Red) {
            if (right instanceof PersistentTreeMap.Red) {
                Node<K,V> app = append(left.right(), right.left());
                if (app instanceof PersistentTreeMap.Red)
                    return red(app.getKey(), app.getValue(),
                               red(left.getKey(), left.getValue(), left.left(), app.left()),
                               red(right.getKey(), right.getValue(), app.right(), right.right()));
                else
                    return red(left.getKey(), left.getValue(), left.left(),
                               red(right.getKey(), right.getValue(), app, right.right()));
            } else
                return red(left.getKey(), left.getValue(), left.left(), append(left.right(), right));
        } else if (right instanceof PersistentTreeMap.Red)
            return red(right.getKey(), right.getValue(), append(left, right.left()), right.right());
        else //black/black
        {
            Node<K,V> app = append(left.right(), right.left());
            if (app instanceof PersistentTreeMap.Red)
                return red(app.getKey(), app.getValue(),
                           black(left.getKey(), left.getValue(), left.left(), app.left()),
                           black(right.getKey(), right.getValue(), app.right(), right.right()));
            else
                return balanceLeftDel(left.getKey(), left.getValue(), left.left(),
                                      black(right.getKey(), right.getValue(), app, right.right()));
        }
    }

    private static <K, V, K1 extends K, V1 extends V>
    Node<K,V> balanceLeftDel(K1 key, V1 val,
                             Node<? extends K,? extends V> del,
                             Node<? extends K,? extends V> right) {
        if (del instanceof PersistentTreeMap.Red)
            return red(key, val, del.blacken(), right);
        else if (right instanceof PersistentTreeMap.Black)
            return rightBalance(key, val, del, right.redden());
        else if (right instanceof PersistentTreeMap.Red && right.left() instanceof PersistentTreeMap.Black)
            return red(right.left().getKey(), right.left().getValue(),
                       black(key, val, del, right.left().left()),
                       rightBalance(right.getKey(), right.getValue(), right.left().right(),
                                    right.right().redden()));
        else
            throw new UnsupportedOperationException("Invariant violation");
    }

    private static <K, V, K1 extends K, V1 extends V>
    Node<K,V> balanceRightDel(K1 key, V1 val,
                              Node<? extends K,? extends V> left,
                              Node<? extends K,? extends V> del) {
        if (del instanceof PersistentTreeMap.Red)
            return red(key, val, left, del.blacken());
        else if (left instanceof PersistentTreeMap.Black)
            return leftBalance(key, val, left.redden(), del);
        else if (left instanceof PersistentTreeMap.Red && left.right() instanceof PersistentTreeMap.Black)
            return red(left.right().getKey(), left.right().getValue(),
                       leftBalance(left.getKey(), left.getValue(), left.left().redden(), left.right().left()),
                       black(key, val, left.right().right(), del));
        else
            throw new UnsupportedOperationException("Invariant violation");
    }

    private static <K, V, K1 extends K, V1 extends V>
    Node<K,V> leftBalance(K1 key, V1 val,
                          Node<? extends K,? extends V> ins,
                          Node<? extends K,? extends V> right) {
        if (ins instanceof PersistentTreeMap.Red && ins.left() instanceof PersistentTreeMap.Red)
            return red(ins.getKey(), ins.getValue(), ins.left().blacken(),
                       black(key, val, ins.right(), right));
        else if (ins instanceof PersistentTreeMap.Red && ins.right() instanceof PersistentTreeMap.Red)
            return red(ins.right().getKey(), ins.right().getValue(),
                       black(ins.getKey(), ins.getValue(), ins.left(), ins.right().left()),
                       black(key, val, ins.right().right(), right));
        else
            return black(key, val, ins, right);
    }

    private static <K, V, K1 extends K, V1 extends V>
    Node<K,V> rightBalance(K1 key, V1 val,
                           Node<? extends K,? extends V> left,
                           Node<? extends K,? extends V> ins) {
        if (ins instanceof PersistentTreeMap.Red && ins.right() instanceof PersistentTreeMap.Red)
            return red(ins.getKey(), ins.getValue(), black(key, val, left, ins.left()),
                       ins.right().blacken());
        else if (ins instanceof PersistentTreeMap.Red && ins.left() instanceof PersistentTreeMap.Red)
            return red(ins.left().getKey(), ins.left().getValue(),
                       black(key, val, left, ins.left().left()),
                       black(ins.getKey(), ins.getValue(), ins.left().right(), ins.right()));
        else
            return black(key, val, left, ins);
    }

    private Node<K,V> replace(Node<K,V> t, K key, V val) {
        int c = comp.compare(key, t.getKey());
        return t.replace(t.getKey(),
                         c == 0 ? val : t.getValue(),
                         c < 0 ? replace(t.left(), key, val) : t.left(),
                         c > 0 ? replace(t.right(), key, val) : t.right());
    }

    @SuppressWarnings({"unchecked", "RedundantCast", "Convert2Diamond"})
    private static <K, V, K1 extends K, V1 extends V>
    Red<K,V> red(K1 key, V1 val,
                 Node<? extends K,? extends V> left,
                 Node<? extends K,? extends V> right) {
        if (left == null && right == null) {
//            if (val == null)
//                return new Red<K,V>(key, val);
            return new Red<K,V>(key, val);
        }
//        if (val == null)
//            return new RedBranch<K,V>((K) key, (Node<K,V>) left, (Node<K,V>) right);
        return new RedBranch<K,V>((K) key, (V) val, (Node<K,V>) left, (Node<K,V>) right);
    }

    @SuppressWarnings({"unchecked", "RedundantCast", "Convert2Diamond"})
    private static <K, V, K1 extends K, V1 extends V>
    Black<K,V> black(K1 key, V1 val,
                     Node<? extends K,? extends V> left,
                     Node<? extends K,? extends V> right) {
        if (left == null && right == null) {
//            if (val == null)
//                return new Black<>(key);
            return new Black<K,V>(key, val);
        }
//        if (val == null)
//            return new BlackBranch<K,V>((K) key, (Node<K,V>) left, (Node<K,V>) right);
        return new BlackBranch<K,V>((K) key, (V) val, (Node<K,V>) left, (Node<K,V>) right);
    }

//    public static class Reduced<A> {
//        public final A val;
//        private Reduced(A a) { val = a; }
//    }

    private static abstract class Node<K, V> extends Tuple2<K,V> {
        Node(K key, V val) { super(key, val); }

        Node<K,V> left() { return null; }

        Node<K,V> right() { return null; }

        abstract Node<K,V> addLeft(Node<K,V> ins);

        abstract Node<K,V> addRight(Node<K,V> ins);

        @SuppressWarnings("UnusedDeclaration")
        abstract Node<K,V> removeLeft(Node<K,V> del);

        @SuppressWarnings("UnusedDeclaration")
        abstract Node<K,V> removeRight(Node<K,V> del);

        abstract Node<K,V> blacken();

        abstract Node<K,V> redden();

        Node<K,V> balanceLeft(Node<K,V> parent) {
            return black(parent._1, parent._2, this, parent.right());
        }

        Node<K,V> balanceRight(Node<K,V> parent) {
            return black(parent._1, parent._2, parent.left(), this);
        }

        abstract Node<K,V> replace(K key, V val, Node<K,V> left, Node<K,V> right);

        @Override public String toString() {
            return stringify(_1) + "=" + stringify(_2);
        }

//        public <R> R kvreduce(Fn3<R,K,V,R> f, R init) {
//            if (left() != null) {
//                init = left().kvreduce(f, init);
//                if (init instanceof Reduced)
//                    return init;
//            }
//            init = f.apply(init, key(), val());
//            if (init instanceof Reduced)
//                return init;
//
//            if (right() != null) {
//                init = right().kvreduce(f, init);
//            }
//            return init;
//        }
    } // end class Node.

    private static class Black<K, V> extends Node<K,V> {
        Black(K key, V val) { super(key, val); }

        @Override Node<K,V> addLeft(Node<K,V> ins) { return ins.balanceLeft(this); }

        @Override Node<K,V> addRight(Node<K,V> ins) { return ins.balanceRight(this); }

        @Override Node<K,V> removeLeft(Node<K,V> del) {
            return balanceLeftDel(_1, _2, del, right());
        }

        @Override Node<K,V> removeRight(Node<K,V> del) {
            return balanceRightDel(_1, _2, left(), del);
        }

        @Override Node<K,V> blacken() { return this; }

        @Override Node<K,V> redden() { return new Red<>(_1, _2); }

        @Override
        Node<K,V> replace(K key, V val, Node<K,V> left, Node<K,V> right) {
            return black(key, val, left, right);
        }
    }

    private static class BlackBranch<K, V> extends Black<K,V> {
        final transient Node<K,V> left;
        final transient Node<K,V> right;

        BlackBranch(K key, V val, Node<K,V> l, Node<K,V> r) {
            super(key, val); left = l; right = r;
        }

        @Override public Node<K,V> left() { return left; }

        @Override public Node<K,V> right() { return right; }

        @Override Node<K,V> redden() { return new RedBranch<>(_1, _2, left, right); }
    }

    private static class Red<K, V> extends Node<K,V> {
        Red(K key, V val) { super(key, val); }

        @Override Node<K,V> addLeft(Node<K,V> ins) { return red(_1, _2, ins, right()); }

        @Override Node<K,V> addRight(Node<K,V> ins) { return red(_1, _2, left(), ins); }

        @Override Node<K,V> removeLeft(Node<K,V> del) { return red(_1, _2, del, right()); }

        @Override Node<K,V> removeRight(Node<K,V> del) { return red(_1, _2, left(), del); }

        @Override Node<K,V> blacken() { return new Black<>(_1, _2); }

        @Override
        Node<K,V> redden() { throw new UnsupportedOperationException("Invariant violation"); }

        @Override
        Node<K,V> replace(K key, V val, Node<K,V> left, Node<K,V> right) {
            return red(key, val, left, right);
        }
    }

    private static class RedBranch<K, V> extends Red<K,V> {
        final transient Node<K,V> left;
        final transient Node<K,V> right;

        RedBranch(K key, V val, Node<K,V> left, Node<K,V> right) {
            super(key, val);
            this.left = left;
            this.right = right;
        }

        @Override public Node<K,V> left() { return left; }

        @Override public Node<K,V> right() { return right; }

        @Override Node<K,V> balanceLeft(Node<K,V> parent) {
            if (left instanceof PersistentTreeMap.Red)
                return red(_1, _2, left.blacken(),
                           black(parent.getKey(), parent.getValue(), right, parent.right()));
            else if (right instanceof PersistentTreeMap.Red)
                return red(right.getKey(), right.getValue(), black(_1, _2, left, right.left()),
                           black(parent.getKey(), parent.getValue(), right.right(), parent.right()));
            else
                return super.balanceLeft(parent);

        }

        @Override Node<K,V> balanceRight(Node<K,V> parent) {
            if (right instanceof PersistentTreeMap.Red)
                return red(_1, _2,
                           black(parent.getKey(), parent.getValue(), parent.left(), left),
                           right.blacken());
            else if (left instanceof PersistentTreeMap.Red)
                return red(left.getKey(), left.getValue(),
                           black(parent.getKey(), parent.getValue(), parent.left(), left.left()),
                           black(_1, _2, left.right(), right));
            else
                return super.balanceRight(parent);
        }

        @Override Node<K,V> blacken() { return new BlackBranch<>(_1, _2, left, right); }
    }

//    static public class Iter<K, V> extends ASeq<Map.Entry<K,V>> {
//        final ISeq<Node<K,V>> stack;
//        final boolean asc;
//        final int cnt;
//
//        public Iter(ISeq<Node<K,V>> stack, boolean asc) {
//            this.stack = stack;
//            this.asc = asc;
//            this.cnt = -1;
//        }
//
//        public Iter(ISeq<Node<K,V>> stack, boolean asc, int cnt) {
//            this.stack = stack;
//            this.asc = asc;
//            this.cnt = cnt;
//        }
//
//        Iter(ISeq<Node<K,V>> stack, boolean asc, int cnt) {
//            super();
//            this.stack = stack;
//            this.asc = asc;
//            this.cnt = cnt;
//        }
//
//        static <K, V> Iter<K,V> create(Node<K,V> t, boolean asc, int cnt) {
//            return new Iter<>(push(t, null, asc), asc, cnt);
//        }
//
//        static <K, V> ISeq<Node<K,V>> push(Node<K,V> t, ISeq<Node<K,V>> stack, boolean asc) {
//            while (t != null) {
//                stack = RT.cons(t, stack);
//                t = asc ? t.left() : t.right();
//            }
//            return stack;
//        }
//
//        @Override
//        public Node<K,V> head() {
//            return stack.head();
//        }
//
//        @Override
//        public ISeq<Map.Entry<K,V>> next() {
//            Node<K,V> t = stack.head();
//            ISeq<Node<K,V>> nextstack = push(asc ? t.right() : t.left(), stack.next(), asc);
//            if (nextstack != null) {
//                return new Iter<>(nextstack, asc, cnt - 1);
//            }
//            return null;
//        }
//
//        @Override
//        public int count() {
//            if (cnt < 0)
//                return super.count();
//            return cnt;
//        }
//    }

    /**
     This currently returns chunks of the inner tree structure that implement Map.Entry.
     They are not serializable and should not be made so.  I can alter this to return nice,
     neat, Tuple2 objects which are serializable, but we've made it this far without so...
     */
    private static class NodeIterator<K,V,R> implements UnmodSortedIterator<R> {
        //, Serializable {
        // For serializable.  Make sure to change whenever internal data format changes.
        // private static final long serialVersionUID = 20160827174100L;

        private Stack<Node<K,V>> stack = new Stack<>();
        private final boolean asc;
        private Fn1<Node<K, V>, R> aFn;

        NodeIterator(Node<K,V> t, boolean asc, Fn1<Node<K,V>,R> aFn) {
            this.asc = asc;
            this.aFn = aFn;
            push(t);
        }

        private void push(Node<K,V> t) {
            while (t != null) {
                stack.push(t);
                t = asc ? t.left() : t.right();
            }
        }

        @Override public boolean hasNext() {
            return !stack.isEmpty();
        }

        @SuppressWarnings("unchecked")
        @Override public R next() {
            Node<K,V> t = stack.pop();
            push(asc ? t.right() : t.left());

            return aFn.apply(t);
        }
    }

//    static class KeyIterator<K> implements Iterator<K> {
//        NodeIterator<K,?> it;
//
//        KeyIterator(NodeIterator<K,?> it) {
//            this.it = it;
//        }
//
//        @Override
//        public boolean hasNext() {
//            return it.hasNext();
//        }
//
//        @Override
//        public K next() {
//            return it.next().getKey();
//        }
//
//        @Override
//        public void remove() {
//            throw new UnsupportedOperationException();
//        }
//    }
//
//    static class ValIterator<V> implements Iterator<V> {
//        NodeIterator<?,V> it;
//
//        ValIterator(NodeIterator<?,V> it) {
//            this.it = it;
//        }
//
//        @Override
//        public boolean hasNext() {
//            return it.hasNext();
//        }
//
//        @Override
//        public V next() {
//            return it.next().getValue();
//        }
//
//        @Override
//        public void remove() {
//            throw new UnsupportedOperationException();
//        }
//    }
}


// Copyright 2014-08-30 PlanBase Inc. & Glen Peterson
//
// 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 org.organicdesign.fp.xform;

import org.organicdesign.fp.collections.UnmodIterable;
import org.organicdesign.fp.collections.UnmodIterator;
import org.organicdesign.fp.function.Fn1;
import org.organicdesign.fp.function.Fn2;
import org.organicdesign.fp.oneOf.Or;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

/**
 An immutable description of operations to be performed (a transformation, transform, or x-form).
 When fold() (or another terminating function) is called, the Xform definition is "compiled" into
 a one-time mutable transformation which is then carried out.  This allows certain performance
 shortcuts (such as doing a drop with index addition instead of iteration) and also hides the
 mutability otherwise inherent in a transformation.

 Xform is an abstract class.  Most of the methods on Xform produce immutable descriptions of actions
 to take at a later time.  These are represented by ___Desc classes.  When fold() is called
 (or any of the helper methods that wrap it), that produces a result by first stringing together
 a bunch of Operations (____Op classes) and then "running" them.  This is analogous to compiling
 a program and running it.  The ____Desc classes are like the immutable source, the ____Op classes
 like the op-codes it's compiled into.

 Special thanks to Nathan Williams for pointing me toward separating the mutation from the
 description of a transformation.  Also to Paul Phillips (@extempore2) whose lectures provided
 an outline for what was ideal and also what was important.  All errors are my own.
 -Glen 2015-08-30
 */
public abstract class Xform<A> implements UnmodIterable<A> {

    enum OpStrategy { HANDLE_INTERNALLY, ASK_SUPPLIER, CANNOT_HANDLE }

    private static final Object TERMINATE = new Object();
    @SuppressWarnings("unchecked")
    private A terminate() { return (A) TERMINATE; }

    /**
     These are mutable operations that the transform carries out when it is run.  This is like the
     compiled "op codes" in contrast to the Xform is like the immutable "source code" of the
     transformation description.
     */
    static abstract class Operation {
        // Time using a linked list of ops instead of array, so that we can easily remove ops from
        // the list when they are used up.
        Fn1<Object,Boolean> filter = null;
        Fn1 map = null;
        Fn1<Object,Iterable> flatMap = null;

        /**
         Drops as many items as the source can handle.
         @param num the number of items to drop
         @return  whether the source can handle the take, or pass-through (ask-supplier), or can't
         do either.
         */
        public Or<Long,OpStrategy> drop(long num) {
            return (num < 1) ? Or.good(0L)
                             : Or.bad(OpStrategy.CANNOT_HANDLE);
        }

        /**
         Takes as many items as the source can handle.
         @param num the number of items to take.
         @return whether the source can handle the take, or pass-through (ask-supplier), or can't
         do either.
         */
        public OpStrategy take(long num) { return OpStrategy.CANNOT_HANDLE; }

        /**
         We need to model this as a separate op for when the previous op is CANNOT_HANDLE.  It is
         coded as a filter, but still needs to be modeled separately so that subsequent drops can be
         combined into the earliest single explicit drop op.  Such combinations are additive,
         meaning that drop(3).drop(5) is equivalent to drop(8).
         */
        private static class DropOp extends Operation {
            private long leftToDrop;
            DropOp(long drop) {
                leftToDrop = drop;
                filter = o -> {
                    if (leftToDrop > 0) {
                        leftToDrop = leftToDrop - 1;
                        return Boolean.FALSE;
                    }
                    return Boolean.TRUE;
                };
            }
            @Override public Or<Long,OpStrategy> drop(long num) {
                leftToDrop = leftToDrop + num;
                return Or.good(num);
            }
        }

        private static class FilterOp extends Operation {
            FilterOp(Fn1<Object,Boolean> func) { filter = func; }
        }

        private static class MapOp extends Operation {
            MapOp(Fn1 func) { map = func; }
            @Override public Or<Long,OpStrategy> drop(long num) {
                return Or.bad(OpStrategy.ASK_SUPPLIER);
            }
            @Override public OpStrategy take(long num) { return OpStrategy.ASK_SUPPLIER; }
        }

        // TODO: FlatMap should drop and take internally using addition/subtraction on each output
        // TODO: list instead of testing each list item individually.
        private static class FlatMapOp extends Operation {
//            ListSourceDesc<U> cache = null;
//            int numToDrop = 0;

            FlatMapOp(Fn1<Object,Iterable> func) { flatMap = func; }
        }

        /**
         We need to model this as a separate op for when the previous op is CANNOT_HANDLE.  It is
         coded as a map, but still needs to be modeled separately so that subsequent takes can be
         combined into the earliest single explicit take op.  Such combination is a pick-least of
         all the takes, meaning that take(5).take(3) is equivalent to take(3).
         */
        private static class TakeOp extends Operation {
            private long numToTake;
            TakeOp(long take) {
                numToTake = take;
                map = a -> {
                    if (numToTake > 0) {
                        numToTake = numToTake - 1;
                        return a;
                    }
                    return TERMINATE;
                };
            }

            @Override public OpStrategy take(long num) {
                // This data condition is prevented in Xform.take()
//                if (num < 0) {
//                    throw new IllegalArgumentException("Can't take less than 0 items.");
//                }
                if (num < numToTake) {
                    numToTake = num;
                }
                return OpStrategy.HANDLE_INTERNALLY;
            }
        }
    } // end class Operation

    /**
     A RunList is a list of Operations "compiled" from an Xform.  It contains an Iterable data
     source (or some day and array source or List source) and a List of Operation op-codes.

     A RunList is also a SourceProvider, since the output of one transform can be the input to
     another.  FlatMap is implemented that way.  Notice that there are almost no generic types used
     here: Since the input could be one type, and each map or flatmap operation could change that to
     another type.

     For speed, we ignore all that in the "compiled" version and just use Objects and avoid any
     wrapping or casting.
     */
    protected static class RunList implements Iterable {
        Iterable source;
        List<Operation> list = new ArrayList<>();
//        RunList next = null;
        RunList prev = null;

        private RunList(RunList prv, Iterable src) { prev = prv; source = src; }

        public static RunList of(RunList prv, Iterable src) {
            return new RunList(prv, src);
//            if (prv != null) { prv.next = ret; }
//            return ret;
        }

        Operation[] opArray() {
            return list.toArray(new Operation[list.size()]);
        }
        @Override public Iterator iterator() { return source.iterator(); }
    }

    /**
     When iterator() is called, the AppendOp processes the previous source and operation into
     an ArrayList.  Then yields an iterator that yield the result of that operation until it runs
     out.  Then continues to yield the appended items until they run out, at which point hasNext()
     returns false;
     */
    private static class AppendOp extends RunList {
        private AppendOp(RunList prv, Iterable src) { super(prv, src); }

        @Override public Iterator iterator() {
            @SuppressWarnings("Convert2Lambda")
            ArrayList prevSrc = _fold(prev, prev.opArray(), 0, new ArrayList(),
                                      new Fn2<ArrayList,Object,ArrayList>() {
                                          @SuppressWarnings("unchecked")
                                          @Override
                                          public ArrayList applyEx(ArrayList res, Object item) throws Exception {
                                              res.add(item);
                                              return res;
                                          }
                                      });
            //noinspection unchecked
            return new Iterator() {
                Iterator innerIter = prevSrc.iterator();
                boolean usingPrevSrc = true;
                /** {@inheritDoc} */
                @Override public boolean hasNext() {
                    if (innerIter.hasNext()) {
                        return true;
                    } else if (usingPrevSrc) {
                        usingPrevSrc = false;
                        innerIter = source.iterator();
                    }
                    return innerIter.hasNext();
                }

                @Override public Object next() {
                    return innerIter.next();
                }
            };
        } // end iterator()
    }

    /** Describes an concat() operation, but does not perform it. */
    private static class AppendIterDesc<T> extends Xform<T> {
        final Xform<T> src;

        AppendIterDesc(Xform<T> prev, Xform<T> s) { super(prev); src = s; }

        @SuppressWarnings("unchecked")
        @Override protected RunList toRunList() {
            return new AppendOp(prevOp.toRunList(), src);
        }
    }

    /**
     Describes a "drop" operation.  Drops will be pushed as early in the operation-list as possible,
     ideally being done using one-time pointer addition on the source.

     I have working source-pointer-addition code, but it added a fair amount of complexity to
     implement it for Lists and arrays, but not for Iterables in general, so it is not currently
     (2015-08-21) part of this implementation.

     When source-pointer-addition is not possible, a Drop op-code is created (implemented as a
     filter function).  Subsequent drop ops will be combined into the earliest drop (for speed).
     @param <T> the expected input type to drop.
     */
    private static class DropDesc<T> extends Xform<T> {
        private final long dropAmt;
        DropDesc(Xform<T> prev, long d) { super(prev); dropAmt = d; }

        @SuppressWarnings("unchecked")
        @Override protected RunList toRunList() {
//                System.out.println("in toRunList() for drop");
            RunList ret = prevOp.toRunList();
            int i = ret.list.size() - 1;
//              System.out.println("\tchecking previous items to see if they can handle a drop...");
            Or<Long,OpStrategy> earlierDs;
            for (; i >= 0; i--) {
                Operation op = ret.list.get(i);
                earlierDs = op.drop(dropAmt);
                if (earlierDs.isBad() && (earlierDs.bad() == OpStrategy.CANNOT_HANDLE) ) {
//                        System.out.println("\tNone can handle a drop...");
                    break;
                } else if (earlierDs.isGood()) {
//                        System.out.println("\tHandled internally by " + opRun);
                    return ret;
                }
            }
//            if ( !Or.bad(OpStrategy.CANNOT_HANDLE).equals(earlierDs) && (i <= 0) ) {
//                Or<Long,OpStrategy> srcDs = ret.source.drop(dropAmt);
//                if (srcDs.isGood()) {
//                    if (srcDs.good() == dropAmt) {
////                        System.out.println("\tHandled internally by source: " + ret.source);
//                        return ret;
//                    } else {
//                        // TODO: Think about this and implement!
//                        throw new UnsupportedOperationException("Not implemented yet!");
//                    }
//                }
//            }
//                System.out.println("\tSource could not handle drop.");
//                System.out.println("\tMake a drop for " + dropAmt + " items.");
            ret.list.add(new Operation.DropOp(dropAmt));
            return ret;
        }
    }

    /** Describes a dropWhile() operation (implemented as a filter), but does not perform it. */
    private static class DropWhileDesc<T> extends Xform<T> {
        final Fn1<? super T,Boolean> f;

        DropWhileDesc(Xform<T> prev, Fn1<? super T,Boolean> func) { super(prev); f = func; }

        @SuppressWarnings("unchecked")
        @Override protected RunList toRunList() {
            RunList ret = prevOp.toRunList();
            ret.list.add(new Operation.FilterOp(new Fn1<Object, Boolean>() {
                // Starts out active (meaning dropping items until the inner function returns true).
                // Once inner function returns true, switches into passive mode in which this (outer)
                // function always returns true.
                // There are probably more efficient ways to do this, but I'm going for correct first.
                private boolean active = true;
                @Override public Boolean applyEx(Object o) throws Exception {
                    if (!active) {
                        return true;
                    }
                    boolean ret = ! ((Fn1<Object, Boolean>) f).apply(o);
                    if (ret) { active = false; }
                    return ret;
                }
            }));
            return ret;
        }
    }

    /** Describes a filter() operation, but does not perform it. */
    private static class FilterDesc<T> extends Xform<T> {
        final Fn1<? super T,Boolean> f;

        FilterDesc(Xform<T> prev, Fn1<? super T,Boolean> func) { super(prev);f = func; }

        @SuppressWarnings("unchecked")
        @Override protected RunList toRunList() {
            RunList ret = prevOp.toRunList();
            ret.list.add(new Operation.FilterOp((Fn1<Object,Boolean>) f));
            return ret;
        }
    }

    /** Describes a map() operation, but does not perform it. */
    private static class MapDesc<T,U> extends Xform<U> {
        final Fn1<? super T,? extends U> f;

        MapDesc(Xform<T> prev, Fn1<? super T,? extends U> func) { super(prev);f = func; }

        @SuppressWarnings("unchecked")
        @Override protected RunList toRunList() {
            RunList ret = prevOp.toRunList();
            ret.list.add(new Operation.MapOp(f));
            return ret;
        }
    }

    /** Describes a flatMap() operation, but does not perform it. */
    private static class FlatMapDesc<T,U> extends Xform<U> {
        final Fn1<? super T,Iterable<U>> f;
        FlatMapDesc(Xform<T> prev, Fn1<? super T,Iterable<U>> func) {
            super(prev); f = func;
        }

        @SuppressWarnings("unchecked")
        @Override protected RunList toRunList() {
            RunList ret = prevOp.toRunList();
            ret.list.add(new Operation.FlatMapOp((Fn1) f));
            return ret;
        }
    }

    /**
     Describes a "take" operation, but does not perform it.  Takes will be pushed as early in the
     operation-list as possible, ideally being done using one-time pointer addition on the source.
     When source pointer addition is not possible, a Take op-code is created (implemented as a
     filter function).  Subsequent take ops will be combined into the earliest take (for speed).
     @param <T> the expected input type to take.
     */
    private static class TakeDesc<T> extends Xform<T> {
        private final long take;
        TakeDesc(Xform<T> prev, long t) { super(prev); take = t; }

        @SuppressWarnings("unchecked")
        @Override protected RunList toRunList() {
//                System.out.println("in toRunList() for take");
            RunList ret = prevOp.toRunList();
            int i = ret.list.size() - 1;
//              System.out.println("\tchecking previous items to see if they can handle a take...");
            OpStrategy earlierTs;
            for (; i >= 0; i--) {
                Operation op = ret.list.get(i);
                earlierTs = op.take(take);
                if (earlierTs == OpStrategy.CANNOT_HANDLE) {
//                        System.out.println("\tNone can handle a take...");
                    break;
                } else if (earlierTs == OpStrategy.HANDLE_INTERNALLY) {
//                        System.out.println("\tHandled internally by " + opRun);
                    return ret;
                }
            }
//            if ( (earlierTs != OpStrategy.CANNOT_HANDLE) && (i <= 0) ) {
//                OpStrategy srcDs = ret.source.take(take);
//                if (srcDs == OpStrategy.HANDLE_INTERNALLY) {
////                        System.out.println("\tHandled internally by source: " + ret.source);
//                    return ret;
//                }
//            }
//                System.out.println("\tSource could not handle take.");
//                System.out.println("\tMake a take for " + take + " items.");
            ret.list.add(new Operation.TakeOp(take));
            return ret;
        }
    }

    static class SourceProviderIterableDesc<T> extends Xform<T> {
        private final Iterable<? extends T> list;
        SourceProviderIterableDesc(Iterable<? extends T> l) { super(null); list = l; }
        @Override protected RunList toRunList() {
            return RunList.of(null, list);
        }

        @Override public int hashCode() { return UnmodIterable.hash(this); }
        @Override public boolean equals(Object other) {
            if (this == other) { return true; }
            //noinspection SimplifiableIfStatement
            if ( !(other instanceof SourceProviderIterableDesc) ) { return false; }
            return Objects.equals(this.list, ((SourceProviderIterableDesc) other).list);
        }
    }

//    /** Static factory methods */
//    @SafeVarargs
//    public static <T> Xform<T> ofArray(T... list) {
//        return new SourceProviderIterableDesc<>(Arrays.asList(list));
//    }

    public static final Xform EMPTY = new SourceProviderIterableDesc<>(Collections.emptyList());

    @SuppressWarnings("unchecked")
    public static <T> Xform<T> empty() { return (Xform<T>) EMPTY; }

    /** Static factory methods */
    public static <T> Xform<T> of(Iterable<? extends T> list) {
        if (list == null) { return empty(); }
        return new SourceProviderIterableDesc<>(list);
    }

    // ========================================= Instance =========================================

    // Fields
    /** This is the previous operation or source. */
    final Xform prevOp;

    // Constructor
    Xform(Xform pre) { prevOp = pre; }

    // TODO: Everything should be implemented in terms of foldUntil now that we have that.
    /**
     @param reducer combines each value in the list with the result so far.  The result so far is the first argument.
     the current value to combine with it is the second argument.  The return type is the same as the result so far.
     Fn2<? super U,? super T,U>
     */
    // This is the main method of this whole file.  Everything else lives to serve this.
    // We used a linked-list to build the type-safe operations so if that code compiles, the types
    // should work out here too.  However, for performance, we don't want to be stuck creating and
    // passing Options around, nor do we want a telescoping stack of hasNext() and next() calls.
    // So abandon type safety, store all the intermediate results as Objects, and use loops and
    // sentinel values to break out or skip processing as appropriate.  Initial tests indicate this
    // is 2.6 times faster than wrapping items type-safely in Options and 10 to 100 times faster
    // than lazily evaluated and cached linked-list, Sequence model.
    @SuppressWarnings("unchecked")
    private static <H> H _fold(Iterable source, Operation[] ops, int opIdx, H ident, Fn2 reducer) {
        Object ret = ident;

        // This is a label - the first one I have used in Java in years, or maybe ever.
        // I'm assuming this is fast, but will have to test to confirm it.
        sourceLoop:
        for (Object o : source) {
            for (int j = opIdx; j < ops.length; j++) {
                Operation op = ops[j];
                if ( (op.filter != null) && !op.filter.apply(o) ) {
                    // stop processing this source item and go to the next one.
                    continue sourceLoop;
                }
                if (op.map != null) {
                    o = op.map.apply(o);
                    // This is how map can handle takeWhile, take, and other termination marker
                    // roles.  Remember, the fewer functions we have to check for, the faster this
                    // will execute.
                    if (o == TERMINATE) {
                        return (H) ret;
                    }
                } else if (op.flatMap != null) {
                    ret = _fold(op.flatMap.apply(o), ops, j + 1, (H) ret, reducer);
                    // stop processing this source item and go to the next one.
                    continue sourceLoop;
                }
//                    if ( (op.terminate != null) && op.terminate.apply(o) ) {
//                        return (G) ret;
//                    }
            }
            // Here, the item made it through all the operations.  Combine it with the result.
            ret = reducer.apply(ret, o);
        }
        return (H) ret;
    } // end _fold();

    @Override public UnmodIterator<A> iterator() {
        // TODO: I had a really fast array-list implementation that I could probably hack into this for performance (assuming it actually works).
        return toMutableList().iterator();
    }

    // =============================================================================================
    // These will come from Transformable, but (will be) overridden to have a different return type.

//    public Xform<A> concatList(List<? extends A> list) {
//        if ( (list == null) || (list.size() < 1) ) { return this; }
//        return concat(list);
//    }

    @Override public Xform<A> concat(Iterable<? extends A> list) {
        if (list == null) { throw new IllegalArgumentException("Can't concat a null iterable"); }
        return new AppendIterDesc<>(this, new SourceProviderIterableDesc<>(list));
    }

//    @SafeVarargs
//    public final Xform<A> concatArray(A... list) {
//        if ( (list == null) || (list.length < 1) ) { return this; }
//        return concat(Arrays.asList(list));
//    }

//    public Xform<A> precatList(List<? extends A> list) {
//        if ( (list == null) || (list.size() < 1) ) { return this; }
//        return precat(list);
//    }

    @Override public Xform<A> precat(Iterable<? extends A> list) {
        if (list == null) { throw new IllegalArgumentException("Can't precat a null iterable"); }
        return new AppendIterDesc<>(of(list), this);
    }

//    @SafeVarargs
//    public final Xform<A> precatArray(A... list) {
//        if ( (list == null) || (list.length < 1) ) { return this; }
//        return precat(Arrays.asList(list));
//    }

    /** The number of items to drop from the beginning of the output. */
    @Override public Xform<A> drop(long n) {
        if (n < 0) { throw new IllegalArgumentException("Can't drop less than zero items."); }
        return new DropDesc<>(this, n);
    }

    /** The number of items to drop from the beginning of the output. */
    @Override public Xform<A> dropWhile(Fn1<? super A,Boolean> predicate) {
        if (predicate == null) { throw new IllegalArgumentException("Can't dropWhile without a function."); }
        return new DropWhileDesc<>(this, predicate);
    }

    /** Provides a way to collect the results of the transformation. */
    @Override public <B> B fold(B ident, Fn2<? super B,? super A,B> reducer) {
        if (reducer == null) {
            throw new IllegalArgumentException("Can't fold with a null reduction function.");
        }

        // Construct an optimized array of OpRuns (mutable operations for this run)
        RunList runList = toRunList();
        return _fold(runList, runList.opArray(), 0, ident, reducer);
    }

    /**
     Thit implementation should be correct, but could be slow in the case where previous operations
     are slow and the terminateWhen operation is fast and terminates early.  It actually renders
     items to a mutable List, then runs through the list performing the requested reduction,
     checking for early termination on the result.  If you can to a takeWhile() or take() earlier
     in the transform chain instead of doing it here, always do that.  If you really need early
     termination based on the *result* of a fold, and the operations are expensive or the input
     is huge, try using a View instead.  If you don't care about those things, then this method is
     perfect for you.

     {@inheritDoc}
     */
    @Override public <G,B> Or<G,B> foldUntil(G accum,
                                             Fn2<? super G,? super A,B> terminator,
                                             Fn2<? super G,? super A,G> reducer) {
        if (terminator == null) {
            return Or.good(fold(accum, reducer));
        }
        if (reducer == null) {
            throw new IllegalArgumentException("Can't fold with a null reduction function.");
        }

        // Yes, this is a cheap plastic imitation of what you'd hope for if you really need this
        // method.  The trouble is that when I implemented it correctly in _fold, I found
        // it was going to be incredibly difficult, or more likely impossible to implement
        // when the previous operation was flatMap, since you don't have the right result type to
        // check against when you recurse in to the flat mapping function, and if you check the
        // return from the recursion, it may have too many elements already.
        // In XformTest.java, there's something marked "Early termination test" that illustrates
        // this exact problem.
        List<A> as = this.toMutableList();
        for (A a : as) {
            B term = terminator.apply(accum, a);
            if (term != null) {
                return Or.bad(term);
            }
            accum = reducer.apply(accum, a);
        }
        return Or.good(accum);
    }

    @Override public Xform<A> filter(Fn1<? super A,Boolean> f) {
        if (f == null) { throw new IllegalArgumentException("Can't filter with a null function."); }
        return new FilterDesc<>(this, f);
    }

    @Override public <B> Xform<B> flatMap(Fn1<? super A,Iterable<B>> f) {
        if (f == null) { throw new IllegalArgumentException("Can't flatmap with a null function."); }
        return new FlatMapDesc<>(this, f);
    }

    @Override public <B> Xform<B> map(Fn1<? super A, ? extends B> f) {
        if (f == null) { throw new IllegalArgumentException("Can't map with a null function."); }
        return new MapDesc<>(this, f);
    }

    protected abstract RunList toRunList();

    @Override public Xform<A> take(long numItems) {
        if (numItems < 0) { throw new IllegalArgumentException("Num items must be >= 0"); }
        return new TakeDesc<>(this, numItems);
    }

    @Override public Xform<A> takeWhile(Fn1<? super A,Boolean> f) {
        if (f == null) {
            throw new IllegalArgumentException("Can't takeWhile with a null function.");
        }
        // I'm coding this as a map operation that either returns the source, or a TERMINATE
        // sentinel value.
        return new MapDesc<>(this, a -> f.apply(a) ? a : terminate());
    }
}


/*
 Copyright (c) Rich Hickey. All rights reserved. The use and distribution terms for this software
 are covered by the Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
 which can be found in the file epl-v10.html at the root of this distribution. By using this
 software in any fashion, you are agreeing to be bound by the terms of this license. You must not
 remove this notice, or any other, from this software.
 */

/* rich Mar 3, 2008 */

package org.organicdesign.fp.collections;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Comparator;
import java.util.Map;
import java.util.SortedSet;

import org.organicdesign.fp.oneOf.Option;

import static org.organicdesign.fp.collections.Equator.defaultComparator;

/**
 A wrapper that turns a PersistentTreeMap into a set.

 This file is a derivative work based on a Clojure collection licensed under the Eclipse Public
 License 1.0 Copyright Rich Hickey.  Errors by Glen Peterson.
 */
public class PersistentTreeSet<E> extends AbstractUnmodSet<E>
        implements ImSortedSet<E>, Serializable {

    /**
     Be extremely careful with this because it uses the default comparator, which only works for
     items that implement Comparable (have a "natural ordering").  An attempt to use it with other
     items will blow up at runtime.  Either a withComparator() method will be added, or this will
     be removed.
     */
    // TODO: Should really require a comparator.
    @SuppressWarnings("unchecked")
    static public final PersistentTreeSet EMPTY = new PersistentTreeSet(PersistentTreeMap.EMPTY);

    /**
     Be extremely careful with this because it uses the default comparator, which only works for
     items that implement Comparable (have a "natural ordering").  An attempt to use it with other
     items will blow up at runtime.  Either a withComparator() method will be added, or this will
     be removed.
     */
    @SuppressWarnings("unchecked")
    static public <T extends Comparable<T>> PersistentTreeSet<T> empty() { return EMPTY; }

    /**
     Returns a new PersistentTreeSet of the given comparator.  Always use this instead of starting
     with empty() because there is no way to assign a comparator to an existing set.
     */
    public static <T> PersistentTreeSet<T> ofComp(Comparator<? super T> comp) {
        return new PersistentTreeSet<>(PersistentTreeMap.empty(comp));
    }

    /**
     Returns a new PersistentTreeSet of the given comparator and items.

     @param comp A comparator that defines the sort order of elements in the new set.  This
     becomes part of the set (it's not for pre-sorting).
     @param elements items to go into the set.  In the case of a duplicate element, later
     values in the input list overwrite the earlier ones.
     @return a new PersistentTreeSet of the specified comparator and the given elements
     */
    public static <T> PersistentTreeSet<T> ofComp(Comparator<? super T> comp,
                                                  Iterable<T> elements) {
        PersistentTreeSet<T> ret = new PersistentTreeSet<>(PersistentTreeMap.empty(comp));
        if (elements == null) { return ret; }
        for (T element : elements) {
            ret = ret.put(element);
        }
        return ret;
    }

    /** Returns a new PersistentTreeSet of the given comparable items. */
    public static <T extends Comparable<T>> PersistentTreeSet<T> of(Iterable<T> items) {
        // empty() uses default comparator
        if (items == null) { return empty(); }
        PersistentTreeSet<T> ret = empty();
        for (T item : items) {
            ret = ret.put(item);
        }
        return ret;
    }

    /**
     Returns a new PersistentTreeSet of the keys and comparator in the given map.  Since
     PersistentTreeSet is just a wrapper for a PersistentTreeMap, this can be a very cheap
     operation.
     */
    public static <T> PersistentTreeSet<T> ofMap(ImSortedMap<T,?> i) {
        return new PersistentTreeSet<>(i);
    }

    // ==================================== Instance Variables ====================================
    private transient final ImSortedMap<E,?> impl;

    // ======================================= Constructor =======================================
    private PersistentTreeSet(ImSortedMap<E,?> i) { impl = i; }

    // ======================================= Serialization =======================================
    // This class has a custom serialized form designed to be as small as possible.  It does not
    // have the same internal structure as an instance of this class.

    // For serializable.  Make sure to change whenever internal data format changes.
    private static final long serialVersionUID = 20160904120000L;

    // Check out Josh Bloch Item 78, p. 312 for an explanation of what's going on here.
    private static class SerializationProxy<K> implements Serializable {
        // For serializable.  Make sure to change whenever internal data format changes.
        private static final long serialVersionUID = 20160904120000L;

        private Comparator<? super K> comparator;
        private final int size;
        private transient ImSortedMap<K,?> theMap;
        SerializationProxy(ImSortedMap<K,?> phm) {
            comparator = phm.comparator();
            if ( (comparator != null) && !(comparator instanceof Serializable) ) {
                throw new IllegalStateException("Comparator must implement serializable." +
                                                "  Instead it was " + comparator);
            }
            if (comparator instanceof PersistentTreeMap.KeyComparator) {
                Comparator wc = ((PersistentTreeMap.KeyComparator) comparator).unwrap();
                if ( !(wc instanceof Serializable) ) {
                    throw new IllegalStateException("Wrapped key comparator must implement" +
                                                    "serializable.  Instead it was " + wc);
                }
            }
            size = phm.size();
            theMap = phm;
        }

        // Taken from Josh Bloch Item 75, p. 298
        private void writeObject(ObjectOutputStream s) throws IOException {
            s.defaultWriteObject();
            // Write out all elements in the proper order
            for (Map.Entry<K,?> entry : theMap) {
                s.writeObject(entry.getKey());
            }
        }

        @SuppressWarnings("unchecked")
        private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
            s.defaultReadObject();
            if (comparator == null) {
                comparator = defaultComparator();
            }
            theMap = PersistentTreeMap.ofComp(comparator, null);
            for (int i = 0; i < size; i++) {
                theMap = theMap.assoc((K) s.readObject(), null);
            }
        }

        private Object readResolve() { return new PersistentTreeSet<>(theMap); }
    }

    private Object writeReplace() { return new SerializationProxy<>(impl); }

    private void readObject(java.io.ObjectInputStream in) throws IOException,
            ClassNotFoundException {
        throw new InvalidObjectException("Proxy required");
    }

    // ===================================== Instance Methods =====================================
    /**
     Returns the comparator used to order the items in this set, or null if it uses
     Fn2.DEFAULT_COMPARATOR (for compatibility with java.util.SortedSet).
     */
    @Override public Comparator<? super E> comparator() { return impl.comparator(); }

    /**
     Returns true if the set contains the given item in O(log n) time.  This is the defining method
     of a set.
     */
    @SuppressWarnings("SuspiciousMethodCalls")
    @Override public boolean contains(Object o) { return impl.containsKey(o); }

    /** {@inheritDoc} */
    @Override public PersistentTreeSet<E> without(E key) {
        return (impl.containsKey(key)) ? new PersistentTreeSet<>(impl.without(key))
                                       : this;
    }

    /** {@inheritDoc} */
    @Override
    public UnmodSortedIterator<E> iterator() {
        return new UnmodSortedIterator<E>() {
            UnmodSortedIterator<? extends UnmodMap.UnEntry<E,?>> iter = impl.iterator();
            @Override public boolean hasNext() { return iter.hasNext(); }
            @Override public E next() {
                UnmodMap.UnEntry<E,?> e = iter.next();
                return e == null ? null : e.getKey();
            }
        };
    }

    /**
     This is designed to be correct, rather than fully compatible with TreeSet.equals().
     TreeSet.equals() does not take ordering into account and this does.

     You want better equality?  Define an Equator.  This is for Java@trade; interop.
     */
    @Override public boolean equals(Object other) {
        if (this == other) { return true; }
        if ( !(other instanceof SortedSet) ) { return false; }
        SortedSet<?> that = (SortedSet) other;

        if (size() != that.size()) { return false; }
        return UnmodSortedIterable.equal(this, UnmodSortedIterable.castFromSortedSet(that));
    }

    /**
     Use head() inherited from Sequence instead of this method which is inherited from SortedSet.
     This method returns the first element if it exists, or throws a NoSuchElementException if the
     set is empty.

     head() returns an Option of the first element where as this method throws an exception if this
     set is empty.  I had to rename the method on Sequence from first() to head() to work around
     this.  Also returning an Option is thread-safe for building a lazy sequence.  The alternative,
     examining the rest() of a sequence to see if it's == Sequence.empty() gets ugly very quickly
     and makes many transformations eager (especially flatMap).
     */
    @Override public E first() { return impl.firstKey(); }

    /** {@inheritDoc} */
    @Override public Option<E> head() {
        return size() > 0 ? Option.some(impl.firstKey()) : Option.none();
    }

    /** {@inheritDoc} */
    @Override public boolean isEmpty() { return impl.isEmpty(); }

    /**
     Inherited from SortedSet, returns the last item in this set, or throw an exception if this set
     is empty.  Good luck with that.
     */
    @Override public E last() { return impl.lastKey(); }

    /** {@inheritDoc} */
    @Override public PersistentTreeSet<E> put(E e) {
        return (impl.containsKey(e)) ? this
                                     : new PersistentTreeSet<>(impl.assoc(e, null));
    }

    /** The size of this set. */
    @Override public int size() { return impl.size(); }

    /** {@inheritDoc} */
    @Override public ImSortedSet<E> subSet(E fromElement, E toElement) {
        return PersistentTreeSet.ofMap(impl.subMap(fromElement, toElement));
    }

    /** {@inheritDoc} */
    @Override public ImSortedSet<E> tailSet(E fromElement) {
        return PersistentTreeSet.ofMap(impl.tailMap(fromElement));
    }

//    /** {@inheritDoc} */
//    @Override public Sequence<E> tail() { return impl.without(first()).keySet().seq(); }

//    @Override
//    public ISeq<E> rseq() {
//        return APersistentMap.KeySeq.create(((Reversible<E>) impl).rseq());
//    }
//
//    @Override
//    public Comparator comparator() {
//        return ((Sorted) impl).comparator();
//    }

//    @Override
//    public Object entryKey(E entry) {
//        return entry;
//    }

//    @SuppressWarnings("unchecked")
//    @Override
//    public ISeq<E> seq(boolean ascending) {
//        PersistentTreeMap m = (PersistentTreeMap) impl;
//        return RT.keys(m.seq(ascending));
//    }
//
//    @SuppressWarnings("unchecked")
//    @Override
//    public ISeq<E> seqFrom(Object key, boolean ascending) {
//        PersistentTreeMap m = (PersistentTreeMap) impl;
//        return RT.keys(m.seqFrom(key, ascending));
//    }

}


package org.organicdesign.fp.oneOf;

import org.organicdesign.fp.function.Fn0;
import org.organicdesign.fp.function.Fn1;

/** Represents the absence of a value */
public final class None<T> implements Option<T> {
    // For serializable.  Make sure to change whenever internal data format changes.
    private static final long serialVersionUID = 20170810211300L;

    // ========================================== Static ==========================================
    /** None is a singleton and this is its only instance. */
    static final Option NONE = new None();

    /** Generic version of the singleton instance. */
    @SuppressWarnings("unchecked")
    public static <T> None<T> none() { return (None<T>) NONE; }

    /** Private constructor for singleton. */
    private None() {}

    /** {@inheritDoc} */
    @Override public T get() { throw new IllegalStateException("Called get on None"); }

    /** {@inheritDoc} */
    @Override public T getOrElse(T t) { return t; }

    /** {@inheritDoc} */
    @Override public boolean isSome() { return false; }
//
//        @Override public UnmodSortedIterator<T> iterator() {
//            return UnmodSortedIterator.empty();
//        }

    /** {@inheritDoc} */
    @Override public <U> U match(Fn1<T,U> has, Fn0<U> hasNot) {
        return hasNot.get();
    }

    /** {@inheritDoc} */
    @Override public <U> Option<U> then(Fn1<T,Option<U>> f) { return Option.none(); }

    /** Valid, but deprecated because it's usually an error to call this in client code. */
    @Deprecated // Has no effect.  Darn!
    @Override public int hashCode() { return 0; }

    /** Valid, but deprecated because it's usually an error to call this in client code. */
    @Deprecated // Has no effect.  Darn!
    @Override public boolean equals(Object other) {
        return (this == other) || (other instanceof org.organicdesign.fp.oneOf.None);
    }

    /** Defend our singleton property in the face of deserialization. */
    private Object readResolve() { return NONE; }

    @Override public String toString() { return "None"; }
}


// Copyright 2016 PlanBase Inc. & Glen Peterson
//
// 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 org.organicdesign.fp.tuple;

import java.io.Serializable;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;

import org.organicdesign.fp.collections.UnmodMap;

import static org.organicdesign.fp.FunctionUtils.stringify;

/**
 Holds 2 items of potentially different types, and implements Map.Entry (and UnmodMap.UnEntry
 (there is no ImMap.ImEntry)).  Designed to let you easily create immutable subclasses (to give your
 data structures meaningful names) with correct equals(), hashCode(), and toString() methods.
 */
public class Tuple2<A,B> implements Entry<A,B>, UnmodMap.UnEntry<A,B>, Serializable {

    // For serializable.  Make sure to change whenever internal data format changes.
    private static final long serialVersionUID = 20160906065000L;

    // Fields are protected so that sub-classes can make accessor methods with meaningful names.
    protected final A _1;
    protected final B _2;

    /**
     Constructor is protected (not public) for easy inheritance.  Josh Bloch's "Item 1" says public
     static factory methods are better than constructors because they have names, they can return
     an existing object instead of a new one, and they can return a sub-type.  Therefore, you
     have more flexibility with a static factory as part of your public API then with a public
     constructor.
     */
    protected Tuple2(A a, B b) {
        _1 = a; _2 = b;
    }

    /** Public static factory method */
    public static <A,B> Tuple2<A,B> of(A a, B b) {
        return new Tuple2<>(a, b);
    }

    /** Map.Entry factory method */
    public static <K,V> Tuple2<K,V> of(Map.Entry<K,V> entry) {
        // Protect against multiple-instantiation
        if (entry instanceof Tuple2) {
            return (Tuple2<K,V>) entry;
        }
        return new Tuple2<>(entry.getKey(), entry.getValue());
    }

    /** Returns the 1st field */
    public A _1() { return _1; }
    /** Returns the 2nd field */
    public B _2() { return _2; }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "(" +
               stringify(_1) + "," +
               stringify(_2) + ")";
    }

    @Override
    public boolean equals(Object other) {
        // Cheapest operation first...
        if (this == other) { return true; }
        if (!(other instanceof Entry)) { return false; }
        // Details...
        @SuppressWarnings("rawtypes") final Entry that = (Entry) other;

        return Objects.equals(_1, that.getKey()) &&
               Objects.equals(_2, that.getValue());
    }

    @Override
    public int hashCode() {
        // This is specified in java.util.Map as part of the map contract.
        return  (_1 == null ? 0 : _1.hashCode()) ^
                (_2 == null ? 0 : _2.hashCode());
    }

    // Inherited from Map.Entry
    /** Returns the first field of the tuple.  To implement Map.Entry. */
    @Override public A getKey() { return _1; }

    /** Returns the second field of the tuple.  To implement Map.Entry. */
    @Override public B getValue() { return _2; }

    /** This method is required to implement Map.Entry, but calling it only issues an exception */
    @SuppressWarnings("deprecation")
    @Override @Deprecated public B setValue(B value) {
        throw new UnsupportedOperationException("Tuple2 is immutable");
    }
}