Skip to content

Commit 9fd83ff

Browse files
Refactor to eliminate code duplication between partial and full functions
Extract common cycle-finding logic into helper functions: - floyd_find_cycle: shared by floyd and floyd_partial - brent_find_cycle: shared by brent and brent_partial This eliminates code repetition while maintaining identical behavior. All tests pass. Co-authored-by: samueltardieu <44656+samueltardieu@users.noreply.github.com>
1 parent dee8c30 commit 9fd83ff

File tree

1 file changed

+57
-48
lines changed

1 file changed

+57
-48
lines changed

src/directed/cycle_detection.rs

Lines changed: 57 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,50 @@
11
//! Identify a cycle in an infinite sequence.
22
3+
/// Find the meeting point and lambda using Floyd's algorithm.
4+
/// Returns (`lambda`, `meeting_element`, `tortoise_steps`).
5+
fn floyd_find_cycle<T, FS>(start: &T, successor: &FS) -> (usize, T, usize)
6+
where
7+
T: Clone + PartialEq,
8+
FS: Fn(T) -> T,
9+
{
10+
let mut tortoise = successor(start.clone());
11+
let mut hare = successor(successor(start.clone()));
12+
let mut tortoise_steps = 1;
13+
while tortoise != hare {
14+
(tortoise, hare) = (successor(tortoise), successor(successor(hare)));
15+
tortoise_steps += 1;
16+
}
17+
// tortoise and hare met at position tortoise_steps
18+
let mut lam = 1;
19+
hare = successor(tortoise.clone());
20+
while tortoise != hare {
21+
(hare, lam) = (successor(hare), lam + 1);
22+
}
23+
(lam, tortoise, tortoise_steps)
24+
}
25+
26+
/// Find the cycle using Brent's algorithm.
27+
/// Returns (`lambda`, `meeting_element`, `hare_steps`).
28+
fn brent_find_cycle<T, FS>(start: &T, successor: &FS) -> (usize, T, usize)
29+
where
30+
T: Clone + PartialEq,
31+
FS: Fn(T) -> T,
32+
{
33+
let mut power = 1;
34+
let mut lam = 1;
35+
let mut tortoise = start.clone();
36+
let mut hare = successor(start.clone());
37+
let mut hare_steps = 1;
38+
while tortoise != hare {
39+
if power == lam {
40+
(tortoise, power, lam) = (hare.clone(), power * 2, 0);
41+
}
42+
(hare, lam) = (successor(hare), lam + 1);
43+
hare_steps += 1;
44+
}
45+
(lam, hare, hare_steps)
46+
}
47+
348
/// Identify a cycle in an infinite sequence using Floyd's algorithm (partial version).
449
/// Return the cycle size, an element in the cycle, and an upper bound on the index of
550
/// the first element.
@@ -31,19 +76,7 @@ where
3176
T: Clone + PartialEq,
3277
FS: Fn(T) -> T,
3378
{
34-
let mut tortoise = successor(start.clone());
35-
let mut hare = successor(successor(start.clone()));
36-
let mut tortoise_steps = 1;
37-
while tortoise != hare {
38-
(tortoise, hare) = (successor(tortoise), successor(successor(hare)));
39-
tortoise_steps += 1;
40-
}
41-
// tortoise and hare met at position tortoise_steps
42-
let mut lam = 1;
43-
hare = successor(tortoise.clone());
44-
while tortoise != hare {
45-
(hare, lam) = (successor(hare), lam + 1);
46-
}
79+
let (lam, tortoise, tortoise_steps) = floyd_find_cycle(&start, &successor);
4780
// Handle edge case where they meet at the start position (pure cycle, mu = 0)
4881
// In this case, tortoise_steps equals lam, and to satisfy mu_tilde < mu + lam,
4982
// we must return 0.
@@ -57,26 +90,20 @@ where
5790
/// # Warning
5891
///
5992
/// If no cycle exist, this function loops forever.
93+
#[allow(clippy::needless_pass_by_value)]
6094
pub fn floyd<T, FS>(start: T, successor: FS) -> (usize, T, usize)
6195
where
6296
T: Clone + PartialEq,
6397
FS: Fn(T) -> T,
6498
{
65-
let mut tortoise = successor(start.clone());
66-
let mut hare = successor(successor(start.clone()));
67-
while tortoise != hare {
68-
(tortoise, hare) = (successor(tortoise), successor(successor(hare)));
69-
}
99+
let (lam, mut tortoise, _tortoise_steps) = floyd_find_cycle(&start, &successor);
100+
// Find the exact mu
70101
let mut mu = 0;
71-
tortoise = start;
102+
let mut hare = tortoise.clone();
103+
tortoise = start.clone();
72104
while tortoise != hare {
73105
(tortoise, hare, mu) = (successor(tortoise), successor(hare), mu + 1);
74106
}
75-
let mut lam = 1;
76-
hare = successor(tortoise.clone());
77-
while tortoise != hare {
78-
(hare, lam) = (successor(hare), lam + 1);
79-
}
80107
(lam, tortoise, mu)
81108
}
82109

@@ -113,19 +140,7 @@ where
113140
T: Clone + PartialEq,
114141
FS: Fn(T) -> T,
115142
{
116-
let mut power = 1;
117-
let mut lam = 1;
118-
let mut tortoise = start.clone();
119-
let mut hare = successor(start.clone());
120-
let mut hare_steps = 1;
121-
while tortoise != hare {
122-
if power == lam {
123-
(tortoise, power, lam) = (hare.clone(), power * 2, 0);
124-
}
125-
(hare, lam) = (successor(hare), lam + 1);
126-
hare_steps += 1;
127-
}
128-
// At this point, hare has taken hare_steps steps and met tortoise.
143+
let (lam, hare, hare_steps) = brent_find_cycle(&start, &successor);
129144
// Use hare_steps as the upper bound, as it represents where we detected the cycle.
130145
let mu_tilde = hare_steps;
131146
(lam, hare, mu_tilde)
@@ -137,23 +152,17 @@ where
137152
/// # Warning
138153
///
139154
/// If no cycle exist, this function loops forever.
155+
#[allow(clippy::needless_pass_by_value)]
140156
pub fn brent<T, FS>(start: T, successor: FS) -> (usize, T, usize)
141157
where
142158
T: Clone + PartialEq,
143159
FS: Fn(T) -> T,
144160
{
145-
let mut power = 1;
146-
let mut lam = 1;
147-
let mut tortoise = start.clone();
148-
let mut hare = successor(start.clone());
149-
while tortoise != hare {
150-
if power == lam {
151-
(tortoise, power, lam) = (hare.clone(), power * 2, 0);
152-
}
153-
(hare, lam) = (successor(hare), lam + 1);
154-
}
161+
let (lam, _hare, _hare_steps) = brent_find_cycle(&start, &successor);
162+
// Find the exact mu
155163
let mut mu = 0;
156-
(tortoise, hare) = (start.clone(), (0..lam).fold(start, |x, _| successor(x)));
164+
let mut tortoise = start.clone();
165+
let mut hare = (0..lam).fold(start, |x, _| successor(x));
157166
while tortoise != hare {
158167
(tortoise, hare, mu) = (successor(tortoise), successor(hare), mu + 1);
159168
}

0 commit comments

Comments
 (0)