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

import java.io.Serializable;
import java.util.function.IntFunction;
import java.util.function.IntUnaryOperator;

/**
 * Strictly speaking, it is not functional interface, because it requires to provide two,
 * complementary operations, where the second is inversion of the first.
 *
 * Every projection should meet conditions for every "x" and "z":
 *      invert( apply(x) ) := x
 *      apply( invert(z) ) := z
 *
 * If you want to create projection in place, by writing only lambda expressions, you can use
 * static "produce" methods, which create composite object from provided lambda expressions.
 *
 * It is specialization of ProjectionFunction for integer types.
 * It avoids uneccessary boxing and unboxing
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public interface IntProjectionFunction extends IntFunction<Integer>, IntUnaryOperator, IntCoFunction {

    /**
     * Returns identity projection
     *
     * @return functional
     */
    static IntProjectionFunction identity() {
        return produce(v->v, v->v);
    }

    @Override
    default Integer apply(int value) {
        return applyAsInt(value);
    }

    /**
     * Creates composite projection with provided implementations
     *
     * @param f function
     * @param cf inverted function
     * @return functional
     */
    static IntProjectionFunction produce(IntUnaryOperator f, IntCoFunction cf) {
        class IPF implements IntProjectionFunction, Serializable {
			
			private static final long serialVersionUID = 1L;
			
            @Override
            public int applyAsInt(int operand) {
                return f.applyAsInt(operand);
            }

            @Override
            public int invert(int value) {
                return cf.invert(value);
            }
        }
        return new IPF();
    }
    
}
