LCOV - code coverage report
Current view: top level - foo/src - rational.h (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 34 45 75.6 %
Date: 2025-08-24 09:11:10 Functions: 24 49 49.0 %

          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        5609 :     constexpr rational() {}
      43             : 
      44             :     // Equal to n/1
      45           1 :     constexpr rational(I n)
      46           1 :         : num_(n) {}
      47             : 
      48             :     // General case (n/d)
      49         474 :     constexpr rational(I n, I d)
      50         474 :         : num_(n)
      51         474 :         , den_(d)
      52             :     {
      53         474 :         reduce();
      54         474 :     }
      55             : 
      56             :     // Define conversions to and from AVRational (equivalent)
      57         393 :     constexpr rational(AVRational r)
      58         393 :         : num_(r.num)
      59         393 :         , den_(r.den) {};
      60         376 :     constexpr operator AVRational() const { return AVRational {(int) num_, (int) den_}; }
      61             : 
      62           0 :     std::string to_string() const {
      63           0 :         return fmt::format("{}/{}", num_, den_);
      64             :     }
      65             : 
      66             :     // Normal copy constructors and assignment operators
      67             : 
      68             :     // Assignment from I
      69         335 :     rational& operator=(I n)
      70             :     {
      71         335 :         num_ = n;
      72         335 :         den_ = 1;
      73         335 :         return *this;
      74             :     }
      75             : 
      76             :     // Assign in place
      77             :     rational& assign(I n, I d)
      78             :     {
      79             :         num_ = n;
      80             :         den_ = d;
      81             :         reduce();
      82             :         return *this;
      83             :     }
      84             : 
      85             :     // Representation
      86         311 :     constexpr I numerator() const { return num_; };
      87         159 :     constexpr I denominator() const { return den_; };
      88             : 
      89             :     template<typename R = double>
      90         808 :     constexpr R real() const
      91             :     {
      92         808 :         return num_ / (R) den_;
      93             :     }
      94             : 
      95             :     // Arithmetic operators
      96             :     constexpr rational operator+(const rational& r) const
      97             :     {
      98             :         return {num_ * r.den_ + r.num_ * den_, den_ * r.den_};
      99             :     }
     100           0 :     constexpr rational operator-(const rational& r) const
     101             :     {
     102           0 :         return {num_ * r.den_ - r.num_ * den_, den_ * r.den_};
     103             :     }
     104           0 :     constexpr rational operator*(const rational& r) const { return {num_ * r.num_, den_ * r.den_}; }
     105             :     constexpr rational operator/(const rational& r) const { return {num_ * r.den_, den_ * r.num_}; }
     106             : 
     107             :     constexpr rational& operator+=(const rational& r)
     108             :     {
     109             :         std::swap(*this, *this + r);
     110             :         return *this;
     111             :     }
     112             :     constexpr rational& operator-=(const rational& r)
     113             :     {
     114             :         std::swap(*this, *this - r);
     115             :         return *this;
     116             :     }
     117             :     constexpr rational& operator*=(const rational& r)
     118             :     {
     119             :         num_ *= r.num_;
     120             :         den_ *= r.den_;
     121             :         reduce();
     122             :         return *this;
     123             :     }
     124             :     constexpr rational& operator/=(const rational& r)
     125             :     {
     126             :         num_ *= r.den_;
     127             :         den_ *= r.num_;
     128             :         reduce();
     129             :         return *this;
     130             :     }
     131             : 
     132             :     // Arithmetic with integers
     133             :     constexpr rational& operator+=(I i)
     134             :     {
     135             :         num_ += i * den_;
     136             :         return *this;
     137             :     }
     138             :     constexpr rational& operator-=(I i)
     139             :     {
     140             :         num_ -= i * den_;
     141             :         return *this;
     142             :     }
     143             :     constexpr rational& operator*=(I i)
     144             :     {
     145             :         num_ *= i;
     146             :         reduce();
     147             :         return *this;
     148             :     }
     149             :     constexpr rational& operator/=(I i)
     150             :     {
     151             :         den_ *= i;
     152             :         reduce();
     153             :         return *this;
     154             :     }
     155             : 
     156             :     // Increment and decrement
     157             :     constexpr const rational& operator++()
     158             :     {
     159             :         num_ += den_;
     160             :         return *this;
     161             :     }
     162             :     constexpr const rational& operator--()
     163             :     {
     164             :         num_ -= den_;
     165             :         return *this;
     166             :     }
     167             : 
     168             :     // Operator not
     169         159 :     constexpr bool operator!() const { return !num_; };
     170             : 
     171             :     // Boolean conversion
     172         407 :     explicit constexpr operator bool() const { return num_; }
     173             : 
     174             :     // Comparison operators
     175         297 :     constexpr bool operator<(const rational& r) const
     176             :     {
     177         297 :         bool inv = (den_ > 0) != (r.den_ > 0);
     178         297 :         return inv != (num_ * r.den_ < den_ * r.num_);
     179             :     }
     180             :     constexpr bool operator>(const rational& r) const
     181             :     {
     182             :         bool inv = (den_ > 0) != (r.den_ > 0);
     183             :         return inv != (num_ * r.den_ > den_ * r.num_);
     184             :     }
     185           0 :     constexpr bool operator==(const rational& r) const { return num_ * r.den_ == den_ * r.num_; }
     186             :     constexpr bool operator!=(const rational& r) const { return num_ * r.den_ != den_ * r.num_; }
     187             : 
     188             :     // Comparison with integers
     189          33 :     constexpr bool operator<(I i) const { return den_ < 0 ? (num_ > i * den_) : (num_ < i * den_); }
     190             :     constexpr bool operator>(I i) const { return den_ < 0 ? (num_ < i * den_) : (num_ > i * den_); }
     191             :     constexpr bool operator==(I i) const { return num_ == i * den_; }
     192             :     constexpr bool operator!=(I i) const { return num_ != i * den_; }
     193             : 
     194             : private:
     195             :     I num_ {0};
     196             :     I den_ {1};
     197             : 
     198         910 :     static constexpr I gcd(I a, I b) { return b == (I) 0 ? a : gcd(b, std::modulus<I>()(a, b)); }
     199         474 :     constexpr void reduce()
     200             :     {
     201             :         if constexpr (std::is_integral<I>::value) {
     202         431 :             if (num_ and den_) {
     203         322 :                 auto g = gcd(num_ >= 0 ? num_ : -num_, den_ >= 0 ? den_ : -den_);
     204         322 :                 if (g > (I) 1) {
     205           0 :                     num_ /= g;
     206           0 :                     den_ /= g;
     207             :                 }
     208             :             }
     209             :         }
     210         474 :     }
     211             : };
     212             : 
     213             : // Unary operators
     214             : template<typename I>
     215             : rational<I>
     216             : operator+(const rational<I>& r)
     217             : {
     218             :     return r;
     219             : }
     220             : template<typename I>
     221             : rational<I>
     222             : operator-(const rational<I>& r)
     223             : {
     224             :     return {-r.numerator(), r.denominator()};
     225             : }
     226             : 
     227             : // Reversed order operators for - and / between (types convertible to) I and rational
     228             : template<typename I, typename II>
     229             : inline rational<I> operator-(II i, const rational<I>& r);
     230             : template<typename I, typename II>
     231             : inline rational<I>
     232         106 : operator/(II i, const rational<I>& r)
     233             : {
     234         106 :     return {i * r.denominator(), r.numerator()};
     235             : }
     236             : 
     237             : // Absolute value
     238             : template<typename I>
     239             : rational<I>
     240             : abs(const rational<I>& r)
     241             : {
     242             :     return {std::abs(r.numerator()), std::abs(r.denominator())};
     243             : }
     244             : 
     245             : // Input and output
     246             : template<typename I>
     247             : std::istream&
     248             : operator>>(std::istream& is, rational<I>& r)
     249             : {
     250             :     char sep;
     251             :     is >> r.num_ >> sep >> r.den_;
     252             :     return is;
     253             : }
     254             : 
     255             : template<typename I>
     256             : std::ostream&
     257           0 : operator<<(std::ostream& os, const rational<I>& r)
     258             : {
     259           0 :     os << r.numerator() << '/' << r.denominator();
     260           0 :     return os;
     261             : }
     262             : 
     263             : // Type conversion
     264             : template<typename T, typename I>
     265             : T rational_cast(const rational<I>& r);
     266             : 
     267             : } // namespace jami
     268             : 
     269             : namespace std {
     270             : template<>
     271             : struct modulus<double>
     272             : {
     273             :     double operator()(const double& lhs, const double& rhs) const { return std::fmod(lhs, rhs); }
     274             : };
     275             : } // namespace std
     276             : 
     277             : template<typename I>
     278             : struct fmt::formatter<jami::rational<I>> : fmt::formatter<std::string_view> {
     279             :     template<typename FormatContext>
     280             :     auto format(const jami::rational<I>& r, FormatContext& ctx) const -> decltype(ctx.out()) {
     281             :         return fmt::formatter<std::string_view>::format(r.to_string(), ctx);
     282             :     }
     283             : };

Generated by: LCOV version 1.14