Mathilda is a tiny, AI Agent-generated, computer algebra system (CAS) heavily inspired by the core architecture and evaluation semantics of Mathematica. Written entirely in C99, Mathilda implements a recursive expression model, structural pattern matching, rewriting rules, and an extensive library of built-in functions.
- Infinite Evaluation Semantics: Expressions are repeatedly evaluated top-down until a fixed point is reached.
-
First-Class Pattern Matching: Supports
Blank(_),BlankSequence(__),BlankNullSequence(___), conditional matching (/;), and named bindings (x_). -
Rule Engine: Built-in support for transformation rules (
->,:>) and repeated replacements (/.,//.). - Arbitrary Precision Arithmetic: Powered natively by the GNU Multiple Precision Arithmetic Library (GMP) for handling operations on exceedingly large integers.
-
Advanced Integer Factorization: Includes a unified, automatic factorization pipeline, alongside explicit execution of algorithms such as:
- Pollard's Rho, Pollard's
$P-1$ , Williams'$P+1$ , Fermat, CFRAC, Dixon's Method, and the Elliptic Curve Method (ECM).
- Pollard's Rho, Pollard's
- Extensive Standard Library: Built-in functions covering linear algebra, calculus, polynomial manipulation, combinatorics, statistics, list structures, and symbolic tensor operations.
To build and run Mathilda, you need the following development libraries installed on your system:
- A C99-compliant compiler (
gccorclang) - GMP (
libgmp/gmp-dev) - Readline (
libreadline/readline-dev) - CMake (Optional, only required if you intend to build the test suite)
Note: The GMP-ECM library is utilized for factorization methods and is included as an external source dependency. It will be built automatically during the compilation process.
The makefile automatically handles the configuration and compilation of internal dependencies before linking the main executable.
- Clone the repository (including the bundled GMP-ECM submodule):
If you already cloned without
git clone --recurse-submodules https://github.com/stblake/Mathilda.git cd Mathilda--recurse-submodules, rungit submodule update --init --recursivefrom inside the repo before building. (The build script will also attempt this automatically if it detects an uninitialized submodule.) - Build the project using
make:make -j$(nproc) - Start the interactive REPL:
./Mathilda
Mathilda boasts a comprehensive C-based unit test suite.
cd tests
mkdir -p build && cd build
cmake ..
make -j$(nproc)
# Run all test binaries
for t in *_tests; do ./$t; doneEverything in Mathilda is represented by an immutable-by-convention Expr AST node, implemented as a tagged union (Integer, Real, BigInt, Rational, Complex, Symbol, String, Function).
The system is modularized into several independent subsystems:
- Parser (
parse.c): A robust Pratt parser mirroring Mathematica's exact operator precedences. - Evaluator (
eval.c): Manages the infinite evaluation loop. It processes attributes likeHoldAll,Flat,Orderless, andListablebefore recursively evaluating function arguments. - Symbol Table (
symtab.c): Stores global variable definitions,DownValues(user-defined rules), and native C built-in function pointers. - Pattern Matcher & Rule Engine (
match.c,replace.c): Implements structural tree unification and sequence segmenting through backtracking.
Adding new functionality to Mathilda is straightforward:
-
Write the C Implementation: Create your evaluation logic in the appropriate
.cmodule (e.g.,core.c). Your function signature must beExpr* builtin_myfunc(Expr* res).- Memory Rule: Built-in functions take ownership of the expression passed to them. If you successfully evaluate the input and return a new
Expr*, you MUST free the original expression (or structurally reuse its nodes). If the function cannot evaluate the arguments (e.g., symbolic inputs to a purely numeric function), returnNULL, and the evaluator will automatically retain ownership.
Expr* builtin_myfunc(Expr* res) { if (res->data.function.arg_count != 1) return NULL; // ... mathematical logic ... expr_free(res); // Free input if returning a new expression return expr_new_integer(42); }
- Memory Rule: Built-in functions take ownership of the expression passed to them. If you successfully evaluate the input and return a new
-
Register the Function: In the module's initialization routine (e.g.,
core_init()), register the function and assign its documentation string so it is available via?MyFunc:symtab_add_builtin("MyFunc", builtin_myfunc); symtab_set_docstring("MyFunc", "MyFunc[x]\n\tComputes the ultimate answer.");
-
Assign Attributes: If your function automatically threads over lists, operates symmetrically, or holds its arguments unevaluated, assign the corresponding attributes during initialization:
symtab_get_def("MyFunc")->attributes |= ATTR_LISTABLE | ATTR_PROTECTED;
-
Test and Document:
- Add test cases to the appropriate suite in the
tests/directory using theTEST(...)macro. - Update
Mathilda_spec.mdwith your new function, describing its behavior and providing sample inputs/outputs.
- Add test cases to the appropriate suite in the
Mathilda is open-source software licensed under the GNU General Public License v3.0 (GPLv3).
You are heavily encouraged to explore the codebase, submit pull requests, report issues, and expand the CAS with new mathematical algorithms! Please see the LICENSE file for more details.