/*
 * @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.util.function.Function;

/**
 * This functional interface represents operation inverted to {@link Function}.
 * Strictly speaking, inverted operation does not require to be separate interface,
 * but we want to declare method other than "apply" and be able to implement both methods in ProjectionFunction
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @param <R> R
 * @param <T> T
 */
public interface CoFunction<T,R> {

    /**
     * Performs operation on given argument.
     *
     * @param value value
     * @return value
     */
    T invert(R value);

    /**
     * Returns identity function
     *
     * @param <T> T
     * @return input
     */
    static <T> CoFunction<T,T> identity() {
        return v -> v;
    }

    /**
     * Converts specified function into CoFunction interface.
     * Obviously, it does not change operation, it just changes interface.
     *
     * @param f f
     * @param <T> T
     * @param <R> R
     * @return functional
     */
    static <T,R> CoFunction<T,R> inverted(Function<R,T> f) {
        return v -> f.apply(v);
    }

    /**
     * Converts specified inverted function into Function.
     * Obviously, it does not change operation, it just changes interface.
     *
     * @param f f
     * @param <T> T
     * @param <R> R
     * @return functional
     */
    static <T,R> Function<R,T> inverted(CoFunction<T,R> f) {
        return v -> f.invert(v);
    }
    
}
