1 // Copyright 2022 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 //
15 // -----------------------------------------------------------------------------
16 // File: algorithm.h
17 // -----------------------------------------------------------------------------
18 //
19 // This header file provides Container-based versions of algorithmic functions
20 // within the C++ standard library, based on a subset of
21 // absl/algorithm/container.h. The following standard library sets of functions
22 // are covered within this file:
23 //
24 // * <algorithm> functions
25 //
26 // The standard library functions operate on iterator ranges; the functions
27 // within this API operate on containers, though many return iterator ranges.
28 //
29 // All functions within this API are CamelCase instead of their std::
30 // snake_case counterparts. Calls such as `pw::containers::Foo(container, ...)
31 // are equivalent to std:: functions such as
32 // `std::foo(std::begin(cont), std::end(cont), ...)`. Functions that act on
33 // iterators but not conceptually on iterator ranges (e.g. `std::iter_swap`)
34 // have no equivalent here.
35 //
36 // For template parameter and variable naming, `C` indicates the container type
37 // to which the function is applied, `Pred` indicates the predicate object type
38 // to be used by the function and `T` indicates the applicable element type.
39 //
40 // This was forked from
41 // https://cs.opensource.google/abseil/abseil-cpp/+/main:absl/algorithm/algorithm.h;drc=12bc53e0318d80569270a5b26ccbc62b52022b89
42 #pragma once
43
44 #include <algorithm>
45 #include <utility>
46
47 #include "pw_containers/internal/algorithm_internal.h"
48
49 namespace pw::containers {
50
51 //------------------------------------------------------------------------------
52 // <algorithm> Non-modifying sequence operations
53 //------------------------------------------------------------------------------
54
55 // AllOf()
56 //
57 // Container-based version of the <algorithm> `std::all_of()` function to
58 // test if all elements within a container satisfy a condition.
59 template <typename C, typename Pred>
AllOf(const C & c,Pred && pred)60 bool AllOf(const C& c, Pred&& pred) {
61 return std::all_of(std::begin(c), std::end(c), std::forward<Pred>(pred));
62 }
63
64 // AnyOf()
65 //
66 // Container-based version of the <algorithm> `std::any_of()` function to
67 // test if any element in a container fulfills a condition.
68 template <typename C, typename Pred>
AnyOf(const C & c,Pred && pred)69 bool AnyOf(const C& c, Pred&& pred) {
70 return std::any_of(std::begin(c), std::end(c), std::forward<Pred>(pred));
71 }
72
73 // NoneOf()
74 //
75 // Container-based version of the <algorithm> `std::none_of()` function to
76 // test if no elements in a container fulfill a condition.
77 template <typename C, typename Pred>
NoneOf(const C & c,Pred && pred)78 bool NoneOf(const C& c, Pred&& pred) {
79 return std::none_of(std::begin(c), std::end(c), std::forward<Pred>(pred));
80 }
81
82 // ForEach()
83 //
84 // Container-based version of the <algorithm> `std::for_each()` function to
85 // apply a function to a container's elements.
86 template <typename C, typename Function>
ForEach(C && c,Function && f)87 std::decay_t<Function> ForEach(C&& c, Function&& f) {
88 return std::for_each(std::begin(c), std::end(c), std::forward<Function>(f));
89 }
90
91 // Find()
92 //
93 // Container-based version of the <algorithm> `std::find()` function to find
94 // the first element containing the passed value within a container value.
95 template <typename C, typename T>
Find(C & c,T && value)96 internal_algorithm::ContainerIter<C> Find(C& c, T&& value) {
97 return std::find(std::begin(c), std::end(c), std::forward<T>(value));
98 }
99
100 // FindIf()
101 //
102 // Container-based version of the <algorithm> `std::find_if()` function to find
103 // the first element in a container matching the given condition.
104 template <typename C, typename Pred>
FindIf(C & c,Pred && pred)105 internal_algorithm::ContainerIter<C> FindIf(C& c, Pred&& pred) {
106 return std::find_if(std::begin(c), std::end(c), std::forward<Pred>(pred));
107 }
108
109 // FindIfNot()
110 //
111 // Container-based version of the <algorithm> `std::find_if_not()` function to
112 // find the first element in a container not matching the given condition.
113 template <typename C, typename Pred>
FindIfNot(C & c,Pred && pred)114 internal_algorithm::ContainerIter<C> FindIfNot(C& c, Pred&& pred) {
115 return std::find_if_not(std::begin(c), std::end(c), std::forward<Pred>(pred));
116 }
117
118 // FindEnd()
119 //
120 // Container-based version of the <algorithm> `std::find_end()` function to
121 // find the last subsequence within a container.
122 template <typename Sequence1, typename Sequence2>
FindEnd(Sequence1 & sequence,Sequence2 & subsequence)123 internal_algorithm::ContainerIter<Sequence1> FindEnd(Sequence1& sequence,
124 Sequence2& subsequence) {
125 return std::find_end(std::begin(sequence),
126 std::end(sequence),
127 std::begin(subsequence),
128 std::end(subsequence));
129 }
130
131 // Overload of FindEnd() for using a predicate evaluation other than `==` as
132 // the function's test condition.
133 template <typename Sequence1, typename Sequence2, typename BinaryPredicate>
FindEnd(Sequence1 & sequence,Sequence2 & subsequence,BinaryPredicate && pred)134 internal_algorithm::ContainerIter<Sequence1> FindEnd(Sequence1& sequence,
135 Sequence2& subsequence,
136 BinaryPredicate&& pred) {
137 return std::find_end(std::begin(sequence),
138 std::end(sequence),
139 std::begin(subsequence),
140 std::end(subsequence),
141 std::forward<BinaryPredicate>(pred));
142 }
143
144 // FindFirstOf()
145 //
146 // Container-based version of the <algorithm> `std::find_first_of()` function to
147 // find the first element within the container that is also within the options
148 // container.
149 template <typename C1, typename C2>
FindFirstOf(C1 & container,C2 & options)150 internal_algorithm::ContainerIter<C1> FindFirstOf(C1& container, C2& options) {
151 return std::find_first_of(std::begin(container),
152 std::end(container),
153 std::begin(options),
154 std::end(options));
155 }
156
157 // Overload of FindFirstOf() for using a predicate evaluation other than
158 // `==` as the function's test condition.
159 template <typename C1, typename C2, typename BinaryPredicate>
FindFirstOf(C1 & container,C2 & options,BinaryPredicate && pred)160 internal_algorithm::ContainerIter<C1> FindFirstOf(C1& container,
161 C2& options,
162 BinaryPredicate&& pred) {
163 return std::find_first_of(std::begin(container),
164 std::end(container),
165 std::begin(options),
166 std::end(options),
167 std::forward<BinaryPredicate>(pred));
168 }
169
170 // AdjacentFind()
171 //
172 // Container-based version of the <algorithm> `std::adjacent_find()` function to
173 // find equal adjacent elements within a container.
174 template <typename Sequence>
AdjacentFind(Sequence & sequence)175 internal_algorithm::ContainerIter<Sequence> AdjacentFind(Sequence& sequence) {
176 return std::adjacent_find(std::begin(sequence), std::end(sequence));
177 }
178
179 // Overload of AdjacentFind() for using a predicate evaluation other than
180 // `==` as the function's test condition.
181 template <typename Sequence, typename BinaryPredicate>
AdjacentFind(Sequence & sequence,BinaryPredicate && pred)182 internal_algorithm::ContainerIter<Sequence> AdjacentFind(
183 Sequence& sequence, BinaryPredicate&& pred) {
184 return std::adjacent_find(std::begin(sequence),
185 std::end(sequence),
186 std::forward<BinaryPredicate>(pred));
187 }
188
189 // Count()
190 //
191 // Container-based version of the <algorithm> `std::count()` function to count
192 // values that match within a container.
193 template <typename C, typename T>
Count(const C & c,T && value)194 internal_algorithm::ContainerDifferenceType<const C> Count(const C& c,
195 T&& value) {
196 return std::count(std::begin(c), std::end(c), std::forward<T>(value));
197 }
198
199 // CountIOf()
200 //
201 // Container-based version of the <algorithm> `std::count_if()` function to
202 // count values matching a condition within a container.
203 template <typename C, typename Pred>
CountIf(const C & c,Pred && pred)204 internal_algorithm::ContainerDifferenceType<const C> CountIf(const C& c,
205 Pred&& pred) {
206 return std::count_if(std::begin(c), std::end(c), std::forward<Pred>(pred));
207 }
208
209 // Mismatch()
210 //
211 // Container-based version of the <algorithm> `std::mismatch()` function to
212 // return the first element where two ordered containers differ. Applies `==` to
213 // the first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)).
214 template <typename C1, typename C2>
Mismatch(C1 & c1,C2 & c2)215 internal_algorithm::ContainerIterPairType<C1, C2> Mismatch(C1& c1, C2& c2) {
216 auto first1 = std::begin(c1);
217 auto last1 = std::end(c1);
218 auto first2 = std::begin(c2);
219 auto last2 = std::end(c2);
220
221 for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) {
222 // Negates equality because Cpp17EqualityComparable doesn't require clients
223 // to overload both `operator==` and `operator!=`.
224 if (!(*first1 == *first2)) {
225 break;
226 }
227 }
228
229 return std::make_pair(first1, first2);
230 }
231
232 // Overload of Mismatch() for using a predicate evaluation other than `==` as
233 // the function's test condition. Applies `pred`to the first N elements of `c1`
234 // and `c2`, where N = min(size(c1), size(c2)).
235 template <typename C1, typename C2, typename BinaryPredicate>
Mismatch(C1 & c1,C2 & c2,BinaryPredicate pred)236 internal_algorithm::ContainerIterPairType<C1, C2> Mismatch(
237 C1& c1, C2& c2, BinaryPredicate pred) {
238 auto first1 = std::begin(c1);
239 auto last1 = std::end(c1);
240 auto first2 = std::begin(c2);
241 auto last2 = std::end(c2);
242
243 for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) {
244 if (!pred(*first1, *first2)) {
245 break;
246 }
247 }
248
249 return std::make_pair(first1, first2);
250 }
251
252 // Equal()
253 //
254 // Container-based version of the <algorithm> `std::equal()` function to
255 // test whether two containers are equal.
256 //
257 // NOTE: the semantics of Equal() are slightly different than those of
258 // equal(): while the latter iterates over the second container only up to the
259 // size of the first container, Equal() also checks whether the container
260 // sizes are equal. This better matches expectations about Equal() based on
261 // its signature.
262 //
263 // Example:
264 // vector v1 = <1, 2, 3>;
265 // vector v2 = <1, 2, 3, 4>;
266 // equal(std::begin(v1), std::end(v1), std::begin(v2)) returns true
267 // Equal(v1, v2) returns false
268
269 template <typename C1, typename C2>
Equal(const C1 & c1,const C2 & c2)270 bool Equal(const C1& c1, const C2& c2) {
271 return ((std::size(c1) == std::size(c2)) &&
272 std::equal(std::begin(c1), std::end(c1), std::begin(c2)));
273 }
274
275 // Overload of Equal() for using a predicate evaluation other than `==` as
276 // the function's test condition.
277 template <typename C1, typename C2, typename BinaryPredicate>
Equal(const C1 & c1,const C2 & c2,BinaryPredicate && pred)278 bool Equal(const C1& c1, const C2& c2, BinaryPredicate&& pred) {
279 return ((std::size(c1) == std::size(c2)) &&
280 std::equal(std::begin(c1),
281 std::end(c1),
282 std::begin(c2),
283 std::forward<BinaryPredicate>(pred)));
284 }
285
286 // IsPermutation()
287 //
288 // Container-based version of the <algorithm> `std::is_permutation()` function
289 // to test whether a container is a permutation of another.
290 template <typename C1, typename C2>
IsPermutation(const C1 & c1,const C2 & c2)291 bool IsPermutation(const C1& c1, const C2& c2) {
292 using std::begin;
293 using std::end;
294 return c1.size() == c2.size() &&
295 std::is_permutation(begin(c1), end(c1), begin(c2));
296 }
297
298 // Overload of IsPermutation() for using a predicate evaluation other than
299 // `==` as the function's test condition.
300 template <typename C1, typename C2, typename BinaryPredicate>
IsPermutation(const C1 & c1,const C2 & c2,BinaryPredicate && pred)301 bool IsPermutation(const C1& c1, const C2& c2, BinaryPredicate&& pred) {
302 using std::begin;
303 using std::end;
304 return c1.size() == c2.size() &&
305 std::is_permutation(begin(c1),
306 end(c1),
307 begin(c2),
308 std::forward<BinaryPredicate>(pred));
309 }
310
311 // Search()
312 //
313 // Container-based version of the <algorithm> `std::search()` function to search
314 // a container for a subsequence.
315 template <typename Sequence1, typename Sequence2>
Search(Sequence1 & sequence,Sequence2 & subsequence)316 internal_algorithm::ContainerIter<Sequence1> Search(Sequence1& sequence,
317 Sequence2& subsequence) {
318 return std::search(std::begin(sequence),
319 std::end(sequence),
320 std::begin(subsequence),
321 std::end(subsequence));
322 }
323
324 // Overload of Search() for using a predicate evaluation other than
325 // `==` as the function's test condition.
326 template <typename Sequence1, typename Sequence2, typename BinaryPredicate>
Search(Sequence1 & sequence,Sequence2 & subsequence,BinaryPredicate && pred)327 internal_algorithm::ContainerIter<Sequence1> Search(Sequence1& sequence,
328 Sequence2& subsequence,
329 BinaryPredicate&& pred) {
330 return std::search(std::begin(sequence),
331 std::end(sequence),
332 std::begin(subsequence),
333 std::end(subsequence),
334 std::forward<BinaryPredicate>(pred));
335 }
336
337 // SearchN()
338 //
339 // Container-based version of the <algorithm> `std::search_n()` function to
340 // search a container for the first sequence of N elements.
341 template <typename Sequence, typename Size, typename T>
SearchN(Sequence & sequence,Size count,T && value)342 internal_algorithm::ContainerIter<Sequence> SearchN(Sequence& sequence,
343 Size count,
344 T&& value) {
345 return std::search_n(
346 std::begin(sequence), std::end(sequence), count, std::forward<T>(value));
347 }
348
349 // Overload of SearchN() for using a predicate evaluation other than
350 // `==` as the function's test condition.
351 template <typename Sequence,
352 typename Size,
353 typename T,
354 typename BinaryPredicate>
SearchN(Sequence & sequence,Size count,T && value,BinaryPredicate && pred)355 internal_algorithm::ContainerIter<Sequence> SearchN(Sequence& sequence,
356 Size count,
357 T&& value,
358 BinaryPredicate&& pred) {
359 return std::search_n(std::begin(sequence),
360 std::end(sequence),
361 count,
362 std::forward<T>(value),
363 std::forward<BinaryPredicate>(pred));
364 }
365
366 } // namespace pw::containers
367