Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Nonanti/mathcore/llms.txt

Use this file to discover all available pages before exploring further.

At the heart of MathCore is a single recursive enum: Expr. Every mathematical object — a literal number, a symbolic variable, an arithmetic operation, a function call, a derivative, an integral — is represented as a variant of this enum. Because each Expr can contain other Expr values (via Box<Expr> or Vec<Expr>), the type naturally forms a tree that mirrors the structure of a mathematical expression. Understanding Expr is the prerequisite for everything else MathCore does: parsing, evaluation, differentiation, integration, and solving all operate by constructing, traversing, or transforming these trees.

The Expr Enum

#[derive(Debug, Clone)]
pub enum Expr {
    Number(f64),
    Complex(Complex64),
    Symbol(String),
    Binary {
        op: BinaryOp,
        left: Box<Expr>,
        right: Box<Expr>,
    },
    Unary {
        op: UnaryOp,
        expr: Box<Expr>,
    },
    Function {
        name: String,
        args: Vec<Expr>,
    },
    Derivative {
        expr: Box<Expr>,
        var: String,
        order: u32,
    },
    Integral {
        expr: Box<Expr>,
        var: String,
        lower: Option<Box<Expr>>,
        upper: Option<Box<Expr>>,
    },
}

Variants

Number(f64)

A real-valued floating-point literal. This is the leaf node produced whenever the parser encounters a numeric token.
let two = Expr::Number(2.0);
let pi  = Expr::Number(std::f64::consts::PI);
Scientific notation and decimal fractions are both supported in the string form: "2.5e-3" parses to Expr::Number(0.0025).

Complex(Complex64)

A complex number stored as a num_complex::Complex64 value — a pair of f64 values (re, im). Arithmetic operations on complex values are handled automatically by the engine.
use num_complex::Complex64;

let z = Expr::Complex(Complex64::new(3.0, 4.0)); // 3 + 4i
The parser recognises inline complex literals like 3+4i and 1-2i and produces Expr::Complex nodes directly. See Parsing for the full syntax.

Symbol(String)

A named symbolic variable. Any identifier that is not a known function name or constant is parsed as a Symbol.
let x = Expr::Symbol("x".to_string());
let theta = Expr::Symbol("theta".to_string());
During evaluation, Symbol nodes are resolved against the active Context. If no value is registered for the name, the engine returns MathError::UndefinedVariable.

Binary { op, left, right }

A binary operation applied to two sub-expressions. Both operands are heap-allocated (Box<Expr>) to allow the recursive structure.
// Represents x^2 + 1
let expr = Expr::Binary {
    op: BinaryOp::Add,
    left: Box::new(Expr::Binary {
        op: BinaryOp::Power,
        left: Box::new(Expr::Symbol("x".to_string())),
        right: Box::new(Expr::Number(2.0)),
    }),
    right: Box::new(Expr::Number(1.0)),
};
See BinaryOp below for the full set of operators.

Unary { op, expr }

A unary operation applied to a single sub-expression.
// Represents -(x + 1)
let expr = Expr::Unary {
    op: UnaryOp::Negate,
    expr: Box::new(Expr::Binary {
        op: BinaryOp::Add,
        left: Box::new(Expr::Symbol("x".to_string())),
        right: Box::new(Expr::Number(1.0)),
    }),
};
See UnaryOp below for the full set of operators.

Function { name, args }

A named function applied to zero or more argument expressions. The name field is a plain String; the engine dispatches on it at evaluation time to call the correct built-in (or custom) implementation.
// Represents sin(x)
let expr = Expr::Function {
    name: "sin".to_string(),
    args: vec![Expr::Symbol("x".to_string())],
};

// Represents log(x, 10)
let expr = Expr::Function {
    name: "log".to_string(),
    args: vec![
        Expr::Symbol("x".to_string()),
        Expr::Number(10.0),
    ],
};

Derivative { expr, var, order }

A symbolic representation of a derivative. var is the differentiation variable and order is the order (1 for first derivative, 2 for second, and so on). This node is produced by the calculus layer, not the parser.
// Represents d²/dx²(sin(x))
let expr = Expr::Derivative {
    expr: Box::new(Expr::Function {
        name: "sin".to_string(),
        args: vec![Expr::Symbol("x".to_string())],
    }),
    var: "x".to_string(),
    order: 2,
};
The Display impl formats this as d^2/dx^2(sin(x)).

Integral { expr, var, lower, upper }

A symbolic integral expression. Both lower and upper are Option<Box<Expr>>: when both are Some, the node represents a definite integral; when both are None, it represents an indefinite integral.
// Represents ∫[0,1] x^2 dx  (definite)
let expr = Expr::Integral {
    expr: Box::new(Expr::Binary {
        op: BinaryOp::Power,
        left: Box::new(Expr::Symbol("x".to_string())),
        right: Box::new(Expr::Number(2.0)),
    }),
    var: "x".to_string(),
    lower: Some(Box::new(Expr::Number(0.0))),
    upper: Some(Box::new(Expr::Number(1.0))),
};

// Represents ∫ x dx  (indefinite)
let expr = Expr::Integral {
    expr: Box::new(Expr::Symbol("x".to_string())),
    var: "x".to_string(),
    lower: None,
    upper: None,
};
The Display impl formats the definite form as ∫[0,1] x^2 dx and the indefinite form as ∫ x dx.

BinaryOp

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BinaryOp {
    Add,        // left + right
    Subtract,   // left - right
    Multiply,   // left * right
    Divide,     // left / right
    Power,      // left ^ right
    Modulo,     // left % right
}
VariantSymbolNotes
Add+Real and complex
Subtract-Real and complex
Multiply*Real and complex
Divide/Returns MathError::DivisionByZero when denominator is zero
Power^l.powf(r) for reals; l.powc(r) for complex
Modulo%Real only; not defined for complex numbers
BinaryOp::Modulo is not defined for complex operands. Attempting it returns MathError::InvalidOperation("Modulo not defined for complex numbers").

UnaryOp

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum UnaryOp {
    Negate,     // -(expr)
    Factorial,  // expr!
    Abs,        // |expr|
}
VariantNotationNotes
Negate-(x)Works on both real and complex values
Factorialx!Requires a non-negative integer; returns MathError::InvalidOperation otherwise
Abs|x|Returns the magnitude (norm) for complex values

Helper Methods

Expr exposes several convenience methods that are used extensively throughout the library.

Constant Constructors

let zero = Expr::zero(); // Expr::Number(0.0)
let one  = Expr::one();  // Expr::Number(1.0)

Predicate Methods

// true iff the value is within f64::EPSILON of 0
expr.is_zero() -> bool

// true iff the value is within f64::EPSILON of 1
expr.is_one() -> bool

// true iff the expression contains no Symbol nodes
// (Numbers, Complex, and operations/functions on constants)
expr.is_constant() -> bool

// true iff the expression tree contains the named Symbol
expr.contains_var("x") -> bool
use mathcore::MathCore;

let expr = MathCore::parse("3 + 4").unwrap();
assert!(expr.is_constant());

let expr = MathCore::parse("x^2 + 1").unwrap();
assert!(expr.contains_var("x"));
assert!(!expr.contains_var("y"));

degree(var: &str) -> u32

Returns the polynomial degree of the expression with respect to var. This is used by the solver and simplifier.
let expr = MathCore::parse("x^3 + 2*x^2 + x + 1").unwrap();
assert_eq!(expr.degree("x"), 3);

let expr = MathCore::parse("5").unwrap();
assert_eq!(expr.degree("x"), 0);
degree only understands polynomial structure via Symbol, Power, Multiply, Add, and Subtract. It returns 0 for function nodes and other constructs even if those expressions are technically polynomial in var.

Display Formatting

Expr implements fmt::Display. The output is a parenthesised infix notation for binary expressions, and standard mathematical notation for everything else.
use mathcore::MathCore;

let expr = MathCore::parse("x^2 + 2*x + 1").unwrap();
println!("{}", expr);
// → (((x ^ 2) + (2 * x)) + 1)
Expression kindExample output
Number42, 3.14
Complex3+4i, 1-2i
Symbolx, theta
Binary(x + y), (a * b)
Unary Negate-(x)
Unary Factorial5!
Unary Abs|x|
Functionsin(x), log(x, 10)
Derivatived^1/dx^1(x^2)
Integral∫[0,1] x dx

The Context Type

Context maps variable names to Expr values and holds custom function implementations. The Engine is initialised with a Context and consults it whenever it encounters a Symbol node.
use mathcore::types::Context;
use mathcore::Expr;

let mut ctx = Context::new();
ctx.set_var("a", Expr::Number(3.0));
ctx.set_var("b", Expr::Number(4.0));

// ctx.get_var("a") → Some(&Expr::Number(3.0))
Context::with_defaults() pre-populates the three standard mathematical constants:
NameValue
pi3.141592653589793 (f64::consts::PI)
e2.718281828459045 (f64::consts::E)
tau6.283185307179586 (f64::consts::TAU)
The Engine created by Engine::new() uses Context::with_defaults(), so pi, e, and tau are always available without any extra setup.
use mathcore::MathCore;

let math = MathCore::new();
let result = math.calculate("2 * pi").unwrap();
// → 6.283185307179586

Build docs developers (and LLMs) love