1 // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
2 // https://github.com/Dobiasd/FunctionalPlus
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 //  http://www.boost.org/LICENSE_1_0.txt)
6 
7 #pragma once
8 
9 #include <fplus/function_traits.hpp>
10 #include <fplus/container_common.hpp>
11 #include <fplus/pairs.hpp>
12 #include <fplus/internal/invoke.hpp>
13 
14 #include <algorithm>
15 #include <cmath>
16 #include <functional>
17 #include <limits>
18 #include <stdexcept>
19 #include <type_traits>
20 
21 namespace fplus
22 {
23 
24 // API search type: is_in_interval : (a, a, a) -> Bool
25 // fwd bind count: 2
26 // Checks if x is in [low, high), i.e. left-closed and right-open.
27 template <typename T>
is_in_interval(const T & low,const T & high,const T & x)28 bool is_in_interval(const T& low, const T& high, const T& x)
29 {
30     return (low <= x) && (x < high);
31 }
32 
33 // API search type: is_in_interval_around : (a, a, a) -> Bool
34 // fwd bind count: 2
35 // Checks if x is in [center - radius, center + radius),
36 // i.e. left-closed and right-open.
37 template <typename T>
is_in_interval_around(const T & radius,const T & center,const T & x)38 bool is_in_interval_around(const T& radius, const T& center, const T& x)
39 {
40     return is_in_interval(center - radius, center + radius, x);
41 }
42 
43 // API search type: is_in_open_interval : (a, a, a) -> Bool
44 // fwd bind count: 2
45 // Checks if x is in (low, high), i.e. left-open and right-open.
46 template <typename T>
is_in_open_interval(const T & low,const T & high,const T & x)47 bool is_in_open_interval(const T& low, const T& high, const T& x)
48 {
49     return (low < x) && (x < high);
50 }
51 
52 // API search type: is_in_open_interval_around : (a, a, a) -> Bool
53 // fwd bind count: 2
54 // Checks if x is in (center - radius, center + radius),
55 // i.e. left-open and right-open.
56 template <typename T>
is_in_open_interval_around(const T & radius,const T & center,const T & x)57 bool is_in_open_interval_around(const T& radius, const T& center, const T& x)
58 {
59     return is_in_open_interval(center - radius, center + radius, x);
60 }
61 
62 // API search type: is_in_closed_interval : (a, a, a) -> Bool
63 // fwd bind count: 2
64 // Checks if x is in [low, high], i.e. left-closed and right-closed.
65 template <typename T>
is_in_closed_interval(const T & low,const T & high,const T & x)66 bool is_in_closed_interval(const T& low, const T& high, const T& x)
67 {
68     return (low <= x) && (x <= high);
69 }
70 
71 // API search type: is_in_closed_interval_around : (a, a, a) -> Bool
72 // Checks if x is in [center - radius, center + radius],
73 // i.e. left-closed and right-closed.
74 template <typename T>
is_in_closed_interval_around(const T & radius,const T & center,const T & x)75 bool is_in_closed_interval_around(const T& radius, const T& center, const T& x)
76 {
77     return is_in_closed_interval(center - radius, center + radius, x);
78 }
79 
80 // API search type: reference_interval : (Float, Float, Float, Float, Float) -> Float
81 // fwd bind count: 4
82 // Linearly projects a value
83 // from [old_low, old_high] into [new_low, new_high].
84 // Does not clamp.
85 // reference_interval(2, 6, 0, 4, 3) == 5
86 // reference_interval(2, 10, 0, 4, 3) == 8
87 // reference_interval(2, 6, 0, 4, -1) == 1
88 // reference_interval(2, 10, 0, 4, -1) == 0
89 template <typename T>
reference_interval(const T & new_low,const T & new_high,const T & old_low,const T & old_high,const T & x)90 T reference_interval(const T& new_low, const T& new_high,
91     const T& old_low, const T& old_high, const T& x)
92 {
93     const T scale = (new_high - new_low) / (old_high - old_low);
94     return scale * (x - old_low) + new_low;
95 }
96 
97 // API search type: clamp : (a, a, a) -> a
98 // fwd bind count: 2
99 // Puts value into [low, high], i.e. left-closed and right-closed.
100 template <typename T>
clamp(const T & low,const T & high,const T & x)101 T clamp(const T& low, const T& high, const T& x)
102 {
103     return std::max(low, std::min(high, x));
104 }
105 
106 // API search type: is_negative : a -> Bool
107 // fwd bind count: 0
108 // Checks if x < 0.
109 template <typename X>
is_negative(X x)110 bool is_negative(X x)
111 {
112     return x < 0;
113 }
114 
115 // API search type: is_positive : a -> Bool
116 // fwd bind count: 0
117 // Checks if x is not negative.
118 template <typename X>
is_positive(X x)119 bool is_positive(X x)
120 {
121     return !is_negative(x);
122 }
123 
124 namespace internal
125 {
126     template <typename X>
127     typename std::enable_if<std::is_unsigned<X>::value, X>::type
abs_helper(X x)128     abs_helper(X x)
129     {
130         return x;
131     }
132 
133     template <typename X>
134     typename std::enable_if<!std::is_unsigned<X>::value, X>::type
abs_helper(X x)135     abs_helper(X x)
136     {
137         return std::abs(x);
138     }
139 }
140 
141 // API search type: abs : a -> a
142 // fwd bind count: 0
143 // Returns the absolute (always non-negative) value of x.
144 template <typename X>
abs(X x)145 X abs(X x)
146 {
147     return internal::abs_helper(x);
148 }
149 
150 // API search type: abs_diff : (a, a) -> a
151 // fwd bind count: 1
152 // Returns the absolute difference of two values.
153 template <typename X>
abs_diff(X a,X b)154 X abs_diff(X a, X b)
155 {
156     return a > b ? a - b : b - a;
157 }
158 
159 // API search type: square : a -> a
160 // fwd bind count: 0
161 // Returns the square (x*x) of a value x.
162 template <typename X>
square(X x)163 X square(X x)
164 {
165     return x * x;
166 }
167 
168 // API search type: cube : a -> a
169 // fwd bind count: 0
170 // Returns the cube (x*x*x) of a value x.
171 template <typename X>
cube(X x)172 X cube(X x)
173 {
174     return x * x * x;
175 }
176 
177 // API search type: sign : a -> Int
178 // fwd bind count: 0
179 // Returns -1 for negative values, 1 otherwise.
180 // sign(-3) == -1
181 // sign(0) == 1
182 // sign(16) == 1
183 template <typename X>
sign(X x)184 int sign(X x)
185 {
186     return is_negative(x) ? -1 : 1;
187 }
188 
189 // API search type: sign_with_zero : a -> Int
190 // fwd bind count: 0
191 // Returns -1 for negative values, 0 for zero, 1 for positive values.
192 // sign_with_zero(-3) == -1
193 // sign_with_zero(0) == 0
194 // sign_with_zero(16) == 1
195 template <typename X>
sign_with_zero(X x)196 int sign_with_zero(X x)
197 {
198     return x == 0 ? 0 : sign(x);
199 }
200 
201 // API search type: integral_cast_throw : Int -> Int
202 // fwd bind count: 0
203 // Converts one integer type into another.
204 // Throws an std::underflow_error or std::overflow_error
205 // if the value does not fit into the destination type.
206 template <typename Out, typename X>
207 Out integral_cast_throw(X x)
208 {
209 #ifdef _MSC_VER
210 __pragma(warning(push))
211 __pragma(warning(disable:4127))
212 #endif
213     static_assert(std::is_integral<X>::value, "type must be integral");
214     static_assert(std::is_integral<Out>::value, "type must be integral");
215     if (std::is_signed<X>::value && std::is_signed<Out>::value)
216     {
217         if (static_cast<std::int64_t>(x) <
218             static_cast<std::int64_t>(std::numeric_limits<Out>::lowest()))
219         {
220             throw std::underflow_error("");
221         }
222         if (static_cast<std::int64_t>(x) >
223             static_cast<std::int64_t>(std::numeric_limits<Out>::max()))
224         {
225             throw std::overflow_error("");
226         }
227         return static_cast<Out>(x);
228     }
229     else if (!std::is_signed<X>::value && !std::is_signed<Out>::value)
230     {
231         if (static_cast<std::uint64_t>(x) <
232             static_cast<std::uint64_t>(std::numeric_limits<Out>::lowest()))
233         {
234             throw std::underflow_error("");
235         }
236         if (static_cast<std::uint64_t>(x) >
237             static_cast<std::uint64_t>(std::numeric_limits<Out>::max()))
238         {
239             throw std::overflow_error("");
240         }
241         return static_cast<Out>(x);
242     }
243     else if (std::is_signed<X>::value && !std::is_signed<Out>::value)
244     {
245         if (x < 0)
246             return 0;
247         if (static_cast<std::uint64_t>(x) >
248             static_cast<std::uint64_t>(std::numeric_limits<Out>::max()))
249         {
250             throw std::overflow_error("");
251         }
252         return static_cast<Out>(x);
253     }
254     else if (!std::is_signed<X>::value && std::is_signed<Out>::value)
255     {
256         if (static_cast<std::uint64_t>(x) >
257             static_cast<std::uint64_t>(std::numeric_limits<Out>::max()))
258         {
259             throw std::overflow_error("");
260         }
261         return static_cast<Out>(x);
262     }
263     else
264     {
265         assert(false);
266         return static_cast<Out>(x);
267     }
268 #ifdef _MSC_VER
269 __pragma(warning(pop))
270 #endif
271 }
272 
273 // API search type: integral_cast_clamp : Int -> Int
274 // fwd bind count: 0
275 // Converts one integer type into another.
276 // If the value does not fit into the destination type,
277 // the nearest possible value is used.
278 // Also known as saturate_cast.
279 template <typename Out, typename X>
280 Out integral_cast_clamp(X x)
281 {
282     static_assert(std::is_integral<X>::value, "type must be integral");
283     static_assert(std::is_integral<Out>::value, "type must be integral");
284     if (std::is_signed<X>::value && std::is_signed<Out>::value)
285     {
286         if (static_cast<std::int64_t>(x) <
287             static_cast<std::int64_t>(std::numeric_limits<Out>::lowest()))
288         {
289             return std::numeric_limits<Out>::lowest();
290         }
291         if (static_cast<std::int64_t>(x) >
292             static_cast<std::int64_t>(std::numeric_limits<Out>::max()))
293         {
294             return std::numeric_limits<Out>::max();
295         }
296         return static_cast<Out>(x);
297     }
298     else if (!std::is_signed<X>::value && !std::is_signed<Out>::value)
299     {
300         if (static_cast<std::uint64_t>(x) <
301             static_cast<std::uint64_t>(std::numeric_limits<Out>::lowest()))
302         {
303             return std::numeric_limits<Out>::lowest();
304         }
305         if (static_cast<std::uint64_t>(x) >
306             static_cast<std::uint64_t>(std::numeric_limits<Out>::max()))
307         {
308             return std::numeric_limits<Out>::max();
309         }
310         return static_cast<Out>(x);
311     }
312     else if (std::is_signed<X>::value && !std::is_signed<Out>::value)
313     {
314         if (x < 0)
315             return 0;
316         if (static_cast<std::uint64_t>(x) >
317             static_cast<std::uint64_t>(std::numeric_limits<Out>::max()))
318         {
319             return std::numeric_limits<Out>::max();
320         }
321         return static_cast<Out>(x);
322     }
323     else if (!std::is_signed<X>::value && std::is_signed<Out>::value)
324     {
325         if (static_cast<std::uint64_t>(x) >
326             static_cast<std::uint64_t>(std::numeric_limits<Out>::max()))
327         {
328             return std::numeric_limits<Out>::max();
329         }
330         return static_cast<Out>(x);
331     }
332     else
333     {
334         assert(false);
335         return static_cast<Out>(x);
336     }
337 }
338 
339 // API search type: round : a -> Int
340 // fwd bind count: 0
341 // Converts a value to the nearest integer.
342 template <typename X, typename Out = int>
round(X x)343 Out round(X x)
344 {
345     static_assert(!std::is_integral<X>::value, "type must be non-integral");
346     static_assert(std::is_integral<Out>::value, "type must be integral");
347     if (static_cast<double>(x) < static_cast<double>(std::numeric_limits<Out>::lowest()))
348         return std::numeric_limits<Out>::lowest();
349     if (static_cast<double>(x) > static_cast<double>(std::numeric_limits<Out>::max()))
350         return std::numeric_limits<Out>::max();
351     if (is_negative(x))
352         x -= 1;
353     return static_cast<Out>(x + 0.5);
354 }
355 
356 // API search type: floor : a -> b
357 // fwd bind count: 0
358 // Converts a value to the nearest smaller integer.
359 template <typename X, typename Out = int>
floor(X x)360 Out floor(X x)
361 {
362     static_assert(!std::is_integral<X>::value, "type must be non-integral");
363     static_assert(std::is_integral<Out>::value, "type must be integral");
364     if (is_negative(x))
365         x -= 1;
366     return static_cast<Out>(x);
367 }
368 
369 // API search type: floor_to_int_mult : (Int, Int) -> Int
370 // fwd bind count: 1
371 // Rounds an integer down to the nearest smaller or equal multiple of n.
372 // n may not be zero.
373 template <typename X>
floor_to_int_mult(X n,X x)374 X floor_to_int_mult(X n, X x)
375 {
376     static_assert(std::is_integral<X>::value, "type must be integral");
377     assert(n != 0);
378     if (is_negative(n))
379         n = abs(n);
380     if (is_negative(x) && n != 1)
381         x = static_cast<X>(x - 1);
382     return static_cast<X>((x / n) * n);
383 }
384 
385 // API search type: ceil_to_int_mult : (Int, Int) -> Int
386 // fwd bind count: 1
387 // Rounds an integer up to the nearest greater or equal multiple of n.
388 // n may not be zero.
389 template <typename X>
ceil_to_int_mult(X n,X x)390 X ceil_to_int_mult(X n, X x)
391 {
392     return floor_to_int_mult(n, static_cast<X>(x + abs(n) - 1));
393 }
394 
395 // API search type: ceil : a -> b
396 // fwd bind count: 0
397 // Converts a value to the nearest greater integer.
398 template <typename X, typename Out = int>
ceil(X x)399 Out ceil(X x)
400 {
401     static_assert(!std::is_integral<X>::value, "type must be non-integral");
402     static_assert(std::is_integral<Out>::value, "type must be integral");
403     return floor<X, Out>(x) + 1;
404 }
405 
406 // API search type: int_power : (Int, Int) -> Int
407 // fwd bind count: 1
408 // integer power, only exponents >= 0
409 template <typename X>
int_power(X base,X exp)410 X int_power(X base, X exp)
411 {
412     static_assert(std::is_integral<X>::value,
413         "type must be unsigned integral");
414     assert(!is_negative(exp));
415     if (exp == 0)
416         return 1;
417     if (exp == 1)
418         return base;
419     return base * int_power(base, exp - 1);
420 }
421 
422 namespace internal
423 {
424     // minimum of x values after transformation
425     // (has an overload for non-POD types)
426     // min_on(mod2, 4, 3) == 4
427     // min_on(mod7, 3, 5, 7, 3) == 7
428     template <typename F, typename FirstT, typename... FIn>
helper_min_on(F f,const FirstT & first,const FIn &...v)429     auto helper_min_on(F f, const FirstT& first, const FIn&... v) ->
430         typename std::common_type<FirstT, FIn...>::type
431     {
432       using rettype = typename std::common_type<FirstT, FIn...>::type;
433       using f_rettype = std::decay_t<internal::invoke_result_t<F, decltype(first)>>;
434 
435       rettype result = first;
436       f_rettype result_trans = internal::invoke(f, first);
437       f_rettype v_trans;
438       unused(result_trans);
439       unused(v_trans);
440 
441       (void)std::initializer_list<int>{
442           ((v_trans = internal::invoke(f, v), v_trans < result_trans)
443                ? (result = static_cast<rettype>(v), result_trans = v_trans, 0)
444                : 0)...};
445       return result;
446     }
447 
448     template <typename F>
449     struct helper_min_on_t
450     {
helper_min_on_tfplus::internal::helper_min_on_t451         helper_min_on_t(F _f) : f(_f) {}
452         template <typename T, typename... Ts>
operator ()fplus::internal::helper_min_on_t453         auto operator()(T&& x, Ts&&... xs) -> typename std::common_type<T, Ts...>::type
454         {
455             return helper_min_on(std::forward<F>(f), std::forward<T>(x), std::forward<Ts>(xs)...);
456         }
457     private:
458         F f;
459     };
460 }
461 
462 // API search type: min_on : ((a -> b), a, a) -> a
463 // minimum of x values after transformation (curried version)
464 // min_on(mod2)(4, 3) == 4
465 // min_on(mod7)(3, 5, 7, 3) == 7
466 template <typename F>
min_on(F f)467 auto min_on(F f) -> internal::helper_min_on_t<F>
468 {
469     return internal::helper_min_on_t<F>{f};
470 }
471 
472 // API search type: min_2_on : ((a -> b), a, a) -> a
473 // fwd bind count: 2
474 // minimum of 2 values after transformation
475 // min_2_on(mod2, 4, 3) == 4
476 template <typename F, typename T>
min_2_on(F f,const T & x,const T & y)477 T min_2_on(F f, const T& x, const T& y)
478 {
479     return internal::invoke(f, y) < internal::invoke(f, x) ? y : x;
480 }
481 
482 namespace internal
483 {
484     // maximum of x values after transformation
485     // (has an overload for non-POD types)
486     // max_on(mod2, 4, 3) == 3
487     // max_on(mod7, 3, 5, 7, 3) == 5
488     template <typename F, typename FirstT, typename... FIn>
helper_max_on(F f,const FirstT & first,const FIn &...v)489     auto helper_max_on(F f, const FirstT& first, const FIn&... v) ->
490         typename std::common_type<FirstT, FIn...>::type
491     {
492       using rettype = typename std::common_type<FirstT, FIn...>::type;
493       using f_rettype = decltype(f(first));
494 
495       rettype result = first;
496       f_rettype result_trans = internal::invoke(f, first);
497       f_rettype v_trans;
498       unused(result_trans);
499       unused(v_trans);
500 
501       (void)std::initializer_list<int>{
502           ((v_trans = internal::invoke(f, v), v_trans > result_trans)
503                ? (result = static_cast<rettype>(v), result_trans = v_trans, 0)
504                : 0)...};
505       return result;
506     }
507 
508     template <typename F>
509     struct helper_max_on_t
510     {
helper_max_on_tfplus::internal::helper_max_on_t511         helper_max_on_t(F _f) : f(_f) {}
512         template <typename T, typename... Ts>
operator ()fplus::internal::helper_max_on_t513         auto operator()(T&& x, Ts&&... xs) -> typename std::common_type<T, Ts...>::type
514         {
515             return helper_max_on(std::forward<F>(f), std::forward<T>(x), std::forward<Ts>(xs)...);
516         }
517     private:
518         F f;
519     };
520 }
521 
522 // API search type: max_on : (a -> b) -> ((a, a) -> a)
523 // maximum of x values after transformation (curried version)
524 // (has an overload for non POD types)
525 // max_on(mod2)(4, 3) == 3
526 // max_on(mod7)(3, 5, 7, 3) == 5
527 template <typename F>
max_on(F f)528 auto max_on(F f) -> internal::helper_max_on_t<F>
529 {
530     return internal::helper_max_on_t<F>{f};
531 }
532 
533 // API search type: max_2_on : ((a -> b), a, a) -> a
534 // fwd bind count: 2
535 // maximum of 2 values after transformation
536 // max_2_on(mod2, 4, 3) == 3
537 template <typename F, typename T>
max_2_on(F f,const T & x,const T & y)538 T max_2_on(F f, const T& x, const T& y)
539 {
540     return internal::invoke(f, y) > internal::invoke(f, x) ? y : x;
541 }
542 
543 // API search type: min : (a, a) -> a
544 // Minimum of x number of values
545 // min(4, 3) == 3
546 // min(4, 3, 6, 2, 3) == 2
547 template <typename U, typename... V>
min(const U & u,const V &...v)548 auto min(const U& u, const V&... v) -> typename std::common_type<U, V...>::type
549 {
550   using rettype = typename std::common_type<U, V...>::type;
551   rettype result = static_cast<rettype>(u);
552   (void)std::initializer_list<int>{((v < result) ? (result = static_cast<rettype>(v), 0) : 0)...};
553   return result;
554 }
555 
556 // API search type: min_2 : (a, a) -> a
557 // fwd bind count: 1
558 // minimum of 2 values
559 // min_2(4, 3) == 3
560 template <typename T>
min_2(const T & x,const T & y)561 T min_2(const T& x, const T& y)
562 {
563     return y < x ? y : x;
564 }
565 
566 // API search type: max : (a, a) -> a
567 // Maximum of x number of values.
568 // max(4, 3) == 4
569 // max(4, 3, 6, 2, 3) == 6
570 template <typename U, typename... V>
max(const U & u,const V &...v)571 auto max(const U& u, const V&... v) -> typename std::common_type<U, V...>::type
572 {
573   using rettype = typename std::common_type<U, V...>::type;
574   rettype result = static_cast<rettype>(u);
575   (void)std::initializer_list<int>{((v > result) ? (result = static_cast<rettype>(v), 0) : 0)...};
576   return result;
577 }
578 
579 // API search type: max_2 : (a, a) -> a
580 // fwd bind count: 1
581 // maximum of 2 values
582 // max_2(4, 3) == 4
583 template <typename T>
max_2(const T & x,const T & y)584 T max_2(const T& x, const T& y)
585 {
586     return y > x ? y : x;
587 }
588 
589 namespace internal
590 {
591     template <typename X>
592     typename std::enable_if<std::is_floating_point<X>::value, X>::type
cyclic_value_helper_mod(X x,X y)593     cyclic_value_helper_mod(X x, X y)
594     {
595         return std::fmod(x, y);
596     }
597 
598     template <typename X>
599     typename std::enable_if<std::is_integral<X>::value, X>::type
cyclic_value_helper_mod(X x,X y)600     cyclic_value_helper_mod(X x, X y)
601     {
602         return x % y;
603     }
604 }
605 
606 // API search type: cyclic_value : a -> (a -> a)
607 // Modulo for floating point values.
608 // circumfence must be > 0
609 // cyclic_value(8)(3) == 3
610 // cyclic_value(8)(11) == 3
611 // cyclic_value(8)(19) == 3
612 // cyclic_value(8)(-2) == 6
613 // cyclic_value(8)(-5) == 3
614 // cyclic_value(8)(-13) == 3
615 // Can be useful to normalize an angle into [0, 360]
616 // For positive values it behaves like std::fmod with flipped arguments.
617 template <typename X>
cyclic_value(X circumfence)618 std::function<X(X)> cyclic_value(X circumfence)
619 {
620     assert(circumfence > 0);
621     return [circumfence](X x) -> X
622     {
623         if (sign(x) < 0)
624             return circumfence - internal::cyclic_value_helper_mod(
625                 abs(x), abs(circumfence));
626         else
627             return internal::cyclic_value_helper_mod(
628                 abs(x), abs(circumfence));
629     };
630 }
631 
632 // API search type: cyclic_difference : a -> ((a, a) -> a)
633 // Returns the distance the first value has to advance forward on a circle
634 // to reach the second value.
635 // circumfence must be > 0
636 // cyclic_difference(100)(5, 2) == 3
637 // cyclic_difference(100)(2, 5) == 97
638 // cyclic_difference(100)(3, -2) == 5
639 // cyclic_difference(100)(-2, 3) == 95
640 // cyclic_difference(100)(90, 10) == 80
641 // cyclic_difference(100)(10, 90) == 20
642 template <typename X>
cyclic_difference(X circumfence)643 std::function<X(X, X)> cyclic_difference(X circumfence)
644 {
645     assert(circumfence > 0);
646     return [circumfence](X a, X b) -> X
647     {
648         auto cyclic_value_f = cyclic_value(circumfence);
649         const auto c_v_a = cyclic_value_f(a);
650         const auto c_v_b = cyclic_value_f(b);
651         return c_v_a > c_v_b ?
652             c_v_a - c_v_b :
653             circumfence + c_v_a - c_v_b;
654     };
655 }
656 
657 // API search type: cyclic_shortest_difference : a -> ((a, a) -> a)
658 // Returns displacement (shortest way) the first value has to move on a circle
659 // to reach the second value.
660 // circumfence must be > 0
661 // cyclic_shortest_difference(100)(5, 2) == 3
662 // cyclic_shortest_difference(100)(2, 5) == -3
663 // cyclic_shortest_difference(100)(3, -2) == 5
664 // cyclic_shortest_difference(100)(-2, 3) == -5
665 // cyclic_shortest_difference(100)(90, 10) == -20
666 // cyclic_shortest_difference(100)(10, 90) == 20
667 template <typename X>
cyclic_shortest_difference(X circumfence)668 std::function<X(X, X)> cyclic_shortest_difference(X circumfence)
669 {
670     assert(circumfence > 0);
671     return [circumfence](X a, X b) -> X
672     {
673         auto diff_func = cyclic_difference(circumfence);
674         auto a_minus_b = diff_func(a, b);
675         auto b_minus_a = diff_func(b, a);
676         return a_minus_b <= b_minus_a ? a_minus_b : -b_minus_a;
677     };
678 }
679 
680 // API search type: cyclic_distance : a -> ((a, a) -> a)
681 // Returns distance (shortest way) the first value has to move on a circle
682 // to reach the second value.
683 // circumfence must be > 0
684 // cyclic_distance(100)(2, 5) == 3
685 // cyclic_distance(100)(5, 2) == 3
686 // cyclic_distance(100)(-2, 3) == 5
687 // cyclic_distance(100)(3, -2) == 5
688 // cyclic_distance(100)(10, 90) == 20
689 // cyclic_distance(100)(90, 10) == 20
690 // Can be useful to calculate the difference of two angles;
691 template <typename X>
cyclic_distance(X circumfence)692 std::function<X(X, X)> cyclic_distance(X circumfence)
693 {
694     assert(circumfence > 0);
695     return [circumfence](X a, X b) -> X
696     {
697         auto diff_func = cyclic_difference(circumfence);
698         auto a_minus_b = diff_func(a, b);
699         auto b_minus_a = diff_func(b, a);
700         return a_minus_b <= b_minus_a ? a_minus_b : b_minus_a;
701     };
702 }
703 
704 // API search type: pi : () -> Float
705 // Pi.
pi()706 constexpr inline double pi()
707 {
708     return 3.14159265358979323846;
709 }
710 
711 // API search type: deg_to_rad : Float -> Float
712 // fwd bind count: 0
713 // converts degrees to radians
714 template <typename T>
deg_to_rad(T x)715 T deg_to_rad(T x)
716 {
717     static_assert(std::is_floating_point<T>::value, "Please use a floating-point type.");
718     return static_cast<T>(x * pi() / 180.0);
719 }
720 
721 // API search type: rad_to_deg : Float -> Float
722 // fwd bind count: 0
723 // converts radians to degrees
724 template <typename T>
rad_to_deg(T x)725 T rad_to_deg(T x)
726 {
727     static_assert(std::is_floating_point<T>::value, "Please use a floating-point type.");
728     return static_cast<T>(x * 180.0 / pi());
729 }
730 
731 namespace internal
732 {
733 
734 template <typename Container, typename T>
735 Container normalize_min_max(internal::reuse_container_t,
736     const T& lower, const T& upper, Container&& xs)
737 {
738     assert(size_of_cont(xs) != 0);
739     assert(lower <= upper);
740     const auto minmax_it_p = std::minmax_element(std::begin(xs), std::end(xs));
741     const T x_min = *minmax_it_p.first;
742     const T x_max = *minmax_it_p.second;
743     const auto f = [&](const T& x) -> T
__anon8f97d98b0502(const T& x) 744     {
745         return lower + (upper - lower) * (x - x_min) / (x_max - x_min);
746     };
747     std::transform(std::begin(xs), std::end(xs), std::begin(xs), f);
748     return std::forward<Container>(xs);
749 }
750 
751 template <typename Container, typename T>
752 Container normalize_min_max(internal::create_new_container_t,
753     const T& lower, const T& upper, const Container& xs)
754 {
755     auto ys = xs;
756     return normalize_min_max(internal::reuse_container_t(),
757         lower, upper, std::move(ys));
758 }
759 
760 } // namespace internal
761 
762 // API search type: normalize_min_max : (a, a, [a]) -> [a]
763 // fwd bind count: 2
764 // Linearly scales the values into the given interval.
765 // normalize_min_max(0, 10, [1, 3, 6]) == [0, 4, 10]
766 // It is recommended to convert integers to double beforehand.
767 template <typename Container,
768     typename T = typename internal::remove_const_and_ref_t<Container>::value_type>
normalize_min_max(const T & lower,const T & upper,Container && xs)769 auto normalize_min_max(const T& lower, const T& upper, Container&& xs)
770 {
771     return internal::normalize_min_max(internal::can_reuse_v<Container>{},
772         lower, upper, std::forward<Container>(xs));
773 }
774 
775 namespace internal
776 {
777 
778 template <typename Container, typename T>
779 Container normalize_mean_stddev(internal::reuse_container_t,
780     const T& mean, const T& stddev, Container&& xs)
781 {
782     assert(size_of_cont(xs) != 0);
783     const auto mean_and_stddev = fplus::mean_stddev<T>(xs);
784     const auto f = [&](const T& x) -> T
__anon8f97d98b0602(const T& x) 785     {
786         return mean +
787             stddev * (x - mean_and_stddev.first) / mean_and_stddev.second;
788     };
789     std::transform(std::begin(xs), std::end(xs), std::begin(xs), f);
790     return std::forward<Container>(xs);
791 }
792 
793 template <typename Container, typename T>
794 Container normalize_mean_stddev(internal::create_new_container_t,
795     const T& mean, const T& stddev, const Container& xs)
796 {
797     auto ys = xs;
798     return normalize_mean_stddev(internal::reuse_container_t(),
799         mean, stddev, std::move(ys));
800 }
801 
802 } // namespace internal
803 
804 // API search type: normalize_mean_stddev : (a, a, [a]) -> [a]
805 // fwd bind count: 2
806 // Linearly scales the values
807 // to match the given mean and population standard deviation.
808 // normalize_mean_stddev(3, 2, [7, 8]) == [1, 5]
809 template <typename Container,
810     typename T = typename internal::remove_const_and_ref_t<Container>::value_type>
normalize_mean_stddev(const T & mean,const T & stddev,Container && xs)811 auto normalize_mean_stddev(
812     const T& mean, const T& stddev, Container&& xs)
813 {
814     return internal::normalize_mean_stddev(internal::can_reuse_v<Container>{},
815         mean, stddev, std::forward<Container>(xs));
816 }
817 
818 // API search type: standardize : [a] -> [a]
819 // fwd bind count: 0
820 // Linearly scales the values to zero mean and population standard deviation 1.
821 // standardize([7, 8]) == [-1, 1]
822 template <typename Container>
standardize(Container && xs)823 auto standardize(Container&& xs)
824 {
825     typedef typename internal::remove_const_and_ref_t<Container>::value_type T;
826     T mean(0);
827     T stddev(1);
828     return normalize_mean_stddev(mean, stddev, std::forward<Container>(xs));
829 }
830 
831 // API search type: add_to : a -> (a -> a)
832 // Provide a function adding to a given constant.
833 // add_to(3)(2) == 5
834 template <typename X>
add_to(const X & x)835 std::function<X(X)> add_to(const X& x)
836 {
837     return [x](X y) -> X
838     {
839         return x + y;
840     };
841 }
842 
843 // API search type: subtract_from : a -> (a -> a)
844 // Provide a function subtracting from a given constant.
845 // subtract_from(3)(2) == 1
846 template <typename X>
subtract_from(const X & x)847 std::function<X(X)> subtract_from(const X& x)
848 {
849     return [x](X y) -> X
850     {
851         return x - y;
852     };
853 }
854 
855 // API search type: subtract : a -> (a -> a)
856 // Provide a function subtracting a given constant.
857 // subtract(2)(3) == 1
858 template <typename X>
subtract(const X & x)859 std::function<X(X)> subtract(const X& x)
860 {
861     return [x](X y) -> X
862     {
863         return y - x;
864     };
865 }
866 
867 // API search type: multiply_with : a -> (a -> a)
868 // Provide a function multiplying with a given constant.
869 // multiply_with(3)(2) == 6
870 template <typename X>
multiply_with(const X & x)871 std::function<X(X)> multiply_with(const X& x)
872 {
873     return [x](X y) -> X
874     {
875         return y * x;
876     };
877 }
878 
879 // API search type: divide_by : a -> (a -> a)
880 // Provide a function dividing by a given constant.
881 // divide_by(2)(6) == 3
882 template <typename X>
divide_by(const X & x)883 std::function<X(X)> divide_by(const X& x)
884 {
885     return [x](X y) -> X
886     {
887         return y / x;
888     };
889 }
890 
891 // API search type: histogram_using_intervals : ([(a, a)], [a]) -> [((a, a), Int)]
892 // fwd bind count: 1
893 // Generate a histogram of a sequence with given bins.
894 // histogram_using_intervals([(0,4), (4,5), (6,8)], [0,1,4,5,6,7,8,9]) ==
895 //     [((0, 4), 2), ((4, 5), 1), ((6, 8), 2)]
896 template <typename ContainerIn,
897         typename ContainerIntervals,
898         typename ContainerOut =
899             std::vector<
900                 std::pair<
901                     typename ContainerIntervals::value_type,
902                     std::size_t>>,
903         typename T = typename ContainerIn::value_type>
904 ContainerOut histogram_using_intervals(
905         const ContainerIntervals& intervals, const ContainerIn& xs)
906 {
907     ContainerOut bins;
908     internal::prepare_container(bins, size_of_cont(intervals));
909     auto itOut = internal::get_back_inserter(bins);
910     for (const auto& interval : intervals)
911     {
912         *itOut = std::make_pair(interval, 0);
913     }
914     for (const auto& x : xs)
915     {
916         for (auto& bin : bins)
917         {
918             if (x >= bin.first.first && x < bin.first.second)
919             {
920                 ++bin.second;
921             }
922         }
923     }
924     return bins;
925 }
926 
927 // API search type: generate_consecutive_intervals : (a, a, a) -> [(a, a)]
928 // fwd bind count: 2
929 // Return intervals of a given size adjacent to each other
930 // generate_consecutive_intervals(0, 2, 4) == [(0,2), (2,4), (4,6), (6,8)]
931 template <typename T>
generate_consecutive_intervals(const T & first_lower_bound,const T & step,std::size_t count)932 std::vector<std::pair<T, T>> generate_consecutive_intervals(
933         const T& first_lower_bound, const T& step, std::size_t count)
934 {
935     const auto count_as_T = static_cast<T>(count);
936     return zip(
937         numbers_step<T>(
938             first_lower_bound,
939             first_lower_bound + count_as_T * step,
940             step),
941         numbers_step<T>(
942             first_lower_bound + step,
943             first_lower_bound + step + count_as_T * step,
944             step));
945 }
946 
947 // API search type: histogram : (a, a, a, [a]) -> [((a, a), Int)]
948 // fwd bind count: 3
949 // Calculate the histogram of a sequence using a given bin width.
950 // histogram(1, 2, 4, [0,1,4,5,7,8,9]) == [(1, 2), (3, 0), (5, 2), (7, 1)]
951 template <typename ContainerIn,
952         typename ContainerOut =
953             std::vector<
954                 std::pair<
955                     typename ContainerIn::value_type,
956                     std::size_t>>,
957         typename T = typename ContainerIn::value_type>
958 ContainerOut histogram(
959         const T& first_center, const T& bin_width, std::size_t count,
960         const ContainerIn& xs)
961 {
962     const auto interval_histogram = histogram_using_intervals(
963         generate_consecutive_intervals(
964             first_center - bin_width / 2,
965             bin_width,
966             count),
967         xs);
968 
969     assert(size_of_cont(interval_histogram) == count);
970 
971     ContainerOut histo;
972     internal::prepare_container(histo, count);
973     auto itOut = internal::get_back_inserter(histo);
974     for (const auto& bin : interval_histogram)
975     {
976         const auto current_center = (bin.first.first + bin.first.second) / 2;
977         *itOut = std::make_pair(current_center, bin.second);
978     }
979     return histo;
980 }
981 
982 // API search type: modulo_chain : ([Int], Int) -> [Int]
983 // fwd bind count: 1
984 // For every factor (value % factor) is pushed into the result,
985 // and value is divided by this factor for the next iteration.
986 // Can be useful to convert a time in seconds
987 // into hours, minutes and seconds and similar calculations.
988 // modulo_chain([24, 60, 60], 7223) == [0, 2, 0, 23]
989 template <typename T>
modulo_chain(const std::vector<T> & factors,T val)990 std::vector<T> modulo_chain(const std::vector<T>& factors, T val)
991 {
992     std::vector<T> result;
993     result.reserve(factors.size());
994     const auto factors_reversed = reverse(factors);
995     for (const auto& factor : factors_reversed)
996     {
997         result.push_back(val % factor);
998         val /= factor;
999     }
1000     result.push_back(val);
1001     return reverse(result);
1002 }
1003 
1004 // API search type: line_equation : ((Float, Float), (Float, Float), Float) -> Float
1005 // fwd bind count: 2
1006 // Can be used to interpolate and to extrapolate
1007 // based on two given two-dimensional points (x, y).
1008 // Using slope, return NaN if x_1 == x_2.
1009 // line_equation((0.0, 0.0), (2.0, 1.0), 3.0) == 1.5
1010 // line_equation((-1.0, 1.0), (-2.0, 4.0), 0.0) == -2.0
1011 template <typename T>
line_equation(const std::pair<T,T> & a,const std::pair<T,T> & b,T x)1012 T line_equation(const std::pair<T, T>& a, const std::pair<T, T>& b, T x)
1013 {
1014     static_assert(std::is_floating_point<T>::value, "Please use a floating-point type.");
1015     const double m = (b.second - a.second) / (b.first - a.first);
1016     return m * x + a.second - m * a.first;
1017 }
1018 
1019 } // namespace fplus
1020