Last updated: 2026-09-18

U
Undergraduate level

Symbolic Math & Interval Arithmetic: Try It Live

For new readers

Type a piece of ordinary typeset maths — the same LaTeX notation MathJax renders elsewhere on this site, e.g. x^2 - 2 = 0 or \sqrt{2} — into the box below and a small PatLang program, compiled to WebAssembly and run entirely in this browser tab, parses it into a real expression tree and solves or evaluates it, returning a rigorous interval rather than a single rounded-looking number.

Overview & Architecture

Interval Arithmetic covers the underlying idea: a computed result that hides how wrong it might be is worse than one that states its own error bound honestly. self_hosting/lib/symbolic.patlang turns that into a working library — expression trees built from the same tagged-list idiom PatLang's own compiler uses for its AST (["Add", a, b], dispatched on the tag), an interval evaluator whose endpoints stay exact Numeric Tower values (Int or Rational) for as long as an expression stays purely algebraic, structural differentiation, closed-form solving for linear and quadratic equations (including genuine complex roots), and closed-form solving for periodic equations like sin(x) = 0 that returns a set-production rule — a generator plus a free integer parameter — rather than trying to enumerate an infinite solution set as a list. self_hosting/lib/symbolic_latex.patlang is the piece that makes it practically usable: a small recursive-descent parser turning the LaTeX text a person would actually type into those same expression trees, so a quadratic doesn't have to be built up call by call in PatLang source before it can be solved.

Try It

Type a LaTeX expression or equation below, or pick one of the examples.

(not run yet)

Examples:

Solutions print as exact fractions where the Tower can keep them exact (an irrational root's bracket, like the one on the Interval Arithmetic page, is the tightest pair of adjacent doubles the Tower can prove contains the true value — not a rounded decimal). The periodic example shows several concrete members of an infinite family, materialized within a default window of [-20, 20]; the no-closed-form example finds its root by splitting the search range wherever monotonicity can't be proven and bisecting each surviving piece, the same algorithm hand-verified for √2 on the Interval Arithmetic page, generalized to an arbitrary expression.

What's In Scope

The parser covers the LaTeX a person would actually type for ordinary single-variable algebra: numbers, the variable x, + - * /, implicit juxtaposition multiplication (2x, (x-1)(x+1)), ^ for integer exponents, \sqrt{}, \frac{}{}, \sin/\cos/\tan (bare, parenthesised, or braced argument), \pi, and \left/\right as transparent delimiters. It is deliberately not a general LaTeX engine — multi-letter identifiers are read as adjacent single-letter variables the way typeset maths actually reads, and matrices, subscripts, and multi-variable equations are out of scope. The periodic solver's closed form for cos only recognises the degenerate case where its two solution branches collapse into one (cos(x) = 1, for instance); the general two-branch case correctly falls through to the numeric fallback rather than returning a silently incomplete answer.

PatLang source (the driver program compiled to WebAssembly above)
# Browser/CLI driver for the symbolic + interval arithmetic library
# (self_hosting/lib/symbolic.patlang, self_hosting/lib/symbolic_latex.patlang).
# Takes ONE LaTeX math expression or equation as argv()[0], always solving
# for the variable "x".

make a function called sd_contains_equals takes s returns yes
  let i = 0
  while i < s.length do
    if s[i] == "=" then
      return true
    end
    let i = i + 1
  end
  return false
end

let args = argv()
if args.length < 1 then
  print("Usage: pass one LaTeX expression or equation, e.g. x^2 - 2 = 0")
else
  let raw = args[0]
  let expr = sym_parse_latex(raw)
  let default_range = sym_interval(0 - 20, 20)
  if sd_contains_equals(raw) then
    print("Input:   " + raw)
    print("Parsed:  " + sym_to_string(expr) + " = 0")
    let closed = sym_solve_closed_form(expr, "x")
    if closed[0] == "Solutions" then
      let items = closed[1]
      print("Closed-form polynomial solve -- " + items.length + " solution(s):")
      let i = 0
      while i < items.length do
        print("  " + sym_format_item(items[i]))
        let i = i + 1
      end
    else
      let trig = sym_solve_trig_closed_form(expr, "x")
      if trig[0] == "Family" then
        print("Closed-form periodic solve -- infinitely many solutions.")
        let members = sym_materialize(trig, default_range)
        print("Members in the range [-20, 20]:")
        let i = 0
        while i < members.length do
          print("  " + sym_format_item(members[i]))
          let i = i + 1
        end
      else
        print("No closed form recognized -- searching numerically in [-20, 20].")
        let numsol = sym_solve_numeric_bounded(expr, "x", default_range)
        let items = numsol[1]
        if items.length == 0 then
          print("No roots found in [-20, 20].")
        else
          print("Found " + items.length + " root(s):")
          let i = 0
          while i < items.length do
            print("  " + sym_format_item(items[i]))
            let i = i + 1
          end
        end
      end
    end
  else
    print("Input:   " + raw)
    print("Parsed:  " + sym_to_string(expr))
    let v = sym_eval_interval(expr, [])
    print("Value:   [" + sym_ival_lo(v) + ", " + sym_ival_hi(v) + "]")
  end
end
print("")