emlabcpp
modern opinionated embedded C++ library
algorithm.h
Go to the documentation of this file.
1 
24 #pragma once
25 
26 #include "./algorithm/impl.h"
27 #include "./bounded.h"
28 #include "./min_max.h"
29 #include "./range.h"
30 #include "./types.h"
31 #include "./view.h"
32 
33 #include <cmath>
34 #include <cstdlib>
35 #include <tuple>
36 
37 namespace emlabcpp
38 {
39 
40 constexpr float default_epsilon = 1.19e-07f;
41 
43 template < typename T >
44 constexpr int sign( T const& val )
45 {
46  using value_type = std::decay_t< T >;
47  if ( value_type{ 0 } > val )
48  return -1;
49  if ( value_type{ 0 } < val )
50  return 1;
51  return 0;
52 }
53 
55 template < typename T >
56 [[nodiscard]] constexpr T ceil_to( T val, T base )
57 {
58  auto m = val % base;
59  if ( m == 0 )
60  return val;
61  return val + base - m;
62 }
63 
65 template < additive_operators T, arithmetic_operators U >
66 [[nodiscard]] constexpr U map_range( T input, T from_min, T from_max, U to_min, U to_max )
67 {
68  return to_min + static_cast< U >(
69  ( to_max - to_min ) * static_cast< U >( input - from_min ) /
70  static_cast< U >( from_max - from_min ) );
71 }
72 
74 template < container Container >
75 [[nodiscard]] constexpr std::size_t cont_size( Container const& cont ) noexcept
76 {
77  if constexpr ( static_sized< Container > )
78  return std::tuple_size_v< Container >;
79  else
80  return std::size( cont );
81 }
82 
85 template < typename T >
86 [[nodiscard]] constexpr bool
87 almost_equal( T const& lh, T const& rh, float const eps = default_epsilon )
88 {
89  return float( std::abs( lh - rh ) ) < eps;
90 }
91 
93 template < referenceable_container Container, typename Iterator = iterator_of_t< Container > >
94 [[nodiscard]] constexpr view< Iterator > tail( Container&& cont, int const step = 1 )
95 {
96  return view< Iterator >( std::next( std::begin( cont ), step ), std::end( cont ) );
97 }
98 
100 template < referenceable_container Container, typename Iterator = iterator_of_t< Container > >
101 [[nodiscard]] constexpr view< Iterator > init( Container&& cont, int const step = 1 )
102 {
103  return view< Iterator >( std::begin( cont ), std::end( cont ) - step );
104 }
105 
109 template <
110  range_container Container,
111  container_invocable< Container > PredicateCallable = std::identity >
112 [[nodiscard]] constexpr auto find_if( Container&& cont, PredicateCallable&& f = std::identity() )
113 {
114  auto beg = std::begin( cont );
115  auto end = std::end( cont );
116  for ( ; beg != end; ++beg )
117  if ( f( *beg ) )
118  return beg;
119  return cont.end();
120 }
121 
124 template <
125  gettable_container Container,
126  container_invocable< Container > PredicateCallable = std::identity >
127 requires( !range_container< Container > )
128 [[nodiscard]] constexpr std::size_t
129 find_if( Container&& t, PredicateCallable&& f = std::identity() )
130 {
131  return impl::find_if_impl(
132  std::forward< Container >( t ),
133  std::forward< PredicateCallable >( f ),
134  std::make_index_sequence< std::tuple_size_v< std::decay_t< Container > > >{} );
135 }
136 
139 template < container Container, typename T >
140 [[nodiscard]] constexpr auto find( Container&& cont, T const& item )
141 {
142  return find_if( std::forward< Container >( cont ), [&]( auto const& sub_item ) {
143  return sub_item == item;
144  } );
145 }
146 
148 template < container Container, typename T >
149 [[nodiscard]] constexpr bool contains( Container const& cont, T const& item )
150 {
151  if constexpr ( range_container< Container > )
152  return find( cont, item ) != cont.end();
153  else
154  return find( cont, item ) != cont_size( cont );
155 }
156 
158 template < gettable_container Container, container_invocable< Container > UnaryCallable >
159 requires( !range_container< Container > )
160 constexpr void for_each( Container&& cont, UnaryCallable&& f )
161 {
162  std::apply(
163  [&f]< typename... Items >( Items&&... items ) {
164  ( f( std::forward< Items >( items ) ), ... );
165  },
166  std::forward< Container >( cont ) );
167 }
168 
170 template < range_container Container, container_invocable< Container > UnaryCallable >
171 constexpr void for_each( Container&& cont, UnaryCallable&& f )
172 {
173  for ( auto&& item : std::forward< Container >( cont ) )
174  f( std::forward< decltype( item ) >( item ) );
175 }
176 
180 template <
181  container Container,
182  container_invocable< Container > UnaryCallable = std::identity,
183  typename T = std::decay_t< mapped_t< Container, UnaryCallable > > >
184 [[nodiscard]] constexpr min_max< T >
185 min_max_elem( Container&& cont, UnaryCallable&& f = std::identity() )
186 {
190 
191  for_each( cont, [&]( auto& item ) {
192  auto val = f( item );
193  res.max() = std::max( res.max(), val );
194  res.min() = std::min( res.min(), val );
195  } );
196  return res;
197 }
198 
202 template <
203  container Container,
204  container_invocable< Container > UnaryCallable = std::identity,
205  typename T = std::decay_t< mapped_t< Container, UnaryCallable > > >
206 [[nodiscard]] constexpr T max_elem( Container&& cont, UnaryCallable&& f = std::identity() )
207 {
209  for_each( cont, [&]( auto& item ) {
210  val = std::max( f( item ), val );
211  } );
212  return val;
213 }
214 
218 template <
219  container Container,
220  container_invocable< Container > UnaryCallable = std::identity,
221  typename T = std::decay_t< mapped_t< Container, UnaryCallable > > >
222 [[nodiscard]] constexpr T min_elem( Container&& cont, UnaryCallable&& f = std::identity() )
223 {
225  for_each( cont, [&]( auto& item ) {
226  val = std::min( f( item ), val );
227  } );
228  return val;
229 }
230 
233 template < container Container, container_invocable< Container > UnaryCallable = std::identity >
234 [[nodiscard]] constexpr std::size_t count( Container&& cont, UnaryCallable&& f = std::identity() )
235 {
236  std::size_t res = 0;
237  for_each( cont, [&]( auto& item ) {
238  if ( f( item ) )
239  res += 1;
240  } );
241  return res;
242 }
243 
246 template <
247  container Container,
248  container_invocable< Container > UnaryCallable = std::identity,
249  typename T = std::decay_t< mapped_t< Container, UnaryCallable > > >
250 [[nodiscard]] constexpr T sum( Container&& cont, UnaryCallable&& f = std::identity(), T init = {} )
251 {
252  for_each( cont, [&]( auto& item ) {
253  init += f( item );
254  } );
255  return init;
256 }
257 
260 template < container Container, typename T, typename BinaryCallable >
261 [[nodiscard]] constexpr T accumulate( Container&& cont, T init, BinaryCallable&& f )
262 {
263  for_each( cont, [&]( auto& item ) {
264  init = f( std::move( init ), item );
265  } );
266  return init;
267 }
268 
271 template <
272  container Container,
273  container_invocable< Container > UnaryCallable = std::identity,
274  typename T = std::decay_t< mapped_t< Container, UnaryCallable > > >
275 [[nodiscard]] constexpr T avg( Container&& cont, UnaryCallable&& f = std::identity() )
276 {
277  T res{};
278  for_each( cont, [&]( auto& item ) {
279  res += f( item );
280  } );
281  if constexpr ( std::is_arithmetic_v< T > )
282  return res / static_cast< T >( cont_size( cont ) );
283  else
284  return res / cont_size( cont );
285 }
286 
289 template <
290  container Container,
291  container_invocable< Container > UnaryCallable = std::identity,
292  typename T = std::decay_t< mapped_t< Container, UnaryCallable > > >
293 [[nodiscard]] constexpr T variance( Container&& cont, UnaryCallable&& f = std::identity() )
294 {
295  T u = avg( cont, f );
296 
297  T res = sum( cont, [&f, &u]( auto& val ) {
298  auto v = f( val ) - u;
299  return v * v;
300  } );
301  if constexpr ( std::is_arithmetic_v< T > )
302  return res / static_cast< T >( cont_size( cont ) );
303  else
304  return res / cont_size( cont );
305 }
306 
309 template < container LhContainer, container RhContainer, typename BinaryCallable >
310 constexpr void for_cross_joint( LhContainer&& lh_cont, RhContainer&& rh_cont, BinaryCallable&& f )
311 {
312  for_each( lh_cont, [&]( auto& lh_item ) {
313  for_each( rh_cont, [&]( auto& rh_item ) {
314  f( lh_item, rh_item );
315  } );
316  } );
317 }
318 
321 template < container Container, container_invocable< Container > PredicateCallable = std::identity >
322 [[nodiscard]] constexpr bool any_of( Container&& cont, PredicateCallable&& f = std::identity() )
323 {
324  auto res = find_if( cont, std::forward< PredicateCallable >( f ) );
325 
326  if constexpr ( is_std_tuple_v< Container > )
327  return res != std::tuple_size_v< std::decay_t< Container > >;
328  else
329  return res != cont.end();
330 }
331 
334 template < container Container, container_invocable< Container > PredicateCallable = std::identity >
335 [[nodiscard]] constexpr bool none_of( Container&& cont, PredicateCallable&& f = std::identity() )
336 {
337  return !any_of( cont, std::forward< PredicateCallable >( f ) );
338 }
339 
341 template < container Container, container_invocable< Container > PredicateCallable = std::identity >
342 [[nodiscard]] constexpr bool all_of( Container&& cont, PredicateCallable&& f = std::identity() )
343 {
344  return !any_of( cont, [&]( auto& item ) {
345  return !f( item );
346  } );
347 }
348 
351 template <
352  range_container LhContainer,
353  range_container RhContainer,
354  typename BinaryPredicateCallable = std::equal_to< void > >
355 [[nodiscard]] constexpr bool
356 equal( LhContainer&& lh, RhContainer&& rh, BinaryPredicateCallable&& f = std::equal_to< void >{} )
357 {
358  if ( cont_size( lh ) != cont_size( rh ) )
359  return false;
360  auto rbeg = std::begin( rh );
361  for ( auto& item : lh ) {
362  if ( !f( item, *rbeg ) )
363  return false;
364  ++rbeg;
365  }
366  return true;
367 }
368 
376 template <
377  impl::map_f_collectable ResultContainer,
378  container Container,
379  container_invocable< Container > UnaryCallable = std::identity >
380 [[nodiscard]] ResultContainer map_f( Container&& cont, UnaryCallable&& f = std::identity() )
381 {
382  ResultContainer res{};
384 
385  for_each( std::forward< Container >( cont ), [&]< typename Item >( Item&& item ) {
386  collector.collect( res, f( std::forward< Item >( item ) ) );
387  } );
388  return res;
389 }
390 
393 template <
394  std::size_t N,
395  range_container Container,
396  container_invocable< Container > UnaryCallable = std::identity,
397  typename T = std::decay_t< mapped_t< Container, UnaryCallable > > >
398 [[nodiscard]] std::array< T, N > map_f_to_a( Container&& cont, UnaryCallable&& f = std::identity() )
399 requires( !static_sized< Container > )
400 {
401  return impl::map_f_to_a_impl< T, N >(
402  std::forward< Container >( cont ),
403  std::forward< UnaryCallable >( f ),
404  std::make_index_sequence< N >() );
405 }
406 
409 template <
410  gettable_container Container,
411  std::size_t N = std::tuple_size< std::decay_t< Container > >::value,
412  container_invocable< Container > UnaryCallable = std::identity,
413  typename T = std::decay_t< mapped_t< Container, UnaryCallable > > >
414 [[nodiscard]] std::array< T, N > map_f_to_a( Container&& cont, UnaryCallable&& f = std::identity() )
415 requires static_sized< Container >
416 {
417  return impl::map_f_to_a_impl< T, N >(
418  std::forward< Container >( cont ),
419  std::forward< UnaryCallable >( f ),
420  std::make_index_sequence< N >() );
421 }
422 
425 template < typename T >
427 {
428  template < typename U >
429  constexpr T operator()( U&& src ) const
430  noexcept( noexcept( T{ std::forward< U >( src ) } ) )
431  {
432  return T{ std::forward< U >( src ) };
433  }
434 };
435 
439 template <
440  range_container Container,
441  typename T,
442  container_invocable< Container > UnaryCallable = std::identity >
443 [[nodiscard]] constexpr T
444 joined( Container&& cont, T const& val, UnaryCallable&& f = std::identity() )
445 {
446  if ( cont.empty() )
447  return T{};
448  T res = f( *std::begin( cont ) );
449  for ( auto& item : tail( cont ) )
450  res += val + f( item );
451  return res;
452 }
453 
454 template < container Container, typename Iterator >
455 void copy( Container&& cont, Iterator iter )
456 {
457  for_each( cont, [&]( auto& item ) {
458  *iter = item;
459  ++iter;
460  } );
461 }
462 
465 template < std::size_t N, typename NullCallable >
466 constexpr void for_each_index( NullCallable&& f )
467 {
468  impl::index_seq< 0, N >( f );
469 }
470 
474 template < std::size_t N, typename PredicateCallable >
475 constexpr std::size_t find_if_index( PredicateCallable&& f )
476 {
477  std::size_t res = N;
478  until_index< N >( [&f, &res]< std::size_t i >() {
479  res = i;
480  return f.template operator()< i >();
481  } );
482  return res;
483 }
484 
487 template < std::size_t i, typename PredicateCallable >
488 constexpr bool until_index( PredicateCallable&& f )
489 {
490  return impl::index_until< 0, i >( f );
491 }
492 
496 //
498 template < bounded_derived IndexType, typename Callable >
499 requires( !requires( Callable f ) {
500  { f.template operator()< 0 >() } -> std::same_as< void >;
501 } )
502 constexpr auto select_index( IndexType i, Callable&& f )
503 {
504  using T = std::decay_t< decltype( f.template operator()< 0 >() ) >;
505  T res{};
506  select_index( i, [&res, &f]< std::size_t i >() {
507  res = f.template operator()< i >();
508  } );
509  return res;
510 }
511 
512 template < bounded_derived IndexType, typename Callable >
513 requires requires( Callable f ) {
514  { f.template operator()< 0 >() } -> std::same_as< void >;
515 }
516 constexpr void select_index( IndexType i, Callable&& f )
517 {
518  static_assert( IndexType::min_val == 0 );
519  impl::index_switch< 0, IndexType::max_val + 1 >( *i, f );
520 }
521 
523 template < typename... Args, std::size_t N = sizeof...( Args ) >
524 constexpr std::array< std::byte, N > bytes( Args const&... args )
525 {
526  return std::array< std::byte, N >{ static_cast< std::byte >( args )... };
527 }
528 
530 template < typename Arr, typename... Arrs >
531 constexpr auto merge_arrays( Arr&& first, Arrs&&... arrs )
532 {
534 
535  static_assert(
536  ( std::convertible_to< typename std::decay_t< Arrs >::value_type, value_type > && ... &&
537  true ),
538  "All arrays have to provide similar types" );
539 
540  constexpr std::size_t size =
541  ( std::tuple_size_v< std::decay_t< Arrs > > + ... +
542  std::tuple_size_v< std::decay_t< Arr > > );
543 
544  auto f = [&]< std::size_t... Is >( std::index_sequence< Is... > ) {
545  return std::array< value_type, size >{
546  impl::get_ith_item_from_arrays< Is >( first, arrs... )... };
547  };
548  return f( std::make_index_sequence< size >{} );
549 }
550 
552 template < std::size_t N, typename T >
553 constexpr std::array< T, N > filled( T const& item )
554 {
555  return map_f_to_a< N >( range( N ), [&]( auto ) -> T {
556  return item;
557  } );
558 }
559 
560 } // namespace emlabcpp
Generic class to represent view of some container.
Definition: view.h:41
hdr_state next(hdr_state cs) noexcept
Definition: page.h:43
constexpr std::size_t find_if_impl(std::tuple< Args... > const &t, UnaryPredicate &&f, std::index_sequence< Idx... >)
Definition: impl.h:35
concept map_f_collectable
Definition: impl.h:130
Definition: impl.h:96
std::variant< int64_t, float, bool, string_buffer > value_type
Definition: base.h:51
MIT License.
Definition: impl.h:31
constexpr std::size_t count(Container &&cont, UnaryCallable &&f=std::identity())
Applies the predicate 'f(x)' to each element of container 'cont' and returns the count of items,...
Definition: algorithm.h:234
constexpr T min_elem(Container &&cont, UnaryCallable &&f=std::identity())
Applies unary callable 'f(x) to each element of container 'cont‘, returns the smallest return value o...
Definition: algorithm.h:222
constexpr U map_range(T input, T from_min, T from_max, U to_min, U to_max)
maps input value 'input' from input range to equivalent value in output range
Definition: algorithm.h:66
std::array< T, N > map_f_to_a(Container &&cont, UnaryCallable &&f=std::identity()) requires(!static_sized< Container >)
Calls callable f(cont[i]) for i = 0...N and stores the result in array of an size N.
Definition: algorithm.h:398
constexpr float default_epsilon
Definition: algorithm.h:40
select_index(i, [&res, &f]< std::size_t i >() { res=f.template operator()< i >();})
constexpr view< Iterator > tail(Container &&cont, int const step=1)
Returns range over Container, which skips first item of container.
Definition: algorithm.h:94
constexpr std::size_t find_if_index(PredicateCallable &&f)
Executes unary callable f() with template argument of type 'std::size_t', which ranges from 0 to N un...
Definition: algorithm.h:475
constexpr T accumulate(Container &&cont, T init, BinaryCallable &&f)
Applies callable 'f(init,x)' to each element of container 'x' and actual value of 'init' in iteration...
Definition: algorithm.h:261
constexpr bool until_index(PredicateCallable &&f)
Executes predicate f() with template argument of type 'std::size_t', which ranges from 0 to i until f...
Definition: algorithm.h:488
constexpr bool none_of(Container &&cont, PredicateCallable &&f=std::identity())
Returns true if call to predicate 'f(x)' returns false for all items in 'cont'.
Definition: algorithm.h:335
constexpr bool all_of(Container &&cont, PredicateCallable &&f=std::identity())
Returns true if call to predicate 'f(x)' returns true for all items in 'cont'.
Definition: algorithm.h:342
constexpr T max_elem(Container &&cont, UnaryCallable &&f=std::identity())
Applies unary callable 'f(x)' to each element of container 'cont', returns the largest return value o...
Definition: algorithm.h:206
constexpr T ceil_to(T val, T base)
takes an value val and rounds it up to nearest multiply of base
Definition: algorithm.h:56
constexpr T sum(Container &&cont, UnaryCallable &&f=std::identity(), T init={})
Applies f(x) to each item of container 'cont', returns the sum of all the return values of each call ...
Definition: algorithm.h:250
constexpr T joined(Container &&cont, T const &val, UnaryCallable &&f=std::identity())
Function applies callable f to each item in container cont and contacts results with operator+,...
Definition: algorithm.h:444
constexpr T variance(Container &&cont, UnaryCallable &&f=std::identity())
Applies callable 'f(x)' to each element of container 'cont' and returns the variance of values return...
Definition: algorithm.h:293
constexpr bool equal(LhContainer &&lh, RhContainer &&rh, BinaryPredicateCallable &&f=std::equal_to< void >{})
Returns true if containers 'lh' and 'rh' has same size and calls to predicate f - f(lh[i],...
Definition: algorithm.h:356
constexpr T avg(Container &&cont, UnaryCallable &&f=std::identity())
Applies callable 'f(x)' to each element of container 'cont' and returns the average value of each cal...
Definition: algorithm.h:275
constexpr void for_each_index(NullCallable &&f)
Executes unary callable f() with template argument of type 'std::size_t', which ranges from 0 to N.
Definition: algorithm.h:466
constexpr std::array< std::byte, N > bytes(Args const &... args)
Conveft the provided arguments into array of std::byte.
Definition: algorithm.h:524
void copy(Container &&cont, Iterator iter)
Definition: algorithm.h:455
constexpr view< Iterator > init(Container &&cont, int const step=1)
Returns range over Container, which skips last item of container.
Definition: algorithm.h:101
constexpr bool any_of(Container &&cont, PredicateCallable &&f=std::identity())
Returns true if call to predicate 'f(x)' returns true for at least one item x in 'cont'.
Definition: algorithm.h:322
Args const & args
Definition: min_max.h:83
T value_type
Definition: static_storage.h:100
constexpr Derived abs(vec_point_base< Derived, N > const &a)
Creates absolute version of A - removing signs on all dimensions.
Definition: vec_point_base.h:219
constexpr void for_each(Container &&cont, UnaryCallable &&f)
Applies unary callable 'f' to each element of container 'cont'.
Definition: algorithm.h:171
constexpr std::array< T, N > filled(T const &item)
Constructs an array filled with value x
Definition: algorithm.h:553
concept static_sized
Definition: concepts.h:116
constexpr int sign(T const &val)
returns sign of variable T: -1,0,1
Definition: algorithm.h:44
concept range_container
so, std::ranges::range is meh because it expects return of begin() being input_output_iterator,...
Definition: concepts.h:78
constexpr void for_cross_joint(LhContainer &&lh_cont, RhContainer &&rh_cont, BinaryCallable &&f)
Applies binary callable 'f(x,y)' to each combination of items x from lh_cont and y from rh_cont
Definition: algorithm.h:310
T res
Definition: algorithm.h:505
requires(!range_container< Container >) const expr std
Returns index of an element in tuple 't', for which call to predicate f(x) holds true,...
Definition: algorithm.h:127
constexpr min_max< T > min_max_elem(Container &&cont, UnaryCallable &&f=std::identity())
Applies unary callable 'f(x)' to each element of container 'cont', returns the largest and the smalle...
Definition: algorithm.h:185
constexpr Derived max(vec_point_base< Derived, N > const &a, vec_point_base< Derived, N > const &b)
Definition: vec_point_base.h:229
UnaryCallable
Definition: types.h:54
constexpr auto find(Container &&cont, T const &item)
Finds first item in container 'cont' that is equal to 'item', returns iterator for container,...
Definition: algorithm.h:140
constexpr Derived const & min(vec_point_base< Derived, N > const &a, vec_point_base< Derived, N > const &b)
Definition: vec_point_base.h:236
concept container
Definition: concepts.h:93
constexpr bool contains(Container const &cont, T const &item)
Checks if container cont contains at least one occurence of item, returns true/false.
Definition: algorithm.h:149
constexpr view< iterators::numeric_iterator< Numeric > > range(Numeric from, Numeric to)
Builds numeric view over interval [from, to)
Definition: range.h:34
constexpr auto merge_arrays(Arr &&first, Arrs &&... arrs)
Expects multiple std::arrays on input, and merges all together into one std::array instance.
Definition: algorithm.h:531
constexpr auto find_if(Container &&cont, PredicateCallable &&f=std::identity())
Returns iterator for first item, for which call to predicate f(*iter) holds true.
Definition: algorithm.h:112
concept gettable_container
Definition: concepts.h:71
constexpr bool almost_equal(T const &lh, T const &rh, float const eps=default_epsilon)
Two items 'lh' and 'rh' are almost equal if their difference is smaller than value 'eps'.
Definition: algorithm.h:87
ResultContainer map_f(Container &&cont, UnaryCallable &&f=std::identity())
Calls callable f(x) for each item in container 'cont' (or tuple) and stores result in 'ResultContaine...
Definition: algorithm.h:380
UnaryCallable && f
Definition: algorithm.h:161
N
Definition: static_storage.h:97
physical_quantity< 0, 0, 0, 0, 0, 0, 0, 0, 1 > byte
Definition: physical_quantity.h:118
constexpr std::size_t cont_size(Container const &cont) noexcept
Returns the size of the container, regardless of what it is.
Definition: algorithm.h:75
Object with operator() that constructs object of type Tout of passed-in value.
Definition: algorithm.h:427
constexpr T operator()(U &&src) const noexcept(noexcept(T{ std::forward< U >(src) }))
Definition: algorithm.h:429
A structure representing a range defined by a minimum and a maximum value.
Definition: min_max.h:37
constexpr static T lowest()
Definition: quantity.h:288