import "copyrightAndOthers.stg"
import "primitiveLiteral.stg"

targetPath() ::= "org/eclipse/collections/api"

fileName(primitive) ::= "<primitive.name>Iterable"

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

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

package org.eclipse.collections.api;

import java.util.Collection;
import java.util.NoSuchElementException;
<(wideStatisticsImport.(type))>

import org.eclipse.collections.api.bag.primitive.Mutable<name>Bag;
import org.eclipse.collections.api.block.function.primitive.<(wideName.(type))><name>To<(wideName.(type))>Function;
import org.eclipse.collections.api.block.function.primitive.<name>ToBooleanFunction;
import org.eclipse.collections.api.block.function.primitive.<name>ToByteFunction;
import org.eclipse.collections.api.block.function.primitive.<name>ToShortFunction;
import org.eclipse.collections.api.block.function.primitive.<name>ToCharFunction;
import org.eclipse.collections.api.block.function.primitive.<name>ToDoubleFunction;
import org.eclipse.collections.api.block.function.primitive.<name>ToFloatFunction;
import org.eclipse.collections.api.block.function.primitive.<name>ToIntFunction;
import org.eclipse.collections.api.block.function.primitive.<name>ToLongFunction;
import org.eclipse.collections.api.block.function.primitive.Object<name>ToObjectFunction;
import org.eclipse.collections.api.block.function.primitive.<name>ToObjectFunction;
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.collection.primitive.MutableBooleanCollection;
import org.eclipse.collections.api.collection.primitive.MutableByteCollection;
import org.eclipse.collections.api.collection.primitive.MutableCharCollection;
import org.eclipse.collections.api.collection.primitive.MutableDoubleCollection;
import org.eclipse.collections.api.collection.primitive.MutableFloatCollection;
import org.eclipse.collections.api.collection.primitive.MutableIntCollection;
import org.eclipse.collections.api.collection.primitive.MutableLongCollection;
import org.eclipse.collections.api.collection.primitive.MutableShortCollection;
import org.eclipse.collections.api.iterator.<name>Iterator;
import org.eclipse.collections.api.list.primitive.Mutable<name>List;
import org.eclipse.collections.api.set.primitive.Mutable<name>Set;

/**
 * <name>Iterable is an interface which is memory-optimized for <type> primitives.
 * It is inspired by the interface RichIterable, and contains a subset of the internal iterator methods on RichIterable like collect, sum, etc.
 * The API also includes an external iterator method, which returns an <name>Iterator. <name>Iterator helps iterate over the <name>Iterable without boxing the primitives.
 * This file was automatically generated from template file primitiveIterable.stg.
 */
public interface <name>Iterable extends PrimitiveIterable
{
    /**
     * Returns a primitive iterator that can be used to iterate over the <name>Iterable in an
     * imperative style.
     */
    <name>Iterator <type>Iterator();

    /**
     * Converts the <name>Iterable to a primitive <type> array.
     */
    <type>[] toArray();

    /**
     * Converts the <name>Iterable to a primitive <type> array. If the collection fits into the provided array it is used
     * to store its elements and is returned from the method, otherwise a new array of the appropriate size is allocated
     * and returned. If the iterable is empty, the target array is returned unchanged.
     */
    default <type>[] toArray(<type>[] target)
    {
        return this.toList().toArray(target);
    }

    /**
     * Returns true if the value is contained in the <name>Iterable, and false if it is not.
     */
    boolean contains(<type> value);

    /**
     * Returns true if the all of the values specified in the source array are contained
     * in the <name>Iterable, and false if they are not.
     */
    boolean containsAll(<type>... source);

    /**
     * Returns true if the all of the values specified in the source <name>Iterable are contained
     * in the <name>Iterable, and false if they are not.
     */
    boolean containsAll(<name>Iterable source);

    /**
     * Applies the <name>Procedure to each element in the <name>Iterable.
     */
    void forEach(<name>Procedure procedure);

    /**
     * A synonym for forEach.
     *
     * @since 7.0.
     */
    void each(<name>Procedure procedure);

    /**
     * @since 9.0.
     */
    default <name>Iterable tap(<name>Procedure procedure)
    {
        this.forEach(procedure);
        return this;
    }

    /**
     * Returns a new <name>Iterable with all of the elements in the <name>Iterable that
     * return true for the specified predicate.
     */
    <name>Iterable select(<name>Predicate predicate);

    /**
     * Returns a new <name>Iterable with all of the elements in the <name>Iterable that
     * return false for the specified predicate.
     */
    <name>Iterable reject(<name>Predicate predicate);

    /**
     * Same as {@link #select(<name>Predicate)} , only the results are added to the target Mutable<name>Collection.
     *
     * @since 8.1.
     */
    default \<R extends Mutable<name>Collection> R select(<name>Predicate predicate, R target)
    {
        this.each(each ->
        {
            if (predicate.accept(each))
            {
                target.add(each);
            }
        });
        return target;
    }

    /**
     * Same as {@link #reject(<name>Predicate)} , only the results are added to the target Mutable<name>Collection.
     *
     * @since 8.1.
     */
    default \<R extends Mutable<name>Collection> R reject(<name>Predicate predicate, R target)
    {
        this.each(each ->
        {
            if (!predicate.accept(each))
            {
                target.add(each);
            }
        });
        return target;
    }

    /**
     * Returns a new collection with the results of applying the specified function on each element of the source
     * collection.  This method is also commonly called transform or map.
     */
    \<V> RichIterable\<V> collect(<name>ToObjectFunction\<? extends V> function);

    /**
     * Same as {@link #collect(<name>ToObjectFunction)} , only the results are added to the target Collection.
     *
     * @since 8.1.
     */
    default \<V, R extends Collection\<V>\> R collect(<name>ToObjectFunction\<? extends V> function, R target)
    {
        this.each(each -> target.add(function.valueOf(each)));
        return target;
    }

    /**
     * {@code flatCollect} is a special case of {@link #collect(<name>ToObjectFunction)}. With {@code collect}, when the {@link <name>ToObjectFunction} returns
     * a collection, the result is a collection of collections. {@code flatCollect} outputs a single "flattened" collection
     * instead.  This method is commonly called flatMap.
     *
     * @since 8.1.
     */
    default \<V, R extends Collection\<V>\> R flatCollect(<name>ToObjectFunction\<? extends Iterable\<V>\> function, R target)
    {
        this.each(each ->
        {
            Iterable\<V> iterable = function.valueOf(each);
            if (iterable instanceof Collection)
            {
                target.addAll((Collection\<V>) iterable);
            }
            else
            {
                iterable.forEach(target::add);
            }
        });
        return target;
    }

    /**
     * Returns the target {@code MutableBooleanCollection} with the results of applying the specified function on each element
     * of the source collection.
     *
     * @since 8.1.
     */
    default \<R extends MutableBooleanCollection> R collectBoolean(<name>ToBooleanFunction function, R target)
    {
        this.each(each ->  target.add(function.valueOf(each)));
        return target;
    }

    /**
     * Returns the target {@code MutableByteCollection} with the results of applying the specified function on each element
     * of the source collection.
     *
     * @since 8.1.
     */
    default \<R extends MutableByteCollection> R collectByte(<name>ToByteFunction function, R target)
    {
        this.each(each ->  target.add(function.valueOf(each)));
        return target;
    }

    /**
     * Returns the target {@code MutableCharCollection} with the results of applying the specified function on each element
     * of the source collection.
     *
     * @since 8.1.
     */
    default \<R extends MutableCharCollection> R collectChar(<name>ToCharFunction function, R target)
    {
        this.each(each ->  target.add(function.valueOf(each)));
        return target;
    }

    /**
     * Returns the target {@code MutableShortCollection} with the results of applying the specified function on each element
     * of the source collection.
     *
     * @since 8.1.
     */
    default \<R extends MutableShortCollection> R collectShort(<name>ToShortFunction function, R target)
    {
        this.each(each ->  target.add(function.valueOf(each)));
        return target;
    }

    /**
     * Returns the target {@code MutableIntCollection} with the results of applying the specified function on each element
     * of the source collection.
     *
     * @since 8.1.
     */
    default \<R extends MutableIntCollection> R collectInt(<name>ToIntFunction function, R target)
    {
        this.each(each ->  target.add(function.valueOf(each)));
        return target;
    }

    /**
     * Returns the target {@code MutableFloatCollection} with the results of applying the specified function on each element
     * of the source collection.
     *
     * @since 8.1.
     */
    default \<R extends MutableFloatCollection> R collectFloat(<name>ToFloatFunction function, R target)
    {
        this.each(each ->  target.add(function.valueOf(each)));
        return target;
    }

    /**
     * Returns the target {@code MutableLongCollection} with the results of applying the specified function on each element
     * of the source collection.
     *
     * @since 8.1.
     */
    default \<R extends MutableLongCollection> R collectLong(<name>ToLongFunction function, R target)
    {
        this.each(each ->  target.add(function.valueOf(each)));
        return target;
    }

    /**
     * Returns the target {@code MutableDoubleCollection} with the results of applying the specified function on each element
     * of the source collection.
     *
     * @since 8.1.
     */
    default \<R extends MutableDoubleCollection> R collectDouble(<name>ToDoubleFunction function, R target)
    {
        this.each(each ->  target.add(function.valueOf(each)));
        return target;
    }

    <type> detectIfNone(<name>Predicate predicate, <type> ifNone);

    /**
     * Returns a count of the number of elements in the <name>Iterable that return true for the
     * specified predicate.
     */
    int count(<name>Predicate predicate);

    /**
     * Returns true if any of the elements in the <name>Iterable return true for the
     * specified predicate, otherwise returns false.
     */
    boolean anySatisfy(<name>Predicate predicate);

    /**
     * Returns true if all of the elements in the <name>Iterable return true for the
     * specified predicate, otherwise returns false.
     */
    boolean allSatisfy(<name>Predicate predicate);

    /**
     * Returns true if none of the elements in the <name>Iterable return true for the
     * specified predicate, otherwise returns false.
     */
    boolean noneSatisfy(<name>Predicate predicate);

    /**
     * Converts the <name>Iterable to a new Mutable<name>List.
     */
    Mutable<name>List toList();

    /**
     * Converts the <name>Iterable to a new Mutable<name>Set.
     */
    Mutable<name>Set toSet();

    /**
     * Converts the <name>Iterable to a new Mutable<name>Bag.
     */
    Mutable<name>Bag toBag();

    /**
     * Returns a Lazy<name>Iterable adapter wrapping the source <name>Iterable.
     */
    Lazy<name>Iterable asLazy();

    \<T> T injectInto(T injectedValue, Object<name>ToObjectFunction\<? super T, ? extends T> function);

    /**
     * @see #reduce(<(wideName.(type))><name>To<(wideName.(type))>Function)
     *
     * @since 10.0
     */
    default <(wideType.(type))> reduceIfEmpty(<(wideName.(type))><name>To<(wideName.(type))>Function accumulator, <(wideType.(type))> defaultValue)
    {
        if (this.isEmpty())
        {
            return defaultValue;
        }
        else
        {
            return this.reduce(accumulator);
        }
    }

    /**
     * @see RichIterable#reduce(BinaryOperator)
     *
     * @since 10.0
     */
    default <(wideType.(type))> reduce(<(wideName.(type))><name>To<(wideName.(type))>Function accumulator)
    {
        boolean[] seenOne = new boolean[1];
        <(wideType.(type))>[] result = new <(wideType.(type))>[1];
        this.each(each ->
        {
            if (seenOne[0])
            {
                result[0] = accumulator.valueOf(result[0], each);
            }
            else
            {
                seenOne[0] = true;
                result[0] = <castWideType.(type)>each;
            }
        });
        if (!seenOne[0])
        {
            throw new NoSuchElementException();
        }
        else
        {
            return result[0];
        }
    }

    /**
     * Partitions elements in fixed size chunks.
     *
     * @param size the number of elements per chunk
     *
     * @return A {@code RichIterable} containing {@code <name>Iterable}s of size {@code size}, except the last will be
     * truncated if the elements don't divide evenly.
     *
     * @since 9.2
     */
    default RichIterable\<<name>Iterable> chunk(int size)
    {
        throw new UnsupportedOperationException("Default method to prevent breaking backwards compatibility");
    }
    <(arithmeticMethods.(type))()>
}

>>

arithmeticMethods ::= [
    "byte": "allMethods",
    "short": "allMethods",
    "char": "allMethods",
    "int": "allMethods",
    "long": "allMethods",
    "float": "allMethods",
    "double": "allMethods",
    "boolean": "noMethods"
    ]

allMethods() ::= <<


<(wideType.(type))> sum();

/**
 * @since 8.0
 */
default <(wideStatistics.(type))> summaryStatistics()
{
    <(wideStatistics.(type))> stats = new <(wideStatistics.(type))>();
    this.forEach(stats::accept);
    return stats;
}

<type> max();

<type> maxIfEmpty(<type> defaultValue);

<type> min();

<type> minIfEmpty(<type> defaultValue);

double average();

/**
 * @since 9.0
 */
default double averageIfEmpty(double defaultValue)
{
    if (this.isEmpty())
    {
        return defaultValue;
    }
    return this.average();
}

double median();

/**
 * @since 9.0
 */
default double medianIfEmpty(double defaultValue)
{
    if (this.isEmpty())
    {
        return defaultValue;
    }
    return this.median();
}

<type>[] toSortedArray();

Mutable<name>List toSortedList();
>>

noMethods() ::= ""

wideStatistics ::= [
    "byte": "IntSummaryStatistics",
    "short": "IntSummaryStatistics",
    "char": "IntSummaryStatistics",
    "int": "IntSummaryStatistics",
    "long": "LongSummaryStatistics",
    "float": "DoubleSummaryStatistics",
    "double": "DoubleSummaryStatistics",
    default: "no matching wide type"
]

wideStatisticsImport ::= [
    "boolean": "",
    "byte": "import java.util.IntSummaryStatistics;",
    "short": "import java.util.IntSummaryStatistics;",
    "char": "import java.util.IntSummaryStatistics;",
    "int": "import java.util.IntSummaryStatistics;",
    "long": "import java.util.LongSummaryStatistics;",
    "float": "import java.util.DoubleSummaryStatistics;",
    "double": "import java.util.DoubleSummaryStatistics;",
    default: "no matching wide type"
]
