C++ Library Extensions 2022.12.09
To help learn modern C++ programming
cpg_rational.hpp
Go to the documentation of this file.
1/*
2 Author: Thomas Kim
3 First Edit: July 21, 2021 - July 26, 2021
4
5 Our primary tool for the implementation of rational number is
6
7 Euclidean Algorithm GCD - std::gcd() - Greatest Common Divisor
8
9*/
10
11#ifndef _CPG_RATIONAL_HPP
12#define _CPG_RATIONAL_HPP
13
14#ifndef NOMINMAX
15#define NOMINMAX
16#endif
17
18#include "cpg_types.hpp"
19#include <numeric>
20#include <cassert>
21#include <cmath>
22#include <compare>
23#include <optional>
24
25#if defined(INCLUDE_PERMU_COMBI_TABLE) && !defined(PERMU_COMBI_TABLE_INCLUDED)
26
27 #define PERMU_COMBI_TABLE_INCLUDED
28
29 #if defined(PERMU_COMBI_TABLE_NAME)
30 #include PERMU_COMBI_TABLE_NAME
31 #else
32 #include "permu_combi_table.cxx"
33 #endif
34#endif
35
37{
38 #ifdef INCLUDE_PERMU_COMBI_TABLE
39
40 extern const std::vector<unsigned long long> factorial_table;
41 extern const std::vector<std::vector<unsigned long long>> permutation_table;
42 extern const std::vector<std::vector<unsigned long long>> combination_table;
43
44 #endif
45
46 namespace types = cpg::types;
47
49 types::type_container<float, double, long double>;
50
51 template<typename Type>
53
54 // we will allow all integral types
56 types::type_container<char, signed char,
57 unsigned char, short, unsigned short,
58 int, unsigned int, long, unsigned long,
59 long long, unsigned long long>;
60
61 template<typename T>
64
66 types::type_container<char, unsigned char, short, unsigned short,
67 int, unsigned int, long, unsigned long,
68 long long, unsigned long long, float, double, long double>;
69
70 template<typename Type>
72
73 // For A = a x G, B = b x G, where G = gcd(A, B)
74 // A -> a, B -> b, and returns G, the greatest common divisor,
75 // and the sign of b (B reduced) is always positive
76 template<allowed_type_c ElementType>
77 constexpr ElementType reduce_adjusted(ElementType& A, ElementType& B) noexcept
78 {
79
80 auto G = std::gcd(A, B);
81
82 A /= G; B /= G;
83
84 if constexpr(std::is_signed_v<ElementType>)
85 {
86 // ensure that B is always positive
87 if(B < 0)
88 {
89 A = -A; B = -B;
90 }
91 }
92
93 return G;
94 }
95
96 // For A = a x G, B = b x G, where G = gcd(A, B)
97 // A -> a, B -> b, and returns G, the greatest common divisor
98 // the signs of a and b are NOT adjusted
99 template<allowed_type_c ElementType>
100 constexpr ElementType reduce_simple(ElementType& A, ElementType& B) noexcept
101 {
102 try
103 {
104 auto G = std::gcd(A, B);
105
106 A /= G; B /= G; return G;
107 }
108 catch(const std::exception& e)
109 {
110 std::cerr << e.what() << '\n';
111 std::terminate();
112 }
113 }
114
115 template<allowed_type_c T>
117 {
118 auto max_number = std::numeric_limits<T>::max();
119
120 auto lhs = (long double)(T)1/(long double)max_number; // 1 / 3
121 auto rhs = (long double)(T)1/(long double)(max_number - 1); // 1 / 2
122
123 return lhs < rhs;
124 }
125
126 template<allowed_type_c ElementType>
128 {
129 public:
130
131 using value_type = ElementType;
132
133 ElementType m_num = 0; // numerator
134 ElementType m_den = 1; // denominator
135
136 constexpr void reduce() noexcept
137 {
138 reduce_adjusted(this->m_num, this->m_den);
139 }
140
141 constexpr rational() noexcept = default;
142
143 constexpr explicit
144 rational(ElementType numerator) noexcept:
145 m_num{numerator} { }
146
147 constexpr rational(ElementType numerator,
148 ElementType denominator, bool DoNotReduce = false) noexcept:
149 m_num{ numerator }, m_den{ denominator }
150 {
151 if(DoNotReduce == false)
152 reduce();
153 }
154
155 constexpr rational abs() const noexcept
156 {
157 return { this->m_num > 0 ? this->m_num : -this->m_num,
158 this->m_den, true}; // we do not need to call reduce();
159 }
160
161 template<typename Type = ElementType>
162 Type mag() const noexcept
163 {
164 const auto& p = this->m_num;
165 const auto& q = this->m_den;
166
167 if constexpr(allowed_type_c<Type>)
168 {
169 return (Type)std::round((float) p / q);
170 }
171 else
172 {
173 return (Type)p / q;
174 }
175 }
176
177 template<typename Type = ElementType>
178 Type abs_mag() const noexcept
179 {
180 const auto& p = this->m_num > 0 ? this->m_num : -this->m_num;
181 const auto& q = this->m_den;
182
183 if constexpr(allowed_type_c<Type>)
184 {
185 return (Type)std::round((float) p / q);
186 }
187 else
188 {
189 return (Type)p / q;
190 }
191 }
192
193 constexpr ~rational() { }
194
195 // denominator can never be zero
196 // the rational number is in valid state
197 constexpr bool valid() const noexcept
198 {
199 return (this->m_den > 0);
200 }
201
202 // valid and not zero
203 constexpr operator bool() const noexcept
204 {
205 return valid() && (this->m_num != 0);
206 }
207
208 // valid and zero
209 constexpr bool operator!() const noexcept
210 {
211 return valid() && (this->m_num == 0);
212 }
213
214 // access numerator
215 constexpr const ElementType& num() const noexcept
216 {
217 return this->m_num;
218 }
219
220 // access denominator
221 constexpr const ElementType& den() const noexcept
222 {
223 return this->m_den;
224 }
225
226 // return old value of the numerator,
227 // set a new value to the numerator
228 constexpr ElementType num(ElementType new_value, bool DoNotReduce = false) noexcept
229 {
230 auto old_value = this->m_num;
231 this->m_num = new_value;
232
233 if(DoNotReduce == false)
234 this->reduce(); // critically important
235
236 return old_value;
237 }
238
239 // return old value of the denominator
240 // set a new value to the denominator
241 constexpr ElementType den(ElementType new_value, bool DoNotReduce = false) noexcept
242 {
243 auto old_value = this->m_den;
244 this->m_den = new_value;
245
246 if(DoNotReduce == false)
247 this->reduce(); // critically important
248
249 return old_value;
250 }
251
252 // returns old value of the rational number
253 // set new value and reduce
254 constexpr rational operator()
255 (ElementType numerator, ElementType denominator, bool DoNotReduce = false) noexcept
256 {
257 auto old_value = *this;
258 this->m_num = numerator;
259 this->m_den = denominator;
260
261 if(DoNotReduce==false)
262 this->reduce(); // critically important
263
264 return old_value;
265 }
266
267 // set new value and reduce
268 constexpr void set(ElementType numerator,
269 ElementType denominator, bool DoNotReduce = false) noexcept
270 {
271 this->m_num = numerator;
272 this->m_den = denominator;
273
274 if(DoNotReduce==false)
275 this->reduce(); // critically important
276 }
277
278 // pre-increment operator++()
279 // increment the value by 1
280 // then return the updated value
281 constexpr const rational& operator++() noexcept
282 {
283 *this += 1; return *this;
284 }
285
286 // pre-decrement operator--()
287 // decrement the value by 1
288 // then return the updated value
289 constexpr const rational& operator--() noexcept
290 {
291 *this -= 1; return *this;
292 }
293
294 // post-increment operator++(int)
295 // increment the value by 1
296 // return the old value
297 constexpr rational operator++(int) noexcept
298 {
299 auto old_value = *this;
300 *this += 1;
301 return old_value;
302 }
303
304 // post-decrement operator--(int)
305 // decrement the value by 1
306 // return the old value
307 constexpr rational operator--(int) noexcept
308 {
309 auto old_value = *this;
310 *this -= 1;
311 return old_value;
312 }
313
314 template<number_c Type = long long>
315 constexpr operator Type() const noexcept
316 {
317 return (Type)this->m_num / this->m_den;
318 }
319
320 template<allowed_type_c TargetType = ElementType,
321 real_number_c RoundType = long double>
322 TargetType round() const noexcept
323 {
324 return (TargetType)std::round((RoundType)this->m_num / this->m_den);
325 }
326
327 // we define Rational Number
328 friend constexpr std::strong_ordering
329 operator <=>(const rational& lhs, const rational& rhs)
330 {
331 if(lhs.m_den == rhs.m_den)
332 {
333 if(lhs.m_num == rhs.m_num)
334 return std::strong_ordering::equal;
335 else if(lhs.m_num < rhs.m_num)
336 return std::strong_ordering::less;
337 else
338 return std::strong_ordering::greater;
339 }
340 else
341 {
342 if constexpr(tell_comparable_as_double<ElementType>())
343 {
344 long double l = lhs; long double r = rhs;
345
346 if(l < r)
347 return std::strong_ordering::less;
348 else if (l > r )
349 return std::strong_ordering::greater;
350 else // this part never executed
351 return std::strong_ordering::equal;
352 }
353 else
354 {
355 auto [l, a] = lhs;
356 auto [r, b] = rhs;
357
358 auto g = reduce_adjusted(a, b);
359
360 if(l != 0 || r != 0)
361 {
362 auto gg = std::gcd(l, r);
363 l /= gg; r /= gg;
364 }
365
366 l *= b; r *= a;
367
368 if(l < r)
369 return std::strong_ordering::less;
370 else if (l > r )
371 return std::strong_ordering::greater;
372 else // this part never executed
373 return std::strong_ordering::equal;
374 }
375 }
376 }
377
378 friend constexpr std::strong_ordering operator <=>
379 (const rational& lhs, ElementType rhs) noexcept
380 {
381 return lhs <=> rational{rhs};
382 }
383
384 friend constexpr bool operator ==
385 (const rational& lhs, const rational& rhs) noexcept
386 {
387 if( lhs.m_den == rhs.m_den && lhs.m_num == rhs.m_num)
388 return true;
389 else
390 return false;
391 }
392
393 friend constexpr bool operator ==
394 (const rational& lhs, ElementType rhs) noexcept
395 {
396 return lhs == rational{rhs};
397 }
398
400
401 // multiplicative inverse
402 constexpr rational reciprocal() const noexcept
403 {
404 // return { this->m_den, this->m_num };
405
406 if constexpr(std::is_signed_v<ElementType>)
407 {
408 if(this->m_num < 0)
409 return { -this->m_den, -this->m_num, true };
410 else
411 return { this->m_den, this->m_num, true };
412
413 }
414 else // unsigned integral types,
415 // such as unsigned int, unsigned long long
416 {
417 // we don't need to reduce, because
418 // m_den, n_num are already reduced
419 return { this->m_den, this->m_num, true };
420 }
421 }
422
423 constexpr rational& operator *=(const rational& rhs) noexcept
424 {
425 /*
426 this->m_num rhs.m_num rhs.m_num this->m_num
427 ------------ x ----------- = ------------ x ----------------
428 this->m_den rhs.m_den this->m_den rhs.m_den
429
430 */
431
432 rational L{ rhs.m_num, this->m_den};
433 rational R{ this->m_num, rhs.m_den};
434
435 *this = rational{ L.m_num * R.m_num, L.m_den * R.m_den };
436
437 return *this;
438 }
439
440 constexpr rational& operator /=(const rational& rhs) noexcept
441 {
442 *this *= rhs.reciprocal(); return *this;
443 }
444
445 constexpr rational& operator *=(ElementType rhs) noexcept
446 {
447 /* this->m_num rhs
448 ----------- x rhs = this->m_num x -------------
449 this->m_den this->m_den
450 */
451
452 // auto num = this->m_num;
453 rational R{rhs, this->m_den};
454
455 *this = rational{ this->m_num * R.m_num, R.m_den };
456
457 return *this;
458 }
459
460 friend constexpr rational
461 operator*(ElementType lhs, rational rhs) noexcept
462 {
463 rhs *= lhs; return rhs;
464 }
465
466 friend constexpr rational
467 operator*(rational lhs, ElementType rhs) noexcept
468 {
469 lhs *= rhs; return lhs;
470 }
471
472 constexpr rational& operator /=(ElementType rhs) noexcept
473 {
474 /* this->m_num 1 this->m_num 1
475 ----------- x ------ = ------------ x -----------
476 this->m_den rhs rhs this->m_den
477 */
478
479 rational L{this->m_num, rhs};
480
481 *this = rational{ L.m_num, L.m_den * this->m_den };
482
483 return *this;
484 }
485
486 friend constexpr rational
487 operator/(ElementType lhs, const rational& rhs) noexcept
488 {
489 auto R = rhs.reciprocal();
490 R *= lhs; return R;
491 }
492
493 friend constexpr rational
494 operator/(const rational &lhs, ElementType rhs) noexcept
495 {
496 auto [A, P] = lhs; // A is numerator, P is denominator
497
498 /* A 1 1 A
499 ---------- * --------- = ---------- x -----------
500 P rhs P rhs
501 */
502
503 reduce_simple(A, rhs); // (A / rhs)
504
505 return { A, P * rhs };
506 }
507
508 friend constexpr rational
509 operator*(rational lhs, const rational& rhs) noexcept
510 {
511 lhs *= rhs; return lhs;
512 }
513
514 friend constexpr rational
515 operator/(rational lhs, const rational& rhs) noexcept
516 {
517 lhs /= rhs; return lhs;
518 }
519
521
522 // additive inverse
523 constexpr rational inverse() const noexcept
524 {
525 return { -this->m_num, this->m_den, true };
526 }
527
528 // additvie inverse
529 constexpr rational operator-() const noexcept
530 {
531 return { -this->m_num, this->m_den, true };
532 }
533
534 constexpr rational& operator +=(const rational& rhs) noexcept
535 {
536 auto [A, P] = *this; // IMPORTANT - P, Q are denominators
537 auto [B, Q] = rhs; // A, B are numerators
538
539 /*
540 A B A B
541 ----- + ----- = ---------- + --------, G = gcd(P, Q)
542 P Q p x G q x G
543
544 A B q x A p x B
545 --------- + -------- = ---------- + ------------
546 p x G q x G q x p x G p x q x G
547
548
549 q x A + p x B
550 -------------------
551 q x p x G
552 */
553
554 // P -> p, Q -> q
555 auto G = rational_number::reduce_simple(P, Q);
556
557 ElementType g = 1;
558
559 if(A != 0 || B != 0)
560 g = reduce_simple(A, B);
561
562 auto gg = reduce_simple(g, G);
563
564 *this = rational{ g * Q * A + g * P * B, Q * P * G };
565 return *this;
566 }
567
568 constexpr rational& operator -=(const rational& rhs) noexcept
569 {
570 auto [A, P] = *this; // IMPORTANT - P, Q are denominators
571 auto [B, Q] = rhs; // A, B are numerators
572
573 /*
574 A B A B
575 ----- - ----- = ---------- - --------, G = gcd(P, Q)
576 P Q p x G q x G
577
578 A B q x A p x B
579 --------- - -------- = ---------- - ------------
580 p x G q x G q x p x G p x q x G
581
582
583 q x A - p x B
584 -------------------
585 q x p x G
586 */
587
588 // P -> p, Q -> q
589 auto G = rational_number::reduce_simple(P, Q);
590
591 ElementType g = 1;
592
593 if(A != 0 || B != 0)
594 g = reduce_simple(A, B);
595
596 auto gg = reduce_simple(g, G);
597
598 *this = rational{ g * Q * A - g * P * B, Q * P * G };
599
600 return *this;
601 }
602
603 friend constexpr rational
604 operator+(rational lhs, const rational& rhs) noexcept
605 {
606 lhs += rhs; return lhs;
607 }
608
609 friend constexpr rational
610 operator-(rational lhs, const rational& rhs) noexcept
611 {
612 lhs -= rhs; return lhs;
613 }
614
615 constexpr rational& operator +=(ElementType rhs) noexcept
616 {
617 auto [A, P] = *this; // IMPORTANT - P is denominator
618 // A is numerator
619
620 /*
621 A rhs A P x rhs
622 ----- + ----- = ------- + --------
623 P 1 P P
624
625 A + P x rhs
626 -------------------
627 P
628 */
629
630 *this = rational{ A + P * rhs, P};
631 return *this;
632 }
633
634 constexpr rational& operator -=(ElementType rhs) noexcept
635 {
636 auto [A, P] = *this; // IMPORTANT - P is denominator
637 // A is numerator
638
639 /*
640 A rhs A P x rhs
641 ----- - ----- = ------- - --------
642 P 1 P P
643
644 A - P x rhs
645 -------------------
646 P
647 */
648
649 *this = rational{ A - P * rhs, P};
650 return *this;
651 }
652
653 friend constexpr rational
654 operator +(ElementType lhs, rational rhs) noexcept
655 {
656 rhs += lhs; return rhs;
657 }
658
659 friend constexpr rational
660 operator -(ElementType lhs, const rational& rhs) noexcept
661 {
662 auto& [B, Q] = rhs; // Q is denominator, B is numerator
663
664 /*
665 lhs B lhs * Q - B
666 ----- - ------- = --------------
667 1 Q Q
668 */
669
670 return { lhs * Q - B, Q };
671 }
672
673 friend constexpr rational
674 operator+(rational lhs, ElementType rhs) noexcept
675 {
676 lhs += rhs; return lhs;
677 }
678
679 friend constexpr rational
680 operator - (rational lhs, ElementType rhs) noexcept
681 {
682 lhs -= rhs; return lhs;
683 }
684
685 template<typename CharType>
686 friend std::basic_ostream<CharType>& operator<<(
687 std::basic_ostream<CharType>& os, const rational& r) noexcept
688 {
689 if constexpr(std::is_same_v<ElementType, char>
690 || std::is_same_v<ElementType, signed char>)
691 {
692 os << Cpg_CharStr("( ")
693 << (short)r.m_num
694 << Cpg_CharStr(", ")
695 << (short)r.m_den
696 << Cpg_CharStr(" )");
697 }
698 else if constexpr(std::is_same_v<ElementType, unsigned char> )
699 {
700 os << Cpg_CharStr("( ")
701 << (unsigned short)r.m_num
702 << Cpg_CharStr(", ")
703 << (unsigned short)r.m_den
704 << Cpg_CharStr(" )");
705 }
706 else
707 {
708 os << Cpg_CharStr("( ")
709 << r.m_num
710 << Cpg_CharStr(", ")
711 << r.m_den
712 << Cpg_CharStr(" )");
713 }
714
715 return os;
716 }
717 };
718 // end of class rational
719
720 template<allowed_type_c L, allowed_type_c R>
721 requires ( !std::same_as<L, R>)
722 std::strong_ordering operator <=>(rational<L> const& lhs, rational<R> const& rhs)
723 {
724 using common_t = std::common_type_t<L, R>;
725
726 rational<common_t> a{ (common_t)lhs.m_num, (common_t)lhs.m_den, true};
727 rational<common_t> b{ (common_t)rhs.m_num, (common_t)rhs.m_den, true};
728
729 return a <=> b;
730 }
731
732 template<allowed_type_c L, allowed_type_c R>
733 requires ( !std::same_as<L, R>)
734 std::strong_ordering operator <=>(rational<L> const& lhs, R rhs)
735 {
736 using common_t = std::common_type_t<L, R>;
737
738 rational<common_t> a{ (common_t)lhs.m_num, (common_t)lhs.m_den, true};
740
741 return a <=> b;
742 }
743
744 template<allowed_type_c L, real_number_c R>
745 std::strong_ordering operator <=>(rational<L> const& lhs, R rhs)
746 {
747 long double l = (long double)lhs;
748 long double r = (long double)rhs;
749
750 if(l < r)
751 return std::strong_ordering::less;
752 else if(l > r)
753 return std::strong_ordering::greater;
754 else
755 return std::strong_ordering::equal;
756 }
757
758 template<allowed_type_c L, allowed_type_c R>
759 requires ( !std::same_as<L, R>)
760 bool operator ==(rational<L> const& lhs, rational<R> const& rhs)
761 {
762 using common_t = std::common_type_t<L, R>;
763
764 rational<common_t> a{ (common_t)lhs.m_num, (common_t)lhs.m_den, true};
765 rational<common_t> b{ (common_t)rhs.m_num, (common_t)rhs.m_den, true};
766
767 return a == b;
768 }
769
770 template<allowed_type_c L, allowed_type_c R>
771 requires ( !std::same_as<L, R>)
772 bool operator ==(rational<L> const& lhs, R rhs)
773 {
774 using common_t = std::common_type_t<L, R>;
775
776 rational<common_t> a{ (common_t)lhs.m_num, (common_t)lhs.m_den, true};
778
779 return a == b;
780 }
781
782 template<allowed_type_c L, real_number_c R>
783 bool operator ==(rational<L> const& lhs, R rhs)
784 {
785 long double l = (long double)lhs;
786 long double r = (long double)rhs;
787
788 long double diff = abs(l - r);
789
790 constexpr auto epsilon =
791 std::numeric_limits<R>::epsilon();
792
793 return (diff < epsilon);
794 }
795
796 #ifdef INCLUDE_PERMU_COMBI_TABLE
797
798 auto factorial(unsigned long long n) noexcept
799 {
800 std::optional<unsigned long long> result;
801
802 if(n < factorial_table.size() )
803 result = factorial_table[n];
804
805 return result;
806 }
807 // end of factorial()
808
809 auto nPr(unsigned long long n, unsigned long long r) noexcept
810 {
811 std::optional<unsigned long long> result;
812
813 if(r == 0)
814 {
815 result = 1; return result;
816 }
817 else if (r == 1)
818 {
819 result = n; return result;
820 }
821 else if( n < permutation_table.size())
822 {
823 if(r < permutation_table[n].size())
824 result = permutation_table[n][r];
825
826 return result;
827 }
828 else
829 {
830 unsigned long long old_value = 0;
831 unsigned long long new_value = 1;
832
833 bool success = true;
834
835 for(unsigned long long k = 0; k < r; ++k )
836 {
837 old_value = new_value;
838 new_value *= (n-k);
839
840 if(old_value != new_value /(n-k))
841 {
842 success = false; break;
843 }
844 }
845
846 if(success) result = new_value;
847
848 return result;
849 }
850 }
851 // end of nPr()
852
853 auto nCr(unsigned long long n, unsigned long long r) noexcept
854 {
855 std::optional<unsigned long long> result;
856
857 if(r == 0 || r == n)
858 {
859 result = 1; return result;
860 }
861
862 if ( r > (n-r) ) r = n-r;
863
864 if( n < combination_table.size())
865 {
866 if ( r > (n-r) ) r = n-r;
867
868 if(r < combination_table[n].size())
869 result = combination_table[n][r];
870
871 return result;
872 }
873 else
874 {
875 /* nPr (n - 0) x (n - 1) x .... x (n - (r-1) )
876 nCr * r! = nPr => nCr = ---------- = -------------------------------------------
877 r! (r-0) x (r - 1) x .....x (r - (r-1) )
878
879 nCr, r = 0 => 1, r = n => 1
880 nCr = nCn-r, r = min{ r, n-r }
881 */
882
883 rational<unsigned long long> old_value;
884 rational<unsigned long long> new_value{1};
885
886 bool success = true;
887
888 for(unsigned long long k = 0; k < r; ++k)
889 {
890 auto value = rational<unsigned long long>{n - k, r - k};
891
892 old_value = new_value;
893 new_value *= value;
894
895 if(old_value != (new_value / value))
896 {
897 success = false; break;
898 }
899 }
900
901 if(success)
902 result = (unsigned long long)new_value;
903
904 return result;
905 }
906 }
907 // end of nCr()
908 #else
909 auto factorial(unsigned long long n) noexcept
910 {
911 std::optional<unsigned long long> result;
912
913 unsigned long long old_value;
914 unsigned long long new_value = 1;
915 bool success = true;
916
917 for(unsigned long long k = 1; k <= n; ++k)
918 {
919 old_value = new_value; new_value *= k;
920
921 if(old_value != new_value / k)
922 {
923 success = false; break;
924 }
925 }
926
927 if(success) result = new_value;
928
929 return result;
930 }
931 // end of factorial()
932
933 auto nPr(unsigned long long n, unsigned long long r = 1) noexcept
934 {
935 std::optional<unsigned long long> result;
936
937 unsigned long long old_value;
938 unsigned long long new_value = 1;
939 bool success = true;
940
941 for(unsigned long long k = 0; k < r; ++k)
942 {
943 old_value = new_value;
944 new_value *= (n-k);
945
946 if(old_value != (new_value / (n-k)))
947 {
948 success = false; break;
949 }
950 }
951
952 if(success) result = new_value;
953
954 return result;
955 }
956 // end of nPr()
957
958 auto nCr(unsigned long long n, unsigned long long r) noexcept
959 {
960 /* nPr (n - 0) x (n - 1) x .... x (n - (r-1) )
961 nCr * r! = nPr => nCr = ---------- = ----------------------------------
962 r! (r-0) x (r - 1) x .....x (r - (r-1) )
963
964
965 nCr, r = 0 => 1, r = n => 1
966 nCr = nCn-r, r = min{ r, n-r }
967 */
968
969 std::optional<unsigned long long> result;
970
971 if(r == 0 || r == n)
972 {
973 result = 1; return result;
974 }
975
976 if ( r > (n-r) ) r = n-r;
977
979 rational<unsigned long long> new_value{1};
980
981 bool success = true;
982
983 for(unsigned long long k = 0; k < r; ++k)
984 {
985 auto value = rational<unsigned long long>{n - k, r - k};
986
987 old_value = new_value;
988 new_value *= value;
989
990 if(old_value != (new_value / value))
991 {
992 success = false; break;
993 }
994 }
995
996 if(success)
997 result = (unsigned long long)new_value;
998
999 return result;
1000 }
1001 // end of nCr()
1002
1003 #endif // end of INCLUDE_PERMU_COMBI_TABLE
1004}
1005// end of namespace rational_number
1006
1007#endif // end of file _CPG_RATIONAL_HPP
constexpr rational reciprocal() const noexcept
constexpr rational abs() const noexcept
constexpr rational operator++(int) noexcept
constexpr bool operator!() const noexcept
friend constexpr rational operator-(rational lhs, const rational &rhs) noexcept
constexpr rational & operator*=(const rational &rhs) noexcept
friend constexpr rational operator/(const rational &lhs, ElementType rhs) noexcept
friend constexpr rational operator*(rational lhs, ElementType rhs) noexcept
friend constexpr std::strong_ordering operator<=>(const rational &lhs, const rational &rhs)
friend constexpr rational operator*(rational lhs, const rational &rhs) noexcept
Type mag() const noexcept
constexpr rational(ElementType numerator, ElementType denominator, bool DoNotReduce=false) noexcept
friend constexpr rational operator+(rational lhs, const rational &rhs) noexcept
constexpr rational operator--(int) noexcept
friend constexpr rational operator/(rational lhs, const rational &rhs) noexcept
constexpr rational & operator-=(const rational &rhs) noexcept
friend std::basic_ostream< CharType > & operator<<(std::basic_ostream< CharType > &os, const rational &r) noexcept
friend constexpr rational operator*(ElementType lhs, rational rhs) noexcept
constexpr rational inverse() const noexcept
constexpr const rational & operator++() noexcept
friend constexpr rational operator/(ElementType lhs, const rational &rhs) noexcept
constexpr void set(ElementType numerator, ElementType denominator, bool DoNotReduce=false) noexcept
constexpr const rational & operator--() noexcept
TargetType round() const noexcept
constexpr const ElementType & num() const noexcept
constexpr bool valid() const noexcept
constexpr rational & operator/=(const rational &rhs) noexcept
constexpr ElementType num(ElementType new_value, bool DoNotReduce=false) noexcept
constexpr ElementType den(ElementType new_value, bool DoNotReduce=false) noexcept
constexpr const ElementType & den() const noexcept
constexpr rational() noexcept=default
constexpr rational & operator+=(const rational &rhs) noexcept
constexpr void reduce() noexcept
constexpr rational operator-() const noexcept
Type abs_mag() const noexcept
friend constexpr rational operator+(rational lhs, ElementType rhs) noexcept
#define Cpg_CharStr(asciistr)
Definition: cpg_types.hpp:582
std::common_type_t< S, T > common_t
Definition: cpg_bitwise.hpp:79
types::type_container< char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long > allowed_type_list
types::type_container< float, double, long double > real_number_list
constexpr bool tell_comparable_as_double()
constexpr ElementType reduce_simple(ElementType &A, ElementType &B) noexcept
std::strong_ordering operator<=>(rational< L > const &lhs, rational< R > const &rhs)
const std::vector< std::vector< unsigned long long > > permutation_table
const std::vector< unsigned long long > factorial_table
const std::vector< std::vector< unsigned long long > > combination_table
auto factorial(unsigned long long n) noexcept
types::type_container< char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long, float, double, long double > numerical_type_list
auto nCr(unsigned long long n, unsigned long long r) noexcept
constexpr ElementType reduce_adjusted(ElementType &A, ElementType &B) noexcept
auto nPr(unsigned long long n, unsigned long long r=1) noexcept
bool operator==(rational< L > const &lhs, rational< R > const &rhs)
enable_if_in_list_t< Type, integral_list_t > gcd(Type a, Type b)
Type to string name conversions are defined.
Definition: 31-visit.cpp:7