Skip to content

Commit 0acbef9

Browse files
Address code review feedback
- Replace #[allow] with #[expect] for clippy lints - Fix floyd_partial to handle pure cycle case (mu=0) correctly - Remove hardcoded upper bound checks for brent_partial in tests - Update floyd_partial doctest to reflect correct behavior Co-authored-by: samueltardieu <44656+samueltardieu@users.noreply.github.com>
1 parent b01d71f commit 0acbef9

File tree

2 files changed

+11
-13
lines changed

2 files changed

+11
-13
lines changed

src/directed/cycle_detection.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@
1919
///
2020
/// let (lam, _elem, mu_tilde) = floyd_partial(1, |x| (x * 2) % 7);
2121
/// assert_eq!(lam, 3); // Cycle length
22-
/// assert!(mu_tilde >= 1); // Upper bound on mu
22+
/// assert!(mu_tilde == 0); // Upper bound on mu (start is in cycle, so mu = 0)
2323
/// ```
2424
///
2525
/// # Warning
2626
///
2727
/// If no cycle exist, this function loops forever.
28-
#[allow(clippy::needless_pass_by_value)]
28+
#[expect(clippy::needless_pass_by_value)]
2929
pub fn floyd_partial<T, FS>(start: T, successor: FS) -> (usize, T, usize)
3030
where
3131
T: Clone + PartialEq,
@@ -39,14 +39,19 @@ where
3939
tortoise_steps += 1;
4040
}
4141
// tortoise and hare met at position tortoise_steps
42-
// By Floyd's algorithm analysis: mu <= tortoise_steps < mu + lam
4342
let mut lam = 1;
4443
hare = successor(tortoise.clone());
4544
while tortoise != hare {
4645
(hare, lam) = (successor(hare), lam + 1);
4746
}
48-
// tortoise_steps is a valid upper bound satisfying mu <= mu_tilde < mu + lam
49-
let mu_tilde = tortoise_steps;
47+
// Handle edge case where they meet at the start position (pure cycle, mu = 0)
48+
// In this case, tortoise_steps equals lam, and to satisfy mu_tilde < mu + lam,
49+
// we must return 0.
50+
let mu_tilde = if tortoise == start {
51+
0
52+
} else {
53+
tortoise_steps
54+
};
5055
(lam, tortoise, mu_tilde)
5156
}
5257

@@ -106,7 +111,7 @@ where
106111
/// # Warning
107112
///
108113
/// If no cycle exist, this function loops forever.
109-
#[allow(clippy::needless_pass_by_value)]
114+
#[expect(clippy::needless_pass_by_value)]
110115
pub fn brent_partial<T, FS>(start: T, successor: FS) -> (usize, T, usize)
111116
where
112117
T: Clone + PartialEq,

tests/cycle_detection.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ fn brent_partial_works() {
3232
assert!([4, 6, 8].contains(&elem));
3333
// Check that mu_tilde is an upper bound: mu <= mu_tilde
3434
// We know mu = 2 from the full algorithm
35-
// Brent's partial may have a looser bound than mu + lam
3635
assert!(2 <= mu_tilde);
37-
assert!(mu_tilde <= 2 + 2 * 3);
3836
}
3937

4038
#[test]
@@ -66,8 +64,6 @@ fn partial_functions_match_full_functions() {
6664
assert!(mu_floyd <= mu_tilde_floyd);
6765
assert!(mu_tilde_floyd < mu_floyd + lam_floyd);
6866
assert!(mu_brent <= mu_tilde_brent);
69-
// Brent's partial may have a looser bound due to power-of-2 stepping
70-
assert!(mu_tilde_brent <= mu_brent + 2 * lam_brent);
7167
}
7268

7369
#[test]
@@ -101,7 +97,6 @@ fn test_longer_cycle() {
10197
assert!(mu_floyd <= mu_tilde_floyd);
10298
assert!(mu_tilde_floyd <= mu_floyd + lam_floyd);
10399
assert!(mu_brent <= mu_tilde_brent);
104-
assert!(mu_tilde_brent <= mu_brent + 3 * lam_brent);
105100
}
106101

107102
#[test]
@@ -138,6 +133,4 @@ fn test_short_cycle_large_mu() {
138133
assert!(mu_floyd <= mu_tilde_floyd);
139134
assert!(mu_tilde_floyd <= mu_floyd + lam_floyd);
140135
assert!(mu_brent <= mu_tilde_brent);
141-
// Brent's partial may have a looser bound
142-
assert!(mu_tilde_brent <= mu_brent + 4 * lam_brent);
143136
}

0 commit comments

Comments
 (0)