Sequoia
Loading...
Searching...
No Matches
Algorithms.hpp
Go to the documentation of this file.
1
2// Copyright Oliver J. Rosten 2018. //
3// Distributed under the GNU GENERAL PUBLIC LICENSE, Version 3.0. //
4// (See accompanying file LICENSE.md or copy at //
5// https://www.gnu.org/licenses/gpl-3.0.en.html) //
7
8#pragma once
9
15
16#include <algorithm>
17#include <functional>
18#include <iterator>
19#include <vector>
20
21namespace sequoia
22{
23 template<class Iter, class Comp=std::ranges::less, class Proj = std::identity>
24 constexpr void bubble_up(Iter begin, Iter current, Comp comp = {}, Proj proj = {})
25 {
26 if(current == begin) return;
27
28 auto parent{begin + (std::ranges::distance(begin, current) - 1) / 2};
29 if(comp(proj(*parent), proj(*current)))
30 {
31 std::ranges::iter_swap(parent, current);
32 bubble_up(begin, parent, comp, proj);
33 }
34 }
35
36 template<class Iter, class Comp=std::ranges::less, class Proj = std::identity>
37 constexpr void bubble_down(Iter begin, Iter current, Iter end, Comp comp = {}, Proj proj = {})
38 {
39 if(std::ranges::distance(begin, end) <= 1) return;
40
41 if(2*(std::ranges::distance(begin, current) + 1) < std::ranges::distance(begin, end))
42 {
43 auto rightChild{begin + 2*(std::ranges::distance(begin, current) + 1)};
44 auto leftChild{rightChild - 1};
45
46 auto dominantChild{comp(proj(*rightChild), proj(*leftChild)) ? leftChild : rightChild};
47 if(comp(proj(*current), proj(*dominantChild)))
48 {
49 std::ranges::iter_swap(dominantChild, current);
50 bubble_down(begin, dominantChild, end, comp, proj);
51 }
52 }
53 else if(2* std::ranges::distance(begin, current) + 1 < std::ranges::distance(begin, end))
54 {
55 auto leftChild{begin + 2* std::ranges::distance(begin, current) + 1};
56 if(comp(proj(*current), proj(*leftChild))) std::ranges::iter_swap(leftChild, current);
57 }
58 }
59
60 template<class Iter, class Comp=std::ranges::less, class Proj=std::identity>
61 constexpr void make_heap(Iter begin, Iter end, Comp comp = {}, Proj proj = {})
62 {
63 if(std::ranges::distance(begin, end) <= 1) return;
64
65 auto current{begin+1};
66 while(current != end)
67 {
68 bubble_up(begin, current, comp, proj);
69 std::ranges::advance(current,1);
70 }
71 }
72
79 template<class Iter, class Comp=std::ranges::less, class Proj = std::identity>
80 constexpr void sort(Iter begin, Iter end, Comp comp = {}, Proj proj = {})
81 {
82 if(std::ranges::distance(begin, end) <= 1) return;
83
84 sequoia::make_heap(begin, end, comp, proj);
85 while(end != begin)
86 {
87 std::ranges::iter_swap(begin, end-1);
88 bubble_down(begin, begin, end-1, comp, proj);
89 --end;
90 }
91 }
92
93 template<class Iter, class Comp, class Proj>
94 inline constexpr bool merge_sortable{
95 requires{
96 typename std::iterator_traits<Iter>::value_type;
97 requires (std::is_copy_constructible_v<typename std::iterator_traits<Iter>::value_type> || is_initializable_v<typename std::iterator_traits<Iter>::value_type>);
98 }
99 && std::mergeable<Iter, Iter, typename std::vector<typename std::iterator_traits<Iter>::value_type>::iterator, Comp, Proj, Proj>
100 };
101
102 template<std::input_iterator Iter, std::weakly_incrementable OutIter, class Comp = std::ranges::less, class Proj = std::identity>
103 requires merge_sortable<Iter, Comp, Proj>
104 constexpr void stable_sort(Iter first, Iter last, OutIter out, Comp compare = {}, Proj proj = {})
105 {
106 const auto dist{std::ranges::distance(first, last)};
107 if(dist < 2) return;
108
109 const auto partition{std::ranges::next(first, dist / 2)};
110 stable_sort(first, partition, out, compare, proj);
111 stable_sort(partition, last, std::ranges::next(out, dist / 2), compare, proj);
112 std::ranges::merge(first, partition, partition, last, out, compare, proj, proj);
113 std::ranges::copy(out, std::ranges::next(out, dist), first);
114 }
115
116 template<std::input_iterator Iter, class Comp = std::ranges::less, class Proj = std::identity>
117 requires merge_sortable<Iter, Comp, Proj>
118 constexpr void stable_sort(Iter first, Iter last, Comp compare = {}, Proj proj = {})
119 {
120 using T = typename std::iterator_traits<Iter>::value_type;
121 auto v{
122 [first, last](){
123 if constexpr (is_initializable_v<T>) return std::vector<T>(std::ranges::distance(first, last));
124 else if constexpr (std::is_copy_constructible_v<T>) return std::vector<T>(first, last);
125 }()
126 };
127
128 stable_sort(first, last, v.begin(), std::move(compare), std::move(proj));
129 }
130
131 template<class Iter, class Comp, class Proj>
132 inline constexpr bool clusterable{
133 std::forward_iterator<Iter>
134 && requires(std::iter_reference_t<Iter> r, Comp comp, Proj proj){
135 { comp(proj(r), proj(r)) } -> std::convertible_to<bool>;
136 }
137 };
138
142 template<std::forward_iterator Iter, class Comp=std::ranges::equal_to, class Proj = std::identity>
143 requires clusterable<Iter, Comp, Proj>
144 constexpr void cluster(Iter begin, Iter end, Comp comp = {}, Proj proj = {})
145 {
146 if(begin == end) return;
147
148 auto current{begin};
149 while((current != end) && comp(proj(*current), proj(*begin))) std::ranges::advance(current, 1);
150
151 auto endOfCluster{current};
152
153 while(current != end)
154 {
155 if(comp(proj(*current), proj(*begin)))
156 {
157 std::ranges::iter_swap(endOfCluster, current);
158 std::ranges::advance(endOfCluster, 1);
159 }
160
161 std::ranges::advance(current, 1);
162 }
163
164 cluster(endOfCluster, end, comp, proj);
165 }
166}
constexpr void cluster(Iter begin, Iter end, Comp comp={}, Proj proj={})
An algorithm which clusters together elements which compare equal.
Definition: Algorithms.hpp:144
constexpr void sort(Iter begin, Iter end, Comp comp={}, Proj proj={})
Definition: Algorithms.hpp:80
Concepts which are sufficiently general to appear in the sequoia namespace.