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
|