Prepare the given expression for integration by
- Remove parentheses
- Reduce standard functions into more generic structures:
- Convert 'add', 'subtract', 'multiply', and 'divide' into their operator counterparts
- Convert 'sqrt', 'nthRoot', 'exp', and 'pow' into '^' operator
- Convert 2-arg 'log' into quotient of natrual logarithms
- Convert division into multiplication to power of -1 (only in non-constant nodes)

@param {Node} expr  The expression tree representing the integrand to be processed
@param {Object} context  The integration context to interpret the integrand
@return {Node}  The processed integrand
function preprocessIntegrand(expr, context) {
    expr = removeParenthesis(expr);
    expr = reduceFunctions(expr);
    expr = removeDivision(expr);
    return expr;

    function removeParenthesis(node) {
      if(node.type === "ParenthesisNode") {
        return removeParenthesis(node.content);
      } else {
        return node.map(removeParenthesis);
      }
    }

    function reduceFunctions(expr) {
      return helper(expr);

      function helper(expr) {
        if(!context.isConstant(expr) && expr.type === "FunctionNode") {
          var funcName = typeof expr.fn === "string" ? expr.fn : expr.fn.name;

          switch(funcName) {
            case "add":
              return new OperatorNode('+', 'add', expr.args);
            case "subtract":
              return new OperatorNode('-', 'subtract', expr.args);
            case "multiply":
              return new OperatorNode('*', 'multiply', expr.args);
            case "divide":
              return new OperatorNode('/', 'divide', expr.args);
            case "sqrt":
              return new OperatorNode('^', 'pow', [
                expr.args[0].map(helper),
                new OperatorNode('/', 'divide', [
                  new ConstantNode(1),
                  new ConstantNode(2)
                ])
              ]);
            case "nthRoot":
              return new OperatorNode('^', 'pow', [
                expr.args[0].map(helper),
                new OperatorNode('/', 'divide', [
                  new ConstantNode(1),
                  expr.args[1].map(helper)
                ])
              ]);
            case "exp":
              return new OperatorNode('^', 'pow', [new SymbolNode('e'), expr.args[0]]);
            case "pow":
              return new OperatorNode('^', 'pow', expr.args);
            case "log":
              if(expr.args.length === 2) {
                return new OperatorNode('/', 'divide', [
                  new FunctionNode('log', [expr.args[0].map(helper)]),
                  new FunctionNode('log', [expr.args[1].map(helper)])
                ]);
              } else {
                break;
              }
            default:
              break;
          }
        }
        return expr.map(helper);
      }
    }

    function removeDivision(expr) {
      return expr.transform(function(node) {
        if(!context.isConstant(node) && node.type === 'OperatorNode' && node.op === '/') {
          return new OperatorNode('*', 'multiply', [
            node.args[0],
            new OperatorNode('^', 'pow', [
              node.args[1],
              new ConstantNode(-1)
            ])
          ]);
        } else {
          return node;
        }
      });
    }
  }