import "copyrightAndOthers.stg"
import "primitiveEquals.stg"
import "primitiveHashCode.stg"
import "primitiveLiteral.stg"

targetPath() ::= "org/eclipse/collections/impl/list/immutable/primitive"

fileName(primitive) ::= "Immutable<primitive.name>ArrayList"

class(primitive) ::= <<
<body(primitive.type, primitive.name)>
>>

body(type, name) ::= <<
<copyrightAndOthers()>

package org.eclipse.collections.impl.list.immutable.primitive;

import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;

import org.eclipse.collections.api.<name>Iterable;
import org.eclipse.collections.api.Lazy<name>Iterable;
import org.eclipse.collections.api.RichIterable;
import org.eclipse.collections.api.bag.primitive.Mutable<name>Bag;
import org.eclipse.collections.api.block.function.primitive.<name>ToObjectFunction;
import org.eclipse.collections.api.block.function.primitive.Object<name>ToObjectFunction;
import org.eclipse.collections.api.block.function.primitive.Object<name>IntToObjectFunction;
import org.eclipse.collections.api.block.predicate.primitive.<name>Predicate;
import org.eclipse.collections.api.block.procedure.primitive.<name>Procedure;
import org.eclipse.collections.api.block.procedure.primitive.<name>IntProcedure;
import org.eclipse.collections.api.collection.primitive.Mutable<name>Collection;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.iterator.<name>Iterator;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.list.MutableList;
import org.eclipse.collections.api.list.primitive.Immutable<name>List;
import org.eclipse.collections.api.list.primitive.<name>List;
import org.eclipse.collections.api.list.primitive.Mutable<name>List;
import org.eclipse.collections.api.set.primitive.Mutable<name>Set;
import org.eclipse.collections.api.tuple.primitive.<name><name>Pair;
import org.eclipse.collections.api.tuple.primitive.<name>ObjectPair;
import org.eclipse.collections.impl.bag.mutable.primitive.<name>HashBag;
import org.eclipse.collections.impl.factory.primitive.<name>Lists;
import org.eclipse.collections.impl.lazy.primitive.Lazy<name>IterableAdapter;
import org.eclipse.collections.impl.lazy.primitive.Reverse<name>Iterable;
import org.eclipse.collections.impl.list.mutable.primitive.<name>ArrayList;
import org.eclipse.collections.impl.set.mutable.primitive.<name>HashSet;
import org.eclipse.collections.impl.tuple.primitive.PrimitiveTuples;
import org.eclipse.collections.impl.utility.Iterate;
<if(primitive.specializedStream)>
import java.util.Spliterator;
import java.util.Spliterators;<endif>


/**
 * Immutable<name>ArrayList is the non-modifiable equivalent of {@link <name>ArrayList}.
 * It wraps a Java <type> array.
 * This file was automatically generated from template file immutablePrimitiveArrayList.stg.
 *
 * @since 3.2.
 */
final class Immutable<name>ArrayList
        implements Immutable<name>List, Serializable
{
    private static final long serialVersionUID = 1L;
    private final <type>[] items;

    private Immutable<name>ArrayList(<type>[] newElements)
    {
        if (newElements.length \<= 1)
        {
            throw new IllegalArgumentException("Use <name>Lists.immutable.with() to instantiate an optimized collection");
        }
        this.items = newElements;
    }

    public static Immutable<name>ArrayList newList(<name>Iterable iterable)
    {
        return new Immutable<name>ArrayList(iterable.toArray());
    }

    public static Immutable<name>ArrayList newListWith(<type>... elements)
    {
        <type>[] newArray = new <type>[elements.length];
        System.arraycopy(elements, 0, newArray, 0, elements.length);
        return new Immutable<name>ArrayList(newArray);
    }

    @Override
    public <type> get(int index)
    {
        return this.items[index];
    }

    @Override
    public <type> getFirst()
    {
        return this.items[0];
    }

    @Override
    public <type> getLast()
    {
        return this.items[this.items.length - 1];
    }

    @Override
    public int indexOf(<type> value)
    {
        for (int i = 0; i \< this.items.length; i++)
        {
            if (<(equals.(type))("this.items[i]", "value")>)
            {
                return i;
            }
        }
        return -1;
    }

    @Override
    public int lastIndexOf(<type> value)
    {
        for (int i = this.items.length - 1; i >= 0; i--)
        {
            if (<(equals.(type))("this.items[i]", "value")>)
            {
                return i;
            }
        }
        return -1;
    }

    @Override
    public <name>Iterator <type>Iterator()
    {
        return new Internal<name>Iterator();
    }

    /**
     * @since 7.0.
     */
    @Override
    public void each(<name>Procedure procedure)
    {
        for (<type> item : this.items)
        {
            procedure.value(item);
        }
    }

    @Override
    public void forEachWithIndex(<name>IntProcedure procedure)
    {
        for (int i = 0; i \< this.items.length; i++)
        {
            procedure.value(this.items[i], i);
        }
    }

    @Override
    public int count(<name>Predicate predicate)
    {
        int count = 0;
        for (<type> item : this.items)
        {
            if (predicate.accept(item))
            {
                count++;
            }
        }
        return count;
    }

    @Override
    public boolean anySatisfy(<name>Predicate predicate)
    {
        for (<type> item : this.items)
        {
            if (predicate.accept(item))
            {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean allSatisfy(<name>Predicate predicate)
    {
        for (<type> item : this.items)
        {
            if (!predicate.accept(item))
            {
                return false;
            }
        }
        return true;
    }

    @Override
    public Immutable<name>List select(<name>Predicate predicate)
    {
        return this.select(predicate, new <name>ArrayList()).toImmutable();
    }

    /**
     * @since 8.1.
     */
    @Override
    public \<R extends Mutable<name>Collection> R select(<name>Predicate predicate, R target)
    {
        for (<type> item : this.items)
        {
            if (predicate.accept(item))
            {
                target.add(item);
            }
        }
        return target;
    }

    @Override
    public Immutable<name>List reject(<name>Predicate predicate)
    {
        return this.reject(predicate, new <name>ArrayList()).toImmutable();
    }

    /**
     * @since 8.1.
     */
    @Override
    public \<R extends Mutable<name>Collection> R reject(<name>Predicate predicate, R target)
    {
        for (<type> item : this.items)
        {
            if (!predicate.accept(item))
            {
                target.add(item);
            }
        }
        return target;
    }

    @Override
    public <type> detectIfNone(<name>Predicate predicate, <type> ifNone)
    {
        for (<type> item : this.items)
        {
            if (predicate.accept(item))
            {
                return item;
            }
        }
        return ifNone;
    }

    @Override
    public \<V> ImmutableList\<V> collect(<name>ToObjectFunction\<? extends V> function)
    {
        MutableList\<V> list = this.collect(function, Lists.mutable.withInitialCapacity(this.items.length));
        return list.toImmutable();
    }

    /**
     * @since 8.1.
     */
    @Override
    public \<V, R extends Collection\<V>\> R collect(<name>ToObjectFunction\<? extends V> function, R target)
    {
        for (<type> item : this.items)
        {
            target.add(function.valueOf(item));
        }
        return target;
    }

    @Override
    <if(primitive.floatingPoint)>public <wideType.(type)> sum()
{
    <wideType.(type)> result = <wideZero.(type)>;
    <wideType.(type)> compensation = <wideZero.(type)>;
    for (<type> item : this.items)
    {
        <wideType.(type)> adjustedValue = item - compensation;
        <wideType.(type)> nextSum = result + adjustedValue;
        compensation = nextSum - result - adjustedValue;
        result = nextSum;
    }
    return result;
}

    <else>public <wideType.(type)> sum()
{
    <wideType.(type)> result = <wideZero.(type)>;
    for (<type> item : this.items)
    {
        result += item;
    }
    return result;
}

    <endif>
    @Override
    public <type> max()
    {
        <type> max = this.items[0];
        for (int i = 1; i \< this.items.length; i++)
        {
            <type> value = this.items[i];
            if (<(lessThan.(type))("max", "value")>)
            {
                max = value;
            }
        }
        return max;
    }

    @Override
    public <type> maxIfEmpty(<type> defaultValue)
    {
        return this.max();
    }

    @Override
    public <type> min()
    {
        <type> min = this.items[0];
        for (int i = 1; i \< this.items.length; i++)
        {
            <type> value = this.items[i];
            if (<(lessThan.(type))("value", "min")>)
            {
                min = value;
            }
        }
        return min;
    }

    @Override
    public <type> minIfEmpty(<type> defaultValue)
    {
        return this.min();
    }

    @Override
    public double average()
    {
        return <castSum.(type)>this.sum() / (double) this.size();
    }

    @Override
    public double median()
    {
        <type>[] sortedArray = this.toSortedArray();
        int middleIndex = sortedArray.length >\> 1;
        if (sortedArray.length > 1 && (sortedArray.length & 1) == 0)
        {
            <type> first = sortedArray[middleIndex];
            <type> second = sortedArray[middleIndex - 1];
            return (<castDouble.(type)>first + <castDouble.(type)>second) / 2.0;
        }
        return <castDouble.(type)>sortedArray[middleIndex];
    }

    @Override
    public <type>[] toSortedArray()
    {
        <type>[] array = this.toArray();
        Arrays.sort(array);
        return array;
    }

    @Override
    public <wideType.(type)> dotProduct(<name>List list)
    {
        if (this.size() != list.size())
        {
            throw new IllegalArgumentException("Lists used in dotProduct must be the same size");
        }
        <wideType.(type)> sum = <wideZero.(type)>;
        for (int i = 0; i \< this.size(); i++)
        {
            sum += <castWideType.(type)>this.items[i] * list.get(i);
        }
        return sum;
    }

    @Override
    public Lazy<name>Iterable asReversed()
    {
        return Reverse<name>Iterable.adapt(this);
    }

    @Override
    public Mutable<name>List toSortedList()
    {
        return <name>ArrayList.newList(this).sortThis();
    }

    @Override
    public int binarySearch(<type> value)
    {
        return Arrays.binarySearch(this.items, value);
    }

    @Override
    public <type>[] toArray()
    {
        <type>[] newItems = new <type>[this.items.length];
        System.arraycopy(this.items, 0, newItems, 0, this.items.length);
        return newItems;
    }

    @Override
    public <type>[] toArray(<type>[] target)
    {
        if (target.length \< this.items.length)
        {
            target = new <type>[this.items.length];
        }
        System.arraycopy(this.items, 0, target, 0, this.items.length);
        return target;
    }

    @Override
    public boolean contains(<type> value)
    {
        for (<type> item : this.items)
        {
            if (<(equals.(type))("item", "value")>)
            {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean containsAll(<type>... source)
    {
        for (<type> value : source)
        {
            if (!this.contains(value))
            {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean containsAll(<name>Iterable source)
    {
        for (<name>Iterator iterator = source.<type>Iterator(); iterator.hasNext(); )
        {
            if (!this.contains(iterator.next()))
            {
                return false;
            }
        }
        return true;
    }

    @Override
    public Mutable<name>List toList()
    {
        return <name>ArrayList.newList(this);
    }

    @Override
    public Mutable<name>Set toSet()
    {
        return <name>HashSet.newSet(this);
    }

    @Override
    public Mutable<name>Bag toBag()
    {
        return <name>HashBag.newBag(this);
    }

    @Override
    public Lazy<name>Iterable asLazy()
    {
        return new Lazy<name>IterableAdapter(this);
    }

    @Override
    public Immutable<name>List toImmutable()
    {
        return this;
    }

    @Override
    public Immutable<name>ArrayList toReversed()
    {
        return Immutable<name>ArrayList.newList(this.asReversed());
    }

    @Override
    public Immutable<name>List newWith(<type> element)
    {
        <type>[] newItems = new <type>[this.items.length + 1];
        System.arraycopy(this.items, 0, newItems, 0, this.items.length);
        newItems[this.items.length] = element;
        return new Immutable<name>ArrayList(newItems);
    }

    @Override
    public Immutable<name>List newWithout(<type> element)
    {
        int index = this.indexOf(element);
        if (index != -1)
        {
            <type>[] newItems = new <type>[this.items.length - 1];
            System.arraycopy(this.items, 0, newItems, 0, index);
            System.arraycopy(this.items, index + 1, newItems, index, this.items.length - index - 1);
            return <name>Lists.immutable.with(newItems);
        }
        return this;
    }

    @Override
    public Immutable<name>List newWithAll(<name>Iterable elements)
    {
        <type>[] newItems = new <type>[this.items.length + elements.size()];
        System.arraycopy(this.items, 0, newItems, 0, this.items.length);
        int index = 0;
        for (<name>Iterator iterator = elements.<type>Iterator(); iterator.hasNext(); index++)
        {
            newItems[this.items.length + index] = iterator.next();
        }
        return new Immutable<name>ArrayList(newItems);
    }

    @Override
    public Immutable<name>List newWithoutAll(<name>Iterable elements)
    {
        Mutable<name>List mutable<name>List = this.toList();
        mutable<name>List.removeAll(elements);
        return mutable<name>List.toImmutable();
    }

    @Override
    public int size()
    {
        return this.items.length;
    }

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

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

    @Override
    public \<T> T injectInto(T injectedValue, Object<name>ToObjectFunction\<? super T, ? extends T> function)
    {
        T result = injectedValue;
        for (int i = 0; i \< this.items.length; i++)
        {
            result = function.valueOf(result, this.items[i]);
        }
        return result;
    }

    @Override
    public \<T> T injectIntoWithIndex(T injectedValue, Object<name>IntToObjectFunction\<? super T, ? extends T> function)
    {
        T result = injectedValue;
        for (int i = 0; i \< this.items.length; i++)
        {
            result = function.valueOf(result, this.items[i], i);
        }
        return result;
    }

    @Override
    public RichIterable\<<name>Iterable> chunk(int size)
    {
        if (size \<= 0)
        {
            throw new IllegalArgumentException("Size for groups must be positive but was: " + size);
        }
        MutableList\<<name>Iterable> result = Lists.mutable.empty();
        if (this.notEmpty())
        {
            if (this.size() \<= size)
            {
                result.add(this);
            }
            else
            {
                <name>Iterator iterator = this.<type>Iterator();
                while (iterator.hasNext())
                {
                    Mutable<name>List batch = <name>Lists.mutable.empty();
                    for (int i = 0; i \< size && iterator.hasNext(); i++)
                    {
                        batch.add(iterator.next());
                    }
                    result.add(batch.toImmutable());
                }
            }
        }
        return result.toImmutable();
    }

    @Override
    public boolean equals(Object otherList)
    {
        if (otherList == this)
        {
            return true;
        }
        if (!(otherList instanceof <name>List))
        {
            return false;
        }
        <name>List list = (<name>List) otherList;
        if (this.items.length != list.size())
        {
            return false;
        }
        for (int i = 0; i \< this.items.length; i++)
        {
            if (<(notEquals.(type))("this.items[i]", "list.get(i)")>)
            {
                return false;
            }
        }
        return true;
    }

    @Override
    public int hashCode()
    {
        int hashCode = 1;
        for (<type> item : this.items)
        {
            hashCode = 31 * hashCode + <(hashCode.(type))("item")>;
        }
        return hashCode;
    }

    @Override
    public String toString()
    {
        return this.makeString("[", ", ", "]");
    }

    @Override
    public String makeString()
    {
        return this.makeString(", ");
    }

    @Override
    public String makeString(String separator)
    {
        return this.makeString("", separator, "");
    }

    @Override
    public String makeString(String start, String separator, String end)
    {
        Appendable stringBuilder = new StringBuilder();
        this.appendString(stringBuilder, start, separator, end);
        return stringBuilder.toString();
    }

    @Override
    public void appendString(Appendable appendable)
    {
        this.appendString(appendable, ", ");
    }

    @Override
    public void appendString(Appendable appendable, String separator)
    {
        this.appendString(appendable, "", separator, "");
    }

    @Override
    public void appendString(Appendable appendable, String start, String separator, String end)
    {
        try
        {
            appendable.append(start);
            for (int i = 0; i \< this.items.length; i++)
            {
                if (i > 0)
                {
                    appendable.append(separator);
                }
                <type> value = this.items[i];
                appendable.append(String.valueOf(value));
            }
            appendable.append(end);
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }
    }

    /**
     * @since 6.0.
     */
    @Override
    public Immutable<name>List distinct()
    {
        <name>ArrayList target = new <name>ArrayList();
        Mutable<name>Set seenSoFar = new <name>HashSet(this.size());

        for (<type> each : this.items)
        {
            if (seenSoFar.add(each))
            {
                target.add(each);
            }
        }
        return target.toImmutable();
    }

    @Override
    public Immutable<name>List subList(int fromIndex, int toIndex)
    {
        throw new UnsupportedOperationException("subList not yet implemented!");
    }

    /**
     * @since 9.1.
     */
    @Override
    public ImmutableList\<<name><name>Pair> zip<name>(<name>Iterable iterable)
    {
        int size = this.size();
        int otherSize = iterable.size();
        MutableList\<<name><name>Pair> target = Lists.mutable.withInitialCapacity(Math.min(size, otherSize));
        <name>Iterator iterator = iterable.<type>Iterator();
        for (int i = 0; i \< size && iterator.hasNext(); i++)
        {
            target.add(PrimitiveTuples.pair(this.items[i], iterator.next()));
        }
        return target.toImmutable();
    }

    /**
     * @since 9.1.
     */
    @Override
    public \<T> ImmutableList\<<name>ObjectPair\<T\>> zip(Iterable\<T> iterable)
    {
        int size = this.size();
        int otherSize = Iterate.sizeOf(iterable);
        MutableList\<<name>ObjectPair\<T\>> target = Lists.mutable.withInitialCapacity(Math.min(size, otherSize));
        Iterator\<T> iterator = iterable.iterator();
        for (int i = 0; i \< size && iterator.hasNext(); i++)
        {
            target.add(PrimitiveTuples.pair(this.items[i], iterator.next()));
        }
        return target.toImmutable();
    }

    private class Internal<name>Iterator implements <name>Iterator
    {
        /**
         * Index of element to be returned by subsequent call to next.
         */
        private int currentIndex;

        @Override
        public boolean hasNext()
        {
            return this.currentIndex != Immutable<name>ArrayList.this.items.length;
        }

        @Override
        public <type> next()
        {
            if (!this.hasNext())
            {
                throw new NoSuchElementException();
            }
            <type> next = Immutable<name>ArrayList.this.items[this.currentIndex];
            this.currentIndex++;
            return next;
        }
    }
    <if(primitive.specializedStream)>

    @Override
    public Spliterator.Of<name> spliterator()
    {
        int characteristics = Spliterator.ORDERED | Spliterator.IMMUTABLE;
        return Spliterators.spliterator(this.items, characteristics);
    }
    <endif>
}

>>
