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