package xapi.fu;
import java.util.Map.Entry;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
* @author James X. Nelson (james@wetheinter.net)
* Created on 07/11/15.
*/
@SuppressWarnings("unchecked")
public interface In2<I1, I2> extends HasInput, Rethrowable, Lambda {
void in(I1 in1, I2 in2);
@Override
default int accept(int position, Object... values) {
in((I1)values[position++], (I2)values[position++]);
return position;
}
default BiConsumer<I1, I2> toBiConsumer() {
return this::in;
}
default <N> In3<N, I1, I2> requireBefore(In1<N> and){
return In3.in3(and, this);
}
default <N> In3<I1, I2, N> requireAfter(In1<N> and){
return In3.in3(this, and);
}
default In2<I2, I1> reverse(){
return (i2, i1) -> in(i1, i2);
}
default In1<I1> provide2(Out1<I2> and){
return i1 -> in(i1, and.out1());
}
default In1<I2> adapt1(In1Out1<I2, I1> adapt) {
return i2 -> in(adapt.io(i2), i2);
}
default In1<I1> adapt2(In1Out1<I1, I2> adapt) {
return i1 -> in(i1, adapt.io(i1));
}
default <In extends I1> In1<I2> provide1(In and){
return i2 -> in(and, i2);
}
default In1<I2> provide1Deferred(Out1<I1> and){
return i2 -> {
final I1 i1 = and.out1();
in(i1, i2);
};
}
default In1<I2> provide1Immediate(Out1<I1> and){
final I1 i1 = and.out1();
return i2 -> in(i1, i2);
}
default In1<I1> provide2(I2 and){
return i1 -> in(i1, and);
}
default In1<I1> provide2Deferred(Out1<I2> and){
return i1 -> {
final I2 i2 = and.out1();
in(i1, i2);
};
}
default In1<I1> provide2Immediate(Out1<I2> and){
final I2 i2 = and.out1();
return i1 -> in(i1, i2);
}
static <I1, I2> In2<I1, I2> in2(In2<I1, I2> of) {
return of;
}
static <I1, I2> In2<I1, I2> fromBiconsumer(BiConsumer<I1, I2> of) {
return of::accept;
}
static <I1, I2> In2<I1, I2> in2(In1<I1> in1, In1<I2> in2) {
return (i1, i2)->{
in1.in(i1);
in2.in(i2);
};
}
/**
* This method just exists to give you somewhere to create a lambda that will rethrow exceptions,
* but exposes an exceptionless api. If you don't have to call code with checked exceptions,
* prefer the standard {@link #fromBiconsumer(BiConsumer)}, as try/catch can disable / weaken some JIT compilers.
*/
static <I1, I2> In2<I1, I2> in2Unsafe(In2Unsafe<I1, I2> of) {
return of;
}
default <W> In1 <W> adapt(In1Out1<W, I1> getter1, In1Out1<W, I2> getter2) {
return w->in(getter1.io(w), getter2.io(w));
}
default <To> In2<To, I2> map1 (In1Out1<To, I1> mapper) {
return (i1, i2) -> in(mapper.io(i1), i2);
}
default <To> In2<I1, To> map2 (In1Out1<To, I2> mapper) {
return (i1, i2) -> in(i1, mapper.io(i2));
}
default Consumer<Entry<I1, I2>> mapAdapter() {
// You can't overload the same type to have different generics in a single expression,
// so we can't actually have two different In1Out generics in an inline expression
In1Out1<Entry<I1, I2>, I1> key = Entry::getKey;
In1Out1<Entry<I1, I2>, I2> value = Entry::getValue;
return adapt(key, value).toConsumer();
}
interface In2Unsafe <I1, I2> extends In2<I1, I2> {
void inUnsafe(I1 in1, I2 in2) throws Throwable;
default void in(I1 in1, I2 in2) {
try {
inUnsafe(in1, in2);
} catch (Throwable e) {
throw rethrow(e);
}
}
}
static <I1, I2> In2<I1, I2> ignoreSecond(In1<I1> callback) {
return (i1, i2) -> callback.in(i1);
}
static <I1, I2> In2<I1, I2> ignoreFirst(In1<I2> callback) {
return (i1, i2) -> callback.in(i2);
}
static <I1, I2> In2<I2, I1> reversed(In2<I1, I2> from) {
return (i1, i2) -> from.in(i2, i1);
}
default <O1> In2Out1<I1, I2, O1> supply1(O1 value) {
return (i1, i2) -> {
in(i1, i2);
return value;
};
}
default <O1> In2Out1<I1, I2, O1> supply1Immediate(Out1<O1> factory) {
final O1 value = factory.out1();
return (i1, i2) -> {
in(i1, i2);
return value;
};
}
default <O1> In2Out1<I1, I2, O1> supply1BeforeRead(Out1<O1> factory) {
return (i1, i2) -> {
final O1 value = factory.out1();
in(i1, i2);
return value;
};
}
default <O1> In2Out1<I1, I2, O1> supply1AfterRead(Out1<O1> factory) {
return (i1, i2) -> {
in(i1, i2);
final O1 value = factory.out1();
return value;
};
}
static <I1, I2> Do reduceAll(In2<I1, I2> lambda, I1 i1, I2 i2) {
return lambda.provide1(i1).provide(i2);
}
static <I1, I2> In1<I2> reduce1(In2<I1, I2> lambda, I1 i1) {
return lambda.provide1(i1);
}
static <I1, I2> In1<I1> reduce2(In2<I1, I2> lambda, I2 i2) {
return lambda.provide2(i2);
}
}