LCOV - code coverage report
Current view: top level - snapdev - math.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 34 34 100.0 %
Date: 2022-07-09 19:51:09 Functions: 17 17 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2022  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/snapdev
       4             : // contact@m2osw.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 2 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 along
      17             : // with this program; if not, write to the Free Software Foundation, Inc.,
      18             : // 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      19             : #pragma once
      20             : 
      21             : /** \file
      22             :  * \brief Various mathematical functions.
      23             :  *
      24             :  * These functions are extensions to the math functions offered by the C
      25             :  * and the C++ libraries.
      26             :  */
      27             : 
      28             : // self
      29             : //
      30             : #include    <snapdev/int128_literal.h>
      31             : #include    <snapdev/limits_int128.h>
      32             : #include    <snapdev/not_reached.h>
      33             : 
      34             : 
      35             : // C
      36             : //
      37             : #include    <signal.h>
      38             : 
      39             : 
      40             : namespace snapdev
      41             : {
      42             : 
      43             : 
      44             : /** \brief Computer a power of an integer number.
      45             :  *
      46             :  * This function multiplies the specified \p value by itself \p power times.
      47             :  *
      48             :  * This is a function to compute an integer power so \p power must be an
      49             :  * integer and is generally expected to be positive. Since a negative power
      50             :  * is legal, we handle the case, but in most likelihood, the result will
      51             :  * be zero in this case.
      52             :  *
      53             :  * \note
      54             :  * If \p power is 0, the result is always 1.
      55             :  *
      56             :  * \note
      57             :  * If \p power is negative, the result is 0 unless \p value is 1 or -1 in
      58             :  * which case the result is 1 or -1.
      59             :  *
      60             :  * \todo
      61             :  * The function does not check for overflow.
      62             :  *
      63             :  * \todo
      64             :  * Once all versions use g++ v10 or more, we can remove the is_same_v<>
      65             :  * against __int128.
      66             :  *
      67             :  * \param[in] value  An integer.
      68             :  * \param[in] power  The number of times to multiply \p value by itself.
      69             :  *
      70             :  * \return The resulting "value ** power".
      71             :  */
      72             : #pragma GCC diagnostic push
      73             : #pragma GCC diagnostic ignored "-Wpedantic"
      74             : template<typename T>
      75             : constexpr std::enable_if_t<std::is_integral_v<T>
      76             :               || std::is_same_v<T, __int128>
      77        2196 :               || std::is_same_v<T, unsigned __int128>, T> pow(T value, int power)
      78             : {
      79             :     using namespace snapdev::literals;
      80             : 
      81        2196 :     if(power == 0)
      82             :     {
      83          22 :         return static_cast<T>(1);
      84             :     }
      85        2174 :     if(power < 0)
      86             :     {
      87             :         // negative powers are similar to (1 / value ** power) which is
      88             :         // 1 or -1 if value is 1 or -1
      89             :         //
      90        1920 :         switch(value)
      91             :         {
      92         128 :         case -1:
      93         128 :             return static_cast<T>((power & 1) == 0 ? 1 : -1);
      94             : 
      95         256 :         case 1:
      96         256 :             return static_cast<T>(1);
      97             : 
      98        1536 :         default:
      99        1536 :             return static_cast<T>(0);
     100             : 
     101             :         }
     102             :         snapdev::NOT_REACHED();
     103             :     }
     104             : 
     105         254 :     T result(static_cast<T>(1));
     106             :     for(;;)
     107             :     {
     108        2822 :         if((power & 1) != 0)
     109             :         {
     110         896 :             result *= value;
     111             :         }
     112        1538 :         power /= 2;
     113        1538 :         if(power == 0)
     114             :         {
     115         254 :             return result;
     116             :         }
     117        1284 :         value *= value;
     118             :     }
     119             :     snapdev::NOT_REACHED();
     120             : }
     121             : #pragma GCC diagnostic pop
     122             : 
     123             : 
     124             : /** \brief Add two signed numbers, return min or max on overflow.
     125             :  *
     126             :  * This function adds \p lhs to \p rhs and returns the total unless there
     127             :  * is an overflow in which case the minimum (if \p lhs and \p rhs are
     128             :  * negative) or the maximum (if \p lhs and \p rhs are positive) is returned.
     129             :  *
     130             :  * \note
     131             :  * This function is picked only if T is signed.
     132             :  *
     133             :  * \note
     134             :  * T can be `signed __int128`.
     135             :  *
     136             :  * \tparam T  The type of integer concerned. \p lhs and \p rhs must be of the
     137             :  * same type.
     138             :  * \param[in] lhs  The left handside number to add.
     139             :  * \param[in] rhs  The right handside number to add.
     140             :  *
     141             :  * \return Saturated lhs + rhs.
     142             :  */
     143             : #pragma GCC diagnostic push
     144             : #pragma GCC diagnostic ignored "-Wpedantic"
     145             : template<typename T>
     146             : std::enable_if_t<(std::is_integral_v<T> && std::is_signed_v<T>)
     147     1821407 :               || std::is_same_v<T, __int128>, T> saturated_add(T lhs, T rhs)
     148             : {
     149     1821407 :     T const result(lhs + rhs);
     150     1821407 :     T const sign(lhs ^ rhs);
     151     1821407 :     if(sign < 0)
     152             :     {
     153             :         // no possible overflow (lhs & rhs are opposite signs)
     154             :         //
     155      910965 :         return result;
     156             :     }
     157      910442 :     T const result_sign(result ^ lhs);
     158      910442 :     if(result_sign >= 0)
     159             :     {
     160             :         // overflow did not happen (result is still the same sign)
     161             :         //
     162      446404 :         return result;
     163             :     }
     164             :     return lhs < 0
     165      464038 :             ? std::numeric_limits<T>::min()
     166      464038 :             : std::numeric_limits<T>::max();
     167             : }
     168             : #pragma GCC diagnostic pop
     169             : 
     170             : 
     171             : /** \brief Add two unsigned numbers, return max on overflow.
     172             :  *
     173             :  * This function adds \p lhs to \p rhs and returns the total unless there
     174             :  * is an overflow in which case the maximum is returned.
     175             :  *
     176             :  * \note
     177             :  * This function is picked only if T is unsigned.
     178             :  *
     179             :  * \note
     180             :  * T can be `unsigned __int128`. This is currently checked explicitly
     181             :  * because older C++ standard libraries do not view __int128 as an integral
     182             :  * type (it is not included in the list of types checked).
     183             :  *
     184             :  * \tparam T  The type of integer concerned. \p lhs and \p rhs must be of the
     185             :  * same type.
     186             :  * \param[in] lhs  The left handside number to add.
     187             :  * \param[in] rhs  The right handside number to add.
     188             :  *
     189             :  * \return Saturated lhs + rhs.
     190             :  */
     191             : #pragma GCC diagnostic push
     192             : #pragma GCC diagnostic ignored "-Wpedantic"
     193             : template<typename T>
     194             : std::enable_if_t<(std::is_integral_v<T> && std::is_unsigned_v<T>)
     195     1832837 :                || std::is_same_v<T, unsigned __int128>, T> saturated_add(T lhs, T rhs)
     196             : {
     197     1832837 :     return lhs > std::numeric_limits<T>::max() - rhs
     198          15 :             ? std::numeric_limits<T>::max()
     199     1832837 :             : lhs + rhs;
     200             : }
     201             : #pragma GCC diagnostic pop
     202             : 
     203             : 
     204             : /** \brief Subtract an unsigned number from another, return min on overflow.
     205             :  *
     206             :  * This function subtract \p rhs from \p lhs and returns the difference
     207             :  * unless there is an underflow in which case the minimum (0) is returned.
     208             :  *
     209             :  * \note
     210             :  * This function is picked only if T is unsigned.
     211             :  *
     212             :  * \note
     213             :  * T can be `unsigned __int128`. This is currently checked explicitly
     214             :  * because older C++ standard libraries do not view __int128 as an integral
     215             :  * type (it is not included in the list of types checked).
     216             :  *
     217             :  * \tparam T  The type of integer concerned. \p lhs and \p rhs must be of the
     218             :  * same type.
     219             :  * \param[in] lhs  The left handside number to subtract from.
     220             :  * \param[in] rhs  The right handside number to subtract.
     221             :  *
     222             :  * \return Saturated lhs - rhs.
     223             :  */
     224             : #pragma GCC diagnostic push
     225             : #pragma GCC diagnostic ignored "-Wpedantic"
     226             : template<typename T>
     227             : std::enable_if_t<(std::is_integral_v<T> && std::is_unsigned_v<T>)
     228     1801710 :                || std::is_same_v<T, unsigned __int128>, T> saturated_subtract(T lhs, T rhs)
     229             : {
     230     1801710 :     return lhs < rhs ? 0 : lhs - rhs;
     231             : }
     232             : #pragma GCC diagnostic pop
     233             : 
     234             : 
     235             : } // namespace snapdev
     236             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13