package com.googlecode.totallylazy;
import com.googlecode.totallylazy.functions.*;
import java.util.Map;
import java.util.concurrent.Callable;
import static com.googlecode.totallylazy.Callers.call;
import static com.googlecode.totallylazy.functions.Functions.returns;
import static com.googlecode.totallylazy.Sequences.sequence;
public class Pair<F, S> implements First<F>, Second<S>, Value<F>, Functor<F>, Map.Entry<F,S> {
private final Lazy<F> first;
private final Lazy<S> second;
protected Pair(final Callable<? extends F> first, final Callable<? extends S> second) {
this.first = Lazy.lazy(first);
this.second = Lazy.lazy(second);
}
public static <F, S> Pair<F, S> pair(final F first, final S second) {
return new Pair<F, S>(returns(first), returns(second));
}
public static <F, S> Pair<F, S> pair(final Callable<? extends F> first, final Callable<? extends S> second) {
return new Pair<F, S>(first, second);
}
public static <F, S> Curried2<F, S, Pair<F, S>> pair() {
return Pair::pair;
}
public final F first() {
return first.value();
}
public final S second() {
return second.value();
}
@Override
public F value() {
return first();
}
@Override
public String toString() {
return toString("[", ",", "]");
}
public String toString(String separator) {
return toString("", separator, "");
}
public String toString(String start, String separator, String end) {
return values().toString(start, separator, end);
}
public Sequence<Object> values() {
return sequence(first(), second());
}
@Override
public F getKey() {
return first();
}
@Override
public S getValue() {
return second();
}
@Override
public S setValue(S value) {
throw new UnsupportedOperationException();
}
@Override
public boolean equals(final Object o) {
return o instanceof Pair && values().equals(((Pair) o).values());
}
@Override
public int hashCode() {
return values().hashCode();
}
public static <A, B, C> Curried2<Pair<A, B>, C, Pair<B, C>> leftShift2() {
return Pair::leftShift;
}
public static <A, B, C> Pair<B, C> leftShift(Pair<? extends A, ? extends B> pair, C c) {
return Pair.pair(pair.second(), c);
}
public static <A, B, C> Pair<B, C> reduceLeftShift(final Pair<? extends A, ? extends B> pair, final Function2<? super A, ? super B, ? extends C> callable) {
return Pair.leftShift(pair, call(callable, pair.first(), pair.second()));
}
public static <A, B, C> Curried2<Pair<A, B>, Function2<A, B, C>, Pair<B, C>> reduceLeftShift() {
return Pair::reduceLeftShift;
}
public static <A, B, C> Function1<Pair<A, B>, Pair<B, C>> reduceLeftShift(Function2<A, B, C> callable) {
return Pair.<A, B, C>reduceLeftShift().flip().apply(callable);
}
@Override
@SuppressWarnings("unchecked")
public <NewF> Pair<NewF, S> map(final Function1<? super F, ? extends NewF> callable) {
return pair(first.map(callable), second);
}
public <R> Pair<R, S> first(Function1<? super F, ? extends R> map) {
return pair(first.then(map), second);
}
public <R> Pair<F,R> second(Function1<? super S, ? extends R> map) {
return pair(first, second.then(map));
}
public static class functions {
public static <T> Function1<First<T>, T> first() {
return Callables.first();
}
public static <T> Function1<First<T>, T> first(Class<T> aClass) {
return first();
}
public static <T> Function1<Second<T>, T> second() {
return Callables.second();
}
public static <T> Function1<Second<T>, T> second(Class<T> aClass) {
return second();
}
public static <F1, F2, S> Function1<Pair<F1, S>, Pair<F2, S>> replaceFirst(final F2 newFirst) {
return pair -> pair(newFirst, pair.second());
}
public static <F1, F2, S> Function1<Pair<F1, S>, Pair<F2, S>> replaceFirst(final F2 newFirst, final Class<S> aClass) {
return replaceFirst(newFirst);
}
public static <F1, F2, S> Function1<Pair<F1, S>, Pair<F2, S>> replaceFirst(final Function1<F1,F2> map) {
return pair -> pair(map.call(pair.first()), pair.second());
}
public static <F1, F2, S> Function1<Pair<F1, S>, Pair<F2, S>> replaceFirst(final Function1<F1,F2> map, Class<S> aClass) {
return replaceFirst(map);
}
public static <F, S1, S2> Function1<Pair<F, S1>, Pair<F, S2>> replaceSecond(final S2 newSecond) {
return pair -> pair(pair.first(), newSecond);
}
public static <F, S1, S2> Function1<Pair<F, S1>, Pair<F, S2>> replaceSecond(final Class<F> aClass, final S2 map) {
return replaceSecond(map);
}
public static <F, S1, S2> Function1<Pair<F, S1>, Pair<F, S2>> replaceSecond(final Function1<S1,S2> map) {
return pair -> pair(pair.first(), map.call(pair.second()));
}
public static <F, S1, S2> Function1<Pair<F, S1>, Pair<F, S2>> replaceSecond(Class<F> aClass, final Function1<S1,S2> newSecond) {
return replaceSecond(newSecond);
}
public static <F, S> Function1<S, Pair<F, S>> toPairWithFirst(final F first) {
return second1 -> pair(first, second1);
}
public static <F, S> Function1<F, Pair<F, S>> toPairWithSecond(final S second) {
return first1 -> pair(first1, second);
}
public static Function1<Pair<?,?>, Sequence<Object>> values() {
return Pair::values;
}
public static Function1<Pair<?,?>, String> toString(final String separator) {
return pair -> pair.toString(separator);
}
public static Function1<Pair<?,?>, String> toString(final String start, final String separator, final String end) {
return pair -> pair.toString(start, separator, end);
}
public static Function1<Pair<?,?>, String> pairToString(final String separator) {
return pair -> pair.toString(separator);
}
public static Function1<Pair<?,?>, String> pairToString(final String start, final String separator, final String end) {
return pair -> pair.toString(start, separator, end);
}
}
}