public static MRInstruction parseSingleInstruction(String str)
      throws DMLUnsupportedOperationException, DMLRuntimeException {
    if (str == null || str.isEmpty()) return null;

    MRINSTRUCTION_TYPE mrtype = InstructionUtils.getMRType(str);
    return parseSingleInstruction(mrtype, str);
  }
  public static CPInstruction parseSingleInstruction(String str)
      throws DMLUnsupportedOperationException, DMLRuntimeException {
    if (str == null || str.isEmpty()) return null;

    CPINSTRUCTION_TYPE cptype = InstructionUtils.getCPType(str);
    if (cptype == null)
      throw new DMLRuntimeException("Unable derive cptype for instruction: " + str);
    CPInstruction cpinst = parseSingleInstruction(cptype, str);
    if (cpinst == null) throw new DMLRuntimeException("Unable to parse instruction: " + str);
    return cpinst;
  }
  public static MRInstruction[] parseCombineInstructions(String str)
      throws DMLUnsupportedOperationException, DMLRuntimeException {
    MRInstruction[] inst = null;
    if (str != null && !str.isEmpty()) {
      String[] strlist = str.split(Instruction.INSTRUCTION_DELIM);
      inst = new MRInstruction[strlist.length];

      for (int i = 0; i < strlist.length; i++) {
        MRINSTRUCTION_TYPE type = InstructionUtils.getMRType(strlist[i]);
        if (type == MRINSTRUCTION_TYPE.CombineBinary)
          inst[i] =
              (CombineBinaryInstruction) CombineBinaryInstruction.parseInstruction(strlist[i]);
        else if (type == MRINSTRUCTION_TYPE.CombineTernary)
          inst[i] =
              (CombineTernaryInstruction) CombineTernaryInstruction.parseInstruction(strlist[i]);
        else throw new DMLRuntimeException("unknown combine instruction: " + strlist[i]);
      }
    }
    return inst;
  }
  public static MRInstruction parseSingleInstruction(MRINSTRUCTION_TYPE mrtype, String str)
      throws DMLUnsupportedOperationException, DMLRuntimeException {
    if (str == null || str.isEmpty()) return null;

    switch (mrtype) {
      case Aggregate:
        return AggregateInstruction.parseInstruction(str);

      case ArithmeticBinary:
        {
          String opcode = InstructionUtils.getOpCode(str);
          String[] parts = InstructionUtils.getInstructionPartsWithValueType(str);
          // extract datatypes of first and second input operands
          String dt1 =
              parts[1].split(Instruction.DATATYPE_PREFIX)[1].split(Instruction.VALUETYPE_PREFIX)[0];
          String dt2 =
              parts[2].split(Instruction.DATATYPE_PREFIX)[1].split(Instruction.VALUETYPE_PREFIX)[0];
          if (dt1.equalsIgnoreCase("SCALAR") || dt2.equalsIgnoreCase("SCALAR")) {
            return ScalarInstruction.parseInstruction(str);
          } else {
            if (BinaryM.isOpcode(opcode)) return BinaryMInstruction.parseInstruction(str);
            else return BinaryInstruction.parseInstruction(str);
          }
        }

      case AggregateBinary:
        return AggregateBinaryInstruction.parseInstruction(str);

      case AggregateUnary:
        return AggregateUnaryInstruction.parseInstruction(str);

      case Ternary:
        return TernaryInstruction.parseInstruction(str);

      case Quaternary:
        return QuaternaryInstruction.parseInstruction(str);

      case Rand:
        return RandInstruction.parseInstruction(str);

      case Seq:
        return SeqInstruction.parseInstruction(str);

      case Reblock:
        return ReblockInstruction.parseInstruction(str);

      case Append:
        return AppendInstruction.parseInstruction(str);

      case Reorg:
        return ReorgInstruction.parseInstruction(str);

      case Replicate:
        return ReplicateInstruction.parseInstruction(str);

      case Unary:
        {
          String opcode = InstructionUtils.getOpCode(str);
          String[] parts = InstructionUtils.getInstructionPartsWithValueType(str);
          if (parts.length == 4
              && (opcode.equalsIgnoreCase("log") || opcode.equalsIgnoreCase("log_nz")))
            return ScalarInstruction.parseInstruction(str);
          else // default case
          return UnaryInstruction.parseInstruction(str);
        }
      case MMTSJ:
        return MMTSJMRInstruction.parseInstruction(str);

      case PMMJ:
        return PMMJMRInstruction.parseInstruction(str);

      case MapMultChain:
        return MapMultChainInstruction.parseInstruction(str);

      case BinUaggChain:
        return BinUaggChainInstruction.parseInstruction(str);

      case UaggOuterChain:
        return UaggOuterChainInstruction.parseInstruction(str);

      case CombineTernary:
        return CombineTernaryInstruction.parseInstruction(str);

      case CombineBinary:
        return CombineBinaryInstruction.parseInstruction(str);

      case CombineUnary:
        return CombineUnaryInstruction.parseInstruction(str);

      case PickByCount:
        return PickByCountInstruction.parseInstruction(str);

      case CM_N_COV:
        return CM_N_COVInstruction.parseInstruction(str);

      case GroupedAggregate:
        return GroupedAggregateInstruction.parseInstruction(str);

      case MapGroupedAggregate:
        return GroupedAggregateMInstruction.parseInstruction(str);

      case RangeReIndex:
        return RangeBasedReIndexInstruction.parseInstruction(str);

      case ZeroOut:
        return ZeroOutInstruction.parseInstruction(str);

      case MatrixReshape:
        return MatrixReshapeMRInstruction.parseInstruction(str);

      case Sort: // workaround for dummy MR sort instruction
        return SortMR.parseSortInstruction(str);

      case CSVReblock:
        return CSVReblockInstruction.parseInstruction(str);

      case CSVWrite:
        return CSVWriteInstruction.parseInstruction(str);

      case ParameterizedBuiltin:
        return ParameterizedBuiltinMRInstruction.parseInstruction(str);

      case RemoveEmpty:
        return RemoveEmptyMRInstruction.parseInstruction(str);

      case Partition:
        return DataPartitionMRInstruction.parseInstruction(str);

      case CumsumAggregate:
        return CumulativeAggregateInstruction.parseInstruction(str);

      case CumsumSplit:
        return CumulativeSplitInstruction.parseInstruction(str);

      case CumsumOffset:
        return CumulativeOffsetInstruction.parseInstruction(str);

      case INVALID:

      default:
        throw new DMLRuntimeException("Invalid MR Instruction Type: " + mrtype);
    }
  }
  public static CPInstruction parseSingleInstruction(CPINSTRUCTION_TYPE cptype, String str)
      throws DMLUnsupportedOperationException, DMLRuntimeException {
    ExecType execType = null;

    if (str == null || str.isEmpty()) return null;

    switch (cptype) {
      case AggregateUnary:
        return AggregateUnaryCPInstruction.parseInstruction(str);

      case AggregateBinary:
        return AggregateBinaryCPInstruction.parseInstruction(str);

      case AggregateTernary:
        return AggregateTernaryCPInstruction.parseInstruction(str);

      case ArithmeticBinary:
        return ArithmeticBinaryCPInstruction.parseInstruction(str);

      case Ternary:
        return TernaryCPInstruction.parseInstruction(str);

      case Quaternary:
        return QuaternaryCPInstruction.parseInstruction(str);

      case BooleanBinary:
        return BooleanBinaryCPInstruction.parseInstruction(str);

      case BooleanUnary:
        return BooleanUnaryCPInstruction.parseInstruction(str);

      case BuiltinBinary:
        return BuiltinBinaryCPInstruction.parseInstruction(str);

      case BuiltinUnary:
        return BuiltinUnaryCPInstruction.parseInstruction(str);

      case Reorg:
        return ReorgCPInstruction.parseInstruction(str);

      case UaggOuterChain:
        return UaggOuterChainCPInstruction.parseInstruction(str);

      case MatrixReshape:
        return MatrixReshapeCPInstruction.parseInstruction(str);

      case Append:
        return AppendCPInstruction.parseInstruction(str);

      case RelationalBinary:
        return RelationalBinaryCPInstruction.parseInstruction(str);

      case File:
        return FileCPInstruction.parseInstruction(str);

      case Variable:
        return VariableCPInstruction.parseInstruction(str);

      case Rand:
        return DataGenCPInstruction.parseInstruction(str);

      case StringInit:
        return StringInitCPInstruction.parseInstruction(str);

      case External:
        return FunctionCallCPInstruction.parseInstruction(str);

      case ParameterizedBuiltin:
        execType = ExecType.valueOf(str.split(Instruction.OPERAND_DELIM)[0]);
        if (execType == ExecType.CP) return ParameterizedBuiltinCPInstruction.parseInstruction(str);
        else // exectype CP_FILE
        return ParameterizedBuiltinCPFileInstruction.parseInstruction(str);

      case MultiReturnBuiltin:
        return MultiReturnBuiltinCPInstruction.parseInstruction(str);

      case QSort:
        return QuantileSortCPInstruction.parseInstruction(str);

      case QPick:
        return QuantilePickCPInstruction.parseInstruction(str);

      case MatrixIndexing:
        execType = ExecType.valueOf(str.split(Instruction.OPERAND_DELIM)[0]);
        if (execType == ExecType.CP) return MatrixIndexingCPInstruction.parseInstruction(str);
        else // exectype CP_FILE
        return MatrixIndexingCPFileInstruction.parseInstruction(str);

      case Builtin:
        String[] parts = InstructionUtils.getInstructionPartsWithValueType(str);
        if (parts[0].equals("log") || parts[0].equals("log_nz")) {
          if (parts.length == 3) {
            // B=log(A), y=log(x)
            return BuiltinUnaryCPInstruction.parseInstruction(str);
          } else if (parts.length == 4) {
            // B=log(A,10), y=log(x,10)
            return BuiltinBinaryCPInstruction.parseInstruction(str);
          }
        } else {
          throw new DMLRuntimeException("Invalid Builtin Instruction: " + str);
        }
      case MMTSJ:
        return MMTSJCPInstruction.parseInstruction(str);

      case PMMJ:
        return PMMJCPInstruction.parseInstruction(str);

      case MMChain:
        return MMChainCPInstruction.parseInstruction(str);

      case Partition:
        return DataPartitionCPInstruction.parseInstruction(str);

      case CentralMoment:
        return CentralMomentCPInstruction.parseInstruction(str);

      case Covariance:
        return CovarianceCPInstruction.parseInstruction(str);

      case INVALID:

      default:
        throw new DMLRuntimeException("Invalid CP Instruction Type: " + cptype);
    }
  }