diff --git a/CMakeLists.txt b/CMakeLists.txt index e67cf756..9c2328e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,6 +177,7 @@ find_package(OpenCL CONFIG REQUIRED) find_package(cereal CONFIG REQUIRED) find_package(PCGRandom REQUIRED) find_package(GMP REQUIRED) +find_package(MPFR REQUIRED) find_package(range-v3 CONFIG REQUIRED) message(STATUS "Target architecture ${RPY_ARCH}") diff --git a/cmake/Modules/FindGMP.cmake b/cmake/Modules/FindGMP.cmake index 67efa41d..174798ae 100644 --- a/cmake/Modules/FindGMP.cmake +++ b/cmake/Modules/FindGMP.cmake @@ -9,15 +9,17 @@ include(SelectLibraryConfigurations) find_package(PkgConfig QUIET) if (PkgConfig_FOUND) - pkg_check_modules(PC_GMP QUIET gmp) + pkg_check_modules(PC_GMP QUIET gmp IMPORTED_TARGET) endif () -if (GMP_PKG_FOUND) +if (PC_GMP_FOUND) set(GMP_INCLUDE_DIR "${PC_GMP_INCLUDE_DIRS}") set(GMP_LIBRARY_DIRS "${PC_GMP_LIBRARY_DIRS}") set(GMP_LIBRARIES "${PC_GMP_LIBRARIES}") set(GMP_COMPILE_OPTIONS ${PC_GMP_CFLAGS} ${PC_GMP_CFLAGS_OTHER}) set(GMP_LINK_OPTIONS ${PC_GMP_LDFLAGS} ${PC_GMP_LDFLAGS_OTHER}) + + add_library(GMP::GMP ALIAS PkgConfig::PC_GMP) else() set(_gmp_lib_names gmp libgmp gmp-10 libgmp-10 mpir libmpir) @@ -52,60 +54,55 @@ else() select_library_configurations(GMP) - -endif () - - - - -find_package_handle_standard_args(GMP - FOUND_VAR GMP_FOUND - REQUIRED_VARS + find_package_handle_standard_args(GMP + FOUND_VAR GMP_FOUND + REQUIRED_VARS GMP_LIBRARY GMP_INCLUDE_DIR) -mark_as_advanced( - GMP_INCLUDE_DIR - GMP_LIBRARIES - GMP_LIBRARY_DIRS - GMP_COMPILE_OPTIONS - GMP_LINK_OPTIONS -) - - -if (GMP_FOUND AND NOT TARGET GMP::GMP) - add_library(GMP::GMP UNKNOWN IMPORTED GLOBAL) - set_target_properties(GMP::GMP PROPERTIES - IMPORTED_LOCATION "${GMP_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${GMP_INCLUDE_DIR}" - IMPORTED_IMPLIB "${GMP_LIBRARY}" + mark_as_advanced( + GMP_INCLUDE_DIR + GMP_LIBRARIES + GMP_LIBRARY_DIRS + GMP_COMPILE_OPTIONS + GMP_LINK_OPTIONS ) -# if (GMP_COMPILE_OPTIONS) -# set_target_properties(GMP::GMP PROPERTIES -# INTERFACE_COMPILE_OPTIONS "${GMP_COMPILE_OPTIONS}" -# ) -# endif() -# if(GMP_LINK_OPTIONS) -# set_target_properties(GMP::GMP PROPERTIES -# INTERFACE_LINK_OPTIONS "${GMP_LINK_OPTIONS}" -# ) -# endif() -# -# if (GMP_LIBRARY_RELEASE) -# set_property(TARGET GMP::GMP APPEND PROPERTY -# IMPORTED_CONFIGURATIONS RELEASE -# ) -# set_target_properties(GMP::GMP PROPERTIES -# IMPORTED_LOCATION_RELEASE "${GMP_LIBRARY_RELEASE}" -# ) -# endif() -# if (GMP_LIBRARY_DEBUG) -# set_property(TARGET GMP::GMP APPEND PROPERTY -# IMPORTED_CONFIGURATIONS DEBUG -# ) -# set_target_properties(GMP::GMP PROPERTIES -# IMPORTED_LOCATION_DEBUG "${GMP_LIBRARY_DEBUG}" -# ) -# endif() + if (GMP_FOUND AND NOT TARGET GMP::GMP) + add_library(GMP::GMP UNKNOWN IMPORTED GLOBAL) + set_target_properties(GMP::GMP PROPERTIES + IMPORTED_LOCATION "${GMP_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${GMP_INCLUDE_DIR}" + IMPORTED_IMPLIB "${GMP_LIBRARY}" + ) + # if (GMP_COMPILE_OPTIONS) + # set_target_properties(GMP::GMP PROPERTIES + # INTERFACE_COMPILE_OPTIONS "${GMP_COMPILE_OPTIONS}" + # ) + # endif() + # if(GMP_LINK_OPTIONS) + # set_target_properties(GMP::GMP PROPERTIES + # INTERFACE_LINK_OPTIONS "${GMP_LINK_OPTIONS}" + # ) + # endif() + # + # if (GMP_LIBRARY_RELEASE) + # set_property(TARGET GMP::GMP APPEND PROPERTY + # IMPORTED_CONFIGURATIONS RELEASE + # ) + # set_target_properties(GMP::GMP PROPERTIES + # IMPORTED_LOCATION_RELEASE "${GMP_LIBRARY_RELEASE}" + # ) + # endif() + # if (GMP_LIBRARY_DEBUG) + # set_property(TARGET GMP::GMP APPEND PROPERTY + # IMPORTED_CONFIGURATIONS DEBUG + # ) + # set_target_properties(GMP::GMP PROPERTIES + # IMPORTED_LOCATION_DEBUG "${GMP_LIBRARY_DEBUG}" + # ) + # endif() + + + endif() +endif () -endif() diff --git a/cmake/Modules/FindMPFR.cmake b/cmake/Modules/FindMPFR.cmake new file mode 100644 index 00000000..ae5443a4 --- /dev/null +++ b/cmake/Modules/FindMPFR.cmake @@ -0,0 +1,84 @@ + +if (MPFR_FOUND) + return() +endif () + +include(FindPackageHandleStandardArgs) +include(SelectLibraryConfigurations) + +find_package(PkgConfig QUIET) + + +if (PkgConfig_FOUND) + pkg_check_modules(PC_MPFR QUIET mpfr IMPORTED_TARGET) +endif () + + +if (PC_MPFR_FOUND) + set(MPFR_INCLUDE_DIR "${PC_MPFR_INCLUDE_DIRS}") + set(MPFR_LIBRARY_DIRS "${PC_MPFR_LIBRARY_DIRS}") + set(MPFR_LIBRARIES "${PC_MPFR_LIBRARIES}") + set(MPFR_COMPILE_OPTIONS "${PC_MPFR_CFLAGS} ${PC_MPFR_CFLAGS_OTHER}") + set(MPFR_LINK_OPTIONS "${PC_MPFR_LDFLAGS} ${PC_MPFR_LDFLAGS_OTHER}") + + add_library(MPFR::MPFR ALIAS PkgConfig::PC_MPFR) +else () + set(_mpfr_lib_names mpfr libmpfr) + + find_library(MPFR_LIBRARY_RELEASE + NAMES ${_mpfr_lib_names} + HINTS "${VCPKG_INSTALLED_DIR}") + + set(_use_debug_dir OFF) + if (DEFINED VCPKG_INSTALLED_DIR) + cmake_path(IS_PREFIX VCPKG_INSTALLED_DIR CMAKE_BINARY_DIR OUTPUT_VARIABLE _use_debug_dir) + endif () + + if (WIN32 OR _use_debug_dir) + find_library(MPFR_LIBRARY_DEBUG + NAMES ${_mpfr_lib_names} + HINTS "${VCPKG_INSTALLED_DIR}/debug" + ) + else () + foreach (_name IN ITEMS ${_mpfr_lib_names}) + list(APPEND _mpfr_lib_names_debug "${_name}d") + endforeach () + + find_library(MPFR_LIBRARY_DEBUG + NAMES ${_mpfr_lib_names} + HINTS "${VCPKG_INSTALLED_DIR}") + endif () + + set(MPFR_COMPILE_OPTIONS "") + set(MPFR_LINK_OPTIONS "") + + find_path(MPFR_INCLUDE_DIR NAMES mpfr.h HINTS "${VCPKG_INSTALLED_DIR}") + + select_library_configurations(MPFR) + find_package_handle_standard_args(MPFR + FOUND_VAR MPFR_FOUND + REQUIRED_VARS MPFR_LIBRARY MPFR_INCLUDE_DIR + ) + mark_as_advanced( + MPFR_INCLUDE_DIR + MPFR_LIBRARIES + MPFR_LIBRARY_DIRS + MPFR_COMPILE_OPTIONS + MPFR_LINK_OPTIONS + ) + + if (MPFR_FOUND AND NOT MPFR::MPFR) + add_library(MPFR::MPFR UNKNOWN IMPORTED GLOBAL) + set_target_properties(MPFR::MPFR PROPERTIES + IMPORTED_LOCATION "${MPFR_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${MPFR_INCLUDE_DIR}" + IMPORTED_IMPLIB "${MPFR_LIBRARY}" + ) + + endif() + + +endif () + + + diff --git a/core/include/roughpy/core/detail/config.h b/core/include/roughpy/core/detail/config.h index 11b22fae..7be2118c 100644 --- a/core/include/roughpy/core/detail/config.h +++ b/core/include/roughpy/core/detail/config.h @@ -58,13 +58,18 @@ // Detect Compiler and Version # if defined(_MSC_VER) +# define RPY_COMPILER_VERSION(major, minor, patch) ((major)*10 + (minor)) # define RPY_COMPILER_MSVC _MSC_VER # elif defined(__clang__) -# define RPY_COMPILER_CLANG \ - (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) +# define RPY_COMPILER_VERSION(major, minor, patch) \ + ((major)*10000 + (minor)*100 + (patch)) +# define RPY_COMPILER_CLANG \ + RPY_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) # elif defined(__GNUC__) || defined(__GNUG__) -# define RPY_COMPILER_GCC \ - (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +# define RPY_COMPILER_VERSION(major, minor, patch) \ + ((major)*10000 + (minor)*100 + (patch)) +# define RPY_COMPILER_GCC \ + RPY_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) # elif defined(__INTEL_COMPILER) # define RPY_COMPILER_INTEL __INTEL_COMPILER # elif defined(__NVCC__) diff --git a/platform/include/roughpy/generics/comparison_trait.h b/platform/include/roughpy/generics/comparison_trait.h index f5e0b71a..8cdd60f8 100644 --- a/platform/include/roughpy/generics/comparison_trait.h +++ b/platform/include/roughpy/generics/comparison_trait.h @@ -340,7 +340,7 @@ bool ComparisonTraitImpl::has_comparison(ComparisonType comp) const noexcept return dtl::has_greater_equal_test_v || (dtl::has_greater_test_v && dtl::has_equal_test_v); } - RPY_UNREACHABLE_RETURN(); + RPY_UNREACHABLE_RETURN(false); } template bool ComparisonTraitImpl::unsafe_compare_equal( diff --git a/platform/include/roughpy/generics/type.h b/platform/include/roughpy/generics/type.h index a82027df..f80eda66 100644 --- a/platform/include/roughpy/generics/type.h +++ b/platform/include/roughpy/generics/type.h @@ -59,11 +59,11 @@ 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); -} +TypePtr get_type() noexcept; +//{ +// static_assert(false, "There is no Type associated with T"); +// RPY_UNREACHABLE_RETURN(nullptr); +//} /** * @brief Encapsulates the definition and functionalities related to custom @@ -311,6 +311,23 @@ ROUGHPY_PLATFORM_EXPORT const BuiltinTypes& get_builtin_types() noexcept; +class ROUGHPY_PLATFORM_EXPORT MultiPrecisionTypes +{ + MultiPrecisionTypes(); +public: + + TypePtr integer_type; + TypePtr rational_type; + + RPY_NO_DISCARD + TypePtr float_type(int n_precision) const; + + RPY_NO_DISCARD + static const MultiPrecisionTypes& get() noexcept; +}; + + + template /** * @brief Determines the basic properties of a given type. diff --git a/platform/include/roughpy/generics/values.h b/platform/include/roughpy/generics/values.h index 316a6a95..d87baff2 100644 --- a/platform/include/roughpy/generics/values.h +++ b/platform/include/roughpy/generics/values.h @@ -424,7 +424,7 @@ class ROUGHPY_PLATFORM_EXPORT Value namespace dtl { -void backup_display(std::ostream& os); +void ROUGHPY_PLATFORM_EXPORT backup_display(std::ostream& os); } diff --git a/platform/src/CMakeLists.txt b/platform/src/CMakeLists.txt index b9f0d3c4..a5f18b10 100644 --- a/platform/src/CMakeLists.txt +++ b/platform/src/CMakeLists.txt @@ -1,8 +1,5 @@ -if(ROUGHPY_EXPERIMENTAL) add_subdirectory(generics) -endif() - add_subdirectory(devices) if(ROUGHPY_EXPERIMENTAL) diff --git a/platform/src/generics/CMakeLists.txt b/platform/src/generics/CMakeLists.txt index cd959e6f..08094945 100644 --- a/platform/src/generics/CMakeLists.txt +++ b/platform/src/generics/CMakeLists.txt @@ -15,6 +15,7 @@ target_sources(RoughPy_Platform PRIVATE backup_display.cpp comparison_trait.cpp conversion_trait.cpp + multiprecision_types.cpp number_trait.cpp type.cpp type_builtin.cpp @@ -27,10 +28,9 @@ target_sources(RoughPy_Platform PRIVATE add_subdirectory(builtin_types) +add_subdirectory(multiprecision_types) + -target_link_libraries(RoughPy_Platform PRIVATE - RoughPy_Generics_BuiltinType -) if (ROUGHPY_BUILD_TESTS) diff --git a/platform/src/generics/builtin_types/CMakeLists.txt b/platform/src/generics/builtin_types/CMakeLists.txt index 210626a1..45ae2d93 100644 --- a/platform/src/generics/builtin_types/CMakeLists.txt +++ b/platform/src/generics/builtin_types/CMakeLists.txt @@ -2,7 +2,7 @@ -add_library(RoughPy_Generics_BuiltinType STATIC +target_sources(RoughPy_Platform PRIVATE builtin_type.h builtin_type_ids.h builtin_type_methods.h @@ -18,19 +18,6 @@ add_library(RoughPy_Generics_BuiltinType STATIC unsigned_int_type.h ) -set_target_properties(RoughPy_Generics_BuiltinType PROPERTIES - POSITION_INDEPENDENT_CODE ON -) - -target_include_directories(RoughPy_Generics_BuiltinType PRIVATE - ${ROUGHPY_PLATFORM_INCLUDE_DIR}/../ - ${CMAKE_CURRENT_BINARY_DIR}/../../.. -) - -target_link_libraries(RoughPy_Generics_BuiltinType PRIVATE - RoughPy::Core -) - if (ROUGHPY_BUILD_TESTS) diff --git a/platform/src/generics/builtin_types/builtin_type_methods.h b/platform/src/generics/builtin_types/builtin_type_methods.h index 7486822e..001dfb5a 100644 --- a/platform/src/generics/builtin_types/builtin_type_methods.h +++ b/platform/src/generics/builtin_types/builtin_type_methods.h @@ -7,10 +7,10 @@ #include "builtin_type.h" +#include #include #include - #include "roughpy/core/check.h" #include "roughpy/core/debug_assertion.h" #include "roughpy/core/hash.h" @@ -26,7 +26,6 @@ namespace rpy::generics { - template string_view BuiltinTypeBase::id() const noexcept { @@ -39,7 +38,6 @@ const std::type_info& BuiltinTypeBase::type_info() const noexcept return typeid(T); } - template void BuiltinTypeBase::inc_ref() const noexcept { @@ -53,7 +51,6 @@ bool BuiltinTypeBase::dec_ref() const noexcept return false; } - template bool BuiltinTypeBase::parse_from_string( void* data, @@ -64,24 +61,33 @@ bool BuiltinTypeBase::parse_from_string( T value; - const auto* begin = str.data(); - const auto* end = str.data() + str.size(); - auto result = std::from_chars(begin, end, value); - - if (result.ec == std::errc::invalid_argument) { - return false; + if constexpr (is_integral_v) { + const auto* begin = str.data(); + const auto* end = str.data() + str.size(); + auto [ptr, ec] = std::from_chars(begin, end, value, 10); + if (ec != std::errc() || ptr != end) { return false; } + } else if constexpr (is_same_v) { + string tmp(str); + try { + value = std::stof(tmp); + } catch (std::exception& RPY_UNUSED(exc)) { + return false; + } + } else if constexpr(is_same_v) { + string tmp(str); + try { + value = std::stod(tmp); + } catch (std::exception& RPY_UNUSED(exc)) { + return false; + } } - if (result.ptr != end) { - return false; - } *static_cast(data) = std::move(value); return true; } - template void* BuiltinTypeBase::allocate_object() const { @@ -101,13 +107,11 @@ void BuiltinTypeBase::copy_or_move( void* dst, const void* src, size_t count, - bool RPY_UNUSED_VAR(move) // Move semantics makes no difference for T + bool RPY_UNUSED_VAR(move)// Move semantics makes no difference for T ) const noexcept { - if (RPY_UNLIKELY(dst == nullptr || count == 0)) { - return; - } + if (RPY_UNLIKELY(dst == nullptr || count == 0)) { return; } auto* dst_ptr = static_cast(dst); @@ -130,8 +134,6 @@ void BuiltinTypeBase::destroy_range(void* data, size_t count) const } } - - template std::unique_ptr BuiltinTypeBase::convert_to(const Type& type) const noexcept @@ -167,21 +169,17 @@ BuiltinTypeBase::convert_from(const Type& type) const noexcept return it->second->make(this, &type); } - return Type::convert_from(type); } template -const BuiltinTrait* BuiltinTypeBase::get_builtin_trait(BuiltinTraitID id -) const noexcept +const BuiltinTrait* +BuiltinTypeBase::get_builtin_trait(BuiltinTraitID id) const noexcept { switch (id) { - case BuiltinTraitID::Comparison: - return &m_comparison_trait; - case BuiltinTraitID::Arithmetic: - return &m_arithmetic_trait; - case BuiltinTraitID::Number: - return &m_number_trait; + case BuiltinTraitID::Comparison: return &m_comparison_trait; + case BuiltinTraitID::Arithmetic: return &m_arithmetic_trait; + case BuiltinTraitID::Number: return &m_number_trait; } RPY_UNREACHABLE_RETURN(nullptr); } @@ -206,18 +204,14 @@ BuiltinTypeBase::display(std::ostream& os, const void* value) const return os << *static_cast(value); } - template hash_t BuiltinTypeBase::hash_of(const void* value) const noexcept { Hash hasher; - if (RPY_UNLIKELY(value == nullptr)) { - return hasher(0.0); - } + if (RPY_UNLIKELY(value == nullptr)) { return hasher(0.0); } return hasher(*static_cast(value)); } -} - +}// namespace rpy::generics -#endif //ROUGHPY_GENERICS_INTERNAL_BUILTIN_TYPE_METHODS_H +#endif// ROUGHPY_GENERICS_INTERNAL_BUILTIN_TYPE_METHODS_H diff --git a/platform/src/generics/multiprecision_types.cpp b/platform/src/generics/multiprecision_types.cpp new file mode 100644 index 00000000..563143d6 --- /dev/null +++ b/platform/src/generics/multiprecision_types.cpp @@ -0,0 +1,43 @@ +// +// Created by sammorley on 25/11/24. +// + +#include "multiprecision_types/float_type.h" +#include "roughpy/generics/type.h" + +#include + +#include "multiprecision_types/integer_type.h" +#include "multiprecision_types/rational_type.h" + +using namespace rpy; +using namespace rpy::generics; + + +MultiPrecisionTypes::MultiPrecisionTypes() + : integer_type(IntegerType::get()), + rational_type(RationalType::get()) +{ + +} + +TypePtr MultiPrecisionTypes::float_type(int n_precision) const { + if (n_precision <= std::numeric_limits::digits) { + return get_builtin_types().float_type; + } + if (n_precision <= std::numeric_limits::digits) { + return get_builtin_types().double_type; + } + + ignore_unused(this); + + return TypePtr(new MPFloatType(n_precision)); + + RPY_THROW(std::domain_error, "this precision is not available"); +} + +const MultiPrecisionTypes& MultiPrecisionTypes::get() noexcept +{ + static const MultiPrecisionTypes object; + return object; +} diff --git a/platform/src/generics/multiprecision_types/CMakeLists.txt b/platform/src/generics/multiprecision_types/CMakeLists.txt new file mode 100644 index 00000000..e962ae2a --- /dev/null +++ b/platform/src/generics/multiprecision_types/CMakeLists.txt @@ -0,0 +1,63 @@ + + + + +target_sources(RoughPy_Platform PRIVATE + rational_type.cpp + rational_type.h + rational_arithmetic.cpp + rational_arithmetic.h + rational_conversion.cpp + rational_conversion.h + rational_number.cpp + rational_number.h + rational_comparison.cpp + rational_comparison.h + mpz_hash.h + integer_type.cpp + integer_type.h + integer_arithmetic.cpp + integer_arithmetic.h + integer_comparison.cpp + integer_comparison.h + integer_number.cpp + integer_number.h + float_type.cpp + float_type.h + float_arithmetic.cpp + float_arithmetic.h + float_comparison.cpp + float_comparison.h + float_number.cpp + float_number.h +) + + + + + + + +target_link_libraries(RoughPy_Platform PRIVATE + GMP::GMP + MPFR::MPFR +) + + +if (ROUGHPY_BUILD_TESTS) + + add_executable(test_multiprecision_types + test_rational_type.cpp + test_integer_type.cpp + test_float_type.cpp + ) + + target_link_Libraries(test_multiprecision_types PRIVATE + RoughPy_Platform + GTest::gtest + ) + + setup_roughpy_cpp_tests(test_multiprecision_types) + +endif() + diff --git a/platform/src/generics/multiprecision_types/float_arithmetic.cpp b/platform/src/generics/multiprecision_types/float_arithmetic.cpp new file mode 100644 index 00000000..e787eb12 --- /dev/null +++ b/platform/src/generics/multiprecision_types/float_arithmetic.cpp @@ -0,0 +1,63 @@ +// +// Created by sammorley on 25/11/24. +// + +#include "float_arithmetic.h" + +#include +#include + + +using namespace rpy; +using namespace rpy::generics; + + +bool FloatArithmetic::has_operation(ArithmeticOperation op) const noexcept +{ + return true; +} + +void FloatArithmetic::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); + + mpfr_add(dst_ptr, dst_ptr, src_ptr, MPFR_RNDN); +} + +void FloatArithmetic::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); + + mpfr_sub(dst_ptr, dst_ptr, src_ptr, MPFR_RNDN); +} + +void FloatArithmetic::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); + + mpfr_mul(dst_ptr, dst_ptr, src_ptr, MPFR_RNDN); +} + +void FloatArithmetic::unsafe_div_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); + + mpfr_div(dst_ptr, dst_ptr, src_ptr, MPFR_RNDN); +} diff --git a/platform/src/generics/multiprecision_types/float_arithmetic.h b/platform/src/generics/multiprecision_types/float_arithmetic.h new file mode 100644 index 00000000..5c70701a --- /dev/null +++ b/platform/src/generics/multiprecision_types/float_arithmetic.h @@ -0,0 +1,35 @@ +// +// Created by sammorley on 25/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_FLOAT_ARITHMETIC_H +#define ROUGHPY_GENERICS_INTERNAL_FLOAT_ARITHMETIC_H + +#include "roughpy/generics/arithmetic_trait.h" + + +namespace rpy { +namespace generics { + +class FloatArithmetic : public ArithmeticTrait{ +public: + explicit FloatArithmetic(const Type* type) + : ArithmeticTrait(type, type) + {} + + RPY_NO_DISCARD bool + has_operation(ArithmeticOperation op) const noexcept override; + + void unsafe_add_inplace(void* lhs, const void* rhs) const noexcept override; + + void unsafe_sub_inplace(void* lhs, const void* rhs) const override; + + void unsafe_mul_inplace(void* lhs, const void* rhs) const override; + + void unsafe_div_inplace(void* lhs, const void* rhs) const override; +}; + +} // generics +} // rpy + +#endif //ROUGHPY_GENERICS_INTERNAL_FLOAT_ARITHMETIC_H diff --git a/platform/src/generics/multiprecision_types/float_comparison.cpp b/platform/src/generics/multiprecision_types/float_comparison.cpp new file mode 100644 index 00000000..f87a9d00 --- /dev/null +++ b/platform/src/generics/multiprecision_types/float_comparison.cpp @@ -0,0 +1,77 @@ +// +// Created by sammorley on 25/11/24. +// + +#include "float_comparison.h" + +#include +#include + +using namespace rpy; +using namespace rpy::generics; + + +bool FloatComparison::has_comparison(ComparisonType comp) const noexcept +{ + return true; +} + +bool FloatComparison::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(rhs); + auto* rhs_ptr = static_cast(lhs); + + return mpfr_equal_p(lhs_ptr, rhs_ptr) != 0; +} + +bool FloatComparison::unsafe_compare_less(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(rhs); + auto* rhs_ptr = static_cast(lhs); + + return mpfr_less_p(lhs_ptr, rhs_ptr) != 0; +} + +bool FloatComparison::unsafe_compare_less_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(rhs); + auto* rhs_ptr = static_cast(lhs); + + return mpfr_lessequal_p(lhs_ptr, rhs_ptr) != 0; +} + +bool FloatComparison::unsafe_compare_greater(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(rhs); + auto* rhs_ptr = static_cast(lhs); + + return mpfr_greater_p(lhs_ptr, rhs_ptr) != 0; +} + +bool FloatComparison::unsafe_compare_greater_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(rhs); + auto* rhs_ptr = static_cast(lhs); + + return mpfr_greaterequal_p(lhs_ptr, rhs_ptr) != 0; +} diff --git a/platform/src/generics/multiprecision_types/float_comparison.h b/platform/src/generics/multiprecision_types/float_comparison.h new file mode 100644 index 00000000..dcfa81e8 --- /dev/null +++ b/platform/src/generics/multiprecision_types/float_comparison.h @@ -0,0 +1,42 @@ +// +// Created by sammorley on 25/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_FLOAT_COMPARISON_H +#define ROUGHPY_GENERICS_INTERNAL_FLOAT_COMPARISON_H + +#include "roughpy/generics/comparison_trait.h" + +namespace rpy { +namespace generics { + +class FloatComparison : public ComparisonTrait { + +public: + + FloatComparison(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_FLOAT_COMPARISON_H diff --git a/platform/src/generics/multiprecision_types/float_number.cpp b/platform/src/generics/multiprecision_types/float_number.cpp new file mode 100644 index 00000000..da969f2a --- /dev/null +++ b/platform/src/generics/multiprecision_types/float_number.cpp @@ -0,0 +1,111 @@ +// +// Created by sammorley on 25/11/24. +// + +#include "float_number.h" + +#include + +using namespace rpy; +using namespace rpy::generics; + +bool FloatNumber::has_function(NumberFunction fn_id) const noexcept +{ + return true; +} + +void FloatNumber::unsafe_real(void* dst, const void* src) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + RPY_DBG_ASSERT_NE(src, nullptr); + + auto* src_ptr = static_cast(src); + auto* dst_ptr = static_cast(dst); + + mpfr_set(dst_ptr, src_ptr, MPFR_RNDN); +} + +void FloatNumber::unsafe_imaginary(void* dst, const void* src) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + ignore_unused(src); + + auto* dst_ptr = static_cast(dst); + + mpfr_set_zero(dst_ptr, 1); +} + +void FloatNumber::unsafe_abs(void* dst, const void* src) const noexcept +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + RPY_DBG_ASSERT_NE(src, nullptr); + + auto* src_ptr = static_cast(src); + auto* dst_ptr = static_cast(dst); + + mpfr_abs(dst_ptr, src_ptr, MPFR_RNDN); +} + +void FloatNumber::unsafe_sqrt(void* dst, const void* src) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + RPY_DBG_ASSERT_NE(src, nullptr); + + auto* src_ptr = static_cast(src); + auto* dst_ptr = static_cast(dst); + + mpfr_sqrt(dst_ptr, src_ptr, MPFR_RNDN); +} + +void FloatNumber::unsafe_pow(void* dst, + const void* base, + exponent_t exponent) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + RPY_DBG_ASSERT_NE(base, nullptr); + + auto* base_ptr = static_cast(base); + auto* dst_ptr = static_cast(dst); + + mpfr_pow_si(dst_ptr, base_ptr, exponent, MPFR_RNDN); +} + +void FloatNumber::unsafe_exp(void* dst, const void* src) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + RPY_DBG_ASSERT_NE(src, nullptr); + + auto* src_ptr = static_cast(src); + auto* dst_ptr = static_cast(dst); + + mpfr_exp(dst_ptr, src_ptr, MPFR_RNDN); +} + +void FloatNumber::unsafe_log(void* dst, const void* src) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + RPY_DBG_ASSERT_NE(src, nullptr); + + auto* src_ptr = static_cast(src); + auto* dst_ptr = static_cast(dst); + + mpfr_log(dst_ptr, src_ptr, MPFR_RNDN); +} + +void FloatNumber::unsafe_from_rational(void* dst, + int64_t numerator, + int64_t denominator) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + RPY_CHECK_NE(denominator, 0); + + if (numerator < 0) { + numerator = -numerator; + denominator = -denominator; + } + + auto* dst_ptr = static_cast(dst); + + mpfr_set_si(dst_ptr, numerator, MPFR_RNDN); + mpfr_div_si(dst_ptr, dst_ptr, denominator, MPFR_RNDN); +} diff --git a/platform/src/generics/multiprecision_types/float_number.h b/platform/src/generics/multiprecision_types/float_number.h new file mode 100644 index 00000000..29104d93 --- /dev/null +++ b/platform/src/generics/multiprecision_types/float_number.h @@ -0,0 +1,45 @@ +// +// Created by sammorley on 25/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_FLOAT_NUMBER_H +#define ROUGHPY_GENERICS_INTERNAL_FLOAT_NUMBER_H + + +#include "roughpy/generics/number_trait.h" + +namespace rpy { +namespace generics { + +class FloatNumber : public NumberTrait { +public: + + explicit FloatNumber(const Type* type) : NumberTrait(type, type) {} + + bool has_function(NumberFunction fn_id) const noexcept override; + + void unsafe_real(void* dst, const void* src) const override; + + void unsafe_imaginary(void* dst, const void* src) const override; + + void unsafe_abs(void* dst, const void* src) const noexcept override; + + void unsafe_sqrt(void* dst, const void* src) const override; + + void unsafe_pow(void* dst, + const void* base, + exponent_t exponent) const override; + + void unsafe_exp(void* dst, const void* src) const override; + + void unsafe_log(void* dst, const void* src) const override; + + void unsafe_from_rational(void* dst, + int64_t numerator, + int64_t denominator) const override; +}; + +} // generics +} // rpy + +#endif //ROUGHPY_GENERICS_INTERNAL_FLOAT_NUMBER_H diff --git a/platform/src/generics/multiprecision_types/float_type.cpp b/platform/src/generics/multiprecision_types/float_type.cpp new file mode 100644 index 00000000..609ca0df --- /dev/null +++ b/platform/src/generics/multiprecision_types/float_type.cpp @@ -0,0 +1,201 @@ +// +// Created by sammorley on 25/11/24. +// + +#include "float_type.h" + +#include "mpz_hash.h" + + +#include +#include + +using namespace rpy; +using namespace rpy::generics; + +MPFloatType::MPFloatType(int precision) + : m_arithmetic(this), + m_comparison(this), + m_number(this), + m_precision(precision) +{ + RPY_CHECK_LE(m_precision, MPFR_PREC_MAX); +} + +string_view MPFloatType::name() const noexcept +{ + return "MultiPrecisionFloat"; +} + +string_view MPFloatType::id() const noexcept +{ + return "apf"; +} + +const std::type_info& MPFloatType::type_info() const noexcept +{ + return typeid(mpfr_t); +} + +BasicProperties MPFloatType::basic_properties() const noexcept +{ + return { + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false + }; +} + +size_t MPFloatType::object_size() const noexcept { return sizeof(mpfr_ptr); } + +void* MPFloatType::allocate_object() const +{ + auto* new_float = static_cast(mem::aligned_alloc( + alignof(mpfr_t), + sizeof(mpfr_t))); + + if (new_float == nullptr) { throw std::bad_alloc(); } + mpfr_init2(new_float, m_precision); + mpfr_set_zero(new_float, 1); + return new_float; +} + +void MPFloatType::free_object(void* ptr) const +{ + RPY_DBG_ASSERT_NE(ptr, nullptr); + mpfr_clear(static_cast(ptr)); + mem::aligned_free(ptr); +} + +bool MPFloatType::parse_from_string(void* data, string_view str) const noexcept +{ + RPY_DBG_ASSERT_NE(data, nullptr); + string tmp(str); + return mpfr_set_str(static_cast(data), tmp.c_str(), 10, MPFR_RNDN) + != -1; +} + +void MPFloatType::copy_or_move(void* dst, + const void* src, + size_t count, + bool move) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + + if (RPY_UNLIKELY(count == 0)) { return; } + + auto* dst_ptr = static_cast(dst); + if (src == nullptr) { + for (size_t i = 0; i < count; ++i) { mpfr_set_zero(++dst_ptr, 1); } + } else if (move) { + auto* src_ptr = static_cast(const_cast(src)); + for (size_t i = 0; i < count; ++i, ++src_ptr, ++dst_ptr) { + mpfr_swap(dst_ptr, src_ptr); + mpfr_clear(src_ptr); + } + } else { + const auto* src_ptr = static_cast(src); + for (size_t i = 0; i < count; ++i, ++src_ptr, ++dst_ptr) { + mpfr_set(dst_ptr, src_ptr, MPFR_RNDN); + } + } + +} + +void MPFloatType::destroy_range(void* data, size_t count) const +{ + RPY_DBG_ASSERT_NE(data, nullptr); + auto* ptr = static_cast(data); + for (size_t i = 0; i < count; ++i) { mpfr_clear(ptr++); } +} + +std::unique_ptr MPFloatType:: +convert_to(const Type& type) const noexcept +{ + return RefCountedMiddle::convert_to(type); +} + +std::unique_ptr MPFloatType:: +convert_from(const Type& type) const noexcept +{ + return RefCountedMiddle::convert_from(type); +} + +const BuiltinTrait* MPFloatType:: +get_builtin_trait(BuiltinTraitID id) const noexcept +{ + switch (id) { + case BuiltinTraitID::Comparison: return &m_comparison; + case BuiltinTraitID::Arithmetic: return &m_arithmetic; + case BuiltinTraitID::Number: return &m_number; + } + RPY_UNREACHABLE_RETURN(nullptr); +} + +namespace { + +struct StringCleanup +{ + char* ptr = nullptr; + + ~StringCleanup() { if (ptr != nullptr) { mpfr_free_str(ptr); } } +}; + + +} + +const std::ostream& MPFloatType::display(std::ostream& os, + const void* value) const +{ + if (value == nullptr) { return os << 0; } + + const auto* flt_val = static_cast(value); + + // Choosing a format to emulate double output. + // "%.17g" is a common format to represent doubles with sufficient precision + // where 17 significant digits should be sufficient to represent a double value. + StringCleanup buffer; + const auto n_digits = mpfr_asprintf(&buffer.ptr, "%.17Rg", flt_val); + + RPY_CHECK_GT(n_digits, 0); + + return os << string_view(buffer.ptr, n_digits); +} + + +namespace { + +struct MPZCleanup +{ + mpz_t value; + + ~MPZCleanup() + { + mpz_clear(value); + } +}; + +} + +hash_t MPFloatType::hash_of(const void* value) const noexcept +{ + if (value == nullptr) { return 0; } + + const auto* flt_val = static_cast(value); + + MPZCleanup significand; + auto exp = mpfr_get_z_2exp(significand.value, flt_val); + + auto result = mpz_hash(significand.value); + hash_combine(result, exp); + + return result; +} \ No newline at end of file diff --git a/platform/src/generics/multiprecision_types/float_type.h b/platform/src/generics/multiprecision_types/float_type.h new file mode 100644 index 00000000..dde39976 --- /dev/null +++ b/platform/src/generics/multiprecision_types/float_type.h @@ -0,0 +1,70 @@ +// +// Created by sammorley on 25/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_FLOAT_TYPE_H +#define ROUGHPY_GENERICS_INTERNAL_FLOAT_TYPE_H + +#include "roughpy/core/macros.h" +#include "roughpy/core/hash.h" + +#include "roughpy/platform/reference_counting.h" + +#include "roughpy/generics/type.h" + +#include "float_arithmetic.h" +#include "float_comparison.h" +#include "float_number.h" + +namespace rpy { +namespace generics { + +class MPFloatType : public mem::RefCountedMiddle { + FloatArithmetic m_arithmetic; + FloatComparison m_comparison; + FloatNumber m_number; + + // 32 bits for precision should be plenty + int m_precision; + +public: + + explicit MPFloatType(int precision); + + 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; +}; + +} // generics +} // rpy + +#endif //ROUGHPY_GENERICS_INTERNAL_FLOAT_TYPE_H diff --git a/platform/src/generics/multiprecision_types/integer_arithmetic.cpp b/platform/src/generics/multiprecision_types/integer_arithmetic.cpp new file mode 100644 index 00000000..a3df2b18 --- /dev/null +++ b/platform/src/generics/multiprecision_types/integer_arithmetic.cpp @@ -0,0 +1,52 @@ +// +// Created by sammorley on 25/11/24. +// + +#include "integer_arithmetic.h" + +#include + +using namespace rpy; +using namespace rpy::generics; + +bool IntegerArithmetic::has_operation(ArithmeticOperation op) const noexcept +{ + if (op == ArithmeticOperation::Div) { + return false; + } + return true; +} +void IntegerArithmetic::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); + + mpz_add(dst_ptr, dst_ptr, src_ptr); +} +void IntegerArithmetic::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); + + mpz_sub(dst_ptr, dst_ptr, src_ptr); +} +void IntegerArithmetic::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); + + mpz_mul(dst_ptr, dst_ptr, src_ptr); +} +void IntegerArithmetic::unsafe_div_inplace(void* lhs, const void* rhs) const {} diff --git a/platform/src/generics/multiprecision_types/integer_arithmetic.h b/platform/src/generics/multiprecision_types/integer_arithmetic.h new file mode 100644 index 00000000..6f4e9c3c --- /dev/null +++ b/platform/src/generics/multiprecision_types/integer_arithmetic.h @@ -0,0 +1,32 @@ +// +// Created by sammorley on 25/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_INTEGER_ARITHMETIC_H +#define ROUGHPY_GENERICS_INTERNAL_INTEGER_ARITHMETIC_H + +#include "roughpy/generics/arithmetic_trait.h" + + +namespace rpy { +namespace generics { + +class IntegerArithmetic : public ArithmeticTrait { +public: + + explicit IntegerArithmetic(const Type* type) + : ArithmeticTrait(type, nullptr) + {} + + RPY_NO_DISCARD bool + has_operation(ArithmeticOperation op) const noexcept override; + void unsafe_add_inplace(void* lhs, const void* rhs) const noexcept override; + void unsafe_sub_inplace(void* lhs, const void* rhs) const override; + void unsafe_mul_inplace(void* lhs, const void* rhs) const override; + void unsafe_div_inplace(void* lhs, const void* rhs) const override; +}; + +} // generics +} // rpy + +#endif //ROUGHPY_GENERICS_INTERNAL_INTEGER_ARITHMETIC_H diff --git a/platform/src/generics/multiprecision_types/integer_comparison.cpp b/platform/src/generics/multiprecision_types/integer_comparison.cpp new file mode 100644 index 00000000..a201efdb --- /dev/null +++ b/platform/src/generics/multiprecision_types/integer_comparison.cpp @@ -0,0 +1,82 @@ +// +// Created by sammorley on 25/11/24. +// + +#include "integer_comparison.h" + +#include + +#include "roughpy/core/debug_assertion.h" + +using namespace rpy; +using namespace rpy::generics; + +bool IntegerComparison::has_comparison(ComparisonType comp) const noexcept +{ + return true; +} +bool IntegerComparison::unsafe_compare_equal( + const void* lhs, + const void* rhs +) const noexcept +{ + RPY_DBG_ASSERT_NE(lhs, nullptr); + RPY_DBG_ASSERT_NE(rhs, nullptr); + + const auto* lhs_ptr = static_cast(lhs); + const auto* rhs_ptr = static_cast(rhs); + + return mpz_cmp(lhs_ptr, rhs_ptr) != 0; +} +bool IntegerComparison::unsafe_compare_less( + const void* lhs, + const void* rhs +) const noexcept +{ + RPY_DBG_ASSERT_NE(lhs, nullptr); + RPY_DBG_ASSERT_NE(rhs, nullptr); + + const auto* lhs_ptr = static_cast(lhs); + const auto* rhs_ptr = static_cast(rhs); + + return mpz_cmp(lhs_ptr, rhs_ptr) < 0; +} +bool IntegerComparison::unsafe_compare_less_equal( + const void* lhs, + const void* rhs +) const noexcept +{ + RPY_DBG_ASSERT_NE(lhs, nullptr); + RPY_DBG_ASSERT_NE(rhs, nullptr); + + const auto* lhs_ptr = static_cast(lhs); + const auto* rhs_ptr = static_cast(rhs); + + return mpz_cmp(lhs_ptr, rhs_ptr) <= 0; +} +bool IntegerComparison::unsafe_compare_greater( + const void* lhs, + const void* rhs +) const noexcept +{ + RPY_DBG_ASSERT_NE(lhs, nullptr); + RPY_DBG_ASSERT_NE(rhs, nullptr); + + const auto* lhs_ptr = static_cast(lhs); + const auto* rhs_ptr = static_cast(rhs); + + return mpz_cmp(lhs_ptr, rhs_ptr) > 0; +} +bool IntegerComparison::unsafe_compare_greater_equal( + const void* lhs, + const void* rhs +) const noexcept +{ + RPY_DBG_ASSERT_NE(lhs, nullptr); + RPY_DBG_ASSERT_NE(rhs, nullptr); + + const auto* lhs_ptr = static_cast(lhs); + const auto* rhs_ptr = static_cast(rhs); + + return mpz_cmp(lhs_ptr, rhs_ptr) >= 0; +} diff --git a/platform/src/generics/multiprecision_types/integer_comparison.h b/platform/src/generics/multiprecision_types/integer_comparison.h new file mode 100644 index 00000000..6d31af59 --- /dev/null +++ b/platform/src/generics/multiprecision_types/integer_comparison.h @@ -0,0 +1,45 @@ +// +// Created by sammorley on 25/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_INTEGER_COMPARISON_H +#define ROUGHPY_GENERICS_INTERNAL_INTEGER_COMPARISON_H + +#include "roughpy/generics/comparison_trait.h" + +namespace rpy { +namespace generics { + +class IntegerComparison : public ComparisonTrait +{ +public: + explicit IntegerComparison(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; +}; + +}// namespace generics +}// namespace rpy + +#endif// ROUGHPY_GENERICS_INTERNAL_INTEGER_COMPARISON_H diff --git a/platform/src/generics/multiprecision_types/integer_number.cpp b/platform/src/generics/multiprecision_types/integer_number.cpp new file mode 100644 index 00000000..ee77ddea --- /dev/null +++ b/platform/src/generics/multiprecision_types/integer_number.cpp @@ -0,0 +1,81 @@ +// +// Created by sammorley on 25/11/24. +// + +#include "integer_number.h" + +#include + +#include "roughpy/core/debug_assertion.h" +#include "roughpy/core/macros.h" +#include "roughpy/core/types.h" + +#include + +using namespace rpy; +using namespace rpy::generics; + +bool IntegerNumber::has_function(NumberFunction fn_id) const noexcept +{ + switch (fn_id) { + case NumberFunction::Abs: RPY_FALLTHROUGH; + case NumberFunction::Pow: RPY_FALLTHROUGH; + case NumberFunction::Real: RPY_FALLTHROUGH; + case NumberFunction::Imaginary: RPY_FALLTHROUGH; + case NumberFunction::FromRational: return true; + case NumberFunction::Sqrt: RPY_FALLTHROUGH; + case NumberFunction::Exp: RPY_FALLTHROUGH; + case NumberFunction::Log: return false; + } + RPY_UNREACHABLE_RETURN(false); +} +void IntegerNumber::unsafe_real(void* dst, const void* src) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + RPY_DBG_ASSERT_NE(src, nullptr); + + auto* src_ptr = static_cast(src); + auto* dst_ptr = static_cast(dst); + + mpz_set(dst_ptr, src_ptr); +} +void IntegerNumber::unsafe_imaginary(void* dst, const void* src) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + ignore_unused(src); + + auto* dst_ptr = static_cast(dst); + + mpz_set_si(dst_ptr, 0); +} +void IntegerNumber::unsafe_abs(void* dst, const void* src) const noexcept +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + RPY_DBG_ASSERT_NE(src, nullptr); + + auto* src_ptr = static_cast(src); + auto* dst_ptr = static_cast(dst); + + mpz_abs(dst_ptr, src_ptr); +} + +void IntegerNumber::unsafe_from_rational( + void* dst, + int64_t numerator, + int64_t denominator +) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + RPY_CHECK_NE(denominator, 0); + + if (numerator < 0) { + numerator = -numerator; + denominator = -denominator; + } + + RPY_CHECK_EQ(denominator, 1); + + auto* dst_ptr = static_cast(dst); + + mpz_set_si(dst_ptr, numerator); +} diff --git a/platform/src/generics/multiprecision_types/integer_number.h b/platform/src/generics/multiprecision_types/integer_number.h new file mode 100644 index 00000000..145e74cb --- /dev/null +++ b/platform/src/generics/multiprecision_types/integer_number.h @@ -0,0 +1,33 @@ +// +// Created by sammorley on 25/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_INTEGER_NUMBER_H +#define ROUGHPY_GENERICS_INTERNAL_INTEGER_NUMBER_H + +#include "roughpy/generics/number_trait.h" + +namespace rpy { +namespace generics { + +class IntegerNumber : public NumberTrait +{ + +public: + explicit IntegerNumber(const Type* type) : NumberTrait(type, type) {} + + bool has_function(NumberFunction fn_id) const noexcept override; + void unsafe_real(void* dst, const void* src) const override; + void unsafe_imaginary(void* dst, const void* src) const 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; +}; + +}// namespace generics +}// namespace rpy + +#endif// ROUGHPY_GENERICS_INTERNAL_INTEGER_NUMBER_H diff --git a/platform/src/generics/multiprecision_types/integer_type.cpp b/platform/src/generics/multiprecision_types/integer_type.cpp new file mode 100644 index 00000000..d5b81cb8 --- /dev/null +++ b/platform/src/generics/multiprecision_types/integer_type.cpp @@ -0,0 +1,178 @@ +// +// Created by sammorley on 25/11/24. +// + +#include "integer_type.h" + +#include "mpz_hash.h" + +#include + +#include "roughpy/core/check.h" +#include "roughpy/core/debug_assertion.h" +#include "roughpy/core/hash.h" +#include "roughpy/core/types.h" + +using namespace rpy; +using namespace rpy::generics; + +IntegerType::IntegerType() + : m_arithmetic(this), + m_comparison(this), + m_number(this) +{} +void IntegerType::inc_ref() const noexcept +{ + // Do Nothing +} +bool IntegerType::dec_ref() const noexcept +{ + // do nothing + return false; +} +intptr_t IntegerType::ref_count() const noexcept +{ + return 1; +} +const std::type_info& IntegerType::type_info() const noexcept +{ + return typeid(mpz_t); +} +BasicProperties IntegerType::basic_properties() const noexcept +{ + return {false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false + }; +} +size_t IntegerType::object_size() const noexcept +{ + return sizeof(mpz_ptr); +} +string_view IntegerType::name() const noexcept +{ + return "MultiPrecisionInteger"; +} +string_view IntegerType::id() const noexcept +{ + return "apz"; +} +void* IntegerType::allocate_object() const +{ + auto* new_int = static_cast( + mem::aligned_alloc(alignof(mpz_t), sizeof(mpz_t)) + ); + + if (new_int == nullptr) { throw std::bad_alloc(); } + + mpz_init(new_int); + return new_int; +} +void IntegerType::free_object(void* ptr) const +{ + RPY_DBG_ASSERT_NE(ptr, nullptr); + mpz_clear(static_cast(ptr)); + mem::aligned_free(ptr, sizeof(mpz_t)); +} +bool IntegerType::parse_from_string(void* data, string_view str) const noexcept +{ + RPY_DBG_ASSERT_NE(data, nullptr); + auto* ptr = static_cast(data); + string tmp(str); + return mpz_set_str(ptr, tmp.c_str(), 10) != -1; +} +void IntegerType::copy_or_move( + void* dst, + const void* src, + size_t count, + bool move +) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + + if (RPY_UNLIKELY(count == 0)) { return; } + + auto* dst_ptr = static_cast(dst); + if (src == nullptr) { + for (size_t i = 0; i < count; ++i) { mpz_set_si(++dst_ptr, 0); } + } else if (move) { + auto* src_ptr = static_cast(const_cast(src)); + for (size_t i=0; i(src); + for (size_t i = 0; i < count; ++i, ++src_ptr, ++dst_ptr) { + mpz_set(dst_ptr, src_ptr); + } + } +} +void IntegerType::destroy_range(void* data, size_t count) const +{ + RPY_DBG_ASSERT_NE(data, nullptr); + auto* ptr = static_cast(data); + for (size_t i = 0; i < count; ++i) { mpz_clear(ptr++); } +} +std::unique_ptr +IntegerType::convert_to(const Type& type) const noexcept +{ + return Type::convert_to(type); +} +std::unique_ptr +IntegerType::convert_from(const Type& type) const noexcept +{ + return Type::convert_from(type); +} +const BuiltinTrait* +IntegerType::get_builtin_trait(BuiltinTraitID id) const noexcept +{ + switch (id) { + case BuiltinTraitID::Comparison: + return &m_comparison; + case BuiltinTraitID::Arithmetic: + return &m_arithmetic; + case BuiltinTraitID::Number: + return &m_number; + } + RPY_UNREACHABLE_RETURN(nullptr); +} +const std::ostream& +IntegerType::display(std::ostream& os, const void* value) const +{ + if (value == nullptr) { return os << 0; } + + const auto* ptr = static_cast(value); + + // The GMP docs describe the size of a mpz string representation in the + // documentation https://gmplib.org/manual/Converting-Integers + auto num_chars = mpz_sizeinbase(ptr, 10) + 2; + + string buffer; + buffer.resize(num_chars); + + mpz_get_str(buffer.data(), 10, ptr); + + while (buffer.back() == '\0') { buffer.pop_back(); } + return os << buffer; +} +hash_t IntegerType::hash_of(const void* value) const noexcept +{ + if (value == nullptr) { return 0; } + + const auto* ptr = static_cast(value); + return mpz_hash(ptr); +} +TypePtr IntegerType::get() noexcept +{ + static IntegerType tp; + return &tp; +} diff --git a/platform/src/generics/multiprecision_types/integer_type.h b/platform/src/generics/multiprecision_types/integer_type.h new file mode 100644 index 00000000..e081c944 --- /dev/null +++ b/platform/src/generics/multiprecision_types/integer_type.h @@ -0,0 +1,64 @@ +// +// Created by sammorley on 25/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_INTEGER_TYPE_H +#define ROUGHPY_GENERICS_INTERNAL_INTEGER_TYPE_H + +#include "roughpy/generics/type.h" + +#include "integer_arithmetic.h" +#include "integer_comparison.h" +#include "integer_number.h" + +namespace rpy { +namespace generics { + +class IntegerType : public Type { + IntegerArithmetic m_arithmetic; + IntegerComparison m_comparison; + IntegerNumber m_number; + + IntegerType(); +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_INTEGER_TYPE_H diff --git a/platform/src/generics/multiprecision_types/mpz_hash.h b/platform/src/generics/multiprecision_types/mpz_hash.h new file mode 100644 index 00000000..9e91050e --- /dev/null +++ b/platform/src/generics/multiprecision_types/mpz_hash.h @@ -0,0 +1,30 @@ +// +// Created by sammorley on 25/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_MPZ_HASH_H +#define ROUGHPY_GENERICS_INTERNAL_MPZ_HASH_H + +#include + +#include + +namespace rpy::generics { + + +inline hash_t mpz_hash(mpz_srcptr integer) noexcept +{ + const size_t nlimbs = mpz_size(integer); + const auto* limbs = mpz_limbs_read(integer); + + hash_t result = Hash{}(integer->_mp_size); + for (size_t i = 0; i < nlimbs; ++i) { + hash_combine(result, limbs[i]); + } + return result; +} + + +} + +#endif //ROUGHPY_GENERICS_INTERNAL_MPZ_HASH_H diff --git a/platform/src/generics/multiprecision_types/rational_arithmetic.cpp b/platform/src/generics/multiprecision_types/rational_arithmetic.cpp new file mode 100644 index 00000000..d9931ead --- /dev/null +++ b/platform/src/generics/multiprecision_types/rational_arithmetic.cpp @@ -0,0 +1,59 @@ +// +// Created by sammorley on 25/11/24. +// + +#include "rational_arithmetic.h" + +#include + +using namespace rpy; +using namespace rpy::generics; + + +bool RationalArithmetic::has_operation(ArithmeticOperation op) const noexcept +{ + return true; +} +void RationalArithmetic::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(lhs); + auto* dst_ptr = static_cast(lhs); + + mpq_add(dst_ptr, dst_ptr, src_ptr); +} +void RationalArithmetic::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); + + mpq_sub(dst_ptr, dst_ptr, src_ptr); +} +void RationalArithmetic::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); + + mpq_mul(dst_ptr, dst_ptr, src_ptr); +} +void RationalArithmetic::unsafe_div_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); + + mpq_div(dst_ptr, dst_ptr, src_ptr); +} \ No newline at end of file diff --git a/platform/src/generics/multiprecision_types/rational_arithmetic.h b/platform/src/generics/multiprecision_types/rational_arithmetic.h new file mode 100644 index 00000000..bbf50465 --- /dev/null +++ b/platform/src/generics/multiprecision_types/rational_arithmetic.h @@ -0,0 +1,32 @@ +// +// Created by sammorley on 25/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_RATIONAL_ARITHMETIC_H +#define ROUGHPY_GENERICS_INTERNAL_RATIONAL_ARITHMETIC_H + +#include "roughpy/generics/arithmetic_trait.h" + +namespace rpy { +namespace generics { + +class RationalArithmetic : public ArithmeticTrait { + +public: + + explicit RationalArithmetic(const Type* type) + : ArithmeticTrait(type, type) + {} + + RPY_NO_DISCARD bool + has_operation(ArithmeticOperation op) const noexcept override; + void unsafe_add_inplace(void* lhs, const void* rhs) const noexcept override; + void unsafe_sub_inplace(void* lhs, const void* rhs) const override; + void unsafe_mul_inplace(void* lhs, const void* rhs) const override; + void unsafe_div_inplace(void* lhs, const void* rhs) const override; +}; + +} // generics +} // rpy + +#endif //ROUGHPY_GENERICS_INTERNAL_RATIONAL_ARITHMETIC_H diff --git a/platform/src/generics/multiprecision_types/rational_comparison.cpp b/platform/src/generics/multiprecision_types/rational_comparison.cpp new file mode 100644 index 00000000..d8783498 --- /dev/null +++ b/platform/src/generics/multiprecision_types/rational_comparison.cpp @@ -0,0 +1,81 @@ +// +// Created by sammorley on 25/11/24. +// + +#include "rational_comparison.h" + +#include + + +using namespace rpy; +using namespace rpy::generics; + +bool RationalComparison::has_comparison(ComparisonType comp) const noexcept +{ + return true; +} +bool RationalComparison::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 mpq_equal(lhs_ptr, rhs_ptr) != 0; +} +bool RationalComparison::unsafe_compare_less( + 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 mpq_cmp(lhs_ptr, rhs_ptr) < 0; +} +bool RationalComparison::unsafe_compare_less_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 mpq_cmp(lhs_ptr, rhs_ptr) <= 0; +} +bool RationalComparison::unsafe_compare_greater( + 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 mpq_cmp(lhs_ptr, rhs_ptr) > 0; +} +bool RationalComparison::unsafe_compare_greater_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 mpq_cmp(lhs_ptr, rhs_ptr) >= 0; +} diff --git a/platform/src/generics/multiprecision_types/rational_comparison.h b/platform/src/generics/multiprecision_types/rational_comparison.h new file mode 100644 index 00000000..a6c53ce7 --- /dev/null +++ b/platform/src/generics/multiprecision_types/rational_comparison.h @@ -0,0 +1,45 @@ +// +// Created by sammorley on 25/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_RATIONAL_COMPARISON_H +#define ROUGHPY_GENERICS_INTERNAL_RATIONAL_COMPARISON_H + +#include "roughpy/generics/comparison_trait.h" + +namespace rpy { +namespace generics { + +class RationalComparison : public ComparisonTrait { +public: + + explicit RationalComparison(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_RATIONAL_COMPARISON_H diff --git a/platform/src/generics/multiprecision_types/rational_conversion.cpp b/platform/src/generics/multiprecision_types/rational_conversion.cpp new file mode 100644 index 00000000..42d2a8a7 --- /dev/null +++ b/platform/src/generics/multiprecision_types/rational_conversion.cpp @@ -0,0 +1,10 @@ +// +// Created by sammorley on 25/11/24. +// + +#include "rational_conversion.h" + +using namespace rpy; +using namespace rpy::generics; + + diff --git a/platform/src/generics/multiprecision_types/rational_conversion.h b/platform/src/generics/multiprecision_types/rational_conversion.h new file mode 100644 index 00000000..20aa9523 --- /dev/null +++ b/platform/src/generics/multiprecision_types/rational_conversion.h @@ -0,0 +1,18 @@ +// +// Created by sammorley on 25/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_RATIONAL_CONVERSION_H +#define ROUGHPY_GENERICS_INTERNAL_RATIONAL_CONVERSION_H + +namespace rpy { +namespace generics { + +class RationalConversion { + +}; + +} // generics +} // rpy + +#endif //ROUGHPY_GENERICS_INTERNAL_RATIONAL_CONVERSION_H diff --git a/platform/src/generics/multiprecision_types/rational_number.cpp b/platform/src/generics/multiprecision_types/rational_number.cpp new file mode 100644 index 00000000..b10bbf02 --- /dev/null +++ b/platform/src/generics/multiprecision_types/rational_number.cpp @@ -0,0 +1,109 @@ +// +// Created by sammorley on 25/11/24. +// + +#include "rational_number.h" + +#include + + +#include "roughpy/core/debug_assertion.h" +#include "roughpy/core/macros.h" +#include "roughpy/core/types.h" + +using namespace rpy; +using namespace rpy::generics; + +bool RationalNumber::has_function(NumberFunction fn_id) const noexcept +{ + switch (fn_id) { + case NumberFunction::Abs: RPY_FALLTHROUGH; + case NumberFunction::Pow: RPY_FALLTHROUGH; + case NumberFunction::FromRational: RPY_FALLTHROUGH; + case NumberFunction::Real: RPY_FALLTHROUGH; + case NumberFunction::Imaginary: return true; + case NumberFunction::Sqrt: RPY_FALLTHROUGH; + case NumberFunction::Exp: RPY_FALLTHROUGH; + case NumberFunction::Log: return false; + } + RPY_UNREACHABLE_RETURN(false); +} +void RationalNumber::unsafe_real(void* dst, const void* src) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + RPY_DBG_ASSERT_NE(src, nullptr); + + auto* src_ptr = static_cast(src); + auto* dst_ptr = static_cast(dst); + + mpq_set(dst_ptr, src_ptr); +} +void RationalNumber::unsafe_imaginary(void* dst, const void* src) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + ignore_unused(src); + + auto* dst_ptr = static_cast(dst); + + mpq_set_si(dst_ptr, 0, 1); +} +void RationalNumber::unsafe_abs(void* dst, const void* src) const noexcept +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + RPY_DBG_ASSERT_NE(src, nullptr); + + auto* src_ptr = static_cast(src); + auto* dst_ptr = static_cast(dst); + + mpq_abs(dst_ptr, src_ptr); +} +void RationalNumber::unsafe_pow( + void* dst, + const void* base, + exponent_t exponent +) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + RPY_DBG_ASSERT_NE(base, nullptr); + + auto* base_ptr = static_cast(base); + auto* dst_ptr = static_cast(dst); + + if (exponent == 0) { + mpq_set_si(dst_ptr, 1, 1); + } else if (exponent == 1) { + mpq_set(dst_ptr, base_ptr); + } else if (exponent == -1) { + mpq_inv(dst_ptr, base_ptr); + } else if (exponent > 0) { + const auto expo = static_cast(exponent); + mpz_pow_ui(mpq_numref(dst_ptr), mpq_numref(base_ptr), expo); + mpz_pow_ui(mpq_denref(dst_ptr), mpq_denref(base_ptr), expo); + } else { + const auto expo = static_cast(exponent); + mpz_pow_ui(mpq_numref(dst_ptr), mpq_denref(base_ptr), expo); + mpz_pow_ui(mpq_denref(dst_ptr), mpq_numref(base_ptr), expo); + } +} +void RationalNumber::unsafe_from_rational( + void* dst, + int64_t numerator, + int64_t denominator +) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + + // This should have been checked in the outer scope + RPY_DBG_ASSERT_NE(numerator, 0); + + auto* dst_ptr = static_cast(dst); + + if (denominator < 0) { + numerator = -numerator; + denominator = -denominator; + } + + const auto denom = static_cast(denominator); + + mpq_set_si(dst_ptr, numerator, denom); +} diff --git a/platform/src/generics/multiprecision_types/rational_number.h b/platform/src/generics/multiprecision_types/rational_number.h new file mode 100644 index 00000000..7439f6b4 --- /dev/null +++ b/platform/src/generics/multiprecision_types/rational_number.h @@ -0,0 +1,35 @@ +// +// Created by sammorley on 25/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_RATIONAL_NUMBER_H +#define ROUGHPY_GENERICS_INTERNAL_RATIONAL_NUMBER_H + +#include "roughpy/generics/number_trait.h" +namespace rpy { +namespace generics { + +class RationalNumber : public NumberTrait { +public: + + explicit RationalNumber(const Type* type) + : NumberTrait(type, type) + {} + + bool has_function(NumberFunction fn_id) const noexcept override; + void unsafe_real(void* dst, const void* src) const override; + void unsafe_imaginary(void* dst, const void* src) const override; + void unsafe_abs(void* dst, const void* src) const noexcept override; + void + unsafe_pow(void* dst, const void* base, exponent_t exponent) const override; + void unsafe_from_rational( + void* dst, + int64_t numerator, + int64_t denominator + ) const override; +}; + +} // generics +} // rpy + +#endif //ROUGHPY_GENERICS_INTERNAL_RATIONAL_NUMBER_H diff --git a/platform/src/generics/multiprecision_types/rational_type.cpp b/platform/src/generics/multiprecision_types/rational_type.cpp new file mode 100644 index 00000000..53f43e25 --- /dev/null +++ b/platform/src/generics/multiprecision_types/rational_type.cpp @@ -0,0 +1,179 @@ +// +// Created by sammorley on 25/11/24. +// + +#include "rational_type.h" + +#include + +#include + +#include + +#include "mpz_hash.h" + +using namespace rpy; +using namespace rpy::generics; + +RationalType::RationalType() + : m_arithmetic(this), + m_comparison(this), + m_number(this) +{} + +void RationalType::inc_ref() const noexcept +{ + // Do Nothing +} +bool RationalType::dec_ref() const noexcept +{ + // do nothing + return false; +} +intptr_t RationalType::ref_count() const noexcept { return 1; } +const std::type_info& RationalType::type_info() const noexcept +{ + return typeid(mpq_t); +} +BasicProperties RationalType::basic_properties() const noexcept +{ + return {false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false}; +} +size_t RationalType::object_size() const noexcept { return sizeof(mpq_ptr); } +string_view RationalType::name() const noexcept { return "rational"; } +string_view RationalType::id() const noexcept { return "apr"; } +void* RationalType::allocate_object() const +{ + auto* new_rat = static_cast( + mem::aligned_alloc(alignof(mpq_t), sizeof(mpq_t)) + ); + + if (new_rat == nullptr) { throw std::bad_alloc(); } + + mpq_init(new_rat); + return new_rat; +} +void RationalType::free_object(void* ptr) const +{ + RPY_DBG_ASSERT_NE(ptr, nullptr); + mpq_clear(static_cast(ptr)); + mem::aligned_free(ptr); +} +bool RationalType::parse_from_string(void* data, string_view str) const noexcept +{ + RPY_DBG_ASSERT_NE(data, nullptr); + + auto* ptr = static_cast(data); + string tmp(str); + + if (mpq_set_str(ptr, tmp.c_str(), 10) == -1) { return false; } + + // Just in case the string was not a canonical rational number, reduce it + mpq_canonicalize(ptr); + + return true; +} +void RationalType::copy_or_move( + void* dst, + const void* src, + size_t count, + bool move +) const +{ + RPY_DBG_ASSERT_NE(dst, nullptr); + + if (count == 0) { return; } + + auto* dst_ptr = static_cast(dst); + if (src == nullptr) { + for (size_t i = 0; i < count; ++i, ++dst_ptr) { + mpq_set_si(dst_ptr, 0, 1); + } + } else if (move) { + auto* src_ptr = static_cast(const_cast(src)); + for (size_t i = 0; i < count; ++i, ++src_ptr, ++dst_ptr) { + mpq_swap(dst_ptr, src_ptr); + mpq_clear(src_ptr); + } + } else { + const auto* src_ptr = static_cast(src); + for (size_t i = 0; i < count; ++i, ++src_ptr, ++dst_ptr) { + mpq_set(dst_ptr, src_ptr); + } + } +} +void RationalType::destroy_range(void* data, size_t count) const +{ + RPY_DBG_ASSERT_NE(data, nullptr); + auto* ptr = static_cast(data); + for (size_t i = 0; i < count; ++i) { mpq_clear(ptr++); } +} +std::unique_ptr +RationalType::convert_to(const Type& type) const noexcept +{ + return Type::convert_to(type); +} +std::unique_ptr +RationalType::convert_from(const Type& type) const noexcept +{ + return Type::convert_from(type); +} +const BuiltinTrait* +RationalType::get_builtin_trait(BuiltinTraitID id) const noexcept +{ + switch (id) { + case BuiltinTraitID::Comparison: return &m_comparison; + case BuiltinTraitID::Arithmetic: return &m_arithmetic; + case BuiltinTraitID::Number: return &m_number; + } + RPY_UNREACHABLE_RETURN(nullptr); +} +const std::ostream& +RationalType::display(std::ostream& os, const void* value) const +{ + if (value == nullptr) { return os << 0; } + + 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); + + // The buffer has at least one null byte at the end, cut these off + while (buffer.back() == '\0') { buffer.pop_back(); } + return os << buffer; +} + +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; +} + +TypePtr RationalType::get() noexcept +{ + static RationalType tp; + return &tp; +} diff --git a/platform/src/generics/multiprecision_types/rational_type.h b/platform/src/generics/multiprecision_types/rational_type.h new file mode 100644 index 00000000..3a4203de --- /dev/null +++ b/platform/src/generics/multiprecision_types/rational_type.h @@ -0,0 +1,72 @@ +// +// Created by sammorley on 25/11/24. +// + +#ifndef ROUGHPY_GENERICS_INTERNAL_RATIONAL_TYPE_H +#define ROUGHPY_GENERICS_INTERNAL_RATIONAL_TYPE_H + +#include +#include + +#include + + +#include "roughpy/generics/type.h" + + +#include "rational_arithmetic.h" +#include "rational_conversion.h" +#include "rational_number.h" +#include "rational_comparison.h" + +namespace rpy::generics { + +class RationalType : public Type { + RationalArithmetic m_arithmetic; + RationalComparison m_comparison; + RationalNumber m_number; + + RationalType(); + +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: + RPY_NO_DISCARD 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; +}; + +} // rpy::generics + +#endif //ROUGHPY_GENERICS_INTERNAL_RATIONAL_TYPE_H diff --git a/platform/src/generics/multiprecision_types/test_float_type.cpp b/platform/src/generics/multiprecision_types/test_float_type.cpp new file mode 100644 index 00000000..1604e960 --- /dev/null +++ b/platform/src/generics/multiprecision_types/test_float_type.cpp @@ -0,0 +1,249 @@ +// +// Created by sammorley on 25/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 TestMPFloatType : public ::testing::Test +{ +public: + const size_t precision = 75; +protected: + + void SetUp() override + { + float_type = MultiPrecisionTypes::get().float_type(precision); + if (float_type == nullptr) { + FAIL() << "Failed to create float type with precision " << precision; + } + } + +public: + const ComparisonTrait* comp_trait() const noexcept + { + return trait_cast( + float_type->get_builtin_trait(BuiltinTraitID::Comparison) + ); + } + + const ArithmeticTrait* arith_trait() const noexcept + { + return trait_cast( + float_type->get_builtin_trait(BuiltinTraitID::Arithmetic) + ); + } + + const NumberTrait* num_trait() const noexcept + { + return trait_cast( + float_type->get_builtin_trait(BuiltinTraitID::Number) + ); + } + + + TypePtr float_type; + +}; + + +} + + + + +TEST_F(TestMPFloatType, TestID) +{ + EXPECT_EQ(float_type->id(), "apf"); + EXPECT_EQ(float_type->name(), "MultiPrecisionFloat"); +} + +TEST_F(TestMPFloatType, TestRefCounting) +{ + EXPECT_EQ(float_type->ref_count(), 1); + + { + RPY_MAYBE_UNUSED TypePtr new_ref(float_type); + EXPECT_EQ(float_type->ref_count(), 2); + } + + EXPECT_EQ(float_type->ref_count(), 1); +} + +TEST_F(TestMPFloatType, TestBasicProperties) +{ + EXPECT_EQ(float_type->object_size(), sizeof(void*)); + + EXPECT_FALSE(concepts::is_standard_layout(*float_type)); + EXPECT_FALSE(concepts::is_trivially_copyable(*float_type)); + EXPECT_FALSE(concepts::is_trivially_constructible(*float_type)); + EXPECT_FALSE(concepts::is_trivially_default_constructible(*float_type)); + EXPECT_FALSE(concepts::is_trivially_copy_constructible(*float_type)); + EXPECT_FALSE(concepts::is_trivially_copy_assignable(*float_type)); + EXPECT_FALSE(concepts::is_trivially_destructible(*float_type)); + EXPECT_FALSE(concepts::is_polymorphic(*float_type)); + EXPECT_TRUE(concepts::is_signed(*float_type)); + EXPECT_FALSE(concepts::is_unsigned(*float_type)); + EXPECT_FALSE(concepts::is_integral(*float_type)); + EXPECT_FALSE(concepts::is_arithmetic(*float_type)); +} + + +TEST_F(TestMPFloatType, TestDisplayAndParseFromString) +{ + Value value(float_type, string_view("1234567890")); + std::stringstream ss; + float_type->display(ss, value.data()); + EXPECT_EQ(ss.str(), "1234567890"); +} + + +/****************************************************************************** + * Comparison * + ******************************************************************************/ + +TEST_F(TestMPFloatType, TestHasEqualOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + + EXPECT_TRUE(comp->has_comparison(ComparisonType::Equal)); +} + +TEST_F(TestMPFloatType, TestHasLessThanOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_TRUE(comp->has_comparison(ComparisonType::Less)); +} + +TEST_F(TestMPFloatType, TestHasGreaterThanOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_TRUE(comp->has_comparison(ComparisonType::Greater)); +} + +TEST_F(TestMPFloatType, TestHasLessThanOrEqualOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_TRUE(comp->has_comparison(ComparisonType::LessEqual)); +} + +TEST_F(TestMPFloatType, TestHasGreaterThanOrEqualOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_TRUE(comp->has_comparison(ComparisonType::GreaterEqual)); +} + +/****************************************************************************** + * Arithmetic * + ******************************************************************************/ + +TEST_F(TestMPFloatType, TestAddOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Add)); +} + +TEST_F(TestMPFloatType, TestSubtractOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Sub)); +} + +TEST_F(TestMPFloatType, TestMultiplyOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Mul)); +} + +TEST_F(TestMPFloatType, TestDivideOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Div)); +} + +/****************************************************************************** + * Number-like * + ******************************************************************************/ + +TEST_F(TestMPFloatType, TestRealAndImaginary) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + + EXPECT_TRUE(num->has_function(NumberFunction::Real)); + EXPECT_TRUE(num->has_function(NumberFunction::Imaginary)); + +} + +TEST_F(TestMPFloatType, TestAbsFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + EXPECT_TRUE(num->has_function(NumberFunction::Abs)); +} + +TEST_F(TestMPFloatType, TestSqrtFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + + EXPECT_TRUE(num->has_function(NumberFunction::Sqrt)); +} + +TEST_F(TestMPFloatType, TestExpFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + EXPECT_TRUE(num->has_function(NumberFunction::Exp)); +} + +TEST_F(TestMPFloatType, TestLogFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + EXPECT_TRUE(num->has_function(NumberFunction::Log)); +} + +TEST_F(TestMPFloatType, TestPowFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + EXPECT_TRUE(num->has_function(NumberFunction::Pow)); +} + + +TEST_F(TestMPFloatType, TestFromRationalFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + EXPECT_TRUE(num->has_function(NumberFunction::FromRational)); + +} + + diff --git a/platform/src/generics/multiprecision_types/test_integer_type.cpp b/platform/src/generics/multiprecision_types/test_integer_type.cpp new file mode 100644 index 00000000..2ace17fe --- /dev/null +++ b/platform/src/generics/multiprecision_types/test_integer_type.cpp @@ -0,0 +1,234 @@ +// +// Created by sammorley on 25/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 TestIntegerType : public ::testing::Test +{ +protected: + + void SetUp() override + { + integer_type = MultiPrecisionTypes::get().integer_type; + + } + +public: + const ComparisonTrait* comp_trait() const noexcept + { + return trait_cast( + integer_type->get_builtin_trait(BuiltinTraitID::Comparison) + ); + } + + const ArithmeticTrait* arith_trait() const noexcept + { + return trait_cast( + integer_type->get_builtin_trait(BuiltinTraitID::Arithmetic) + ); + } + + const NumberTrait* num_trait() const noexcept + { + return trait_cast( + integer_type->get_builtin_trait(BuiltinTraitID::Number) + ); + } + + + TypePtr integer_type; + +}; + + +} + + + +TEST_F(TestIntegerType, TestID) +{ + EXPECT_EQ(integer_type->id(), "apz"); + EXPECT_EQ(integer_type->name(), "MultiPrecisionInteger"); +} + + +TEST_F(TestIntegerType, TestRefCounting) +{ + EXPECT_EQ(integer_type->ref_count(), 1); + + { + RPY_MAYBE_UNUSED TypePtr new_ref(integer_type); + EXPECT_EQ(integer_type->ref_count(), 1); + } + + EXPECT_EQ(integer_type->ref_count(), 1); +} + +TEST_F(TestIntegerType, TestBasicProperties) +{ + EXPECT_EQ(integer_type->object_size(), sizeof(void*)); + + EXPECT_FALSE(concepts::is_standard_layout(*integer_type)); + EXPECT_FALSE(concepts::is_trivially_copyable(*integer_type)); + EXPECT_FALSE(concepts::is_trivially_constructible(*integer_type)); + EXPECT_FALSE(concepts::is_trivially_default_constructible(*integer_type)); + EXPECT_FALSE(concepts::is_trivially_copy_constructible(*integer_type)); + EXPECT_FALSE(concepts::is_trivially_copy_assignable(*integer_type)); + EXPECT_FALSE(concepts::is_trivially_destructible(*integer_type)); + EXPECT_FALSE(concepts::is_polymorphic(*integer_type)); + EXPECT_TRUE(concepts::is_signed(*integer_type)); + EXPECT_FALSE(concepts::is_unsigned(*integer_type)); + EXPECT_FALSE(concepts::is_integral(*integer_type)); + EXPECT_FALSE(concepts::is_arithmetic(*integer_type)); +} + + +TEST_F(TestIntegerType, TestDisplayAndParseFromString) +{ + Value value(integer_type, string_view("1234567890")); + std::stringstream ss; + integer_type->display(ss, value.data()); + EXPECT_EQ(ss.str(), "1234567890"); +} + + +/****************************************************************************** + * Comparison * + ******************************************************************************/ + + TEST_F(TestIntegerType, TestHasEqualOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + + EXPECT_TRUE(comp->has_comparison(ComparisonType::Equal)); +} + +TEST_F(TestIntegerType, TestHasLessThanOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_TRUE(comp->has_comparison(ComparisonType::Less)); +} + +TEST_F(TestIntegerType, TestHasGreaterThanOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_TRUE(comp->has_comparison(ComparisonType::Greater)); +} + +TEST_F(TestIntegerType, TestHasLessThanOrEqualOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_TRUE(comp->has_comparison(ComparisonType::LessEqual)); +} + +TEST_F(TestIntegerType, TestHasGreaterThanOrEqualOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_TRUE(comp->has_comparison(ComparisonType::GreaterEqual)); +} + +/****************************************************************************** + * Arithmetic * + ******************************************************************************/ + +TEST_F(TestIntegerType, TestAddOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Add)); +} + +TEST_F(TestIntegerType, TestSubtractOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Sub)); +} + +TEST_F(TestIntegerType, TestMultiplyOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Mul)); +} + +TEST_F(TestIntegerType, TestDivideOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_FALSE(arith->has_operation(ArithmeticOperation::Div)); +} + +/****************************************************************************** + * Number-like * + ******************************************************************************/ + +TEST_F(TestIntegerType, TestRealAndImaginary) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + + EXPECT_TRUE(num->has_function(NumberFunction::Real)); + EXPECT_TRUE(num->has_function(NumberFunction::Imaginary)); + +} + +TEST_F(TestIntegerType, TestAbsFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + EXPECT_TRUE(num->has_function(NumberFunction::Abs)); +} + +TEST_F(TestIntegerType, TestSqrtExpLogFunction) +{ + // Rational numbers do not have sqrt, exp, or log. + 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(TestIntegerType, TestPowFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + EXPECT_TRUE(num->has_function(NumberFunction::Pow)); +} + + +TEST_F(TestIntegerType, TestFromRationalFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + EXPECT_TRUE(num->has_function(NumberFunction::FromRational)); + +} diff --git a/platform/src/generics/multiprecision_types/test_rational_type.cpp b/platform/src/generics/multiprecision_types/test_rational_type.cpp new file mode 100644 index 00000000..275cab29 --- /dev/null +++ b/platform/src/generics/multiprecision_types/test_rational_type.cpp @@ -0,0 +1,217 @@ +// +// Created by sammorley on 25/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 TestRationalType : public ::testing::Test +{ +protected: + void SetUp() override { rational_type = MultiPrecisionTypes::get().rational_type; } + +public: + const ComparisonTrait* comp_trait() const noexcept + { + return trait_cast( + rational_type->get_builtin_trait(BuiltinTraitID::Comparison) + ); + } + + const ArithmeticTrait* arith_trait() const noexcept + { + return trait_cast( + rational_type->get_builtin_trait(BuiltinTraitID::Arithmetic) + ); + } + + const NumberTrait* num_trait() const noexcept + { + return trait_cast( + rational_type->get_builtin_trait(BuiltinTraitID::Number) + ); + } + + TypePtr rational_type; +}; + +}// namespace + +TEST_F(TestRationalType, TestID) +{ + EXPECT_EQ(rational_type->id(), "apr"); + EXPECT_EQ(rational_type->name(), "rational"); +} + +TEST_F(TestRationalType, TestRefCounting) +{ + EXPECT_EQ(rational_type->ref_count(), 1); + + { + RPY_MAYBE_UNUSED TypePtr new_ref(rational_type); + EXPECT_EQ(rational_type->ref_count(), 1); + } + + EXPECT_EQ(rational_type->ref_count(), 1); +} + +TEST_F(TestRationalType, TestBasicProperties) +{ + EXPECT_EQ(rational_type->object_size(), sizeof(void*)); + + EXPECT_FALSE(concepts::is_standard_layout(*rational_type)); + EXPECT_FALSE(concepts::is_trivially_copyable(*rational_type)); + EXPECT_FALSE(concepts::is_trivially_constructible(*rational_type)); + EXPECT_FALSE(concepts::is_trivially_default_constructible(*rational_type)); + EXPECT_FALSE(concepts::is_trivially_copy_constructible(*rational_type)); + EXPECT_FALSE(concepts::is_trivially_copy_assignable(*rational_type)); + EXPECT_FALSE(concepts::is_trivially_destructible(*rational_type)); + EXPECT_FALSE(concepts::is_polymorphic(*rational_type)); + EXPECT_TRUE(concepts::is_signed(*rational_type)); + EXPECT_FALSE(concepts::is_unsigned(*rational_type)); + EXPECT_FALSE(concepts::is_integral(*rational_type)); + EXPECT_FALSE(concepts::is_arithmetic(*rational_type)); +} + +TEST_F(TestRationalType, TestDisplayAndParseFromString) +{ + Value value(rational_type, string_view("253/17")); + std::stringstream ss; + rational_type->display(ss, value.data()); + EXPECT_EQ(ss.str(), "253/17"); +} + +/****************************************************************************** + * Comparison * + ******************************************************************************/ + +TEST_F(TestRationalType, TestHasEqualOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + + EXPECT_TRUE(comp->has_comparison(ComparisonType::Equal)); +} + +TEST_F(TestRationalType, TestHasLessThanOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_TRUE(comp->has_comparison(ComparisonType::Less)); +} + +TEST_F(TestRationalType, TestHasGreaterThanOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_TRUE(comp->has_comparison(ComparisonType::Greater)); +} + +TEST_F(TestRationalType, TestHasLessThanOrEqualOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_TRUE(comp->has_comparison(ComparisonType::LessEqual)); +} + +TEST_F(TestRationalType, TestHasGreaterThanOrEqualOperator) +{ + auto* comp = comp_trait(); + ASSERT_NE(comp, nullptr); + EXPECT_TRUE(comp->has_comparison(ComparisonType::GreaterEqual)); +} + +/****************************************************************************** + * Arithmetic * + ******************************************************************************/ + +TEST_F(TestRationalType, TestAddOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Add)); +} + +TEST_F(TestRationalType, TestSubtractOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Sub)); +} + +TEST_F(TestRationalType, TestMultiplyOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Mul)); +} + +TEST_F(TestRationalType, TestDivideOperator) +{ + auto* arith = arith_trait(); + ASSERT_NE(arith, nullptr); + + EXPECT_TRUE(arith->has_operation(ArithmeticOperation::Div)); +} + +/****************************************************************************** + * Number-like * + ******************************************************************************/ + +TEST_F(TestRationalType, TestRealAndImaginary) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + + EXPECT_TRUE(num->has_function(NumberFunction::Real)); + EXPECT_TRUE(num->has_function(NumberFunction::Imaginary)); + +} + +TEST_F(TestRationalType, TestAbsFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + EXPECT_TRUE(num->has_function(NumberFunction::Abs)); +} + +TEST_F(TestRationalType, TestSqrtExpLogFunction) +{ + // Rational numbers do not have sqrt, exp, or log. + 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(TestRationalType, TestPowFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + EXPECT_TRUE(num->has_function(NumberFunction::Pow)); +} + + +TEST_F(TestRationalType, TestFromRationalFunction) +{ + const auto* num = num_trait(); + ASSERT_NE(num, nullptr); + EXPECT_TRUE(num->has_function(NumberFunction::FromRational)); + +} \ No newline at end of file diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json index 8455745c..78492010 100644 --- a/vcpkg-configuration.json +++ b/vcpkg-configuration.json @@ -2,7 +2,7 @@ "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg-configuration.schema.json", "default-registry": { "kind": "git", - "baseline": "9558037875497b9db8cf38fcd7db68ec661bffe7", + "baseline": "5e5d0e1cd7785623065e77eff011afdeec1a3574", "repository": "https://github.com/microsoft/vcpkg" }, "overlay-ports": [ diff --git a/vcpkg.json b/vcpkg.json index 04fc4db5..2648405c 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,76 +1,97 @@ { - "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" : "gmp", - "version>=" : "6.2.1#21", - "platform" : "!windows" - }, { - "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" : "mpir", - "version>=" : "2022-03-02#3", - "platform" : "windows" - }, { - "name" : "range-v3", - "version>=" : "0.12.0#4" - } ], - "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" + } + ], + "features": { + "tests": { + "description": "dependencies for the unit tests", + "dependencies": [ + { + "name": "gtest", + "version>=": "1.13.0" + } + ] } } -} \ No newline at end of file +}