Line data Source code
1 : /* 2 : * Copyright (C) 2004-2024 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 : 26 : extern "C" { 27 : #include <libavutil/rational.h> // specify conversions for AVRational 28 : } 29 : 30 : namespace jami { 31 : 32 : /** 33 : * Naive implementation of the boost::rational interface, described here: 34 : * http://www.boost.org/doc/libs/1_57_0/libs/rational/rational.html 35 : */ 36 : template<typename I> 37 : class rational 38 : { 39 : public: 40 : // Constructors 41 5753 : constexpr rational() {} // Zero 42 1 : constexpr rational(I n) 43 1 : : num_(n) {} // Equal to n/1 44 485 : constexpr rational(I n, I d) 45 485 : : num_(n) 46 485 : , den_(d) 47 : { 48 485 : reduce(); 49 485 : } // General case (n/d) 50 : 51 : // Define conversions to and from AVRational (equivalent) 52 363 : constexpr rational(AVRational r) 53 363 : : num_(r.num) 54 363 : , den_(r.den) {}; 55 332 : constexpr operator AVRational() const { return AVRational {(int) num_, (int) den_}; } 56 : 57 : // Normal copy constructors and assignment operators 58 : 59 : // Assignment from I 60 341 : rational& operator=(I n) 61 : { 62 341 : num_ = n; 63 341 : den_ = 1; 64 341 : return *this; 65 : } 66 : 67 : // Assign in place 68 : rational& assign(I n, I d) 69 : { 70 : num_ = n; 71 : den_ = d; 72 : reduce(); 73 : return *this; 74 : } 75 : 76 : // Representation 77 311 : constexpr I numerator() const { return num_; }; 78 159 : constexpr I denominator() const { return den_; }; 79 : 80 : template<typename R = double> 81 1102 : constexpr R real() const 82 : { 83 1102 : return num_ / (R) den_; 84 : } 85 : 86 : // In addition to the following operators, all of the "obvious" derived 87 : // operators are available - see operators.hpp 88 : 89 : // Arithmetic operators 90 : constexpr rational operator+(const rational& r) const 91 : { 92 : return {num_ * r.den_ + r.num_ * den_, den_ * r.den_}; 93 : } 94 0 : constexpr rational operator-(const rational& r) const 95 : { 96 0 : return {num_ * r.den_ - r.num_ * den_, den_ * r.den_}; 97 : } 98 0 : constexpr rational operator*(const rational& r) const { return {num_ * r.num_, den_ * r.den_}; } 99 : constexpr rational operator/(const rational& r) const { return {num_ * r.den_, den_ * r.num_}; } 100 : 101 : rational& operator+=(const rational& r) 102 : { 103 : std::swap(*this, *this + r); 104 : return *this; 105 : } 106 : rational& operator-=(const rational& r) 107 : { 108 : std::swap(*this, *this - r); 109 : return *this; 110 : } 111 : rational& operator*=(const rational& r) 112 : { 113 : num_ *= r.num_; 114 : den_ *= r.den_; 115 : reduce(); 116 : return *this; 117 : } 118 : rational& operator/=(const rational& r) 119 : { 120 : num_ *= r.den_; 121 : den_ *= r.num_; 122 : reduce(); 123 : return *this; 124 : } 125 : 126 : // Arithmetic with integers 127 : rational& operator+=(I i) 128 : { 129 : num_ += i * den_; 130 : return *this; 131 : } 132 : rational& operator-=(I i) 133 : { 134 : num_ -= i * den_; 135 : return *this; 136 : } 137 : rational& operator*=(I i) 138 : { 139 : num_ *= i; 140 : reduce(); 141 : return *this; 142 : } 143 : rational& operator/=(I i) 144 : { 145 : den_ *= i; 146 : reduce(); 147 : return *this; 148 : } 149 : 150 : // Increment and decrement 151 : const rational& operator++() 152 : { 153 : num_ += den_; 154 : return *this; 155 : } 156 : const rational& operator--() 157 : { 158 : num_ -= den_; 159 : return *this; 160 : } 161 : 162 : // Operator not 163 159 : constexpr bool operator!() const { return !num_; }; 164 : 165 : // Boolean conversion 166 409 : explicit constexpr operator bool() const { return num_; } 167 : 168 : // Comparison operators 169 304 : constexpr bool operator<(const rational& r) const 170 : { 171 304 : bool inv = (den_ > 0) != (r.den_ > 0); 172 304 : return inv != (num_ * r.den_ < den_ * r.num_); 173 : } 174 : constexpr bool operator>(const rational& r) const 175 : { 176 : bool inv = (den_ > 0) != (r.den_ > 0); 177 : return inv != (num_ * r.den_ > den_ * r.num_); 178 : } 179 0 : constexpr bool operator==(const rational& r) const { return num_ * r.den_ == den_ * r.num_; } 180 : constexpr bool operator!=(const rational& r) const { return num_ * r.den_ != den_ * r.num_; } 181 : 182 : // Comparison with integers 183 38 : constexpr bool operator<(I i) const { return den_ < 0 ? (num_ > i * den_) : (num_ < i * den_); } 184 : constexpr bool operator>(I i) const { return den_ < 0 ? (num_ < i * den_) : (num_ > i * den_); } 185 : constexpr bool operator==(I i) const { return num_ == i * den_; } 186 : constexpr bool operator!=(I i) const { return num_ != i * den_; } 187 : 188 : private: 189 : I num_ {0}; 190 : I den_ {1}; 191 : 192 928 : static constexpr I gcd(I a, I b) { return b == (I) 0 ? a : gcd(b, std::modulus<I>()(a, b)); } 193 485 : void reduce() 194 : { 195 : if (std::is_integral<I>::value) { 196 437 : if (num_ and den_) { 197 328 : auto g = gcd(num_ >= 0 ? num_ : -num_, den_ >= 0 ? den_ : -den_); 198 328 : if (g > (I) 1) { 199 0 : num_ /= g; 200 0 : den_ /= g; 201 : } 202 : } 203 : } 204 485 : } 205 : }; 206 : 207 : // Unary operators 208 : template<typename I> 209 : rational<I> 210 : operator+(const rational<I>& r) 211 : { 212 : return r; 213 : } 214 : template<typename I> 215 : rational<I> 216 : operator-(const rational<I>& r) 217 : { 218 : return {-r.numerator(), r.denominator()}; 219 : }; 220 : 221 : // Reversed order operators for - and / between (types convertible to) I and rational 222 : template<typename I, typename II> 223 : inline rational<I> operator-(II i, const rational<I>& r); 224 : template<typename I, typename II> 225 : inline rational<I> 226 106 : operator/(II i, const rational<I>& r) 227 : { 228 106 : return {i * r.denominator(), r.numerator()}; 229 : } 230 : 231 : // Absolute value 232 : template<typename I> 233 : rational<I> 234 : abs(const rational<I>& r) 235 : { 236 : return {std::abs(r.numerator()), std::abs(r.denominator())}; 237 : } 238 : 239 : // Input and output 240 : template<typename I> 241 : std::istream& 242 : operator>>(std::istream& is, rational<I>& r) 243 : { 244 : char sep; 245 : is >> r.num_ >> sep >> r.den_; 246 : return is; 247 : } 248 : 249 : template<typename I> 250 : std::ostream& 251 0 : operator<<(std::ostream& os, const rational<I>& r) 252 : { 253 0 : os << r.numerator() << '/' << r.denominator(); 254 0 : return os; 255 : } 256 : 257 : // Type conversion 258 : template<typename T, typename I> 259 : T rational_cast(const rational<I>& r); 260 : 261 : } // namespace jami 262 : 263 : namespace std { 264 : template<> 265 : struct modulus<double> 266 : { 267 : double operator()(const double& lhs, const double& rhs) const { return std::fmod(lhs, rhs); } 268 : }; 269 : } // namespace std