Symbolic Math Solver

Enter an expression or equation to simplify or solve for x

Examples: 2*x+3*x+3-4 2*x+3=0 x+-2*x+5+3

History

Code Behind the Magic

class plusNode extends Node {
  constructor(left, right) {
    super();
    this.left = left;
    this.right = right;
  }
  toString() {
    const rightStr = this.right.toString();
    if (rightStr.startsWith("-")) {
      return \${this.left.toString()} - \${rightStr.slice(1)};
    }
    return (\${this.left.toString()} + \${rightStr});
  }
  simplify() {
    const left = this.left.simplify();
    const right = this.right.simplify();
    const terms = collectTerms(this);
    const xCoef = terms.x || 0;
    const constant = terms.constant || 0;
    if (xCoef === 0 && constant === 0) return new NumberNode(0);
    if (xCoef === 0) return new NumberNode(constant);
    if (constant === 0)
      return new multiplyNode(new NumberNode(xCoef), new SymbolNode("x"));
    const xTerm = new multiplyNode(new NumberNode(xCoef), new SymbolNode("x"));
    return new plusNode(xTerm, new NumberNode(constant));
  }
}
function tokenize(str) {
  const operators = ["+", "-", "*", "="];
  const tokens = [];
  let current = "";
  for (let i = 0; i < str.length; i++) {
    if (operators.includes(str[i])) {
      if (current) {
        tokens.push(current);
        current = "";
      }
      if (
        str[i] === "-" &&
        (i === 0 || operators.includes(str[i - 1])) &&
        i + 1 < str.length &&
        /\d/.test(str[i + 1])
      ) {
        current = "-";
      } else {
        tokens.push(str[i]);
      }
    } else {
      current += str[i];
    }
  }
  if (current) tokens.push(current);
  return tokens.filter((t) => t !== "");
}
function parseTerm(tokens, start) {
  if (start >= tokens.length) throw new Error("Invalid term: no tokens left");
  if (start + 2 < tokens.length && tokens[start + 1] === "*") {
    const coef = new NumberNode(parseFloat(tokens[start]));
    const varX = new SymbolNode(tokens[start + 2]);
    const node = new multiplyNode(coef, varX);
    node.tokensConsumed = 3;
    return node;
  } else {
    const value = parseFloat(tokens[start]);
    const node = isNaN(value) ? new SymbolNode(tokens[start]) : new NumberNode(value);
    node.tokensConsumed = 1;
    return node;
  }
}