/**
   * @param c
   * @param vars
   * @return
   */
  private static LiteralOp replaceLiteralValueTypeCastScalarRead(Hop c, LocalVariableMap vars) {
    LiteralOp ret = null;

    // as.double/as.integer/as.boolean over scalar read - literal replacement
    if (c instanceof UnaryOp
        && (((UnaryOp) c).getOp() == OpOp1.CAST_AS_DOUBLE
            || ((UnaryOp) c).getOp() == OpOp1.CAST_AS_INT
            || ((UnaryOp) c).getOp() == OpOp1.CAST_AS_BOOLEAN)
        && c.getInput().get(0) instanceof DataOp
        && c.getDataType() == DataType.SCALAR) {
      Data dat = vars.get(c.getInput().get(0).getName());
      if (dat != null) // required for selective constant propagation
      {
        ScalarObject sdat = (ScalarObject) dat;
        UnaryOp cast = (UnaryOp) c;
        switch (cast.getOp()) {
          case CAST_AS_INT:
            ret = new LiteralOp(sdat.getLongValue());
            break;
          case CAST_AS_DOUBLE:
            ret = new LiteralOp(sdat.getDoubleValue());
            break;
          case CAST_AS_BOOLEAN:
            ret = new LiteralOp(sdat.getBooleanValue());
            break;
          default:
            // otherwise: do nothing
        }
      }
    }

    return ret;
  }
  @Override
  public Object clone() throws CloneNotSupportedException {
    UnaryOp ret = new UnaryOp();

    // copy generic attributes
    ret.clone(this, false);

    // copy specific attributes
    ret._op = _op;

    return ret;
  }
  @Override
  public boolean compare(Hop that) {
    if (!(that instanceof UnaryOp)) return false;

    /*
     * NOTE:
     * This compare() method currently is invoked from Hops RewriteCommonSubexpressionElimination,
     * which tries to merge two hops if this function returns true. However, two PRINT hops should
     * never be merged, and hence returning false.
     *
     * If this method needs to be used elsewhere, then it must be refactored accordingly.
     */
    if (_op == OpOp1.PRINT) return false;

    UnaryOp that2 = (UnaryOp) that;
    return (_op == that2._op && getInput().get(0) == that2.getInput().get(0));
  }
  /**
   * @param c
   * @param vars
   * @return
   * @throws DMLRuntimeException
   */
  private static LiteralOp replaceLiteralValueTypeCastLiteral(Hop c, LocalVariableMap vars)
      throws DMLRuntimeException {
    LiteralOp ret = null;

    // as.double/as.integer/as.boolean over scalar literal (potentially created by other replacement
    // rewrite in same dag) - literal replacement
    if (c instanceof UnaryOp
        && (((UnaryOp) c).getOp() == OpOp1.CAST_AS_DOUBLE
            || ((UnaryOp) c).getOp() == OpOp1.CAST_AS_INT
            || ((UnaryOp) c).getOp() == OpOp1.CAST_AS_BOOLEAN)
        && c.getInput().get(0) instanceof LiteralOp) {
      LiteralOp sdat = (LiteralOp) c.getInput().get(0);
      UnaryOp cast = (UnaryOp) c;
      try {
        switch (cast.getOp()) {
          case CAST_AS_INT:
            long ival = HopRewriteUtils.getIntValue(sdat);
            ret = new LiteralOp(ival);
            break;
          case CAST_AS_DOUBLE:
            double dval = HopRewriteUtils.getDoubleValue(sdat);
            ret = new LiteralOp(dval);
            break;
          case CAST_AS_BOOLEAN:
            boolean bval = HopRewriteUtils.getBooleanValue(sdat);
            ret = new LiteralOp(bval);
            break;
          default:
            // otherwise: do nothing
        }
      } catch (HopsException ex) {
        throw new DMLRuntimeException(ex);
      }
    }

    return ret;
  }