diff --git a/.gitignore b/.gitignore index 9aadd1a..3869416 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,36 @@ +# CMake build artifacts +cpp/build/ +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +Makefile +*.o +*.a +bin/ + +# Conan generated files +conan_toolchain.cmake +conanbuildinfo.* +conaninfo.txt +graph_info.json +CMakePresets.json +CMakeUserPresets.json +conanbuild.sh +conanrun.sh +conanbuildenv-* +conanrunenv-* +deactivate_conanbuild.sh +deactivate_conanrun.sh +cmakedeps_macros.cmake +conandeps_legacy.cmake +Find*.cmake +*Config.cmake +*ConfigVersion.cmake +*Targets.cmake +*-Target-*.cmake +*-release-*.cmake +module-*.cmake + ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## diff --git a/cpp/Platform.Numbers.Tests/BitTests.cpp b/cpp/Platform.Numbers.Tests/BitTests.cpp index 0e8050d..31bcbd1 100644 --- a/cpp/Platform.Numbers.Tests/BitTests.cpp +++ b/cpp/Platform.Numbers.Tests/BitTests.cpp @@ -1,22 +1,94 @@ -namespace Platform::Numbers::Tests +#include "gtest/gtest.h" +#include "Platform.Numbers.h" + +namespace Platform::Numbers::Tests { - // Assuming that GetLowestPosition is a valid function in your bit operations library + // Test GetLowestPosition function with various values based on C# tests TEST(BitTests, GetLowestBitPositionTest) { - std::uint64_t value = 32; - std::int32_t expectedPosition = 5; - ASSERT_EQ(Platform::Numbers::Bit::GetLowestPosition(value), expectedPosition); + // Test cases from C# implementation + ASSERT_EQ(Platform::Numbers::Bit::GetLowestPosition(0), -1); // 0000 0000 (none, -1) + ASSERT_EQ(Platform::Numbers::Bit::GetLowestPosition(1), 0); // 0000 0001 (first, 0) + ASSERT_EQ(Platform::Numbers::Bit::GetLowestPosition(8), 3); // 0000 1000 (forth, 3) + ASSERT_EQ(Platform::Numbers::Bit::GetLowestPosition(88), 3); // 0101 1000 (forth, 3) + ASSERT_EQ(Platform::Numbers::Bit::GetLowestPosition(32), 5); // Previous test case } - // Similar tests can be written for other operations like And, Or, ShiftLeft, ShiftRight, etc. + // Test Count function + TEST(BitTests, CountTest) + { + ASSERT_EQ(Platform::Numbers::Bit::Count(0), 0); + ASSERT_EQ(Platform::Numbers::Bit::Count(1), 1); // 0001 + ASSERT_EQ(Platform::Numbers::Bit::Count(3), 2); // 0011 + ASSERT_EQ(Platform::Numbers::Bit::Count(7), 3); // 0111 + ASSERT_EQ(Platform::Numbers::Bit::Count(15), 4); // 1111 + } - // PartialRead and PartialWrite tests + // PartialRead and PartialWrite tests based on C# implementation TEST(BitTests, PartialReadWriteTest) { std::uint32_t firstValue = 1; std::uint32_t secondValue = 1543; - std::uint32_t value = Platform::Numbers::Bit::PartialWrite(secondValue, firstValue, 0, 1); - std::uint32_t readValue = Platform::Numbers::Bit::PartialRead(value, 0, 1); - ASSERT_EQ(readValue, firstValue); + + // Pack (join) two values at the same time + std::uint32_t value = secondValue << 1 | firstValue; + + std::uint32_t unpackagedFirstValue = value & 1; + std::uint32_t unpackagedSecondValue = (value & 0xFFFFFFFE) >> 1; + + ASSERT_EQ(firstValue, unpackagedFirstValue); + ASSERT_EQ(secondValue, unpackagedSecondValue); + + // Using universal functions: + ASSERT_EQ(Platform::Numbers::Bit::PartialRead(value, 0, 1), firstValue); + ASSERT_EQ(Platform::Numbers::Bit::PartialRead(value, 1, -1), secondValue); + + firstValue = 0; + secondValue = 6892; + + value = Platform::Numbers::Bit::PartialWrite(value, firstValue, 0, 1); + value = Platform::Numbers::Bit::PartialWrite(value, secondValue, 1, -1); + + ASSERT_EQ(Platform::Numbers::Bit::PartialRead(value, 0, 1), firstValue); + ASSERT_EQ(Platform::Numbers::Bit::PartialRead(value, 1, -1), secondValue); + } + + // Test Math functions + TEST(MathTests, FactorialTest) + { + ASSERT_EQ(Platform::Numbers::Math::Factorial(0), 1); + ASSERT_EQ(Platform::Numbers::Math::Factorial(1), 1); + ASSERT_EQ(Platform::Numbers::Math::Factorial(2), 2); + ASSERT_EQ(Platform::Numbers::Math::Factorial(3), 6); + ASSERT_EQ(Platform::Numbers::Math::Factorial(4), 24); + ASSERT_EQ(Platform::Numbers::Math::Factorial(5), 120); + + // Test out of range + ASSERT_THROW(Platform::Numbers::Math::Factorial(21), std::out_of_range); + } + + TEST(MathTests, CatalanTest) + { + ASSERT_EQ(Platform::Numbers::Math::Catalan(0), 1); + ASSERT_EQ(Platform::Numbers::Math::Catalan(1), 1); + ASSERT_EQ(Platform::Numbers::Math::Catalan(2), 2); + ASSERT_EQ(Platform::Numbers::Math::Catalan(3), 5); + ASSERT_EQ(Platform::Numbers::Math::Catalan(4), 14); + + // Test out of range + ASSERT_THROW(Platform::Numbers::Math::Catalan(37), std::out_of_range); + } + + TEST(MathTests, IsPowerOfTwoTest) + { + ASSERT_FALSE(Platform::Numbers::Math::IsPowerOfTwo(0)); + ASSERT_TRUE(Platform::Numbers::Math::IsPowerOfTwo(1)); + ASSERT_TRUE(Platform::Numbers::Math::IsPowerOfTwo(2)); + ASSERT_FALSE(Platform::Numbers::Math::IsPowerOfTwo(3)); + ASSERT_TRUE(Platform::Numbers::Math::IsPowerOfTwo(4)); + ASSERT_FALSE(Platform::Numbers::Math::IsPowerOfTwo(5)); + ASSERT_TRUE(Platform::Numbers::Math::IsPowerOfTwo(8)); + ASSERT_TRUE(Platform::Numbers::Math::IsPowerOfTwo(16)); + ASSERT_FALSE(Platform::Numbers::Math::IsPowerOfTwo(17)); } } diff --git a/cpp/Platform.Numbers/Bit.h b/cpp/Platform.Numbers/Bit.h index 5149d8d..e210a04 100644 --- a/cpp/Platform.Numbers/Bit.h +++ b/cpp/Platform.Numbers/Bit.h @@ -1,5 +1,21 @@ -namespace Platform::Numbers::Bit +#pragma once +#include +#include + +namespace Platform::Numbers::Bit { + /// + /// Counts the number of bits set in a number. + /// Подсчитывает количество установленных бит в числе. + /// + /// + /// Bitwise number. + /// Число в битовом представлении. + /// + /// + /// Number of bits set in a number. + /// Количество установленных бит в числе. + /// constexpr std::int64_t Count(std::int64_t x) { std::int64_t n = 0; @@ -11,6 +27,18 @@ return n; } + /// + /// Searches for the first bit set in a number. + /// Ищет первый установленный бит в числе. + /// + /// + /// Bitwise number. + /// Число в битовом представлении. + /// + /// + /// First bit set. + /// Первый установленный бит. + /// constexpr std::int32_t GetLowestPosition(std::uint64_t value) { if (value == 0) diff --git a/cpp/Platform.Numbers/Math.h b/cpp/Platform.Numbers/Math.h index ac98dcd..67af4a0 100644 --- a/cpp/Platform.Numbers/Math.h +++ b/cpp/Platform.Numbers/Math.h @@ -1,55 +1,116 @@ -namespace Platform::Numbers -{ +#pragma once +#include +#include +#include +#include + +namespace Platform::Numbers +{ + /// + /// Represents a set of math methods. + /// Представляет набор математических методов. + /// class Math - { - private: static readonly std::uint64_t[] _factorials = - { + { + private: + static 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 + 479001600, 6227020800ULL, 87178291200ULL, 1307674368000ULL, 20922789888000ULL, + 355687428096000ULL, 6402373705728000ULL, 121645100408832000ULL, 2432902008176640000ULL }; - - private: static readonly std::uint64_t[] _catalans = - { + + static 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 + 6564120420ULL, 24466267020ULL, 91482563640ULL, 343059613650ULL, 1289904147324ULL, 4861946401452ULL, + 18367353072152ULL, 69533550916004ULL, 263747951750360ULL, 1002242216651368ULL, 3814986502092304ULL, + 14544636039226909ULL, 55534064877048198ULL, 212336130412243110ULL, 812944042149730764ULL, 3116285494907301262ULL, 11959798385860453492ULL }; - - public: inline static const std::uint64_t MaximumFactorialNumber = 20; - - public: inline static const std::uint64_t MaximumCatalanIndex = 36; - - public: static std::uint64_t Factorial(std::uint64_t n) - { - if (n >= 0 && n <= MaximumFactorialNumber) - { - return _factorials[n] = { {0} }; + + public: + /// + /// Represents the limit for calculating the factorial number, supported by the type. + /// Представляет предел расчёта факториала числа, поддерживаемый типом. + /// + static constexpr std::uint64_t MaximumFactorialNumber = 20; + + /// + /// Represents the limit for calculating the catanal number, supported by the type. + /// Представляет предел расчёта катаналового числа, поддерживаемый типом. + /// + static constexpr std::uint64_t MaximumCatalanIndex = 36; + + /// + /// Returns the product of all positive integers less than or equal to the number specified as an argument. + /// Возвращает произведение всех положительных чисел меньше или равных указанному в качестве аргумента числу. + /// + /// + /// The maximum positive number that will participate in factorial's product. + /// Максимальное положительное число, которое будет участвовать в произведении факториала. + /// + /// + /// The product of all positive integers less than or equal to the number specified as an argument. + /// Произведение всех положительных чисел меньше или равных указанному, в качестве аргумента, числу. + /// + template + static constexpr TLinkAddress Factorial(TLinkAddress n) + { + static_assert(std::is_unsigned_v, "TLinkAddress must be an unsigned integer type"); + if (n <= MaximumFactorialNumber) + { + return static_cast(_factorials[static_cast(n)]); } 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.")); + { + throw std::out_of_range("Only numbers from 0 to " + std::to_string(MaximumFactorialNumber) + " are supported by unsigned integer with 64 bits length."); } } - - public: static std::uint64_t Catalan(std::uint64_t n) - { - if (n >= 0 && n <= MaximumCatalanIndex) - { - return _catalans[n] = { {0} }; + + /// + /// Returns the Catalan Number with the number specified as an argument. + /// Возвращает Число Катанала с номером, указанным в качестве аргумента. + /// + /// + /// The number of the Catalan number. + /// Номер Числа Катанала. + /// + /// + /// The Catalan Number with the number specified as an argument. + /// Число Катанала с номером, указанным в качестве аргумента. + /// + template + static constexpr TLinkAddress Catalan(TLinkAddress n) + { + static_assert(std::is_unsigned_v, "TLinkAddress must be an unsigned integer type"); + if (n <= MaximumCatalanIndex) + { + return static_cast(_catalans[static_cast(n)]); } 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.")); + { + throw std::out_of_range("Only numbers from 0 to " + std::to_string(MaximumCatalanIndex) + " are supported by unsigned integer with 64 bits length."); } } - - public: static bool IsPowerOfTwo(std::uint64_t x) { return {x & x - 1} == 0; } - - public: template static T Abs(T x) { return Math.Abs(x); } - - public: template static T Negate(T x) { return Math.Negate(x); } + + /// + /// Checks if a number is a power of two. + /// Проверяет, является ли число степенью двойки. + /// + /// + /// The number to check. + /// Число для проверки. + /// + /// + /// True if the number is a power of two otherwise false. + /// True, если число является степенью двойки, иначе - false. + /// + template + static constexpr bool IsPowerOfTwo(TLinkAddress x) + { + static_assert(std::is_unsigned_v, "TLinkAddress must be an unsigned integer type"); + return x != 0 && (x & (x - 1)) == 0; + } }; } diff --git a/cpp/Platform.Numbers/Platform.Numbers.h b/cpp/Platform.Numbers/Platform.Numbers.h index 1dabc42..79f9245 100644 --- a/cpp/Platform.Numbers/Platform.Numbers.h +++ b/cpp/Platform.Numbers/Platform.Numbers.h @@ -6,6 +6,6 @@ #include "Bit.h" //#include "BitExtensions.h" //#include "Bit[T].h" -//#include "Math.h" +#include "Math.h" //#include "MathExtensions.h" //#include "Math[T].h"