LCOV - code coverage report
Current view: top level - foo/src - rational.h (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 35 44 79.5 %
Date: 2025-12-18 10:07:43 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        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             : };

Generated by: LCOV version 1.14