From f713c801ee00a0815df620ea18433a13e24559c4 Mon Sep 17 00:00:00 2001 From: Xvolks Date: Sun, 22 Feb 2026 12:38:13 +0100 Subject: [PATCH 1/4] Write a test that shows the issue --- src/lib.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 91ede27..c6a7751 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -693,4 +693,44 @@ mod tests { assert_eq!(a.into_iter().collect::>(), c); } } + + #[test] + fn reverse() { + let mut a = list![0, 1, 2, 3, 4]; + reverse_segment(&mut a, 0, 3); + assert_eq!(a.clone().into_iter().collect::>(), &[2, 1, 0, 3, 4]); + reverse_segment(&mut a, 3, 4); + assert_eq!(a.clone().into_iter().collect::>(), &[4, 3, 0, 1, 2]); + reverse_segment(&mut a, 1, 1); + assert_eq!(a.clone().into_iter().collect::>(), &[4, 3, 0, 1, 2]); // No change + reverse_segment(&mut a, 1, 5); + assert_eq!(a.clone().into_iter().collect::>(), &[3, 4, 2, 1, 0]); + } + + fn reverse_segment(list: &mut CircularList, start: usize, count: usize) { + if count <= 1 { + return; + } + + let mut cur = list.double_cursor().unwrap(); + // move to start + for _ in 0..start { + cur.move_next_a(); + cur.move_next_b(); + } + + // move to end of segment (start + count - 1) + for _ in 0..(count - 1) { + cur.move_next_b(); + } + + // number of swaps is count / 2 + for _ in 0..(count / 2) { + // swap the payloads, no extra temp binding: + cur.swap(); + cur.move_next_a(); + cur.move_prev_b(); + } + } + } From 9d98a49ed9ca7c3030001c9b54250db5ec80e84d Mon Sep 17 00:00:00 2001 From: Xvolks Date: Sun, 22 Feb 2026 12:39:14 +0100 Subject: [PATCH 2/4] Fix the swap if idx_a is greater than idx_b --- src/head/cursor.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/head/cursor.rs b/src/head/cursor.rs index 8433b40..64dae79 100644 --- a/src/head/cursor.rs +++ b/src/head/cursor.rs @@ -270,6 +270,11 @@ impl<'life, T> DoubleCursor<'life, T> { /// Swaps the list nodes pointed by the 'a' and 'b' cursors. It is a `O(1)` operation. pub fn swap(&mut self) { + // Makes swap backwards work? + if self.idx_a > self.idx_b { + self.swap_cursors(); + } + unsafe { // SAFETY: Invariants (3) and (5) assert that `self.a` and `self.b` are part of // a valid circular linked list. From 7d414568bd8c661450e39b3418fef31419b1c541 Mon Sep 17 00:00:00 2001 From: Xvolks Date: Sun, 22 Feb 2026 13:26:13 +0100 Subject: [PATCH 3/4] Revert "Fix the swap if idx_a is greater than idx_b" This reverts commit 9d98a49ed9ca7c3030001c9b54250db5ec80e84d. --- src/head/cursor.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/head/cursor.rs b/src/head/cursor.rs index 64dae79..8433b40 100644 --- a/src/head/cursor.rs +++ b/src/head/cursor.rs @@ -270,11 +270,6 @@ impl<'life, T> DoubleCursor<'life, T> { /// Swaps the list nodes pointed by the 'a' and 'b' cursors. It is a `O(1)` operation. pub fn swap(&mut self) { - // Makes swap backwards work? - if self.idx_a > self.idx_b { - self.swap_cursors(); - } - unsafe { // SAFETY: Invariants (3) and (5) assert that `self.a` and `self.b` are part of // a valid circular linked list. From a2c75f4511712ee1d582e5e571f4f8986a57b58c Mon Sep 17 00:00:00 2001 From: Xvolks Date: Sun, 22 Feb 2026 13:33:59 +0100 Subject: [PATCH 4/4] Add reverse_segment function to CircularList --- src/lib.rs | 91 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 32 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c6a7751..d078fe4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ //! has to preserve some invariants (e.g. pointers must be valid). To achieve this, the source code //! is commented with careful justifications to *prove* correctness (at least it is a try). -#![no_std] +// #![no_std] extern crate alloc; mod head; @@ -413,6 +413,50 @@ impl CircularList { } } + /// Reverse a part of the list from `start` for `count` elements. + /// + /// # Example + /// ``` + /// # use cdll::CircularList; + /// let mut saying: CircularList<_> = "reverting is part of coding".chars().collect(); + /// saying.reverse_segment(13, 4); + /// assert_eq!("reverting is trap of coding", saying.into_iter().collect::()) + /// ``` + pub fn reverse_segment(&mut self, start: usize, count: usize) -> Result<(), &str> { + if count <= 1 { + return Ok(()); + } + + let mut cur = match self.double_cursor() { + Some(c) => c, + None => { + return Err("Can't get a double cursor, check your list"); + }, + }; + // move to start + for _ in 0..start { + cur.move_next_a(); + cur.move_next_b(); + } + + // move to end of segment (start + count - 1) + for _ in 0..(count - 1) { + cur.move_next_b(); + } + + // number of swaps is count / 2 + for _ in 0..(count / 2) { + // swap the payloads, no extra temp binding: + cur.swap(); + // after a swap, a and b are pointing on the same node + // so to continue, we have to swap back to where we were. + cur.swap_cursors(); + cur.move_next_a(); + cur.move_prev_b(); + } + Ok(()) + } + /// Returns an infinite iterator over the list except if it is empty, in which case the /// returned iterator is also empty. pub fn iter_forever(&self) -> impl Iterator { @@ -530,7 +574,7 @@ impl IntoIterator for CircularList { mod tests { use { super::*, - alloc::{vec, vec::Vec}, + alloc::vec::{self, Vec}, std::fmt::Debug, }; #[test] @@ -694,43 +738,26 @@ mod tests { } } + + #[test] + fn manual_reverse() { + let mut a = list![0, 1, 2, 3, 4, 5, 6]; + let _ = a.reverse_segment(1, 5); + assert_eq!(a.clone().into_iter().collect::>(), &[0, 5, 4, 3, 2, 1, 6]); + + } + #[test] fn reverse() { let mut a = list![0, 1, 2, 3, 4]; - reverse_segment(&mut a, 0, 3); + let _ = a.reverse_segment(0, 3); assert_eq!(a.clone().into_iter().collect::>(), &[2, 1, 0, 3, 4]); - reverse_segment(&mut a, 3, 4); + let _ = a.reverse_segment(3, 4); assert_eq!(a.clone().into_iter().collect::>(), &[4, 3, 0, 1, 2]); - reverse_segment(&mut a, 1, 1); + let _ = a.reverse_segment(1, 1); assert_eq!(a.clone().into_iter().collect::>(), &[4, 3, 0, 1, 2]); // No change - reverse_segment(&mut a, 1, 5); + let _ = a.reverse_segment(1, 5); assert_eq!(a.clone().into_iter().collect::>(), &[3, 4, 2, 1, 0]); } - fn reverse_segment(list: &mut CircularList, start: usize, count: usize) { - if count <= 1 { - return; - } - - let mut cur = list.double_cursor().unwrap(); - // move to start - for _ in 0..start { - cur.move_next_a(); - cur.move_next_b(); - } - - // move to end of segment (start + count - 1) - for _ in 0..(count - 1) { - cur.move_next_b(); - } - - // number of swaps is count / 2 - for _ in 0..(count / 2) { - // swap the payloads, no extra temp binding: - cur.swap(); - cur.move_next_a(); - cur.move_prev_b(); - } - } - }