whole_func_string: 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;
          }
        });
      }
    }
func_code_string: 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;
          }
        });
      }
    }
func_documentation_string: 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