/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.functional.special;

import java.util.function.IntBinaryOperator;
import net.ranides.assira.collection.iterators.IntIterator;

/**
 * Specialization of IntBinaryOperator.
 * Fold operation is supposed to be called by reduce operations.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
@FunctionalInterface
public interface IntFold extends IntBinaryOperator, Fold<Integer> {

    /**
     * non-generic version of method {@link #apply(Integer,Integer)}
     * @param a a
     * @param b b
     * @return int
     */
    int apply(int a, int b);
    
    @Override
    default Integer apply(Integer a, Integer b) {
        return apply(a.intValue(), b.intValue());
    }
    
    @Override
    default int applyAsInt(int a, int b) {
        return apply(a, b);
    }

    /**
     * It folds all values from iterator
     *
     * @param iterator iterator
     * @return value
     */
    default int apply(IntIterator iterator) {
        return apply(iterator, this);
    }

    /**
     * It folds all values from array
     *
     * @param array array
     * @return value
     */
    default int apply(int[] array) {
        return apply(array, this);
    }

    /**
     * It folds all values from iterator
     *
     * @param iterator iterator
     * @param operator operator
     * @return value
     */
    static int apply(IntIterator iterator, IntBinaryOperator operator) {
        int ret = iterator.nextInt();
        while(iterator.hasNext()) {
            ret = operator.applyAsInt(ret, iterator.nextInt());
        }
        return ret;
    }

    /**
     * It folds all values from array
     *
     * @param array array
     * @param operator operator
     * @return value
     */
    static int apply(int[] array, IntBinaryOperator operator) {
        int ret = array[0];
        for(int i=1; i<array.length; i++) {
            ret = operator.applyAsInt(ret, array[i]);
        }
        return ret;
    }
}
