//Version 0.0, April 10, 2006, (c) jason howald (jason.howald@gmail.com)
//License: GNU General Public License (http://www.gnu.org/copyleft/gpl.html)

expression.grammar = new grammar();
var g = new grammar(); //namespace pollution?
expression.ruleToRuleName = {
    //some of these names are pretty awful.
    derivative_constant : "Constant Rule",
    derivative_variable : "Simple Variable Rule",
    derivative_group : "Parenthesis Rule",
    derivative_functionRule : "Simple Function Rule",
    derivative_chain : "Chain Rule",
    derivative_unaryMinus : "Unary Minus Rule",
    derivative_plusMinusGroup : "Sum and Difference Rules",
    derivative_product:"Product Rule",
    derivative_constantMultiple: "Constant Multiple Rule",
    derivative_quotient: "Quotient Rule",
    derivative_quotientNegativeExponent: "Quotient Rule Avoidance",
    derivative_constantDivisor: "Constant Divisor Rule",
    derivative_power: "Power Rule",
    derivative_exponent: "Exponent Rule",
    derivative_functionalExponentiation: "Functional Exponentiation Trick"    
}

function expression(inputtype,inputname) { 
    this.type = inputtype;
    this.name = inputname;
    //this.right;//unnecessary to declare
    //this.left;
    //this.parent;
    this.meta = {badSyntax:false};
    var g = expression.grammar;
    //alert(this.meta.prop);
}

expression.newFromAscii = function(str) {
    var ascii;
    if ((str == undefined) || (str.length == 0)) {
        ascii = "0";
    } else {
        ascii = str.replace(/\s*/g,'');//strip white space
    }
    var active = new expression();
    //active.meta.badSyntax = false;//unnecessary -- set in constructor
    active.meta.userAscii = ascii;
    for (var i=0;i<ascii.length;/*variable advance at end*/) {
        var symbol = g.getSymbol(ascii.substr(i));
        if (symbol.type != g.Nonsense) {
            active = active.treeInsert(symbol.type,symbol.name);
        } else {
            active.syntaxError(symbol.name);
        }
        //alert(active.ancestryString());
        i += symbol.name.length;
    }
    while (active.parent != undefined) {
        if ((active.pending()) || (active.lacksClosure())) {
            active.syntaxError("");
        }
        active = active.parent;
    }
    return active.stripOpenGroup();
}    

var randomModel = function(blankModel) {
    var M = {};
    for (x in blankModel) {
        var z = Math.PI-.01;
        M[x] = Math.tan((Math.random()-.5)*z);
        //alert(M[x]);
    }
    return M;
}

expression.prototype.detrivialize = function() {
    //remove silly operations like *1, +0, *0, etc.  Also remove double parenthesizations (())
    //first detrivialize children, so we can work bottom up.
    if (this.left !== undefined) {
    this.left.detrivialize();
    }
    if (this.right !== undefined) {
    this.right.detrivialize();
    }    
    if ((this.type == g.Funktion) || (this.type == g.OpenGroup)) {
        if (this.left.type == g.OpenGroup) {
            //double parenthesization (()) or sin(())  drop the inner ones.
            this.adoptLeftChild(this.left.left);
            return this;
        } else if ((this.left.type == g.Variable) || (this.left.type == g.Constant) || (this.left.type == g.Numb)) {
            if (this.type == g.OpenGroup) {
                //replace (x) with x, (1) with 1, (pi) with pi.
                this.left.replace(this);
                return this;
            }
        }
        
    }
    if (this.type == g.Operation) {
    if (this.name.match(/\+/)) {
        if ((this.right.type == g.Numb) && (this.right.name == 0)) {
            this.left.replace(this);
        return this.left;
        } else if ((this.left.type == g.Numb) && (this.left.name == 0)) {
        this.right.replace(this);
        return this.right;
        }
    } else if (this.name.match(/\-/)) {
        if ((this.right.type == g.Numb) && (this.right.name == 0)) {
            this.left.replace(this);
        return this.left;
        }
    } else if (this.name.match(/\//)) {
        if ((this.right.type == g.Numb) && (this.right.name == 1)) {
            this.left.replace(this);
        return this.left;
        }
        if ((this.left.type == g.Numb) && (this.left.name == 0)) {
        var zero = expression.newFromAscii("0");
            zero.replace(this);
        return zero;
        }
    } else if (this.name.match(/\*/)) {
        if        ((this.right.type == g.Numb) && (this.right.name == 1)) {
            this.left.replace(this);
        return this.left;
        } else if ((this.left.type == g.Numb) && (this.left.name == 1)) {
            this.right.replace(this);
        return this.right;
        } else if ((this.right.type == g.Numb) && (this.right.name == 0)) {
        var zero = expression.newFromAscii("0");
            zero.replace(this);
        return zero;
        } else if ((this.left.type == g.Numb) && (this.left.name == 0)) {
        var zero = expression.newFromAscii("0");
            zero.replace(this);
        return zero;
        }
    } else if (this.name.match(/\^/)) {
        if        ((this.right.type == g.Numb) && (this.right.name == 1)) {
            this.left.replace(this);
        return this.left;
        } else if ((this.left.type == g.Numb) && (this.left.name == 1)) {
        var one = expression.newFromAscii("1");
            one.replace(this);
        return one;
        } else if ((this.right.type == g.Numb) && (this.right.name == 0)) {
        //catches 0^0 case
        var one = expression.newFromAscii("1");
            one.replace(this);
        return one;
        } else if ((this.left.type == g.Numb) && (this.left.name == 0)) {
        //doesn't catch 0^0 case, because that's caught above.
        var zero = expression.newFromAscii("0");
            zero.replace(this);
        return zero;
        }
    }
    } 
    return this;
}



    expression.prototype.consistentModel = function() {
        var Ans;
        for (var patience = 77;patience >0;patience ++) {
            var M = randomModel(this.freeVariables());
            Ans = this.evaluateAt(M);
            if (!isNaN(Ans)) { //if it evaluates to a number...
                return M;
            }    
        }
        return false;
    }
    
    expression.prototype.semanticEquiv = function(that) {
        var skepticism = 1; //very low. Number of models to accept equality.
        var M;
        var thisvalue;
        var thatvalue;
        var jointModelCounter = 0;
        var thisfree = this.freeVariables();
        var thatfree = that.freeVariables();
        for (x in thisfree) {
            if (thatfree[x] == undefined) {
                return false;
            }
        }
        for (x in thatfree) {
            if (thisfree[x] == undefined) {
                return false;
            }
        }
        for (var patience = 17;patience >0;patience--) {
            var M = this.consistentModel();
            if (!!M) {
                thatvalue = that.evaluateAt(M);
                thisvalue = this.evaluateAt(M);
                if (!isNaN(thatvalue)) {
                    jointModelCounter ++;
                    var error = thisvalue - thatvalue;
                    if ((error < -g.epsilon) || (error > g.epsilon)) {
                        return false;
                    }
                }
            }
        }
        if (jointModelCounter >= skepticism) {
            return true;
        } else {
            return "maybe";
        }
    }

//(rest of code is unnecessarily indented)
    
    //expression.prototype.name = function() {return name;}
    //expression.prototype.type = function() {return type;}
        
    expression.prototype.applyOp = function(op,that) {
        var answer = new expression(g.Operation,op);
        var cthis = this.clone();
        var cthat = that.clone();
        answer.adoptLeftChild(cthis);
        answer.adoptRightChild(cthat);
        return answer;
    }
    
    expression.prototype.plus  = function(that) {return this.applyOp("+",that);}
    expression.prototype.minus = function(that) {return this.applyOp("-",that);}
    expression.prototype.times = function(that) {return this.applyOp("*",that);}
    expression.prototype.div   = function(that) {return this.applyOp("/",that);}
    expression.prototype.tothe = function(that) {return this.applyOp("^",that);}
        
    expression.prototype.adoptRightChild = function(child) {
        //alert("adoption\n" + this.ancestryString() + "right adopts\n" + child.ancestryString());
        this.right = child;
        child.parent = this;
        child.meta = this.meta;
    }
    
    expression.prototype.adoptLeftChild = function(child) {
        //alert("adoption\n" + this.ancestryString() + "left adopts\n" + child.ancestryString());
        this.left = child;
        child.parent = this;
        child.meta = this.meta;
    }
    
    expression.prototype.upgradeChild = function(original,improved) {
        if (this.left == original) {
            this.adoptLeftChild(improved);
        } else {
            this.adoptRightChild(improved);
        }
    }
    
    expression.prototype.replace = function(original) {
    //this takes the place of original.
        if (original.parent != undefined) {
            original.parent.upgradeChild(original,this);
        }
        this.meta = original.meta;
    }
    
    expression.prototype.clone = function() {
        var clone = new expression(this.type,this.name);
        clone.meta = this.meta;
        if (this.parent != undefined) {
            clone.parent = this.parent;
            clone.meta = this.meta;
        }
        if (this.left != undefined) {
            clone.adoptLeftChild(this.left.clone());
         }
        if (this.right != undefined) {
            clone.adoptRightChild(this.right.clone());
        }
        return clone;
    }
    
    expression.prototype.stripOpenGroup = function() {
        //Opengroup is not strictly necessary in expression trees,
        //because the tree structure determines the operations.
        if (this.type == g.OpenGroup) {
            return this.left.clone();//slightly inefficient on recursion
        } else {
            var ans = new expression(this.type,this.name);
            ans.meta = this.meta;
            if (this.left != undefined) {
                ans.adoptLeftChild(this.left.stripOpenGroup());
             }
            if (this.right != undefined) {
                ans.adoptRightChild(this.right.stripOpenGroup());
            }
            return ans;
        }
    }
            
    expression.prototype.pending = function() {
        if (this.type == undefined) {return 1;}
        if ((this.type == g.Operation) && (this.right == undefined)) {return 1;}
        if (((this.type == g.OpenGroup) || (this.type == g.Funktion)) && (this.left == undefined)) {return 1;}
        return 0;
    }        

    expression.prototype.lacksClosure = function() {
        if (((this.type == g.OpenGroup) || (this.type == g.Funktion)) && (this.right == undefined)) {return 1;}
        return 0;
    }        

    expression.prototype.syntaxError = function(str) {
        //alert("Syntax Error on symbol <"+str+">.\n"+this.ancestryString());      
        this.meta.badSyntax = true;
        this.commentAscii(str,"Error");
    }
    
    expression.prototype.ancestryString = function() {
        var ans = "[" + g.Typology[this.type] + ".." +  this.name+ "..";
        if (this.left != undefined){
            ans+=this.left + "..";
        }
        if (this.right != undefined){
            ans+=this.right + "..";
        }
        ans += "]\n";
        if (this.parent != undefined) {
            ans += this.parent.ancestryString();
        }
        return ans;
    }

    expression.prototype.commentAscii = function(str,type) {
        var response = document.createElement("span");
        response.className = this.type;
        response.appendChild(document.createTextNode(str));
        if (this.meta.commentedAscii == undefined) {
            this.meta.commentedAscii = document.createElement("span");
        }
        this.meta.commentedAscii.appendChild(response);
    }

    expression.prototype.toStringDisambiguate = function() {
        var leftString = "";// = "_";
        var rightString = "";// = "_";
        if (this.left !== undefined) {
            leftString = this.left.toStringDisambiguate();
        }
        if (this.right !== undefined) {
            rightString = this.right.toStringDisambiguate();
        }
        if (this.type == g.Operation) {
            return "[" +leftString + this.name + rightString+ "]";
        } else if (this.type == g.OpenGroup) {
            return leftString;
        } else if (this.type == g.Funktion) {
            return this.name+leftString+")";
        } else if ((this.type == g.Constant) || (this.type == g.Numb) || (this.type == g.Variable)) {
            return this.name;
        } else {
            return "unknowntype";
        }
    }
    
        
    expression.prototype.toStringLatex = function() {
        var leftString="";// = "_";
        var rightString="";// = "_";
        if (this.left !== undefined) {
            leftString = this.left.toStringLatex();
        }
        if (this.right !== undefined) {
            rightString = this.right.toStringLatex();
        }
        if (this.type == g.Operation) {
            var leftNeedsParen = this.parenthesizeLeft();
            var rightNeedsParen = this.parenthesizeRight();
            if (this.name.match(/\//)) {
                return "\\frac{"+leftString+"}{"+rightString+"}";
            } else {
                if (this.parenthesizeLeft())   {leftString = "(" + leftString + ")";}
                if (this.parenthesizeRight()) {rightString = "(" + rightString + ")";}
                if (this.name.match(/\*/)) {
                    return leftString + " \\cdot " + rightString;
                } else if (this.name.match(/\^/)) {
                    return leftString + "^{" +rightString+ "}";
                } else {
                    return leftString +" "+ this.name +" "+ rightString;
                }
            }
        } else if (this.type == g.OpenGroup) {//won't happen b/c of OpenGroup stripping.
            return this.name + leftString + g.closeParen(this.name);
        } else if (this.type == g.Funktion) {
            return "\\"+this.name+leftString+")"; //(assuming the function name is also a latex command!)
        } else if (this.type == g.Constant) {
            if (this.name.match(/^e$/i)) { 
                return this.name;
            } else {
                return "\\"+this.name; //assuming the constant is a latex command.
            }
        } else if ((this.type == g.Numb) || (this.type == g.Variable)) {
            return this.name;
        } else {
            return "unknowntype";
        }
    }

    expression.prototype.toStringAscii = function() {
        var leftString="";
        var rightString="";
        if (this.left !== undefined) {
            leftString = this.left.toStringAscii();
        }
        if (this.right !== undefined) {
            rightString = this.right.toStringAscii();
        }
        if (this.type == g.Operation) {
            if (this.parenthesizeLeft())   {leftString = "(" + leftString + ")";}
            if (this.parenthesizeRight()) {rightString = "(" + rightString + ")";}
            if (this.name.match(/\*/)) {
                return leftString + "*" + rightString;
            } else if (this.name.match(/\//)) {
                return leftString + "/" + rightString;
            } else if (this.name.match(/\^/)) {
                return leftString + "^" + rightString;
            } else {
                 return leftString + this.name + rightString;
            }
        } else if (this.type == g.OpenGroup) {//won't happen b/c of OpenGroup stripping.
            return this.name + leftString + g.closeParen(this.name);
        } else if (this.type == g.Funktion) {
            return this.name+leftString+")";
        } else if (this.type == g.Constant) {
            return this.name;
        } else if ((this.type == g.Numb) || (this.type == g.Variable)) {
            return this.name;
        } else {
            return "unknowntype";
        }
    }
    
    expression.prototype.parenthesizeLeft = function() {
        if (this.type !== g.Operation) {return false;}
        if (g.isPlusOrMinus(this.name)) {return false;}
        if (this.left == undefined) {return false;}
        var lname = this.left.name;
        var ltype = this.left.type;
        if (ltype !== g.Operation) {return false;}
        if (g.precedence(this.left.name) < g.precedence(this.name)) {return true;}
        if ((this.name.match(/\^/)) && (lname.match(/\^/))) {return true;}
        return false;
    }
    
    expression.prototype.parenthesizeRight = function() {
        if (this.type !== g.Operation) {return false;}
        if (this.right == undefined) {return false;}
        var rname = this.right.name;
        var rtype = this.right.type;
        if (rtype !== g.Operation) {return false;}
        if (g.precedence(this.right.name) < g.precedence(this.name)) {return true;}
        if (g.precedence(this.right.name) > g.precedence(this.name)) {return false;}
        if ((this.name.match(/\+/)) || (this.name.match(/\*/))) {return false;}
        return true;//catches the cases a^(b^c), a/(b*c), a/(b/c), a-(b-c), a-(b+c).
    }
    
    expression.prototype.treeInsert = function(newType,newName) {
        //returns the node that is now "hot".
        //alert("insert " + g.Typology[newType] + newName + " into " + g.Typology[this.type] + this.name);
        var error = false;
        var next; //the next node.
        if (newType == g.CloseGroup) {
            if (((this.type == g.Operation) || (this.type == g.OpenGroup) || (this.type == g.Funktion))&&(this.right == undefined)) { 
                //unclosed group or function or pending operation
                if (g.parenMatch(this.name,newName)) {
                    var n = new expression(newType,newName);
                    this.adoptRightChild(n);
                    next = this;
                } else {
                    error = true;
                }
            } else {
                if (this.parent == undefined) {
                    error = true;
                } else {
                    return this.parent.treeInsert(newType,newName);//return to avoid double comments
                }
            }  
        } else if (this.type == undefined) { //expression fresh out of the factory.
            if ((newType == g.Operation) && !(g.legalAsUnary)) {
                error = true;
            } else {
                this.type = newType;
                this.name = newName;
                next = this;
            }
        } else if (this.pending()) {
            if (g.beginsNoun(newType) || (g.beginsContext(this.type) && g.legalAsUnary(newName))) {
                var n = new expression(newType,newName);
                if (g.beginsContext(this.type)) {
                    this.adoptLeftChild(n);
                } else {
                    this.adoptRightChild(n);
                }
                //alert(n.ancestryString());
                next = n;
            } else {
                error = true;
            }
        } else { //this.pending() false, unary +- is not legal here.
            if (g.beginsNoun(newType)) {
                //not expecting a noun.  infer implicit *.
                return this.treeInsert(g.Operation, "*").treeInsert(newType,newName);
                //return to avoid double comments about the insertion.
            } else { //this.pending false, newType = operation
                if ((this.parent == undefined) || (g.precedence(this.parent.name) < g.precedence(newName))) {
                    var n = new expression(newType,newName);
                    n.replace(this);
                    n.adoptLeftChild(this);
                    next = n;
                } else { //float
                    return this.parent.treeInsert(newType,newName);
                }
            } 
        }
        if (error) {
            this.syntaxError(newName);
            return this;
        } else {
            this.commentAscii(newName,"Fine");
            return next;
        }
    }
    
    expression.prototype.dependsOn = function(v) {
        if (this.meta.badSyntax) {
            return "Syntax Error -- dependence undefined";
        }
        var ld = false;//this.left child depends on x
        var rd = false;
        if (this.left  != undefined) {ld = this.left. dependsOn(v,this.left);}
        if (this.right != undefined) {rd = this.right.dependsOn(v,this.right);}
        var ans = !!(ld || rd || ((this.type==g.Variable) && (this.name.match(v))));
        this.dependsOnRecent = ans; //option remember
        return ans;
    }
    
    expression.prototype.freeVariables = function() {
        //alert(this.ancestryString());
        this.freeVars = {};
        //alert(g.Typology[this.type] + this.name);
        if (this.type == g.Variable) {
            this.freeVars[this.name] = "1";
        } else {
            if (this.left  != undefined) {
                for (var x in this.left.freeVariables()) {
                    this.freeVars[x] = "1";
                }
            }
            if (this.right  != undefined) {
                for (var x in this.right.freeVariables()) {
                    this.freeVars[x] = "1";
                }
            }
        }
        return this.freeVars;
    }

    expression.prototype.substitute = function(v,exp) {
        //substitute exp (expression) for the variable v in this.
        //alert(this.ancestryString());
        if ((this.type == g.Variable) && (this.name.match(v))) {
            //alert("subit");
            return exp.clone();
        } else {
            if (this.left  != undefined) {this.left = this.left.substitute(v,exp);}
            if (this.right != undefined) {this.right = this.right.substitute(v,exp);}
        }
        return this;
    }

    expression.prototype.evaluateAt = function(model) {
        if (this.meta.badSyntax) {
            return "Syntax Error -- evaluation undefined";
        }
        if (this.type == g.Numb) {
            return this.name;
        } else if (this.type == g.Constant) {
            return g.constantModel[this.name.toLowerCase()];
        } else if (this.type == g.Variable) {
            return model[this.name.toLowerCase()];
        } else if (this.type == g.OpenGroup) {
            return this.left.evaluateAt(model);
        } else if (this.type == g.Funktion) {
            //chop the trailing right paren, then lookup function name, then recurse.
            return g.FnLookup[this.name.substr(0,this.name.length-1).toLowerCase()](this.left.evaluateAt(model));
        } else if (this.type == g.Operation) {
            var leftValue;
            var rightValue;
            if (this.left == undefined) {leftValue = 0;} //e.g., unary "-" 
                else {leftValue = this.left.evaluateAt(model);}
            rightValue = this.right.evaluateAt(model); //cannot be undef -- would throw syntax error
            if (this.name.match(/\+/)) {return (+leftValue) + (+rightValue);} //type-convert to avoid string concatenation
            else if (this.name.match(/\-/)) {return leftValue - rightValue;}
            else if (this.name.match(/\*/)) {return leftValue * rightValue;}
            else if (this.name.match(/\//)) {return leftValue / rightValue;} //division by zero???
            else if (this.name.match(/\^/)) {return Math.pow(leftValue,rightValue);} //negative to fractional power??
        }        
    }
    

    expression.prototype.derivative = function(v) {
        //Allows recursion, but avoids redundant calculations of the dependencies.
        //we have a lot of case checking, because we want to use the most specific
        //rule possible in each case, rather than the most general, for pedagogical purposes.
        //To do: break this into baby functions "power rule", "exponential rule", "chain rule", etc.
        //named to coincide with pedagogy
        //chain rule is right, but displays wrong if g' is a sum -- it forgets parens in the display.
        if (v == undefined) {v = "x";}//default variable of differentiation is x.
        this.dependsOn(v); //for side effect -- sets dependsOnRecent variables all the way down.
        if (this.dependsOnRecent == false) { //takes care of const and numb cases.
            return new expression(g.Numb,0);
        } else if (this.type == g.Variable) {
            //must just be v, since it apparently depends on v.
            return new expression(g.Numb,1);
        } else if (this.type == g.OpenGroup) {
            var d = new expression(g.OpenGroup,this.name);
            d.adoptRightChild(new expression(g.CloseGroup,g.closeParen(this.name)));
            d.adoptLeftChild(this.left.derivative(v));
            return d;
        } else if (this.type == g.Funktion) {
            //Chain rule case.
            var inner = this.left;
            var innerprime = inner.derivative(v); //later test whether = Numb 1.
            if (g.isPlusOrMinus(innerprime.name)) {
                var within = innerprime;
                innerprime = expression.newFromAscii("(x)");
                innerprime.substitute("x",within);
            }
            innerprime.comment = "The g'(x) part of the chain rule.";
            var fprime = new expression();
            var asciiDerivative = g.FnDiff[this.name.substr(0,this.name.length-1)];//lookup table FnDiff.
            fprime = expression.newFromAscii(asciiDerivative); 
            f_prime_of_g = fprime.substitute("x",inner);//all looked-up derivatives are functions of x.
            //alert(innerprime.type + innerprime.name);
            if ((innerprime.type == g.Numb) && (innerprime.name == 1)) { //no need for *1
                f_prime_of_g.comment = "The derivative of " + this.name + " is " + asciiDerivative ;
                return f_prime_of_g;
            } else {
                f_prime_of_g.comment = "The f'(g(x)) part of the chain rule";
                var prod = f_prime_of_g.times(innerprime);
                prod.comment = "By the chain rule.";
                return prod;
            }
        } else if (this.type == g.Operation) {
            if (g.isPlusOrMinus(this.name)) { //should be more clever, prune the op if half is constant.
                var d = new expression(this.type,this.name);
                if (this.left != undefined) { //(could be unary use)
                    d.adoptLeftChild(this.left.derivative(v));
                }
                d.adoptRightChild(this.right.derivative(v));
                return d;
            } else if (this.name.match(/\*/)) { //product rule, probably, but avoid if possible, following student.
                if ((this.left.dependsOnRecent) && (this.right.dependsOnRecent)) {
                    var leftturn = this.left.derivative(v).times(this.right);
                    leftturn.comment = "the f'*g part of the product rule";
                    var rightturn = this.left.times(this.right.derivative(v));
                    rightturn.comment = "the f*g' part of the product rule";
                    var d = leftturn.plus(rightturn);
                    d.comment = "the product rule";
                    return d;
                } else { //at least one must vary, or we wouldn't be here.
                    var varying;
                    var fixed;
                    if (this.left.dependsOnRecent) {
                        varying = this.left;
                        fixed=this.right;
                    } else {
                        varying = this.right;
                        fixed=this.left;
                    }
                    var d = fixed.times(varying.derivative(v));
                    d.comment = "constant multiple rule";
                    return d;
                }
            } else if (this.name.match(/\//)) { //quotient rule, or possibly constant multiple if denom = const.
                if ((this.right.dependsOnRecent) && (this.left.dependsOnRecent)) { //true quotient rule case
                    var two = new expression(g.Numb,"2");
                    var denominator = this.right.tothe(two);
                    denominator.comment = "quotient rule:  g^2 term";
                    var leftnumerator = this.left.derivative(v).times(this.right);
                    leftnumerator.comment = "quotient rule, f'*g term";
                    var rightnumerator = this.left.times(this.right.derivative(v));
                    rightnumerator.comment = "quotient rule, f*g' term";
                    var d = leftnumerator.minus(rightnumerator).div(denominator);
                    d.comment = "quotient rule";
                    return d;          
                } else if (this.right.dependsOnRecent) {
                    alert("sneaky quotient not yet coded.");
                    return this;
                } else {
                    var d = this.left.derivative(v).div(this.right);
                    d.comment = "constant multiple rule, because the denominator is constant.";
                    return d;
                }
            } else if (this.name.match(/\^/)) { //awful case.  lots of specific rules.
                if ((this.left.dependsOnRecent) && !(this.right.dependsOnRecent)) { //power rule, possible chain.
                    var oneLess;
                    if (this.right.type == g.Numb) {
                        oneLess = new expression(g.Numb,this.right.name-1);
                    } else {
                        var one = new expression(g.Numb,1);
                        oneLess = this.right.minus(one);
                    }
                    var d = this.right.times(this.left.tothe(oneLess));
                    d.comment = "power rule";
                    var gprime = this.left.derivative(v);
                    if ((gprime.type == g.Numb) && (gprime.name == 1)) { //no need for *1
                        return d;
                    } else {
                        var prod = d.times(gprime);
                        prod.comment = "By the chain rule.";
                        return prod;
                    }                    
                } else if (!(this.left.dependsOnRecent) && (this.right.dependsOnRecent)){ //exponent rule, possible chain
                    var base = this.left;
                    var exponent = this.right;
                    var gprime = exponent.derivative(v);
                    var logbase = new expression();
                    logbase = expression.newFromAscii("ln(x)");
                    logbase.substitute("x",base);
                    var outer; //the ln(a)a^(g(x)) part
                    var d; // outer, multiplied by g'(x) if necessary.
                    if ((base.type == g.Constant) && (base.name.match(/e/i))) {//no need for log
                        outer = this;
                    } else {
                        outer = logbase.times(this);
                    }
                    if ((gprime.type == g.Numb) && (gprime.name == 1)) {
                        d = outer;
                        d.comment = "exponent rule";
                    } else {
                        d = outer.times(gprime);
                        d.comment = "exponent rule and chain rule";
                    }
                    return d;
                } else { //rule for f(x)^g(x), usually hidden from students
                    
                    alert("not yet programmed for f(x)^g(x)");
                    return this;
                }
            }            
        }
    }
    
    expression.prototype.derivativeAdvice = function(v) {
        //we have a lot of case checking, because we want to use the most specific
        //rule possible in each case, rather than the most general, for pedagogical purposes.
        if (v == undefined) {v = "x";}//default variable of differentiation is x.
        this.dependsOn(v); //for side effect -- sets dependsOnRecent variables all the way down.
        if (this.dependsOnRecent == false) {
            return {rule:"derivative_constant"};
        } else if (this.type == g.Variable) {
            return {rule:"derivative_variable", variable:this};
        } else if (this.type == g.OpenGroup) {
            return {rule:"derivative_group", inner:this.left};
        } else if (this.type == g.Funktion) {
            //Chain rule case, possibly.
            var inner = this.left;
            var outer = expression.newFromAscii(this.name + "x)");//e.g. "sin(" becomes expression "sin(x)"
            if (inner.type == g.Variable) {
                return {rule:"derivative_functionRule", inside:inner, outside:outer};
            } else {
                return {rule:"derivative_chain", inside:inner, outside:outer};
            }
        } else if (this.type == g.Operation) {
            if (g.isPlusOrMinus(this.name)) {
                if (this.left == undefined) {
                    return {rule:"derivative_unaryMinus", inside:this.right};
                } else {
                    return {rule:"derivative_plusMinusGroup", upon:this};
                }
            } else if (this.name.match(/\*/)) { //product rule, probably, but avoid if part is constant.
                if ((this.left.dependsOnRecent) && (this.right.dependsOnRecent)) {
                    return {rule:"derivative_product", upon:this};
                } else { //at least one must vary, or we wouldn't be here.
                    return {rule:"derivative_constantMultiple", upon:this};//figure out later which half is constant.
                }
            } else if (this.name.match(/\//)) { //quotient rule, or possibly constant multiple if denom = const.
                if ((this.right.dependsOnRecent) && (this.left.dependsOnRecent)) { //true quotient rule case
                    return {rule:"derivative_quotient", upon:this};
                } else if (this.right.dependsOnRecent) {
                    //Something like 2/(x^2+5) will be rewritten 2*(x^2+5)^(-1) to avoid quotient rule.
                    return {rule:"derivative_quotientNegativeExponent", upon:this};
                } else {
                    return {rule:"derivative_constantDivisor",upon:this};
                }
            } else if (this.name.match(/\^/)) { //awful case.  lots of specific rules.
                if ((this.left.dependsOnRecent) && !(this.right.dependsOnRecent)) { //power rule, possible chain.
                    //constant exponent.  power rule and possible chain.
                    var base = this.left;
                    var power = this.right;
                    if (base.type == g.Variable) {
                        return {rule:"derivative_power", base:base, power:power};
                    } else {
                        outer = this.clone();
                        outer.left = new expression(g.Variable,"x");//(3t+4)^6 becomes x^6
                        return {rule:"derivative_chain", inside:base, outside:outer};
                    }
                } else if (!(this.left.dependsOnRecent) && (this.right.dependsOnRecent)){ 
                    //constant base.  exponent rule, possible chain
                    var base = this.left;
                    var power = this.right;
                    if (power.type == g.Variable) {
                        return {rule:"derivative_exponent", base:base, power:power};
                    } else {
                        outer = this.clone();
                        outer.right = new expression(g.Variable,"x");//2^{17t+4} becomes 2^x
                        return {rule:"derivative_chain", inside:power, outside:outer};
                    }
                } else { 
                    //rule for f(x)^g(x), usually hidden from students
                    return {rule:"derivative_functionalExponentiation", upon:this};
                }
            }            
        }
    }

//end senseless indenting

expression.prototype.termstructure = function() {
    var Ans = {};
    if ((this.type == g.Operation) && (this.name.match(/\+/))) {
        var lstruct = this.left.termstructure();
        var rstruct = this.right.termstructure();
        Ans.terms = lstruct.terms.concat(rstruct.terms);
        lstruct.operations.push("+");
        Ans.operations = lstruct.operations.concat(rstruct.operations);
    } else if ((this.type == g.Operation) && (this.name.match(/\-/))) {
        var lstruct = this.left.termstructure();
        lstruct.terms.push(this.right);
        lstruct.operations.push("-");
        Ans = lstruct;
        //the minus sign will group anything to its right, so that's one term.
    } else {
        Ans = {terms:[this],operations:[]};
    }        
    return Ans;
}

expression.prototype.pretty = function() {
    var container = document.createElement("span");
    var spanner = document.createElement("span");
    spanner.className = "math";
    //alert(this.name);
    //alert(this.toStringLatex());
    spanner.appendChild(document.createTextNode(this.toStringLatex()));
    container.appendChild(spanner);
    jsMath.Process(container);
    //jsMath will seek all div/span elements of class "math" *within*
    //the given node, but not operate on the thing itself.  So we need a wrapper
    //class "container" on which to call jsMath.Process.
    return container;
}

