delaunay 0.8.0

D-dimensional Delaunay triangulations and convex hulls in Rust, with exact predicates, deterministic degeneracy handling, explicit topology validation, and bistellar flips for finite point sets.
Documentation
# Performance Tuning

Use this workflow when making performance-sensitive Rust changes. Performance
work in this crate is scientific evidence: measure representative behavior,
preserve invariants, and report exactly what changed.

---

## Contents

- [Core Rule]#core-rule
- [Benchmark Before Editing]#benchmark-before-editing
- [Add a Benchmark When None Exists]#add-a-benchmark-when-none-exists
- [Dimension Coverage]#dimension-coverage
- [Performance Scope]#performance-scope
- [Invariant-Preserving Changes]#invariant-preserving-changes
- [Benchmark After Editing]#benchmark-after-editing
- [Validation]#validation
- [Reporting]#reporting

---

## Core Rule

Do not claim a performance improvement without before/after evidence from the
same representative benchmark command.

The preferred loop is:

1. Identify the hot path and the existing benchmark that covers it.
2. Run the benchmark before editing and record representative results.
3. Make the smallest invariant-preserving change.
4. Rerun the same benchmark and compare the same named cases.
5. Run the appropriate invariant validator before handoff.

## Benchmark Before Editing

Before changing performance-sensitive code, run the smallest benchmark that
covers the claimed hot path. Use the `perf` profile for measured data:

```bash
cargo bench --profile perf --bench <bench-name>
```

Use `just bench-smoke` only for harness validation. Do not treat smoke output
as performance evidence.

Record:

- exact command
- benchmark case names
- before timings or throughput
- whether the benchmark covers the dimensions and input families affected by
  the change

## Add a Benchmark When None Exists

If no benchmark covers the affected hot path, add one before optimizing.

Benchmarks should:

- use deterministic fixtures
- cover realistic sizes for the operation
- include adversarial or near-degenerate inputs when the algorithm is sensitive
  to geometry or topology
- keep setup outside Criterion-measured closures
- avoid logging, parsing, allocation-heavy configuration, or validation work
  inside measured closures unless that is the behavior being measured
- use `#[cfg(feature = "bench")]` fixture helpers only when the benchmark needs
  deliberately invalid-but-structurally-coherent topology

Do not weaken public constructors, validation, typed errors, or topology
guarantees to make benchmark fixtures easier to build.

## Dimension Coverage

For dimension-generic hot paths, benchmarks and correctness tests should cover
2D through 5D whenever feasible.

Prefer per-dimension benchmark groups or cases with explicit names such as:

```text
delete_vertex/success/2d/...
delete_vertex/success/3d/...
delete_vertex/success/4d/...
delete_vertex/success/5d/...
```

If a dimension is intentionally omitted, document why in the benchmark or the
change summary.

## Performance Scope

Performance is a design goal but is strictly subordinate to scientific
invariants: numerical correctness, topological correctness, API stability,
composability, and clarity. Never trade invariants or diagnostics for speed; if
performance and invariants appear to conflict, re-scope the problem.

In scope:

- d-dimensional Delaunay triangulations for small-to-medium dimensions,
  typically `2 <= D <= 7`
- single-threaded in-memory construction
- `DenseSlotMap`-backed topology
- Hilbert-ordered insertion
- allocation-conscious local repair and validation paths

Out of scope:

- massively parallel or GPU meshing
- out-of-core triangulations
- sparse sampling
- dynamic remeshing at scale

Those domains belong to specialized tools such as CGAL, TetGen, or Gmsh.

Within scope, prefer:

- allocation-free hot paths through stack arrays, `SmallBuffer`, borrowed views,
  and iterators
- Shewchuk-style f64 fast filters with cold exact-arithmetic fallbacks
- `const fn` for pure helpers where the inputs allow
- typed flip, insertion, and repair budgets instead of heuristic timeouts

## Invariant-Preserving Changes

Performance fixes must preserve the crate's scientific invariant model.

Prefer optimizations that make existing evidence explicit:

- carry local repair seed scopes instead of rediscovering global state
- reuse validated handles, keys, or proof-bearing types
- move repeated validation out of inner loops only when validation evidence is
  still represented by the API
- reduce allocation through existing stack buffers, borrowed views, or iterator
  streaming
- keep rollback and typed error paths intact

Do not optimize by:

- skipping numerical, topological, or Delaunay validation that protects public
  invariants
- replacing typed errors with `bool`, `Option`, sentinels, strings, or panics
- introducing stale caches without an explicit invalidation story
- changing public semantics without an intentional API decision
- adding `unsafe`

## Benchmark After Editing

After the change, rerun the same benchmark command used for the baseline.

Compare the same named cases. Treat a clear representative regression as a
failed optimization: revert, narrow, or redesign before handoff. Small mixed
changes can be normal for noisy microbenchmarks, but the summary must say so
honestly.

When Criterion reports a statistically significant improvement, include the
before and after medians or the reported percentage change. For example:

```text
4D near-boundary 100 vertices: 291.26 ms -> 52.61 ms, -81.83%
```

## Validation

Benchmarks are not the only invariant oracle, but they must not publish
performance evidence for invariant-violating results.

While iterating, run focused tests that cover the invariant the optimization
relies on. Add a regression test when the optimization depends on internal
evidence that could be lost later, such as a local repair seed scope. Benchmark
harnesses should assert known-answer checks or validation results around the
measured workflow; keep those checks outside the Criterion-measured closure
unless validation itself is the behavior being measured.

For final handoff after Rust code changes, run:

```bash
just ci
```

For documentation-only, configuration-only, or Python-only changes, follow the
command matrix in [`commands.md`](commands.md).

## Reporting

Performance-change summaries should include:

- hot path changed
- invariant-preserving mechanism
- benchmark command
- before/after results for representative 2D-5D cases when applicable
- invariant validation command and result
- any benchmark gaps or dimensions intentionally not covered