package water.udf;
import com.google.common.collect.Lists;
import water.fvec.Chunk;
import water.fvec.RawChunk;
import water.fvec.Vec;
import water.util.fp.Foldable;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import static water.util.Java7.*;
/**
* This column depends a plurality of columns
*/
public class FoldingColumn<X, Y> extends FunColumnBase<Y> {
private final Foldable<X, Y> f;
private final Column<X>[] columns;
@Override public int rowLayout() {
return columns.length > 0 ? columns[0].rowLayout() : 0;
}
/**
* deserialization :(
*/
public FoldingColumn() {
f = null; columns = null;
}
public FoldingColumn(Foldable<X, Y> f, Column<X>... columns) {
super(columns.length == 0 ? null : columns[0]);
assert columns.length > 0 : "Require at least one column for folding";
this.f = f;
this.columns = columns;
if (columns.length > 1) {
Column<X> c0 = columns[0];
for (int i = 1; i < columns.length; i++) {
Column<X> c = columns[i];
assert c0.isCompatibleWith(c) : "Columns must be compatible; " + c0 + " vs #" + i + ": " + c;
}
}
}
@SuppressWarnings("unchecked")
public FoldingColumn(Foldable<X, Y> f, Iterable<Column<X>> columns) {
super(columns.iterator().next());
this.f = f;
this.columns = (Column<X>[])Lists.newArrayList(columns).toArray();
}
@Override public Y get(long idx) {
Y y = f.initial();
for (Column<X> col : columns) y = f.apply(y, col.apply(idx));
return y;
}
@Override
public TypedChunk<Y> chunkAt(int i) {
List<TypedChunk<X>> chunks = new LinkedList<>();
for (Column<X> c : columns) chunks.add(c.chunkAt(i));
return new FunChunk(chunks);
}
@Override public boolean isNA(long idx) {
for (Column<X> col : columns) if (col.isNA(idx)) return true;
return false;
}
private class FunChunk extends DependentChunk<Y> {
private final List<TypedChunk<X>> chunks;
public FunChunk(List<TypedChunk<X>> chunks) {
super(chunks.get(0));
this.chunks = chunks;
}
private RawChunk myChunk = new RawChunk(this);
@Override public Vec vec() { return FoldingColumn.this.vec(); }
@Override public Chunk rawChunk() { return myChunk; }
@Override public boolean isNA(int i) {
for (TypedChunk<X> c : chunks) if (c.isNA(i)) return true;
return false;
}
@Override
public Y get(int idx) {
Y y = f.initial();
for (TypedChunk<X> c : chunks) y = f.apply(y, c.get(idx));
return y;
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof FoldingColumn) {
FoldingColumn other = (FoldingColumn) o;
return Objects.equals(f, other.f) && Arrays.equals(columns, other.columns);
}
return false;
}
@Override
public int hashCode() {
return 61 * Arrays.hashCode(columns) + Objects.hashCode(f);
}
}