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