@Override
  public String toOperatorString(CalcFunction function) {
    int precedence = getPrecedence();
    char operatorChar = '+';
    StringBuffer buffer = new StringBuffer();
    CalcObject temp;

    for (int ii = 0; ii < function.size(); ii++) {
      temp = function.get(ii);

      if (temp.getHeader().equals(CALC.MULTIPLY)
          && ((CalcFunction) temp).get(0).compareTo(CALC.ZERO) < 0) {
        // special case -> negative number
        buffer.append(temp);
        continue;
      } else {
        if (ii > 0) {
          buffer.append(operatorChar);
        }

        if (temp.getPrecedence() < precedence) {
          buffer.append('(');
        }

        buffer.append(temp.toString());

        if (temp.getPrecedence() < precedence) {
          buffer.append(')');
        }
      }
    }

    return buffer.toString();
  }
 @Override
 public CalcObject evaluate(CalcFunction input) {
   if (input.size() == 1) {
     CalcObject parameter = input.get(0);
     // parameter = CALC.SYM_EVAL(parameter);
     CalcObject returnVal = evaluateObject(parameter);
     if (returnVal != null) return returnVal;
     else if (parameter instanceof CalcInteger) return evaluateInteger((CalcInteger) parameter);
     else if (parameter instanceof CalcDouble) return evaluateDouble((CalcDouble) parameter);
     else if (parameter instanceof CalcFraction) return evaluateFraction((CalcFraction) parameter);
     else if (parameter instanceof CalcSymbol) return evaluateSymbol((CalcSymbol) parameter);
     else if (parameter instanceof CalcFunction) return evaluateFunction((CalcFunction) parameter);
     else return input;
   } else
     return null; // function has more than 1 parameter. This evaluator does not apply so return
                  // null
 }
  @Override
  protected CalcObject evaluateObject(CalcObject input1, CalcObject input2) {
    // optimization cases
    if (input1.equals(CALC.ZERO)) {
      return input2;
    } else if (input2.equals(CALC.ZERO)) {
      return input1;
    } else if (input1.equals(input2)) {
      return CALC.MULTIPLY.createFunction(CALC.TWO, input1);
    }
    // else if (input1 instanceof CalcSymbol || input2 instanceof CalcSymbol) {
    //	return CALC.ADD.createFunction(input1, input2); //if either input is a symbol, can't
    // evaluate..return original
    // }
    // end optimization cases
    // simplifiable cases
    else if (input1.getHeader().equals(CALC.MULTIPLY) && ((CalcFunction) input1).size() > 1) {
      CalcFunction function1 = (CalcFunction) input1;

      if (function1.get(0).isNumber()) {
        if (function1.size() == 2 && function1.get(1).equals(input2)) {
          return CALC.MULTIPLY.createFunction(
              CALC.ADD.createFunction(CALC.ONE, function1.get(0)), input2);
        } else if (input2.getHeader().equals(CALC.MULTIPLY) && ((CalcFunction) input2).size() > 1) {
          CalcFunction function2 = (CalcFunction) input2;

          if (function2.get(0).isNumber()) {
            if (function1.equalsFromIndex(1, function2, 1)) {
              CalcFunction result = new CalcFunction(CALC.MULTIPLY, function1, 1, function1.size());
              return CALC.MULTIPLY.createFunction(
                  CALC.ADD.createFunction(function1.get(0), function2.get(0)), result);
            }
          } else {
            if (function1.equalsFromIndex(1, function2, 0)) {
              CalcFunction result = new CalcFunction(CALC.MULTIPLY, function1, 1, function1.size());
              return CALC.MULTIPLY.createFunction(
                  CALC.ADD.createFunction(CALC.ONE, function1.get(0)), result);
            }
          }
        }
      } else {
        if (input2.getHeader().equals(CALC.MULTIPLY) && (((CalcFunction) input2).size() > 1)) {
          CalcFunction function2 = (CalcFunction) input2;

          if (function2.get(0).isNumber()) {
            if (function1.equalsFromIndex(0, function2, 1)) {
              CalcFunction result = new CalcFunction(CALC.MULTIPLY, function2, 1, function2.size());

              return CALC.MULTIPLY.createFunction(
                  CALC.ADD.createFunction(CALC.ONE, function2.get(0)), result);
            }
          }
        }
      }
    }

    if (input2.getHeader().equals(CALC.MULTIPLY)
        && (((CalcFunction) input2).size() > 1)
        && ((CalcFunction) input2).get(0).isNumber()) {
      CalcFunction function2 = (CalcFunction) input2;

      if ((function2.size() == 2) && function2.get(1).equals(input1)) {
        return CALC.MULTIPLY.createFunction(
            CALC.ADD.createFunction(CALC.ONE, function2.get(0)), input1);
      }
    }

    return null;
  }