include/maths/common/CIntegerTools.h (88 lines of code) (raw):

/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the following additional limitation. Functionality enabled by the * files subject to the Elastic License 2.0 may only be used in production when * invoked by an Elasticsearch process with a license key installed that permits * use of machine learning features. You may not use this file except in * compliance with the Elastic License 2.0 and the foregoing additional * limitation. */ #ifndef INCLUDED_ml_maths_common_CIntegerTools_h #define INCLUDED_ml_maths_common_CIntegerTools_h #include <maths/common/ImportExport.h> #include <cstddef> #include <cstdint> #include <vector> namespace ml { namespace maths { namespace common { //! \brief A collection of utility functions for operations we do on integers. //! //! DESCRIPTION:\n //! This implements common integer operations: checking alignment, rounding //! and so on. Also any integer operations we sometimes need that can be done //! cheaply with some "bit twiddling hack". class MATHS_COMMON_EXPORT CIntegerTools { public: //! Checks whether a double holds an an integer. static bool isInteger(double value, double tolerance = 0.0); //! Get the next larger power of 2, i.e. //! <pre class="fragment"> //! \f$p = \left \lceil \log_2(x) \right \rceil\f$ //! </pre> static std::size_t nextPow2(std::uint64_t x); //! Computes the integer with the reverse of the bits of the binary //! representation of \p x. static std::uint64_t reverseBits(std::uint64_t x); //! Check if \p value is \p alignment aligned. template<typename INT_TYPE> static inline bool aligned(INT_TYPE value, INT_TYPE alignment) { return alignment == static_cast<INT_TYPE>(0) || (value % alignment) == static_cast<INT_TYPE>(0); } //! Align \p value to \p alignment rounding up. //! //! \param[in] value The value to align to a multiple of \p alignment. //! \param[in] alignment The alignment. //! \note It is assumed that \p value and \p alignment are integral types. template<typename INT_TYPE> static inline INT_TYPE ceil(INT_TYPE value, INT_TYPE alignment) { INT_TYPE result{floor(value, alignment)}; if (result != value) { result += alignment; } return result; } //! Align \p value to \p alignment rounding down. //! //! \param[in] value The value to align to a multiple of \p alignment. //! \param[in] alignment The alignment. //! \note It is assumed that \p value and \p alignment are integral types. template<typename INT_TYPE> static inline INT_TYPE floor(INT_TYPE value, INT_TYPE alignment) { if (alignment == 0) { return value; } INT_TYPE result{(value / alignment) * alignment}; return result == value ? result : (value < 0 ? result - alignment : result); } //! Get the largest value smaller than \p value which is an integer //! multiple of \p alignment. //! //! \param[in] value The value for which to compute the infimum. //! \param[in] alignment The alignment. //! \note It is assumed that \p value and \p alignment are integral types. template<typename INT_TYPE> static inline INT_TYPE strictInfimum(INT_TYPE value, INT_TYPE alignment) { INT_TYPE result{floor(value, alignment)}; // Since this is a strict lower bound we need to trap the case the // value is an exact multiple of the alignment. if (result == value) { result -= alignment; } return result; } //! Compute the greatest common divisor of \p a and \p b. //! //! Implements Euclid's algorithm for finding the greatest common divisor //! of two integers. //! //! \note The tail recursion will be optimized away. template<typename INT_TYPE> static INT_TYPE gcd(INT_TYPE a, INT_TYPE b) { if (a < b) { std::swap(a, b); } return b == 0 ? a : gcd(b, a % b); } //! Compute the greatest common divisor of all integers in \p c. template<typename INT_TYPE> static INT_TYPE gcd(std::vector<INT_TYPE> c) { if (c.empty()) { return 0; } if (c.size() == 1) { return c[0]; } // Repeatedly apply Euclid's algorithm and use the fact that // gcd(a, b, c) = gcd(gcd(a, b), c). INT_TYPE result{gcd(c[0], c[1])}; for (std::size_t i = 2; i < c.size(); ++i) { result = gcd(result, c[i]); } return result; } //! Compute the least common multiple of \p a and \p b. template<typename INT_TYPE> static INT_TYPE lcm(INT_TYPE a, INT_TYPE b) { // This also handles the case that a == b == 0. return a == b ? a : a * b / gcd(a, b); } //! Compute the least common multiple of all integers in \p c. template<typename INT_TYPE> static INT_TYPE lcm(std::vector<INT_TYPE> c) { if (c.empty()) { return 0; } if (c.size() == 1) { return c[0]; } INT_TYPE result{lcm(c[0], c[1])}; for (std::size_t i = 2; i < c.size(); ++i) { result = lcm(result, c[i]); } return result; } //! Compute the binomial coefficient \f$\frac{n!}{k!(n-k)!}\f$. static double binomial(unsigned int n, unsigned int k); }; } } } #endif // INCLUDED_ml_maths_common_CIntegerTools_h