Tess is a computer algebra system that has been in the works for quite some time now. Initially created to express the solutions to quadratic polynomial equations algebraically, it has evidently grown to become far more than what I initially set out to make. Source code

YIKES!
Nasty error message here.
Under active development. Expect bugs. | ▼ Scroll
Usage
  • Type or paste your mathematical expression (in ASCII math) in the provided textbox. If the expression parses incorrectly, use parentheses to clarify the order of operations.
  • All angles must be in radians.
  • Press enter to simplify your expression.
  • Any rendered expression can be copied as Latex by clicking on the expression.
  • You can use Desmos, which can evaluate Latex, to verify the value of an expression.
Examples
  • Basic Algebraic Expression: (x+3)(x-2)+6
  • Manipulating constants: (2pi+3pie^2+7pi+3e^3(1/pi)e^(-1)+27*(pi/e^3)*(e^2/(9pi^2)))/(9+6)
  • Radicals and factoring: (sqrt(2+sqrt12)a+sqrt(2+sqrt12)b)/(sqrt2c+sqrt2d)
  • Evaluation of constructible trigonometric ratios: tan(3pi/16)
Known issues
  • Expressions are sometimes rendered unconventionally (for example, \(\pi3\) instead of \(3\pi\)). This is because all factors in a term are sorted according to their hashes, and this order might not always be what is conventionally expected. A fix is being worked on.
  • Some compute intensive expressions (like tan(9p/16)) take a while to evaluate, during which time the browser tab becomes unusable. A fix is being worked on where all compute tasks are delegated to a service worker, leaving the primary thread free.

The data structure

Tess can take any ASCII math expression and can represent it using a data structure I developed to facilitate the representation and manipulation of mathematical objects. From my research, I learnt that most computer algebra systems use a trees to represent mathematical entities (“expressions”). However, I decided to use nested arrays instead, since I believed that it would make performing mathematical operations and maintaining a hierarchical structure easier. An expression is an array, in which each element represents a term, all of which are in addition. Each term is also an array, with each element representing a factor, all of which are in multiplication. Each factor is can array, with the first element storing the base, and the second element storing the exponent. For example, 4/x + 3x^2 would be represented as [[[4, 1], [x, -1]], [[3, 1], [x, 2]]]. Every element in the factor arrays can be a simple number or variable, or it can be another expression array. This allows my data structure to represent any mathematical expression.

Expression parsing

The input strings (in ASCII math) are parsed using the Shunting - yard algorithm. The input string is first split into tokens, which are then sequenced in reverse polish. Then, the reverse Polish is, in a way, evaluated. However, instead of finding the actual value of the expression, this is used to transform the reverse polish into the data structure described above. However, the downside to this approach is that it introduces redundant expressions (“wrapper expressions”). This is because the evaluator can only accept two operands for binary operators (like + and *) which are associative and usually have multiple operands. For example, x+y+z would be parsed as (x+y) +z, with x + y in one expression, which is then added to z. The resulting output is unnecessarily bloated, violates the associativity of these operands, and is incredibly difficult to manipulate. Thus, the output of the parser is fed to a standardizer, which gets rid of unnecessary nesting by emptying out the contents of children expressions into their parent expressions if they do not serve any purpose in defining the structure of their parent expression. The standardizer also sorts all elements of all arrays in a specific order according to their hash values. This speeds up simplification by allowing direct comparison of two arrays - it ensures that if they have the same elements, they must have them in the same order too.

Latex representation

Tess can represent (“stringify”) any expression represented using its native data structure as Latex, which can be rendered using a mathematics library like MathJax. The stringify method works recursively (like many other methods in Tess) to produce its final output. When called on an expression, it gets the Latex of all of its children by calling their respective stringify methods, and combines them in an appropriate manner before returning the result to its caller.

Expression evaluation

Tess can numerically evaluate an expression, given it doesn’t have any undefined variables. The logic works very similarly to the stringify function - children return their values to their parents, which combine them according to the operation they are designated to perform, and return the result to their parent.

Simplification

Tess can perform rudimentary simplification. It can combine like terms in addition and multiplication. It can also factor expressions, and use factorization as a tool in simplification by cancelling out common factors. The nice child to parent cascading strategy which i employed in the stringify and evaluation methods proves to be inadequate here - for example, a parent expression might require a child expression to be formatted differently based on the type of simplification it intends to attempt. However, it is sufficient for simple simplification tasks like combining like terms and factoring out a common factor. I will have to think of a more sophisticated algorithm to undertake more involved simplification.

Trigonometric function Evaluator

Tess can algebraically evaluate some trigonometric ratios. It does this by applying standard trigonometric formulae to construct new trig ratios from the ones it already knows.