C++ Library Extensions 2022.12.09
To help learn modern C++ programming
tpf_std_extensions.hpp
Go to the documentation of this file.
1
12#ifndef _TPF_STD_EXTENSIONS_HPP
13#define _TPF_STD_EXTENSIONS_HPP
14
15#ifndef NOMINMAX
16#define NOMINMAX
17#endif
18
19#ifdef _MSVC_LANG
20
21 #if _MSVC_LANG < 201703L
22 #error This libary requires C++17 Standard (Visual Studio 2017).
23 #endif
24
25#else
26
27 #if __cplusplus < 201703
28 #error This library requires C++17 Standard (GNU g++ version 8.0 or clang++ version 8.0 above)
29 #endif // end of __cplusplus
30
31#endif // end of _MSVC_LANG
32
33#pragma warning(push)
34#pragma warning(disable: 4244)
35
36#include <tpf_types.hpp>
37#include <cmath>
38
39namespace std
40{
41 namespace types = tpf::types;
42
43 template<typename OperationType, typename... ArgTypes>
44 auto apply_operation(OperationType&& operation, const std::tuple<ArgTypes...>& tuple);
45
46 template<typename OperationType, typename T, size_t Size>
47 auto apply_operation(OperationType&& operation, const std::array<T, Size>& array);
48
49 template<typename OperationType, typename T, size_t Size>
50 void apply_operation_inplace(OperationType&& operation, std::array<T, Size>& array);
51
52 template<typename T, size_t Size1, size_t Size2, typename = std::enable_if_t<Size1 != Size2>>
53 auto operator==(const std::array<T, Size1>& array_a, const std::array<T, Size2>& array_b);
54
55 template<typename T, size_t Size1, size_t Size2, typename = std::enable_if_t<Size1 != Size2>>
56 auto operator!=(const std::array<T, Size1>& array_a, const std::array<T, Size2>& array_b);
57
58 template<typename T, typename S, size_t Size,
59 typename = std::enable_if_t<!std::is_same_v<T, S>>>
60 auto operator == (const std::array<T, Size>& array_a, const std::array<S, Size>& array_b);
61
62 template<typename T, typename S, size_t Size,
63 typename = std::enable_if_t<!std::is_same_v<T, S>>>
64 auto operator != (const std::array<T, Size>& array_a, const std::array<S, Size>& array_b);
65
66 template<typename T, typename S, size_t Size,
67 typename = std::enable_if_t<!std::is_same_v<T, S>>>
68 auto operator < (const std::array<T, Size>& array_a, const std::array<S, Size>& array_b);
69
70 template<typename T, typename S, size_t Size,
71 typename = std::enable_if_t<!std::is_same_v<T, S>>>
72 auto operator <= (const std::array<T, Size>& array_a, const std::array<S, Size>& array_b);
73
74 template<typename T, typename S, size_t Size,
75 typename = std::enable_if_t<!std::is_same_v<T, S>>>
76 auto operator > (const std::array<T, Size>& array_a, const std::array<S, Size>& array_b);
77
78 template<typename T, typename S, size_t Size,
79 typename = std::enable_if_t<!std::is_same_v<T, S>>>
80 auto operator >= (const std::array<T, Size>& array_a, const std::array<S, Size>& array_b);
81
83 template<typename T, size_t Size1, size_t Size2, typename = std::enable_if_t<Size1 != Size2>>
84 auto operator < (const std::array<T, Size1>& array_a, const std::array<T, Size2>& array_b);
85
86 template<typename T, size_t Size1, size_t Size2, typename = std::enable_if_t<Size1 != Size2>>
87 auto operator <= (const std::array<T, Size1>& array_a, const std::array<T, Size2>& array_b);
88
89 template<typename T, size_t Size1, size_t Size2, typename = std::enable_if_t<Size1 != Size2>>
90 auto operator > (const std::array<T, Size1>& array_a, const std::array<T, Size2>& array_b);
91
92 template<typename T, size_t Size1, size_t Size2, typename = std::enable_if_t<Size1 != Size2>>
93 auto operator >= (const std::array<T, Size1>& array_a, const std::array<T, Size2>& array_b);
94
95 template<typename T, typename S> auto fail_safe_addition(T&& a, T&& b);
96 template<typename T, typename S> auto fail_safe_subtraction(T&& a, T&& b);
97 template<typename T, typename S> auto fail_safe_subtraction(T&& a, T&& b);
98 template<typename T, typename S> auto fail_safe_multiplication(T&& a, T&& b);
99
100 template<typename... ArgTypes1, typename... ArgTypes2,
101 typename common_type = types::tuple_operation_valid_t<std::tuple<ArgTypes1...>, std::tuple<ArgTypes2...>>,
102 typename = std::enable_if_t<!std::is_same_v<common_type, std::tuple<>>>>
103 auto operator+(const std::tuple<ArgTypes1...>& tuple_a, const std::tuple<ArgTypes2...>& tuple_b);
104
105 template<typename... ArgTypes1, typename... ArgTypes2,
106 typename common_type = types::tuple_operation_valid_t<std::tuple<ArgTypes1...>, std::tuple<ArgTypes2...>>,
107 typename = std::enable_if_t<!std::is_same_v<common_type, std::tuple<>>>>
108 auto operator-(const std::tuple<ArgTypes1...>& tuple_a, const std::tuple<ArgTypes2...>& tuple_b);
109
110 template<typename... ArgTypes1, typename... ArgTypes2,
111 typename common_type = types::tuple_operation_valid_t<std::tuple<ArgTypes1...>, std::tuple<ArgTypes2...>>,
112 typename = std::enable_if_t<!std::is_same_v<common_type, std::tuple<>>>>
113 auto operator*(const std::tuple<ArgTypes1...>& tuple_a, const std::tuple<ArgTypes2...>& tuple_b);
114
115 template<typename... ArgTypes1, typename... ArgTypes2,
116 typename common_type = types::tuple_operation_valid_t<std::tuple<ArgTypes1...>, std::tuple<ArgTypes2...>>,
117 typename = std::enable_if_t<!std::is_same_v<common_type, std::tuple<>>>>
118 auto operator/(const std::tuple<ArgTypes1...>& tuple_a, const std::tuple<ArgTypes2...>& tuple_b);
119
120 template<typename T, typename S>
121 inline auto fail_safe_addition(T&& a, S&& b)
122 {
123 using a_t = types::remove_cvref_t<T>;
124 using b_t = types::remove_cvref_t<T>;
125
126 if constexpr(types::is_addition_valid_v<a_t, b_t>)
127 {
128 return a + b;
129 }
130 else
131 {
132 return types::no_type_t{};
133 }
134 }
135
136 template<typename T, typename S>
137 inline auto fail_safe_subtraction(T&& a, S&& b)
138 {
139 using a_t = types::remove_cvref_t<T>;
140 using b_t = types::remove_cvref_t<T>;
141
142 if constexpr(types::is_subtraction_valid_v<a_t, b_t>)
143 {
144 return a - b;
145 }
146 else
147 {
148 return types::no_type_t{};
149 }
150 }
151
152 template<typename T, typename S>
153 inline auto fail_safe_multiplication(T&& a, S&& b)
154 {
155 using a_t = types::remove_cvref_t<T>;
156 using b_t = types::remove_cvref_t<T>;
157
158 if constexpr(types::is_multiplication_valid_v<a_t, b_t>)
159 {
160 return a * b;
161 }
162 else
163 {
164 return types::no_type_t{};
165 }
166 }
167
168 template<typename T, typename S>
169 inline auto fail_safe_division(T&& a, S&& b)
170 {
171 using a_t = types::remove_cvref_t<T>;
172 using b_t = types::remove_cvref_t<T>;
173
174 if constexpr(types::is_division_valid_v<a_t, b_t>)
175 {
176 return a / b;
177 }
178 else
179 {
180 return types::no_type_t{};
181 }
182 }
183
184 namespace hidden
185 {
186 template<typename... ArgTypes1, typename... ArgTypes2, auto... Indices>
187 auto tuple_addition(const std::tuple<ArgTypes1...>& tuple_a,
188 const std::tuple<ArgTypes2...>& tuple_b, types::typed_sequence_t<Indices...>)
189 {
190 return std::tuple{ (std::get<Indices>(tuple_a) + std::get<Indices>(tuple_b))... };
191 }
192
193 template<typename T, typename S, auto Size, auto... Indices>
194 auto array_equality(const std::array<T, Size>& array_a,
195 const std::array<S, Size>& array_b, types::typed_sequence_t<Indices...>)
196 {
197 auto equal_opr = [&array_a, &array_b](auto indexer)
198 {
199 if constexpr(types::is_std_array_v<T> && types::is_std_array_v<S>)
200 {
201 return array_a[indexer.Index] == array_b[indexer.Index] ;
202 }
203 else if constexpr(!types::is_std_array_v<T> && !types::is_std_array_v<T>)
204 {
205 return array_a[indexer.Index] == array_b[indexer.Index];
206 }
207 else
208 return false;
209 };
210
211 return (true && ... && equal_opr(types::indexer_t<Indices>{}));
212 }
213
214 template<typename T, typename S, auto Size, auto... Indices>
215 auto array_less(const std::array<T, Size>& array_a,
216 const std::array<S, Size>& array_b, types::typed_sequence_t<Indices...>)
217 {
219
220 auto equal_opr = [&array_a, &array_b](auto indexer)
221 {
222 if constexpr(types::is_std_array_v<T> && types::is_std_array_v<S>)
223 {
224 return array_a[indexer.Index] < array_b[indexer.Index] ;
225 }
226 else if constexpr(!types::is_std_array_v<T> && !types::is_std_array_v<T>)
227 {
228 return (common_t)array_a[indexer.Index] < (common_t)array_b[indexer.Index] ;
229 }
230 else
231 {
232 std::cout <<"Wrong!!" << std::endl;
233
234 return false;
235 }
236 };
237
238 return (true && ... && equal_opr(types::indexer_t<Indices>{}));
239 }
240
241 template<typename T, typename S, auto Size, auto... Indices>
242 auto array_less_equal(const std::array<T, Size>& array_a,
243 const std::array<S, Size>& array_b, types::typed_sequence_t<Indices...>)
244 {
245 auto equal_opr = [&array_a, &array_b](auto indexer)
246 {
247 if constexpr(types::is_std_array_v<T> && types::is_std_array_v<S>)
248 {
249 return array_a[indexer.Index] <= array_b[indexer.Index] ;
250 }
251 else if constexpr(!types::is_std_array_v<T> && !types::is_std_array_v<T>)
252 {
253 return array_a[indexer.Index] <= array_b[indexer.Index];
254 }
255 else
256 return false;
257 };
258
259 return (true && ... && equal_opr(types::indexer_t<Indices>{}));
260 }
261
262 template<typename... ArgTypes1, typename... ArgTypes2, auto... Indices>
263 auto tuple_subtraction(const std::tuple<ArgTypes1...>& tuple_a,
264 const std::tuple<ArgTypes2...>& tuple_b, types::typed_sequence_t<Indices...>)
265 {
266 return std::tuple{ (std::get<Indices>(tuple_a) - std::get<Indices>(tuple_b))... };
267 }
268
269 template<typename... ArgTypes1, typename... ArgTypes2, auto... Indices>
270 auto tuple_multiplication(const std::tuple<ArgTypes1...>& tuple_a,
271 const std::tuple<ArgTypes2...>& tuple_b, types::typed_sequence_t<Indices...>)
272 {
273 return std::tuple{ (std::get<Indices>(tuple_a) * std::get<Indices>(tuple_b))... };
274 }
275
276 template<typename... ArgTypes1, typename... ArgTypes2, auto... Indices>
277 auto tuple_division(const std::tuple<ArgTypes1...>& tuple_a,
278 const std::tuple<ArgTypes2...>& tuple_b, types::typed_sequence_t<Indices...>)
279 {
280 return std::tuple{ (std::get<Indices>(tuple_a) / std::get<Indices>(tuple_b))... };
281 }
282
283 template<typename T, typename... ArgTypes, auto... Indices>
284 auto tuple_addition(const T& scalar, const std::tuple<ArgTypes...>& tuple, types::typed_sequence_t<Indices...>)
285 {
286 return std::tuple{ (scalar + std::get<Indices>(tuple))... };
287 }
288
289 template<typename T, typename... ArgTypes, auto... Indices>
290 auto tuple_addition(const std::tuple<ArgTypes...>& tuple, const T& scalar, types::typed_sequence_t<Indices...>)
291 {
292 return std::tuple{ (std::get<Indices>(tuple) + scalar)... };
293 }
294
295 template<typename T, typename... ArgTypes, auto... Indices>
296 auto tuple_subtraction(const T& scalar, const std::tuple<ArgTypes...>& tuple, types::typed_sequence_t<Indices...>)
297 {
298 return std::tuple{ (scalar - std::get<Indices>(tuple))... };
299 }
300
301 template<typename T, typename... ArgTypes, auto... Indices>
302 auto tuple_subtraction(const std::tuple<ArgTypes...>& tuple, const T& scalar, types::typed_sequence_t<Indices...>)
303 {
304 return std::tuple{ (std::get<Indices>(tuple) - scalar)... };
305 }
306
307 template<typename T, typename... ArgTypes, auto... Indices>
308 auto tuple_multiplication(const T& scalar, const std::tuple<ArgTypes...>& tuple, types::typed_sequence_t<Indices...>)
309 {
310 return std::tuple{ (scalar * std::get<Indices>(tuple))... };
311 }
312
313 template<typename T, typename... ArgTypes, auto... Indices>
314 auto tuple_multiplication(const std::tuple<ArgTypes...>& tuple, const T& scalar, types::typed_sequence_t<Indices...>)
315 {
316 return std::tuple{ (std::get<Indices>(tuple) * scalar)... };
317 }
318
319 template<typename T, typename... ArgTypes, auto... Indices>
320 auto tuple_division(const T& scalar, const std::tuple<ArgTypes...>& tuple, types::typed_sequence_t<Indices...>)
321 {
322 return std::tuple{ (scalar / std::get<Indices>(tuple))... };
323 }
324
325 template<typename T, typename... ArgTypes, auto... Indices>
326 auto tuple_division(const std::tuple<ArgTypes...>& tuple, const T& scalar, types::typed_sequence_t<Indices...>)
327 {
328 return std::tuple{ (std::get<Indices>(tuple) / scalar)... };
329 }
330
331 template<typename T, typename S, size_t Size, auto...Indices>
332 auto array_addition(const std::array<T, Size>& array_a,
333 const std::array<S, Size>& array_b, types::typed_sequence_t<Indices...>)
334 {
335 return std::array{ (array_a[Indices] + array_b[Indices])... };
336 }
337
338 template<typename T, typename S, size_t Size, auto...Indices>
339 auto array_subtraction(const std::array<T, Size>& array_a,
340 const std::array<S, Size>& array_b, types::typed_sequence_t<Indices...>)
341 {
342 return std::array{ (array_a[Indices] - array_b[Indices])... };
343 }
344
345 template<typename T, typename S, size_t Size, auto...Indices>
346 auto array_multiplication(const std::array<T, Size>& array_a,
347 const std::array<S, Size>& array_b, types::typed_sequence_t<Indices...>)
348 {
349 return std::array{ (array_a[Indices] * array_b[Indices])... };
350 }
351
352 template<typename T, typename S, size_t Size, auto...Indices>
353 auto array_division(const std::array<T, Size>& array_a,
354 const std::array<S, Size>& array_b, types::typed_sequence_t<Indices...>)
355 {
356 return std::array{ (array_a[Indices] / array_b[Indices])... };
357 }
358
359 template<typename T, typename S, size_t Size, auto...Indices>
360 auto array_addition(const T& scalar, const std::array<S, Size>& array,
362 {
363 return std::array{ (scalar + array[Indices])... };
364 }
365
366 template<typename T, typename S, size_t Size, auto...Indices>
367 auto array_addition(const std::array<S, Size>& array,
369 {
370 return std::array{ (array[Indices] + scalar)... };
371 }
372
373 template<typename T, typename S, size_t Size, auto...Indices>
374 auto array_subtraction(const T& scalar, const std::array<S, Size>& array,
376 {
377 return std::array{ (scalar - array[Indices])... };
378 }
379
380 template<typename T, typename S, size_t Size, auto...Indices>
381 auto array_subtraction(const std::array<S, Size>& array,
383 {
384 return std::array{ (array[Indices] - scalar)... };
385 }
386
387 template<typename T, typename S, size_t Size, auto...Indices>
388 auto array_multiplication(const T& scalar, const std::array<S, Size>& array,
390 {
391 return std::array{ (scalar * array[Indices])... };
392 }
393
394 template<typename T, typename S, size_t Size, auto...Indices>
395 auto array_multiplication(const std::array<S, Size>& array,
397 {
398 return std::array{ (array[Indices] * scalar)... };
399 }
400
401 template<typename T, typename S, size_t Size, auto...Indices>
402 auto array_division(const T& scalar, const std::array<S, Size>& array,
404 {
405 return std::array{ (scalar / array[Indices])... };
406 }
407
408 template<typename T, typename S, size_t Size, auto...Indices>
409 auto array_division(const std::array<S, Size>& array,
411 {
412 return std::array{ (array[Indices] / scalar)... };
413 }
414
415 // WARNING: new type is created
416 template<typename OperationType, typename... ArgTypes, auto... Indices>
417 auto apply_operation(OperationType&& operation,
418 const std::tuple<ArgTypes...>& tuple, types::typed_sequence_t<Indices...>)
419 {
420 auto apply_opr = [&operation, &tuple](auto indexer)
421 {
422 using ele_t = std::tuple_element_t<indexer.Index, std::tuple<ArgTypes...>>;
423
424 if constexpr(types::is_tuple_v<ele_t>)
425 {
426 return std::apply_operation(operation, std::get<indexer.Index>(tuple));
427 }
428 else
429 {
430 return operation(std::get<indexer.Index>(tuple));
431 }
432 };
433
434 return std::tuple{ apply_opr( types::indexer_t<Indices>{} )... };
435 }
436
437 template<typename OperationType, typename T, size_t Size, auto... Indices>
438 auto apply_operation(OperationType&& operation,
439 const std::array<T, Size>& array, types::typed_sequence_t<Indices...>)
440 {
441 auto apply_opr = [&operation, &array](auto indexer)
442 {
443 using ele_t = decltype(array[indexer.Index]);
444
445 if constexpr(types::is_std_array_v<ele_t>)
446 {
447 return std::apply_operation(operation, array[indexer.Index]);
448 }
449 else
450 {
451 return operation(array[indexer.Index]);
452 }
453 };
454
455 return std::array{ apply_opr(types::indexer_t<Indices>{})... };
456 }
457
458 template<typename OperationType, typename T, size_t Size, auto... Indices>
459 auto apply_operation_inplace(OperationType&& operation,
460 std::array<T, Size>& array, types::typed_sequence_t<Indices...>)
461 {
462 auto workhorse = [&operation, &array](auto indexer)
463 {
464 using ele_t = decltype(array[indexer.Index]);
465 if constexpr(types::is_std_array_v<ele_t>)
466 {
467 std::apply_operation_inplace(operation, array[indexer.Index]);
468 }
469 else
470 {
471 array[indexer.Index] = operation(array[indexer.Index]);
472 }
473 };
474
475 types::drive_workhorse<Size>(workhorse);
476 }
477
478 } // end of namespace hidden
479
481
482 template<typename... ArgTypes1, typename... ArgTypes2, typename common_type, typename>
483 auto operator+(const std::tuple<ArgTypes1...>& tuple_a, const std::tuple<ArgTypes2...>& tuple_b)
484 {
485 constexpr auto Size = sizeof...(ArgTypes1);
487 }
488
489 template<typename... ArgTypes1, typename... ArgTypes2, typename common_type, typename>
490 auto operator-(const std::tuple<ArgTypes1...>& tuple_a, const std::tuple<ArgTypes2...>& tuple_b)
491 {
492 constexpr auto Size = sizeof...(ArgTypes1);
494 }
495
496 template<typename... ArgTypes1, typename... ArgTypes2, typename common_type, typename>
497 auto operator*(const std::tuple<ArgTypes1...>& tuple_a, const std::tuple<ArgTypes2...>& tuple_b)
498 {
499 constexpr auto Size = sizeof...(ArgTypes1);
501 }
502
503 template<typename... ArgTypes1, typename... ArgTypes2, typename common_type, typename>
504 auto operator/(const std::tuple<ArgTypes1...>& tuple_a, const std::tuple<ArgTypes2...>& tuple_b)
505 {
506 constexpr auto Size = sizeof...(ArgTypes1);
508 }
509
511 template<typename T, typename... ArgTypes>
512 auto operator+(const T& scalar, const std::tuple<ArgTypes...>& tuple)
513 {
514 constexpr auto Size = sizeof...(ArgTypes);
516 }
517
518 template<typename T, typename... ArgTypes>
519 auto operator+(const std::tuple<ArgTypes...>& tuple, const T& scalar)
520 {
521 constexpr auto Size = sizeof...(ArgTypes);
523 }
524
525 template<typename T, typename... ArgTypes>
526 auto operator-(const T& scalar, const std::tuple<ArgTypes...>& tuple)
527 {
528 constexpr auto Size = sizeof...(ArgTypes);
530 }
531
532 template<typename T, typename... ArgTypes>
533 auto operator-(const std::tuple<ArgTypes...>& tuple, const T& scalar)
534 {
535 constexpr auto Size = sizeof...(ArgTypes);
537 }
538
539 template<typename T, typename... ArgTypes>
540 auto operator*(const T& scalar, const std::tuple<ArgTypes...>& tuple)
541 {
542 constexpr auto Size = sizeof...(ArgTypes);
544 }
545
546 template<typename T, typename... ArgTypes>
547 auto operator*(const std::tuple<ArgTypes...>& tuple, const T& scalar)
548 {
549 constexpr auto Size = sizeof...(ArgTypes);
551 }
552
553 template<typename T, typename... ArgTypes>
554 auto operator/(const T& scalar, const std::tuple<ArgTypes...>& tuple)
555 {
556 constexpr auto Size = sizeof...(ArgTypes);
558 }
559
560 template<typename T, typename... ArgTypes>
561 auto operator/(const std::tuple<ArgTypes...>& tuple, const T& scalar)
562 {
563 constexpr auto Size = sizeof...(ArgTypes);
565 }
566
567 // WARNING: new type is created
568 template<typename OperationType, typename... ArgTypes>
569 auto apply_operation(OperationType&& operation, const std::tuple<ArgTypes...>& tuple)
570 {
571 constexpr auto Size = sizeof...(ArgTypes);
573 }
574
575 // WARNING: no new type is created
576 template<typename OperationType, typename... ArgTypes>
577 void apply_operation_inplace(OperationType&& operation, std::tuple<ArgTypes...>& tuple)
578 {
579 constexpr auto Size = tuple_size_v<std::tuple<ArgTypes...>>;
580
581 auto workhorse = [&operation, &tuple](auto indexer)
582 {
583 using ele_t = std::tuple_element_t<indexer.Index, std::tuple<ArgTypes...>>;
584
585 if constexpr(types::is_tuple_v<ele_t>)
586 {
587 std::apply_operation_inplace(operation, std::get<indexer.Index>(tuple));
588 }
589 else
590 {
591 std::get<indexer.Index>(tuple) =
592 operation(std::get<indexer.Index>(tuple));
593 }
594 };
595
596 types::drive_workhorse<Size>(workhorse);
597 }
598
600 // same element type, different size
601 template<typename T, size_t Size1, size_t Size2, typename>
602 auto operator==(const std::array<T, Size1>& array_a, const std::array<T, Size2>& array_b)
603 {
604 return false;
605 }
606
607 template<typename T, size_t Size1, size_t Size2, typename>
608 auto operator!=(const std::array<T, Size1>& array_a, const std::array<T, Size2>& array_b)
609 {
610 return true;
611 }
612
613 template<typename T, size_t Size1, size_t Size2, typename>
614 auto operator < (const std::array<T, Size1>& array_a, const std::array<T, Size2>& array_b)
615 {
616 return false;
617 }
618
619 template<typename T, size_t Size1, size_t Size2, typename>
620 auto operator <= (const std::array<T, Size1>& array_a, const std::array<T, Size2>& array_b)
621 {
622 return false;
623 }
624
625 template<typename T, size_t Size1, size_t Size2, typename>
626 auto operator > (const std::array<T, Size1>& array_a, const std::array<T, Size2>& array_b)
627 {
628 return false;
629 }
630
631 template<typename T, size_t Size1, size_t Size2, typename>
632 auto operator >= (const std::array<T, Size1>& array_a, const std::array<T, Size2>& array_b)
633 {
634 return false;
635 }
636
637 template<typename T, typename S, size_t Size, typename>
638 auto operator==(const std::array<T, Size>& array_a, const std::array<S, Size>& array_b)
639 {
641 }
642
643 template<typename T, typename S, size_t Size, typename>
644 auto operator!=(const std::array<T, Size>& array_a, const std::array<S, Size>& array_b)
645 {
647 }
648
649 template<typename T, typename S, size_t Size, typename>
650 auto operator < (const std::array<T, Size>& array_a, const std::array<S, Size>& array_b)
651 {
652 return hidden::array_less(array_a, array_b, types::make_typed_sequence_t<Size>{});
653 }
654
655 template<typename T, typename S, size_t Size, typename>
656 auto operator <= (const std::array<T, Size>& array_a, const std::array<S, Size>& array_b)
657 {
659 }
660
661 template<typename T, typename S, size_t Size, typename>
662 auto operator > (const std::array<T, Size>& array_a, const std::array<S, Size>& array_b)
663 {
665 }
666
667 template<typename T, typename S, size_t Size, typename>
668 auto operator >= (const std::array<T, Size>& array_a, const std::array<S, Size>& array_b)
669 {
670 return !hidden::array_less(array_a, array_b, types::make_typed_sequence_t<Size>{});
671 }
672
673 template<typename T, typename S, size_t Size>
674 auto operator+(const std::array<T, Size>& array_a,
675 const std::array<S, Size>& array_b)
676 {
678 }
679
680 template<typename T, typename S, size_t Size>
681 auto operator-(const std::array<T, Size>& array_a,
682 const std::array<S, Size>& array_b)
683 {
685 }
686
687 template<typename T, typename S, size_t Size>
688 auto operator*(const std::array<T, Size>& array_a,
689 const std::array<S, Size>& array_b)
690 {
692 }
693
694 template<typename T, typename S, size_t Size>
695 auto operator/(const std::array<T, Size>& array_a,
696 const std::array<S, Size>& array_b)
697 {
699 }
700
702 template<typename T, typename S, size_t Size>
703 auto operator+(const T& scalar, const std::array<S, Size>& array)
704 {
706 }
707
708 template<typename T, typename S, size_t Size>
709 auto operator+(const std::array<S, Size>& array, const T& scalar)
710 {
712 }
713
714 template<typename T, typename S, size_t Size>
715 auto operator-(const T& scalar, const std::array<S, Size>& array)
716 {
718 }
719
720 template<typename T, typename S, size_t Size>
721 auto operator-(const std::array<S, Size>& array, const T& scalar)
722 {
724 }
725
726 template<typename T, typename S, size_t Size>
727 auto operator*(const T& scalar, const std::array<S, Size>& array)
728 {
730 }
731
732 template<typename T, typename S, size_t Size>
733 auto operator*(const std::array<S, Size>& array, const T& scalar)
734 {
736 }
737
738 template<typename T, typename S, size_t Size>
739 auto operator/(const T& scalar, const std::array<S, Size>& array)
740 {
742 }
743
744 template<typename T, typename S, size_t Size>
745 auto operator/(const std::array<S, Size>& array, const T& scalar)
746 {
748 }
749
750 template<typename OperationType, typename T, size_t Size>
751 auto apply_operation(OperationType&& operation, const std::array<T, Size>& array)
752 {
754 }
755
756 template<typename OperationType, typename T, size_t Size>
757 void apply_operation_inplace(OperationType&& operation, std::array<T, Size>& array)
758 {
760 }
761
762 template<typename... Elements, typename commom_element_type
763 = types::tuple_common_element_t<std::tuple<Elements...>>>
764 auto sum_of_elements(const std::tuple<Elements...>& tuple)
765 {
766 commom_element_type sum{};
767
768 constexpr auto Size = sizeof...(Elements);
769
770 types::for_workhorse<Size>(
771 [&sum, &tuple](auto indexer)
772 {
773 sum += std::get<indexer.Index>(tuple);
774 }
775 );
776
777 return sum;
778 }
779
780 template<typename... Elements, typename commom_element_type
781 = types::tuple_common_element_t<std::tuple<Elements...>>>
782 double mean_of_elements(const std::tuple<Elements...>& tuple)
783 {
784 auto sum = sum_of_elements(tuple);
785 constexpr auto Size = sizeof...(Elements);
786
787 return sum / Size;
788 }
789
790 template<typename... Elements, typename commom_element_type
791 = types::tuple_common_element_t<std::tuple<Elements...>>>
792 double standard_deviation(const std::tuple<Elements...>& tuple)
793 {
794 double mean = mean_of_elements(tuple);
795
796 constexpr auto Size = sizeof...(Elements);
797
798 double denominator = 1.0 / Size;
799 double deviation{};
800
801 types::for_workhorse<Size>(
802 [&denominator, &deviation, &mean, &tuple](auto indexer)
803 {
804 double difference = mean - std::get<indexer.Index>(tuple);
806 difference *= denominator;
807 deviation += difference;
808 }
809 );
810
811 return std::sqrt(deviation);
812 }
813
815 // if operation fails, the operation is simply ignored, does not throw
816
817 template<typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
818 std::enable_if_t<types::is_type_in_list_v<types::no_throw_t, list>, std::variant<EleTypes...>>
819 operator+(const std::variant<EleTypes...>& variant_a,
820 const std::variant<EleTypes...>& variant_b)
821 {
822 constexpr auto Size = sizeof...(EleTypes);
823
824 std::variant<EleTypes...> result;
825
826 try
827 {
828 types::for_workhorse<Size>([&result, &variant_a, &variant_b](auto first)
829 {
830 constexpr auto Size = sizeof...(EleTypes);
831
832 if(auto a = std::get_if<first.Index>(&variant_a))
833 {
834 types::for_workhorse<Size>([&](auto second)
835 {
836 if(auto b = std::get_if<second.Index>(&variant_b))
837 {
838 result = fail_safe_addition(*a, *b);
839
840 // operation completed
841 throw types::no_type_t{};
842 }
843 });
844 }
845 });
846
847 // failed at this point
848 }
849 catch(const types::no_type_t&) { }
850
851 return result;
852 }
853
854 // if operation fails, the operation is simply ignored, does not throw
855 template<typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
856 std::enable_if_t<types::is_type_in_list_v<types::no_throw_t, list>, std::variant<EleTypes...>>
857 operator-(const std::variant<EleTypes...>& variant_a,
858 const std::variant<EleTypes...>& variant_b)
859 {
860 constexpr auto Size = sizeof...(EleTypes);
861
862 std::variant<EleTypes...> result;
863
864 try
865 {
866 types::for_workhorse<Size>([&result, &variant_a, &variant_b](auto first)
867 {
868 constexpr auto Size = sizeof...(EleTypes);
869
870 if(auto a = std::get_if<first.Index>(&variant_a))
871 {
872 types::for_workhorse<Size>([&](auto second)
873 {
874 if(auto b = std::get_if<second.Index>(&variant_b))
875 {
876 result = fail_safe_subtraction(*a, *b);
877
878 // operation completed
879 throw types::no_type_t{};
880 }
881 });
882 }
883 });
884
885 // failed at this point
886 }
887 catch(const types::no_type_t&) { }
888
889 return result;
890 }
891
892 // if operation fails, the operation is simply ignored, does not throw
893 template<typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
894 std::enable_if_t<types::is_type_in_list_v<types::no_throw_t, list>, std::variant<EleTypes...>>
895 operator*(const std::variant<EleTypes...>& variant_a,
896 const std::variant<EleTypes...>& variant_b)
897 {
898 constexpr auto Size = sizeof...(EleTypes);
899
900 std::variant<EleTypes...> result;
901
902 try
903 {
904 types::for_workhorse<Size>([&result, &variant_a, &variant_b](auto first)
905 {
906 constexpr auto Size = sizeof...(EleTypes);
907
908 if(auto a = std::get_if<first.Index>(&variant_a))
909 {
910 types::for_workhorse<Size>([&](auto second)
911 {
912 if(auto b = std::get_if<second.Index>(&variant_b))
913 {
914 result = fail_safe_multiplication(*a, *b);
915
916 // operation completed
917 throw types::no_type_t{};
918 }
919 });
920 }
921 });
922
923 // failed at this point
924 }
925 catch(const types::no_type_t&) { }
926
927 return result;
928 }
929
930 // if operation fails, the operation is simply ignored, does not throw
931 template<typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
932 std::enable_if_t<types::is_type_in_list_v<types::no_throw_t, list>, std::variant<EleTypes...>>
933 operator/(const std::variant<EleTypes...>& variant_a,
934 const std::variant<EleTypes...>& variant_b)
935 {
936 constexpr auto Size = sizeof...(EleTypes);
937
938 std::variant<EleTypes...> result;
939
940 try
941 {
942 types::for_workhorse<Size>([&result, &variant_a, &variant_b](auto first)
943 {
944 constexpr auto Size = sizeof...(EleTypes);
945
946 if(auto a = std::get_if<first.Index>(&variant_a))
947 {
948 types::for_workhorse<Size>([&](auto second)
949 {
950 if(auto b = std::get_if<second.Index>(&variant_b))
951 {
952 result = fail_safe_operation(*a, *b);
953
954 // operation completed
955 throw types::no_type_t{};
956 }
957 });
958 }
959 });
960
961 // failed at this point
962 }
963 catch(const types::no_type_t&) { }
964
965 return result;
966 }
967
968 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
969 std::enable_if_t<types::is_type_in_list_v<types::no_throw_t, list> &&
970 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
971 operator+(const T& v, const std::variant<EleTypes...>& variant)
972 {
973 constexpr auto Size = sizeof...(EleTypes);
974
975 std::variant<EleTypes...> result;
976
977 try
978 {
979 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
980 {
981 if(auto a = std::get_if<indexer.Index>(&variant))
982 {
983 result = fail_safe_addition(v, *a);
984
985 // operation completed
986 throw types::no_type_t{};
987 }
988 });
989
990 // failed at this point
991 }
992 catch(const types::no_type_t&) { }
993
994 return result;
995 }
996
997 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
998 std::enable_if_t<types::is_type_in_list_v<types::no_throw_t, list> &&
999 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1000 operator+(const std::variant<EleTypes...>& variant, const T& v)
1001 {
1002 constexpr auto Size = sizeof...(EleTypes);
1003
1004 std::variant<EleTypes...> result;
1005
1006 try
1007 {
1008 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1009 {
1010 if(auto a = std::get_if<indexer.Index>(&variant))
1011 {
1012 result = fail_safe_addition(*a, v);
1013
1014 // operation completed
1015 throw types::no_type_t{};
1016 }
1017 });
1018
1019 // failed at this point
1020 }
1021 catch(const types::no_type_t&) { }
1022
1023 return result;
1024 }
1025
1026 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1027 std::enable_if_t<types::is_type_in_list_v<types::no_throw_t, list> &&
1028 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1029 operator-(const T& v, const std::variant<EleTypes...>& variant)
1030 {
1031 constexpr auto Size = sizeof...(EleTypes);
1032
1033 std::variant<EleTypes...> result;
1034
1035 try
1036 {
1037 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1038 {
1039 if(auto a = std::get_if<indexer.Index>(&variant))
1040 {
1041 result = fail_safe_subtraction(v, *a);
1042
1043 // operation completed
1044 throw types::no_type_t{};
1045 }
1046 });
1047
1048 // failed at this point
1049 }
1050 catch(const types::no_type_t&) { }
1051
1052 return result;
1053 }
1054
1055 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1056 std::enable_if_t<types::is_type_in_list_v<types::no_throw_t, list> &&
1057 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1058 operator-(const std::variant<EleTypes...>& variant, const T& v)
1059 {
1060 constexpr auto Size = sizeof...(EleTypes);
1061
1062 std::variant<EleTypes...> result;
1063
1064 try
1065 {
1066 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1067 {
1068 if(auto a = std::get_if<indexer.Index>(&variant))
1069 {
1070 result = fail_safe_subtraction(*a, v);
1071
1072 // operation completed
1073 throw types::no_type_t{};
1074 }
1075 });
1076
1077 // failed at this point
1078 }
1079 catch(const types::no_type_t&) { }
1080
1081 return result;
1082 }
1083
1084 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1085 std::enable_if_t<types::is_type_in_list_v<types::no_throw_t, list> &&
1086 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1087 operator*(const T& v, const std::variant<EleTypes...>& variant)
1088 {
1089 constexpr auto Size = sizeof...(EleTypes);
1090
1091 std::variant<EleTypes...> result;
1092
1093 try
1094 {
1095 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1096 {
1097 if(auto a = std::get_if<indexer.Index>(&variant))
1098 {
1099 result = fail_safe_mulitplication(v, *a);
1100
1101 // operation completed
1102 throw types::no_type_t{};
1103 }
1104 });
1105
1106 // failed at this point
1107 }
1108 catch(const types::no_type_t&) { }
1109
1110 return result;
1111 }
1112
1113 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1114 std::enable_if_t<types::is_type_in_list_v<types::no_throw_t, list> &&
1115 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1116 operator*(const std::variant<EleTypes...>& variant, const T& v)
1117 {
1118 constexpr auto Size = sizeof...(EleTypes);
1119
1120 std::variant<EleTypes...> result;
1121
1122 try
1123 {
1124 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1125 {
1126 if(auto a = std::get_if<indexer.Index>(&variant))
1127 {
1128 result = fail_safe_multiplication(*a, v);
1129
1130 // operation completed
1131 throw types::no_type_t{};
1132 }
1133 });
1134
1135 // failed at this point
1136 }
1137 catch(const types::no_type_t&) { }
1138
1139 return result;
1140 }
1141
1142 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1143 std::enable_if_t<types::is_type_in_list_v<types::no_throw_t, list> &&
1144 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1145 operator/(const T& v, const std::variant<EleTypes...>& variant)
1146 {
1147 constexpr auto Size = sizeof...(EleTypes);
1148
1149 std::variant<EleTypes...> result;
1150
1151 try
1152 {
1153 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1154 {
1155 if(auto a = std::get_if<indexer.Index>(&variant))
1156 {
1157 result = fail_safe_division(v, *a);
1158
1159 // operation completed
1160 throw types::no_type_t{};
1161 }
1162 });
1163
1164 // failed at this point
1165 }
1166 catch(const types::no_type_t&) { }
1167
1168 return result;
1169 }
1170
1171 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1172 std::enable_if_t<types::is_type_in_list_v<types::no_throw_t, list> &&
1173 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1174 operator/(const std::variant<EleTypes...>& variant, const T& v)
1175 {
1176 constexpr auto Size = sizeof...(EleTypes);
1177
1178 std::variant<EleTypes...> result;
1179
1180 try
1181 {
1182 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1183 {
1184 if(auto a = std::get_if<indexer.Index>(&variant))
1185 {
1186 result = fail_safe_division(*a, v);
1187
1188 // operation completed
1189 throw types::no_type_t{};
1190 }
1191 });
1192
1193 // failed at this point
1194 }
1195 catch(const types::no_type_t&) { }
1196
1197 return result;
1198 }
1199
1201
1202 // if operation fails, throws std::bad_variant_access
1203 template<typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1204 std::enable_if_t<!types::is_type_in_list_v<types::no_throw_t, list>, std::variant<EleTypes...>>
1205 operator+(const std::variant<EleTypes...>& variant_a,
1206 const std::variant<EleTypes...>& variant_b)
1207 {
1208 constexpr auto Size = sizeof...(EleTypes);
1209
1210 std::variant<EleTypes...> result;
1211
1212 try
1213 {
1214 types::for_workhorse<Size>([&result, &variant_a, &variant_b](auto first)
1215 {
1216 constexpr auto Size = sizeof...(EleTypes);
1217
1218 if(auto a = std::get_if<first.Index>(&variant_a))
1219 {
1220 types::for_workhorse<Size>([&](auto second)
1221 {
1222 if(auto b = std::get_if<second.Index>(&variant_b))
1223 {
1224 using ele_a_t = types::remove_cvref_t<decltype(*a)>;
1225 using ele_b_t = types::remove_cvref_t<decltype(*b)>;
1226
1227 if constexpr(types::is_addition_valid_v<ele_a_t, ele_b_t>)
1228 {
1229 result = (*a) + (*b);
1230
1231 // operation succeeded - abort operation
1232 throw types::no_type_t{};
1233 }
1234 else
1235 {
1236 // invalid operation
1237 throw std::bad_variant_access{};
1238 }
1239 }
1240 });
1241 }
1242 });
1243
1244 // failed at this point - operation cannot reach this point
1245 }
1246 catch(const types::no_type_t&) { }
1247
1248 return result;
1249 }
1250
1251 // if operation fails, throws std::bad_variant_access
1252 template<typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1253 std::enable_if_t<!types::is_type_in_list_v<types::no_throw_t, list>, std::variant<EleTypes...>>
1254 operator-(const std::variant<EleTypes...>& variant_a,
1255 const std::variant<EleTypes...>& variant_b)
1256 {
1257 constexpr auto Size = sizeof...(EleTypes);
1258
1259 std::variant<EleTypes...> result;
1260
1261 try
1262 {
1263 types::for_workhorse<Size>([&result, &variant_a, &variant_b](auto first)
1264 {
1265 constexpr auto Size = sizeof...(EleTypes);
1266
1267 if(auto a = std::get_if<first.Index>(&variant_a))
1268 {
1269 types::for_workhorse<Size>([&](auto second)
1270 {
1271 if(auto b = std::get_if<second.Index>(&variant_b))
1272 {
1273 using ele_a_t = types::remove_cvref_t<decltype(*a)>;
1274 using ele_b_t = types::remove_cvref_t<decltype(*b)>;
1275
1276 if constexpr(types::is_subtraction_valid_v<ele_a_t, ele_b_t>)
1277 {
1278 result = (*a) - (*b);
1279
1280 // operation succeeded - abort operation
1281 throw types::no_type_t{};
1282 }
1283 else
1284 {
1285 // invalid operation
1286 throw std::bad_variant_access{};
1287 }
1288 }
1289 });
1290 }
1291 });
1292
1293 // failed at this point - operation cannot reach this point
1294 }
1295 catch(const types::no_type_t&) { }
1296
1297 return result;
1298 }
1299
1300 // if operation fails, throws std::bad_variant_access
1301 template<typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1302 std::enable_if_t<!types::is_type_in_list_v<types::no_throw_t, list>, std::variant<EleTypes...>>
1303 operator*(const std::variant<EleTypes...>& variant_a,
1304 const std::variant<EleTypes...>& variant_b)
1305 {
1306 constexpr auto Size = sizeof...(EleTypes);
1307
1308 std::variant<EleTypes...> result;
1309
1310 try
1311 {
1312 types::for_workhorse<Size>([&result, &variant_a, &variant_b](auto first)
1313 {
1314 constexpr auto Size = sizeof...(EleTypes);
1315
1316 if(auto a = std::get_if<first.Index>(&variant_a))
1317 {
1318 types::for_workhorse<Size>([&](auto second)
1319 {
1320 if(auto b = std::get_if<second.Index>(&variant_b))
1321 {
1322 using ele_a_t = types::remove_cvref_t<decltype(*a)>;
1323 using ele_b_t = types::remove_cvref_t<decltype(*b)>;
1324
1325 if constexpr(types::is_multiplication_valid_v<ele_a_t, ele_b_t>)
1326 {
1327 result = (*a) * (*b);
1328
1329 // operation succeeded - abort operation
1330 throw types::no_type_t{};
1331 }
1332 else
1333 {
1334 // invalid operation
1335 throw std::bad_variant_access{};
1336 }
1337 }
1338 });
1339 }
1340 });
1341
1342 // failed at this point - operation cannot reach this point
1343 }
1344 catch(const types::no_type_t&) { }
1345
1346 return result;
1347 }
1348
1349 // if operation fails, throws std::bad_variant_access
1350 template<typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1351 std::enable_if_t<!types::is_type_in_list_v<types::no_throw_t, list>, std::variant<EleTypes...>>
1352 operator/(const std::variant<EleTypes...>& variant_a,
1353 const std::variant<EleTypes...>& variant_b)
1354 {
1355 constexpr auto Size = sizeof...(EleTypes);
1356
1357 std::variant<EleTypes...> result;
1358
1359 try
1360 {
1361 types::for_workhorse<Size>([&result, &variant_a, &variant_b](auto first)
1362 {
1363 constexpr auto Size = sizeof...(EleTypes);
1364
1365 if(auto a = std::get_if<first.Index>(&variant_a))
1366 {
1367 types::for_workhorse<Size>([&](auto second)
1368 {
1369 if(auto b = std::get_if<second.Index>(&variant_b))
1370 {
1371 using ele_a_t = types::remove_cvref_t<decltype(*a)>;
1372 using ele_b_t = types::remove_cvref_t<decltype(*b)>;
1373
1374 if constexpr(types::is_division_valid_v<ele_a_t, ele_b_t>)
1375 {
1376 result = (*a) / (*b);
1377
1378 // operation succeeded - abort operation
1379 throw types::no_type_t{};
1380 }
1381 else
1382 {
1383 // invalid operation
1384 throw std::bad_variant_access{};
1385 }
1386 }
1387 });
1388 }
1389 });
1390
1391 // failed at this point - operation cannot reach this point
1392 }
1393 catch(const types::no_type_t&) { }
1394
1395 return result;
1396 }
1397
1399 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1400 std::enable_if_t<!types::is_type_in_list_v<types::no_throw_t, list> &&
1401 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1402 operator+(const T& v, const std::variant<EleTypes...>& variant)
1403 {
1404 constexpr auto Size = sizeof...(EleTypes);
1405
1406 std::variant<EleTypes...> result;
1407
1408 try
1409 {
1410 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1411 {
1412 if(auto a = std::get_if<indexer.Index>(&variant))
1413 {
1414 using ele_a_t = types::remove_cvref_t<decltype(*a)>;
1415
1416 if constexpr(types::is_addition_valid_v<T, ele_a_t>)
1417 {
1418 result = v + (*a);
1419
1420 // operation completed
1421 throw types::no_type_t{};
1422 }
1423 else
1424 {
1425 // invalid operation
1426 throw std::bad_variant_access{};
1427 }
1428 }
1429 });
1430
1431 // failed at this point
1432 }
1433 catch(const types::no_type_t&) { }
1434
1435 return result;
1436 }
1437
1438 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1439 std::enable_if_t<!types::is_type_in_list_v<types::no_throw_t, list> &&
1440 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1441 operator+(const std::variant<EleTypes...>& variant, const T& v)
1442 {
1443 constexpr auto Size = sizeof...(EleTypes);
1444
1445 std::variant<EleTypes...> result;
1446
1447 try
1448 {
1449 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1450 {
1451 if(auto a = std::get_if<indexer.Index>(&variant))
1452 {
1453 using ele_a_t = types::remove_cvref_t<decltype(*a)>;
1454
1455 if constexpr(types::is_addition_valid_v<ele_a_t, T>)
1456 {
1457 result = (*a) + v;
1458 throw types::no_type_t{};
1459 }
1460 else
1461 {
1462 // operation completed
1463 throw std::bad_variant_access{};
1464 }
1465 }
1466 });
1467
1468 // failed at this point
1469 }
1470 catch(const types::no_type_t&) { }
1471
1472 return result;
1473 }
1474
1475 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1476 std::enable_if_t<!types::is_type_in_list_v<types::no_throw_t, list> &&
1477 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1478 operator-(const T& v, const std::variant<EleTypes...>& variant)
1479 {
1480 constexpr auto Size = sizeof...(EleTypes);
1481
1482 std::variant<EleTypes...> result;
1483
1484 try
1485 {
1486 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1487 {
1488 if(auto a = std::get_if<indexer.Index>(&variant))
1489 {
1490 using ele_a_t = types::remove_cvref_t<decltype(*a)>;
1491
1492 if constexpr(types::is_subtraction_valid_v<T, ele_a_t>)
1493 {
1494 result = v - (*a);
1495
1496 // operation succeeded - abort operation
1497 throw types::no_type_t{};
1498 }
1499 else
1500 {
1501 // invalid operation
1502 throw std::bad_variant_access{};
1503 }
1504 }
1505 });
1506
1507 // failed at this point
1508 }
1509 catch(const types::no_type_t&) { }
1510
1511 return result;
1512 }
1513
1514 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1515 std::enable_if_t<!types::is_type_in_list_v<types::no_throw_t, list> &&
1516 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1517 operator-(const std::variant<EleTypes...>& variant, const T& v)
1518 {
1519 constexpr auto Size = sizeof...(EleTypes);
1520
1521 std::variant<EleTypes...> result;
1522
1523 try
1524 {
1525 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1526 {
1527 if(auto a = std::get_if<indexer.Index>(&variant))
1528 {
1529 using ele_a_t = types::remove_cvref_t<decltype(*a)>;
1530
1531 if constexpr(types::is_subtraction_valid_v<ele_a_t, T>)
1532 {
1533 result = (*a) - v;
1534
1535 // operation succeeded - abort operation
1536 throw types::no_type_t{};
1537 }
1538 else
1539 {
1540 // operation completed
1541 throw std::bad_variant_access{};
1542 }
1543 }
1544 });
1545
1546 // failed at this point
1547 }
1548 catch(const types::no_type_t&) { }
1549
1550 return result;
1551 }
1552
1553 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1554 std::enable_if_t<!types::is_type_in_list_v<types::no_throw_t, list> &&
1555 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1556 operator*(const T& v, const std::variant<EleTypes...>& variant)
1557 {
1558 constexpr auto Size = sizeof...(EleTypes);
1559
1560 std::variant<EleTypes...> result;
1561
1562 try
1563 {
1564 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1565 {
1566 if(auto a = std::get_if<indexer.Index>(&variant))
1567 {
1568 using ele_a_t = types::remove_cvref_t<decltype(*a)>;
1569
1570 if constexpr(types::is_multiplication_valid_v<T, ele_a_t>)
1571 {
1572 result = v * (*a);
1573
1574 // operation succeeded - abort operation
1575 throw types::no_type_t{};
1576 }
1577 else
1578 {
1579 // invalid operation
1580 throw std::bad_variant_access{};
1581 }
1582 }
1583 });
1584
1585 // failed at this point
1586 }
1587 catch(const types::no_type_t&) { }
1588
1589 return result;
1590 }
1591
1592 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1593 std::enable_if_t<!types::is_type_in_list_v<types::no_throw_t, list> &&
1594 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1595 operator*(const std::variant<EleTypes...>& variant, const T& v)
1596 {
1597 constexpr auto Size = sizeof...(EleTypes);
1598
1599 std::variant<EleTypes...> result;
1600
1601 try
1602 {
1603 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1604 {
1605 if(auto a = std::get_if<indexer.Index>(&variant))
1606 {
1607 using ele_a_t = types::remove_cvref_t<decltype(*a)>;
1608
1609 if constexpr(types::is_multiplication_valid_v<ele_a_t, T>)
1610 {
1611 result = (*a) * v;
1612
1613 // operation succeeded - abort operation
1614 throw types::no_type_t{};
1615 }
1616 else
1617 {
1618 // operation completed
1619 throw std::bad_variant_access{};
1620 }
1621 }
1622 });
1623
1624 // failed at this point
1625 }
1626 catch(const types::no_type_t&) { }
1627
1628 return result;
1629 }
1630
1631 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1632 std::enable_if_t<!types::is_type_in_list_v<types::no_throw_t, list> &&
1633 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1634 operator/(const T& v, const std::variant<EleTypes...>& variant)
1635 {
1636 constexpr auto Size = sizeof...(EleTypes);
1637
1638 std::variant<EleTypes...> result;
1639
1640 try
1641 {
1642 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1643 {
1644 if(auto a = std::get_if<indexer.Index>(&variant))
1645 {
1646 using ele_a_t = types::remove_cvref_t<decltype(*a)>;
1647
1648 if constexpr(types::is_division_valid_v<T, ele_a_t>)
1649 {
1650 result = v / (*a);
1651
1652 // operation succeeded - abort operation
1653 throw types::no_type_t{};
1654 }
1655 else
1656 {
1657 // invalid operation
1658 throw std::bad_variant_access{};
1659 }
1660 }
1661 });
1662
1663 // failed at this point
1664 }
1665 catch(const types::no_type_t&) { }
1666
1667 return result;
1668 }
1669
1670 template<typename T, typename ...EleTypes, typename list = types::type_list_t<EleTypes...>>
1671 std::enable_if_t<!types::is_type_in_list_v<types::no_throw_t, list> &&
1672 types::is_type_in_list_v<T, list>, std::variant<EleTypes...>>
1673 operator/(const std::variant<EleTypes...>& variant, const T& v)
1674 {
1675 constexpr auto Size = sizeof...(EleTypes);
1676
1677 std::variant<EleTypes...> result;
1678
1679 try
1680 {
1681 types::for_workhorse<Size>([&result, &v, &variant](auto indexer)
1682 {
1683 if(auto a = std::get_if<indexer.Index>(&variant))
1684 {
1685 using ele_a_t = types::remove_cvref_t<decltype(*a)>;
1686
1687 if constexpr(types::is_division_valid_v<ele_a_t, T>)
1688 {
1689 result = (*a) / v;
1690
1691 // operation succeeded - abort operation
1692 throw types::no_type_t{};
1693 }
1694 else
1695 {
1696 // operation failed
1697 throw std::bad_variant_access{};
1698 }
1699 }
1700 });
1701
1702 // failed at this point
1703 }
1704 catch(const types::no_type_t&) { }
1705
1706 return result;
1707 }
1708
1709 template<auto N1, auto D1, auto N2, auto D2>
1710 constexpr auto operator*(std::ratio<N1, D1>, std::ratio<N2, D2>)
1711 {
1712 /*
1713 N1 N2 N1 N2
1714 ----- x ----- = ----- x ----
1715 D1 D2 D2 D1
1716 */
1717
1718 constexpr std::ratio<N1, D2> aa;
1719 constexpr std::ratio<N2, D1> bb;
1720
1721 return std::ratio<aa.num * bb.num, aa.den * bb.den>{};
1722 }
1723
1724 template<auto N1, auto D1, auto N2, auto D2>
1725 constexpr auto operator/(std::ratio<N1, D1> a, std::ratio<N2, D2>)
1726 {
1727 constexpr std::ratio<D2, N2> bb;
1728
1729 return a * bb;
1730 }
1731
1732 template<auto N1, auto D1, auto N2, auto D2>
1733 constexpr auto operator+(std::ratio<N1, D1>, std::ratio<N2, D2>)
1734 {
1735 /*
1736 N1 N2 (D2/g) * N1 + (D1/g) * N2
1737 ----- + ----- = ---------------------------
1738 D1 D2 LCM(D1, D2)
1739
1740 */
1741
1742 constexpr auto g = std::gcd(D1, D2); // GCD - Greatest Common Divisor
1743 constexpr auto l = std::lcm(D1, D2); // LCM - Least Common Multiple
1744
1745 constexpr auto d2_g = D2 / g; // evenly divisible
1746 constexpr auto d1_g = D1 / g; // evenly divisible
1747
1748 return std::ratio<d2_g * N1 + d1_g * N2, l>{};
1749 }
1750
1751 template<auto N1, auto D1, auto N2, auto D2>
1752 constexpr auto operator-(std::ratio<N1, D1>, std::ratio<N2, D2>)
1753 {
1754 /*
1755 N1 N2 (D2/g) * N1 - (D1/g) * N2
1756 ----- + ----- = ---------------------------
1757 D1 D2 LCM(D1, D2)
1758
1759 */
1760
1761 constexpr auto g = std::gcd(D1, D2); // GCD - Greatest Common Divisor
1762 constexpr auto l = std::lcm(D1, D2); // LCM - Least Common Multiple
1763
1764 constexpr auto d2_g = D2 / g; // evenly divisible
1765 constexpr auto d1_g = D1 / g; // evenly divisible
1766
1767 return std::ratio<d2_g * N1 - d1_g * N2, l>{};
1768 }
1769
1770} // end of namespace std
1771
1772 #pragma warning(pop)
1773
1774#endif // end of file _TPF_STD_EXTENSIONS_HPP
return_t sum(int a, int b)
auto & cout
auto & endl
std::common_type_t< S, T > common_t
Definition: cpg_bitwise.hpp:79
auto apply_operation_inplace(OperationType &&operation, std::array< T, Size > &array, types::typed_sequence_t< Indices... >)
auto tuple_addition(const std::tuple< ArgTypes1... > &tuple_a, const std::tuple< ArgTypes2... > &tuple_b, types::typed_sequence_t< Indices... >)
auto tuple_division(const std::tuple< ArgTypes1... > &tuple_a, const std::tuple< ArgTypes2... > &tuple_b, types::typed_sequence_t< Indices... >)
auto array_less(const std::array< T, Size > &array_a, const std::array< S, Size > &array_b, types::typed_sequence_t< Indices... >)
auto array_equality(const std::array< T, Size > &array_a, const std::array< S, Size > &array_b, types::typed_sequence_t< Indices... >)
auto array_division(const std::array< T, Size > &array_a, const std::array< S, Size > &array_b, types::typed_sequence_t< Indices... >)
auto apply_operation(OperationType &&operation, const std::tuple< ArgTypes... > &tuple, types::typed_sequence_t< Indices... >)
auto array_multiplication(const std::array< T, Size > &array_a, const std::array< S, Size > &array_b, types::typed_sequence_t< Indices... >)
auto tuple_multiplication(const std::tuple< ArgTypes1... > &tuple_a, const std::tuple< ArgTypes2... > &tuple_b, types::typed_sequence_t< Indices... >)
auto array_less_equal(const std::array< T, Size > &array_a, const std::array< S, Size > &array_b, types::typed_sequence_t< Indices... >)
auto tuple_subtraction(const std::tuple< ArgTypes1... > &tuple_a, const std::tuple< ArgTypes2... > &tuple_b, types::typed_sequence_t< Indices... >)
auto array_subtraction(const std::array< T, Size > &array_a, const std::array< S, Size > &array_b, types::typed_sequence_t< Indices... >)
auto array_addition(const std::array< T, Size > &array_a, const std::array< S, Size > &array_b, types::typed_sequence_t< Indices... >)
auto operator!=(const std::array< T, Size1 > &array_a, const std::array< T, Size2 > &array_b)
auto operator==(const std::array< T, Size1 > &array_a, const std::array< T, Size2 > &array_b)
double standard_deviation(const std::tuple< Elements... > &tuple)
auto fail_safe_subtraction(T &&a, T &&b)
auto fail_safe_division(T &&a, S &&b)
auto fail_safe_addition(T &&a, T &&b)
auto operator<=(const std::array< T, Size > &array_a, const std::array< S, Size > &array_b)
auto apply_operation(OperationType &&operation, const std::tuple< ArgTypes... > &tuple)
auto operator>=(const std::array< T, Size > &array_a, const std::array< S, Size > &array_b)
void apply_operation_inplace(OperationType &&operation, std::array< T, Size > &array)
auto sum_of_elements(const std::tuple< Elements... > &tuple)
auto operator-(const std::tuple< ArgTypes1... > &tuple_a, const std::tuple< ArgTypes2... > &tuple_b)
auto fail_safe_multiplication(T &&a, T &&b)
double mean_of_elements(const std::tuple< Elements... > &tuple)
auto operator+(const std::tuple< ArgTypes1... > &tuple_a, const std::tuple< ArgTypes2... > &tuple_b)
auto operator>(const std::array< T, Size > &array_a, const std::array< S, Size > &array_b)
auto operator/(const std::tuple< ArgTypes1... > &tuple_a, const std::tuple< ArgTypes2... > &tuple_b)
auto operator*(const std::tuple< ArgTypes1... > &tuple_a, const std::tuple< ArgTypes2... > &tuple_b)
auto operator<(const std::array< T, Size > &array_a, const std::array< S, Size > &array_b)
constexpr decltype(auto) get(T(&c_array)[N]) noexcept
enable_if_in_list_t< Type, integral_list_t > lcm(Type a, Type b)
enable_if_in_list_t< Type, integral_list_t > gcd(Type a, Type b)
ContainerType< EleType, Types... > difference(const ContainerType< EleType, Types... > &left, const ContainerType< EleType, Types... > &right)
Definition: tpf_set.hpp:573
constexpr auto tuple_size_v
Definition: tpf_types.hpp:1913
Type to string name conversions are defined.
Definition: 31-visit.cpp:7
std::enable_if_t< tuple_common_type_v< T, S >, tuple_common_type_t< T, S > > tuple_operation_valid_t
Definition: tpf_types.hpp:5560
std::make_integer_sequence< remove_cvref_t< decltype(Size)>, Size > make_typed_sequence_t
Definition: tpf_types.hpp:2285
result_type signed_common_t
Definition: tpf_types.hpp:6606
std::integer_sequence< std::common_type_t< remove_cvref_t< decltype(Indices)>... >, Indices... > typed_sequence_t
Definition: tpf_types.hpp:2281
hidden::tuple_common_element_t< remove_cvref_t< TupleType > > tuple_common_element_t
Definition: tpf_types.hpp:5702
remove_cv_ref_t< Type > remove_cvref_t
Definition: tpf_types.hpp:298
This type is used to test validity of a type.
Definition: tpf_types.hpp:2020
This type is used to manipulate type list.
Definition: tpf_types.hpp:956
Type functions are implemented.