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