Simple C++ Algorithms
scalgorithm.hpp
1 #ifndef SIMPLE_CPLUSPLUS_ALGORITHM
2 #define SIMPLE_CPLUSPLUS_ALGORITHM
3 
4 // cpp stl
5 #include <type_traits>
6 #include <vector>
7 #include <functional>
8 #include <iterator>
9 #include <memory>
10 #include <algorithm>
11 
55 namespace sca { // simple cpp algorithm
56 namespace detail {
57 
58 // -----------------------------------------------------------------------------
59 // is_lvalue_ref_t
60 
61 template <typename C>
62 using is_lvalue_ref_t = typename std::is_lvalue_reference<C>::type;
63 
64 //------------------------------------------------------------------------------
65 // to_vector_t
66 
67 // a `std::vector<T>` where `T` is the `::value_type` of a container `C`
68 template <typename C>
69 using to_vector_t = std::vector<typename std::decay_t<C>::value_type>;
70 
71 // -----------------------------------------------------------------------------
72 // container_reference_value_t
73 
74 // acquire the ::value_type of a container<T> (normally T) as a reference (T&)
75 template <typename C>
76 using container_reference_value_t = typename std::decay_t<C>::value_type&;
77 
78 // -----------------------------------------------------------------------------
79 // callable_return_t
80 
81 // handle pre and post c++17
82 #if __cplusplus >= 201703L
83 template <typename F, typename... Ts>
84 using callable_return_t = typename std::invoke_result<std::decay_t<F>,Ts...>::type;
85 #else
86 template <typename F, typename... Ts>
87 using callable_return_t = typename std::result_of<std::decay_t<F>(Ts...)>::type;
88 #endif
89 
90 // -----------------------------------------------------------------------------
91 // size
92 
93 template <typename T>
95  typedef typename std::decay_t<T> BT; // remove any references from T
96  template<typename U, typename U::size_type (U::*)() const> struct SFINAE {};
97  template<typename U> static char test(SFINAE<U, &U::size>*);
98  template<typename U> static int test(...);
99  static const bool has = sizeof(test<BT>(0)) == sizeof(char);
100 };
101 
102 template <typename T>
103 using has_size = std::integral_constant<bool, detail::has_size_struct<T>::has>;
104 
105 // Get the size of an object with its `size()` method
106 template <typename C>
107 size_t size(C& c, std::true_type) {
108  return c.size();
109 }
110 
111 // Get the size of an object the *slow* way by iterating through it
112 template <typename C>
113 size_t size(C& c, std::false_type) {
114  return std::distance(c.begin(), c.end());
115 }
116 
117 // -----------------------------------------------------------------------------
118 // transfer
119 
120 // Copy or move only one value
121 template <typename DEST, typename SRC>
122 void transfer(std::true_type, DEST& dst, SRC& src) {
123  dst = src;
124 }
125 
126 template <typename DEST, typename SRC>
127 void transfer(std::false_type, DEST& dst, SRC& src) {
128  dst = std::move(src);
129 }
130 
131 // -----------------------------------------------------------------------------
132 // range_transfer
133 
134 // Copy or move a range of data to another range of data. Don't use std::copy or
135 // std::move algorithms to preserve the side effects of incrementing
136 template <typename DIT, typename IT>
137 void range_transfer(std::true_type, DIT&& dst_cur, IT&& src_cur, IT&& src_end) {
138  for(; src_cur != src_end; ++src_cur, ++dst_cur) {
139  *dst_cur = *src_cur;
140  }
141 }
142 
143 template <typename DIT, typename IT>
144 void range_transfer(std::false_type, DIT&& dst_cur, IT&& src_cur, IT&& src_end) {
145  for(; src_cur != src_end; ++src_cur, ++dst_cur) {
146  *dst_cur = std::move(*src_cur);
147  }
148 }
149 
150 // -----------------------------------------------------------------------------
151 // values
152 
153 // copy pointed values
154 template <typename OUT_IT, typename INP_IT>
155 void
156 values(std::true_type, OUT_IT&& out_it, INP_IT&& cur_it, INP_IT&& end_it) {
157  while(cur_it != end_it) {
158  *out_it = **cur_it; // dereference pointer
159  ++cur_it;
160  ++out_it;
161  }
162 }
163 
164 // copy values
165 template <typename OUT_IT, typename INP_IT>
166 void
167 values(std::false_type, OUT_IT&& out_it, INP_IT&& cur_it, INP_IT&& end_it) {
168  while(cur_it != end_it) {
169  *out_it = *cur_it;
170  ++cur_it;
171  ++out_it;
172  }
173 }
174 
175 // -----------------------------------------------------------------------------
176 // sum
177 
178 template <typename V>
179 size_t sum(V cur_sum, V v) {
180  return cur_sum + v;
181 }
182 
183 template <typename V, typename... Values>
184 size_t sum(V cur_sum, V v, V v2, Values... vs) {
185  return sum(cur_sum + v, v2, vs...);
186 }
187 
188 // -----------------------------------------------------------------------------
189 // group
190 
191 template <typename IT>
192 void group(IT&& cur) {
193 }
194 
195 template <typename IT, typename C, typename... Cs>
196 void group(IT&& cur, C&& c, Cs&&... cs) {
197  detail::range_transfer(detail::is_lvalue_ref_t<C>(), cur, c.begin(), c.end());
198  group(cur, std::forward<Cs>(cs)...);
199 }
200 
201 // -----------------------------------------------------------------------------
202 // advance group
203 
204 /*
205  * The purpose of this algorithm is to increment any number of iterators by reference
206  */
207 template <typename IT>
208 void advance_group(IT& it) {
209  ++it;
210 }
211 
212 template <typename IT, typename IT2, typename... ITs>
213 void advance_group(IT& it, IT2& it2, ITs&... its) {
214  ++it;
215  advance_group(it2, its...);
216 }
217 
218 // -----------------------------------------------------------------------------
219 // map
220 template <typename F, typename RIT, typename IT, typename... ITs>
221 void map(F&& f, RIT&& rit, IT&& it, IT&& it_end, ITs&&... its) {
222  while(it != it_end) {
223  *rit = f(*it, *its...);
224  advance_group(rit, it, its...);
225  }
226 }
227 
228 // ----------------------------------------------------------------------------
229 // fold
230 template <typename F,
231  typename R,
232  typename IT,
233  typename... ITs>
234 std::decay_t<R>
235 fold(F& f, R&& init, IT&& it, IT&& it_end, ITs&&... its) {
236  std::decay_t<R> mutable_state(std::forward<R>(init));
237 
238  while(it != it_end) {
239  mutable_state = f(std::move(mutable_state), *it, *its...);
240  advance_group(it, its...);
241  }
242 
243  return mutable_state;
244 }
245 
246 // -----------------------------------------------------------------------------
247 // each
248 template <typename F, typename IT, typename... ITs>
249 void each(F&& f, IT&& it, IT&& it_end, ITs&&... its) {
250  while(it != it_end) {
251  f(*it, *its...);
252  advance_group(it, its...);
253  }
254 }
255 
256 // ----------------------------------------------------------------------------
257 // all
258 template <typename F, typename IT, typename... ITs>
259 bool
260 all(F&& f, IT&& it, IT&& it_end, ITs&&... its) {
261  bool ret = true;
262 
263  while(it != it_end) {
264  if(!f(*it, *its...)) {
265  ret = false;
266  break;
267  }
268 
269  advance_group(it, its...);
270  }
271 
272  return ret;
273 }
274 
275 // ----------------------------------------------------------------------------
276 // some
277 template <typename F, typename IT, typename... ITs>
278 bool
279 some(F&& f, IT&& it, IT&& it_end, ITs&&... its) {
280  bool ret = false;
281 
282  while(it != it_end) {
283  if(f(*it, *its...)) {
284  ret = true;
285  break;
286  }
287 
288  advance_group(it, its...);
289  }
290 
291  return ret;
292 }
293 
294 }
295 
296 //------------------------------------------------------------------------------
297 // size
298 
304 template <typename C>
305 size_t // size_t is convertable from all std:: container `size_type`s
306 size(C&& c) {
307  return detail::size(c, detail::has_size<C>());
308 }
309 
310 //------------------------------------------------------------------------------
311 // pointers
312 
332 template <typename C>
333 auto
334 pointers(C& c) {
335  typedef typename std::decay_t<C>::value_type CV;
336  std::vector<CV*> ret(sca::size(c));
337  std::transform(c.begin(), c.end(), ret.begin(), [](CV& e){ return &e; });
338  return ret;
339 }
340 
341 template <typename C>
342 auto
343 pointers(const C& c) {
344  typedef typename std::decay_t<C>::value_type CV;
345  std::vector<const CV*> ret(sca::size(c));
346  std::transform(c.begin(), c.end(), ret.begin(), [](const CV& e){ return &e; });
347  return ret;
348 }
349 
350 //------------------------------------------------------------------------------
351 // values
352 
366 template <typename C>
367 auto
368 values(C&& c) {
369  typedef typename std::decay_t<C>::value_type CV;
370  typedef typename std::decay_t<std::remove_pointer_t<CV>> BCV; // base container value type
371  std::vector<BCV> ret(sca::size(c));
372  detail::values(typename std::is_pointer<CV>::type(), ret.begin(), c.begin(), c.end());
373  return ret;
374 }
375 
376 //------------------------------------------------------------------------------
377 // slice
378 
380 template<typename C>
381 class slice_of {
382  typedef std::decay_t<C> DC;
383 
384 public:
385  typedef typename DC::iterator iterator;
386  typedef typename DC::value_type value_type;
387  typedef typename DC::size_type size_type;
388 
389  slice_of() = delete; // no default initialization
390  slice_of(const C& c, size_t idx, size_t len) = delete; // must use const_slice_of
391 
392  // mutable lvalue constructor
393  slice_of(C& c, size_t idx, size_t len) :
394  m_size(len),
395  m_begin(std::next(c.begin(), idx)),
396  m_end(std::next(m_begin, len))
397  { }
398 
399  // rvalue constructor
400  slice_of(C&& c, size_t idx, size_t len) :
401  m_mem(std::make_shared<DC>(std::move(c))), // keep container in memory
402  m_size(len),
403  m_begin(std::next(m_mem->begin(), idx)),
404  m_end(std::next(m_begin, len))
405  { }
406 
407  // iterator constructor
408  template <typename IT>
409  slice_of(IT begin, IT end) :
410  m_size(std::distance(begin, end)),
411  m_begin(std::move(begin)),
412  m_end(std::move(end))
413  { }
414 
416  inline size_t size() const {
417  return m_size;
418  }
419 
421  inline auto begin() {
422  return m_begin;
423  }
424 
426  inline auto end() {
427  return m_end;
428  }
429 
430 private:
431  std::shared_ptr<DC> m_mem; // place to hold container memory if constructed with an rvalue
432  const size_t m_size;
433  iterator m_begin;
434  iterator m_end;
435 };
436 
438 template <typename C>
440  typedef std::decay_t<C> DC;
441 
442 public:
443  typedef typename DC::const_iterator const_iterator;
444  typedef typename DC::value_type value_type;
445  typedef typename DC::size_type size_type;
446 
447  const_slice_of() = delete; // no default initialization
448 
449  // const lvalue constructor
450  const_slice_of(const C& c, size_t idx, size_t len) :
451  m_size(len),
452  m_cbegin(std::next(c.cbegin(), idx)),
453  m_cend(std::next(m_cbegin, len))
454  { }
455 
456  // iterator constructor
457  template <typename IT>
458  const_slice_of(IT begin, IT end) :
459  m_size(std::distance(begin, end)),
460  m_cbegin(std::move(begin)),
461  m_cend(std::move(end))
462  { }
463 
465  inline size_t size() const {
466  return m_size;
467  }
468 
470  inline auto begin() const {
471  return m_cbegin;
472  }
473 
475  inline auto end() const {
476  return m_cend;
477  }
478 
479 private:
480  const size_t m_size;
481  const_iterator m_cbegin;
482  const_iterator m_cend;
483 };
484 
508 template <typename C>
509 auto
510 slice(C&& c, size_t idx, size_t len) {
511  return slice_of<C>(std::forward<C>(c), idx, len);
512 }
513 
519 template <typename C>
520 auto
521 slice(const C& c, size_t idx, size_t len) {
522  return const_slice_of<C>(c, idx, len);
523 }
524 
533 template <typename C>
534 auto
535 slice(C& c, size_t idx, size_t len) {
536  return const_slice_of<C>(c, idx, len);
537 }
538 
556 template <typename C>
557 auto
558 mslice(C&& c, size_t idx, size_t len) {
559  return slice_of<C>(std::forward<C>(c), idx, len);
560 }
561 
562 // explicitly catch mutable lvalue reference so compiler doesn't convert to `const C&`
563 template <typename C>
564 auto
565 mslice(C& c, size_t idx, size_t len) {
566  return slice_of<C>(c, idx, len);
567 }
568 
569 //------------------------------------------------------------------------------
570 // group
571 
580 template <typename C, typename C2, typename... Cs>
581 auto
582 group(C&& c, C2&& c2, Cs&&... cs) {
583  detail::to_vector_t<C> ret(detail::sum(sca::size(c), sca::size(c2), sca::size(cs)...));
584  detail::group(ret.begin(), std::forward<C>(c), std::forward<C2>(c2), std::forward<Cs>(cs)...);
585  return ret;
586 }
587 
588 //------------------------------------------------------------------------------
589 // reverse
590 
596 template <typename C>
597 auto
598 reverse(C&& c) {
599  detail::to_vector_t<C> ret(sca::size(c));
600  detail::range_transfer(detail::is_lvalue_ref_t<C>(), ret.begin(), c.begin(), c.end());
601  std::reverse(ret.begin(), ret.end());
602  return ret;
603 }
604 
605 //------------------------------------------------------------------------------
606 // sort
607 
614 template <typename C, typename F>
615 auto
616 sort(C&& c, F&& cmp) {
617  detail::to_vector_t<C> ret(sca::size(c));
618  detail::range_transfer(detail::is_lvalue_ref_t<C>(), ret.begin(), c.begin(), c.end());
619  std::sort(ret.begin(), ret.end(), cmp);
620  return ret;
621 }
622 
623 //------------------------------------------------------------------------------
624 // filter
625 
632 template <typename F, typename C>
633 auto
634 filter(F&& f, C&& c) {
635  detail::to_vector_t<C> ret(sca::size(c));
636  size_t cur = 0;
637 
638  for(auto& e : c) {
639  if(f(e)) {
640  detail::transfer(detail::is_lvalue_ref_t<C>(), ret[cur], e);
641  ++cur;
642  }
643  }
644 
645  ret.resize(cur);
646  return ret;
647 }
648 
649 //------------------------------------------------------------------------------
650 // map
651 
668 template <typename F, typename C, typename... Cs>
669 auto
670 map(F&& f, C&& c, Cs&&... cs) {
671  typedef detail::callable_return_t<
672  F,
673  detail::container_reference_value_t<C>,
674  detail::container_reference_value_t<Cs>...
675  > FR;
676 
677  std::vector<FR> ret(sca::size(c));
678  detail::map(std::forward<F>(f), ret.begin(), c.begin(), c.end(), cs.begin()...);
679  return ret;
680 }
681 
682 //------------------------------------------------------------------------------
683 // fold
684 
707 template <typename F, typename Result, typename C, typename... Cs>
708 auto
709 fold(F&& f, Result&& init, C&& c, Cs&&... cs) {
710  return detail::fold(f, std::forward<Result>(init), c.begin(), c.end(), cs.begin()...);
711 }
712 
713 //------------------------------------------------------------------------------
714 // for_each
715 
731 template <typename F, typename C, typename... Cs>
732 void
733 each(F&& f, C&& c, Cs&&... cs) {
734  detail::each(f, c.begin(), c.end(), cs.begin()...);
735 }
736 
737 //------------------------------------------------------------------------------
738 // all
739 
753 template <typename F, typename C, typename... Cs>
754 bool
755 all(F&& f, C&& c, Cs&&... cs) {
756  return detail::all(f, c.begin(), c.end(), cs.begin()...);
757 }
758 
759 //------------------------------------------------------------------------------
760 // some
761 
775 template <typename F, typename C, typename... Cs>
776 bool
777 some(F&& f, C&& c, Cs&&... cs) {
778  return detail::some(f, c.begin(), c.end(), cs.begin()...);
779 }
780 
781 }
782 
783 #endif
const variation of slice_of
Definition: scalgorithm.hpp:439
auto end() const
return a const_iterator to the end of the container subset
Definition: scalgorithm.hpp:475
size_t size() const
return the iterable length of the slice
Definition: scalgorithm.hpp:465
auto begin() const
return a const_iterator to the beginning of the container subset
Definition: scalgorithm.hpp:470
the underlying type returned by slice() representing a subset of a container
Definition: scalgorithm.hpp:381
size_t size() const
return the iterable length of the slice
Definition: scalgorithm.hpp:416
auto end()
return an iterator to the end of the container subset
Definition: scalgorithm.hpp:426
auto begin()
return an iterator to the beginning of the container subset
Definition: scalgorithm.hpp:421
Definition: scalgorithm.hpp:55
auto filter(F &&f, C &&c)
return a filtered container of elements
Definition: scalgorithm.hpp:634
auto slice(C &&c, size_t idx, size_t len)
create a slice_of object from a container which allows iteration over a subset of another container
Definition: scalgorithm.hpp:510
size_t size(C &&c)
return an iterable container's size, regardless if it implements a size() method
Definition: scalgorithm.hpp:306
auto reverse(C &&c)
return a container where the order of elements is the reverse of the input container
Definition: scalgorithm.hpp:598
auto fold(F &&f, Result &&init, C &&c, Cs &&... cs)
perform a calculation on the elements of containers grouped by index
Definition: scalgorithm.hpp:709
auto map(F &&f, C &&c, Cs &&... cs)
evaluate function with the elements of containers grouped by index and return a container filled with...
Definition: scalgorithm.hpp:670
void each(F &&f, C &&c, Cs &&... cs)
evaluate function with the elements of containers grouped by index
Definition: scalgorithm.hpp:733
auto values(C &&c)
return a container of deep value copies (never pointers) from a container of values or pointers
Definition: scalgorithm.hpp:368
auto mslice(C &&c, size_t idx, size_t len)
create a mutable slice_of object which allows iteration of a subset of another container
Definition: scalgorithm.hpp:558
bool all(F &&f, C &&c, Cs &&... cs)
evaluate if a function returns true with all the elements of containers grouped by index
Definition: scalgorithm.hpp:755
auto group(C &&c, C2 &&c2, Cs &&... cs)
assemble a container containing all elements of two or more containers
Definition: scalgorithm.hpp:582
auto sort(C &&c, F &&cmp)
return a container whose elements are sorted based on a comparison Callable
Definition: scalgorithm.hpp:616
auto pointers(C &c)
copy the addresses of elements in a container to a new container
Definition: scalgorithm.hpp:334
bool some(F &&f, C &&c, Cs &&... cs)
evaluate if a function returns true with at least one of the elements of containers grouped by index
Definition: scalgorithm.hpp:777
Definition: scalgorithm.hpp:96
Definition: scalgorithm.hpp:94