package water.util.fp;
import static water.util.Java7.*;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
/**
* Operations on functions
*/
public class Functions {
static class Composition<X,Y,Z> implements Function<X,Z> {
private final Function<X, Y> f;
private final Function<Y, Z> g;
Composition(final Function<X,Y> f, final Function<Y, Z> g) {
this.f = f;
this.g = g;
}
@Override
public int hashCode() {
return f.hashCode() * 211 + g.hashCode() * 79;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Composition)) return false;
Composition other = (Composition) obj;
return Objects.equals(f, other.f) && Objects.equals(g, other.g);
}
@Override public Z apply(X x) { return g.apply(f.apply(x)); }
}
public static <X,Y,Z> Function<X, Z> compose(final Function<Y, Z> g, final Function<X,Y> f) {
return new Composition<>(f, g);
}
public static <X> Function<X, X> identity() {
return new Function<X, X>() {
@Override public X apply(X x) { return x; }
};
}
public static <T> Function<Long, T> onList(final List<T> list) {
return new Function<Long, T>() {
public T apply(Long i) { return list.get(i.intValue()); }
};
}
public static <X, Y> Iterable<Y> map(Iterable<X> xs, Function<X, Y> f) {
List<Y> ys = new LinkedList<>();
for (X x : xs) ys.add(f.apply(x));
return ys;
}
public static <X,Y> Function<X,Y> constant(final Y y) {
return new Function<X, Y>() {
public Y apply(X x) { return y; }
};
}
static class StringSplitter implements Unfoldable<String, String> {
private final String separator;
StringSplitter(String separator) {
this.separator = separator;
}
@Override public List<String> apply(String s) {
return Arrays.asList(s.split(separator));
}
@Override
public int hashCode() {
return 211 + separator.hashCode() * 7;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof StringSplitter)) return false;
StringSplitter other = (StringSplitter) obj;
return Objects.equals(separator, other.separator);
}
}
public static Unfoldable<String, String> splitBy(final String separator) {
return new StringSplitter(separator);
}
/**
* Integrates "area under curve" (assuming it exists),
* that is, for a parametric curve specified by functions x and y,
* defined on integer domain [from, to], calculate the area
* between x[from], x[to], horizontal axis, and the curve.
* @param x x-component of the curve
* @param y y-component of the curve
* @param from min value of the curve range
* @param to max value of the curve range
* @return the area under curve, the result of integrating x*y' over [from,to].
*/
public static double integrate(
Function<Integer, Double> x,
Function<Integer, Double> y,
int from, int to) {
double s = 0;
double x0 = x.apply(from);
double y0 = y.apply(from);
for (int i = from + 1; i <= to; i++) {
double x1 = x.apply(i);
double y1 = y.apply(i);
s += (y1+y0)*(x1-x0)*.5;
x0 = x1; y0 = y1;
}
return s;
}
}