package water.rapids.ast.prims.operators;

import water.MRTask;
import water.fvec.Chunk;
import water.fvec.Frame;
import water.fvec.NewChunk;
import water.fvec.Vec;
import water.parser.BufferedString;
import water.rapids.vals.ValFrame;
import water.util.MathUtils;
import water.util.StringUtils;

/**
 */
public class AstNe extends AstBinOp {
  public String str() {
    return "!=";
  }

  public double op(double l, double r) {
    return MathUtils.equalsWithinOneSmallUlp(l, r) ? 0 : 1;
  }

  @Override
  public ValFrame frame_op_scalar(Frame fr, final double d) {
    return new ValFrame(new MRTask() {
      @Override
      public void map(Chunk[] chks, NewChunk[] cress) {
        for (int c = 0; c < chks.length; c++) {
          Chunk chk = chks[c];
          NewChunk cres = cress[c];
          BufferedString bStr = new BufferedString();
          if (chk.vec().isString())
            for (int i = 0; i < chk._len; i++)
              cres.addNum(str_op(chk.atStr(bStr, i), Double.isNaN(d) ? null : new BufferedString(String.valueOf(d))));
          else if (!chk.vec().isNumeric()) cres.addZeros(chk._len);
          else
            for (int i = 0; i < chk._len; i++)
              cres.addNum(op(chk.atd(i), d));
        }
      }
    }.doAll(fr.numCols(), Vec.T_NUM, fr).outputFrame());
  }

  @Override
  public boolean categoricalOK() {
    return true;
  }  // Make sense to run this OP on an enm?

  public double str_op(BufferedString l, BufferedString r) {
    if (StringUtils.isNullOrEmpty(l))
      return StringUtils.isNullOrEmpty(r) ? 0 : 1;
    else
      return l.equals(r) ? 0 : 1;
  }
}
