aboutsummaryrefslogtreecommitdiffstats
path: root/include/jau/int_math_ct.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/jau/int_math_ct.hpp')
-rw-r--r--include/jau/int_math_ct.hpp271
1 files changed, 271 insertions, 0 deletions
diff --git a/include/jau/int_math_ct.hpp b/include/jau/int_math_ct.hpp
new file mode 100644
index 0000000..6d23744
--- /dev/null
+++ b/include/jau/int_math_ct.hpp
@@ -0,0 +1,271 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020-2024 Gothel Software e.K.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef JAU_INT_MATH_CT_HPP_
+#define JAU_INT_MATH_CT_HPP_
+
+#include <cstdint>
+#include <cmath>
+#include <climits>
+
+#include <jau/int_types.hpp>
+
+namespace jau {
+
+ /** \addtogroup Constant Time (CT) Integral Operations
+ *
+ * @{
+ */
+
+ /**
+ // *************************************************
+ // *************************************************
+ // *************************************************
+ */
+
+ // Remember: constexpr specifier used in a function or static data member (since C++17) declaration implies inline.
+
+ /**
+ * Returns the value of the sign function (w/o branching) in O(1) and constant time (CT)
+ * <pre>
+ * -1 for x < 0
+ * 0 for x = 0
+ * 1 for x > 0
+ * </pre>
+ * Implementation is type safe.
+ *
+ * Branching may occur due to relational operator.
+ *
+ * @tparam T an integral number type
+ * @param x the number
+ * @return function result
+ */
+ template <typename T,
+ std::enable_if_t< std::is_integral_v<T>, bool> = true>
+ constexpr int ct_sign(const T x) noexcept
+ {
+ return (x != 0) | -(int)((std::make_unsigned_t<T>)((T)x) >> (sizeof(T) * CHAR_BIT - 1));
+ // return (int) ( (T(0) < x) - (x < T(0)) );
+ }
+
+ /**
+ * Returns the absolute value of an arithmetic number (w/o branching) in O(1) and constant time (CT),
+ * while not covering INT_MIN -> INT_MAX conversion as abs(), see above.
+ *
+ * This implementation is equivalent to std::abs(), i.e. unsafe
+ *
+ * - signed integral uses 2-complement branch-less conversion, [bithacks Integer-Abs](http://www.graphics.stanford.edu/~seander/bithacks.html#IntegerAbs)
+ * - signed floating-point uses x * sign(x)
+ * - unsigned just returns the value
+ *
+ * This implementation uses 2-complement branch-less conversion, [bithacks Integer-Abs](http://www.graphics.stanford.edu/~seander/bithacks.html#IntegerAbs)
+ *
+ * Note: On an x86_64 architecture abs() w/ branching is of equal speed or even faster.
+ *
+ * @tparam T an arithmetic number type
+ * @param x the number
+ * @return function result
+ */
+ template <typename T,
+ std::enable_if_t< std::is_arithmetic_v<T> &&
+ std::is_integral_v<T> &&
+ !std::is_unsigned_v<T>, bool> = true>
+ constexpr T ct_abs(const T x) noexcept
+ {
+ using unsigned_T = std::make_unsigned_t<T>;
+ const T mask = x >> ( sizeof(T) * CHAR_BIT - 1 );
+ const unsigned_T r = static_cast<unsigned_T>( ( x + mask ) ^ mask );
+ return r;
+ }
+ template <typename T,
+ std::enable_if_t< std::is_arithmetic_v<T> &&
+ !std::is_integral_v<T> &&
+ !std::is_unsigned_v<T>, bool> = true>
+ constexpr T ct_abs(const T x) noexcept
+ {
+ return x * jau::ct_sign<T>(x);
+ }
+ template <typename T,
+ std::enable_if_t< std::is_arithmetic_v<T> &&
+ std::is_unsigned_v<T>, bool> = true>
+ constexpr T ct_abs(const T x) noexcept
+ {
+ return x;
+ }
+
+ /**
+ * Returns the minimum of two integrals for `MIN <= x - y <= MAX` (w/o branching) in O(1) and constant time (CT).
+ *
+ * Source: [bithacks Test IntegerMinOrMax](http://www.graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax)
+ *
+ * Note: On an x86_64 architecture min() w/ branching is of equal speed or even faster.
+ *
+ * @tparam T an integral number type
+ * @param x one number
+ * @param x the other number
+ */
+ template <typename T,
+ std::enable_if_t< std::is_integral_v<T>, bool> = true>
+ constexpr T ct_min(const T x, const T y) noexcept
+ {
+ return y + ( (x - y) & ( (x - y) >> ( sizeof(T) * CHAR_BIT - 1 ) ) );
+ }
+
+ /**
+ * Returns the maximum of two integrals for `MIN <= x - y <= MAX` (w/o branching) in O(1) and constant time (CT).
+ *
+ * Source: [bithacks Test IntegerMinOrMax](http://www.graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax)
+ *
+ * Note: On an x86_64 architecture max() w/ branching is of equal speed or even faster.
+ *
+ * @tparam T an integral number type
+ * @param x one number
+ * @param x the other number
+ */
+ template <typename T,
+ std::enable_if_t< std::is_integral_v<T>, bool> = true>
+ constexpr T ct_max(const T x, const T y) noexcept
+ {
+ return x - ( (x - y) & ( (x - y) >> ( sizeof(T) * CHAR_BIT - 1 ) ) );
+ }
+
+ /**
+ * Returns constrained integral value to lie between given min- and maximum value for `MIN <= x - y <= MAX`
+ * (w/o branching) in O(1) and constant time (CT).
+ *
+ * Implementation returns `ct_min(ct_max(x, min_val), max_val)`, analog to GLSL's clamp()
+ *
+ * Note: On an x86_64 architecture clamp() w/ branching is of equal speed or even faster.
+ *
+ * @tparam T an integral number type
+ * @param x one number
+ * @param min_val the minimum limes, inclusive
+ * @param max_val the maximum limes, inclusive
+ */
+ template <typename T,
+ std::enable_if_t< std::is_integral_v<T>, bool> = true>
+ constexpr T ct_clamp(const T x, const T min_val, const T max_val) noexcept
+ {
+ return jau::ct_min<T>(jau::ct_max<T>(x, min_val), max_val);
+ }
+
+ /**
+ * Returns merged `a_if_masked` bits selected by `mask` `1` bits and `b_if_unmasked` bits selected by `mask` `0` bits
+ * (w/o branching) in O(1) and constant time (CT).
+ *
+ * Source: [bithacks MaskedMerge](http://www.graphics.stanford.edu/~seander/bithacks.html#MaskedMerge)
+ *
+ * @tparam T an unsigned integral number type
+ * @param mask 1 where bits from `a_if_masked` should be selected; 0 where from `b_if_unmasked`.
+ * @param a_if_masked value to merge in masked bits
+ * @param b_if_unmasked value to merge in non-masked bits
+ */
+ template <typename T,
+ std::enable_if_t< std::is_integral_v<T> && std::is_unsigned_v<T>, bool> = true>
+ constexpr T ct_masked_merge(T mask, T a_if_masked, T b_if_unmasked) {
+ return b_if_unmasked ^ ( mask & ( a_if_masked ^ b_if_unmasked ) );
+ }
+
+ /**
+ * Returns the next higher power of 2 of given unsigned 32-bit {@code n}
+ * (w/o branching) in O(1) and constant time (CT).
+ * <p>
+ * Source: [bithacks RoundUpPowerOf2](http://www.graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2)
+ * </p>
+ */
+ constexpr uint32_t ct_next_power_of_2(uint32_t n) {
+ n--;
+ n |= n >> 1;
+ n |= n >> 2;
+ n |= n >> 4;
+ n |= n >> 8;
+ n |= n >> 16;
+ return n + 1;
+ }
+
+ /**
+ * Returns the number of set bits within given 32bit integer
+ * (w/o branching) in O(1) and constant time (CT).
+ *
+ * Uses a <i>HAKEM 169 Bit Count</i> inspired implementation:
+ * <pre>
+ * http://www.inwap.com/pdp10/hbaker/hakmem/hakmem.html
+ * http://home.pipeline.com/~hbaker1/hakmem/hacks.html#item169
+ * http://tekpool.wordpress.com/category/bit-count/
+ * https://github.com/aistrate/HackersDelight/blob/master/Original/HDcode/pop.c.txt
+ * https://github.com/aistrate/HackersDelight/blob/master/Original/HDcode/newCode/popDiff.c.txt
+ * </pre>
+ */
+ constexpr uint32_t ct_bit_count(uint32_t n) noexcept {
+ // Note: Original used 'unsigned int',
+ // hence we use the unsigned right-shift '>>>'
+ /**
+ * Original using 'unsigned' right-shift and modulo
+ *
+ const uint32_t c = n
+ - ( (n >> 1) & 033333333333 )
+ - ( (n >> 2) & 011111111111 );
+ return ( ( c + ( c >> 3 ) ) & 030707070707 ) % 63;
+ *
+ */
+ // Hackers Delight, Figure 5-2, pop1 of pop.c.txt (or popDiff.c.txt in git repo)
+ n = n - ((n >> 1) & 0x55555555);
+ n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
+ n = (n + (n >> 4)) & 0x0f0f0f0f;
+ n = n + (n >> 8);
+ n = n + (n >> 16);
+ return n & 0x3f;
+ }
+
+ /**
+ * Returns ~0 (2-complement) if top bit of arg is set, otherwise 0
+ * (w/o branching) in O(1) and constant time (CT).
+ *
+ * @tparam T an unsigned integral number type
+ */
+ template <typename T,
+ std::enable_if_t< std::is_integral_v<T> && std::is_unsigned_v<T>, bool> = true>
+ inline constexpr T ct_expand_top_bit(T x)
+ {
+ return T(0) - ( x >> ( sizeof(T) * CHAR_BIT - 1 ) );
+ }
+
+ /**
+ * Returns ~0 (2-complement) if arg is zero, otherwise 0
+ * (w/o branching) in O(1) and constant time (CT).
+ *
+ * @tparam T an unsigned integral number type
+ */
+ template <typename T,
+ std::enable_if_t< std::is_integral_v<T> && std::is_unsigned_v<T>, bool> = true>
+ inline constexpr T ct_is_zero(T x)
+ {
+ return jau::ct_expand_top_bit<T>( ~x & (x - 1) );
+ }
+
+ /**@}*/
+
+} // namespace jau
+
+#endif /* JAU_INT_MATH_CT_HPP_ */