Sequoia
Loading...
Searching...
No Matches
Ratio.hpp
Go to the documentation of this file.
1
2// Copyright Oliver J. Rosten 2025. //
3// Distributed under the GNU GENERAL PUBLIC LICENSE, Version 3.0. //
4// (See accompanying file LICENSE.md or copy at //
5// https://www.gnu.org/licenses/gpl-3.0.en.html) //
7
8#pragma once
9
12#include <numeric>
13#include <ratio>
14
15namespace sequoia::maths
16{
17 template<auto Num, auto Den>
18 struct ratio;
19
20 template<auto Num, auto Den>
21 requires std::integral<decltype(Num)> && std::integral<decltype(Den)>
22 struct ratio<Num, Den>
23 {
24 using num_type = std::remove_cv_t<decltype(Num)>;
25 using den_type = std::remove_cv_t<decltype(Den)>;
26
27 constexpr static auto divisor{std::gcd(Num, Den)};
28 constexpr static auto num{Num/divisor};
29 constexpr static auto den{Den/divisor};
30 };
31
32 template<auto Num, auto Den>
33 requires arithmetic<decltype(Num)> && arithmetic<decltype(Den)>
34 && (std::floating_point<decltype(Num)> || std::floating_point<decltype(Den)>)
35 struct ratio<Num, Den>
36 {
37 using num_type = std::remove_cv_t<decltype(Num)>;
38 using den_type = std::remove_cv_t<decltype(Den)>;
39
40 // TO DO: once constexpr fmod / remainder is supported,
41 // consider more general reductions
42 constexpr static bool is_identity_v{Num == Den};
43 constexpr static auto num{is_identity_v ? num_type(1) : Num};
44 constexpr static auto den{is_identity_v ? den_type(1) : Den};
45 };
46
47 template<class T>
48 inline constexpr bool defines_ratio_v{
49 requires {
50 T::num;
51 T::den;
52
53 requires arithmetic<decltype(T::num)>;
54 requires arithmetic<decltype(T::den)>;
55 }
56 };
57
58 enum class allow_ratio_fp_conversion : bool { no, yes };
59
60 namespace impl
61 {
62
63 template<allow_ratio_fp_conversion, class...>
65
66 template<allow_ratio_fp_conversion Relaxed, class T, class U>
67 using ratio_product_t = ratio_product<Relaxed, T, U>::type;
68
69 template<allow_ratio_fp_conversion Relaxed, auto Num1, auto Den1, auto Num2, auto Den2>
70 requires arithmetic<decltype(Num1)> && arithmetic<decltype(Den1)>
71 && arithmetic<decltype(Num2)> && arithmetic<decltype(Den2)>
72 struct ratio_product<Relaxed, ratio<Num1, Den1>, ratio<Num2, Den2>>
73 {
74 constexpr auto static reduced_num_1{ratio<Num1, Den1>::num};
75 constexpr auto static reduced_den_1{ratio<Num1, Den1>::den};
76 constexpr auto static reduced_num_2{ratio<Num2, Den2>::num};
77 constexpr auto static reduced_den_2{ratio<Num2, Den2>::den};
80
81 template<auto M, auto N>
82 constexpr static auto product() {
83 if constexpr ((M == 1) && (N == 1))
84 {
85 if constexpr (std::floating_point<decltype(M)> || std::floating_point<decltype(N)>)
86 return std::intmax_t{1};
87 else
88 return std::common_type_t<std::remove_cv_t<decltype(M)>, std::remove_cv_t<decltype(N)>>{1};
89 }
90 else if constexpr (M == 1)
91 return N;
92 else if constexpr (N == 1)
93 return M;
94 else
95 {
96 using common_t = std::common_type_t<std::remove_cv_t<decltype(M)>, std::remove_cv_t<decltype(N)>>;
97 constexpr static bool overflow{std::numeric_limits<common_t>::max() / M < N};
98 if constexpr((Relaxed == allow_ratio_fp_conversion::no) || (!overflow))
99 {
100 return M * N;
101 }
102 else
103 {
104 if constexpr(std::integral<common_t>)
105 {
106 if constexpr(std::is_signed_v<common_t> && (sizeof(common_t) < sizeof(std::intmax_t)))
107 {
108 return product<static_cast<std::intmax_t>(M), static_cast<std::intmax_t>(N)>();
109 }
110 else if constexpr(!std::is_signed_v<common_t> && (sizeof(common_t) < sizeof(std::size_t)))
111 {
112 return product<static_cast<std::intmax_t>(M), static_cast<std::intmax_t>(N)>();
113 }
114 else
115 {
116 return static_cast<long double>(M) * static_cast<long double>(N);
117 }
118 }
119 else
120 {
121 return static_cast<long double>(M) * static_cast<long double>(N);
122 }
123 }
124 }
125 }
126
129
130 using type = ratio<num, den>;
131 };
132
133 template<allow_ratio_fp_conversion Relaxed, auto Num1, auto Den1, std::intmax_t Num2, std::intmax_t Den2>
134 requires arithmetic<decltype(Num1)> && arithmetic<decltype(Den1)>
135 struct ratio_product<Relaxed, ratio<Num1, Den1>, std::ratio<Num2, Den2>>
136 : ratio_product<Relaxed, ratio<Num1, Den1>, ratio<Num2, Den2>>
137 {
138 };
139
140 template<allow_ratio_fp_conversion Relaxed, std::intmax_t Num1, std::intmax_t Den1, auto Num2, auto Den2>
141 requires arithmetic<decltype(Num2)> && arithmetic<decltype(Den2)>
142 struct ratio_product<Relaxed, std::ratio<Num1, Den1>, ratio<Num2, Den2>>
143 : ratio_product<Relaxed, ratio<Num1, Den1>, ratio<Num2, Den2>>
144 {
145 };
146
147
148 template<allow_ratio_fp_conversion Relaxed, std::intmax_t Num1, std::intmax_t Den1, std::intmax_t Num2, std::intmax_t Den2>
149 struct ratio_product<Relaxed, std::ratio<Num1, Den1>, std::ratio<Num2, Den2>>
150 : ratio_product<Relaxed, ratio<Num1, Den1>, ratio<Num2, Den2>>
151 {
152 };
153 }
154
155 template<class T, class U, allow_ratio_fp_conversion Relaxed=allow_ratio_fp_conversion::no>
156 requires defines_ratio_v<T> && defines_ratio_v<U>
157 using ratio_multiply = impl::ratio_product_t<Relaxed, T, U>;
158
159 template<class T, class U, allow_ratio_fp_conversion Relaxed=allow_ratio_fp_conversion::no>
160 requires defines_ratio_v<T> && defines_ratio_v<U>
161 using ratio_divide = impl::ratio_product_t<Relaxed, T, ratio<U::den, U::num>>;
162}
\brieft A concept for arithmetic types
Definition: Concepts.hpp:81
Definition: Ratio.hpp:64
Definition: Ratio.hpp:18
Definition: PhysicalValues.hpp:950