diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt new file mode 100644 index 0000000..909bfea --- /dev/null +++ b/cpp/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.8) + +project(Platform.Numbers) +include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) +conan_basic_setup(TARGETS) + +add_library(${PROJECT_NAME}.Library INTERFACE) +target_include_directories(${PROJECT_NAME}.Library INTERFACE ${PROJECT_NAME}) + +if((DEFINED LINKS_PLATFORM_TESTS) AND (${LINKS_PLATFORM_TESTS})) + add_executable(${PROJECT_NAME}.Tests ${PROJECT_NAME}.Tests/AllTests.cpp) + set_target_properties(${PROJECT_NAME}.Tests PROPERTIES CXX_STANDARD 20) + target_link_libraries(${PROJECT_NAME}.Tests PUBLIC CONAN_PKG::gtest) + target_link_libraries(${PROJECT_NAME}.Tests PUBLIC ${PROJECT_NAME}.Library) +endif() \ No newline at end of file diff --git a/cpp/Platform.Numbers.Tests/AllTests.cpp b/cpp/Platform.Numbers.Tests/AllTests.cpp new file mode 100644 index 0000000..2c32edb --- /dev/null +++ b/cpp/Platform.Numbers.Tests/AllTests.cpp @@ -0,0 +1 @@ +#include diff --git a/cpp/Platform.Numbers/Bit.h b/cpp/Platform.Numbers/Bit.h index 8b489b3..eb710f8 100644 --- a/cpp/Platform.Numbers/Bit.h +++ b/cpp/Platform.Numbers/Bit.h @@ -1,45 +1,32 @@ -namespace Platform::Numbers +namespace Platform::Numbers::Bit { - class Bit - { - public: static std::int64_t Count(std::int64_t x) - { - std::int64_t n = 0; - while (x != 0) - { - n++; - x &= x - 1; - } - return n; - } - - public: static std::int32_t GetLowestPosition(std::uint64_t value) - { - if (value == 0) - { - return -1; - } - auto position = 0; - while ((value & 1UL) == 0) - { - value >>= 1; - ++position; - } - return position; - } - - public: template static T Not(T x) { return Bit.Not(x); } - - public: template static T Or(T x, T y) { return Bit.Or(x, y); } - - public: template static T And(T x, T y) { return Bit.And(x, y); } + constexpr auto Count(std::unsigned_integral auto x) noexcept { return std::popcount(x); } - public: template static T ShiftLeft(T x, std::int32_t y) { return Bit.ShiftLeft(x, y); } + constexpr auto GetLowestPosition(std::unsigned_integral auto value) noexcept { return (value == 0) ? -1 : std::countr_zero(value); } - public: template static T ShiftRight(T x, std::int32_t y) { return Bit.ShiftRight(x, y); } - - public: template static T PartialWrite(T target, T source, std::int32_t shift, std::int32_t limit) { return Bit.PartialWrite(target, source, shift, limit); } - - public: template static T PartialRead(T target, std::int32_t shift, std::int32_t limit) { return Bit.PartialRead(target, shift, limit); } - }; + template + constexpr auto PartialRead(T target, int shift, int limit) noexcept + { + constexpr auto bits = sizeof(T) * 8; + shift = shift + bits * (shift < 0); + limit = limit + bits * (limit < 0); + + constexpr auto max_value = std::numeric_limits::max(); + auto sourceMask = ~(max_value << limit) & max_value; + auto targetMask = sourceMask << shift; + return (target & targetMask) >> shift; + } + + template + constexpr auto PartialWrite(T target, T source, int shift, int limit) noexcept + { + constexpr auto bits = sizeof(T) * 8; + shift = shift + bits * (shift < 0); + limit = limit + bits * (limit < 0); + + constexpr auto max_value = std::numeric_limits::max(); + auto sourceMask = ~(max_value << limit) & max_value; + auto targetMask = ~(sourceMask << shift); + return target & targetMask | (source & sourceMask) << shift; + } } diff --git a/cpp/Platform.Numbers/Math.h b/cpp/Platform.Numbers/Math.h index ac98dcd..3a14b4b 100644 --- a/cpp/Platform.Numbers/Math.h +++ b/cpp/Platform.Numbers/Math.h @@ -1,55 +1,51 @@ -namespace Platform::Numbers +namespace Platform::Numbers::Math { - class Math + namespace Internal { - private: static readonly std::uint64_t[] _factorials = + constexpr std::uint64_t _factorials[] = { - 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, - 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000, - 355687428096000, 6402373705728000, 121645100408832000, 2432902008176640000 - }; + 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, + 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000, + 355687428096000, 6402373705728000, 121645100408832000, 2432902008176640000 + }; - private: static readonly std::uint64_t[] _catalans = + constexpr std::uint64_t _catalans[] = { - 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, - 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, - 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, - 18367353072152, 69533550916004, 263747951750360, 1002242216651368, 3814986502092304, - 14544636039226909, 55534064877048198, 212336130412243110, 812944042149730764, 3116285494907301262, 11959798385860453492 - }; + 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, + 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, + 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, + 18367353072152, 69533550916004, 263747951750360, 1002242216651368, 3814986502092304, + 14544636039226909, 55534064877048198, 212336130412243110, 812944042149730764, 3116285494907301262, 11959798385860453492UL + }; + } - public: inline static const std::uint64_t MaximumFactorialNumber = 20; + constexpr auto MaximumFactorialNumber = std::size(Internal::_factorials) - 1; - public: inline static const std::uint64_t MaximumCatalanIndex = 36; + constexpr auto MaximumCatalanIndex = std::size(Internal::_catalans) - 1; - public: static std::uint64_t Factorial(std::uint64_t n) + constexpr std::uint64_t Factorial(std::uint64_t n) + { + if (n <= MaximumFactorialNumber) { - if (n >= 0 && n <= MaximumFactorialNumber) - { - return _factorials[n] = { {0} }; - } - else - { - throw std::invalid_argument(std::string("Only numbers from 0 to ").append(Platform::Converters::To(MaximumFactorialNumber)).append(" are supported by unsigned integer with 64 bits length.")); - } - } - - public: static std::uint64_t Catalan(std::uint64_t n) + return Internal::_factorials[n]; + } + else { - if (n >= 0 && n <= MaximumCatalanIndex) - { - return _catalans[n] = { {0} }; - } - else - { - throw std::invalid_argument(std::string("Only numbers from 0 to ").append(Platform::Converters::To(MaximumCatalanIndex)).append(" are supported by unsigned integer with 64 bits length.")); - } - } - - public: static bool IsPowerOfTwo(std::uint64_t x) { return {x & x - 1} == 0; } + throw std::out_of_range(std::string("Only numbers from 0 to ").append(std::to_string(MaximumFactorialNumber)).append("are supported by unsigned integer with 64 bits length.")); + } + } - public: template static T Abs(T x) { return Math.Abs(x); } + constexpr std::uint64_t Catalan(std::size_t n) + { + if (n <= MaximumCatalanIndex) + { + return Internal::_catalans[n]; + } + else + { + throw std::out_of_range(std::string("Only numbers from 0 to ").append(std::to_string(MaximumCatalanIndex)).append("are supported by unsigned integer with 64 bits length.")); + } + } - public: template static T Negate(T x) { return Math.Negate(x); } - }; -} + constexpr bool IsPowerOfTwo(std::size_t x) { return std::has_single_bit(x); } +} diff --git a/cpp/Platform.Numbers/Platform.Numbers.h b/cpp/Platform.Numbers/Platform.Numbers.h new file mode 100644 index 0000000..685b72e --- /dev/null +++ b/cpp/Platform.Numbers/Platform.Numbers.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "Bit.h" +#include "Math.h" diff --git a/cpp/conanfile.txt b/cpp/conanfile.txt new file mode 100644 index 0000000..b9d4a73 --- /dev/null +++ b/cpp/conanfile.txt @@ -0,0 +1,5 @@ +[requires] +gtest/cci.20210126 + +[generators] +cmake