From e9de16a170baf44266baa133bde1173d59426d4f Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Tue, 26 Nov 2024 18:51:47 +0000 Subject: [PATCH 01/27] 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. --- platform/src/generics/CMakeLists.txt | 2 + .../generics/polynomial_type/CMakeLists.txt | 11 ++ .../polynomial_type/indeterminate.cpp | 15 ++ .../generics/polynomial_type/indeterminate.h | 140 ++++++++++++++++++ .../src/generics/polynomial_type/monomial.cpp | 9 ++ .../src/generics/polynomial_type/monomial.h | 78 ++++++++++ 6 files changed, 255 insertions(+) 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 diff --git a/platform/src/generics/CMakeLists.txt b/platform/src/generics/CMakeLists.txt index cd959e6f..0b6c032d 100644 --- a/platform/src/generics/CMakeLists.txt +++ b/platform/src/generics/CMakeLists.txt @@ -28,6 +28,8 @@ target_sources(RoughPy_Platform PRIVATE add_subdirectory(builtin_types) +add_subdirectory(polynomial_type) + target_link_libraries(RoughPy_Platform PRIVATE RoughPy_Generics_BuiltinType ) diff --git a/platform/src/generics/polynomial_type/CMakeLists.txt b/platform/src/generics/polynomial_type/CMakeLists.txt new file mode 100644 index 00000000..90b0679c --- /dev/null +++ b/platform/src/generics/polynomial_type/CMakeLists.txt @@ -0,0 +1,11 @@ + + + + +target_sources(RoughPy_Platform PRIVATE + indeterminate.h + indeterminate.cpp + monomial.cpp + monomial.h +) + diff --git a/platform/src/generics/polynomial_type/indeterminate.cpp b/platform/src/generics/polynomial_type/indeterminate.cpp new file mode 100644 index 00000000..e4007a24 --- /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& 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..b9265d43 --- /dev/null +++ b/platform/src/generics/polynomial_type/indeterminate.h @@ -0,0 +1,140 @@ +// +// 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" + +#include "roughpy/platform/roughpy_platform_export.h" + +namespace rpy::generics { + +class Indeterminate +{ + using base_type = int64_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; + + constexpr Indeterminate() noexcept : m_data(0) {} + + constexpr explicit Indeterminate(base_type integer) noexcept + : m_data(integer & integer_mask) + { + RPY_DBG_ASSERT_LE(integer, integer_mask); + } + + constexpr 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..3f88a07d --- /dev/null +++ b/platform/src/generics/polynomial_type/monomial.cpp @@ -0,0 +1,9 @@ +// +// Created by sam on 26/11/24. +// + +#include "monomial.h" + +#include + + diff --git a/platform/src/generics/polynomial_type/monomial.h b/platform/src/generics/polynomial_type/monomial.h new file mode 100644 index 00000000..506f957e --- /dev/null +++ b/platform/src/generics/polynomial_type/monomial.h @@ -0,0 +1,78 @@ +// +// 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(indeterminate, degree); + } + + template + explicit Monomial(InputIt begin, InputIt end) + : m_data(begin, end) + {} + + + deg_t degree() const noexcept; + deg_t type() const noexcept { return m_data.size(); } + + iterator begin() noexcept { return m_data.begin(); } + iterator end() noexcept { return m_data.end(); } + const_iterator begin() const noexcept { return m_data.begin(); } + const_iterator end() const noexcept { return m_data.end(); } + + + Monomial& operator*=(const Monomial& rhs); + + + friend hash_t hash_value(const Monomial& value); + +}; + + +hash_t hash_value(const Monomial& value); +Monomial operator*(const Monomial& lhs, const Monomial& rhs); +std::ostream &operator<<(std::ostream &os, const Monomial& value); + + + +}// namespace generics +}// namespace rpy + +#endif// ROUGHPY_GENERICS_INTERNAL_MONOMIAL_H From 224f845bcb97644c13af41c1d81c024d1900c290 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Tue, 26 Nov 2024 20:13:59 +0000 Subject: [PATCH 02/27] 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. --- .../polynomial_type/indeterminate.cpp | 2 +- .../generics/polynomial_type/indeterminate.h | 4 +- .../src/generics/polynomial_type/monomial.cpp | 104 ++++++++++++++++++ .../src/generics/polynomial_type/monomial.h | 30 ++++- 4 files changed, 135 insertions(+), 5 deletions(-) diff --git a/platform/src/generics/polynomial_type/indeterminate.cpp b/platform/src/generics/polynomial_type/indeterminate.cpp index e4007a24..5d669cb5 100644 --- a/platform/src/generics/polynomial_type/indeterminate.cpp +++ b/platform/src/generics/polynomial_type/indeterminate.cpp @@ -10,6 +10,6 @@ using namespace rpy; using namespace rpy::generics; -std::ostream& operator<<(std::ostream& os, const Indeterminate& value) { +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 index b9265d43..33935d7f 100644 --- a/platform/src/generics/polynomial_type/indeterminate.h +++ b/platform/src/generics/polynomial_type/indeterminate.h @@ -45,13 +45,13 @@ class Indeterminate constexpr Indeterminate() noexcept : m_data(0) {} - constexpr explicit Indeterminate(base_type integer) noexcept + explicit Indeterminate(base_type integer) noexcept : m_data(integer & integer_mask) { RPY_DBG_ASSERT_LE(integer, integer_mask); } - constexpr explicit Indeterminate(char prefix, base_type integer) noexcept + explicit Indeterminate(char prefix, base_type integer) noexcept { RPY_DBG_ASSERT_LE(integer, integer_mask); diff --git a/platform/src/generics/polynomial_type/monomial.cpp b/platform/src/generics/polynomial_type/monomial.cpp index 3f88a07d..31e7dba1 100644 --- a/platform/src/generics/polynomial_type/monomial.cpp +++ b/platform/src/generics/polynomial_type/monomial.cpp @@ -4,6 +4,110 @@ #include "monomial.h" +#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 index 506f957e..e5c33049 100644 --- a/platform/src/generics/polynomial_type/monomial.h +++ b/platform/src/generics/polynomial_type/monomial.h @@ -25,7 +25,7 @@ namespace generics { class Monomial { using map_storage_type = boost::container:: - small_vector, 1>; + small_vector, 1>; using map_type = boost::container:: flat_map, map_storage_type>; @@ -40,7 +40,7 @@ class Monomial explicit Monomial(Indeterminate indeterminate, deg_t degree=1) noexcept { - m_data.emplace(indeterminate, degree); + m_data.emplace(std::move(indeterminate), std::move(degree)); } template @@ -58,11 +58,17 @@ class Monomial const_iterator end() const noexcept { return m_data.end(); } + deg_t operator[](Indeterminate indeterminate) const noexcept; + deg_t& operator[](Indeterminate indeterminate) noexcept { + return m_data[indeterminate]; + }; + Monomial& operator*=(const Monomial& rhs); friend hash_t hash_value(const Monomial& value); + }; @@ -70,6 +76,26 @@ hash_t hash_value(const Monomial& value); Monomial operator*(const Monomial& lhs, const Monomial& rhs); std::ostream &operator<<(std::ostream &os, const Monomial& value); +bool operator==(const Monomial& lhs, const Monomial& rhs) noexcept; + +inline bool operator!=(const Monomial& lhs, const Monomial& rhs) noexcept +{ + return !(lhs == rhs); +} + +bool operator<(const Monomial& lhs, const Monomial& rhs) noexcept; +bool operator<=(const Monomial& lhs, const Monomial& rhs) noexcept; + +inline bool operator>(const Monomial& lhs, const Monomial& rhs) noexcept +{ + return !(lhs <= rhs); +} + +inline bool operator>=(const Monomial& lhs, const Monomial& rhs) noexcept +{ + return !(lhs < rhs); +} + }// namespace generics From 2aac4db757040693b24f80e6385c366a1a920dd7 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Tue, 26 Nov 2024 20:19:05 +0000 Subject: [PATCH 03/27] 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. --- platform/src/generics/polynomial_type/monomial.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/src/generics/polynomial_type/monomial.cpp b/platform/src/generics/polynomial_type/monomial.cpp index 31e7dba1..f453ba14 100644 --- a/platform/src/generics/polynomial_type/monomial.cpp +++ b/platform/src/generics/polynomial_type/monomial.cpp @@ -1,4 +1,4 @@ -// + zr// // Created by sam on 26/11/24. // From 734f5004f8f06b3b0398e53448d5d505cb873b7e Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Tue, 26 Nov 2024 20:19:20 +0000 Subject: [PATCH 04/27] 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. --- platform/src/generics/polynomial_type/monomial.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/src/generics/polynomial_type/monomial.cpp b/platform/src/generics/polynomial_type/monomial.cpp index f453ba14..31e7dba1 100644 --- a/platform/src/generics/polynomial_type/monomial.cpp +++ b/platform/src/generics/polynomial_type/monomial.cpp @@ -1,4 +1,4 @@ - zr// +// // Created by sam on 26/11/24. // From a0d71fdb587ea2b34b228a3d515c780e626f13b6 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Tue, 26 Nov 2024 22:15:29 +0000 Subject: [PATCH 05/27] 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. --- .../multiprecision_types/mpq_string_rep.h | 32 +++++++++++++++++++ .../generics/multiprecision_types/mpz_hash.h | 9 ++++++ .../multiprecision_types/rational_type.cpp | 18 ++--------- 3 files changed, 44 insertions(+), 15 deletions(-) create mode 100644 platform/src/generics/multiprecision_types/mpq_string_rep.h 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 From 2b8559de6211c94cbc145117299096da565342f0 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Tue, 26 Nov 2024 22:15:40 +0000 Subject: [PATCH 06/27] 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`. --- .../generics/polynomial_type/CMakeLists.txt | 2 + .../src/generics/polynomial_type/monomial.h | 17 +++ .../generics/polynomial_type/polynomial.cpp | 139 ++++++++++++++++++ .../src/generics/polynomial_type/polynomial.h | 131 +++++++++++++++++ 4 files changed, 289 insertions(+) create mode 100644 platform/src/generics/polynomial_type/polynomial.cpp create mode 100644 platform/src/generics/polynomial_type/polynomial.h diff --git a/platform/src/generics/polynomial_type/CMakeLists.txt b/platform/src/generics/polynomial_type/CMakeLists.txt index 90b0679c..bcc72ea6 100644 --- a/platform/src/generics/polynomial_type/CMakeLists.txt +++ b/platform/src/generics/polynomial_type/CMakeLists.txt @@ -7,5 +7,7 @@ target_sources(RoughPy_Platform PRIVATE indeterminate.cpp monomial.cpp monomial.h + polynomial.cpp + polynomial.h ) diff --git a/platform/src/generics/polynomial_type/monomial.h b/platform/src/generics/polynomial_type/monomial.h index e5c33049..6e9c4f34 100644 --- a/platform/src/generics/polynomial_type/monomial.h +++ b/platform/src/generics/polynomial_type/monomial.h @@ -49,16 +49,24 @@ class Monomial {} + RPY_NO_DISCARD deg_t degree() const noexcept; + RPY_NO_DISCARD deg_t type() const noexcept { return m_data.size(); } + 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(); } + RPY_NO_DISCARD deg_t operator[](Indeterminate indeterminate) const noexcept; + RPY_NO_DISCARD deg_t& operator[](Indeterminate indeterminate) noexcept { return m_data[indeterminate]; }; @@ -66,31 +74,40 @@ class Monomial Monomial& operator*=(const Monomial& rhs); + RPY_NO_DISCARD 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); diff --git a/platform/src/generics/polynomial_type/polynomial.cpp b/platform/src/generics/polynomial_type/polynomial.cpp new file mode 100644 index 00000000..9c52bcdb --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial.cpp @@ -0,0 +1,139 @@ +// +// Created by sam on 26/11/24. +// + +#include "polynomial.h" + +#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; + +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, const dtl::RationalCoeff& rhs) +{ + dtl::RationalCoeff zero; + if (mpq_equal(rhs.content, zero.content)) { + RPY_THROW(std::domain_error, "division by zero"); + } + + for (auto it = lhs.begin(); it != lhs.end();) { + // Divide the coefficient of the current term by rhs + mpq_div(it->second.content, it->second.content, rhs.content); + } +} +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 << '(' << 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..79c5683e --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial.h @@ -0,0 +1,131 @@ +// +// Created by sam on 26/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_H +#define ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_H + +#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); + mpq_clear(other.content); + } + + // 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; + + 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))}) + {} + + 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::size; + using flat_map::empty; + using flat_map::emplace; + using flat_map::operator[]; +}; + +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, const dtl::RationalCoeff& rhs); + +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); + + + +}// namespace generics + +}// namespace rpy + +#endif// ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_H From 03bd13bc4d3a7852c34aed6774a9bc99cbe74301 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Tue, 26 Nov 2024 22:41:19 +0000 Subject: [PATCH 07/27] 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. --- .../generics/polynomial_type/CMakeLists.txt | 28 ++++++++++ .../generics/polynomial_type/indeterminate.h | 3 +- .../polynomial_type/test_monomial.cpp | 52 +++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 platform/src/generics/polynomial_type/test_monomial.cpp diff --git a/platform/src/generics/polynomial_type/CMakeLists.txt b/platform/src/generics/polynomial_type/CMakeLists.txt index bcc72ea6..96bb1f09 100644 --- a/platform/src/generics/polynomial_type/CMakeLists.txt +++ b/platform/src/generics/polynomial_type/CMakeLists.txt @@ -11,3 +11,31 @@ target_sources(RoughPy_Platform PRIVATE polynomial.h ) + +if (ROUGHPY_BUILD_TESTS) + + add_executable(test_polynomial + test_monomial.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) + + +endif() \ No newline at end of file diff --git a/platform/src/generics/polynomial_type/indeterminate.h b/platform/src/generics/polynomial_type/indeterminate.h index 33935d7f..e1dc163e 100644 --- a/platform/src/generics/polynomial_type/indeterminate.h +++ b/platform/src/generics/polynomial_type/indeterminate.h @@ -15,7 +15,6 @@ #include "roughpy/core/traits.h" #include "roughpy/core/types.h" -#include "roughpy/platform/roughpy_platform_export.h" namespace rpy::generics { @@ -85,7 +84,7 @@ class Indeterminate return result; } - base_type index() const noexcept { return m_data & (~integer_mask); } + base_type index() const noexcept { return m_data & integer_mask; } friend constexpr bool operator==(const Indeterminate& lhs, const Indeterminate& rhs) noexcept 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"); +} + + + From 28584e1eb91f6951acee9f04f41a6b56413cc332 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Tue, 26 Nov 2024 23:03:35 +0000 Subject: [PATCH 08/27] 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. --- .../generics/polynomial_type/CMakeLists.txt | 2 + .../generics/polynomial_type/indeterminate.h | 8 +- .../src/generics/polynomial_type/monomial.h | 3 +- .../polynomial_type/test_indeterminate.cpp | 120 ++++++++++++++++++ .../polynomial_type/test_polynomial.cpp | 11 ++ 5 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 platform/src/generics/polynomial_type/test_indeterminate.cpp create mode 100644 platform/src/generics/polynomial_type/test_polynomial.cpp diff --git a/platform/src/generics/polynomial_type/CMakeLists.txt b/platform/src/generics/polynomial_type/CMakeLists.txt index 96bb1f09..44904bca 100644 --- a/platform/src/generics/polynomial_type/CMakeLists.txt +++ b/platform/src/generics/polynomial_type/CMakeLists.txt @@ -15,7 +15,9 @@ target_sources(RoughPy_Platform PRIVATE if (ROUGHPY_BUILD_TESTS) add_executable(test_polynomial + test_indeterminate.cpp test_monomial.cpp + test_polynomial.cpp indeterminate.cpp indeterminate.h monomial.cpp diff --git a/platform/src/generics/polynomial_type/indeterminate.h b/platform/src/generics/polynomial_type/indeterminate.h index e1dc163e..d1a97414 100644 --- a/platform/src/generics/polynomial_type/indeterminate.h +++ b/platform/src/generics/polynomial_type/indeterminate.h @@ -42,10 +42,14 @@ class Indeterminate static constexpr base_type integer_mask = (static_cast(1) << char_shift) - 1; - constexpr Indeterminate() noexcept : m_data(0) {} + 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(integer & integer_mask) + : m_data(default_value | (integer & integer_mask)) { RPY_DBG_ASSERT_LE(integer, integer_mask); } diff --git a/platform/src/generics/polynomial_type/monomial.h b/platform/src/generics/polynomial_type/monomial.h index 6e9c4f34..ad99ef42 100644 --- a/platform/src/generics/polynomial_type/monomial.h +++ b/platform/src/generics/polynomial_type/monomial.h @@ -22,6 +22,7 @@ namespace rpy { namespace generics { + class Monomial { using map_storage_type = boost::container:: @@ -74,7 +75,6 @@ class Monomial Monomial& operator*=(const Monomial& rhs); - RPY_NO_DISCARD friend hash_t hash_value(const Monomial& value); @@ -83,6 +83,7 @@ class Monomial 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); 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_polynomial.cpp b/platform/src/generics/polynomial_type/test_polynomial.cpp new file mode 100644 index 00000000..883d52cf --- /dev/null +++ b/platform/src/generics/polynomial_type/test_polynomial.cpp @@ -0,0 +1,11 @@ +// +// Created by sam on 26/11/24. +// + +#include + +#include "polynomial.h" + +using namespace rpy; +using namespace rpy::generics; + From bda7337e2e6eb3a7bd81f5c851f0bba450e75574 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 09:55:04 +0000 Subject: [PATCH 09/27] 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, --- .../generics/polynomial_type/polynomial.cpp | 17 ++++++++-- .../src/generics/polynomial_type/polynomial.h | 33 ++++++++++++++----- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/platform/src/generics/polynomial_type/polynomial.cpp b/platform/src/generics/polynomial_type/polynomial.cpp index 9c52bcdb..01db9e34 100644 --- a/platform/src/generics/polynomial_type/polynomial.cpp +++ b/platform/src/generics/polynomial_type/polynomial.cpp @@ -4,6 +4,9 @@ #include "polynomial.h" +#include +#include + #include "roughpy/core/hash.h" #include "generics/multiprecision_types/mpq_string_rep.h" @@ -12,6 +15,13 @@ 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(); @@ -89,7 +99,7 @@ void generics::poly_div_inplace(Polynomial& lhs, const dtl::RationalCoeff& rhs) RPY_THROW(std::domain_error, "division by zero"); } - for (auto it = lhs.begin(); it != lhs.end();) { + 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.content); } @@ -133,7 +143,10 @@ void generics::poly_print(std::ostream& os, const Polynomial& value) for (const auto& [key, coeff] : value) { tmp_coeff.clear(); mpq_display_rep(tmp_coeff, coeff.content); - os << ' ' << tmp_coeff << '(' << key << ')'; + 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 index 79c5683e..9ab7abd8 100644 --- a/platform/src/generics/polynomial_type/polynomial.h +++ b/platform/src/generics/polynomial_type/polynomial.h @@ -6,6 +6,7 @@ #define ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_H #include +#include #include #include @@ -35,7 +36,12 @@ struct RationalCoeff { { mpq_init(content); mpq_swap(content, other.content); - mpq_clear(other.content); + } + + RationalCoeff(int64_t num, int64_t den) + { + mpq_init(content); + mpq_set_si(content, num, den); } // Copy assignment operator @@ -86,6 +92,12 @@ class Polynomial : 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; @@ -100,10 +112,12 @@ class Polynomial using flat_map::rbegin; using flat_map::rend; - using flat_map::size; - using flat_map::empty; using flat_map::emplace; + using flat_map::empty; + using flat_map::size; using flat_map::operator[]; + + deg_t degree() const noexcept; }; void poly_add_inplace(Polynomial& lhs, const Polynomial& rhs); @@ -112,17 +126,18 @@ void poly_mul_inplace(Polynomial& lhs, const Polynomial& rhs); void poly_div_inplace(Polynomial& lhs, const dtl::RationalCoeff& rhs); bool poly_cmp_equal(const Polynomial& lhs, const Polynomial& rhs) noexcept; -inline bool poly_cmp_is_zero(const Polynomial& value) -{ - return value.empty(); -} +inline bool poly_cmp_is_zero(const Polynomial& value) { return value.empty(); } -RPY_NO_DISCARD -hash_t hash_value(const Polynomial& value); +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); +} + }// namespace generics From 9bff9aa5ccab1af894ced9ba887b252e6baa665f Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 09:55:08 +0000 Subject: [PATCH 10/27] 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. --- platform/src/generics/polynomial_type/monomial.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/platform/src/generics/polynomial_type/monomial.h b/platform/src/generics/polynomial_type/monomial.h index ad99ef42..d68f27c1 100644 --- a/platform/src/generics/polynomial_type/monomial.h +++ b/platform/src/generics/polynomial_type/monomial.h @@ -44,6 +44,11 @@ class Monomial 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) @@ -55,6 +60,8 @@ class Monomial RPY_NO_DISCARD deg_t type() const noexcept { return m_data.size(); } + RPY_NO_DISCARD bool empty() const noexcept { return m_data.empty(); } + RPY_NO_DISCARD iterator begin() noexcept { return m_data.begin(); } RPY_NO_DISCARD From ed84985a4e98b0fad48f85ace38037c8ef0e9fa3 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 09:55:14 +0000 Subject: [PATCH 11/27] 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. --- .../polynomial_type/test_polynomial.cpp | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/platform/src/generics/polynomial_type/test_polynomial.cpp b/platform/src/generics/polynomial_type/test_polynomial.cpp index 883d52cf..b657ea86 100644 --- a/platform/src/generics/polynomial_type/test_polynomial.cpp +++ b/platform/src/generics/polynomial_type/test_polynomial.cpp @@ -9,3 +9,123 @@ 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); +} + + +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(Indeterminate('x'), 1), {2, 1}} + };// { 1 2(x1) } + Polynomial p2{ + {Monomial(Indeterminate('x'), 1), {3, 1}} + };// { 1(x1) } + + + poly_mul_inplace(p1, p2); + Polynomial expected { + {Monomial('x', 1), {1, 1}}, + {Monomial('x', 1, 2), {1, 1}} + }; // { 1(x1) 2(x1^2) } + + EXPECT_EQ(p1, expected); +} + + +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); +} From 89d0fc56a61c72f235e8a83b7f49c21410ff8a4e Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 10:35:09 +0000 Subject: [PATCH 12/27] 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. --- platform/src/generics/polynomial_type/polynomial.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/platform/src/generics/polynomial_type/polynomial.h b/platform/src/generics/polynomial_type/polynomial.h index 9ab7abd8..903c771a 100644 --- a/platform/src/generics/polynomial_type/polynomial.h +++ b/platform/src/generics/polynomial_type/polynomial.h @@ -84,6 +84,9 @@ class Polynomial 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))}) {} @@ -138,6 +141,11 @@ 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 From 87325014521557139fa4f78925433a262ed1bd05 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 10:35:12 +0000 Subject: [PATCH 13/27] 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. --- .../polynomial_type/test_polynomial.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/platform/src/generics/polynomial_type/test_polynomial.cpp b/platform/src/generics/polynomial_type/test_polynomial.cpp index b657ea86..54aa98b6 100644 --- a/platform/src/generics/polynomial_type/test_polynomial.cpp +++ b/platform/src/generics/polynomial_type/test_polynomial.cpp @@ -95,20 +95,22 @@ TEST(TestPolynomial, TestPolynomialMultiplication) { Polynomial p1{ {Monomial(), {1, 1}}, - {Monomial(Indeterminate('x'), 1), {2, 1}} + {Monomial('x', 1), {2, 1}} };// { 1 2(x1) } Polynomial p2{ - {Monomial(Indeterminate('x'), 1), {3, 1}} - };// { 1(x1) } + {Monomial('x', 1), {3, 1}} + };// { 3(x1) } + Polynomial result(p1); - poly_mul_inplace(p1, p2); + + poly_mul_inplace(result, p2); Polynomial expected { - {Monomial('x', 1), {1, 1}}, - {Monomial('x', 1, 2), {1, 1}} - }; // { 1(x1) 2(x1^2) } + {Monomial('x', 1), {3, 1}}, + {Monomial('x', 1, 2), {6, 1}} + }; // { 3(x1) 6(x1^2) } - EXPECT_EQ(p1, expected); + EXPECT_EQ(result, expected) << p1 << ' ' << p2; } From 940cd957717a1b4316b734e22ef348f696a25ab0 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 10:41:35 +0000 Subject: [PATCH 14/27] 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. --- .../polynomial_type/test_polynomial.cpp | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/platform/src/generics/polynomial_type/test_polynomial.cpp b/platform/src/generics/polynomial_type/test_polynomial.cpp index 54aa98b6..957696b4 100644 --- a/platform/src/generics/polynomial_type/test_polynomial.cpp +++ b/platform/src/generics/polynomial_type/test_polynomial.cpp @@ -25,9 +25,62 @@ TEST(TestPolynomial, TestDefaultValue) 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; From 743a52a3c49bc7423717c3657ba4d0da8b0b5582 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 11:30:23 +0000 Subject: [PATCH 15/27] 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. --- platform/include/roughpy/generics/type.h | 82 ++++--- .../generics/polynomial_type/CMakeLists.txt | 43 ++-- .../polynomial_type/polynomial_arithmetic.cpp | 10 + .../polynomial_type/polynomial_arithmetic.h | 18 ++ .../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 | 155 +++++++++++++ .../polynomial_type/polynomial_type.h | 70 ++++++ .../polynomial_type/test_polynomial_type.cpp | 215 ++++++++++++++++++ platform/src/generics/polynomial_types.cpp | 13 ++ 12 files changed, 707 insertions(+), 58 deletions(-) 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_polynomial_type.cpp create mode 100644 platform/src/generics/polynomial_types.cpp 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/polynomial_type/CMakeLists.txt b/platform/src/generics/polynomial_type/CMakeLists.txt index 44904bca..3009fea7 100644 --- a/platform/src/generics/polynomial_type/CMakeLists.txt +++ b/platform/src/generics/polynomial_type/CMakeLists.txt @@ -3,27 +3,35 @@ target_sources(RoughPy_Platform PRIVATE - indeterminate.h - indeterminate.cpp - monomial.cpp - monomial.h + 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 ) 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 + 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 @@ -39,5 +47,12 @@ if (ROUGHPY_BUILD_TESTS) 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 +endif () \ No newline at end of file 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..99cd2154 --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial_arithmetic.cpp @@ -0,0 +1,10 @@ +// +// Created by sam on 27/11/24. +// + +#include "polynomial_arithmetic.h" + +namespace rpy { +namespace generics { +} // generics +} // rpy \ 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..121c35e6 --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial_arithmetic.h @@ -0,0 +1,18 @@ +// +// Created by sam on 27/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_ARITHMETIC_H +#define ROUGHPY_GENERICS_INTERNAL_POLYNOMIAL_ARITHMETIC_H + +namespace rpy { +namespace generics { + +class PolynomialArithmetic { + +}; + +} // 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..acfd392e --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial_type.cpp @@ -0,0 +1,155 @@ +// +// Created by sam on 27/11/24. +// + +#include "polynomial_type.h" + +#include +#include + + +#include +#include + +#include + +#include "indeterminate.h" +#include "monomial.h" +#include "polynomial.h" + + +using namespace rpy; +using namespace rpy::generics; + +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, false, + 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)); +} + +bool PolynomialType::parse_from_string(void* data, + string_view str) const noexcept +{ + return Type::parse_from_string(data, str); +} + +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 +{ + return Type::get_builtin_trait(id); +} + +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; +} 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..9b3b8345 --- /dev/null +++ b/platform/src/generics/polynomial_type/polynomial_type.h @@ -0,0 +1,70 @@ +// +// 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" + +namespace rpy { +namespace generics { + +class PolynomialType : public Type { + + +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_polynomial_type.cpp b/platform/src/generics/polynomial_type/test_polynomial_type.cpp new file mode 100644 index 00000000..7e90c2a1 --- /dev/null +++ b/platform/src/generics/polynomial_type/test_polynomial_type.cpp @@ -0,0 +1,215 @@ +// +// 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(), "polynomial_id"); // Adjust ID accordingly + 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_EQ(polynomial_type->object_size(), sizeof(void*)); // Adjust if necessary + + 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("x^2+3*x+2")); // Example string + std::stringstream ss; + polynomial_type->display(ss, value.data()); + EXPECT_EQ(ss.str(), "x^2+3*x+2"); // Example string +} + +/****************************************************************************** + * 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_FALSE(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_FALSE(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 From c53443ac2e38c60cce2e57b5f074da9b588cd2a6 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 11:37:46 +0000 Subject: [PATCH 16/27] 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. --- platform/src/generics/polynomial_type/polynomial.cpp | 6 +++--- platform/src/generics/polynomial_type/polynomial.h | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/platform/src/generics/polynomial_type/polynomial.cpp b/platform/src/generics/polynomial_type/polynomial.cpp index 01db9e34..79bda2d5 100644 --- a/platform/src/generics/polynomial_type/polynomial.cpp +++ b/platform/src/generics/polynomial_type/polynomial.cpp @@ -92,16 +92,16 @@ void generics::poly_mul_inplace(Polynomial& lhs, const Polynomial& rhs) lhs = std::move(result); } -void generics::poly_div_inplace(Polynomial& lhs, const dtl::RationalCoeff& rhs) +void generics::poly_div_inplace(Polynomial& lhs, mpq_srcptr rhs) { dtl::RationalCoeff zero; - if (mpq_equal(rhs.content, zero.content)) { + 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.content); + mpq_div(it->second.content, it->second.content, rhs); } } bool generics::poly_cmp_equal( diff --git a/platform/src/generics/polynomial_type/polynomial.h b/platform/src/generics/polynomial_type/polynomial.h index 903c771a..d775853b 100644 --- a/platform/src/generics/polynomial_type/polynomial.h +++ b/platform/src/generics/polynomial_type/polynomial.h @@ -126,7 +126,11 @@ class Polynomial 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, const dtl::RationalCoeff& 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(); } From d14f473591ae2dffe5f349dfc84a319cd42def22 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 11:37:50 +0000 Subject: [PATCH 17/27] 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. --- .../polynomial_type/polynomial_arithmetic.cpp | 59 +++++++++++++++++-- .../polynomial_type/polynomial_arithmetic.h | 23 +++++++- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/platform/src/generics/polynomial_type/polynomial_arithmetic.cpp b/platform/src/generics/polynomial_type/polynomial_arithmetic.cpp index 99cd2154..5d480a85 100644 --- a/platform/src/generics/polynomial_type/polynomial_arithmetic.cpp +++ b/platform/src/generics/polynomial_type/polynomial_arithmetic.cpp @@ -4,7 +4,58 @@ #include "polynomial_arithmetic.h" -namespace rpy { -namespace generics { -} // generics -} // rpy \ No newline at end of file +#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 index 121c35e6..3c38a93f 100644 --- a/platform/src/generics/polynomial_type/polynomial_arithmetic.h +++ b/platform/src/generics/polynomial_type/polynomial_arithmetic.h @@ -5,11 +5,32 @@ #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 { +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 From c1192c21e72c506cb54bc17bc6976937e73bf51f Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 11:39:15 +0000 Subject: [PATCH 18/27] 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. --- platform/src/generics/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/src/generics/CMakeLists.txt b/platform/src/generics/CMakeLists.txt index db7bee45..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 From 3e7c31158aa4e80412ff7516d24831a0a6e4328c Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 11:45:32 +0000 Subject: [PATCH 19/27] 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. --- .../polynomial_type/polynomial_type.cpp | 37 ++++++------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/platform/src/generics/polynomial_type/polynomial_type.cpp b/platform/src/generics/polynomial_type/polynomial_type.cpp index acfd392e..43e0ae4d 100644 --- a/platform/src/generics/polynomial_type/polynomial_type.cpp +++ b/platform/src/generics/polynomial_type/polynomial_type.cpp @@ -41,7 +41,7 @@ const std::type_info& PolynomialType::type_info() const noexcept BasicProperties PolynomialType::basic_properties() const noexcept { - return {false, false, false, false, false, false, false, false, false, + return {false, false, false, false, false, false, false, false, true, false, false}; } @@ -50,22 +50,15 @@ size_t PolynomialType::object_size() const noexcept return sizeof(Polynomial); } -string_view PolynomialType::name() const noexcept -{ - return "Polynomial"; -} +string_view PolynomialType::name() const noexcept { return "Polynomial"; } -string_view PolynomialType::id() const noexcept -{ - return "poly"; -} +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(); - } + auto* ptr = static_cast(mem::small_object_alloc( + sizeof(Polynomial))); + if (ptr == nullptr) { throw std::bad_alloc(); } construct_inplace(ptr); return ptr; @@ -94,17 +87,13 @@ void PolynomialType::copy_or_move(void* dst, const auto* src_poly = static_cast(src); if (src_poly == nullptr) { - for (size_t i = 0; i < count; ++i) { - dst_poly[i] = Polynomial(); - } + 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); - } + } else { std::copy_n(src_poly, count, dst_poly); } } void PolynomialType::destroy_range(void* data, size_t count) const @@ -132,9 +121,7 @@ get_builtin_trait(BuiltinTraitID id) const noexcept const std::ostream& PolynomialType::display(std::ostream& os, const void* value) const { - if (value == nullptr) { - return os << "{ }"; - } + if (value == nullptr) { return os << "{ }"; } const auto* poly = static_cast(value); poly_print(os, *poly); return os; @@ -142,9 +129,7 @@ const std::ostream& PolynomialType::display(std::ostream& os, hash_t PolynomialType::hash_of(const void* value) const noexcept { - if (value == nullptr) { - return 0; - } + if (value == nullptr) { return 0; } return hash_value(*static_cast(value)); } @@ -152,4 +137,4 @@ TypePtr PolynomialType::get() noexcept { static PolynomialType type; return &type; -} +} \ No newline at end of file From a9a6c709900b1a8c79502089d3c4b14d9399099d Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 11:45:43 +0000 Subject: [PATCH 20/27] 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. --- .../generics/polynomial_type/test_polynomial_type.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/platform/src/generics/polynomial_type/test_polynomial_type.cpp b/platform/src/generics/polynomial_type/test_polynomial_type.cpp index 7e90c2a1..49d9f930 100644 --- a/platform/src/generics/polynomial_type/test_polynomial_type.cpp +++ b/platform/src/generics/polynomial_type/test_polynomial_type.cpp @@ -52,8 +52,8 @@ class TestPolynomialType : public ::testing::Test TEST_F(TestPolynomialType, TestID) { - EXPECT_EQ(polynomial_type->id(), "polynomial_id"); // Adjust ID accordingly - EXPECT_EQ(polynomial_type->name(), "polynomial"); + EXPECT_EQ(polynomial_type->id(), "poly"); + EXPECT_EQ(polynomial_type->name(), "Polynomial"); } TEST_F(TestPolynomialType, TestRefCounting) @@ -70,8 +70,6 @@ TEST_F(TestPolynomialType, TestRefCounting) TEST_F(TestPolynomialType, TestBasicProperties) { - EXPECT_EQ(polynomial_type->object_size(), sizeof(void*)); // Adjust if necessary - EXPECT_FALSE(concepts::is_standard_layout(*polynomial_type)); EXPECT_FALSE(concepts::is_trivially_copyable(*polynomial_type)); EXPECT_FALSE(concepts::is_trivially_constructible(*polynomial_type)); @@ -88,10 +86,10 @@ TEST_F(TestPolynomialType, TestBasicProperties) TEST_F(TestPolynomialType, TestDisplayAndParseFromString) { - Value value(polynomial_type, string_view("x^2+3*x+2")); // Example string + Value value(polynomial_type, string_view("{ 15/2 2(x1) 1(x2^2) }")); // Example string std::stringstream ss; polynomial_type->display(ss, value.data()); - EXPECT_EQ(ss.str(), "x^2+3*x+2"); // Example string + EXPECT_EQ(ss.str(), "{ 15/2 2(x1) 1(x2^2) }"); // Example string } /****************************************************************************** From aae2ca893174049a5a2851e0972482141009d5b1 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 11:49:37 +0000 Subject: [PATCH 21/27] 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. --- .../generics/polynomial_type/polynomial_type.cpp | 13 ++++++++++++- .../src/generics/polynomial_type/polynomial_type.h | 8 ++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/platform/src/generics/polynomial_type/polynomial_type.cpp b/platform/src/generics/polynomial_type/polynomial_type.cpp index 43e0ae4d..7920a1c3 100644 --- a/platform/src/generics/polynomial_type/polynomial_type.cpp +++ b/platform/src/generics/polynomial_type/polynomial_type.cpp @@ -13,6 +13,7 @@ #include + #include "indeterminate.h" #include "monomial.h" #include "polynomial.h" @@ -21,6 +22,11 @@ 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 @@ -115,7 +121,12 @@ convert_from(const Type& type) const noexcept const BuiltinTrait* PolynomialType:: get_builtin_trait(BuiltinTraitID id) const noexcept { - return Type::get_builtin_trait(id); + 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, diff --git a/platform/src/generics/polynomial_type/polynomial_type.h b/platform/src/generics/polynomial_type/polynomial_type.h index 9b3b8345..952944ec 100644 --- a/platform/src/generics/polynomial_type/polynomial_type.h +++ b/platform/src/generics/polynomial_type/polynomial_type.h @@ -7,11 +7,19 @@ #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; From 0ab768bae0019b2c87f854039464d1d0987fe105 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 13:13:41 +0000 Subject: [PATCH 22/27] 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. --- .../src/generics/polynomial_type/test_polynomial_type.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/src/generics/polynomial_type/test_polynomial_type.cpp b/platform/src/generics/polynomial_type/test_polynomial_type.cpp index 49d9f930..64d62ab1 100644 --- a/platform/src/generics/polynomial_type/test_polynomial_type.cpp +++ b/platform/src/generics/polynomial_type/test_polynomial_type.cpp @@ -165,7 +165,7 @@ TEST_F(TestPolynomialType, TestDivideOperator) auto* arith = arith_trait(); ASSERT_NE(arith, nullptr); - EXPECT_FALSE(arith->has_operation(ArithmeticOperation::Div)); // Depending on your polynomial type + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Div)); // Depending on your polynomial type } /****************************************************************************** @@ -209,5 +209,5 @@ TEST_F(TestPolynomialType, TestFromRationalFunction) { const auto* num = num_trait(); ASSERT_NE(num, nullptr); - EXPECT_FALSE(num->has_function(NumberFunction::FromRational)); + EXPECT_TRUE(num->has_function(NumberFunction::FromRational)); } \ No newline at end of file From d1710c494761adfc35acd15c72fd03f154e5cdb7 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 21:04:48 +0000 Subject: [PATCH 23/27] 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. --- CMakeLists.txt | 2 + .../generics/polynomial_type/CMakeLists.txt | 2 + vcpkg.json | 171 ++++++++---------- 3 files changed, 81 insertions(+), 94 deletions(-) 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/src/generics/polynomial_type/CMakeLists.txt b/platform/src/generics/polynomial_type/CMakeLists.txt index 3009fea7..14487cde 100644 --- a/platform/src/generics/polynomial_type/CMakeLists.txt +++ b/platform/src/generics/polynomial_type/CMakeLists.txt @@ -19,6 +19,8 @@ target_sources(RoughPy_Platform PRIVATE polynomial_type.h ) +target_link_libraries(RoughPy_Platform PRIVATE + ctre::ctre) if (ROUGHPY_BUILD_TESTS) 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 From 2fdb4e3454f5f2e40160dba7bed8cf3e009a81e9 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 21:05:00 +0000 Subject: [PATCH 24/27] 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. --- .../generics/polynomial_type/indeterminate.h | 2 +- .../src/generics/polynomial_type/monomial.h | 6 + .../src/generics/polynomial_type/polynomial.h | 1 + .../polynomial_type/polynomial_type.cpp | 150 +++++++++++++++++- 4 files changed, 153 insertions(+), 6 deletions(-) diff --git a/platform/src/generics/polynomial_type/indeterminate.h b/platform/src/generics/polynomial_type/indeterminate.h index d1a97414..0728707f 100644 --- a/platform/src/generics/polynomial_type/indeterminate.h +++ b/platform/src/generics/polynomial_type/indeterminate.h @@ -20,7 +20,7 @@ namespace rpy::generics { class Indeterminate { - using base_type = int64_t; + using base_type = uint64_t; base_type m_data; public: diff --git a/platform/src/generics/polynomial_type/monomial.h b/platform/src/generics/polynomial_type/monomial.h index d68f27c1..64ef60b9 100644 --- a/platform/src/generics/polynomial_type/monomial.h +++ b/platform/src/generics/polynomial_type/monomial.h @@ -60,6 +60,7 @@ class Monomial 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 @@ -71,6 +72,11 @@ class Monomial 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; diff --git a/platform/src/generics/polynomial_type/polynomial.h b/platform/src/generics/polynomial_type/polynomial.h index d775853b..577acceb 100644 --- a/platform/src/generics/polynomial_type/polynomial.h +++ b/platform/src/generics/polynomial_type/polynomial.h @@ -118,6 +118,7 @@ class Polynomial 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; diff --git a/platform/src/generics/polynomial_type/polynomial_type.cpp b/platform/src/generics/polynomial_type/polynomial_type.cpp index 7920a1c3..2258abd1 100644 --- a/platform/src/generics/polynomial_type/polynomial_type.cpp +++ b/platform/src/generics/polynomial_type/polynomial_type.cpp @@ -6,6 +6,10 @@ #include #include +#include +#include + +#include #include @@ -77,11 +81,6 @@ void PolynomialType::free_object(void* ptr) const mem::small_object_free(poly, sizeof(Polynomial)); } -bool PolynomialType::parse_from_string(void* data, - string_view str) const noexcept -{ - return Type::parse_from_string(data, str); -} void PolynomialType::copy_or_move(void* dst, const void* src, @@ -148,4 +147,145 @@ 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 From feb999dff396889a39a34026d07a4ccaedb231c0 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 21:17:09 +0000 Subject: [PATCH 25/27] 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. --- .../polynomial_type/test_polynomial_type.cpp | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/platform/src/generics/polynomial_type/test_polynomial_type.cpp b/platform/src/generics/polynomial_type/test_polynomial_type.cpp index 64d62ab1..bbe2f004 100644 --- a/platform/src/generics/polynomial_type/test_polynomial_type.cpp +++ b/platform/src/generics/polynomial_type/test_polynomial_type.cpp @@ -86,10 +86,44 @@ TEST_F(TestPolynomialType, TestBasicProperties) TEST_F(TestPolynomialType, TestDisplayAndParseFromString) { - Value value(polynomial_type, string_view("{ 15/2 2(x1) 1(x2^2) }")); // Example string + 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) }"); // Example string + 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, 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) }"); } /****************************************************************************** From 4792aabe7390f200fef0b81f991d6f39a3874da4 Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 21:20:34 +0000 Subject: [PATCH 26/27] 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. --- .../src/generics/polynomial_type/test_polynomial_type.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/platform/src/generics/polynomial_type/test_polynomial_type.cpp b/platform/src/generics/polynomial_type/test_polynomial_type.cpp index bbe2f004..a5990060 100644 --- a/platform/src/generics/polynomial_type/test_polynomial_type.cpp +++ b/platform/src/generics/polynomial_type/test_polynomial_type.cpp @@ -100,6 +100,14 @@ TEST_F(TestPolynomialType, TestParseMixedMonomial) 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) }")); From af091fe1741b5f036c02c454c112b8863512824d Mon Sep 17 00:00:00 2001 From: Sam Morley Date: Wed, 27 Nov 2024 21:54:44 +0000 Subject: [PATCH 27/27] 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. --- platform/src/generics/polynomial_type/monomial.cpp | 1 + platform/src/generics/polynomial_type/polynomial_type.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/src/generics/polynomial_type/monomial.cpp b/platform/src/generics/polynomial_type/monomial.cpp index 31e7dba1..339b5937 100644 --- a/platform/src/generics/polynomial_type/monomial.cpp +++ b/platform/src/generics/polynomial_type/monomial.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include "roughpy/core/ranges.h" diff --git a/platform/src/generics/polynomial_type/polynomial_type.cpp b/platform/src/generics/polynomial_type/polynomial_type.cpp index 2258abd1..4895e95e 100644 --- a/platform/src/generics/polynomial_type/polynomial_type.cpp +++ b/platform/src/generics/polynomial_type/polynomial_type.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include