delaunay 0.7.6

D-dimensional Delaunay triangulations and convex hulls in Rust, with exact predicates, multi-level validation, and bistellar flips
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
# Rust Development Guidelines

Rust coding conventions for this repository.

Agents must follow these rules when modifying or adding Rust code.

---

## Contents

- [Core Principles]#core-principles
- [Safety]#safety
- [Dimension Generic Architecture]#dimension-generic-architecture
- [Borrowing and Ownership]#borrowing-and-ownership
- [Error Handling]#error-handling
- [Panic Policy]#panic-policy
- [Error Types]#error-types
  - [Orthogonal variants]#orthogonal-variants
  - [Struct‑with‑named‑fields throughout]#structwithnamedfields-throughout
  - [Preserve typed sources — no boxing, no `dyn Error`]#preserve-typed-sources--no-boxing-no-dyn-error
  - [Do not stringify; carry typed context instead]#do-not-stringify-carry-typed-context-instead
  - [Derive `Clone, Debug, Error, PartialEq, Eq`]#derive-clone-debug-error-partialeq-eq
- [Naming and Paths]#naming-and-paths
- [Imports]#imports
- [Module Layout]#module-layout
- [Prelude Design]#prelude-design
- [Documentation]#documentation
- [Integration Tests]#integration-tests
- [Testing Expectations]#testing-expectations
- [Performance]#performance
- [External Dependencies]#external-dependencies
- [Formatting and Lints]#formatting-and-lints
- [API Stability]#api-stability
- [Logging and Diagnostics]#logging-and-diagnostics
- [Preferred Patch Style]#preferred-patch-style

---

## Core Principles

This project is a **scientific computational geometry library**.

Key goals:

- Correctness
- Predictable performance
- API stability
- Zero unsafe code
- Dimension-generic architecture

All design decisions should prioritize these goals.

---

## Safety

Unsafe Rust is forbidden.

The crate enforces:

```rust
#![forbid(unsafe_code)]
```

Agents must never introduce:

- `unsafe`
- `unsafe fn`
- `unsafe impl`
- `unsafe` blocks

---

## Dimension Generic Architecture

The library is generic over dimension using const generics:

```rust
const D: usize
```

Code must remain compatible with:

- 2D
- 3D
- 4D
- 5D

Avoid hard‑coding dimension assumptions unless they are explicitly isolated.

Prefer patterns like:

```rust
struct Point<const D: usize> {
    coords: [f64; D],
}
```

Algorithms should operate generically over `D` whenever practical.

---

## Borrowing and Ownership

Prefer **borrowing APIs** whenever possible.

### Function arguments

Prefer:

```rust
fn foo(points: &[Point<D>])
```

Instead of:

```rust
fn foo(points: Vec<Point<D>>)
```

### Return values

Prefer borrowed results:

```rust
fn vertex(&self, key: VertexKey) -> Option<&Vertex<D>>
```

Avoid unnecessary allocations.

Public APIs should also avoid unnecessary cloning. Prefer returning references
or iterators over internal data instead of cloning structures.

Avoid patterns like:

```rust
fn vertices(&self) -> Vec<Vertex<D>> {
    self.vertices.clone()
}
```

Prefer borrowed views instead:

```rust
fn vertices(&self) -> &[Vertex<D>] {
    &self.vertices
}
```

Cloning large structures in public APIs can introduce hidden performance
costs and should only be done when ownership transfer is required.

Only return owned values (`Vec`, `String`, etc.) when necessary.

---

## Error Handling

Public APIs must **not panic**.

Use explicit error propagation.

### Fallible public functions

Return `Result`:

```rust
pub fn insert_vertex(...) -> Result<VertexKey, InsertError>
```

### Lookup functions

Return `Option`:

```rust
pub fn vertex(&self, key: VertexKey) -> Option<&Vertex<D>>
```

### Infallible APIs

These should return values directly:

Infallible functions **must not return `Result`**.
If a function cannot fail under normal operation, it should return its value
directly rather than wrapping it in `Result`. Returning `Result` from
infallible APIs is considered unidiomatic and unnecessarily complicates
callers.

- `len()`
- `is_empty()`
- iterators
- accessors
- builder setters

Example:

```rust
pub fn len(&self) -> usize
```

Examples of infallible APIs include:

- accessors (`len`, `dimension`, `capacity`)
- iterators and views
- builder setters
- simple queries over internal state

If a function may fail due to invalid input or algorithmic conditions, it
should return `Result`. If the value may or may not exist (e.g. lookup by key),
return `Option`.

Do not introduce artificial error types simply to satisfy a `Result` return type.

### Builder pattern

Builder setters return `Self`.

Errors occur in `build()`.

Example:

```rust
builder
    .with_capacity(100)
    .with_seed(seed)
    .build()?;
```

---

## Panic Policy

Panics should be avoided in library code.

Acceptable panic situations:

- internal invariants violated
- unreachable logic errors
- debugging assertions

Prefer returning:

- `Result`
- `Option`

instead of panicking.

---

## Error Types

Errors should be defined **within the module where they are used**.

Avoid large centralized error enums.

Example:

```rust
#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)]
#[non_exhaustive]
pub enum InsertError {
    #[error("duplicate vertex")]
    DuplicateVertex,
}
```

The sub‑sections below spell out the conventions that keep error values
**debuggable, composable, and stable**. They apply to every new error enum
and to edits of existing ones.

### Orthogonal variants

Every variant represents a **distinct failure mode**. Two variants must not
overlap in meaning: if a caller can't decide which one to match on, the
taxonomy is wrong.

When the same underlying condition occurs in two different contexts
(e.g. primary failure vs. failure during fallback), model it with
**separate variants that each carry the full typed context**, not with a
single variant and a free‑form `context: String` field.

Good:

```rust
pub enum DelaunayizeError {
    TopologyRepairFailed {
        source: PlManifoldRepairError,
    },
    TopologyRepairFailedWithRebuild {
        source: PlManifoldRepairError,
        rebuild_error: DelaunayTriangulationConstructionError,
    },
    DelaunayRepairFailed {
        source: DelaunayRepairError,
    },
    DelaunayRepairFailedWithRebuild {
        source: DelaunayRepairError,
        rebuild_error: DelaunayTriangulationConstructionError,
    },
}
```

Each pair `Failed` / `FailedWithRebuild` is **orthogonal**: the caller
always knows whether a fallback was attempted, and if so which specific
rebuild error was produced.

### Struct‑with‑named‑fields throughout

Prefer **struct variants with named fields** over positional (tuple) variants,
even for single‑field carriers. Named fields:

- document the semantics of each payload at the declaration site,
- keep `Display` format strings readable (`{source}`, `{rebuild_error}`),
- let downstream code pattern‑match by field name without caring about
  positional order,
- remain additive: adding a new field is a compile‑error surface that
  forces callers to consider it.

Prefer:

```rust
#[error("Invalid facet index {index} for cell with {facet_count} facets")]
InvalidFacetIndex {
    index: u8,
    facet_count: usize,
},
```

Avoid:

```rust
#[error("Invalid facet index {0} for cell with {1} facets")]
InvalidFacetIndex(u8, usize),
```

### Preserve typed sources — no boxing, no `dyn Error`

Source and "secondary" errors must be stored **by value as typed enums**,
not as `Box<dyn Error>`, not as `anyhow::Error`, and not stringified into a
`message: String` field. The whole point of the taxonomy is that consumers
can pattern-match the full structured error, while [`Error::source`]
exposes whichever field is annotated as the primary source.

- Use `#[source]` (and `#[from]` where the conversion is unambiguous) on
  the typed field so `thiserror` wires up the source chain.
- Use `Box<T>` only when the **typed** payload would make the enum
  unbalanced in size (e.g. `NonConvergent` carries a fat diagnostics
  struct); the inner type is still fully typed.
- Never replace a typed error with a `String` just because the enum lived
  in a different crate — that erases variant and source information.

```rust
// Good: typed rebuild error preserved by value; primary source chain intact.
TopologyRepairFailedWithRebuild {
    #[source]
    source: PlManifoldRepairError,
    rebuild_error: DelaunayTriangulationConstructionError,
},
```

```rust
// Bad: stringification erases the typed variant.
TopologyRepairFailedWithRebuild {
    source: PlManifoldRepairError,
    rebuild_message: String,
},
```

### Do not stringify; carry typed context instead

Free‑form `message: String` fields are only acceptable when the context is
genuinely unstructured prose (rare). In practice, **most** "context" is
structured — indices, counts, keys, UUIDs, other enums — and belongs in
named fields of a struct variant.

Prefer:

```rust
#[error("Ridge indices ({omit_a}, {omit_b}) out of bounds for cell {cell_key:?} with {vertex_count} vertices")]
InvalidRidgeIndex {
    cell_key: CellKey,
    omit_a: u8,
    omit_b: u8,
    vertex_count: usize,
},
```

Avoid:

```rust
#[error("Ridge indices out of bounds: {message}")]
InvalidRidgeIndex {
    message: String,
},
```

Structured payloads support:

- test assertions via `assert_eq!` / `matches!` without string parsing,
- diagnostic tools that filter or aggregate by field,
- localization and richer `Display` implementations without rewriting
  call‑sites.

### Derive `Clone, Debug, Error, PartialEq, Eq`

All error enums should derive the standard set:

```rust
#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)]
#[non_exhaustive]
pub enum FooError { ... }
```

- `Clone` — lets callers attach the error to multiple diagnostics paths
  and lets tests construct expected values once and compare them.
- `Debug` — required for `Error`.
- `thiserror::Error` — wires up `Display` and `source()`.
- `PartialEq, Eq` — deriveable whenever all payload types are `Eq`
  (integers, strings, UUIDs, keys, other `Eq` enums, `Arc<T>` /
  `Box<T>` where `T: Eq`). All error enums in this crate satisfy
  this today. Skip these only when a payload genuinely cannot be `Eq`
  (e.g. `f64`, `io::Error`, `Box<dyn Error>`) — none of which belong in
  error values anyway.
- `#[non_exhaustive]` — new variants must remain additive; downstream
  matches need a `_` arm.

Use `assert_eq!` for fixed‑shape variants in tests; prefer `matches!` for
"just check the variant" when the payload contains long free‑form strings
or nondeterministic samples.

---

## Naming and Paths

Function names should be concise but specific. Prefer short verbs and domain
terms over names that restate the module, type, or every implementation detail.

Prefer:

```rust
fn align_offsets(...)
fn validate_link(...)
fn rebuild_candidate(...)
```

Avoid:

```rust
fn align_periodic_vertex_offsets_for_source_cell_to_target_cell(...)
fn validate_manifold_link_consistency_for_all_ridges(...)
fn rebuild_delaunay_triangulation_candidate_after_repair_failure(...)
```

Use short, unqualified paths inside function bodies. If a function needs a type,
trait, constant, or helper from another module, import it at the top of the
module and refer to the item by its short name locally.

---

## Imports

Always import types at the top of the module rather than using fully‑qualified
paths inline. This keeps code readable and consistent.

Prefer:

```rust
use crate::core::tds::TdsError;

fn check(err: &TdsError) -> bool { ... }
```

Instead of:

```rust
fn check(err: &crate::core::tds::TdsError) -> bool { ... }
```

Group imports from the same module into a single `use` statement with braces:

```rust
use crate::core::tds::{
    CellKey, EntityKind, Tds, TdsError, VertexKey,
};
```

Do not add `use` statements inside function bodies just to shorten a path.
Move those imports to the top of the module. Local imports are acceptable only
when they are intentionally scoped for conditional compilation, tests, macro
expansion, or to avoid a documented name collision.

If a test module already has `use super::*;`, do not re‑import items that are
already brought into scope by the parent module's imports.

---

## Module Layout

Never use `mod.rs`.

Modules are declared from `src/lib.rs`.

Example:

```rust
pub mod core;
pub mod geometry;
pub mod algorithms;
```

Nested modules may use inline declaration:

```rust
pub mod core {
    pub mod triangulation;
    pub mod vertex;
}
```

---

## Prelude Design

Preludes should remain **small and purpose‑specific**.

A prelude should import only the items needed for a specific task.

Example:

```text
delaunay::prelude::triangulation
```

Avoid giant catch‑all preludes.

---

## Documentation

All public items must have documentation. Public functions must include a
doctest in their documentation.

Example:

```rust
/// Inserts a vertex into the triangulation.
///
/// Returns the key of the inserted vertex.
///
/// # Examples
///
/// ```rust
/// # use delaunay::prelude::triangulation::*;
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut triangulation = DelaunayTriangulation::<_, _, _, 2>::default();
/// let key = triangulation.insert_vertex([0.0, 0.0])?;
/// assert!(triangulation.contains_vertex(key));
/// # Ok(())
/// # }
/// ```
pub fn insert_vertex(...)
```

### Private functions

Private functions must have a brief doc comment (`///`) explaining **why they
exist** — what problem they solve or what invariant they maintain. The *what*
is often clear from the signature; the *why* is not.

Prefer:

```rust
/// Aligns source-cell periodic offsets into the target-cell frame so
/// cross-cell insphere predicates see consistent lifted coordinates.
fn align_periodic_offset<const D: usize>(...) -> Result<[i8; D], FlipError>
```

Use normal comments (`//`) for documentation inside function bodies or other
implementation-local notes:

```rust
fn align_periodic_offset<const D: usize>(...) -> Result<[i8; D], FlipError> {
    // Compare deltas in each coordinate so conflicting frame translations are
    // rejected before lifted coordinates are constructed.
    ...
}
```

A bare signature with no context forces readers to reverse-engineer
intent from the implementation.

After Rust changes, verify documentation builds:

```bash
just doc-check
```

or

```bash
cargo doc
```

---

## Integration Tests

Integration tests live in:

```text
tests/
```

Each integration test crate should include a crate‑level doc comment:

```rust
//! Integration tests for triangulation invariants.
```

This satisfies `clippy::missing_docs` in CI.

Fixed-bug regression integration tests belong in `tests/regressions.rs` unless
they need separate crate-level configuration, feature flags, or profile
isolation.

---

## Testing Expectations

Rust changes should be validated with:

```bash
just test
just test-integration
```

Property tests are preferred for geometric invariants such as:

- Euler characteristic checks
- simplex adjacency invariants
- manifold consistency

---

## Performance

Avoid unnecessary allocations.

Prefer:

- iterators
- slices
- stack arrays `[T; D]`
- fixed‑size containers

Avoid cloning large structures unless necessary.

---

## External Dependencies

Dependencies should be minimal.

Before adding a dependency, consider:

1. compile time impact
2. MSRV compatibility
3. maintenance status
4. dependency tree size

---

## Formatting and Lints

Code must pass:

```bash
cargo fmt
cargo clippy
```

Typically run via:

```bash
just fix
just check
```

CI treats warnings as errors.

### Lint Suppression

When suppressing a lint, use `#[expect(...)]` instead of `#[allow(...)]`.

`expect` causes a compiler warning if the lint is no longer triggered,
ensuring suppressions are removed when they become unnecessary.

Always include a `reason`:

```rust
#[expect(clippy::too_many_lines, reason = "test covers multiple cases")]
fn test_large_dataset_performance() { ... }
```

---

## API Stability

The crate is intended for external use.

Agents must avoid:

- breaking public APIs
- renaming public types
- removing public functions

If an API change is necessary, prefer:

```rust
#[deprecated]
```

with migration guidance.

---

## Logging and Diagnostics

Use `tracing` for committed diagnostics across production code, tests,
and benchmarks. This includes library/runtime code, non-trivial test
diagnostics, and debugging of numerical instability or topological
invariants. Prefer `tracing::debug!`, `tracing::trace!`, etc. over
ad-hoc printing.

This ensures all diagnostic output is:

- filterable via `RUST_LOG` / `tracing-subscriber`
- structured and machine-parseable
- suppressible in production builds

`eprintln!` is acceptable only for short-lived local debugging while
investigating an issue. Do not leave it in committed code when `tracing`
or a typed error path is more appropriate.

Debug hooks gated on environment variables should still use `tracing`:

```rust
#[cfg(debug_assertions)]
if std::env::var_os("DELAUNAY_DEBUG_FOO").is_some() {
    tracing::debug!("diagnostic message: {value}");
}
```

### Tests and Benchmarks

- Use `tracing` for non-trivial test diagnostics rather than
  `eprintln!`, especially when diagnosing geometric predicate behavior,
  invariant failures, or shrink/reproduction context.
- Never log inside hot benchmark loops or Criterion-measured closures.
  Emit diagnostics before or after the measured path so measurements stay
  meaningful.
- Gate non-essential test and benchmark diagnostics behind feature flags.
  In this repository, use `test-debug` for test diagnostics and
  `bench-logging` for benchmark diagnostics:

```rust
#[cfg(feature = "test-debug")]
tracing::debug!("test diagnostic");

#[cfg(feature = "bench-logging")]
tracing::debug!("benchmark diagnostic");
```

---

## Preferred Patch Style

When modifying Rust code:

- make **small focused changes**
- avoid large refactors
- maintain existing naming conventions
- preserve module boundaries