From ce1fce0633e854c9295d56874d4b26d0032cf33e Mon Sep 17 00:00:00 2001 From: Sam Morley <41870650+inakleinbottle@users.noreply.github.com> Date: Wed, 27 Nov 2024 22:06:17 +0000 Subject: [PATCH] Overhaul polynomial type (#172) * Add polynomial type support to generics Introduced a new polynomial_type subdirectory with Indeterminate and Monomial classes. Updated CMakeLists to include the new directory and source files, enhancing the generics module with polynomial capabilities. * Update Monomial class with operator overloads and functions Refactored Monomial class to include new operator overloads and additional member functions for improved functionality. Simplified the 'Indeterminate' constructor and modified stream operators to use the generics namespace. * Fix typo in monomial.cpp header comment Corrected the accidental removal of comment characters which unintentionally disrupted the header section of the file. This change ensures the header comment remains properly formatted. * Remove extraneous characters from comment header Deleted unnecessary characters "zr" from the comment header in monomial.cpp to clean up the file. This change helps maintain code readability and prevents potential confusion for future developers. * Add mpq string representation and hash functions Introduced `mpq_display_rep` for generating string representations of GMP rational numbers and `mpq_hash` for computing their hash values. Refactored `rational_type.cpp` to utilize these new functions, enhancing code modularity and clarity. * Add Polynomial class with arithmetic operations Introduced a new `Polynomial` class along with its header and source files. Implemented essential polynomial arithmetic operations including addition, subtraction, multiplication, and division. Updated the CMakeLists to include the new files, and annotated relevant methods with `RPY_NO_DISCARD` in `monomial.h`. * Refactor index method and add monomial tests Refactored the `index` method in `indeterminate.h` to use `integer_mask` directly for improved clarity and correctness. Added comprehensive unit tests for `Monomial` in the new file `test_monomial.cpp` and updated `CMakeLists.txt` to include these tests when building. * Add unit tests for `Indeterminate` and `Polynomial` classes Implemented GTest-based unit tests for the `Indeterminate` class, verifying constructors, operators, and hash functionality. Additionally, created a test file for the `Polynomial` class and updated the CMakeLists.txt to include these new tests. * Add degree method and various enhancements to Polynomial Introduced a `degree` method to compute the degree of a polynomial. Added additional constructors and utility functions, cleaned up headers, * Add constructor and empty check to Monomial class Introduce a new constructor to initialize Monomial with a prefix, index, and degree. Implement a method to check if the Monomial data is empty, enhancing class usability. * Add unit tests for polynomial operations Implement unit tests for default value, display, addition, subtraction, multiplication, and division of Polynomial class. These tests ensure proper functionality and correctness of polynomial operations. * Add constructors and stream operator to Polynomial class Implemented default copy and move constructors for the Polynomial class to ensure proper object management. Also added an overloaded stream operator for easy printing of Polynomial objects. * Refactor polynomial multiplication test logic. Changed the test case to multiply into a separate result polynomial instead of in-place. The expected result polynomial has been updated to reflect correct multiplication values. Adjusted assertions to compare the updated result correctly. * Add tests for polynomial equality and hashing This commit introduces new test cases for validating polynomial equality and hashing functionality. The tests ensure that polynomials are compared correctly and that their hash values are consistent as expected. This improves reliability and correctness in polynomial-related operations. * Add polynomial type handling files Introduced new header and source files for polynomial types, including arithmetic, comparison, and number traits. Modified the CMakeLists to include these files and added tests for the new polynomial type functionalities in a dedicated test suite. * Refactor `poly_div_inplace` to use `mpq_srcptr` Updated the `poly_div_inplace` function to accept `mpq_srcptr` directly instead of `dtl::RationalCoeff`. This change improves clarity and efficiency by eliminating unnecessary indirection. Additionally, provided an inline function for backward compatibility with existing `dtl::RationalCoeff` usage. * Extend PolynomialArithmetic with ArithmeticTrait methods Implemented the unsafe_add_inplace, unsafe_sub_inplace, unsafe_mul_inplace, and unsafe_div_inplace methods in PolynomialArithmetic, inheriting from ArithmeticTrait. Added necessary includes and assertions to ensure robustness of operations. This enhances polynomial arithmetic capabilities with runtime checks and debug assertions. * Add polynomial_types.cpp to the build configuration This commit includes polynomial_types.cpp in the list of source files in CMakeLists.txt. This change ensures that the polynomial types are compiled and linked properly in the build process. * Refactor `polynomial_type.cpp` for improved readability. Condensed single-line function bodies for better clarity. Updated `basic_properties` to reflect accurate properties. These changes enhance code maintainability without altering functionality. * Update test assertions in `test_polynomial_type.cpp`. Revised test expectations to match updated polynomial representation and improved formatting for properties assertions. These changes make tests more accurate and improve code maintainability without changing core functionality. * Implement PolynomialType constructor and specialized traits Added a constructor to PolynomialType to initialize arithmetic, comparison, and number traits. Updated `get_builtin_trait` method to return these specialized traits based on the given trait ID. * Update tests for PolynomialType operations and functions Modified unit tests to check that division and FromRational functions are supported by PolynomialType. Changed assertions from `EXPECT_FALSE` to `EXPECT_TRUE` to reflect current implementation capabilities. * Add ctre and fmt dependencies Included ctre and fmt packages in CMakeLists.txt for library linkage. Updated vcpkg.json to reflect new dependency versions for consistent package management. * Change base_type to uint64_t and enhance parser functionality. Updated the Indeterminate class to use uint64_t for base_type. Added clear and emplace methods to Monomial. Enhanced PolynomialType parsing with support for integers and rational coefficients, and restored parse_from_string. * Change base_type to uint64_t and enhance parser functionality. Updated the Indeterminate class to use uint64_t for base_type. Added clear and emplace methods to Monomial. Enhanced PolynomialType parsing with support for integers and rational coefficients, and restored parse_from_string. * Add test for parsing mixed monomial with powers Introduce a new unit test to ensure the PolynomialType can correctly parse and display mixed monomials with powers. This helps maintain the accuracy and robustness of polynomial representation. * Add missing include and remove unused The header was added to monomial.cpp to utilize numeric algorithms. The unused header was removed from polynomial_type.cpp to clean up the codebase and improve maintainability. --- CMakeLists.txt | 2 + platform/include/roughpy/generics/type.h | 82 +++-- platform/src/generics/CMakeLists.txt | 2 + .../multiprecision_types/mpq_string_rep.h | 32 ++ .../generics/multiprecision_types/mpz_hash.h | 9 + .../multiprecision_types/rational_type.cpp | 18 +- .../generics/polynomial_type/CMakeLists.txt | 60 ++++ .../polynomial_type/indeterminate.cpp | 15 + .../generics/polynomial_type/indeterminate.h | 143 +++++++++ .../src/generics/polynomial_type/monomial.cpp | 114 +++++++ .../src/generics/polynomial_type/monomial.h | 135 ++++++++ .../generics/polynomial_type/polynomial.cpp | 152 +++++++++ .../src/generics/polynomial_type/polynomial.h | 159 ++++++++++ .../polynomial_type/polynomial_arithmetic.cpp | 61 ++++ .../polynomial_type/polynomial_arithmetic.h | 39 +++ .../polynomial_type/polynomial_comparison.cpp | 57 ++++ .../polynomial_type/polynomial_comparison.h | 39 +++ .../polynomial_type/polynomial_number.cpp | 31 ++ .../polynomial_type/polynomial_number.h | 32 ++ .../polynomial_type/polynomial_type.cpp | 290 ++++++++++++++++++ .../polynomial_type/polynomial_type.h | 78 +++++ .../polynomial_type/test_indeterminate.cpp | 120 ++++++++ .../polynomial_type/test_monomial.cpp | 52 ++++ .../polynomial_type/test_polynomial.cpp | 186 +++++++++++ .../polynomial_type/test_polynomial_type.cpp | 255 +++++++++++++++ platform/src/generics/polynomial_types.cpp | 13 + vcpkg.json | 171 +++++------ 27 files changed, 2194 insertions(+), 153 deletions(-) create mode 100644 platform/src/generics/multiprecision_types/mpq_string_rep.h create mode 100644 platform/src/generics/polynomial_type/CMakeLists.txt create mode 100644 platform/src/generics/polynomial_type/indeterminate.cpp create mode 100644 platform/src/generics/polynomial_type/indeterminate.h create mode 100644 platform/src/generics/polynomial_type/monomial.cpp create mode 100644 platform/src/generics/polynomial_type/monomial.h create mode 100644 platform/src/generics/polynomial_type/polynomial.cpp create mode 100644 platform/src/generics/polynomial_type/polynomial.h create mode 100644 platform/src/generics/polynomial_type/polynomial_arithmetic.cpp create mode 100644 platform/src/generics/polynomial_type/polynomial_arithmetic.h create mode 100644 platform/src/generics/polynomial_type/polynomial_comparison.cpp create mode 100644 platform/src/generics/polynomial_type/polynomial_comparison.h create mode 100644 platform/src/generics/polynomial_type/polynomial_number.cpp create mode 100644 platform/src/generics/polynomial_type/polynomial_number.h create mode 100644 platform/src/generics/polynomial_type/polynomial_type.cpp create mode 100644 platform/src/generics/polynomial_type/polynomial_type.h create mode 100644 platform/src/generics/polynomial_type/test_indeterminate.cpp create mode 100644 platform/src/generics/polynomial_type/test_monomial.cpp create mode 100644 platform/src/generics/polynomial_type/test_polynomial.cpp create mode 100644 platform/src/generics/polynomial_type/test_polynomial_type.cpp create mode 100644 platform/src/generics/polynomial_types.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c2328e0..1e92c84d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -179,6 +179,8 @@ find_package(PCGRandom REQUIRED) find_package(GMP REQUIRED) find_package(MPFR REQUIRED) find_package(range-v3 CONFIG REQUIRED) +find_package(ctre CONFIG REQUIRED) +find_package(fmt CONFIG REQUIRED) message(STATUS "Target architecture ${RPY_ARCH}") diff --git a/platform/include/roughpy/generics/type.h b/platform/include/roughpy/generics/type.h index f80eda66..c96a48dd 100644 --- a/platform/include/roughpy/generics/type.h +++ b/platform/include/roughpy/generics/type.h @@ -30,18 +30,19 @@ namespace rpy::generics { * includes methods and attributes which can be leveraged to manage and * manipulate these properties efficiently and effectively. */ -struct BasicProperties { - bool standard_layout : 1; - bool trivially_copyable : 1; - bool trivially_constructible : 1; - bool trivially_default_constructible : 1; - bool trivially_copy_constructible : 1; - bool trivially_copy_assignable : 1; - bool trivially_destructible : 1; - bool polymorphic : 1; - bool is_signed : 1; - bool is_floating_point : 1; - bool is_integral : 1; +struct BasicProperties +{ + bool standard_layout: 1; + bool trivially_copyable: 1; + bool trivially_constructible: 1; + bool trivially_default_constructible: 1; + bool trivially_copy_constructible: 1; + bool trivially_copy_assignable: 1; + bool trivially_destructible: 1; + bool polymorphic: 1; + bool is_signed: 1; + bool is_floating_point: 1; + bool is_integral: 1; }; template @@ -60,6 +61,7 @@ constexpr BasicProperties basic_properties_of() noexcept; template TypePtr get_type() noexcept; + //{ // static_assert(false, "There is no Type associated with T"); // RPY_UNREACHABLE_RETURN(nullptr); @@ -78,7 +80,6 @@ class ROUGHPY_PLATFORM_EXPORT Type : public mem::PolymorphicRefCounted friend class Value; public: - /** * @brief Returns the type information of the current instance. * @@ -164,7 +165,6 @@ class ROUGHPY_PLATFORM_EXPORT Type : public mem::PolymorphicRefCounted virtual void free_object(void* ptr) const = 0; public: - virtual bool parse_from_string(void* data, string_view str) const noexcept; /** @@ -183,7 +183,7 @@ class ROUGHPY_PLATFORM_EXPORT Type : public mem::PolymorphicRefCounted */ virtual void copy_or_move(void* dst, const void* src, size_t count, bool move) const - = 0; + = 0; virtual void destroy_range(void* data, size_t count) const = 0; @@ -273,10 +273,7 @@ class ROUGHPY_PLATFORM_EXPORT Type : public mem::PolymorphicRefCounted * * @return A TypePtr representing the specific type. */ - static TypePtr of() noexcept - { - return get_type>(); - } + static TypePtr of() noexcept { return get_type >(); } }; @@ -314,8 +311,8 @@ const BuiltinTypes& get_builtin_types() noexcept; class ROUGHPY_PLATFORM_EXPORT MultiPrecisionTypes { MultiPrecisionTypes(); -public: +public: TypePtr integer_type; TypePtr rational_type; @@ -326,6 +323,9 @@ class ROUGHPY_PLATFORM_EXPORT MultiPrecisionTypes static const MultiPrecisionTypes& get() noexcept; }; +RPY_NO_DISCARD +ROUGHPY_PLATFORM_EXPORT +TypePtr get_polynomial_type() noexcept; template @@ -338,18 +338,18 @@ constexpr BasicProperties basic_properties_of() noexcept { using base_t = remove_cvref_t; return { - is_standard_layout_v, - is_trivially_copyable_v, - is_trivially_constructible_v, - is_trivially_default_constructible_v, - is_trivially_copy_constructible_v, - is_trivially_copy_assignable_v, - is_trivially_destructible_v, - is_polymorphic_v, - is_signed_v, - is_floating_point_v, - is_integral_v, -}; + is_standard_layout_v, + is_trivially_copyable_v, + is_trivially_constructible_v, + is_trivially_default_constructible_v, + is_trivially_copy_constructible_v, + is_trivially_copy_assignable_v, + is_trivially_destructible_v, + is_polymorphic_v, + is_signed_v, + is_floating_point_v, + is_integral_v, + }; } /** @@ -366,7 +366,9 @@ constexpr BasicProperties basic_properties_of() noexcept * @return The promoted type based on the provided `lhs` and `rhs` types. * If neither type can be promoted to the other, `nullptr` is returned. */ -RPY_NO_DISCARD TypePtr ROUGHPY_PLATFORM_EXPORT compute_promotion(const Type* lhs, const Type* rhs) noexcept; +RPY_NO_DISCARD TypePtr ROUGHPY_PLATFORM_EXPORT compute_promotion( + const Type* lhs, + const Type* rhs) noexcept; /** * @brief Computes the hash value of a given Type object. @@ -386,16 +388,10 @@ inline hash_t hash_value(const Type& value) noexcept } RPY_NO_DISCARD constexpr bool -operator==(const Type& lhs, const Type& rhs) noexcept -{ - return &lhs == &rhs; -} +operator==(const Type& lhs, const Type& rhs) noexcept { return &lhs == &rhs; } RPY_NO_DISCARD constexpr bool -operator!=(const Type& lhs, const Type& rhs) noexcept -{ - return !(lhs == rhs); -} +operator!=(const Type& lhs, const Type& rhs) noexcept { return !(lhs == rhs); } /** * @brief Retrieves the size of an object of the specified type. @@ -411,7 +407,6 @@ inline size_t size_of(const Type& type) noexcept { return type.object_size(); } namespace concepts { - /** * @brief Check if the given type has standard layout * @param type A reference to the Type object @@ -550,7 +545,6 @@ inline bool is_arithmetic(Type const& type) }// namespace concepts - template <> inline TypePtr get_type() noexcept { @@ -614,4 +608,4 @@ inline TypePtr get_type() noexcept }// namespace rpy::generics -#endif// ROUGHPY_GENERICS_TYPE_H +#endif// ROUGHPY_GENERICS_TYPE_H \ No newline at end of file diff --git a/platform/src/generics/CMakeLists.txt b/platform/src/generics/CMakeLists.txt index 08094945..e4df2491 100644 --- a/platform/src/generics/CMakeLists.txt +++ b/platform/src/generics/CMakeLists.txt @@ -17,6 +17,7 @@ target_sources(RoughPy_Platform PRIVATE conversion_trait.cpp multiprecision_types.cpp number_trait.cpp + polynomial_types.cpp type.cpp type_builtin.cpp type_promotion.cpp @@ -29,6 +30,7 @@ target_sources(RoughPy_Platform PRIVATE add_subdirectory(builtin_types) add_subdirectory(multiprecision_types) +add_subdirectory(polynomial_type) diff --git a/platform/src/generics/multiprecision_types/mpq_string_rep.h b/platform/src/generics/multiprecision_types/mpq_string_rep.h new file mode 100644 index 00000000..14ae4ea4 --- /dev/null +++ b/platform/src/generics/multiprecision_types/mpq_string_rep.h @@ -0,0 +1,32 @@ +// +// Created by sam on 26/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_MPQ_STRING_REP_H +#define ROUGHPY_GENERICS_INTERNAL_MPQ_STRING_REP_H + + +#include + +#include "roughpy/core/types.h" + + +namespace rpy::generics { + +inline void mpq_display_rep(string& buffer, mpq_srcptr value) noexcept +{ + // The GMP docs describe the size of a mpq string representation in the + // documentation https://gmplib.org/manual/Rational-Conversions + auto num_size = mpz_sizeinbase(mpq_numref(value), 10); + auto denom_size = mpz_sizeinbase(mpq_denref(value), 10); + buffer.resize(num_size + denom_size + 3); + + mpq_get_str(buffer.data(), 10, value); + + // The buffer has at least one null byte at the end, cut these off + while (buffer.back() == '\0') { buffer.pop_back(); } +} + +} + +#endif //ROUGHPY_GENERICS_INTERNAL_MPQ_STRING_REP_H diff --git a/platform/src/generics/multiprecision_types/mpz_hash.h b/platform/src/generics/multiprecision_types/mpz_hash.h index 9e91050e..0cffad6f 100644 --- a/platform/src/generics/multiprecision_types/mpz_hash.h +++ b/platform/src/generics/multiprecision_types/mpz_hash.h @@ -24,6 +24,15 @@ inline hash_t mpz_hash(mpz_srcptr integer) noexcept return result; } +inline hash_t mpq_hash(mpq_srcptr integer) noexcept +{ + auto num_hash = mpz_hash(mpq_numref(integer)); + const auto denom_hash = mpz_hash(mpq_denref(integer)); + + hash_combine(num_hash, denom_hash); + return num_hash; +} + } diff --git a/platform/src/generics/multiprecision_types/rational_type.cpp b/platform/src/generics/multiprecision_types/rational_type.cpp index 53f43e25..3d0a9c20 100644 --- a/platform/src/generics/multiprecision_types/rational_type.cpp +++ b/platform/src/generics/multiprecision_types/rational_type.cpp @@ -11,6 +11,7 @@ #include #include "mpz_hash.h" +#include "mpq_string_rep.h" using namespace rpy; using namespace rpy::generics; @@ -145,18 +146,9 @@ RationalType::display(std::ostream& os, const void* value) const const auto* rat = static_cast(value); - // The GMP docs describe the size of a mpq string representation in the - // documentation https://gmplib.org/manual/Rational-Conversions - auto num_size = mpz_sizeinbase(mpq_numref(rat), 10); - auto denom_size = mpz_sizeinbase(mpq_denref(rat), 10); - string buffer; - buffer.resize(num_size + denom_size + 3); - - mpq_get_str(buffer.data(), 10, rat); + mpq_display_rep(buffer, rat); - // The buffer has at least one null byte at the end, cut these off - while (buffer.back() == '\0') { buffer.pop_back(); } return os << buffer; } @@ -165,11 +157,7 @@ hash_t RationalType::hash_of(const void* value) const noexcept if (value == nullptr) { return 0; } auto* rat = static_cast(value); - auto num_hash = mpz_hash(mpq_numref(rat)); - const auto denom_hash = mpz_hash(mpq_denref(rat)); - - hash_combine(num_hash, denom_hash); - return num_hash; + return mpq_hash(rat); } TypePtr RationalType::get() noexcept diff --git a/platform/src/generics/polynomial_type/CMakeLists.txt b/platform/src/generics/polynomial_type/CMakeLists.txt new file mode 100644 index 00000000..14487cde --- /dev/null +++ b/platform/src/generics/polynomial_type/CMakeLists.txt @@ -0,0 +1,60 @@ + + + + +target_sources(RoughPy_Platform PRIVATE + indeterminate.h + indeterminate.cpp + monomial.cpp + monomial.h + polynomial.cpp + polynomial.h + polynomial_arithmetic.cpp + polynomial_arithmetic.h + polynomial_comparison.cpp + polynomial_comparison.h + polynomial_number.cpp + polynomial_number.h + polynomial_type.cpp + polynomial_type.h +) + +target_link_libraries(RoughPy_Platform PRIVATE + ctre::ctre) + +if (ROUGHPY_BUILD_TESTS) + + add_executable(test_polynomial + test_indeterminate.cpp + test_monomial.cpp + test_polynomial.cpp + indeterminate.cpp + indeterminate.h + monomial.cpp + monomial.h + polynomial.cpp + polynomial.h + ) + + target_include_directories(test_polynomial PRIVATE + ${ROUGHPY_PLATFORM_SOURCE} + ) + + target_link_libraries(test_polynomial PRIVATE + RoughPy::Core + Boost::headers + GMP::GMP + GTest::gtest + ) + + setup_roughpy_cpp_tests(test_polynomial) + + add_executable(test_polynomial_type + test_polynomial_type.cpp + ) + + target_link_libraries(test_polynomial_type PRIVATE RoughPy::Platform GTest::gtest) + + setup_roughpy_cpp_tests(test_polynomial_type) + +endif () \ No newline at end of file diff --git a/platform/src/generics/polynomial_type/indeterminate.cpp b/platform/src/generics/polynomial_type/indeterminate.cpp new file mode 100644 index 00000000..5d669cb5 --- /dev/null +++ b/platform/src/generics/polynomial_type/indeterminate.cpp @@ -0,0 +1,15 @@ +// +// Created by sam on 26/11/24. +// + +#include "indeterminate.h" + +#include + +using namespace rpy; +using namespace rpy::generics; + + +std::ostream& generics::operator<<(std::ostream& os, const Indeterminate& value) { + return os << value.prefix() << value.index(); +} \ No newline at end of file diff --git a/platform/src/generics/polynomial_type/indeterminate.h b/platform/src/generics/polynomial_type/indeterminate.h new file mode 100644 index 00000000..0728707f --- /dev/null +++ b/platform/src/generics/polynomial_type/indeterminate.h @@ -0,0 +1,143 @@ +// +// Created by sam on 26/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_INDETERMINATE_H +#define ROUGHPY_GENERICS_INTERNAL_INDETERMINATE_H + +#include +#include + +#include "roughpy/core/check.h" +#include "roughpy/core/debug_assertion.h" +#include "roughpy/core/hash.h" +#include "roughpy/core/helpers.h" +#include "roughpy/core/traits.h" +#include "roughpy/core/types.h" + + +namespace rpy::generics { + +class Indeterminate +{ + using base_type = uint64_t; + base_type m_data; + +public: + static constexpr size_t data_size = sizeof(base_type); + static constexpr size_t num_chars = 1; + + static_assert( + num_chars >= 1, + "sanity check failed: at least one prefix character" + ); + static_assert( + data_size > num_chars, + "sanity check failed, base type be large than num_chars bytes" + ); + + static constexpr int char_shift + = static_cast(data_size - num_chars) * 8; + + static constexpr base_type integer_mask + = (static_cast(1) << char_shift) - 1; + + static constexpr base_type default_value = + static_cast('x') << char_shift; + + + constexpr Indeterminate() noexcept : m_data(default_value) {} + + explicit Indeterminate(base_type integer) noexcept + : m_data(default_value | (integer & integer_mask)) + { + RPY_DBG_ASSERT_LE(integer, integer_mask); + } + + explicit Indeterminate(char prefix, base_type integer) noexcept + { + RPY_DBG_ASSERT_LE(integer, integer_mask); + + m_data = static_cast(prefix) << char_shift; + m_data |= (integer & integer_mask); + } + + template > + Indeterminate(char prefix[N], base_type base) noexcept + { + RPY_DBG_ASSERT_LE(base, integer_mask); + + m_data = 0; + std::memcpy(&m_data, prefix, N); + m_data <<= char_shift; + m_data |= (base & integer_mask); + } + + string prefix() const + { + // This will almost surely never throw because the number of characters + // should never exceed the size of the string internal storage + string result; + + char tmp_array[num_chars]; + base_type tmp = m_data >> char_shift; + std::memcpy(tmp_array, &tmp, num_chars); + + result.assign(tmp_array, num_chars); + + return result; + } + + base_type index() const noexcept { return m_data & integer_mask; } + + friend constexpr bool + operator==(const Indeterminate& lhs, const Indeterminate& rhs) noexcept + { + return lhs.m_data == rhs.m_data; + } + + friend constexpr bool + operator!=(const Indeterminate& lhs, const Indeterminate& rhs) noexcept + { + return lhs.m_data != rhs.m_data; + } + + friend constexpr bool + operator <(const Indeterminate& lhs, const Indeterminate& rhs) noexcept + { + return lhs.m_data < rhs.m_data; + } + + friend constexpr bool + operator <=(const Indeterminate& lhs, const Indeterminate& rhs) noexcept + { + return lhs.m_data <= rhs.m_data; + } + + friend constexpr bool + operator >(const Indeterminate& lhs, const Indeterminate& rhs) noexcept + { + return lhs.m_data > rhs.m_data; + } + + friend constexpr bool + operator >=(const Indeterminate& lhs, const Indeterminate& rhs) noexcept + { + return lhs.m_data >= rhs.m_data; + } + + friend hash_t hash_value(const Indeterminate& value) noexcept { + Hash hasher; + return hasher(value.m_data); + } + +}; + +std::ostream& operator<<(std::ostream& os, const Indeterminate& value); + + + + +};// namespace rpy::generics + +#endif// ROUGHPY_GENERICS_INTERNAL_INDETERMINATE_H diff --git a/platform/src/generics/polynomial_type/monomial.cpp b/platform/src/generics/polynomial_type/monomial.cpp new file mode 100644 index 00000000..339b5937 --- /dev/null +++ b/platform/src/generics/polynomial_type/monomial.cpp @@ -0,0 +1,114 @@ +// +// Created by sam on 26/11/24. +// + +#include "monomial.h" + +#include +#include +#include +#include + +#include "roughpy/core/ranges.h" + + +using namespace rpy; +using namespace rpy::generics; + +deg_t Monomial::degree() const noexcept +{ + return std::accumulate(m_data.begin(), + m_data.end(), + 0, + [](auto acc, const auto& pair) { + return acc + pair.second; + }); +} + + +deg_t Monomial::operator[](Indeterminate indeterminate) const noexcept { + auto it = m_data.find(indeterminate); + if (it == m_data.end()) { return 0; } + return it->second; +} + +Monomial& Monomial::operator*=(const Monomial& rhs) +{ + const auto lend = m_data.end(); + for (const auto& [ind, pow] : rhs) { + if (auto it = m_data.find(ind); it != lend) { + it->second += pow; + } else { m_data.emplace(ind, pow); } + } + return *this; +} + +Monomial generics::operator*(const Monomial& lhs, const Monomial& rhs) +{ + Monomial result(lhs); + result *= rhs; + return result; +} + +std::ostream& generics::operator<<(std::ostream& os, const Monomial& value) +{ + bool first = true; + for (const auto& [ind, pow] : value) { + if (first) { first = false; } else { os << ' '; } + if (pow > 0) { + os << ind; + if (pow > 1) { os << '^' << pow; } + } + } + return os; +} + +bool generics::operator==(const Monomial& lhs, const Monomial& rhs) noexcept +{ + if (lhs.type() != rhs.type()) { return false; } + + return std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +bool generics::operator<(const Monomial& lhs, const Monomial& rhs) noexcept +{ + const auto ldegree = lhs.degree(); + const auto rdegree = rhs.degree(); + + if (ldegree < rdegree) { return true; } + + if (ldegree == rdegree) { + return std::lexicographical_compare(lhs.begin(), + lhs.end(), + rhs.begin(), + rhs.end()); + } + + return false; +} + +bool generics::operator<=(const Monomial& lhs, const Monomial& rhs) noexcept +{ + const auto ldegree = lhs.degree(); + const auto rdegree = rhs.degree(); + + if (ldegree < rdegree) { return true; } + + if (ldegree == rdegree) { + return std::lexicographical_compare(lhs.begin(), + lhs.end(), + rhs.begin(), + rhs.end(), + std::less_equal<>()); + } + + return false; +} + + + +hash_t generics::hash_value(const Monomial& value) +{ + Hash hasher; + return hasher(value.m_data); +} diff --git a/platform/src/generics/polynomial_type/monomial.h b/platform/src/generics/polynomial_type/monomial.h new file mode 100644 index 00000000..64ef60b9 --- /dev/null +++ b/platform/src/generics/polynomial_type/monomial.h @@ -0,0 +1,135 @@ +// +// Created by sam on 26/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_MONOMIAL_H +#define ROUGHPY_GENERICS_INTERNAL_MONOMIAL_H + +#include +#include + +#include +#include + +#include "roughpy/core/debug_assertion.h" +#include "roughpy/core/macros.h" +#include "roughpy/core/hash.h" +#include "roughpy/core/types.h" + + +#include "indeterminate.h" + +namespace rpy { +namespace generics { + + +class Monomial +{ + using map_storage_type = boost::container:: + small_vector, 1>; + using map_type = boost::container:: + flat_map, map_storage_type>; + + map_type m_data; + +public: + + using iterator = typename map_type::iterator; + using const_iterator = typename map_type::const_iterator; + + Monomial() = default; + + explicit Monomial(Indeterminate indeterminate, deg_t degree=1) noexcept + { + m_data.emplace(std::move(indeterminate), std::move(degree)); + } + + explicit Monomial(char prefix, size_t index, deg_t degree=1) noexcept + { + m_data.emplace(Indeterminate(prefix, index), std::move(degree)); + } + + template + explicit Monomial(InputIt begin, InputIt end) + : m_data(begin, end) + {} + + + RPY_NO_DISCARD + deg_t degree() const noexcept; + RPY_NO_DISCARD + deg_t type() const noexcept { return m_data.size(); } + + void clear() noexcept { m_data.clear(); } + RPY_NO_DISCARD bool empty() const noexcept { return m_data.empty(); } + + RPY_NO_DISCARD + iterator begin() noexcept { return m_data.begin(); } + RPY_NO_DISCARD + iterator end() noexcept { return m_data.end(); } + RPY_NO_DISCARD + const_iterator begin() const noexcept { return m_data.begin(); } + RPY_NO_DISCARD + const_iterator end() const noexcept { return m_data.end(); } + + template + auto emplace(Args&&... args) -> decltype(m_data.emplace(std::forward(args)...)) + { + return m_data.emplace(std::forward(args)...); + } + + RPY_NO_DISCARD + deg_t operator[](Indeterminate indeterminate) const noexcept; + RPY_NO_DISCARD + deg_t& operator[](Indeterminate indeterminate) noexcept { + return m_data[indeterminate]; + }; + + Monomial& operator*=(const Monomial& rhs); + + + friend hash_t hash_value(const Monomial& value); + + +}; + + +RPY_NO_DISCARD +hash_t hash_value(const Monomial& value); + +RPY_NO_DISCARD +Monomial operator*(const Monomial& lhs, const Monomial& rhs); +std::ostream &operator<<(std::ostream &os, const Monomial& value); + +RPY_NO_DISCARD +bool operator==(const Monomial& lhs, const Monomial& rhs) noexcept; + +RPY_NO_DISCARD +inline bool operator!=(const Monomial& lhs, const Monomial& rhs) noexcept +{ + return !(lhs == rhs); +} + +RPY_NO_DISCARD +bool operator<(const Monomial& lhs, const Monomial& rhs) noexcept; +RPY_NO_DISCARD +bool operator<=(const Monomial& lhs, const Monomial& rhs) noexcept; + +RPY_NO_DISCARD +inline bool operator>(const Monomial& lhs, const Monomial& rhs) noexcept +{ + return !(lhs <= rhs); +} + +RPY_NO_DISCARD +inline bool operator>=(const Monomial& lhs, const Monomial& rhs) noexcept +{ + return !(lhs < rhs); +} + + + +}// namespace generics +}// namespace rpy + +#endif// ROUGHPY_GENERICS_INTERNAL_MONOMIAL_H diff --git a/platform/src/generics/polynomial_type/polynomial.cpp b/platform/src/generics/polynomial_type/polynomial.cpp new file mode 100644 index 00000000..79bda2d5 --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial.cpp @@ -0,0 +1,152 @@ +// +// Created by sam on 26/11/24. +// + +#include "polynomial.h" + +#include +#include + +#include "roughpy/core/hash.h" + +#include "generics/multiprecision_types/mpq_string_rep.h" +#include "generics/multiprecision_types/mpz_hash.h" + +using namespace rpy; +using namespace rpy::generics; + +deg_t Polynomial::degree() const noexcept +{ + return std::accumulate(begin(), end(), 0, [](auto acc, const auto& pair) { + return std::max(acc, pair.first.degree()); + }); +} + +void generics::poly_add_inplace(Polynomial& lhs, const Polynomial& rhs) +{ + const auto lend = lhs.end(); + dtl::RationalCoeff zero; + for (const auto& [rhs_key, rhs_coeff] : rhs) { + auto it = lhs.find(rhs_key); + if (it != lend) { + // Update existing term + mpq_add(it->second.content, it->second.content, rhs_coeff.content); + // If the resulting coefficient is zero, remove the term + if (mpq_equal(it->second.content, zero.content)) { lhs.erase(it); } + } else { + // Insert new term + lhs.emplace(rhs_key, rhs_coeff); + } + } +} + +void generics::poly_sub_inplace(Polynomial& lhs, const Polynomial& rhs) +{ + const auto lend = lhs.end(); + dtl::RationalCoeff zero; + for (const auto& [rhs_key, rhs_coeff] : rhs) { + auto it = lhs.find(rhs_key); + if (it != lend) { + // Update existing term + mpq_sub(it->second.content, it->second.content, rhs_coeff.content); + // If the resulting coefficient is zero, remove the term + if (mpq_equal(it->second.content, zero.content)) { lhs.erase(it); } + } else { + // Insert new term + auto [pos, inserted] = lhs.emplace(rhs_key, rhs_coeff); + RPY_DBG_ASSERT(inserted); + mpq_neg(pos->second.content, rhs_coeff.content); + } + } +} + +void generics::poly_mul_inplace(Polynomial& lhs, const Polynomial& rhs) +{ + Polynomial result; + dtl::RationalCoeff zero; + + for (const auto& [lhs_key, lhs_coeff] : lhs) { + for (const auto& [rhs_key, rhs_coeff] : rhs) { + auto new_key = lhs_key * rhs_key; + + dtl::RationalCoeff new_coeff; + mpq_mul(new_coeff.content, lhs_coeff.content, rhs_coeff.content); + + auto it = result.find(new_key); + if (it != result.end()) { + mpq_add(it->second.content, + it->second.content, + new_coeff.content); + // If the resulting coefficient is zero, remove the term + if (mpq_equal(it->second.content, zero.content)) { + result.erase(it); + } + } else { + // Insert new term + result.emplace(new_key, new_coeff); + } + } + } + + // Assign result back to lhs + lhs = std::move(result); +} + +void generics::poly_div_inplace(Polynomial& lhs, mpq_srcptr rhs) +{ + dtl::RationalCoeff zero; + if (mpq_equal(rhs, zero.content)) { + RPY_THROW(std::domain_error, "division by zero"); + } + + for (auto it = lhs.begin(); it != lhs.end(); ++it) { + // Divide the coefficient of the current term by rhs + mpq_div(it->second.content, it->second.content, rhs); + } +} +bool generics::poly_cmp_equal( + const Polynomial& lhs, + const Polynomial& rhs +) noexcept +{ + if (lhs.size() != rhs.size()) { return false; } + + return std::equal( + lhs.begin(), + lhs.end(), + rhs.begin(), + rhs.end(), + [](const auto& lhs_pair, const auto& rhs_pair) { + return lhs_pair.first == rhs_pair.first + && mpq_equal( + lhs_pair.second.content, + rhs_pair.second.content + ); + } + ); +} + +hash_t generics::hash_value(const Polynomial& value) +{ + hash_t hash = 0; + for (const auto& [key, coeff] : value) { + hash_combine(hash, hash_value(key)); + hash_combine(hash, mpq_hash(coeff.content)); + } + return hash; +} + +void generics::poly_print(std::ostream& os, const Polynomial& value) +{ + os << '{'; + string tmp_coeff; + for (const auto& [key, coeff] : value) { + tmp_coeff.clear(); + mpq_display_rep(tmp_coeff, coeff.content); + os << ' ' << tmp_coeff; + if (RPY_UNLIKELY(!key.empty())) { + os << '(' << key << ')'; + } + } + os << " }"; +} diff --git a/platform/src/generics/polynomial_type/polynomial.h b/platform/src/generics/polynomial_type/polynomial.h new file mode 100644 index 00000000..577acceb --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial.h @@ -0,0 +1,159 @@ +// +// Created by sam on 26/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_H +#define ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_H + +#include +#include + +#include +#include + +#include "indeterminate.h" +#include "monomial.h" + +namespace rpy { +namespace generics { + +namespace dtl { + +struct RationalCoeff { + mpq_t content; + + RationalCoeff() { mpq_init(content); } + + // Copy constructor + RationalCoeff(const RationalCoeff& other) + { + mpq_init(content); + mpq_set(content, other.content); + } + + // Move constructor + RationalCoeff(RationalCoeff&& other) noexcept + { + mpq_init(content); + mpq_swap(content, other.content); + } + + RationalCoeff(int64_t num, int64_t den) + { + mpq_init(content); + mpq_set_si(content, num, den); + } + + // Copy assignment operator + RationalCoeff& operator=(const RationalCoeff& other) + { + if (this != &other) { mpq_set(content, other.content); } + return *this; + } + + // Move assignment operator + RationalCoeff& operator=(RationalCoeff&& other) noexcept + { + if (this != &other) { + mpq_swap(content, other.content); + mpq_set_si(other.content, 0, 1); + } + return *this; + } + + ~RationalCoeff() { mpq_clear(content); } +}; + +}// namespace dtl + +class Polynomial + : private boost::container::flat_map +{ +public: + using typename flat_map::const_iterator; + using typename flat_map::const_pointer; + using typename flat_map::const_reference; + using typename flat_map::difference_type; + using typename flat_map::iterator; + using typename flat_map::key_type; + using typename flat_map::mapped_type; + using typename flat_map::pointer; + using typename flat_map::reference; + using typename flat_map::size_type; + using typename flat_map::value_type; + + using flat_map::flat_map; + + Polynomial(const Polynomial& other) = default; + Polynomial(Polynomial&& other) noexcept = default; + + explicit Polynomial(dtl::RationalCoeff coeff) + : flat_map({std::make_pair(Monomial(), std::move(coeff))}) + {} + + explicit Polynomial(const Monomial& monomial, dtl::RationalCoeff coeff) + : flat_map({std::make_pair(monomial, std::move(coeff))}) + {} + + explicit Polynomial( + std::initializer_list> list + ) + : flat_map(list) + {} + + Polynomial& operator=(const Polynomial& other) = default; + Polynomial& operator=(Polynomial&& other) noexcept = default; + + using flat_map::begin; + using flat_map::cbegin; + using flat_map::cend; + using flat_map::crbegin; + using flat_map::crend; + using flat_map::end; + using flat_map::erase; + using flat_map::find; + using flat_map::rbegin; + using flat_map::rend; + + using flat_map::emplace; + using flat_map::empty; + using flat_map::size; + using flat_map::clear; + using flat_map::operator[]; + + deg_t degree() const noexcept; +}; + +void poly_add_inplace(Polynomial& lhs, const Polynomial& rhs); +void poly_sub_inplace(Polynomial& lhs, const Polynomial& rhs); +void poly_mul_inplace(Polynomial& lhs, const Polynomial& rhs); +void poly_div_inplace(Polynomial& lhs, mpq_srcptr rhs); +inline void poly_div_inplace(Polynomial& lhs, const dtl::RationalCoeff& rhs) +{ + poly_div_inplace(lhs, rhs.content); +} + +bool poly_cmp_equal(const Polynomial& lhs, const Polynomial& rhs) noexcept; +inline bool poly_cmp_is_zero(const Polynomial& value) { return value.empty(); } + +RPY_NO_DISCARD hash_t hash_value(const Polynomial& value); + +void poly_print(std::ostream& os, const Polynomial& value); + + +inline bool operator==(const Polynomial& lhs, const Polynomial& rhs) noexcept +{ + return poly_cmp_equal(lhs, rhs); +} + +inline std::ostream& operator<<(std::ostream& os, const Polynomial& value) +{ + poly_print(os, value); + return os; +} + +}// namespace generics + +}// namespace rpy + +#endif// ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_H diff --git a/platform/src/generics/polynomial_type/polynomial_arithmetic.cpp b/platform/src/generics/polynomial_type/polynomial_arithmetic.cpp new file mode 100644 index 00000000..5d480a85 --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial_arithmetic.cpp @@ -0,0 +1,61 @@ +// +// Created by sam on 27/11/24. +// + +#include "polynomial_arithmetic.h" + +#include "roughpy/core/check.h" +#include "roughpy/core/debug_assertion.h" + +#include "polynomial.h" + +bool rpy::generics::PolynomialArithmetic:: +has_operation(ArithmeticOperation op) const noexcept { return true; } + +void rpy::generics::PolynomialArithmetic::unsafe_add_inplace(void* lhs, + const void* rhs) const noexcept +{ + RPY_DBG_ASSERT_NE(lhs, nullptr); + RPY_DBG_ASSERT_NE(rhs, nullptr); + + auto* src_ptr = static_cast(rhs); + auto* dst_ptr = static_cast(lhs); + + poly_add_inplace(*dst_ptr, *src_ptr); +} + +void rpy::generics::PolynomialArithmetic::unsafe_sub_inplace(void* lhs, + const void* rhs) const +{ + RPY_DBG_ASSERT_NE(lhs, nullptr); + RPY_DBG_ASSERT_NE(rhs, nullptr); + + auto* src_ptr = static_cast(rhs); + auto* dst_ptr = static_cast(lhs); + + poly_sub_inplace(*dst_ptr, *src_ptr); +} + +void rpy::generics::PolynomialArithmetic::unsafe_mul_inplace(void* lhs, + const void* rhs) const +{ + RPY_DBG_ASSERT_NE(lhs, nullptr); + RPY_DBG_ASSERT_NE(rhs, nullptr); + + auto* src_ptr = static_cast(rhs); + auto* dst_ptr = static_cast(lhs); + + poly_mul_inplace(*dst_ptr, *src_ptr); +} + +void rpy::generics::PolynomialArithmetic::unsafe_div_inplace(void* lhs, + const void* rhs) const +{ + RPY_DBG_ASSERT_NE(lhs, nullptr); + RPY_DBG_ASSERT_NE(rhs, nullptr); + + const auto* src_ptr = static_cast(rhs); + auto* dst_ptr = static_cast(lhs); + + poly_div_inplace(*dst_ptr, src_ptr); +} \ No newline at end of file diff --git a/platform/src/generics/polynomial_type/polynomial_arithmetic.h b/platform/src/generics/polynomial_type/polynomial_arithmetic.h new file mode 100644 index 00000000..3c38a93f --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial_arithmetic.h @@ -0,0 +1,39 @@ +// +// Created by sam on 27/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_ARITHMETIC_H +#define ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_ARITHMETIC_H + +#include "roughpy/core/macros.h" + +#include "roughpy/generics/arithmetic_trait.h" + + +namespace rpy { +namespace generics { + +class PolynomialArithmetic : public ArithmeticTrait { + +public: + + explicit PolynomialArithmetic(const Type* type, const Type* rational_type) + : ArithmeticTrait(type, rational_type) + {} + + RPY_NO_DISCARD bool + has_operation(ArithmeticOperation op) const noexcept override; + + void unsafe_add_inplace(void* lhs, const void* rhs) const noexcept override; + + void unsafe_sub_inplace(void* lhs, const void* rhs) const override; + + void unsafe_mul_inplace(void* lhs, const void* rhs) const override; + + void unsafe_div_inplace(void* lhs, const void* rhs) const override; +}; + +} // generics +} // rpy + +#endif //ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_ARITHMETIC_H diff --git a/platform/src/generics/polynomial_type/polynomial_comparison.cpp b/platform/src/generics/polynomial_type/polynomial_comparison.cpp new file mode 100644 index 00000000..3f17df47 --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial_comparison.cpp @@ -0,0 +1,57 @@ +// +// Created by sam on 27/11/24. +// + +#include "polynomial_comparison.h" + + +#include "roughpy/core/debug_assertion.h" + +#include "polynomial.h" + + + +using namespace rpy; +using namespace rpy::generics; + +bool PolynomialComparison::has_comparison(ComparisonType comp) const noexcept +{ + if (comp == ComparisonType::Equal) { return true; } + return false; +} + +bool PolynomialComparison::unsafe_compare_equal(const void* lhs, + const void* rhs) const noexcept +{ + RPY_DBG_ASSERT_NE(lhs, nullptr); + RPY_DBG_ASSERT_NE(rhs, nullptr); + + auto* lhs_ptr = static_cast(lhs); + auto* rhs_ptr = static_cast(rhs); + + return poly_cmp_equal(*lhs_ptr, *rhs_ptr); +} + +bool PolynomialComparison::unsafe_compare_less(const void* lhs, + const void* rhs) const noexcept +{ + return false; +} + +bool PolynomialComparison::unsafe_compare_less_equal(const void* lhs, + const void* rhs) const noexcept +{ + return false; +} + +bool PolynomialComparison::unsafe_compare_greater(const void* lhs, + const void* rhs) const noexcept +{ + return false; +} + +bool PolynomialComparison::unsafe_compare_greater_equal(const void* lhs, + const void* rhs) const noexcept +{ + return false; +} diff --git a/platform/src/generics/polynomial_type/polynomial_comparison.h b/platform/src/generics/polynomial_type/polynomial_comparison.h new file mode 100644 index 00000000..124c4b52 --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial_comparison.h @@ -0,0 +1,39 @@ +// +// Created by sam on 27/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_COMPARISON_H +#define ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_COMPARISON_H + +#include "roughpy/generics/comparison_trait.h" + +namespace rpy { +namespace generics { + +class PolynomialComparison : public ComparisonTrait { +public: + PolynomialComparison(const Type* type) : ComparisonTrait(type) {} + + RPY_NO_DISCARD bool + has_comparison(ComparisonType comp) const noexcept override; + + RPY_NO_DISCARD bool unsafe_compare_equal(const void* lhs, + const void* rhs) const noexcept override; + + RPY_NO_DISCARD bool unsafe_compare_less(const void* lhs, + const void* rhs) const noexcept override; + + RPY_NO_DISCARD bool unsafe_compare_less_equal(const void* lhs, + const void* rhs) const noexcept override; + + RPY_NO_DISCARD bool unsafe_compare_greater(const void* lhs, + const void* rhs) const noexcept override; + + RPY_NO_DISCARD bool unsafe_compare_greater_equal(const void* lhs, + const void* rhs) const noexcept override; +}; + +} // generics +} // rpy + +#endif //ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_COMPARISON_H diff --git a/platform/src/generics/polynomial_type/polynomial_number.cpp b/platform/src/generics/polynomial_type/polynomial_number.cpp new file mode 100644 index 00000000..ed3d91ad --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial_number.cpp @@ -0,0 +1,31 @@ +// +// Created by sam on 27/11/24. +// + +#include "polynomial_number.h" + + +#include "polynomial.h" + + +using namespace rpy; +using namespace rpy::generics; + +bool PolynomialNumber::has_function(NumberFunction fn_id) const noexcept +{ + if (fn_id == NumberFunction::FromRational) { return true; } + return false; +} + +void PolynomialNumber::unsafe_abs(void* dst, const void* src) const noexcept +{ + // Do nothing, undefined +} + +void PolynomialNumber::unsafe_from_rational(void* dst, + int64_t numerator, + int64_t denominator) const +{ + auto* dst_ptr = static_cast(dst); + *dst_ptr = Polynomial(dtl::RationalCoeff{numerator, denominator}); +} diff --git a/platform/src/generics/polynomial_type/polynomial_number.h b/platform/src/generics/polynomial_type/polynomial_number.h new file mode 100644 index 00000000..8dcbc649 --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial_number.h @@ -0,0 +1,32 @@ +// +// Created by sam on 27/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_NUMBER_H +#define ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_NUMBER_H + +#include "roughpy/generics/number_trait.h" + +namespace rpy { +namespace generics { + +class PolynomialNumber : public NumberTrait { + +public: + explicit PolynomialNumber(const Type* tp) noexcept + : NumberTrait(tp, nullptr) + {} + + bool has_function(NumberFunction fn_id) const noexcept override; + + void unsafe_abs(void* dst, const void* src) const noexcept override; + + void unsafe_from_rational(void* dst, + int64_t numerator, + int64_t denominator) const override; +}; + +} // generics +} // rpy + +#endif //ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_NUMBER_H \ No newline at end of file diff --git a/platform/src/generics/polynomial_type/polynomial_type.cpp b/platform/src/generics/polynomial_type/polynomial_type.cpp new file mode 100644 index 00000000..4895e95e --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial_type.cpp @@ -0,0 +1,290 @@ +// +// Created by sam on 27/11/24. +// + +#include "polynomial_type.h" + +#include +#include +#include + +#include + + +#include +#include + +#include + + +#include "indeterminate.h" +#include "monomial.h" +#include "polynomial.h" + + +using namespace rpy; +using namespace rpy::generics; + +PolynomialType::PolynomialType() + : m_arithmetic(this, MultiPrecisionTypes::get().rational_type.get()), + m_comparison(this), + m_number(this) {} + +void PolynomialType::inc_ref() const noexcept +{ + // do nothing +} + +bool PolynomialType::dec_ref() const noexcept +{ + // Do nothing + return false; +} + +intptr_t PolynomialType::ref_count() const noexcept { return 1; } + +const std::type_info& PolynomialType::type_info() const noexcept +{ + return typeid(Polynomial); +} + +BasicProperties PolynomialType::basic_properties() const noexcept +{ + return {false, false, false, false, false, false, false, false, true, + false, false}; +} + +size_t PolynomialType::object_size() const noexcept +{ + return sizeof(Polynomial); +} + +string_view PolynomialType::name() const noexcept { return "Polynomial"; } + +string_view PolynomialType::id() const noexcept { return "poly"; } + +void* PolynomialType::allocate_object() const +{ + auto* ptr = static_cast(mem::small_object_alloc( + sizeof(Polynomial))); + if (ptr == nullptr) { throw std::bad_alloc(); } + construct_inplace(ptr); + + return ptr; +} + +void PolynomialType::free_object(void* ptr) const +{ + auto* poly = static_cast(ptr); + poly->~Polynomial(); + mem::small_object_free(poly, sizeof(Polynomial)); +} + + +void PolynomialType::copy_or_move(void* dst, + const void* src, + size_t count, + bool move) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + auto* dst_poly = static_cast(dst); + const auto* src_poly = static_cast(src); + + if (src_poly == nullptr) { + for (size_t i = 0; i < count; ++i) { dst_poly[i] = Polynomial(); } + } else if (move) { + auto* modifiable_src = const_cast(src_poly); + for (size_t i = 0; i < count; ++i) { + dst_poly[i] = std::move(modifiable_src[i]); + } + } else { std::copy_n(src_poly, count, dst_poly); } +} + +void PolynomialType::destroy_range(void* data, size_t count) const +{ + RPY_DBG_ASSERT_NE(data, nullptr); + auto* poly = static_cast(data); + std::destroy_n(poly, count); +} + +std::unique_ptr PolynomialType:: +convert_to(const Type& type) const noexcept { return Type::convert_to(type); } + +std::unique_ptr PolynomialType:: +convert_from(const Type& type) const noexcept +{ + return Type::convert_from(type); +} + +const BuiltinTrait* PolynomialType:: +get_builtin_trait(BuiltinTraitID id) const noexcept +{ + switch (id) { + case BuiltinTraitID::Arithmetic: return &m_arithmetic; + case BuiltinTraitID::Comparison: return &m_comparison; + case BuiltinTraitID::Number: return &m_number; + } + RPY_UNREACHABLE_RETURN(nullptr); +} + +const std::ostream& PolynomialType::display(std::ostream& os, + const void* value) const +{ + if (value == nullptr) { return os << "{ }"; } + const auto* poly = static_cast(value); + poly_print(os, *poly); + return os; +} + +hash_t PolynomialType::hash_of(const void* value) const noexcept +{ + if (value == nullptr) { return 0; } + return hash_value(*static_cast(value)); +} + +TypePtr PolynomialType::get() noexcept +{ + static PolynomialType type; + return &type; +} + + +/****************************************************************************** + * Parser * + ******************************************************************************/ + +namespace { + + +template +bool parse_integer(T& value, string_view str) noexcept +{ + + auto parse_result = std::from_chars( + str.data(), + str.data() + str.size(), + value, + 10 + ); + + return parse_result.ec == std::errc(); +} + +bool parse_dbl_coeff(generics::dtl::RationalCoeff& coeff, bool negative, string_view data) noexcept +{ + try { + auto dbl_coeff = std::stod(string(data)); + mpq_set_d(coeff.content, negative ? -dbl_coeff : dbl_coeff); + } catch (std::invalid_argument&) { + return false; + } + return true; +} + +bool parse_rat_coeff(generics::dtl::RationalCoeff& coeff, bool neg, string_view data) noexcept +{ + int64_t numerator; + int64_t denominator = 1; + + constexpr auto rat_pattern = ctll::fixed_string{ + R"((?[1-9]\d*)(?:\/(?[1-9]\d*))?)" + }; + + if (auto match = ctre::match(data)) { + if (auto num = match.get<"num">()) { + if (!parse_integer(numerator, num.view())) { + return false; + } + } + if (auto den = match.get<"den">()) { + if (!parse_integer(denominator, den.view())) { + return false; + } + } + } else { + return false; + } + + mpq_set_si(coeff.content, neg ? -numerator : numerator, denominator); + + return true; +} + + +bool parse_monomial(Monomial& result, string_view monomial_string) noexcept +{ + constexpr auto monomial_pattern = ctll::fixed_string{ + R"(([a-zA-Z])([1-9]\d*)(?:\^([1-9]\d*))?)" + }; + + for (auto match : ctre::search_all(monomial_string)) { + auto prefix = match.get<1>(); + RPY_DBG_ASSERT(prefix); + auto index = match.get<2>(); + RPY_DBG_ASSERT(index); + + uint64_t index_digits = 0; + if (!parse_integer(index_digits, index.view())) { + result.clear(); + return false; + } + + Indeterminate ind(*prefix.begin(), index_digits); + + deg_t pow = 1; + if (auto power = match.get<3>()) { + if (!parse_integer(pow, power.view())) { + result.clear(); + return false; + } + } + + result.emplace(ind, pow); + } + + return true; +} + +} + +bool PolynomialType::parse_from_string(void* data, + string_view str) const noexcept +{ + RPY_DBG_ASSERT_NE(data, nullptr); + + auto* poly = static_cast(data); + + constexpr auto term_pattern = ctll::fixed_string{ + R"((?\+|\-)?(?:(?\d+\.\d*)|(?[1-9]\d*(?:\/[1-9]\d*)?))(?:\((?(?:[a-zA-Z][1-9]\d*(?:\^(?:[1-9]\d*))?)+)\))?)" + }; + + generics::dtl::RationalCoeff tmp_coeff; + for (auto match : ctre::search_all(str)) { + auto sign_m = match.get<"sgn">(); + bool negative = sign_m && *sign_m.begin() == '-'; + + if (auto dbl_grp = match.get<"dbl">()) { + if (!parse_dbl_coeff(tmp_coeff, negative, dbl_grp.view())) { + poly->clear(); + return false; + } + } else if (auto rat_grp = match.get<"rat">()) { + if (!parse_rat_coeff(tmp_coeff, negative, rat_grp.view())) { + poly->clear(); + return false; + } + } + + Monomial mon; + if (auto monomial_grp = match.get<"mon">()) { + if (!parse_monomial(mon, monomial_grp.view())) { + poly->clear(); + return false; + } + } + + poly->emplace(mon, std::move(tmp_coeff)); + } + + return true; +} \ No newline at end of file diff --git a/platform/src/generics/polynomial_type/polynomial_type.h b/platform/src/generics/polynomial_type/polynomial_type.h new file mode 100644 index 00000000..952944ec --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial_type.h @@ -0,0 +1,78 @@ +// +// Created by sam on 27/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_TYPE_H +#define ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_TYPE_H + +#include "roughpy/generics/type.h" + +#include "polynomial_arithmetic.h" +#include "polynomial_comparison.h" +#include "polynomial_number.h" + +namespace rpy { +namespace generics { + +class PolynomialType : public Type { + PolynomialArithmetic m_arithmetic; + PolynomialComparison m_comparison; + PolynomialNumber m_number; + + PolynomialType(); + +protected: + void inc_ref() const noexcept override; + + RPY_NO_DISCARD bool dec_ref() const noexcept override; + +public: + RPY_NO_DISCARD intptr_t ref_count() const noexcept override; + + RPY_NO_DISCARD const std::type_info& type_info() const noexcept override; + + RPY_NO_DISCARD BasicProperties basic_properties() const noexcept override; + + RPY_NO_DISCARD size_t object_size() const noexcept override; + + RPY_NO_DISCARD string_view name() const noexcept override; + + RPY_NO_DISCARD string_view id() const noexcept override; + +protected: + void* allocate_object() const override; + + void free_object(void* ptr) const override; + +public: + bool parse_from_string(void* data, string_view str) const noexcept override; + + void copy_or_move(void* dst, + const void* src, + size_t count, + bool move) const override; + + void destroy_range(void* data, size_t count) const override; + + RPY_NO_DISCARD std::unique_ptr convert_to( + const Type& type) const noexcept override; + + RPY_NO_DISCARD std::unique_ptr convert_from( + const Type& type) const noexcept override; + + RPY_NO_DISCARD const BuiltinTrait* get_builtin_trait( + BuiltinTraitID id) const noexcept override; + + const std::ostream& + display(std::ostream& os, const void* value) const override; + + hash_t hash_of(const void* value) const noexcept override; + + + static TypePtr get() noexcept; +}; + +} // generics +} // rpy + +#endif //ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_TYPE_H diff --git a/platform/src/generics/polynomial_type/test_indeterminate.cpp b/platform/src/generics/polynomial_type/test_indeterminate.cpp new file mode 100644 index 00000000..75cd25c0 --- /dev/null +++ b/platform/src/generics/polynomial_type/test_indeterminate.cpp @@ -0,0 +1,120 @@ +// +// Created by sam on 26/11/24. +// +#include + +#include "indeterminate.h" + + +using namespace rpy; +using namespace rpy::generics; + + +// Test Default Constructor +TEST(TestIndeterminate, TestDefaultConstructor) { + Indeterminate ind; + EXPECT_EQ(ind.prefix(), "x"); + EXPECT_EQ(ind.index(), 0); +} + +// Test Constructor with Integer +TEST(TestIndeterminate, TestConstructorWithInteger) { + Indeterminate ind(123); + EXPECT_EQ(ind.prefix(), "x"); + EXPECT_EQ(ind.index(), 123); +} + +// Test Constructor with Character and Integer +TEST(TestIndeterminate, TestConstructorWithCharAndInteger) { + Indeterminate ind('y', 456); + EXPECT_EQ(ind.prefix(), "y"); + EXPECT_EQ(ind.index(), 456); +} + +// Test Constructor with Character Array and Integer +TEST(TestIndeterminate, TestConstructorWithCharArrayAndInteger) { + Indeterminate ind({'z'}, 789); + EXPECT_EQ(ind.prefix(), "z"); + EXPECT_EQ(ind.index(), 789); +} + +// Test Equality Operator +TEST(TestIndeterminate, TestEqualityOperator) { + Indeterminate ind1('x', 303); + Indeterminate ind2('x', 303); + EXPECT_TRUE(ind1 == ind2); + + Indeterminate ind3('y', 303); + EXPECT_FALSE(ind1 == ind3); + + Indeterminate ind4('x', 304); + EXPECT_FALSE(ind1 == ind4); + + Indeterminate ind5('x', 2304134); + EXPECT_FALSE(ind1 == ind5); +} + +// Test Inequality Operator +TEST(TestIndeterminate, TestInequalityOperator) { + Indeterminate ind1('x', 304); + Indeterminate ind2('x', 304); + EXPECT_FALSE(ind1 != ind2); + + Indeterminate ind3('y', 304); + EXPECT_TRUE(ind1 != ind3); + + Indeterminate ind4('x', 305); + EXPECT_TRUE(ind1 != ind4); + + Indeterminate ind5('x', 2304134); + EXPECT_TRUE(ind1 != ind5); +} + +// Test Less Than Operator +TEST(TestIndeterminate, TestLessThanOperator) { + Indeterminate ind1('x', 606); + Indeterminate ind2('x', 707); + EXPECT_LT(ind1, ind2); +} + +// Test Less Than or Equal Operator +TEST(TestIndeterminate, TestLessThanOrEqualOperator) { + Indeterminate ind1('x', 808); + Indeterminate ind2('x', 808); + EXPECT_LE(ind1, ind2); +} + +// Test Greater Than Operator +TEST(TestIndeterminate, TestGreaterThanOperator) { + Indeterminate ind1('x', 909); + Indeterminate ind2('x', 808); + EXPECT_GT(ind1, ind2); +} + +// Test Greater Than or Equal Operator +TEST(TestIndeterminate, TestGreaterThanOrEqualOperator) { + Indeterminate ind1('x', 1010); + Indeterminate ind2('x', 1010); + EXPECT_GE(ind1, ind2); +} + +TEST(TestIndeterminate, TestHash) +{ + Indeterminate ind1('x', 1); + Indeterminate ind2('x', 1); + EXPECT_EQ(ind1, ind2); + EXPECT_EQ(hash_value(ind1), hash_value(ind2)); + + Indeterminate ind3('x', 2); + EXPECT_NE(hash_value(ind1), hash_value(ind3)); +} + + +TEST(TestIndeterminate, TestDisplay) +{ + Indeterminate ind1('x', 1); + + std::stringstream ss1; + ss1 << ind1; + EXPECT_EQ(ss1.str(), "x1"); +} diff --git a/platform/src/generics/polynomial_type/test_monomial.cpp b/platform/src/generics/polynomial_type/test_monomial.cpp new file mode 100644 index 00000000..2853333c --- /dev/null +++ b/platform/src/generics/polynomial_type/test_monomial.cpp @@ -0,0 +1,52 @@ +// +// Created by sam on 26/11/24. +// + +#include + +#include + + +#include "monomial.h" + +using namespace rpy; +using namespace rpy::generics; + +TEST(TestMonomial, TestEmptyMonomial) +{ + Monomial m; + + EXPECT_EQ(m.degree(), 0); + EXPECT_EQ(m.type(), 0); +} + +TEST(TestMonomial, TestUnidimConstructorDegreeAndType) +{ + Monomial m(Indeterminate('x', 1)); + + EXPECT_EQ(m.degree(), 1); + EXPECT_EQ(m.type(), 1); +} + +TEST(TestMonomial, TestDisplay) +{ + Monomial m1; + Monomial m2(Indeterminate('x', 1)); + Monomial m3(Indeterminate('x', 2), 2); + + std::stringstream ss1; + ss1 << m1; + + EXPECT_EQ(ss1.str(), ""); + + std::stringstream ss2; + ss2 << m2; + EXPECT_EQ(ss2.str(), "x1"); + + std::stringstream ss3; + ss3 << m3; + EXPECT_EQ(ss3.str(), "x2^2"); +} + + + diff --git a/platform/src/generics/polynomial_type/test_polynomial.cpp b/platform/src/generics/polynomial_type/test_polynomial.cpp new file mode 100644 index 00000000..957696b4 --- /dev/null +++ b/platform/src/generics/polynomial_type/test_polynomial.cpp @@ -0,0 +1,186 @@ +// +// Created by sam on 26/11/24. +// + +#include + +#include "polynomial.h" + +using namespace rpy; +using namespace rpy::generics; + + +namespace rpy::generics { + +void PrintTo(const Polynomial& p, std::ostream* os) +{ + poly_print(*os, p); +} + +} + +TEST(TestPolynomial, TestDefaultValue) +{ + Polynomial p; + + EXPECT_TRUE(p.empty()); + EXPECT_EQ(p.degree(), 0); + EXPECT_TRUE(poly_cmp_is_zero(p)); +} + + +TEST(TestPolynomial, TestPolynomialEquality) +{ + Polynomial p1{ + {Monomial(), {1, 1}}, + {Monomial(Indeterminate('x'), 1), {2, 1}} + };// { 1 + 2(x1) } + + Polynomial p2{ + {Monomial(), {1, 1}}, + {Monomial(Indeterminate('x'), 1), {2, 1}} + }; // { 1 + 2(x1) } + + Polynomial p3{ + {Monomial(), {1, 1}}, + {Monomial(Indeterminate('x'), 1), {3, 1}} + }; // { 1 + 3(x1) } + + Polynomial p4{ + {Monomial(), {1, 1}}, + {Monomial(Indeterminate('y'), 1), {2, 1}} + }; // { 1 + 3(x1) } + + EXPECT_TRUE(poly_cmp_equal(p1, p2)); + EXPECT_FALSE(poly_cmp_equal(p1, p3)); + EXPECT_FALSE(poly_cmp_equal(p1, p4)); +} + +TEST(TestPolynomial, TestPolynomialHash) +{ + Polynomial p1{ + {Monomial(), {1, 1}}, + {Monomial(Indeterminate('x'), 1), {2, 1}} + };// { 1 + 2(x1) } + + Polynomial p2{ + {Monomial(), {1, 1}}, + {Monomial(Indeterminate('x'), 1), {2, 1}} + };// { 1 + 2(x1) } + + Polynomial p3{ + {Monomial(), {1, 1}}, + {Monomial(Indeterminate('x'), 1), {3, 1}} + };// { 1 + 3(x1) } + + auto hash1 = Hash{}(p1); + auto hash2 = Hash{}(p2); + auto hash3 = Hash{}(p3); + + EXPECT_EQ(hash1, hash2);// Hashes should be the same for p1 and p2 + EXPECT_NE(hash1, hash3);// Hashes should be different for p1 and p3 +} + +TEST(TestPolynomial, TestPolynomialDisplay) +{ + Polynomial p1; + + std::stringstream ss1; + poly_print(ss1, p1); + + EXPECT_EQ(ss1.str(), "{ }"); + + Polynomial p2 { {Monomial(), {1, 1}}}; + std::stringstream ss2; + poly_print(ss2, p2); + + EXPECT_EQ(ss2.str(), "{ 1 }"); + + Polynomial p3 { {Monomial(), {1, 1}}, {Monomial(Indeterminate('x', 2), 2), {1, 2}}}; + std::stringstream ss3; + poly_print(ss3, p3); + + EXPECT_EQ(ss3.str(), "{ 1 1/2(x2^2) }"); +} + +TEST(TestPolynomial, TestPolynomialAdditionCommonTerms) +{ + Polynomial p1{ + {Monomial(), {1, 1}}, + {Monomial(Indeterminate('x'), 1), {2, 1}} + };// { 1 + 2(x1) } + Polynomial p2{ + {Monomial(Indeterminate('x'), 1), {1, 1}} + };// { 1(x1) } + + poly_add_inplace(p1, p2); + + Polynomial expected { + {Monomial(), {1, 1}}, + {Monomial(Indeterminate('x'), 1), {3, 1}} + }; + + EXPECT_EQ(p1, expected); +} + + +TEST(TestPolynomial, TestPolynomialSubtractionCommonTerms) +{ + Polynomial p1{ + {Monomial(), {1, 1}}, + {Monomial(Indeterminate('x'), 1), {2, 1}} + };// { 1 + 2(x1) } + Polynomial p2{ + {Monomial(Indeterminate('x'), 1), {1, 1}} + };// { 1(x1) } + + poly_sub_inplace(p1, p2); + Polynomial expected { + {Monomial(), {1, 1}}, + {Monomial(Indeterminate('x'), 1), {1, 1}} + }; // { 1 1(x1) } + + EXPECT_EQ(p1, expected); + +} + +TEST(TestPolynomial, TestPolynomialMultiplication) +{ + Polynomial p1{ + {Monomial(), {1, 1}}, + {Monomial('x', 1), {2, 1}} + };// { 1 2(x1) } + Polynomial p2{ + {Monomial('x', 1), {3, 1}} + };// { 3(x1) } + + Polynomial result(p1); + + + poly_mul_inplace(result, p2); + Polynomial expected { + {Monomial('x', 1), {3, 1}}, + {Monomial('x', 1, 2), {6, 1}} + }; // { 3(x1) 6(x1^2) } + + EXPECT_EQ(result, expected) << p1 << ' ' << p2; +} + + +TEST(TestPolynomial, TestDivisonByRational) +{ + Polynomial p1{ + {Monomial(), {1, 1}}, + {Monomial(Indeterminate('x'), 1), {2, 1}} + };// { 1 2(x1) } + + generics::dtl::RationalCoeff r(1, 2); // 1/2 + poly_div_inplace(p1, r); + + Polynomial expected { + {Monomial(), {2, 1}}, + {Monomial(Indeterminate('x'), 1), {4, 1}} + }; // { 2 2(x1) } + + EXPECT_EQ(p1, expected); +} diff --git a/platform/src/generics/polynomial_type/test_polynomial_type.cpp b/platform/src/generics/polynomial_type/test_polynomial_type.cpp new file mode 100644 index 00000000..a5990060 --- /dev/null +++ b/platform/src/generics/polynomial_type/test_polynomial_type.cpp @@ -0,0 +1,255 @@ +// +// Created by sam on 27/11/24. +// +#include + +#include "roughpy/generics/arithmetic_trait.h" +#include "roughpy/generics/builtin_trait.h" +#include "roughpy/generics/comparison_trait.h" +#include "roughpy/generics/number_trait.h" +#include "roughpy/generics/type.h" +#include "roughpy/generics/values.h" + +using namespace rpy; +using namespace rpy::generics; + +namespace { + +class TestPolynomialType : public ::testing::Test +{ +protected: + void SetUp() override + { + polynomial_type = get_polynomial_type(); + } + +public: + const ComparisonTrait* comp_trait() const noexcept + { + return trait_cast( + polynomial_type->get_builtin_trait(BuiltinTraitID::Comparison) + ); + } + + const ArithmeticTrait* arith_trait() const noexcept + { + return trait_cast( + polynomial_type->get_builtin_trait(BuiltinTraitID::Arithmetic) + ); + } + + const NumberTrait* num_trait() const noexcept + { + return trait_cast( + polynomial_type->get_builtin_trait(BuiltinTraitID::Number) + ); + } + + TypePtr polynomial_type; +}; + +}// namespace + +TEST_F(TestPolynomialType, TestID) +{ + EXPECT_EQ(polynomial_type->id(), "poly"); + EXPECT_EQ(polynomial_type->name(), "Polynomial"); +} + +TEST_F(TestPolynomialType, TestRefCounting) +{ + EXPECT_EQ(polynomial_type->ref_count(), 1); + + { + RPY_MAYBE_UNUSED TypePtr new_ref(polynomial_type); + EXPECT_EQ(polynomial_type->ref_count(), 1); + } + + EXPECT_EQ(polynomial_type->ref_count(), 1); +} + +TEST_F(TestPolynomialType, TestBasicProperties) +{ + EXPECT_FALSE(concepts::is_standard_layout(*polynomial_type)); + EXPECT_FALSE(concepts::is_trivially_copyable(*polynomial_type)); + EXPECT_FALSE(concepts::is_trivially_constructible(*polynomial_type)); + EXPECT_FALSE(concepts::is_trivially_default_constructible(*polynomial_type)); + EXPECT_FALSE(concepts::is_trivially_copy_constructible(*polynomial_type)); + EXPECT_FALSE(concepts::is_trivially_copy_assignable(*polynomial_type)); + EXPECT_FALSE(concepts::is_trivially_destructible(*polynomial_type)); + EXPECT_FALSE(concepts::is_polymorphic(*polynomial_type)); + EXPECT_TRUE(concepts::is_signed(*polynomial_type)); + EXPECT_FALSE(concepts::is_unsigned(*polynomial_type)); + EXPECT_FALSE(concepts::is_integral(*polynomial_type)); + EXPECT_FALSE(concepts::is_arithmetic(*polynomial_type)); +} + +TEST_F(TestPolynomialType, TestDisplayAndParseFromString) +{ + Value value(polynomial_type, string_view("{ 15/2 2(x1) 1(x2^2) }")); + std::stringstream ss; + polynomial_type->display(ss, value.data()); + EXPECT_EQ(ss.str(), "{ 15/2 2(x1) 1(x2^2) }"); +} + +TEST_F(TestPolynomialType, TestParseMixedMonomial) +{ + Value value(polynomial_type, string_view("{ 1(x1x2) }")); + std::stringstream ss; + polynomial_type->display(ss, value.data()); + EXPECT_EQ(ss.str(), "{ 1(x1 x2) }"); +} + +TEST_F(TestPolynomialType, TestParseMixedMonomialWithPowers) +{ + Value value(polynomial_type, string_view("{ 1(x1^2x2x3) }")); + std::stringstream ss; + polynomial_type->display(ss, value.data()); + EXPECT_EQ(ss.str(), "{ 1(x1^2 x2 x3) }"); +} + +TEST_F(TestPolynomialType, TestParseFloatCoefficient) +{ + Value value(polynomial_type, string_view("{ 1.5(x1) }")); + std::stringstream ss; + polynomial_type->display(ss, value.data()); + EXPECT_EQ(ss.str(), "{ 3/2(x1) }"); +} + +TEST_F(TestPolynomialType, TestParseNegativeCoefficients) +{ + Value value(polynomial_type, + string_view("{ -1 -1.25(x1) -22/7(x2) }")); + std::stringstream ss; + polynomial_type->display(ss, value.data()); + EXPECT_EQ(ss.str(), "{ -1 -5/4(x1) -22/7(x2) }"); +} + +TEST_F(TestPolynomialType, TestParsePositiveCoefficients) +{ + Value value(polynomial_type, + string_view("{ +1 +1.25(x1) +22/7(x2) }")); + std::stringstream ss; + polynomial_type->display(ss, value.data()); + EXPECT_EQ(ss.str(), "{ 1 5/4(x1) 22/7(x2) }"); +} + +/****************************************************************************** + * Comparison * + ******************************************************************************/ + +TEST_F(TestPolynomialType, TestHasEqualOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + + EXPECT_TRUE(comp->has_comparison(ComparisonType::Equal)); +} + +TEST_F(TestPolynomialType, TestHasLessThanOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_FALSE(comp->has_comparison(ComparisonType::Less)); +} + +TEST_F(TestPolynomialType, TestHasGreaterThanOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_FALSE(comp->has_comparison(ComparisonType::Greater)); +} + +TEST_F(TestPolynomialType, TestHasLessThanOrEqualOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_FALSE(comp->has_comparison(ComparisonType::LessEqual)); +} + +TEST_F(TestPolynomialType, TestHasGreaterThanOrEqualOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_FALSE(comp->has_comparison(ComparisonType::GreaterEqual)); +} + +/****************************************************************************** + * Arithmetic * + ******************************************************************************/ + +TEST_F(TestPolynomialType, TestAddOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Add)); +} + +TEST_F(TestPolynomialType, TestSubtractOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Sub)); +} + +TEST_F(TestPolynomialType, TestMultiplyOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Mul)); +} + +TEST_F(TestPolynomialType, TestDivideOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Div)); // Depending on your polynomial type +} + +/****************************************************************************** + * Number-like * + ******************************************************************************/ + +TEST_F(TestPolynomialType, TestRealAndImaginary) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + + EXPECT_FALSE(num->has_function(NumberFunction::Real)); + EXPECT_FALSE(num->has_function(NumberFunction::Imaginary)); +} + +TEST_F(TestPolynomialType, TestAbsFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + EXPECT_FALSE(num->has_function(NumberFunction::Abs)); +} + +TEST_F(TestPolynomialType, TestSqrtExpLogFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + + EXPECT_FALSE(num->has_function(NumberFunction::Sqrt)); + EXPECT_FALSE(num->has_function(NumberFunction::Exp)); + EXPECT_FALSE(num->has_function(NumberFunction::Log)); +} + +TEST_F(TestPolynomialType, TestPowFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + EXPECT_FALSE(num->has_function(NumberFunction::Pow)); +} + +TEST_F(TestPolynomialType, TestFromRationalFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + EXPECT_TRUE(num->has_function(NumberFunction::FromRational)); +} \ No newline at end of file diff --git a/platform/src/generics/polynomial_types.cpp b/platform/src/generics/polynomial_types.cpp new file mode 100644 index 00000000..8ebde8cf --- /dev/null +++ b/platform/src/generics/polynomial_types.cpp @@ -0,0 +1,13 @@ +// +// Created by sam on 27/11/24. +// + + +#include "roughpy/generics/type.h" + +#include "polynomial_type/polynomial_type.h" + + +rpy::generics::TypePtr rpy::generics::get_polynomial_type() noexcept { + return PolynomialType::get(); +} \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index 2648405c..ac3f00c3 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,97 +1,80 @@ { - "name": "roughpy", - "version-string": "1.0.0", - "dependencies": [ - { - "name": "libsndfile", - "version>=": "1.2.0" - }, - { - "name": "eigen3", - "version>=": "3.4.0#2" - }, - { - "name": "boost-align", - "version>=": "1.83.0" - }, - { - "name": "boost-container", - "version>=": "1.83.0" - }, - { - "name": "boost-core", - "version>=": "1.83.0" - }, - { - "name": "boost-url", - "version>=": "1.83.0" - }, - { - "name": "boost-multiprecision", - "version>=": "1.83.0" - }, - { - "name": "boost-type-traits", - "version>=": "1.83.0" - }, - { - "name": "boost-endian", - "version>=": "1.83.0" - }, - { - "name": "boost-dll", - "version>=": "1.83.0" - }, - { - "name": "pcg", - "version>=": "2021-04-06#2" - }, - { - "name": "cereal", - "version>=": "1.3.2#1" - }, - { - "name": "boost-smart-ptr", - "version>=": "1.83.0" - }, - { - "name": "boost-uuid", - "version>=": "1.83.0" - }, - { - "name": "boost-interprocess", - "version>=": "1.83.0" - }, - { - "name": "opencl", - "version>=": "v2024.05.08" - }, - { - "name": "boost-stacktrace", - "version>=": "1.83.0" - }, - { - "name": "range-v3", - "version>=": "0.12.0#4" - }, - { - "name": "mpfr", - "version>=": "4.2.1" - }, - { - "name": "gmp", - "version>=": "6.3.0#1" - } - ], - "features": { - "tests": { - "description": "dependencies for the unit tests", - "dependencies": [ - { - "name": "gtest", - "version>=": "1.13.0" - } - ] + "name" : "roughpy", + "version-string" : "1.0.0", + "dependencies" : [ { + "name" : "libsndfile", + "version>=" : "1.2.0" + }, { + "name" : "eigen3", + "version>=" : "3.4.0#2" + }, { + "name" : "boost-align", + "version>=" : "1.83.0" + }, { + "name" : "boost-container", + "version>=" : "1.83.0" + }, { + "name" : "boost-core", + "version>=" : "1.83.0" + }, { + "name" : "boost-url", + "version>=" : "1.83.0" + }, { + "name" : "boost-multiprecision", + "version>=" : "1.83.0" + }, { + "name" : "boost-type-traits", + "version>=" : "1.83.0" + }, { + "name" : "boost-endian", + "version>=" : "1.83.0" + }, { + "name" : "boost-dll", + "version>=" : "1.83.0" + }, { + "name" : "pcg", + "version>=" : "2021-04-06#2" + }, { + "name" : "cereal", + "version>=" : "1.3.2#1" + }, { + "name" : "boost-smart-ptr", + "version>=" : "1.83.0" + }, { + "name" : "boost-uuid", + "version>=" : "1.83.0" + }, { + "name" : "boost-interprocess", + "version>=" : "1.83.0" + }, { + "name" : "opencl", + "version>=" : "v2024.05.08" + }, { + "name" : "boost-stacktrace", + "version>=" : "1.83.0" + }, { + "name" : "range-v3", + "version>=" : "0.12.0#4" + }, { + "name" : "mpfr", + "version>=" : "4.2.1" + }, { + "name" : "gmp", + "version>=" : "6.3.0#1" + }, { + "name" : "ctre", + "version>=" : "3.9.0" + }, { + "name" : "fmt", + "version>=" : "11.0.2#1" + } ], + "features" : { + "tests" : { + "description" : "dependencies for the unit tests", + "dependencies" : [ { + "name" : "gtest", + "version>=" : "1.13.0" + } ] } } -} +} \ No newline at end of file