package org.organicdesign.fp.oneOf;

import org.organicdesign.fp.collections.ImList;
import org.organicdesign.fp.function.Fn1;
import org.organicdesign.fp.type.RuntimeTypes;

import java.util.Objects;

import static org.organicdesign.fp.type.RuntimeTypes.union2Str;

/**
 Holds one of 3 values. See {@link OneOf2} for a full description.  If you're passing the same type, you probably
 want a tuple instead.
 */
public class OneOf3<A,B,C> {
    private final Object item;
    private final int sel;
    private final ImList<Class> types;

    /**
     Protected constructor for subclassing.

     @param o the item (may be null)
     @param aClass class 0 (to have at runtime for descriptive error messages and toString()).
     @param bClass class 1 (to have at runtime for descriptive error messages and toString()).
     @param cClass class 2 (to have at runtime for descriptive error messages and toString()).
     @param index 0 means this represents an A, 1 represents a B, 2 represents a C, 3 means D
     */
    protected OneOf3(Object o, Class<A> aClass, Class<B> bClass, Class<C> cClass, int index) {
        types = RuntimeTypes.registerClasses(aClass, bClass, cClass);
        sel = index;
        item = o;
        if (index < 0) {
            throw new IllegalArgumentException("Selected item index must be 0-2");
        } else if (index > 2) {
            throw new IllegalArgumentException("Selected item index must be 0-2");
        }
        if ( (o != null) && (!types.get(index).isInstance(o)) ) {
            throw new ClassCastException("You specified index " + index + ", indicating a(n) " +
                                         types.get(index).getCanonicalName() + "," +
                                         " but passed a " + o.getClass().getCanonicalName());
        }
    }

    /**
     Languages that have union types built in have a match statement that works like this method.
     Exactly one of these functions will be executed - determined by which type of item this object holds.
     @param fa the function to be executed if this OneOf stores the first type.
     @param fb the function to be executed if this OneOf stores the second type.
     @param fc the function to be executed if this OneOf stores the third type.
     @return the return value of whichever function is executed.
     */
    // We only store one item and it's type is erased, so we have to cast it at runtime.
    // If sel is managed correctly, it ensures that the cast is accurate.
    @SuppressWarnings("unchecked")
    public <R> R match(Fn1<A, R> fa,
                       Fn1<B, R> fb,
                       Fn1<C, R> fc) {
        if (sel == 0) {
            return fa.apply((A) item);
        } else if (sel == 1) {
            return fb.apply((B) item);
        } else {
            return fc.apply((C) item);
        }
    }

    public int hashCode() {
        // Simplest way to make the two items different.
        return Objects.hashCode(item) + sel;
    }

    @SuppressWarnings("unchecked")
    @Override public boolean equals(Object other) {
        if (this == other) { return true; }
        if (!(other instanceof OneOf3)) { return false; }

        OneOf3 that = (OneOf3) other;
        return (sel == that.sel) &&
               Objects.equals(item, that.item);
    }

    @Override public String toString() { return union2Str(item, types); }
}


// Copyright (c) 2014-03-08 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.collections;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.List;
import java.util.NoSuchElementException;

/**
 An efficient (in both time and memory) implementation of List.  If you want to compare a RangeOfInt
 to generic List<Integer>, use RangeOfInt.LIST_EQUATOR so that the hashCodes will be
 compatible.

 A RangeOfInt is an indexed sequence of integers.  It currently assumes a step of 1.
 Like everything in Java and similar classes in Clojure, Python, and Scala, it is inclusive of the
 start value but exclusive of the end.

 In theory, a class like this could be made for anything that can provide it's next() and previous()
 item and a size.  To do that, Integer would need to implement something that defined what the
 next() and previous() values.  Currently limited to Integer.MIN_VALUE to Integer.MAX_VALUE.
 */
// Note: In theory, this could implement both List<Integer> and SortedSet<Integer>
public class RangeOfInt implements UnmodList<Integer>, Serializable {

    // Enums are serializable and anonymous classes are not.  Therefore enums make better singletons.
    public enum Equat implements Equator<List<Integer>> {
        LIST {
            /**
             This is an *inefficient* hash code for RangeOfInt, but it is compatible with List.
             @param integers the list
             @return a HashCode which is compatible with java.util.List of the same integers as
             a range.
             */
            @Override public int hash(List<Integer> integers) {
                return UnmodIterable.hash(integers);
            }

            @Override
            public boolean eq(List<Integer> o1, List<Integer> o2) {
                if (o1 == o2) { return true; }
                if ( (o1 == null) || (o2 == null) ) { return false; }
                if ((o1 instanceof RangeOfInt) && (o2 instanceof RangeOfInt)) {
                    return o1.equals(o2);
                }
                return o1.size() == o2.size() &&
                       UnmodSortedIterable.equal(UnmodSortedIterable.castFromList(o1),
                                                 UnmodSortedIterable.castFromList(o2));
            }
        }
    }

//    public static RangeOfInt of(int s, int e) {
//        if (e < s) {
//            throw new IllegalArgumentException("end of range must be >= start of range");
//        }
//        return new RangeOfInt(s, e);
//    }

    public static RangeOfInt of(Number s, Number e) {
        if ((s == null) || (e == null)) {
            throw new IllegalArgumentException("Nulls not allowed");
        }
        if (e.longValue() < s.longValue()) {
            throw new IllegalArgumentException("end of range must be >= start of range");
        }
        return new RangeOfInt(s.intValue(), e.intValue());
    }

    //
    public static RangeOfInt of(Number e) {
        if (e == null) {
            throw new IllegalArgumentException("Nulls not allowed");
        }
        if (e.longValue() < 0) {
            throw new IllegalArgumentException("Single argument factory can't accept a negative" +
                                               " endpoint (it assumes start is zero and positive" +
                                               " unit step).");
        }
        return new RangeOfInt(0, e.intValue());
    }

//    public static RangeOfInt of(int s, int e) { return of((int) s, (int) e); }

    // ==================================== Instance Variables ====================================

    private final int start;
    private final int end;
    private transient int size;

    // ======================================== Constructor ========================================

    private RangeOfInt(int s, int e) { start = s; end = e; size = (end - start); }

    // ======================================= Serialization =======================================

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

    private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
        s.defaultReadObject();
        size = end - start;
    }

    // ===================================== Instance Methods =====================================

//    public int start() { return start; }
//    public int end() { return end; }

    /**
     Returns true if the number is within the bounds of this range (low end incluive, high end
     exclusive).  In math terms, returns true if the argument is [low, high).  False
     otherwise.  This is an efficient method when the compiler can see that it's being passed a
     primitive int, but contains(Object o) delegates to this method when it's passed a reasonable
     and unambiguous argument.
     */
    public boolean contains(int i) {
        return (i >= start) && (i < end);
    }

    /**
     Though this overrides List.contains(Object o), it is effectively a convenience method for
     calling contains(int i).  Therefore, it only accepts Integers, Longs, BigIntegers, and
     Strings that parse as signed decimal Integers.  It does not accept Numbers since they can't
     easily be checked for truncation and floating-point might not round properly with respect to
     bounds, or might not make sense if you are using your range to define a set of integers.
     Handles truncation (returns false) for the types it accepts.  Throws exceptions for types it
     does not accept.

     Thanks for all the help from codereview.stackexchange:
     http://codereview.stackexchange.com/questions/100846/rounding-and-truncation-in-intrange-containsobject-o

     @param o an Integer, Long, BigInteger, or String that parses as a signed decimal Integer.
     @return true if the number is within the bounds of this range (high end is exclusive).  False
     otherwise.
     @throws IllegalArgumentException if the argument is not an Integer, Long, BigInteger, or
     String.
     @throws NumberFormatException if a String argument cannot be parsed using Integer.valueOf().
     */
    @SuppressWarnings("deprecation")
    @Override public boolean contains(Object o) {
        // Only accept classes where we can convert to an integer while preventing
        // overflow/underflow.  If there were a way to reliably test for overflow/underflow in
        // Numbers, we could accept them too, but with the rounding errors of floats and doubles
        // that's impractical.
        if (o instanceof Integer) {
            return contains(((Integer) o).intValue());
        } else if (o instanceof Long) {
            long l = (Long) o;
            return (l <= ((long) Integer.MAX_VALUE)) &&
                   (l >= ((long) Integer.MIN_VALUE)) &&
                   contains((int) l);
        } else if (o instanceof BigInteger) {
            try {
                // Throws an exception if it's more than 32 bits.
                return contains(((BigInteger) o).intValueExact());
            } catch (ArithmeticException ignore) {
                return false;
            }
        } else if (o instanceof String) {
            return contains(Integer.valueOf((String) o));
        } else {
            throw new IllegalArgumentException("Don't know how to convert to a primitive int" +
                                               " without risking accidental truncation.  Pass an" +
                                               " Integer, Long, BigInteger, or String that parses" +
                                               " within Integer bounds instead.");
        }
    }

    @Override public Integer get(int idx) {
        if ( (idx >= 0) && (idx < size) ) { return start + idx; }
        throw new IndexOutOfBoundsException("Index " + idx +
                                            " was outside the size of this range: " + start +
                                            " to " + end);
    }

    /**
     Unlike most implementations of List, this method has excellent O(1) performance!
     {@inheritDoc}
     */
    @Override public int indexOf(Object o) {
        if (o instanceof Number) {
            int i = ((Number) o).intValue();
            if ( (i >= start) && (i < end) ) {
                return i - start;
            }
        }
        return -1;
    }

    /**
     Unlike most implementations of List, this method has excellent O(1) performance!
     {@inheritDoc}
     */
    @Override public int lastIndexOf(Object o) { return indexOf(o); }

    @Override public int size() { return size; }

//    @Override
//    public int hashCode() {
//        // My theory is that we can compute a hashCode compatible with ArrayList using
//        // sum of a series.  Maybe the summing hashCode truncates differently on overflow, but I
//        // thought this was worth a try.
//
//        // size * (start + end) / 2
//        // But convert to long to avoid premature overflow
//        long tmp = ((long) start) + ((long) end);
//        tmp = tmp * (long) size;
//        return (int) (tmp / 2L);
//    }
//    @Override
//    public boolean equals(Object other) {
//        if (this == other) { return true; }
//        if ( !(other instanceof List) ) { return false; }
//
//        // Fast compare for other int ranges:
//        if (other instanceof RangeOfInt) {
//            final RangeOfInt that = (RangeOfInt) other;
//            return (this.start == that.start) &&
//                   (this.end == that.end);
//        }
//
//        // Slower compare for other lists:
//        final List that = (List) other;
//        // This is not a database object; compare "significant" fields here.
//        return UnmodSortedIterable.equals(this, UnmodSortedIterable.castFromList(that));
//    }

    @Override
    public int hashCode() { return start + end; }

    @Override
    public boolean equals(Object other) {
        if (this == other) { return true; }
        if ( !(other instanceof RangeOfInt) ) { return false; }

        // Details...
        final RangeOfInt that = (RangeOfInt) other;
        // This is not a database object; compare "significant" fields here.
        return (this.start == that.start) &&
               (this.end == that.end);
    }

    /**
     {@inheritDoc}
     Iterates from start of range (inclusive) up-to, but excluding, the end of the range.
     I'm not sure this is a good idea, but Python, Clojure, Scala, and just about everything in
     Java expects similar behavior.
     */
    @Override public UnmodListIterator<Integer> listIterator(final int startIdx) {
        if( (startIdx < 0) || (startIdx > size) ) {
            // To match ArrayList and other java.util.List expectations
            throw new IndexOutOfBoundsException("Index: " + startIdx);
        }
        return new UnmodListIterator<Integer>() {
            int val = start + startIdx;
            @Override public boolean hasNext() { return val < end; }
            @Override public Integer next() {
                if (val >= end) {
                    // To match ArrayList and other java.util.List expectations
                    throw new NoSuchElementException();
                }
                Integer t = val;
                val = val + 1;
                return t;
            }
            @Override public boolean hasPrevious() { return val > start; }
            @Override public Integer previous() {
                if (val <= start) {
                    // To match ArrayList and other java.util.List expectations
                    throw new NoSuchElementException();
                }
                val = val - 1;
                return val;
            }
            @Override public int nextIndex() { return val - start; }
        };
    }

    /** {@inheritDoc} */
    @Override public RangeOfInt subList(int fromIndex, int toIndex) {
        if ( (fromIndex == 0) && (toIndex == size) ) {
            return this;
        }
        // Note that this is an IllegalArgumentException, not IndexOutOfBoundsException in order to
        // match ArrayList.
        if (fromIndex > toIndex) {
            throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex +
                                               ")");
        }
        // The text of this matches ArrayList
        if (fromIndex < 0) {
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        }
        if (toIndex > size) {
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        }

        // Look very closely at the second parameter because the bounds checking is *different*
        // from the get() method.  get(toIndex) can throw an exception if toIndex >= start+size.
        // But since a range is exclusive of it's right-bound, we can create a new sub-range
        // with a right bound index of size, as opposed to size minus 1.  I spent hours
        // understanding this before fixing a bug with it.  In the end, subList should do the same
        // thing on a Range that it does on the equivalent ArrayList.  I made tests for the same.
        return RangeOfInt.of(start + fromIndex, start + toIndex);
    }
}


package org.organicdesign.fp.type;

import org.organicdesign.fp.collections.ImList;

import java.util.HashMap;

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

/**
 Stores the classes from the compile-time generic type parameters in a vector in the *same order* as the
 generics in the type signature of that class.
 Store them here using {@link #registerClasses(Class[])} to avoid duplication.  For example:

private static final ImList<Class> CLASS_STRING_INTEGER =
    RuntimeTypes.registerClasses(vec(String.class, Integer.class));

 Now you if you use CLASS_STRING_INTEGER, you are never creating a new vector.
 For a full example of how to use these RuntimeTypes, see {@link org.organicdesign.fp.oneOf.OneOf2}.

 This is an experiment in runtime types for Java.  Constructive criticism is appreciated!
 If you write a programming language, your compiler can manage these vectors so that humans don't have to
 ever think about them, except to query them when they want to.  I wanted to do this with arrays, but I didn't trust
 that they wouldn't be mutated, so I used ImList's.
 */
public final class RuntimeTypes {

    // This is a static (mutable) class.  Don't instantiate.
    @Deprecated
    private RuntimeTypes() { throw new UnsupportedOperationException("No instantiation"); }

    private enum Lock { INSTANCE }

    // Keep a single copy of combinations of generic parameters at runtime. These are all arrays for size,
    // cache-closeness, and speed.  But arrays can be modified, so this must be kept private to guard against
    // modification.
    private static final HashMap<ArrayHolder<Class>,ImList<Class>> typeMap = new HashMap<>();

    /**
     Use this to prevent duplicate runtime types.
     @param cs an immutable vector of classes to register
     */
    public static ImList<Class> registerClasses(Class... cs) {
        if (cs == null) {
            throw new IllegalArgumentException("Can't register a null type array");
        }
        if (cs.length == 0) {
            throw new IllegalArgumentException("Can't register a zero-length type array");
        }
        for (Class c : cs) {
            if (c == null) {
                throw new IllegalArgumentException("There shouldn't be any null types in this array!");
            }
        }

        ArrayHolder<Class> ah = new ArrayHolder<>(cs);
        ImList<Class> registeredTypes;
        synchronized (Lock.INSTANCE) {
            registeredTypes = typeMap.get(ah);
            if (registeredTypes == null) {
                ImList<Class> vecCs = vec(cs);
                typeMap.put(ah, vecCs);
                registeredTypes = vecCs;
            }
        }
        // We are returning the original array.  If we returned our safe copy, it could be modified!
        return registeredTypes;
    }

    // The one time I thought I needed this I was confused.  I had a class Or which has types Good and Bad.
    // I got confused and passed whatever the good and bad values were when I really wanted just the types Good and Bad.
//    /**
//     Use this to prevent duplicate runtime types.
//     @param os an array of objects whose classes to register
//     */
//    public static ImList<Class> registerObjects(Object... os) {
//        if (os == null) {
//            throw new IllegalArgumentException("Can't register a null object array");
//        }
//        // We aren't doing extra work here because we have to return a different array from what we store
//        // in the hash map.
//        return registerClasses( vec(os).map(o -> o == null ? (Class) null
//                                                           : o.getClass())
//                                       .toImList() );
//    }

    public static String name(Class c) { return (c == null) ? "null" : c.getSimpleName(); }

    public static String union2Str(Object item, ImList<Class> types) {
            StringBuilder sB = new StringBuilder();
            sB.append(stringify(item)).append(":");
            boolean isFirst = true;
            for (Class c : types) {
                if (isFirst) {
                    isFirst = false;
                } else {
                    sB.append("|");
                }
                sB.append(RuntimeTypes.name(c));
            }
            return sB.toString();
    }

    static int size() { return typeMap.size(); }
}


// 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.Objects;

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

// ======================================================================================
// THIS CLASS IS GENERATED BY /tupleGenerator/TupleGenerator.java.  DO NOT EDIT MANUALLY!
// ======================================================================================

/**
 Holds 7 items of potentially different types.  Designed to let you easily create immutable
 subclasses (to give your data structures meaningful names) with correct equals(), hashCode(), and
 toString() methods.
 */
public class Tuple7<A,B,C,D,E,F,G> implements Serializable {

    // For serializable.  Make sure to change whenever internal data format changes.
    // Implemented because implementing serializable only on a sub-class of an
    // immutable class requires a serialization proxy.  That's probably worse than
    // the conceptual burdeon of all tuples being Serializable.    private static final long serialVersionUID = 20160906065500L;

    // Fields are protected so that sub-classes can make accessor methods with meaningful names.
    protected final A _1;
    protected final B _2;
    protected final C _3;
    protected final D _4;
    protected final E _5;
    protected final F _6;
    protected final G _7;

    /**
     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 Tuple7(A a, B b, C c, D d, E e, F f, G g) {
        _1 = a; _2 = b; _3 = c; _4 = d; _5 = e; _6 = f; _7 = g;
    }

    /** Public static factory method */
    public static <A,B,C,D,E,F,G> Tuple7<A,B,C,D,E,F,G> of(A a, B b, C c, D d, E e, F f, G g) {
        return new Tuple7<>(a, b, c, d, e, f, g);
    }

    /** Returns the 1st field */
    public A _1() { return _1; }
    /** Returns the 2nd field */
    public B _2() { return _2; }
    /** Returns the 3rd field */
    public C _3() { return _3; }
    /** Returns the 4th field */
    public D _4() { return _4; }
    /** Returns the 5th field */
    public E _5() { return _5; }
    /** Returns the 6th field */
    public F _6() { return _6; }
    /** Returns the 7th field */
    public G _7() { return _7; }

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

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

        return Objects.equals(this._1, that._1()) &&
               Objects.equals(this._2, that._2()) &&
               Objects.equals(this._3, that._3()) &&
               Objects.equals(this._4, that._4()) &&
               Objects.equals(this._5, that._5()) &&
               Objects.equals(this._6, that._6()) &&
               Objects.equals(this._7, that._7());
    }

    @Override
    public int hashCode() {
        // First 2 fields match Tuple2 which implements java.util.Map.Entry as part of the map
        // contract and therefore must match java.util.HashMap.Node.hashCode().
        int ret = 0;
        if (_1 != null) { ret = _1.hashCode(); }
        if (_2 != null) { ret = ret ^ _2.hashCode(); }
        if (_3 != null) { ret = ret + _3.hashCode(); }
        if (_4 != null) { ret = ret + _4.hashCode(); }
        if (_5 != null) { ret = ret + _5.hashCode(); }
        if (_6 != null) { ret = ret + _6.hashCode(); }
        if (_7 != null) { ret = ret + _7.hashCode(); }
        return ret;
    }
}


/*
 *   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 Jul 5, 2007 */
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.NoSuchElementException;
import java.util.concurrent.atomic.AtomicReference;

/**
 This started out as Rich Hickey's PersistentVector class from Clojure in late 2014.  Glen added
 generic types, tried to make it a little more pure-Java friendly, and removed dependencies on other
 Clojure stuff.

 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 (Primary author)
 @author Glen Peterson (Java-centric editor)
 */
public class PersistentVector<E> extends UnmodList.AbstractUnmodList<E>
        implements ImList<E>, Serializable {

    // There's bit shifting going on here because it's a very fast operation.
    // Shifting right by 5 is aeons faster than dividing by 32.
    private static final int NODE_LENGTH_POW_2 = 5;

    // 0b00000000000000000000000000100000 = 0x20 = 32
    private static final int MAX_NODE_LENGTH = 1 << NODE_LENGTH_POW_2;
    // 0b00000000000000000000000000011111 = 0x1f
    private static final int LOW_BITS = MAX_NODE_LENGTH - 1;

    // Java shift operator review:
    // The signed left shift operator "<<" shifts a bit pattern to the left, and
    // the signed right shift operator ">>" shifts a bit pattern to the right.
    // The bit pattern is given by the left-hand operand, and the
    // number of positions to shift by the right-hand operand.
    // The unsigned right shift operator ">>>" shifts a zero into the leftmost position,
    // while the leftmost position after ">>" depends on sign extension.
    //
    // The bitwise & operator performs a bitwise AND operation.
    //
    // The bitwise ^ operator performs a bitwise exclusive OR operation.
    //
    // The bitwise | operator performs a bitwise inclusive OR operation

    private static class Node {
        // The same data structure backs both the mutable and immutable vector.  The immutable
        // one uses copy-on-write for all operations.  The mutable one still uses copy-on-write
        // for the tree, but not for the tail.  Instead of creating a new tail one bigger after
        // each append, it creates a STRICT_NODE_SIZE tail and inserts items into it in place.
        //
        // The reason we need this AtomicReference is that some mutable vector could still have
        // a pointer to the Tail array that's in the tree.  When first mutating, the current thread
        // is placed in here.  After the mutable structure is made immutable, a null is placed in
        // here.  Subsequent attempts to mutate anything check and if they find the null, they
        // throw an exception.
        transient public final AtomicReference<Thread> edit;

        // This is either the data in the node (for a leaf node), or it's pointers to sub-nodes (for
        // a branch node).  We could probably have two separate classes: NodeLeaf and NodeBranch
        // where NodeLeaf has T[] and NodeBranch has Node<T>[].
        public final Object[] array;

        Node(AtomicReference<Thread> edit, Object[] array) {
            this.edit = edit;
            this.array = array;
        }

        Node(AtomicReference<Thread> edit) {
            this.edit = edit;
            this.array = new Object[MAX_NODE_LENGTH];
        }
    }

    private final static AtomicReference<Thread> NOEDIT = new AtomicReference<>(null);

    private final static Node EMPTY_NODE = new Node(NOEDIT, new Object[MAX_NODE_LENGTH]);

    public final static PersistentVector<?> EMPTY =
            new PersistentVector<>(0, NODE_LENGTH_POW_2, EMPTY_NODE, new Object[]{});

    /** Returns the empty ImList (there only needs to be one) */
    @SuppressWarnings("unchecked")
    public static <T> PersistentVector<T> empty() { return (PersistentVector<T>) EMPTY; }

    /**
     Returns a new mutable vector.  For some reason calling empty().mutable() sometimes requires
     an explicit type parameter in Java, so this convenience method works around that.
     */
    @SuppressWarnings("unchecked")
    public static <T> MutableVector<T> emptyMutable() {
        PersistentVector<T> e = empty();
        return e.mutable();
    }

    /**
     Public static factory method to create a vector from an Iterable.  A varargs version of this
     method is: {@link org.organicdesign.fp.StaticImports#vec(Object...)}.
     */
    static public <T> PersistentVector<T> ofIter(Iterable<T> items) {
        MutableVector<T> ret = emptyMutable();
        for (T item : items) {
            ret.append(item);
        }
        return ret.immutable();
    }

    // ==================================== Instance Variables ====================================
    // The number of items in this Vector.
    private final int size;
    private final int shift;
    private transient final Node root;
    private final E[] tail;

    // ======================================= Constructor =======================================
    /** Constructor */
    private PersistentVector(int z, int shift, Node root, E[] tail) {
        size = z;
        this.shift = shift;
        this.root = root;
        this.tail = tail;
    }

    // ======================================= 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 = 20160904160500L;

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

        private final int size;
        private transient ImList<E> vector;
        SerializationProxy(PersistentVector<E> v) {
            size = v.size();
            vector = v;
        }

        // 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 (E entry : vector) {
                s.writeObject(entry);
            }
        }

        @SuppressWarnings("unchecked")
        private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
            s.defaultReadObject();
            MutableList<E> temp = emptyMutable();
            for (int i = 0; i < size; i++) {
                temp.append((E) s.readObject());
            }
            vector = temp.immutable();
        }

        private Object readResolve() { return vector; }
    }

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

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

    // ===================================== Instance Methods =====================================

    // IEditableCollection has this return ITransientCollection<E>,
    // not MutableVector<E> as this originally returned.
//    @Override
    // We could make this public some day, maybe.
    @Override public MutableVector<E> mutable() { return new MutableVector<>(this); }

    // Returns the high (gt 5) bits of the index of the last item.
    // I think this is the index of the start of the last array in the tree.
    private int tailoff() {
        // ((size - 1) / 32) * 32
        // (Size - 1) is an index into an array because size starts counting from 1 and array
        //            indices start from 0.
        // /32 *32 zeroes out the low 5 bits.
        return (size < MAX_NODE_LENGTH)
                ? 0
                : ((size - 1) >>> NODE_LENGTH_POW_2) << NODE_LENGTH_POW_2;
        // Last line can be replaced with (size -1) & HIGH_BITS
    }

    /** Returns the array (of type E) from the leaf node indicated by the given index. */
    @SuppressWarnings("unchecked")
    private E[] leafNodeArrayFor(int i) {
        // i is the index into this vector.  Each 5 bits represent an index into an array.  The
        // highest 5 bits (that are less than the shift value) are the index into the top-level
        // array. The lowest 5 bits index the the leaf.  The guts of this method indexes into the
        // array at each level, finally indexing into the leaf node.

        if (i >= 0 && i < size) {
            if (i >= tailoff()) {
                return tail;
            }
            Node node = root;
            for (int level = shift; level > 0; level -= NODE_LENGTH_POW_2) {
                node = (Node) node.array[(i >>> level) & LOW_BITS];
            }
            return (E[]) node.array;
        }
        throw new IndexOutOfBoundsException();
    }

    /** Returns the item specified by the given index. */
    @Override public E get(int i) {
        E[] node = leafNodeArrayFor(i);
        return node[i & LOW_BITS];
    }

    /** {@inheritDoc} */
    @SuppressWarnings("unchecked")
    @Override public PersistentVector<E> replace(int i, E val) {
        if (i >= 0 && i < size) {
            if (i >= tailoff()) {
                Object[] newTail = new Object[tail.length];
                System.arraycopy(tail, 0, newTail, 0, tail.length);
                newTail[i & LOW_BITS] = val;

                return new PersistentVector<>(size, shift, root, (E[]) newTail);
            }

            return new PersistentVector<>(size, shift, doAssoc(shift, root, i, val), tail);
        }
        if (i == size) {
            return append(val);
        }
        throw new IndexOutOfBoundsException();
    }

    /** {@inheritDoc} */
    @Override public int size() { return size; }

    /**
     * Inserts a new item at the end of the Vecsicle.
     * @param val the value to insert
     * @return a new Vecsicle with the additional item.
     */
    @SuppressWarnings("unchecked")
    @Override public PersistentVector<E> append(E val) {
        //room in tail?
        //	if(tail.length < MAX_NODE_LENGTH)
        if (size - tailoff() < MAX_NODE_LENGTH) {
            E[] newTail = (E[]) new Object[tail.length + 1];
            System.arraycopy(tail, 0, newTail, 0, tail.length);
            newTail[tail.length] = val;
            return new PersistentVector<>(size + 1, shift, root, newTail);
        }
        //full tail, push into tree
        Node newroot;
        Node tailnode = new Node(root.edit, tail);
        int newshift = shift;
        //overflow root?
        if ((size >>> NODE_LENGTH_POW_2) > (1 << shift)) {
            newroot = new Node(root.edit);
            newroot.array[0] = root;
            newroot.array[1] = newPath(root.edit, shift, tailnode);
            newshift += NODE_LENGTH_POW_2;
        } else {
            newroot = pushTail(shift, root, tailnode);
        }
        return new PersistentVector<>(size + 1, newshift, newroot, (E[]) new Object[]{val});
    }

    /**
     Efficiently adds items to the end of this PersistentVector.
     @param items the values to insert
     @return a new PersistentVector with the additional items at the end.
     */
    @Override public PersistentVector<E> concat(Iterable<? extends E> items) {
        return (PersistentVector<E>) ImList.super.concat(items);
    }

    private Node pushTail(int level, Node parent, Node tailnode) {
        //if parent is leaf, insert node,
        // else does it map to an existing child? -> nodeToInsert = pushNode one more level
        // else alloc new path
        //return  nodeToInsert placed in copy of parent
        int subidx = ((size - 1) >>> level) & LOW_BITS;
        Node ret = new Node(parent.edit, parent.array.clone());
        Node nodeToInsert;
        if (level == NODE_LENGTH_POW_2) {
            nodeToInsert = tailnode;
        } else {
            Node child = (Node) parent.array[subidx];
            nodeToInsert = (child == null)
                    ? newPath(root.edit, level - NODE_LENGTH_POW_2, tailnode)
                    : pushTail(level - NODE_LENGTH_POW_2, child, tailnode);
        }
        ret.array[subidx] = nodeToInsert;
        return ret;
    }

    /** {@inheritDoc} */
    @Override public UnmodListIterator<E> listIterator(int index) {
        if( (index < 0) || (index > size) ) {
            // To match ArrayList and other java.util.List expectations
            throw new IndexOutOfBoundsException("Index: " + index);
        }
        return new UnmodListIterator<E>() {
            private int i = index;
            private int base = i - (i % MAX_NODE_LENGTH);
            private E[] array = (index < size()) ? leafNodeArrayFor(i) : null;

            /** {@inheritDoc} */
            @Override public boolean hasNext() { return i < size(); }
            /** {@inheritDoc} */
            @Override public boolean hasPrevious() { return i > 0; }

            /** {@inheritDoc} */
            @Override public E next() {
                if (i >= size) {
                    // To match ArrayList and other java.util.List expectations
                    // If we didn't catch this, it would be an ArrayIndexOutOfBoundsException.
                    throw new NoSuchElementException();
                }
                if (i - base == MAX_NODE_LENGTH) {
                    array = leafNodeArrayFor(i);
                    base += MAX_NODE_LENGTH;
                }
                return array[i++ & LOW_BITS];
            }

            /** {@inheritDoc} */
            @Override public int nextIndex() { return i; }
            /** {@inheritDoc} */
            @Override public E previous() {
                // To match contract of ListIterator and implementation of ArrayList
                if (i < 1) {
                    // To match ArrayList and other java.util.List expectations.
                    throw new NoSuchElementException();
                }
                if (i - base == 0) {
//                    System.out.println("i - base was zero");
                    array = leafNodeArrayFor(i - 1);
                    base -= MAX_NODE_LENGTH;
                } else if (i == size) {
                    // Can start with index past array.
                    array = leafNodeArrayFor(i - 1);
                    base = i - (i % MAX_NODE_LENGTH);
                }
                return array[--i & LOW_BITS];
            }
        };
    }

//    Iterator<E> rangedIterator(final int start, final int end) {
//        return new Iterator<E>() {
//            int i = start;
//            int base = i - (i % MAX_NODE_LENGTH);
//            E[] array = (start < size()) ? leafNodeArrayFor(i) : null;
//
//            @Override
//            public boolean hasNext() {
//                return i < end;
//            }
//
//            @Override
//            public E next() {
//                if (i - base == MAX_NODE_LENGTH) {
//                    array = leafNodeArrayFor(i);
//                    base += MAX_NODE_LENGTH;
//                }
//                return array[i++ & LOW_BITS];
//            }
//
//            @Override
//            public void remove() {
//                throw new UnsupportedOperationException();
//            }
//        };
//    }

//    public UnmodIterator<E> iterator() {
//        return rangedIterator(0, size());
//    }

//    @SuppressWarnings("unchecked")
//    public <U> U reduce(Fn2<U, E, U> f, U init) {
//        int step = 0;
//        for (int i = 0; i < size; i += step) {
//            E[] array = leafNodeArrayFor(i);
//            for (int j = 0; j < array.length; ++j) {
//                init = f.apply(init, array[j]);
//
//                if ( (init != null) && (init instanceof Reduced) ) {
//                    return ((Reduced<U>) init).val;
//                }
//            }
//            step = array.length;
//        }
//        return init;
//    }

//    @Override public IPersistentCollection<E> empty(){
//    	return emptyPersistentCollection(meta());
//    }

//    @SuppressWarnings("unchecked")
//    public ImVectorImpl<E> pop() {
//        if (size == 0)
//            throw new IllegalStateException("Can't pop empty vector");
//        if (size == 1)
//            return empty();
//        //if(tail.length > 1)
//        if (size - tailoff() > 1) {
//            E[] newTail = (E[]) new Object[tail.length - 1];
//            System.arraycopy(tail, 0, newTail, 0, newTail.length);
//            return new ImVectorImpl<>(size - 1, shift, root, newTail);
//        }
//        E[] newtail = leafNodeArrayFor(size - 2);
//
//        Node newroot = popTail(shift, root);
//        int newshift = shift;
//        if (newroot == null) {
//            newroot = EMPTY_NODE;
//        }
//        if (shift > NODE_LENGTH_POW_2 && newroot.array[1] == null) {
//            newroot = (Node) newroot.array[0];
//            newshift -= NODE_LENGTH_POW_2;
//        }
//        return new ImVectorImpl<>(size - 1, newshift, newroot, newtail);
//    }

//    private Node popTail(int level, Node node) {
//        int subidx = ((size - 2) >>> level) & LOW_BITS;
//        if (level > NODE_LENGTH_POW_2) {
//            Node newchild = popTail(level - NODE_LENGTH_POW_2, (Node) node.array[subidx]);
//            if (newchild == null && subidx == 0)
//                return null;
//            else {
//                Node ret = new Node(root.edit, node.array.clone());
//                ret.array[subidx] = newchild;
//                return ret;
//            }
//        } else if (subidx == 0)
//            return null;
//        else {
//            Node ret = new Node(root.edit, node.array.clone());
//            ret.array[subidx] = null;
//            return ret;
//        }
//    }

    private static Node doAssoc(int level, Node node, int i, Object val) {
        Node ret = new Node(node.edit, node.array.clone());
        if (level == 0) {
            ret.array[i & LOW_BITS] = val;
        } else {
            int subidx = (i >>> level) & LOW_BITS;
            ret.array[subidx] = doAssoc(level - NODE_LENGTH_POW_2,
                                        (Node) node.array[subidx],
                                        i, val);
        }
        return ret;
    }

    private static Node newPath(AtomicReference<Thread> edit, int level, Node node) {
        if (level == 0) {
            return node;
        }
        Node ret = new Node(edit);
        ret.array[0] = newPath(edit, level - NODE_LENGTH_POW_2, node);
        return ret;
    }

//    public static class Reduced<A> {
//        public final A val;
//        private Reduced(A a) { val = a; }
//    }
//
//    /**
//     * This is an early exit indicator for reduce operations.  Return one of these when you want
//     * the reduction to end. It uses types, but not in a "traditional" way.
//     */
//    public static <A> Reduced<A> done(A a) { return new Reduced<>(a); }

    // Implements Counted through ITransientVector<E> -> Indexed<E> -> Counted.
    @SuppressWarnings("WeakerAccess")
    public static final class MutableVector<F> extends UnmodList.AbstractUnmodList<F>
            implements MutableList<F> {

        // The number of items in this Vector.
        private int size;

        private int shift;

        // The root node of the data tree inside this vector.
        private Node root;

        private F[] tail;

        private MutableVector(int c, int s, Node r, F[] t) {
            size = c; shift = s; root = r; tail = t;
        }

        private MutableVector(PersistentVector<F> v) {
            this(v.size, v.shift, editableRoot(v.root), editableTail(v.tail));
        }

        private Node ensureEditable(Node node) {
            if (node.edit == root.edit)
                return node;
            return new Node(root.edit, node.array.clone());
        }

        private void ensureEditable() {
            if (root.edit.get() == null) {
                throw new IllegalAccessError("Mutable used after immutable! call");
            }
            //		root = editableRoot(root);
            //		tail = editableTail(tail);
        }

        @Override  public int size() {
            ensureEditable();
            return size;
        }

        @SuppressWarnings("unchecked")
        @Override  public PersistentVector<F> immutable() {
            ensureEditable();
            //		Thread owner = root.edit.get();
            //		if(owner != null && owner != Thread.currentThread())
            //			{
            //			throw new IllegalAccessError("Mutation release by non-owner thread");
            //			}
            root.edit.set(null);
            F[] trimmedTail = (F[]) new Object[size - tailoff()];
            System.arraycopy(tail, 0, trimmedTail, 0, trimmedTail.length);
            return new PersistentVector<>(size, shift, root, trimmedTail);
        }

        @SuppressWarnings("unchecked")
        @Override  public MutableList<F> append(F val) {
            ensureEditable();
            int i = size;
            //room in tail?
            if (i - tailoff() < MAX_NODE_LENGTH) {
                tail[i & LOW_BITS] = val;
                ++size;
                return this;
            }
            //full tail, push into tree
            Node newroot;
            Node tailnode = new Node(root.edit, tail);
            tail = (F[]) new Object[MAX_NODE_LENGTH];
            tail[0] = val;
            int newshift = shift;
            //overflow root?
            if ((size >>> NODE_LENGTH_POW_2) > (1 << shift)) {
                newroot = new Node(root.edit);
                newroot.array[0] = root;
                newroot.array[1] = newPath(root.edit, shift, tailnode);
                newshift += NODE_LENGTH_POW_2;
            } else
                newroot = pushTail(shift, root, tailnode);
            root = newroot;
            shift = newshift;
            ++size;
            return this;
        }

        // TODO: are these all node<F> or could this return a super-type of F?
        @SuppressWarnings("unchecked")
        private Node pushTail(int level, Node parent, Node tailnode) {
            //if parent is leaf, insert node,
            // else does it map to an existing child? -> nodeToInsert = pushNode one more level
            // else alloc new path
            //return  nodeToInsert placed in parent
            parent = ensureEditable(parent);
            int subidx = ((size - 1) >>> level) & LOW_BITS;
            Node ret = parent;
            Node nodeToInsert;
            if (level == NODE_LENGTH_POW_2) {
                nodeToInsert = tailnode;
            } else {
                Node child = (Node) parent.array[subidx];
                nodeToInsert = (child != null) ?
                        pushTail(level - NODE_LENGTH_POW_2, child, tailnode)
                        : newPath(root.edit, level - NODE_LENGTH_POW_2, tailnode);
            }
            ret.array[subidx] = nodeToInsert;
            return ret;
        }

        // Returns the high (gt 5) bits of the index of the last item.
        // I think this is the index of the start of the last array in the tree.
        private int tailoff() {
            // ((size - 1) / 32) * 32
            // (Size - 1) is an index into an array because size starts counting from 1 and array
            //            indices start from 0.
            // /32 *32 zeroes out the low 5 bits.
            return (size < MAX_NODE_LENGTH)
                    ? 0
                    : ((size - 1) >>> NODE_LENGTH_POW_2) << NODE_LENGTH_POW_2;
            // Last line can be replaced with (size -1) & HIGH_BITS
        }

//        @SuppressWarnings("unchecked")
//        private F[] leafNodeArrayFor(int i) {
//            if (i >= 0 && i < size) {
//                if (i >= tailoff()) {
//                    return tail;
//                }
//                Node node = root;
//                for (int level = shift; level > 0; level -= NODE_LENGTH_POW_2) {
//                    node = (Node) node.array[(i >>> level) & LOW_BITS];
//                }
//                return (F[]) node.array;
//            }
//            throw new IndexOutOfBoundsException();
//        }

        @SuppressWarnings("unchecked")
        private F[] editableArrayFor(int i) {
            if (i >= 0 && i < size) {
                if (i >= tailoff())
                    return tail;
                Node node = root;
                for (int level = shift; level > 0; level -= NODE_LENGTH_POW_2)
                    node = ensureEditable((Node) node.array[(i >>> level) & LOW_BITS]);
                return (F[]) node.array;
            }
            throw new IndexOutOfBoundsException();
        }

        @Override public F get(int i) {
            ensureEditable();
            F[] node = editableArrayFor(i);
            return node[i & LOW_BITS];
        }

        @Override public MutableList<F> replace(int idx, F e) {
            ensureEditable();
            F[] node = editableArrayFor(idx);
            node[idx & LOW_BITS] = e;
            return this;
        }

//
//        public F nth(int i, F notFound) {
//            if (i >= 0 && i < size())
//                return nth(i);
//            return notFound;
//        }
//
//        /** Convenience method for using any class that implements Number as a key. */
//        public F nth(Number key) { return nth(key.intValue(), null); }
//
//        /** Convenience method for using any class that implements Number as a key. */
//        public F nth(Number key, F notFound) { return nth(key.intValue(), notFound); }

//        public MutableList<F> insertAt(int i, F val) {
//            ensureEditable();
//            if (i >= 0 && i < size) {
//                if (i >= tailoff()) {
//                    tail[i & LOW_BITS] = val;
//                    return this;
//                }
//
//                root = doAssoc(shift, root, i, val);
//                return this;
//            } else if (i == size) {
//                return concat(val);
//            }
//            throw new IndexOutOfBoundsException();
//        }

//        public MutableList<F> assoc(int key, F val) {
//            //note - relies on ensureEditable in insertAt
//            return insertAt(key, val);
//        }
//
//        public MutableList<F> assoc(Number key, F val) {
//            return insertAt(key.intValue(), val);
//        }

//        @SuppressWarnings("unchecked")
//        private Node doAssoc(int level, Node node, int i, Object val) {
//            node = ensureEditable(node);
//            Node ret = node;
//            if (level == 0) {
//                ret.array[i & LOW_BITS] = val;
//            } else {
//                int subidx = (i >>> level) & LOW_BITS;
//                ret.array[subidx] = doAssoc(level - NODE_LENGTH_POW_2,
//                                            (Node) node.array[subidx], i, val);
//            }
//            return ret;
//        }

//        @SuppressWarnings("unchecked")
//        public MutableList<F> pop() {
//            ensureEditable();
//            if (size == 0)
//                throw new IllegalStateException("Can't pop empty vector");
//            if (size == 1) {
//                size = 0;
//                return this;
//            }
//            int i = size - 1;
//            //pop in tail?
//            if ((i & LOW_BITS) > 0) {
//                --size;
//                return this;
//            }
//
//            F[] newtail = editableArrayFor(size - 2);
//
//            Node newroot = popTail(shift, root);
//            int newshift = shift;
//            if (newroot == null) {
//                newroot = new Node(root.edit);
//            }
//            if (shift > NODE_LENGTH_POW_2 && newroot.array[1] == null) {
//                newroot = ensureEditable((Node) newroot.array[0]);
//                newshift -= NODE_LENGTH_POW_2;
//            }
//            root = newroot;
//            shift = newshift;
//            --size;
//            tail = newtail;
//            return this;
//        }

//        @SuppressWarnings("unchecked")
//        private Node popTail(int level, Node node) {
//            node = ensureEditable(node);
//            int subidx = ((size - 2) >>> level) & LOW_BITS;
//            if (level > NODE_LENGTH_POW_2) {
//                Node newchild = popTail(level - NODE_LENGTH_POW_2, (Node) node.array[subidx]);
//                if (newchild == null && subidx == 0)
//                    return null;
//                else {
//                    node.array[subidx] = newchild;
//                    return node;
//                }
//            } else if (subidx == 0)
//                return null;
//            else {
//                node.array[subidx] = null;
//                return node;
//            }
//        }

        private static Node editableRoot(Node node) {
            return new Node(new AtomicReference<>(Thread.currentThread()), node.array.clone());
        }
        @SuppressWarnings("unchecked")
        private static <T> T[] editableTail(T[] tl) {
            Object[] ret = new Object[MAX_NODE_LENGTH];
            System.arraycopy(tl, 0, ret, 0, tl.length);
            return (T[]) ret;
        }
    } // end inner static class MutableVector
}


// 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.Objects;

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

// ======================================================================================
// THIS CLASS IS GENERATED BY /tupleGenerator/TupleGenerator.java.  DO NOT EDIT MANUALLY!
// ======================================================================================

/**
 Holds 9 items of potentially different types.  Designed to let you easily create immutable
 subclasses (to give your data structures meaningful names) with correct equals(), hashCode(), and
 toString() methods.
 */
public class Tuple9<A,B,C,D,E,F,G,H,I> implements Serializable {

    // For serializable.  Make sure to change whenever internal data format changes.
    // Implemented because implementing serializable only on a sub-class of an
    // immutable class requires a serialization proxy.  That's probably worse than
    // the conceptual burdeon of all tuples being Serializable.    private static final long serialVersionUID = 20160906065500L;

    // Fields are protected so that sub-classes can make accessor methods with meaningful names.
    protected final A _1;
    protected final B _2;
    protected final C _3;
    protected final D _4;
    protected final E _5;
    protected final F _6;
    protected final G _7;
    protected final H _8;
    protected final I _9;

    /**
     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 Tuple9(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
        _1 = a; _2 = b; _3 = c; _4 = d; _5 = e; _6 = f; _7 = g; _8 = h; _9 = i;
    }

    /** Public static factory method */
    public static <A,B,C,D,E,F,G,H,I> Tuple9<A,B,C,D,E,F,G,H,I>
    of(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
        return new Tuple9<>(a, b, c, d, e, f, g, h, i);
    }

    /** Returns the 1st field */
    public A _1() { return _1; }
    /** Returns the 2nd field */
    public B _2() { return _2; }
    /** Returns the 3rd field */
    public C _3() { return _3; }
    /** Returns the 4th field */
    public D _4() { return _4; }
    /** Returns the 5th field */
    public E _5() { return _5; }
    /** Returns the 6th field */
    public F _6() { return _6; }
    /** Returns the 7th field */
    public G _7() { return _7; }
    /** Returns the 8th field */
    public H _8() { return _8; }
    /** Returns the 9th field */
    public I _9() { return _9; }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "(" +
               stringify(_1) + "," + stringify(_2) + "," +
               stringify(_3) + "," + stringify(_4) + "," + stringify(_5) + "," +
               stringify(_6) + "," + stringify(_7) + "," + stringify(_8) + "," +
               stringify(_9) + ")";
    }

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

        return Objects.equals(this._1, that._1()) &&
               Objects.equals(this._2, that._2()) &&
               Objects.equals(this._3, that._3()) &&
               Objects.equals(this._4, that._4()) &&
               Objects.equals(this._5, that._5()) &&
               Objects.equals(this._6, that._6()) &&
               Objects.equals(this._7, that._7()) &&
               Objects.equals(this._8, that._8()) &&
               Objects.equals(this._9, that._9());
    }

    @Override
    public int hashCode() {
        // First 2 fields match Tuple2 which implements java.util.Map.Entry as part of the map
        // contract and therefore must match java.util.HashMap.Node.hashCode().
        int ret = 0;
        if (_1 != null) { ret = _1.hashCode(); }
        if (_2 != null) { ret = ret ^ _2.hashCode(); }
        if (_3 != null) { ret = ret + _3.hashCode(); }
        if (_4 != null) { ret = ret + _4.hashCode(); }
        if (_5 != null) { ret = ret + _5.hashCode(); }
        if (_6 != null) { ret = ret + _6.hashCode(); }
        if (_7 != null) { ret = ret + _7.hashCode(); }
        if (_8 != null) { ret = ret + _8.hashCode(); }
        if (_9 != null) { ret = ret + _9.hashCode(); }
        return ret;
    }
}


// Copyright 2014-09-22 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;

import org.organicdesign.fp.collections.ImList;
import org.organicdesign.fp.collections.ImMap;
import org.organicdesign.fp.collections.ImSet;
import org.organicdesign.fp.collections.ImSortedMap;
import org.organicdesign.fp.collections.ImSortedSet;
import org.organicdesign.fp.collections.MutableList;
import org.organicdesign.fp.collections.MutableMap;
import org.organicdesign.fp.collections.MutableSet;
import org.organicdesign.fp.collections.PersistentHashMap;
import org.organicdesign.fp.collections.PersistentHashSet;
import org.organicdesign.fp.collections.PersistentTreeMap;
import org.organicdesign.fp.collections.PersistentTreeSet;
import org.organicdesign.fp.collections.PersistentVector;
import org.organicdesign.fp.collections.RrbTree;
import org.organicdesign.fp.collections.RrbTree.ImRrbt;
import org.organicdesign.fp.collections.RrbTree.MutableRrbt;
import org.organicdesign.fp.collections.UnmodIterable;
import org.organicdesign.fp.collections.UnmodIterator;
import org.organicdesign.fp.tuple.Tuple2;
import org.organicdesign.fp.tuple.Tuple3;
import org.organicdesign.fp.xform.Xform;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;

/**

A mini data definition language composed of short methods like vec(), tup(), map(),
 set(), plus xform() which makes java.util collections transformable.

import org.organicdesign.fp.StaticImports.*

// Create a new vector of integers
vec(1, 2, 3, 4);

// Create a new set of Strings
set("a", "b", "c");

// Create a tuple of an int and a string (a type-safe heterogeneous container)
tup("a", 1);

// Create a map with a few key value pairs
map(tup("a", 1),
    tup("b", 2),
    tup("c", 3));

There are only a few methods in this project to take varargs and they are all in this file.
 Writing out versions that took multiple type-safe arguments caused IntelliJ to present all of them
 for auto-completion which was overwhelming, so I reverted to varargs.  Also,
 varargs relax some type safety rules (variance) for data definition in a generally helpful (rarely
 dangerous) way.

If you're used to Clojure/JSON, you'll find that what's a map (dictionary) in those languages
 sometimes becomes a tuple in Paguro and sometimes becomes a map. A map in a type-safe language is
 homogeneous, meaning that every member is of the same type (or a descendant of a common ancestor).
 Tuples are designed to contain unrelated data types and enforce those types.

As with any usage of import *, there could be issues if you import 2 different versions of this
 file in your classpath, or if a method is ever removed from this file.  Java needs a data
 definition language so badly that I think it is worth this small risk.

 */
@SuppressWarnings("UnusedDeclaration")
public final class StaticImports {
    // Prevent instantiation
    private StaticImports() { throw new UnsupportedOperationException("No instantiation"); }

    /**
     Returns a new PersistentHashMap of the given keys and their paired values.  Use the
     {@link StaticImports#tup(Object, Object)} method to define those key/value pairs briefly and
     easily.  This data definition method is one of the few methods in this project that support
     varargs.

     @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 and any number of null values.  Null k/v pairs will be silently ignored.

     @return a new PersistentHashMap of the given key/value pairs
     */
    @SafeVarargs
    public static <K,V> ImMap<K,V> map(Map.Entry<K,V>... kvPairs) {
        if ( (kvPairs == null) || (kvPairs.length < 1) ) { return PersistentHashMap.empty(); }
        return PersistentHashMap.of(Arrays.asList(kvPairs));
    }

    /**
     Returns a new MutableMap of the given keys and their paired values.  Use the
     {@link StaticImports#tup(Object, Object)} method to define those key/value pairs briefly and
     easily.  This data definition method is one of the few methods in this project that support
     varargs.

     @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 and any number of null values.  Null k/v pairs will be silently ignored.

     @return a new MutableMap of the given key/value pairs
     */
    @SafeVarargs
    public static <K,V> MutableMap<K,V> mutableMap(Map.Entry<K,V>... kvPairs) {
        MutableMap<K,V> ret = PersistentHashMap.emptyMutable();
        if (kvPairs == null) { return ret; }
        for (Map.Entry<K,V> me : kvPairs) {
            ret.assoc(me);
        }
        return ret;
    }

    /**
     Returns a mutable RRB Tree {@link MutableRrbt} of the given items.
     The RRB Tree is a list-type data structure that supports random inserts, split, and join
     (the PersistentVector does not).  The mutable RRB Tree append() method is only about half
     as fast as the PersistentVector method of the same name.  If you build it entirely with random
     inserts, then the RRB tree get() method may be about 5x slower.  Otherwise, performance
     is about the same.
     This data definition method is one of the few methods in this project that support varargs.
     */
    @SafeVarargs
    static public <T> MutableRrbt<T> mutableRrb(T... items) {
        if ( (items == null) || (items.length < 1) ) { return RrbTree.emptyMutable(); }
        return RrbTree.<T>emptyMutable()
                .concat(Arrays.asList(items));
    }

    /**
     Returns a new MutableSet of the values.  This data definition method is one of the few
     methods in this project that support varargs.  If the input contains duplicate elements, later
     values overwrite earlier ones.
     */
    @SafeVarargs
    public static <T> MutableSet<T> mutableSet(T... items) {
        MutableSet<T> ret = PersistentHashSet.emptyMutable();
        if (items == null) { return ret; }
        for (T t : items) {
            ret.put(t);
        }
        return ret;
    }

    /**
     Returns a MutableVector of the given items.  This data definition method is one of the
     few methods in this project that support varargs.
     */
    @SafeVarargs
    public static <T> MutableList<T> mutableVec(T... items) {
        MutableList<T> ret = PersistentVector.emptyMutable();
        if (items == null) { return ret; }
        for (T t : items) {
            ret.append(t);
        }
        return ret;
    }

    /**
     Returns a new immutable RRB Tree {@link ImRrbt} of the given items.  An RRB Tree
     is an immutable list that supports random inserts, split, and join (the PersistentVector does
     not).  If you build it entirely with random
     inserts, then the RRB tree get() method may be about 5x slower.  Otherwise, performance
     is about the same.

     This data definition method is one of the few methods in this project that support varargs.
     */
    @SafeVarargs
    static public <T> ImRrbt<T> rrb(T... items) {
        if ( (items == null) || (items.length < 1) ) { return RrbTree.empty(); }
        return mutableRrb(items).immutable();
    }

    /**
     Returns a new PersistentHashSet of the values.  This data definition method is one of the few
     methods in this project that support varargs.  If the input contains duplicate elements, later
     values overwrite earlier ones.
     */
    @SafeVarargs
    public static <T> ImSet<T> set(T... items) {
        if ( (items == null) || (items.length < 1) ) { return PersistentHashSet.empty(); }
        return PersistentHashSet.of(Arrays.asList(items));
    }

    /**
     Returns a new PersistentTreeMap of the specified comparator and the given key/value pairs.  Use
     the tup() method to define those key/value pairs briefly and easily.  The keys are sorted
     according to the comparator you provide.

     @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> ImSortedMap<K,V>
    sortedMap(Comparator<? super K> comp, Iterable<Map.Entry<K,V>> kvPairs) {
        return PersistentTreeMap.ofComp(comp, kvPairs);
    }

    /**
     Returns a new PersistentTreeMap of the given comparable keys and their paired values, sorted in
     the default ordering of the keys.  Use the tup() method to define those key/value pairs briefly
     and easily.

     @param kvPairs Key/value pairs (to go into the map).  In the case of a duplicate key, later
     values overwrite earlier ones.
     @return a new PersistentTreeMap of the specified comparator and the given key/value pairs which
     uses the default comparator defined on the element type.
     */
    public static <K extends Comparable<K>,V> ImSortedMap<K,V>
    sortedMap(Iterable<Map.Entry<K,V>> kvPairs) { return PersistentTreeMap.of(kvPairs); }

    /**
     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 duplicates, later elements overwrite
     earlier ones.
     @return a new PersistentTreeSet of the specified comparator and the given elements
     */
    public static <T> ImSortedSet<T> sortedSet(Comparator<? super T> comp, Iterable<T> elements) {
        return Xform.of(elements).toImSortedSet(comp);
    }

    /** Returns a new PersistentTreeSet of the given comparable items. */
    public static <T extends Comparable<T>> ImSortedSet<T> sortedSet(Iterable<T> items) {
        return PersistentTreeSet.of(items);
    }

    /** Returns a new Tuple2 of the given items. */
    public static <T,U> Tuple2<T,U> tup(T t, U u) { return Tuple2.of(t, u); }

    /** Returns a new Tuple3 of the given items. */
    public static <T,U,V> Tuple3<T,U,V> tup(T t, U u, V v) { return Tuple3.of(t, u, v); }

    /**
     Returns a new PersistentVector of the given items.  This data definition method is one of the
     few methods in this project that support varargs.
     */
    @SafeVarargs
    static public <T> ImList<T> vec(T... items) {
        if ( (items == null) || (items.length < 1) ) { return PersistentVector.empty(); }
        return mutableVec(items).immutable();
    }

    /**
     If you need to wrap a regular Java collection or other iterable outside this project to perform
     a transformation on it, this method is the most convenient, efficient way to do so.
     */
    public static <T> UnmodIterable<T> xform(Iterable<T> iterable) { return Xform.of(iterable); }

    /**
     If you need to wrap a regular Java array outside this project to perform
     a transformation on it, this method is the most convenient, efficient way to do so.
     */
    @SafeVarargs
    public static <T> UnmodIterable<T> xformArray(T... items) {
        return Xform.of(Arrays.asList(items));
    }

    // TODO: Enable this to make Maps, Strings, and StringBuilders work like other collections.
//    /** Wrap a Java.util.Map to perform a transformation on it. */
//    public static <K,V> UnmodIterable<Map.Entry<K,V>> xform(Map<K,V> map) {
//        return Xform.of(map.entrySet());
//    }
//
    /** Wrap a String (or CharSequence) to perform a Character-by-Character transformation on it. */
    public static UnmodIterable<Character> xformChars(CharSequence seq) {
        //noinspection Convert2Lambda
        return new UnmodIterable<Character>() {
            @Override public UnmodIterator<Character> iterator() {
                return new UnmodIterator<Character>() {
                    private int idx = 0;
                    @Override public boolean hasNext() { return idx < seq.length(); }

                    @Override public Character next() {
                        int nextIdx = idx + 1;
                        Character c = seq.charAt(idx);
                        idx = nextIdx;
                        return c;
                    }
                };
            }
        };
    }
}


package org.organicdesign.fp.indent;

public class IndentUtils {

    private IndentUtils() {
        throw new UnsupportedOperationException("No instantiation");
    }

    // ========================================== STATIC ==========================================
    // Note, this is part of something completely different, but was especially useful for
    // debugging the above.  So much so, that I want to keep it when I'm done, but it needs
    // to move somewhere else before releasing.
    private static final String[] SPACES = {
            "",
            " ",
            "  ",
            "   ",
            "    ",
            "     ",
            "      ",
            "       ",
            "        ",
            "         ",
            "          ",
            "           ",
            "            ",
            "             ",
            "              ",
            "               ",
            "                ",
            "                 ",
            "                  ",
            "                   ",
            "                    ",
            "                     ",
            "                      ",
            "                       ",
            "                        ",
            "                         ",
            "                          ",
            "                           ",
            "                            ",
            "                             ",
            "                              ",
            "                               ",
            "                                ",
            "                                 ",
            "                                  ",
            "                                   ",
            "                                    ",
            "                                     ",
            "                                      ",
            "                                       ",
            "                                        ",
            "                                         ",
            "                                          ",
            "                                           ",
            "                                            ",
            "                                             ",
            "                                              ",
            "                                               ",
            "                                                "};

    private static final int SPACES_LENGTH_MINUS_ONE = SPACES.length - 1;

    /**
     Creates a new StringBuilder with the given number of spaces and returns it.
     @param len the number of spaces
     @return a {@link StringBuilder} with the specificed number of initial spaces.
     */
    public static StringBuilder indentSpace(int len) {
        StringBuilder sB = new StringBuilder();
        if (len < 1) { return sB; }
        while (len > SPACES_LENGTH_MINUS_ONE) {
            sB.append(SPACES[SPACES_LENGTH_MINUS_ONE]);
            len = len - SPACES_LENGTH_MINUS_ONE;
        }
        return sB.append(SPACES[len]);
    }

    /**
     There is Arrays.toString, but this is intended to produce Cymling code some day.
     */
    public static <T> String arrayString(T[] items) {
        StringBuilder sB = new StringBuilder("A[");
        boolean isFirst = true;
        for (T item : items) {
            if (isFirst) {
                isFirst = false;
            } else {
                sB.append(" ");
            }
            if (item instanceof String) {
                sB.append("\"").append(item).append("\"");
            } else {
                sB.append(item);
            }
        }
        return sB.append("]").toString();
    }

    /**
     There is Arrays.toString, but this is intended to produce Cymling code some day.
     */
    // TODO: We need one of these for each type of primitive for pretty-printing without commas.
    public static String arrayString(int[] items) {
        StringBuilder sB = new StringBuilder("i[");
        boolean isFirst = true;
        for (int item : items) {
            if (isFirst) {
                isFirst = false;
            } else {
                sB.append(" ");
            }
            sB.append(item);
        }
        return sB.append("]").toString();
    }
}


package org.organicdesign.fp.function;

// Should this bee an inner class of Fn0?
/**
 Lazily initialize a value (and free the initialization resources) on the first call to get().
 Subsequent calls to get() cheaply return the previously initialized value.  This class is thread-safe if the producer
 function and the value it produces are free from side effects.
 */
public class LazyRef<T> implements Fn0<T> {
    private Fn0<T> producer;
    private T value;

    private LazyRef(Fn0<T> p) { producer = p; }

    /**
     Construct a LazyRef from the given initialization function.

     @param producer a zero-argument function that produces the desired value when called.

     @return a LazyRef with the given producer.
     */
    public static <T> LazyRef<T> of(Fn0<T> producer) {
        if (producer == null) {
            throw new IllegalArgumentException("The producer function cannot be null (the value it returns can)");
        }
        return new LazyRef<>(producer);
    }

    /**
     The first call to this method calls the initialization function, caches the result, and hands
     the initialization function reference to the garbage collector so that initialization resources
     can be freed.  Subsequent calls return the precomputed value.

     @return the same value every time it is called.
     */
    // This whole method is synchronized on the advice of Goetz2006 p. 347
    public synchronized T applyEx() {
        // Have we produced our value yet?
        if (producer != null) {
            // produce our value.
            value = producer.apply();
            // Delete the producer to 1. mark the work done and 2. free resources.
            producer = null;
        }
        // We're clear to return the lazily computed value.
        return value;
    }

    // I don't like this because it's not referentially transparent.
//        public boolean isRealizedYet() { return producer == null; }

    // I don't like this because it's not referentially transparent, but it could be helpful for testing.
    /**
     Useful for debugging, but not referentially transparent (sometimes returns LazyRef(*not-computed-yet*),
     sometimes shows the value that was computed).
     @return a string describing this LazyRef and showing whether or not its value has been computed yet.
     */
    public String toString() { return "LazyRef(" + ((producer == null) ? value : "*not-computed-yet*") + ")"; }
}


package org.organicdesign.fp.collections;

import java.lang.reflect.Array;
import java.util.Arrays;

import org.organicdesign.fp.tuple.Tuple2;

/**
 Cowry is short for Copy On Write aRraY and contains utilities for doing this quickly and correctly.
 While a key goal of Paguro is to get away from working with arrays, you still need to do it sometimes and
 shouldn't have to re-implement common copy-on-write modifictions.

 Never, ever, ever do this:
 (Class<T>) items[0].getClass()

 If you are using an array of Numbers and the first item is an Integer and the second a Double,
 You'll get an array of Integers which will blow up when you try to add a Double.

 This class is final and cannot be instantiated.
 Created by gpeterso on 5/21/2017.
 */
@SuppressWarnings("WeakerAccess")
public final class Cowry {
    private Cowry() {
        throw new UnsupportedOperationException("Do not instantiate");
    }

    /**
     * We only one empty array and it makes the code simpler than pointing to null all the time.
     * Have to time the difference between using this and null.  The only difference I can imagine
     * is that this has an address in memory and null does not, so it could save a memory lookup
     * in some places.
     *
     * Since no objects are ever added to or removed from an empty array, it is effectively immutable
     * and there only ever needs to be one.  It also turns out that the type doesn't matter.
     */
    static final Object[] EMPTY_ARRAY = new Object[0];

    // =================================== Array Helper Functions ==================================
    // Helper function to return the empty array of whatever type you need.
    @SuppressWarnings("unchecked")
    public static <T> T[] emptyArray() { return (T[]) EMPTY_ARRAY; }

//    // Thank you jeannicolas
//    // http://stackoverflow.com/questions/80476/how-can-i-concatenate-two-arrays-in-java
//    private static <T> T[] arrayGenericConcat(T[] a, T[] b) {
//        int aLen = a.length;
//        int bLen = b.length;
//
//        @SuppressWarnings("unchecked")
//        T[] c = (T[]) Array.newInstance(a.getClass().getComponentType(), aLen + bLen);
//        System.arraycopy(a, 0, c, 0, aLen);
//        System.arraycopy(b, 0, c, aLen, bLen);
//
//        return c;
//    }

    /**
     * Helper function to avoid type warnings.
     * @return an Object[1] containing the single element.
     */
    @SuppressWarnings("unchecked")
    static <T> T[] singleElementArray(T elem) { return (T[]) new Object[] { elem }; }

    /**
     * Helper function to avoid type warnings.
     * @param elem the item to put in the array.
     * @param tClass the class of the array.
     * @return an array of the appropriate class containing the single element.
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] singleElementArray(T elem, Class<T> tClass) {
        if (tClass == null) {
            return (T[]) new Object[] { elem };
        }

        T[] newItems = (T[]) Array.newInstance(tClass, 1);
        newItems[0] = elem;
        return newItems;
    }

    /**
     * Returns a new array one longer than the given one, with the specified item inserted at the specified index.
     * @param item The item to insert
     * @param items The original array (will not be modified)
     * @param idx the index to insert the item at.
     * @param tClass The runtime class to store in the new array (or null for an Object array).
     * @return A copy of the given array with the additional item at the appropriate index
     *         (will be one longer than the original array).
     */
    public static <T> T[] insertIntoArrayAt(T item, T[] items, int idx, Class<T> tClass) {
        // Make an array that's one bigger.  It's too bad that the JVM bothers to
        // initialize this with nulls.
        @SuppressWarnings("unchecked")
        T[] newItems = (T[]) ((tClass == null) ? new Object[items.length + 1]
                                               : Array.newInstance(tClass, items.length + 1) );

        // If we aren't inserting at the first item, array-copy the items before the insert
        // point.
        if (idx > 0) {
            System.arraycopy(items, 0, newItems, 0, idx);
        }

        // Insert the new item.
        newItems[idx] = item;

        // If we aren't inserting at the last item, array-copy the items after the insert
        // point.
        if (idx < items.length) {
            System.arraycopy(items, idx, newItems, idx + 1, items.length - idx);
        }

        return newItems;
    }

    /**
     * Returns a new array containing the first n items of the given array.
     *
     * @param items the items to copy
     * @param length the maximum length of the new array
     * @param tClass the class of the items in the array (null for Object)
     * @return a copy of the original array with the given length
     */
    public static <T> T[] arrayCopy(T[] items, int length, Class<T> tClass) {
        // Make an array of the appropriate size.  It's too bad that the JVM bothers to
        // initialize this with nulls.
        @SuppressWarnings("unchecked")
        T[] newItems = (T[]) ((tClass == null) ? new Object[length]
                                               : Array.newInstance(tClass, length) );

        // array-copy the items up to the new length.
        if (length > 0) {
            System.arraycopy(items, 0, newItems, 0,
                             items.length < length ? items.length
                                                   : length);
        }
        return newItems;
    }

//    private static <T> T[] insertIntoArrayAt(T item, T[] items, int idx) {
//        return insertIntoArrayAt(item, items, idx, null);
//    }

    /**
     Called splice, but handles precat (idx = 0) and concat (idx = origItems.length).
     @param insertedItems the items to insert
     @param origItems the original items.
     @param idx the index to insert new items at
     @param tClass the class of the resulting new array
     @return a new array with the new items inserted at the proper position of the old array.
     */
    public static <A> A[] spliceIntoArrayAt(A[] insertedItems, A[] origItems, int idx,
                                            Class<A> tClass) {
        // Make an array that big enough.  It's too bad that the JVM bothers to
        // initialize this with nulls.
        @SuppressWarnings("unchecked")
        A[] newItems = tClass == null ? (A[]) new Object[insertedItems.length + origItems.length] :
                       (A[]) Array.newInstance(tClass, insertedItems.length + origItems.length);

        // If we aren't inserting at the first item, array-copy the items before the insert
        // point.
        if (idx > 0) {
            //               src,  srcPos, dest,destPos,length
            System.arraycopy(origItems, 0, newItems, 0, idx);
        }

        // Insert the new items
        //               src,      srcPos,     dest, destPos, length
        System.arraycopy(insertedItems, 0, newItems, idx, insertedItems.length);

        // If we aren't inserting at the last item, array-copy the items after the insert
        // point.
        if (idx < origItems.length) {
            System.arraycopy(origItems, idx, newItems, idx + insertedItems.length,
                             origItems.length - idx);
        }
        return newItems;
    }

//    private static int[] replaceInIntArrayAt(int replacedItem, int[] origItems, int idx) {
//        // Make an array that big enough.  It's too bad that the JVM bothers to
//        // initialize this with nulls.
//        int[] newItems = new int[origItems.length];
//        System.arraycopy(origItems, 0, newItems, 0, origItems.length);
//        newItems[idx] = replacedItem;
//        return newItems;
//    }

    public static <T> T[] replaceInArrayAt(T replacedItem, T[] origItems, int idx,
                                           Class<T> tClass) {
        // Make an array that big enough.  It's too bad that the JVM bothers to
        // initialize this with nulls.
        @SuppressWarnings("unchecked")
        T[] newItems = (T[]) ( (tClass == null) ? new Object[origItems.length]
                                                : Array.newInstance(tClass, origItems.length) );
        System.arraycopy(origItems, 0, newItems, 0, origItems.length);
        newItems[idx] = replacedItem;
        return newItems;
    }

    /**
     Only call this if the array actually needs to be split (0 < splitPoint < orig.length).
     @param orig array to split
     @param splitIndex items less than this index go in the left, equal or greater in the right.
     @return a 2D array of leftItems then rightItems
     */
    public static <T> Tuple2<T[],T[]> splitArray(T[] orig, int splitIndex) { //, Class<T> tClass) {
//        if (splitIndex < 1) {
//            throw new IllegalArgumentException("Called split when splitIndex < 1");
//        }
//        if (splitIndex > orig.length - 1) {
//            throw new IllegalArgumentException("Called split when splitIndex > orig.length - 1");
//        }

        // NOTE:
        // I sort of suspect that generic 2D array creation where the two arrays are of a different
        // length is not possible in Java, or if it is, it's not likely to be much faster than
        // what we have here.  I'd just copy the Arrays.copyOf code everywhere this function is used
        // if you want more speed.
//        int rightLength = orig.length - splitIndex;
//        Class<T> tClass = (Class<T>) orig.getClass().getComponentType();
//        Tuple2<T[],T[]> split = Tuple2.of((T[]) Array.newInstance(tClass, splitIndex),
//                                          (T[]) Array.newInstance(tClass, rightLength));
//
        // Tuple2<T[],T[]> split =
        return Tuple2.of(Arrays.copyOf(orig, splitIndex),
                         Arrays.copyOfRange(orig, splitIndex, orig.length));

//        // original array, offset, newArray, offset, length
//        System.arraycopy(orig, 0, split._1(), 0, splitIndex);
//
//        System.arraycopy(orig, splitIndex, split._2(), 0, rightLength);
//        return split;
    }

    /**
     Only call this if the array actually needs to be split (0 < splitPoint < orig.length).
     @param orig array to split
     @param splitIndex items less than this index go in the left, equal or greater in the right.
     @return a 2D array of leftItems then rightItems
     */
    public static int[][] splitArray(int[] orig, int splitIndex) {
        // This function started an exact duplicate of the one above, but for ints.
//        if (splitIndex < 1) {
//            throw new IllegalArgumentException("Called split when splitIndex < 1");
//        }
//        if (splitIndex > orig.length - 1) {
//            throw new IllegalArgumentException("Called split when splitIndex > orig.length - 1");
//        }
        int rightLength = orig.length - splitIndex;
        int[][] split = new int[][] {new int[splitIndex],
                                     new int[rightLength]};
        // original array, offset, newArray, offset, length
        System.arraycopy(orig, 0, split[0], 0, splitIndex);
        System.arraycopy(orig, splitIndex, split[1], 0, rightLength);
        return split;
    }

//    static <T> T[] truncateArray(T[] origItems, int newLength, Class<T> tClass) {
//        if (origItems.length == newLength) {
//            return origItems;
//        }
//
//        @SuppressWarnings("unchecked")
//        T[] newItems = (T[]) ((tClass == null) ? new Object[newLength]
//                                               : Array.newInstance(tClass, newLength) );
//
//        //                      src, srcPos,    dest,destPos, length
//        System.arraycopy(origItems, 0, newItems, 0, newLength);
//        return newItems;
//    }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.mobicents.slee.container.component.deployment.jaxb.descriptors;

import java.io.InputStream;

import javax.slee.SLEEException;
import javax.slee.management.DeploymentException;

import org.mobicents.slee.container.component.du.DeployableUnitDescriptorFactory;

/**
 *
 * @author martins
 *
 */
public class DeployableUnitDescriptorFactoryImpl extends AbstractDescriptorFactory implements DeployableUnitDescriptorFactory {

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.core.component.du.DeployableUnitDescriptorFactory#parse(java.io.InputStream)
	 */
	public DeployableUnitDescriptorImpl parse(InputStream inputStream) throws DeploymentException {

		Object jaxbPojo = buildJAXBPojo(inputStream);

		if (jaxbPojo instanceof org.mobicents.slee.container.component.deployment.jaxb.slee11.du.DeployableUnit) {
			return new DeployableUnitDescriptorImpl((org.mobicents.slee.container.component.deployment.jaxb.slee11.du.DeployableUnit)jaxbPojo);
		}
		else if (jaxbPojo instanceof org.mobicents.slee.container.component.deployment.jaxb.slee.du.DeployableUnit) {
			return  new DeployableUnitDescriptorImpl((org.mobicents.slee.container.component.deployment.jaxb.slee.du.DeployableUnit)jaxbPojo);
		}
		else {
			throw new SLEEException("unexpected class of jaxb pojo built: "+(jaxbPojo != null ? jaxbPojo.getClass() : null));
		}
	}
}