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
}
| Variant | Symbol | Notes |
|---|
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|
}
| Variant | Notation | Notes |
|---|
Negate | -(x) | Works on both real and complex values |
Factorial | x! | 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.
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 kind | Example output |
|---|
Number | 42, 3.14 |
Complex | 3+4i, 1-2i |
Symbol | x, theta |
Binary | (x + y), (a * b) |
Unary Negate | -(x) |
Unary Factorial | 5! |
Unary Abs | |x| |
Function | sin(x), log(x, 10) |
Derivative | d^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:
| Name | Value |
|---|
pi | 3.141592653589793 (f64::consts::PI) |
e | 2.718281828459045 (f64::consts::E) |
tau | 6.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