Line data Source code
1 : /*
2 : * Copyright (C) 2004-2025 Savoir-faire Linux Inc.
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 : */
17 : #pragma once
18 :
19 : #include <utility> // std::swap
20 : #include <cstdlib> // std::abs
21 : #include <iostream>
22 : #include <cmath> // std::fmod
23 : #include <functional> // std::modulus
24 : #include <ciso646> // and, or ...
25 : #include <fmt/format.h>
26 :
27 : extern "C" {
28 : #include <libavutil/rational.h> // specify conversions for AVRational
29 : }
30 :
31 : namespace jami {
32 :
33 : /**
34 : * Naive implementation of the boost::rational interface, described here:
35 : * https://www.boost.org/doc/libs/latest/libs/rational/rational.html
36 : */
37 : template<typename I>
38 : class rational
39 : {
40 : public:
41 : // Zero
42 5064 : constexpr rational() {}
43 :
44 : // Equal to n/1
45 1 : constexpr rational(I n)
46 1 : : num_(n)
47 1 : {}
48 :
49 : // General case (n/d)
50 437 : constexpr rational(I n, I d)
51 437 : : num_(n)
52 437 : , den_(d)
53 : {
54 437 : reduce();
55 437 : }
56 :
57 : // Define conversions to and from AVRational (equivalent)
58 391 : constexpr rational(AVRational r)
59 391 : : num_(r.num)
60 391 : , den_(r.den) {};
61 358 : constexpr operator AVRational() const { return AVRational {(int) num_, (int) den_}; }
62 :
63 0 : std::string to_string() const { return fmt::format("{}/{}", num_, den_); }
64 :
65 : // Normal copy constructors and assignment operators
66 :
67 : // Assignment from I
68 342 : rational& operator=(I n)
69 : {
70 342 : num_ = n;
71 342 : den_ = 1;
72 342 : return *this;
73 : }
74 :
75 : // Assign in place
76 : rational& assign(I n, I d)
77 : {
78 : num_ = n;
79 : den_ = d;
80 : reduce();
81 : return *this;
82 : }
83 :
84 : // Representation
85 301 : constexpr I numerator() const { return num_; };
86 154 : constexpr I denominator() const { return den_; };
87 :
88 : template<typename R = double>
89 770 : constexpr R real() const
90 : {
91 770 : return num_ / (R) den_;
92 : }
93 :
94 : // Arithmetic operators
95 : constexpr rational operator+(const rational& r) const { return {num_ * r.den_ + r.num_ * den_, den_ * r.den_}; }
96 0 : constexpr rational operator-(const rational& r) const { return {num_ * r.den_ - r.num_ * den_, den_ * r.den_}; }
97 0 : constexpr rational operator*(const rational& r) const { return {num_ * r.num_, den_ * r.den_}; }
98 : constexpr rational operator/(const rational& r) const { return {num_ * r.den_, den_ * r.num_}; }
99 :
100 : constexpr rational& operator+=(const rational& r)
101 : {
102 : std::swap(*this, *this + r);
103 : return *this;
104 : }
105 : constexpr rational& operator-=(const rational& r)
106 : {
107 : std::swap(*this, *this - r);
108 : return *this;
109 : }
110 : constexpr rational& operator*=(const rational& r)
111 : {
112 : num_ *= r.num_;
113 : den_ *= r.den_;
114 : reduce();
115 : return *this;
116 : }
117 : constexpr rational& operator/=(const rational& r)
118 : {
119 : num_ *= r.den_;
120 : den_ *= r.num_;
121 : reduce();
122 : return *this;
123 : }
124 :
125 : // Arithmetic with integers
126 : constexpr rational& operator+=(I i)
127 : {
128 : num_ += i * den_;
129 : return *this;
130 : }
131 : constexpr rational& operator-=(I i)
132 : {
133 : num_ -= i * den_;
134 : return *this;
135 : }
136 : constexpr rational& operator*=(I i)
137 : {
138 : num_ *= i;
139 : reduce();
140 : return *this;
141 : }
142 : constexpr rational& operator/=(I i)
143 : {
144 : den_ *= i;
145 : reduce();
146 : return *this;
147 : }
148 :
149 : // Increment and decrement
150 : constexpr const rational& operator++()
151 : {
152 : num_ += den_;
153 : return *this;
154 : }
155 : constexpr const rational& operator--()
156 : {
157 : num_ -= den_;
158 : return *this;
159 : }
160 :
161 : // Operator not
162 150 : constexpr bool operator!() const { return !num_; };
163 :
164 : // Boolean conversion
165 381 : explicit constexpr operator bool() const { return num_; }
166 :
167 : // Comparison operators
168 288 : constexpr bool operator<(const rational& r) const
169 : {
170 288 : bool inv = (den_ > 0) != (r.den_ > 0);
171 288 : return inv != (num_ * r.den_ < den_ * r.num_);
172 : }
173 : constexpr bool operator>(const rational& r) const
174 : {
175 : bool inv = (den_ > 0) != (r.den_ > 0);
176 : return inv != (num_ * r.den_ > den_ * r.num_);
177 : }
178 0 : constexpr bool operator==(const rational& r) const { return num_ * r.den_ == den_ * r.num_; }
179 : constexpr bool operator!=(const rational& r) const { return num_ * r.den_ != den_ * r.num_; }
180 :
181 : // Comparison with integers
182 32 : constexpr bool operator<(I i) const { return den_ < 0 ? (num_ > i * den_) : (num_ < i * den_); }
183 : constexpr bool operator>(I i) const { return den_ < 0 ? (num_ < i * den_) : (num_ > i * den_); }
184 : constexpr bool operator==(I i) const { return num_ == i * den_; }
185 : constexpr bool operator!=(I i) const { return num_ != i * den_; }
186 :
187 : private:
188 : I num_ {0};
189 : I den_ {1};
190 :
191 830 : static constexpr I gcd(I a, I b) { return b == (I) 0 ? a : gcd(b, std::modulus<I>()(a, b)); }
192 437 : constexpr void reduce()
193 : {
194 : if constexpr (std::is_integral<I>::value) {
195 400 : if (num_ and den_) {
196 294 : auto g = gcd(num_ >= 0 ? num_ : -num_, den_ >= 0 ? den_ : -den_);
197 294 : if (g > (I) 1) {
198 0 : num_ /= g;
199 0 : den_ /= g;
200 : }
201 : }
202 : }
203 437 : }
204 : };
205 :
206 : // Unary operators
207 : template<typename I>
208 : rational<I>
209 : operator+(const rational<I>& r)
210 : {
211 : return r;
212 : }
213 : template<typename I>
214 : rational<I>
215 : operator-(const rational<I>& r)
216 : {
217 : return {-r.numerator(), r.denominator()};
218 : }
219 :
220 : // Reversed order operators for - and / between (types convertible to) I and rational
221 : template<typename I, typename II>
222 : inline rational<I> operator-(II i, const rational<I>& r);
223 : template<typename I, typename II>
224 : inline rational<I>
225 101 : operator/(II i, const rational<I>& r)
226 : {
227 101 : return {i * r.denominator(), r.numerator()};
228 : }
229 :
230 : // Absolute value
231 : template<typename I>
232 : rational<I>
233 : abs(const rational<I>& r)
234 : {
235 : return {std::abs(r.numerator()), std::abs(r.denominator())};
236 : }
237 :
238 : // Input and output
239 : template<typename I>
240 : std::istream&
241 : operator>>(std::istream& is, rational<I>& r)
242 : {
243 : char sep;
244 : is >> r.num_ >> sep >> r.den_;
245 : return is;
246 : }
247 :
248 : template<typename I>
249 : std::ostream&
250 0 : operator<<(std::ostream& os, const rational<I>& r)
251 : {
252 0 : os << r.numerator() << '/' << r.denominator();
253 0 : return os;
254 : }
255 :
256 : // Type conversion
257 : template<typename T, typename I>
258 : T rational_cast(const rational<I>& r);
259 :
260 : } // namespace jami
261 :
262 : namespace std {
263 : template<>
264 : struct modulus<double>
265 : {
266 : double operator()(const double& lhs, const double& rhs) const { return std::fmod(lhs, rhs); }
267 : };
268 : } // namespace std
269 :
270 : template<typename I>
271 : struct fmt::formatter<jami::rational<I>> : fmt::formatter<std::string_view>
272 : {
273 : template<typename FormatContext>
274 : auto format(const jami::rational<I>& r, FormatContext& ctx) const -> decltype(ctx.out())
275 : {
276 : return fmt::formatter<std::string_view>::format(r.to_string(), ctx);
277 : }
278 : };
|