diff options
| author | Matthew Edwards <mje-nz@users.noreply.github.com> | 2020-04-12 09:16:18 +1200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-04-11 23:16:18 +0200 |
| commit | 008bc53c45d1131d0eedf5c1fe276f5808911681 (patch) | |
| tree | 37d6c4255e560c736019a3532957c8905ac2f61d | |
| parent | 1582d97d44b9dbe9b89e9ec6e7e979ae73e3b583 (diff) | |
| download | cython-008bc53c45d1131d0eedf5c1fe276f5808911681.tar.gz | |
Add partitioning and sorting operations to libcpp.algorithm (GH-3202)
* Add is_partitioned and partition to libcpp.algorithm
* Add is_sorted and is_sorted_until to libcpp.algorithm
* Add partition_copy, stable_partition, and partition_point to libcpp.algorithm
* Move tests for sort and partial_sort into cpp_stl_algo_sorting_ops, add greater to libcpp.functional
* Add partial_sort_copy to libcpp.algorithm
* Add stable_sort to libcpp.algorithm
* Add nth_element to libcpp.algorithm
* Add missing except specifiers in libcpp.algorithm
| -rw-r--r-- | Cython/Includes/libcpp/algorithm.pxd | 103 | ||||
| -rw-r--r-- | Cython/Includes/libcpp/functional.pxd | 10 | ||||
| -rw-r--r-- | tests/run/cpp_stl_algo_partitioning_ops.pyx | 90 | ||||
| -rw-r--r-- | tests/run/cpp_stl_algo_sorting_ops.pyx | 187 | ||||
| -rw-r--r-- | tests/run/libcpp_algo.pyx | 32 |
5 files changed, 350 insertions, 72 deletions
diff --git a/Cython/Includes/libcpp/algorithm.pxd b/Cython/Includes/libcpp/algorithm.pxd index 9829101ef..c07bae139 100644 --- a/Cython/Includes/libcpp/algorithm.pxd +++ b/Cython/Includes/libcpp/algorithm.pxd @@ -11,44 +11,45 @@ cdef extern from "<algorithm>" namespace "std" nogil: void for_each[Iter, UnaryFunction](Iter first, Iter last, UnaryFunction f) except + # actually returns f - ptrdiff_t count[Iter, T](Iter first, Iter last, const T& value) + ptrdiff_t count[Iter, T](Iter first, Iter last, const T& value) except + ptrdiff_t count_if[Iter, Pred](Iter first, Iter last, Pred pred) except + - pair[Iter1, Iter2] mismatch[Iter1, Iter2](Iter1 first1, Iter1 last1, Iter2 first2) # other overloads are tricky + pair[Iter1, Iter2] mismatch[Iter1, Iter2]( + Iter1 first1, Iter1 last1, Iter2 first2) except + # other overloads are tricky - Iter find[Iter, T](Iter first, Iter last, const T& value) + Iter find[Iter, T](Iter first, Iter last, const T& value) except + Iter find_if[Iter, Pred](Iter first, Iter last, Pred pred) except + Iter find_if_not[Iter, Pred](Iter first, Iter last, Pred pred) except + - Iter1 find_end[Iter1, Iter2](Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) + Iter1 find_end[Iter1, Iter2](Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) except + Iter1 find_end[Iter1, Iter2, BinaryPred]( Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2, BinaryPred pred) except + - Iter1 find_first_of[Iter1, Iter2](Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) + Iter1 find_first_of[Iter1, Iter2](Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) except + Iter1 find_first_of[Iter1, Iter2, BinaryPred]( Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2, BinaryPred pred) except + - Iter adjacent_find[Iter](Iter first, Iter last) + Iter adjacent_find[Iter](Iter first, Iter last) except + Iter adjacent_find[Iter, BinaryPred](Iter first, Iter last, BinaryPred pred) except + - Iter1 search[Iter1, Iter2](Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) + Iter1 search[Iter1, Iter2](Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) except + Iter1 search[Iter1, Iter2, BinaryPred]( Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2, BinaryPred pred) except + - Iter search_n[Iter, Size, T](Iter first1, Iter last1, Size count, const T& value) + Iter search_n[Iter, Size, T](Iter first1, Iter last1, Size count, const T& value) except + Iter search_n[Iter, Size, T, BinaryPred]( Iter first1, Iter last1, Size count, const T& value, BinaryPred pred) except + # Modifying sequence operations - OutputIt copy[InputIt, OutputIt](InputIt first, InputIt last, OutputIt d_first) + OutputIt copy[InputIt, OutputIt](InputIt first, InputIt last, OutputIt d_first) except + OutputIt copy_if[InputIt, OutputIt, Pred](InputIt first, InputIt last, OutputIt d_first, Pred pred) except + - OutputIt copy_n[InputIt, Size, OutputIt](InputIt first, Size count, OutputIt result) - Iter2 copy_backward[Iter1, Iter2](Iter1 first, Iter1 last, Iter2 d_last) + OutputIt copy_n[InputIt, Size, OutputIt](InputIt first, Size count, OutputIt result) except + + Iter2 copy_backward[Iter1, Iter2](Iter1 first, Iter1 last, Iter2 d_last) except + - OutputIt move[InputIt, OutputIt](InputIt first, InputIt last, OutputIt d_first) - Iter2 move_backward[Iter1, Iter2](Iter1 first, Iter1 last, Iter2 d_last) + OutputIt move[InputIt, OutputIt](InputIt first, InputIt last, OutputIt d_first) except + + Iter2 move_backward[Iter1, Iter2](Iter1 first, Iter1 last, Iter2 d_last) except + - void fill[Iter, T](Iter first, Iter last, const T& value) - Iter fill_n[Iter, Size, T](Iter first, Size count, const T& value) + void fill[Iter, T](Iter first, Iter last, const T& value) except + + Iter fill_n[Iter, Size, T](Iter first, Size count, const T& value) except + OutputIt transform[InputIt, OutputIt, UnaryOp]( InputIt first1, InputIt last1, OutputIt d_first, UnaryOp unary_op) except + @@ -58,53 +59,75 @@ cdef extern from "<algorithm>" namespace "std" nogil: void generate[Iter, Generator](Iter first, Iter last, Generator g) except + void generate_n[Iter, Size, Generator](Iter first, Size count, Generator g) except + - Iter remove[Iter, T](Iter first, Iter last, const T& value) - Iter remove_if[Iter, UnaryPred](Iter first, Iter last, UnaryPred pred) - OutputIt remove_copy[InputIt, OutputIt, T](InputIt first, InputIt last, OutputIt d_first, const T& value) + Iter remove[Iter, T](Iter first, Iter last, const T& value) except + + Iter remove_if[Iter, UnaryPred](Iter first, Iter last, UnaryPred pred) except + + OutputIt remove_copy[InputIt, OutputIt, T](InputIt first, InputIt last, OutputIt d_first, const T& value) except + OutputIt remove_copy_if[InputIt, OutputIt, UnaryPred]( InputIt first, InputIt last, OutputIt d_first, UnaryPred pred) except + - void replace[Iter, T](Iter first, Iter last, const T& old_value, const T& new_value) + void replace[Iter, T](Iter first, Iter last, const T& old_value, const T& new_value) except + void replace_if[Iter, UnaryPred, T](Iter first, Iter last, UnaryPred pred, const T& new_value) except + OutputIt replace_copy[InputIt, OutputIt, T]( - InputIt first, InputIt last, OutputIt d_first, const T& old_value, const T& new_value) + InputIt first, InputIt last, OutputIt d_first, const T& old_value, const T& new_value) except + OutputIt replace_copy_if[InputIt, OutputIt, UnaryPred, T]( InputIt first, InputIt last, OutputIt d_first, UnaryPred pred, const T& new_value) except + - void swap[T](T& a, T& b) # array overload also works - Iter2 swap_ranges[Iter1, Iter2](Iter1 first1, Iter1 last1, Iter2 first2) - void iter_swap[Iter](Iter a, Iter b) + void swap[T](T& a, T& b) except + # array overload also works + Iter2 swap_ranges[Iter1, Iter2](Iter1 first1, Iter1 last1, Iter2 first2) except + + void iter_swap[Iter](Iter a, Iter b) except + - void reverse[Iter](Iter first, Iter last) - OutputIt reverse_copy[InputIt, OutputIt](InputIt first, InputIt last, OutputIt d_first) + void reverse[Iter](Iter first, Iter last) except + + OutputIt reverse_copy[InputIt, OutputIt](InputIt first, InputIt last, OutputIt d_first) except + - Iter rotate[Iter](Iter first, Iter n_first, Iter last) - OutputIt rotate_copy[InputIt, OutputIt](InputIt first, InputIt n_first, InputIt last, OutputIt d_first) + Iter rotate[Iter](Iter first, Iter n_first, Iter last) except + + OutputIt rotate_copy[InputIt, OutputIt](InputIt first, InputIt n_first, InputIt last, OutputIt d_first) except + - Iter unique[Iter](Iter first, Iter last) + Iter unique[Iter](Iter first, Iter last) except + Iter unique[Iter, BinaryPred](Iter first, Iter last, BinaryPred p) except + - OutputIt unique_copy[InputIt, OutputIt](InputIt first, InputIt last, OutputIt d_first) + OutputIt unique_copy[InputIt, OutputIt](InputIt first, InputIt last, OutputIt d_first) except + OutputIt unique_copy[InputIt, OutputIt, BinaryPred]( InputIt first, InputIt last, OutputIt d_first, BinaryPred pred) except + - # Partitioning operations + bool is_partitioned[Iter, Pred](Iter first, Iter last, Pred p) except + + Iter partition[Iter, Pred](Iter first, Iter last, Pred p) except + + pair[OutputIt1, OutputIt2] partition_copy[InputIt, OutputIt1, OutputIt2, Pred]( + InputIt first, InputIt last, OutputIt1 d_first_true, OutputIt2 d_first_false, Pred p) except + + Iter stable_partition[Iter, Pred](Iter first, Iter last, Pred p) except + + Iter partition_point[Iter, Pred](Iter first, Iter last, Pred p) except + # Sorting operations - void sort[Iter](Iter first, Iter last) + bool is_sorted[Iter](Iter first, Iter last) except + + bool is_sorted[Iter, Compare](Iter first, Iter last, Compare comp) except + + + Iter is_sorted_until[Iter](Iter first, Iter last) except + + Iter is_sorted_until[Iter, Compare](Iter first, Iter last, Compare comp) except + + + void sort[Iter](Iter first, Iter last) except + void sort[Iter, Compare](Iter first, Iter last, Compare comp) except + - void partial_sort[Iter](Iter first, Iter middle, Iter last) + void partial_sort[Iter](Iter first, Iter middle, Iter last) except + void partial_sort[Iter, Compare](Iter first, Iter middle, Iter last, Compare comp) except + + OutputIt partial_sort_copy[InputIt, OutputIt]( + InputIt first, InputIt last, OutputIt d_first, OutputIt d_last) except + + OutputIt partial_sort_copy[InputIt, OutputIt, Compare]( + InputIt first, InputIt last, OutputIt d_first, OutputIt d_last, Compare comp) except + + + void stable_sort[Iter](Iter first, Iter last) except + + void stable_sort[Iter, Compare](Iter first, Iter last, Compare comp) except + + + void nth_element[Iter](Iter first, Iter nth, Iter last) except + + void nth_element[Iter, Compare](Iter first, Iter nth, Iter last, Compare comp) except + + # Binary search operations (on sorted ranges) - Iter lower_bound[Iter, T](Iter first, Iter last, const T& value) + Iter lower_bound[Iter, T](Iter first, Iter last, const T& value) except + Iter lower_bound[Iter, T, Compare](Iter first, Iter last, const T& value, Compare comp) except + - Iter upper_bound[Iter, T](Iter first, Iter last, const T& value) + Iter upper_bound[Iter, T](Iter first, Iter last, const T& value) except + Iter upper_bound[Iter, T, Compare](Iter first, Iter last, const T& value, Compare comp) except + - bool binary_search[Iter, T](Iter first, Iter last, const T& value) + bool binary_search[Iter, T](Iter first, Iter last, const T& value) except + bool binary_search[Iter, T, Compare](Iter first, Iter last, const T& value, Compare comp) except + # Other operations on sorted ranges @@ -112,20 +135,20 @@ cdef extern from "<algorithm>" namespace "std" nogil: # Set operations (on sorted ranges) # Heap operations - void make_heap[Iter](Iter first, Iter last) + void make_heap[Iter](Iter first, Iter last) except + void make_heap[Iter, Compare](Iter first, Iter last, Compare comp) except + - void push_heap[Iter](Iter first, Iter last) + void push_heap[Iter](Iter first, Iter last) except + void push_heap[Iter, Compare](Iter first, Iter last, Compare comp) except + - void pop_heap[Iter](Iter first, Iter last) + void pop_heap[Iter](Iter first, Iter last) except + void pop_heap[Iter, Compare](Iter first, Iter last, Compare comp) except + - void sort_heap[Iter](Iter first, Iter last) + void sort_heap[Iter](Iter first, Iter last) except + void sort_heap[Iter, Compare](Iter first, Iter last, Compare comp) except + # Minimum/maximum operations - Iter min_element[Iter](Iter first, Iter last) + Iter min_element[Iter](Iter first, Iter last) except + # Comparison operations diff --git a/Cython/Includes/libcpp/functional.pxd b/Cython/Includes/libcpp/functional.pxd index 94cbd9e1d..4786d39eb 100644 --- a/Cython/Includes/libcpp/functional.pxd +++ b/Cython/Includes/libcpp/functional.pxd @@ -1,3 +1,5 @@ +from libcpp cimport bool + cdef extern from "<functional>" namespace "std" nogil: cdef cppclass function[T]: function() except + @@ -10,4 +12,10 @@ cdef extern from "<functional>" namespace "std" nogil: function operator=(void*) function operator=[U](U) - bint operator bool() + bool operator bool() + + # Comparisons + cdef cppclass greater[T=*]: + # https://github.com/cython/cython/issues/3193 + greater() except + + bool operator()(const T& lhs, const T& rhs) except + diff --git a/tests/run/cpp_stl_algo_partitioning_ops.pyx b/tests/run/cpp_stl_algo_partitioning_ops.pyx new file mode 100644 index 000000000..3bdc3d2af --- /dev/null +++ b/tests/run/cpp_stl_algo_partitioning_ops.pyx @@ -0,0 +1,90 @@ +# mode: run +# tag: cpp, werror, cpp11 + +from __future__ import print_function + +from libcpp cimport bool +from libcpp.algorithm cimport is_partitioned, partition, partition_copy, stable_partition, partition_point +from libcpp.algorithm cimport for_each, copy, reverse +from libcpp.iterator cimport back_inserter +from libcpp.vector cimport vector + + +cdef bool is_even(int i): + return i % 2 == 0 + + +def test_is_partitioned(): + """ + >>> test_is_partitioned() + False + True + False + """ + cdef vector[int] values = range(10) + print(is_partitioned(values.begin(), values.end(), is_even)) + + partition(values.begin(), values.end(), &is_even) + print(is_partitioned(values.begin(), values.end(), is_even)) + + reverse(values.begin(), values.end()) + print(is_partitioned(values.begin(), values.end(), is_even)) + + +cdef int print_int(int v) except -1: + print(v, end=" ") + + +def print_partition(vector[int] values): + """ + Test partition. + + >> print_partition(range(10)) + 0 8 2 6 4 * 5 3 7 1 9 + """ + it = partition(values.begin(), values.end(), &is_even) + for_each(values.begin(), it, &print_int) + print("*", end=" ") + for_each(it, values.end(), &print_int) + print() + + +def partition_ints_even(vector[int] values): + """ + Test partition_copy. + + >>> partition_ints_even(range(10)) + ([0, 2, 4, 6, 8], [1, 3, 5, 7, 9]) + """ + cdef vector[int] even_values, odd_values + partition_copy(values.begin(), values.end(), back_inserter(even_values), back_inserter(odd_values), &is_even) + return even_values, odd_values + + +cdef bool is_positive(int v): + return v > 0 + + +def partition_ints_positive(vector[int] values): + """ + Test stable_partition. + + >>> partition_ints_positive([0, 0, 3, 0, 2, 4, 5, 0, 7]) + [3, 2, 4, 5, 7, 0, 0, 0, 0] + """ + stable_partition(values.begin(), values.end(), &is_positive) + return values + + +def partition_point_ints_even(vector[int] values): + """ + Test partition_point. + + >>> partition_point_ints_even([0, 8, 2, 6, 4, 5, 3, 7, 1, 9]) + ([0, 8, 2, 6, 4], [5, 3, 7, 1, 9]) + """ + it = partition_point(values.begin(), values.end(), is_even) + cdef vector[int] even_values, odd_values + copy(values.begin(), it, back_inserter(even_values)) + copy(it, values.end(), back_inserter(odd_values)) + return even_values, odd_values diff --git a/tests/run/cpp_stl_algo_sorting_ops.pyx b/tests/run/cpp_stl_algo_sorting_ops.pyx new file mode 100644 index 000000000..299089c4c --- /dev/null +++ b/tests/run/cpp_stl_algo_sorting_ops.pyx @@ -0,0 +1,187 @@ +# mode: run +# tag: cpp, werror, cpp11 + +from __future__ import print_function + +from libcpp cimport bool +from libcpp.algorithm cimport is_sorted, is_sorted_until, sort, partial_sort, partial_sort_copy, stable_sort +from libcpp.algorithm cimport nth_element +from libcpp.functional cimport greater +from libcpp.iterator cimport distance +from libcpp.string cimport string +from libcpp.vector cimport vector + + +def is_sorted_ints(vector[int] values): + """ + Test is_sorted. + + >>> is_sorted_ints([3, 1, 4, 1, 5]) + False + >>> is_sorted_ints([1, 1, 3, 4, 5]) + True + """ + return is_sorted(values.begin(), values.end()) + + +def initial_sorted_elements(vector[int] values): + """ + Test is_sorted_until. + + >>> initial_sorted_elements([4, 1, 9, 5, 1, 3]) + 1 + >>> initial_sorted_elements([4, 5, 9, 3, 1, 1]) + 3 + >>> initial_sorted_elements([9, 3, 1, 4, 5, 1]) + 1 + >>> initial_sorted_elements([1, 3, 5, 4, 1, 9]) + 3 + >>> initial_sorted_elements([5, 9, 1, 1, 3, 4]) + 2 + >>> initial_sorted_elements([4, 9, 1, 5, 1, 3]) + 2 + >>> initial_sorted_elements([1, 1, 4, 9, 5, 3]) + 4 + """ + sorted_end = is_sorted_until(values.begin(), values.end()) + return distance(values.begin(), sorted_end) + + +def sort_ints(vector[int] values): + """Test sort using the default operator<. + + >>> sort_ints([5, 7, 4, 2, 8, 6, 1, 9, 0, 3]) + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + """ + sort(values.begin(), values.end()) + return values + + +def sort_ints_reverse(vector[int] values): + """Test sort using a standard library comparison function object. + + >>> sort_ints_reverse([5, 7, 4, 2, 8, 6, 1, 9, 0, 3]) + [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] + """ + sort(values.begin(), values.end(), greater[int]()) + return values + + +def partial_sort_ints(vector[int] values, int k): + """ + Test partial_sort using the default operator<. + + >>> partial_sort_ints([4, 2, 3, 1, 5], 2)[:2] + [1, 2] + """ + partial_sort(values.begin(), values.begin() + k, values.end()) + return values + + +def partial_sort_ints_reverse(vector[int] values, int k): + """ + Test partial_sort using a standard library comparison function object. + + >>> partial_sort_ints_reverse([4, 2, 3, 1, 5], 2)[:2] + [5, 4] + """ + partial_sort(values.begin(), values.begin() + k, values.end(), greater[int]()) + return values + + +def partial_sort_ints2(vector[int] values, int k): + """ + Test partial_sort_copy using the default operator<. + + >>> partial_sort_ints2([4, 2, 3, 1, 5], 2) + [1, 2] + """ + output = vector[int](2) + partial_sort_copy(values.begin(), values.end(), output.begin(), output.end()) + return output + + +def partial_sort_ints_reverse2(vector[int] values, int k): + """ + Test partial_sort_copy using a standard library comparison function object. + + >>> partial_sort_ints_reverse2([4, 2, 3, 1, 5], 2) + [5, 4] + """ + output = vector[int](2) + partial_sort_copy(values.begin(), values.end(), output.begin(), output.end(), greater[int]()) + return output + + +cdef extern from *: + """ + struct Employee + { + Employee() = default; + Employee(int age, std::string name): age(age), name(name) {} + int age; + std::string name; // Does not participate in comparisons + }; + + bool operator<(const Employee& lhs, const Employee& rhs) + { + return lhs.age < rhs.age; + } + """ + cppclass Employee: + Employee() + Employee(int, string) + int age + string name + +cdef bool Employee_greater(const Employee& lhs, const Employee& rhs): + return lhs.age > rhs.age + +def test_stable_sort(): + """ + Test stable_sort using cppreference example. + + >>> test_stable_sort() + 32, Arthur + 108, Zaphod + 108, Ford + 108, Zaphod + 108, Ford + 32, Arthur + """ + cdef vector[Employee] employees + employees.push_back(Employee(108, <string>b"Zaphod")) + employees.push_back(Employee(32, <string>b"Arthur")) + employees.push_back(Employee(108, <string>b"Ford")) + + stable_sort(employees.begin(), employees.end()) + + for e in employees: + print("%s, %s" % (e.age, <str>(e.name).decode("ascii"))) + + stable_sort(employees.begin(), employees.end(), &Employee_greater) + + for e in employees: + print("%s, %s" % (e.age, <str>(e.name).decode("ascii"))) + + +def second_smallest(vector[int] values): + """ + Test nth_element using the default operator<. + + >>> second_smallest([5, 6, 4, 3, 2, 6, 7, 9, 3]) + 3 + """ + nth_element(values.begin(), values.begin() + 1, values.end()) + return values[1] + + +def second_largest(vector[int] values): + """ + Test nth_element using a standard library comparison function object. + + >>> second_largest([5, 6, 4, 3, 2, 6, 7, 9, 3]) + 7 + """ + nth_element(values.begin(), values.begin() + 1, values.end(), greater[int]()) + return values[1] diff --git a/tests/run/libcpp_algo.pyx b/tests/run/libcpp_algo.pyx index f969b22a6..758da7705 100644 --- a/tests/run/libcpp_algo.pyx +++ b/tests/run/libcpp_algo.pyx @@ -2,7 +2,7 @@ # tag: cpp from libcpp cimport bool -from libcpp.algorithm cimport make_heap, sort_heap, sort, partial_sort +from libcpp.algorithm cimport make_heap, sort_heap from libcpp.vector cimport vector @@ -28,33 +28,3 @@ def heapsort(l, bool reverse=False): sort_heap(v.begin(), v.end()) return v - - -def partialsort(l, int k, reverse=False): - """ - >>> partialsort([4, 2, 3, 1, 5], k=2)[:2] - [1, 2] - >>> partialsort([4, 2, 3, 1, 5], k=2, reverse=True)[:2] - [5, 4] - """ - cdef vector[int] v = l - if reverse: - partial_sort(v.begin(), v.begin() + k, v.end(), &greater) - else: - partial_sort(v.begin(), v.begin() + k, v.end()) - return v - - -def stdsort(l, reverse=False): - """ - >>> stdsort([3, 2, 1, 4, 5]) - [1, 2, 3, 4, 5] - >>> stdsort([3, 2, 1, 4, 5], reverse=True) - [5, 4, 3, 2, 1] - """ - cdef vector[int] v = l - if reverse: - sort(v.begin(), v.end(), &greater) - else: - sort(v.begin(), v.end()) - return v |
