function(expr, context, subIntegral) {
      if(expr.type === "OperatorNode" && expr.op === '*') {
        var argsAsPower = expr.args.map(getExprInPowerForm);

        // Collect common bases (this is O(n^2) worst case)
        var reducedArgs = argsAsPower.reduce(function(acc, exprPower) {
          for(var i = 0; i < acc.length; i++) {
            if(acc[i].base.equals(exprPower.base)) {
              acc[i].power = new OperatorNode('+', 'add', [
                acc[i].power,
                exprPower.power
              ]);
              return acc;
            }
          }

          // Did not share a common base with any other factor so far
          acc.push(exprPower);
          return acc;
        }, []);

        if(reducedArgs.length < expr.args.length) {
          // We combined some things
          var reducedExpr = powerFactorsToNode(reducedArgs);

          return subIntegral(reducedExpr, context, "combining powers");
        }
      }

      function getExprInPowerForm(expr) {
        if(expr.type === "OperatorNode" && expr.op === '^') {
          return {
            base: expr.args[0],
            power: expr.args[1]
          };
        } else {
          return {
            base: expr,
            power: new ConstantNode(1)
          };
        }
      }

      function powerFactorsToNode(factors) {
        if(factors.length === 1) {
          return powerToNode(factors[0]);
        } else {
          return new OperatorNode('*', 'multiply', factors.map(powerToNode));
        }

        function powerToNode(powerExpr) {
          return new OperatorNode('^', 'pow', [powerExpr.base, powerExpr.power]);
        }
      }
    }
function(expr, context, subIntegral) {
      if(expr.type === "OperatorNode" && expr.op === '*') {
        var argsAsPower = expr.args.map(getExprInPowerForm);

        
        var reducedArgs = argsAsPower.reduce(function(acc, exprPower) {
          for(var i = 0; i < acc.length; i++) {
            if(acc[i].base.equals(exprPower.base)) {
              acc[i].power = new OperatorNode('+', 'add', [
                acc[i].power,
                exprPower.power
              ]);
              return acc;
            }
          }

          
          acc.push(exprPower);
          return acc;
        }, []);

        if(reducedArgs.length < expr.args.length) {
          
          var reducedExpr = powerFactorsToNode(reducedArgs);

          return subIntegral(reducedExpr, context, "combining powers");
        }
      }

      function getExprInPowerForm(expr) {
        if(expr.type === "OperatorNode" && expr.op === '^') {
          return {
            base: expr.args[0],
            power: expr.args[1]
          };
        } else {
          return {
            base: expr,
            power: new ConstantNode(1)
          };
        }
      }

      function powerFactorsToNode(factors) {
        if(factors.length === 1) {
          return powerToNode(factors[0]);
        } else {
          return new OperatorNode('*', 'multiply', factors.map(powerToNode));
        }

        function powerToNode(powerExpr) {
          return new OperatorNode('^', 'pow', [powerExpr.base, powerExpr.power]);
        }
      }
    }
function ( expr , context , subIntegral ) { if ( expr . type === "OperatorNode" && expr . op === '*' ) { var argsAsPower = expr . args . map ( getExprInPowerForm ) ; // Collect common bases (this is O(n^2) worst case) var reducedArgs = argsAsPower . reduce ( function ( acc , exprPower ) { for ( var i = 0 ; i < acc . length ; i ++ ) { if ( acc [ i ] . base . equals ( exprPower . base ) ) { acc [ i ] . power = new OperatorNode ( '+' , 'add' , [ acc [ i ] . power , exprPower . power ] ) ; return acc ; } } // Did not share a common base with any other factor so far acc . push ( exprPower ) ; return acc ; } , [ ] ) ; if ( reducedArgs . length < expr . args . length ) { // We combined some things var reducedExpr = powerFactorsToNode ( reducedArgs ) ; return subIntegral ( reducedExpr , context , "combining powers" ) ; } } function getExprInPowerForm ( expr ) { if ( expr . type === "OperatorNode" && expr . op === '^' ) { return { base : expr . args [ 0 ] , power : expr . args [ 1 ] } ; } else { return { base : expr , power : new ConstantNode ( 1 ) } ; } } function powerFactorsToNode ( factors ) { if ( factors . length === 1 ) { return powerToNode ( factors [ 0 ] ) ; } else { return new OperatorNode ( '*' , 'multiply' , factors . map ( powerToNode ) ) ; } function powerToNode ( powerExpr ) { return new OperatorNode ( '^' , 'pow' , [ powerExpr . base , powerExpr . power ] ) ; } } }
integral(f(x)^g(x) * f(x)^h(x), x) = integral(f(x)^(g(x)+h(x)), x)
integral ( f ( x ) ^g ( x ) * f ( x ) ^h ( x ) x ) = integral ( f ( x ) ^ ( g ( x ) + h ( x )) x )