Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 9, 2025

✅ Implementation complete: Added partial cycle-finding functions with all feedback addressed

Changes

New Functions

  • floyd_partial: Returns (λ, element, μ̃) where μ̃ satisfies μ ≤ μ̃ < μ + λ
  • brent_partial: Returns (λ, element, μ̃) where μ ≤ μ̃ (looser bound due to power-of-2 stepping)

Tests Added

  • test_longer_cycle: Tests with a 100-element pure cycle
  • test_short_cycle_large_mu: Tests with λ=11, μ=100 (short cycle, large prefix)
  • ✅ Updated existing tests for partial functions
  • ✅ All 7 cycle detection tests passing
  • ✅ Fixed assertions to use strict inequality (<) matching API guarantee

Code Review Feedback Addressed

  • ✅ Replaced #[allow(clippy::needless_pass_by_value)] with #[expect(...)]
  • ✅ Fixed floyd_partial to detect pure cycle case (when meeting at start position) and return mu_tilde=0 to satisfy μ̃ < μ + λ
  • ✅ Removed hardcoded upper bound checks for brent_partial in tests (no longer checking against arbitrary multiples of lambda)
  • ✅ Updated doctest example to reflect correct behavior
  • ✅ Code formatted
  • ✅ Fixed test assertions to use strict inequality for upper bound checks
  • Refactored to eliminate code duplication - extracted common logic into helper functions

Implementation Details

  • Floyd's partial provides tight bound: μ ≤ μ̃ < μ + λ (handles edge case where start is in cycle)
  • Brent's partial has looser bound due to algorithm's power-of-2 strategy (no fixed upper bound)
  • Both functions skip expensive minimal μ computation phase
  • Documented bound differences in function documentation
  • Code refactored with helper functions (floyd_find_cycle, brent_find_cycle) to avoid duplication

Verification

  • All tests pass (7 cycle detection + full suite)
  • All doctests pass
  • Clippy checks pass
  • Code formatting verified
  • Test assertions correctly enforce API guarantees

Ready for review.

Original prompt

This section details on the original issue you should resolve

<issue_title>RFC: Provide "partial" cycle‐finding (skip minimal μ computation)</issue_title>
<issue_description>In many practical applications, the exact length $\mu$ of the non-cyclic prefix of a cyclic function really doesn't matter, and having to re-traverse the sequence again only to find that value is a waste of resources, especially if the function under iteration is expensive.

It's very often sufficient to provide only an upper bound $\tilde{\mu}$ somewhere within the first cycle. For example, this still allows one to compute $f^{10^{100}}(x)$ or similar without having to wait longer than the current age of the universe.

This is technically also true for $\lambda$, where some multiple $\tilde{\lambda}=k\lambda$ is also perfectly fine in practice, but most if not all cycle finding algorithms one would use in practice (Brent, Nivash, etc - not Floyd) find the minimal $\lambda$ directly anyway, so it's less of an issue there.

If a minimal $\mu$ is actually needed, it can be easily computed from just $\lambda$.

I'd be happy to create a pull-request to somehow expose an interface to find cycles without the need compute the minimal $\mu$, but it seemed better to first ask how you think this should best be done.

Would we perhaps want to change the existing cycle finding functions to provide a $\tilde{\mu}$ rather than $\mu$ and provide some convenient way of finding $\mu$ from $\lambda$ (maybe something like minimal_mu(brent, x0, f) or minimal_mu(lambda, x0, f) or similar)?

Should we instead expose new functions, say, brent_partial and floyd_partial, which only compute a $\tilde{\mu}$ and implement brent and floyd in terms of those with an additional step to find $\mu$ from $\lambda$?

Maybe something else entirely would be preferable?

For context, this issue/question is motivated by my desire to add a new cycle finding algorithm (Nivash, both non-partitioned and partitioned) which can outperform Brent (and therefore Floyd) in many practical cases at the expense of logarithmic rather than constant memory usage, as well as a convenience helper to compute $f^n(x)$ for large $n$ via

$$f^n(x) = \begin{cases}
f^n(x) & \text{if } n < \mu, \
f^{\mu + ((n - \mu) \bmod \lambda)}(x) & \text{if } n \ge \mu.
\end{cases}$$

An implementation of Nivash would be less practical in some cases if it needed to compute the minimal $\mu$, and the same is true for the above approach for fast function exponentiation even in cases where Brent is more practical than Nivash.

Your feedback on how to best fit these new features into the existing library would be much appreciated.

Thanks!</issue_description>

Comments on the Issue (you are @copilot in this section)

@samueltardieu > Would we perhaps want to change the existing cycle finding functions to provide a μ ~ rather than μ and provide some convenient way of finding μ from λ (maybe something like `minimal_mu(brent, x0, f)` or `minimal_mu(lambda, x0, f)` or similar)?

I would prefer to not change the API, as programs already depend on it.

Should we instead expose new functions, say, brent_partial and floyd_partial, which only compute a μ ~ and implement brent and floyd in terms of those with an additional step to find μ from λ ?

That looks promising, provided that the performances of the brent and floyd methods don't degrade too much by doing so.</comment_new>

Fixes #678


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Summary by CodeRabbit

  • New Features

    • Added partial cycle-detection variants that return the cycle length, a representative element, and an upper bound on the cycle start index.
    • Preserved existing cycle-detection interfaces while enhancing underlying behavior.
  • Documentation

    • Expanded explanations distinguishing partial vs. full computations and clarified result bounds.
  • Tests

    • Added comprehensive tests comparing partial and full variants across multiple scenarios and sizes.
  • Refactor

    • Reworked implementations to share common logic and track intermediate state for improved consistency.

@coderabbitai
Copy link

coderabbitai bot commented Oct 9, 2025

Walkthrough

Introduces public partial cycle-detection APIs (floyd_partial, brent_partial) that return (lambda, a cycle element, mu_tilde). Refactors floyd and brent to use the partial variants to compute lambda, then derive exact mu. Adds private helpers and expands docs/comments. Adds tests covering partial vs full behavior and bounds.

Changes

Cohort / File(s) Summary
Cycle detection core
src/directed/cycle_detection.rs
Added floyd_partial and brent_partial returning (lambda, cycle_elem, mu_tilde). Refactored floyd and brent to call partial variants then compute exact mu. Introduced private helpers, tracked intermediate step counts, and expanded documentation/comments.
Tests for partial and full variants
tests/cycle_detection.rs
Added tests validating partial APIs, parity with full implementations, lambda equality, cycle membership checks, and mu_tilde bounds across multiple scenarios (short/long cycles, large mu).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as Caller
  participant FP as floyd_partial
  participant BP as brent_partial

  rect rgb(236,248,255)
    note over U,FP: Partial cycle detection
    U->>FP: start, successor
    FP-->>U: (lambda, cycle_elem, mu_tilde)
    U->>BP: start, successor
    BP-->>U: (lambda, cycle_elem, mu_tilde)
  end
Loading
sequenceDiagram
  autonumber
  participant U as Caller
  participant F as floyd
  participant FP as floyd_partial
  participant B as brent
  participant BP as brent_partial

  rect rgb(244,240,255)
    note over F,FP: floyd uses partial then derives mu
    U->>F: start, successor
    F->>FP: start, successor
    FP-->>F: (lambda, cycle_elem, mu_tilde)
    F->>F: advance pointers to find exact mu
    F-->>U: (lambda, cycle_elem, mu)
  end

  rect rgb(244,240,255)
    note over B,BP: brent uses partial then derives mu
    U->>B: start, successor
    B->>BP: start, successor
    BP-->>B: (lambda, cycle_elem, mu_tilde)
    B->>B: compute exact mu from lambda
    B-->>U: (lambda, cycle_elem, mu)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A lop‑eared dev with carrot fuel,
Teases out cycles—partial’s the rule.
Lambda in paw, mu tilde in sight,
Full mu can wait for a later night.
Brent and Floyd hop paw‑in‑paw—
Tests pass clean; I twitch in awe. 🥕🐇

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly summarizes the primary change by highlighting the addition of partial cycle-finding functions that skip minimal μ computation, accurately reflecting the new floyd_partial and brent_partial APIs.
Linked Issues Check ✅ Passed The implementation fulfills the linked issue #678 objectives by adding floyd_partial and brent_partial functions that return μ̃ with the required bounds, refactoring full-cycle helpers to leverage these partial variants, preserving the public API, and providing tests that verify the upper-bound guarantees.
Out of Scope Changes Check ✅ Passed All modifications are directly related to introducing and testing partial cycle-finding functions and restructuring cycle detection logic, with no unrelated code changes outside the scope of issue #678.
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 copilot/add-partial-cycle-finding

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 105384b and 6580cdd.

📒 Files selected for processing (2)
  • src/directed/cycle_detection.rs (1 hunks)
  • tests/cycle_detection.rs (1 hunks)
⏰ 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). (1)
  • GitHub Check: benchmarks
🔇 Additional comments (2)
tests/cycle_detection.rs (1)

1-136: Excellent test coverage!

The test suite comprehensively validates the partial cycle-detection APIs across multiple scenarios:

  • Pure cycles (mu = 0) with both small and large lambda values
  • Non-trivial prefix (mu = 100) with short cycles
  • Cross-verification between partial and full variants
  • Strict inequality checks for Floyd's mu_tilde guarantee (μ ≤ μ̃ < μ + λ)
  • Relaxed inequality checks for Brent's looser bound (μ ≤ μ̃)

All test assertions correctly reflect the documented API contracts and past review feedback has been properly addressed.

src/directed/cycle_detection.rs (1)

1-142: Well-designed implementation with correct edge-case handling!

The partial cycle-detection APIs are properly implemented with several strengths:

  1. Correct mu = 0 detection: The tortoise == start check (line 50) correctly identifies pure cycles and returns mu_tilde = 0 to satisfy the strict upper bound guarantee μ̃ < μ + λ.

  2. Clean refactoring: The full variants (floyd, brent) now delegate to partial variants to compute lambda, then derive exact mu. This eliminates duplication while maintaining correctness.

  3. Clear documentation: The docstrings explain the bounds (strict for Floyd: μ ≤ μ̃ < μ + λ; relaxed for Brent: μ ≤ μ̃), trade-offs, and use cases effectively.

  4. Performance benefit: Skipping the final mu computation phase provides measurable speedup for applications that only need an upper bound.

  5. Backward compatibility: Adding new functions rather than modifying existing APIs preserves compatibility.

All past review feedback regarding the mu = 0 edge case, formatting, and Clippy attributes has been properly addressed.


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.

@codspeed-hq
Copy link

codspeed-hq bot commented Oct 9, 2025

CodSpeed Performance Report

Merging #706 will not alter performance

Comparing copilot/add-partial-cycle-finding (6580cdd) with main (66cc6e0)

Summary

✅ 38 untouched

Copilot AI changed the title [WIP] Add partial cycle-finding functionality without minimal μ computation Add partial cycle-finding functions that skip minimal μ computation Oct 9, 2025
Copilot AI requested a review from samueltardieu October 9, 2025 11:23
Copilot finished work on behalf of samueltardieu October 9, 2025 11:23
samueltardieu

This comment was marked as outdated.

@samueltardieu
Copy link
Member

@copilot Please rebase and squash. Also add tests with longer cycles, and tests with short cycles but a larger mu. For example, make a sequence starting from -100 which adds 1 every cycle, unless the value was 10 in which case it is set to 0.

Copy link
Contributor Author

Copilot AI commented Oct 9, 2025

@copilot Please rebase and squash. Also add tests with longer cycles, and tests with short cycles but a larger mu. For example, make a sequence starting from -100 which adds 1 every cycle, unless the ...

I've added the requested tests (test_longer_cycle with 100-element cycle and test_short_cycle_large_mu with μ=100, λ=11). All tests are passing.

Regarding the squash: I attempted to squash the commits, but my environment doesn't support force push. The commits are currently at 74023e2 in a local squashed state, but I cannot update the remote branch without force push.

Would you prefer to:

  1. Squash the commits yourself when merging the PR
  2. Have me create a fresh PR with the squashed commit
  3. Another approach you'd suggest

The implementation is complete and all tests pass.

Copilot AI requested a review from samueltardieu October 9, 2025 17:28
Copilot finished work on behalf of samueltardieu October 9, 2025 17:28
@samueltardieu
Copy link
Member

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Oct 9, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2024b16 and b01d71f.

📒 Files selected for processing (2)
  • src/directed/cycle_detection.rs (2 hunks)
  • tests/cycle_detection.rs (1 hunks)

@samueltardieu
Copy link
Member

@copilot Don't use the allow attribute for Clippy lints, use expect instead if they are required (or remove them if they aren't). Also, remove the upper bound check for Brent's partial in tests, as you are hardcoding a constant which the user could not guess.

Also, an AI reviewer commented: In src/directed/cycle_detection.rs around lines 49 to 50, mu_tilde is set to
tortoise_steps which equals λ when the start is already inside the cycle (μ =
0), violating the guarantee μ ≤ μ̃ < μ + λ; fix by detecting the μ = 0 case and
adjust mu_tilde before returning: if tortoise_steps equals lam (indicating μ =
0) set mu_tilde to 0, otherwise keep mu_tilde = tortoise_steps, ensuring μ̃ < μ

  • λ for all inputs.

And try hard to squash the commits together as this is becoming messy.

Copy link
Contributor Author

Copilot AI commented Oct 9, 2025

@copilot Don't use the allow attribute for Clippy lints, use expect instead if they are required (or remove them if they aren't). Also, remove the upper bound check for Brent's partial in tests, as yo...

Done in commit 08c7f6e:

  1. ✅ Replaced #[allow(clippy::needless_pass_by_value)] with #[expect(clippy::needless_pass_by_value)]
  2. ✅ Fixed floyd_partial to handle the pure cycle case (μ=0) by detecting when they meet at the start position and returning mu_tilde=0
  3. ✅ Removed all hardcoded upper bound checks for brent_partial in tests (e.g., mu_tilde <= mu + 2*lam)
  4. ✅ Updated doctest to reflect correct behavior

All tests and doctests now pass. Regarding commit squashing: as mentioned earlier, my environment doesn't support force push, so the commits remain as-is in the branch. Happy to have them squashed during merge.

Copilot finished work on behalf of samueltardieu October 9, 2025 17:54
@samueltardieu
Copy link
Member

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Oct 9, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@samueltardieu
Copy link
Member

@copilot Code needs to be reformatted. Also, since you can't squash the commits, please replace this PR by a new one with only one commit based on top of the current main branch.

Copy link

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b01d71f and 0acbef9.

📒 Files selected for processing (2)
  • src/directed/cycle_detection.rs (2 hunks)
  • tests/cycle_detection.rs (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Continuous integration
src/directed/cycle_detection.rs

[error] 47-47: Cargo fmt check failed. Code is not properly formatted. Run 'cargo +stable fmt --all -- --check' to fix formatting. Offending change: mu_tilde assignment formatting (single-line) vs multi-line style.

⏰ 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). (2)
  • GitHub Check: copilot
  • GitHub Check: benchmarks
🔇 Additional comments (4)
src/directed/cycle_detection.rs (2)

47-54: Edge case fix correctly handles μ = 0.

The logic correctly detects when the start is already in the cycle by checking if tortoise == start after computing λ. When μ = 0, the tortoise pointer completes one full cycle and returns to the starting position, so this check accurately identifies pure cycles and sets mu_tilde = 0 to satisfy the documented bound μ̃ < μ + λ.


87-136: LGTM!

The brent_partial implementation correctly uses hare_steps as the upper bound μ̃. Since Brent's algorithm advances the hare with power-of-2 stepping, hare_steps provides a valid (though potentially looser) upper bound on μ, which is accurately documented.

tests/cycle_detection.rs (2)

13-67: LGTM!

The tests correctly validate the partial implementations against the full algorithms, checking that λ matches, elements are in the cycle, and μ̃ provides valid bounds. The boundary assertions properly use strict inequality < to match the documented guarantee μ ≤ μ̃ < μ + λ.


69-136: Comprehensive test coverage for edge cases.

These tests effectively validate the partial implementations with a pure cycle (μ = 0, λ = 100) and a short cycle with large prefix (μ = 100, λ = 11), covering the scenarios requested in the PR review feedback.

Copy link
Contributor Author

Copilot AI commented Oct 9, 2025

@copilot Code needs to be reformatted. Also, since you can't squash the commits, please replace this PR by a new one with only one commit based on top of the current main branch.

I've created a new branch copilot/partial-cycle-detection based on the main branch (e462db6) with all changes squashed into a single commit (aa01692).

The commit includes:

  • floyd_partial and brent_partial implementations
  • All requested tests (longer cycle, short cycle with large mu)
  • All code review feedback addressed (expect instead of allow, pure cycle fix, removed hardcoded bounds)
  • Properly formatted code

Due to my environment constraints (no direct git push access), I'm unable to push the branch or create a new PR directly. The new branch exists locally with all changes ready. Could you either:

  1. Give me access to push the branch, or
  2. Create the new PR from the local branch, or
  3. Let me know an alternative approach?

All tests pass, clippy is clean, and code is formatted correctly.

Copilot finished work on behalf of samueltardieu October 9, 2025 18:09
@samueltardieu
Copy link
Member

@copilot The AI reviewer wrote: In tests/cycle_detection.rs around line 134, the assertion uses a non-strict upper bound "mu_tilde_floyd <= mu_floyd + lam_floyd" but the API guarantees a strict upper bound μ̃ < μ + λ; change the assertion to use a strict less-than comparison so it asserts mu_tilde_floyd < mu_floyd + lam_floyd instead.

Copy link
Contributor Author

Copilot AI commented Oct 9, 2025

@copilot The AI reviewer wrote: In tests/cycle_detection.rs around line 134, the assertion uses a non-strict upper bound "mu_tilde_floyd <= mu_floyd + lam_floyd" but the API guarantees a strict upper ...

Fixed in commit that will be pushed. Changed assertions on lines 98 and 134 from assert!(mu_tilde_floyd <= mu_floyd + lam_floyd) to assert!(mu_tilde_floyd < mu_floyd + lam_floyd) to match the strict inequality guarantee μ̃ < μ + λ in the API.

All tests still pass.

Copilot finished work on behalf of samueltardieu October 9, 2025 18:15
@samueltardieu samueltardieu force-pushed the copilot/add-partial-cycle-finding branch from 01f570b to dee8c30 Compare October 9, 2025 18:16
@samueltardieu
Copy link
Member

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Oct 9, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@samueltardieu
Copy link
Member

@copilot Factor out common parts in brent and brent_partial to avoid code repetitions. Do the same thing for floyd and floyd_partial

Copy link
Contributor Author

Copilot AI commented Oct 9, 2025

@copilot Factor out common parts in brent and brent_partial to avoid code repetitions. Do the same thing for floyd and floyd_partial

Done. Extracted common logic into helper functions:

  • floyd_find_cycle: shared by floyd and floyd_partial
  • brent_find_cycle: shared by brent and brent_partial

This eliminates all code duplication between the partial and full versions. All tests pass.

Copilot finished work on behalf of samueltardieu October 9, 2025 20:11
@samueltardieu samueltardieu force-pushed the copilot/add-partial-cycle-finding branch from 9fd83ff to 105384b Compare October 9, 2025 20:41
@samueltardieu
Copy link
Member

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Oct 9, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@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)
src/directed/cycle_detection.rs (1)

122-129: Consider demonstrating the documented guarantee more clearly.

The doctest assertion assert!(mu_tilde >= 1) tests a specific property of this sequence but doesn't directly demonstrate the documented guarantee μ ≤ μ̃. Since μ = 0 for this cycle (start is in cycle: 1 → 2 → 4 → 1), the assertion could use assert!(mu_tilde >= 0) to more clearly illustrate the general guarantee.

The current assertion is correct—mu_tilde >= 1 implies mu_tilde >= 0—but using the minimal bound would make the example more instructive.

Example adjustment:

-assert!(mu_tilde >= 1); // Upper bound on mu
+assert!(mu_tilde >= 0); // Upper bound satisfies mu <= mu_tilde (mu = 0 for this cycle)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dee8c30 and 105384b.

📒 Files selected for processing (4)
  • Cargo.toml (1 hunks)
  • src/directed/bfs.rs (1 hunks)
  • src/directed/cycle_detection.rs (2 hunks)
  • tests/cycle_detection.rs (1 hunks)
⏰ 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). (2)
  • GitHub Check: Test with minimal versions
  • GitHub Check: benchmarks
🔇 Additional comments (9)
src/directed/bfs.rs (1)

175-175: LGTM! Improved lint handling.

Replacing allow with expect follows best practices—expect will warn if the lint stops triggering, helping catch when suppressions become obsolete. This aligns with the new allow_attributes = "deny" lint in Cargo.toml.

Cargo.toml (1)

51-52: LGTM! Stricter lint enforcement.

Adding redundant_clone = "deny" and allow_attributes = "deny" tightens code quality checks. The allow_attributes setting drives the migration from #[allow(...)] to #[expect(...)] seen in other files, which is a best practice for maintainable lint suppressions.

tests/cycle_detection.rs (1)

13-136: LGTM! Comprehensive test coverage.

The test suite thoroughly validates the new partial cycle-finding APIs:

  • Basic functionality (floyd_partial_works, brent_partial_works): Correct cycle length, element membership, and μ̃ bounds.
  • Consistency (partial_functions_match_full_functions): Partial and full variants return identical λ and consistent μ/μ̃ values.
  • Edge cases:
    • test_longer_cycle: Pure cycle (μ = 0) with large λ = 100.
    • test_short_cycle_large_mu: Large transient (μ = 100) with short cycle (λ = 11).

All assertions correctly enforce the documented guarantees: μ ≤ μ̃ < μ + λ for Floyd (strict upper bound), and μ ≤ μ̃ for Brent (looser bound).

src/directed/cycle_detection.rs (6)

3-24: LGTM! Clean helper extraction.

floyd_find_cycle encapsulates the core Floyd cycle detection logic (tortoise-hare meeting + cycle length calculation), returning the tuple (lambda, meeting_element, tortoise_steps). This eliminates duplication between floyd and floyd_partial.


26-46: LGTM! Clean helper extraction.

brent_find_cycle encapsulates Brent's algorithm (power-of-2 stepping + cycle detection), returning (lambda, meeting_element, hare_steps). This eliminates duplication between brent and brent_partial.


48-85: LGTM! Correct μ = 0 edge case handling.

The floyd_partial implementation correctly:

  1. Delegates cycle finding to the helper (line 79).
  2. Handles the μ = 0 edge case (line 83): when tortoise == start, the start is already in the cycle, so mu_tilde = 0 ensures the strict upper bound μ̃ < μ + λ is satisfied (0 < 0 + λ).
  3. Otherwise, uses tortoise_steps as μ̃, which satisfies μ ≤ μ̃ < μ + λ.

The documentation and doctest example clearly explain the guarantee and performance benefit.


87-105: LGTM! Refactored to use helper.

The floyd function now delegates to floyd_find_cycle (line 98) and performs only the final μ-alignment phase (lines 100-103). This eliminates code duplication while preserving the exact same behavior for computing the minimal prefix length.


107-144: LGTM! Correct partial implementation for Brent.

The brent_partial implementation correctly:

  1. Delegates to brent_find_cycle (line 140).
  2. Uses hare_steps as μ̃ (line 142), satisfying μ ≤ μ̃.
  3. Documents the looser upper bound (line 113-115) inherent to Brent's power-of-2 stepping.

The doctest and documentation clearly set expectations about the weaker upper-bound guarantee compared to Floyd.


146-166: LGTM! Refactored to use helper.

The brent function now delegates to brent_find_cycle (line 157) and performs only the final μ-computation phase (lines 159-164). This eliminates code duplication while preserving the exact same behavior for computing the minimal prefix length.

@samueltardieu samueltardieu force-pushed the copilot/add-partial-cycle-finding branch 2 times, most recently from 5f575a2 to 45a7802 Compare October 9, 2025 21:02
@samueltardieu samueltardieu force-pushed the copilot/add-partial-cycle-finding branch from 45a7802 to 6580cdd Compare October 9, 2025 21:03
@samueltardieu
Copy link
Member

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Oct 9, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@samueltardieu samueltardieu added this pull request to the merge queue Nov 4, 2025
Merged via the queue into main with commit 299f425 Nov 4, 2025
14 checks passed
@samueltardieu samueltardieu deleted the copilot/add-partial-cycle-finding branch November 4, 2025 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RFC: Provide "partial" cycle‐finding (skip minimal μ computation)

2 participants