diff --git a/math/core/sources/vector.move b/math/core/sources/vector.move new file mode 100644 index 0000000..ac18b9e --- /dev/null +++ b/math/core/sources/vector.move @@ -0,0 +1,137 @@ +module openzeppelin_math::vector; + +/// Sort an unsigned integer vector in-place using the quicksort algorithm. +/// +/// NOTE: This is an unstable in-place sort. +/// +/// This macro implements the iterative quicksort algorithm with the Lomuto partition scheme, +/// which efficiently sorts vectors in-place with `O(n log n)` average-case time complexity and +/// `O(n²)` worst-case complexity, when the smallest or largest element is consistently +/// selected as the pivot. +/// +/// The macro uses an explicit stack to avoid recursion limitations for `Move` macros, making +/// it suitable for arbitrarily large vectors. +/// +/// #### Generics +/// - `$Int`: Any unsigned integer type (`u8`, `u16`, `u32`, `u64`, `u128`, or `u256`). +/// +/// #### Parameters +/// - `$vec`: A mutable reference to the vector to be sorted in-place. +/// +/// #### Example +/// ```move +/// let mut vec = vector[3u64, 1, 4, 1, 5, 9, 2, 6]; +/// macros::quick_sort!(&mut vec); +/// // vec is now [1, 1, 2, 3, 4, 5, 6, 9] +/// ``` +public macro fun quick_sort<$Int>($vec: &mut vector<$Int>) { + quick_sort_by!($vec, |x: &$Int, y: &$Int| *x <= *y) +} + +/// Sort a vector in-place using the quicksort algorithm with a custom comparison function. +/// +/// NOTE: This is an unstable in-place sort. +/// +/// This macro implements the iterative quicksort algorithm with the Lomuto partition scheme, +/// which efficiently sorts vectors in-place with `O(n log n)` average-case time complexity and +/// `O(n²)` worst-case complexity, when the smallest or largest element is consistently +/// selected as the pivot. +/// +/// The macro uses an explicit stack to avoid recursion limitations for `Move` macros, making +/// it suitable for arbitrarily large vectors. +/// +/// #### Generics +/// - `$Int`: Any type that can be compared using the provided comparison function. +/// +/// #### Parameters +/// - `$vec`: A mutable reference to the vector to be sorted in-place. +/// - `$le`: A comparison function that takes two references and returns `true` if the first +/// element should be ordered before or equal to the second element. For ascending order, +/// this should implement "less than or equal to" semantics. +/// +/// #### Example +/// ```move +/// // Sort in ascending order +/// let mut vec = vector[3u64, 1, 4, 1, 5, 9, 2, 6]; +/// vector::quick_sort_by!(&mut vec, |x: &u64, y: &u64| *x <= *y); +/// // vec is now [1, 1, 2, 3, 4, 5, 6, 9] +/// +/// // Sort in descending order +/// let mut vec = vector[3u64, 1, 4, 1, 5, 9, 2, 6]; +/// vector::quick_sort_by!(&mut vec, |x: &u64, y: &u64| *x >= *y); +/// // vec is now [9, 6, 5, 4, 3, 2, 1, 1] +/// ``` +public macro fun quick_sort_by<$T>($vec: &mut vector<$T>, $le: |&$T, &$T| -> bool) { + let vec = $vec; + let len = vec.length(); + + // Iterative implementation based on stack data structure (vector). + let mut stack_start = vector[0]; + let mut stack_end = vector[len]; + + while (!stack_start.is_empty()) { + let start = stack_start.pop_back(); + let end = stack_end.pop_back(); + + // Ensure we have at least two elements in vector. + if (start + 1 >= end) { + continue + }; + + // Pivot index is the last element. + let pivot_index = end - 1; + + // Choose median-of-three (start, mid, pivot_index) as a pivot + // and place it on the last position. + let mid = (start + end) / 2; + if ($le(&vec[mid], &vec[start])) { + vec.swap(start, mid); + }; + if ($le(&vec[pivot_index], &vec[start])) { + vec.swap(start, pivot_index) + }; + if ($le(&vec[mid], &vec[pivot_index])) { + vec.swap(mid, pivot_index); + }; + + // Partition vector around pivot_index. + let mut i = start; + let mut j = start; + while (j < pivot_index) { + // If second index `j` is smaller (or equal) than pivot, + if ($le(&vec[j], &vec[pivot_index])) { + // swap it with element from the first partition. + vec.swap(i, j); + i = i + 1; + }; + j = j + 1; + }; + + // Swap pivot to the partition index. Swapped element will be greater, + // than a pivot since it was already processed. + // Index `i` is now partition index. + vec.swap(i, pivot_index); + + // Push partitions: larger first, smaller second. + // Since we use pop_back, smaller will be processed first. + // Stack size will be no longer than O(log(n)). + let left_size = i - start; + let right_size = end - (i + 1); + + if (left_size <= right_size) { + // Left ≤ right: push right (larger) first, left (smaller) second. + stack_start.push_back(i + 1); + stack_end.push_back(end); + + stack_start.push_back(start); + stack_end.push_back(i); + } else { + // Left > right: push left (larger) first, right (smaller) second. + stack_start.push_back(start); + stack_end.push_back(i); + + stack_start.push_back(i + 1); + stack_end.push_back(end); + }; + }; +} diff --git a/math/core/tests/macros/binary_search.move b/math/core/tests/macros/binary_search.move new file mode 100644 index 0000000..741114a --- /dev/null +++ b/math/core/tests/macros/binary_search.move @@ -0,0 +1,239 @@ +#[test_only] +module openzeppelin_math::binary_search; + +use openzeppelin_math::macros; +use std::unit_test::assert_eq; + +#[test] +fun binary_search_finds_element_at_beginning() { + let haystack = vector[1u64, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(macros::binary_search!(haystack, 1u64), true); +} + +#[test] +fun binary_search_finds_element_at_end() { + let haystack = vector[1u64, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(macros::binary_search!(haystack, 10u64), true); +} + +#[test] +fun binary_search_finds_element_in_middle() { + let haystack = vector[1u64, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(macros::binary_search!(haystack, 5u64), true); + assert_eq!(macros::binary_search!(haystack, 6u64), true); +} + +#[test] +fun binary_search_finds_all_elements() { + let haystack = vector[10u32, 20, 30, 40, 50]; + assert_eq!(macros::binary_search!(haystack, 10u32), true); + assert_eq!(macros::binary_search!(haystack, 20u32), true); + assert_eq!(macros::binary_search!(haystack, 30u32), true); + assert_eq!(macros::binary_search!(haystack, 40u32), true); + assert_eq!(macros::binary_search!(haystack, 50u32), true); +} + +#[test] +fun binary_search_returns_false_for_missing_element() { + let haystack = vector[1u64, 3, 5, 7, 9]; + assert_eq!(macros::binary_search!(haystack, 2u64), false); + assert_eq!(macros::binary_search!(haystack, 4u64), false); + assert_eq!(macros::binary_search!(haystack, 6u64), false); + assert_eq!(macros::binary_search!(haystack, 8u64), false); +} + +#[test] +fun binary_search_returns_false_for_value_below_range() { + let haystack = vector[10u64, 20, 30, 40, 50]; + assert_eq!(macros::binary_search!(haystack, 0u64), false); + assert_eq!(macros::binary_search!(haystack, 5u64), false); + assert_eq!(macros::binary_search!(haystack, 9u64), false); +} + +#[test] +fun binary_search_returns_false_for_value_above_range() { + let haystack = vector[10u64, 20, 30, 40, 50]; + assert_eq!(macros::binary_search!(haystack, 51u64), false); + assert_eq!(macros::binary_search!(haystack, 60u64), false); + assert_eq!(macros::binary_search!(haystack, 100u64), false); +} + +#[test] +fun binary_search_handles_empty_vector() { + let haystack = vector[]; + assert_eq!(macros::binary_search!(haystack, 0u64), false); + assert_eq!(macros::binary_search!(haystack, 1u64), false); +} + +#[test] +fun binary_search_handles_single_element_found() { + let haystack = vector[42u64]; + assert_eq!(macros::binary_search!(haystack, 42u64), true); +} + +#[test] +fun binary_search_handles_single_element_not_found() { + let haystack = vector[42u64]; + assert_eq!(macros::binary_search!(haystack, 41u64), false); + assert_eq!(macros::binary_search!(haystack, 43u64), false); +} + +#[test] +fun binary_search_handles_two_elements() { + let haystack = vector[10u64, 20]; + assert_eq!(macros::binary_search!(haystack, 10u64), true); + assert_eq!(macros::binary_search!(haystack, 20u64), true); + assert_eq!(macros::binary_search!(haystack, 5u64), false); + assert_eq!(macros::binary_search!(haystack, 15u64), false); + assert_eq!(macros::binary_search!(haystack, 25u64), false); +} + +#[test] +fun binary_search_handles_duplicates() { + let haystack = vector[1u64, 2, 2, 2, 3]; + assert_eq!(macros::binary_search!(haystack, 2u64), true); + assert_eq!(macros::binary_search!(haystack, 1u64), true); + assert_eq!(macros::binary_search!(haystack, 3u64), true); +} + +#[test] +fun binary_search_works_with_u8() { + let haystack = vector[1u8, 10, 20, 30, 40, 50, 100, 200, 255]; + assert_eq!(macros::binary_search!(haystack, 1u8), true); + assert_eq!(macros::binary_search!(haystack, 30u8), true); + assert_eq!(macros::binary_search!(haystack, 255u8), true); + assert_eq!(macros::binary_search!(haystack, 25u8), false); +} + +#[test] +fun binary_search_works_with_u16() { + let haystack = vector[100u16, 1000, 10000, 50000, 65535]; + assert_eq!(macros::binary_search!(haystack, 100u16), true); + assert_eq!(macros::binary_search!(haystack, 10000u16), true); + assert_eq!(macros::binary_search!(haystack, 65535u16), true); + assert_eq!(macros::binary_search!(haystack, 500u16), false); +} + +#[test] +fun binary_search_works_with_u32() { + let haystack = vector[1u32, 1000, 1000000, 1000000000]; + assert_eq!(macros::binary_search!(haystack, 1u32), true); + assert_eq!(macros::binary_search!(haystack, 1000000u32), true); + assert_eq!(macros::binary_search!(haystack, 999999u32), false); +} + +#[test] +fun binary_search_works_with_u128() { + let haystack = vector[ + 1u128, + 1000, + 1000000, + 1000000000, + 1000000000000, + 340282366920938463463374607431768211455, // max u128 + ]; + assert_eq!(macros::binary_search!(haystack, 1u128), true); + assert_eq!(macros::binary_search!(haystack, 1000000000000u128), true); + assert_eq!(macros::binary_search!(haystack, 340282366920938463463374607431768211455u128), true); + assert_eq!(macros::binary_search!(haystack, 999u128), false); +} + +#[test] +fun binary_search_works_with_u256() { + let haystack = vector[ + 1u256, + 1000, + 1000000, + 1000000000, + 1000000000000000000, + std::u256::max_value!(), + ]; + assert_eq!(macros::binary_search!(haystack, 1u256), true); + assert_eq!(macros::binary_search!(haystack, 1000000000u256), true); + assert_eq!(macros::binary_search!(haystack, std::u256::max_value!()), true); + assert_eq!(macros::binary_search!(haystack, 999u256), false); +} + +#[test] +fun binary_search_handles_large_sorted_vector() { + // Test with a larger vector to ensure binary search efficiency + let haystack = vector[ + 1u64, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + ]; + assert_eq!(macros::binary_search!(haystack, 1u64), true); + assert_eq!(macros::binary_search!(haystack, 15u64), true); + assert_eq!(macros::binary_search!(haystack, 30u64), true); + assert_eq!(macros::binary_search!(haystack, 16u64), true); + assert_eq!(macros::binary_search!(haystack, 0u64), false); + assert_eq!(macros::binary_search!(haystack, 31u64), false); +} + +#[test] +fun binary_search_handles_powers_of_ten() { + // Test with actual powers of 10 like in is_power_of_ten + let powers = vector[ + 1u64, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000, + ]; + + // All powers should be found + assert_eq!(macros::binary_search!(powers, 1u64), true); + assert_eq!(macros::binary_search!(powers, 10u64), true); + assert_eq!(macros::binary_search!(powers, 1000u64), true); + assert_eq!(macros::binary_search!(powers, 1000000u64), true); + assert_eq!(macros::binary_search!(powers, 10000000000000000000u64), true); + + // Non-powers should not be found + assert_eq!(macros::binary_search!(powers, 2u64), false); + assert_eq!(macros::binary_search!(powers, 11u64), false); + assert_eq!(macros::binary_search!(powers, 999u64), false); + assert_eq!(macros::binary_search!(powers, 1001u64), false); + assert_eq!(macros::binary_search!(powers, 9999u64), false); +} diff --git a/math/core/tests/macros_tests.move b/math/core/tests/macros/math_ops.move similarity index 85% rename from math/core/tests/macros_tests.move rename to math/core/tests/macros/math_ops.move index b3ab05b..ea69eeb 100644 --- a/math/core/tests/macros_tests.move +++ b/math/core/tests/macros/math_ops.move @@ -1,5 +1,5 @@ #[test_only] -module openzeppelin_math::macros_tests; +module openzeppelin_math::math_ops; use openzeppelin_math::macros; use openzeppelin_math::rounding; @@ -1170,239 +1170,3 @@ fun sqrt_works_with_different_widths() { assert_eq!(macros::sqrt!(1000000u128, rounding::down()), 1000u128); assert_eq!(macros::sqrt!(10000000u256, rounding::down()), 3162u256); } - -// === binary_search === - -#[test] -fun binary_search_finds_element_at_beginning() { - let haystack = vector[1u64, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(macros::binary_search!(haystack, 1u64), true); -} - -#[test] -fun binary_search_finds_element_at_end() { - let haystack = vector[1u64, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(macros::binary_search!(haystack, 10u64), true); -} - -#[test] -fun binary_search_finds_element_in_middle() { - let haystack = vector[1u64, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(macros::binary_search!(haystack, 5u64), true); - assert_eq!(macros::binary_search!(haystack, 6u64), true); -} - -#[test] -fun binary_search_finds_all_elements() { - let haystack = vector[10u32, 20, 30, 40, 50]; - assert_eq!(macros::binary_search!(haystack, 10u32), true); - assert_eq!(macros::binary_search!(haystack, 20u32), true); - assert_eq!(macros::binary_search!(haystack, 30u32), true); - assert_eq!(macros::binary_search!(haystack, 40u32), true); - assert_eq!(macros::binary_search!(haystack, 50u32), true); -} - -#[test] -fun binary_search_returns_false_for_missing_element() { - let haystack = vector[1u64, 3, 5, 7, 9]; - assert_eq!(macros::binary_search!(haystack, 2u64), false); - assert_eq!(macros::binary_search!(haystack, 4u64), false); - assert_eq!(macros::binary_search!(haystack, 6u64), false); - assert_eq!(macros::binary_search!(haystack, 8u64), false); -} - -#[test] -fun binary_search_returns_false_for_value_below_range() { - let haystack = vector[10u64, 20, 30, 40, 50]; - assert_eq!(macros::binary_search!(haystack, 0u64), false); - assert_eq!(macros::binary_search!(haystack, 5u64), false); - assert_eq!(macros::binary_search!(haystack, 9u64), false); -} - -#[test] -fun binary_search_returns_false_for_value_above_range() { - let haystack = vector[10u64, 20, 30, 40, 50]; - assert_eq!(macros::binary_search!(haystack, 51u64), false); - assert_eq!(macros::binary_search!(haystack, 60u64), false); - assert_eq!(macros::binary_search!(haystack, 100u64), false); -} - -#[test] -fun binary_search_handles_empty_vector() { - let haystack = vector[]; - assert_eq!(macros::binary_search!(haystack, 0u64), false); - assert_eq!(macros::binary_search!(haystack, 1u64), false); -} - -#[test] -fun binary_search_handles_single_element_found() { - let haystack = vector[42u64]; - assert_eq!(macros::binary_search!(haystack, 42u64), true); -} - -#[test] -fun binary_search_handles_single_element_not_found() { - let haystack = vector[42u64]; - assert_eq!(macros::binary_search!(haystack, 41u64), false); - assert_eq!(macros::binary_search!(haystack, 43u64), false); -} - -#[test] -fun binary_search_handles_two_elements() { - let haystack = vector[10u64, 20]; - assert_eq!(macros::binary_search!(haystack, 10u64), true); - assert_eq!(macros::binary_search!(haystack, 20u64), true); - assert_eq!(macros::binary_search!(haystack, 5u64), false); - assert_eq!(macros::binary_search!(haystack, 15u64), false); - assert_eq!(macros::binary_search!(haystack, 25u64), false); -} - -#[test] -fun binary_search_handles_duplicates() { - let haystack = vector[1u64, 2, 2, 2, 3]; - assert_eq!(macros::binary_search!(haystack, 2u64), true); - assert_eq!(macros::binary_search!(haystack, 1u64), true); - assert_eq!(macros::binary_search!(haystack, 3u64), true); -} - -#[test] -fun binary_search_works_with_u8() { - let haystack = vector[1u8, 10, 20, 30, 40, 50, 100, 200, 255]; - assert_eq!(macros::binary_search!(haystack, 1u8), true); - assert_eq!(macros::binary_search!(haystack, 30u8), true); - assert_eq!(macros::binary_search!(haystack, 255u8), true); - assert_eq!(macros::binary_search!(haystack, 25u8), false); -} - -#[test] -fun binary_search_works_with_u16() { - let haystack = vector[100u16, 1000, 10000, 50000, 65535]; - assert_eq!(macros::binary_search!(haystack, 100u16), true); - assert_eq!(macros::binary_search!(haystack, 10000u16), true); - assert_eq!(macros::binary_search!(haystack, 65535u16), true); - assert_eq!(macros::binary_search!(haystack, 500u16), false); -} - -#[test] -fun binary_search_works_with_u32() { - let haystack = vector[1u32, 1000, 1000000, 1000000000]; - assert_eq!(macros::binary_search!(haystack, 1u32), true); - assert_eq!(macros::binary_search!(haystack, 1000000u32), true); - assert_eq!(macros::binary_search!(haystack, 999999u32), false); -} - -#[test] -fun binary_search_works_with_u128() { - let haystack = vector[ - 1u128, - 1000, - 1000000, - 1000000000, - 1000000000000, - 340282366920938463463374607431768211455, // max u128 - ]; - assert_eq!(macros::binary_search!(haystack, 1u128), true); - assert_eq!(macros::binary_search!(haystack, 1000000000000u128), true); - assert_eq!(macros::binary_search!(haystack, 340282366920938463463374607431768211455u128), true); - assert_eq!(macros::binary_search!(haystack, 999u128), false); -} - -#[test] -fun binary_search_works_with_u256() { - let haystack = vector[ - 1u256, - 1000, - 1000000, - 1000000000, - 1000000000000000000, - std::u256::max_value!(), - ]; - assert_eq!(macros::binary_search!(haystack, 1u256), true); - assert_eq!(macros::binary_search!(haystack, 1000000000u256), true); - assert_eq!(macros::binary_search!(haystack, std::u256::max_value!()), true); - assert_eq!(macros::binary_search!(haystack, 999u256), false); -} - -#[test] -fun binary_search_handles_large_sorted_vector() { - // Test with a larger vector to ensure binary search efficiency - let haystack = vector[ - 1u64, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - ]; - assert_eq!(macros::binary_search!(haystack, 1u64), true); - assert_eq!(macros::binary_search!(haystack, 15u64), true); - assert_eq!(macros::binary_search!(haystack, 30u64), true); - assert_eq!(macros::binary_search!(haystack, 16u64), true); - assert_eq!(macros::binary_search!(haystack, 0u64), false); - assert_eq!(macros::binary_search!(haystack, 31u64), false); -} - -#[test] -fun binary_search_handles_powers_of_ten() { - // Test with actual powers of 10 like in is_power_of_ten - let powers = vector[ - 1u64, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - 10000000000, - 100000000000, - 1000000000000, - 10000000000000, - 100000000000000, - 1000000000000000, - 10000000000000000, - 100000000000000000, - 1000000000000000000, - 10000000000000000000, - ]; - - // All powers should be found - assert_eq!(macros::binary_search!(powers, 1u64), true); - assert_eq!(macros::binary_search!(powers, 10u64), true); - assert_eq!(macros::binary_search!(powers, 1000u64), true); - assert_eq!(macros::binary_search!(powers, 1000000u64), true); - assert_eq!(macros::binary_search!(powers, 10000000000000000000u64), true); - - // Non-powers should not be found - assert_eq!(macros::binary_search!(powers, 2u64), false); - assert_eq!(macros::binary_search!(powers, 11u64), false); - assert_eq!(macros::binary_search!(powers, 999u64), false); - assert_eq!(macros::binary_search!(powers, 1001u64), false); - assert_eq!(macros::binary_search!(powers, 9999u64), false); -} diff --git a/math/core/tests/macros/quick_sort.move b/math/core/tests/macros/quick_sort.move new file mode 100644 index 0000000..c31bbd3 --- /dev/null +++ b/math/core/tests/macros/quick_sort.move @@ -0,0 +1,655 @@ +#[test_only] +module openzeppelin_math::quick_sort; + +use openzeppelin_math::vector; +use std::unit_test::assert_eq; + +// === quick_sort === + +#[test] +fun quick_sort_empty_vector() { + // Sorting an empty vector should remain empty + let mut vec = vector[]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[]); +} + +#[test] +fun quick_sort_single_element() { + // A vector with a single element should remain unchanged + let mut vec = vector[42u64]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[42u64]); +} + +#[test] +fun quick_sort_two_elements_ascending() { + // Two elements already in order should remain in order + let mut vec = vector[1u64, 2]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 2]); +} + +#[test] +fun quick_sort_two_elements_descending() { + // Two elements in reverse order should be sorted + let mut vec = vector[2u64, 1]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 2]); +} + +#[test] +fun quick_sort_three_elements() { + // Three elements in random order + let mut vec = vector[3u64, 1, 2]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 2, 3]); +} + +#[test] +fun quick_sort_already_sorted() { + // A vector that is already sorted should remain unchanged + let mut vec = vector[1u32, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u32, 2, 3, 4, 5, 6, 7, 8, 9, 10]); +} + +#[test] +fun quick_sort_reverse_sorted() { + // A vector sorted in reverse order should be fully sorted + let mut vec = vector[10u32, 9, 8, 7, 6, 5, 4, 3, 2, 1]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u32, 2, 3, 4, 5, 6, 7, 8, 9, 10]); +} + +#[test] +fun quick_sort_random_small_vector() { + // Small random vector + let mut vec = vector[5u64, 2, 8, 1, 9, 3, 7, 4, 6]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 2, 3, 4, 5, 6, 7, 8, 9]); +} + +#[test] +fun quick_sort_with_duplicates() { + // Vector with duplicate values + let mut vec = vector[5u32, 2, 5, 1, 5, 2, 3, 5, 1]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u32, 1, 2, 2, 3, 5, 5, 5, 5]); +} + +#[test] +fun quick_sort_all_same_values() { + // Vector where all elements are the same + let mut vec = vector[7u64, 7, 7, 7, 7, 7, 7]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[7u64, 7, 7, 7, 7, 7, 7]); +} + +#[test] +fun quick_sort_two_distinct_values() { + // Vector with only two distinct values + let mut vec = vector[2u32, 1, 2, 1, 2, 1, 1, 2]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u32, 1, 1, 1, 2, 2, 2, 2]); +} + +#[test] +fun quick_sort_medium_vector() { + // Medium-sized vector with random values + let mut vec = vector[45u64, 23, 87, 12, 56, 34, 78, 90, 11, 33, 22, 67, 88, 15, 42]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[11u64, 12, 15, 22, 23, 33, 34, 42, 45, 56, 67, 78, 87, 88, 90]); +} + +#[test] +fun quick_sort_large_vector() { + // Larger vector with random values + let mut vec = vector[ + 100u64, + 50, + 75, + 25, + 90, + 10, + 85, + 35, + 60, + 45, + 70, + 15, + 80, + 40, + 55, + 20, + 65, + 30, + 95, + 5, + 68, + 37, + 58, + 48, + 72, + 18, + 82, + 38, + 57, + 47, + ]; + vector::quick_sort!(&mut vec); + assert_eq!( + vec, + vector[ + 5u64, + 10, + 15, + 18, + 20, + 25, + 30, + 35, + 37, + 38, + 40, + 45, + 47, + 48, + 50, + 55, + 57, + 58, + 60, + 65, + 68, + 70, + 72, + 75, + 80, + 82, + 85, + 90, + 95, + 100, + ], + ); +} + +#[test] +fun quick_sort_u8_values() { + // Test with u8 type + let mut vec = vector[255u8, 127, 0, 64, 192, 32, 96, 200]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[0u8, 32, 64, 96, 127, 192, 200, 255]); +} + +#[test] +fun quick_sort_u16_values() { + // Test with u16 type + let mut vec = vector[65535u16, 32768, 0, 16384, 49152, 8192, 24576, 57344]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[0u16, 8192, 16384, 24576, 32768, 49152, 57344, 65535]); +} + +#[test] +fun quick_sort_u32_values() { + // Test with u32 type + let mut vec = vector[1000000u32, 500000, 250000, 750000, 100, 50, 900000, 200]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[50u32, 100, 200, 250000, 500000, 750000, 900000, 1000000]); +} + +#[test] +fun quick_sort_u128_values() { + // Test with u128 type + let mut vec = vector[ + 1000000000000u128, + 500000000000, + 250000000000, + 750000000000, + 100, + 50, + 900000000000, + 200, + ]; + vector::quick_sort!(&mut vec); + assert_eq!( + vec, + vector[ + 50u128, + 100, + 200, + 250000000000, + 500000000000, + 750000000000, + 900000000000, + 1000000000000, + ], + ); +} + +#[test] +fun quick_sort_u256_values() { + // Test with u256 type + let mut vec = vector[ + 1000000000000000000u256, + 500000000000000000, + 250000000000000000, + 750000000000000000, + 100, + 50, + 900000000000000000, + 200, + ]; + vector::quick_sort!(&mut vec); + assert_eq!( + vec, + vector[ + 50u256, + 100, + 200, + 250000000000000000, + 500000000000000000, + 750000000000000000, + 900000000000000000, + 1000000000000000000, + ], + ); +} + +#[test] +fun quick_sort_partition_edge_case_pivot() { + // Test a case where pivot selection matters + let mut vec = vector[1u64, 2, 3, 4, 5]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 2, 3, 4, 5]); +} + +#[test] +fun quick_sort_alternating_values() { + // Alternating high and low values + let mut vec = vector[1u64, 100, 2, 99, 3, 98, 4, 97, 5, 96]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 2, 3, 4, 5, 96, 97, 98, 99, 100]); +} + +#[test] +fun quick_sort_mostly_sorted_with_one_outlier() { + // Mostly sorted vector with one element out of place at the beginning + let mut vec = vector[100u64, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100]); +} + +#[test] +fun quick_sort_mostly_sorted_with_one_outlier_at_end() { + // Mostly sorted vector with one element out of place at the end + let mut vec = vector[1u64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[0u64, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); +} + +#[test] +fun quick_sort_saw_tooth_pattern() { + // Saw-tooth pattern: goes up then down repeatedly + let mut vec = vector[1u64, 3, 2, 4, 3, 5, 4, 6, 5, 7, 6, 8]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 8]); +} + +#[test] +fun quick_sort_ascending_gap_then_descending() { + // Values go up then have a big gap then go down + let mut vec = vector[1u64, 2, 3, 100, 99, 98]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 2, 3, 98, 99, 100]); +} + +#[test] +fun quick_sort_extreme_range_values() { + // Test with extreme range values + let mut vec = vector[0u64, std::u64::max_value!(), 1, std::u64::max_value!() - 1]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[0u64, 1, std::u64::max_value!() - 1, std::u64::max_value!()]); +} + +#[test] +fun quick_sort_many_duplicates_spread_throughout() { + // Many duplicate values spread throughout + let mut vec = vector[5u32, 1, 5, 2, 5, 3, 5, 4, 5, 5]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u32, 2, 3, 4, 5, 5, 5, 5, 5, 5]); +} + +#[test] +fun quick_sort_pairs_of_duplicates() { + // Pairs of duplicates + let mut vec = vector[2u64, 2, 1, 1, 4, 4, 3, 3]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 1, 2, 2, 3, 3, 4, 4]); +} + +#[test] +fun quick_sort_pyramid_pattern() { + // Pyramid pattern: increases to middle then decreases + let mut vec = vector[1u32, 5, 3, 2, 4, 5, 1]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u32, 1, 2, 3, 4, 5, 5]); +} + +#[test] +fun quick_sort_single_large_value_at_start() { + // Single large value at the start + let mut vec = vector[1000u64, 1, 2, 3, 4, 5]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 2, 3, 4, 5, 1000]); +} + +#[test] +fun quick_sort_single_small_value_at_end() { + // Single small value at the end + let mut vec = vector[5u64, 4, 3, 2, 1, 0]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[0u64, 1, 2, 3, 4, 5]); +} + +#[test] +fun quick_sort_three_equal_one_different() { + // Three equal values and one different + let mut vec = vector[5u64, 5, 5, 1]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 5, 5, 5]); +} + +#[test] +fun quick_sort_one_different_three_equal() { + // One different value and three equal + let mut vec = vector[1u64, 5, 5, 5]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 5, 5, 5]); +} + +#[test] +fun quick_sort_maintains_stability_like_behavior() { + // While quicksort is not guaranteed to be stable, verify correct ordering + let mut vec = vector[3u64, 1, 4, 1, 5, 9, 2, 6, 5, 3]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 1, 2, 3, 3, 4, 5, 5, 6, 9]); +} + +#[test] +fun quick_sort_very_large_values() { + // Test with very large u64 values + let mut vec = vector[ + 18446744073709551615u64, // u64::MAX + 9223372036854775808u64, // Near half + 4611686018427387904u64, // Near quarter + 13835058055282163712u64, // Near three-quarters + ]; + vector::quick_sort!(&mut vec); + assert_eq!( + vec, + vector[ + 4611686018427387904u64, + 9223372036854775808u64, + 13835058055282163712u64, + 18446744073709551615u64, + ], + ); +} + +#[test] +fun quick_sort_sequential_with_shuffle() { + // Sequential values that are shuffled + let mut vec = vector[7u32, 1, 3, 6, 2, 8, 4, 9, 5, 10]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u32, 2, 3, 4, 5, 6, 7, 8, 9, 10]); +} + +#[test] +fun quick_sort_nested_ranges() { + // Multiple ranges: [1-3], [10-12], [20-22] + let mut vec = vector[22u64, 1, 11, 2, 21, 3, 12, 20, 10]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u64, 2, 3, 10, 11, 12, 20, 21, 22]); +} + +#[test] +fun quick_sort_binary_like_pattern() { + // Powers of 2 in random order + let mut vec = vector[256u32, 1, 4, 32, 2, 128, 8, 64, 16]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[1u32, 2, 4, 8, 16, 32, 64, 128, 256]); +} + +#[test] +fun quick_sort_high_to_low_middle_low() { + // High values first, then low values mixed in middle + let mut vec = vector[100u64, 90, 80, 5, 10, 70, 15, 60]; + vector::quick_sort!(&mut vec); + assert_eq!(vec, vector[5u64, 10, 15, 60, 70, 80, 90, 100]); +} + +#[test] +fun quick_sort_returns_consistent_results() { + // Verify that sorting the same vec + let mut data1 = vector[42u64, 17, 93, 8, 54, 31, 67, 22, 85, 11]; + let mut data2 = vector[42u64, 17, 93, 8, 54, 31, 67, 22, 85, 11]; + + vector::quick_sort!(&mut data1); + vector::quick_sort!(&mut data2); + + assert_eq!(data1, data2); +} + +#[test] +fun quick_sort_produces_sorted_output() { + // Verify output is actually sorted by checking each element <= next element + let mut vec = vector[89u32, 12, 76, 45, 23, 98, 34, 67, 1, 55]; + vector::quick_sort!(&mut vec); + + let len = vec.length(); + let mut i = 0; + while (i + 1 < len) { + assert!(vec[i] <= vec[i + 1]); + i = i + 1; + }; +} + +#[test] +fun quick_sort_preserves_all_elements() { + // Verify that all original elements are still present after sorting + let original = vector[7u64, 2, 9, 1, 5, 8, 3, 6, 4]; + let mut sorted = original; + vector::quick_sort!(&mut sorted); + + // Check that sorted contains all elements from original + let len = sorted.length(); + assert_eq!(len, original.length()); + + // Count occurrences in both vectors + let mut i = 0; + while (i < len) { + let value = sorted[i]; + + // Count this value in original + let mut count_original = 0; + let mut j = 0; + while (j < len) { + if (original[j] == value) { + count_original = count_original + 1; + }; + j = j + 1; + }; + + // Count this value in sorted + let mut count_sorted = 0; + j = 0; + while (j < len) { + if (sorted[j] == value) { + count_sorted = count_sorted + 1; + }; + j = j + 1; + }; + + // They should match + assert_eq!(count_original, count_sorted); + i = i + 1; + }; +} + +// === quick_sort_by === + +#[test] +fun quick_sort_by_descending_basic() { + // Descending order using a custom comparator + let mut vec = vector[3u64, 1, 4, 1, 5, 9, 2, 6]; + vector::quick_sort_by!(&mut vec, |x: &u64, y: &u64| *x >= *y); + assert_eq!(vec, vector[9u64, 6, 5, 4, 3, 2, 1, 1]); +} + +#[test] +fun quick_sort_by_descending_duplicates() { + // Descending order with repeated values + let mut vec = vector[5u32, 2, 5, 1, 5, 2, 3, 5, 1]; + vector::quick_sort_by!(&mut vec, |x: &u32, y: &u32| *x >= *y); + assert_eq!(vec, vector[5u32, 5, 5, 5, 3, 2, 2, 1, 1]); +} + +#[test] +fun quick_sort_by_descending_already_sorted() { + // Vector already in descending order remains unchanged + let mut vec = vector[10u32, 9, 8, 7, 6, 5, 4, 3, 2, 1]; + vector::quick_sort_by!(&mut vec, |x: &u32, y: &u32| *x >= *y); + assert_eq!(vec, vector[10u32, 9, 8, 7, 6, 5, 4, 3, 2, 1]); +} + +#[test_only] +public struct Transfer has copy, drop { + id: u8, // Transfer identifier + value: u64, // Transfer value in smallest unit +} + +#[test] +fun quick_sort_by_struct_member_ascending() { + // Sort transfers by value member in ascending order + let transfer1 = Transfer { id: 1, value: 3000 }; + let transfer2 = Transfer { id: 2, value: 2500 }; + let transfer3 = Transfer { id: 3, value: 3500 }; + let transfer4 = Transfer { id: 4, value: 2000 }; + + let mut vec = vector[transfer1, transfer2, transfer3, transfer4]; + vector::quick_sort_by!(&mut vec, |x: &Transfer, y: &Transfer| x.value <= y.value); + + assert_eq!(vec[0].value, 2000); + assert_eq!(vec[1].value, 2500); + assert_eq!(vec[2].value, 3000); + assert_eq!(vec[3].value, 3500); +} + +#[test] +fun quick_sort_by_struct_member_descending() { + // Sort transfers by value member in descending order + let transfer1 = Transfer { id: 1, value: 3000 }; + let transfer2 = Transfer { id: 2, value: 2500 }; + let transfer3 = Transfer { id: 3, value: 3500 }; + let transfer4 = Transfer { id: 4, value: 2000 }; + + let mut vec = vector[transfer1, transfer2, transfer3, transfer4]; + vector::quick_sort_by!(&mut vec, |x: &Transfer, y: &Transfer| x.value >= y.value); + + assert_eq!(vec[0].value, 3500); + assert_eq!(vec[1].value, 3000); + assert_eq!(vec[2].value, 2500); + assert_eq!(vec[3].value, 2000); +} + +#[test] +fun quick_sort_by_struct_member_with_duplicates() { + // Sort transfers by value when multiple have the same value + let transfer1 = Transfer { id: 1, value: 3000 }; + let transfer2 = Transfer { id: 2, value: 2500 }; + let transfer3 = Transfer { id: 3, value: 3000 }; + let transfer4 = Transfer { id: 4, value: 2500 }; + let transfer5 = Transfer { id: 5, value: 3500 }; + + let mut vec = vector[transfer1, transfer2, transfer3, transfer4, transfer5]; + vector::quick_sort_by!(&mut vec, |x: &Transfer, y: &Transfer| x.value <= y.value); + + assert_eq!(vec[0].value, 2500); + assert_eq!(vec[1].value, 2500); + assert_eq!(vec[2].value, 3000); + assert_eq!(vec[3].value, 3000); + assert_eq!(vec[4].value, 3500); +} + +#[test] +fun quick_sort_by_struct_member_already_sorted() { + // Vector of transfers already sorted by value should remain unchanged + let transfer1 = Transfer { id: 1, value: 2000 }; + let transfer2 = Transfer { id: 2, value: 2500 }; + let transfer3 = Transfer { id: 3, value: 3000 }; + let transfer4 = Transfer { id: 4, value: 3500 }; + + let mut vec = vector[transfer1, transfer2, transfer3, transfer4]; + vector::quick_sort_by!(&mut vec, |x: &Transfer, y: &Transfer| x.value <= y.value); + + assert_eq!(vec[0].value, 2000); + assert_eq!(vec[1].value, 2500); + assert_eq!(vec[2].value, 3000); + assert_eq!(vec[3].value, 3500); +} + +#[test] +fun quick_sort_by_struct_member_reverse_sorted() { + // Vector of transfers in reverse value order should be sorted correctly + let transfer1 = Transfer { id: 1, value: 3500 }; + let transfer2 = Transfer { id: 2, value: 3000 }; + let transfer3 = Transfer { id: 3, value: 2500 }; + let transfer4 = Transfer { id: 4, value: 2000 }; + + let mut vec = vector[transfer1, transfer2, transfer3, transfer4]; + vector::quick_sort_by!(&mut vec, |x: &Transfer, y: &Transfer| x.value <= y.value); + + assert_eq!(vec[0].value, 2000); + assert_eq!(vec[1].value, 2500); + assert_eq!(vec[2].value, 3000); + assert_eq!(vec[3].value, 3500); +} + +#[test] +fun quick_sort_by_struct_single_element() { + // Single transfer in vector + let transfer = Transfer { id: 1, value: 3000 }; + let mut vec = vector[transfer]; + vector::quick_sort_by!(&mut vec, |x: &Transfer, y: &Transfer| x.value <= y.value); + + assert_eq!(vec.length(), 1); + assert_eq!(vec[0].value, 3000); +} + +#[test] +fun quick_sort_by_struct_member_large_vector() { + // Larger vector of transfers sorted by value + let mut vec = vector[ + Transfer { id: 1, value: 4500 }, + Transfer { id: 2, value: 2300 }, + Transfer { id: 3, value: 6700 }, + Transfer { id: 4, value: 1200 }, + Transfer { id: 5, value: 8900 }, + Transfer { id: 6, value: 3400 }, + Transfer { id: 7, value: 5600 }, + Transfer { id: 8, value: 1800 }, + ]; + + vector::quick_sort_by!(&mut vec, |x: &Transfer, y: &Transfer| x.value <= y.value); + + assert_eq!(vec[0].value, 1200); + assert_eq!(vec[1].value, 1800); + assert_eq!(vec[2].value, 2300); + assert_eq!(vec[3].value, 3400); + assert_eq!(vec[4].value, 4500); + assert_eq!(vec[5].value, 5600); + assert_eq!(vec[6].value, 6700); + assert_eq!(vec[7].value, 8900); +}