package water.cascade;
//import hex.Quantiles;
//import hex.FrameTask.DataInfo;
//import hex.gram.Gram.GramTask;
//import hex.la.Matrix;
import java.util.*;
//import org.apache.commons.math3.util.*;
//import org.joda.time.DateTime;
import org.joda.time.MutableDateTime;
import water.*;
import water.cascade.Env;
import water.fvec.*;
import water.util.MathUtils;
//import water.fvec.Vec.VectorGroup;
//import water.util.Log;
//import water.util.Utils;
/** Parse a generic R string and build an AST, in the context of an H2O Cloud
* @author cliffc@0xdata.com
*/
// --------------------------------------------------------------------------
public abstract class ASTOp extends AST {
// The order of operator precedence follows R rules.
// Highest the first
static final public int OPP_PREFIX = 100; /* abc() */
static final public int OPP_POWER = 13; /* ^ */
static final public int OPP_UPLUS = 12; /* + */
static final public int OPP_UMINUS = 12; /* - */
static final public int OPP_MOD = 11; /* %xyz% */
static final public int OPP_MUL = 10; /* * */
static final public int OPP_DIV = 10; /* / */
static final public int OPP_PLUS = 9; /* + */
static final public int OPP_MINUS = 9; /* - */
static final public int OPP_GT = 8; /* > */
static final public int OPP_GE = 8; /* >= */
static final public int OPP_LT = 8; /* < */
static final public int OPP_LE = 8; /* <= */
static final public int OPP_EQ = 8; /* == */
static final public int OPP_NE = 8; /* != */
static final public int OPP_NOT = 7; /* ! */
static final public int OPP_AND = 6; /* &, && */
static final public int OPP_OR = 5; /* |, || */
static final public int OPP_DILDA = 4; /* ~ */
static final public int OPP_RARROW = 3; /* ->, ->> */
static final public int OPP_ASSN = 2; /* = */
static final public int OPP_LARROW = 1; /* <-, <<- */
// Operator assocation order
static final public int OPA_LEFT = 0;
static final public int OPA_RIGHT = 1;
// Operation formula notations
static final public int OPF_INFIX = 0;
static final public int OPF_PREFIX = 1;
// Tables of operators by arity
static final public HashMap<String,ASTOp> UNI_INFIX_OPS = new HashMap<>();
static final public HashMap<String,ASTOp> BIN_INFIX_OPS = new HashMap<>();
static final public HashMap<String,ASTOp> PREFIX_OPS = new HashMap<>();
static final public HashMap<String,ASTOp> UDF_OPS = new HashMap<>();
// Too avoid a cyclic class-loading dependency, these are init'd before subclasses.
static final String VARS1[] = new String[]{ "", "x"};
static final String VARS2[] = new String[]{ "", "x","y"};
static {
// Unary infix ops
// putUniInfix(new ASTUniPlus());
// putUniInfix(new ASTUniMinus());
// putUniInfix(new ASTNot());
// Binary infix ops
putBinInfix(new ASTPlus());
putBinInfix(new ASTSub());
putBinInfix(new ASTMul());
putBinInfix(new ASTDiv());
// putBinInfix(new ASTPow());
// putBinInfix(new ASTPow2());
// putBinInfix(new ASTMod());
// putBinInfix(new ASTAND());
// putBinInfix(new ASTOR());
// putBinInfix(new ASTLT());
// putBinInfix(new ASTLE());
// putBinInfix(new ASTGT());
// putBinInfix(new ASTGE());
putBinInfix(new ASTEQ());
putBinInfix(new ASTNE());
// putBinInfix(new ASTLA());
// putBinInfix(new ASTLO());
// putBinInfix(new ASTMMult());
// Unary prefix ops
// putPrefix(new ASTIsNA());
// putPrefix(new ASTNrow());
// putPrefix(new ASTNcol());
// putPrefix(new ASTLength());
// putPrefix(new ASTAbs ());
// putPrefix(new ASTSgn ());
// putPrefix(new ASTSqrt());
// putPrefix(new ASTCeil());
// putPrefix(new ASTFlr ());
// putPrefix(new ASTLog ());
// putPrefix(new ASTExp ());
// putPrefix(new ASTScale());
// putPrefix(new ASTFactor());
// putPrefix(new ASTIsFactor());
// putPrefix(new ASTAnyFactor()); // For Runit testing
// putPrefix(new ASTCanBeCoercedToLogical());
// putPrefix(new ASTAnyNA());
// putPrefix(new ASTIsTRUE());
// putPrefix(new ASTMTrans());
// Trigonometric functions
// putPrefix(new ASTCos());
// putPrefix(new ASTSin());
// putPrefix(new ASTTan());
// putPrefix(new ASTACos());
// putPrefix(new ASTASin());
// putPrefix(new ASTATan());
// putPrefix(new ASTCosh());
// putPrefix(new ASTSinh());
// putPrefix(new ASTTanh());
// Time extractions, to and from msec since the Unix Epoch
// putPrefix(new ASTYear ());
// putPrefix(new ASTMonth ());
// putPrefix(new ASTDay ());
// putPrefix(new ASTHour ());
// putPrefix(new ASTMinute());
// putPrefix(new ASTSecond());
// putPrefix(new ASTMillis());
//
// // Time series operations
// putPrefix(new ASTDiff ());
//
// // More generic reducers
// putPrefix(new ASTMin ());
// putPrefix(new ASTMax ());
// putPrefix(new ASTSum ());
// putPrefix(new ASTSdev());
// putPrefix(new ASTVar());
// putPrefix(new ASTMean());
// putPrefix(new ASTMinNaRm());
// putPrefix(new ASTMaxNaRm());
// putPrefix(new ASTSumNaRm());
// putPrefix(new ASTXorSum ());
//
// // Misc
// putPrefix(new ASTSeq ());
// putPrefix(new ASTSeqLen());
// putPrefix(new ASTRepLen());
// putPrefix(new ASTQtile ());
// putPrefix(new ASTCat ());
// putPrefix(new ASTCbind ());
// putPrefix(new ASTTable ());
// putPrefix(new ASTReduce());
// putPrefix(new ASTIfElse());
// putPrefix(new ASTRApply());
// putPrefix(new ASTSApply());
// putPrefix(new ASTddply ());
// putPrefix(new ASTUnique());
// putPrefix(new ASTRunif ());
// putPrefix(new ASTCut ());
// putPrefix(new ASTfindInterval());
// putPrefix(new ASTPrint ());
// putPrefix(new ASTLs ());
}
static private void putUniInfix(ASTOp ast) { UNI_INFIX_OPS.put(ast.opStr(),ast); }
static private void putBinInfix(ASTOp ast) { BIN_INFIX_OPS.put(ast.opStr(),ast); }
static private void putPrefix (ASTOp ast) { PREFIX_OPS.put(ast.opStr(),ast); }
static void putUDF (ASTOp ast, String fn) { UDF_OPS.put(fn,ast); }
static void removeUDF (String fn) { UDF_OPS.remove(fn); }
static public ASTOp isOp(String id) {
// This order matters. If used as a prefix OP, `+` and `-` are binary only.
ASTOp op4 = UDF_OPS.get(id); if( op4 != null ) return op4;
return isBuiltinOp(id);
}
static public ASTOp isBuiltinOp(String id) {
ASTOp op3 = PREFIX_OPS.get(id); if( op3 != null ) return op3;
ASTOp op2 = BIN_INFIX_OPS.get(id); if( op2 != null ) return op2;
ASTOp op1 = UNI_INFIX_OPS.get(id); return op1;
}
static public boolean isInfixOp(String id) {
return BIN_INFIX_OPS.containsKey(id) || UNI_INFIX_OPS.containsKey(id);
}
static public boolean isUDF(String id) {
return UDF_OPS.containsKey(id);
}
static public boolean isUDF(ASTOp op) { return isUDF(op.opStr()); }
static public Set<String> opStrs() {
Set<String> all = UNI_INFIX_OPS.keySet();
all.addAll(BIN_INFIX_OPS.keySet());
all.addAll(PREFIX_OPS.keySet());
all.addAll(UDF_OPS.keySet());
return all;
}
final int _form; // formula notation, 0 - infix, 1 - prefix
final int _precedence; // operator precedence number
final int _association; // 0 - left associated, 1 - right associated
// All fields are final, because functions are immutable
final String _vars[]; // Variable names
ASTOp( String vars[], int form, int prec, int asso) {
// super(null);
_form = form;
_precedence = prec;
_association = asso;
_vars = vars;
}
abstract String opStr();
abstract ASTOp make();
// Standard column-wise function application
abstract void apply(Env global, Env local);
// Special row-wise 'apply'
double[] map(Env env, double[] in, double[] out) { throw H2O.unimpl(); }
@Override void exec(Env e) { throw H2O.fail(); }
public boolean leftAssociate( ) {
return _association == OPA_LEFT;
}
// @Override public String toString() {
// String s = _t._ts[0]+" "+opStr()+"(";
// int len=_t._ts.length;
// for( int i=1; i<len-1; i++ )
// s += _t._ts[i]+" "+(_vars==null?"":_vars[i])+", ";
// return s + (len > 1 ? _t._ts[len-1]+" "+(_vars==null?"":_vars[len-1]) : "")+")";
// }
// public String toString(boolean verbose) {
// if( !verbose ) return toString(); // Just the fun name& arg names
// return toString();
// }
public static ASTOp get(String op) {
if (BIN_INFIX_OPS.containsKey(op)) return BIN_INFIX_OPS.get(op);
if (UNI_INFIX_OPS.containsKey(op)) return UNI_INFIX_OPS.get(op);
if (isUDF(op)) return UDF_OPS.get(op);
if (PREFIX_OPS.containsKey(op)) return PREFIX_OPS.get(op);
throw H2O.fail("Unimplemented: Could not find the operation or function "+op);
}
}
//abstract class ASTUniOp extends ASTOp {
// ASTUniOp( int form, int precedence, int association ) {
// super(VARS1,form,precedence,association);
// }
// double op( double d ) { throw H2O.fail(); }
// protected ASTUniOp( String[] vars, int form, int precedence, int association ) {
// super(vars,form,precedence,association);
// }
// @Override void apply(Env global, Env local) {
// // Expect we can broadcast across all functions as needed.
// if( !env.isAry() ) { env.poppush(op(env.popDbl())); return; }
// Frame fr = env.popAry();
// String skey = env.key();
// final ASTUniOp uni = this; // Final 'this' so can use in closure
// Frame fr2 = new MRTask2() {
// @Override public void map( Chunk chks[], NewChunk nchks[] ) {
// for( int i=0; i<nchks.length; i++ ) {
// NewChunk n =nchks[i];
// Chunk c = chks[i];
// int rlen = c._len;
// for( int r=0; r<rlen; r++ )
// n.addNum(uni.op(c.at0(r)));
// }
// }
// }.doAll(fr.numCols(),fr).outputFrame(fr._names, null);
// env.subRef(fr,skey);
// env.pop(); // Pop self
// env.push(fr2);
// }
//}
//
//abstract class ASTUniPrefixOp extends ASTUniOp {
// ASTUniPrefixOp( ) { super(OPF_PREFIX,OPP_PREFIX,OPA_RIGHT); }
// ASTUniPrefixOp( String[] vars) { super(vars, OPF_PREFIX,OPP_PREFIX,OPA_RIGHT); }
//}
//
//class ASTCos extends ASTUniPrefixOp { @Override String opStr(){ return "cos"; } @Override ASTOp make() {return new ASTCos ();} @Override double op(double d) { return Math.cos(d);}}
//class ASTSin extends ASTUniPrefixOp { @Override String opStr(){ return "sin"; } @Override ASTOp make() {return new ASTSin ();} @Override double op(double d) { return Math.sin(d);}}
//class ASTTan extends ASTUniPrefixOp { @Override String opStr(){ return "tan"; } @Override ASTOp make() {return new ASTTan ();} @Override double op(double d) { return Math.tan(d);}}
//class ASTACos extends ASTUniPrefixOp { @Override String opStr(){ return "acos"; } @Override ASTOp make() {return new ASTACos();} @Override double op(double d) { return Math.acos(d);}}
//class ASTASin extends ASTUniPrefixOp { @Override String opStr(){ return "asin"; } @Override ASTOp make() {return new ASTASin();} @Override double op(double d) { return Math.asin(d);}}
//class ASTATan extends ASTUniPrefixOp { @Override String opStr(){ return "atan"; } @Override ASTOp make() {return new ASTATan();} @Override double op(double d) { return Math.atan(d);}}
//class ASTCosh extends ASTUniPrefixOp { @Override String opStr(){ return "cosh"; } @Override ASTOp make() {return new ASTCosh ();} @Override double op(double d) { return Math.cosh(d);}}
//class ASTSinh extends ASTUniPrefixOp { @Override String opStr(){ return "sinh"; } @Override ASTOp make() {return new ASTSinh ();} @Override double op(double d) { return Math.sinh(d);}}
//class ASTTanh extends ASTUniPrefixOp { @Override String opStr(){ return "tanh"; } @Override ASTOp make() {return new ASTTanh ();} @Override double op(double d) { return Math.tanh(d);}}
//
//class ASTAbs extends ASTUniPrefixOp { @Override String opStr(){ return "abs"; } @Override ASTOp make() {return new ASTAbs ();} @Override double op(double d) { return Math.abs(d);}}
//class ASTSgn extends ASTUniPrefixOp { @Override String opStr(){ return "sgn" ; } @Override ASTOp make() {return new ASTSgn ();} @Override double op(double d) { return Math.signum(d);}}
//class ASTSqrt extends ASTUniPrefixOp { @Override String opStr(){ return "sqrt"; } @Override ASTOp make() {return new ASTSqrt();} @Override double op(double d) { return Math.sqrt(d);}}
//class ASTCeil extends ASTUniPrefixOp { @Override String opStr(){ return "ceil"; } @Override ASTOp make() {return new ASTCeil();} @Override double op(double d) { return Math.ceil(d);}}
//class ASTFlr extends ASTUniPrefixOp { @Override String opStr(){ return "floor"; } @Override ASTOp make() {return new ASTFlr ();} @Override double op(double d) { return Math.floor(d);}}
//class ASTLog extends ASTUniPrefixOp { @Override String opStr(){ return "log"; } @Override ASTOp make() {return new ASTLog ();} @Override double op(double d) { return Math.log(d);}}
//class ASTExp extends ASTUniPrefixOp { @Override String opStr(){ return "exp"; } @Override ASTOp make() {return new ASTExp ();} @Override double op(double d) { return Math.exp(d);}}
////class ASTIsNA extends ASTUniPrefixOp { @Override String opStr(){ return "is.na"; } @Override ASTOp make() {return new ASTIsNA();} @Override double op(double d) { return Double.isNaN(d)?1:0;}}
//class ASTIsNA extends ASTUniPrefixOp { @Override String opStr(){ return "is.na";} @Override ASTOp make() { return new ASTIsNA();} @Override double op(double d) { return Double.isNaN(d)?1:0;}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// // Expect we can broadcast across all functions as needed.
// if( !env.isAry() ) { env.poppush(op(env.popDbl())); return; }
// Frame fr = env.popAry();
// String skey = env.key();
// final ASTUniOp uni = this; // Final 'this' so can use in closure
// Frame fr2 = new MRTask2() {
// @Override public void map( Chunk chks[], NewChunk nchks[] ) {
// for( int i=0; i<nchks.length; i++ ) {
// NewChunk n = nchks[i];
// Chunk c = chks[i];
// int rlen = c._len;
// for( int r=0; r<rlen; r++ )
// n.addNum( c.isNA0(r) ? 1 : 0);
// }
// }
// }.doAll(fr.numCols(),fr).outputFrame(fr._names, null);
// env.subRef(fr,skey);
// env.pop(); // Pop self
// env.push(fr2);
// }
//}
//class ASTNrow extends ASTUniPrefixOp {
// ASTNrow() { super(VARS1,new Type[]{Type.DBL,Type.ARY}); }
// @Override String opStr() { return "nrow"; }
// @Override ASTOp make() {return this;}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// Frame fr = env.popAry();
// String skey = env.key();
// double d = fr.numRows();
// env.subRef(fr,skey);
// env.poppush(d);
// }
//}
//
//class ASTNcol extends ASTUniPrefixOp {
// ASTNcol() { super(VARS1,new Type[]{Type.DBL,Type.ARY}); }
// @Override String opStr() { return "ncol"; }
// @Override ASTOp make() {return this;}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// Frame fr = env.popAry();
// String skey = env.key();
// double d = fr.numCols();
// env.subRef(fr,skey);
// env.poppush(d);
// }
//}
//
//class ASTLength extends ASTUniPrefixOp {
// ASTLength() { super(VARS1, new Type[]{Type.DBL,Type.ARY}); }
// @Override String opStr() { return "length"; }
// @Override ASTOp make() { return this; }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// Frame fr = env.popAry();
// String skey = env.key();
// double d = fr.numCols() == 1 ? fr.numRows() : fr.numCols();
// env.subRef(fr,skey);
// env.poppush(d);
// }
//}
//
//class ASTIsFactor extends ASTUniPrefixOp {
// ASTIsFactor() { super(VARS1,new Type[]{Type.DBL,Type.ARY}); }
// @Override String opStr() { return "is.factor"; }
// @Override ASTOp make() {return this;}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// if(!env.isAry()) { env.poppush(0); return; }
// Frame fr = env.popAry();
// String skey = env.key();
// double d = 1;
// Vec[] v = fr.vecs();
// for(int i = 0; i < v.length; i++) {
// if(!v[i].isEnum()) { d = 0; break; }
// }
// env.subRef(fr,skey);
// env.poppush(d);
// }
//}
//
//// Added to facilitate Runit testing
//class ASTAnyFactor extends ASTUniPrefixOp {
// ASTAnyFactor() { super(VARS1,new Type[]{Type.DBL,Type.ARY}); }
// @Override String opStr() { return "any.factor"; }
// @Override ASTOp make() {return this;}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// if(!env.isAry()) { env.poppush(0); return; }
// Frame fr = env.popAry();
// String skey = env.key();
// double d = 0;
// Vec[] v = fr.vecs();
// for(int i = 0; i < v.length; i++) {
// if(v[i].isEnum()) { d = 1; break; }
// }
// env.subRef(fr,skey);
// env.poppush(d);
// }
//}
//
//class ASTCanBeCoercedToLogical extends ASTUniPrefixOp {
// ASTCanBeCoercedToLogical() { super(VARS1,new Type[]{Type.DBL,Type.ARY}); }
// @Override String opStr() { return "canBeCoercedToLogical"; }
// @Override ASTOp make() {return this;}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// if(!env.isAry()) { env.poppush(0); return; }
// Frame fr = env.popAry();
// String skey = env.key();
// double d = 0;
// Vec[] v = fr.vecs();
// for (Vec aV : v) {
// if (aV.isInt()) {
// if (aV.min() == 0 && aV.max() == 1) {
// d = 1;
// break;
// }
// }
// }
// env.subRef(fr,skey);
// env.poppush(d);
// }
//}
//
//class ASTAnyNA extends ASTUniPrefixOp {
// ASTAnyNA() { super(VARS1,new Type[]{Type.DBL,Type.ARY}); }
// @Override String opStr() { return "any.na"; }
// @Override ASTOp make() {return this;}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// if(!env.isAry()) { env.poppush(0); return; }
// Frame fr = env.popAry();
// String skey = env.key();
// double d = 0;
// Vec[] v = fr.vecs();
// for(int i = 0; i < v.length; i++) {
// if(v[i].naCnt() > 0) { d = 1; break; }
// }
// env.subRef(fr, skey);
// env.poppush(d);
// }
//}
//
//class ASTIsTRUE extends ASTUniPrefixOp {
// ASTIsTRUE() {super(VARS1,new Type[]{Type.DBL,Type.unbound()});}
// @Override String opStr() { return "isTRUE"; }
// @Override ASTOp make() {return new ASTIsTRUE();} // to make sure fcn get bound at each new context
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// double res = env.isDbl() && env.popDbl()==1.0 ? 1:0;
// env.pop();
// env.poppush(res);
// }
//}
//
//class ASTScale extends ASTUniPrefixOp {
// ASTScale() { super(VARS1,new Type[]{Type.ARY,Type.ARY}); }
// @Override String opStr() { return "scale"; }
// @Override ASTOp make() {return this;}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// if(!env.isAry()) { env.poppush(Double.NaN); return; }
// Frame fr = env.popAry();
// String skey = env.key();
// Frame fr2 = new Scale().doIt(fr.numCols(), fr).outputFrame(fr._names, fr.domains());
// env.subRef(fr,skey);
// env.pop(); // Pop self
// env.push(fr2);
// }
//
// private static class Scale extends MRTask2<Scale> {
// protected int _nums = 0;
// protected int[] _ind; // Saves indices of numeric cols first, followed by enums
// protected double[] _normSub;
// protected double[] _normMul;
//
// @Override public void map(Chunk chks[], NewChunk nchks[]) {
// // Normalize numeric cols only
// for(int k = 0; k < _nums; k++) {
// int i = _ind[k];
// NewChunk n = nchks[i];
// Chunk c = chks[i];
// int rlen = c._len;
// for(int r = 0; r < rlen; r++)
// n.addNum((c.at0(r)-_normSub[i])*_normMul[i]);
// }
//
// for(int k = _nums; k < chks.length; k++) {
// int i = _ind[k];
// NewChunk n = nchks[i];
// Chunk c = chks[i];
// int rlen = c._len;
// for(int r = 0; r < rlen; r++)
// n.addNum(c.at0(r));
// }
// }
//
// public Scale doIt(int outputs, Frame fr) { return dfork2(outputs, fr).getResult(); }
// public Scale dfork2(int outputs, Frame fr) {
// final Vec [] vecs = fr.vecs();
// for(int i = 0; i < vecs.length; i++) {
// if(!vecs[i].isEnum()) _nums++;
// }
// if(_normSub == null) _normSub = MemoryManager.malloc8d(_nums);
// if(_normMul == null) { _normMul = MemoryManager.malloc8d(_nums); Arrays.fill(_normMul,1); }
// if(_ind == null) _ind = MemoryManager.malloc4(vecs.length);
//
// int ncnt = 0; int ccnt = 0;
// for(int i = 0; i < vecs.length; i++){
// if(!vecs[i].isEnum()) {
// _normSub[ncnt] = vecs[i].mean();
// _normMul[ncnt] = 1.0/vecs[i].sigma();
// _ind[ncnt++] = i;
// } else
// _ind[_nums+(ccnt++)] = i;
// }
// assert ncnt == _nums && (ncnt + ccnt == vecs.length);
// return dfork(outputs, fr, false);
// }
// }
//}
//
//// ----
//abstract class ASTTimeOp extends ASTOp {
// static Type[] newsig() {
// Type t1 = Type.dblary();
// return new Type[]{t1,t1};
// }
// ASTTimeOp() { super(VARS1,newsig(),OPF_PREFIX,OPP_PREFIX,OPA_RIGHT); }
// abstract long op( MutableDateTime dt );
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// // Single instance of MDT for the single call
// if( !env.isAry() ) { // Single point
// double d = env.popDbl();
// if( !Double.isNaN(d) ) d = op(new MutableDateTime((long)d));
// env.poppush(d);
// return;
// }
// // Whole column call
// Frame fr = env.popAry();
// String skey = env.key();
// final ASTTimeOp uni = this; // Final 'this' so can use in closure
// Frame fr2 = new MRTask2() {
// @Override public void map( Chunk chks[], NewChunk nchks[] ) {
// MutableDateTime dt = new MutableDateTime(0);
// for( int i=0; i<nchks.length; i++ ) {
// NewChunk n =nchks[i];
// Chunk c = chks[i];
// int rlen = c._len;
// for( int r=0; r<rlen; r++ ) {
// double d = c.at0(r);
// if( !Double.isNaN(d) ) {
// dt.setMillis((long)d);
// d = uni.op(dt);
// }
// n.addNum(d);
// }
// }
// }
// }.doAll(fr.numCols(),fr).outputFrame(fr._names, null);
// env.subRef(fr,skey);
// env.pop(); // Pop self
// env.push(fr2);
// }
//}
//
//class ASTYear extends ASTTimeOp { @Override String opStr(){ return "year" ; } @Override ASTOp make() {return new ASTYear ();} @Override long op(MutableDateTime dt) { return dt.getYear();}}
//class ASTMonth extends ASTTimeOp { @Override String opStr(){ return "month"; } @Override ASTOp make() {return new ASTMonth ();} @Override long op(MutableDateTime dt) { return dt.getMonthOfYear()-1;}}
//class ASTDay extends ASTTimeOp { @Override String opStr(){ return "day" ; } @Override ASTOp make() {return new ASTDay ();} @Override long op(MutableDateTime dt) { return dt.getDayOfMonth();}}
//class ASTHour extends ASTTimeOp { @Override String opStr(){ return "hour" ; } @Override ASTOp make() {return new ASTHour ();} @Override long op(MutableDateTime dt) { return dt.getHourOfDay();}}
//class ASTMinute extends ASTTimeOp { @Override String opStr(){return "minute";} @Override ASTOp make() {return new ASTMinute();} @Override long op(MutableDateTime dt) { return dt.getMinuteOfHour();}}
//class ASTSecond extends ASTTimeOp { @Override String opStr(){return "second";} @Override ASTOp make() {return new ASTSecond();} @Override long op(MutableDateTime dt) { return dt.getSecondOfMinute();}}
//class ASTMillis extends ASTTimeOp { @Override String opStr(){return "millis";} @Override ASTOp make() {return new ASTMillis();} @Override long op(MutableDateTime dt) { return dt.getMillisOfSecond();}}
//
//// Finite backward difference for user-specified lag
//// http://en.wikipedia.org/wiki/Finite_difference
//class ASTDiff extends ASTOp {
// ASTDiff() { super(new String[]{"diff", "x", "lag", "differences"},
// new Type[]{Type.ARY, Type.ARY, Type.DBL, Type.DBL},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT); }
// @Override String opStr() { return "diff"; }
// @Override ASTOp make() {return new ASTDiff();}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// final int diffs = (int)env.popDbl();
// if(diffs < 0) throw new IllegalArgumentException("differences must be an integer >= 1");
// final int lag = (int)env.popDbl();
// if(lag < 0) throw new IllegalArgumentException("lag must be an integer >= 1");
//
// Frame fr = env.popAry();
// String skey = env.key();
// if(fr.vecs().length != 1 || fr.vecs()[0].isEnum())
// throw new IllegalArgumentException("diff takes a single numeric column vector");
//
// Frame fr2 = new MRTask2() {
// @Override public void map(Chunk chk, NewChunk nchk) {
// int rstart = (int)(diffs*lag - chk._start);
// if(rstart > chk._len) return;
// rstart = Math.max(0, rstart);
//
// // Formula: \Delta_h^n x_t = \sum_{i=0}^n (-1)^i*\binom{n}{k}*x_{t-i*h}
// for(int r = rstart; r < chk._len; r++) {
// double x = chk.at0(r);
// long row = chk._start + r;
//
// for(int i = 1; i <= diffs; i++) {
// double x_lag = chk.at_slow(row - i*lag);
// double coef = ArithmeticUtils.binomialCoefficient(diffs, i);
// x += (i % 2 == 0) ? coef*x_lag : -coef*x_lag;
// }
// nchk.addNum(x);
// }
// }
// }.doAll(1,fr).outputFrame(fr.names(), fr.domains());
// env.subRef(fr, skey);
// env.pop();
// env.push(fr2);
// }
//}
// ----
// Class of things that will auto-expand across arrays in a 2-to-1 way:
// applying 2 things (from an array or scalar to array or scalar) producing an
// array or scalar result.
abstract class ASTBinOp extends ASTOp {
ASTBinOp( int form, int precedence, int association ) {
super(VARS2, form, precedence, association); // binary ops are infix ops
}
AST parse_impl(Exec E) {
AST l = E.parse();
AST r = E.xpeek(' ').parse();
AST res = (AST)clone();
res._asts = new AST[]{l,r};
return res;
}
abstract double op( double d0, double d1 );
@Override void apply(Env global, Env local) {
// Expect we can broadcast across all functions as needed.
Env env = local == null ? global : local;
Frame fr0 = null, fr1 = null;
double d0=0, d1=0;
if( env.isAry() ) fr1 = (Frame) env.pop(); else d1 = (double)env.pop(); //String k0 = env.key();
if( env.isAry() ) fr0 = (Frame) env.pop(); else d0 = (double)env.pop(); //String k1 = env.key();
if( fr0==null && fr1==null ) {
env.push(op(d0, d1));
return;
}
final boolean lf = fr0 != null;
final boolean rf = fr1 != null;
final double df0 = d0, df1 = d1;
Frame fr = null; // Do-All frame
int ncols = 0; // Result column count
if( fr0 !=null ) { // Left?
ncols = fr0.numCols();
if( fr1 != null ) {
if( fr0.numCols() != fr1.numCols() ||
fr0.numRows() != fr1.numRows() )
throw new IllegalArgumentException("Arrays must be same size: "+fr0+" vs "+fr1);
fr = new Frame(fr0).add(fr1);
} else {
fr = fr0;
}
} else {
ncols = fr1.numCols();
fr = fr1;
}
final ASTBinOp bin = this; // Final 'this' so can use in closure
// Run an arbitrary binary op on one or two frames & scalars
Frame fr2 = new MRTask() {
@Override public void map( Chunk chks[], NewChunk nchks[] ) {
for( int i=0; i<nchks.length; i++ ) {
NewChunk n =nchks[i];
int rlen = chks[0].len();
Chunk c0 = chks[i];
if( (!c0.vec().isEnum() &&
!(lf && rf && chks[i+nchks.length].vec().isEnum())) ||
bin instanceof ASTEQ ||
bin instanceof ASTNE ) {
for( int r=0; r<rlen; r++ ) {
double lv; double rv;
if (lf) {
if(chks[i].isNA0(r)) { n.addNum(Double.NaN); continue; }
lv = chks[i].at0(r);
} else {
if (Double.isNaN(df0)) { n.addNum(Double.NaN); continue; }
lv = df0;
}
if (rf) {
if(chks[i].isNA0(r)) { n.addNum(Double.NaN); continue; }
rv = chks[i+(lf ? nchks.length:0)].at0(r);
} else {
if (Double.isNaN(df1)) { n.addNum(Double.NaN); continue; }
rv = df1;
}
n.addNum(bin.op(lv, rv));
}
} else {
for( int r=0; r<rlen; r++ ) n.addNA();
}
}
}
}.doAll(ncols,fr).outputFrame((lf ? fr0 : fr1)._names,null);
env.push(fr2);
}
@Override public String toString() { return "("+opStr()+" "+Arrays.toString(_asts)+")"; }
}
//class ASTUniPlus extends ASTUniOp { ASTUniPlus() { super(OPF_INFIX, OPP_UPLUS, OPA_RIGHT); } @Override String opStr(){ return "+" ;} @Override ASTOp make() {return new ASTUniPlus(); } @Override double op(double d) { return d;}}
//class ASTUniMinus extends ASTUniOp { ASTUniMinus() { super(OPF_INFIX, OPP_UMINUS, OPA_RIGHT); } @Override String opStr(){ return "-" ;} @Override ASTOp make() {return new ASTUniMinus();} @Override double op(double d) { return -d;}}
//class ASTNot extends ASTUniOp { ASTNot() { super(OPF_INFIX, OPP_NOT, OPA_RIGHT); } @Override String opStr(){ return "!" ;} @Override ASTOp make() {return new ASTNot(); } @Override double op(double d) { return d==0?1:0; }}
class ASTPlus extends ASTBinOp { ASTPlus() { super(OPF_INFIX, OPP_PLUS, OPA_LEFT ); } @Override String opStr(){ return "+" ;} @Override ASTOp make() {return new ASTPlus();} @Override double op(double d0, double d1) { return d0+d1;}}
class ASTSub extends ASTBinOp { ASTSub() { super(OPF_INFIX, OPP_MINUS, OPA_LEFT); } @Override String opStr(){ return "-" ;} @Override ASTOp make() {return new ASTSub ();} @Override double op(double d0, double d1) { return d0-d1;}}
class ASTMul extends ASTBinOp { ASTMul() { super(OPF_INFIX, OPP_MUL, OPA_LEFT); } @Override String opStr(){ return "*" ;} @Override ASTOp make() {return new ASTMul ();} @Override double op(double d0, double d1) { return d0*d1;}}
class ASTDiv extends ASTBinOp { ASTDiv() { super(OPF_INFIX, OPP_DIV, OPA_LEFT); } @Override String opStr(){ return "/" ;} @Override ASTOp make() {return new ASTDiv ();} @Override double op(double d0, double d1) { return d0/d1;}}
//class ASTPow extends ASTBinOp { ASTPow() { super(OPF_INFIX, OPP_POWER, OPA_RIGHT);} @Override String opStr(){ return "^" ;} @Override ASTOp make() {return new ASTPow ();} @Override double op(double d0, double d1) { return Math.pow(d0,d1);}}
//class ASTPow2 extends ASTBinOp { ASTPow2() { super(OPF_INFIX, OPP_POWER, OPA_RIGHT);} @Override String opStr(){ return "**" ;} @Override ASTOp make() {return new ASTPow2();} @Override double op(double d0, double d1) { return Math.pow(d0,d1);}}
//class ASTMod extends ASTBinOp { ASTMod() { super(OPF_INFIX, OPP_MOD, OPA_LEFT); } @Override String opStr(){ return "%" ;} @Override ASTOp make() {return new ASTMod ();} @Override double op(double d0, double d1) { return d0%d1;}}
//class ASTLT extends ASTBinOp { ASTLT() { super(OPF_INFIX, OPP_LT, OPA_LEFT); } @Override String opStr(){ return "<" ;} @Override ASTOp make() {return new ASTLT ();} @Override double op(double d0, double d1) { return d0<d1 && !Utils.equalsWithinOneSmallUlp(d0,d1)?1:0;}}
//class ASTLE extends ASTBinOp { ASTLE() { super(OPF_INFIX, OPP_LE, OPA_LEFT); } @Override String opStr(){ return "<=" ;} @Override ASTOp make() {return new ASTLE ();} @Override double op(double d0, double d1) { return d0<d1 || Utils.equalsWithinOneSmallUlp(d0,d1)?1:0;}}
//class ASTGT extends ASTBinOp { ASTGT() { super(OPF_INFIX, OPP_GT, OPA_LEFT); } @Override String opStr(){ return ">" ;} @Override ASTOp make() {return new ASTGT ();} @Override double op(double d0, double d1) { return d0>d1 && !Utils.equalsWithinOneSmallUlp(d0,d1)?1:0;}}
//class ASTGE extends ASTBinOp { ASTGE() { super(OPF_INFIX, OPP_GE, OPA_LEFT); } @Override String opStr(){ return ">=" ;} @Override ASTOp make() {return new ASTGE ();} @Override double op(double d0, double d1) { return d0>d1 || Utils.equalsWithinOneSmallUlp(d0,d1)?1:0;}}
class ASTEQ extends ASTBinOp { ASTEQ() { super(OPF_INFIX, OPP_EQ, OPA_LEFT); } @Override String opStr(){ return "==" ;} @Override ASTOp make() {return new ASTEQ ();} @Override double op(double d0, double d1) { return MathUtils.equalsWithinOneSmallUlp(d0, d1)?1:0;}}
class ASTNE extends ASTBinOp { ASTNE() { super(OPF_INFIX, OPP_NE, OPA_LEFT); } @Override String opStr(){ return "!=" ;} @Override ASTOp make() {return new ASTNE ();} @Override double op(double d0, double d1) { return MathUtils.equalsWithinOneSmallUlp(d0,d1)?0:1;}}
//class ASTLA extends ASTBinOp { ASTLA() { super(OPF_INFIX, OPP_AND, OPA_LEFT); } @Override String opStr(){ return "&" ;} @Override ASTOp make() {return new ASTLA ();} @Override double op(double d0, double d1) { return (d0!=0 && d1!=0) ? (Double.isNaN(d0) || Double.isNaN(d1)?Double.NaN:1) :0;}}
//class ASTLO extends ASTBinOp { ASTLO() { super(OPF_INFIX, OPP_OR, OPA_LEFT); } @Override String opStr(){ return "|" ;} @Override ASTOp make() {return new ASTLO ();} @Override double op(double d0, double d1) { return (d0==0 && d1==0) ? (Double.isNaN(d0) || Double.isNaN(d1)?Double.NaN:0) :1;}}
// Variable length; instances will be created of required length
//abstract class ASTReducerOp extends ASTOp {
// final double _init;
// final boolean _narm; // na.rm in R
// ASTReducerOp( double init, boolean narm ) {
// super(new String[]{"","dbls"},
// new Type[]{Type.DBL,Type.varargs(Type.dblary())},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT);
// _init = init;
// _narm = narm;
// }
// @Override double[] map(Env env, double[] in, double[] out) {
// double s = _init;
// for (double v : in) if (!_narm || !Double.isNaN(v)) s = op(s,v);
// if (out == null || out.length < 1) out = new double[1];
// out[0] = s;
// return out;
// }
// abstract double op( double d0, double d1 );
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// double sum=_init;
// for( int i=0; i<argcnt-1; i++ )
// if( env.isDbl() ) sum = op(sum,env.popDbl());
// else {
// Frame fr = env.popAry();
// String skey = env.key();
// sum = op(sum,_narm?new NaRmRedOp(this).doAll(fr)._d:new RedOp(this).doAll(fr)._d);
// env.subRef(fr,skey);
// }
// env.poppush(sum);
// }
//
// private static class RedOp extends MRTask2<RedOp> {
// final ASTReducerOp _bin;
// RedOp( ASTReducerOp bin ) { _bin = bin; _d = bin._init; }
// double _d;
// @Override public void map( Chunk chks[] ) {
// for( int i=0; i<chks.length; i++ ) {
// Chunk C = chks[i];
// for( int r=0; r<C._len; r++ )
// _d = _bin.op(_d,C.at0(r));
// if( Double.isNaN(_d) ) break;
// }
// }
// @Override public void reduce( RedOp s ) { _d = _bin.op(_d,s._d); }
// }
//
// private static class NaRmRedOp extends MRTask2<NaRmRedOp> {
// final ASTReducerOp _bin;
// NaRmRedOp( ASTReducerOp bin ) { _bin = bin; _d = bin._init; }
// double _d;
// @Override public void map( Chunk chks[] ) {
// for( int i=0; i<chks.length; i++ ) {
// Chunk C = chks[i];
// for( int r=0; r<C._len; r++ )
// if (!Double.isNaN(C.at0(r)))
// _d = _bin.op(_d,C.at0(r));
// if( Double.isNaN(_d) ) break;
// }
// }
// @Override public void reduce( NaRmRedOp s ) { _d = _bin.op(_d,s._d); }
// }
//}
//class ASTSum extends ASTReducerOp { ASTSum( ) {super(0,false);} @Override String opStr(){ return "sum" ;} @Override ASTOp make() {return new ASTSum(); } @Override double op(double d0, double d1) { return d0+d1;}}
//class ASTSumNaRm extends ASTReducerOp { ASTSumNaRm( ) {super(0,true) ;} @Override String opStr(){ return "sum.na.rm";} @Override ASTOp make() {return new ASTSumNaRm();} @Override double op(double d0, double d1) { return d0+d1;}}
//
//class ASTReduce extends ASTOp {
// static final String VARS[] = new String[]{ "", "op2", "ary"};
// static final Type TYPES[]= new Type []{ Type.ARY, Type.fcn(new Type[]{Type.DBL,Type.DBL,Type.DBL}), Type.ARY };
// ASTReduce( ) { super(VARS,TYPES,OPF_PREFIX,OPP_PREFIX,OPA_RIGHT); }
// @Override String opStr(){ return "Reduce";}
// @Override ASTOp make() {return this;}
// @Override void apply(Env env, int argcnt, ASTApply apply) { throw H2O.unimpl(); }
//}
//
//// TODO: Check refcnt mismatch issue: tmp = cbind(h.hex,3.5) results in different refcnts per col
//class ASTCbind extends ASTOp {
// @Override String opStr() { return "cbind"; }
// ASTCbind( ) { super(new String[]{"cbind","ary"},
// new Type[]{Type.ARY,Type.varargs(Type.dblary())},
// OPF_PREFIX,
// OPP_PREFIX,OPA_RIGHT); }
// @Override ASTOp make() {return this;}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// Vec vmax = null;
// for(int i = 0; i < argcnt-1; i++) {
// if(env.isAry(-argcnt+1+i)) {
// Frame tmp = env.ary(-argcnt+1+i);
// if(vmax == null) vmax = tmp.vecs()[0];
// else if(tmp.numRows() != vmax.length())
// // R pads shorter cols to match max rows by cycling/repeating, but we won't support that
// throw new IllegalArgumentException("Row mismatch! Expected " + String.valueOf(vmax.length()) + " but frame has " + String.valueOf(tmp.numRows()));
// }
// }
//
// Frame fr = new Frame(new String[0],new Vec[0]);
// for(int i = 0; i < argcnt-1; i++) {
// if( env.isAry(-argcnt+1+i) ) {
// String name = null;
// Frame fr2 = env.ary(-argcnt+1+i);
// Frame fr3 = fr.makeCompatible(fr2);
// if( fr3 != fr2 ) { // If copied into a new Frame, need to adjust refs
// env.addRef(fr3);
// env.subRef(fr2,null);
// }
// // Take name from an embedded assign: "cbind(colNameX = some_frame, ...)"
// if( fr2.numCols()==1 && apply != null && (name = apply._args[i+1].argName()) != null )
// fr.add(name,fr3.anyVec());
// else fr.add(fr3,true);
// } else {
// double d = env.dbl(-argcnt+1+i);
// Vec v = vmax == null ? Vec.make1Elem(d) : vmax.makeCon(d);
// fr.add("C" + String.valueOf(i+1), v);
// env.addRef(v);
// }
// }
// env._ary[env._sp-argcnt] = fr; env._fcn[env._sp-argcnt] = null;
// env._sp -= argcnt-1;
// Arrays.fill(env._ary,env._sp,env._sp+(argcnt-1),null);
// assert env.check_refcnt(fr.anyVec());
// }
//}
//class ASTMinNaRm extends ASTReducerOp {
// ASTMinNaRm( ) { super( Double.POSITIVE_INFINITY, true ); }
// @Override
// String opStr(){ return "min.na.rm";}
// @Override
// ASTOp make() {return new ASTMinNaRm();}
// @Override double op(double d0, double d1) { return Math.min(d0, d1); }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// double min = Double.POSITIVE_INFINITY;
// int nacnt = 0;
// for( int i=0; i<argcnt-1; i++ )
// if( env.isDbl() ) {
// double a = env.popDbl();
// if (Double.isNaN(a)) nacnt++;
// else min = Math.min(min, a);
// }
// else {
// Frame fr = env.peekAry();
// for (Vec v : fr.vecs())
// min = Math.min(min, v.min());
// env.pop();
// }
// if (nacnt > 0 && min == Double.POSITIVE_INFINITY)
// min = Double.NaN;
// env.poppush(min);
// }
//}
//
//class ASTMaxNaRm extends ASTReducerOp {
// ASTMaxNaRm( ) { super( Double.NEGATIVE_INFINITY, true ); }
// @Override
// String opStr(){ return "max.na.rm";}
// @Override
// ASTOp make() {return new ASTMaxNaRm();}
// @Override double op(double d0, double d1) { return Math.max(d0,d1); }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// double max = Double.NEGATIVE_INFINITY;
// int nacnt = 0;
// for( int i=0; i<argcnt-1; i++ )
// if( env.isDbl() ) {
// double a = env.popDbl();
// if (Double.isNaN(a)) nacnt++;
// else max = Math.max(max, a);
// }
// else {
// Frame fr = env.peekAry();
// for (Vec v : fr.vecs())
// max = Math.max(max, v.max());
// env.pop();
// }
// if (nacnt > 0 && max == Double.NEGATIVE_INFINITY)
// max = Double.NaN;
// env.poppush(max);
// }
//}
//
//class ASTMin extends ASTReducerOp {
// ASTMin( ) { super( Double.POSITIVE_INFINITY, false); }
// @Override
// String opStr(){ return "min";}
// @Override
// ASTOp make() {return new ASTMin();}
// @Override double op(double d0, double d1) { return Math.min(d0, d1); }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// double min = Double.POSITIVE_INFINITY;
// for( int i=0; i<argcnt-1; i++ )
// if( env.isDbl() ) min = Math.min(min, env.popDbl());
// else {
// Frame fr = env.peekAry();
// for (Vec v : fr.vecs())
// if (v.naCnt() > 0) { min = Double.NaN; break; }
// else min = Math.min(min, v.min());
// env.pop();
// }
// env.poppush(min);
// }
//}
//class ASTMax extends ASTReducerOp {
// ASTMax( ) { super( Double.NEGATIVE_INFINITY, false ); }
// @Override
// String opStr(){ return "max";}
// @Override
// ASTOp make() {return new ASTMax();}
// @Override double op(double d0, double d1) { return Math.max(d0,d1); }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// double max = Double.NEGATIVE_INFINITY;
// for( int i=0; i<argcnt-1; i++ )
// if( env.isDbl() ) max = Math.max(max, env.popDbl());
// else {
// Frame fr = env.peekAry();
// for (Vec v : fr.vecs())
// if (v.naCnt() > 0) { max = Double.NaN; break; }
// else max = Math.max(max, v.max());
// env.pop();
// }
// env.poppush(max);
// }
//}
//
//// R like binary operator &&
//class ASTAND extends ASTOp {
// @Override String opStr() { return "&&"; }
// ASTAND( ) {
// super(new String[]{"", "x", "y"},
// new Type[]{Type.DBL,Type.dblary(),Type.dblary()},
// OPF_PREFIX,
// OPP_AND,
// OPA_RIGHT);
// }
// @Override ASTOp make() { return new ASTAND(); }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// double op1 = env.isAry(-2) ? env.ary(-2).vecs()[0].at(0) : env.dbl(-2);
// double op2 = op1==0 ? 0 :
// Double.isNaN(op1) ? Double.NaN :
// env.isAry(-1) ? env.ary(-1).vecs()[0].at(0) : env.dbl(-1);
// env.pop(3);
// if (!Double.isNaN(op2)) op2 = op2==0?0:1;
// env.push(op2);
// }
//}
//
//// R like binary operator ||
//class ASTOR extends ASTOp {
// @Override String opStr() { return "||"; }
// ASTOR( ) {
// super(new String[]{"", "x", "y"},
// new Type[]{Type.DBL,Type.dblary(),Type.dblary()},
// OPF_PREFIX,
// OPP_OR,
// OPA_RIGHT);
// }
// @Override ASTOp make() { return new ASTOR(); }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// double op1 = env.isAry(-2) ? env.ary(-2).vecs()[0].at(0) : env.dbl(-2);
// double op2 = !Double.isNaN(op1) && op1!=0 ? 1 :
// env.isAry(-1) ? env.ary(-1).vecs()[0].at(0) : env.dbl(-1);
// if (!Double.isNaN(op2) && op2 != 0)
// op2 = 1;
// else if (op2 == 0 && Double.isNaN(op1))
// op2 = Double.NaN;
// env.push(op2);
// }
//}
// Brute force implementation of matrix multiply
//class ASTMMult extends ASTOp {
// @Override String opStr() { return "%*%"; }
// ASTMMult( ) {
// super(new String[]{"", "x", "y"},
// new Type[]{Type.ARY,Type.ARY,Type.ARY},
// OPF_PREFIX,
// OPP_MUL,
// OPA_RIGHT);
// }
// @Override ASTOp make() { return new ASTMMult(); }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// env.poppush(3,new Matrix(env.ary(-2)).mult(env.ary(-1)),null);
// }
//}
//
//// Brute force implementation of matrix transpose
//class ASTMTrans extends ASTOp {
// @Override String opStr() { return "t"; }
// ASTMTrans( ) {
// super(new String[]{"", "x"},
// new Type[]{Type.ARY,Type.dblary()},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT);
// }
// @Override ASTOp make() { return new ASTMTrans(); }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// if(!env.isAry(-1)) {
// Key k = new Vec.VectorGroup().addVec();
// Futures fs = new Futures();
// AppendableVec avec = new AppendableVec(k);
// NewChunk chunk = new NewChunk(avec, 0);
// chunk.addNum(env.dbl(-1));
// chunk.close(0, fs);
// Vec vec = avec.close(fs);
// fs.blockForPending();
// vec._domain = null;
// Frame fr = new Frame(new String[] {"C1"}, new Vec[] {vec});
// env.poppush(2,new Matrix(fr).trans(),null);
// } else
// env.poppush(2,new Matrix(env.ary(-1)).trans(),null);
// }
//}
//
//// Similar to R's seq_len
//class ASTSeqLen extends ASTOp {
// @Override String opStr() { return "seq_len"; }
// ASTSeqLen( ) {
// super(new String[]{"seq_len", "n"},
// new Type[]{Type.ARY,Type.DBL},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT);
// }
// @Override ASTOp make() { return this; }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// int len = (int)env.popDbl();
// if (len <= 0)
// throw new IllegalArgumentException("Error in seq_len(" +len+"): argument must be coercible to positive integer");
// env.poppush(1,new Frame(new String[]{"c"}, new Vec[]{Vec.makeSeq(len)}),null);
// }
//}
// Same logic as R's generic seq method
//class ASTSeq extends ASTOp {
// @Override String opStr() { return "seq"; }
// ASTSeq() { super(new String[]{"seq", "from", "to", "by"},
// new Type[]{Type.dblary(), Type.DBL, Type.DBL, Type.DBL},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT);
// }
// @Override ASTOp make() { return this; }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// double by = env.popDbl();
// double to = env.popDbl();
// double from = env.popDbl();
//
// double delta = to - from;
// if(delta == 0 && to == 0)
// env.poppush(to);
// else {
// double n = delta/by;
// if(n < 0)
// throw new IllegalArgumentException("wrong sign in 'by' argument");
// else if(n > Double.MAX_VALUE)
// throw new IllegalArgumentException("'by' argument is much too small");
//
// double dd = Math.abs(delta)/Math.max(Math.abs(from), Math.abs(to));
// if(dd < 100*Double.MIN_VALUE)
// env.poppush(from);
// else {
// Key k = new Vec.VectorGroup().addVec();
// Futures fs = new Futures();
// AppendableVec av = new AppendableVec(k);
// NewChunk nc = new NewChunk(av, 0);
// int len = (int)n + 1;
// for (int r = 0; r < len; r++) nc.addNum(from + r*by);
// // May need to adjust values = by > 0 ? min(values, to) : max(values, to)
// nc.close(0, fs);
// Vec vec = av.close(fs);
// fs.blockForPending();
// vec._domain = null;
// env.poppush(1, new Frame(new String[] {"C1"}, new Vec[] {vec}), null);
// }
// }
// }
//}
//class ASTRepLen extends ASTOp {
// @Override String opStr() { return "rep_len"; }
// ASTRepLen() { super(new String[]{"rep_len", "x", "length.out"},
// new Type[]{Type.dblary(), Type.DBL, Type.DBL},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT);
// }
// @Override ASTOp make() { return this; }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// if(env.isAry(-2)) H2O.unimpl();
// else {
// int len = (int)env.popDbl();
// if(len <= 0)
// throw new IllegalArgumentException("Error in rep_len: argument length.out must be coercible to a positive integer");
// double x = env.popDbl();
// env.poppush(1,new Frame(new String[]{"C1"}, new Vec[]{Vec.makeConSeq(x, len)}),null);
// }
// }
//}
// Compute exact quantiles given a set of cutoffs, using multipass binning algo.
//class ASTQtile extends ASTOp {
// @Override String opStr() { return "quantile"; }
//
// ASTQtile( ) {
// super(new String[]{"quantile","x","probs"},
// new Type[]{Type.ARY, Type.ARY, Type.ARY},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT);
// }
// @Override ASTQtile make() { return new ASTQtile(); }
//
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// Frame x = env.ary(-2);
// Vec xv = x .theVec("Argument #1 in Quantile contains more than 1 column.");
// Vec pv = env.ary(-1).theVec("Argument #2 in Quantile contains more than 1 column.");
// double p[] = new double[(int)pv.length()];
//
// for (int i = 0; i < pv.length(); i++) {
// if ((p[i]=pv.at((long)i)) < 0 || p[i] > 1)
// throw new IllegalArgumentException("Quantile: probs must be in the range of [0, 1].");
// }
// if ( xv.isEnum() ) {
// throw new IllegalArgumentException("Quantile: column type cannot be Enum.");
// }
//
// // create output vec
// Vec res = pv.makeCon(Double.NaN);
//
// final int MAX_ITERATIONS = 16;
// final int MAX_QBINS = 1000; // less uses less memory, can take more passes
// final boolean MULTIPASS = true; // approx in 1 pass if false
// // Type 7 matches R default
// final int INTERPOLATION = 7; // linear if quantile not exact on row. 2 uses mean.
//
// // a little obtuse because reusing first pass object, if p has multiple thresholds
// // since it's always the same (always had same valStart/End seed = vec min/max
// // some MULTIPASS conditionals needed if we were going to make this work for approx or exact
// final Quantiles[] qbins1 = new Quantiles.BinTask2(MAX_QBINS, xv.min(), xv.max()).doAll(xv)._qbins;
// for( int i=0; i<p.length; i++ ) {
// double quantile = p[i];
// // need to pass a different threshold now for each finishUp!
// qbins1[0].finishUp(xv, new double[]{quantile}, INTERPOLATION, MULTIPASS);
// if( qbins1[0]._done ) {
// res.set(i,qbins1[0]._pctile[0]);
// } else {
// // the 2-N map/reduces are here (with new start/ends. MULTIPASS is implied
// Quantiles[] qbinsM = new Quantiles.BinTask2(MAX_QBINS, qbins1[0]._newValStart, qbins1[0]._newValEnd).doAll(xv)._qbins;
// for( int iteration = 2; iteration <= MAX_ITERATIONS; iteration++ ) {
// qbinsM[0].finishUp(xv, new double[]{quantile}, INTERPOLATION, MULTIPASS);
// if( qbinsM[0]._done ) {
// res.set(i,qbinsM[0]._pctile[0]);
// break;
// }
// // the 2-N map/reduces are here (with new start/ends. MULTIPASS is implied
// qbinsM = new Quantiles.BinTask2(MAX_QBINS, qbinsM[0]._newValStart, qbinsM[0]._newValEnd).doAll(xv)._qbins;
// }
// }
// }
//
// res.chunkForChunkIdx(0).close(0,null);
// res.postWrite();
// env.poppush(argcnt, new Frame(new String[]{"Quantile"}, new Vec[]{res}), null);
// }
//}
// Variable length; flatten all the component arys
//class ASTCat extends ASTOp {
// @Override String opStr() { return "c"; }
// ASTCat( ) { super(new String[]{"cat","dbls"},
// new Type[]{Type.ARY,Type.varargs(Type.dblary())},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT); }
// @Override ASTOp make() {return new ASTCat();}
// @Override double[] map(Env env, double[] in, double[] out) {
// if (out == null || out.length < in.length) out = new double[in.length];
// for (int i = 0; i < in.length; i++) out[i] = in[i];
// return out;
// }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// Key key = Vec.VectorGroup.VG_LEN1.addVecs(1)[0];
// AppendableVec av = new AppendableVec(key);
// NewChunk nc = new NewChunk(av,0);
// for( int i=0; i<argcnt-1; i++ ) {
// if (env.isAry(i-argcnt+1)) for (Vec vec : env.ary(i-argcnt+1).vecs()) {
// if (vec.nChunks() > 1) H2O.unimpl();
// for (int r = 0; r < vec.length(); r++) nc.addNum(vec.at(r));
// }
// else nc.addNum(env.dbl(i-argcnt+1));
// }
// nc.close(0,null);
// Vec v = av.close(null);
// env.pop(argcnt);
// env.push(new Frame(new String[]{"C1"}, new Vec[]{v}));
// }
//}
//class ASTRunif extends ASTOp {
// @Override String opStr() { return "runif"; }
// ASTRunif() { super(new String[]{"runif","dbls","seed"},
// new Type[]{Type.ARY,Type.ARY,Type.DBL},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT); }
// @Override ASTOp make() {return new ASTRunif();}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// double temp = env.popDbl();
// final long seed = (temp == -1) ? System.currentTimeMillis() : (long)temp;
// Frame fr = env.popAry();
// String skey = env.key();
// long [] espc = fr.anyVec()._espc;
// long rem = fr.numRows();
// if(rem > espc[espc.length-1]) throw H2O.unimpl();
// for(int i = 0; i < espc.length; ++i){
// if(rem <= espc[i]){
// espc = Arrays.copyOf(espc, i+1);
// break;
// }
// }
// espc[espc.length-1] = rem;
// Vec randVec = new Vec(fr.anyVec().group().addVecs(1)[0],espc);
// Futures fs = new Futures();
// DKV.put(randVec._key,randVec, fs);
// for(int i = 0; i < espc.length-1; ++i)
// DKV.put(randVec.chunkKey(i),new C0DChunk(0,(int)(espc[i+1]-espc[i])),fs);
// fs.blockForPending();
// new MRTask2() {
// @Override public void map(Chunk c){
// Random rng = new Random(seed*c.cidx());
// for(int i = 0; i < c._len; ++i)
// c.set0(i, (float)rng.nextDouble());
// }
// }.doAll(randVec);
// env.subRef(fr,skey);
// env.pop();
// env.push(new Frame(new String[]{"rnd"},new Vec[]{randVec}));
// }
//}
//class ASTSdev extends ASTOp {
// ASTSdev() { super(new String[]{"sd", "ary"}, new Type[]{Type.DBL,Type.ARY},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT); }
// @Override String opStr() { return "sd"; }
// @Override ASTOp make() { return new ASTSdev(); }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// Frame fr = env.peekAry();
// if (fr.vecs().length > 1)
// throw new IllegalArgumentException("sd does not apply to multiple cols.");
// if (fr.vecs()[0].isEnum())
// throw new IllegalArgumentException("sd only applies to numeric vector.");
// double sig = fr.vecs()[0].sigma();
// env.pop();
// env.poppush(sig);
// }
//}
//class ASTVar extends ASTOp {
// ASTVar() { super(new String[]{"var", "ary"}, new Type[]{Type.dblary(),Type.dblary()},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT); }
// @Override String opStr() { return "var"; }
// @Override ASTOp make() { return new ASTVar(); }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// if(env.isDbl()) {
// env.pop(2); env.push(Double.NaN);
// } else {
// Frame fr = env.ary(-1);
// String[] colnames = fr.names();
//
// // Save standard deviations for later use
// double[] sdev = new double[fr.numCols()];
// for(int i = 0; i < fr.numCols(); i++)
// sdev[i] = fr.vecs()[i].sigma();
//
// // TODO: Might be more efficient to modify DataInfo to allow for separate standardization of mean and std dev
// DataInfo dinfo = new DataInfo(fr, 0, true, DataInfo.TransformType.STANDARDIZE);
// GramTask tsk = new GramTask(null, dinfo, false, false).doAll(dinfo._adaptedFrame);
// double[][] var = tsk._gram.getXX();
// long nobs = tsk._nobs;
//
// assert sdev.length == var.length;
// assert sdev.length == var[0].length;
//
// // Just push the scalar if input is a single col
// if(var.length == 1 && var[0].length == 1) {
// env.pop(2);
// double x = var[0][0]*sdev[0]*sdev[0]; // Undo normalization of each col's standard deviation
// x = x*nobs/(nobs-1); // Divide by n-1 rather than n so unbiased
// env.push(x);
// } else {
// // Build output vecs for var-cov matrix
// Key keys[] = Vec.VectorGroup.VG_LEN1.addVecs(var.length);
// Vec[] vecs = new Vec[var.length];
// for(int i = 0; i < var.length; i++) {
// AppendableVec v = new AppendableVec(keys[i]);
// NewChunk c = new NewChunk(v,0);
// v._domain = null;
// for (int j = 0; j < var[0].length; j++) {
// double x = var[i][j]*sdev[i]*sdev[j]; // Undo normalization of each col's standard deviation
// x = x*nobs/(nobs-1); // Divide by n-1 rather than n so unbiased
// c.addNum(x);
// }
// c.close(0, null);
// vecs[i] = v.close(null);
// }
// env.pop(2); env.push(new Frame(colnames, vecs));
// }
// }
// }
//}
//class ASTMean extends ASTOp {
// ASTMean() { super(new String[]{"mean", "ary"}, new Type[]{Type.DBL,Type.ARY},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT); }
// @Override String opStr() { return "mean"; }
// @Override ASTOp make() { return new ASTMean(); }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// Frame fr = env.peekAry();
// if (fr.vecs().length > 1)
// throw new IllegalArgumentException("mean does not apply to multiple cols.");
// if (fr.vecs()[0].isEnum())
// throw new IllegalArgumentException("mean only applies to numeric vector.");
// double ave = fr.vecs()[0].mean();
// env.pop();
// env.poppush(ave);
// }
// @Override double[] map(Env env, double[] in, double[] out) {
// if (out == null || out.length < 1) out = new double[1];
// double s = 0; int cnt=0;
// for (double v : in) if( !Double.isNaN(v) ) { s+=v; cnt++; }
// out[0] = s/cnt;
// return out;
// }
//}
//
//class ASTXorSum extends ASTReducerOp { ASTXorSum() {super(0,false); }
// @Override String opStr(){ return "xorsum";}
// @Override ASTOp make() {return new ASTXorSum();}
// @Override double op(double d0, double d1) {
// long d0Bits = Double.doubleToLongBits(d0);
// long d1Bits = Double.doubleToLongBits(d1);
// long xorsumBits = d0Bits ^ d1Bits;
// // just need to not get inf or nan. If we zero the upper 4 bits, we won't
// final long ZERO_SOME_SIGN_EXP = 0x0fffffffffffffffL;
// xorsumBits = xorsumBits & ZERO_SOME_SIGN_EXP;
// double xorsum = Double.longBitsToDouble(xorsumBits);
// return xorsum;
// }
// @Override double[] map(Env env, double[] in, double[] out) {
// if (out == null || out.length < 1) out = new double[1];
// long xorsumBits = 0;
// long vBits;
// // for dp ieee 754 , sign and exp are the high 12 bits
// // We don't want infinity or nan, because h2o will return a string.
// double xorsum = 0;
// for (double v : in) {
// vBits = Double.doubleToLongBits(v);
// xorsumBits = xorsumBits ^ vBits;
// }
// // just need to not get inf or nan. If we zero the upper 4 bits, we won't
// final long ZERO_SOME_SIGN_EXP = 0x0fffffffffffffffL;
// xorsumBits = xorsumBits & ZERO_SOME_SIGN_EXP;
// xorsum = Double.longBitsToDouble(xorsumBits);
// out[0] = xorsum;
// return out;
// }
//}
//
//class ASTTable extends ASTOp {
// ASTTable() { super(new String[]{"table", "ary"}, new Type[]{Type.ARY,Type.ARY},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT); }
// @Override String opStr() { return "table"; }
// @Override ASTOp make() { return new ASTTable(); }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// int ncol;
// Frame fr = env.ary(-1);
// if ((ncol = fr.vecs().length) > 2)
// throw new IllegalArgumentException("table does not apply to more than two cols.");
// for (int i = 0; i < ncol; i++) if (!fr.vecs()[i].isInt())
// throw new IllegalArgumentException("table only applies to integer vectors.");
// String[][] domains = new String[ncol][]; // the domain names to display as row and col names
// // if vec does not have original domain, use levels returned by CollectDomain
// long[][] levels = new long[ncol][];
// for (int i = 0; i < ncol; i++) {
// Vec v = fr.vecs()[i];
// levels[i] = new Vec.CollectDomain(v).doAll(new Frame(v)).domain();
// domains[i] = v.domain();
// }
// long[][] counts = new Tabularize(levels).doAll(fr)._counts;
// // Build output vecs
// Key keys[] = Vec.VectorGroup.VG_LEN1.addVecs(counts.length+1);
// Vec[] vecs = new Vec[counts.length+1];
// String[] colnames = new String[counts.length+1];
// AppendableVec v0 = new AppendableVec(keys[0]);
// v0._domain = fr.vecs()[0].domain() == null ? null : fr.vecs()[0].domain().clone();
// NewChunk c0 = new NewChunk(v0,0);
// for( int i=0; i<levels[0].length; i++ ) c0.addNum((double) levels[0][i]);
// c0.close(0,null);
// vecs[0] = v0.close(null);
// colnames[0] = "row.names";
// if (ncol==1) colnames[1] = "Count";
// for (int level1=0; level1 < counts.length; level1++) {
// AppendableVec v = new AppendableVec(keys[level1+1]);
// NewChunk c = new NewChunk(v,0);
// v._domain = null;
// for (int level0=0; level0 < counts[level1].length; level0++)
// c.addNum((double) counts[level1][level0]);
// c.close(0, null);
// vecs[level1+1] = v.close(null);
// if (ncol>1) {
// colnames[level1+1] = domains[1]==null? Long.toString(levels[1][level1]) : domains[1][(int)(levels[1][level1])];
// }
// }
// env.pop(2);
// env.push(new Frame(colnames, vecs));
// }
// private static class Tabularize extends MRTask2<Tabularize> {
// public final long[][] _domains;
// public long[][] _counts;
//
// public Tabularize(long[][] dom) { super(); _domains=dom; }
// @Override public void map(Chunk[] cs) {
// assert cs.length == _domains.length;
// _counts = _domains.length==1? new long[1][] : new long[_domains[1].length][];
// for (int i=0; i < _counts.length; i++) _counts[i] = new long[_domains[0].length];
// for (int i=0; i < cs[0]._len; i++) {
// if (cs[0].isNA0(i)) continue;
// long ds[] = _domains[0];
// int level0 = Arrays.binarySearch(ds,cs[0].at80(i));
// assert 0 <= level0 && level0 < ds.length : "l0="+level0+", len0="+ds.length+", min="+ds[0]+", max="+ds[ds.length-1];
// int level1;
// if (cs.length>1) {
// if (cs[1].isNA0(i)) continue; else level1 = Arrays.binarySearch(_domains[1],(int)cs[1].at80(i));
// assert 0 <= level1 && level1 < _domains[1].length;
// } else {
// level1 = 0;
// }
// _counts[level1][level0]++;
// }
// }
// @Override public void reduce(Tabularize that) { Utils.add(_counts,that._counts); }
// }
//}
// Selective return. If the selector is a double, just eval both args and
// return the selected one. If the selector is an array, then it must be
// compatible with argument arrays (if any), and the selection is done
// element-by-element.
//class ASTIfElse extends ASTOp {
// static final String VARS[] = new String[]{"ifelse","tst","true","false"};
// static Type[] newsig() {
// Type t1 = Type.unbound(), t2 = Type.unbound(), t3=Type.unbound();
// return new Type[]{Type.anyary(new Type[]{t1,t2,t3}),t1,t2,t3};
// }
// ASTIfElse( ) { super(VARS, newsig(),OPF_INFIX,OPP_PREFIX,OPA_RIGHT); }
// @Override ASTOp make() {return new ASTIfElse();}
// @Override String opStr() { return "ifelse"; }
// // Parse an infix trinary ?: operator
//
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// // All or none are functions
// assert ( env.isFcn(-1) && env.isFcn(-2) && _t.ret().isFcn())
// || (!env.isFcn(-1) && !env.isFcn(-2) && !_t.ret().isFcn());
// // If the result is an array, then one of the other of the two must be an
// // array. , and this is a broadcast op.
// assert !_t.isAry() || env.isAry(-1) || env.isAry(-2);
//
// // Single selection? Then just pick slots
// if( !env.isAry(-3) ) {
// if( env.dbl(-3)==0 ) env.pop_into_stk(-4);
// else { env.pop(); env.pop_into_stk(-3); }
// return;
// }
//
// Frame frtst=null, frtru= null, frfal= null;
// double dtst= 0 , dtru= 0 , dfal= 0 ;
// if( env.isAry() ) frfal= env.popAry(); else dfal = env.popDbl(); String kf = env.key();
// if( env.isAry() ) frtru= env.popAry(); else dtru = env.popDbl(); String kt = env.key();
// if( env.isAry() ) frtst= env.popAry(); else dtst = env.popDbl(); String kq = env.key();
//
// // Multi-selection
// // Build a doAll frame
// Frame fr = new Frame(frtst); // Do-All frame
// final int ncols = frtst.numCols(); // Result column count
// final long nrows = frtst.numRows(); // Result row count
// String names[]=null;
// if( frtru !=null ) { // True is a Frame?
// if( frtru.numCols() != ncols || frtru.numRows() != nrows )
// throw new IllegalArgumentException("Arrays must be same size: "+frtst+" vs "+frtru);
// fr.add(frtru,true);
// names = frtru._names;
// }
// if( frfal !=null ) { // False is a Frame?
// if( frfal.numCols() != ncols || frfal.numRows() != nrows )
// throw new IllegalArgumentException("Arrays must be same size: "+frtst+" vs "+frfal);
// fr.add(frfal,true);
// names = frfal._names;
// }
// if( names==null && frtst!=null ) names = frtst._names;
// final boolean t = frtru != null;
// final boolean f = frfal != null;
// final double fdtru = dtru;
// final double fdfal = dfal;
//
// // Run a selection picking true/false across the frame
// Frame fr2 = new MRTask2() {
// @Override public void map( Chunk chks[], NewChunk nchks[] ) {
// for( int i=0; i<nchks.length; i++ ) {
// NewChunk n =nchks[i];
// int off=i;
// Chunk ctst= chks[off];
// Chunk ctru= t ? chks[off+=ncols] : null;
// Chunk cfal= f ? chks[off+=ncols] : null;
// int rlen = ctst._len;
// for( int r=0; r<rlen; r++ )
// if( ctst.isNA0(r) ) n.addNA();
// else n.addNum(ctst.at0(r)!=0 ? (t ? ctru.at0(r) : fdtru) : (f ? cfal.at0(r) : fdfal));
// }
// }
// }.doAll(ncols,fr).outputFrame(names,fr.domains());
// env.subRef(frtst,kq);
// if( frtru != null ) env.subRef(frtru,kt);
// if( frfal != null ) env.subRef(frfal,kf);
// env.pop();
// env.push(fr2);
// }
//}
//
//class ASTCut extends ASTOp {
// ASTCut() { super(new String[]{"cut", "ary", "dbls"},
// new Type[]{Type.ARY, Type.ARY, Type.dblary()},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT); }
// @Override String opStr() { return "cut"; }
// @Override ASTOp make() {return new ASTCut();}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// if(env.isDbl()) {
// final int nbins = (int) Math.floor(env.popDbl());
// if(nbins < 2)
// throw new IllegalArgumentException("Number of intervals must be at least 2");
//
// Frame fr = env.popAry();
// String skey = env.key();
// if(fr.vecs().length != 1 || fr.vecs()[0].isEnum())
// throw new IllegalArgumentException("First argument must be a numeric column vector");
//
// final double fmax = fr.vecs()[0].max();
// final double fmin = fr.vecs()[0].min();
// final double width = (fmax - fmin)/nbins;
// if(width == 0) throw new IllegalArgumentException("Data vector is constant!");
// // Note: I think R perturbs constant vecs slightly so it can still bin values
//
// // Construct domain names from bins intervals
// String[][] domains = new String[1][nbins];
// domains[0][0] = "(" + String.valueOf(fmin - 0.001*(fmax-fmin)) + "," + String.valueOf(fmin + width) + "]";
// for(int i = 1; i < nbins; i++)
// domains[0][i] = "(" + String.valueOf(fmin + i*width) + "," + String.valueOf(fmin + (i+1)*width) + "]";
//
// Frame fr2 = new MRTask2() {
// @Override public void map(Chunk chk, NewChunk nchk) {
// for(int r = 0; r < chk._len; r++) {
// double x = chk.at0(r);
// double n = x == fmax ? nbins-1 : Math.floor((x - fmin)/width);
// nchk.addNum(n);
// }
// }
// }.doAll(1,fr).outputFrame(fr._names, domains);
// env.subRef(fr, skey);
// env.pop();
// env.push(fr2);
// } else if(env.isAry()) {
// Frame ary = env.popAry();
// String skey1 = env.key();
// if(ary.vecs().length != 1 || ary.vecs()[0].isEnum())
// throw new IllegalArgumentException("Second argument must be a numeric column vector");
// Vec brks = ary.vecs()[0];
// // TODO: Check that num rows below some cutoff, else this will likely crash
//
// // Remove duplicates and sort vector of breaks in ascending order
// SortedSet<Double> temp = new TreeSet<Double>();
// for(int i = 0; i < brks.length(); i++) temp.add(brks.at(i));
// int cnt = 0; final double[] cutoffs = new double[temp.size()];
// for(Double x : temp) { cutoffs[cnt] = x; cnt++; }
//
// if(cutoffs.length < 2)
// throw new IllegalArgumentException("Vector of breaks must have at least 2 unique values");
// Frame fr = env.popAry();
// String skey2 = env.key();
// if(fr.vecs().length != 1 || fr.vecs()[0].isEnum())
// throw new IllegalArgumentException("First argument must be a numeric column vector");
//
// // Construct domain names from bin intervals
// final int nbins = cutoffs.length-1;
// String[][] domains = new String[1][nbins];
// for(int i = 0; i < nbins; i++)
// domains[0][i] = "(" + cutoffs[i] + "," + cutoffs[i+1] + "]";
//
// Frame fr2 = new MRTask2() {
// @Override public void map(Chunk chk, NewChunk nchk) {
// for(int r = 0; r < chk._len; r++) {
// double x = chk.at0(r);
// if(Double.isNaN(x) || x <= cutoffs[0] || x > cutoffs[cutoffs.length-1])
// nchk.addNum(Double.NaN);
// else {
// for(int i = 1; i < cutoffs.length; i++) {
// if(x <= cutoffs[i]) { nchk.addNum(i-1); break; }
// }
// }
// }
// }
// }.doAll(1,fr).outputFrame(fr._names, domains);
// env.subRef(ary, skey1);
// env.subRef(fr, skey2);
// env.pop();
// env.push(fr2);
// } else throw H2O.unimpl();
// }
//}
//class ASTfindInterval extends ASTOp {
// ASTfindInterval() { super(new String[]{"findInterval", "ary", "vec", "rightmost.closed"},
// new Type[]{Type.ARY, Type.ARY, Type.dblary(), Type.DBL},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT); }
// @Override String opStr() { return "findInterval"; }
// @Override ASTOp make() { return new ASTfindInterval(); }
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// final boolean rclosed = env.popDbl() == 0 ? false : true;
//
// if(env.isDbl()) {
// final double cutoff = env.popDbl();
//
// Frame fr = env.popAry();
// String skey = env.key();
// if(fr.vecs().length != 1 || fr.vecs()[0].isEnum())
// throw new IllegalArgumentException("First argument must be a numeric column vector");
//
// Frame fr2 = new MRTask2() {
// @Override public void map(Chunk chk, NewChunk nchk) {
// for(int r = 0; r < chk._len; r++) {
// double x = chk.at0(r);
// if(Double.isNaN(x))
// nchk.addNum(Double.NaN);
// else {
// if(rclosed)
// nchk.addNum(x > cutoff ? 1 : 0); // For rightmost.closed = TRUE
// else
// nchk.addNum(x >= cutoff ? 1 : 0);
// }
// }
// }
// }.doAll(1,fr).outputFrame(fr._names, fr.domains());
// env.subRef(fr, skey);
// env.pop();
// env.push(fr2);
// } else if(env.isAry()) {
// Frame ary = env.popAry();
// String skey1 = env.key();
// if(ary.vecs().length != 1 || ary.vecs()[0].isEnum())
// throw new IllegalArgumentException("Second argument must be a numeric column vector");
// Vec brks = ary.vecs()[0];
// // TODO: Check that num rows below some cutoff, else this will likely crash
//
// // Check if vector of cutoffs is sorted in weakly ascending order
// final int len = (int)brks.length();
// final double[] cutoffs = new double[len];
// for(int i = 0; i < len-1; i++) {
// if(brks.at(i) > brks.at(i+1))
// throw new IllegalArgumentException("Second argument must be sorted in non-decreasing order");
// cutoffs[i] = brks.at(i);
// }
// cutoffs[len-1] = brks.at(len-1);
//
// Frame fr = env.popAry();
// String skey2 = env.key();
// if(fr.vecs().length != 1 || fr.vecs()[0].isEnum())
// throw new IllegalArgumentException("First argument must be a numeric column vector");
//
// Frame fr2 = new MRTask2() {
// @Override public void map(Chunk chk, NewChunk nchk) {
// for(int r = 0; r < chk._len; r++) {
// double x = chk.at0(r);
// if(Double.isNaN(x))
// nchk.addNum(Double.NaN);
// else {
// double n = Arrays.binarySearch(cutoffs, x);
// if(n < 0) nchk.addNum(-n-1);
// else if(rclosed && n == len-1) nchk.addNum(n); // For rightmost.closed = TRUE
// else nchk.addNum(n+1);
// }
// }
// }
// }.doAll(1,fr).outputFrame(fr._names, fr.domains());
// env.subRef(ary, skey1);
// env.subRef(fr, skey2);
// env.pop();
// env.push(fr2);
// }
// }
//}
//
//class ASTFactor extends ASTOp {
// ASTFactor() { super(new String[]{"factor", "ary"},
// new Type[]{Type.ARY, Type.ARY},
// OPF_PREFIX,
// OPP_PREFIX,OPA_RIGHT); }
// @Override String opStr() { return "factor"; }
// @Override ASTOp make() {return new ASTFactor();}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// Frame ary = env.peekAry(); // Ary on top of stack, keeps +1 refcnt
// String skey = env.peekKey();
// if( ary.numCols() != 1 )
// throw new IllegalArgumentException("factor requires a single column");
// Vec v0 = ary.vecs()[0];
// Vec v1 = v0.isEnum() ? null : v0.toEnum();
// if (v1 != null) {
// ary = new Frame(ary._names,new Vec[]{v1});
// skey = null;
// }
// env.poppush(2, ary, skey);
// }
//}
//
//class ASTPrint extends ASTOp {
// static Type[] newsig() {
// Type t1 = Type.unbound();
// return new Type[]{t1, t1, Type.varargs(Type.unbound())};
// }
// ASTPrint() { super(new String[]{"print", "x", "y..."},
// newsig(),
// OPF_PREFIX,
// OPP_PREFIX,OPA_RIGHT); }
// @Override String opStr() { return "print"; }
// @Override ASTOp make() {return new ASTPrint();}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// for( int i=1; i<argcnt; i++ ) {
// if( env.isAry(i-argcnt) ) {
// env._sb.append(env.ary(i-argcnt).toStringAll());
// } else {
// env._sb.append(env.toString(env._sp+i-argcnt,true));
// }
// }
// env.pop(argcnt-2); // Pop most args
// env.pop_into_stk(-2); // Pop off fcn, returning 1st arg
// }
//}
//
///**
// * R 'ls' command.
// *
// * This method is purely for the console right now. Print stuff into the string buffer.
// * JSON response is not configured at all.
// */
//class ASTLs extends ASTOp {
// ASTLs() { super(new String[]{"ls"},
// new Type[]{Type.DBL},
// OPF_PREFIX,
// OPP_PREFIX,
// OPA_RIGHT); }
// @Override String opStr() { return "ls"; }
// @Override ASTOp make() {return new ASTLs();}
// @Override void apply(Env env, int argcnt, ASTApply apply) {
// for( Key key : H2O.KeySnapshot.globalSnapshot().keys())
// if( key.user_allowed() && H2O.get(key) != null )
// env._sb.append(key.toString());
// // Pop the self-function and push a zero.
// env.pop();
// env.push(0.0);
// }
//}