PicoCAS is a tiny, AI Agent-generated, symbolic computer algebra system (CAS) heavily inspired by the core architecture and evaluation semantics of Mathematica. Written entirely in C99, PicoCAS implements a recursive expression model, structural pattern matching, rewriting rules, and a surprisingly extensive library of built-in functions.
The name "PicoCAS" pays homage to David Stoutemyer's seminal PICOMATH-80 tiny computer algebra system. PicoCAS has the goal of implementing the core of the Mathematica programming language, and the algebraic and calculus capabilities of the Derive computer algebra system.
- 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 PicoCAS, 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:
git clone https://github.com/stblake/picocas.git cd picocas - Build the project using
make:make -j$(nproc) - Start the interactive REPL:
./picocas
PicoCAS 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 PicoCAS 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 PicoCAS 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
picocas_spec.mdwith your new function, describing its behavior and providing sample inputs/outputs.
- Add test cases to the appropriate suite in the
PicoCAS 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.