whole_func_string: function(expr, context, subIntegral) {
        var createIntegralWrapper = null;
  
        var uniqueParent = getParentOfUniqueVariable(expr);
        if(uniqueParent !== null && uniqueParent.type === "OperatorNode") {
          if(uniqueParent.op === '+' || uniqueParent.op === '-') {
            if(uniqueParent.args.length === 1) {
              // unary + or -
              createIntegralWrapper = function(int) {
                return new OperatorNode(uniqueParent.op, uniqueParent.fn, [int]);
              }
            } else {
              createIntegralWrapper = function(int) {
                return int;
              }
            }
          } else if(uniqueParent.op === '*') {
            createIntegralWrapper = function(int) {
              return new OperatorNode('/', 'divide', [int,
                // "remove" the variable of integration
                replaceNodeInTree(uniqueParent, context.variable, new ConstantNode(1))
              ]);
            };
          }
  
          if(createIntegralWrapper !== null) {
            var preIntegral = replaceNodeInTree(expr, uniqueParent, context.variable.clone());
            var int = subIntegral(preIntegral, context, "linear substitution");
            if(int) {
              var backSubstituted = replaceNodeInTree(int, context.variable, uniqueParent);
              return createIntegralWrapper(backSubstituted);
            }
          }
        }
  
        function replaceNodeInTree(expr, node, replacement) {
          return replaceHelper(expr);
  
          function replaceHelper(curNode) {
            return node.equals(curNode) ? replacement : curNode.map(replaceHelper);
          }
        }
  
        function getParentOfUniqueVariable(expr) {
          return helper(expr, null);
  
          function helper(expr, parent) {
            if(context.isConstant(expr)) {
              return null;
            } else if(expr.type === "SymbolNode" && expr.name === context.variable.name) {
              return parent;
            } else {
              var nonConstantChildren = [];
              expr.forEach(function(child) {
                if(!context.isConstant(child)) {
                  nonConstantChildren.push(child);
                }
              });
  
              if(nonConstantChildren.length === 1) {
                return helper(nonConstantChildren[0], expr);
              } else {
                return null;
              }
            }
          }
        }
      }
func_code_string: function(expr, context, subIntegral) {
        var createIntegralWrapper = null;
  
        var uniqueParent = getParentOfUniqueVariable(expr);
        if(uniqueParent !== null && uniqueParent.type === "OperatorNode") {
          if(uniqueParent.op === '+' || uniqueParent.op === '-') {
            if(uniqueParent.args.length === 1) {
              // unary + or -
              createIntegralWrapper = function(int) {
                return new OperatorNode(uniqueParent.op, uniqueParent.fn, [int]);
              }
            } else {
              createIntegralWrapper = function(int) {
                return int;
              }
            }
          } else if(uniqueParent.op === '*') {
            createIntegralWrapper = function(int) {
              return new OperatorNode('/', 'divide', [int,
                // "remove" the variable of integration
                replaceNodeInTree(uniqueParent, context.variable, new ConstantNode(1))
              ]);
            };
          }
  
          if(createIntegralWrapper !== null) {
            var preIntegral = replaceNodeInTree(expr, uniqueParent, context.variable.clone());
            var int = subIntegral(preIntegral, context, "linear substitution");
            if(int) {
              var backSubstituted = replaceNodeInTree(int, context.variable, uniqueParent);
              return createIntegralWrapper(backSubstituted);
            }
          }
        }
  
        function replaceNodeInTree(expr, node, replacement) {
          return replaceHelper(expr);
  
          function replaceHelper(curNode) {
            return node.equals(curNode) ? replacement : curNode.map(replaceHelper);
          }
        }
  
        function getParentOfUniqueVariable(expr) {
          return helper(expr, null);
  
          function helper(expr, parent) {
            if(context.isConstant(expr)) {
              return null;
            } else if(expr.type === "SymbolNode" && expr.name === context.variable.name) {
              return parent;
            } else {
              var nonConstantChildren = [];
              expr.forEach(function(child) {
                if(!context.isConstant(child)) {
                  nonConstantChildren.push(child);
                }
              });
  
              if(nonConstantChildren.length === 1) {
                return helper(nonConstantChildren[0], expr);
              } else {
                return null;
              }
            }
          }
        }
      }
func_documentation_string: integral(f(a*x + b), x) = 1/a * F(a*x + b) where F(u) = integral(f(u), u) We also only handle the case where u shows up only once in f(u)