Common Patterns
Common patterns and best practices when using MathHook, including macro usage guidelines, polynomial construction, substitution patterns, function composition, matrix operations, error handling, performance patterns, and educational features. Includes detailed pitfalls to avoid.
Code Examples
Macro Usage - Correct Patterns
When to use macros vs explicit API
use mathhook::prelude::*;
// ALWAYS use macros for symbols
let x = symbol!(x); // NOT Symbol::new("x")
// Simple expressions - use macros
let expr = expr!(x + y);
let expr = expr!(2 * x);
let expr = expr!(x ^ 2);
// Function calls - use macros
let expr = expr!(sin(x));
Runtime Variables - Explicit API Required
Why macros don't work with loop variables
use mathhook::prelude::*;
// WRONG - creates Symbol("i"), not integer value
for i in 0..10 {
let expr = expr!(i); // BAD!
}
// CORRECT - use explicit API for runtime variables
for i in 0..10 {
let term = Expression::integer(i); // GOOD!
}
// CORRECT - programmatic construction
let x = symbol!(x);
let coefficients = vec![1, 2, 3];
let mut terms = Vec::new();
for i in 0..coefficients.len() {
let coeff = Expression::integer(coefficients[i]);
let x_expr = Expression::from(x.clone());
let power = Expression::integer(i as i64);
terms.push(Expression::mul(vec![coeff, Expression::pow(x_expr, power)]));
}
let polynomial = Expression::add(terms);
Building Polynomials - Dynamic Degree
Construct polynomials with runtime coefficients
use mathhook::prelude::*;
fn build_polynomial(coefficients: &[i64], x: &Symbol) -> Expression {
let mut terms = Vec::new();
for (i, &coeff) in coefficients.iter().enumerate() {
let coeff_expr = Expression::integer(coeff);
let x_expr = Expression::from(x.clone());
let power = Expression::integer(i as i64);
let term = Expression::mul(vec![coeff_expr, Expression::pow(x_expr, power)]);
terms.push(term);
}
Expression::add(terms)
}
let x = symbol!(x);
let poly = build_polynomial(&[1, -5, 6], &x); // x^2 - 5x + 6
Substitution - Single and Multiple
Replace symbols with values
use mathhook::prelude::*;
use std::collections::HashMap;
let x = symbol!(x);
let y = symbol!(y);
let expr = expr!(add: (x * y), x, y);
// Single substitution
let mut vars = HashMap::new();
vars.insert("x".to_string(), Expression::integer(3));
let result = expr.substitute(&vars);
// Multiple substitutions
let mut vars = HashMap::new();
vars.insert("x".to_string(), Expression::integer(2));
vars.insert("y".to_string(), Expression::integer(3));
let result = expr.substitute(&vars);
Function Composition
Compose functions by nesting
use mathhook::prelude::*;
let x = symbol!(x);
// sin(cos(x)) - direct nesting
let composed = expr!(sin(cos(x)));
// Or build step by step
let inner = expr!(cos(x));
let composed_alt = function!(sin, inner);
println!("Composed function: {}", composed);
Performance - Bulk Operations
Efficient batch processing
use mathhook::prelude::*;
let x = symbol!(x);
// Simplify many expressions efficiently
let expressions = vec![
expr!(x + x),
expr!(x * 1),
expr!(add: (x ^ 2), (-(x ^ 2))),
];
let simplified: Vec<_> = expressions
.iter()
.map(|e| e.simplify())
.collect();
Performance - Caching Results
Cache frequently computed expressions
use mathhook::prelude::*;
use std::collections::HashMap;
let x = symbol!(x);
let mut cache: HashMap<String, Expression> = HashMap::new();
let expr = expr!(x ^ 2);
let key = format!("{}", expr);
if let Some(cached) = cache.get(&key) {
println!("Using cached result");
} else {
let result = expr.simplify();
cache.insert(key, result.clone());
}
Common Pitfall - Float Equality
Never use == for approximate values
use mathhook::prelude::*;
// WRONG - comparing floats directly
let val1: f64 = 3.14;
let val2: f64 = 3.14000000001;
// if val1 == val2 { } // BAD!
// CORRECT - use epsilon comparison
let tolerance: f64 = 1e-10;
if (val1 - val2).abs() < tolerance {
println!("Values are approximately equal");
}
// OR use exact rationals for symbolic computation
let exact = Expression::rational(314, 100); // Exact 3.14