LCOV - code coverage report
Current view: top level - src - rational.h (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 79.5 % 44 35
Test Date: 2026-06-13 09:18:46 Functions: 51.0 % 49 25

            Line data    Source code
       1              : /*
       2              :  *  Copyright (C) 2004-2026 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              : 
      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         5022 :     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          426 :     constexpr rational(I n, I d)
      51          426 :         : num_(n)
      52          426 :         , den_(d)
      53              :     {
      54          426 :         reduce();
      55          426 :     }
      56              : 
      57              :     // Define conversions to and from AVRational (equivalent)
      58          413 :     constexpr rational(AVRational r)
      59          413 :         : num_(r.num)
      60          413 :         , den_(r.den) {};
      61          341 :     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          361 :     rational& operator=(I n)
      69              :     {
      70          361 :         num_ = n;
      71          361 :         den_ = 1;
      72          361 :         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          287 :     constexpr I numerator() const { return num_; };
      86          147 :     constexpr I denominator() const { return den_; };
      87              : 
      88              :     template<typename R = double>
      89         1053 :     constexpr R real() const
      90              :     {
      91         1053 :         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          143 :     constexpr bool operator!() const { return !num_; };
     163              : 
     164              :     // Boolean conversion
     165          374 :     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          818 :     static constexpr I gcd(I a, I b) { return b == (I) 0 ? a : gcd(b, std::modulus<I>()(a, b)); }
     192          426 :     constexpr void reduce()
     193              :     {
     194              :         if constexpr (std::is_integral<I>::value) {
     195          389 :             if (num_ and den_) {
     196          290 :                 auto g = gcd(num_ >= 0 ? num_ : -num_, den_ >= 0 ? den_ : -den_);
     197          290 :                 if (g > (I) 1) {
     198            0 :                     num_ /= g;
     199            0 :                     den_ /= g;
     200              :                 }
     201              :             }
     202              :         }
     203          426 :     }
     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           94 : operator/(II i, const rational<I>& r)
     226              : {
     227           94 :     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              : };
        

Generated by: LCOV version 2.0-1