jiggzson / nerdamer

a symbolic math expression evaluator for javascript
http://www.nerdamer.com
MIT License
517 stars 82 forks source link

Order of result expression #502

Closed richardbullin closed 4 years ago

richardbullin commented 5 years ago

nerdamer.diff('x^2+x','x').toString() returns '1+2*x' nerdamer.diff('x^2+x','x').toTex() returns '2 \cdot x+1' which shows as '2x+1' on your demo page

Is there a way of specifying how the result is ordered? Although the answer is technically correct, it feels nicer to display it as '2x+1'? Likewise if you had a polynomial it would be nice to order it going from higher to lower degrees.

I tried to convert the result from latex back using 'convertFromLaTeX(TeX)' but the result still came back as '1+2*x'.

Cheers

jiggzson commented 5 years ago

@richbully,

There's no current solution but your suggestion is noted and I'll try to add this functionality. If you need an immediate workaround, something like the snippet below might work. I haven't really tested it so let me know if it does the trick.

//Modify the exsting toTeX function
var core = nerdamer.getCore();
core.Expression.prototype._toTeX = core.Expression.prototype.toTeX; //store the old toTeX function
core.Expression.prototype.toTeX = function() {
    if(this.symbol.isComposite()) {
        var symbols = this.symbol.collectSymbols(null, null, function(a, b) {
            if(a.group == b.group)
                return a.power - b.power; //sort by descending power
            return (b.group - a.group); //sort by descending groups.
        }, true);
        var tex = [];
        for(var i=0; i<symbols.length; i++) {
            tex.push(symbols[i].latex());
        }
        return tex.join('+').replace(/\+\-/g,'-'); //replace +- with -
    }
    //return the old toTeX
    return this._toTeX();
}

//TESTS
var symbol = nerdamer.diff('a*x^3-x+c*x^2','x');
console.log(symbol.toTeX());
console.log(nerdamer('a^2*b*x*c').toTeX())
richardbullin commented 5 years ago

Cheers, but the issue is with toString(), the result from toTeX() already comes out in the order that I'm after (higher orders first). Thank you for the snippet though, I appreciate it!

nerdamer.diff('x^2+x','x').toString() returns '1+2*x'

It would be nice if it came out as "2*x+1"

richardbullin commented 5 years ago

Actually your snippet works for the initial case if you replace "toTeX" with "toString"

Is there a easy way to extend this snippet to work for things like fractions or function params or root operands? For example nerdamer('(1+2x+3x^2)/(5x+4x^3)').toTeX() gives the numerator and denominator ordered correctly (image below is from your demo page)

image

But if you call toString() it returns "(1+2x+3x^2)(4x^3+5*x)^(-1)" even with the snippet you provided.

Thank you!

richardbullin commented 5 years ago

Just another example which comes out correctly in toTeX() but result from toString() = "sin(x+x^2)"

image

richardbullin commented 5 years ago

Ok I think I've got it. If you use the snippet you created for toTeX(). I then added the below to nerdamer.core.js, LaTeX.parse() seems to always return the result surrounded in brackets so these get removed but this could also be the cause of a future bug i.e. if it stopped returning answer in brackets then something like "(4x+2)/(3x+1)" would end up as "4x+2)/(3x+1"

    libExports.toStringFromLaTeX = function (e)
    {
        let result = LaTeX.parse(_.tokenize(e));

        if (result.charAt(0) === '(' && result.charAt(result.length - 1) === ')')
        {
            // Remove surrounding bracket
            result = result.substr(1).slice(0, -1);
        }

        return result;
    };

Probably not the best in terms of performance though as I'm converting to latex first then re tokenising but so far gives the right results. My test is below

      var core = nerdamer.getCore();
      core.Expression.prototype._toTeX = core.Expression.prototype.toTeX; //store the old toTeX function
      core.Expression.prototype.toTeX = function ()
      {
        if (this.symbol.isComposite())
        {
          var symbols = this.symbol.collectSymbols(null, null, function (a, b)
          {
            if (a.group == b.group)
              return a.power - b.power; //sort by descending power
            return (b.group - a.group); //sort by descending groups.
          }, true);
          var tex = [];
          for (var i = 0; i < symbols.length; i++)
          {
            tex.push(symbols[i].latex());
          }
          return tex.join('+').replace(/\+\-/g, '-'); //replace +- with -
        }
        //return the old toTeX
        return this._toTeX();
      }

      core.Expression.prototype.toStringViaLatex = function ()
      {
        return nerdamer.toStringFromLaTeX(this.toTeX());
      };

      //TESTS
      const TEST1 = nerdamer.diff('a*x^3-x+c*x^2', 'x').toStringViaLatex();
      console.log(TEST1); // Returns "3*x^(2)*a+2*x*c-1"

      const TEST2 = nerdamer('a^2*b*x*c').toStringViaLatex();
      console.log(TEST2); // Returns "a^(2)*b*x*c"

      const TEST3 = nerdamer.diff('x^2+x', 'x').toStringViaLatex();
      console.log(TEST3); // Returns "2*x+1"

      const TEST4 = nerdamer('(1+2x+3x^2)/(5x+4x^3)').toStringViaLatex();
      console.log(TEST4); // Returns "(3*x^(2)+2*x+1)/(4*x^(3)+5*x)"
jiggzson commented 5 years ago

@richbully, got it. As for efficiency, unless you're rendering a textbook I doubt your method would present any issues. I'll work on the ability to order polynomials for both the toString and toTeX methods.

jiggzson commented 4 years ago

@richbully, I'm assuming that you used the snippet to create a fix.

richardbullin commented 4 years ago

@jiggzson yep sure did. Just used what I commented above, you find some issues with that?