Skip to content

fix(core): Fix remove_vertex topology consistency and test failures#124

Merged
acgetchell merged 14 commits intomainfrom
fix/tds-insertion
Nov 22, 2025
Merged

fix(core): Fix remove_vertex topology consistency and test failures#124
acgetchell merged 14 commits intomainfrom
fix/tds-insertion

Conversation

@acgetchell
Copy link
Owner

This commit addresses three critical issues:

  1. Fix remove_vertex to maintain topology consistency

    • Added logic to clear dangling neighbor references in remaining cells
    • Prevents triangulation corruption when removing vertices
    • Added 3 comprehensive unit tests for remove_vertex functionality
    • Fixes failing doctest at triangulation_data_structure.rs:1784
  2. Fix proptest quality metric failures

    • Changed cell comparison from key-order to UUID-based matching
    • Translation/scaling operations can reorder cells in Delaunay triangulation
    • Tests now match cells by their vertex UUIDs instead of assuming same order
    • Fixes prop_normalized_volume and prop_radius_ratio tests in 4D/5D
  3. Fix convex hull integration test

    • Updated test_convex_hull_cache_during_construction with proper hull vertices
    • Previous vertices were all interior, causing "No boundary facets" error

Additional improvements:

  • Improve rollback functionality for missed inserts
  • RobustBowyerWatson now enforces Delaunay property post-insertion
  • Improved duplicate coordinate detection in insertion algorithms
  • Archive completed invariant_validation_plan.md documentation
  • Remove proptest regression files for cleaner repo state

This commit addresses three critical issues:

1. **Fix remove_vertex to maintain topology consistency**
   - Added logic to clear dangling neighbor references in remaining cells
   - Prevents triangulation corruption when removing vertices
   - Added 3 comprehensive unit tests for remove_vertex functionality
   - Fixes failing doctest at triangulation_data_structure.rs:1784

2. **Fix proptest quality metric failures**
   - Changed cell comparison from key-order to UUID-based matching
   - Translation/scaling operations can reorder cells in Delaunay triangulation
   - Tests now match cells by their vertex UUIDs instead of assuming same order
   - Fixes prop_normalized_volume and prop_radius_ratio tests in 4D/5D

3. **Fix convex hull integration test**
   - Updated test_convex_hull_cache_during_construction with proper hull vertices
   - Previous vertices were all interior, causing "No boundary facets" error

Additional improvements:
- Improve rollback functionality for missed inserts
- RobustBowyerWatson now enforces Delaunay property post-insertion
- Improved duplicate coordinate detection in insertion algorithms
- Archive completed invariant_validation_plan.md documentation
- Remove proptest regression files for cleaner repo state
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 12, 2025

Walkthrough

Introduces a unified two-stage insertion pipeline (fast + robust), extensive invariant and Delaunay validation/diagnostics, transactional rollback/repair paths, API signature changes (e.g., Vertex::from_points(&[Point]), Tds::new -> Result), pervasive test updates, documentation and CI/tooling adjustments, and small spelling additions.

Changes

Cohort / File(s) Summary
Unified insertion & orchestration
src/core/algorithms/unified_insertion_pipeline.rs, src/core/algorithms/...bowyer_watson.rs, src/core/algorithms/...robust_bowyer_watson.rs
New UnifiedInsertionPipeline coordinating fast (IncrementalBowyerWatson) and robust (RobustBowyerWatson) insertion paths; per‑vertex outcomes, policy-driven global Delaunay validation/repair, unsalvageable reports, statistics, and pipeline-impl of InsertionAlgorithm.
Insertion trait & transactional flow
src/core/traits/insertion_algorithm.rs
Trait refactor: public insert_vertex wrapper with duplicate-detection delegates to insert_vertex_impl; added helpers for finding Delaunay violations, rollback/restore, iterative cavity refinement, stricter finalization and robust-predicate paths.
Robust algorithm internals
src/core/algorithms/robust_bowyer_watson.rs
Large additions: robust predicate fallbacks, repair_global_delaunay_violations, unsalvageable_vertices state and accessors, RobustInsertionInfo, extended diagnostic/error propagation and new robust/rollback insertion flows.
Incremental algorithm
src/core/algorithms/bowyer_watson.rs
Minor refactors and docs changes; added statistics() accessor; implementation entrypoint renamed to insert_vertex_impl to match trait split.
Triangulation validation & diagnostics
src/core/triangulation_data_structure.rs, src/core/util.rs
Added diagnostics/statistics/report types, public Delaunay validation APIs (is_delaunay, find_delaunay_violations, validate_delaunay), new validation error kinds and TriangulationDiagnostics/Statistics surface.
Cell / Vertex construction & validation
src/core/cell.rs, src/core/vertex.rs
Cell::new now rejects duplicate vertices early; cell! macro accepts slice-borrowable inputs; Vertex::from_points signature changed to accept &[Point] (many call sites updated).
Tds construction & error surface
src/geometry/algorithms/convex_hull.rs, src/geometry/quality.rs, src/core/triangulation_data_structure.rs
Tds::new now returns Result<Tds<...>, TriangulationConstructionError>; callers/tests updated to handle construction failures (e.g., geometric degeneracy).
Tests — pervasive updates
tests/*, proptest-regressions/*
Widespread test changes: adapt to Vertex::from_points(&[Point]), handle Tds::new Result, add proptest_duplicates and deterministic regressions, UUID-based cell matching, degeneracy filtering, slow-tests gating, and pruning/removal of regression seeds.
Examples & docs
examples/*, README.md, docs/*, docs/archive/*
Examples reduced to 20-point configs, new docs (e.g., docs/archive/invariant_validation_plan.md, docs/archive/fix-delaunay.md), updated correctness guarantees, and insertion-pipeline documentation and references.
Tooling & CI
Cargo.toml, rust-toolchain.toml, justfile, .github/workflows/ci.yml, clippy.toml, CONTRIBUTING.md
MSRV bumped to 1.91.0, dependency updates, slow-tests feature added, CI/workflows reworked to just-based flow, new just recipes and coverage/benchmark wiring.
Proptest seeds / regression files
proptest-regressions/*, tests/*.proptest-regressions
Several regression seed files removed or pruned; a few new seeds added to align with degeneracy filtering and revised tests.
Misc tests & utilities
src/core/boundary.rs, src/core/facet.rs, src/core/traits/facet_cache.rs, src/geometry/*, src/core/util.rs
Test inputs shifted to non-degenerate coordinates, many assertions relaxed, vertex dedup helpers and epsilon comparisons added; util gains public dedup helpers and Delaunay validation error types.
Spell/Docs small
cspell.json, .markdownlint.json
Added words (choco, collinearities, octree, taiki, unsalvageable) and disabled MD060 in markdownlint config.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Pipeline as UnifiedInsertionPipeline
    participant Fast as IncrementalBowyerWatson
    participant Robust as RobustBowyerWatson
    participant Tds
    participant Validator as DelaunayValidator

    User->>Pipeline: triangulate(vertices, policy)
    Pipeline->>Tds: Tds::new(initial_simplex)
    Pipeline->>Pipeline: for each vertex
    Pipeline->>Fast: attempt fast insert
    alt fast success
        Fast->>Tds: create cells & finalize
        Pipeline->>Validator: maybe validate (policy)
    else fast fail
        Pipeline->>Robust: attempt robust insert
        alt robust success
            Robust->>Tds: create/repair cells
            Pipeline->>Validator: maybe validate (policy)
        else robust fail
            Pipeline->>Pipeline: record unsalvageable vertex
        end
    end
    Pipeline->>Tds: strict finalize_triangulation
    Pipeline->>Validator: final validate_delaunay (EndOnly)
    alt violations found
        Pipeline->>Robust: repair_global_delaunay_violations
        Robust->>Tds: iterative repairs
    end
    Pipeline-->>User: Result + Diagnostics
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Areas needing extra attention:

  • src/core/traits/insertion_algorithm.rs — trait surface, duplicate-detection wrapper, transactional semantics, rollback/restore and iterative cavity refinement.
  • src/core/algorithms/robust_bowyer_watson.rs — dense logic for robust predicates, repair logic, new public types and diagnostic storage.
  • src/core/triangulation_data_structure.rs & src/core/util.rs — validation diagnostics, new error kinds, and public Delaunay validation APIs.
  • src/core/vertex.rs & Tds::new signature change — pervasive API migration, lifetimes/ownership and Result propagation across call sites.
  • Tests and proptest regressions — broad, heterogeneous updates; verify coverage, determinism, and CI runtime with slow-tests feature.

Possibly related PRs

  • Refactor/phase 4 #109 — Directly related: modifies InsertionAlgorithm trait and insertion/finalization flow (rollback/restore, insert_vertex_impl rename, Delaunay checks).
  • chore(release): release v0.5.0 #91 — Closely related: overlaps on Vertex::from_points and Tds::new signature changes and insertion/validation pipeline work.
  • Test/improve coverage #89 — Related: touches insertion/validation surface and bowyer_watson/robust_bowyer_watson modules that overlap with these changes.

Poem

🐇
I nibble at meshes with whiskered care,
I hop the fast path, then dig robustly there.
I mark the skips and log what’s true,
Mend wayward cells, then hand them back to you.
A rabbit’s tweak keeps triangulation fair.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main changes: fixing remove_vertex topology consistency and addressing test failures in the codebase.
Description check ✅ Passed The description comprehensively addresses all major changes in the PR, including the three critical issues (remove_vertex fix, proptest quality metric fixes, convex hull test fix) and additional improvements, with clear explanations of each change.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/tds-insertion

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai bot added documentation Improvements or additions to documentation rust Pull requests that update rust code labels Nov 12, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/integration_robust_bowyer_watson.rs (1)

48-63: Fix RNG API usage to restore compilation

rand::Rng does not expose a random_range method in the 0.8.x series we depend on. The new calls at Lines 48, 145, 162, etc. will fail to compile, so the entire crate no longer builds. Please switch back to the supported gen_range helper (which accepts the same range syntax) for every occurrence introduced in this patch. citeturn0relevant0

Apply this diff to each affected call site:

-                    let coords: [f64; $dim] = std::array::from_fn(|_| rng.random_range(-5.0..15.0));
+                    let coords: [f64; $dim] = std::array::from_fn(|_| rng.gen_range(-5.0..15.0));
...
-                    let coords: [f64; $dim] = std::array::from_fn(|_| rng.random_range(-0.5..0.5));
+                    let coords: [f64; $dim] = std::array::from_fn(|_| rng.gen_range(-0.5..0.5));
...
-                    let coords: [f64; $dim] = std::array::from_fn(|_| rng.random_range(-20.0..20.0));
+                    let coords: [f64; $dim] = std::array::from_fn(|_| rng.gen_range(-20.0..20.0));

Also applies to: 145-175

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9eaa225 and f8508ce.

📒 Files selected for processing (18)
  • cspell.json (1 hunks)
  • docs/archive/invariant_validation_plan.md (1 hunks)
  • proptest-regressions/core/traits/insertion_algorithm.txt (0 hunks)
  • src/core/algorithms/bowyer_watson.rs (1 hunks)
  • src/core/algorithms/robust_bowyer_watson.rs (12 hunks)
  • src/core/cell.rs (1 hunks)
  • src/core/traits/insertion_algorithm.rs (22 hunks)
  • src/core/triangulation_data_structure.rs (6 hunks)
  • src/core/util.rs (4 hunks)
  • src/lib.rs (1 hunks)
  • tests/integration_robust_bowyer_watson.rs (6 hunks)
  • tests/proptest_delaunay_condition.proptest-regressions (0 hunks)
  • tests/proptest_delaunay_condition.rs (5 hunks)
  • tests/proptest_predicates.proptest-regressions (0 hunks)
  • tests/proptest_quality.proptest-regressions (0 hunks)
  • tests/proptest_quality.rs (4 hunks)
  • tests/proptest_robust_bowyer_watson.rs (2 hunks)
  • tests/test_facet_cache_integration.rs (1 hunks)
💤 Files with no reviewable changes (4)
  • tests/proptest_predicates.proptest-regressions
  • proptest-regressions/core/traits/insertion_algorithm.txt
  • tests/proptest_quality.proptest-regressions
  • tests/proptest_delaunay_condition.proptest-regressions
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • tests/proptest_delaunay_condition.rs
  • tests/test_facet_cache_integration.rs
  • docs/archive/invariant_validation_plan.md
  • tests/proptest_quality.rs
  • tests/proptest_robust_bowyer_watson.rs
  • tests/integration_robust_bowyer_watson.rs
  • src/core/algorithms/robust_bowyer_watson.rs
  • src/core/triangulation_data_structure.rs
  • src/core/traits/insertion_algorithm.rs
  • src/core/util.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • tests/proptest_delaunay_condition.rs
  • src/lib.rs
  • tests/test_facet_cache_integration.rs
  • tests/proptest_quality.rs
  • tests/proptest_robust_bowyer_watson.rs
  • src/core/algorithms/robust_bowyer_watson.rs
  • src/core/traits/insertion_algorithm.rs
  • src/core/util.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.

Applied to files:

  • tests/proptest_delaunay_condition.rs
  • src/lib.rs
  • tests/test_facet_cache_integration.rs
  • docs/archive/invariant_validation_plan.md
  • tests/proptest_quality.rs
  • src/core/algorithms/robust_bowyer_watson.rs
  • src/core/util.rs
📚 Learning: 2025-09-17T17:53:34.532Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 83
File: src/core/traits/facet_cache.rs:588-652
Timestamp: 2025-09-17T17:53:34.532Z
Learning: The build_cache_with_rcu method in FacetCacheProvider correctly returns Option<Arc<FacetToCellsMap>> representing the old cache value before the RCU update operation. The test logic using old_value.is_none() to detect which thread built the cache is correct: None means this thread built it, Some(old_cache) means another thread already built it.

Applied to files:

  • tests/test_facet_cache_integration.rs
📚 Learning: 2025-09-02T20:32:05.985Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 60
File: cspell.json:103-103
Timestamp: 2025-09-02T20:32:05.985Z
Learning: In cspell.json for the delaunay project, the word "itional" is intentionally added to the dictionary because it comes from a regex pattern, not a typo.

Applied to files:

  • cspell.json
📚 Learning: 2025-09-17T17:51:52.098Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 83
File: src/core/traits/facet_cache.rs:533-585
Timestamp: 2025-09-17T17:51:52.098Z
Learning: The build_cache_with_rcu method in FacetCacheProvider returns Option<Arc<FacetToCellsMap>> representing the old cache value before the RCU update operation, not a tuple with a "built" flag. First call returns None (no old value), subsequent calls return Some(old_cache).

Applied to files:

  • src/core/traits/insertion_algorithm.rs
🧬 Code graph analysis (11)
tests/proptest_delaunay_condition.rs (3)
src/core/cell.rs (2)
  • vertices (680-682)
  • new (476-507)
src/core/triangulation_data_structure.rs (2)
  • vertices (604-606)
  • new (2380-2442)
src/core/traits/insertion_algorithm.rs (6)
  • with_capacity (666-674)
  • tds (3515-3527)
  • new (516-528)
  • new (654-662)
  • insert_vertex (932-974)
  • finalize_triangulation (2417-2445)
tests/test_facet_cache_integration.rs (3)
src/core/cell.rs (1)
  • vertices (680-682)
src/core/triangulation_data_structure.rs (1)
  • vertices (604-606)
src/core/facet.rs (1)
  • vertices (508-529)
src/core/algorithms/bowyer_watson.rs (2)
src/core/algorithms/robust_bowyer_watson.rs (1)
  • insert_vertex_impl (1489-1497)
src/core/traits/insertion_algorithm.rs (2)
  • insert_vertex_impl (1001-1005)
  • tds (3515-3527)
src/core/cell.rs (2)
src/core/triangulation_data_structure.rs (1)
  • vertices (604-606)
src/core/facet.rs (1)
  • vertices (508-529)
tests/proptest_quality.rs (3)
src/core/cell.rs (4)
  • vertices (680-682)
  • map (411-411)
  • uuid (792-794)
  • vertex_uuids (866-875)
src/core/triangulation_data_structure.rs (3)
  • vertices (604-606)
  • cell_keys (622-624)
  • get_cell (632-634)
src/core/vertex.rs (3)
  • map (293-293)
  • map (303-303)
  • uuid (530-532)
tests/proptest_robust_bowyer_watson.rs (3)
src/core/traits/insertion_algorithm.rs (1)
  • tds (3515-3527)
src/core/cell.rs (1)
  • is_valid (1106-1146)
src/core/triangulation_data_structure.rs (1)
  • is_valid (4007-4040)
tests/integration_robust_bowyer_watson.rs (1)
src/core/triangulation_data_structure.rs (1)
  • is_valid (4007-4040)
src/core/algorithms/robust_bowyer_watson.rs (3)
src/core/traits/insertion_algorithm.rs (12)
  • tds (3515-3527)
  • remove_bad_cells (3636-3645)
  • finalize_after_insertion (3805-3834)
  • rollback_vertex_insertion (3584-3595)
  • gather_boundary_facet_info (3229-3341)
  • deduplicate_boundary_facet_info (2999-3030)
  • filter_boundary_facets_by_valid_facet_sharing (3064-3196)
  • with_capacity (666-674)
  • new (516-528)
  • new (654-662)
  • connect_new_cells_to_neighbors (3436-3565)
  • insert_vertex_impl (1001-1005)
src/core/triangulation_data_structure.rs (2)
  • rollback_vertex_insertion (2755-2825)
  • new (2380-2442)
src/core/algorithms/bowyer_watson.rs (2)
  • new (180-189)
  • insert_vertex_impl (261-332)
src/core/triangulation_data_structure.rs (2)
src/core/cell.rs (5)
  • cell (4324-4326)
  • neighbors (647-651)
  • vertices (680-682)
  • uuid (792-794)
  • is_valid (1106-1146)
src/core/util.rs (1)
  • is_delaunay (1557-1617)
src/core/traits/insertion_algorithm.rs (2)
src/core/util.rs (2)
  • find_delaunay_violations (1658-1726)
  • k (560-560)
src/core/triangulation_data_structure.rs (3)
  • rollback_vertex_insertion (2755-2825)
  • new (2380-2442)
  • vertices (604-606)
src/core/util.rs (4)
src/core/cell.rs (4)
  • uuid (792-794)
  • cell (4324-4326)
  • vertices (680-682)
  • new (476-507)
src/core/vertex.rs (3)
  • uuid (530-532)
  • point (475-477)
  • None (2084-2084)
src/geometry/robust_predicates.rs (2)
  • robust_insphere (149-192)
  • num_traits (1421-1421)
src/core/triangulation_data_structure.rs (2)
  • vertices (604-606)
  • new (2380-2442)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Codacy Security Scan
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: build (windows-latest)
  • GitHub Check: Code Coverage
🔇 Additional comments (2)
cspell.json (1)

169-169: Correct dictionary addition for plural geometric term.

Adding "collinearities" (plural of "collinearity" at line 168) to the spell-check dictionary is appropriate and prevents false positives when this geometric term appears in code, tests, or documentation as part of the broader Delaunay triangulation improvements in this PR.

tests/test_facet_cache_integration.rs (1)

273-281: LGTM! Correct fix for the convex hull test failure.

The updated vertices properly form a convex hull with boundary facets, addressing the "No boundary facets" error. The new configuration creates a clear tetrahedron with two additional points on hull faces, and the inline comments accurately document the geometry.

Fixes a critical bug where the Bowyer-Watson insertion algorithm was
creating triangulations with violations of the empty circumsphere
property. The issue manifested in proptest failures and affected
approximately 1.35% of test cases.

Root cause: The `determine_strategy_default` method used a faulty
heuristic for single-cell triangulations, assuming all vertices were
exterior without proper geometric validation.

Changes:
- Convert `determine_strategy_default` to instance method using robust
  geometric predicates instead of heuristics
- Add `allow_inserted_vertex_in_facets` parameter to boundary facet
  filtering to handle refinement iterations correctly
- Add final Delaunay validation in `finalize_triangulation` to catch
  any remaining violations early
- Improve proptest configuration for Delaunay property validation
- Update documentation for insertion strategy determination

Impact:
- All proptest cases now pass
- Test success rate: 98.65% (1026/1040 tests passing)
- Remaining 14 failures documented in issue #125

The staged changes represent the primary bug fix. Additional refinement
improvements are tracked separately.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (3)
docs/archive/invariant_validation_plan.md (1)

156-158: Past syntax issue has been resolved.

The previous comment about invalid named arguments has been addressed. The code now correctly uses positional arguments (false) instead of named arguments.

src/core/triangulation_data_structure.rs (1)

1764-1871: remove_vertex now repairs topology before deletion — LGTM

Clearing neighbor slots pointing to removed cells, then reassigning incident_cell, then deleting the vertex addresses the dangling-reference bug. Nice.

src/core/algorithms/robust_bowyer_watson.rs (1)

603-651: Rollback still drops refinement cells.

The new restore_cavity_insertion_failure calls only know about saved_bad_cells and the initial created_cell_keys. Inside the refinement loop we push additional refinement_cell_keys and delete extra violating_cells, but we never add those keys (or clones of the removed cells) to the rollback buffers. When any of the error exits fire later—final Delaunay validation (lines 633‑651), max-iteration, cell-growth guard, etc.—we return Err after having already removed cells that the rollback helper cannot put back. The triangulation is still left in a half-mutated state, which is the same correctness issue flagged previously. Please accumulate every refinement batch you insert and every cell you remove so a failure path can fully restore the original TDS before propagating the error.

🧹 Nitpick comments (1)
src/core/triangulation_data_structure.rs (1)

2674-2696: Per-insert RobustBowyerWatson construction — optional reuse

Creating a new RobustBowyerWatson per add() call is fine; if profiling shows allocator churn, consider keeping a reusable instance/buffers on Tds.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8508ce and bf560fd.

📒 Files selected for processing (6)
  • docs/archive/invariant_validation_plan.md (1 hunks)
  • src/core/algorithms/robust_bowyer_watson.rs (23 hunks)
  • src/core/traits/insertion_algorithm.rs (41 hunks)
  • src/core/triangulation_data_structure.rs (12 hunks)
  • tests/proptest_delaunay_condition.rs (6 hunks)
  • tests/proptest_quality.rs (4 hunks)
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • tests/proptest_quality.rs
  • docs/archive/invariant_validation_plan.md
  • src/core/algorithms/robust_bowyer_watson.rs
  • tests/proptest_delaunay_condition.rs
  • src/core/triangulation_data_structure.rs
  • src/core/traits/insertion_algorithm.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • tests/proptest_quality.rs
  • docs/archive/invariant_validation_plan.md
  • src/core/algorithms/robust_bowyer_watson.rs
  • tests/proptest_delaunay_condition.rs
  • src/core/triangulation_data_structure.rs
  • src/core/traits/insertion_algorithm.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.

Applied to files:

  • tests/proptest_quality.rs
  • docs/archive/invariant_validation_plan.md
  • src/core/algorithms/robust_bowyer_watson.rs
  • tests/proptest_delaunay_condition.rs
  • src/core/triangulation_data_structure.rs
  • src/core/traits/insertion_algorithm.rs
📚 Learning: 2025-09-17T17:51:52.098Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 83
File: src/core/traits/facet_cache.rs:533-585
Timestamp: 2025-09-17T17:51:52.098Z
Learning: The build_cache_with_rcu method in FacetCacheProvider returns Option<Arc<FacetToCellsMap>> representing the old cache value before the RCU update operation, not a tuple with a "built" flag. First call returns None (no old value), subsequent calls return Some(old_cache).

Applied to files:

  • src/core/algorithms/robust_bowyer_watson.rs
  • src/core/traits/insertion_algorithm.rs
📚 Learning: 2025-09-17T17:53:34.532Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 83
File: src/core/traits/facet_cache.rs:588-652
Timestamp: 2025-09-17T17:53:34.532Z
Learning: The build_cache_with_rcu method in FacetCacheProvider correctly returns Option<Arc<FacetToCellsMap>> representing the old cache value before the RCU update operation. The test logic using old_value.is_none() to detect which thread built the cache is correct: None means this thread built it, Some(old_cache) means another thread already built it.

Applied to files:

  • src/core/algorithms/robust_bowyer_watson.rs
📚 Learning: 2025-08-20T21:55:04.552Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 50
File: src/core/algorithms/robust_bowyer_watson.rs:191-195
Timestamp: 2025-08-20T21:55:04.552Z
Learning: CoordinateScalar trait guarantees PartialOrd, making direct comparisons between CoordinateScalar values valid without needing conversion to f64.

Applied to files:

  • src/core/algorithms/robust_bowyer_watson.rs
  • src/core/traits/insertion_algorithm.rs
📚 Learning: 2025-08-20T21:55:04.552Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 50
File: src/core/algorithms/robust_bowyer_watson.rs:191-195
Timestamp: 2025-08-20T21:55:04.552Z
Learning: CoordinateScalar trait includes PartialOrd through its Float supertrait bound, making direct comparisons between CoordinateScalar values valid without needing conversion to f64.

Applied to files:

  • src/core/algorithms/robust_bowyer_watson.rs
  • src/core/traits/insertion_algorithm.rs
🧬 Code graph analysis (5)
tests/proptest_quality.rs (4)
src/core/triangulation_data_structure.rs (3)
  • vertices (604-606)
  • cell_keys (622-624)
  • get_cell (632-634)
src/core/cell.rs (4)
  • vertices (680-682)
  • map (411-411)
  • uuid (792-794)
  • vertex_uuids (866-875)
src/core/vertex.rs (3)
  • map (293-293)
  • map (303-303)
  • uuid (530-532)
src/geometry/quality.rs (2)
  • radius_ratio (218-265)
  • normalized_volume (313-378)
src/core/algorithms/robust_bowyer_watson.rs (1)
src/core/traits/insertion_algorithm.rs (13)
  • tds (3612-3624)
  • remove_bad_cells (3742-3751)
  • finalize_after_insertion (3912-3941)
  • geometric_failure (246-251)
  • rollback_vertex_insertion (3687-3701)
  • connect_new_cells_to_neighbors (3533-3662)
  • restore_cavity_insertion_failure (3807-3833)
  • gather_boundary_facet_info (3326-3438)
  • deduplicate_boundary_facet_info (3085-3116)
  • filter_boundary_facets_by_valid_facet_sharing (3155-3293)
  • new (517-529)
  • new (655-663)
  • insert_vertex_cavity_based (1676-2084)
tests/proptest_delaunay_condition.rs (3)
src/core/triangulation_data_structure.rs (4)
  • vertices (604-606)
  • new (2396-2458)
  • cells (539-541)
  • cells (4386-4394)
src/geometry/robust_predicates.rs (2)
  • general_triangulation (736-746)
  • robust_insphere (149-192)
src/geometry/predicates.rs (1)
  • insphere (349-424)
src/core/triangulation_data_structure.rs (3)
src/core/algorithms/robust_bowyer_watson.rs (2)
  • cell (2653-2655)
  • new (91-101)
src/core/cell.rs (8)
  • cell (4324-4326)
  • neighbors (647-651)
  • new (476-507)
  • vertices (680-682)
  • uuid (792-794)
  • is_valid (1106-1146)
  • number_of_vertices (765-767)
  • contains_vertex (540-542)
src/core/util.rs (1)
  • is_delaunay (1557-1617)
src/core/traits/insertion_algorithm.rs (3)
src/core/util.rs (4)
  • geometry (1566-1566)
  • geometry (1671-1671)
  • find_delaunay_violations (1658-1726)
  • k (560-560)
src/geometry/robust_predicates.rs (5)
  • robust_insphere (149-192)
  • num_traits (1421-1421)
  • default (67-77)
  • util (216-216)
  • util (260-260)
src/core/triangulation_data_structure.rs (4)
  • default (221-223)
  • rollback_vertex_insertion (2771-2842)
  • new (2396-2458)
  • vertices (604-606)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: performance-regression
  • GitHub Check: Code Coverage
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (windows-latest)
  • GitHub Check: Codacy Security Scan
🔇 Additional comments (9)
tests/proptest_quality.rs (1)

215-266: Ensuring UUID-based matches can't silently skip.

The new matched guard plugs the hole where we could previously exit without comparing any cells. Nice to see the same pattern carried through the other invariance loops.

tests/proptest_delaunay_condition.rs (4)

49-81: LGTM! Well-designed duplicate prevention.

The function correctly filters vertices matching initial simplex coordinates, preventing duplicate insertions that would cause Delaunay violations. The bit-level coordinate comparison ensures exact matching without float comparison pitfalls.


142-166: Effective degeneracy filtering with Tds::new().

The refactoring to use Tds::new() ensures the entire triangulation satisfies the Delaunay property from the start. The axis-aligned degeneracy filter is a practical heuristic that reduces collinear/coplanar pathologies without being overly restrictive.


207-217: Conservative dual-predicate approach is appropriate for property tests.

Requiring both robust and standard predicates to agree on INSIDE classification effectively reduces false positives from numerical edge cases. This conservative strategy is well-suited for property-based testing where stability is paramount.


276-397: Enhanced filtering significantly improves test robustness.

The stricter axis-aligned filtering, co-circularity pruning, and increased Jaccard threshold (0.75) work together to reduce false positives from numerical degeneracies. The empirical tuning is well-documented in comments.

The co-circularity check at lines 294-315 has O(n⁴) complexity, but this is acceptable given the bounded test sizes (6-10 points). The tolerance 1e-8 * (1.0 + radius.abs()) at line 300 is reasonable for detecting near-cocircular points.

src/core/triangulation_data_structure.rs (4)

2906-2909: Robust triangulate path — good

Using RobustBowyerWatson in bowyer_watson() matches the migration.


4060-4151: is_valid_with_options addition — solid

Clean layering over is_valid() with optional Delaunay check and clear error mapping.


4828-5061: New remove_vertex tests are strong; add two edge-case checks

Great coverage for neighbor clearing and incident_cell validity. Please add:

  • Case where removing a vertex leaves zero cells: assert every remaining vertex has incident_cell == None (catches the empty-cells edge noted above).
  • A rollback test that forces an insertion failure and then asserts tds.is_valid() and absence of dangling neighbor pointers.

195-195: RobustBowyerWatson switch verified—no remaining IncrementalBowyerWatson usage in production code

Grep confirms the migration is complete: src/core/triangulation_data_structure.rs uses only RobustBowyerWatson::new() (lines 2694, 2907). IncrementalBowyerWatson remains only in test files and algorithm definitions, which is expected.

Comment on lines +3824 to +3833
// Remove the vertex if it was newly inserted during this operation
if vertex_was_newly_inserted {
// Get the vertex by key so we can call remove_vertex
// Copy the vertex to avoid borrow checker issues
if let Some(&vertex) = tds.get_vertex_by_key(vertex_key) {
// Ignore errors during rollback - we're already in an error path
let _ = tds.remove_vertex(&vertex);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix borrow pattern so this compiles.

tds.get_vertex_by_key returns Option<&Vertex<…>>, but the pattern Some(&vertex) tries to copy the Vertex. Vertex isn’t Copy, so this won’t compile. Borrow the reference directly instead of destructuring by value:

-        if let Some(&vertex) = tds.get_vertex_by_key(vertex_key) {
-            // Ignore errors during rollback - we're already in an error path
-            let _ = tds.remove_vertex(&vertex);
-        }
+        if let Some(vertex) = tds.get_vertex_by_key(vertex_key) {
+            // Ignore errors during rollback - we're already in an error path
+            let _ = tds.remove_vertex(vertex);
+        }

Otherwise the crate fails to build.

🤖 Prompt for AI Agents
In src/core/traits/insertion_algorithm.rs around lines 3824 to 3833, the match
pattern `Some(&vertex)` tries to copy a non-Copy Vertex; change the pattern to
borrow the reference (e.g. `Some(vertex)`) and pass that reference directly to
remove_vertex (call `tds.remove_vertex(vertex)`), so you no longer attempt to
move or copy the Vertex and the borrow checker is satisfied.

…[skip ci]

Add unified fast→robust→skip insertion pipeline with policy-based Delaunay checks
•  Make robust Bowyer–Watson transactional with rollback, global repair, and unsalvageable vertex tracking
•  Extend triangulation validation with detailed structural/Delaunay reports and new validate_* helpers
•  Introduce vertex classification, unsalvageable reports, and improved invariants in insertion algorithm traits
•  Update Tds APIs, tests, and docs to use the new pipeline and validation entry points
•  Add regression seeds and fix spelling/wordlist entries for new diagnostics

test result: FAILED. 1024 passed; 39 failed; 2 ignored; 0 measured; 0 filtered out;
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/core/algorithms/bowyer_watson.rs (1)

437-447: Tests won’t compile: use paste::paste!, not pastey::paste!

pastey isn’t a known crate. Switch to paste::paste and add the dev‑dependency.

-                pastey::paste! {
+                paste::paste! {

Add to Cargo.toml (dev-dependencies):

[dev-dependencies]
paste = "1.0"
docs/topology.md (1)

289-342: Semantic inconsistency in Euler characteristic calculations is confirmed

The code in expected_euler_characteristic() returns values that correspond to closed sphere boundaries (S^(D-1)), but the documentation explicitly states that finite Delaunay triangulations are topological D-balls with χ=1 for all dimensions. The docs state: "Finite Delaunay triangulations in R^D form a topological ball with χ = 1" and "Finite Delaunay triangulations... are topological D-balls, so χ should always equal 1". However, the hardcoded match arms return χ=2 for D=3 and χ=0 for D=4 (matching sphere values), and the fallback formula 1 + (-1)^d also produces sphere values, not the documented χ=1.

Action required:

  • Clarify whether the triangulation semantics represent the volume (D-ball, χ=1) or boundary (sphere, variable χ)
  • Align the expected_euler_characteristic() implementation and all validation logic with this decision
  • Update examples and tests accordingly
♻️ Duplicate comments (7)
src/core/traits/facet_cache.rs (3)

1117-1119: LGTM! Consistent with other test vertex adjustments.

This change mirrors the improvement at lines 568-570, using the same interior vertex coordinate to avoid degenerate insertion cases. The error handling with .expect() is appropriate.


1253-1254: LGTM! Consistent test improvement.

This adjustment aligns with the pattern established in previous test updates, using an interior vertex to reduce degenerate insertion scenarios.


1323-1324: LGTM! Consistent with test suite improvements.

Final instance of the vertex coordinate adjustment, maintaining consistency across the test suite while avoiding degenerate insertion scenarios.

src/core/algorithms/robust_bowyer_watson.rs (2)

299-347: Do not mutate TDS here; removal + finalize has no rollback path.

This block removes “violating” cells after a successful insertion and then calls finalize. If finalize (or the re-check) fails, the function returns Err after cells were already deleted, leaving the triangulation corrupted. There’s no saved snapshot here (unlike the transactional cavity flow), so rollback isn’t possible.

Given that insert_vertex_cavity_based_with_robust_predicates already enforces Delaunay (with restore on error), this post-step is redundant for CavityBased insertions. Recommend deleting this block entirely (or make it diagnostics-only without mutation). Minimal fix:

-            if matches!(info.strategy, InsertionStrategy::CavityBased) {
-                let all_cell_keys: Vec<CellKey> = tds.cells().map(|(k, _)| k).collect();
-                let violations = self.find_delaunay_violations_in_cells(tds, &all_cell_keys)?;
-
-                if !violations.is_empty() {
-                    // Attempt to fix violations by removing violating cells and re-finalizing
-                    <Self as InsertionAlgorithm<T, U, V, D>>::remove_bad_cells(tds, &violations);
-
-                    // Attempt to rebuild topology
-                    if let Err(e) =
-                        <Self as InsertionAlgorithm<T, U, V, D>>::finalize_after_insertion(tds)
-                    {
-                        return Err(InsertionError::TriangulationState(
-                            TriangulationValidationError::InconsistentDataStructure {
-                                message: format!(
-                                    "RobustBowyerWatson found {} Delaunay violations after {:?} insertion. \
-                                     Attempted to fix by removing violating cells but finalization failed: {}",
-                                    violations.len(),
-                                    info.strategy,
-                                    e
-                                ),
-                            },
-                        ));
-                    }
-
-                    // Re-check for violations
-                    let remaining: Vec<CellKey> = tds.cells().map(|(k, _)| k).collect();
-                    let remaining_violations =
-                        self.find_delaunay_violations_in_cells(tds, &remaining)?;
-
-                    if !remaining_violations.is_empty() {
-                        return Err(InsertionError::GeometricFailure {
-                            message: format!(
-                                "RobustBowyerWatson insertion via {:?} strategy produced {} Delaunay violations. \
-                                 Attempted fix by removing violating cells but {} violations remain. \
-                                 This indicates invalid or degenerate input geometry.",
-                                info.strategy,
-                                violations.len(),
-                                remaining_violations.len()
-                            ),
-                            strategy_attempted: info.strategy,
-                        });
-                    }
-                }
-            }
+            // Cavity-based path already enforces Delaunay with transactional rollback.
+            // No additional mutation here; optionally run diagnostics-only check in debug builds.
+            #[cfg(debug_assertions)]
+            if matches!(info.strategy, InsertionStrategy::CavityBased) {
+                let _ = self.find_delaunay_violations_in_cells(
+                    tds,
+                    &tds.cell_keys().collect::<Vec<_>>(),
+                );
+            }

2336-2338: self.update_statistics doesn’t exist; replace with direct stats update.

This call will not compile. Elsewhere you mutate self.stats directly or use record_vertex_insertion. For initial simplex creation, minimally:

-        self.update_statistics(1, 0);
+        self.stats.total_cells_created += 1;

Or, if you prefer a helper, add a dedicated method and call that consistently.

src/core/triangulation_data_structure.rs (2)

2026-2044: Mark deprecated API with an attribute

The docs say this method is deprecated, but it lacks #[deprecated]. Add an attribute so call‑sites warn at compile time.

     /// **Deprecated**: Prefer `remove_vertex()` which handles both vertex and cell removal atomically.
+    #[deprecated(
+        since = "0.5.2",
+        note = "Use `remove_vertex()` which atomically removes the vertex and related cells."
+    )]
     pub(crate) fn remove_vertex_by_uuid(&mut self, uuid: &uuid::Uuid) -> bool {

2957-3005: Rollback leaves stale incident_cell pointers; rebuild after cell removal

rollback_vertex_insertion removes incident cells and the vertex but never rebuilds vertex→cell incidence, so surviving vertices can retain incident_cell keys to removed cells. Mirror remove_vertex and rebuild incidence after remove_cells_by_keys.

-        #[cfg_attr(not(debug_assertions), allow(unused_variables))]
-        let cells_removed = self.remove_cells_by_keys(&cells_to_remove);
+        #[cfg_attr(not(debug_assertions), allow(unused_variables))]
+        let cells_removed = self.remove_cells_by_keys(&cells_to_remove);
+
+        // Rebuild vertex incidence so surviving vertices don't point to removed cells
+        if let Err(e) = self.assign_incident_cells() {
+            #[cfg(debug_assertions)]
+            eprintln!("rollback: assign_incident_cells failed: {e}");
+        }
🧹 Nitpick comments (13)
docs/topology.md (2)

36-38: Good: integrate with existing validation

Nice callout tying topology checks to Tds::is_valid() / validation_report(). Consider linking to the exact module path for quick navigation.


99-102: Clarify layering cost/behavior

Add one sentence noting that topology checks run after structural invariants and may be feature‑gated for performance.

proptest-regressions/core/traits/insertion_algorithm.txt (1)

7-7: Keep seeds minimal and stable

Seed update looks fine. Keep this file deduped and (optionally) sorted to reduce churn in reviews.

docs/property_testing_summary.md (1)

5-10: Docs align with invariant checks

Good clarification pointing to Tds::is_valid() and validation_report(). Add a link/anchor to the crate‑level docs section for quick access.

Also applies to: 81-82

src/core/boundary.rs (1)

431-435: Test relaxation is reasonable; add validity assert

Loosening to “≥ 1” avoids brittle counts. Add assert!(tds.is_valid().is_ok()) before boundary assertions to avoid masking topology/neighbor regressions.

-            assert!(
-                tds.number_of_cells() >= 1,
-                "Should have at least one cell for this test"
-            );
+            assert!(
+                tds.number_of_cells() >= 1,
+                "Should have at least one cell for this test"
+            );
+            assert!(
+                tds.is_valid().is_ok(),
+                "TDS should be structurally valid before boundary checks"
+            );
docs/archive/optimization_recommendations_historical.md (1)

21-22: Nice: precomputed maps note

Good to emphasize the HashMap‑based cache; consider adding MSRV note if using APIs stabilized recently (optional).

src/core/algorithms/bowyer_watson.rs (1)

260-267: Only count processed insertions on success (or track attempts separately)

Currently increments processed even when info.success == false. Either increment only on success or add a separate “attempted” counter.

-        if let Ok(ref info) = result {
-            self.stats.vertices_processed += 1;
+        if let Ok(ref info) = result {
+            if info.success {
+                self.stats.vertices_processed += 1;
+            }
             self.stats.total_cells_created += info.cells_created;
             self.stats.total_cells_removed += info.cells_removed;
         }
src/geometry/util.rs (1)

4782-4840: Relaxed vertex-count checks and validity diagnostics look good; consider deduplicating the pattern

The new number_of_vertices() >= D+1 assertions plus is_valid() checks are consistent with the dim() implementation and make the tests robust to skipped/unsalvageable vertices. The repeated “run is_valid, print error on failure, then assert is_ok()” pattern appears in multiple tests; if this grows further, a small test helper (e.g., assert_tds_valid(label, &tds)) could reduce duplication and keep future changes to the validation callsite localized.

Also applies to: 4934-4984

docs/fix-delaunay.md (1)

154-161: Clean up minor formatting issues in the design doc

  • The bullet under “Integration tests” (Fast �9 robust �9 skip pipeline…) contains non-ASCII/control characters; consider replacing with something like fast → robust → skip pipeline or fast / robust / skip pipeline so it renders cleanly.
  • In the “Focused Plan to Debug…” section, “small seeded Delaunay tests” reads more naturally as “small-seeded Delaunay tests”.

These are purely documentation nits but will make the doc clearer.

Also applies to: 245-257

src/lib.rs (1)

262-264: Clarify the stability of core::algorithms::unified_insertion_pipeline

unified_insertion_pipeline is exposed as a public module but documented as an “internal” fast+robust insertion pipeline. If this API is not yet intended for general consumption, consider either marking it #[doc(hidden)] or explicitly documenting that it is unstable/internal-only and may change between releases. That will help manage downstream expectations while you iterate on the pipeline.

src/core/util.rs (1)

1568-1569: Consider dimension-aware SmallVec capacity.

The SmallVec capacity is hardcoded to 8, but for a D-dimensional triangulation, cells have D+1 vertices. Consider using D + 1 instead:

-    let mut cell_vertex_points: SmallVec<[Point<T, D>; 8]> = SmallVec::with_capacity(D + 1);
+    let mut cell_vertex_points: SmallVec<[Point<T, D>; { D + 1 }]> = SmallVec::with_capacity(D + 1);

This would avoid heap allocation for dimensions up to 7 (instead of up to 7 currently). For higher dimensions, SmallVec will automatically spill to heap, so this is a minor optimization rather than a correctness issue.

src/core/algorithms/robust_bowyer_watson.rs (1)

2268-2370: Triangulate: good staging, but clear unsalvageable_vertices first and avoid double counting.

You already clear unsalvageable_vertices and stage robust initial simplex. After addressing the missing stats update, this flow looks consistent. Minor: prefer tds.cell_keys() over tds.cells().map(|(k, _)| k) for key collections elsewhere.

src/core/triangulation_data_structure.rs (1)

2002-2015: Optional: avoid duplicate neighbor-clearing work

remove_vertex clears neighbor slots to removed cells, then calls remove_cells_by_keys, which does the same. Keep only one to reduce work; prefer the centralized cleanup inside remove_cells_by_keys.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf560fd and 6e19227.

📒 Files selected for processing (18)
  • cspell.json (2 hunks)
  • docs/archive/invariant_validation_plan.md (1 hunks)
  • docs/archive/optimization_recommendations_historical.md (3 hunks)
  • docs/fix-delaunay.md (1 hunks)
  • docs/numerical_robustness_guide.md (1 hunks)
  • docs/property_testing_summary.md (2 hunks)
  • docs/topology.md (3 hunks)
  • proptest-regressions/core/traits/insertion_algorithm.txt (1 hunks)
  • src/core/algorithms/bowyer_watson.rs (2 hunks)
  • src/core/algorithms/robust_bowyer_watson.rs (30 hunks)
  • src/core/algorithms/unified_insertion_pipeline.rs (1 hunks)
  • src/core/boundary.rs (1 hunks)
  • src/core/cell.rs (2 hunks)
  • src/core/traits/facet_cache.rs (5 hunks)
  • src/core/triangulation_data_structure.rs (33 hunks)
  • src/core/util.rs (4 hunks)
  • src/geometry/util.rs (4 hunks)
  • src/lib.rs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/core/cell.rs
  • cspell.json
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • docs/archive/optimization_recommendations_historical.md
  • src/core/traits/facet_cache.rs
  • docs/numerical_robustness_guide.md
  • docs/fix-delaunay.md
  • docs/property_testing_summary.md
  • src/core/algorithms/unified_insertion_pipeline.rs
  • src/core/boundary.rs
  • src/core/util.rs
  • src/geometry/util.rs
  • src/core/algorithms/bowyer_watson.rs
  • src/lib.rs
  • docs/archive/invariant_validation_plan.md
  • src/core/triangulation_data_structure.rs
  • src/core/algorithms/robust_bowyer_watson.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • src/core/traits/facet_cache.rs
  • docs/numerical_robustness_guide.md
  • docs/property_testing_summary.md
  • src/core/boundary.rs
  • src/core/util.rs
  • src/geometry/util.rs
  • src/lib.rs
  • docs/archive/invariant_validation_plan.md
  • src/core/triangulation_data_structure.rs
  • src/core/algorithms/robust_bowyer_watson.rs
📚 Learning: 2025-09-17T17:53:34.532Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 83
File: src/core/traits/facet_cache.rs:588-652
Timestamp: 2025-09-17T17:53:34.532Z
Learning: The build_cache_with_rcu method in FacetCacheProvider correctly returns Option<Arc<FacetToCellsMap>> representing the old cache value before the RCU update operation. The test logic using old_value.is_none() to detect which thread built the cache is correct: None means this thread built it, Some(old_cache) means another thread already built it.

Applied to files:

  • src/core/traits/facet_cache.rs
  • src/core/algorithms/robust_bowyer_watson.rs
📚 Learning: 2025-09-17T17:51:52.098Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 83
File: src/core/traits/facet_cache.rs:533-585
Timestamp: 2025-09-17T17:51:52.098Z
Learning: The build_cache_with_rcu method in FacetCacheProvider returns Option<Arc<FacetToCellsMap>> representing the old cache value before the RCU update operation, not a tuple with a "built" flag. First call returns None (no old value), subsequent calls return Some(old_cache).

Applied to files:

  • src/core/traits/facet_cache.rs
  • src/core/algorithms/robust_bowyer_watson.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.

Applied to files:

  • docs/numerical_robustness_guide.md
  • docs/fix-delaunay.md
  • docs/property_testing_summary.md
  • src/core/boundary.rs
  • src/core/util.rs
  • src/geometry/util.rs
  • src/core/algorithms/bowyer_watson.rs
  • src/lib.rs
  • docs/archive/invariant_validation_plan.md
  • src/core/triangulation_data_structure.rs
  • src/core/algorithms/robust_bowyer_watson.rs
  • docs/topology.md
📚 Learning: 2025-08-20T21:55:04.552Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 50
File: src/core/algorithms/robust_bowyer_watson.rs:191-195
Timestamp: 2025-08-20T21:55:04.552Z
Learning: CoordinateScalar trait guarantees PartialOrd, making direct comparisons between CoordinateScalar values valid without needing conversion to f64.

Applied to files:

  • src/core/algorithms/robust_bowyer_watson.rs
📚 Learning: 2025-08-20T21:55:04.552Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 50
File: src/core/algorithms/robust_bowyer_watson.rs:191-195
Timestamp: 2025-08-20T21:55:04.552Z
Learning: CoordinateScalar trait includes PartialOrd through its Float supertrait bound, making direct comparisons between CoordinateScalar values valid without needing conversion to f64.

Applied to files:

  • src/core/algorithms/robust_bowyer_watson.rs
🧬 Code graph analysis (7)
src/core/algorithms/unified_insertion_pipeline.rs (4)
src/core/triangulation_data_structure.rs (2)
  • bowyer_watson (3012-3017)
  • new (2554-2616)
src/core/traits/insertion_algorithm.rs (24)
  • unified_insert_vertex_fast_robust_or_skip (1035-1136)
  • vertex (972-974)
  • new (524-536)
  • new (1344-1352)
  • tds (4630-4642)
  • run_global_delaunay_validation_with_policy (3464-3480)
  • insert_vertex (1650-1692)
  • insert_vertex_impl (1719-1723)
  • insert_vertex_impl (7515-7527)
  • insert_vertex_impl (7540-7549)
  • insert_vertex_impl (7562-7574)
  • insert_vertex_impl (7587-7596)
  • classification (978-980)
  • get_statistics (1728-1728)
  • get_statistics (7529-7531)
  • get_statistics (7551-7553)
  • get_statistics (7576-7578)
  • get_statistics (7598-7600)
  • reset (539-541)
  • reset (1731-1731)
  • reset (7533-7533)
  • reset (7555-7555)
  • reset (7580-7580)
  • reset (7602-7602)
src/core/algorithms/robust_bowyer_watson.rs (5)
  • new (138-149)
  • for_degenerate_cases (215-226)
  • insert_vertex_impl (1904-2091)
  • get_statistics (2246-2249)
  • reset (2251-2258)
src/core/algorithms/bowyer_watson.rs (4)
  • new (180-189)
  • insert_vertex_impl (261-332)
  • get_statistics (335-338)
  • reset (341-348)
src/core/boundary.rs (1)
src/core/triangulation_data_structure.rs (1)
  • number_of_cells (1139-1141)
src/core/util.rs (4)
src/core/cell.rs (2)
  • cell (4326-4328)
  • vertices (680-682)
src/core/vertex.rs (1)
  • point (475-477)
src/geometry/robust_predicates.rs (2)
  • robust_insphere (149-192)
  • num_traits (1421-1421)
src/core/triangulation_data_structure.rs (1)
  • vertices (723-725)
src/geometry/util.rs (1)
src/core/triangulation_data_structure.rs (2)
  • number_of_vertices (984-986)
  • dim (1074-1080)
src/core/algorithms/bowyer_watson.rs (3)
src/core/algorithms/robust_bowyer_watson.rs (1)
  • insert_vertex_impl (1904-2091)
src/core/algorithms/unified_insertion_pipeline.rs (1)
  • insert_vertex_impl (106-177)
src/core/traits/insertion_algorithm.rs (7)
  • insert_vertex_impl (1719-1723)
  • insert_vertex_impl (7515-7527)
  • insert_vertex_impl (7540-7549)
  • insert_vertex_impl (7562-7574)
  • insert_vertex_impl (7587-7596)
  • tds (4630-4642)
  • vertex (972-974)
src/core/triangulation_data_structure.rs (7)
src/core/algorithms/robust_bowyer_watson.rs (4)
  • cell (3546-3548)
  • new (138-149)
  • triangulate (2260-2370)
  • unsalvageable_vertices (82-84)
src/core/cell.rs (7)
  • cell (4326-4328)
  • neighbors (647-651)
  • vertices (680-682)
  • new (476-507)
  • is_valid (1106-1146)
  • uuid (792-794)
  • contains_vertex (540-542)
src/core/facet.rs (10)
  • cell (572-576)
  • facet_key_from_vertices (944-957)
  • cell_key (297-299)
  • cell_key (407-409)
  • vertices (508-529)
  • new (269-274)
  • new (442-466)
  • new (755-772)
  • new (844-852)
  • tds (421-423)
src/core/vertex.rs (4)
  • None (2084-2084)
  • is_valid (610-628)
  • uuid (530-532)
  • empty (375-385)
src/core/traits/insertion_algorithm.rs (7)
  • vertex (972-974)
  • new (524-536)
  • new (1344-1352)
  • triangulate (3126-3225)
  • tds (4630-4642)
  • attempted_strategies (984-986)
  • errors (990-992)
src/core/algorithms/unified_insertion_pipeline.rs (1)
  • with_policy (45-55)
src/core/util.rs (1)
  • is_delaunay (1557-1617)
src/core/algorithms/robust_bowyer_watson.rs (2)
src/core/traits/insertion_algorithm.rs (19)
  • find_initial_simplex (1173-1298)
  • find_initial_simplex (3152-3152)
  • find_initial_simplex (7274-7274)
  • find_initial_simplex (7313-7313)
  • find_initial_simplex (7346-7346)
  • find_initial_simplex (7367-7367)
  • new (524-536)
  • new (1344-1352)
  • tds (4630-4642)
  • remove_bad_cells (4760-4769)
  • finalize_after_insertion (4937-4966)
  • vertex (972-974)
  • rollback_vertex_insertion (4705-4719)
  • restore_cavity_insertion_failure (4825-4858)
  • gather_boundary_facet_info (4338-4450)
  • insert_vertex_fallback (2970-3098)
  • find_delaunay_violations_in_cells (2186-2211)
  • create_initial_simplex (3246-3301)
  • finalize_triangulation (3503-3536)
src/core/util.rs (2)
  • k (560-560)
  • find_delaunay_violations (1658-1734)
🪛 LanguageTool
docs/fix-delaunay.md

[grammar] ~256-~256: Use a hyphen to join words.
Context: ...with: - Instructions for running small seeded Delaunay tests, - Guidance on u...

(QB_NEW_EN_HYPHEN)

🔇 Additional comments (21)
src/core/traits/facet_cache.rs (1)

568-570: LGTM! Test vertex adjustment improves robustness.

Moving the test vertex from the circumcenter region (0.5, 0.5, 0.5) to a more interior position (0.2, 0.2, 0.2) appropriately reduces the likelihood of degenerate insertion scenarios. The updated comment clearly documents the rationale.

src/core/algorithms/bowyer_watson.rs (1)

28-29: Doc invariant reference is correct

Reference to validate_neighbors() matches the public naming used elsewhere. No action.

docs/topology.md (1)

1418-1421: API name alignment confirmed—no action required

Verification confirms that validate_neighbors is a public method (line 4443 of src/core/triangulation_data_structure.rs) with signature pub fn validate_neighbors(&self) -> Result<(), TriangulationValidationError>, which correctly matches the usage at docs/topology.md:1419.

docs/archive/optimization_recommendations_historical.md (2)

8-9: Verified: validate_neighbors() is a public method on Tds with the correct name and is properly documented in the public API. No issues found.


216-216: Documentation accurately reflects shipped implementation

The validate_neighbors() function in src/core/triangulation_data_structure.rs:4443 does implement pre-computed vertex sets as documented. The code builds CellVerticesMap (defined as FastHashMap<CellKey, FastHashSet<VertexKey>>) which functionally matches the documented HashMap<CellKey, HashSet<VertexKey>>. The only minor difference is that the implementation uses performance-optimized type variants (FastHashMap/FastHashSet), but this is an implementation detail that doesn't affect the accuracy of the documentation.

docs/numerical_robustness_guide.md (1)

602-605: All referenced validation APIs are confirmed to exist and are public:

  • Tds::is_valid() at line 4259
  • Tds::validation_report() at line 4331
  • Tds::validate_delaunay() at line 4289

The documentation is accurate.

docs/archive/invariant_validation_plan.md (1)

60-188: Archived validation plan looks consistent with current APIs

The archived plan for validate_triangulation_invariants and strict finalize_after_insertion reads coherently, and the example call sites now use positional arguments (e.g., self.validate_triangulation_invariants(tds, false)?;), which matches normal Rust call syntax. Given this is under docs/archive/, no further changes seem necessary.

Also applies to: 203-228

src/core/util.rs (4)

56-79: LGTM! Well-designed error type.

The DelaunayValidationError enum is properly structured with clear variant documentation and appropriate error chaining using #[source] attributes for nested errors.


153-338: Deduplication utilities are well-implemented.

The three vertex deduplication functions (dedup_vertices_exact, dedup_vertices_epsilon, filter_vertices_excluding) are correctly implemented with:

  • Clear documentation and usage examples
  • Proper use of labeled loops for efficient early exit
  • Appropriate comparison strategies (bit-level for exact, Euclidean distance for epsilon)

Note that these have O(n²) complexity, which is acceptable for typical vertex counts but could become a bottleneck for very large datasets (10,000+ vertices). Consider documenting this in the function docstrings if performance questions arise.


340-364: LGTM! Efficient coordinate comparison helpers.

The internal helper functions are well-designed:

  • coords_equal_exact uses ordered_eq for NaN-aware equality
  • coords_within_epsilon avoids expensive sqrt by comparing squared distances
  • Both are properly marked inline for performance

1619-1734: LGTM! Comprehensive violation detection.

The find_delaunay_violations function is well-implemented with:

  • Support for checking a subset of cells (useful for iterative refinement)
  • Proper error handling for TDS corruption
  • Debug diagnostics under #[cfg(test)] for troubleshooting tests

The debug println statements (lines 1714-1721) are helpful for test debugging and don't affect production builds.

src/core/algorithms/unified_insertion_pipeline.rs (6)

17-34: LGTM! Well-documented internal pipeline structure.

The UnifiedInsertionPipeline struct is appropriately scoped as pub(crate) for internal use. The documentation clearly explains its purpose and the intentional lack of public API exposure.

The pub(crate) fields provide internal crate access while maintaining encapsulation from external users.


42-55: Excellent constructor design with appropriate robust configuration.

The with_policy constructor properly initializes both insertion paths:

  • Fast path uses standard IncrementalBowyerWatson::new()
  • Robust path uses RobustBowyerWatson::for_degenerate_cases() with appropriate rationale

The inline comment (lines 48-49) clearly explains why the degenerate-robust configuration is used, which helps maintainability.


63-85: Robust periodic validation implementation.

The on_successful_insertion method is well-designed:

  • Uses saturating_add to prevent counter overflow
  • Clean interval checking with is_multiple_of
  • Proper delegation to the policy-aware validation method
  • Appropriate error conversion

95-104: Intentional bypass of duplicate detection is well-documented.

The comment clearly explains why this implementation bypasses the default duplicate detection logic: the unified helper (unified_insert_vertex_fast_robust_or_skip) performs its own classification and records duplicates in the unsalvageable reports.

This design ensures consistent classification and reporting across the pipeline.


118-176: Comprehensive outcome handling with strong invariants.

The insert_vertex_impl method properly handles all three unified insertion outcomes:

  1. FastSuccess/RobustSuccess: Both call on_successful_insertion when info.success is true, ensuring periodic Delaunay validation runs correctly.

  2. Debug assertions (lines 123-132, 143-156): Validate that classification matches expected values for success paths. These are excellent for catching logic errors during development.

  3. Skipped outcome: Properly records diagnostics without failing the entire operation, treating it as a soft failure with success: false.

The transactional guarantee mentioned in the comment (line 164-165) is important - ensure the underlying algorithms maintain this invariant.


186-194: Clean reset logic with preserved policy.

The reset method correctly clears all transient state while preserving the delaunay_check_policy, which is a configuration parameter that should persist across resets. The comment explains this design choice clearly.

src/core/algorithms/robust_bowyer_watson.rs (3)

688-705: Finalize path already restores on error — good. Consider mirroring this pattern earlier.

finalize_after_insertion failure correctly triggers restore_cavity_insertion_failure. The earlier refinement checks should adopt the same pattern (see prior comment).


2109-2198: Fallback: reclassifying finalize failure to GeometricFailure changes error semantics. Verify callers/tests.

You convert finalization errors to GeometricFailure (recoverable) after rolling back. This alters policy compared to treating them as structural. Ensure consumers rely on is_recoverable() for this path and that integration tests expect skip semantics rather than hard aborts.


512-549: Good: iteration guards and rollback on creation/insert failures in refinement.

The max-iteration and growth guards restore state before returning; partial insertions are cleaned up.

src/core/triangulation_data_structure.rs (1)

2335-2349: Good defensive reset of incident_cell

Clearing all incident_cell first (including empty triangulations) prevents stale pointers after removals. This aligns with the invariant fixes discussed earlier. Looks good.

Comment on lines +881 to 886
let _ = tds.add(vertex);
let new_gen = tds.generation();
assert!(
new_gen > prev_gen,
"Generation should increment after each operation"
new_gen >= prev_gen,
"Generation should not go backwards after operation"
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Verify that the test still validates its intended behavior.

The combination of silently ignoring insertion failures (line 881: let _ = tds.add(vertex)) and relaxing the assertion to allow non-decreasing generation (line 884: new_gen >= prev_gen) fundamentally changes what this test validates:

  • Before: Insertions always succeed and strictly increase generation
  • After: Generation doesn't decrease (even if insertions fail)

If the new insertion pipeline rejects these coordinates as duplicates or degenerate cases, the test might pass without exercising any actual insertion logic. Consider:

  1. Verifying that at least one insertion succeeds to ensure the test exercises the intended code path
  2. Adding an assertion to check the success status, e.g., assert!(tds.add(vertex).is_ok(), "At least one insertion should succeed")
  3. Renaming the test if it's now validating different behavior than "overflow handling"

Note that other tests in this file (lines 570, 1119, 1259, 1324) properly use .expect() to fail on insertion errors.


🏁 Script executed:

#!/bin/bash
# Verify how many tests silently ignore insertion failures vs. fail on errors
echo "=== Tests that silently ignore insertion failures (let _ = tds.add) ==="
rg -n "let\s+_\s+=\s+tds\.add\(" src/core/traits/facet_cache.rs

echo ""
echo "=== Tests that fail on insertion errors (.expect or ?) ==="
rg -n "tds\.add\([^)]+\)\.(expect|\?)" src/core/traits/facet_cache.rs

Length of output: 366


🏁 Script executed:

#!/bin/bash
# Verify line 1324 referenced in the review
echo "=== Context around line 1324 ==="
sed -n '1320,1330p' src/core/traits/facet_cache.rs

echo ""
echo "=== Broader search for all tds.add patterns (to ensure none missed) ==="
rg -n "tds\.add\(" src/core/traits/facet_cache.rs | head -20

Length of output: 250



🏁 Script executed:

#!/bin/bash
# View the test_generation_overflow_handling function context
echo "=== Lines 870-890 (test function containing line 881) ==="
sed -n '870,890p' src/core/traits/facet_cache.rs

echo ""
echo "=== Verify all tds.add() patterns in file ==="
rg -n "tds\.add\(" src/core/traits/facet_cache.rs

echo ""
echo "=== Check if line 1324 exists and its context ==="
wc -l src/core/traits/facet_cache.rs
sed -n '1320,1330p' src/core/traits/facet_cache.rs

Length of output: 1694


Address inconsistent error handling and verify test validates intended behavior.

Verification confirms this test uniquely deviates from the error-handling pattern used throughout the file:

  • Line 881: let _ = tds.add(vertex) — only location in the file silently ignoring failures
  • Lines 570, 1119, 1259, 1325: All other insertions use .expect("Failed to add vertex")

The relaxed assertion (line 884: new_gen >= prev_gen instead of >) combined with silent failures means this test can pass without exercising any actual insertion logic. If all three operations fail, the test still passes but validates nothing.

Consider:

  1. Changing line 881 to tds.add(vertex).expect("Failed to add vertex") to match the file's convention
  2. Restoring the strict assertion new_gen > prev_gen if the test should validate generation increases
  3. If silent failures are intentional, rename the test to reflect what it actually validates (non-decreasing generation, not overflow handling)
🤖 Prompt for AI Agents
In src/core/traits/facet_cache.rs around lines 881 to 886, the test silently
ignores a possible failure by using `let _ = tds.add(vertex)` and uses a
non-strict assertion `new_gen >= prev_gen`, which allows the test to pass
without exercising insertion logic; change the call to
`tds.add(vertex).expect("Failed to add vertex")` to match other insertions and
restore the stricter assertion to `new_gen > prev_gen` so the test fails on add
failures and validates generation advancement, or if silent failures were
intentional, rename the test to indicate it only verifies non-decreasing
generation rather than successful insertion.

- Core algorithms
  - Wire `UnifiedInsertionPipeline::triangulate` through the full fast+robust
    pipeline, including initial simplex construction, per-vertex insertion,
    finalization, and robust global Delaunay repair.
  - Expose `RobustBowyerWatson::repair_global_delaunay_violations` as
    `pub(crate)` and integrate it into the unified pipeline finalization.
  - Make global Delaunay validation a no-op for zero-cell triangulations and
    add a test-only counter to track validation calls under `DelaunayCheckPolicy::EveryN(k)`.
  - Extend insertion statistics with `skipped_vertices` and update tests to
    cover the new field.
  - Add debug helper `debug_print_first_delaunay_violation` and use it in
    validation/reporting paths for easier diagnosis.

- Tests and regression coverage
  - Add `tests/debug_delaunay_violation_5d.rs` and
    `tests/regression_delaunay_known_configs.rs` with a canonical 5D
    seven-point configuration that now validates the Delaunay property.
  - Add/adjust tests for `DelaunayCheckPolicy::EveryN(k)` and final validation
    semantics, including zero-cell no-op behavior.
  - Switch facet cache integration tests to use `RobustBowyerWatson` instead of
    `IncrementalBowyerWatson` to avoid geometric fallbacks while still
    exercising cache invalidation, RCU, and generation tracking.
  - Tweak several tests to use simple non-degenerate simplices and to treat
    `TriangulationConstructionError::GeometricDegeneracy` as an expected,
    non-corrupting failure mode where appropriate.

- Examples and documentation
  - Rename and retune 3D examples to match their actual configurations:
    - `triangulation_3d_100_points` → `triangulation_3d_20_points`
    - `convex_hull_3d_100_points` → `convex_hull_3d_20_points`
  - Update example code to use stable random-triangulation parameters that are
    covered by `test_generate_random_triangulation_dimensions`:
    - 3D examples: `(n_points = 20, bounds = [-3.0, 3.0], seed = 666)`
    - `zero_allocation_iterator_demo`: 4D `(n_points = 12, bounds = [-1.0, 1.0], seed = 777)`
  - Align example banners, log messages, and `examples/README.md` sample output
    with the new 20-point and 4D parameter sets.
  - Add a “Stable Random-Triangulation Parameter Sets” section to
    `examples/README.md` explaining the shared configurations between tests and examples.
  - Update root `README.md` and `docs/code_organization.md` to reference the
    renamed examples and new test files.
  - Expand `docs/fix-delaunay.md` with current status of global Delaunay repair,
    captured regression seeds, and an execution plan snapshot.
  - Refine `docs/topology.md` to clarify Euler characteristic expectations for
    D-dimensional triangulation boundaries.

- Dependencies and cleanup
  - Bump `clap` from `4.5.51` to `4.5.52` and `smallvec` from `1.13.2` to
    `1.15.1`, updating `Cargo.lock` accordingly.
  - Remove stale proptest regression file
    `proptest-regressions/core/traits/insertion_algorithm.txt` that no longer
    matches the current insertion/repair behavior.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tests/proptest_quality.rs (1)

372-379: Doc comment mismatches the normalized-volume scale test

The comment:

/// Property: Radius ratio is scale-invariant (uniform scaling)

precedes [<prop_normalized_volume_scale_invariant_ $dim d>], which asserts on normalized_volume, not radius_ratio. Recommend updating the wording to avoid confusion:

-/// Property: Radius ratio is scale-invariant (uniform scaling)
+/// Property: Normalized volume is scale-invariant (uniform scaling)
src/core/triangulation_data_structure.rs (1)

2948-3005: Rollback leaves stale incident_cell pointers that should be rebuilt

The review comment is correct and well-founded. Code inspection confirms:

  • remove_vertex() (line 1958) explicitly calls assign_incident_cells() to rebuild vertex incidence after removing cells and before removing the vertex itself (line 2005).
  • rollback_vertex_insertion() (line 2934) calls remove_cells_by_keys() and removes the vertex but never rebuilds the incident_cell pointers that other vertices may still hold to the removed cells.
  • remove_cells_by_keys() only clears neighbor references between cells; it does not touch vertex→cell incidence.

This creates an asymmetry: surviving vertices may have incident_cell pointing to deleted cells, violating the TDS invariant that this field is a valid starting point for cell traversal.

The proposed fix (calling assign_incident_cells() after cell removal but before vertex removal, with debug logging on failure) mirrors the pattern established by remove_vertex() and is syntactically correct. This should be applied to maintain rollback atomicity and data structure consistency.

♻️ Duplicate comments (5)
tests/proptest_quality.rs (1)

214-277: UUID-based cell matching is correct but can become vacuous

The per-cell UUID-matching logic (translation & scale invariance) is structurally sound:

  • uuid_map is built by zipping original and transformed vertices.
  • For each original cell, you derive its vertex UUIDs, map them through uuid_map, and search transformed cells for an equal UUID set (len + contains check) before comparing the metric.

However, because unmatched cells are now simply skipped:

if !matched {
    continue;
}

the whole property can pass without performing any comparison when every cell fails to find a counterpart (e.g., under extreme robustness-driven topology changes), effectively making the test vacuous in those cases.

To keep the tests meaningful while still tolerating legitimate topology differences, consider tracking a global flag or counter:

-                            // Match cells by translating their vertex UUIDs
-                            for orig_key in tds.cell_keys() {
+                            let mut matched_any = false;
+                            // Match cells by translating their vertex UUIDs
+                            for orig_key in tds.cell_keys() {
@@
-                                        if !matched {
-                                            continue;
-                                        }
+                                        if matched {
+                                            matched_any = true;
+                                        }
                                     }
                                 }
                             }
+                            // If absolutely no cells matched, either treat it as an assumption
+                            // (discard the case) or fail explicitly:
+                            prop_assume!(matched_any);

Same pattern can be applied to the normalized-volume translation and scale invariance properties.

This keeps robustness (no failure just because a few cells were dropped/retessellated) while guarding against runs where the property never actually exercises the metrics.

Also applies to: 310-365, 398-453

src/core/algorithms/robust_bowyer_watson.rs (2)

1440-1460: NumCast::from(raw) is still invalid here; use f64::from(raw) and convert back to T

In fallback_visibility_heuristic, the threshold computation:

let raw = scale * scale * multiplier;
// Clamp to finite max to avoid overflow with extreme configs
<f64 as NumCast>::from(raw).map_or_else(
    || <T as NumCast>::from(1e100).unwrap(),
    |raw_f64| { ... }
)

reintroduces the earlier issue: <f64 as NumCast>::from(raw) requires raw: ToPrimitive, but T is only bounded by NumCast, From<f64>, etc., not ToPrimitive. This is very likely to fail to compile for generic T and doesn’t buy anything over the From<T> bound you already have.

A straightforward, bound-compatible fix is:

         let threshold = {
             let scale = self.predicate_config.perturbation_scale;
             let multiplier = self.predicate_config.visibility_threshold_multiplier;
             let raw = scale * scale * multiplier;
-            // Clamp to finite max to avoid overflow with extreme configs
-            <f64 as NumCast>::from(raw).map_or_else(
-                || <T as NumCast>::from(1e100).unwrap(),
-                |raw_f64| {
-                    if raw_f64.is_finite() {
-                        raw
-                    } else {
-                        // Use safe conversion for extreme values
-                        <T as NumCast>::from(f64::MAX / 2.0)
-                            .unwrap_or_else(|| <T as NumCast>::from(1e100).unwrap()) // Fallback for safety
-                    }
-                },
-            )
+            // Clamp to finite max to avoid overflow with extreme configs.
+            // Use the existing `f64: From<T>` bound instead of `NumCast` on `f64`.
+            let raw_f64: f64 = f64::from(raw);
+            if raw_f64.is_finite() {
+                raw
+            } else {
+                // Safe fallback for extreme values; convert back from f64 -> T.
+                <T as NumCast>::from(f64::MAX / 2.0)
+                    .unwrap_or_else(|| <T as NumCast>::from(1e100).unwrap())
+            }
         };

This relies only on the existing f64: From<T>, T: NumCast bounds and removes the implicit T: ToPrimitive requirement.

To double-check the num_traits::NumCast signatures you’re targeting, please verify against your pinned num_traits version:

What is the signature of `num_traits::NumCast::from<T>(n: T)` in the num-traits crate, and what trait bounds does it impose on `T`?

512-582: Refinement loop still uses ? after cavity deletion without restore; rollback needs to be wired through all fallible calls

Inside insert_vertex_cavity_based_with_robust_predicates, once you:

  • save saved_cavity_cells,
  • call remove_bad_cells(tds, &bad_cells), and
  • start the refinement loop,

any early return Err(..) must restore the saved cavity and undo all created cells. Right now these helpers are still invoked with ? after the point-of-no-return:

  • self.find_delaunay_violations_in_cells(tds, &cells_to_check)?;
  • self.find_cavity_boundary_facets(tds, &violating_cells)?;
  • gather_boundary_facet_info(...) ?
  • deduplicate_boundary_facet_info(...) ?
  • filter_boundary_facets_by_valid_facet_sharing(...) ?
  • final self.find_delaunay_violations_in_cells(tds, &all_cell_keys)?;

If any of them returns an InsertionError (e.g. structural validation failure), the function exits without calling restore_cavity_insertion_failure, leaving the TDS missing the original cavity cells and with newly created cells partially wired. This is the same class of issue that was flagged earlier around these sites and is still present in this fallback path.

A minimal fix pattern is to wrap each call in a match, restore, and then return, for example for the first call in the loop:

-            // Check if any cells violate the Delaunay property (using robust predicates)
-            let violating_cells = self.find_delaunay_violations_in_cells(tds, &cells_to_check)?;
+            // Check if any cells violate the Delaunay property (using robust predicates)
+            let violating_cells = match self.find_delaunay_violations_in_cells(tds, &cells_to_check) {
+                Ok(v) => v,
+                Err(e) => {
+                    <Self as InsertionAlgorithm<T, U, V, D>>::restore_cavity_insertion_failure(
+                        tds,
+                        &saved_cavity_cells,
+                        &created_cell_keys,
+                        !vertex_existed_before,
+                        inserted_vk,
+                    );
+                    return Err(e);
+                }
+            };

The same pattern should be applied to:

  • find_cavity_boundary_facets,
  • gather_boundary_facet_info,
  • deduplicate_boundary_facet_info,
  • filter_boundary_facets_by_valid_facet_sharing, and
  • the final find_delaunay_violations_in_cells just before returning GeometricFailure at the end of the function.

That keeps this robust fallback path transactional in the same way as the trait’s default insert_vertex_cavity_based implementation.

Also applies to: 551-582, 706-728

src/core/triangulation_data_structure.rs (2)

2026-2044: Mark remove_vertex_by_uuid as deprecated at the type level

The doc comment marks remove_vertex_by_uuid as deprecated but there is no #[deprecated] attribute, so callers won’t see compiler warnings. This was previously noted and is still missing.

You can align docs and behavior with:

 /// **Deprecated**: Prefer `remove_vertex()` which handles both vertex and cell removal atomically.
-    ///
-    /// # Safety Warning
+    #[deprecated(
+        since = "0.5.2",
+        note = "Use `remove_vertex()` which atomically removes the vertex and related cells."
+    )]
+    ///
+    /// # Safety Warning
     ///
     /// This method only removes the vertex and updates the UUID→Key mapping.

1865-1889: Fix borrow‑checker conflict in neighbor‑clearing let‑chains

Both remove_cells_by_keys and remove_vertex use:

for neighbor_slot in neighbors.iter_mut() {
    if let Some(neighbor_key) = neighbor_slot
        && cells_to_remove.contains(neighbor_key)
    {
        *neighbor_slot = None;
    }
}

This pattern holds a mutable borrow on neighbor_slot for the entire if let scope and then attempts to assign through it, which will not compile (cannot borrow ... as mutable more than once at a time). The same issue was previously flagged and needs to be fixed here as well.

You can resolve it by first checking via an immutable view, then mutating:

-        if let Some(neighbors) = &mut cell.neighbors {
-            for neighbor_slot in neighbors.iter_mut() {
-                if let Some(neighbor_key) = neighbor_slot
-                    && cells_to_remove.contains(neighbor_key)
-                {
-                    *neighbor_slot = None; // Clear dangling reference (becomes boundary)
-                }
-            }
-        }
+        if let Some(neighbors) = &mut cell.neighbors {
+            for neighbor_slot in neighbors.iter_mut() {
+                if neighbor_slot
+                    .as_ref()
+                    .is_some_and(|n| cells_to_remove.contains(n))
+                {
+                    *neighbor_slot = None; // Clear dangling reference (becomes boundary)
+                }
+            }
+        }

Apply the same pattern in remove_vertex where you clear cells_to_remove_set.

Also applies to: 1991-1999

🧹 Nitpick comments (20)
docs/fix-delaunay.md (2)

1-11: Clarify the document's intended audience and role in the repository.

The file declares itself as an internal working document (line 10), which is helpful. However, there's no clear indication where this file should be linked from or how developers should discover it. Consider adding:

  • A brief note at the top indicating this is a tracked working document for the ongoing insertion pipeline redesign effort.
  • A pointer to where this fits in the project roadmap (e.g., link to a ROADMAP.md or relevant issue tracker).
  • A note on when/how to update this document as phases complete.

14-106: Document and clarify the implementation status of Stage 1 and Stage 2.

Stages 1 and 2 are marked [x] as complete, but the nested subsections still contain unchecked items (e.g., lines 149–169 under "Tests and Documentation" are mostly [ ]). This creates ambiguity about what "complete" means.

Suggested clarification:

  • For Stage 1 & 2, explicitly state in a summary callout: "Core insertion logic is implemented; tests and docs remain pending."
  • Or, decompose the [x] markers so only genuinely complete sub-items are marked.
tests/proptest_serialization.rs (2)

229-229: Update stale comment reference.

The comment references proptest_serialization.proptest-regressions, but the PR objectives indicate that proptest regression files were removed for a cleaner repository state. Consider updating or removing this reference.


241-242: Consider calling assign_neighbors() for consistency.

The proptest now explicitly calls assign_neighbors() before counting neighbors (lines 173-174), but this debug test does not. Consider either:

  1. Adding tds.assign_neighbors().expect(...) here to match the fix and show the corrected behavior, or
  2. Documenting why it's intentionally omitted (e.g., to reproduce the original bug)

Adding the call would make the test's diagnostic output more aligned with the corrected behavior being verified in the proptests.

Apply this diff to add the explicit neighbor assignment:

     let vertices: Vec<Vertex<f64, Option<()>, 2>> = Vertex::from_points(points);
-    let tds = Tds::<f64, Option<()>, Option<()>, 2>::new(&vertices)
+    let mut tds = Tds::<f64, Option<()>, Option<()>, 2>::new(&vertices)
         .expect("regression case should construct a valid Tds");
+    tds.assign_neighbors()
+        .expect("assign_neighbors should succeed for constructed Tds");
tests/regression_delaunay_known_configs.rs (2)

18-49: Unused macro parameter: $ignore_reason.

The $ignore_reason parameter is defined in the macro signature but never used in the macro body. Consider either:

  • Removing it if not needed
  • Using it (e.g., in a comment or conditional compilation attribute)

Apply this diff if the parameter is not needed:

 macro_rules! regression_delaunay_violation_test {
     (
         $name:ident,
         $dim:literal,
-        $ignore_reason:expr,
         $build_vertices:block
     ) => {

And update the call site:

 regression_delaunay_violation_test!(
     regression_delaunay_violation_5d_known_config,
     5,
-    "Known 5D Delaunay violation; see docs/fix-delaunay.md (Captured seeds) and debug_delaunay_violation_5d.rs",
     {

63-120: Code duplication: 5D vertex data repeated across test files.

The 5D vertex configuration (lines 69-118) is duplicated verbatim in tests/debug_delaunay_violation_5d.rs (lines 20-70). Consider:

  • Extracting the vertex data into a shared test utility module
  • Or documenting why the duplication is intentional (e.g., different test purposes/contexts)

This improves maintainability if the canonical configuration needs updates.

src/geometry/algorithms/convex_hull.rs (1)

3507-3550: Robust handling of extreme-precision TDS construction outcomes

The new tds_extreme_result flow correctly:

  • Accepts successful Tds::new and exercises both visibility and fallback visibility paths.
  • Treats GeometricDegeneracy as an expected outcome with a clear warning.
  • Panics on any other TriangulationConstructionError, preserving signal for real regressions.

Behavior-wise this is solid. You might consider rewriting the chained if let / else if let into a single match tds_extreme_result (like the earlier extreme_vertices loop) for consistency and slightly clearer control flow, but it’s not required.

src/geometry/util.rs (1)

4888-4921: Dimension-coverage test parameters documentation

The added comment documenting the specific (n_points, bounds, seed) choices in test_generate_random_triangulation_dimensions is helpful context, especially given the interplay with the robust Delaunay repair limits in CI; the actual checks (dim == D and number_of_cells() > 0) still look appropriate.

If you see recurring flakes, consider asserting that at least some vertices were deduped/removed (or logging number_of_vertices() and number_of_cells()) to better diagnose borderline configurations, but this can be deferred.

src/core/util.rs (3)

56-79: Delaunay validation utilities are structurally sound; only minor behavior nits

The new DelaunayValidationError plus is_delaunay / find_delaunay_violations / debug_print_first_delaunay_violation form a coherent validation stack:

  • Cells are structurally checked via cell.is_valid() before geometric tests, with corruption surfaced as TriangulationState(InconsistentDataStructure { .. }).
  • Circumsphere checks use robust predicates and correctly exclude the cell’s own vertices via cell_vertex_keys.
  • INSIDE is treated as a hard violation; BOUNDARY/OUTSIDE are allowed, which is a deliberate “non-strict Delaunay” choice.
  • The O(N×V) cost is clearly documented and gated to debug-style use cases.

Two minor behavioral points you might want to consider:

  1. In both is_delaunay and find_delaunay_violations, any robust_insphere error is treated the same as OUTSIDE/BOUNDARY. If those errors tend to signal numeric trouble rather than benign degeneracy, a debug log (or optional strict mode) could make it easier to catch borderline configurations instead of silently accepting them.

  2. dedup_vertices_epsilon currently accepts any epsilon and uses dist_sq < epsilon * epsilon. For negative epsilon this still “works” because of squaring, but it’s a bit surprising; an debug_assert!(epsilon >= T::zero()) (or a documented precondition) would make the contract explicit without impacting the hot path.

Nothing here is a blocker; the overall design and error surface look good.

Also applies to: 155-213, 215-276, 340-364, 1501-1738


188-213: Vertex dedup helpers are correct but quadratic; that’s acceptable for their intended use

The three helpers dedup_vertices_exact, dedup_vertices_epsilon, and filter_vertices_excluding all:

  • Rely on Vertex<T,U,D> → [T; D] conversions and CoordinateScalar::ordered_eq / an Euclidean-distance-based epsilon check.
  • Preserve the first occurrence in each equivalence class, which is consistent with their documentation and typical triangulation usage.
  • Have straightforward O(n²) behavior due to nested scans over the “unique so far” or reference list.

Given they are intended for pre-processing and degenerate-case handling rather than high-frequency hot paths, this tradeoff seems reasonable. If you later see them show up in profiling for large inputs, switching the exact-dedup variant to a HashSet<Point<T,D>>-based approach (similar to what Tds::new does) would be the natural upgrade, while keeping the epsilon-based variant as-is.

Also applies to: 215-276, 278-338


2396-2424: Delaunay smoke test is a good sanity check for the new utilities

delaunay_validator_reports_no_violations_for_simple_tetrahedron exercises is_delaunay, find_delaunay_violations, and the debug printer on a minimal 3D tetrahedron, which is a nice end-to-end sanity test of the new validation path.

You might eventually add a second test with a deliberately perturbed/non-Delaunay configuration to ensure DelaunayViolation is actually reported as expected, but that’s strictly a nice‑to‑have.

src/core/algorithms/unified_insertion_pipeline.rs (2)

182-298: triangulate flow and degeneracy handling are well-structured

The new triangulate implementation does the right high-level steps:

  • Guards obvious edge-cases (empty input, fewer than D+1 vertices) with precise TriangulationConstructionErrors.
  • Uses find_initial_simplex for a robust, duplicate-aware simplex search and only proceeds with cell creation when a non-degenerate simplex is found.
  • Avoids re-inserting simplex vertices by building a FastHashSet of UUIDs, letting the unified per-vertex pipeline focus on the remaining points.
  • Maps insertion failures in a way that distinguishes structural/construction errors from data-dependent failures via FailedToAddVertex.

When no non-degenerate simplex exists, the fallback of:

  • Seeding the TDS with the unique vertex subset (if it has no cells), and
  • Marking construction_state = TriangulationConstructionState::Incomplete(vertex_count)

gives callers a recoverable, well-labeled “degenerate input” state rather than a half-constructed triangulation.

Given that structural finalization is delegated to finalize_after_insertion, followed by robust.repair_global_delaunay_violations and a policy-driven global Delaunay validation, the pipeline should leave the triangulation in a consistent, Delaunay-respecting state whenever construction succeeds.

I’d keep an eye on the error mapping from InsertionError into TriangulationConstructionError::FailedToAddVertex for downstream tooling (e.g., logging unsalvageable reports alongside the high-level error), but the core control flow looks solid.


317-405: Stepwise 5D debug test is a useful regression harness; expectations will need flipping later

The helper run_stepwise_unified_insertion_debug plus the ignored test debug_5d_stepwise_insertion_of_seventh_vertex give you a very focused harness for the known 5D failure:

  • They assert that the base 6-vertex triangulation is both structurally valid and Delaunay before inserting the 7th vertex.
  • They exercise the unified pipeline with DelaunayCheckPolicy::EndOnly, check Delaunay immediately after insertion, then deliberately expect finalize_triangulation to fail, printing detailed debug diagnostics when it does.

That’s an excellent “living” debug fixture. Once the underlying unified insertion behavior is fixed for this configuration, remember to:

  • Flip the expectation so finalize_triangulation is required to succeed, and
  • Remove the #[ignore] so this becomes a regular regression test instead of a manual debug tool.

Also applies to: 415-476

src/core/algorithms/robust_bowyer_watson.rs (3)

1488-1709: Global Delaunay repair hook looks well‑bounded and consistent with unsalvageable‑vertex semantics

The new repair_global_delaunay_violations pass:

  • Uses find_delaunay_violations in a read‑only way.
  • Searches for a witness vertex strictly inside each violating cell’s circumsphere via robust_insphere.
  • Reuses the transactional insert_vertex_cavity_based path (with rollback) to re‑triangulate around witnesses.
  • Treats recoverable failures by removing the witness vertex via tds.remove_vertex and tracking it in unsalvageable_vertices, while surfacing non‑recoverable TriangulationConstructionError::FailedToAddVertex early.
  • Is guarded by MAX_GLOBAL_REPAIR_ITERATIONS and MAX_REPAIRS_PER_ITERATION and returns a structured ValidationError when no progress is possible.

This is a reasonable and conservative design for enforcing global Delaunay post‑construction while preserving TDS invariants and making “dropped” vertices observable to callers.


1903-2090: insert_vertex_impl wrapper correctly centralizes strategy selection, fallbacks, and unsalvageable tracking

The new InsertionAlgorithm::insert_vertex_impl for RobustBowyerWatson:

  • Routes the initial attempt based on determine_strategy.
  • Tries cavity‑based, hull‑extension, or fallback insertion as appropriate, with clear secondary fallbacks.
  • Treats e.is_recoverable() cases as “Skip” insertions, pushes the vertex into unsalvageable_vertices, and records a synthetic InsertionInfo with strategy = Skip.
  • Ensures statistics are updated exactly once per insert attempt via self.stats.record_vertex_insertion(&info) in the final match.

This is a clean separation of policy (strategy + fallback + diagnostics) from the core insertion mechanics in the trait default implementations, and it works well with the new global repair and unsalvageable‑vertex API.


2260-2369: Robust triangulate entrypoint integrates initial simplex search, incremental insertion, and global repair coherently

The new triangulate implementation:

  • Clears unsalvageable_vertices per run, preserving diagnostics semantics.
  • Uses find_initial_simplex to filter duplicates/near‑duplicates and either:
    • Builds an initial simplex via create_initial_simplex and marks the TDS as constructed, or
    • Leaves an incomplete, vertex‑only TDS with a detailed GeometricDegeneracy error and construction_state = Incomplete(count) when no non‑degenerate simplex exists.
  • Inserts remaining vertices incrementally via self.insert_vertex, mapping InsertionError into TriangulationConstructionError without losing context.
  • Performs a single structural finalization (finalize_after_insertion) followed by the new repair_global_delaunay_violations pass, so the final triangulation is both structurally valid and globally Delaunay for the salvageable subset of vertices.

Overall this gives a clear, single “robust triangulate” entrypoint that composes correctly with the rest of the robust insertion pipeline.

src/core/triangulation_data_structure.rs (4)

1909-2015: Avoid duplicate neighbor cleanup between remove_vertex and remove_cells_by_keys

remove_vertex manually clears neighbor slots that point to cells_to_remove_set and then calls remove_cells_by_keys, which now performs essentially the same neighbor cleanup internally. This is functionally correct but redundant and adds another place that must be kept in sync if neighbor handling changes.

Once the borrow‑checker fix is in place, consider simplifying remove_vertex to:

  • Compute cells_to_remove.
  • Call remove_cells_by_keys(&cells_to_remove) and rely on its neighbor cleanup.
  • Rebuild incident cells and drop the vertex.

That keeps all neighbor‑slot semantics in one method.


5051-5109: Rollback test doesn’t currently exercise incident_cell invariants

test_add_vertex_atomic_rollback_on_topology_failure verifies vertex/cell counts, UUID mapping, generation, and is_valid(), which is good coverage. However, since is_valid() doesn’t inspect incident_cell, this test wouldn’t catch stale incident_cell pointers left by rollback (the issue noted in rollback_vertex_insertion).

Once you wire assign_incident_cells() into rollback, consider extending this test (or adding a new one) to assert that:

  • No vertex has incident_cell pointing to a removed cell.

  • Any non‑None incident_cell matches a cell that actually contains the vertex.


5405-5615: Diagnostics and Delaunay policy tests are valuable; watch global counter concurrency

The tests around bowyer_watson_with_diagnostics (unique vs duplicate vertices) and the DelaunayCheckPolicy variants (EveryN(1) and EndOnly) give good confidence in the unified insertion pipeline and in the wiring of GLOBAL_DELAUNAY_VALIDATION_CALLS.

One caveat: because that counter is global and tests can run in parallel, there’s still some risk of occasional flakiness if other tests also touch it. Your “minimum expected calls” assertions mitigate this, but if you see intermittent failures downstream it may be worth forcing these particular tests to run serially (or scoping the counter more tightly).


10254-10325: UUID mapping integrity tests are strong; they assume Tds::new always succeeds

Both test_uuid_mapping_prevents_corruption and test_uuid_mapping_integrity_after_construction do a good job of asserting that each vertex UUID maps to exactly one key and round‑trips through vertex_key_from_uuid/get_vertex_by_key. They currently assume Tds::new(&vertices) succeeds; if construction can legitimately fail for some higher‑dimensional or degenerate inputs in future, you may want to pattern‑match on the Result (as you’ve done in some other tests) and treat structured errors as acceptable.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e19227 and c84bfbf.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (26)
  • Cargo.toml (1 hunks)
  • README.md (1 hunks)
  • docs/code_organization.md (4 hunks)
  • docs/fix-delaunay.md (1 hunks)
  • docs/topology.md (8 hunks)
  • examples/README.md (8 hunks)
  • examples/convex_hull_3d_20_points.rs (2 hunks)
  • examples/triangulation_3d_20_points.rs (2 hunks)
  • examples/zero_allocation_iterator_demo.rs (1 hunks)
  • proptest-regressions/core/traits/insertion_algorithm.txt (0 hunks)
  • src/core/algorithms/robust_bowyer_watson.rs (32 hunks)
  • src/core/algorithms/unified_insertion_pipeline.rs (1 hunks)
  • src/core/cell.rs (10 hunks)
  • src/core/facet.rs (1 hunks)
  • src/core/triangulation_data_structure.rs (41 hunks)
  • src/core/util.rs (5 hunks)
  • src/geometry/algorithms/convex_hull.rs (5 hunks)
  • src/geometry/quality.rs (5 hunks)
  • src/geometry/util.rs (6 hunks)
  • tests/debug_delaunay_violation_5d.rs (1 hunks)
  • tests/proptest_invariants.rs (1 hunks)
  • tests/proptest_quality.rs (4 hunks)
  • tests/proptest_serialization.rs (2 hunks)
  • tests/regression_delaunay_known_configs.rs (1 hunks)
  • tests/serialization_vertex_preservation.rs (1 hunks)
  • tests/test_facet_cache_integration.rs (9 hunks)
💤 Files with no reviewable changes (1)
  • proptest-regressions/core/traits/insertion_algorithm.txt
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/core/cell.rs
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • examples/zero_allocation_iterator_demo.rs
  • tests/proptest_invariants.rs
  • docs/code_organization.md
  • src/core/facet.rs
  • tests/debug_delaunay_violation_5d.rs
  • tests/test_facet_cache_integration.rs
  • src/geometry/algorithms/convex_hull.rs
  • tests/proptest_quality.rs
  • docs/fix-delaunay.md
  • tests/proptest_serialization.rs
  • examples/README.md
  • src/geometry/quality.rs
  • examples/triangulation_3d_20_points.rs
  • tests/regression_delaunay_known_configs.rs
  • examples/convex_hull_3d_20_points.rs
  • README.md
  • src/geometry/util.rs
  • src/core/algorithms/unified_insertion_pipeline.rs
  • src/core/util.rs
  • src/core/triangulation_data_structure.rs
  • src/core/algorithms/robust_bowyer_watson.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • examples/zero_allocation_iterator_demo.rs
  • tests/proptest_invariants.rs
  • docs/code_organization.md
  • src/core/facet.rs
  • Cargo.toml
  • tests/debug_delaunay_violation_5d.rs
  • tests/test_facet_cache_integration.rs
  • src/geometry/algorithms/convex_hull.rs
  • tests/serialization_vertex_preservation.rs
  • tests/proptest_quality.rs
  • tests/proptest_serialization.rs
  • src/geometry/quality.rs
  • examples/triangulation_3d_20_points.rs
  • tests/regression_delaunay_known_configs.rs
  • examples/convex_hull_3d_20_points.rs
  • README.md
  • src/geometry/util.rs
  • src/core/util.rs
  • src/core/triangulation_data_structure.rs
  • src/core/algorithms/robust_bowyer_watson.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.

Applied to files:

  • tests/proptest_invariants.rs
  • docs/code_organization.md
  • src/core/facet.rs
  • tests/debug_delaunay_violation_5d.rs
  • tests/test_facet_cache_integration.rs
  • src/geometry/algorithms/convex_hull.rs
  • tests/proptest_quality.rs
  • docs/fix-delaunay.md
  • tests/proptest_serialization.rs
  • src/geometry/quality.rs
  • tests/regression_delaunay_known_configs.rs
  • README.md
  • src/geometry/util.rs
  • src/core/util.rs
  • src/core/triangulation_data_structure.rs
📚 Learning: 2025-09-02T02:40:54.439Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 58
File: scripts/tests/test_subprocess_utils.py:92-101
Timestamp: 2025-09-02T02:40:54.439Z
Learning: In Rust projects, cargo is expected to be available in development and CI environments, so conditional skipping of cargo-dependent tests is typically unnecessary.

Applied to files:

  • docs/code_organization.md
📚 Learning: 2025-09-17T17:53:34.532Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 83
File: src/core/traits/facet_cache.rs:588-652
Timestamp: 2025-09-17T17:53:34.532Z
Learning: The build_cache_with_rcu method in FacetCacheProvider correctly returns Option<Arc<FacetToCellsMap>> representing the old cache value before the RCU update operation. The test logic using old_value.is_none() to detect which thread built the cache is correct: None means this thread built it, Some(old_cache) means another thread already built it.

Applied to files:

  • tests/test_facet_cache_integration.rs
  • src/core/algorithms/robust_bowyer_watson.rs
📚 Learning: 2025-09-17T17:51:52.098Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 83
File: src/core/traits/facet_cache.rs:533-585
Timestamp: 2025-09-17T17:51:52.098Z
Learning: The build_cache_with_rcu method in FacetCacheProvider returns Option<Arc<FacetToCellsMap>> representing the old cache value before the RCU update operation, not a tuple with a "built" flag. First call returns None (no old value), subsequent calls return Some(old_cache).

Applied to files:

  • src/core/algorithms/robust_bowyer_watson.rs
📚 Learning: 2025-08-20T21:55:04.552Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 50
File: src/core/algorithms/robust_bowyer_watson.rs:191-195
Timestamp: 2025-08-20T21:55:04.552Z
Learning: CoordinateScalar trait guarantees PartialOrd, making direct comparisons between CoordinateScalar values valid without needing conversion to f64.

Applied to files:

  • src/core/algorithms/robust_bowyer_watson.rs
📚 Learning: 2025-08-20T21:55:04.552Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 50
File: src/core/algorithms/robust_bowyer_watson.rs:191-195
Timestamp: 2025-08-20T21:55:04.552Z
Learning: CoordinateScalar trait includes PartialOrd through its Float supertrait bound, making direct comparisons between CoordinateScalar values valid without needing conversion to f64.

Applied to files:

  • src/core/algorithms/robust_bowyer_watson.rs
🧬 Code graph analysis (16)
examples/zero_allocation_iterator_demo.rs (1)
src/geometry/util.rs (16)
  • generate_random_triangulation (2089-2139)
  • generate_random_triangulation (4779-4779)
  • generate_random_triangulation (4796-4796)
  • generate_random_triangulation (4813-4813)
  • generate_random_triangulation (4817-4817)
  • generate_random_triangulation (4846-4846)
  • generate_random_triangulation (4856-4856)
  • generate_random_triangulation (4867-4867)
  • generate_random_triangulation (4871-4871)
  • generate_random_triangulation (4896-4896)
  • generate_random_triangulation (4903-4903)
  • generate_random_triangulation (4910-4910)
  • generate_random_triangulation (4917-4917)
  • generate_random_triangulation (4930-4930)
  • generate_random_triangulation (4958-4958)
  • generate_random_triangulation (4976-4976)
tests/proptest_invariants.rs (2)
src/core/triangulation_data_structure.rs (2)
  • vertices (723-725)
  • add (2758-2907)
src/core/vertex.rs (2)
  • point (475-477)
  • from_points (419-424)
tests/debug_delaunay_violation_5d.rs (2)
src/core/cell.rs (2)
  • vertices (680-682)
  • new (476-507)
src/core/triangulation_data_structure.rs (3)
  • vertices (723-725)
  • new (2554-2616)
  • validate_delaunay (4289-4314)
tests/test_facet_cache_integration.rs (3)
src/core/algorithms/robust_bowyer_watson.rs (1)
  • new (138-149)
src/core/triangulation_data_structure.rs (2)
  • new (2554-2616)
  • vertices (723-725)
src/core/traits/facet_cache.rs (1)
  • new (462-467)
src/geometry/algorithms/convex_hull.rs (1)
src/core/triangulation_data_structure.rs (2)
  • new (2554-2616)
  • vertices (723-725)
tests/proptest_quality.rs (3)
src/core/cell.rs (4)
  • vertices (680-682)
  • map (411-411)
  • uuid (792-794)
  • vertex_uuids (866-875)
src/core/triangulation_data_structure.rs (3)
  • vertices (723-725)
  • cell_keys (741-743)
  • get_cell (751-753)
src/core/vertex.rs (3)
  • map (293-293)
  • map (303-303)
  • uuid (530-532)
tests/proptest_serialization.rs (3)
src/core/cell.rs (5)
  • new (476-507)
  • vertices (680-682)
  • number_of_vertices (765-767)
  • cell (4340-4342)
  • neighbors (647-651)
src/core/triangulation_data_structure.rs (5)
  • new (2554-2616)
  • vertices (723-725)
  • assign_neighbors (797-911)
  • number_of_cells (1139-1141)
  • number_of_vertices (984-986)
src/core/vertex.rs (2)
  • point (475-477)
  • from_points (419-424)
src/geometry/quality.rs (1)
src/core/triangulation_data_structure.rs (2)
  • vertices (723-725)
  • new (2554-2616)
examples/triangulation_3d_20_points.rs (1)
src/geometry/util.rs (16)
  • generate_random_triangulation (2089-2139)
  • generate_random_triangulation (4779-4779)
  • generate_random_triangulation (4796-4796)
  • generate_random_triangulation (4813-4813)
  • generate_random_triangulation (4817-4817)
  • generate_random_triangulation (4846-4846)
  • generate_random_triangulation (4856-4856)
  • generate_random_triangulation (4867-4867)
  • generate_random_triangulation (4871-4871)
  • generate_random_triangulation (4896-4896)
  • generate_random_triangulation (4903-4903)
  • generate_random_triangulation (4910-4910)
  • generate_random_triangulation (4917-4917)
  • generate_random_triangulation (4930-4930)
  • generate_random_triangulation (4958-4958)
  • generate_random_triangulation (4976-4976)
tests/regression_delaunay_known_configs.rs (1)
src/core/triangulation_data_structure.rs (3)
  • vertices (723-725)
  • new (2554-2616)
  • validate_delaunay (4289-4314)
examples/convex_hull_3d_20_points.rs (1)
src/geometry/util.rs (16)
  • generate_random_triangulation (2089-2139)
  • generate_random_triangulation (4779-4779)
  • generate_random_triangulation (4796-4796)
  • generate_random_triangulation (4813-4813)
  • generate_random_triangulation (4817-4817)
  • generate_random_triangulation (4846-4846)
  • generate_random_triangulation (4856-4856)
  • generate_random_triangulation (4867-4867)
  • generate_random_triangulation (4871-4871)
  • generate_random_triangulation (4896-4896)
  • generate_random_triangulation (4903-4903)
  • generate_random_triangulation (4910-4910)
  • generate_random_triangulation (4917-4917)
  • generate_random_triangulation (4930-4930)
  • generate_random_triangulation (4958-4958)
  • generate_random_triangulation (4976-4976)
src/geometry/util.rs (2)
src/core/cell.rs (2)
  • number_of_vertices (765-767)
  • dim (946-948)
src/core/triangulation_data_structure.rs (2)
  • number_of_vertices (984-986)
  • dim (1074-1080)
src/core/algorithms/unified_insertion_pipeline.rs (4)
src/core/triangulation_data_structure.rs (3)
  • bowyer_watson (3012-3017)
  • new (2554-2616)
  • vertices (723-725)
src/core/algorithms/robust_bowyer_watson.rs (6)
  • find_initial_simplex (2290-2290)
  • new (138-149)
  • for_degenerate_cases (215-226)
  • insert_vertex_impl (1904-2091)
  • cell (3546-3548)
  • reset (2251-2258)
src/core/traits/insertion_algorithm.rs (26)
  • find_initial_simplex (1182-1307)
  • find_initial_simplex (3161-3161)
  • find_initial_simplex (7283-7283)
  • find_initial_simplex (7322-7322)
  • find_initial_simplex (7355-7355)
  • find_initial_simplex (7376-7376)
  • unified_insert_vertex_fast_robust_or_skip (1044-1145)
  • vertex (981-983)
  • new (527-540)
  • new (1353-1361)
  • tds (4598-4610)
  • run_global_delaunay_validation_with_policy (3425-3448)
  • insert_vertex (1659-1701)
  • insert_vertex_impl (1728-1732)
  • insert_vertex_impl (7561-7573)
  • insert_vertex_impl (7586-7595)
  • insert_vertex_impl (7608-7620)
  • insert_vertex_impl (7633-7642)
  • classification (987-989)
  • reset (543-545)
  • reset (1740-1740)
  • reset (7579-7579)
  • reset (7601-7601)
  • reset (7626-7626)
  • reset (7648-7648)
  • finalize_triangulation (3471-3504)
src/core/algorithms/bowyer_watson.rs (3)
  • new (180-189)
  • insert_vertex_impl (261-332)
  • reset (341-348)
src/core/util.rs (4)
src/core/cell.rs (6)
  • uuid (792-794)
  • cell (4340-4342)
  • vertices (680-682)
  • new (476-507)
  • number_of_vertices (765-767)
  • neighbors (647-651)
src/core/vertex.rs (3)
  • uuid (530-532)
  • point (475-477)
  • None (2084-2084)
src/geometry/robust_predicates.rs (2)
  • robust_insphere (149-192)
  • num_traits (1421-1421)
src/core/triangulation_data_structure.rs (4)
  • vertices (723-725)
  • new (2554-2616)
  • number_of_vertices (984-986)
  • number_of_cells (1139-1141)
src/core/triangulation_data_structure.rs (4)
src/core/algorithms/robust_bowyer_watson.rs (5)
  • cell (3546-3548)
  • default (1877-1879)
  • new (138-149)
  • triangulate (2260-2370)
  • unsalvageable_vertices (82-84)
src/core/traits/insertion_algorithm.rs (7)
  • vertex (981-983)
  • default (1340-1342)
  • default (1610-1612)
  • new (527-540)
  • new (1353-1361)
  • triangulate (3135-3234)
  • tds (4598-4610)
src/core/algorithms/unified_insertion_pipeline.rs (2)
  • with_policy (48-58)
  • triangulate (182-298)
src/core/util.rs (1)
  • is_delaunay (1557-1617)
src/core/algorithms/robust_bowyer_watson.rs (3)
src/core/traits/insertion_algorithm.rs (27)
  • find_initial_simplex (1182-1307)
  • find_initial_simplex (3161-3161)
  • find_initial_simplex (7283-7283)
  • find_initial_simplex (7322-7322)
  • find_initial_simplex (7355-7355)
  • find_initial_simplex (7376-7376)
  • new (527-540)
  • new (1353-1361)
  • tds (4598-4610)
  • remove_bad_cells (4728-4737)
  • finalize_after_insertion (4905-4934)
  • vertex (981-983)
  • rollback_vertex_insertion (4673-4687)
  • connect_new_cells_to_neighbors (4513-4648)
  • restore_cavity_insertion_failure (4793-4826)
  • gather_boundary_facet_info (4306-4418)
  • deduplicate_boundary_facet_info (4065-4096)
  • filter_boundary_facets_by_valid_facet_sharing (4135-4273)
  • validate_no_delaunay_violations (3329-3413)
  • insert_vertex_cavity_based (2402-2855)
  • insert_vertex_impl (1728-1732)
  • insert_vertex_impl (7561-7573)
  • insert_vertex_impl (7586-7595)
  • insert_vertex_impl (7608-7620)
  • insert_vertex_impl (7633-7642)
  • find_delaunay_violations_in_cells (2195-2220)
  • create_initial_simplex (3255-3310)
src/geometry/robust_predicates.rs (13)
  • num_traits (1421-1421)
  • util (216-216)
  • util (260-260)
  • robust_insphere (149-192)
  • config_presets (1174-1174)
  • config_presets (1278-1278)
  • config_presets (1337-1337)
  • config_presets (1344-1344)
  • config_presets (1349-1349)
  • config_presets (1355-1355)
  • config_presets (1404-1404)
  • config_presets (1645-1645)
  • general_triangulation (736-746)
src/core/util.rs (5)
  • k (560-560)
  • geometry (1566-1566)
  • geometry (1671-1671)
  • find_delaunay_violations (1658-1738)
  • config_presets (1854-1854)
🪛 LanguageTool
docs/fix-delaunay.md

[grammar] ~306-~306: Use a hyphen to join words.
Context: ...with: - Instructions for running small seeded Delaunay tests, - Guidance on u...

(QB_NEW_EN_HYPHEN)


[locale-violation] ~356-~356: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...ize_triangulationand remains violated afterwards, using the stepwise helper inun...

(AFTERWARDS_US)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: performance-regression
  • GitHub Check: Codacy Security Scan
  • GitHub Check: Code Coverage
  • GitHub Check: build (windows-latest)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: build (macos-latest)
  • GitHub Check: Codacy Static Code Analysis

- If `validate_delaunay()` fails, treat that as a genuine algorithm bug and keep
the test strict until fixed.
- [ ] Update this document and `tests/README.md` with:
- Instructions for running small seeded Delaunay tests,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix hyphenation in line 306.

Use a hyphen to join the compound adjective "small-seeded" (or rephrase).

-  - Instructions for running small seeded Delaunay tests,
+  - Instructions for running small-seeded Delaunay tests,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Instructions for running small seeded Delaunay tests,
- Instructions for running small-seeded Delaunay tests,
🧰 Tools
🪛 LanguageTool

[grammar] ~306-~306: Use a hyphen to join words.
Context: ...with: - Instructions for running small seeded Delaunay tests, - Guidance on u...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
In docs/fix-delaunay.md around line 306, the phrase "Instructions for running
small seeded Delaunay tests," should use a hyphenated compound adjective; change
it to "Instructions for running small-seeded Delaunay tests," (or rephrase to
avoid the compound) so "small-seeded" correctly modifies "Delaunay tests".

- [x] For incremental cases (5D canonical configuration), start from a small valid base
triangulation and insert remaining vertices one-by-one using the unified pipeline.
- [x] After inserting the final vertex (7th), assert that Delaunay is already violated
before `finalize_triangulation` and remains violated afterwards, using the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Prefer "afterward" over "afterwards" (American English).

-    - After insertion, run `validate_delaunay` before finalization and again after
-      `finalize_triangulation`.
+    - After insertion, run `validate_delaunay` before finalization and again after
+      `finalize_triangulation`.
-  - Outcome (5D): the base 6-vertex triangulation is globally Delaunay, but the
-    7th insertion already produces Delaunay violations *before* finalization,
-    indicating that the per-vertex unified insertion pipeline itself can leave
-    non-Delaunay cells for this configuration.
+  - Outcome (5D): the base 6-vertex triangulation is globally Delaunay, but the
+    7th insertion already produces Delaunay violations *before* finalization,
+    indicating that the per-vertex unified insertion pipeline itself can leave
+    non-Delaunay cells for this configuration.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 LanguageTool

[locale-violation] ~356-~356: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...ize_triangulationand remains violated afterwards, using the stepwise helper inun...

(AFTERWARDS_US)

🤖 Prompt for AI Agents
In docs/fix-delaunay.md around line 356, replace the word "afterwards" with
American English "afterward" so the sentence reads "before
`finalize_triangulation` and remains violated afterward, using the". Make this
single-word substitution to conform to preferred American usage.

- Switch large random and mixed-pattern robust BW tests to use
  RobustBowyerWatson::triangulate on full vertex sets instead of
  per-insertion validate_delaunay() hooks
- Preserve structural + global Delaunay checks via Tds::is_valid() and
  Tds::validate_delaunay(), using debug_print_first_delaunay_violation
  for diagnostics on failure
- Mark unstable robust Delaunay stress tests (large random sets,
  clustered+scattered patterns, 2D grid, near-degenerate 3D config)
  as #[ignore] with references to docs/fix-delaunay.md so they remain
  available for manual debugging without breaking CI
- Keep reset/reuse and mixed interior/exterior 3D tests active while
  routing them through triangulate to exercise the unified robust
  pipeline entrypoint
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (8)
src/core/algorithms/bowyer_watson.rs (1)

268-340: Trait insert_vertex_impl wiring matches existing insertion logic

The new insert_vertex_impl implementation:

  • Delegates to the same strategy selection (determine_insertion_strategy) and trait-level insertion helpers as before.
  • Applies the same fallback sequence (cavity → hull extension → fallback).
  • Updates the stats struct only on successful insertions.

This cleanly fits the new InsertionAlgorithm trait shape where the default insert_vertex does duplicate detection and then calls insert_vertex_impl. If you later want richer statistics (e.g., strategy counts), consider reusing InsertionStatistics::record_vertex_insertion instead of manual field increments, but the current behavior is consistent with prior tests.

src/core/algorithms/unified_insertion_pipeline.rs (1)

81-137: Statistic aggregation matches per-algorithm counters and pipeline metadata

unified_statistics:

  • Sums the underlying algorithms’ InsertionStatistics.
  • Derives duplicate vs unsalvageable counts from unsalvageable_reports.
  • Exposes fast/robust attempt and success counts, skipped count, and initial simplex stats via TriangulationStatistics.

This gives a coherent, single view of a triangulation run. One subtle point: since counts for duplicates/unsalvageable/skipped are derived from unsalvageable_reports, callers should obtain statistics before calling take_unsalvageable_reports(); documenting this ordering expectation near both methods would prevent misuse.

src/geometry/algorithms/convex_hull.rs (1)

2565-2593: Graceful handling of Tds::new failures for extreme coordinates

Matching on Result<Tds, TriangulationConstructionError> and explicitly special‑casing GeometricDegeneracy makes these tests robust to platforms where extreme coordinates are rejected as numerically unstable, while still panicking on any unexpected error variant. That’s a solid pattern for “best‑effort” extreme‑value tests.

You repeat essentially the same match logic in multiple tests; if this grows further, consider a small test helper (e.g. fn tds_or_skip_on_degeneracy(...)) to centralize the behavior, but it’s fine as is.

Also applies to: 3507-3552

docs/fix-delaunay.md (1)

280-287: Tighten Markdown list formatting for multi‑line checklist items

In the Phase 3 sections, several logical single bullet points are split across multiple lines that each start with - (e.g., “For at least one canonical failing 5D configuration…” followed by - and call Tds::new(...), etc.). This renders as multiple separate bullets rather than one wrapped item, which makes the checklist a bit harder to scan.

Consider dropping the leading - on continuation lines and indenting them instead so each checklist item is a single list entry with wrapped text.

Also applies to: 388-401

tests/integration_robust_bowyer_watson.rs (1)

14-16: Robust Bowyer–Watson integration tests align with the unified pipeline; consider RNG choice for long‑term reproducibility

The updated integration tests correctly:

  • Build full vertex sets (initial simplex + random / clustered / scattered points),
  • Triangulate via RobustBowyerWatson::triangulate starting from Tds::empty(),
  • Assert tds.is_valid().is_ok() and tds.validate_delaunay().is_ok() on the final TDS, and
  • Use #[ignore] on known-stress scenarios with clear messages tied to docs/fix-delaunay.md.

This matches the new two-stage construction story and exercises global Delaunay repair in realistic workloads.

One minor improvement you might want to consider later: StdRng::seed_from_u64 is convenient, but the rand 0.9 docs recommend using a ChaCha RNG (e.g., ChaCha8Rng from rand_chacha) when you care about stable, cross-platform reproducibility of seeded tests. If you want these stress tests to be maximally reproducible across platforms and future rand versions, swapping StdRng for a ChaCha RNG in the test-only code would better align with that guidance.

Please double-check the current rand book / docs for 0.9.x to confirm their latest recommendation on reproducible RNG choice in tests and whether StdRng meets your needs long term.

Also applies to: 35-90, 151-216, 248-255, 260-307

src/lib.rs (1)

107-263: Docs and public surface for invariants/Delaunay look consistent; clarify “internal” wording on unified_insertion_pipeline

The expanded crate‑level docs around structural vs Delaunay invariants, validation helpers, correctness guarantees, diagnostics, and zero‑cell behavior read consistently with the new Tds and utility APIs you’ve introduced. They set good expectations for is_valid, validation_report, validate_delaunay, and the unified fast+robust insertion pipeline.

One small naming/expectation nit: Line 432–433 documents core::algorithms::unified_insertion_pipeline as an “Internal unified fast+robust insertion pipeline for Stage 2” but exposes it as a regular pub mod. If you intend this to be semi‑internal and not a stable public API, consider either:

  • Marking it #[doc(hidden)] while still keeping it pub for tests/benchmarks, or
  • Rephrasing the doc comment to avoid “internal” and instead note that it’s a lower‑level orchestration module with fewer stability guarantees.

This avoids mixed signals for downstream users relying on the public module tree.

Also applies to: 347-397, 432-435

src/core/util.rs (1)

4-22: Delaunay validation surface and vertex‑dedup utilities look correct; refactor shared logic only if it starts to drift

The new pieces here fit together well:

  • DelaunayValidationError cleanly distinguishes:

    • true Delaunay violations (DelaunayViolation),
    • structural TDS issues (TriangulationState wrapping TriangulationValidationError),
    • per‑cell problems (InvalidCell wrapping CellValidationError), and
    • numeric predicate failures (NumericPredicateError with cell/vertex keys plus CoordinateConversionError).
  • is_delaunay and find_delaunay_violations:

    • Validate each cell via cell.is_valid() first,
    • Detect missing vertex references as TriangulationState::InconsistentDataStructure,
    • Use a shared robust insphere configuration and skip the cell’s own vertices, and
    • Either short‑circuit on the first violation (is_delaunay) or accumulate all violating cells (find_delaunay_violations).
      Semantics match what Tds::validate_delaunay expects when mapping these errors back into TriangulationValidationError, and the documented O(N×V) cost is accurate.
  • The debug helper debug_print_first_delaunay_violation gives rich diagnostics while being cfg‑gated to tests / debug builds only, so there’s no runtime cost in release builds.

  • The vertex dedup/filter helpers (dedup_vertices_exact, dedup_vertices_epsilon, filter_vertices_excluding) and their coords_equal_exact / coords_within_epsilon helpers are straightforward and type‑safe. They are inherently O(n²) in vertex count, but that’s acceptable for the intended “helper” usages and is called out in comments; heavier paths (like Tds::new) already use HashSet‑based dedup.

If you later find is_delaunay and find_delaunay_violations diverging, it may be worth extracting a shared internal iterator over (cell_key, cell_vertex_points, cell_vertex_keys) to remove duplication, but that’s a nice‑to‑have rather than something that needs to block this PR.

Also applies to: 56-92, 162-383, 841-932, 971-1057, 1520-1952, 2439-2467

src/core/triangulation_data_structure.rs (1)

2951-3060: Rollback path is much safer now; consider aligning docs with error‑handling behavior

rollback_vertex_insertion now mirrors remove_vertex:

  • It finds all cells containing the vertex and removes them via remove_cells_by_keys, which also clears neighbor slots pointing at those cells.
  • It calls assign_incident_cells() (logged in debug; silently ignored in release) to rebuild vertex→cell incidence.
  • It removes the vertex and UUID mapping, logs cell removals in debug, sanity‑checks counts against pre_state under a slack bound, and bumps generation.

This is a solid improvement over the previous behavior that left dangling neighbor/incident references; the tests that explicitly assert there are no incident_cell pointers to removed cells are appropriate.

The only nit is that the docstring still says this “ensur[es] the triangulation remains in a consistent state”, while in release builds you ignore a possible assign_incident_cells error, which could leave the TDS corrupted if that error is hit. If you want to keep the current behavior, you might slightly soften the wording (“best effort” / “attempts to restore consistency and logs failures”) or, alternatively, consider returning a Result and propagating serious validation failures.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c84bfbf and ca666be.

📒 Files selected for processing (17)
  • docs/code_organization.md (2 hunks)
  • docs/fix-delaunay.md (1 hunks)
  • src/core/algorithms/bowyer_watson.rs (3 hunks)
  • src/core/algorithms/unified_insertion_pipeline.rs (1 hunks)
  • src/core/triangulation_data_structure.rs (46 hunks)
  • src/core/util.rs (5 hunks)
  • src/geometry/algorithms/convex_hull.rs (5 hunks)
  • src/geometry/util.rs (6 hunks)
  • src/lib.rs (3 hunks)
  • tests/README.md (2 hunks)
  • tests/integration_robust_bowyer_watson.rs (10 hunks)
  • tests/proptest_duplicates.rs (1 hunks)
  • tests/proptest_quality.rs (4 hunks)
  • tests/proptest_serialization.rs (2 hunks)
  • tests/regression_delaunay_known_configs.rs (1 hunks)
  • tests/tds_basic_integration.rs (4 hunks)
  • tests/test_insertion_algorithm_trait.rs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/geometry/util.rs
  • docs/code_organization.md
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • tests/tds_basic_integration.rs
  • tests/proptest_quality.rs
  • tests/README.md
  • tests/proptest_duplicates.rs
  • src/core/algorithms/bowyer_watson.rs
  • tests/integration_robust_bowyer_watson.rs
  • docs/fix-delaunay.md
  • tests/proptest_serialization.rs
  • tests/regression_delaunay_known_configs.rs
  • src/lib.rs
  • src/core/algorithms/unified_insertion_pipeline.rs
  • tests/test_insertion_algorithm_trait.rs
  • src/geometry/algorithms/convex_hull.rs
  • src/core/util.rs
  • src/core/triangulation_data_structure.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • tests/tds_basic_integration.rs
  • tests/proptest_quality.rs
  • tests/README.md
  • tests/proptest_duplicates.rs
  • tests/integration_robust_bowyer_watson.rs
  • tests/proptest_serialization.rs
  • tests/regression_delaunay_known_configs.rs
  • src/lib.rs
  • src/core/algorithms/unified_insertion_pipeline.rs
  • tests/test_insertion_algorithm_trait.rs
  • src/geometry/algorithms/convex_hull.rs
  • src/core/util.rs
  • src/core/triangulation_data_structure.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.

Applied to files:

  • tests/tds_basic_integration.rs
  • tests/proptest_quality.rs
  • tests/README.md
  • tests/proptest_duplicates.rs
  • tests/integration_robust_bowyer_watson.rs
  • docs/fix-delaunay.md
  • tests/proptest_serialization.rs
  • tests/regression_delaunay_known_configs.rs
  • src/lib.rs
  • src/core/algorithms/unified_insertion_pipeline.rs
  • tests/test_insertion_algorithm_trait.rs
  • src/geometry/algorithms/convex_hull.rs
  • src/core/util.rs
  • src/core/triangulation_data_structure.rs
📚 Learning: 2025-11-18T15:01:47.717Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 124
File: src/core/algorithms/unified_insertion_pipeline.rs:71-88
Timestamp: 2025-11-18T15:01:47.717Z
Learning: The `is_multiple_of` method on unsigned integer types (u8, u16, u32, u64, u128, usize) was stabilized in Rust 1.87.0 and is available for use without feature gates in projects with MSRV >= 1.87.0.

Applied to files:

  • src/core/algorithms/unified_insertion_pipeline.rs
🧬 Code graph analysis (12)
tests/tds_basic_integration.rs (1)
src/core/triangulation_data_structure.rs (1)
  • validate_delaunay (4350-4384)
tests/proptest_quality.rs (4)
src/core/triangulation_data_structure.rs (3)
  • vertices (780-782)
  • cell_keys (798-800)
  • get_cell (808-810)
src/core/cell.rs (4)
  • vertices (680-682)
  • map (411-411)
  • uuid (792-794)
  • vertex_uuids (866-875)
src/core/vertex.rs (3)
  • map (293-293)
  • map (303-303)
  • uuid (530-532)
src/geometry/quality.rs (2)
  • radius_ratio (218-265)
  • normalized_volume (313-378)
tests/proptest_duplicates.rs (3)
src/core/algorithms/robust_bowyer_watson.rs (2)
  • new (147-158)
  • unsalvageable_vertices (83-85)
src/core/vertex.rs (2)
  • point (475-477)
  • from_points (419-424)
src/core/triangulation_data_structure.rs (5)
  • new (2595-2658)
  • vertices (780-782)
  • is_valid (4320-4334)
  • validate_delaunay (4350-4384)
  • bowyer_watson_with_diagnostics (3112-3119)
src/core/algorithms/bowyer_watson.rs (2)
src/core/algorithms/robust_bowyer_watson.rs (2)
  • statistics (91-93)
  • insert_vertex_impl (1994-2181)
src/core/traits/insertion_algorithm.rs (8)
  • insert_vertex_impl (1758-1762)
  • insert_vertex_impl (6921-6930)
  • insert_vertex_impl (7744-7756)
  • insert_vertex_impl (7771-7777)
  • insert_vertex_impl (7790-7802)
  • insert_vertex_impl (7817-7823)
  • tds (4638-4650)
  • vertex (999-1001)
tests/integration_robust_bowyer_watson.rs (4)
src/core/triangulation_data_structure.rs (7)
  • new (2595-2658)
  • vertices (780-782)
  • empty (2476-2486)
  • is_valid (4320-4334)
  • validate_delaunay (4350-4384)
  • number_of_vertices (1041-1043)
  • number_of_cells (1196-1198)
src/core/algorithms/robust_bowyer_watson.rs (2)
  • new (147-158)
  • triangulate (2350-2460)
tests/proptest_delaunay_condition.rs (1)
  • create_initial_simplex (106-127)
src/core/util.rs (1)
  • debug_print_first_delaunay_violation (1792-1952)
tests/proptest_serialization.rs (3)
src/core/triangulation_data_structure.rs (6)
  • new (2595-2658)
  • vertices (780-782)
  • assign_neighbors (854-968)
  • dim (1131-1137)
  • number_of_cells (1196-1198)
  • number_of_vertices (1041-1043)
src/core/cell.rs (6)
  • new (476-507)
  • vertices (680-682)
  • dim (946-948)
  • number_of_vertices (765-767)
  • cell (4340-4342)
  • neighbors (647-651)
src/core/vertex.rs (3)
  • point (475-477)
  • from_points (419-424)
  • dim (574-576)
tests/regression_delaunay_known_configs.rs (1)
src/core/triangulation_data_structure.rs (3)
  • vertices (780-782)
  • new (2595-2658)
  • validate_delaunay (4350-4384)
src/core/algorithms/unified_insertion_pipeline.rs (1)
src/core/traits/insertion_algorithm.rs (16)
  • find_initial_simplex (1212-1337)
  • find_initial_simplex (3192-3192)
  • find_initial_simplex (7466-7466)
  • find_initial_simplex (7505-7505)
  • find_initial_simplex (7538-7538)
  • find_initial_simplex (7559-7559)
  • unified_insert_vertex_fast_robust_or_skip (1069-1171)
  • vertex (999-1001)
  • new (545-558)
  • new (1383-1391)
  • tds (4638-4650)
  • run_global_delaunay_validation_with_policy (3465-3488)
  • insert_vertex (1689-1731)
  • classification (1005-1007)
  • create_initial_simplex (3286-3341)
  • finalize_after_insertion (4945-4974)
tests/test_insertion_algorithm_trait.rs (2)
src/core/triangulation_data_structure.rs (6)
  • bowyer_watson (3067-3072)
  • vertices (780-782)
  • new (2595-2658)
  • number_of_vertices (1041-1043)
  • number_of_cells (1196-1198)
  • is_valid (4320-4334)
src/core/algorithms/bowyer_watson.rs (1)
  • new (180-189)
src/geometry/algorithms/convex_hull.rs (3)
src/core/triangulation_data_structure.rs (2)
  • new (2595-2658)
  • vertices (780-782)
src/core/facet.rs (6)
  • new (269-274)
  • new (442-466)
  • new (755-772)
  • new (844-852)
  • vertices (508-529)
  • tds (421-423)
src/geometry/point.rs (2)
  • new (115-117)
  • validate (132-144)
src/core/util.rs (3)
src/core/cell.rs (5)
  • uuid (792-794)
  • cell (4340-4342)
  • vertices (680-682)
  • new (476-507)
  • neighbors (647-651)
src/geometry/robust_predicates.rs (3)
  • robust_insphere (149-192)
  • num_traits (1421-1421)
  • general_triangulation (736-746)
src/core/triangulation_data_structure.rs (4)
  • vertices (780-782)
  • new (2595-2658)
  • is_empty (353-355)
  • is_empty (510-512)
src/core/triangulation_data_structure.rs (5)
src/core/algorithms/robust_bowyer_watson.rs (6)
  • cell (3636-3638)
  • default (1967-1969)
  • statistics (91-93)
  • new (147-158)
  • triangulate (2350-2460)
  • unsalvageable_vertices (83-85)
src/core/traits/insertion_algorithm.rs (7)
  • vertex (999-1001)
  • default (1370-1372)
  • default (1640-1642)
  • new (545-558)
  • new (1383-1391)
  • triangulate (3166-3265)
  • tds (4638-4650)
src/core/algorithms/bowyer_watson.rs (3)
  • default (240-242)
  • statistics (195-197)
  • new (180-189)
src/core/algorithms/unified_insertion_pipeline.rs (2)
  • with_policy (63-79)
  • triangulate (290-409)
src/core/util.rs (1)
  • is_delaunay (1575-1645)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: build (windows-latest)
  • GitHub Check: Codacy Security Scan
  • GitHub Check: Code Coverage
🔇 Additional comments (32)
tests/proptest_serialization.rs (2)

171-204: Explicit neighbor assignment before comparison is correct

Calling assign_neighbors() before counting neighbor relationships ensures the original TDS has fully populated neighbor data, so the neighbor-count comparison with the deserialized TDS is meaningful. The logic for counting and comparing neighbor relationships remains sound.


218-289: Regression test is well-scoped and non-invasive

The ignored 2D regression test cleanly captures a minimal failing case and prints detailed neighbor diagnostics before and after JSON roundtrip. Being #[ignore], it does not affect normal test runs while remaining very useful for targeted debugging.

tests/README.md (2)

231-252: New proptest_duplicates documentation matches implementation

The added section accurately describes the new property tests (duplicates/near-duplicates, Delaunay validation, unsalvageable vertex provenance) and provides clear run commands, consistent with tests/proptest_duplicates.rs.


644-667: regression_delaunay_known_configs section clearly documents canonical cases

The description, coverage bullets, and run instructions for the deterministic Delaunay regression tests match the new test module and fit the existing README structure.

tests/proptest_quality.rs (1)

19-19: UUID-based cell matching avoids vacuous quality invariance checks

Switching translation/scale invariance tests to:

  • Build a uuid_map between original and transformed vertices.
  • Match cells by comparing vertex UUID sets across triangulations.
  • Track matched_any and prop_assume!(matched_any) to discard cases where no UUID-matched cells exist,

prevents vacuous “success” when topology changes preclude per-cell comparison while still exercising the invariants whenever at least one matching cell is present. The use of HashMap and the matching logic look correct and efficient for these test sizes.

Also applies to: 187-378, 381-471

src/core/algorithms/bowyer_watson.rs (2)

28-31: Neighbor invariant documentation updated to current API

Referencing validate_neighbors() instead of an internal helper keeps the invariants table aligned with the public validation surface. No behavioral change here.


191-197: Public statistics() accessor is consistent with robust variant

Exposing &InsertionStatistics mirrors RobustBowyerWatson::statistics() and is useful for diagnostics and for the unified pipeline. The method is const, read-only, and does not affect behavior.

src/core/algorithms/unified_insertion_pipeline.rs (6)

27-52: UnifiedInsertionPipeline struct and constructor are well-factored

The pipeline cleanly encapsulates:

  • Fast and robust algorithms.
  • Per-vertex outcomes (stored as unsalvageable_reports).
  • Delaunay-check policy and counters for attempts, successes, and validation runs.
  • Initial simplex search stats.

with_policy wires everything together with sensible defaults (IncrementalBowyerWatson::new() and RobustBowyerWatson::for_degenerate_cases()), matching the intended “fast → robust → skip” semantics.

Also applies to: 60-79


139-170: DelaunayCheckPolicy::EveryN handling is idiomatic and MSRV-safe

on_successful_insertion increments successful_insertions using saturating arithmetic and, for DelaunayCheckPolicy::EveryN(k), triggers run_global_delaunay_validation_with_policy whenever successful_insertions.is_multiple_of(k.get()). This is a clear and efficient way to schedule global checks, and using usize::is_multiple_of is fine given your MSRV (≥1.87.0). Other policies correctly skip this per-insertion hook.


173-288: Per-vertex fast/robust/skip orchestration is consistent with helper outcome semantics

In the InsertionAlgorithm impl:

  • insert_vertex bypasses the default duplicate-detection wrapper, delegating directly to insert_vertex_impl so that unified_insert_vertex_fast_robust_or_skip can perform its own classification and diagnostics.
  • insert_vertex_impl:
    • Updates fast/robust attempt counters based on the variant of UnifiedPerVertexInsertionOutcome.
    • Calls on_successful_insertion only when info.success is true, so global validation is driven by effective insertions.
    • Pushes Skipped reports into unsalvageable_reports and returns an InsertionInfo with strategy=Skip and success=false, matching test expectations.

The debug assertions around classification and fast_error are helpful sanity checks in debug builds and do not affect release behavior.


290-409: Triangulate implementation follows a clear staged pipeline and handles degeneracy gracefully

triangulate implements:

  1. Input guards for empty/insufficient vertices with a structured InsufficientVertices error.
  2. Stage 1: find_initial_simplex with stats capture.
  3. Stage 2: initial simplex creation via create_initial_simplex and a UUID set to skip re-inserting those vertices.
  4. Stage 3: incremental per-vertex insertion through the unified fast/robust/skip path, mapping recoverable insertion errors to TriangulationConstructionError::FailedToAddVertex.
  5. Stage 4: structural finalization (finalize_after_insertion), robust Delaunay repair, and a policy-controlled final run_global_delaunay_validation_with_policy invocation, tracking how many times global validation runs.

In the no-simplex case, the function populates the TDS with the unique vertex subset, marks construction_state as Incomplete, and returns a GeometricDegeneracy error with detailed counts, leaving the TDS in a valid zero-cell state. This matches the stated guarantees for degenerate inputs.


411-432: Statistics and reset delegation aggregate underlying algorithms correctly

get_statistics simply sums the fast and robust (processed, created, removed) tuples, and reset:

  • Resets both algorithms.
  • Clears all pipeline-level counters and initial simplex stats.
  • Leaves the Delaunay policy unchanged (as documented).

This behavior is appropriate for reusing a pipeline instance across multiple triangulation runs with the same policy.


443-621: 5D stepwise regression test robustly validates unified pipeline behavior

The run_stepwise_unified_insertion_debug helper and the debug_5d_stepwise_insertion_of_seventh_vertex test:

  • Build a known 5D configuration via Tds::new and assert structural validity and global Delaunay before insertion.
  • Run the unified pipeline with EndOnly policy to insert the 7th vertex.
  • Assert that the vertex is skipped (strategy Skip, success=false), that the TDS remains globally Delaunay after the insertion attempt, and that finalize_triangulation preserves both validity and Delaunay.

This is a strong regression guard around the previously failing configuration and the new skip semantics.

tests/tds_basic_integration.rs (1)

44-47: Adding validate_delaunay() checks solidifies basic integration guarantees

The new assertions that simple 3D configurations (one cell, two adjacent cells, and boundary-facet scenarios) are globally Delaunay—on top of existing structural checks—are appropriate and inexpensive for these small test cases. They align these tests with the expanded correctness guarantees elsewhere in the crate.

Also applies to: 71-74, 115-118, 176-179

tests/proptest_duplicates.rs (2)

25-88: Duplicate/near-duplicate cloud generators and uniqueness helper are appropriate

The 2D/3D cloud strategies:

  • Generate random finite points, then explicitly add one exact duplicate and one tiny jittered near-duplicate when possible.
  • Use count_unique_coords_by_bits (bitwise equality) to ensure enough distinct points before proceeding.

This is a solid way to exercise duplicate handling without over-constraining the distributions.


94-191: Properties correctly couple Delaunay validity with unsalvageable-vertex provenance

For both 2D and 3D:

  • Tds::new is required to succeed; otherwise the case is discarded as truly degenerate via prop_assume!(false).
  • The constructed TDS is required to be structurally valid and globally Delaunay via is_valid() and validate_delaunay().
  • A second TDS is built and bowyer_watson_with_diagnostics is run, with the property that every reported unsalvageable vertex’s UUID belongs to the original input set.

This neatly validates both the behavior of the construction pipeline for duplicate-heavy inputs and the integrity of diagnostics.

tests/regression_delaunay_known_configs.rs (2)

17-57: Macro scaffolding generates clear, focused regression tests

regression_delaunay_violation_test! and regression_delaunay_known_configs_all_dims! macros:

  • Standardize the pattern of constructing Tds, panicking on construction failure with dimension-specific context, and asserting validate_delaunay().is_ok().
  • Keep each regression test compact and readable while making it easy to add new canonical configurations.

This macro design is appropriate for the intended regression suite.


68-166: Canonical 2D–5D configurations provide strong deterministic Delaunay regression coverage

The chosen configurations:

  • 2D–4D: simple interior/exterior patterns on top of basic simplices.
  • 5D: a historically failing 7-point configuration, documented as shared with debug and stepwise unified-pipeline tests.

Each is required to construct successfully and remain globally Delaunay, which is exactly what you want to guard against regressions in the new insertion/repair pipeline. The explicit duplication of the 5D coordinates, called out in comments, is reasonable to keep each regression self-contained.

src/geometry/algorithms/convex_hull.rs (3)

1508-1510: Import of TriangulationConstructionError is correct and necessary

Bringing TriangulationConstructionError into scope aligns the tests with Tds::new’s new Result return type; this looks consistent and minimal.


2925-2932: Adjusting “very small” coordinates to avoid construction‑time degeneracy

Bumping the small coordinates to 1e-3 with an explicit comment keeps the test exercising small‑scale geometry without tripping the new robust initial‑simplex degeneracy checks in Tds::new. This is an appropriate way to stabilize the test under stronger validation.


3458-3464: test_degenerate_orientation_fallback setup matches new simplex heuristics

Using 1e-3 coordinates and clarifying that the configuration should still allow the initial simplex search to succeed keeps this test focused on orientation/fallback behavior instead of failing earlier at triangulation construction. The intent and the numbers are well chosen.

tests/test_insertion_algorithm_trait.rs (1)

15-23: Incremental Bowyer–Watson duplicate-insertion test looks correct and well‑scoped

The new imports and test_incremental_bowyer_watson_insert_vertex_is_transactional_on_duplicate exercise the public InsertionAlgorithm::insert_vertex API against a minimal valid 3D TDS, verify that duplicates surface as InsertionError::InvalidVertex, and check that vertex/cell counts and tds.is_valid() are unchanged on failure. The use of a relaxed substring check on the error message keeps the test resilient to minor wording changes while still asserting useful diagnostics. No issues from a correctness or API-usage standpoint.

Also applies to: 348-390

src/core/triangulation_data_structure.rs (10)

383-521: Validation surface (DelaunayViolation, InvariantKind, reports) is coherent and extensible

Adding TriangulationValidationError::DelaunayViolation, InvariantKind, InvariantViolation, TriangulationValidationReport, and ValidationOptions gives you a nice, future‑proof diagnostics surface. The mapping from low‑level errors into invariant‑tagged violations in validation_report is straightforward to consume from tests, and the docs around which invariants is_valid vs. validation_report vs. validate_delaunay check are clear and consistent. No issues from a correctness perspective.


1922-1965: Neighbor cleanup in remove_cells_by_keys closes a real dangling‑pointer hole

The new pass that builds a CellKeySet and clears any neighbor slot pointing at a soon‑to‑be‑removed cell ensures you no longer leave dangling neighbors[i] references after batch removal. This aligns with the documented contract that callers must still rebuild topology for new adjacencies (via assign_neighbors()), while guaranteeing the remaining neighbor arrays never reference non‑existent cells. Implementation looks correct and avoids borrow issues by using as_ref().is_some_and(...).


1967-2051: Vertex removal and incident‑cell reassignment now maintain structural consistency

The new remove_vertex correctly:

  1. Locates the vertex by UUID.
  2. Collects all cells containing its key.
  3. Uses remove_cells_by_keys so neighbor slots pointing to those cells are cleared.
  4. Calls assign_incident_cells() to rebuild vertex→cell incidence.
  5. Finally removes the vertex (and its UUID mapping) via the deprecated remove_vertex_by_uuid.

Combined with the updated assign_incident_cells (which now clears all incident_cell pointers even when cells is empty), this ensures:

  • No incident_cell points at a removed cell.
  • No vertex or cell map points at missing entities after removal.
  • Empty triangulations leave all incident_cell as None.

From a topology standpoint, this matches the docs: you leave a potentially “incomplete” triangulation, but structurally valid. Nice fix.

Also applies to: 2350-2436


3062-3168: Bowyer–Watson wrappers cleanly delegate to the unified insertion pipeline

Refactoring bowyer_watson into the new bowyer_watson_with_diagnostics[_and_policy] entrypoints is well done:

  • bowyer_watson() now just discards diagnostics, preserving the old API.
  • bowyer_watson_with_diagnostics defaults to DelaunayCheckPolicy::default() (i.e., EndOnly), which is appropriate for most callers.
  • bowyer_watson_with_diagnostics_and_policy constructs a fresh UnifiedInsertionPipeline, delegates to InsertionAlgorithm::triangulate, updates construction_state, captures unified statistics into last_triangulation_statistics, and exposes any unsalvageable vertices.

The empty‑vertex fast‑path that returns a default statistics struct and sets last_triangulation_statistics is also a nice touch for observability. I don’t see functional issues here.


5111-5695: New rollback/remove‑vertex/diagnostics tests strongly exercise the invariants

The added tests around:

  • atomic rollback on topology failures (test_add_vertex_atomic_rollback_on_topology_failure, test_rollback_clears_incident_cells_pointing_to_removed_cells),
  • vertex removal across dimensions and on non‑existent vertices (test_remove_vertex_*),
  • incident/neighbor/incident_cell invariants after removals,
  • Bowyer–Watson diagnostics behavior (unique vs. duplicate vertices, coverage invariants),
  • global Delaunay validation policies (EveryN, EndOnly),
  • and last_triangulation_statistics semantics

are thorough and correctly aligned with the new API contracts. Assertions are relaxed where the robust constructors may legitimately discard or skip vertices (using >= instead of exact counts), which avoids brittle tests while still checking the essential properties (validity, minimum simplex size, monotone counts).

These tests give good confidence that the topology and diagnostics changes are doing what they claim.

Also applies to: 5759-5811, 5961-6020, 6682-6716, 6795-6845


7900-8062: Adjusted expectations for robust/complex constructions are sensible

In places where triangulation may legitimately discard vertices or produce fewer cells due to degeneracy or robust handling (e.g., random inputs, complex point sets, dense 2D grids), you now assert lower bounds like “at least 4 vertices / at least 1 cell” and focus on is_valid().is_ok(). That’s the right trade‑off: you’re still checking that a valid D‑simplex exists and the structure is sound, without over‑constraining the exact combinatorics of the mesh.

Also applies to: 8102-8110, 10204-10230, 10452-10491


7395-7479: Facet‑sharing repair and its tests are well‑balanced

The fix_invalid_facet_sharing implementation is intricate but now well covered:

  • It iterates with a hard cap, uses strict facet maps when possible but falls back to lenient ones to salvage partially corrupted triangulations.
  • It removes cells that don’t actually contain the facet vertices and uses radius‑ratio quality (with sane fallbacks) to select among over‑represented cells.
  • It then either rebuilds topology itself or delegates to remove_duplicate_cells() (which already rebuilds topology when needed).

The corresponding tests exercise:

  • triple‑shared facet repair,
  • error mapping from facet operations into TriangulationValidationError,
  • and consistency between “cells removed” and the function’s return value.

Given the complexity, the current behavior and coverage are appropriate; I don’t see any obvious missing edge‑cases in this file.

Also applies to: 7718-7779, 9004-9119


649-657: MSRV is compatible — no compilation issue with const fn and Option::as_ref

Option::as_ref was made const in Rust 1.48.0. The crate declares rust-version = "1.90.0" in Cargo.toml, which is well above 1.48.0. The caveat in the original review is resolved: using Option::as_ref() in const fn is fully supported by the declared MSRV and will compile without issues.


296-356: Diagnostics and statistics structs are well-factored and correctly derive Default

TriangulationStatistics and TriangulationDiagnostics cleanly separate per-run aggregated stats from per-vertex unsalvageable reports, and the is_empty helper on diagnostics matches how the tests use it. InitialSimplexSearchStats derives Default with all primitive field types (usize), so the derived Default on TriangulationStatistics compiles correctly. No correctness issues detected.


4321-4592: Pattern match is exhaustive and correct

All four DelaunayValidationError variants are properly defined and matched:

Enum variants: DelaunayViolation { cell_key }, TriangulationState { source }, InvalidCell { source }, and NumericPredicateError { cell_key, vertex_key, source }. The is_delaunay function returns all four variants, and the pattern match in Tds::validate_delaunay handles each with matching field names. No variants are missing, no dead arms exist.

…re; un-ignore stable invariants

•  Add Cargo feature slow-tests to control execution of long-running tests
•  Gate large random-set and grid-pattern integration tests (2D–4D) with cfg(feature = "slow-tests")
•  Keep 5D heavy tests explicitly ignored (extremely slow; run manually when needed)
•  Gate TDS stress tests (1000–5000 vertices, removal ops) with cfg_attr to skip unless slow-tests is enabled
•  Update justfile:
◦  Add test-slow, test-slow-release, quality-slow recipes
◦  Document new workflows in help
•  Increase global Delaunay repair limits (128 iterations, 32 repairs/iter) to stabilize large cases
•  Un-ignore previously skipped integration tests (now stable) except insertion-order invariance prop test
•  Adjust degenerate 3D test expectations to accept near-duplicate rejection
•  Update docs/fix-delaunay.md to reflect Phases 4 and 6 completion and current status

Files touched: Cargo.toml, justfile, docs/fix-delaunay.md, robust_bowyer_watson.rs, integration_robust_bowyer_watson.rs, test_tds_edge_cases.rs, proptest regressions

CI impact: standard workflows remain fast; comprehensive slow tests available via --features slow-tests or just quality-slow
- Bump MSRV from 1.90.0 to 1.91.0 across all configuration files
- Update dependencies: clap 4.5.52 -> 4.5.53, pastey 0.1.1 -> 0.2.0
- Refactor Vertex::from_points to accept &[Point] slices instead of Vec
- Update Tds::set_neighbors_by_key to accept &[Option<CellKey>] slices
- Make DelaunayCheckPolicy derive Default with EndOnly as default
- Streamline CI workflow: consolidate Ubuntu checks into 'just ci'
- Install CI tools: just, uv, shellcheck, jq, shfmt
- Change 'just ci' recipe to use 'test' instead of 'test-release'
- Archive fix-delaunay.md documentation (move to docs/archive/)
- Update code_organization.md to reflect documentation reorganization
- Add 'taiki' to cspell dictionary
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/core/vertex.rs (1)

387-405: Update doc comments: refer to "slice" not "vector"

All call sites have been verified as correctly updated to pass &[Point<T, D>]—no remaining old-style Vec-consuming calls found. The API change is sound and properly propagated across 119+ test and source locations.

The only remaining issue is doc wording: lines 387–405 describe the parameter as "a vector of points" and points as "a vector of [Point] objects," but the signature now takes a slice. Update these to say "slice of points" and "a slice of [Point] objects" to match the actual API.

src/core/cell.rs (1)

480-503: Cell::new duplicate-vertex guard is correct, but docs are now incomplete

Early duplicate detection via a FastHashSet<VertexKey> is consistent with is_valid() and prevents constructing structurally invalid cells. However, the new doc comment still only mentions InsufficientVertices in the # Errors section; it should also document the DuplicateVertices path for completeness.

-    /// # Errors
-    ///
-    /// Returns `CellValidationError::InsufficientVertices` if `vertices` doesn't
-    /// have exactly D+1 elements.
+    /// # Errors
+    ///
+    /// Returns:
+    /// - `CellValidationError::InsufficientVertices` if `vertices` doesn't have exactly D+1 elements.
+    /// - `CellValidationError::DuplicateVertices` if any vertex key appears more than once.
🧹 Nitpick comments (13)
docs/archive/fix-delaunay.md (1)

217-311: Consolidate redundant phase descriptions in the debugging plan sections.

Lines 217–311 ("Focused Plan to Debug Remaining Delaunay Violations") and lines 348–442 ("Execution Plan Snapshot") describe overlapping debugging phases with inconsistent organization. The earlier section covers phases 1–4, while the snapshot extends to phase 6. This duplication creates ambiguity about which version represents the actual execution plan, especially for readers trying to understand the current debugging strategy.

Since this is an archived document, consider consolidating these sections into a single, unified multi-phase plan (potentially titled "Multi-Phase Debugging Plan & Execution Strategy") that:

  • Merges the phase descriptions, preferring the more recent phase 6 version.
  • Maintains the "Captured seeds (canonical)" section and specific test commands for reproducibility.
  • Clearly marks which phases are complete ([x]) vs. pending.

Also applies to: 348-442

tests/test_convex_hull_error_paths.rs (1)

90-96: Updated Tds::new calls to slice literals are correct and clearer

All these tests now construct the TDS from &[vertex!(...), …] rather than a borrowed Vec, which matches the Tds::new(&[Vertex<_, _, D>]) API and avoids unnecessary heap allocations for small fixed configurations. The surrounding convex‑hull error‑path and validation logic is unchanged and still reads clearly.

If you ever want to reduce duplication, the repeated 3D/4D simplex vertex lists could be factored into small helpers or constants in this module, but that’s purely optional test cleanup.

Also applies to: 140-146, 178-184, 231-237, 269-275, 315-321, 393-400, 434-440, 461-467, 493-499, 521-527, 562-568, 595-601, 633-639, 655-661

.github/workflows/ci.yml (1)

53-57: CI split between Ubuntu (just ci) and lighter macOS/Windows looks intentional—confirm coverage expectations

The workflow now:

  • Installs just on all platforms and, on ubuntu‑latest, also installs uv, shellcheck, jq, and shfmt.
  • Runs the full project CI via just ci only on ubuntu‑latest.
  • Limits macOS/Windows to cargo build --all-targets plus cargo test --lib and cargo test --doc.

This is a reasonable structure if Ubuntu is your primary exhaustive CI target and macOS/Windows are just smoke builds. If you still want full integration/property tests on macOS/Windows, you’d need to expand the non‑ubuntu step (e.g., run your existing just targets there as well).

Also applies to: 58-63, 64-77, 79-82, 83-88

src/core/util.rs (3)

3-22: DelaunayValidationError layering cleanly exposes validation failure modes

Import additions and the new DelaunayValidationError enum give a clear separation between pure Delaunay violations, structural Tds issues, invalid-cell structure, and robust-predicate failures. Error messages are descriptive and should be useful in higher-level diagnostics. If you anticipate asserting on these in tests, you might later consider deriving PartialEq/Eq, but it's not required for current usage.

Also applies to: 56-92


1523-1645: is_delaunay implementation matches the geometric spec and surfaces predicate errors

The full-triangulation is_delaunay check is logically correct: it enforces cell.is_valid(), rebuilds each cell’s point set, and classifies every external vertex via robust_insphere, treating only INSIDE as a violation and surfacing predicate failures as NumericPredicateError. The documented O(#cells × #vertices) cost and “debug/test only” guidance are appropriate.

One small design thought: the TriangulationState variant is currently only reachable via missing vertex keys. If the intent is to mirror Tds::is_valid(), you might optionally call that once up front and early-return on TriangulationValidationError to distinguish “bad triangulation” from “good triangulation but non‑Delaunay”.


1647-1776: find_delaunay_violations mirrors is_delaunay semantics; consider factoring shared core

The multi-violation scanner reuses the same logic as is_delaunay (cell validation, vertex-key resolution, robust insphere classification, and numeric error handling) and correctly supports an optional subset of cells, skipping removed keys.

There is quite a bit of duplicated code between this and is_delaunay (cell/vertex extraction and error mapping). If this starts to evolve further, consider factoring out a small internal helper like validate_cell_delaunay(tds, cell_key, &config) -> Result<Option<CellKey>, DelaunayValidationError> to centralize behavior and reduce maintenance surface. The current form is still perfectly readable.

tests/proptest_duplicates.rs (4)

31-51: Consider increasing the jitter amount for more robust testing.

The near-duplicate jitter of 1e-9 is extremely small and may not adequately test the robustness of the Delaunay insertion algorithms. Robust geometric predicates typically handle perturbations at the scale of 1e-12 to 1e-14, but 1e-9 falls within a range where floating-point arithmetic can introduce inconsistencies without triggering the robust predicate fallbacks.

Consider increasing the jitter to a more meaningful value (e.g., 1e-6 or 1e-7) to better test how the algorithm handles near-degenerate configurations that are more likely to occur in practice.

Apply this diff to adjust the jitter amount:

             // Jittered near-duplicate of the second point.
             let mut coords: [f64; 2] = pts[1].into();
             for c in &mut coords {
-                *c += 1e-9;
+                *c += 1e-7;
             }

55-75: Consider increasing the jitter amount for more robust testing.

Same issue as in the 2D strategy: the 1e-9 jitter may be too small to meaningfully test robustness. Consider using 1e-6 or 1e-7 for more realistic near-duplicate scenarios.

Apply this diff:

             // Jittered near-duplicate of the second point.
             let mut coords: [f64; 3] = pts[1].into();
             for c in &mut coords {
-                *c += 1e-9;
+                *c += 1e-7;
             }

106-141: Inconsistent error handling between first and second Tds construction.

The first Tds::new call uses conditional logic with prop_assume!(false) to skip degenerate inputs (lines 110-116), while the second construction uses .expect() which will panic on failure (line 127). Since both constructions use the same input, the second should never fail if the first succeeds. However, for consistency and defensive programming, consider handling both the same way.

If you want to maintain the panic on the second construction (which is reasonable since it should never fail), consider adding a comment explaining why. Otherwise, apply this diff to make error handling consistent:

         let mut tds_diag: Tds<f64, Option<()>, Option<()>, 2> =
-            Tds::new(&vertices).expect("Second construction should succeed for same input");
+            if let Ok(tds) = Tds::new(&vertices) {
+                tds
+            } else {
+                // Should never fail since first construction succeeded
+                prop_assume!(false);
+                unreachable!();
+            };

155-190: Inconsistent error handling between first and second Tds construction.

Same issue as in the 2D test: the second Tds::new call uses .expect() (line 176) while the first uses prop_assume!(false) for error handling (lines 160-165). Consider making the error handling consistent.

Apply this diff:

         let mut tds_diag: Tds<f64, Option<()>, Option<()>, 3> =
-            Tds::new(&vertices).expect("Second construction should succeed for same input");
+            if let Ok(tds) = Tds::new(&vertices) {
+                tds
+            } else {
+                // Should never fail since first construction succeeded
+                prop_assume!(false);
+                unreachable!();
+            };
tests/integration_robust_bowyer_watson.rs (2)

271-309: Mixed interior/exterior 3D test now correctly targets triangulate

Feeding the base tetrahedron plus mixed interior/exterior vertices through a single triangulate call on Tds::empty() and asserting both structural validity and Delaunay-ness is a clean end-to-end integration test of the robust algorithm’s handling of heterogeneous inputs.

If you still want explicit coverage of the incremental insert_vertex path, consider keeping a smaller separate test that exercises only insert_vertex without going through triangulate.


428-487: Reset-and-reuse test now covers full robust run semantics

Running two separate triangulate calls on fresh Tds::empty() instances around algorithm.reset(), and asserting Delaunay validity both times plus zeroed statistics after reset, provides good coverage that internal stats/state don’t leak across runs.

You might also assert that unsalvageable_vertices().is_empty() after each successful run to confirm degeneracy bookkeeping is reset alongside the counters.

tests/proptest_quality.rs (1)

19-19: UUID-based cell matching + matched_any guard fix invariance checks

The new HashMap-backed UUID mapping and per-cell matching logic correctly:

  • Correlate original vs transformed cells by vertex UUID sets (not key/index order).
  • Compare radius_ratio / normalized_volume only when a matching cell exists.
  • Use matched_any + prop_assume!(matched_any) to avoid vacuous success when topology changes prevent any matches.

The relative-difference checks and tolerances look numerically sane, and the pattern is applied consistently across translation and scale invariance.

If you want to reduce duplication later, you could factor the “match cells by UUIDs and run a metric comparison closure” into a small helper used by all three properties, but it’s fine as-is for tests.

Also applies to: 214-285, 315-377, 404-468

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5782d7a and c6d44fe.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (37)
  • .github/workflows/ci.yml (2 hunks)
  • CONTRIBUTING.md (3 hunks)
  • Cargo.toml (4 hunks)
  • WARP.md (1 hunks)
  • clippy.toml (1 hunks)
  • cspell.json (3 hunks)
  • docs/archive/fix-delaunay.md (1 hunks)
  • docs/code_organization.md (2 hunks)
  • justfile (4 hunks)
  • rust-toolchain.toml (1 hunks)
  • src/core/algorithms/bowyer_watson.rs (7 hunks)
  • src/core/boundary.rs (10 hunks)
  • src/core/cell.rs (14 hunks)
  • src/core/util.rs (5 hunks)
  • src/core/vertex.rs (6 hunks)
  • src/geometry/algorithms/convex_hull.rs (6 hunks)
  • tests/circumsphere_debug_tools.rs (2 hunks)
  • tests/convex_hull_bowyer_watson_integration.rs (1 hunks)
  • tests/integration_robust_bowyer_watson.rs (10 hunks)
  • tests/proptest_bowyer_watson.rs (7 hunks)
  • tests/proptest_cell.rs (5 hunks)
  • tests/proptest_convex_hull.rs (7 hunks)
  • tests/proptest_delaunay_condition.rs (8 hunks)
  • tests/proptest_duplicates.rs (1 hunks)
  • tests/proptest_facet.rs (5 hunks)
  • tests/proptest_facet_cache.rs (9 hunks)
  • tests/proptest_invariants.rs (2 hunks)
  • tests/proptest_quality.rs (8 hunks)
  • tests/proptest_robust_bowyer_watson.rs (10 hunks)
  • tests/proptest_serialization.rs (5 hunks)
  • tests/proptest_triangulation.rs (2 hunks)
  • tests/serialization_vertex_preservation.rs (5 hunks)
  • tests/tds_basic_integration.rs (8 hunks)
  • tests/test_cavity_boundary_error.rs (6 hunks)
  • tests/test_convex_hull_error_paths.rs (15 hunks)
  • tests/test_insertion_algorithm_trait.rs (4 hunks)
  • tests/test_tds_edge_cases.rs (9 hunks)
✅ Files skipped from review due to trivial changes (3)
  • CONTRIBUTING.md
  • clippy.toml
  • tests/convex_hull_bowyer_watson_integration.rs
🚧 Files skipped from review as they are similar to previous changes (8)
  • tests/proptest_robust_bowyer_watson.rs
  • justfile
  • Cargo.toml
  • cspell.json
  • tests/test_insertion_algorithm_trait.rs
  • tests/serialization_vertex_preservation.rs
  • tests/proptest_serialization.rs
  • docs/code_organization.md
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • tests/proptest_duplicates.rs
  • src/core/cell.rs
  • tests/integration_robust_bowyer_watson.rs
  • tests/test_convex_hull_error_paths.rs
  • tests/proptest_bowyer_watson.rs
  • tests/proptest_convex_hull.rs
  • tests/proptest_triangulation.rs
  • docs/archive/fix-delaunay.md
  • src/core/util.rs
  • tests/circumsphere_debug_tools.rs
  • tests/proptest_delaunay_condition.rs
  • tests/proptest_facet_cache.rs
  • tests/proptest_quality.rs
  • tests/proptest_facet.rs
  • src/geometry/algorithms/convex_hull.rs
  • src/core/boundary.rs
  • WARP.md
  • tests/tds_basic_integration.rs
  • tests/proptest_invariants.rs
  • tests/test_tds_edge_cases.rs
  • src/core/algorithms/bowyer_watson.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • tests/proptest_duplicates.rs
  • src/core/cell.rs
  • tests/integration_robust_bowyer_watson.rs
  • tests/test_convex_hull_error_paths.rs
  • tests/test_cavity_boundary_error.rs
  • tests/proptest_bowyer_watson.rs
  • tests/proptest_convex_hull.rs
  • tests/proptest_triangulation.rs
  • docs/archive/fix-delaunay.md
  • src/core/util.rs
  • tests/circumsphere_debug_tools.rs
  • tests/proptest_delaunay_condition.rs
  • tests/proptest_quality.rs
  • tests/proptest_facet.rs
  • src/geometry/algorithms/convex_hull.rs
  • src/core/boundary.rs
  • WARP.md
  • tests/tds_basic_integration.rs
  • tests/proptest_invariants.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.

Applied to files:

  • tests/proptest_duplicates.rs
  • tests/integration_robust_bowyer_watson.rs
  • tests/test_convex_hull_error_paths.rs
  • docs/archive/fix-delaunay.md
  • src/core/util.rs
  • tests/proptest_delaunay_condition.rs
  • tests/proptest_quality.rs
  • tests/proptest_facet.rs
  • src/geometry/algorithms/convex_hull.rs
  • src/core/boundary.rs
  • WARP.md
  • tests/tds_basic_integration.rs
  • src/core/algorithms/bowyer_watson.rs
📚 Learning: 2025-08-20T21:55:04.552Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 50
File: src/core/algorithms/robust_bowyer_watson.rs:191-195
Timestamp: 2025-08-20T21:55:04.552Z
Learning: CoordinateScalar trait guarantees PartialOrd, making direct comparisons between CoordinateScalar values valid without needing conversion to f64.

Applied to files:

  • src/core/cell.rs
📚 Learning: 2025-09-17T17:53:34.532Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 83
File: src/core/traits/facet_cache.rs:588-652
Timestamp: 2025-09-17T17:53:34.532Z
Learning: The build_cache_with_rcu method in FacetCacheProvider correctly returns Option<Arc<FacetToCellsMap>> representing the old cache value before the RCU update operation. The test logic using old_value.is_none() to detect which thread built the cache is correct: None means this thread built it, Some(old_cache) means another thread already built it.

Applied to files:

  • tests/test_convex_hull_error_paths.rs
📚 Learning: 2025-08-30T00:51:50.849Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:481-512
Timestamp: 2025-08-30T00:51:50.849Z
Learning: The DOI 10.5281/zenodo.16931097 in the delaunay project documentation is valid and correctly resolves to acgetchell's Rust delaunay triangulation library on Zenodo.

Applied to files:

  • WARP.md
📚 Learning: 2025-08-28T14:12:46.198Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CITATION.cff:5-7
Timestamp: 2025-08-28T14:12:46.198Z
Learning: The DOI 10.5281/zenodo.16931097 in the CITATION.cff file belongs to the Rust delaunay triangulation library by acgetchell, not to any Julia DelaunayTriangulation.jl package.

Applied to files:

  • WARP.md
📚 Learning: 2025-09-02T02:40:54.439Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 58
File: scripts/tests/test_subprocess_utils.py:92-101
Timestamp: 2025-09-02T02:40:54.439Z
Learning: In Rust projects, cargo is expected to be available in development and CI environments, so conditional skipping of cargo-dependent tests is typically unnecessary.

Applied to files:

  • tests/test_tds_edge_cases.rs
🧬 Code graph analysis (21)
tests/proptest_duplicates.rs (3)
src/core/algorithms/robust_bowyer_watson.rs (2)
  • new (147-158)
  • unsalvageable_vertices (83-85)
src/core/vertex.rs (6)
  • point (475-477)
  • from_points (419-424)
  • is_valid (610-628)
  • map (293-293)
  • map (303-303)
  • uuid (530-532)
src/core/triangulation_data_structure.rs (5)
  • new (2595-2658)
  • vertices (780-782)
  • is_valid (4320-4334)
  • validate_delaunay (4350-4384)
  • bowyer_watson_with_diagnostics (3112-3119)
src/core/cell.rs (1)
src/core/triangulation_data_structure.rs (3)
  • new (2595-2658)
  • default (228-230)
  • vertices (780-782)
src/core/vertex.rs (2)
benches/large_scale_performance.rs (1)
  • points (183-183)
benches/profiling_suite.rs (4)
  • points (266-266)
  • points (313-313)
  • points (362-362)
  • points (410-410)
tests/integration_robust_bowyer_watson.rs (4)
src/core/triangulation_data_structure.rs (7)
  • new (2595-2658)
  • vertices (780-782)
  • empty (2476-2486)
  • is_valid (4320-4334)
  • validate_delaunay (4350-4384)
  • number_of_vertices (1041-1043)
  • number_of_cells (1196-1198)
src/core/algorithms/robust_bowyer_watson.rs (3)
  • new (147-158)
  • triangulate (2353-2463)
  • unsalvageable_vertices (83-85)
src/core/traits/insertion_algorithm.rs (6)
  • new (545-558)
  • new (1383-1391)
  • create_initial_simplex (3281-3336)
  • vertex (999-1001)
  • tds (4633-4645)
  • triangulate (3161-3260)
src/core/util.rs (1)
  • debug_print_first_delaunay_violation (1792-1952)
tests/test_convex_hull_error_paths.rs (1)
src/core/triangulation_data_structure.rs (1)
  • new (2595-2658)
tests/test_cavity_boundary_error.rs (1)
src/core/vertex.rs (1)
  • from_points (419-424)
tests/proptest_bowyer_watson.rs (1)
src/core/vertex.rs (2)
  • from_points (419-424)
  • point (475-477)
tests/proptest_cell.rs (1)
src/core/vertex.rs (1)
  • from_points (419-424)
tests/proptest_convex_hull.rs (1)
src/core/vertex.rs (1)
  • from_points (419-424)
tests/proptest_triangulation.rs (1)
src/core/vertex.rs (1)
  • from_points (419-424)
src/core/util.rs (4)
src/core/cell.rs (6)
  • uuid (796-798)
  • cell (4342-4344)
  • vertices (684-686)
  • new (480-511)
  • number_of_vertices (769-771)
  • neighbors (651-655)
src/core/vertex.rs (3)
  • uuid (530-532)
  • point (475-477)
  • None (2084-2084)
src/geometry/robust_predicates.rs (2)
  • robust_insphere (149-192)
  • num_traits (1421-1421)
src/core/triangulation_data_structure.rs (6)
  • vertices (780-782)
  • new (2595-2658)
  • number_of_vertices (1041-1043)
  • number_of_cells (1196-1198)
  • is_empty (353-355)
  • is_empty (510-512)
tests/proptest_delaunay_condition.rs (5)
src/core/triangulation_data_structure.rs (4)
  • vertices (780-782)
  • new (2595-2658)
  • cells (703-705)
  • cells (4736-4744)
src/core/vertex.rs (1)
  • from_points (419-424)
src/geometry/robust_predicates.rs (1)
  • robust_insphere (149-192)
src/geometry/predicates.rs (1)
  • insphere (349-424)
tests/integration_robust_bowyer_watson.rs (1)
  • create_initial_simplex (227-242)
tests/proptest_facet_cache.rs (1)
src/core/vertex.rs (1)
  • from_points (419-424)
tests/proptest_quality.rs (3)
src/core/vertex.rs (4)
  • from_points (419-424)
  • map (293-293)
  • map (303-303)
  • uuid (530-532)
src/core/cell.rs (5)
  • new (480-511)
  • vertices (684-686)
  • map (415-415)
  • uuid (796-798)
  • vertex_uuids (870-879)
src/core/triangulation_data_structure.rs (4)
  • new (2595-2658)
  • vertices (780-782)
  • cell_keys (798-800)
  • get_cell (808-810)
tests/proptest_facet.rs (1)
src/core/vertex.rs (1)
  • from_points (419-424)
src/geometry/algorithms/convex_hull.rs (2)
src/core/triangulation_data_structure.rs (2)
  • new (2595-2658)
  • vertices (780-782)
src/geometry/point.rs (2)
  • new (115-117)
  • validate (132-144)
src/core/boundary.rs (2)
src/core/triangulation_data_structure.rs (2)
  • vertices (780-782)
  • new (2595-2658)
src/core/vertex.rs (1)
  • from_points (419-424)
tests/tds_basic_integration.rs (2)
src/core/triangulation_data_structure.rs (1)
  • validate_delaunay (4350-4384)
src/core/vertex.rs (1)
  • from_points (419-424)
tests/proptest_invariants.rs (2)
src/core/vertex.rs (2)
  • from_points (419-424)
  • point (475-477)
src/core/triangulation_data_structure.rs (3)
  • new (2595-2658)
  • vertices (780-782)
  • add (2800-2949)
tests/test_tds_edge_cases.rs (1)
src/core/vertex.rs (1)
  • from_points (419-424)
src/core/algorithms/bowyer_watson.rs (3)
src/core/algorithms/robust_bowyer_watson.rs (2)
  • statistics (91-93)
  • insert_vertex_impl (1997-2184)
src/core/traits/insertion_algorithm.rs (8)
  • insert_vertex_impl (1753-1757)
  • insert_vertex_impl (6916-6925)
  • insert_vertex_impl (7739-7751)
  • insert_vertex_impl (7766-7772)
  • insert_vertex_impl (7785-7797)
  • insert_vertex_impl (7812-7818)
  • tds (4633-4645)
  • vertex (999-1001)
src/core/vertex.rs (1)
  • from_points (419-424)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Code Coverage
  • GitHub Check: performance-regression
  • GitHub Check: Codacy Security Scan
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (windows-latest)
  • GitHub Check: audit
  • GitHub Check: audit
🔇 Additional comments (48)
rust-toolchain.toml (1)

3-3: MSRV consistency verified.

Both rust-toolchain.toml and Cargo.toml correctly declare version 1.91.0. Rust 1.91.0 was released on October 30, 2025, confirming this is a valid stable release. The inline comment is accurate.

tests/circumsphere_debug_tools.rs (1)

1133-1133: LGTM! Good refactor to use fixed-size arrays.

Changing from Vec to array literals for these fixed-size vertex collections is more idiomatic and efficient. Arrays avoid heap allocation and provide compile-time size checking, while .iter() usage remains identical.

Also applies to: 1172-1172

src/core/boundary.rs (2)

257-257: LGTM: Consistent API updates for Vertex::from_points.

All test call sites correctly updated to pass borrowed slices (&points) instead of owned vectors, aligning with the signature change to pub fn from_points(points: &[Point<T, D>]). This is more idiomatic Rust and avoids unnecessary moves.

Also applies to: 290-290, 328-328, 397-397, 428-428, 475-475, 577-577, 695-695, 750-750, 790-790


431-435: LGTM: Assertion relaxed with clear explanation.

The assertion change from requiring at least 2 cells to at least 1 cell is well-documented and aligns with the robust cleanup and facet-sharing filtering improvements mentioned in the PR objectives. The added comment clearly explains the expected post-cleanup behavior.

docs/archive/fix-delaunay.md (1)

276-278: Delaunay violation definition is correct.

The document correctly specifies that a Delaunay triangulation contains no point in the circumsphere interior, treating on-sphere as valid. This aligns with the standard definition and the implementation approach described in the debugging plan.

WARP.md (1)

241-248: MSRV/edition doc looks good; keep it aligned with rust-toolchain

The updated MSRV/edition bullet is consistent with the Rust-centric context here. Please just double‑check that rust-toolchain.toml and any MSRV in Cargo.toml match 1.91.0 so WARP.md doesn’t drift again from the authoritative config. (Based on learnings)

tests/test_cavity_boundary_error.rs (1)

24-28: Slice-based Vertex::from_points usage is correct across these tests

All updated call sites now pass &points_vec into Vertex::from_points, matching the new &[Point<_, _>] signature and keeping ownership with the local Vecs. This is semantically equivalent to the old behavior and avoids needless moves; the surrounding test logic remains unchanged.

Also applies to: 39-43, 60-64, 80-98, 126-141, 185-221

src/core/vertex.rs (1)

1268-1288: Switch to a fixed-size array for vertices is fine here

Using a fixed-size array literal for vertices in vertex_reference_equality is allocation‑free and keeps the test logic identical (iteration over four vertices and equality checks). No behavioral change or edge case introduced.

src/core/cell.rs (4)

200-212: cell! macro slice coercion is sound and broadens inputs

Borrowing $vertices into vertices_slice and relying on slice coercion into Tds::new cleanly supports Vec, arrays, and existing slices without additional allocations. The macro semantics remain the same while becoming more flexible.

Also applies to: 215-227


1892-1900: Interior vertex for eq_by_vertices_same_tds avoids degeneracy issues

Adding the offset interior vertex at [0.2, 0.2, 0.2] to generate multiple non-degenerate cells within the same TDS is a good way to exercise inequality and self-equality without stressing degenerate circumcenter cases.


2731-2738: Updated test simplices use robust non-degenerate configurations

The various test geometry tweaks (simplex vertices for debug-format, serialization, negative/large/small coordinates, and mixed-sign 4D cases) all move to clearly non-degenerate simplices, which is important now that construction and robust predicates are stricter around degeneracy. The intent of the tests is preserved while making them more stable.

Also applies to: 2762-2769, 2873-2879, 2905-2910, 2951-2955, 2967-2971, 2982-2991, 3025-3033


3254-3255: Switching Tds::new calls to array/slice form matches the API change

Using Tds::new(&[vertex1, vertex2, vertex3, vertex4]) instead of a Vec literal aligns tests with the slice-based constructor and avoids an extra allocation. No behavioral change, just cleaner test setup.

Also applies to: 3549-3550

src/core/util.rs (2)

1778-1952: debug_print_first_delaunay_violation provides rich diagnostics and is safely scoped

The debug helper is nicely constrained to #[cfg(any(test, debug_assertions))] and gives comprehensive context (vertex dump, violating cells, at least one offending external vertex when possible, and neighbor info). It reuses the same robust predicates and SmallVec buffers as the validators, and it degrades gracefully on errors by logging and returning without panicking. No issues spotted.


2440-2467: Delaunay validator smoke test is a good minimal sanity check

The new test against a simple 3D tetrahedron exercises is_delaunay, find_delaunay_violations, and the debug helper together, which is a good integration-level sanity check for the new validation surface. Behavior and expectations look correct.

tests/proptest_facet_cache.rs (1)

48-48: LGTM! Consistent API adaptation across facet cache tests.

All invocations of Vertex::from_points have been correctly updated to pass slice references (&v) instead of owned vectors, aligning with the updated API signature that now accepts &[Point<T, D>].

Also applies to: 84-84, 111-111, 154-154, 181-181, 236-236, 276-276, 308-308, 344-344

tests/proptest_cell.rs (1)

43-43: LGTM! Cell property tests updated correctly.

All test macros now correctly pass slice references to Vertex::from_points, consistent with the API change.

Also applies to: 60-60, 75-75, 92-92, 108-108

tests/proptest_facet.rs (1)

44-44: LGTM! Facet property tests adapted correctly.

Vertex construction now consistently uses borrowed slices across all facet property tests.

Also applies to: 73-73, 104-104, 128-128, 170-170

tests/tds_basic_integration.rs (1)

33-33: LGTM! Integration tests updated with enhanced validation.

The API changes are correct, and the addition of validate_delaunay() checks strengthens test coverage by explicitly verifying the global Delaunay property, aligning with the PR's enhanced validation framework.

Also applies to: 60-60, 90-90, 137-137

tests/proptest_convex_hull.rs (1)

46-46: LGTM! Convex hull tests correctly adapted.

All vertex construction calls properly use slice references, including the idiomatic &[new_point] pattern for single-point construction.

Also applies to: 87-87, 133-133, 150-150, 189-189, 239-239, 261-261

tests/proptest_bowyer_watson.rs (1)

42-42: LGTM! Bowyer-Watson tests comprehensively updated.

All vertex construction patterns—both for initial triangulations and single-point insertions—correctly use slice references throughout the property tests.

Also applies to: 50-50, 79-79, 91-91, 124-124, 130-130, 151-151, 197-197, 206-206

tests/proptest_invariants.rs (2)

26-26: LGTM! API adaptations applied correctly.

Vertex construction now uses slice references in both duplicate coordinate and cell vertex count tests.

Also applies to: 60-60


29-42: Excellent correctness improvement for duplicate coordinate testing.

The test now correctly selects a vertex from the triangulation itself rather than the first input vertex. This handles cases where Tds::new may skip input vertices due to degeneracy, ensuring the duplicate coordinate rejection test operates on vertices actually present in the triangulation.

tests/proptest_triangulation.rs (1)

50-50: LGTM! Triangulation property tests fully migrated.

All dimension-specific vertex set generators and the incremental insertion test now correctly use slice references, completing the API migration for this test suite.

Also applies to: 55-55, 60-60, 65-65, 247-247

src/core/algorithms/bowyer_watson.rs (4)

28-28: LGTM!

Documentation update correctly reflects the validation method name change from validate_neighbors_internal() to validate_neighbors().


191-197: LGTM!

The new public statistics() accessor appropriately exposes internal statistics for diagnostics and benchmarking. The method signature is consistent with RobustBowyerWatson::statistics() and follows Rust getter conventions.


268-274: LGTM!

The method rename from insert_vertex to insert_vertex_impl correctly implements the InsertionAlgorithm trait's expected implementation method. The trait provides a default insert_vertex wrapper that handles duplicate detection before calling insert_vertex_impl.

The comment on line 274 is helpful for understanding the separation of concerns.


601-601: LGTM!

Test calls correctly updated to match the new Vertex::from_points(&[Point<T, D>]) API that now accepts a slice reference instead of taking ownership.

Also applies to: 772-772, 818-818, 846-846

src/geometry/algorithms/convex_hull.rs (6)

1508-1510: Importing TriangulationConstructionError into tests is appropriate

Bringing TriangulationConstructionError into scope here is necessary for the new Tds::new result-handling below; looks correct and localized to the test module.


2566-2593: Good handling of extreme-coordinate degeneracy in Tds::new

Wrapping Tds::new in a Result and explicitly accepting the GeometricDegeneracy case (while panicking on other errors) is a sensible way to keep this “extreme coordinates” test meaningful without over-constraining the robust initial simplex search.


2925-2932: Relaxing “very small” coordinates to 1e-3 is reasonable

Bumping the “very small” test coordinates to 1e-3 while documenting the intent avoids triggering numeric-degeneracy rejection in the new construction path, but still exercises small-scale geometry; this is a good compromise for test stability.


3458-3464: Degenerate-orientation test now aligned with robust initial simplex behavior

Adjusting the near-degenerate vertex coordinates to 1e-3 scale while keeping the comment about allowing the initial simplex search to succeed makes this test consistent with the new degeneracy thresholds, without weakening coverage.


3507-3552: Explicitly matching on Tds::new for extreme-precision input is robust

The new tds_extreme_result match cleanly separates the “works” path from the GeometricDegeneracy path and treats any other error as unexpected, which is exactly what you want for a regression-style extreme-precision test.


6265-6271: Tds::new(...).unwrap() update is correct for the stale-hull regression

Updating this regression test to call Tds::new(...).unwrap() keeps its intent intact under the new constructor signature and ensures it still verifies hull staleness against a successfully constructed, non-degenerate 3D simplex.

tests/integration_robust_bowyer_watson.rs (7)

14-15: Verify rand version for StdRng::seed_from_u64 + rng.random_range

The seeded StdRng pattern is fine, but rng.random_range(..) is only available on newer rand releases (the 0.9+ API); older 0.8 uses gen_range instead.(rust-random.github.io)
Please confirm your Cargo.toml uses a compatible rand version; otherwise switch the calls to rng.gen_range(..) to avoid compilation issues.

Also applies to: 35-46, 151-160, 186-188


35-82: Switch to Tds::empty + triangulate with global validation looks solid

Building a full vertex set (initial simplex + random points), then calling RobustBowyerWatson::triangulate on Tds::empty() and asserting both is_valid() and validate_delaunay() gives a much stronger end-to-end guarantee than the old per-insert flow; this aligns well with the new robust pipeline and Delaunay-repair hook.


133-137: Delaunay assertion after exterior insertion sequence is a good strengthening

Adding a final tds.validate_delaunay().is_ok() check after the mixed interior/exterior incremental insertions upgrades the test from “no topological corruption” to “still globally Delaunay”, which is exactly what the robust algorithm is supposed to guarantee here.


151-211: Clustered + scattered tests now exercise triangulate end-to-end

Reworking the clustered/scattered tests to:

  • build explicit clustered + scattered vertex sets,
  • triangulate from Tds::empty() twice (clustered-only, then all points), and
  • assert is_valid() and validate_delaunay() after each phase

gives clear coverage of both dense local structure and wide spread point sets under the robust path.


249-262: Slow-test gating for high-cost dimensions is appropriate

Gating the 2D–4D large-random tests behind feature = "slow-tests" and marking 5D as #[ignore] by default is a pragmatic way to keep CI times reasonable while still allowing deep stress tests to be run manually.


317-353: Grid-pattern 2D test refactor to triangulate is consistent and well-gated

Switching the grid-pattern test to use triangulate on a pre-built vertex set, then checking is_valid() and validate_delaunay(), matches the new integration style; the cfg_attr ignore under non-slow-tests keeps this heavy case opt-in.


366-421: Near-degenerate 3D configuration test matches new “unsalvageable vertices” semantics

Allowing triangulate either to:

  • succeed (with a valid globally Delaunay TDS and some vertices reported via unsalvageable_vertices()), or
  • fail with a clear duplicate/near-duplicate error message

correctly encodes the expected behavior of the robust degeneracy handling; this is a nice, precise regression test for that path.

tests/test_tds_edge_cases.rs (3)

220-220: Update to set_neighbors_by_key slice API looks correct

Passing &new_neighbors matches a slice-based signature and keeps the test logic (“all None clears neighbors”) intact.


347-350: Slow stress tests are cleanly gated behind the slow-tests feature

Using #[cfg_attr(not(feature = "slow-tests"), ignore = "...")] on the large stress tests lets them compile but keeps default CI runs fast while still allowing opt-in runs.

Also applies to: 378-381, 410-413, 442-445


363-363: Borrowing into Vertex::from_points matches the updated signature

Switching to Vertex::<f64, (), _>::from_points(&points) (and the 3D variants) aligns with the &[Point<_, _>] API and preserves behavior.

Also applies to: 395-395, 427-427, 459-459

tests/proptest_quality.rs (1)

113-113: Consistent switch to borrowed Vertex::from_points in strategies

All .prop_map(|v| Vertex::from_points(&v)) updates align the proptest inputs with the borrowed-slice from_points API without changing semantics.

Also applies to: 193-193, 295-295, 387-387, 479-479

tests/proptest_delaunay_condition.rs (4)

49-81: Initial-simplex coordinate filter avoids duplicate-vertex degeneracies

filter_initial_simplex_coords correctly drops any vertex whose coordinates bitwise-match an initial-simplex vertex, preventing “same coords, different UUID” insertions that can violate Delaunay assumptions in the insertion-order tests.


109-122: create_initial_simplex now consistently uses Vertex::from_points

Rebuilding the origin and axis-aligned vertices via Vertex::from_points(&[Point]) keeps UUID generation consistent with the rest of the tests while preserving the intended simplex geometry.


137-137: Strengthened empty-circumsphere property with robust + standard predicates

The updated test_empty_circumsphere! property:

  • Deduplicates vertices by coordinates and rejects highly axis-aligned configurations to cut down on degenerate inputs.
  • Builds the triangulation once via Tds::new(&vertices) and assumes away construction failures instead of asserting on them.
  • For each interior facet, checks the Delaunay condition using both robust_insphere and the standard insphere, only treating a violation as proven when both classify the opposite vertex as INSIDE.

This is a solid, conservative formulation that avoids false positives from borderline numerical cases while still catching clear violations.

Also applies to: 142-157, 159-163, 169-207


273-289: 2D insertion-order property: improved general-position filtering and edge overlap metric

In prop_insertion_order_invariance_2d:

  • The new axis-aligned point count checks (with a small tolerance) help reject obviously collinear/degenerate point sets.
  • Switching to Vertex::from_points(&points) is consistent with the rest of the code.
  • Filtering out vertices that match initial simplex coordinates prevents duplicate-coordinate insertions against the seed simplex; prop_assume!(!vertices.is_empty()) keeps the property meaningful.
  • Raising the Jaccard edge-overlap threshold to 0.75 with documented rationale fits the stronger filtering and still leaves room for floating-point variance.

All of this should make the (currently ignored) invariance test more stable and informative when re-enabled.

Also applies to: 314-314, 319-323, 391-394

This commit addresses multiple coderabbit review comments by refactoring
duplicated code, improving documentation clarity, enhancing CI workflows,
and strengthening test coverage.

## CI/CD Improvements
- Unified CI workflow to run `just ci` on all platforms (Linux, macOS, Windows)
- Added Node.js installation for markdown linting across all platforms
- Made shellcheck optional in justfile for Windows compatibility
- Added platform-specific tool installation steps (apt, brew, choco)

## Code Quality Tools
- Fixed `just clippy` to catch unused code in all feature configurations
- Now runs clippy twice: without features and with `--all-features`
- Made `shell-lint` gracefully skip shellcheck if unavailable

## Documentation
- Consolidated redundant sections in `docs/archive/fix-delaunay.md`
- Fixed markdown linting errors (heading hierarchy)
- Clarified coordinate comparison semantics in `src/core/util.rs`
  - Replaced "bit-level comparison" with "`OrderedFloat`-based comparison"
  - Clarified epsilon parameter semantics
- Updated `src/core/vertex.rs` to refer to "slice" instead of "vector"
- Documented `DuplicateVertices` error case in `Cell::new`

## Test Refactoring
- **proptest_quality.rs**: Extracted `compare_transformed_cells` helper
  - Eliminates ~200 lines of duplicated UUID-based cell matching logic
  - Refactored three invariance tests to use shared helper
  - Improves maintainability and enables future extensions
- **test_convex_hull_error_paths.rs**: Added `simplex_3d()` and `simplex_4d()` helpers
  - Reduces ~14 instances of repeated vertex array definitions
- **integration_robust_bowyer_watson.rs**: Enhanced algorithm reset test
  - Added verification of `unsalvageable_vertices()` reset behavior
  - Confirms `reset()` clears all internal state including statistics
- **proptest_duplicates.rs**: Improved duplicate handling
  - Increased jitter from 1e-9 to 1e-7 for realistic near-degenerate testing
  - Consistent error handling with `prop_assume!(false)` pattern

## Validation Logic
- **src/core/util.rs**: Refactored Delaunay validation
  - Added early `Tds::is_valid()` check in `is_delaunay()`
  - Extracted `validate_cell_delaunay` helper function
  - Reduced ~100+ lines of duplication between `is_delaunay` and `find_delaunay_violations`
  - Improved error layering to distinguish TDS corruption from Delaunay violations

## Minor Fixes
- Added "choco" to cspell dictionary
- Fixed conditional compilation for unused imports in slow-tests feature
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/core/util.rs (1)

166-384: Vertex dedup / filtering helpers: semantics now match implementation; consider complexity note and tests

The three helpers (dedup_vertices_exact, dedup_vertices_epsilon, filter_vertices_excluding) and their shared helpers (coords_equal_exact, coords_within_epsilon) now have docs that accurately reflect behavior (OrderedEq semantics, NaN/+0/‑0 handling, and strict < epsilon distance), and the implementations look correct for small to moderate inputs.

Two minor follow‑ups to consider:

  • All three are inherently O(n²) in the number of vertices; since these are general‑purpose utilities, a short note in each doc comment about the quadratic cost and intended usage on modest‑sized clouds would help set expectations and avoid accidental use on very large inputs.
  • Given their role in stabilizing insertion and dedup paths, adding focused unit tests in this module (including cases with NaNs, +0.0 vs -0.0, and borderline epsilon distances) would tighten confidence beyond the doctest‑style examples.

Functionally they’re fine as written; these are documentation / test‑coverage polish items.

🧹 Nitpick comments (2)
tests/proptest_quality.rs (1)

31-112: UUID-based compare_transformed_cells helper looks correct and removes duplication

Centralizing the “match cells by vertex UUID set, then compare a metric” pattern into compare_transformed_cells substantially simplifies the tests and matches how Tds exposes cell/vertex UUIDs. The matching logic (mapping original vertex UUIDs through uuid_map, then checking equal length and set inclusion against trans_cell_uuids) is sound for D-simplices, and matched_any cleanly captures whether at least one comparison actually ran. The unused _metric_name / _dimension parameters are harmless but could be either dropped or wired into error messages later; no change is strictly needed now.

src/core/util.rs (1)

56-92: Delaunay validation error surface and helpers are well-structured

The DelaunayValidationError variants and the is_delaunay / find_delaunay_violations / debug_print_first_delaunay_violation helpers form a coherent diagnostic layer:

  • is_delaunay correctly runs tds.is_valid() first, then uses robust_insphere with a shared SmallVec buffer and a robust config preset, short‑circuiting on the first violating cell.
  • find_delaunay_violations sensibly reuses validate_cell_delaunay and supports optional subsets via cells_to_check, returning all violators instead of stopping early.
  • validate_cell_delaunay’s flow (cell presence check, cell.is_valid(), vertex lookup with a structured TriangulationValidationError on missing vertices, then robust in-sphere classification) matches the documented error cases.
  • The debug helper is appropriately cfg‑gated, uses the same predicates, and prints detailed per‑cell/vertex diagnostics without affecting core behavior, and the new sanity test on a simple tetrahedron exercises the primary OK-paths.

The only minor consideration is that find_delaunay_violations skips missing CellKeys (returns Ok and just omits them) whereas its docs generically say “Returns an error if TDS corruption is detected”; if you ever pass user-provided subsets, you might either (a) treat missing cells as TriangulationState errors for symmetry with vertex lookups, or (b) clarify in the docs that nonexistent keys in cells_to_check are silently skipped. Otherwise this looks solid.

Also applies to: 1521-1927, 2414-2442

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c6d44fe and 530b4c3.

📒 Files selected for processing (11)
  • .github/workflows/ci.yml (2 hunks)
  • cspell.json (4 hunks)
  • docs/archive/fix-delaunay.md (1 hunks)
  • justfile (6 hunks)
  • src/core/cell.rs (15 hunks)
  • src/core/util.rs (5 hunks)
  • src/core/vertex.rs (7 hunks)
  • tests/integration_robust_bowyer_watson.rs (11 hunks)
  • tests/proptest_duplicates.rs (1 hunks)
  • tests/proptest_quality.rs (11 hunks)
  • tests/test_convex_hull_error_paths.rs (17 hunks)
✅ Files skipped from review due to trivial changes (1)
  • docs/archive/fix-delaunay.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_convex_hull_error_paths.rs
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.
📚 Learning: 2025-09-02T20:32:05.985Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 60
File: cspell.json:103-103
Timestamp: 2025-09-02T20:32:05.985Z
Learning: In cspell.json for the delaunay project, the word "itional" is intentionally added to the dictionary because it comes from a regex pattern, not a typo.

Applied to files:

  • cspell.json
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • tests/proptest_quality.rs
  • tests/proptest_duplicates.rs
  • src/core/cell.rs
  • src/core/util.rs
  • justfile
  • tests/integration_robust_bowyer_watson.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • tests/proptest_quality.rs
  • tests/proptest_duplicates.rs
  • src/core/cell.rs
  • src/core/util.rs
  • tests/integration_robust_bowyer_watson.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.

Applied to files:

  • tests/proptest_duplicates.rs
  • src/core/util.rs
  • tests/integration_robust_bowyer_watson.rs
📚 Learning: 2025-08-20T21:55:04.552Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 50
File: src/core/algorithms/robust_bowyer_watson.rs:191-195
Timestamp: 2025-08-20T21:55:04.552Z
Learning: CoordinateScalar trait guarantees PartialOrd, making direct comparisons between CoordinateScalar values valid without needing conversion to f64.

Applied to files:

  • src/core/cell.rs
📚 Learning: 2025-09-02T02:40:54.439Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 58
File: scripts/tests/test_subprocess_utils.py:92-101
Timestamp: 2025-09-02T02:40:54.439Z
Learning: In Rust projects, cargo is expected to be available in development and CI environments, so conditional skipping of cargo-dependent tests is typically unnecessary.

Applied to files:

  • justfile
🧬 Code graph analysis (5)
tests/proptest_quality.rs (4)
src/core/vertex.rs (5)
  • point (475-477)
  • uuid (530-532)
  • from_points (419-424)
  • map (293-293)
  • map (303-303)
src/geometry/quality.rs (2)
  • normalized_volume (313-378)
  • radius_ratio (218-265)
src/core/cell.rs (4)
  • uuid (797-799)
  • new (481-512)
  • vertices (685-687)
  • map (415-415)
src/core/triangulation_data_structure.rs (2)
  • new (2595-2658)
  • vertices (780-782)
tests/proptest_duplicates.rs (2)
src/core/vertex.rs (6)
  • point (475-477)
  • from_points (419-424)
  • is_valid (610-628)
  • map (293-293)
  • map (303-303)
  • uuid (530-532)
src/core/triangulation_data_structure.rs (5)
  • new (2595-2658)
  • vertices (780-782)
  • is_valid (4320-4334)
  • validate_delaunay (4350-4384)
  • bowyer_watson_with_diagnostics (3112-3119)
src/core/cell.rs (1)
src/core/triangulation_data_structure.rs (3)
  • new (2595-2658)
  • default (228-230)
  • vertices (780-782)
src/core/util.rs (3)
src/core/cell.rs (5)
  • uuid (797-799)
  • cell (4343-4345)
  • vertices (685-687)
  • new (481-512)
  • neighbors (652-656)
src/geometry/robust_predicates.rs (3)
  • robust_insphere (149-192)
  • num_traits (1421-1421)
  • general_triangulation (736-746)
src/core/triangulation_data_structure.rs (4)
  • vertices (780-782)
  • new (2595-2658)
  • is_empty (353-355)
  • is_empty (510-512)
tests/integration_robust_bowyer_watson.rs (4)
src/core/triangulation_data_structure.rs (9)
  • new (2595-2658)
  • vertices (780-782)
  • empty (2476-2486)
  • is_valid (4320-4334)
  • validate_delaunay (4350-4384)
  • number_of_vertices (1041-1043)
  • number_of_cells (1196-1198)
  • is_empty (353-355)
  • is_empty (510-512)
src/core/algorithms/robust_bowyer_watson.rs (3)
  • new (147-158)
  • triangulate (2353-2463)
  • unsalvageable_vertices (83-85)
src/core/traits/insertion_algorithm.rs (6)
  • new (545-558)
  • new (1383-1391)
  • create_initial_simplex (3281-3336)
  • vertex (999-1001)
  • tds (4633-4645)
  • triangulate (3161-3260)
src/core/util.rs (1)
  • debug_print_first_delaunay_violation (1767-1927)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Codacy Security Scan
  • GitHub Check: Code Coverage
  • GitHub Check: performance-regression
  • GitHub Check: build (windows-latest)
  • GitHub Check: build (macos-latest)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (19)
cspell.json (1)

157-157: Dictionary additions are correct and properly ordered.

The four new words—"choco", "collinearities", "taiki", and "unsalvageable"—are alphabetically ordered within their respective sections and are legitimate additions given the PR scope. The word "unsalvageable" aligns with the new vertex-tracking feature mentioned in the PR objectives.

Also applies to: 170-170, 346-346, 361-361

tests/proptest_quality.rs (2)

12-20: Imports correctly reflect new UUID-based matching helper needs

The added imports for CellKey, Tds, HashMap, and Uuid are all used by the new helper and tests, and there’s no apparent redundancy. Looks good as-is.


191-567: Macro-generated quality properties: UUID-based matching and Vertex::from_points(&v) changes are consistent

  • Switching all prop_map calls to Vertex::from_points(&v) matches the &[Point<_, D>] signature and avoids consuming the generated vectors; this is the correct adjustment and keeps strategies reusable across tests.
  • For the translation- and scale-invariance properties, rebuilding vertices from transformed points, constructing fresh Tds instances, and then matching cells via a uuid_map (built with vertices.iter().zip(transformed.iter())) is a robust way to tolerate reordering of cells while still comparing like-for-like geometrically. This aligns well with the PR goal to compare cells by vertex UUID instead of key order.
  • Using compare_transformed_cells plus prop_assume!(matched_any) ensures that cases where no UUID-matched cell exists are treated as rejected inputs rather than silent successes; that addresses the earlier “vacuous success” risk in these invariance tests. The chosen relative tolerances (5% for radius ratio, 1% for normalized volume) are reasonable for floating-point Bowyer–Watson outputs in 2D–5D.
  • The separate degeneracy-focused properties (prop_radius_ratio_positive, prop_degeneracy_consistency, and collapse tests) mean it’s acceptable that the invariance checks themselves quietly skip cells where quality evaluation fails, since degeneracy behavior is covered elsewhere.

Overall, the updated tests match the new TDS/vertex pipeline and the intended UUID-based invariance semantics.

.github/workflows/ci.yml (1)

53-100: LGTM! Well-structured cross-platform CI workflow.

The unified CI approach with platform-specific tool installation is well-organized. The explicit documentation of Windows limitations (shellcheck handling) and the consolidated just ci workflow improve maintainability.

justfile (4)

99-101: Verify the CI recipe change from release to debug tests.

The CI recipe now runs test (debug mode) instead of test-release. This speeds up CI but may miss release-specific optimizations and behaviors. Ensure this aligns with your CI strategy and that release testing occurs elsewhere (e.g., pre-release checks).


365-383: LGTM! Graceful shellcheck handling improves cross-platform support.

The conditional shellcheck execution with a clear warning message when unavailable aligns well with the Windows tooling limitations documented in the CI workflow.


429-436: LGTM! Well-designed slow-tests feature gating.

The new test-slow recipes properly gate slow-running tests behind the slow-tests feature, maintaining fast CI while allowing comprehensive local testing. This aligns well with the test file updates in the PR.


111-112: The two clippy invocations are not duplicates and serve different purposes.

Line 111 checks with default features, while line 112 (with --all-features) verifies the code against all feature combinations. This is an intentional two-pass approach to catch feature-gated code issues.

Likely an incorrect or invalid review comment.

src/core/vertex.rs (1)

387-424: LGTM! Well-executed API improvement to slice-based input.

The signature change from Vec<Point<T, D>> to &[Point<T, D>] improves the API by:

  • Avoiding unnecessary allocations when callers already have slices
  • Maintaining flexibility (callers can pass &vec, &array, or slice)
  • Consistent with modern Rust slice-based APIs

The documentation and implementation are correctly updated, and test usages throughout the codebase have been updated accordingly.

tests/proptest_duplicates.rs (2)

1-94: LGTM! Well-designed property-based test infrastructure.

The test strategies and helpers are well-structured:

  • Finite coordinate generation with proper filtering
  • Realistic near-duplicate jitter (1e-7) that challenges geometric predicates
  • Efficient bitwise equality checking for unique coordinate counting

The approach of injecting both exact duplicates and near-duplicates provides comprehensive coverage of edge cases.


100-214: LGTM! Comprehensive property-based validation.

The property tests effectively validate:

  1. Structural validity via is_valid()
  2. Global Delaunay property via validate_delaunay()
  3. Unsalvageable vertex tracking via UUID membership checking

The two-phase approach (standard construction + diagnostic pass) ensures both pipelines work correctly on the same input. The tests properly skip truly degenerate inputs via prop_assume!.

tests/integration_robust_bowyer_watson.rs (6)

15-18: LGTM! Proper feature gating for slow-tests dependencies.

The conditional imports ensure the test dependencies are only compiled when the slow-tests feature is enabled, reducing unnecessary compilation in standard CI runs.


38-94: LGTM! Improved test reliability with seeded RNG and bulk triangulation.

The updated test approach:

  • Uses seeded RNG (line 40) for reproducible test runs
  • Builds full vertex set upfront (lines 44-50) instead of iterative insertion
  • Uses the triangulate API (lines 54-62) which exercises the unified pipeline
  • Adds global Delaunay validation (lines 71-86) with helpful debug output

These changes improve both test reliability and coverage of the new unified insertion pipeline.


96-148: LGTM! Enhanced validation for exterior vertex insertions.

The addition of global Delaunay validation (lines 137-141) at the end of the exterior vertex insertion sequence ensures the final triangulation state is correct, not just structurally valid. This catches potential issues where incremental validity checks pass but the final state violates global properties.


150-221: LGTM! Well-structured two-phase clustered points test.

The updated test effectively validates:

  1. Phase 1 (lines 159-185): Clustered points form valid Delaunay triangulation
  2. Phase 2 (lines 187-214): Adding scattered points maintains global Delaunay property

The two-phase approach with intermediate validation catches issues that might only appear when combining different point distributions. The use of seeded RNG ensures reproducibility.


368-427: LGTM! Robust handling of near-degenerate configurations.

The updated test correctly handles the expected behavior where:

  • Near-duplicate vertices (within 1e-10) may be rejected as unsalvageable (lines 372-375)
  • Both successful triangulation with skipped vertices (lines 400-415) and explicit rejection (lines 416-425) are acceptable outcomes

The comprehensive documentation and diagnostic output make the test behavior clear. This reflects the robust insertion pipeline's correct handling of numerically challenging inputs.


429-526: LGTM! Comprehensive algorithm reset validation.

The enhanced test now validates that reset() properly clears:

  1. Statistics (lines 471-478)
  2. Unsalvageable vertices bookkeeping (lines 481-485)
  3. All state for independent second run (lines 514-525)

The addition of unsalvageable vertices tracking (lines 460-486, 514-525) ensures the diagnostic bookkeeping is properly managed across algorithm reuse. The comments appropriately explain why zero unsalvageable vertices is not asserted for simple configurations.

src/core/cell.rs (2)

201-219: Macro slice coercion and early duplicate-vertex validation look good

The cell! patterns now borrowing $vertices into vertices_slice and relying on slice coercions makes the macro accept Vec, slices, and arrays uniformly, without over-constraining call sites. The added duplicate-key check in Cell::new is consistent with CellValidationError::DuplicateVertices and is_valid(), and ensures invalid cells (wrong vertex count or repeated VertexKeys) are rejected at construction time. No issues spotted here.

Also applies to: 478-504


1893-1901: Test updates toward non-degenerate simplices and slice-based Tds::new usage are consistent

Shifting the various tests to use interior/off-center vertices (e.g., [0.2, 0.2, 0.2], scaled tetrahedra, negative/large/small coordinates, mixed-sign 4D points) and passing array literals/slices into Tds::new aligns with the robust Bowyer–Watson / robust-predicate pipeline and should reduce degeneracy-induced flakiness while still exercising the same invariants. The updated comments correctly describe the new configurations, and the semantics of the assertions remain valid.

Also applies to: 2732-3034, 3255-3256, 3551-3552

…support

- Add comprehensive octree point deduplication and filtering utilities to src/core/util.rs:
  - exact_deduplicate_points: Remove exact duplicate points with stable ordering
  - epsilon_deduplicate_points: Remove approximate duplicates within epsilon threshold
  - filter_points_excluding_references: Filter points while preserving referenced indices
  - All functions handle edge cases (NaN, +/-0.0, empty inputs) and maintain consistent semantics

- Expand documentation with detailed complexity analysis and error handling for vertex utilities

- Add extensive test coverage for deduplication edge cases and boundary conditions

- Improve CI workflow reliability:
  - Skip non-essential tools (just, uv, Node.js) on Windows to avoid installation failures
  - Simplify Windows CI to core build/test operations only
  - Linux/macOS continue running full just-based CI checks

- Increase codecov timeout from 240s to 600s for property-based tests
- Reduce PROPTEST_CASES to 64 in codecov to prevent timeouts

- Add "octree" to cspell dictionary
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/core/util.rs (2)

1614-1697: LGTM - Excellent performance documentation and robust implementation.

The Delaunay validation functions are well-designed with appropriate warnings:

✅ Performance warning is clear and prominent (O(N×V) complexity)
✅ Uses robust geometric predicates for reliability
✅ Checks structural invariants first (tds.is_valid()) to distinguish corruption from non-Delaunay
✅ Comprehensive error handling with detailed diagnostics
✅ Reuses buffers to minimize allocations

The performance warning correctly emphasizes this should not be used in hot paths. Consider adding a code comment at the function entry point reiterating this for maintainers:

pub fn is_delaunay<T, U, V, const D: usize>(
    tds: &Tds<T, U, V, D>,
) -> Result<(), DelaunayValidationError>
where
    T: CoordinateScalar + AddAssign<T> + SubAssign<T> + std::iter::Sum + NumCast,
    U: DataType,
    V: DataType,
{
    // PERFORMANCE: O(N×V) - extremely expensive, use only for testing/validation
    
    // Check structural invariants first...

2685-2713: Good smoke test, but consider adding a violation case.

The test delaunay_validator_reports_no_violations_for_simple_tetrahedron correctly validates that a simple tetrahedron is Delaunay. To strengthen test coverage of the validation logic, consider adding a complementary test that:

  1. Creates a triangulation with a known Delaunay violation
  2. Verifies that is_delaunay() returns the expected DelaunayValidationError::DelaunayViolation
  3. Verifies that find_delaunay_violations() identifies the violating cell(s)

This would ensure the validation logic correctly detects violations, not just passes valid triangulations.

Example test structure:

#[test]
fn test_delaunay_validator_detects_violation() {
    // Create a triangulation with a known violation
    // (e.g., insert a vertex inside an existing cell's circumsphere)
    
    // Verify is_delaunay returns DelaunayViolation error
    let result = is_delaunay(&tds);
    assert!(matches!(result, Err(DelaunayValidationError::DelaunayViolation { .. })));
    
    // Verify find_delaunay_violations identifies the violating cell
    let violations = find_delaunay_violations(&tds, None).unwrap();
    assert!(!violations.is_empty());
}
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 530b4c3 and 7fe7a2e.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml (2 hunks)
  • .github/workflows/codecov.yml (1 hunks)
  • cspell.json (5 hunks)
  • src/core/util.rs (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • cspell.json
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.
📚 Learning: 2025-09-02T02:40:54.439Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 58
File: scripts/tests/test_subprocess_utils.py:92-101
Timestamp: 2025-09-02T02:40:54.439Z
Learning: In Rust projects, cargo is expected to be available in development and CI environments, so conditional skipping of cargo-dependent tests is typically unnecessary.

Applied to files:

  • .github/workflows/codecov.yml
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • src/core/util.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • src/core/util.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.

Applied to files:

  • src/core/util.rs
🧬 Code graph analysis (1)
src/core/util.rs (4)
src/core/vertex.rs (3)
  • uuid (530-532)
  • point (475-477)
  • None (2084-2084)
src/core/cell.rs (6)
  • uuid (797-799)
  • cell (4343-4345)
  • vertices (685-687)
  • new (481-512)
  • number_of_vertices (770-772)
  • neighbors (652-656)
src/geometry/robust_predicates.rs (2)
  • robust_insphere (149-192)
  • num_traits (1421-1421)
src/core/triangulation_data_structure.rs (6)
  • vertices (780-782)
  • new (2595-2658)
  • number_of_vertices (1041-1043)
  • number_of_cells (1196-1198)
  • is_empty (353-355)
  • is_empty (510-512)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build (windows-latest)
  • GitHub Check: Codacy Security Scan
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: Code Coverage
  • GitHub Check: performance-regression
🔇 Additional comments (3)
.github/workflows/ci.yml (1)

76-95: macOS tooling installation differs subtly from Linux.

Line 81 installs shellcheck jq via apt-get, while line 95 installs shellcheck shfmt jq via brew. Note that Linux (line 95 comment) mentions installing shfmt via brew, but the actual Linux block installs it manually via curl (lines 84-89), while the macOS block (lines 93-95) uses brew install shfmt.

Verify this asymmetry is intentional and that both platforms end up with equivalent tooling.

.github/workflows/codecov.yml (2)

70-71: LGTM - Reasonable tradeoff for instrumented coverage runs.

Reducing PROPTEST_CASES from the default 256 to 64 speeds up coverage collection at the cost of exploring fewer property test cases. This is an acceptable tradeoff for instrumented runs, as long as regular CI (non-coverage) still exercises the full default case count.


77-77: Timeout increase appears justified, but runtime verification requires manual testing.

The evidence supports the 600s timeout increase:

  • Property-based tests exist across dimensions 2D–5D with complex geometric operations (convex hull, serialization, quality metrics)
  • Instrumentation overhead is substantial: Reducing PROPTEST_CASES by 75% (256→64) yet increasing timeout by 150% (240→600) indicates tarpaulin's instrumentation dominates execution time
  • 5D tests confirmed: Multiple regression and property tests target higher dimensions, which are computationally expensive

However, I cannot definitively validate that 600s is optimal without measuring actual coverage run duration, as the sandbox lacks cargo/tarpaulin. The original review's concern about catching performance regressions is valid—this requires empirical verification on your CI system.

Recommendation: Run a manual coverage build locally or on CI and measure actual duration to confirm 600s is appropriate (e.g., if actual runtime is ~300s, you could safely use 400s).

Comment on lines +53 to +74
- name: Install just
if: matrix.os != 'windows-latest'
uses: taiki-e/install-action@62da238c048aa0f865cc5a322082957d34e7fc1a # v2.62.54
with:
tool: just

- name: Install uv (for Python linting)
if: matrix.os == 'ubuntu-latest'
- name: Install uv (for Python scripts and pytest)
if: matrix.os != 'windows-latest'
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
with:
version: "latest"

- name: Lint Python scripts
if: matrix.os == 'ubuntu-latest'
run: |
# Format check with ruff (non-blocking for now)
uvx ruff format --check scripts/ || echo "Python formatting issues found"

# Lint check with ruff (non-blocking for now)
uvx ruff check scripts/ || echo "Python linting issues found"
- name: Install Node.js (for markdownlint and cspell)
if: matrix.os != 'windows-latest'
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: '20'

# Note: Pylint retired in favor of comprehensive ruff linting
- name: Install Node.js packages
if: matrix.os != 'windows-latest'
run: |
npm install -g markdownlint-cli cspell
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Verify tooling installation is sufficient for all CI checks.

The workflow installs just, uv, and Node.js only for non-Windows platforms. Ensure Windows CI doesn't need these tools, or add conditional Windows-specific installations if required for parity.


🏁 Script executed:

#!/bin/bash
# Verify justfile exists and contains `ci` task
if [ -f "justfile" ] || [ -f "Justfile" ]; then
  echo "Justfile found"
  grep -n "^\s*ci\s*:" justfile Justfile 2>/dev/null || echo "No 'ci' task found"
else
  echo "ERROR: No justfile found in repository root"
fi

Length of output: 77



I need to examine the justfile structure to understand what the `ci` task delegates to and whether Windows is missing required tools.

---

🏁 Script executed:

```shell
#!/bin/bash
# Examine the justfile to understand the quality, test, and bench-compile tasks
cat justfile | head -120

Length of output: 4007



---

🏁 Script executed:

```shell
#!/bin/bash
# Search for quality and test task definitions
grep -n "^\s*quality\s*:" justfile
grep -n "^\s*test\s*:" justfile

Length of output: 63



---

🏁 Script executed:

```shell
#!/bin/bash
# Get test task definition and lint task definitions
sed -n '410,430p' justfile
grep -n "^\s*lint-code\s*:\|^\s*lint-docs\s*:\|^\s*lint-config\s*:\|^\s*test-all\s*:" justfile

Length of output: 716



---

🏁 Script executed:

```shell
#!/bin/bash
# Find definitions of key lint tasks
grep -n "^\s*python-lint\s*:\|^\s*shell-lint\s*:\|^\s*markdown-lint\s*:\|^\s*spell-check\s*:" justfile

Length of output: 79



---

🏁 Script executed:

```shell
#!/bin/bash
# Extract full definitions of lint tasks
sed -n '215,225p' justfile
echo "---"
sed -n '294,298p' justfile
echo "---"
sed -n '365,375p' justfile
echo "---"
sed -n '386,396p' justfile

Length of output: 1433



---

🏁 Script executed:

```shell
#!/bin/bash
# Check the Windows CI section of the workflow
sed -n '97,110p' .github/workflows/ci.yml

Length of output: 321


Windows CI skips quality and benchmark checks, reducing test coverage.

Non-Windows runs just ci (quality + test + bench-compile), but Windows only runs basic cargo build/test. This means Windows skips:

  • All linting (markdown, shell, Python, config validation)
  • Spell-checking
  • Python tests (test-python with pytest)
  • Benchmark compilation

Either add Windows-specific tool installations (Node.js, uv, shellcheck, shfmt) and run equivalent checks, or document the intentional divergence and its limitations.

🤖 Prompt for AI Agents
.github/workflows/ci.yml around lines 53 to 74: Windows jobs currently skip
installing just, uv, Node.js and Node packages so they miss linting,
spell-check, Python tests and benchmark compile; update the workflow so Windows
also performs the same quality/bench steps by either (A) removing or duplicating
the "if: matrix.os != 'windows-latest'" guards and adding equivalent Windows
installers (install just, setup uv or Python/pytest, actions/setup-node for
Node.js, and install shellcheck/shfmt or Windows-friendly equivalents) then run
the same "just ci" or explicit lint/test/bench commands on Windows, or (B) if
skipping is intentional, add a clear comment and a new job/step that documents
the divergence and its limitations for maintainers and CI consumers.

Comment on lines +97 to +105
- name: Run CI checks (Linux/macOS)
if: matrix.os != 'windows-latest'
run: just ci

- name: Run tests
- name: Build and test (Windows)
if: matrix.os == 'windows-latest'
run: |
cargo test --lib --tests --verbose
cargo test --examples --verbose
cargo build --verbose --all-targets
cargo test --lib --verbose
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Platform divergence: non-Windows uses just ci while Windows uses explicit cargo commands.

The workflow delegates to just ci for Linux/macOS (line 97-99) but runs explicit cargo commands for Windows (line 101-105). This creates two separate CI paths that could diverge over time.

Consider:

  1. If just ci is meant to be the canonical CI workflow, apply it uniformly across platforms
  2. If Windows requires different handling, document why and ensure parity in test coverage
  3. Verify that Windows explicit cargo commands cover all checks that just ci runs (linting, tests, docs)

🏁 Script executed:

#!/bin/bash
# Verify the just ci task to understand what checks it runs
if [ -f "justfile" ] || [ -f "Justfile" ]; then
  echo "=== just ci task definition ==="
  grep -A 50 "^\s*ci\s*:" justfile Justfile 2>/dev/null | head -60
else
  echo "ERROR: Justfile not found"
fi

Length of output: 2228


I need to understand the full scope of the ci task and compare it with the Windows commands. Let me retrieve the definitions of the subtasks.


🏁 Script executed:

#!/bin/bash
# Extract the definitions of quality, test, and bench-compile tasks
echo "=== quality task ==="
grep -A 20 "^\s*quality\s*:" justfile Justfile 2>/dev/null | head -25

echo ""
echo "=== test task ==="
grep -A 20 "^\s*test\s*:" justfile Justfile 2>/dev/null | head -25

echo ""
echo "=== bench-compile task ==="
grep -A 20 "^\s*bench-compile\s*:" justfile Justfile 2>/dev/null | head -25

Length of output: 67


The grep pattern didn't match. Let me try a simpler search to find these task definitions:


🏁 Script executed:

#!/bin/bash
# Search for task definitions without leading whitespace assumption
echo "=== All tasks in justfile ==="
grep -E "^(quality|test|bench-compile):" justfile Justfile 2>/dev/null

echo ""
echo "=== Expanded view: search for these names ==="
grep -nE "quality|test|bench-compile" justfile Justfile 2>/dev/null | head -40

Length of output: 76


The tasks aren't showing up. Let me retrieve the full justfile to understand the complete structure:


🏁 Script executed:

#!/bin/bash
# Get the full justfile content
if [ -f "justfile" ]; then
  echo "=== Full justfile content ==="
  cat justfile
elif [ -f "Justfile" ]; then
  echo "=== Full Justfile content ==="
  cat Justfile
else
  echo "ERROR: No justfile found"
fi

Length of output: 17721


Now I can see the full picture. Let me verify the CI workflow file to check if linting is running on a separate job:


🏁 Script executed:

#!/bin/bash
# Get the full CI workflow to understand job matrix and linting strategy
cat .github/workflows/ci.yml

Length of output: 3073


Platform divergence with substantial coverage gap confirmed—Windows skips linting, Python tests, and benchmarks.

The workflow intentionally does not install just, uv, Node.js, or other tooling on Windows, resulting in two separate CI paths:

  • Linux/macOS: Runs just ci → quality checks (linting + docs) + all tests + benchmark compilation
  • Windows: Explicit cargo commands → build + lib tests + doc tests only

Windows is missing:

  • All linting (clippy, fmt, doc-check, python-lint, shell-lint, markdown-lint, spell-check, action validation)
  • Python tests
  • Benchmark compilation

The divergence is not documented. Either make Windows use the same just ci command (install required tools), move platform-agnostic linting to a separate job, or explicitly document why Windows has reduced CI coverage.

🤖 Prompt for AI Agents
.github/workflows/ci.yml lines 97-105: the Windows job diverges from Linux/macOS
by skipping linting, Python tests, benchmarks and other tooling because it runs
explicit cargo commands instead of the shared just ci; either make Windows run
the same just ci by installing just/Node/Python/etc before invoking it, or
extract platform-agnostic checks (linting, Python tests, spell/markdown/shell
checks, benchmarks) into separate OS-agnostic jobs that run on all matrix
entries, or add a clear comment documenting the intentional reduced coverage;
update the Windows job to install the missing tools and call just ci, or move
the missing steps into their own jobs so Windows no longer misses those checks.

CI Timeout Fixes:
- Reduce PROPTEST_CASES from 64 to 16 in codecov workflow to prevent timeouts
  during tarpaulin instrumentation (especially for expensive 5D property tests)
- Increase BENCHMARK_TIMEOUT from 1800s (30min) to 3600s (60min) in benchmarks
  workflow to allow CI performance regression tests to complete

Delaunay Validation Improvements:
- Add PartialEq and Eq derives to DelaunayValidationError for consistency with
  other error types in the codebase
- Add performance warning comment in is_delaunay() function to remind maintainers
  of O(N×V) complexity
- Add comprehensive test delaunay_validator_detects_violations() that:
  * Uses low-level insert_cell_unchecked() to create a known Delaunay violation
  * Verifies find_delaunay_violations() correctly identifies violating cells
  * Demonstrates is_delaunay() checks structural invariants before Delaunay property
  * Uses 2D triangle scenario with interior vertex to create violation

These changes address CI timeouts and strengthen the test coverage for Delaunay
validation functions per code review feedback.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.github/workflows/codecov.yml (1)

65-65: Configuration changes well-documented and appropriately scoped for CI efficiency.

The reduction of PROPTEST_CASES from the default (256) to 16 and the corresponding timeout increase (240→600s) are clearly justified in the added comments. These changes align with the PR objective to prevent proptest-driven test timeouts during coverage instrumentation, particularly for 4D/5D tests that exercise property-based tests extensively.

Recommendation: Monitor CI execution times in follow-up runs to ensure the 600-second timeout and 16-case reduction remain adequate as test coverage grows. If 5D tests still timeout, consider further tuning or conditional case reduction for dimensional subsets.

Also applies to: 70-78

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7fe7a2e and 1a5be1a.

📒 Files selected for processing (3)
  • .github/workflows/benchmarks.yml (1 hunks)
  • .github/workflows/codecov.yml (1 hunks)
  • src/core/util.rs (6 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.
📚 Learning: 2025-09-02T02:40:54.439Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 58
File: scripts/tests/test_subprocess_utils.py:92-101
Timestamp: 2025-09-02T02:40:54.439Z
Learning: In Rust projects, cargo is expected to be available in development and CI environments, so conditional skipping of cargo-dependent tests is typically unnecessary.

Applied to files:

  • .github/workflows/codecov.yml
  • .github/workflows/benchmarks.yml
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • .github/workflows/codecov.yml
  • src/core/util.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • src/core/util.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.

Applied to files:

  • src/core/util.rs
🧬 Code graph analysis (1)
src/core/util.rs (2)
src/geometry/robust_predicates.rs (3)
  • robust_insphere (149-192)
  • num_traits (1421-1421)
  • general_triangulation (736-746)
src/core/triangulation_data_structure.rs (7)
  • vertices (780-782)
  • new (2595-2658)
  • number_of_vertices (1041-1043)
  • number_of_cells (1196-1198)
  • is_empty (353-355)
  • is_empty (510-512)
  • empty (2476-2486)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build (windows-latest)
  • GitHub Check: Codacy Security Scan
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: performance-regression
  • GitHub Check: Code Coverage
🔇 Additional comments (4)
src/core/util.rs (4)

56-92: LGTM! Previous review comment successfully addressed.

The DelaunayValidationError enum now includes PartialEq and Eq derives (line 57), making it consistent with other validation error types in the codebase. The error structure is well-designed with proper source error wrapping and clear error messages.


166-402: LGTM! Previous review comment successfully addressed.

The vertex deduplication utilities now have clear, accurate documentation:

  • ordered_eq semantics are explicitly documented (NaN-aware, treats +0.0 == -0.0)
  • Epsilon comparison semantics clarified as strict < (line 388)
  • O(n²) complexity documented with appropriate guidance for large inputs

The implementations are correct and comprehensive tests verify the documented behavior.

Based on learnings


1539-1698: LGTM! Solid implementation of Delaunay validation.

The validation functions are well-designed:

  • Uses robust geometric predicates for numerical stability (line 1592)
  • Validates TDS structural integrity before checking Delaunay property (line 1677)
  • Clear performance warnings about O(N×V) complexity
  • Proper error handling and wrapping
  • SmallVec buffer reuse to minimize allocations (line 1684)

The implementation correctly identifies Delaunay violations using circumsphere tests.


1700-1777: LGTM! Well-designed for iterative refinement.

find_delaunay_violations complements is_delaunay by:

  • Returning ALL violating cells (useful for debugging and iterative fixes)
  • Allowing targeted validation of specific cells
  • Properly handling missing cells (silently skipped, as documented on line 1724)

This design is appropriate for cavity refinement and repair workflows.

…rk timeouts

- codecov: Remove --all-features flag to respect slow-tests feature gate
  This prevents stress tests (test_stress_1000_vertices_2d, etc.) from
  running under tarpaulin instrumentation where they timeout
- benchmarks: Increase workflow timeout from 45 to 75 minutes for CI
  benchmark suite and add configurable bench_timeout parameter
- benchmark_utils: Add bench_timeout parameter (default 1800s) to
  run_regression_test and wire through to compare_with_baseline
- tests: Update test assertion to expect bench_timeout parameter
- proptest: Enable serialization regression test for 2D neighbor preservation

The slow-tests feature was designed to exclude performance-intensive stress
tests from CI, but --all-features was overriding this gate in codecov runs,
causing timeout failures.
@coderabbitai coderabbitai bot added the bug Something isn't working label Nov 21, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
scripts/benchmark_utils.py (1)

2393-2398: CLI bench-timeout wiring is coherent with env-based default

Adding --bench-timeout to the run-regression-test subcommand with default=int(os.getenv("BENCHMARK_TIMEOUT", "1800")) and passing args.bench_timeout into BenchmarkRegressionHelper.run_regression_test(..., bench_timeout=args.bench_timeout) makes the timeout behavior consistent with generate-baseline and compare. This end‑to‑end flow (env → argparse → helper → comparator) looks correct.

If you ever want to harden UX further, you could (optionally) add simple validation to ensure bench_timeout > 0 at argument-parse time, but it’s not required for correctness.

Also applies to: 2495-2496

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a5be1a and 8bdc667.

📒 Files selected for processing (5)
  • .github/workflows/benchmarks.yml (2 hunks)
  • .github/workflows/codecov.yml (1 hunks)
  • scripts/benchmark_utils.py (4 hunks)
  • scripts/tests/test_benchmark_utils.py (1 hunks)
  • tests/proptest_serialization.rs (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/benchmarks.yml
  • .github/workflows/codecov.yml
🧰 Additional context used
📓 Path-based instructions (1)
scripts/*.py

⚙️ CodeRabbit configuration file

scripts/*.py: These are Python utility scripts for changelog and commit processing.
Focus on code quality, maintainability, and adherence to Python best practices.
The scripts use ruff for comprehensive linting and formatting (pylint has been retired).

Files:

  • scripts/benchmark_utils.py
🧠 Learnings (3)
📓 Common learnings
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • tests/proptest_serialization.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • tests/proptest_serialization.rs
🧬 Code graph analysis (3)
scripts/tests/test_benchmark_utils.py (1)
scripts/benchmark_utils.py (1)
  • compare_with_baseline (1417-1481)
scripts/benchmark_utils.py (1)
scripts/tests/test_benchmark_utils.py (3)
  • comparator (251-254)
  • comparator (578-581)
  • comparator (697-700)
tests/proptest_serialization.rs (3)
src/core/vertex.rs (2)
  • from_points (419-424)
  • point (475-477)
src/core/cell.rs (5)
  • new (481-512)
  • vertices (685-687)
  • number_of_vertices (770-772)
  • cell (4343-4345)
  • neighbors (652-656)
src/core/triangulation_data_structure.rs (4)
  • new (2595-2658)
  • vertices (780-782)
  • assign_neighbors (854-968)
  • number_of_vertices (1041-1043)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Security Scan
  • GitHub Check: Code Coverage
  • GitHub Check: performance-regression
  • GitHub Check: build (windows-latest)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (4)
scripts/benchmark_utils.py (1)

2230-2249: bench_timeout parameter is correctly plumbed through comparator

The new bench_timeout: int = 1800 parameter and its propagation via comparator.compare_with_baseline(..., bench_timeout=bench_timeout) are consistent with the comparator API and keep the default behavior unchanged for in-process callers. No functional issues here.

scripts/tests/test_benchmark_utils.py (1)

1365-1374: Test correctly asserts bench_timeout propagation to comparator

The updated assertion to expect compare_with_baseline(baseline_file, bench_timeout=1800) matches the new run_regression_test signature and ensures the default timeout is actually forwarded. This is a good, focused regression guard on the wiring.

tests/proptest_serialization.rs (2)

51-51: LGTM! Correct API adaptation.

The .prop_map(|v| Vertex::from_points(&v)) wrapper correctly adapts to the updated Vertex::from_points signature that now accepts &[Point] instead of an owned Vec<Point>.

Also applies to: 89-89, 115-115, 169-169


171-174: Good defensive improvement to ensure accurate neighbor counting.

Explicitly calling assign_neighbors() before counting neighbor relationships ensures the data is fully populated for comparison. This change applies consistently across all dimensions (2D-5D) via the macro, making the tests more robust.

Fix critical CI/CD performance issues causing timeouts in benchmark regression
testing (60+ minutes) and code coverage analysis (3+ hours).

Changes:
- benchmarks: Add --dev mode support to run-regression-test for 10x speedup
  - Add dev_mode parameter to BenchmarkRegressionHelper.run_regression_test()
  - Wire --dev flag through CLI argument parser and workflow invocation
  - Use DEV_MODE_BENCH_ARGS for faster CI benchmark execution
- codecov: Exclude proptest_*.rs files from coverage instrumentation
  - Proptests take 3+ hours under tarpaulin (only 16 cases with PROPTEST_CASES)
  - Regular CI doesn't run integration tests, so proptests only executed in codecov
  - Coverage still captures unit tests, doc tests, and non-proptest integration tests
- validation: Add bench_timeout > 0 validation at argument parse time for better UX
- tests: Add comprehensive test coverage for dev_mode and timeout parameters
- regression: Add assertion to proptest serialization neighbor preservation test

Performance impact:
- Benchmark workflow: 60+ min → ~10-15 min (10x speedup with dev mode)
- Codecov workflow: 3+ hours → expected <30 min (excludes slow proptest instrumentation)

The --dev mode uses reduced sample sizes (10 vs 100) and shorter measurement
times while still validating regression detection logic. Proptests remain
valuable for development but are too slow under coverage instrumentation.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/benchmark_utils.py (1)

2338-2349: Clarify bench-timeout help text and centralize default derivation

The CLI behavior is correct, but two small UX/maintainability issues are worth tightening:

  • The --bench-timeout help strings hardcode "default: 1800, from BENCHMARK_TIMEOUT env", but the actual default is int(os.getenv("BENCHMARK_TIMEOUT", "1800")). When CI sets BENCHMARK_TIMEOUT=3600, the help text becomes misleading.
  • The int(os.getenv("BENCHMARK_TIMEOUT", "1800")) expression is repeated for three subcommands; a small helper (e.g., get_default_bench_timeout()) that handles parsing and a defensive fallback would avoid duplication and give a single place to adjust behavior if you ever change the env contract.

Consider updating the help text to something like “default from BENCHMARK_TIMEOUT (or 1800 if unset)” and optionally factoring out the default calculation into a shared function.

Also applies to: 2355-2362, 2395-2405, 2556-2558

🧹 Nitpick comments (1)
scripts/tests/test_benchmark_utils.py (1)

1678-1707: CLI timeout validation tests duplicate the validation condition

test_cli_bench_timeout_validation reconstructs the same if hasattr(args, "validate_bench_timeout") and args.validate_bench_timeout and args.bench_timeout <= 0 logic used in main(). This works, but it couples the test to the exact implementation.

If you want to reduce drift risk later, consider either:

  • extracting a small helper (e.g., validate_bench_timeout(args, parser)) used by both main() and the tests, or
  • testing the behavior via a thin wrapper that invokes main() with patched sys.argv and stubs out expensive pieces (e.g., find_project_root), so the test only asserts on SystemExit codes.

Not urgent, but worth considering if the CLI grows more validation rules.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8bdc667 and 1826d57.

📒 Files selected for processing (6)
  • .github/workflows/benchmarks.yml (2 hunks)
  • .github/workflows/codecov.yml (1 hunks)
  • justfile (7 hunks)
  • scripts/benchmark_utils.py (6 hunks)
  • scripts/tests/test_benchmark_utils.py (4 hunks)
  • tests/proptest_serialization.rs (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/codecov.yml
🧰 Additional context used
📓 Path-based instructions (1)
scripts/*.py

⚙️ CodeRabbit configuration file

scripts/*.py: These are Python utility scripts for changelog and commit processing.
Focus on code quality, maintainability, and adherence to Python best practices.
The scripts use ruff for comprehensive linting and formatting (pylint has been retired).

Files:

  • scripts/benchmark_utils.py
🧠 Learnings (4)
📓 Common learnings
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • tests/proptest_serialization.rs
  • justfile
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • tests/proptest_serialization.rs
📚 Learning: 2025-09-02T02:40:54.439Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 58
File: scripts/tests/test_subprocess_utils.py:92-101
Timestamp: 2025-09-02T02:40:54.439Z
Learning: In Rust projects, cargo is expected to be available in development and CI environments, so conditional skipping of cargo-dependent tests is typically unnecessary.

Applied to files:

  • justfile
🧬 Code graph analysis (1)
tests/proptest_serialization.rs (3)
src/core/vertex.rs (3)
  • from_points (419-424)
  • point (475-477)
  • dim (574-576)
src/core/triangulation_data_structure.rs (6)
  • new (2595-2658)
  • vertices (780-782)
  • assign_neighbors (854-968)
  • dim (1131-1137)
  • number_of_cells (1196-1198)
  • number_of_vertices (1041-1043)
src/core/cell.rs (6)
  • new (481-512)
  • vertices (685-687)
  • dim (951-953)
  • number_of_vertices (770-772)
  • cell (4343-4345)
  • neighbors (652-656)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Code Coverage
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: performance-regression
  • GitHub Check: build (windows-latest)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: Codacy Security Scan
🔇 Additional comments (12)
tests/proptest_serialization.rs (3)

51-51: LGTM: Correctly aligned with the new Vertex::from_points API.

The proptest inputs now correctly wrap the generated point vectors with .prop_map(|v| Vertex::from_points(&v)), aligning with the API change where Vertex::from_points now accepts &[Point] instead of Vec<Point>.

Also applies to: 89-89, 115-115, 169-169


171-175: LGTM: Explicit neighbor assignment ensures accurate comparison.

The explicit call to assign_neighbors() is necessary because Tds::new doesn't automatically populate neighbor relationships. This ensures the test compares fully initialized neighbor data during serialization roundtrips.


217-293: LGTM: Regression test now includes the required assertion.

The regression test correctly captures a specific failure case and validates neighbor preservation during JSON roundtrip. The assertion at lines 289-292 addresses the previous review comment by ensuring the test fails when neighbor counts don't match.

justfile (6)

129-129: LGTM: Appropriate coverage exclusion for proptest files.

Excluding proptest regression files from coverage is appropriate, as these are typically auto-generated test artifacts rather than production code.


162-163: LGTM: Clear documentation for slow test variants.

The help text clearly documents the new slow test recipes and distinguishes between standard and slow quality checks.

Also applies to: 170-171


302-305: LGTM: Well-structured comprehensive quality check.

The quality-slow recipe provides a thorough validation workflow for local development, appropriately using test-slow-release for faster execution of slow tests.


374-379: LGTM: Improved cross-platform compatibility.

The conditional shellcheck execution with warning message is a good improvement for cross-platform support, especially for Windows environments where shellcheck may not be readily available.


429-436: LGTM: Well-implemented slow test gating.

The new slow test recipes appropriately use feature flags to separate fast CI tests from comprehensive stress tests. The release mode variant provides a good balance between thoroughness and execution time.


99-100: The CI workflow at .github/workflows/ci.yml lines 105-106 runs cargo test --lib --verbose and cargo test --doc --verbose without the --release flag, confirming tests run in debug mode. The justfile change from test-release to test correctly mirrors this behavior and the comment accurately states it matches the CI workflow.

Likely an incorrect or invalid review comment.

.github/workflows/benchmarks.yml (1)

46-51: Bench timeout wiring now consistent and CI‑friendly

Using BENCHMARK_TIMEOUT: 3600 with a timeout-minutes: 75 job and passing --bench-timeout "${BENCHMARK_TIMEOUT}" --dev into benchmark-utils run-regression-test keeps CI and the Python tooling aligned and leaves a reasonable buffer for setup/teardown. This also resolves the earlier mismatch between job timeout and the benchmark timeout env.

Also applies to: 282-286

scripts/benchmark_utils.py (1)

2230-2251: bench_timeout/dev_mode propagation through regression helper looks solid

BenchmarkRegressionHelper.run_regression_test now clearly reports mode, then forwards both dev_mode and bench_timeout into PerformanceComparator.compare_with_baseline, and execute_regression_commands wires CLI args straight into that helper. The behavior for callers that relied on the old signature is preserved via defaults while giving CI finer‑grained control over runtime.

Also applies to: 2501-2502

scripts/tests/test_benchmark_utils.py (1)

41-43: Good coverage of new run_regression_test API surface

Importing create_argument_parser and extending the BenchmarkRegressionHelper.run_regression_test tests to assert dev_mode/bench_timeout propagation and mode‑specific messaging gives strong protection around the new CLI+helper contract. These tests should catch regressions if the comparator call signature or defaults drift.

Also applies to: 1360-1431

Comment on lines +111 to 112
cargo clippy --workspace --all-targets -- -D warnings -W clippy::pedantic -W clippy::nursery -W clippy::cargo
cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic -W clippy::nursery -W clippy::cargo
Copy link
Contributor

@coderabbitai coderabbitai bot Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove duplicate clippy invocation.

Line 111 appears to duplicate line 112. The second invocation already includes --all-features, making the first redundant.

Apply this diff to remove the duplicate:

 clippy:
-    cargo clippy --workspace --all-targets -- -D warnings -W clippy::pedantic -W clippy::nursery -W clippy::cargo
     cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic -W clippy::nursery -W clippy::cargo
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cargo clippy --workspace --all-targets -- -D warnings -W clippy::pedantic -W clippy::nursery -W clippy::cargo
cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic -W clippy::nursery -W clippy::cargo
clippy:
cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic -W clippy::nursery -W clippy::cargo
🤖 Prompt for AI Agents
In justfile around lines 111 to 112, there are two identical cargo clippy
invocations where the second already includes --all-features, so remove the
duplicate first invocation (line 111) and keep the second line that includes
--all-features; ensure only a single cargo clippy command remains with the
flags: --workspace --all-targets --all-features -- -D warnings -W
clippy::pedantic -W clippy::nursery -W clippy::cargo.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed because I have feature-gated code:

• count-allocations - Used in lib.rs, util.rs
• dense-slotmap - Used in collections.rs
• bench - Used in boundary.rs, cell.rs
• test-debug - Used in robust_bowyer_watson.rs

Answer: Yes, running clippy twice is still necessary because:

  1. Without --all-features: Clippy checks the default code paths (without optional features)
  2. With --all-features: Clippy checks the feature-gated code paths that only compile when features are enabled

If you only ran it once without --all-features, you'd miss lints in:
• Code behind #[cfg(feature = "count-allocations")]
• Code behind #[cfg(feature = "dense-slotmap")]
• Code behind #[cfg(feature = "bench")]
• Code behind #[cfg(feature = "test-debug")]

The current setup is correct and catches lints in both feature configurations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@acgetchell, thank you for the clarification! You're absolutely correct. Running clippy with --all-targets --all-features is standard practice, but when you have feature-gated code, you need both invocations:

  1. Without --all-features: Checks the default configuration and any code that has different implementations when features are disabled
  2. With --all-features: Checks all code behind #[cfg(feature = "...")] gates

This ensures comprehensive linting coverage across both feature configurations. My initial suggestion to remove the first line was incorrect—your current setup is the right approach for projects with feature-gated code. The pattern is especially important since you have features that enable different code paths in multiple files.

I appreciate you taking the time to explain the reasoning!


✏️ Learnings added
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 124
File: justfile:111-112
Timestamp: 2025-11-21T22:40:08.532Z
Learning: In the delaunay repository justfile, the clippy recipe runs cargo clippy twice: once without --all-features to check default code paths, and once with --all-features to check feature-gated code. This is intentional and necessary because the repository has feature-gated code behind count-allocations, dense-slotmap, bench, and test-debug features.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

…tion

Reduce codecov runtime from 3+ hours to minutes by excluding proptest
integration tests while maintaining comprehensive coverage through unit
tests and regular integration tests. Centralize configuration in justfile
to eliminate duplication.

Changes:
- codecov: Refactor to use `just coverage-ci` for single source of truth
  - Install just in workflow to delegate coverage execution
  - Remove 50+ lines of duplicated tarpaulin configuration
  - Simplifies workflow and improves maintainability
- justfile: Create coverage recipes with explicit test selection
  - Add `_coverage_base_args` variable with common tarpaulin arguments
  - Add `coverage` recipe for local development (HTML output)
  - Add `coverage-ci` recipe for CI (XML output for codecov/codacy)
  - Explicitly list 19 non-proptest integration tests with --test flags
  - Use --lib for unit tests, skip proptest_*.rs entirely
  - Remove --all-features to avoid slow-tests feature gate
- benchmark_utils: Centralize bench_timeout default and improve help text
  - Add get_default_bench_timeout() helper with defensive fallback
  - Update help text to clarify env var precedence
  - Eliminates duplication across three CLI subcommands
- tests: Refactor CLI validation test to reduce coupling
  - Test calls main() directly instead of duplicating validation logic
  - More maintainable: validation changes don't require test updates

Performance impact:
- Codecov workflow: 3+ hours → expected <30 minutes
- Skips ~17 proptest files with hundreds of property-based test cases
- Retains coverage from unit tests + 19 regular integration tests
- Single configuration point: add/remove tests only in justfile

Benefits:
- DRY principle: test list maintained in one place (justfile)
- Easier maintenance: update justfile, both local and CI stay in sync
- Faster local coverage runs with `just coverage`
- Comprehensive coverage without extreme runtime costs
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
scripts/benchmark_utils.py (1)

2332-2342: get_default_bench_timeout behavior is reasonable, consider optional stricter sanitization

The helper cleanly reads BENCHMARK_TIMEOUT and falls back to 1800 on parse errors. Note that negative or zero values from the env are currently accepted here and only rejected later by the CLI validation; if you’d rather treat non‑positive env values as “unset,” you could clamp them to 1800 here instead.

justfile (2)

127-148: Centralized tarpaulin arguments and coverage-ci target look solid, with minor optional cleanups.

The _coverage_base_args var cleanly centralizes tarpaulin configuration and enumerates the expected integration tests (allocation_api, convex_hull_bowyer_watson_integration, robust_predicates_* etc.), matching the specialized integration suite in this repo. The raw triple‑quoted string with inner single‑quoted globs ('benches/*', 'examples/*') should expand correctly into the cargo tarpaulin invocations for both HTML (coverage) and XML (coverage-ci) runs.
Two optional polish items you might consider:

  • Add coverage-ci to help-workflows (or document it in CI docs) so it’s discoverable for contributors.
  • If you expect coverage/ artifacts locally, you may want clean to also remove coverage/ alongside target/tarpaulin.

Also applies to: 150-153, 156-158


401-406: Gracefully degrading shell-lint when shellcheck is missing is a good improvement.

Keeping shfmt mandatory but making shellcheck optional (with a clear warning) prevents spurious failures on environments where shellcheck isn’t installed (e.g., some Windows setups), while still enforcing formatting. If cross‑platform ergonomics become a bigger concern, you could later make shfmt optional in the same pattern, but it’s not required now.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1826d57 and 9c161ee.

📒 Files selected for processing (4)
  • .github/workflows/codecov.yml (2 hunks)
  • justfile (7 hunks)
  • scripts/benchmark_utils.py (7 hunks)
  • scripts/tests/test_benchmark_utils.py (5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
scripts/*.py

⚙️ CodeRabbit configuration file

scripts/*.py: These are Python utility scripts for changelog and commit processing.
Focus on code quality, maintainability, and adherence to Python best practices.
The scripts use ruff for comprehensive linting and formatting (pylint has been retired).

Files:

  • scripts/benchmark_utils.py
🧠 Learnings (6)
📓 Common learnings
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.
📚 Learning: 2025-09-02T02:40:54.439Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 58
File: scripts/tests/test_subprocess_utils.py:92-101
Timestamp: 2025-09-02T02:40:54.439Z
Learning: In Rust projects, cargo is expected to be available in development and CI environments, so conditional skipping of cargo-dependent tests is typically unnecessary.

Applied to files:

  • .github/workflows/codecov.yml
  • justfile
📚 Learning: 2025-11-21T22:40:08.532Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 124
File: justfile:111-112
Timestamp: 2025-11-21T22:40:08.532Z
Learning: In the delaunay repository justfile, the clippy recipe runs cargo clippy twice: once without --all-features to check default code paths, and once with --all-features to check feature-gated code. This is intentional and necessary because the repository has feature-gated code behind count-allocations, dense-slotmap, bench, and test-debug features.

Applied to files:

  • justfile
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • justfile
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.

Applied to files:

  • justfile
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • justfile
🧬 Code graph analysis (2)
scripts/tests/test_benchmark_utils.py (3)
scripts/benchmark_utils.py (5)
  • create_argument_parser (2345-2429)
  • main (2560-2579)
  • compare_with_baseline (1417-1481)
  • BenchmarkRegressionHelper (1948-2329)
  • run_regression_test (2230-2266)
scripts/subprocess_utils.py (1)
  • find_project_root (272-288)
scripts/tests/conftest.py (1)
  • temp_chdir (23-51)
scripts/benchmark_utils.py (1)
scripts/subprocess_utils.py (1)
  • find_project_root (272-288)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build (windows-latest)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: performance-regression
  • GitHub Check: Code Coverage
  • GitHub Check: Codacy Security Scan
🔇 Additional comments (15)
.github/workflows/codecov.yml (2)

51-54: Install just CLI with proper version pinning.

The step correctly installs just with explicit version pinning for reproducibility.


62-66: All justfile configuration verified—no issues found.

The refactoring is correctly implemented:

  • _coverage_base_args is properly defined in the justfile with appropriate exclusions
  • The coverage-ci target correctly invokes cargo tarpaulin with Xml output to the coverage directory
  • cargo tarpaulin --out Xml generates cobertura.xml by default in the specified output directory
  • The workflow includes explicit verification that coverage/cobertura.xml exists (line 80) before attempting upload
  • The Codecov upload step correctly references the expected file path

The delegated configuration is sound and the subsequent workflow steps properly validate the output.

scripts/benchmark_utils.py (4)

2230-2267: run_regression_test: timeout/dev-mode propagation looks correct

BenchmarkRegressionHelper.run_regression_test now forwards both dev_mode and bench_timeout to PerformanceComparator.compare_with_baseline, and the mode-dependent banner ("dev mode (10x faster)" vs "full mode") accurately reflects the behavior. Control-flow and error handling remain unchanged.


2356-2362: CLI wiring for --bench-timeout / --dev is consistent and explicit

generate-baseline, compare, and run-regression-test now all expose --bench-timeout (defaulting via get_default_bench_timeout()) and --dev, and the validate_bench_timeout=True flag is set only on parsers that actually accept the option. This keeps the interface uniform and makes the validation hook in main() straightforward.

Also applies to: 2369-2375, 2408-2417


2513-2515: execute_regression_commands correctly forwards new parameters

The run-regression-test branch now passes bench_timeout=args.bench_timeout and dev_mode=args.dev through to BenchmarkRegressionHelper.run_regression_test, matching the updated signature and avoiding any reliance on default values from within the helper.


2569-2571: bench_timeout validation in main is simple and robust

The guard hasattr(args, "validate_bench_timeout") and args.validate_bench_timeout ensures only relevant subcommands are checked, and parser.error(...) gives a standard argparse-style error and exit code when bench_timeout <= 0. This centralizes validation without impacting other commands.

scripts/tests/test_benchmark_utils.py (5)

13-45: Imports for CLI testing are appropriate and targeted

Adding sys plus create_argument_parser and main from benchmark_utils is exactly what’s needed to exercise CLI parsing and main() behavior; no unnecessary symbols are pulled in.


1375-1377: Default timeout assertion aligns with helper signature

The updated assertion that compare_with_baseline is called with bench_timeout=1800 and dev_mode=False matches the new run_regression_test defaults and will catch regressions in parameter wiring.


1381-1399: Dev‑mode regression test verifies mode propagation and messaging

test_run_regression_test_dev_mode correctly checks that dev_mode=True is forwarded to compare_with_baseline and that the user-facing output includes the dev-mode banner, giving good coverage for the new behavior.


1415-1433: Custom timeout test covers non-default bench_timeout path

test_run_regression_test_custom_timeout validates that a non-default bench_timeout (3600) is passed through while keeping dev_mode=False. This ensures the new timeout parameter won’t silently be ignored.


1680-1708: CLI bench-timeout validation test matches main() semantics

test_cli_bench_timeout_validation exercises three important paths: zero and negative --bench-timeout causing SystemExit(2) via parser.error, and a positive value being parsed correctly via create_argument_parser. Using a temporary project with Cargo.toml keeps find_project_root() happy while ensuring the validation logic is what actually drives the failures.

justfile (4)

110-112: Clippy double invocation is correct for feature‑gated code.

Running Clippy once without --all-features and once with --all-features is the right pattern here to lint both default and feature‑gated paths (e.g., count-allocations, dense-slotmap, bench, test-debug). No change needed.
Based on learnings


189-190: New help entries for slow tests and quality-slow are consistent.

The help-workflows entries for test-slow, test-slow-release, and quality-slow correctly describe the new recipes and their purpose (including slow/stress tests and slow‑variant quality runs). No issues here.

Also applies to: 197-198


329-332: quality-slow composition is reasonable for pre‑commit/local runs.

quality-slow layering lint-code lint-docs lint-config test-all test-slow-release gives you the full standard quality suite plus slow tests in release mode, which is a sensible “max checks” target for local or pre‑commit usage. Looks good.


456-463: Slow‑test recipes are wired correctly and match their documentation.

test-slow and test-slow-release mirror the existing test/test-release split while enabling the slow-tests feature, and they’re referenced from help-workflows and quality-slow as expected. This cleanly separates fast CI from heavier stress tests.

@codacy-production
Copy link

codacy-production bot commented Nov 22, 2025

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
-6.31% (target: -1.00%) 46.31%
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (9eaa225) 4427 3095 69.91%
Head commit (d45f495) 5921 (+1494) 3766 (+671) 63.60% (-6.31%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#124) 1667 772 46.31%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

@codecov
Copy link

codecov bot commented Nov 22, 2025

Codecov Report

❌ Patch coverage is 51.48248% with 180 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.60%. Comparing base (9eaa225) to head (d45f495).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/core/util.rs 44.50% 96 Missing ⚠️
src/core/algorithms/unified_insertion_pipeline.rs 55.61% 83 Missing ⚠️
src/core/cell.rs 83.33% 1 Missing ⚠️

❌ Your project status has failed because the head coverage (63.60%) is below the target coverage (70.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #124      +/-   ##
==========================================
- Coverage   69.94%   63.60%   -6.34%     
==========================================
  Files          19       20       +1     
  Lines        4425     5919    +1494     
==========================================
+ Hits         3095     3765     +670     
- Misses       1330     2154     +824     
Flag Coverage Δ
unittests 63.60% <51.48%> (-6.34%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Improve API ergonomics by changing four InsertionBuffers setter methods
to accept slices instead of owned Vec parameters:

- set_bad_cells_from_vec: Vec<CellKey> -> &[CellKey]
- set_boundary_facet_handles: Vec<FacetHandle> -> &[FacetHandle]
- set_vertex_points_from_vec: Vec<Point<T, D>> -> &[Point<T, D>]
- set_visible_facet_handles: Vec<FacetHandle> -> &[FacetHandle]

These methods only iterate and copy data into internal buffers, so they
don't need ownership. Accepting slices is more flexible and idiomatic,
allowing callers to pass references to existing vectors or array slices
without unnecessary moves.

Also includes minor documentation and CI fixes:
- Fix GitHub Actions log group syntax in codecov.yml
- Update justfile help text for clarity
- Add coverage directory cleanup to justfile
- Update all test call sites to pass slice references
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
tests/test_insertion_algorithm_trait.rs (1)

162-178: Consider inline slice construction (optional).

The intermediate points vector is clear but adds verbosity. You could pass the slice directly:

let vertices = delaunay::core::vertex::Vertex::from_points(&[
    delaunay::geometry::point::Point::new([0.0, 0.0, 0.0]),
    delaunay::geometry::point::Point::new([1.0, 0.0, 0.0]),
    delaunay::geometry::point::Point::new([0.0, 1.0, 0.0]),
    delaunay::geometry::point::Point::new([0.0, 0.0, 1.0]),
]);

However, the current pattern is explicit and equally correct.

Also applies to: 206-222

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c161ee and d45f495.

📒 Files selected for processing (4)
  • .github/workflows/codecov.yml (1 hunks)
  • justfile (6 hunks)
  • tests/test_insertion_algorithm_trait.rs (7 hunks)
  • tests/test_insertion_algorithm_utils.rs (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/codecov.yml
  • justfile
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • tests/test_insertion_algorithm_trait.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • tests/test_insertion_algorithm_trait.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.

Applied to files:

  • tests/test_insertion_algorithm_trait.rs
🧬 Code graph analysis (2)
tests/test_insertion_algorithm_utils.rs (2)
src/core/facet.rs (4)
  • new (269-274)
  • new (442-466)
  • new (755-772)
  • new (844-852)
src/geometry/point.rs (1)
  • new (115-117)
tests/test_insertion_algorithm_trait.rs (4)
src/core/triangulation_data_structure.rs (8)
  • bowyer_watson (3067-3072)
  • cells (703-705)
  • cells (4736-4744)
  • new (2595-2658)
  • vertices (780-782)
  • number_of_vertices (1041-1043)
  • number_of_cells (1196-1198)
  • is_valid (4320-4334)
src/core/algorithms/robust_bowyer_watson.rs (1)
  • new (147-158)
src/core/vertex.rs (3)
  • point (475-477)
  • from_points (419-424)
  • is_valid (610-628)
src/core/algorithms/bowyer_watson.rs (1)
  • new (180-189)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Code Coverage
  • GitHub Check: Codacy Security Scan
  • GitHub Check: performance-regression
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: build (windows-latest)
  • GitHub Check: build (macos-latest)
🔇 Additional comments (5)
tests/test_insertion_algorithm_utils.rs (2)

141-174: LGTM! Consistent API usage with slice-based setters.

The test correctly adapts to the new setter signatures that accept slices (&[T]) instead of owned Vecs, while maintaining comprehensive coverage of the Vec compatibility helpers.


176-208: LGTM! Clean slice literal usage.

The test demonstrates the slice literal pattern (&[facet_handle]) for single-element inputs, which is more concise than creating a Vec.

tests/test_insertion_algorithm_trait.rs (3)

15-22: LGTM! Imports support new test coverage.

The added imports (IncrementalBowyerWatson, InsertionAlgorithm, vertex macro) are all utilized in the new transactional behavior test.


127-134: LGTM! Consistent slice-based API usage.

The setter method calls correctly pass references (&cells, &handles) to align with the new slice-based signatures.

Also applies to: 151-159, 195-203


350-392: Excellent transactional behavior test!

This test properly verifies that duplicate vertex insertion:

  • Returns an appropriate InvalidVertex error
  • Leaves the TDS completely unchanged (vertex count, cell count)
  • Maintains structural validity

The transactional semantics are crucial for data structure integrity, and this test provides valuable coverage. The error message matching is flexible enough to handle different error formats while remaining meaningful.

@acgetchell acgetchell merged commit da473c8 into main Nov 22, 2025
10 of 13 checks passed
@acgetchell acgetchell deleted the fix/tds-insertion branch November 22, 2025 02:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant