package water.api;
import water.H2O;
import water.MRTask;
import water.api.schemas3.FindV3;
import water.api.schemas3.FrameV3;
import water.exceptions.H2OColumnNotFoundArgumentException;
import water.exceptions.H2OCategoricalLevelNotFoundArgumentException;
import water.exceptions.H2OIllegalArgumentException;
import water.fvec.Chunk;
import water.fvec.Frame;
import water.fvec.Vec;
import water.util.ArrayUtils;
import water.util.IcedHashMap;
import water.util.IcedHashMapGeneric;
class FindHandler extends Handler {
@SuppressWarnings("unused") // called through reflection by RequestServer
public FindV3 find(int version, FindV3 find) {
Frame frame = find.key._fr;
// Peel out an optional column; restrict to this column
if( find.column != null ) {
Vec vec = frame.vec(find.column);
if( vec==null ) throw new H2OColumnNotFoundArgumentException("column", frame, find.column);
find.key = new FrameV3(new Frame(new String[]{find.column}, new Vec[]{vec}));
}
// Convert the search string into a column-specific flavor
Vec[] vecs = frame.vecs();
double ds[] = new double[vecs.length];
for( int i=0; i<vecs.length; i++ ) {
if( vecs[i].isCategorical() ) {
int idx = ArrayUtils.find(vecs[i].domain(),find.match);
if( idx==-1 && vecs.length==1 ) throw new H2OCategoricalLevelNotFoundArgumentException("match", find.match, frame._key.toString(), frame.name(i));
ds[i] = idx;
} else if( vecs[i].isUUID() ) {
throw H2O.unimpl();
} else if( vecs[i].isString() ) {
throw H2O.unimpl();
} else if( vecs[i].isTime() ) {
throw H2O.unimpl();
} else {
try {
ds[i] = find.match==null ? Double.NaN : Double.parseDouble(find.match);
} catch( NumberFormatException e ) {
if( vecs.length==1 ) {
// There's only one Vec and it's a numeric Vec and our search string isn't a number
IcedHashMapGeneric.IcedHashMapStringObject values = new IcedHashMapGeneric.IcedHashMapStringObject();
String msg = "Frame: " + frame._key.toString() + " as only one column, it is numeric, and the find pattern is not numeric: " + find.match;
values.put("frame_name", frame._key.toString());
values.put("column_name", frame.name(i));
values.put("pattern", find.match);
throw new H2OIllegalArgumentException(msg, msg, values);
}
ds[i] = Double.longBitsToDouble(0xcafebabe); // Do not match
}
}
}
Find f = new Find(find.row,ds).doAll(frame);
find.prev = f._prev;
find.next = f._next==Long.MAX_VALUE ? -1 : f._next;
return find;
}
private static class Find extends MRTask<Find> {
final long _row;
final double[] _ds;
long _prev, _next;
Find( long row, double[] ds ) {
super((byte)(H2O.GUI_PRIORITY - 2));
_row = row; _ds = ds; _prev = -1; _next = Long.MAX_VALUE;
}
@Override public void map( Chunk cs[] ) {
for( int col = 0; col<cs.length; col++ ) {
Chunk C = cs[col];
for( int row=0; row<C._len; row++ ) {
if( C.atd(row) == _ds[col] || (C.isNA(row) && Double.isNaN(_ds[col])) ) {
long r = C.start()+row;
if( r < _row ) { if( r > _prev ) _prev = r; }
else if( r > _row ) { if( r < _next ) _next = r; }
}
}
}
}
@Override public void reduce( Find f ) {
if( _prev < f._prev ) _prev = f._prev;
if( _next > f._next ) _next = f._next;
}
}
}