# multicalc
[](https://crates.io/crates/multicalc)

[](https://github.com/kmolan/multicalc-rust/actions/workflows/ci.yml)
[](https://docs.rs/multicalc)
[](./LICENSE)
`multicalc` is a Rust library for single- and multi-variable calculus: derivatives, integrals,
Jacobians and Hessians, vector-field operators, and Taylor approximation in a `no-std` environment.
## Why use it
- Pure, safe Rust.
- `no_std`, with no heap allocation and no panics — it runs on bare-metal and embedded targets.
- Transcendental functions come from [`libm`](https://crates.io/crates/libm), so the math works
without `std`.
- Every fallible call returns a typed [`CalcError`](./src/utils/error_codes.rs); convenience
wrappers fill in sensible defaults.
- A runnable example for every module, and a test suite covering each error path.
## What it does
- **Differentiation** of any order (finite differences), total and partial.
- **Integration** of any order:
- Iterative rules — Boole, Simpson, Trapezoidal — over finite, semi-infinite, and infinite limits.
- Gaussian quadrature — Gauss-Legendre, Gauss-Hermite, Gauss-Laguerre.
- **Jacobian** and **Hessian** matrices.
- **Vector calculus** — line and flux integrals, curl, and divergence.
- **Approximation** — linear and quadratic (Taylor) models, with goodness-of-fit metrics.
## Install
```sh
cargo add multicalc
```
Enable the `alloc` feature if you want the heap-based methods (see [Heap allocation](#heap-allocation)).
### A note on `no_std`
Methods like `f64::sin` are only available with `std`. In a `no_std` crate, call the `libm` versions
instead — for example `libm::sin(x)` in place of `x.sin()`. `multicalc` re-exports `libm`, so you can
reach it as `multicalc::libm`. The examples below use `x.sin()` because they assume `std`.
## At a glance
### Derivatives
```rust
use multicalc::numerical_derivative::derivator::DerivatorSingleVariable;
use multicalc::numerical_derivative::finite_difference::FiniteDifferenceSingle;
let first = d.get(1, &f, 2.0).unwrap(); // 12.0
let third = d.get(3, &f, 2.0).unwrap(); // 6.0
// get_single / get_double are wrappers for orders 1 and 2
```
For several variables, the derivative order is just the number of indices you pass:
```rust
use multicalc::numerical_derivative::derivator::DerivatorMultiVariable;
use multicalc::numerical_derivative::finite_difference::FiniteDifferenceMulti;
// g(x, y, z) = y*sin(x) + x*cos(y) + x*y*e^z
let point = [1.0, 2.0, 3.0];
let dx = d.get_single_partial(&g, 0, &point).unwrap(); // dg/dx
let mixed = d.get(&g, &[0, 1], &point).unwrap(); // d(dg/dx)/dy
```
### Integration
```rust
use multicalc::numerical_integration::integrator::IntegratorSingleVariable;
use multicalc::numerical_integration::iterative_integration::IterativeSingle;
let area = integrator.get_single(&f, &[0.0, 2.0]).unwrap(); // 4.0
// infinite and semi-infinite limits are supported for decaying integrands
let bell = integrator
.get_single(&|x| (-x * x).exp(), &[f64::NEG_INFINITY, f64::INFINITY])
.unwrap(); // sqrt(pi)
```
Choose the rule and interval count with `from_parameters`:
```rust
use multicalc::numerical_integration::mode::IterativeMethod;
let integrator = IterativeSingle::from_parameters(120, IterativeMethod::Simpsons);
```
### Gaussian quadrature
Each Gaussian rule integrates over a fixed domain. Pass the **bare** integrand `f(x)` — the weights
already carry the weighting factor.
```rust
use multicalc::numerical_integration::integrator::IntegratorSingleVariable;
use multicalc::numerical_integration::gaussian_integration::GaussianSingle;
use multicalc::numerical_integration::mode::GaussianQuadratureMethod;
// Gauss-Hermite integrates f(x) * e^(-x^2) over the whole real line.
let hermite = GaussianSingle::from_parameters(5, GaussianQuadratureMethod::GaussHermite);
let val = hermite
.get_single(&|x| x * x, &[f64::NEG_INFINITY, f64::INFINITY])
.unwrap(); // sqrt(pi)/2
```
| Gauss-Legendre | $\int_a^b f(x)\, \mathrm{d}x$ |
| Gauss-Laguerre | $\int_0^\infty f(x)\, e^{-x}\, \mathrm{d}x$ |
| Gauss-Hermite | $\int_{-\infty}^\infty f(x)\, e^{-x^2}\, \mathrm{d}x$ |
### Jacobian and Hessian
```rust
use multicalc::numerical_derivative::finite_difference::FiniteDifferenceMulti;
use multicalc::numerical_derivative::jacobian::Jacobian;
use multicalc::numerical_derivative::hessian::Hessian;
let functions: [&dyn Fn(&[f64; 3]) -> f64; 2] = [&f1, &f2];
let jacobian = Jacobian::<FiniteDifferenceMulti>::default();
let j = jacobian.get(&functions, &[1.0, 2.0, 3.0]).unwrap(); // [[6, 3, 2], [2, 4, 0]]
let h = hessian.get(&g, &[1.0, 2.0]).unwrap();
```
### Vector calculus
```rust
use multicalc::numerical_derivative::finite_difference::FiniteDifferenceMulti;
use multicalc::vector_field::{curl, divergence, line_integral, flux_integral};
// field (2xy, 3cos y)
let field: [&dyn Fn(&[f64; 2]) -> f64; 2] =
[&(|v: &[f64; 2]| 2.0 * v[0] * v[1]), &(|v: &[f64; 2]| 3.0 * v[1].cos())];
let derivator = FiniteDifferenceMulti::default();
let c = curl::get_2d(derivator, &field, &[1.0, 3.14]).unwrap();
let d = divergence::get_2d(derivator, &field, &[1.0, 3.14]).unwrap();
// field (y, -x) along the unit circle (cos t, sin t)
let g: [&dyn Fn(&[f64; 2]) -> f64; 2] = [&(|v: &[f64; 2]| v[1]), &(|v: &[f64; 2]| -v[0])];
let curve: [&dyn Fn(f64) -> f64; 2] = [&(|t: f64| t.cos()), &(|t: f64| t.sin())];
let limit = [0.0, 2.0 * std::f64::consts::PI];
let line = line_integral::get_2d(&g, &curve, &limit).unwrap(); // -2*pi
let flux = flux_integral::get_2d(&g, &curve, &limit).unwrap(); // 0
```
### Approximation
```rust
use multicalc::approximation::linear_approximation::LinearApproximator;
use multicalc::numerical_derivative::finite_difference::FiniteDifferenceMulti;
.get(&f, &[1.0, 2.0, 3.0])
.unwrap();
let y = model.predict(&[1.1, 2.1, 3.1]);
// model.get_prediction_metrics(&samples, &f) returns RMSE, R^2, and more
```
`QuadraticApproximator` works the same way and captures curvature as well.
## Error handling
Where a sensible default exists, a "safe" wrapper (such as `get_single` or `get_double`) returns the
answer directly. Otherwise the call returns `Result<f64, CalcError>`, so you decide how to handle bad
input. All variants are listed in [error_codes.rs](./src/utils/error_codes.rs).
## Heap allocation
By default everything uses fixed-size stack arrays. Enable the `alloc` feature for `Vec`-based methods
that handle inputs too large for the stack. This currently covers the Jacobian's `get_on_heap`, which
returns a `Vec<Vec<f64>>`.
## Examples
Runnable, self-contained programs for each module live in [`examples/`](./examples) — see
[examples/README.md](./examples/README.md). Run one with:
```sh
cargo run --example <name>
```
## Benchmarks
See [BENCHMARKS.md](./BENCHMARKS.md) for accuracy figures and measured latency.
## Contributing
See [CONTRIBUTIONS.md](./CONTRIBUTIONS.md).
## License
multicalc is licensed under the MIT license.
## Contact
anmolkathail@gmail.com