jaulib v1.4.0-2-g788cf73
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
int_math_ct.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020-2024 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#ifndef JAU_INT_MATH_CT_HPP_
26#define JAU_INT_MATH_CT_HPP_
27
28#include <cstdint>
29#include <climits>
30#include <concepts>
31
32#include <jau/cpp_lang_util.hpp>
33#include <jau/int_types.hpp>
34#include <jau/cpp_pragma.hpp>
35#include <jau/type_concepts.hpp>
36
37namespace jau {
38
39 /** @defgroup ConstantTime Constant Time (CT) Integral Operations
40 * Integral integer operations in constant time (CT), see also and meta-group \ref Math
41 *
42 * @{
43 */
44
45 /**
46 // *************************************************
47 // *************************************************
48 // *************************************************
49 */
50
51 // Remember: constexpr specifier used in a function or static data member (since C++17) declaration implies inline.
52
53 /**
54 * Returns the value of the sign function (w/o branching) in O(1) and constant time (CT)
55 * <pre>
56 * -1 for x < 0
57 * 0 for x = 0
58 * 1 for x > 0
59 * </pre>
60 * Implementation is type safe.
61 *
62 * Branching may occur due to relational operator.
63 *
64 * @tparam T an integral number type
65 * @param x the number
66 * @return function result
67 */
68 template <std::integral T>
69 constexpr int ct_sign(const T x) noexcept {
70 return (x != 0) | -(int)((std::make_unsigned_t<T>)((T)x) >> (sizeof(T) * CHAR_BIT - 1));
71 // return (int) ( (T(0) < x) - (x < T(0)) );
72 }
73
74 /**
75 * Returns the absolute value of an arithmetic number (w/o branching) in O(1) and constant time (CT),
76 * while ct_abs(INT_MIN) is undefined behavior (UB) instead of being mapped correctly to INT_MAX like jau::abs() does, see above.
77 *
78 * This implementation is equivalent to std::abs(), i.e. unsafe
79 *
80 * - signed integral uses 2-complement branch-less conversion, [bithacks Integer-Abs](http://www.graphics.stanford.edu/~seander/bithacks.html#IntegerAbs)
81 * - signed floating-point uses x * sign(x)
82 * - unsigned just returns the value
83 *
84 * This implementation uses 2-complement branch-less conversion, [bithacks Integer-Abs](http://www.graphics.stanford.edu/~seander/bithacks.html#IntegerAbs)
85 *
86 * Note: On an x86_64 architecture abs() w/ branching is of equal speed or even faster.
87 *
88 * @tparam T an arithmetic number type
89 * @param x the number
90 * @return function result
91 */
92 template <jau::req::signed_integral T>
93 constexpr T ct_abs(const T x) noexcept {
94 using unsigned_T = std::make_unsigned_t<T>;
95 const T mask = x >> ( sizeof(T) * CHAR_BIT - 1 );
98 return static_cast<unsigned_T>( ( x + mask ) ^ mask ); // clang 15: int overflow on UB (constrained in API doc)
100 }
101#if 0
102 // the type req is floating_point (signed), but we have no ct_sign for float.
103 template <typename T,
104 std::enable_if_t< std::is_arithmetic_v<T> &&
105 !std::is_integral_v<T> &&
106 !std::is_unsigned_v<T>, bool> = true>
107 constexpr T ct_abs(const T x) noexcept
108 {
109 return x * jau::ct_sign<T>(x);
110 }
111#endif
112
113 template <jau::req::unsigned_arithmetic T>
114 constexpr T ct_abs(const T x) noexcept {
115 return x;
116 }
117
118 /**
119 * Returns the minimum of two integrals for `MIN <= x - y <= MAX` (w/o branching) in O(1) and constant time (CT).
120 *
121 * Source: [bithacks Test IntegerMinOrMax](http://www.graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax)
122 *
123 * Note: On an x86_64 architecture min() w/ branching is of equal speed or even faster.
124 *
125 * @tparam T an integral number type
126 * @param x one number
127 * @param x the other number
128 */
129 template <std::integral T>
130 constexpr T ct_min(const T x, const T y) noexcept {
131 return y + ( (x - y) & ( (x - y) >> ( sizeof(T) * CHAR_BIT - 1 ) ) );
132 }
133
134 /**
135 * Returns the maximum of two integrals for `MIN <= x - y <= MAX` (w/o branching) in O(1) and constant time (CT).
136 *
137 * Source: [bithacks Test IntegerMinOrMax](http://www.graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax)
138 *
139 * Note: On an x86_64 architecture max() w/ branching is of equal speed or even faster.
140 *
141 * @tparam T an integral number type
142 * @param x one number
143 * @param x the other number
144 */
145 template <std::integral T>
146 constexpr T ct_max(const T x, const T y) noexcept {
147 return x - ( (x - y) & ( (x - y) >> ( sizeof(T) * CHAR_BIT - 1 ) ) );
148 }
149
150 /**
151 * Returns constrained integral value to lie between given min- and maximum value for `MIN <= x - y <= MAX`
152 * (w/o branching) in O(1) and constant time (CT).
153 *
154 * Implementation returns `ct_min(ct_max(x, min_val), max_val)`, analog to GLSL's clamp()
155 *
156 * Note: On an x86_64 architecture clamp() w/ branching is of equal speed or even faster.
157 *
158 * @tparam T an integral number type
159 * @param x one number
160 * @param min_val the minimum limes, inclusive
161 * @param max_val the maximum limes, inclusive
162 */
163 template <std::integral T>
164 constexpr T ct_clamp(const T x, const T min_val, const T max_val) noexcept {
165 return jau::ct_min<T>(jau::ct_max<T>(x, min_val), max_val);
166 }
167
168 /**
169 * Returns merged `a_if_masked` bits selected by `mask` `1` bits and `b_if_unmasked` bits selected by `mask` `0` bits
170 * (w/o branching) in O(1) and constant time (CT).
171 *
172 * Source: [bithacks MaskedMerge](http://www.graphics.stanford.edu/~seander/bithacks.html#MaskedMerge)
173 *
174 * @tparam T an unsigned integral number type
175 * @param mask 1 where bits from `a_if_masked` should be selected; 0 where from `b_if_unmasked`.
176 * @param a_if_masked value to merge in masked bits
177 * @param b_if_unmasked value to merge in non-masked bits
178 */
179 template <jau::req::unsigned_integral T>
180 constexpr T ct_masked_merge(T mask, T a_if_masked, T b_if_unmasked) noexcept {
181 return b_if_unmasked ^ ( mask & ( a_if_masked ^ b_if_unmasked ) );
182 }
183
184 /**
185 * Returns the next higher power of 2 of given unsigned 32-bit {@code n}
186 * (w/o branching) in O(1) and constant time (CT).
187 *
188 * Uses either
189 * - C++20: std::has_single_bit and std::bit_ceil
190 * - else: [bithacks RoundUpPowerOf2](http://www.graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2)
191 *
192 * @see std::has_single_bit()
193 * @see std::bit_ceil()
194 */
195 constexpr uint32_t ct_next_power_of_2(uint32_t n) noexcept {
196 if constexpr ( is_cxx20() ) {
197 return std::has_single_bit(n) ? std::bit_ceil(n+1) : std::bit_ceil(n);
198 } else {
199 --n;
200 n |= n >> 1;
201 n |= n >> 2;
202 n |= n >> 4;
203 n |= n >> 8;
204 n |= n >> 16;
205 ++n;
206 n += ( 0 == n ); // avoid 0 -> 0
207 return n;
208 }
209 }
210 constexpr uint64_t ct_next_power_of_2(uint64_t n) noexcept {
211 if constexpr ( is_cxx20() ) {
212 return std::has_single_bit(n) ? std::bit_ceil(n+1) : std::bit_ceil(n);
213 } else {
214 --n;
215 n |= n >> 1;
216 n |= n >> 2;
217 n |= n >> 4;
218 n |= n >> 8;
219 n |= n >> 16;
220 n |= n >> 32;
221 ++n;
222 n += ( 0 == n ); // avoid 0 -> 0
223 return n;
224 }
225 }
226
227 /**
228 * Returns the number of set bits within given 32bit integer
229 * (w/o branching) in O(1) and constant time (CT).
230 *
231 * Uses a <i>HAKEM 169 Bit Count</i> inspired implementation:
232 * <pre>
233 * http://www.inwap.com/pdp10/hbaker/hakmem/hakmem.html
234 * http://home.pipeline.com/~hbaker1/hakmem/hacks.html#item169
235 * http://tekpool.wordpress.com/category/bit-count/
236 * https://github.com/aistrate/HackersDelight/blob/master/Original/HDcode/pop.c.txt
237 * https://github.com/aistrate/HackersDelight/blob/master/Original/HDcode/newCode/popDiff.c.txt
238 * </pre>
239 */
240 constexpr uint32_t ct_bit_count(uint32_t n) noexcept {
241 // Note: Original used 'unsigned int',
242 // hence we use the unsigned right-shift '>>>'
243 /**
244 * Original using 'unsigned' right-shift and modulo
245 *
246 const uint32_t c = n
247 - ( (n >> 1) & 033333333333 )
248 - ( (n >> 2) & 011111111111 );
249 return ( ( c + ( c >> 3 ) ) & 030707070707 ) % 63;
250 *
251 */
252 // Hackers Delight, Figure 5-2, pop1 of pop.c.txt (or popDiff.c.txt in git repo)
253 n = n - ((n >> 1) & 0x55555555);
254 n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
255 n = (n + (n >> 4)) & 0x0f0f0f0f;
256 n = n + (n >> 8);
257 n = n + (n >> 16);
258 return n & 0x3f;
259 }
260
261 /**
262 * Returns ~0 (2-complement) if top bit of arg is set, otherwise 0
263 * (w/o branching) in O(1) and constant time (CT).
264 *
265 * @tparam T an unsigned integral number type
266 */
267 template <jau::req::unsigned_integral T>
268 constexpr T ct_expand_top_bit(T x)
269 {
270 return T(0) - ( x >> ( sizeof(T) * CHAR_BIT - 1 ) );
271 }
272
273 /**
274 * Returns ~0 (2-complement) if arg is zero, otherwise 0
275 * (w/o branching) in O(1) and constant time (CT).
276 *
277 * @tparam T an unsigned integral number type
278 */
279 template <jau::req::unsigned_integral T>
280 constexpr T ct_is_zero(T x)
281 {
282 return jau::ct_expand_top_bit<T>( ~x & (x - 1) );
283 }
284
285 /**@}*/
286
287} // namespace jau
288
289#endif /* JAU_INT_MATH_CT_HPP_ */
constexpr T ct_expand_top_bit(T x)
Returns ~0 (2-complement) if top bit of arg is set, otherwise 0 (w/o branching) in O(1) and constant ...
constexpr uint32_t ct_bit_count(uint32_t n) noexcept
Returns the number of set bits within given 32bit integer (w/o branching) in O(1) and constant time (...
constexpr T ct_masked_merge(T mask, T a_if_masked, T b_if_unmasked) noexcept
Returns merged a_if_masked bits selected by mask 1 bits and b_if_unmasked bits selected by mask 0 bit...
constexpr T ct_max(const T x, const T y) noexcept
Returns the maximum of two integrals for MIN <= x - y <= MAX (w/o branching) in O(1) and constant tim...
constexpr T ct_min(const T x, const T y) noexcept
Returns the minimum of two integrals for MIN <= x - y <= MAX (w/o branching) in O(1) and constant tim...
constexpr uint32_t ct_next_power_of_2(uint32_t n) noexcept
Returns the next higher power of 2 of given unsigned 32-bit n (w/o branching) in O(1) and constant ti...
constexpr T ct_is_zero(T x)
Returns ~0 (2-complement) if arg is zero, otherwise 0 (w/o branching) in O(1) and constant time (CT).
constexpr int ct_sign(const T x) noexcept
Returns the value of the sign function (w/o branching) in O(1) and constant time (CT)
constexpr T ct_clamp(const T x, const T min_val, const T max_val) noexcept
Returns constrained integral value to lie between given min- and maximum value for MIN <= x - y <= MA...
constexpr T ct_abs(const T x) noexcept
Returns the absolute value of an arithmetic number (w/o branching) in O(1) and constant time (CT),...
consteval_cxx20 bool is_cxx20() noexcept
Returns true if compiled with >= C++20.
#define PRAGMA_DISABLE_WARNING_PUSH
#define PRAGMA_DISABLE_WARNING_INT_OVERFLOW
#define PRAGMA_DISABLE_WARNING_POP
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition backtrace.hpp:32