diff --git a/deque/deque.mbt b/deque/deque.mbt index 7973f9270..240eb87e7 100644 --- a/deque/deque.mbt +++ b/deque/deque.mbt @@ -1588,3 +1588,44 @@ pub impl[A : Compare] Compare for T[A] with compare(self, other) { } 0 } + +///| +/// Shuffle the deque in place using Knuth shuffle (Fisher-Yates algorithm) +/// +/// To use this function, you need to provide a rand function, which takes an integer as its upper bound +/// and returns an integer. +/// *rand n* is expected to return a uniformly distributed integer between 0 and n - 1 +/// +/// # Note +/// This function handles the circular buffer nature of the deque internally. +pub fn[A] T::shuffle_in_place(self : T[A], rand~ : (Int) -> Int) -> Unit { + let n = self.len + let buf_length = self.buf.length() + for i = n - 1; i > 0; i = i - 1 { + let j = rand(i + 1) + // Calculate circular buffer positions + let i_pos = (self.head + i) % buf_length + let j_pos = (self.head + j) % buf_length + // Swap elements + let tmp = self.buf[i_pos] + self.buf[i_pos] = self.buf[j_pos] + self.buf[j_pos] = tmp + } +} + +///| +/// Shuffle the deque using Knuth shuffle (Fisher-Yates algorithm) +/// +/// Returns a new shuffled deque without modifying the original deque. +/// +/// To use this function, you need to provide a rand function, which takes an integer as its upper bound +/// and returns an integer. +/// *rand n* is expected to return a uniformly distributed integer between 0 and n - 1 +pub fn[A] shuffle(self : T[A], rand~ : (Int) -> Int) -> T[A] { + // Create a copy of the original deque + let new_deque = self.copy() + // Shuffle the copy in place + new_deque.shuffle_in_place(rand~) + // Return the shuffled copy + new_deque +} diff --git a/deque/deque.mbti b/deque/deque.mbti index 34a158c84..e3bff93ed 100644 --- a/deque/deque.mbti +++ b/deque/deque.mbti @@ -58,6 +58,8 @@ fn[A] T::rev_iter(Self[A]) -> Iter[A] fn[A] T::rev_iter2(Self[A]) -> Iter2[Int, A] fn[A : Eq] T::search(Self[A], A) -> Int? fn[A] T::shrink_to_fit(Self[A]) -> Unit +fn[A] T::shuffle(Self[A], rand~ : (Int) -> Int) -> Self[A] +fn[A] T::shuffle_in_place(Self[A], rand~ : (Int) -> Int) -> Unit fn[A] T::to_array(Self[A]) -> Array[A] fn[A] T::truncate(Self[A], Int) -> Unit fn[A] T::unsafe_pop_back(Self[A]) -> Unit diff --git a/deque/deque_test.mbt b/deque/deque_test.mbt index d40f4fdb0..b2accc9ff 100644 --- a/deque/deque_test.mbt +++ b/deque/deque_test.mbt @@ -1164,3 +1164,28 @@ test "deque_compare" { inspect(dq3.compare(dq1), content="-1") inspect(dq1.compare(dq1), content="0") } + +///| +test "shuffle_in_place" { + let original = @deque.of([1, 2, 3, 4, 5, 6, 7]) + let dq = original.copy() + + // Better deterministic "random" function for testing + fn rand(upper : Int) -> Int { + (upper * 123 + 456) % (upper + 1) // More varied results + } + + // Shuffle in place + T::shuffle_in_place(dq, rand~) + + // Verify properties of shuffle: + assert_eq(dq.length(), original.length()) + assert_eq(dq.to_array().sort(), original.to_array().sort()) + assert_true(dq != original) + + // Test non-in-place version + let shuffled = T::shuffle(original, rand~) + assert_eq(shuffled.length(), original.length()) + assert_eq(shuffled.to_array().sort(), original.to_array().sort()) + assert_true(shuffled != original) +}