From b24422f0dbdf9197cf007ecfcc0b001be9542e1b Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 18 Feb 2023 14:17:54 -0700 Subject: [PATCH 01/65] adding to todo and reving version number --- develop/ToDo.md | 10 ++++++++++ docs/markdown/ReleaseNotes.md | 4 ++++ include/NumCpp/Core/Internal/Version.hpp | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/develop/ToDo.md b/develop/ToDo.md index 464090415..add16ea2e 100644 --- a/develop/ToDo.md +++ b/develop/ToDo.md @@ -1 +1,11 @@ # TODO + +* issue #177: slice with integer index types +* issue #170: insert() +* issue #74, #147, #174: broadcasting + +* default IntegerType = size_type or index_type +* remove c++14 type_traits helpers in favour of std versions +* remove Core/Internal/Filesystem in favour of std::filesystem +* setup job for clang-tidy +* setup job for cpp-check diff --git a/docs/markdown/ReleaseNotes.md b/docs/markdown/ReleaseNotes.md index 79fba8d59..5cc1346cb 100644 --- a/docs/markdown/ReleaseNotes.md +++ b/docs/markdown/ReleaseNotes.md @@ -2,6 +2,10 @@ ## Version 2.9.0 +* + +## Version 2.9.0 + * dropped support of C++14 standard, now requires C++17 or greater * added a new `DateTime` class for working with date times * added `split` for **Issue #168** diff --git a/include/NumCpp/Core/Internal/Version.hpp b/include/NumCpp/Core/Internal/Version.hpp index 8494d0dae..5e9ef184a 100644 --- a/include/NumCpp/Core/Internal/Version.hpp +++ b/include/NumCpp/Core/Internal/Version.hpp @@ -29,5 +29,5 @@ namespace nc { - constexpr char VERSION[] = "2.9.0"; ///< Current NumCpp version number + constexpr char VERSION[] = "2.10.0"; ///< Current NumCpp version number } // namespace nc From 6a4bc347b85f2f2d740af65c7c6de13322e1232f Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 18 Feb 2023 14:39:01 -0700 Subject: [PATCH 02/65] removed type_trait adapters --- .../NumCpp/Core/Internal/StaticAsserts.hpp | 12 +-- include/NumCpp/Core/Internal/TypeTraits.hpp | 80 +++++++++---------- include/NumCpp/Functions/fmod.hpp | 4 +- include/NumCpp/Functions/hammingEncode.hpp | 20 ++--- include/NumCpp/Functions/take.hpp | 7 +- include/NumCpp/Linalg/det.hpp | 4 +- include/NumCpp/Linalg/gaussNewtonNlls.hpp | 6 +- include/NumCpp/NdArray/NdArrayCore.hpp | 71 ++++++++-------- include/NumCpp/NdArray/NdArrayOperators.hpp | 12 +-- include/NumCpp/Utils/essentiallyEqual.hpp | 12 +-- 10 files changed, 116 insertions(+), 112 deletions(-) diff --git a/include/NumCpp/Core/Internal/StaticAsserts.hpp b/include/NumCpp/Core/Internal/StaticAsserts.hpp index e6bcdf8c1..301d5548b 100644 --- a/include/NumCpp/Core/Internal/StaticAsserts.hpp +++ b/include/NumCpp/Core/Internal/StaticAsserts.hpp @@ -35,17 +35,17 @@ static_assert(nc::is_valid_dtype_v, "Template type is not a valid dtype for NdArray") #define STATIC_ASSERT_ARITHMETIC(dtype) \ - static_assert(nc::is_arithmetic_v, "Can only be used with arithmetic types") + static_assert(std::is_arithmetic_v, "Can only be used with arithmetic types") -#define STATIC_ASSERT_INTEGER(dtype) static_assert(nc::is_integral_v, "Can only be used with integer types") +#define STATIC_ASSERT_INTEGER(dtype) static_assert(std::is_integral_v, "Can only be used with integer types") #define STATIC_ASSERT_UNSIGNED_INTEGER(dtype) \ - static_assert(nc::is_integral_v && nc::is_unsigned_v, "Can only be used with integer types") + static_assert(std::is_integral_v && std::is_unsigned_v, "Can only be used with integer types") -#define STATIC_ASSERT_FLOAT(dtype) static_assert(nc::is_floating_point_v, "Can only be used with float types") +#define STATIC_ASSERT_FLOAT(dtype) static_assert(std::is_floating_point_v, "Can only be used with float types") #define STATIC_ASSERT_COMPLEX(dtype) static_assert(nc::is_complex_v, "Can only be used with std::complex types") -#define STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype) \ - static_assert(nc::is_arithmetic_v || nc::is_complex_v, \ +#define STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype) \ + static_assert(std::is_arithmetic_v || nc::is_complex_v, \ "Can only be used with arithmetic types or std::complex types") diff --git a/include/NumCpp/Core/Internal/TypeTraits.hpp b/include/NumCpp/Core/Internal/TypeTraits.hpp index b858b8b78..fe81cf92b 100644 --- a/include/NumCpp/Core/Internal/TypeTraits.hpp +++ b/include/NumCpp/Core/Internal/TypeTraits.hpp @@ -32,48 +32,6 @@ namespace nc { - //============================================================================ - // Class Description: - /// std::enable_if helper, for c++14 compatibility - /// - template - using enable_if_t = typename std::enable_if::type; - - //============================================================================ - // Class Description: - /// std::is_same helper, for c++14 compatibility - /// - template - constexpr bool is_same_v = std::is_same::value; - - //============================================================================ - // Class Description: - /// std::is_arithmetic helper, for c++14 compatibility - /// - template - constexpr bool is_arithmetic_v = std::is_arithmetic::value; - - //============================================================================ - // Class Description: - /// std::is_integral helper, for c++14 compatibility - /// - template - constexpr bool is_integral_v = std::is_integral::value; - - //============================================================================ - // Class Description: - /// std::is_unsigned helper, for c++14 compatibility - /// - template - constexpr bool is_unsigned_v = std::is_unsigned::value; - - //============================================================================ - // Class Description: - /// std::is_floating_point helper, for c++14 compatibility - /// - template - constexpr bool is_floating_point_v = std::is_floating_point::value; - //============================================================================ // Class Description: /// Template class for determining if all of the types are arithmetic @@ -164,6 +122,44 @@ namespace nc template constexpr bool is_valid_dtype_v = is_valid_dtype::value; + // Forward declare + template + class NdArray; + + //============================================================================ + // Class Description: + /// Template class for determining if dtype is a valid index type for NdArray + /// + template + struct is_ndarray_int : std::false_type + { + }; + + //============================================================================ + // Class Description: + /// Template class for determining if dtype is a valid index typefor NdArray + /// + + template + struct is_ndarray_int> + { + static constexpr bool value = std::is_integral_v; + }; + + //============================================================================ + // Class Description: + /// is_ndarray_int helper + /// + template + constexpr bool is_ndarray_int_v = is_ndarray_int::value; + + //============================================================================ + // Class Description: + /// is_ndarray_int + /// + template + using ndarray_int_concept = std::enable_if_t, int>; + //============================================================================ // Class Description: /// Template class for determining if type is std::complex<> diff --git a/include/NumCpp/Functions/fmod.hpp b/include/NumCpp/Functions/fmod.hpp index 1cf0da05c..2ea8e7167 100644 --- a/include/NumCpp/Functions/fmod.hpp +++ b/include/NumCpp/Functions/fmod.hpp @@ -48,7 +48,7 @@ namespace nc /// @param inValue2 /// @return value /// - template, int> = 0> + template, int> = 0> dtype fmod(dtype inValue1, dtype inValue2) noexcept { return inValue1 % inValue2; @@ -65,7 +65,7 @@ namespace nc /// @param inValue2 /// @return value /// - template, int> = 0> + template, int> = 0> dtype fmod(dtype inValue1, dtype inValue2) noexcept { return std::fmod(inValue1, inValue2); diff --git a/include/NumCpp/Functions/hammingEncode.hpp b/include/NumCpp/Functions/hammingEncode.hpp index dda6ff742..a9a48e8d3 100644 --- a/include/NumCpp/Functions/hammingEncode.hpp +++ b/include/NumCpp/Functions/hammingEncode.hpp @@ -54,7 +54,7 @@ namespace nc /// @param n integer value /// @return bool true if value is a power of two, else false /// - template, int> = 0> + template, int> = 0> constexpr bool isPowerOfTwo(IntType n) noexcept { // Returns true if the given non-negative integer n is a power of two. @@ -73,7 +73,7 @@ namespace nc /// @return next power of two /// @exception std::invalid_argument if input value is less than zero //// - template, int> = 0> + template, int> = 0> std::size_t nextPowerOfTwo(IntType n) { if (n < 0) @@ -97,7 +97,7 @@ namespace nc /// @return first n powers of two /// @exception std::bad_alloc if unable to allocate for return vector /// - template, int> = 0> + template, int> = 0> std::vector powersOfTwo(IntType n) { auto i = std::size_t{ 0 }; @@ -124,7 +124,7 @@ namespace nc /// @exception std::invalid_argument if input value is less than zero /// @exception std::runtime_error if the number of data bits does not represent a valid Hamming SECDED code /// - template, int> = 0> + template, int> = 0> std::size_t numSecdedParityBitsNeeded(IntType numDataBits) { const auto n = nextPowerOfTwo(numDataBits); @@ -156,8 +156,8 @@ namespace nc /// template, int> = 0, - enable_if_t, int> = 0> + std::enable_if_t, int> = 0, + std::enable_if_t, int> = 0> std::vector dataBitsCovered(IntType1 numDataBits, IntType2 parityBit) { if (!isPowerOfTwo(parityBit)) @@ -235,7 +235,7 @@ namespace nc /// @exception std::invalid_argument if parityBit is not a power of two /// @exception std::bad_alloc if unable to allocate return vector /// - template, int> = 0> + template, int> = 0> bool calculateParity(const std::bitset& data, IntType parityBit) { bool parity = false; @@ -257,7 +257,7 @@ namespace nc /// template, int> = 0> + std::enable_if_t, int> = 0> std::size_t checkBitsConsistent() { const auto numParityBits = detail::numSecdedParityBitsNeeded(DataBits); @@ -279,7 +279,7 @@ namespace nc /// template, int> = 0> + std::enable_if_t, int> = 0> std::bitset extractData(const std::bitset& encodedBits) noexcept { auto dataBits = std::bitset(); @@ -355,7 +355,7 @@ namespace nc /// template, int> = 0> + std::enable_if_t, int> = 0> int decode(std::bitset encodedBits, std::bitset& decodedBits) { const auto numParityBits = detail::checkBitsConsistent(); diff --git a/include/NumCpp/Functions/take.hpp b/include/NumCpp/Functions/take.hpp index e594b6de6..277395ef6 100644 --- a/include/NumCpp/Functions/take.hpp +++ b/include/NumCpp/Functions/take.hpp @@ -44,9 +44,10 @@ namespace nc /// @param inAxis /// @return NdArray /// - template> || is_same_v>, int> = 0> + template< + typename dtype, + typename Indices, + std::enable_if_t> || std::is_same_v>, int> = 0> NdArray take(const NdArray& inArray, const Indices& inIndices, Axis inAxis = Axis::NONE) { switch (inAxis) diff --git a/include/NumCpp/Linalg/det.hpp b/include/NumCpp/Linalg/det.hpp index 290477bec..d71f9d4c9 100644 --- a/include/NumCpp/Linalg/det.hpp +++ b/include/NumCpp/Linalg/det.hpp @@ -54,11 +54,11 @@ namespace nc /// template auto det(const NdArray& inArray, uint32 order) - -> std::conditional_t, int64, double> + -> std::conditional_t, int64, double> { STATIC_ASSERT_ARITHMETIC(dtype); - using ReturnType = std::conditional_t, int64, double>; + using ReturnType = std::conditional_t, int64, double>; if (order == 1) { diff --git a/include/NumCpp/Linalg/gaussNewtonNlls.hpp b/include/NumCpp/Linalg/gaussNewtonNlls.hpp index 7ecfd165e..9e23225d0 100644 --- a/include/NumCpp/Linalg/gaussNewtonNlls.hpp +++ b/include/NumCpp/Linalg/gaussNewtonNlls.hpp @@ -72,9 +72,9 @@ namespace nc /// template, int> = 0, - nc::enable_if_t, int> = 0, - nc::enable_if_t, int> = 0> + std::enable_if_t, int> = 0, + std::enable_if_t, int> = 0, + std::enable_if_t, int> = 0> std::pair, double> gaussNewtonNlls(const uint32 numIterations, const NdArray& coordinates, diff --git a/include/NumCpp/NdArray/NdArrayCore.hpp b/include/NumCpp/NdArray/NdArrayCore.hpp index 778974cd7..e2b250e30 100644 --- a/include/NumCpp/NdArray/NdArrayCore.hpp +++ b/include/NumCpp/NdArray/NdArrayCore.hpp @@ -72,7 +72,7 @@ namespace nc { private: STATIC_ASSERT_VALID_DTYPE(dtype); - static_assert(is_same_v, + static_assert(std::is_same_v, "value_type and Allocator::value_type must match"); using AllocType = typename std::allocator_traits::template rebind_alloc; @@ -415,7 +415,7 @@ namespace nc /// @param inLast /// template::value_type, dtype>, int> = 0> + std::enable_if_t::value_type, dtype>, int> = 0> NdArray(Iterator inFirst, Iterator inLast) : shape_(1, static_cast(std::distance(inFirst, inLast))), size_(shape_.size()) @@ -457,8 +457,8 @@ namespace nc /// template, int> = 0, - std::enable_if_t, int> = 0> + std::enable_if_t, int> = 0, + std::enable_if_t, int> = 0> NdArray(const_pointer inPtr, UIntType1 numRows, UIntType2 numCols) : shape_(numRows, numCols), size_(shape_.size()) @@ -480,7 +480,7 @@ namespace nc /// @param takeOwnership: whether or not to take ownership of the data /// and call delete[] in the destructor. /// - template, int> = 0> + template, int> = 0> NdArray(pointer inPtr, size_type size, BoolType takeOwnership) noexcept : shape_(1, size), size_(size), @@ -500,7 +500,7 @@ namespace nc /// @param takeOwnership: whether or not to take ownership of the data /// and call delete[] in the destructor. /// - template, int> = 0> + template, int> = 0> NdArray(pointer inPtr, uint32 numRows, uint32 numCols, BoolType takeOwnership) noexcept : shape_(numRows, numCols), size_(numRows * numCols), @@ -757,7 +757,7 @@ namespace nc /// @return NdArray /// /// - template>, int> = 0> + template>, int> = 0> NdArray operator[](const Indices& inIndices) const { if (inIndices.max().item() > size_ - 1) @@ -857,7 +857,8 @@ namespace nc /// @return NdArray /// template> || is_same_v>, int> = 0> + std::enable_if_t> || std::is_same_v>, + int> = 0> NdArray operator()(const Indices& rowIndices, int32 colIndex) const { const NdArray colIndices = { colIndex }; @@ -874,7 +875,8 @@ namespace nc /// @return NdArray /// template> || is_same_v>, int> = 0> + std::enable_if_t> || std::is_same_v>, + int> = 0> NdArray operator()(const Indices& rowIndices, Slice colSlice) const { return operator()(rowIndices, toIndices(colSlice, Axis::COL)); @@ -890,7 +892,8 @@ namespace nc /// @return NdArray /// template> || is_same_v>, int> = 0> + std::enable_if_t> || std::is_same_v>, + int> = 0> NdArray operator()(int32 rowIndex, const Indices& colIndices) const { const NdArray rowIndices = { rowIndex }; @@ -907,7 +910,8 @@ namespace nc /// @return NdArray /// template> || is_same_v>, int> = 0> + std::enable_if_t> || std::is_same_v>, + int> = 0> NdArray operator()(Slice rowSlice, const Indices& colIndices) const { return operator()(toIndices(rowSlice, Axis::ROW), colIndices); @@ -922,10 +926,13 @@ namespace nc /// @param colIndices /// @return NdArray /// - template> || is_same_v>, int> = 0, - enable_if_t> || is_same_v>, int> = 0> + template< + typename RowIndices, + typename ColIndices, + std::enable_if_t> || std::is_same_v>, + int> = 0, + std::enable_if_t> || std::is_same_v>, + int> = 0> NdArray operator()(RowIndices rowIndices, ColIndices colIndices) const { rowIndices.sort(); @@ -2030,10 +2037,10 @@ namespace nc /// @return NdArray /// template, int> = 0, - enable_if_t, int> = 0, - enable_if_t, int> = 0> + typename dtype_ = dtype, + std::enable_if_t, int> = 0, + std::enable_if_t, int> = 0, + std::enable_if_t, int> = 0> NdArray astype() const { NdArray outArray(shape_); @@ -2055,10 +2062,10 @@ namespace nc /// @return NdArray /// template, int> = 0, - enable_if_t, int> = 0, - enable_if_t, int> = 0> + typename dtype_ = dtype, + std::enable_if_t, int> = 0, + std::enable_if_t, int> = 0, + std::enable_if_t, int> = 0> NdArray astype() const { NdArray outArray(shape_); @@ -2081,15 +2088,15 @@ namespace nc /// @return NdArray /// template, int> = 0, - enable_if_t, int> = 0, - enable_if_t, int> = 0> + typename dtype_ = dtype, + std::enable_if_t, int> = 0, + std::enable_if_t, int> = 0, + std::enable_if_t, int> = 0> NdArray astype() const { NdArray outArray(shape_); - if (is_same_v) + if (std::is_same_v) { std::copy(cbegin(), cend(), outArray.begin()); } @@ -2114,10 +2121,10 @@ namespace nc /// @return NdArray /// template, int> = 0, - enable_if_t, int> = 0, - enable_if_t, int> = 0> + typename dtype_ = dtype, + std::enable_if_t, int> = 0, + std::enable_if_t, int> = 0, + std::enable_if_t, int> = 0> NdArray astype() const { NdArray outArray(shape_); diff --git a/include/NumCpp/NdArray/NdArrayOperators.hpp b/include/NumCpp/NdArray/NdArrayOperators.hpp index a68a2c4df..5f769a396 100644 --- a/include/NumCpp/NdArray/NdArrayOperators.hpp +++ b/include/NumCpp/NdArray/NdArrayOperators.hpp @@ -1199,7 +1199,7 @@ namespace nc /// @param rhs /// @return NdArray /// - template::value, int> = 0> + template::value, int> = 0> NdArray& operator%=(NdArray& lhs, const NdArray& rhs) { if (lhs.shape() != rhs.shape()) @@ -1220,7 +1220,7 @@ namespace nc /// @param rhs /// @return NdArray /// - template::value, int> = 0> + template::value, int> = 0> NdArray& operator%=(NdArray& lhs, const NdArray& rhs) { if (lhs.shape() != rhs.shape()) @@ -1243,7 +1243,7 @@ namespace nc /// @param rhs /// @return NdArray /// - template::value, int> = 0> + template::value, int> = 0> NdArray& operator%=(NdArray& lhs, dtype rhs) { const auto function = [rhs](dtype& value) -> dtype { return value %= rhs; }; @@ -1261,7 +1261,7 @@ namespace nc /// @param rhs /// @return NdArray /// - template::value, int> = 0> + template::value, int> = 0> NdArray& operator%=(NdArray& lhs, dtype rhs) { const auto function = [rhs](dtype& value) -> void { value = std::fmod(value, rhs); }; @@ -1311,7 +1311,7 @@ namespace nc /// @param rhs /// @return NdArray /// - template::value, int> = 0> + template::value, int> = 0> NdArray operator%(dtype lhs, const NdArray& rhs) { NdArray returnArray(rhs.shape()); @@ -1331,7 +1331,7 @@ namespace nc /// @param rhs /// @return NdArray /// - template::value, int> = 0> + template::value, int> = 0> NdArray operator%(dtype lhs, const NdArray& rhs) { NdArray returnArray(rhs.shape()); diff --git a/include/NumCpp/Utils/essentiallyEqual.hpp b/include/NumCpp/Utils/essentiallyEqual.hpp index aeb653d59..b82e951a6 100644 --- a/include/NumCpp/Utils/essentiallyEqual.hpp +++ b/include/NumCpp/Utils/essentiallyEqual.hpp @@ -47,7 +47,7 @@ namespace nc /// /// @return bool /// - template::value, int> = 0> + template::value, int> = 0> bool essentiallyEqual(dtype inValue1, dtype inValue2) noexcept { return inValue1 == inValue2; @@ -62,7 +62,7 @@ namespace nc /// /// @return bool /// - template::value, int> = 0> + template::value, int> = 0> bool essentiallyEqual(dtype inValue1, dtype inValue2, dtype inEpsilon) noexcept { const auto absValue1 = std::abs(inValue1); @@ -79,7 +79,7 @@ namespace nc /// /// @return bool /// - template::value, int> = 0> + template::value, int> = 0> bool essentiallyEqual(const std::complex& inValue1, const std::complex& inValue2) noexcept { return inValue1 == inValue2; @@ -94,7 +94,7 @@ namespace nc /// /// @return bool /// - template::value, int> = 0> + template::value, int> = 0> bool essentiallyEqual(const std::complex& inValue1, const std::complex& inValue2, const std::complex& inEpsilon) noexcept @@ -113,7 +113,7 @@ namespace nc /// /// @return bool /// - template::value, int> = 0> + template::value, int> = 0> bool essentiallyEqual(dtype inValue1, dtype inValue2) noexcept { return essentiallyEqual(inValue1, inValue2, DtypeInfo::epsilon()); @@ -127,7 +127,7 @@ namespace nc /// /// @return bool /// - template::value, int> = 0> + template::value, int> = 0> bool essentiallyEqual(const std::complex& inValue1, const std::complex& inValue2) noexcept { return essentiallyEqual(inValue1, inValue2, DtypeInfo>::epsilon()); From b73cab77b18581ef2a26e17f7163fe7e3f3a7250 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 18 Feb 2023 14:40:06 -0700 Subject: [PATCH 03/65] added star history to readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8e0116021..a8bfecb39 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,8 @@ Clang: 14 ## [GitHub](https://github.com/dpilger26/NumCpp) +[![Star History Chart](https://api.star-history.com/svg?repos=dpilger26/NumCpp&type=Date)](https://star-history.com/#dpilger26/NumCpp&Date) + ## [Installation](https://github.com/dpilger26/NumCpp/tree/master/docs/markdown/Installation.md) ## [Building](https://github.com/dpilger26/NumCpp/tree/master/docs/markdown/Building.md) From 480a811a1c144422e43722fc3b784f69a18ee09c Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 18 Feb 2023 14:46:31 -0700 Subject: [PATCH 04/65] adding an index type to ndarray --- develop/ToDo.md | 43 ++++++- include/NumCpp/NdArray/NdArrayCore.hpp | 156 +++++++++++++------------ 2 files changed, 122 insertions(+), 77 deletions(-) diff --git a/develop/ToDo.md b/develop/ToDo.md index add16ea2e..51cdeb25a 100644 --- a/develop/ToDo.md +++ b/develop/ToDo.md @@ -4,8 +4,47 @@ * issue #170: insert() * issue #74, #147, #174: broadcasting -* default IntegerType = size_type or index_type -* remove c++14 type_traits helpers in favour of std versions * remove Core/Internal/Filesystem in favour of std::filesystem * setup job for clang-tidy * setup job for cpp-check + +```cpp +template = 0, + ndarray_int_concept = 0> + +//============================================================================ +// Class Description: +/// Template class for determining if dtype is a valid index type for NdArray +/// +template +struct is_ndarray_int : std::false_type +{ +}; + +//============================================================================ +// Class Description: +/// Template class for determining if dtype is a valid index typefor NdArray +/// + +template +struct is_ndarray_int> +{ + static constexpr bool value = std::is_integral_v; +}; + +//============================================================================ +// Class Description: +/// is_ndarray_int helper +/// +template +constexpr bool is_ndarray_int_v = is_ndarray_int::value; + +//============================================================================ +// Class Description: +/// is_ndarray_int +/// +template +using ndarray_int_concept = std::enable_if_t, int>; +``` diff --git a/include/NumCpp/NdArray/NdArrayCore.hpp b/include/NumCpp/NdArray/NdArrayCore.hpp index e2b250e30..27182fb67 100644 --- a/include/NumCpp/NdArray/NdArrayCore.hpp +++ b/include/NumCpp/NdArray/NdArrayCore.hpp @@ -86,6 +86,7 @@ namespace nc using reference = dtype&; using const_reference = const dtype&; using size_type = uint32; + using index_type = int32; using difference_type = typename AllocTraits::difference_type; using iterator = NdArrayIterator; @@ -629,7 +630,7 @@ namespace nc /// @param inIndex /// @return value /// - reference operator[](int32 inIndex) noexcept + reference operator[](index_type inIndex) noexcept { if (inIndex < 0) { @@ -646,7 +647,7 @@ namespace nc /// @param inIndex /// @return value /// - const_reference operator[](int32 inIndex) const noexcept + const_reference operator[](index_type inIndex) const noexcept { if (inIndex < 0) { @@ -664,7 +665,7 @@ namespace nc /// @param inColIndex /// @return value /// - reference operator()(int32 inRowIndex, int32 inColIndex) noexcept + reference operator()(index_type inRowIndex, index_type inColIndex) noexcept { if (inRowIndex < 0) { @@ -687,7 +688,7 @@ namespace nc /// @param inColIndex /// @return value /// - const_reference operator()(int32 inRowIndex, int32 inColIndex) const noexcept + const_reference operator()(index_type inRowIndex, index_type inColIndex) const noexcept { if (inRowIndex < 0) { @@ -716,7 +717,7 @@ namespace nc uint32 counter = 0; NdArray returnArray(1, inSliceCopy.numElements(size_)); - for (int32 i = inSliceCopy.start; i < inSliceCopy.stop; i += inSliceCopy.step) + for (index_type i = inSliceCopy.start; i < inSliceCopy.stop; i += inSliceCopy.step) { returnArray[counter++] = at(i); } @@ -790,9 +791,9 @@ namespace nc uint32 rowCounter = 0; uint32 colCounter = 0; - for (int32 row = inRowSlice.start; row < inRowSlice.stop; row += inRowSlice.step) + for (index_type row = inRowSlice.start; row < inRowSlice.stop; row += inRowSlice.step) { - for (int32 col = inColSlice.start; col < inColSlice.stop; col += inColSlice.step) + for (index_type col = inColSlice.start; col < inColSlice.stop; col += inColSlice.step) { returnArray(rowCounter, colCounter++) = at(row, col); } @@ -812,12 +813,12 @@ namespace nc /// @param inColIndex /// @return NdArray /// - NdArray operator()(Slice inRowSlice, int32 inColIndex) const + NdArray operator()(Slice inRowSlice, index_type inColIndex) const { NdArray returnArray(inRowSlice.numElements(shape_.rows), 1); uint32 rowCounter = 0; - for (int32 row = inRowSlice.start; row < inRowSlice.stop; row += inRowSlice.step) + for (index_type row = inRowSlice.start; row < inRowSlice.stop; row += inRowSlice.step) { returnArray(rowCounter++, 0) = at(row, inColIndex); } @@ -834,12 +835,12 @@ namespace nc /// @param inColSlice /// @return NdArray /// - NdArray operator()(int32 inRowIndex, Slice inColSlice) const + NdArray operator()(index_type inRowIndex, Slice inColSlice) const { NdArray returnArray(1, inColSlice.numElements(shape_.cols)); uint32 colCounter = 0; - for (int32 col = inColSlice.start; col < inColSlice.stop; col += inColSlice.step) + for (index_type col = inColSlice.start; col < inColSlice.stop; col += inColSlice.step) { returnArray(0, colCounter++) = at(inRowIndex, col); } @@ -856,13 +857,14 @@ namespace nc /// @param colIndex /// @return NdArray /// - template> || std::is_same_v>, - int> = 0> - NdArray operator()(const Indices& rowIndices, int32 colIndex) const + template< + typename Indices, + std::enable_if_t> || std::is_same_v>, + int> = 0> + NdArray operator()(const Indices& rowIndices, index_type colIndex) const { - const NdArray colIndices = { colIndex }; - return operator()(rowIndices, colIndices); + const NdArray colIndices = { colIndex }; + return operator()(rowIndices, colIndices); } //============================================================================ @@ -874,9 +876,10 @@ namespace nc /// @param colSlice /// @return NdArray /// - template> || std::is_same_v>, - int> = 0> + template< + typename Indices, + std::enable_if_t> || std::is_same_v>, + int> = 0> NdArray operator()(const Indices& rowIndices, Slice colSlice) const { return operator()(rowIndices, toIndices(colSlice, Axis::COL)); @@ -891,13 +894,14 @@ namespace nc /// @param colIndices /// @return NdArray /// - template> || std::is_same_v>, - int> = 0> - NdArray operator()(int32 rowIndex, const Indices& colIndices) const + template< + typename Indices, + std::enable_if_t> || std::is_same_v>, + int> = 0> + NdArray operator()(index_type rowIndex, const Indices& colIndices) const { - const NdArray rowIndices = { rowIndex }; - return operator()(rowIndices, colIndices); + const NdArray rowIndices = { rowIndex }; + return operator()(rowIndices, colIndices); } //============================================================================ @@ -909,9 +913,10 @@ namespace nc /// @param colIndices /// @return NdArray /// - template> || std::is_same_v>, - int> = 0> + template< + typename Indices, + std::enable_if_t> || std::is_same_v>, + int> = 0> NdArray operator()(Slice rowSlice, const Indices& colIndices) const { return operator()(toIndices(rowSlice, Axis::ROW), colIndices); @@ -926,20 +931,21 @@ namespace nc /// @param colIndices /// @return NdArray /// - template< - typename RowIndices, - typename ColIndices, - std::enable_if_t> || std::is_same_v>, - int> = 0, - std::enable_if_t> || std::is_same_v>, - int> = 0> + template> || + std::is_same_v>, + int> = 0, + std::enable_if_t> || + std::is_same_v>, + int> = 0> NdArray operator()(RowIndices rowIndices, ColIndices colIndices) const { rowIndices.sort(); colIndices.sort(); - std::vector rowIndicesUnique(rowIndices.size()); - std::vector colIndicesUnique(colIndices.size()); + std::vector rowIndicesUnique(rowIndices.size()); + std::vector colIndicesUnique(colIndices.size()); const auto lastRow = stl_algorithms::unique_copy(rowIndices.begin(), rowIndices.end(), rowIndicesUnique.begin()); @@ -973,7 +979,7 @@ namespace nc /// @param inStepSize (default 1) /// @return Slice /// - Slice cSlice(int32 inStartIdx = 0, uint32 inStepSize = 1) const noexcept + Slice cSlice(index_type inStartIdx = 0, uint32 inStepSize = 1) const noexcept { return Slice(inStartIdx, shape_.cols, inStepSize); } @@ -987,7 +993,7 @@ namespace nc /// @param inStepSize (default 1) /// @return Slice /// - Slice rSlice(int32 inStartIdx = 0, uint32 inStepSize = 1) const noexcept + Slice rSlice(index_type inStartIdx = 0, uint32 inStepSize = 1) const noexcept { return Slice(inStartIdx, shape_.rows, inStepSize); } @@ -999,7 +1005,7 @@ namespace nc /// @param inIndex /// @return value /// - reference at(int32 inIndex) + reference at(index_type inIndex) { // this doesn't allow for calling the first element as -size_... // but why would you really want to do that anyway? @@ -1020,7 +1026,7 @@ namespace nc /// @param inIndex /// @return value /// - const_reference at(int32 inIndex) const + const_reference at(index_type inIndex) const { // this doesn't allow for calling the first element as -size_... // but why would you really want to do that anyway? @@ -1042,11 +1048,11 @@ namespace nc /// @param inColIndex /// @return value /// - reference at(int32 inRowIndex, int32 inColIndex) + reference at(index_type inRowIndex, index_type inColIndex) { // this doesn't allow for calling the first element as -size_... // but why would you really want to do that anyway? - if (std::abs(inRowIndex) > static_cast(shape_.rows - 1)) + if (std::abs(inRowIndex) > static_cast(shape_.rows - 1)) { std::string errStr = "Row index " + utils::num2str(inRowIndex); errStr += " is out of bounds for array of size " + utils::num2str(shape_.rows) + "."; @@ -1055,7 +1061,7 @@ namespace nc // this doesn't allow for calling the first element as -size_... // but why would you really want to that anyway? - if (std::abs(inColIndex) > static_cast(shape_.cols - 1)) + if (std::abs(inColIndex) > static_cast(shape_.cols - 1)) { std::string errStr = "Column index " + utils::num2str(inColIndex); errStr += " is out of bounds for array of size " + utils::num2str(shape_.cols) + "."; @@ -1073,11 +1079,11 @@ namespace nc /// @param inColIndex /// @return value /// - const_reference at(int32 inRowIndex, int32 inColIndex) const + const_reference at(index_type inRowIndex, index_type inColIndex) const { // this doesn't allow for calling the first element as -size_... // but why would you really want to do that anyway? - if (std::abs(inRowIndex) > static_cast(shape_.rows - 1)) + if (std::abs(inRowIndex) > static_cast(shape_.rows - 1)) { std::string errStr = "Row index " + utils::num2str(inRowIndex); errStr += " is out of bounds for array of size " + utils::num2str(shape_.rows) + "."; @@ -1086,7 +1092,7 @@ namespace nc // this doesn't allow for calling the first element as -size_... // but why would you really want to do that anyway? - if (std::abs(inColIndex) > static_cast(shape_.cols - 1)) + if (std::abs(inColIndex) > static_cast(shape_.cols - 1)) { std::string errStr = "Column index " + utils::num2str(inColIndex); errStr += " is out of bounds for array of size " + utils::num2str(shape_.cols) + "."; @@ -1133,7 +1139,7 @@ namespace nc /// @param inColIndex /// @return Ndarray /// - NdArray at(const Slice& inRowSlice, int32 inColIndex) const + NdArray at(const Slice& inRowSlice, index_type inColIndex) const { // the slice operator already provides bounds checking. just including // the at method for completeness @@ -1148,7 +1154,7 @@ namespace nc /// @param inColSlice /// @return Ndarray /// - NdArray at(int32 inRowIndex, const Slice& inColSlice) const + NdArray at(index_type inRowIndex, const Slice& inColSlice) const { // the slice operator already provides bounds checking. just including // the at method for completeness @@ -1163,7 +1169,7 @@ namespace nc /// @param colIndices /// @return Ndarray /// - NdArray at(const NdArray& rowIndices, const NdArray& colIndices) const + NdArray at(const NdArray& rowIndices, const NdArray& colIndices) const { // the slice operator already provides bounds checking. just including // the at method for completeness @@ -2474,7 +2480,7 @@ namespace nc /// @param inAxis: (Optional, default ROW) axis the offset is applied to /// @return NdArray /// - NdArray diagonal(int32 inOffset = 0, Axis inAxis = Axis::ROW) const + NdArray diagonal(index_type inOffset = 0, Axis inAxis = Axis::ROW) const { switch (inAxis) { @@ -2482,7 +2488,7 @@ namespace nc { std::vector diagnolValues; uint32 col = 0; - for (int32 row = inOffset; row < static_cast(shape_.rows); ++row) + for (index_type row = inOffset; row < static_cast(shape_.rows); ++row) { if (row < 0) { @@ -3483,7 +3489,7 @@ namespace nc /// @param inIndex /// @param inValue /// - NdArray& put(int32 inIndex, value_type inValue) + NdArray& put(index_type inIndex, value_type inValue) { at(inIndex) = inValue; @@ -3500,7 +3506,7 @@ namespace nc /// @param inCol /// @param inValue /// - NdArray& put(int32 inRow, int32 inCol, value_type inValue) + NdArray& put(index_type inRow, index_type inCol, value_type inValue) { at(inRow, inCol) = inValue; @@ -3565,7 +3571,7 @@ namespace nc Slice inSliceCopy(inSlice); inSliceCopy.makePositiveAndValidate(size_); - for (int32 i = inSliceCopy.start; i < inSliceCopy.stop; i += inSliceCopy.step) + for (index_type i = inSliceCopy.start; i < inSliceCopy.stop; i += inSliceCopy.step) { put(i, inValue); } @@ -3588,7 +3594,7 @@ namespace nc inSliceCopy.makePositiveAndValidate(size_); std::vector indices; - for (int32 i = inSliceCopy.start; i < inSliceCopy.stop; i += inSliceCopy.step) + for (index_type i = inSliceCopy.start; i < inSliceCopy.stop; i += inSliceCopy.step) { indices.push_back(i); } @@ -3615,9 +3621,9 @@ namespace nc inColSliceCopy.makePositiveAndValidate(shape_.cols); std::vector indices; - for (int32 row = inRowSliceCopy.start; row < inRowSliceCopy.stop; row += inRowSliceCopy.step) + for (index_type row = inRowSliceCopy.start; row < inRowSliceCopy.stop; row += inRowSliceCopy.step) { - for (int32 col = inColSliceCopy.start; col < inColSliceCopy.stop; col += inColSliceCopy.step) + for (index_type col = inColSliceCopy.start; col < inColSliceCopy.stop; col += inColSliceCopy.step) { put(row, col, inValue); } @@ -3636,13 +3642,13 @@ namespace nc /// @param inColIndex /// @param inValue /// - NdArray& put(const Slice& inRowSlice, int32 inColIndex, value_type inValue) + NdArray& put(const Slice& inRowSlice, index_type inColIndex, value_type inValue) { Slice inRowSliceCopy(inRowSlice); inRowSliceCopy.makePositiveAndValidate(shape_.rows); std::vector indices; - for (int32 row = inRowSliceCopy.start; row < inRowSliceCopy.stop; row += inRowSliceCopy.step) + for (index_type row = inRowSliceCopy.start; row < inRowSliceCopy.stop; row += inRowSliceCopy.step) { put(row, inColIndex, inValue); } @@ -3660,13 +3666,13 @@ namespace nc /// @param inColSlice /// @param inValue /// - NdArray& put(int32 inRowIndex, const Slice& inColSlice, value_type inValue) + NdArray& put(index_type inRowIndex, const Slice& inColSlice, value_type inValue) { Slice inColSliceCopy(inColSlice); inColSliceCopy.makePositiveAndValidate(shape_.cols); std::vector indices; - for (int32 col = inColSliceCopy.start; col < inColSliceCopy.stop; col += inColSliceCopy.step) + for (index_type col = inColSliceCopy.start; col < inColSliceCopy.stop; col += inColSliceCopy.step) { put(inRowIndex, col, inValue); } @@ -3693,9 +3699,9 @@ namespace nc inColSliceCopy.makePositiveAndValidate(shape_.cols); std::vector indices; - for (int32 row = inRowSliceCopy.start; row < inRowSliceCopy.stop; row += inRowSliceCopy.step) + for (index_type row = inRowSliceCopy.start; row < inRowSliceCopy.stop; row += inRowSliceCopy.step) { - for (int32 col = inColSliceCopy.start; col < inColSliceCopy.stop; col += inColSliceCopy.step) + for (index_type col = inColSliceCopy.start; col < inColSliceCopy.stop; col += inColSliceCopy.step) { const uint32 index = row * shape_.cols + col; indices.push_back(index); @@ -3715,13 +3721,13 @@ namespace nc /// @param inColIndex /// @param inValues /// - NdArray& put(const Slice& inRowSlice, int32 inColIndex, const NdArray& inValues) + NdArray& put(const Slice& inRowSlice, index_type inColIndex, const NdArray& inValues) { Slice inRowSliceCopy(inRowSlice); inRowSliceCopy.makePositiveAndValidate(shape_.rows); std::vector indices; - for (int32 row = inRowSliceCopy.start; row < inRowSliceCopy.stop; row += inRowSliceCopy.step) + for (index_type row = inRowSliceCopy.start; row < inRowSliceCopy.stop; row += inRowSliceCopy.step) { const uint32 index = row * shape_.cols + inColIndex; indices.push_back(index); @@ -3740,13 +3746,13 @@ namespace nc /// @param inColSlice /// @param inValues /// - NdArray& put(int32 inRowIndex, const Slice& inColSlice, const NdArray& inValues) + NdArray& put(index_type inRowIndex, const Slice& inColSlice, const NdArray& inValues) { Slice inColSliceCopy(inColSlice); inColSliceCopy.makePositiveAndValidate(shape_.cols); std::vector indices; - for (int32 col = inColSliceCopy.start; col < inColSliceCopy.stop; col += inColSliceCopy.step) + for (index_type col = inColSliceCopy.start; col < inColSliceCopy.stop; col += inColSliceCopy.step) { const uint32 index = inRowIndex * shape_.cols + col; indices.push_back(index); @@ -3911,7 +3917,7 @@ namespace nc /// @param inNumRows /// @param inNumCols /// - NdArray& reshape(int32 inNumRows, int32 inNumCols) + NdArray& reshape(index_type inNumRows, index_type inNumCols) { if (inNumRows < 0) { @@ -4255,9 +4261,9 @@ namespace nc /// @param colIdx1 /// @param colIdx2 /// - void swapCols(int32 colIdx1, int32 colIdx2) noexcept + void swapCols(index_type colIdx1, index_type colIdx2) noexcept { - for (int32 row = 0; row < static_cast(shape_.rows); ++row) + for (index_type row = 0; row < static_cast(shape_.rows); ++row) { std::swap(operator()(row, colIdx1), operator()(row, colIdx2)); } @@ -4270,9 +4276,9 @@ namespace nc /// @param rowIdx1 /// @param rowIdx2 /// - void swapRows(int32 rowIdx1, int32 rowIdx2) noexcept + void swapRows(index_type rowIdx1, index_type rowIdx2) noexcept { - for (int32 col = 0; col < static_cast(shape_.cols); ++col) + for (index_type col = 0; col < static_cast(shape_.cols); ++col) { std::swap(operator()(rowIdx1, col), operator()(rowIdx2, col)); } @@ -4341,7 +4347,7 @@ namespace nc /// @param inSlice: the slice object /// @param inAxis: the array axis /// - /// @return NdArray + /// @return NdArray /// NdArray toIndices(Slice inSlice, Axis inAxis = Axis::ROW) const { From 7d189f75b63ca21d8571fe848fa6d11839fca5f8 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 18 Feb 2023 15:00:12 -0700 Subject: [PATCH 05/65] removed filesystem in favour of c++17 filesystem --- develop/ToDo.md | 1 - include/NumCpp/Core/DataCube.hpp | 10 +++++----- include/NumCpp/Functions/fromfile.hpp | 4 ++-- include/NumCpp/NdArray/NdArrayCore.hpp | 18 +++++++++--------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/develop/ToDo.md b/develop/ToDo.md index 51cdeb25a..62db5019d 100644 --- a/develop/ToDo.md +++ b/develop/ToDo.md @@ -4,7 +4,6 @@ * issue #170: insert() * issue #74, #147, #174: broadcasting -* remove Core/Internal/Filesystem in favour of std::filesystem * setup job for clang-tidy * setup job for cpp-check diff --git a/include/NumCpp/Core/DataCube.hpp b/include/NumCpp/Core/DataCube.hpp index a6f5aec18..06932489b 100644 --- a/include/NumCpp/Core/DataCube.hpp +++ b/include/NumCpp/Core/DataCube.hpp @@ -27,12 +27,12 @@ /// #pragma once +#include #include #include #include #include "NumCpp/Core/Internal/Error.hpp" -#include "NumCpp/Core/Internal/Filesystem.hpp" #include "NumCpp/Core/Shape.hpp" #include "NumCpp/Core/Slice.hpp" #include "NumCpp/Core/Types.hpp" @@ -126,13 +126,13 @@ namespace nc /// void dump(const std::string& inFilename) const { - filesystem::File f(inFilename); - if (!f.hasExt()) + std::filesystem::path f(inFilename); + if (!f.has_extension()) { - f.withExt("bin"); + f.replace_extension("bin"); } - std::ofstream ofile(f.fullName().c_str(), std::ios::binary); + std::ofstream ofile(f.c_str(), std::ios::binary); if (!ofile.good()) { THROW_RUNTIME_ERROR("Could not open the input file:\n\t" + inFilename); diff --git a/include/NumCpp/Functions/fromfile.hpp b/include/NumCpp/Functions/fromfile.hpp index aca5cffaf..8f813583a 100644 --- a/include/NumCpp/Functions/fromfile.hpp +++ b/include/NumCpp/Functions/fromfile.hpp @@ -27,13 +27,13 @@ /// #pragma once +#include #include #include #include #include #include "NumCpp/Core/Internal/Error.hpp" -#include "NumCpp/Core/Internal/Filesystem.hpp" #include "NumCpp/Core/Internal/StaticAsserts.hpp" #include "NumCpp/Core/Types.hpp" #include "NumCpp/Functions/fromstring.hpp" @@ -53,7 +53,7 @@ namespace nc template NdArray fromfile(const std::string& inFilename) { - if (!filesystem::File(inFilename).exists()) + if (!std::filesystem::exists(inFilename)) { THROW_INVALID_ARGUMENT_ERROR("input filename does not exist.\n\t" + inFilename); } diff --git a/include/NumCpp/NdArray/NdArrayCore.hpp b/include/NumCpp/NdArray/NdArrayCore.hpp index 27182fb67..bbf0da608 100644 --- a/include/NumCpp/NdArray/NdArrayCore.hpp +++ b/include/NumCpp/NdArray/NdArrayCore.hpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -48,7 +49,6 @@ #include "NumCpp/Core/DtypeInfo.hpp" #include "NumCpp/Core/Internal/Endian.hpp" #include "NumCpp/Core/Internal/Error.hpp" -#include "NumCpp/Core/Internal/Filesystem.hpp" #include "NumCpp/Core/Internal/StaticAsserts.hpp" #include "NumCpp/Core/Internal/StdComplexOperators.hpp" #include "NumCpp/Core/Internal/StlAlgorithms.hpp" @@ -2578,13 +2578,13 @@ namespace nc /// void dump(const std::string& inFilename) const { - filesystem::File f(inFilename); - if (!f.hasExt()) + std::filesystem::path f(inFilename); + if (!f.has_extension()) { - f.withExt(".bin"); + f.replace_extension("bin"); } - std::ofstream ofile(f.fullName().c_str(), std::ios::binary); + std::ofstream ofile(f.c_str(), std::ios::binary); if (!ofile.good()) { THROW_RUNTIME_ERROR("Unable to open the input file:\n\t" + inFilename); @@ -4316,13 +4316,13 @@ namespace nc { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); - filesystem::File f(inFilename); - if (!f.hasExt()) + std::filesystem::path f(inFilename); + if (!f.has_extension()) { - f.withExt("txt"); + f.replace_extension("txt"); } - std::ofstream ofile(f.fullName().c_str()); + std::ofstream ofile(f.c_str()); if (!ofile.good()) { THROW_RUNTIME_ERROR("Input file could not be opened:\n\t" + inFilename); From 478f6e945a1a59e3536f0344a32e94254355fc2e Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 18 Feb 2023 21:40:46 -0700 Subject: [PATCH 06/65] starting to run clang-tidy, enabled more warnings --- .clang-tidy | 37 + clang-format/clang-format-all.sh | 84 - include/NumCpp/Coordinates/Coordinate.hpp | 555 +++-- include/NumCpp/Coordinates/Dec.hpp | 375 ++- include/NumCpp/Coordinates/RA.hpp | 321 ++- .../NumCpp/Coordinates/degreeSeperation.hpp | 57 +- .../NumCpp/Coordinates/radianSeperation.hpp | 57 +- include/NumCpp/Core/Constants.hpp | 43 +- include/NumCpp/Core/DataCube.hpp | 42 +- include/NumCpp/Core/Internal/Endian.hpp | 81 +- include/NumCpp/Core/Internal/Error.hpp | 37 +- include/NumCpp/Core/Internal/Filesystem.hpp | 140 -- .../NumCpp/Core/Internal/StlAlgorithms.hpp | 1641 +++++++------ include/NumCpp/Core/Shape.hpp | 8 +- include/NumCpp/Core/Slice.hpp | 2 +- include/NumCpp/DateTime/DateTime.hpp | 4 +- .../Boundaries/Boundaries1d/addBoundary1d.hpp | 112 +- .../Boundaries/Boundaries1d/constant1d.hpp | 52 +- .../Boundaries/Boundaries1d/mirror1d.hpp | 52 +- .../Boundaries/Boundaries1d/nearest1d.hpp | 50 +- .../Boundaries/Boundaries1d/reflect1d.hpp | 54 +- .../Boundaries1d/trimBoundary1d.hpp | 40 +- .../Filter/Boundaries/Boundaries1d/wrap1d.hpp | 50 +- .../Boundaries/Boundaries2d/addBoundary2d.hpp | 108 +- .../Boundaries/Boundaries2d/constant2d.hpp | 80 +- .../Boundaries/Boundaries2d/fillCorners.hpp | 116 +- .../Boundaries/Boundaries2d/mirror2d.hpp | 130 +- .../Boundaries/Boundaries2d/nearest2d.hpp | 94 +- .../Boundaries/Boundaries2d/reflect2d.hpp | 132 +- .../Boundaries2d/trimBoundary2d.hpp | 46 +- .../Filter/Boundaries/Boundaries2d/wrap2d.hpp | 117 +- include/NumCpp/Filter/Boundaries/Boundary.hpp | 27 +- .../Filters1d/complementaryMedianFilter1d.hpp | 45 +- .../Filter/Filters/Filters1d/convolve1d.hpp | 69 +- .../Filters/Filters1d/gaussianFilter1d.hpp | 95 +- .../Filters/Filters1d/maximumFilter1d.hpp | 67 +- .../Filters/Filters1d/medianFilter1d.hpp | 67 +- .../Filters/Filters1d/minimumFilter1d.hpp | 67 +- .../Filters/Filters1d/percentileFilter1d.hpp | 71 +- .../Filter/Filters/Filters1d/rankFilter1d.hpp | 71 +- .../Filters/Filters1d/uniformFilter1d.hpp | 67 +- .../Filters2d/complementaryMedianFilter.hpp | 45 +- .../Filter/Filters/Filters2d/convolve.hpp | 89 +- .../Filters/Filters2d/gaussianFilter.hpp | 99 +- .../Filter/Filters/Filters2d/laplace.hpp | 45 +- .../Filters/Filters2d/maximumFilter.hpp | 75 +- .../Filter/Filters/Filters2d/medianFilter.hpp | 75 +- .../Filters/Filters2d/minimumFilter.hpp | 75 +- .../Filters/Filters2d/percentileFilter.hpp | 81 +- .../Filter/Filters/Filters2d/rankFilter.hpp | 85 +- .../Filters/Filters2d/uniformFilter.hpp | 75 +- include/NumCpp/Functions/hammingEncode.hpp | 568 +++-- include/NumCpp/ImageProcessing/Centroid.hpp | 555 +++-- include/NumCpp/ImageProcessing/Cluster.hpp | 639 +++-- .../NumCpp/ImageProcessing/ClusterMaker.hpp | 532 +++-- include/NumCpp/ImageProcessing/Pixel.hpp | 229 +- .../NumCpp/ImageProcessing/applyThreshold.hpp | 33 +- .../ImageProcessing/centroidClusters.hpp | 43 +- .../NumCpp/ImageProcessing/clusterPixels.hpp | 40 +- .../ImageProcessing/generateCentroids.hpp | 97 +- .../ImageProcessing/generateThreshold.hpp | 157 +- .../ImageProcessing/windowExceedances.hpp | 63 +- include/NumCpp/Integrate/gauss_legendre.hpp | 261 ++- include/NumCpp/Integrate/romberg.hpp | 86 +- include/NumCpp/Integrate/simpson.hpp | 55 +- include/NumCpp/Integrate/trapazoidal.hpp | 57 +- include/NumCpp/Linalg/cholesky.hpp | 91 +- include/NumCpp/Linalg/det.hpp | 169 +- include/NumCpp/Linalg/gaussNewtonNlls.hpp | 151 +- include/NumCpp/Linalg/hat.hpp | 111 +- include/NumCpp/Linalg/inv.hpp | 136 +- include/NumCpp/Linalg/lstsq.hpp | 61 +- include/NumCpp/Linalg/lu_decomposition.hpp | 73 +- include/NumCpp/Linalg/matrix_power.hpp | 111 +- include/NumCpp/Linalg/multi_dot.hpp | 71 +- include/NumCpp/Linalg/pinv.hpp | 55 +- .../NumCpp/Linalg/pivotLU_decomposition.hpp | 120 +- include/NumCpp/Linalg/solve.hpp | 65 +- include/NumCpp/Linalg/svd.hpp | 49 +- include/NumCpp/Linalg/svd/SVDClass.hpp | 913 ++++---- include/NumCpp/NdArray/NdArrayCore.hpp | 183 +- include/NumCpp/Polynomial/Poly1d.hpp | 938 ++++---- include/NumCpp/Polynomial/chebyshev_t.hpp | 69 +- include/NumCpp/Polynomial/chebyshev_u.hpp | 69 +- include/NumCpp/Polynomial/hermite.hpp | 75 +- include/NumCpp/Polynomial/laguerre.hpp | 167 +- include/NumCpp/Polynomial/legendre_p.hpp | 187 +- include/NumCpp/Polynomial/legendre_q.hpp | 77 +- .../NumCpp/Polynomial/spherical_harmonic.hpp | 131 +- include/NumCpp/Random/RNG.hpp | 2059 ++++++++--------- include/NumCpp/Random/bernoulli.hpp | 117 +- include/NumCpp/Random/beta.hpp | 169 +- include/NumCpp/Random/binomial.hpp | 165 +- include/NumCpp/Random/cauchy.hpp | 137 +- include/NumCpp/Random/chiSquare.hpp | 141 +- include/NumCpp/Random/choice.hpp | 117 +- include/NumCpp/Random/discrete.hpp | 121 +- include/NumCpp/Random/exponential.hpp | 121 +- include/NumCpp/Random/extremeValue.hpp | 153 +- include/NumCpp/Random/f.hpp | 161 +- include/NumCpp/Random/gamma.hpp | 166 +- include/NumCpp/Random/generator.hpp | 37 +- include/NumCpp/Random/geometric.hpp | 141 +- include/NumCpp/Random/laplace.hpp | 129 +- include/NumCpp/Random/lognormal.hpp | 150 +- include/NumCpp/Random/negativeBinomial.hpp | 165 +- .../NumCpp/Random/nonCentralChiSquared.hpp | 171 +- include/NumCpp/Random/normal.hpp | 149 +- include/NumCpp/Random/permutation.hpp | 103 +- include/NumCpp/Random/poisson.hpp | 141 +- include/NumCpp/Random/rand.hpp | 121 +- include/NumCpp/Random/randFloat.hpp | 167 +- include/NumCpp/Random/randInt.hpp | 167 +- include/NumCpp/Random/randN.hpp | 117 +- include/NumCpp/Random/shuffle.hpp | 43 +- include/NumCpp/Random/standardNormal.hpp | 103 +- include/NumCpp/Random/studentT.hpp | 141 +- include/NumCpp/Random/triangle.hpp | 223 +- include/NumCpp/Random/uniform.hpp | 117 +- include/NumCpp/Random/uniformOnSphere.hpp | 83 +- include/NumCpp/Random/weibull.hpp | 165 +- include/NumCpp/Roots/Bisection.hpp | 195 +- include/NumCpp/Roots/Brent.hpp | 423 ++-- include/NumCpp/Roots/Dekker.hpp | 269 ++- include/NumCpp/Roots/Iteration.hpp | 143 +- include/NumCpp/Roots/Newton.hpp | 168 +- include/NumCpp/Roots/Secant.hpp | 173 +- include/NumCpp/Rotations/DCM.hpp | 287 ++- include/NumCpp/Rotations/Quaternion.hpp | 1830 ++++++++------- .../NumCpp/Rotations/rodriguesRotation.hpp | 85 +- include/NumCpp/Rotations/wahbasProblem.hpp | 151 +- include/NumCpp/Special/airy_ai.hpp | 75 +- include/NumCpp/Special/airy_ai_prime.hpp | 75 +- include/NumCpp/Special/airy_bi.hpp | 75 +- include/NumCpp/Special/airy_bi_prime.hpp | 75 +- include/NumCpp/Special/bernoulli.hpp | 77 +- include/NumCpp/Special/bessel_in.hpp | 83 +- include/NumCpp/Special/bessel_in_prime.hpp | 77 +- include/NumCpp/Special/bessel_jn.hpp | 83 +- include/NumCpp/Special/bessel_jn_prime.hpp | 77 +- include/NumCpp/Special/bessel_kn.hpp | 83 +- include/NumCpp/Special/bessel_kn_prime.hpp | 77 +- include/NumCpp/Special/bessel_yn.hpp | 83 +- include/NumCpp/Special/bessel_yn_prime.hpp | 77 +- include/NumCpp/Special/beta.hpp | 85 +- include/NumCpp/Special/cnr.hpp | 29 +- include/NumCpp/Special/comp_ellint_1.hpp | 77 +- include/NumCpp/Special/comp_ellint_2.hpp | 77 +- include/NumCpp/Special/comp_ellint_3.hpp | 91 +- include/NumCpp/Special/cyclic_hankel_1.hpp | 77 +- include/NumCpp/Special/cyclic_hankel_2.hpp | 77 +- include/NumCpp/Special/digamma.hpp | 75 +- include/NumCpp/Special/ellint_1.hpp | 91 +- include/NumCpp/Special/ellint_2.hpp | 91 +- include/NumCpp/Special/ellint_3.hpp | 93 +- include/NumCpp/Special/erf.hpp | 75 +- include/NumCpp/Special/erf_inv.hpp | 75 +- include/NumCpp/Special/erfc.hpp | 73 +- include/NumCpp/Special/erfc_inv.hpp | 75 +- include/NumCpp/Special/expint.hpp | 77 +- include/NumCpp/Special/factorial.hpp | 79 +- include/NumCpp/Special/gamma.hpp | 71 +- include/NumCpp/Special/gamma1pm1.hpp | 71 +- include/NumCpp/Special/log_gamma.hpp | 71 +- include/NumCpp/Special/pnr.hpp | 75 +- include/NumCpp/Special/polygamma.hpp | 79 +- include/NumCpp/Special/prime.hpp | 75 +- include/NumCpp/Special/riemann_zeta.hpp | 81 +- include/NumCpp/Special/softmax.hpp | 107 +- .../NumCpp/Special/spherical_bessel_jn.hpp | 81 +- .../NumCpp/Special/spherical_bessel_yn.hpp | 81 +- include/NumCpp/Special/spherical_hankel_1.hpp | 77 +- include/NumCpp/Special/spherical_hankel_2.hpp | 77 +- include/NumCpp/Special/trigamma.hpp | 75 +- include/NumCpp/Utils/cube.hpp | 31 +- include/NumCpp/Utils/essentiallyEqual.hpp | 179 +- include/NumCpp/Utils/gaussian.hpp | 39 +- include/NumCpp/Utils/gaussian1d.hpp | 39 +- include/NumCpp/Utils/interp.hpp | 31 +- include/NumCpp/Utils/num2str.hpp | 31 +- include/NumCpp/Utils/power.hpp | 49 +- include/NumCpp/Utils/powerf.hpp | 33 +- include/NumCpp/Utils/sqr.hpp | 31 +- include/NumCpp/Utils/value2str.hpp | 35 +- include/NumCpp/Vector/Vec2.hpp | 22 +- include/NumCpp/Vector/Vec3.hpp | 26 +- static_analysis/clang-format-all.sh | 1 + supressions.txt | 0 test/src/CMakeLists.txt | 35 +- test/src/NdArray.cpp | 28 +- 190 files changed, 13382 insertions(+), 14161 deletions(-) create mode 100644 .clang-tidy delete mode 100644 clang-format/clang-format-all.sh delete mode 100644 include/NumCpp/Core/Internal/Filesystem.hpp create mode 100755 static_analysis/clang-format-all.sh create mode 100644 supressions.txt diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 000000000..45593cf03 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,37 @@ +Checks: > + clang-analyzer-*, + cppcoreguidelines-*, + bugprone-*, + google-readability-*, + modernize-*, + hicpp-*, + -modernize-use-trailing-return-type + +# Turn all the warnings from the checks above into errors. +WarningsAsErrors: "*" + +CheckOptions: + - { key: readability-identifier-naming.NamespaceCase, value: lower_case } + - { key: readability-identifier-naming.ClassCase, value: CamelCase } + - { key: readability-identifier-naming.ClassMethodCase, value: camelBack } + - { key: readability-identifier-naming.StructCase, value: CamelCase } + - { + key: readability-identifier-naming.TemplateParameterCase, + value: CamelCase, + } + - { key: readability-identifier-naming.FunctionCase, value: camelBack } + - { key: readability-identifier-naming.VariableCase, value: camelBack } + - { key: readability-identifier-naming.PrivateMemberSuffix, value: _ } + - { key: readability-identifier-naming.ProtectedMemberSuffix, value: _ } + - { + key: readability-identifier-naming.MacroDefinitionCase, + value: UPPER_CASE, + } + - { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } + - { + key: readability-identifier-naming.ConstexprVariableCase, + value: UPPER_CASE, + } + - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } + - { key: readability-identifier-naming.MemberConstantCase, value: UPPER_CASE } + - { key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE } \ No newline at end of file diff --git a/clang-format/clang-format-all.sh b/clang-format/clang-format-all.sh deleted file mode 100644 index e4372339c..000000000 --- a/clang-format/clang-format-all.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/bash -# -# clang-format-all: a tool to run clang-format on an entire project -# Copyright (C) 2016 Evan Klitzke -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -function usage { - echo "Usage: $0 DIR..." - exit 1 -} - -if [ $# -eq 0 ]; then - usage -fi - -# Variable that will hold the name of the clang-format command -FMT="" - -# Some distros just call it clang-format. Others (e.g. Ubuntu) are insistent -# that the version number be part of the command. We prefer clang-format if -# that's present, otherwise we work backwards from highest version to lowest -# version. -for clangfmt in clang-format{,-{4,3}.{9,8,7,6,5,4,3,2,1,0}}; do - if which "$clangfmt" &>/dev/null; then - FMT="$clangfmt" - break - fi -done - -# Check if we found a working clang-format -if [ -z "$FMT" ]; then - echo "failed to find clang-format" - exit 1 -fi - -# Check all of the arguments first to make sure they're all directories -for dir in "$@"; do - if [ ! -d "${dir}" ]; then - echo "${dir} is not a directory" - usage - fi -done - -# Find a dominating file, starting from a given directory and going up. -find-dominating-file() { - if [ -r "$1"/"$2" ]; then - return 0 - fi - if [ "$1" = "/" ]; then - return 1 - fi - find-dominating-file "$(realpath "$1"/..)" "$2" - return $? -} - -# Run clang-format -i on all of the things -for dir in "$@"; do - pushd "${dir}" &>/dev/null - if ! find-dominating-file . .clang-format; then - echo "Failed to find dominating .clang-format starting at $PWD" - continue - fi - find . \ - \( -iname '*.c' \ - -o -iname '*.cc' \ - -o -iname '*.cpp' \ - -o -iname '*.h' \ - -o -iname '*.hh' \ - -o -iname '*.hpp' \) \ - | xargs "${FMT}" -i --verbose --style=file '{}' \; - popd &>/dev/null -done diff --git a/include/NumCpp/Coordinates/Coordinate.hpp b/include/NumCpp/Coordinates/Coordinate.hpp index 7070cdef4..92aa2deb6 100644 --- a/include/NumCpp/Coordinates/Coordinate.hpp +++ b/include/NumCpp/Coordinates/Coordinate.hpp @@ -41,317 +41,314 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Utils/sqr.hpp" -namespace nc +namespace nc::coordinates { - namespace coordinates + //================================================================================ + /// Holds a full coordinate object + class Coordinate { - //================================================================================ - /// Holds a full coordinate object - class Coordinate + public: + //============================================================================ + /// Default Constructor + /// + Coordinate() = default; + + //============================================================================ + /// Constructor + /// + /// @param inRaDegrees + /// @param inDecDegrees + /// + Coordinate(double inRaDegrees, double inDecDegrees) : + ra_(inRaDegrees), + dec_(inDecDegrees) { - public: - //============================================================================ - /// Default Constructor - /// - Coordinate() = default; + polarToCartesian(); + } - //============================================================================ - /// Constructor - /// - /// @param inRaDegrees - /// @param inDecDegrees - /// - Coordinate(double inRaDegrees, double inDecDegrees) : - ra_(inRaDegrees), - dec_(inDecDegrees) - { - polarToCartesian(); - } + //============================================================================ + /// Constructor + /// + /// @param inRaHours + /// @param inRaMinutes + /// @param inRaSeconds + /// @param inSign + /// @param inDecDegreesWhole + /// @param inDecMinutes + /// @param inDecSeconds + /// + Coordinate(uint8 inRaHours, + uint8 inRaMinutes, + double inRaSeconds, + Sign inSign, + uint8 inDecDegreesWhole, + uint8 inDecMinutes, + double inDecSeconds) : + ra_(inRaHours, inRaMinutes, inRaSeconds), + dec_(inSign, inDecDegreesWhole, inDecMinutes, inDecSeconds) + { + polarToCartesian(); + } - //============================================================================ - /// Constructor - /// - /// @param inRaHours - /// @param inRaMinutes - /// @param inRaSeconds - /// @param inSign - /// @param inDecDegreesWhole - /// @param inDecMinutes - /// @param inDecSeconds - /// - Coordinate(uint8 inRaHours, - uint8 inRaMinutes, - double inRaSeconds, - Sign inSign, - uint8 inDecDegreesWhole, - uint8 inDecMinutes, - double inDecSeconds) : - ra_(inRaHours, inRaMinutes, inRaSeconds), - dec_(inSign, inDecDegreesWhole, inDecMinutes, inDecSeconds) - { - polarToCartesian(); - } + //============================================================================ + /// Constructor + /// + /// @param inRA + /// @param inDec + /// + Coordinate(const RA& inRA, const Dec& inDec) noexcept : + ra_(inRA), + dec_(inDec) + { + polarToCartesian(); + } - //============================================================================ - /// Constructor - /// - /// @param inRA - /// @param inDec - /// - Coordinate(const RA& inRA, const Dec& inDec) noexcept : - ra_(inRA), - dec_(inDec) - { - polarToCartesian(); - } + //============================================================================ + /// Constructor + /// + /// @param inX + /// @param inY + /// @param inZ + /// + Coordinate(double inX, double inY, double inZ) noexcept : + x_(inX), + y_(inY), + z_(inZ) + { + cartesianToPolar(); + } - //============================================================================ - /// Constructor - /// - /// @param inX - /// @param inY - /// @param inZ - /// - Coordinate(double inX, double inY, double inZ) noexcept : - x_(inX), - y_(inY), - z_(inZ) + //============================================================================ + /// Constructor + /// + /// @param inCartesianVector + /// + Coordinate(const NdArray& inCartesianVector) + { + if (inCartesianVector.size() != 3) { - cartesianToPolar(); + THROW_INVALID_ARGUMENT_ERROR("NdArray input must be of length 3."); } - //============================================================================ - /// Constructor - /// - /// @param inCartesianVector - /// - Coordinate(const NdArray& inCartesianVector) - { - if (inCartesianVector.size() != 3) - { - THROW_INVALID_ARGUMENT_ERROR("NdArray input must be of length 3."); - } + x_ = inCartesianVector[0]; + y_ = inCartesianVector[1]; + z_ = inCartesianVector[2]; - x_ = inCartesianVector[0]; - y_ = inCartesianVector[1]; - z_ = inCartesianVector[2]; + cartesianToPolar(); + } - cartesianToPolar(); - } + //============================================================================ + /// Returns the Dec object + /// + /// @return Dec + /// + [[nodiscard]] const Dec& dec() const noexcept + { + return dec_; + } - //============================================================================ - /// Returns the Dec object - /// - /// @return Dec - /// - const Dec& dec() const noexcept - { - return dec_; - } + //============================================================================ + /// Returns the RA object + /// + /// @return RA + /// + [[nodiscard]] const RA& ra() const noexcept + { + return ra_; + } - //============================================================================ - /// Returns the RA object - /// - /// @return RA - /// - const RA& ra() const noexcept - { - return ra_; - } + //============================================================================ + /// Returns the cartesian x value + /// + /// @return x + /// + [[nodiscard]] double x() const noexcept + { + return x_; + } - //============================================================================ - /// Returns the cartesian x value - /// - /// @return x - /// - double x() const noexcept - { - return x_; - } + //============================================================================ + /// Returns the cartesian y value + /// + /// @return y + /// + [[nodiscard]] double y() const noexcept + { + return y_; + } - //============================================================================ - /// Returns the cartesian y value - /// - /// @return y - /// - double y() const noexcept - { - return y_; - } + //============================================================================ + /// Returns the cartesian z value + /// + /// @return z + /// + [[nodiscard]] double z() const noexcept + { + return z_; + } - //============================================================================ - /// Returns the cartesian z value - /// - /// @return z - /// - double z() const noexcept - { - return z_; - } + //============================================================================ + /// Returns the cartesian xyz triplet as an NdArray + /// + /// @return NdArray + /// + [[nodiscard]] NdArray xyz() const + { + NdArray out = { x_, y_, z_ }; + return out; + } - //============================================================================ - /// Returns the cartesian xyz triplet as an NdArray - /// - /// @return NdArray - /// - NdArray xyz() const - { - NdArray out = { x_, y_, z_ }; - return out; - } + //============================================================================ + /// Returns the degree seperation between the two Coordinates + /// + /// @param inOtherCoordinate + /// + /// @return degrees + /// + [[nodiscard]] double degreeSeperation(const Coordinate& inOtherCoordinate) const + { + return rad2deg(radianSeperation(inOtherCoordinate)); + } - //============================================================================ - /// Returns the degree seperation between the two Coordinates - /// - /// @param inOtherCoordinate - /// - /// @return degrees - /// - double degreeSeperation(const Coordinate& inOtherCoordinate) const - { - return rad2deg(radianSeperation(inOtherCoordinate)); - } + //============================================================================ + /// Returns the degree seperation between the Coordinate + /// and the input vector + /// + /// @param inVector + /// + /// @return degrees + /// + [[nodiscard]] double degreeSeperation(const NdArray& inVector) const + { + return rad2deg(radianSeperation(inVector)); + } - //============================================================================ - /// Returns the degree seperation between the Coordinate - /// and the input vector - /// - /// @param inVector - /// - /// @return degrees - /// - double degreeSeperation(const NdArray& inVector) const - { - return rad2deg(radianSeperation(inVector)); - } + //============================================================================ + /// Returns the radian seperation between the two Coordinates + /// + /// @param inOtherCoordinate + /// + /// @return radians + /// + [[nodiscard]] double radianSeperation(const Coordinate& inOtherCoordinate) const + { + return std::acos(dot(xyz(), inOtherCoordinate.xyz()).item()); + } - //============================================================================ - /// Returns the radian seperation between the two Coordinates - /// - /// @param inOtherCoordinate - /// - /// @return radians - /// - double radianSeperation(const Coordinate& inOtherCoordinate) const + //============================================================================ + /// Returns the radian seperation between the Coordinate + /// and the input vector + /// + /// @param inVector + /// + /// @return radians + /// + [[nodiscard]] double radianSeperation(const NdArray& inVector) const + { + if (inVector.size() != 3) { - return std::acos(dot(xyz(), inOtherCoordinate.xyz()).item()); + THROW_INVALID_ARGUMENT_ERROR("input vector must be of length 3."); } - //============================================================================ - /// Returns the radian seperation between the Coordinate - /// and the input vector - /// - /// @param inVector - /// - /// @return radians - /// - double radianSeperation(const NdArray& inVector) const - { - if (inVector.size() != 3) - { - THROW_INVALID_ARGUMENT_ERROR("input vector must be of length 3."); - } - - return std::acos(dot(xyz(), inVector.flatten()).item()); - } + return std::acos(dot(xyz(), inVector.flatten()).item()); + } - //============================================================================ - /// Returns coordinate as a string representation - /// - /// @return string - /// - std::string str() const - { - std::string returnStr; - returnStr = ra_.str(); - returnStr += dec_.str(); - returnStr += "Cartesian = " + xyz().str(); - return returnStr; - } + //============================================================================ + /// Returns coordinate as a string representation + /// + /// @return string + /// + [[nodiscard]] std::string str() const + { + std::string returnStr; + returnStr = ra_.str(); + returnStr += dec_.str(); + returnStr += "Cartesian = " + xyz().str(); + return returnStr; + } - //============================================================================ - /// Prints the Coordinate object to the console - /// - void print() const - { - std::cout << *this; - } + //============================================================================ + /// Prints the Coordinate object to the console + /// + void print() const + { + std::cout << *this; + } - //============================================================================ - /// Equality operator - /// - /// @param inRhs - /// - /// @return bool - /// - bool operator==(const Coordinate& inRhs) const noexcept - { - return ra_ == inRhs.ra_ && dec_ == inRhs.dec_; - } + //============================================================================ + /// Equality operator + /// + /// @param inRhs + /// + /// @return bool + /// + bool operator==(const Coordinate& inRhs) const noexcept + { + return ra_ == inRhs.ra_ && dec_ == inRhs.dec_; + } - //============================================================================ - /// Not equality operator - /// - /// @param inRhs - /// - /// @return bool - /// - bool operator!=(const Coordinate& inRhs) const noexcept - { - return !(*this == inRhs); - } + //============================================================================ + /// Not equality operator + /// + /// @param inRhs + /// + /// @return bool + /// + bool operator!=(const Coordinate& inRhs) const noexcept + { + return !(*this == inRhs); + } - //============================================================================ - /// Ostream operator - /// - /// @param inStream - /// @param inCoord - /// - /// @return std::ostream - /// - friend std::ostream& operator<<(std::ostream& inStream, const Coordinate& inCoord) - { - inStream << inCoord.str(); - return inStream; - } + //============================================================================ + /// Ostream operator + /// + /// @param inStream + /// @param inCoord + /// + /// @return std::ostream + /// + friend std::ostream& operator<<(std::ostream& inStream, const Coordinate& inCoord) + { + inStream << inCoord.str(); + return inStream; + } - private: - //====================================Attributes============================== - RA ra_{}; - Dec dec_{}; - double x_{ 1. }; - double y_{ 0. }; - double z_{ 0. }; + private: + //====================================Attributes============================== + RA ra_{}; + Dec dec_{}; + double x_{ 1. }; + double y_{ 0. }; + double z_{ 0. }; - //============================================================================ - /// Converts polar coordinates to cartesian coordinates - /// - void cartesianToPolar() noexcept + //============================================================================ + /// Converts polar coordinates to cartesian coordinates + /// + void cartesianToPolar() noexcept + { + double degreesRa = rad2deg(std::atan2(y_, x_)); + if (degreesRa < 0) { - double degreesRa = rad2deg(std::atan2(y_, x_)); - if (degreesRa < 0) - { - degreesRa += 360; - } - ra_ = RA(degreesRa); - - const double r = std::sqrt(utils::sqr(x_) + utils::sqr(y_) + utils::sqr(z_)); - const double degreesDec = rad2deg(std::asin(z_ / r)); - dec_ = Dec(degreesDec); + degreesRa += 360; } + ra_ = RA(degreesRa); - //============================================================================ - /// Converts polar coordinates to cartesian coordinates - /// - void polarToCartesian() noexcept - { - const double raRadians = deg2rad(ra_.degrees()); - const double decRadians = deg2rad(dec_.degrees()); + const double r = std::sqrt(utils::sqr(x_) + utils::sqr(y_) + utils::sqr(z_)); + const double degreesDec = rad2deg(std::asin(z_ / r)); + dec_ = Dec(degreesDec); + } - x_ = std::cos(raRadians) * std::cos(decRadians); - y_ = std::sin(raRadians) * std::cos(decRadians); - z_ = std::sin(decRadians); - } - }; - } // namespace coordinates -} // namespace nc + //============================================================================ + /// Converts polar coordinates to cartesian coordinates + /// + void polarToCartesian() noexcept + { + const double raRadians = deg2rad(ra_.degrees()); + const double decRadians = deg2rad(dec_.degrees()); + + x_ = std::cos(raRadians) * std::cos(decRadians); + y_ = std::sin(raRadians) * std::cos(decRadians); + z_ = std::sin(decRadians); + } + }; +} // namespace nc::coordinates diff --git a/include/NumCpp/Coordinates/Dec.hpp b/include/NumCpp/Coordinates/Dec.hpp index 295a36b65..5d3fdee71 100644 --- a/include/NumCpp/Coordinates/Dec.hpp +++ b/include/NumCpp/Coordinates/Dec.hpp @@ -37,199 +37,196 @@ #include "NumCpp/Utils/essentiallyEqual.hpp" #include "NumCpp/Utils/num2str.hpp" -namespace nc +namespace nc::coordinates { - namespace coordinates + //================================================================================ + /// Struct Enum for positive or negative Dec angle + enum class Sign { - //================================================================================ - /// Struct Enum for positive or negative Dec angle - enum class Sign - { - NEGATIVE = 0, - POSITIVE - }; + NEGATIVE = 0, + POSITIVE + }; - //================================================================================ - /// Holds a Declination object - class Dec + //================================================================================ + /// Holds a Declination object + class Dec + { + public: + //============================================================================ + /// Default Constructor + /// + Dec() = default; + + //============================================================================ + /// Constructor + /// + /// @param inDegrees + /// + explicit Dec(double inDegrees) : + degrees_(inDegrees), + radians_(deg2rad(inDegrees)) { - public: - //============================================================================ - /// Default Constructor - /// - Dec() = default; - - //============================================================================ - /// Constructor - /// - /// @param inDegrees - /// - explicit Dec(double inDegrees) : - degrees_(inDegrees), - radians_(deg2rad(inDegrees)) - { - if (inDegrees < -90 || inDegrees > 90) - { - THROW_INVALID_ARGUMENT_ERROR("input degrees must be of the range [-90, 90]"); - } - - sign_ = degrees_ < 0 ? Sign::NEGATIVE : Sign::POSITIVE; - const double absDegrees = std::abs(degrees_); - degreesWhole_ = static_cast(std::floor(absDegrees)); - - const double decMinutes = (absDegrees - static_cast(degreesWhole_)) * 60.; - minutes_ = static_cast(std::floor(decMinutes)); - seconds_ = (decMinutes - static_cast(minutes_)) * 60.; - } - - //============================================================================ - /// Constructor - /// - /// @param inSign - /// @param inDegrees - /// @param inMinutes - /// @param inSeconds - /// - Dec(Sign inSign, uint8 inDegrees, uint8 inMinutes, double inSeconds) noexcept : - sign_(inSign), - degreesWhole_(inDegrees), - minutes_(inMinutes), - seconds_(inSeconds) - { - degrees_ = static_cast(degreesWhole_) + static_cast(minutes_) / 60. + seconds_ / 3600.; - degrees_ *= sign_ == Sign::NEGATIVE ? -1 : 1; - - radians_ = deg2rad(degrees_); - } - - //============================================================================ - /// Get the sign of the degrees (positive or negative) - /// - /// @return Sign - /// - Sign sign() const noexcept - { - return sign_; - } - - //============================================================================ - /// Get the degrees value - /// - /// @return degrees - /// - double degrees() const noexcept - { - return degrees_; - } - - //============================================================================ - /// Get the radians value - /// - /// @return minutes - /// - double radians() const noexcept + if (inDegrees < -90 || inDegrees > 90) { - return radians_; + THROW_INVALID_ARGUMENT_ERROR("input degrees must be of the range [-90, 90]"); } - //============================================================================ - /// Get the whole degrees value - /// - /// @return whole degrees - /// - uint8 degreesWhole() const noexcept - { - return degreesWhole_; - } - - //============================================================================ - /// Get the minute value - /// - /// @return minutes - /// - uint8 minutes() const noexcept - { - return minutes_; - } - - //============================================================================ - /// Get the seconds value - /// - /// @return seconds - /// - double seconds() const noexcept - { - return seconds_; - } - - //============================================================================ - /// Return the dec object as a string representation - /// - /// @return std::string - /// - std::string str() const - { - std::string strSign = sign_ == Sign::NEGATIVE ? "-" : "+"; - std::string out = "Dec dms: " + strSign + utils::num2str(degreesWhole_) + " degrees, " + - utils::num2str(minutes_) + " minutes, "; - out += utils::num2str(seconds_) + " seconds\nDec degrees = " + utils::num2str(degrees_) + '\n'; - return out; - } - - //============================================================================ - /// Prints the Dec object to the console - /// - void print() const - { - std::cout << *this; - } - - //============================================================================ - /// Equality operator - /// - /// @param inRhs - /// - /// @return bool - /// - bool operator==(const Dec& inRhs) const noexcept - { - return utils::essentiallyEqual(degrees_, inRhs.degrees_); - } - - //============================================================================ - /// Not equality operator - /// - /// @param inRhs - /// - /// @return bool - /// - bool operator!=(const Dec& inRhs) const noexcept - { - return !(*this == inRhs); - } - - //============================================================================ - /// Ostream operator - /// - /// @param inStream - /// @param inDec - /// - /// @return std::ostream - /// - friend std::ostream& operator<<(std::ostream& inStream, const Dec& inDec) - { - inStream << inDec.str(); - return inStream; - } - - private: - //====================================Attributes============================== - Sign sign_{ Sign::POSITIVE }; - uint8 degreesWhole_{ 0 }; - uint8 minutes_{ 0 }; - double seconds_{ 0. }; - double degrees_{ 0. }; - double radians_{ 0. }; - }; - } // namespace coordinates -} // namespace nc + sign_ = degrees_ < 0 ? Sign::NEGATIVE : Sign::POSITIVE; + const double absDegrees = std::abs(degrees_); + degreesWhole_ = static_cast(std::floor(absDegrees)); + + const double decMinutes = (absDegrees - static_cast(degreesWhole_)) * 60.; + minutes_ = static_cast(std::floor(decMinutes)); + seconds_ = (decMinutes - static_cast(minutes_)) * 60.; + } + + //============================================================================ + /// Constructor + /// + /// @param inSign + /// @param inDegrees + /// @param inMinutes + /// @param inSeconds + /// + Dec(Sign inSign, uint8 inDegrees, uint8 inMinutes, double inSeconds) noexcept : + sign_(inSign), + degreesWhole_(inDegrees), + minutes_(inMinutes), + seconds_(inSeconds) + { + degrees_ = static_cast(degreesWhole_) + static_cast(minutes_) / 60. + seconds_ / 3600.; + degrees_ *= sign_ == Sign::NEGATIVE ? -1 : 1; + + radians_ = deg2rad(degrees_); + } + + //============================================================================ + /// Get the sign of the degrees (positive or negative) + /// + /// @return Sign + /// + [[nodiscard]] Sign sign() const noexcept + { + return sign_; + } + + //============================================================================ + /// Get the degrees value + /// + /// @return degrees + /// + [[nodiscard]] double degrees() const noexcept + { + return degrees_; + } + + //============================================================================ + /// Get the radians value + /// + /// @return minutes + /// + [[nodiscard]] double radians() const noexcept + { + return radians_; + } + + //============================================================================ + /// Get the whole degrees value + /// + /// @return whole degrees + /// + [[nodiscard]] uint8 degreesWhole() const noexcept + { + return degreesWhole_; + } + + //============================================================================ + /// Get the minute value + /// + /// @return minutes + /// + [[nodiscard]] uint8 minutes() const noexcept + { + return minutes_; + } + + //============================================================================ + /// Get the seconds value + /// + /// @return seconds + /// + [[nodiscard]] double seconds() const noexcept + { + return seconds_; + } + + //============================================================================ + /// Return the dec object as a string representation + /// + /// @return std::string + /// + [[nodiscard]] std::string str() const + { + std::string strSign = sign_ == Sign::NEGATIVE ? "-" : "+"; + std::string out = "Dec dms: " + strSign + utils::num2str(degreesWhole_) + " degrees, " + + utils::num2str(minutes_) + " minutes, "; + out += utils::num2str(seconds_) + " seconds\nDec degrees = " + utils::num2str(degrees_) + '\n'; + return out; + } + + //============================================================================ + /// Prints the Dec object to the console + /// + void print() const + { + std::cout << *this; + } + + //============================================================================ + /// Equality operator + /// + /// @param inRhs + /// + /// @return bool + /// + bool operator==(const Dec& inRhs) const noexcept + { + return utils::essentiallyEqual(degrees_, inRhs.degrees_); + } + + //============================================================================ + /// Not equality operator + /// + /// @param inRhs + /// + /// @return bool + /// + bool operator!=(const Dec& inRhs) const noexcept + { + return !(*this == inRhs); + } + + //============================================================================ + /// Ostream operator + /// + /// @param inStream + /// @param inDec + /// + /// @return std::ostream + /// + friend std::ostream& operator<<(std::ostream& inStream, const Dec& inDec) + { + inStream << inDec.str(); + return inStream; + } + + private: + //====================================Attributes============================== + Sign sign_{ Sign::POSITIVE }; + uint8 degreesWhole_{ 0 }; + uint8 minutes_{ 0 }; + double seconds_{ 0. }; + double degrees_{ 0. }; + double radians_{ 0. }; + }; +} // namespace nc::coordinates diff --git a/include/NumCpp/Coordinates/RA.hpp b/include/NumCpp/Coordinates/RA.hpp index ecc86f6af..5a53bb757 100644 --- a/include/NumCpp/Coordinates/RA.hpp +++ b/include/NumCpp/Coordinates/RA.hpp @@ -37,171 +37,168 @@ #include "NumCpp/Utils/essentiallyEqual.hpp" #include "NumCpp/Utils/num2str.hpp" -namespace nc +namespace nc::coordinates { - namespace coordinates + //================================================================================ + /// Holds a right ascension object + class RA { - //================================================================================ - /// Holds a right ascension object - class RA + public: + //============================================================================ + /// Default Constructor + /// + RA() = default; + + //============================================================================ + /// Constructor + /// + /// @param inDegrees + /// + explicit RA(double inDegrees) : + degrees_(inDegrees), + radians_(deg2rad(inDegrees)) { - public: - //============================================================================ - /// Default Constructor - /// - RA() = default; - - //============================================================================ - /// Constructor - /// - /// @param inDegrees - /// - explicit RA(double inDegrees) : - degrees_(inDegrees), - radians_(deg2rad(inDegrees)) + if (inDegrees < 0 || inDegrees >= 360) { - if (inDegrees < 0 || inDegrees >= 360) - { - THROW_INVALID_ARGUMENT_ERROR("input degrees must be of the range [0, 360)"); - } - - hours_ = static_cast(std::floor(degrees_ / 15.)); - const double decMinutes = (degrees_ - static_cast(hours_) * 15.) * 4.; - minutes_ = static_cast(std::floor(decMinutes)); - seconds_ = static_cast((decMinutes - static_cast(minutes_)) * 60.); + THROW_INVALID_ARGUMENT_ERROR("input degrees must be of the range [0, 360)"); } - //============================================================================ - /// Constructor - /// - /// @param inHours - /// @param inMinutes - /// @param inSeconds - /// - RA(uint8 inHours, uint8 inMinutes, double inSeconds) - noexcept : - hours_(inHours), - minutes_(inMinutes), - seconds_(inSeconds) - { - degrees_ = static_cast(hours_) * 15. + static_cast(minutes_) / 4. + seconds_ / 240.; - radians_ = deg2rad(degrees_); - } - - //============================================================================ - /// Get the radians value - /// - /// @return radians - /// - double radians() const noexcept - { - return radians_; - } - - //============================================================================ - /// Get the degrees value - /// - /// @return degrees - /// - double degrees() const noexcept - { - return degrees_; - } - - //============================================================================ - /// Get the hour value - /// - /// @return hours - /// - uint8 hours() const noexcept - { - return hours_; - } - - //============================================================================ - /// Get the minute value - /// - /// @return minutes - /// - uint8 minutes() const noexcept - { - return minutes_; - } - - //============================================================================ - /// Get the seconds value - /// - /// @return seconds - /// - double seconds() const noexcept - { - return seconds_; - } - - //============================================================================ - /// Return the RA object as a string representation - /// - /// @return std::string - /// - std::string str() const - { - std::string out = - "RA hms: " + utils::num2str(hours_) + " hours, " + utils::num2str(minutes_) + " minutes, "; - out += utils::num2str(seconds_) + " seconds\nRA degrees: " + utils::num2str(degrees_) + '\n'; - return out; - } - - //============================================================================ - /// Prints the RA object to the console - /// - void print() const - { - std::cout << *this; - } - - //============================================================================ - /// Equality operator - /// - /// @param inRhs - /// - /// @return bool - /// - bool operator==(const RA& inRhs) const noexcept - { - return utils::essentiallyEqual(degrees_, inRhs.degrees_); - } - - //============================================================================ - /// Not equality operator - /// - /// @param inRhs - /// - /// @return bool - /// - bool operator!=(const RA& inRhs) const noexcept - { - return !(*this == inRhs); - } - - //============================================================================ - /// Ostream operator - /// - /// @param inStream - /// @param inRa - /// - friend std::ostream& operator<<(std::ostream& inStream, const RA& inRa) - { - inStream << inRa.str(); - return inStream; - } - - private: - //====================================Attributes============================== - uint8 hours_{ 0 }; - uint8 minutes_{ 0 }; - double seconds_{ 0. }; - double degrees_{ 0. }; - double radians_{ 0. }; - }; - } // namespace coordinates -} // namespace nc + hours_ = static_cast(std::floor(degrees_ / 15.)); + const double decMinutes = (degrees_ - static_cast(hours_) * 15.) * 4.; + minutes_ = static_cast(std::floor(decMinutes)); + seconds_ = static_cast((decMinutes - static_cast(minutes_)) * 60.); + } + + //============================================================================ + /// Constructor + /// + /// @param inHours + /// @param inMinutes + /// @param inSeconds + /// + RA(uint8 inHours, uint8 inMinutes, double inSeconds) + noexcept : + hours_(inHours), + minutes_(inMinutes), + seconds_(inSeconds) + { + degrees_ = static_cast(hours_) * 15. + static_cast(minutes_) / 4. + seconds_ / 240.; + radians_ = deg2rad(degrees_); + } + + //============================================================================ + /// Get the radians value + /// + /// @return radians + /// + [[nodiscard]] double radians() const noexcept + { + return radians_; + } + + //============================================================================ + /// Get the degrees value + /// + /// @return degrees + /// + [[nodiscard]] double degrees() const noexcept + { + return degrees_; + } + + //============================================================================ + /// Get the hour value + /// + /// @return hours + /// + [[nodiscard]] uint8 hours() const noexcept + { + return hours_; + } + + //============================================================================ + /// Get the minute value + /// + /// @return minutes + /// + [[nodiscard]] uint8 minutes() const noexcept + { + return minutes_; + } + + //============================================================================ + /// Get the seconds value + /// + /// @return seconds + /// + [[nodiscard]] double seconds() const noexcept + { + return seconds_; + } + + //============================================================================ + /// Return the RA object as a string representation + /// + /// @return std::string + /// + [[nodiscard]] std::string str() const + { + std::string out = + "RA hms: " + utils::num2str(hours_) + " hours, " + utils::num2str(minutes_) + " minutes, "; + out += utils::num2str(seconds_) + " seconds\nRA degrees: " + utils::num2str(degrees_) + '\n'; + return out; + } + + //============================================================================ + /// Prints the RA object to the console + /// + void print() const + { + std::cout << *this; + } + + //============================================================================ + /// Equality operator + /// + /// @param inRhs + /// + /// @return bool + /// + bool operator==(const RA& inRhs) const noexcept + { + return utils::essentiallyEqual(degrees_, inRhs.degrees_); + } + + //============================================================================ + /// Not equality operator + /// + /// @param inRhs + /// + /// @return bool + /// + bool operator!=(const RA& inRhs) const noexcept + { + return !(*this == inRhs); + } + + //============================================================================ + /// Ostream operator + /// + /// @param inStream + /// @param inRa + /// + friend std::ostream& operator<<(std::ostream& inStream, const RA& inRa) + { + inStream << inRa.str(); + return inStream; + } + + private: + //====================================Attributes============================== + uint8 hours_{ 0 }; + uint8 minutes_{ 0 }; + double seconds_{ 0. }; + double degrees_{ 0. }; + double radians_{ 0. }; + }; +} // namespace nc::coordinates diff --git a/include/NumCpp/Coordinates/degreeSeperation.hpp b/include/NumCpp/Coordinates/degreeSeperation.hpp index 72fd1dcb0..1ab4cdd0c 100644 --- a/include/NumCpp/Coordinates/degreeSeperation.hpp +++ b/include/NumCpp/Coordinates/degreeSeperation.hpp @@ -30,36 +30,33 @@ #include "NumCpp/Coordinates/Coordinate.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::coordinates { - namespace coordinates + //============================================================================ + /// Returns the degree seperation between the two Coordinates + /// + /// @param inCoordinate1 + /// @param inCoordinate2 + /// + /// @return degrees + /// + inline double degreeSeperation(const Coordinate& inCoordinate1, const Coordinate& inCoordinate2) { - //============================================================================ - /// Returns the degree seperation between the two Coordinates - /// - /// @param inCoordinate1 - /// @param inCoordinate2 - /// - /// @return degrees - /// - inline double degreeSeperation(const Coordinate& inCoordinate1, const Coordinate& inCoordinate2) - { - return inCoordinate1.degreeSeperation(inCoordinate2); - } + return inCoordinate1.degreeSeperation(inCoordinate2); + } - //============================================================================ - /// Returns the degree seperation between the Coordinate - /// and the input vector - /// - /// @param inVector1 - /// @param inVector2 - /// - /// @return degrees - /// - inline double degreeSeperation(const NdArray& inVector1, const NdArray& inVector2) - { - const Coordinate inCoord1(inVector1); - return inCoord1.degreeSeperation(inVector2); - } - } // namespace coordinates -} // namespace nc + //============================================================================ + /// Returns the degree seperation between the Coordinate + /// and the input vector + /// + /// @param inVector1 + /// @param inVector2 + /// + /// @return degrees + /// + inline double degreeSeperation(const NdArray& inVector1, const NdArray& inVector2) + { + const Coordinate inCoord1(inVector1); + return inCoord1.degreeSeperation(inVector2); + } +} // namespace nc::coordinates diff --git a/include/NumCpp/Coordinates/radianSeperation.hpp b/include/NumCpp/Coordinates/radianSeperation.hpp index 726e2eab1..d0e516267 100644 --- a/include/NumCpp/Coordinates/radianSeperation.hpp +++ b/include/NumCpp/Coordinates/radianSeperation.hpp @@ -30,36 +30,33 @@ #include "NumCpp/Coordinates/Coordinate.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::coordinates { - namespace coordinates + //============================================================================ + /// Returns the radian seperation between the two Coordinates + /// + /// @param inCoordinate1 + /// @param inCoordinate2 + /// + /// @return radians + /// + inline double radianSeperation(const Coordinate& inCoordinate1, const Coordinate& inCoordinate2) { - //============================================================================ - /// Returns the radian seperation between the two Coordinates - /// - /// @param inCoordinate1 - /// @param inCoordinate2 - /// - /// @return radians - /// - inline double radianSeperation(const Coordinate& inCoordinate1, const Coordinate& inCoordinate2) - { - return inCoordinate1.radianSeperation(inCoordinate2); - } + return inCoordinate1.radianSeperation(inCoordinate2); + } - //============================================================================ - /// Returns the radian seperation between the Coordinate - /// and the input vector - /// - /// @param inVector1 - /// @param inVector2 - /// - /// @return radians - /// - inline double radianSeperation(const NdArray& inVector1, const NdArray& inVector2) - { - const Coordinate inCoord1(inVector1); - return inCoord1.radianSeperation(inVector2); - } - } // namespace coordinates -} // namespace nc + //============================================================================ + /// Returns the radian seperation between the Coordinate + /// and the input vector + /// + /// @param inVector1 + /// @param inVector2 + /// + /// @return radians + /// + inline double radianSeperation(const NdArray& inVector1, const NdArray& inVector2) + { + const Coordinate inCoord1(inVector1); + return inCoord1.radianSeperation(inVector2); + } +} // namespace nc::coordinates diff --git a/include/NumCpp/Core/Constants.hpp b/include/NumCpp/Core/Constants.hpp index 3d3a97a85..45dab7623 100644 --- a/include/NumCpp/Core/Constants.hpp +++ b/include/NumCpp/Core/Constants.hpp @@ -31,29 +31,24 @@ #include #include -namespace nc +namespace nc::constants { - //================================Constants==================================== - /// Holds usefull constants - namespace constants - { - constexpr double c = 3.e8; ///< speed of light - constexpr double e = 2.718281828459045; ///< eulers number - constexpr double inf = std::numeric_limits::infinity(); ///< infinity - constexpr double pi = 3.141592653589793238462643383279502884; ///< Pi - const double nan = std::nan("1"); ///< NaN - constexpr auto j = std::complex(0, 1); // sqrt(-1) unit imaginary number + constexpr double c = 3.e8; ///< speed of light + constexpr double e = 2.718281828459045; ///< eulers number + constexpr double inf = std::numeric_limits::infinity(); ///< infinity + constexpr double pi = 3.141592653589793238462643383279502884; ///< Pi + const double nan = std::nan("1"); ///< NaN + constexpr auto j = std::complex(0, 1); // sqrt(-1) unit imaginary number - constexpr double DAYS_PER_WEEK = 7; ///< Number of days in a week - constexpr double MINUTES_PER_HOUR = 60; ///< Number of minutes in an hour - constexpr double SECONDS_PER_MINUTE = 60; ///< Number of seconds in a minute - constexpr double MILLISECONDS_PER_SECOND = 1000; ///< Number of milliseconds in a second - constexpr double SECONDS_PER_HOUR = MINUTES_PER_HOUR * SECONDS_PER_MINUTE; ///< Number of seconds in an hour - constexpr double HOURS_PER_DAY = 24; ///< Number of hours in a day - constexpr double MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR; ///< Number of minutes in a day - constexpr double SECONDS_PER_DAY = MINUTES_PER_DAY * SECONDS_PER_MINUTE; ///< Number of seconds in a day - constexpr double MILLISECONDS_PER_DAY = - SECONDS_PER_DAY * MILLISECONDS_PER_SECOND; ///< Number of milliseconds in a day - constexpr double SECONDS_PER_WEEK = SECONDS_PER_DAY * DAYS_PER_WEEK; ///< Number of seconds in a week - } // namespace constants -} // namespace nc + constexpr double DAYS_PER_WEEK = 7; ///< Number of days in a week + constexpr double MINUTES_PER_HOUR = 60; ///< Number of minutes in an hour + constexpr double SECONDS_PER_MINUTE = 60; ///< Number of seconds in a minute + constexpr double MILLISECONDS_PER_SECOND = 1000; ///< Number of milliseconds in a second + constexpr double SECONDS_PER_HOUR = MINUTES_PER_HOUR * SECONDS_PER_MINUTE; ///< Number of seconds in an hour + constexpr double HOURS_PER_DAY = 24; ///< Number of hours in a day + constexpr double MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR; ///< Number of minutes in a day + constexpr double SECONDS_PER_DAY = MINUTES_PER_DAY * SECONDS_PER_MINUTE; ///< Number of seconds in a day + constexpr double MILLISECONDS_PER_DAY = + SECONDS_PER_DAY * MILLISECONDS_PER_SECOND; ///< Number of milliseconds in a day + constexpr double SECONDS_PER_WEEK = SECONDS_PER_DAY * DAYS_PER_WEEK; ///< Number of seconds in a week +} // namespace nc::constants diff --git a/include/NumCpp/Core/DataCube.hpp b/include/NumCpp/Core/DataCube.hpp index 06932489b..9d3470da7 100644 --- a/include/NumCpp/Core/DataCube.hpp +++ b/include/NumCpp/Core/DataCube.hpp @@ -84,7 +84,7 @@ namespace nc /// /// @return NdArray /// - const NdArray& at(uint32 inIndex) const + [[nodiscard]] const NdArray& at(uint32 inIndex) const { return cube_.at(inIndex); } @@ -114,7 +114,7 @@ namespace nc /// /// @return const_iterator /// - const_iterator cbegin() const noexcept + [[nodiscard]] const_iterator cbegin() const noexcept { return cube_.cbegin(); } @@ -171,7 +171,7 @@ namespace nc /// /// @return const_iterator /// - const_iterator cend() const noexcept + [[nodiscard]] const_iterator cend() const noexcept { return cube_.cend(); } @@ -191,7 +191,7 @@ namespace nc /// /// @return Shape /// - const Shape& shape() const noexcept + [[nodiscard]] const Shape& shape() const noexcept { return elementShape_; } @@ -201,7 +201,7 @@ namespace nc /// /// @return size /// - uint32 sizeZ() const noexcept + [[nodiscard]] uint32 sizeZ() const noexcept { return static_cast(cube_.size()); } @@ -244,7 +244,7 @@ namespace nc /// @param inIndex: the flattend 2d index (row, col) to slice /// @return NdArray /// - NdArray sliceZAll(int32 inIndex) const + [[nodiscard]] NdArray sliceZAll(int32 inIndex) const { if (inIndex < 0) { @@ -268,7 +268,7 @@ namespace nc /// @param inSliceZ: the slice dimensions of the z-axis /// @return NdArray /// - NdArray sliceZ(int32 inIndex, Slice inSliceZ) const + [[nodiscard]] NdArray sliceZ(int32 inIndex, Slice inSliceZ) const { if (inIndex < 0) { @@ -293,7 +293,7 @@ namespace nc /// @param inCol /// @return NdArray /// - NdArray sliceZAll(int32 inRow, int32 inCol) const + [[nodiscard]] NdArray sliceZAll(int32 inRow, int32 inCol) const { if (inRow < 0) { @@ -323,7 +323,7 @@ namespace nc /// @param inSliceZ: the slice dimensions of the z-axis /// @return NdArray /// - NdArray sliceZ(int32 inRow, int32 inCol, Slice inSliceZ) const + [[nodiscard]] NdArray sliceZ(int32 inRow, int32 inCol, Slice inSliceZ) const { if (inRow < 0) { @@ -353,7 +353,7 @@ namespace nc /// @param inCol /// @return NdArray /// - NdArray sliceZAll(Slice inRow, int32 inCol) const + [[nodiscard]] NdArray sliceZAll(Slice inRow, int32 inCol) const { if (inCol < 0) { @@ -377,7 +377,7 @@ namespace nc /// @param inSliceZ: the slice dimensions of the z-axis /// @return NdArray /// - NdArray sliceZ(Slice inRow, int32 inCol, Slice inSliceZ) const + [[nodiscard]] NdArray sliceZ(Slice inRow, int32 inCol, Slice inSliceZ) const { if (inCol < 0) { @@ -401,7 +401,7 @@ namespace nc /// @param inCol /// @return NdArray /// - NdArray sliceZAll(int32 inRow, Slice inCol) const + [[nodiscard]] NdArray sliceZAll(int32 inRow, Slice inCol) const { if (inRow < 0) { @@ -425,7 +425,7 @@ namespace nc /// @param inSliceZ: the slice dimensions of the z-axis /// @return NdArray /// - NdArray sliceZ(int32 inRow, Slice inCol, Slice inSliceZ) const + [[nodiscard]] NdArray sliceZ(int32 inRow, Slice inCol, Slice inSliceZ) const { if (inRow < 0) { @@ -485,7 +485,7 @@ namespace nc /// @param inIndex: the flattend 2d index (row, col) to slice /// @return NdArray /// - NdArray sliceZAllat(int32 inIndex) const + [[nodiscard]] NdArray sliceZAllat(int32 inIndex) const { if (inIndex < 0) { @@ -507,7 +507,7 @@ namespace nc /// @param inSliceZ: the slice dimensions of the z-axis /// @return NdArray /// - NdArray sliceZat(int32 inIndex, Slice inSliceZ) const + [[nodiscard]] NdArray sliceZat(int32 inIndex, Slice inSliceZ) const { if (inIndex < 0) { @@ -535,7 +535,7 @@ namespace nc /// @param inCol /// @return NdArray /// - NdArray sliceZAllat(int32 inRow, int32 inCol) const + [[nodiscard]] NdArray sliceZAllat(int32 inRow, int32 inCol) const { if (inRow < 0) { @@ -568,7 +568,7 @@ namespace nc /// @param inSliceZ: the slice dimensions of the z-axis /// @return NdArray /// - NdArray sliceZat(int32 inRow, int32 inCol, Slice inSliceZ) const + [[nodiscard]] NdArray sliceZat(int32 inRow, int32 inCol, Slice inSliceZ) const { if (inRow < 0) { @@ -605,7 +605,7 @@ namespace nc /// @param inCol /// @return NdArray /// - NdArray sliceZAllat(Slice inRow, int32 inCol) const + [[nodiscard]] NdArray sliceZAllat(Slice inRow, int32 inCol) const { auto numRows = inRow.numElements(elementShape_.rows); if (numRows > elementShape_.rows) @@ -634,7 +634,7 @@ namespace nc /// @param inSliceZ: the slice dimensions of the z-axis /// @return NdArray /// - NdArray sliceZat(Slice inRow, int32 inCol, Slice inSliceZ) const + [[nodiscard]] NdArray sliceZat(Slice inRow, int32 inCol, Slice inSliceZ) const { auto numRows = inRow.numElements(elementShape_.rows); if (numRows > elementShape_.rows) @@ -668,7 +668,7 @@ namespace nc /// @param inCol /// @return NdArray /// - NdArray sliceZAllat(int32 inRow, Slice inCol) const + [[nodiscard]] NdArray sliceZAllat(int32 inRow, Slice inCol) const { auto numCols = inCol.numElements(elementShape_.cols); if (numCols > elementShape_.cols) @@ -697,7 +697,7 @@ namespace nc /// @param inSliceZ: the slice dimensions of the z-axis /// @return NdArray /// - NdArray sliceZat(int32 inRow, Slice inCol, Slice inSliceZ) const + [[nodiscard]] NdArray sliceZat(int32 inRow, Slice inCol, Slice inSliceZ) const { auto numCols = inCol.numElements(elementShape_.cols); if (numCols > elementShape_.cols) diff --git a/include/NumCpp/Core/Internal/Endian.hpp b/include/NumCpp/Core/Internal/Endian.hpp index a34e8a228..463b67d3f 100644 --- a/include/NumCpp/Core/Internal/Endian.hpp +++ b/include/NumCpp/Core/Internal/Endian.hpp @@ -31,54 +31,51 @@ #include "NumCpp/Core/Types.hpp" -namespace nc +namespace nc::endian { - namespace endian + //============================================================================ + // Function Description: + /// Determines the endianess of the system + /// + /// @return bool true if the system is little endian + /// + inline bool isLittleEndian() noexcept { - //============================================================================ - // Function Description: - /// Determines the endianess of the system - /// - /// @return bool true if the system is little endian - /// - inline bool isLittleEndian() noexcept + union { - union - { - uint32 i; - char c[4]; - } fourBytes = { 0x01020304 }; + uint32 i{}; + char c[4]; + } fourBytes = { 0x01020304 }; - return fourBytes.c[0] == 4; - } - - //============================================================================ - // Function Description: - /// Swaps the bytes of the input value - /// - /// @param value - /// @return byte swapped value - /// - template - dtype byteSwap(dtype value) noexcept - { - STATIC_ASSERT_INTEGER(dtype); - static_assert(CHAR_BIT == 8, "CHAR_BIT != 8"); + return fourBytes.c[0] == 4; + } - union - { - dtype value; - uint8 value8[sizeof(dtype)]; - } source, dest; + //============================================================================ + // Function Description: + /// Swaps the bytes of the input value + /// + /// @param value + /// @return byte swapped value + /// + template + dtype byteSwap(dtype value) noexcept + { + STATIC_ASSERT_INTEGER(dtype); + static_assert(CHAR_BIT == 8, "CHAR_BIT != 8"); - source.value = value; + union + { + dtype value; + uint8 value8[sizeof(dtype)]; + } source, dest; - for (size_t k = 0; k < sizeof(dtype); ++k) - { - dest.value8[k] = source.value8[sizeof(dtype) - k - 1]; - } + source.value = value; - return dest.value; + for (size_t k = 0; k < sizeof(dtype); ++k) + { + dest.value8[k] = source.value8[sizeof(dtype) - k - 1]; } - } // namespace endian -} // namespace nc + + return dest.value; + } +} // namespace nc::endian diff --git a/include/NumCpp/Core/Internal/Error.hpp b/include/NumCpp/Core/Internal/Error.hpp index c7bea34f4..fd65d6978 100644 --- a/include/NumCpp/Core/Internal/Error.hpp +++ b/include/NumCpp/Core/Internal/Error.hpp @@ -37,25 +37,22 @@ nc::error::throwError(__FILE__, __func__, __LINE__, msg) #define THROW_RUNTIME_ERROR(msg) nc::error::throwError(__FILE__, __func__, __LINE__, msg) -namespace nc +namespace nc::error { - namespace error + //============================================================================ + /// Makes the full error message string + /// + /// @param file: the file + /// @param function: the function + /// @param line: the line of the file + /// @param msg: the message to throw (default "") + /// + template + void throwError(const std::string& file, const std::string& function, uint32 line, const std::string& msg = "") { - //============================================================================ - /// Makes the full error message string - /// - /// @param file: the file - /// @param function: the function - /// @param line: the line of the file - /// @param msg: the message to throw (default "") - /// - template - void throwError(const std::string& file, const std::string& function, uint32 line, const std::string& msg = "") - { - std::string errMsg = "File: " + file + "\n\tFunction: " + function + "\n\tLine: " + std::to_string(line) + - "\n\tError: " + msg; - std::cerr << errMsg; - throw ErrorType(errMsg); - } - } // namespace error -} // namespace nc + std::string errMsg = + "File: " + file + "\n\tFunction: " + function + "\n\tLine: " + std::to_string(line) + "\n\tError: " + msg; + std::cerr << errMsg; + throw ErrorType(errMsg); + } +} // namespace nc::error diff --git a/include/NumCpp/Core/Internal/Filesystem.hpp b/include/NumCpp/Core/Internal/Filesystem.hpp deleted file mode 100644 index a6efad5b6..000000000 --- a/include/NumCpp/Core/Internal/Filesystem.hpp +++ /dev/null @@ -1,140 +0,0 @@ -/// @file -/// @author David Pilger -/// [GitHub Repository](https://github.com/dpilger26/NumCpp) -/// -/// License -/// Copyright 2018-2023 David Pilger -/// -/// Permission is hereby granted, free of charge, to any person obtaining a copy of this -/// software and associated documentation files(the "Software"), to deal in the Software -/// without restriction, including without limitation the rights to use, copy, modify, -/// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -/// permit persons to whom the Software is furnished to do so, subject to the following -/// conditions : -/// -/// The above copyright notice and this permission notice shall be included in all copies -/// or substantial portions of the Software. -/// -/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -/// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -/// PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -/// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -/// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -/// DEALINGS IN THE SOFTWARE. -/// -/// Description -/// Provides simple filesystem functions -/// -#pragma once - -#include -#include - -namespace nc -{ - namespace filesystem - { - //================================================================================ - /// Provides simple filesystem functions - class File - { - public: - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param filename: the full filename - /// - explicit File(const std::string& filename) : - fullFilename_(filename) - { - const size_t dot = filename.find_last_of('.'); - - filename_ = filename.substr(0, dot); - - if (dot != std::string::npos) - { - extension_ = filename.substr(dot + 1, std::string::npos); - } - - std::ifstream f(filename.c_str()); - exists_ = f.good(); - } - - //============================================================================ - // Method Description: - /// Tests whether or not the file exists - /// - /// @return bool - /// - bool exists() const noexcept - { - return exists_; - } - - //============================================================================ - // Method Description: - /// Returns the file extension without the dot - /// - /// @return std::string - /// - const std::string& ext() const noexcept - { - return extension_; - } - - //============================================================================ - // Method Description: - /// Returns the input full filename - /// - /// @return std::string - /// - std::string fullName() const - { - return filename_ + "." + extension_; - } - - //============================================================================ - // Method Description: - /// Returns true if the file has an extension - /// - /// @return bool - /// - bool hasExt() const - { - return !extension_.empty(); - } - - //============================================================================ - // Method Description: - /// Returns the filename - /// - /// @return std::string - /// - const std::string& name() const noexcept - { - return filename_; - } - - //============================================================================ - // Method Description: - /// Sets the extension to the input extension. Do not input the dot. - /// E.g. input "txt", not ".txt" - /// - /// @return std::string - /// - std::string withExt(const std::string& ext) - { - extension_ = ext; - return fullName(); - } - - private: - //================================Attributes================================== - std::string fullFilename_{ "" }; - std::string filename_{ "" }; - std::string extension_{ "" }; - bool exists_{ false }; - }; - } // namespace filesystem -} // namespace nc diff --git a/include/NumCpp/Core/Internal/StlAlgorithms.hpp b/include/NumCpp/Core/Internal/StlAlgorithms.hpp index 9023b4cb5..c444f4ddd 100644 --- a/include/NumCpp/Core/Internal/StlAlgorithms.hpp +++ b/include/NumCpp/Core/Internal/StlAlgorithms.hpp @@ -40,833 +40,818 @@ #define CONDITIONAL_NO_EXCEPT noexcept #endif -namespace nc +namespace nc::stl_algorithms { - namespace stl_algorithms - { - //============================================================================ - // Method Description: - /// Tests if all of the elements of a range satisy a predicate - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param p: unary predicate function - /// @return bool - /// - template - bool all_of(InputIt first, InputIt last, UnaryPredicate p) CONDITIONAL_NO_EXCEPT - { - return std::all_of( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - p); - } - - //============================================================================ - // Method Description: - /// Tests if any of the elements of a range satisy a predicate - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param p: unary predicate function - /// @return bool - /// - template - bool any_of(InputIt first, InputIt last, UnaryPredicate p) CONDITIONAL_NO_EXCEPT - { - return std::any_of( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - p); - } - - //============================================================================ - // Method Description: - /// Copies from one container to another - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param destination: the first iterator of the destination - /// @return OutputIt - /// - template - OutputIt copy(InputIt first, InputIt last, OutputIt destination) CONDITIONAL_NO_EXCEPT - { - return std::copy( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - destination); - } - - //============================================================================ - // Method Description: - /// Counts the values in the range - /// - /// @param first: the first iterator of container - /// @param last: the last iterator of container - /// @param value: the initial value - /// @return count - /// - template - typename std::iterator_traits::difference_type - count(InputIt first, InputIt last, const T& value) CONDITIONAL_NO_EXCEPT - { - return std::count( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - value); - } - - //============================================================================ - // Method Description: - /// Test if two ranges are equal - /// - /// @param first1: the first iterator of first container - /// @param last1: the last iterator of first container - /// @param first2: the first iterator of second container - /// @return bool - /// - template - bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) CONDITIONAL_NO_EXCEPT - { - return std::equal( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first1, - last1, - first2); - } - - //============================================================================ - // Method Description: - /// Test if two ranges are equal - /// - /// @param first1: the first iterator of first container - /// @param last1: the last iterator of first container - /// @param first2: the first iterator of second container - /// @param p: binary predicate to compare the elements - /// @return bool - /// - template - bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, BinaryPredicate p) CONDITIONAL_NO_EXCEPT - { - return std::equal( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first1, - last1, - first2, - p); - } - - //============================================================================ - // Method Description: - /// Fills the range with the value - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param value: the function to apply to the input iterators - /// - template - void fill(ForwardIt first, ForwardIt last, const T& value) CONDITIONAL_NO_EXCEPT - { - return std::fill( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - value); - } - - //============================================================================ - // Method Description: - /// Returns the first element in the range [first, last) - /// that satisfies specific criteria: - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param value: the value to find - /// @return InputIt - /// - template - InputIt find(InputIt first, InputIt last, const T& value) CONDITIONAL_NO_EXCEPT - { - return std::find( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - value); - } - - //============================================================================ - // Method Description: - /// Runs the function on each element of the range - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param f: the function to apply to the input iterators - /// - template - void for_each(InputIt first, InputIt last, UnaryFunction f) - { - std::for_each( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - f); - } - - //============================================================================ - // Method Description: - /// Returns true if the array is sorted - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @return bool true if sorted - /// - template - bool is_sorted(ForwardIt first, ForwardIt last) CONDITIONAL_NO_EXCEPT - { - return std::is_sorted( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last); - } - - //============================================================================ - // Method Description: - /// Returns true if the array is sorted - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param comp: comparitor function - /// @return bool true if sorted - /// - template - bool is_sorted(ForwardIt first, ForwardIt last, Compare comp) CONDITIONAL_NO_EXCEPT - { - return std::is_sorted( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - comp); - } - - //============================================================================ - // Method Description: - /// Returns the maximum element of the range - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @return ForwordIt - /// - template - ForwardIt max_element(ForwardIt first, ForwardIt last) CONDITIONAL_NO_EXCEPT - { - return std::max_element( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last); - } - - //============================================================================ - // Method Description: - /// Returns the maximum element of the range - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param comp: the comparitor function - /// @return ForwordIt - /// - template - ForwardIt max_element(ForwardIt first, ForwardIt last, Compare comp) CONDITIONAL_NO_EXCEPT - { - return std::max_element( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - comp); - } - - //============================================================================ - // Method Description: - /// Returns the minimum element of the range - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @return ForwardIt - template - ForwardIt min_element(ForwardIt first, ForwardIt last) CONDITIONAL_NO_EXCEPT - { - return std::min_element( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last); - } - - //============================================================================ - // Method Description: - /// Returns the minimum element of the range - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param comp: the comparitor function - /// @return ForwordIt - /// - template - ForwardIt min_element(ForwardIt first, ForwardIt last, Compare comp) CONDITIONAL_NO_EXCEPT - { - return std::min_element( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - comp); - } - - //============================================================================ - // Method Description: - /// Runs the minimum and maximum elements of the range - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @return std::pair - /// - template - std::pair minmax_element(ForwardIt first, ForwardIt last) CONDITIONAL_NO_EXCEPT - { - return std::minmax_element( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last); - } - - //============================================================================ - // Method Description: - /// Runs the minimum and maximum elements of the range - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param comp: the comparitor function - /// @return std::pair - /// - template - std::pair - minmax_element(ForwardIt first, ForwardIt last, Compare comp) CONDITIONAL_NO_EXCEPT - { - return std::minmax_element( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - comp); - } - - //============================================================================ - // Method Description: - /// Tests if none of the elements of a range satisy a predicate - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param p: unary predicate function - /// @return bool - /// - template - bool none_of(InputIt first, InputIt last, UnaryPredicate p) CONDITIONAL_NO_EXCEPT - { - return std::none_of( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - p); - } - - //============================================================================ - // Method Description: - /// Sorts up to the nth element - /// - /// @param first: the first iterator of the range - /// @param nth: the element that should be sorted - /// @param last: the last iterator of the range - /// - template - void nth_element(RandomIt first, RandomIt nth, RandomIt last) CONDITIONAL_NO_EXCEPT - { - std::nth_element( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - nth, - last); - } - - //============================================================================ - // Method Description: - /// Sorts up to the nth element - /// - /// @param first: the first iterator of the range - /// @param nth: the element that should be sorted - /// @param last: the last iterator of the range - /// @param comp: the comparitor function - /// - template - void nth_element(RandomIt first, RandomIt nth, RandomIt last, Compare comp) CONDITIONAL_NO_EXCEPT - { - std::nth_element( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - nth, - last, - comp); - } - - //============================================================================ - // Method Description: - /// replaces a value in the range with another value - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param oldValue: the value to replace - /// @param newValue: the replacement value - /// - template - void replace(ForwardIt first, ForwardIt last, const T& oldValue, const T& newValue) CONDITIONAL_NO_EXCEPT - { - std::replace( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - oldValue, - newValue); - } - - //============================================================================ - // Method Description: - /// reverses the range - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// - template - void reverse(BidirIt first, BidirIt last) CONDITIONAL_NO_EXCEPT - { - std::reverse( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last); - } - - //============================================================================ - // Method Description: - /// Rotates the elements of a range - /// - /// @param first: the first iterator of the range - /// @param firstN: the element that should appear at the beginning of the rotated range - /// @param last: the last iterator of the range - /// - template - void rotate(ForwardIt first, ForwardIt firstN, ForwardIt last) CONDITIONAL_NO_EXCEPT - { - std::rotate( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - firstN, - last); - } - - //============================================================================ - // Method Description: - /// finds the difference of two ranges - /// - /// @param first1: the first iterator of the source - /// @param last1: the last iterator of the source - /// @param first2: the first iterator of the second source - /// @param last2: the first iterator of the destination - /// @param destination: the function to apply to the input iterators - /// @return OutputIt - /// - template - OutputIt set_difference(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt destination) - { - return std::set_difference( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first1, - last1, - first2, - last2, - destination); - } - - //============================================================================ - // Method Description: - /// finds the difference of two ranges - /// - /// @param first1: the first iterator of the source - /// @param last1: the last iterator of the source - /// @param first2: the first iterator of the second source - /// @param last2: the first iterator of the destination - /// @param destination: the function to apply to the input iterators - /// @param comp: comparitor function - /// @return OutputIt - /// - template - OutputIt set_difference(InputIt1 first1, - InputIt1 last1, - InputIt2 first2, - InputIt2 last2, - OutputIt destination, - Compare comp) CONDITIONAL_NO_EXCEPT - { - return std::set_difference( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first1, - last1, - first2, - last2, - destination, - comp); - } - - //============================================================================ - // Method Description: - /// finds the intersection of two ranges - /// - /// @param first1: the first iterator of the source - /// @param last1: the last iterator of the source - /// @param first2: the first iterator of the second source - /// @param last2: the first iterator of the destination - /// @param destination: the function to apply to the input iterators - /// @return OutputIt - /// - template - OutputIt set_intersection(InputIt1 first1, - InputIt1 last1, - InputIt2 first2, - InputIt2 last2, - OutputIt destination) CONDITIONAL_NO_EXCEPT - { - return std::set_intersection( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first1, - last1, - first2, - last2, - destination); - } - - //============================================================================ - // Method Description: - /// finds the intersection of two ranges - /// - /// @param first1: the first iterator of the source - /// @param last1: the last iterator of the source - /// @param first2: the first iterator of the second source - /// @param last2: the first iterator of the destination - /// @param destination: the function to apply to the input iterators - /// @param comp: comparitor function - /// @return OutputIt - /// - template - OutputIt set_intersection(InputIt1 first1, - InputIt1 last1, - InputIt2 first2, - InputIt2 last2, - OutputIt destination, - Compare comp) CONDITIONAL_NO_EXCEPT - { - return std::set_intersection( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first1, - last1, - first2, - last2, - destination, - comp); - } - - //============================================================================ - // Method Description: - /// finds the union of two ranges - /// - /// @param first1: the first iterator of the source - /// @param last1: the last iterator of the source - /// @param first2: the first iterator of the second source - /// @param last2: the first iterator of the destination - /// @param destination: the function to apply to the input iterators - /// @return OutputIt - /// - template - OutputIt set_union(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt destination) + //============================================================================ + // Method Description: + /// Tests if all of the elements of a range satisy a predicate + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param p: unary predicate function + /// @return bool + /// + template + bool all_of(InputIt first, InputIt last, UnaryPredicate p) CONDITIONAL_NO_EXCEPT + { + return std::all_of( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + p); + } + + //============================================================================ + // Method Description: + /// Tests if any of the elements of a range satisy a predicate + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param p: unary predicate function + /// @return bool + /// + template + bool any_of(InputIt first, InputIt last, UnaryPredicate p) CONDITIONAL_NO_EXCEPT + { + return std::any_of( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + p); + } + + //============================================================================ + // Method Description: + /// Copies from one container to another + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param destination: the first iterator of the destination + /// @return OutputIt + /// + template + OutputIt copy(InputIt first, InputIt last, OutputIt destination) CONDITIONAL_NO_EXCEPT + { + return std::copy( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + destination); + } + + //============================================================================ + // Method Description: + /// Counts the values in the range + /// + /// @param first: the first iterator of container + /// @param last: the last iterator of container + /// @param value: the initial value + /// @return count + /// + template + typename std::iterator_traits::difference_type + count(InputIt first, InputIt last, const T& value) CONDITIONAL_NO_EXCEPT + { + return std::count( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + value); + } + + //============================================================================ + // Method Description: + /// Test if two ranges are equal + /// + /// @param first1: the first iterator of first container + /// @param last1: the last iterator of first container + /// @param first2: the first iterator of second container + /// @return bool + /// + template + bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) CONDITIONAL_NO_EXCEPT + { + return std::equal( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first1, + last1, + first2); + } + + //============================================================================ + // Method Description: + /// Test if two ranges are equal + /// + /// @param first1: the first iterator of first container + /// @param last1: the last iterator of first container + /// @param first2: the first iterator of second container + /// @param p: binary predicate to compare the elements + /// @return bool + /// + template + bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, BinaryPredicate p) CONDITIONAL_NO_EXCEPT + { + return std::equal( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first1, + last1, + first2, + p); + } + + //============================================================================ + // Method Description: + /// Fills the range with the value + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param value: the function to apply to the input iterators + /// + template + void fill(ForwardIt first, ForwardIt last, const T& value) CONDITIONAL_NO_EXCEPT + { + return std::fill( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + value); + } + + //============================================================================ + // Method Description: + /// Returns the first element in the range [first, last) + /// that satisfies specific criteria: + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param value: the value to find + /// @return InputIt + /// + template + InputIt find(InputIt first, InputIt last, const T& value) CONDITIONAL_NO_EXCEPT + { + return std::find( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + value); + } + + //============================================================================ + // Method Description: + /// Runs the function on each element of the range + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param f: the function to apply to the input iterators + /// + template + void for_each(InputIt first, InputIt last, UnaryFunction f) + { + std::for_each( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + f); + } + + //============================================================================ + // Method Description: + /// Returns true if the array is sorted + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @return bool true if sorted + /// + template + bool is_sorted(ForwardIt first, ForwardIt last) CONDITIONAL_NO_EXCEPT + { + return std::is_sorted( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last); + } + + //============================================================================ + // Method Description: + /// Returns true if the array is sorted + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param comp: comparitor function + /// @return bool true if sorted + /// + template + bool is_sorted(ForwardIt first, ForwardIt last, Compare comp) CONDITIONAL_NO_EXCEPT + { + return std::is_sorted( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + comp); + } + + //============================================================================ + // Method Description: + /// Returns the maximum element of the range + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @return ForwordIt + /// + template + ForwardIt max_element(ForwardIt first, ForwardIt last) CONDITIONAL_NO_EXCEPT + { + return std::max_element( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last); + } + + //============================================================================ + // Method Description: + /// Returns the maximum element of the range + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param comp: the comparitor function + /// @return ForwordIt + /// + template + ForwardIt max_element(ForwardIt first, ForwardIt last, Compare comp) CONDITIONAL_NO_EXCEPT + { + return std::max_element( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + comp); + } + + //============================================================================ + // Method Description: + /// Returns the minimum element of the range + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @return ForwardIt + template + ForwardIt min_element(ForwardIt first, ForwardIt last) CONDITIONAL_NO_EXCEPT + { + return std::min_element( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last); + } + + //============================================================================ + // Method Description: + /// Returns the minimum element of the range + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param comp: the comparitor function + /// @return ForwordIt + /// + template + ForwardIt min_element(ForwardIt first, ForwardIt last, Compare comp) CONDITIONAL_NO_EXCEPT + { + return std::min_element( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + comp); + } + + //============================================================================ + // Method Description: + /// Runs the minimum and maximum elements of the range + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @return std::pair + /// + template + std::pair minmax_element(ForwardIt first, ForwardIt last) CONDITIONAL_NO_EXCEPT + { + return std::minmax_element( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last); + } + + //============================================================================ + // Method Description: + /// Runs the minimum and maximum elements of the range + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param comp: the comparitor function + /// @return std::pair + /// + template + std::pair minmax_element(ForwardIt first, ForwardIt last, Compare comp) CONDITIONAL_NO_EXCEPT + { + return std::minmax_element( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + comp); + } + + //============================================================================ + // Method Description: + /// Tests if none of the elements of a range satisy a predicate + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param p: unary predicate function + /// @return bool + /// + template + bool none_of(InputIt first, InputIt last, UnaryPredicate p) CONDITIONAL_NO_EXCEPT + { + return std::none_of( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + p); + } + + //============================================================================ + // Method Description: + /// Sorts up to the nth element + /// + /// @param first: the first iterator of the range + /// @param nth: the element that should be sorted + /// @param last: the last iterator of the range + /// + template + void nth_element(RandomIt first, RandomIt nth, RandomIt last) CONDITIONAL_NO_EXCEPT + { + std::nth_element( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + nth, + last); + } + + //============================================================================ + // Method Description: + /// Sorts up to the nth element + /// + /// @param first: the first iterator of the range + /// @param nth: the element that should be sorted + /// @param last: the last iterator of the range + /// @param comp: the comparitor function + /// + template + void nth_element(RandomIt first, RandomIt nth, RandomIt last, Compare comp) CONDITIONAL_NO_EXCEPT + { + std::nth_element( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + nth, + last, + comp); + } + + //============================================================================ + // Method Description: + /// replaces a value in the range with another value + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param oldValue: the value to replace + /// @param newValue: the replacement value + /// + template + void replace(ForwardIt first, ForwardIt last, const T& oldValue, const T& newValue) CONDITIONAL_NO_EXCEPT + { + std::replace( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + oldValue, + newValue); + } + + //============================================================================ + // Method Description: + /// reverses the range + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// + template + void reverse(BidirIt first, BidirIt last) CONDITIONAL_NO_EXCEPT + { + std::reverse( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last); + } + + //============================================================================ + // Method Description: + /// Rotates the elements of a range + /// + /// @param first: the first iterator of the range + /// @param firstN: the element that should appear at the beginning of the rotated range + /// @param last: the last iterator of the range + /// + template + void rotate(ForwardIt first, ForwardIt firstN, ForwardIt last) CONDITIONAL_NO_EXCEPT + { + std::rotate( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + firstN, + last); + } + + //============================================================================ + // Method Description: + /// finds the difference of two ranges + /// + /// @param first1: the first iterator of the source + /// @param last1: the last iterator of the source + /// @param first2: the first iterator of the second source + /// @param last2: the first iterator of the destination + /// @param destination: the function to apply to the input iterators + /// @return OutputIt + /// + template + OutputIt set_difference(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt destination) + { + return std::set_difference( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first1, + last1, + first2, + last2, + destination); + } + + //============================================================================ + // Method Description: + /// finds the difference of two ranges + /// + /// @param first1: the first iterator of the source + /// @param last1: the last iterator of the source + /// @param first2: the first iterator of the second source + /// @param last2: the first iterator of the destination + /// @param destination: the function to apply to the input iterators + /// @param comp: comparitor function + /// @return OutputIt + /// + template + OutputIt set_difference(InputIt1 first1, + InputIt1 last1, + InputIt2 first2, + InputIt2 last2, + OutputIt destination, + Compare comp) CONDITIONAL_NO_EXCEPT + { + return std::set_difference( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first1, + last1, + first2, + last2, + destination, + comp); + } + + //============================================================================ + // Method Description: + /// finds the intersection of two ranges + /// + /// @param first1: the first iterator of the source + /// @param last1: the last iterator of the source + /// @param first2: the first iterator of the second source + /// @param last2: the first iterator of the destination + /// @param destination: the function to apply to the input iterators + /// @return OutputIt + /// + template + OutputIt set_intersection(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt destination) + CONDITIONAL_NO_EXCEPT + { + return std::set_intersection( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first1, + last1, + first2, + last2, + destination); + } + + //============================================================================ + // Method Description: + /// finds the intersection of two ranges + /// + /// @param first1: the first iterator of the source + /// @param last1: the last iterator of the source + /// @param first2: the first iterator of the second source + /// @param last2: the first iterator of the destination + /// @param destination: the function to apply to the input iterators + /// @param comp: comparitor function + /// @return OutputIt + /// + template + OutputIt set_intersection(InputIt1 first1, + InputIt1 last1, + InputIt2 first2, + InputIt2 last2, + OutputIt destination, + Compare comp) CONDITIONAL_NO_EXCEPT + { + return std::set_intersection( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first1, + last1, + first2, + last2, + destination, + comp); + } + + //============================================================================ + // Method Description: + /// finds the union of two ranges + /// + /// @param first1: the first iterator of the source + /// @param last1: the last iterator of the source + /// @param first2: the first iterator of the second source + /// @param last2: the first iterator of the destination + /// @param destination: the function to apply to the input iterators + /// @return OutputIt + /// + template + OutputIt set_union(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt destination) + CONDITIONAL_NO_EXCEPT + { + return std::set_union( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first1, + last1, + first2, + last2, + destination); + } + + //============================================================================ + // Method Description: + /// finds the union of two ranges + /// + /// @param first1: the first iterator of the source + /// @param last1: the last iterator of the source + /// @param first2: the first iterator of the second source + /// @param last2: the first iterator of the destination + /// @param destination: the function to apply to the input iterators + /// @param comp: comparitor function + /// @return OutputIt + /// + template + OutputIt + set_union(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt destination, Compare comp) CONDITIONAL_NO_EXCEPT - { - return std::set_union( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first1, - last1, - first2, - last2, - destination); - } - - //============================================================================ - // Method Description: - /// finds the union of two ranges - /// - /// @param first1: the first iterator of the source - /// @param last1: the last iterator of the source - /// @param first2: the first iterator of the second source - /// @param last2: the first iterator of the destination - /// @param destination: the function to apply to the input iterators - /// @param comp: comparitor function - /// @return OutputIt - /// - template - OutputIt set_union(InputIt1 first1, - InputIt1 last1, - InputIt2 first2, - InputIt2 last2, - OutputIt destination, - Compare comp) CONDITIONAL_NO_EXCEPT - { - return std::set_union( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first1, - last1, - first2, - last2, - destination, - comp); - } - - //============================================================================ - // Method Description: - /// Sorts the range - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// - template - void sort(RandomIt first, RandomIt last) CONDITIONAL_NO_EXCEPT - { - return std::sort( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last); - } - - //============================================================================ - // Method Description: - /// Sorts the range - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param comp: the comparitor function - /// - template - void sort(RandomIt first, RandomIt last, Compare comp) CONDITIONAL_NO_EXCEPT - { - return std::sort( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - comp); - } - - //============================================================================ - // Method Description: - /// Sorts the range preserving order - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// - template - void stable_sort(RandomIt first, RandomIt last) CONDITIONAL_NO_EXCEPT - { - std::stable_sort( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last); - } - - //============================================================================ - // Method Description: - /// Sorts the range preserving order - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param comp: the comparitor function - /// - template - void stable_sort(RandomIt first, RandomIt last, Compare comp) CONDITIONAL_NO_EXCEPT - { - std::stable_sort( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - comp); - } - - //============================================================================ - // Method Description: - /// Transforms the elements of the range - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param destination: the first iterator of the destination - /// @param unaryFunction: the function to apply to the input iterators - /// @return OutputIt - /// - template - OutputIt transform(InputIt first, InputIt last, OutputIt destination, UnaryOperation unaryFunction) - { - return std::transform( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - destination, - unaryFunction); - } - - //============================================================================ - // Method Description: - /// Transforms the elements of the range - /// - /// @param first1: the first iterator of the source - /// @param last1: the last iterator of the source - /// @param first2: the first iterator of the second source - /// @param destination: the first iterator of the destination - /// @param unaryFunction: the function to apply to the input iterators - /// @return OutputIt - /// - template - OutputIt transform(InputIt1 first1, - InputIt1 last1, - InputIt2 first2, - OutputIt destination, - BinaryOperation unaryFunction) - { - return std::transform( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first1, - last1, - first2, - destination, - unaryFunction); - } - - //============================================================================ - // Method Description: - /// Copies the unique elements of a range - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param destination: the first iterator of the destination - /// @return OutputIt - /// - template - constexpr OutputIt unique_copy(InputIt first, InputIt last, OutputIt destination) CONDITIONAL_NO_EXCEPT - { - return std::unique_copy( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - destination); - } - - //============================================================================ - // Method Description: - /// Copies the unique elements of a range - /// - /// @param first: the first iterator of the source - /// @param last: the last iterator of the source - /// @param destination: the first iterator of the destination - /// @param binaryFunction: the function to apply to the input iterators - /// @return OutputIt - /// - template - constexpr OutputIt unique_copy(InputIt first, - InputIt last, - OutputIt destination, - BinaryPredicate binaryFunction) CONDITIONAL_NO_EXCEPT - { - return std::unique_copy( -#ifdef PARALLEL_ALGORITHMS_SUPPORTED - std::execution::par_unseq, -#endif - first, - last, - destination, - binaryFunction); - } - } // namespace stl_algorithms -} // namespace nc + { + return std::set_union( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first1, + last1, + first2, + last2, + destination, + comp); + } + + //============================================================================ + // Method Description: + /// Sorts the range + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// + template + void sort(RandomIt first, RandomIt last) CONDITIONAL_NO_EXCEPT + { + return std::sort( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last); + } + + //============================================================================ + // Method Description: + /// Sorts the range + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param comp: the comparitor function + /// + template + void sort(RandomIt first, RandomIt last, Compare comp) CONDITIONAL_NO_EXCEPT + { + return std::sort( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + comp); + } + + //============================================================================ + // Method Description: + /// Sorts the range preserving order + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// + template + void stable_sort(RandomIt first, RandomIt last) CONDITIONAL_NO_EXCEPT + { + std::stable_sort( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last); + } + + //============================================================================ + // Method Description: + /// Sorts the range preserving order + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param comp: the comparitor function + /// + template + void stable_sort(RandomIt first, RandomIt last, Compare comp) CONDITIONAL_NO_EXCEPT + { + std::stable_sort( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + comp); + } + + //============================================================================ + // Method Description: + /// Transforms the elements of the range + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param destination: the first iterator of the destination + /// @param unaryFunction: the function to apply to the input iterators + /// @return OutputIt + /// + template + OutputIt transform(InputIt first, InputIt last, OutputIt destination, UnaryOperation unaryFunction) + { + return std::transform( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + destination, + unaryFunction); + } + + //============================================================================ + // Method Description: + /// Transforms the elements of the range + /// + /// @param first1: the first iterator of the source + /// @param last1: the last iterator of the source + /// @param first2: the first iterator of the second source + /// @param destination: the first iterator of the destination + /// @param unaryFunction: the function to apply to the input iterators + /// @return OutputIt + /// + template + OutputIt + transform(InputIt1 first1, InputIt1 last1, InputIt2 first2, OutputIt destination, BinaryOperation unaryFunction) + { + return std::transform( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first1, + last1, + first2, + destination, + unaryFunction); + } + + //============================================================================ + // Method Description: + /// Copies the unique elements of a range + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param destination: the first iterator of the destination + /// @return OutputIt + /// + template + constexpr OutputIt unique_copy(InputIt first, InputIt last, OutputIt destination) CONDITIONAL_NO_EXCEPT + { + return std::unique_copy( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + destination); + } + + //============================================================================ + // Method Description: + /// Copies the unique elements of a range + /// + /// @param first: the first iterator of the source + /// @param last: the last iterator of the source + /// @param destination: the first iterator of the destination + /// @param binaryFunction: the function to apply to the input iterators + /// @return OutputIt + /// + template + constexpr OutputIt unique_copy(InputIt first, InputIt last, OutputIt destination, BinaryPredicate binaryFunction) + CONDITIONAL_NO_EXCEPT + { + return std::unique_copy( +#ifdef PARALLEL_ALGORITHMS_SUPPORTED + std::execution::par_unseq, +#endif + first, + last, + destination, + binaryFunction); + } +} // namespace nc::stl_algorithms diff --git a/include/NumCpp/Core/Shape.hpp b/include/NumCpp/Core/Shape.hpp index e1a4b7745..05dc89d4a 100644 --- a/include/NumCpp/Core/Shape.hpp +++ b/include/NumCpp/Core/Shape.hpp @@ -101,7 +101,7 @@ namespace nc /// /// @return size /// - uint32 size() const noexcept + [[nodiscard]] uint32 size() const noexcept { return rows * cols; } @@ -112,7 +112,7 @@ namespace nc /// /// @return bool /// - bool isnull() const noexcept + [[nodiscard]] bool isnull() const noexcept { return rows == 0 && cols == 0; } @@ -122,7 +122,7 @@ namespace nc /// /// @return bool /// - bool issquare() const noexcept + [[nodiscard]] bool issquare() const noexcept { return rows == cols; } @@ -132,7 +132,7 @@ namespace nc /// /// @return std::string /// - std::string str() const + [[nodiscard]] std::string str() const { std::string out = "[" + utils::num2str(rows) + ", " + utils::num2str(cols) + "]\n"; return out; diff --git a/include/NumCpp/Core/Slice.hpp b/include/NumCpp/Core/Slice.hpp index 20338bc6c..cfa3a54e2 100644 --- a/include/NumCpp/Core/Slice.hpp +++ b/include/NumCpp/Core/Slice.hpp @@ -118,7 +118,7 @@ namespace nc /// /// @return std::string /// - std::string str() const + [[nodiscard]] std::string str() const { std::string out = "[" + utils::num2str(start) + ":" + utils::num2str(stop) + ":" + utils::num2str(step) + "]\n"; diff --git a/include/NumCpp/DateTime/DateTime.hpp b/include/NumCpp/DateTime/DateTime.hpp index ae3fe0817..9a9d2ba0a 100644 --- a/include/NumCpp/DateTime/DateTime.hpp +++ b/include/NumCpp/DateTime/DateTime.hpp @@ -74,7 +74,7 @@ namespace nc auto fractionalSecond = static_cast(tpSubSeconds.count() % Duration::period::den) / static_cast(Duration::period::den); auto time = Clock::to_time_t(std::chrono::time_point_cast(tp)); - std::tm tm; + std::tm tm{}; #ifdef _MSC_VER gmtime_s(&tm, &time); #else @@ -367,7 +367,7 @@ namespace nc const auto fractionalSeconds = static_cast(timeSinceEpoch % Duration::period::den) / static_cast(Duration::period::den); - std::tm tm; + std::tm tm{}; #ifdef _MSC_VER gmtime_s(&tm, &secondsFromEpoch); #else diff --git a/include/NumCpp/Filter/Boundaries/Boundaries1d/addBoundary1d.hpp b/include/NumCpp/Filter/Boundaries/Boundaries1d/addBoundary1d.hpp index a00a5389e..608a7cb35 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries1d/addBoundary1d.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries1d/addBoundary1d.hpp @@ -40,68 +40,62 @@ #include "NumCpp/Filter/Boundaries/Boundary.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// Wrap boundary + /// + /// @param inImage + /// @param inBoundaryType + /// @param inKernalSize + /// @param inConstantValue (default 0) + /// @return NdArray + /// + template + NdArray addBoundary1d(const NdArray& inImage, + Boundary inBoundaryType, + uint32 inKernalSize, + dtype inConstantValue = 0) { - namespace boundary - { - //============================================================================ - // Method Description: - /// Wrap boundary - /// - /// @param inImage - /// @param inBoundaryType - /// @param inKernalSize - /// @param inConstantValue (default 0) - /// @return NdArray - /// - template - NdArray addBoundary1d(const NdArray& inImage, - Boundary inBoundaryType, - uint32 inKernalSize, - dtype inConstantValue = 0) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inKernalSize % 2 == 0) - { - THROW_INVALID_ARGUMENT_ERROR("input kernal size must be an odd value."); - } + STATIC_ASSERT_ARITHMETIC(dtype); - const uint32 boundarySize = inKernalSize / 2; // integer division + if (inKernalSize % 2 == 0) + { + THROW_INVALID_ARGUMENT_ERROR("input kernal size must be an odd value."); + } - switch (inBoundaryType) - { - case Boundary::REFLECT: - { - return reflect1d(inImage, boundarySize); - } - case Boundary::CONSTANT: - { - return constant1d(inImage, boundarySize, inConstantValue); - } - case Boundary::NEAREST: - { - return nearest1d(inImage, boundarySize); - } - case Boundary::MIRROR: - { - return mirror1d(inImage, boundarySize); - } - case Boundary::WRAP: - { - return wrap1d(inImage, boundarySize); - } - default: - { - // This can't actually happen but just adding to get rid of compiler warning - THROW_INVALID_ARGUMENT_ERROR("ERROR!"); - } - } + const uint32 boundarySize = inKernalSize / 2; // integer division - return NdArray(); // get rid of compiler warning + switch (inBoundaryType) + { + case Boundary::REFLECT: + { + return reflect1d(inImage, boundarySize); + } + case Boundary::CONSTANT: + { + return constant1d(inImage, boundarySize, inConstantValue); } - } // namespace boundary - } // namespace filter -} // namespace nc + case Boundary::NEAREST: + { + return nearest1d(inImage, boundarySize); + } + case Boundary::MIRROR: + { + return mirror1d(inImage, boundarySize); + } + case Boundary::WRAP: + { + return wrap1d(inImage, boundarySize); + } + default: + { + // This can't actually happen but just adding to get rid of compiler warning + THROW_INVALID_ARGUMENT_ERROR("ERROR!"); + } + } + + return NdArray(); // get rid of compiler warning + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundaries1d/constant1d.hpp b/include/NumCpp/Filter/Boundaries/Boundaries1d/constant1d.hpp index a6fec35d3..c373a2ace 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries1d/constant1d.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries1d/constant1d.hpp @@ -32,39 +32,33 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// Constant boundary1d + /// + /// @param inImage + /// @param inBoundarySize + /// @param inConstantValue + /// @return NdArray + /// + template + NdArray constant1d(const NdArray& inImage, uint32 inBoundarySize, dtype inConstantValue) { - namespace boundary - { - //============================================================================ - // Method Description: - /// Constant boundary1d - /// - /// @param inImage - /// @param inBoundarySize - /// @param inConstantValue - /// @return NdArray - /// - template - NdArray constant1d(const NdArray& inImage, uint32 inBoundarySize, dtype inConstantValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - const uint32 outSize = inImage.size() + inBoundarySize * 2; + const uint32 outSize = inImage.size() + inBoundarySize * 2; - NdArray outArray(1, outSize); - outArray.put(Slice(inBoundarySize, inBoundarySize + inImage.size()), inImage); + NdArray outArray(1, outSize); + outArray.put(Slice(inBoundarySize, inBoundarySize + inImage.size()), inImage); - // left - outArray.put(Slice(0, inBoundarySize), inConstantValue); + // left + outArray.put(Slice(0, inBoundarySize), inConstantValue); - // right - outArray.put(Slice(inImage.size() + inBoundarySize, outSize), inConstantValue); + // right + outArray.put(Slice(inImage.size() + inBoundarySize, outSize), inConstantValue); - return outArray; - } - } // namespace boundary - } // namespace filter -} // namespace nc + return outArray; + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundaries1d/mirror1d.hpp b/include/NumCpp/Filter/Boundaries/Boundaries1d/mirror1d.hpp index 81450eef9..c6b97b55e 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries1d/mirror1d.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries1d/mirror1d.hpp @@ -33,39 +33,33 @@ #include "NumCpp/Functions/fliplr.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// Mirror boundary1d + /// + /// @param inImage + /// @param inBoundarySize + /// @return NdArray + /// + template + NdArray mirror1d(const NdArray& inImage, uint32 inBoundarySize) { - namespace boundary - { - //============================================================================ - // Method Description: - /// Mirror boundary1d - /// - /// @param inImage - /// @param inBoundarySize - /// @return NdArray - /// - template - NdArray mirror1d(const NdArray& inImage, uint32 inBoundarySize) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - const uint32 outSize = inImage.size() + inBoundarySize * 2; + const uint32 outSize = inImage.size() + inBoundarySize * 2; - NdArray outArray(1, outSize); - outArray.put(Slice(inBoundarySize, inBoundarySize + inImage.size()), inImage); + NdArray outArray(1, outSize); + outArray.put(Slice(inBoundarySize, inBoundarySize + inImage.size()), inImage); - // left - outArray.put(Slice(0, inBoundarySize), fliplr(inImage[Slice(1, inBoundarySize + 1)])); + // left + outArray.put(Slice(0, inBoundarySize), fliplr(inImage[Slice(1, inBoundarySize + 1)])); - // right - outArray.put(Slice(inImage.size() + inBoundarySize, outSize), - fliplr(inImage[Slice(-static_cast(inBoundarySize) - 1, -1)])); + // right + outArray.put(Slice(inImage.size() + inBoundarySize, outSize), + fliplr(inImage[Slice(-static_cast(inBoundarySize) - 1, -1)])); - return outArray; - } - } // namespace boundary - } // namespace filter -} // namespace nc + return outArray; + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundaries1d/nearest1d.hpp b/include/NumCpp/Filter/Boundaries/Boundaries1d/nearest1d.hpp index e513d78e2..e52f7394b 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries1d/nearest1d.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries1d/nearest1d.hpp @@ -31,38 +31,32 @@ #include "NumCpp/Core/Slice.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// Nearest boundary1d + /// + /// @param inImage + /// @param inBoundarySize + /// @return NdArray + /// + template + NdArray nearest1d(const NdArray& inImage, uint32 inBoundarySize) { - namespace boundary - { - //============================================================================ - // Method Description: - /// Nearest boundary1d - /// - /// @param inImage - /// @param inBoundarySize - /// @return NdArray - /// - template - NdArray nearest1d(const NdArray& inImage, uint32 inBoundarySize) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - const uint32 outSize = inImage.size() + inBoundarySize * 2; + const uint32 outSize = inImage.size() + inBoundarySize * 2; - NdArray outArray(1, outSize); - outArray.put(Slice(inBoundarySize, inBoundarySize + inImage.size()), inImage); + NdArray outArray(1, outSize); + outArray.put(Slice(inBoundarySize, inBoundarySize + inImage.size()), inImage); - // left - outArray.put(Slice(0, inBoundarySize), inImage.front()); + // left + outArray.put(Slice(0, inBoundarySize), inImage.front()); - // right - outArray.put(Slice(inImage.size() + inBoundarySize, outSize), inImage.back()); + // right + outArray.put(Slice(inImage.size() + inBoundarySize, outSize), inImage.back()); - return outArray; - } - } // namespace boundary - } // namespace filter -} // namespace nc + return outArray; + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundaries1d/reflect1d.hpp b/include/NumCpp/Filter/Boundaries/Boundaries1d/reflect1d.hpp index 34092674a..1794286ea 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries1d/reflect1d.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries1d/reflect1d.hpp @@ -33,40 +33,34 @@ #include "NumCpp/Functions/fliplr.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// Reflects the boundaries + /// + /// @param inImage + /// @param inBoundarySize + /// + /// @return NdArray + /// + template + NdArray reflect1d(const NdArray& inImage, uint32 inBoundarySize) { - namespace boundary - { - //============================================================================ - // Method Description: - /// Reflects the boundaries - /// - /// @param inImage - /// @param inBoundarySize - /// - /// @return NdArray - /// - template - NdArray reflect1d(const NdArray& inImage, uint32 inBoundarySize) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - const uint32 outSize = inImage.size() + inBoundarySize * 2; + const uint32 outSize = inImage.size() + inBoundarySize * 2; - NdArray outArray(1, outSize); - outArray.put(Slice(inBoundarySize, inBoundarySize + inImage.size()), inImage); + NdArray outArray(1, outSize); + outArray.put(Slice(inBoundarySize, inBoundarySize + inImage.size()), inImage); - // left - outArray.put(Slice(0, inBoundarySize), fliplr(inImage[Slice(0, inBoundarySize)])); + // left + outArray.put(Slice(0, inBoundarySize), fliplr(inImage[Slice(0, inBoundarySize)])); - // right - outArray.put(Slice(inImage.size() + inBoundarySize, outSize), - fliplr(inImage[Slice(-static_cast(inBoundarySize), inImage.size())])); + // right + outArray.put(Slice(inImage.size() + inBoundarySize, outSize), + fliplr(inImage[Slice(-static_cast(inBoundarySize), inImage.size())])); - return outArray; - } - } // namespace boundary - } // namespace filter -} // namespace nc + return outArray; + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundaries1d/trimBoundary1d.hpp b/include/NumCpp/Filter/Boundaries/Boundaries1d/trimBoundary1d.hpp index c7cd7cbcd..cc8d33182 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries1d/trimBoundary1d.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries1d/trimBoundary1d.hpp @@ -32,30 +32,24 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// trims the boundary off to make the image back to the original size + /// + /// @param inImageWithBoundary + /// @param inSize + /// @return NdArray + /// + template + NdArray trimBoundary1d(const NdArray& inImageWithBoundary, uint32 inSize) { - namespace boundary - { - //============================================================================ - // Method Description: - /// trims the boundary off to make the image back to the original size - /// - /// @param inImageWithBoundary - /// @param inSize - /// @return NdArray - /// - template - NdArray trimBoundary1d(const NdArray& inImageWithBoundary, uint32 inSize) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - uint32 boundarySize = inSize / 2; // integer division - uint32 imageSize = inImageWithBoundary.size() - boundarySize * 2; + uint32 boundarySize = inSize / 2; // integer division + uint32 imageSize = inImageWithBoundary.size() - boundarySize * 2; - return inImageWithBoundary[Slice(boundarySize, boundarySize + imageSize)]; - } - } // namespace boundary - } // namespace filter -} // namespace nc + return inImageWithBoundary[Slice(boundarySize, boundarySize + imageSize)]; + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundaries1d/wrap1d.hpp b/include/NumCpp/Filter/Boundaries/Boundaries1d/wrap1d.hpp index 9ae892b43..041433ef9 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries1d/wrap1d.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries1d/wrap1d.hpp @@ -32,38 +32,32 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// Wrap boundary1d + /// + /// @param inImage + /// @param inBoundarySize + /// @return NdArray + /// + template + NdArray wrap1d(const NdArray& inImage, uint32 inBoundarySize) { - namespace boundary - { - //============================================================================ - // Method Description: - /// Wrap boundary1d - /// - /// @param inImage - /// @param inBoundarySize - /// @return NdArray - /// - template - NdArray wrap1d(const NdArray& inImage, uint32 inBoundarySize) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - const uint32 outSize = inImage.size() + inBoundarySize * 2; + const uint32 outSize = inImage.size() + inBoundarySize * 2; - NdArray outArray(1, outSize); - outArray.put(Slice(inBoundarySize, inBoundarySize + inImage.size()), inImage); + NdArray outArray(1, outSize); + outArray.put(Slice(inBoundarySize, inBoundarySize + inImage.size()), inImage); - // left - outArray.put(Slice(0, inBoundarySize), inImage[Slice(inImage.size() - inBoundarySize, inImage.size())]); + // left + outArray.put(Slice(0, inBoundarySize), inImage[Slice(inImage.size() - inBoundarySize, inImage.size())]); - // right - outArray.put(Slice(inImage.size() + inBoundarySize, outSize), inImage[Slice(0, inBoundarySize)]); + // right + outArray.put(Slice(inImage.size() + inBoundarySize, outSize), inImage[Slice(0, inBoundarySize)]); - return outArray; - } - } // namespace boundary - } // namespace filter -} // namespace nc + return outArray; + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundaries2d/addBoundary2d.hpp b/include/NumCpp/Filter/Boundaries/Boundaries2d/addBoundary2d.hpp index 293e6a6f5..30247ec73 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries2d/addBoundary2d.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries2d/addBoundary2d.hpp @@ -40,66 +40,60 @@ #include "NumCpp/Filter/Boundaries/Boundary.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// Wrap boundary + /// + /// @param inImage + /// @param inBoundaryType + /// @param inKernalSize + /// @param inConstantValue (default 0) + /// @return NdArray + /// + template + NdArray addBoundary2d(const NdArray& inImage, + Boundary inBoundaryType, + uint32 inKernalSize, + dtype inConstantValue = 0) { - namespace boundary - { - //============================================================================ - // Method Description: - /// Wrap boundary - /// - /// @param inImage - /// @param inBoundaryType - /// @param inKernalSize - /// @param inConstantValue (default 0) - /// @return NdArray - /// - template - NdArray addBoundary2d(const NdArray& inImage, - Boundary inBoundaryType, - uint32 inKernalSize, - dtype inConstantValue = 0) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - if (inKernalSize % 2 == 0) - { - THROW_INVALID_ARGUMENT_ERROR("input kernal size must be an odd value."); - } + if (inKernalSize % 2 == 0) + { + THROW_INVALID_ARGUMENT_ERROR("input kernal size must be an odd value."); + } - const uint32 boundarySize = inKernalSize / 2; // integer division + const uint32 boundarySize = inKernalSize / 2; // integer division - switch (inBoundaryType) - { - case Boundary::REFLECT: - { - return reflect2d(inImage, boundarySize); - } - case Boundary::CONSTANT: - { - return constant2d(inImage, boundarySize, inConstantValue); - } - case Boundary::NEAREST: - { - return nearest2d(inImage, boundarySize); - } - case Boundary::MIRROR: - { - return mirror2d(inImage, boundarySize); - } - case Boundary::WRAP: - { - return wrap2d(inImage, boundarySize); - } - default: - { - THROW_INVALID_ARGUMENT_ERROR("Unimplemented axis type."); - return {}; // get rid of compiler warning - } - } + switch (inBoundaryType) + { + case Boundary::REFLECT: + { + return reflect2d(inImage, boundarySize); + } + case Boundary::CONSTANT: + { + return constant2d(inImage, boundarySize, inConstantValue); + } + case Boundary::NEAREST: + { + return nearest2d(inImage, boundarySize); + } + case Boundary::MIRROR: + { + return mirror2d(inImage, boundarySize); + } + case Boundary::WRAP: + { + return wrap2d(inImage, boundarySize); + } + default: + { + THROW_INVALID_ARGUMENT_ERROR("Unimplemented axis type."); + return {}; // get rid of compiler warning } - } // namespace boundary - } // namespace filter -} // namespace nc + } + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundaries2d/constant2d.hpp b/include/NumCpp/Filter/Boundaries/Boundaries2d/constant2d.hpp index 8e1bdc7f0..8ba96ae3e 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries2d/constant2d.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries2d/constant2d.hpp @@ -34,52 +34,46 @@ #include "NumCpp/Filter/Boundaries/Boundaries2d/fillCorners.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// Constant boundary + /// + /// @param inImage + /// @param inBoundarySize + /// @param inConstantValue + /// @return NdArray + /// + template + NdArray constant2d(const NdArray& inImage, uint32 inBoundarySize, dtype inConstantValue) { - namespace boundary - { - //============================================================================ - // Method Description: - /// Constant boundary - /// - /// @param inImage - /// @param inBoundarySize - /// @param inConstantValue - /// @return NdArray - /// - template - NdArray constant2d(const NdArray& inImage, uint32 inBoundarySize, dtype inConstantValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - const Shape inShape = inImage.shape(); - Shape outShape(inShape); - outShape.rows += inBoundarySize * 2; - outShape.cols += inBoundarySize * 2; + const Shape inShape = inImage.shape(); + Shape outShape(inShape); + outShape.rows += inBoundarySize * 2; + outShape.cols += inBoundarySize * 2; - NdArray outArray(outShape); - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inImage); - fillCorners(outArray, inBoundarySize, inConstantValue); + NdArray outArray(outShape); + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), + Slice(inBoundarySize, inBoundarySize + inShape.cols), + inImage); + fillCorners(outArray, inBoundarySize, inConstantValue); - outArray.put(Slice(0, inBoundarySize), - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inConstantValue); /// bottom - outArray.put(Slice(outShape.rows - inBoundarySize, outShape.rows), - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inConstantValue); /// top - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - Slice(0, inBoundarySize), - inConstantValue); /// left - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - Slice(outShape.cols - inBoundarySize, outShape.cols), - inConstantValue); /// right + outArray.put(Slice(0, inBoundarySize), + Slice(inBoundarySize, inBoundarySize + inShape.cols), + inConstantValue); /// bottom + outArray.put(Slice(outShape.rows - inBoundarySize, outShape.rows), + Slice(inBoundarySize, inBoundarySize + inShape.cols), + inConstantValue); /// top + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), + Slice(0, inBoundarySize), + inConstantValue); /// left + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), + Slice(outShape.cols - inBoundarySize, outShape.cols), + inConstantValue); /// right - return outArray; - } - } // namespace boundary - } // namespace filter -} // namespace nc + return outArray; + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundaries2d/fillCorners.hpp b/include/NumCpp/Filter/Boundaries/Boundaries2d/fillCorners.hpp index ae742db12..2edd38f67 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries2d/fillCorners.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries2d/fillCorners.hpp @@ -33,78 +33,70 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// extends the corner values + /// + /// @param inArray + /// @param inBorderWidth + /// + template + void fillCorners(NdArray& inArray, uint32 inBorderWidth) { - namespace boundary - { - //============================================================================ - // Method Description: - /// extends the corner values - /// - /// @param inArray - /// @param inBorderWidth - /// - template - void fillCorners(NdArray& inArray, uint32 inBorderWidth) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - const Shape inShape = inArray.shape(); - const auto numRows = static_cast(inShape.rows); - const auto numCols = static_cast(inShape.cols); + const Shape inShape = inArray.shape(); + const auto numRows = static_cast(inShape.rows); + const auto numCols = static_cast(inShape.cols); - // top left - inArray.put(Slice(0, inBorderWidth), Slice(0, inBorderWidth), inArray(inBorderWidth, inBorderWidth)); + // top left + inArray.put(Slice(0, inBorderWidth), Slice(0, inBorderWidth), inArray(inBorderWidth, inBorderWidth)); - // top right - inArray.put(Slice(0, inBorderWidth), - Slice(numCols - inBorderWidth, numCols), - inArray(inBorderWidth, numCols - inBorderWidth - 1)); + // top right + inArray.put(Slice(0, inBorderWidth), + Slice(numCols - inBorderWidth, numCols), + inArray(inBorderWidth, numCols - inBorderWidth - 1)); - // bottom left - inArray.put(Slice(numRows - inBorderWidth, numRows), - Slice(0, inBorderWidth), - inArray(numRows - inBorderWidth - 1, inBorderWidth)); + // bottom left + inArray.put(Slice(numRows - inBorderWidth, numRows), + Slice(0, inBorderWidth), + inArray(numRows - inBorderWidth - 1, inBorderWidth)); - // bottom right - inArray.put(Slice(numRows - inBorderWidth, numRows), - Slice(numCols - inBorderWidth, numCols), - inArray(numRows - inBorderWidth - 1, numCols - inBorderWidth - 1)); - } + // bottom right + inArray.put(Slice(numRows - inBorderWidth, numRows), + Slice(numCols - inBorderWidth, numCols), + inArray(numRows - inBorderWidth - 1, numCols - inBorderWidth - 1)); + } - //============================================================================ - // Method Description: - /// extends the corner values - /// - /// @param inArray - /// @param inBorderWidth - /// @param inFillValue - /// - template - void fillCorners(NdArray& inArray, uint32 inBorderWidth, dtype inFillValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + //============================================================================ + // Method Description: + /// extends the corner values + /// + /// @param inArray + /// @param inBorderWidth + /// @param inFillValue + /// + template + void fillCorners(NdArray& inArray, uint32 inBorderWidth, dtype inFillValue) + { + STATIC_ASSERT_ARITHMETIC(dtype); - const Shape inShape = inArray.shape(); - const auto numRows = static_cast(inShape.rows); - const auto numCols = static_cast(inShape.cols); + const Shape inShape = inArray.shape(); + const auto numRows = static_cast(inShape.rows); + const auto numCols = static_cast(inShape.cols); - // top left - inArray.put(Slice(0, inBorderWidth), Slice(0, inBorderWidth), inFillValue); + // top left + inArray.put(Slice(0, inBorderWidth), Slice(0, inBorderWidth), inFillValue); - // top right - inArray.put(Slice(0, inBorderWidth), Slice(numCols - inBorderWidth, numCols), inFillValue); + // top right + inArray.put(Slice(0, inBorderWidth), Slice(numCols - inBorderWidth, numCols), inFillValue); - // bottom left - inArray.put(Slice(numRows - inBorderWidth, numRows), Slice(0, inBorderWidth), inFillValue); + // bottom left + inArray.put(Slice(numRows - inBorderWidth, numRows), Slice(0, inBorderWidth), inFillValue); - // bottom right - inArray.put(Slice(numRows - inBorderWidth, numRows), - Slice(numCols - inBorderWidth, numCols), - inFillValue); - } - } // namespace boundary - } // namespace filter -} // namespace nc + // bottom right + inArray.put(Slice(numRows - inBorderWidth, numRows), Slice(numCols - inBorderWidth, numCols), inFillValue); + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundaries2d/mirror2d.hpp b/include/NumCpp/Filter/Boundaries/Boundaries2d/mirror2d.hpp index d522880f2..449f9490b 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries2d/mirror2d.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries2d/mirror2d.hpp @@ -34,84 +34,76 @@ #include "NumCpp/Functions/flipud.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// Mirror boundary + /// + /// @param inImage + /// @param inBoundarySize + /// @return NdArray + /// + template + NdArray mirror2d(const NdArray& inImage, uint32 inBoundarySize) { - namespace boundary - { - //============================================================================ - // Method Description: - /// Mirror boundary - /// - /// @param inImage - /// @param inBoundarySize - /// @return NdArray - /// - template - NdArray mirror2d(const NdArray& inImage, uint32 inBoundarySize) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - const Shape inShape = inImage.shape(); - Shape outShape(inShape); - outShape.rows += inBoundarySize * 2; - outShape.cols += inBoundarySize * 2; + const Shape inShape = inImage.shape(); + Shape outShape(inShape); + outShape.rows += inBoundarySize * 2; + outShape.cols += inBoundarySize * 2; - NdArray outArray(outShape); - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inImage); + NdArray outArray(outShape); + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), + Slice(inBoundarySize, inBoundarySize + inShape.cols), + inImage); - for (uint32 row = 0; row < inBoundarySize; ++row) - { - // bottom - outArray.put(row, - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inImage(inBoundarySize - row, Slice(0, inShape.cols))); + for (uint32 row = 0; row < inBoundarySize; ++row) + { + // bottom + outArray.put(row, + Slice(inBoundarySize, inBoundarySize + inShape.cols), + inImage(inBoundarySize - row, Slice(0, inShape.cols))); - // top - outArray.put(row + inBoundarySize + inShape.rows, - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inImage(inShape.rows - row - 2, Slice(0, inShape.cols))); - } + // top + outArray.put(row + inBoundarySize + inShape.rows, + Slice(inBoundarySize, inBoundarySize + inShape.cols), + inImage(inShape.rows - row - 2, Slice(0, inShape.cols))); + } - for (uint32 col = 0; col < inBoundarySize; ++col) - { - // left - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - col, - inImage(Slice(0, inShape.rows), inBoundarySize - col)); + for (uint32 col = 0; col < inBoundarySize; ++col) + { + // left + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), + col, + inImage(Slice(0, inShape.rows), inBoundarySize - col)); - // right - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - col + inBoundarySize + inShape.cols, - inImage(Slice(0, inShape.rows), inShape.cols - col - 2)); - } + // right + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), + col + inBoundarySize + inShape.cols, + inImage(Slice(0, inShape.rows), inShape.cols - col - 2)); + } - // now fill in the corners - NdArray lowerLeft = - flipud(outArray(Slice(inBoundarySize + 1, 2 * inBoundarySize + 1), Slice(0, inBoundarySize))); - NdArray lowerRight = flipud(outArray(Slice(inBoundarySize + 1, 2 * inBoundarySize + 1), - Slice(outShape.cols - inBoundarySize, outShape.cols))); + // now fill in the corners + NdArray lowerLeft = + flipud(outArray(Slice(inBoundarySize + 1, 2 * inBoundarySize + 1), Slice(0, inBoundarySize))); + NdArray lowerRight = flipud(outArray(Slice(inBoundarySize + 1, 2 * inBoundarySize + 1), + Slice(outShape.cols - inBoundarySize, outShape.cols))); - const uint32 upperRowStart = outShape.rows - 2 * inBoundarySize - 1; - NdArray upperLeft = - flipud(outArray(Slice(upperRowStart, upperRowStart + inBoundarySize), Slice(0, inBoundarySize))); - NdArray upperRight = flipud(outArray(Slice(upperRowStart, upperRowStart + inBoundarySize), - Slice(outShape.cols - inBoundarySize, outShape.cols))); + const uint32 upperRowStart = outShape.rows - 2 * inBoundarySize - 1; + NdArray upperLeft = + flipud(outArray(Slice(upperRowStart, upperRowStart + inBoundarySize), Slice(0, inBoundarySize))); + NdArray upperRight = flipud(outArray(Slice(upperRowStart, upperRowStart + inBoundarySize), + Slice(outShape.cols - inBoundarySize, outShape.cols))); - outArray.put(Slice(0, inBoundarySize), Slice(0, inBoundarySize), lowerLeft); - outArray.put(Slice(0, inBoundarySize), - Slice(outShape.cols - inBoundarySize, outShape.cols), - lowerRight); - outArray.put(Slice(outShape.rows - inBoundarySize, outShape.rows), Slice(0, inBoundarySize), upperLeft); - outArray.put(Slice(outShape.rows - inBoundarySize, outShape.rows), - Slice(outShape.cols - inBoundarySize, outShape.cols), - upperRight); + outArray.put(Slice(0, inBoundarySize), Slice(0, inBoundarySize), lowerLeft); + outArray.put(Slice(0, inBoundarySize), Slice(outShape.cols - inBoundarySize, outShape.cols), lowerRight); + outArray.put(Slice(outShape.rows - inBoundarySize, outShape.rows), Slice(0, inBoundarySize), upperLeft); + outArray.put(Slice(outShape.rows - inBoundarySize, outShape.rows), + Slice(outShape.cols - inBoundarySize, outShape.cols), + upperRight); - return outArray; - } - } // namespace boundary - } // namespace filter -} // namespace nc + return outArray; + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundaries2d/nearest2d.hpp b/include/NumCpp/Filter/Boundaries/Boundaries2d/nearest2d.hpp index dd88236ed..13b9afc37 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries2d/nearest2d.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries2d/nearest2d.hpp @@ -34,64 +34,54 @@ #include "NumCpp/Filter/Boundaries/Boundaries2d/fillCorners.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// Nearest boundary + /// + /// @param inImage + /// @param inBoundarySize + /// @return NdArray + /// + template + NdArray nearest2d(const NdArray& inImage, uint32 inBoundarySize) { - namespace boundary - { - //============================================================================ - // Method Description: - /// Nearest boundary - /// - /// @param inImage - /// @param inBoundarySize - /// @return NdArray - /// - template - NdArray nearest2d(const NdArray& inImage, uint32 inBoundarySize) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - const Shape inShape = inImage.shape(); - Shape outShape(inShape); - outShape.rows += inBoundarySize * 2; - outShape.cols += inBoundarySize * 2; + const Shape inShape = inImage.shape(); + Shape outShape(inShape); + outShape.rows += inBoundarySize * 2; + outShape.cols += inBoundarySize * 2; - NdArray outArray(outShape); - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inImage); - fillCorners(outArray, inBoundarySize); + NdArray outArray(outShape); + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), + Slice(inBoundarySize, inBoundarySize + inShape.cols), + inImage); + fillCorners(outArray, inBoundarySize); - for (uint32 row = 0; row < inBoundarySize; ++row) - { - // bottom - outArray.put(row, - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inImage(0, Slice(0, inShape.cols))); + for (uint32 row = 0; row < inBoundarySize; ++row) + { + // bottom + outArray.put(row, Slice(inBoundarySize, inBoundarySize + inShape.cols), inImage(0, Slice(0, inShape.cols))); - // top - outArray.put(row + inBoundarySize + inShape.rows, - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inImage(inShape.rows - 1, Slice(0, inShape.cols))); - } + // top + outArray.put(row + inBoundarySize + inShape.rows, + Slice(inBoundarySize, inBoundarySize + inShape.cols), + inImage(inShape.rows - 1, Slice(0, inShape.cols))); + } - for (uint32 col = 0; col < inBoundarySize; ++col) - { - // left - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - col, - inImage(Slice(0, inShape.rows), 0)); + for (uint32 col = 0; col < inBoundarySize; ++col) + { + // left + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), col, inImage(Slice(0, inShape.rows), 0)); - // right - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - col + inBoundarySize + inShape.cols, - inImage(Slice(0, inShape.rows), inShape.cols - 1)); - } + // right + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), + col + inBoundarySize + inShape.cols, + inImage(Slice(0, inShape.rows), inShape.cols - 1)); + } - return outArray; - } - } // namespace boundary - } // namespace filter -} // namespace nc + return outArray; + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundaries2d/reflect2d.hpp b/include/NumCpp/Filter/Boundaries/Boundaries2d/reflect2d.hpp index dd2e2884d..c1fdde0d8 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries2d/reflect2d.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries2d/reflect2d.hpp @@ -34,85 +34,77 @@ #include "NumCpp/Functions/flipud.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// Reflects the boundaries + /// + /// @param inImage + /// @param inBoundarySize + /// + /// @return NdArray + /// + template + NdArray reflect2d(const NdArray& inImage, uint32 inBoundarySize) { - namespace boundary - { - //============================================================================ - // Method Description: - /// Reflects the boundaries - /// - /// @param inImage - /// @param inBoundarySize - /// - /// @return NdArray - /// - template - NdArray reflect2d(const NdArray& inImage, uint32 inBoundarySize) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - const Shape inShape = inImage.shape(); - Shape outShape(inShape); - outShape.rows += inBoundarySize * 2; - outShape.cols += inBoundarySize * 2; + const Shape inShape = inImage.shape(); + Shape outShape(inShape); + outShape.rows += inBoundarySize * 2; + outShape.cols += inBoundarySize * 2; - NdArray outArray(outShape); - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inImage); + NdArray outArray(outShape); + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), + Slice(inBoundarySize, inBoundarySize + inShape.cols), + inImage); - for (uint32 row = 0; row < inBoundarySize; ++row) - { - // bottom - outArray.put(row, - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inImage(inBoundarySize - row - 1, Slice(0, inShape.cols))); + for (uint32 row = 0; row < inBoundarySize; ++row) + { + // bottom + outArray.put(row, + Slice(inBoundarySize, inBoundarySize + inShape.cols), + inImage(inBoundarySize - row - 1, Slice(0, inShape.cols))); - // top - outArray.put(row + inBoundarySize + inShape.rows, - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inImage(inShape.rows - row - 1, Slice(0, inShape.cols))); - } + // top + outArray.put(row + inBoundarySize + inShape.rows, + Slice(inBoundarySize, inBoundarySize + inShape.cols), + inImage(inShape.rows - row - 1, Slice(0, inShape.cols))); + } - for (uint32 col = 0; col < inBoundarySize; ++col) - { - // left - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - col, - inImage(Slice(0, inShape.rows), inBoundarySize - col - 1)); + for (uint32 col = 0; col < inBoundarySize; ++col) + { + // left + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), + col, + inImage(Slice(0, inShape.rows), inBoundarySize - col - 1)); - // right - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - col + inBoundarySize + inShape.cols, - inImage(Slice(0, inShape.rows), inShape.cols - col - 1)); - } + // right + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), + col + inBoundarySize + inShape.cols, + inImage(Slice(0, inShape.rows), inShape.cols - col - 1)); + } - // now fill in the corners - NdArray lowerLeft = - flipud(outArray(Slice(inBoundarySize, 2 * inBoundarySize), Slice(0, inBoundarySize))); - NdArray lowerRight = flipud(outArray(Slice(inBoundarySize, 2 * inBoundarySize), - Slice(outShape.cols - inBoundarySize, outShape.cols))); + // now fill in the corners + NdArray lowerLeft = + flipud(outArray(Slice(inBoundarySize, 2 * inBoundarySize), Slice(0, inBoundarySize))); + NdArray lowerRight = flipud( + outArray(Slice(inBoundarySize, 2 * inBoundarySize), Slice(outShape.cols - inBoundarySize, outShape.cols))); - const uint32 upperRowStart = outShape.rows - 2 * inBoundarySize; - NdArray upperLeft = - flipud(outArray(Slice(upperRowStart, upperRowStart + inBoundarySize), Slice(0, inBoundarySize))); - NdArray upperRight = flipud(outArray(Slice(upperRowStart, upperRowStart + inBoundarySize), - Slice(outShape.cols - inBoundarySize, outShape.cols))); + const uint32 upperRowStart = outShape.rows - 2 * inBoundarySize; + NdArray upperLeft = + flipud(outArray(Slice(upperRowStart, upperRowStart + inBoundarySize), Slice(0, inBoundarySize))); + NdArray upperRight = flipud(outArray(Slice(upperRowStart, upperRowStart + inBoundarySize), + Slice(outShape.cols - inBoundarySize, outShape.cols))); - outArray.put(Slice(0, inBoundarySize), Slice(0, inBoundarySize), lowerLeft); - outArray.put(Slice(0, inBoundarySize), - Slice(outShape.cols - inBoundarySize, outShape.cols), - lowerRight); - outArray.put(Slice(outShape.rows - inBoundarySize, outShape.rows), Slice(0, inBoundarySize), upperLeft); - outArray.put(Slice(outShape.rows - inBoundarySize, outShape.rows), - Slice(outShape.cols - inBoundarySize, outShape.cols), - upperRight); + outArray.put(Slice(0, inBoundarySize), Slice(0, inBoundarySize), lowerLeft); + outArray.put(Slice(0, inBoundarySize), Slice(outShape.cols - inBoundarySize, outShape.cols), lowerRight); + outArray.put(Slice(outShape.rows - inBoundarySize, outShape.rows), Slice(0, inBoundarySize), upperLeft); + outArray.put(Slice(outShape.rows - inBoundarySize, outShape.rows), + Slice(outShape.cols - inBoundarySize, outShape.cols), + upperRight); - return outArray; - } - } // namespace boundary - } // namespace filter -} // namespace nc + return outArray; + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundaries2d/trimBoundary2d.hpp b/include/NumCpp/Filter/Boundaries/Boundaries2d/trimBoundary2d.hpp index c635878b0..68930be5c 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries2d/trimBoundary2d.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries2d/trimBoundary2d.hpp @@ -33,34 +33,28 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// trims the boundary off to make the image back to the original size + /// + /// @param inImageWithBoundary + /// @param inSize + /// @return NdArray + /// + template + NdArray trimBoundary2d(const NdArray& inImageWithBoundary, uint32 inSize) { - namespace boundary - { - //============================================================================ - // Method Description: - /// trims the boundary off to make the image back to the original size - /// - /// @param inImageWithBoundary - /// @param inSize - /// @return NdArray - /// - template - NdArray trimBoundary2d(const NdArray& inImageWithBoundary, uint32 inSize) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - Shape inShape = inImageWithBoundary.shape(); - uint32 boundarySize = inSize / 2; /// integer division + Shape inShape = inImageWithBoundary.shape(); + uint32 boundarySize = inSize / 2; /// integer division - inShape.rows -= boundarySize * 2; - inShape.cols -= boundarySize * 2; + inShape.rows -= boundarySize * 2; + inShape.cols -= boundarySize * 2; - return inImageWithBoundary(Slice(boundarySize, boundarySize + inShape.rows), - Slice(boundarySize, boundarySize + inShape.cols)); - } - } // namespace boundary - } // namespace filter -} // namespace nc + return inImageWithBoundary(Slice(boundarySize, boundarySize + inShape.rows), + Slice(boundarySize, boundarySize + inShape.cols)); + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundaries2d/wrap2d.hpp b/include/NumCpp/Filter/Boundaries/Boundaries2d/wrap2d.hpp index 9475e91cf..1980cb924 100644 --- a/include/NumCpp/Filter/Boundaries/Boundaries2d/wrap2d.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundaries2d/wrap2d.hpp @@ -34,78 +34,69 @@ #include "NumCpp/Filter/Boundaries/Boundaries2d/fillCorners.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter::boundary { - namespace filter + //============================================================================ + // Method Description: + /// Wrap boundary + /// + /// @param inImage + /// @param inBoundarySize + /// @return NdArray + /// + template + NdArray wrap2d(const NdArray& inImage, uint32 inBoundarySize) { - namespace boundary - { - //============================================================================ - // Method Description: - /// Wrap boundary - /// - /// @param inImage - /// @param inBoundarySize - /// @return NdArray - /// - template - NdArray wrap2d(const NdArray& inImage, uint32 inBoundarySize) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - const Shape inShape = inImage.shape(); - Shape outShape(inShape); - outShape.rows += inBoundarySize * 2; - outShape.cols += inBoundarySize * 2; + const Shape inShape = inImage.shape(); + Shape outShape(inShape); + outShape.rows += inBoundarySize * 2; + outShape.cols += inBoundarySize * 2; - NdArray outArray(outShape); - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inImage); + NdArray outArray(outShape); + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), + Slice(inBoundarySize, inBoundarySize + inShape.cols), + inImage); - // bottom - outArray.put(Slice(0, inBoundarySize), - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inImage(Slice(inShape.rows - inBoundarySize, inShape.rows), Slice(0, inShape.cols))); + // bottom + outArray.put(Slice(0, inBoundarySize), + Slice(inBoundarySize, inBoundarySize + inShape.cols), + inImage(Slice(inShape.rows - inBoundarySize, inShape.rows), Slice(0, inShape.cols))); - // top - outArray.put(Slice(inShape.rows + inBoundarySize, outShape.rows), - Slice(inBoundarySize, inBoundarySize + inShape.cols), - inImage(Slice(0, inBoundarySize), Slice(0, inShape.cols))); + // top + outArray.put(Slice(inShape.rows + inBoundarySize, outShape.rows), + Slice(inBoundarySize, inBoundarySize + inShape.cols), + inImage(Slice(0, inBoundarySize), Slice(0, inShape.cols))); - // left - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - Slice(0, inBoundarySize), - inImage(Slice(0, inShape.rows), Slice(inShape.cols - inBoundarySize, inShape.cols))); + // left + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), + Slice(0, inBoundarySize), + inImage(Slice(0, inShape.rows), Slice(inShape.cols - inBoundarySize, inShape.cols))); - // right - outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), - Slice(inShape.cols + inBoundarySize, outShape.cols), - inImage(Slice(0, inShape.rows), Slice(0, inBoundarySize))); + // right + outArray.put(Slice(inBoundarySize, inBoundarySize + inShape.rows), + Slice(inShape.cols + inBoundarySize, outShape.cols), + inImage(Slice(0, inShape.rows), Slice(0, inBoundarySize))); - // now fill in the corners - NdArray lowerLeft = - outArray(Slice(inBoundarySize, 2 * inBoundarySize), Slice(0, inBoundarySize)); - NdArray lowerRight = outArray(Slice(inBoundarySize, 2 * inBoundarySize), - Slice(outShape.cols - inBoundarySize, outShape.cols)); + // now fill in the corners + NdArray lowerLeft = outArray(Slice(inBoundarySize, 2 * inBoundarySize), Slice(0, inBoundarySize)); + NdArray lowerRight = + outArray(Slice(inBoundarySize, 2 * inBoundarySize), Slice(outShape.cols - inBoundarySize, outShape.cols)); - const uint32 upperRowStart = outShape.rows - 2 * inBoundarySize; - NdArray upperLeft = - outArray(Slice(upperRowStart, upperRowStart + inBoundarySize), Slice(0, inBoundarySize)); - NdArray upperRight = outArray(Slice(upperRowStart, upperRowStart + inBoundarySize), - Slice(outShape.cols - inBoundarySize, outShape.cols)); + const uint32 upperRowStart = outShape.rows - 2 * inBoundarySize; + NdArray upperLeft = + outArray(Slice(upperRowStart, upperRowStart + inBoundarySize), Slice(0, inBoundarySize)); + NdArray upperRight = outArray(Slice(upperRowStart, upperRowStart + inBoundarySize), + Slice(outShape.cols - inBoundarySize, outShape.cols)); - outArray.put(Slice(0, inBoundarySize), Slice(0, inBoundarySize), upperLeft); - outArray.put(Slice(0, inBoundarySize), - Slice(outShape.cols - inBoundarySize, outShape.cols), - upperRight); - outArray.put(Slice(outShape.rows - inBoundarySize, outShape.rows), Slice(0, inBoundarySize), lowerLeft); - outArray.put(Slice(outShape.rows - inBoundarySize, outShape.rows), - Slice(outShape.cols - inBoundarySize, outShape.cols), - lowerRight); + outArray.put(Slice(0, inBoundarySize), Slice(0, inBoundarySize), upperLeft); + outArray.put(Slice(0, inBoundarySize), Slice(outShape.cols - inBoundarySize, outShape.cols), upperRight); + outArray.put(Slice(outShape.rows - inBoundarySize, outShape.rows), Slice(0, inBoundarySize), lowerLeft); + outArray.put(Slice(outShape.rows - inBoundarySize, outShape.rows), + Slice(outShape.cols - inBoundarySize, outShape.cols), + lowerRight); - return outArray; - } - } // namespace boundary - } // namespace filter -} // namespace nc + return outArray; + } +} // namespace nc::filter::boundary diff --git a/include/NumCpp/Filter/Boundaries/Boundary.hpp b/include/NumCpp/Filter/Boundaries/Boundary.hpp index 621678e52..50d837379 100644 --- a/include/NumCpp/Filter/Boundaries/Boundary.hpp +++ b/include/NumCpp/Filter/Boundaries/Boundary.hpp @@ -27,20 +27,17 @@ /// #pragma once -namespace nc +namespace nc::filter { - namespace filter + //================================================================================ + // Enum Description: + /// Boundary condition to apply to the image filter + enum class Boundary { - //================================================================================ - // Enum Description: - /// Boundary condition to apply to the image filter - enum class Boundary - { - REFLECT = 0, - CONSTANT, - NEAREST, - MIRROR, - WRAP - }; - } // namespace filter -} // namespace nc + REFLECT = 0, + CONSTANT, + NEAREST, + MIRROR, + WRAP + }; +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters1d/complementaryMedianFilter1d.hpp b/include/NumCpp/Filter/Filters/Filters1d/complementaryMedianFilter1d.hpp index 79680ce6c..41172c950 100644 --- a/include/NumCpp/Filter/Filters/Filters1d/complementaryMedianFilter1d.hpp +++ b/include/NumCpp/Filter/Filters/Filters1d/complementaryMedianFilter1d.hpp @@ -32,30 +32,27 @@ #include "NumCpp/Filter/Filters/Filters1d/medianFilter1d.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculate a one-dimensional complemenatry median filter. + /// + /// @param inImageArray + /// @param inSize: square size of the kernel to apply + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray complementaryMedianFilter1d(const NdArray& inImageArray, + uint32 inSize, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculate a one-dimensional complemenatry median filter. - /// - /// @param inImageArray - /// @param inSize: square size of the kernel to apply - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray complementaryMedianFilter1d(const NdArray& inImageArray, - uint32 inSize, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - NdArray inImageArrayCopy(inImageArray); - inImageArrayCopy -= medianFilter1d(inImageArray, inSize, inBoundaryType, inConstantValue); + NdArray inImageArrayCopy(inImageArray); + inImageArrayCopy -= medianFilter1d(inImageArray, inSize, inBoundaryType, inConstantValue); - return inImageArrayCopy; - } - } // namespace filter -} // namespace nc + return inImageArrayCopy; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters1d/convolve1d.hpp b/include/NumCpp/Filter/Filters/Filters1d/convolve1d.hpp index a3bb9e178..7b4fd7f6e 100644 --- a/include/NumCpp/Filter/Filters/Filters1d/convolve1d.hpp +++ b/include/NumCpp/Filter/Filters/Filters1d/convolve1d.hpp @@ -35,46 +35,43 @@ #include "NumCpp/Functions/fliplr.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a one-dimensional kernel convolution. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.convolve1d.html#scipy.ndimage.convolve1d + /// + /// @param inImageArray + /// @param inWeights + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray convolve1d(const NdArray& inImageArray, + const NdArray& inWeights, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a one-dimensional kernel convolution. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.convolve1d.html#scipy.ndimage.convolve1d - /// - /// @param inImageArray - /// @param inWeights - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray convolve1d(const NdArray& inImageArray, - const NdArray& inWeights, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - const uint32 boundarySize = inWeights.size() / 2; // integer division - NdArray arrayWithBoundary = - boundary::addBoundary1d(inImageArray, inBoundaryType, inWeights.size(), inConstantValue); - NdArray output(1, inImageArray.size()); - - NdArray weightsFlat = fliplr(inWeights.flatten()); + const uint32 boundarySize = inWeights.size() / 2; // integer division + NdArray arrayWithBoundary = + boundary::addBoundary1d(inImageArray, inBoundaryType, inWeights.size(), inConstantValue); + NdArray output(1, inImageArray.size()); - const uint32 endPointRow = boundarySize + inImageArray.size(); + NdArray weightsFlat = fliplr(inWeights.flatten()); - for (uint32 i = boundarySize; i < endPointRow; ++i) - { - NdArray window = arrayWithBoundary[Slice(i - boundarySize, i + boundarySize + 1)].flatten(); + const uint32 endPointRow = boundarySize + inImageArray.size(); - output[i - boundarySize] = dot(window, weightsFlat).item(); - } + for (uint32 i = boundarySize; i < endPointRow; ++i) + { + NdArray window = arrayWithBoundary[Slice(i - boundarySize, i + boundarySize + 1)].flatten(); - return output; + output[i - boundarySize] = dot(window, weightsFlat).item(); } - } // namespace filter -} // namespace nc + + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters1d/gaussianFilter1d.hpp b/include/NumCpp/Filter/Filters/Filters1d/gaussianFilter1d.hpp index 4d2078802..cdd3b3406 100644 --- a/include/NumCpp/Filter/Filters/Filters1d/gaussianFilter1d.hpp +++ b/include/NumCpp/Filter/Filters/Filters1d/gaussianFilter1d.hpp @@ -37,61 +37,58 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Utils/gaussian1d.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculate a one-dimensional gaussian filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.generic_filter1d.html#scipy.ndimage.generic_filter1d + /// + /// @param inImageArray + /// @param inSigma: Standard deviation for Gaussian kernel + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray gaussianFilter1d(const NdArray& inImageArray, + double inSigma, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculate a one-dimensional gaussian filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.generic_filter1d.html#scipy.ndimage.generic_filter1d - /// - /// @param inImageArray - /// @param inSigma: Standard deviation for Gaussian kernel - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray gaussianFilter1d(const NdArray& inImageArray, - double inSigma, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) + if (inSigma <= 0) { - if (inSigma <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input sigma value must be greater than zero."); - } + THROW_INVALID_ARGUMENT_ERROR("input sigma value must be greater than zero."); + } - // calculate the kernel size based off of the input sigma value - constexpr uint32 MIN_KERNEL_SIZE = 5; - uint32 kernelSize = - std::max(static_cast(std::ceil(inSigma * 2. * 4.)), MIN_KERNEL_SIZE); // 4 standard deviations - if (kernelSize % 2 == 0) - { - ++kernelSize; // make sure the kernel is an odd size - } + // calculate the kernel size based off of the input sigma value + constexpr uint32 MIN_KERNEL_SIZE = 5; + uint32 kernelSize = + std::max(static_cast(std::ceil(inSigma * 2. * 4.)), MIN_KERNEL_SIZE); // 4 standard deviations + if (kernelSize % 2 == 0) + { + ++kernelSize; // make sure the kernel is an odd size + } - const auto kernalHalfSize = static_cast(kernelSize / 2); // integer division + const auto kernalHalfSize = static_cast(kernelSize / 2); // integer division - // calculate the gaussian kernel - NdArray kernel(1, kernelSize); - for (double i = 0; i < kernelSize; ++i) - { - kernel[static_cast(i)] = utils::gaussian1d(i - kernalHalfSize, 0., inSigma); - } + // calculate the gaussian kernel + NdArray kernel(1, kernelSize); + for (double i = 0; i < kernelSize; ++i) + { + kernel[static_cast(i)] = utils::gaussian1d(i - kernalHalfSize, 0., inSigma); + } - // normalize the kernel - kernel /= kernel.sum().item(); + // normalize the kernel + kernel /= kernel.sum().item(); - // perform the convolution - NdArray output = - convolve1d(inImageArray.template astype(), kernel, inBoundaryType, inConstantValue) - .template astype(); + // perform the convolution + NdArray output = + convolve1d(inImageArray.template astype(), kernel, inBoundaryType, inConstantValue) + .template astype(); - return output; - } - } // namespace filter -} // namespace nc + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters1d/maximumFilter1d.hpp b/include/NumCpp/Filter/Filters/Filters1d/maximumFilter1d.hpp index dfd2a4d56..7dab32c96 100644 --- a/include/NumCpp/Filter/Filters/Filters1d/maximumFilter1d.hpp +++ b/include/NumCpp/Filter/Filters/Filters1d/maximumFilter1d.hpp @@ -33,44 +33,41 @@ #include "NumCpp/Filter/Boundaries/Boundary.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a one-dimensional maximum filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.maximum_filter1d.html#scipy.ndimage.maximum_filter1d + /// + /// @param inImageArray + /// @param inSize: linear size of the kernel to apply + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray maximumFilter1d(const NdArray& inImageArray, + uint32 inSize, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a one-dimensional maximum filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.maximum_filter1d.html#scipy.ndimage.maximum_filter1d - /// - /// @param inImageArray - /// @param inSize: linear size of the kernel to apply - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray maximumFilter1d(const NdArray& inImageArray, - uint32 inSize, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - NdArray arrayWithBoundary = - boundary::addBoundary1d(inImageArray, inBoundaryType, inSize, inConstantValue); - NdArray output(1, inImageArray.size()); - - const uint32 boundarySize = inSize / 2; // integer division - const uint32 endPoint = boundarySize + inImageArray.size(); + NdArray arrayWithBoundary = + boundary::addBoundary1d(inImageArray, inBoundaryType, inSize, inConstantValue); + NdArray output(1, inImageArray.size()); - for (uint32 i = boundarySize; i < endPoint; ++i) - { - NdArray window = arrayWithBoundary[Slice(i - boundarySize, i + boundarySize + 1)]; + const uint32 boundarySize = inSize / 2; // integer division + const uint32 endPoint = boundarySize + inImageArray.size(); - output[i - boundarySize] = window.max().item(); - } + for (uint32 i = boundarySize; i < endPoint; ++i) + { + NdArray window = arrayWithBoundary[Slice(i - boundarySize, i + boundarySize + 1)]; - return output; + output[i - boundarySize] = window.max().item(); } - } // namespace filter -} // namespace nc + + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters1d/medianFilter1d.hpp b/include/NumCpp/Filter/Filters/Filters1d/medianFilter1d.hpp index bbf83921f..55177c9bf 100644 --- a/include/NumCpp/Filter/Filters/Filters1d/medianFilter1d.hpp +++ b/include/NumCpp/Filter/Filters/Filters1d/medianFilter1d.hpp @@ -33,44 +33,41 @@ #include "NumCpp/Filter/Boundaries/Boundary.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a one-dimensional median filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.median_filter.html#scipy.ndimage.median_filter + /// + /// @param inImageArray + /// @param inSize: linear size of the kernel to apply + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray medianFilter1d(const NdArray& inImageArray, + uint32 inSize, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a one-dimensional median filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.median_filter.html#scipy.ndimage.median_filter - /// - /// @param inImageArray - /// @param inSize: linear size of the kernel to apply - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray medianFilter1d(const NdArray& inImageArray, - uint32 inSize, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - NdArray arrayWithBoundary = - boundary::addBoundary1d(inImageArray, inBoundaryType, inSize, inConstantValue); - NdArray output(1, inImageArray.size()); - - const uint32 boundarySize = inSize / 2; // integer division - const uint32 endPoint = boundarySize + inImageArray.size(); + NdArray arrayWithBoundary = + boundary::addBoundary1d(inImageArray, inBoundaryType, inSize, inConstantValue); + NdArray output(1, inImageArray.size()); - for (uint32 i = boundarySize; i < endPoint; ++i) - { - NdArray window = arrayWithBoundary[Slice(i - boundarySize, i + boundarySize + 1)]; + const uint32 boundarySize = inSize / 2; // integer division + const uint32 endPoint = boundarySize + inImageArray.size(); - output[i - boundarySize] = window.median().item(); - } + for (uint32 i = boundarySize; i < endPoint; ++i) + { + NdArray window = arrayWithBoundary[Slice(i - boundarySize, i + boundarySize + 1)]; - return output; + output[i - boundarySize] = window.median().item(); } - } // namespace filter -} // namespace nc + + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters1d/minimumFilter1d.hpp b/include/NumCpp/Filter/Filters/Filters1d/minimumFilter1d.hpp index e4b59bc10..7912e76e3 100644 --- a/include/NumCpp/Filter/Filters/Filters1d/minimumFilter1d.hpp +++ b/include/NumCpp/Filter/Filters/Filters1d/minimumFilter1d.hpp @@ -33,44 +33,41 @@ #include "NumCpp/Filter/Boundaries/Boundary.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a one-dimensional minumum filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.minimum_filter1d.html#scipy.ndimage.minimum_filter1d + /// + /// @param inImageArray + /// @param inSize: linear size of the kernel to apply + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray minumumFilter1d(const NdArray& inImageArray, + uint32 inSize, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a one-dimensional minumum filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.minimum_filter1d.html#scipy.ndimage.minimum_filter1d - /// - /// @param inImageArray - /// @param inSize: linear size of the kernel to apply - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray minumumFilter1d(const NdArray& inImageArray, - uint32 inSize, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - NdArray arrayWithBoundary = - boundary::addBoundary1d(inImageArray, inBoundaryType, inSize, inConstantValue); - NdArray output(1, inImageArray.size()); - - const uint32 boundarySize = inSize / 2; // integer division - const uint32 endPoint = boundarySize + inImageArray.size(); + NdArray arrayWithBoundary = + boundary::addBoundary1d(inImageArray, inBoundaryType, inSize, inConstantValue); + NdArray output(1, inImageArray.size()); - for (uint32 i = boundarySize; i < endPoint; ++i) - { - NdArray window = arrayWithBoundary[Slice(i - boundarySize, i + boundarySize + 1)]; + const uint32 boundarySize = inSize / 2; // integer division + const uint32 endPoint = boundarySize + inImageArray.size(); - output[i - boundarySize] = window.min().item(); - } + for (uint32 i = boundarySize; i < endPoint; ++i) + { + NdArray window = arrayWithBoundary[Slice(i - boundarySize, i + boundarySize + 1)]; - return output; + output[i - boundarySize] = window.min().item(); } - } // namespace filter -} // namespace nc + + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters1d/percentileFilter1d.hpp b/include/NumCpp/Filter/Filters/Filters1d/percentileFilter1d.hpp index 8dba809a4..0f03fe8ea 100644 --- a/include/NumCpp/Filter/Filters/Filters1d/percentileFilter1d.hpp +++ b/include/NumCpp/Filter/Filters/Filters1d/percentileFilter1d.hpp @@ -34,46 +34,43 @@ #include "NumCpp/Functions/percentile.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a one-dimensional percentile filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.percentile_filter.html#scipy.ndimage.percentile_filter + /// + /// @param inImageArray + /// @param inSize: linear size of the kernel to apply + /// @param inPercentile: percentile [0, 100] + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray percentileFilter1d(const NdArray& inImageArray, + uint32 inSize, + double inPercentile, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a one-dimensional percentile filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.percentile_filter.html#scipy.ndimage.percentile_filter - /// - /// @param inImageArray - /// @param inSize: linear size of the kernel to apply - /// @param inPercentile: percentile [0, 100] - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray percentileFilter1d(const NdArray& inImageArray, - uint32 inSize, - double inPercentile, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - NdArray arrayWithBoundary = - boundary::addBoundary1d(inImageArray, inBoundaryType, inSize, inConstantValue); - NdArray output(1, inImageArray.size()); - - const uint32 boundarySize = inSize / 2; // integer division - const uint32 endPoint = boundarySize + inImageArray.size(); + NdArray arrayWithBoundary = + boundary::addBoundary1d(inImageArray, inBoundaryType, inSize, inConstantValue); + NdArray output(1, inImageArray.size()); - for (uint32 i = boundarySize; i < endPoint; ++i) - { - NdArray window = arrayWithBoundary[Slice(i - boundarySize, i + boundarySize + 1)]; + const uint32 boundarySize = inSize / 2; // integer division + const uint32 endPoint = boundarySize + inImageArray.size(); - output[i - boundarySize] = percentile(window, inPercentile).item(); - } + for (uint32 i = boundarySize; i < endPoint; ++i) + { + NdArray window = arrayWithBoundary[Slice(i - boundarySize, i + boundarySize + 1)]; - return output; + output[i - boundarySize] = percentile(window, inPercentile).item(); } - } // namespace filter -} // namespace nc + + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters1d/rankFilter1d.hpp b/include/NumCpp/Filter/Filters/Filters1d/rankFilter1d.hpp index 89a2afd3c..4d3191d59 100644 --- a/include/NumCpp/Filter/Filters/Filters1d/rankFilter1d.hpp +++ b/include/NumCpp/Filter/Filters/Filters1d/rankFilter1d.hpp @@ -34,46 +34,43 @@ #include "NumCpp/Functions/sort.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a one-dimensional rank filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.rank_filter.html#scipy.ndimage.rank_filter + /// + /// @param inImageArray + /// @param inSize: linear size of the kernel to apply + /// @param inRank: ([0, inSize^2 - 1]) + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray rankFilter1d(const NdArray& inImageArray, + uint32 inSize, + uint8 inRank, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a one-dimensional rank filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.rank_filter.html#scipy.ndimage.rank_filter - /// - /// @param inImageArray - /// @param inSize: linear size of the kernel to apply - /// @param inRank: ([0, inSize^2 - 1]) - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray rankFilter1d(const NdArray& inImageArray, - uint32 inSize, - uint8 inRank, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - NdArray arrayWithBoundary = - boundary::addBoundary1d(inImageArray, inBoundaryType, inSize, inConstantValue); - NdArray output(1, inImageArray.size()); - - const uint32 boundarySize = inSize / 2; // integer division - const uint32 endPoint = boundarySize + inImageArray.size(); + NdArray arrayWithBoundary = + boundary::addBoundary1d(inImageArray, inBoundaryType, inSize, inConstantValue); + NdArray output(1, inImageArray.size()); - for (uint32 i = boundarySize; i < endPoint; ++i) - { - NdArray window = arrayWithBoundary[Slice(i - boundarySize, i + boundarySize + 1)]; + const uint32 boundarySize = inSize / 2; // integer division + const uint32 endPoint = boundarySize + inImageArray.size(); - output[i - boundarySize] = sort(window)[inRank]; - } + for (uint32 i = boundarySize; i < endPoint; ++i) + { + NdArray window = arrayWithBoundary[Slice(i - boundarySize, i + boundarySize + 1)]; - return output; + output[i - boundarySize] = sort(window)[inRank]; } - } // namespace filter -} // namespace nc + + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters1d/uniformFilter1d.hpp b/include/NumCpp/Filter/Filters/Filters1d/uniformFilter1d.hpp index 2e2ffdd2e..ceb58ad1b 100644 --- a/include/NumCpp/Filter/Filters/Filters1d/uniformFilter1d.hpp +++ b/include/NumCpp/Filter/Filters/Filters1d/uniformFilter1d.hpp @@ -34,44 +34,41 @@ #include "NumCpp/Functions/mean.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a one-dimensional uniform filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.uniform_filter1d.html#scipy.ndimage.uniform_filter1d + /// + /// @param inImageArray + /// @param inSize: linear size of the kernel to apply + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray uniformFilter1d(const NdArray& inImageArray, + uint32 inSize, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a one-dimensional uniform filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.uniform_filter1d.html#scipy.ndimage.uniform_filter1d - /// - /// @param inImageArray - /// @param inSize: linear size of the kernel to apply - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray uniformFilter1d(const NdArray& inImageArray, - uint32 inSize, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - NdArray arrayWithBoundary = - boundary::addBoundary1d(inImageArray, inBoundaryType, inSize, inConstantValue); - NdArray output(1, inImageArray.size()); - - const uint32 boundarySize = inSize / 2; // integer division - const uint32 endPoint = boundarySize + inImageArray.size(); + NdArray arrayWithBoundary = + boundary::addBoundary1d(inImageArray, inBoundaryType, inSize, inConstantValue); + NdArray output(1, inImageArray.size()); - for (uint32 i = boundarySize; i < endPoint; ++i) - { - NdArray window = arrayWithBoundary[Slice(i - boundarySize, i + boundarySize + 1)]; + const uint32 boundarySize = inSize / 2; // integer division + const uint32 endPoint = boundarySize + inImageArray.size(); - output[i - boundarySize] = mean(window).item(); - } + for (uint32 i = boundarySize; i < endPoint; ++i) + { + NdArray window = arrayWithBoundary[Slice(i - boundarySize, i + boundarySize + 1)]; - return output; + output[i - boundarySize] = mean(window).item(); } - } // namespace filter -} // namespace nc + + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters2d/complementaryMedianFilter.hpp b/include/NumCpp/Filter/Filters/Filters2d/complementaryMedianFilter.hpp index a61166c99..39c234360 100644 --- a/include/NumCpp/Filter/Filters/Filters2d/complementaryMedianFilter.hpp +++ b/include/NumCpp/Filter/Filters/Filters2d/complementaryMedianFilter.hpp @@ -32,30 +32,27 @@ #include "NumCpp/Filter/Filters/Filters2d/medianFilter.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a multidimensional complemenatry median filter. + /// + /// @param inImageArray + /// @param inSize: square size of the kernel to apply + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray complementaryMedianFilter(const NdArray& inImageArray, + uint32 inSize, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a multidimensional complemenatry median filter. - /// - /// @param inImageArray - /// @param inSize: square size of the kernel to apply - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray complementaryMedianFilter(const NdArray& inImageArray, - uint32 inSize, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - NdArray inImageArrayCopy(inImageArray); - inImageArrayCopy -= medianFilter(inImageArray, inSize, inBoundaryType, inConstantValue); + NdArray inImageArrayCopy(inImageArray); + inImageArrayCopy -= medianFilter(inImageArray, inSize, inBoundaryType, inConstantValue); - return inImageArrayCopy; - } - } // namespace filter -} // namespace nc + return inImageArrayCopy; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters2d/convolve.hpp b/include/NumCpp/Filter/Filters/Filters2d/convolve.hpp index cf6c851c8..75895b1f7 100644 --- a/include/NumCpp/Filter/Filters/Filters2d/convolve.hpp +++ b/include/NumCpp/Filter/Filters/Filters2d/convolve.hpp @@ -40,59 +40,56 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Utils/sqr.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a multidimensional kernel convolution. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.convolve.html#scipy.ndimage.convolve + /// + /// @param inImageArray + /// @param inSize: square size of the kernel to apply + /// @param inWeights + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray convolve(const NdArray& inImageArray, + uint32 inSize, + const NdArray& inWeights, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a multidimensional kernel convolution. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.convolve.html#scipy.ndimage.convolve - /// - /// @param inImageArray - /// @param inSize: square size of the kernel to apply - /// @param inWeights - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray convolve(const NdArray& inImageArray, - uint32 inSize, - const NdArray& inWeights, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) + if (inWeights.size() != utils::sqr(inSize)) { - if (inWeights.size() != utils::sqr(inSize)) - { - THROW_INVALID_ARGUMENT_ERROR("input weights do no match input kernal size."); - } + THROW_INVALID_ARGUMENT_ERROR("input weights do no match input kernal size."); + } - NdArray arrayWithBoundary = - boundary::addBoundary2d(inImageArray, inBoundaryType, inSize, inConstantValue); - NdArray output(inImageArray.shape()); + NdArray arrayWithBoundary = + boundary::addBoundary2d(inImageArray, inBoundaryType, inSize, inConstantValue); + NdArray output(inImageArray.shape()); - NdArray weightsFlat = rot90(inWeights, 2).flatten(); - const Shape inShape = inImageArray.shape(); - const uint32 boundarySize = inSize / 2; // integer division - const uint32 endPointRow = boundarySize + inShape.rows; - const uint32 endPointCol = boundarySize + inShape.cols; + NdArray weightsFlat = rot90(inWeights, 2).flatten(); + const Shape inShape = inImageArray.shape(); + const uint32 boundarySize = inSize / 2; // integer division + const uint32 endPointRow = boundarySize + inShape.rows; + const uint32 endPointCol = boundarySize + inShape.cols; - for (uint32 row = boundarySize; row < endPointRow; ++row) + for (uint32 row = boundarySize; row < endPointRow; ++row) + { + for (uint32 col = boundarySize; col < endPointCol; ++col) { - for (uint32 col = boundarySize; col < endPointCol; ++col) - { - NdArray window = arrayWithBoundary(Slice(row - boundarySize, row + boundarySize + 1), - Slice(col - boundarySize, col + boundarySize + 1)) - .flatten(); + NdArray window = arrayWithBoundary(Slice(row - boundarySize, row + boundarySize + 1), + Slice(col - boundarySize, col + boundarySize + 1)) + .flatten(); - output(row - boundarySize, col - boundarySize) = dot(window, weightsFlat).item(); - } + output(row - boundarySize, col - boundarySize) = dot(window, weightsFlat).item(); } - - return output; } - } // namespace filter -} // namespace nc + + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters2d/gaussianFilter.hpp b/include/NumCpp/Filter/Filters/Filters2d/gaussianFilter.hpp index 673e823f7..b0bc0982a 100644 --- a/include/NumCpp/Filter/Filters/Filters2d/gaussianFilter.hpp +++ b/include/NumCpp/Filter/Filters/Filters2d/gaussianFilter.hpp @@ -37,65 +37,62 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Utils/gaussian.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a multidimensional gaussian filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian_filter.html#scipy.ndimage.gaussian_filter + /// + /// @param inImageArray + /// @param inSigma: Standard deviation for Gaussian kernel + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray gaussianFilter(const NdArray& inImageArray, + double inSigma, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a multidimensional gaussian filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian_filter.html#scipy.ndimage.gaussian_filter - /// - /// @param inImageArray - /// @param inSigma: Standard deviation for Gaussian kernel - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray gaussianFilter(const NdArray& inImageArray, - double inSigma, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) + if (inSigma <= 0) { - if (inSigma <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input sigma value must be greater than zero."); - } + THROW_INVALID_ARGUMENT_ERROR("input sigma value must be greater than zero."); + } - // calculate the kernel size based off of the input sigma value - constexpr uint32 MIN_KERNEL_SIZE = 5; - uint32 kernelSize = - std::max(static_cast(std::ceil(inSigma * 2. * 4.)), MIN_KERNEL_SIZE); // 4 standard deviations - if (kernelSize % 2 == 0) - { - ++kernelSize; // make sure the kernel is an odd size - } + // calculate the kernel size based off of the input sigma value + constexpr uint32 MIN_KERNEL_SIZE = 5; + uint32 kernelSize = + std::max(static_cast(std::ceil(inSigma * 2. * 4.)), MIN_KERNEL_SIZE); // 4 standard deviations + if (kernelSize % 2 == 0) + { + ++kernelSize; // make sure the kernel is an odd size + } - const auto kernalHalfSize = static_cast(kernelSize / 2); // integer division + const auto kernalHalfSize = static_cast(kernelSize / 2); // integer division - // calculate the gaussian kernel - NdArray kernel(kernelSize); - for (double row = 0; row < kernelSize; ++row) + // calculate the gaussian kernel + NdArray kernel(kernelSize); + for (double row = 0; row < kernelSize; ++row) + { + for (double col = 0; col < kernelSize; ++col) { - for (double col = 0; col < kernelSize; ++col) - { - kernel(static_cast(row), static_cast(col)) = - utils::gaussian(row - kernalHalfSize, col - kernalHalfSize, inSigma); - } + kernel(static_cast(row), static_cast(col)) = + utils::gaussian(row - kernalHalfSize, col - kernalHalfSize, inSigma); } + } - // normalize the kernel - kernel /= kernel.sum().item(); + // normalize the kernel + kernel /= kernel.sum().item(); - // perform the convolution - NdArray output = - convolve(inImageArray.template astype(), kernelSize, kernel, inBoundaryType, inConstantValue) - .template astype(); + // perform the convolution + NdArray output = + convolve(inImageArray.template astype(), kernelSize, kernel, inBoundaryType, inConstantValue) + .template astype(); - return output; - } - } // namespace filter -} // namespace nc + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters2d/laplace.hpp b/include/NumCpp/Filter/Filters/Filters2d/laplace.hpp index 2a09aab7f..f3e396fbd 100644 --- a/include/NumCpp/Filter/Filters/Filters2d/laplace.hpp +++ b/include/NumCpp/Filter/Filters/Filters2d/laplace.hpp @@ -30,29 +30,26 @@ #include "NumCpp/Filter/Filters/Filters2d/convolve.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculate the 2D laplace filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.laplace.html#scipy.ndimage.laplace + /// + /// @param inImageArray + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray laplace(const NdArray& inImageArray, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculate the 2D laplace filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.laplace.html#scipy.ndimage.laplace - /// - /// @param inImageArray - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray laplace(const NdArray& inImageArray, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - NdArray kernel = { { 0, 1, 0 }, { 1, -4, 1 }, { 0, 1, 0 } }; - return convolve(inImageArray, 3, kernel, inBoundaryType, inConstantValue); - } - } // namespace filter -} // namespace nc + NdArray kernel = { { 0, 1, 0 }, { 1, -4, 1 }, { 0, 1, 0 } }; + return convolve(inImageArray, 3, kernel, inBoundaryType, inConstantValue); + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters2d/maximumFilter.hpp b/include/NumCpp/Filter/Filters/Filters2d/maximumFilter.hpp index 0e8e17161..3fad820a5 100644 --- a/include/NumCpp/Filter/Filters/Filters2d/maximumFilter.hpp +++ b/include/NumCpp/Filter/Filters/Filters2d/maximumFilter.hpp @@ -33,50 +33,47 @@ #include "NumCpp/Filter/Boundaries/Boundaries2d/addBoundary2d.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a multidimensional maximum filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.maximum_filter.html#scipy.ndimage.maximum_filter + /// + /// @param inImageArray + /// @param inSize: square size of the kernel to apply + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray maximumFilter(const NdArray& inImageArray, + uint32 inSize, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a multidimensional maximum filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.maximum_filter.html#scipy.ndimage.maximum_filter - /// - /// @param inImageArray - /// @param inSize: square size of the kernel to apply - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray maximumFilter(const NdArray& inImageArray, - uint32 inSize, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - NdArray arrayWithBoundary = - boundary::addBoundary2d(inImageArray, inBoundaryType, inSize, inConstantValue); - NdArray output(inImageArray.shape()); + NdArray arrayWithBoundary = + boundary::addBoundary2d(inImageArray, inBoundaryType, inSize, inConstantValue); + NdArray output(inImageArray.shape()); - const Shape inShape = inImageArray.shape(); - const uint32 boundarySize = inSize / 2; // integer division - const uint32 endPointRow = boundarySize + inShape.rows; - const uint32 endPointCol = boundarySize + inShape.cols; + const Shape inShape = inImageArray.shape(); + const uint32 boundarySize = inSize / 2; // integer division + const uint32 endPointRow = boundarySize + inShape.rows; + const uint32 endPointCol = boundarySize + inShape.cols; - for (uint32 row = boundarySize; row < endPointRow; ++row) + for (uint32 row = boundarySize; row < endPointRow; ++row) + { + for (uint32 col = boundarySize; col < endPointCol; ++col) { - for (uint32 col = boundarySize; col < endPointCol; ++col) - { - NdArray window = arrayWithBoundary(Slice(row - boundarySize, row + boundarySize + 1), - Slice(col - boundarySize, col + boundarySize + 1)); + NdArray window = arrayWithBoundary(Slice(row - boundarySize, row + boundarySize + 1), + Slice(col - boundarySize, col + boundarySize + 1)); - output(row - boundarySize, col - boundarySize) = window.max().item(); - } + output(row - boundarySize, col - boundarySize) = window.max().item(); } - - return output; } - } // namespace filter -} // namespace nc + + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters2d/medianFilter.hpp b/include/NumCpp/Filter/Filters/Filters2d/medianFilter.hpp index 747c3d345..863eed7b8 100644 --- a/include/NumCpp/Filter/Filters/Filters2d/medianFilter.hpp +++ b/include/NumCpp/Filter/Filters/Filters2d/medianFilter.hpp @@ -33,50 +33,47 @@ #include "NumCpp/Filter/Boundaries/Boundaries2d/addBoundary2d.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a multidimensional median filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.median_filter.html#scipy.ndimage.median_filter + /// + /// @param inImageArray + /// @param inSize: square size of the kernel to apply + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray medianFilter(const NdArray& inImageArray, + uint32 inSize, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a multidimensional median filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.median_filter.html#scipy.ndimage.median_filter - /// - /// @param inImageArray - /// @param inSize: square size of the kernel to apply - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray medianFilter(const NdArray& inImageArray, - uint32 inSize, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - NdArray arrayWithBoundary = - boundary::addBoundary2d(inImageArray, inBoundaryType, inSize, inConstantValue); - NdArray output(inImageArray.shape()); + NdArray arrayWithBoundary = + boundary::addBoundary2d(inImageArray, inBoundaryType, inSize, inConstantValue); + NdArray output(inImageArray.shape()); - const Shape inShape = inImageArray.shape(); - const uint32 boundarySize = inSize / 2; // integer division - const uint32 endPointRow = boundarySize + inShape.rows; - const uint32 endPointCol = boundarySize + inShape.cols; + const Shape inShape = inImageArray.shape(); + const uint32 boundarySize = inSize / 2; // integer division + const uint32 endPointRow = boundarySize + inShape.rows; + const uint32 endPointCol = boundarySize + inShape.cols; - for (uint32 row = boundarySize; row < endPointRow; ++row) + for (uint32 row = boundarySize; row < endPointRow; ++row) + { + for (uint32 col = boundarySize; col < endPointCol; ++col) { - for (uint32 col = boundarySize; col < endPointCol; ++col) - { - NdArray window = arrayWithBoundary(Slice(row - boundarySize, row + boundarySize + 1), - Slice(col - boundarySize, col + boundarySize + 1)); + NdArray window = arrayWithBoundary(Slice(row - boundarySize, row + boundarySize + 1), + Slice(col - boundarySize, col + boundarySize + 1)); - output(row - boundarySize, col - boundarySize) = window.median().item(); - } + output(row - boundarySize, col - boundarySize) = window.median().item(); } - - return output; } - } // namespace filter -} // namespace nc + + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters2d/minimumFilter.hpp b/include/NumCpp/Filter/Filters/Filters2d/minimumFilter.hpp index 2e8cba0ab..f86ed4409 100644 --- a/include/NumCpp/Filter/Filters/Filters2d/minimumFilter.hpp +++ b/include/NumCpp/Filter/Filters/Filters2d/minimumFilter.hpp @@ -33,50 +33,47 @@ #include "NumCpp/Filter/Boundaries/Boundaries2d/addBoundary2d.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a multidimensional minimum filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.minimum_filter.html#scipy.ndimage.minimum_filter + /// + /// @param inImageArray + /// @param inSize: square size of the kernel to apply + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray minimumFilter(const NdArray& inImageArray, + uint32 inSize, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a multidimensional minimum filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.minimum_filter.html#scipy.ndimage.minimum_filter - /// - /// @param inImageArray - /// @param inSize: square size of the kernel to apply - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray minimumFilter(const NdArray& inImageArray, - uint32 inSize, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - NdArray arrayWithBoundary = - boundary::addBoundary2d(inImageArray, inBoundaryType, inSize, inConstantValue); - NdArray output(inImageArray.shape()); + NdArray arrayWithBoundary = + boundary::addBoundary2d(inImageArray, inBoundaryType, inSize, inConstantValue); + NdArray output(inImageArray.shape()); - const Shape inShape = inImageArray.shape(); - const uint32 boundarySize = inSize / 2; // integer division - const uint32 endPointRow = boundarySize + inShape.rows; - const uint32 endPointCol = boundarySize + inShape.cols; + const Shape inShape = inImageArray.shape(); + const uint32 boundarySize = inSize / 2; // integer division + const uint32 endPointRow = boundarySize + inShape.rows; + const uint32 endPointCol = boundarySize + inShape.cols; - for (uint32 row = boundarySize; row < endPointRow; ++row) + for (uint32 row = boundarySize; row < endPointRow; ++row) + { + for (uint32 col = boundarySize; col < endPointCol; ++col) { - for (uint32 col = boundarySize; col < endPointCol; ++col) - { - NdArray window = arrayWithBoundary(Slice(row - boundarySize, row + boundarySize + 1), - Slice(col - boundarySize, col + boundarySize + 1)); + NdArray window = arrayWithBoundary(Slice(row - boundarySize, row + boundarySize + 1), + Slice(col - boundarySize, col + boundarySize + 1)); - output(row - boundarySize, col - boundarySize) = window.min().item(); - } + output(row - boundarySize, col - boundarySize) = window.min().item(); } - - return output; } - } // namespace filter -} // namespace nc + + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters2d/percentileFilter.hpp b/include/NumCpp/Filter/Filters/Filters2d/percentileFilter.hpp index 4c9908e96..2d3341d6e 100644 --- a/include/NumCpp/Filter/Filters/Filters2d/percentileFilter.hpp +++ b/include/NumCpp/Filter/Filters/Filters2d/percentileFilter.hpp @@ -34,53 +34,50 @@ #include "NumCpp/Functions/percentile.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a multidimensional percentile filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.percentile_filter.html#scipy.ndimage.percentile_filter + /// + /// @param inImageArray + /// @param inSize: square size of the kernel to apply + /// @param inPercentile: percentile [0, 100] + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray percentileFilter(const NdArray& inImageArray, + uint32 inSize, + double inPercentile, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a multidimensional percentile filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.percentile_filter.html#scipy.ndimage.percentile_filter - /// - /// @param inImageArray - /// @param inSize: square size of the kernel to apply - /// @param inPercentile: percentile [0, 100] - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray percentileFilter(const NdArray& inImageArray, - uint32 inSize, - double inPercentile, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - NdArray arrayWithBoundary = - boundary::addBoundary2d(inImageArray, inBoundaryType, inSize, inConstantValue); - NdArray output(inImageArray.shape()); + NdArray arrayWithBoundary = + boundary::addBoundary2d(inImageArray, inBoundaryType, inSize, inConstantValue); + NdArray output(inImageArray.shape()); - const Shape inShape = inImageArray.shape(); - const uint32 boundarySize = inSize / 2; // integer division - const uint32 endPointRow = boundarySize + inShape.rows; - const uint32 endPointCol = boundarySize + inShape.cols; + const Shape inShape = inImageArray.shape(); + const uint32 boundarySize = inSize / 2; // integer division + const uint32 endPointRow = boundarySize + inShape.rows; + const uint32 endPointCol = boundarySize + inShape.cols; - for (uint32 row = boundarySize; row < endPointRow; ++row) + for (uint32 row = boundarySize; row < endPointRow; ++row) + { + for (uint32 col = boundarySize; col < endPointCol; ++col) { - for (uint32 col = boundarySize; col < endPointCol; ++col) - { - NdArray window = arrayWithBoundary(Slice(row - boundarySize, row + boundarySize + 1), - Slice(col - boundarySize, col + boundarySize + 1)); + NdArray window = arrayWithBoundary(Slice(row - boundarySize, row + boundarySize + 1), + Slice(col - boundarySize, col + boundarySize + 1)); - output(row - boundarySize, col - boundarySize) = - percentile(window, inPercentile, Axis::NONE, "nearest").item(); - } + output(row - boundarySize, col - boundarySize) = + percentile(window, inPercentile, Axis::NONE, "nearest").item(); } - - return output; } - } // namespace filter -} // namespace nc + + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters2d/rankFilter.hpp b/include/NumCpp/Filter/Filters/Filters2d/rankFilter.hpp index 9fff92d3f..1c977d7a4 100644 --- a/include/NumCpp/Filter/Filters/Filters2d/rankFilter.hpp +++ b/include/NumCpp/Filter/Filters/Filters2d/rankFilter.hpp @@ -37,57 +37,54 @@ #include "NumCpp/Functions/sort.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a multidimensional rank filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.rank_filter.html#scipy.ndimage.rank_filter + /// + /// @param inImageArray + /// @param inSize: square size of the kernel to apply + /// @param inRank: ([0, inSize^2 - 1]) + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray rankFilter(const NdArray& inImageArray, + uint32 inSize, + uint32 inRank, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a multidimensional rank filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.rank_filter.html#scipy.ndimage.rank_filter - /// - /// @param inImageArray - /// @param inSize: square size of the kernel to apply - /// @param inRank: ([0, inSize^2 - 1]) - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray rankFilter(const NdArray& inImageArray, - uint32 inSize, - uint32 inRank, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) + if (inRank >= utils::sqr(inSize)) { - if (inRank >= utils::sqr(inSize)) - { - THROW_INVALID_ARGUMENT_ERROR("rank not within filter footprint size."); - } + THROW_INVALID_ARGUMENT_ERROR("rank not within filter footprint size."); + } - NdArray arrayWithBoundary = - boundary::addBoundary2d(inImageArray, inBoundaryType, inSize, inConstantValue); - NdArray output(inImageArray.shape()); + NdArray arrayWithBoundary = + boundary::addBoundary2d(inImageArray, inBoundaryType, inSize, inConstantValue); + NdArray output(inImageArray.shape()); - const Shape inShape = inImageArray.shape(); - const uint32 boundarySize = inSize / 2; // integer division - const uint32 endPointRow = boundarySize + inShape.rows; - const uint32 endPointCol = boundarySize + inShape.cols; + const Shape inShape = inImageArray.shape(); + const uint32 boundarySize = inSize / 2; // integer division + const uint32 endPointRow = boundarySize + inShape.rows; + const uint32 endPointCol = boundarySize + inShape.cols; - for (uint32 row = boundarySize; row < endPointRow; ++row) + for (uint32 row = boundarySize; row < endPointRow; ++row) + { + for (uint32 col = boundarySize; col < endPointCol; ++col) { - for (uint32 col = boundarySize; col < endPointCol; ++col) - { - NdArray window = arrayWithBoundary(Slice(row - boundarySize, row + boundarySize + 1), - Slice(col - boundarySize, col + boundarySize + 1)); + NdArray window = arrayWithBoundary(Slice(row - boundarySize, row + boundarySize + 1), + Slice(col - boundarySize, col + boundarySize + 1)); - output(row - boundarySize, col - boundarySize) = sort(window)[inRank]; - } + output(row - boundarySize, col - boundarySize) = sort(window)[inRank]; } - - return output; } - } // namespace filter -} // namespace nc + + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Filter/Filters/Filters2d/uniformFilter.hpp b/include/NumCpp/Filter/Filters/Filters2d/uniformFilter.hpp index 3c275691b..e034e8b6f 100644 --- a/include/NumCpp/Filter/Filters/Filters2d/uniformFilter.hpp +++ b/include/NumCpp/Filter/Filters/Filters2d/uniformFilter.hpp @@ -34,50 +34,47 @@ #include "NumCpp/Functions/mean.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::filter { - namespace filter + //============================================================================ + // Method Description: + /// Calculates a multidimensional uniform filter. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.uniform_filter.html#scipy.ndimage.uniform_filter + /// + /// @param inImageArray + /// @param inSize: square size of the kernel to apply + /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) + /// @param inConstantValue: contant value if boundary = 'constant' (default 0) + /// @return NdArray + /// + template + NdArray uniformFilter(const NdArray& inImageArray, + uint32 inSize, + Boundary inBoundaryType = Boundary::REFLECT, + dtype inConstantValue = 0) { - //============================================================================ - // Method Description: - /// Calculates a multidimensional uniform filter. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.uniform_filter.html#scipy.ndimage.uniform_filter - /// - /// @param inImageArray - /// @param inSize: square size of the kernel to apply - /// @param inBoundaryType: boundary mode (default Reflect) options (reflect, constant, nearest, mirror, wrap) - /// @param inConstantValue: contant value if boundary = 'constant' (default 0) - /// @return NdArray - /// - template - NdArray uniformFilter(const NdArray& inImageArray, - uint32 inSize, - Boundary inBoundaryType = Boundary::REFLECT, - dtype inConstantValue = 0) - { - NdArray arrayWithBoundary = - boundary::addBoundary2d(inImageArray, inBoundaryType, inSize, inConstantValue); - NdArray output(inImageArray.shape()); + NdArray arrayWithBoundary = + boundary::addBoundary2d(inImageArray, inBoundaryType, inSize, inConstantValue); + NdArray output(inImageArray.shape()); - const Shape inShape = inImageArray.shape(); - const uint32 boundarySize = inSize / 2; // integer division - const uint32 endPointRow = boundarySize + inShape.rows; - const uint32 endPointCol = boundarySize + inShape.cols; + const Shape inShape = inImageArray.shape(); + const uint32 boundarySize = inSize / 2; // integer division + const uint32 endPointRow = boundarySize + inShape.rows; + const uint32 endPointCol = boundarySize + inShape.cols; - for (uint32 row = boundarySize; row < endPointRow; ++row) + for (uint32 row = boundarySize; row < endPointRow; ++row) + { + for (uint32 col = boundarySize; col < endPointCol; ++col) { - for (uint32 col = boundarySize; col < endPointCol; ++col) - { - NdArray window = arrayWithBoundary(Slice(row - boundarySize, row + boundarySize + 1), - Slice(col - boundarySize, col + boundarySize + 1)); + NdArray window = arrayWithBoundary(Slice(row - boundarySize, row + boundarySize + 1), + Slice(col - boundarySize, col + boundarySize + 1)); - output(row - boundarySize, col - boundarySize) = mean(window).item(); - } + output(row - boundarySize, col - boundarySize) = mean(window).item(); } - - return output; } - } // namespace filter -} // namespace nc + + return output; + } +} // namespace nc::filter diff --git a/include/NumCpp/Functions/hammingEncode.hpp b/include/NumCpp/Functions/hammingEncode.hpp index a9a48e8d3..44e1e4b8f 100644 --- a/include/NumCpp/Functions/hammingEncode.hpp +++ b/include/NumCpp/Functions/hammingEncode.hpp @@ -41,361 +41,357 @@ #include "NumCpp/Core/Internal/TypeTraits.hpp" -namespace nc +namespace nc::edac { - namespace edac + namespace detail { - namespace detail + //============================================================================ + // Method Description: + /// @brief Tests if value is a power of two + /// + /// @param n integer value + /// @return bool true if value is a power of two, else false + /// + template, int> = 0> + constexpr bool isPowerOfTwo(IntType n) noexcept { - //============================================================================ - // Method Description: - /// @brief Tests if value is a power of two - /// - /// @param n integer value - /// @return bool true if value is a power of two, else false - /// - template, int> = 0> - constexpr bool isPowerOfTwo(IntType n) noexcept - { - // Returns true if the given non-negative integer n is a power of two. - return n != 0 && (n & (n - 1)) == 0; - } + // Returns true if the given non-negative integer n is a power of two. + return n != 0 && (n & (n - 1)) == 0; + } - //============================================================================ - // Method Description: - /// Calculates the next power of two after n - /// >>> _next_power_of_two(768) - /// 1024 - /// >>> _next_power_of_two(4) - /// 8 - /// - /// @param n integer value - /// @return next power of two - /// @exception std::invalid_argument if input value is less than zero - //// - template, int> = 0> - std::size_t nextPowerOfTwo(IntType n) + //============================================================================ + // Method Description: + /// Calculates the next power of two after n + /// >>> _next_power_of_two(768) + /// 1024 + /// >>> _next_power_of_two(4) + /// 8 + /// + /// @param n integer value + /// @return next power of two + /// @exception std::invalid_argument if input value is less than zero + //// + template, int> = 0> + std::size_t nextPowerOfTwo(IntType n) + { + if (n < 0) { - if (n < 0) - { - throw std::invalid_argument("Input value must be greater than or equal to zero."); - } - - if (isPowerOfTwo(n)) - { - return static_cast(n) << 1; - } - - return static_cast(std::pow(2, std::ceil(std::log2(n)))); + throw std::invalid_argument("Input value must be greater than or equal to zero."); } - //============================================================================ - // Method Description: - /// Calculates the first n powers of two - /// - /// @param n integer value - /// @return first n powers of two - /// @exception std::bad_alloc if unable to allocate for return vector - /// - template, int> = 0> - std::vector powersOfTwo(IntType n) + if (isPowerOfTwo(n)) { - auto i = std::size_t{ 0 }; - auto power = std::size_t{ 1 }; - auto powers = std::vector(); - powers.reserve(n); - - while (i < static_cast(n)) - { - powers.push_back(power); - power <<= 1; - ++i; - } - - return powers; + return static_cast(n) << 1; } - //============================================================================ - // Method Description: - /// Calculates the number of needed Hamming SECDED parity bits to encode the data - /// - /// @param numDataBits the number of data bits to encode - /// @return number of Hamming SECDED parity bits - /// @exception std::invalid_argument if input value is less than zero - /// @exception std::runtime_error if the number of data bits does not represent a valid Hamming SECDED code - /// - template, int> = 0> - std::size_t numSecdedParityBitsNeeded(IntType numDataBits) - { - const auto n = nextPowerOfTwo(numDataBits); - const auto lowerBin = static_cast(std::floor(std::log2(n))); - const auto upperBin = lowerBin + 1; - const auto dataBitBoundary = n - lowerBin - 1; - const auto numParityBits = numDataBits <= dataBitBoundary ? lowerBin + 1 : upperBin + 1; - - if (!isPowerOfTwo(numParityBits + numDataBits)) - { - throw std::runtime_error( - "input number of data bits is not a valid Hamming SECDED code configuration."); - } + return static_cast(std::pow(2, std::ceil(std::log2(n)))); + } - return numParityBits; - } + //============================================================================ + // Method Description: + /// Calculates the first n powers of two + /// + /// @param n integer value + /// @return first n powers of two + /// @exception std::bad_alloc if unable to allocate for return vector + /// + template, int> = 0> + std::vector powersOfTwo(IntType n) + { + auto i = std::size_t{ 0 }; + auto power = std::size_t{ 1 }; + auto powers = std::vector(); + powers.reserve(n); - //============================================================================ - // Method Description: - /// Returns the indices of all data bits covered by a specified parity bit in a bitstring - /// of length numDataBits. The indices are relative to DATA BITSTRING ITSELF, NOT including - /// parity bits. - /// - /// @param numDataBits the number of data bits to encode - /// @param parityBit the parity bit number - /// @return number of Hamming SECDED parity bits - /// @exception std::invalid_argument if parityBit is not a power of two - /// @exception std::bad_alloc if unable to allocate return vector - /// - template, int> = 0, - std::enable_if_t, int> = 0> - std::vector dataBitsCovered(IntType1 numDataBits, IntType2 parityBit) + while (i < static_cast(n)) { - if (!isPowerOfTwo(parityBit)) - { - throw std::invalid_argument("All hamming parity bits are indexed by powers of two."); - } - - std::size_t dataIndex = 1; // bit we're currently at in the DATA bitstring - std::size_t totalIndex = 3; // bit we're currently at in the OVERALL bitstring - auto parityBit_ = static_cast(parityBit); - - auto indices = std::vector(); - indices.reserve(numDataBits); // worst case - - while (dataIndex <= static_cast(numDataBits)) - { - const auto currentBitIsData = !isPowerOfTwo(totalIndex); - if (currentBitIsData && (totalIndex % (parityBit_ << 1)) >= parityBit_) - { - indices.push_back(dataIndex - 1); // adjust output to be zero indexed - } - - dataIndex += currentBitIsData ? 1 : 0; - ++totalIndex; - } - - return indices; + powers.push_back(power); + power <<= 1; + ++i; } - //============================================================================ - // Method Description: - /// Calculates the overall parity of the data, assumes last bit is the parity bit itself - /// - /// @param data the data word - /// @return overall parity bit value - /// - template - constexpr bool calculateParity(const std::bitset& data) noexcept - { - bool parity = false; - for (std::size_t i = 0; i < DataBits - 1; ++i) - { - parity ^= data[i]; - } + return powers; + } - return parity; - } + //============================================================================ + // Method Description: + /// Calculates the number of needed Hamming SECDED parity bits to encode the data + /// + /// @param numDataBits the number of data bits to encode + /// @return number of Hamming SECDED parity bits + /// @exception std::invalid_argument if input value is less than zero + /// @exception std::runtime_error if the number of data bits does not represent a valid Hamming SECDED code + /// + template, int> = 0> + std::size_t numSecdedParityBitsNeeded(IntType numDataBits) + { + const auto n = nextPowerOfTwo(numDataBits); + const auto lowerBin = static_cast(std::floor(std::log2(n))); + const auto upperBin = lowerBin + 1; + const auto dataBitBoundary = n - lowerBin - 1; + const auto numParityBits = numDataBits <= dataBitBoundary ? lowerBin + 1 : upperBin + 1; - //============================================================================ - // Method Description: - /// Calculates the overall parity of the data, assumes last bit is the parity bit itself - /// - /// @param data the data word - /// @return overall parity bit value - /// - inline bool calculateParity(const boost::dynamic_bitset<>& data) noexcept + if (!isPowerOfTwo(numParityBits + numDataBits)) { - bool parity = false; - for (std::size_t i = 0; i < data.size() - 1; ++i) - { - parity ^= data[i]; - } - - return parity; + throw std::runtime_error("input number of data bits is not a valid Hamming SECDED code configuration."); } - //============================================================================ - // Method Description: - /// Calculates the specified Hamming parity bit (1, 2, 4, 8, etc.) for the given data. - /// Assumes even parity to allow for easier computation of parity using XOR. - /// - /// @param data the data word - /// @param parityBit the parity bit number - /// @return parity bit value - /// @exception std::invalid_argument if parityBit is not a power of two - /// @exception std::bad_alloc if unable to allocate return vector - /// - template, int> = 0> - bool calculateParity(const std::bitset& data, IntType parityBit) - { - bool parity = false; - for (const auto i : dataBitsCovered(DataBits, parityBit)) - { - parity ^= data[i]; - } + return numParityBits; + } - return parity; + //============================================================================ + // Method Description: + /// Returns the indices of all data bits covered by a specified parity bit in a bitstring + /// of length numDataBits. The indices are relative to DATA BITSTRING ITSELF, NOT including + /// parity bits. + /// + /// @param numDataBits the number of data bits to encode + /// @param parityBit the parity bit number + /// @return number of Hamming SECDED parity bits + /// @exception std::invalid_argument if parityBit is not a power of two + /// @exception std::bad_alloc if unable to allocate return vector + /// + template, int> = 0, + std::enable_if_t, int> = 0> + std::vector dataBitsCovered(IntType1 numDataBits, IntType2 parityBit) + { + if (!isPowerOfTwo(parityBit)) + { + throw std::invalid_argument("All hamming parity bits are indexed by powers of two."); } - //============================================================================ - // Method Description: - /// Checks that the number of DataBits and EncodedBits are consistent - /// - /// @return the number of parity bits - /// @exception std::runtime_error if DataBits and EncodedBits are not consistent - /// @exception std::runtime_error if the number of data bits does not represent a valid Hamming SECDED code - /// - template, int> = 0> - std::size_t checkBitsConsistent() - { - const auto numParityBits = detail::numSecdedParityBitsNeeded(DataBits); - if (numParityBits + DataBits != EncodedBits) - { - throw std::runtime_error("DataBits and EncodedBits are not consistent"); - } + std::size_t dataIndex = 1; // bit we're currently at in the DATA bitstring + std::size_t totalIndex = 3; // bit we're currently at in the OVERALL bitstring + auto parityBit_ = static_cast(parityBit); - return numParityBits; - } + auto indices = std::vector(); + indices.reserve(numDataBits); // worst case - //============================================================================ - // Method Description: - /// Returns the Hamming SECDED decoded bits from the endoded bits. Assumes that the - /// DataBits and EncodedBits have been checks for consistancy already - /// - /// @param encodedBits the Hamming SECDED encoded word - /// @return data bits from the encoded word - /// - template, int> = 0> - std::bitset extractData(const std::bitset& encodedBits) noexcept + while (dataIndex <= static_cast(numDataBits)) { - auto dataBits = std::bitset(); - - std::size_t dataIndex = 0; - for (std::size_t encodedIndex = 0; encodedIndex < EncodedBits; ++encodedIndex) + const auto currentBitIsData = !isPowerOfTwo(totalIndex); + if (currentBitIsData && (totalIndex % (parityBit_ << 1)) >= parityBit_) { - if (!isPowerOfTwo(encodedIndex + 1)) - { - dataBits[dataIndex++] = encodedBits[encodedIndex]; - if (dataIndex == DataBits) - { - break; - } - } + indices.push_back(dataIndex - 1); // adjust output to be zero indexed } - return dataBits; + dataIndex += currentBitIsData ? 1 : 0; + ++totalIndex; } - } // namespace detail + + return indices; + } //============================================================================ // Method Description: - /// Returns the Hamming SECDED encoded bits for the data bits + /// Calculates the overall parity of the data, assumes last bit is the parity bit itself /// - /// @param dataBits the data bits to encode - /// @return encoded data bits - /// @exception std::runtime_error if the number of data bits does not represent a valid Hamming SECDED code + /// @param data the data word + /// @return overall parity bit value /// template - boost::dynamic_bitset<> encode(const std::bitset& dataBits) + constexpr bool calculateParity(const std::bitset& data) noexcept { - const auto numParityBits = detail::numSecdedParityBitsNeeded(DataBits); - const auto numEncodedBits = numParityBits + DataBits; + bool parity = false; + for (std::size_t i = 0; i < DataBits - 1; ++i) + { + parity ^= data[i]; + } - auto encodedBits = boost::dynamic_bitset<>(numEncodedBits); + return parity; + } - // set the parity bits - for (const auto parityBit : detail::powersOfTwo( - numParityBits - 1)) // -1 because overall parity will be calculated seperately later + //============================================================================ + // Method Description: + /// Calculates the overall parity of the data, assumes last bit is the parity bit itself + /// + /// @param data the data word + /// @return overall parity bit value + /// + inline bool calculateParity(const boost::dynamic_bitset<>& data) noexcept + { + bool parity = false; + for (std::size_t i = 0; i < data.size() - 1; ++i) { - encodedBits[parityBit - 1] = detail::calculateParity(dataBits, parityBit); + parity ^= data[i]; } - // set the data bits, switch to 1 based to make things easier for isPowerOfTwo - std::size_t dataBitIndex = 0; - for (std::size_t bitIndex = 1; bitIndex <= numEncodedBits - 1; - ++bitIndex) // -1 to account for the overall parity bit + return parity; + } + + //============================================================================ + // Method Description: + /// Calculates the specified Hamming parity bit (1, 2, 4, 8, etc.) for the given data. + /// Assumes even parity to allow for easier computation of parity using XOR. + /// + /// @param data the data word + /// @param parityBit the parity bit number + /// @return parity bit value + /// @exception std::invalid_argument if parityBit is not a power of two + /// @exception std::bad_alloc if unable to allocate return vector + /// + template, int> = 0> + bool calculateParity(const std::bitset& data, IntType parityBit) + { + bool parity = false; + for (const auto i : dataBitsCovered(DataBits, parityBit)) { - if (!detail::isPowerOfTwo(bitIndex)) - { - encodedBits[bitIndex - 1] = dataBits[dataBitIndex++]; - } + parity ^= data[i]; } - // compute and set overall parity for the entire encoded data (not including the overall parity bit itself) - encodedBits[numEncodedBits - 1] = detail::calculateParity(encodedBits); // overall parity at the end - - // all done! - return encodedBits; + return parity; } //============================================================================ // Method Description: - /// Returns the Hamming SECDED decoded bits for the enocoded bits - /// https://en.wikipedia.org/wiki/Hamming_code + /// Checks that the number of DataBits and EncodedBits are consistent /// - /// @param encodedBits the encoded bits to decode - /// @param decodedBits the output decoded bits - /// @return int status (0=no errors, 1=1 corrected error, 2=2 errors detected) + /// @return the number of parity bits /// @exception std::runtime_error if DataBits and EncodedBits are not consistent /// @exception std::runtime_error if the number of data bits does not represent a valid Hamming SECDED code /// template, int> = 0> - int decode(std::bitset encodedBits, std::bitset& decodedBits) + std::size_t checkBitsConsistent() { - const auto numParityBits = detail::checkBitsConsistent(); + const auto numParityBits = detail::numSecdedParityBitsNeeded(DataBits); + if (numParityBits + DataBits != EncodedBits) + { + throw std::runtime_error("DataBits and EncodedBits are not consistent"); + } - // the data bits, which may be corrupted - decodedBits = detail::extractData(encodedBits); + return numParityBits; + } - // check the overall parity bit - const auto overallExpected = detail::calculateParity(encodedBits); - const auto overallActual = encodedBits[EncodedBits - 1]; - const auto overallCorrect = overallExpected == overallActual; + //============================================================================ + // Method Description: + /// Returns the Hamming SECDED decoded bits from the endoded bits. Assumes that the + /// DataBits and EncodedBits have been checks for consistancy already + /// + /// @param encodedBits the Hamming SECDED encoded word + /// @return data bits from the encoded word + /// + template, int> = 0> + std::bitset extractData(const std::bitset& encodedBits) noexcept + { + auto dataBits = std::bitset(); - // check individual parities - each parity bit's index (besides overall parity) is a power of two - std::size_t indexOfError = 0; - for (const auto parityBit : detail::powersOfTwo(numParityBits - 1)) + std::size_t dataIndex = 0; + for (std::size_t encodedIndex = 0; encodedIndex < EncodedBits; ++encodedIndex) { - const auto expected = detail::calculateParity(decodedBits, parityBit); - const auto actual = encodedBits[parityBit - 1]; // -1 because parityBit is 1 based - if (expected != actual) + if (!isPowerOfTwo(encodedIndex + 1)) { - indexOfError += parityBit; + dataBits[dataIndex++] = encodedBits[encodedIndex]; + if (dataIndex == DataBits) + { + break; + } } } - // attempt to repair a single flipped bit or throw exception if more than one - if (overallCorrect && indexOfError != 0) + return dataBits; + } + } // namespace detail + + //============================================================================ + // Method Description: + /// Returns the Hamming SECDED encoded bits for the data bits + /// + /// @param dataBits the data bits to encode + /// @return encoded data bits + /// @exception std::runtime_error if the number of data bits does not represent a valid Hamming SECDED code + /// + template + boost::dynamic_bitset<> encode(const std::bitset& dataBits) + { + const auto numParityBits = detail::numSecdedParityBitsNeeded(DataBits); + const auto numEncodedBits = numParityBits + DataBits; + + auto encodedBits = boost::dynamic_bitset<>(numEncodedBits); + + // set the parity bits + for (const auto parityBit : + detail::powersOfTwo(numParityBits - 1)) // -1 because overall parity will be calculated seperately later + { + encodedBits[parityBit - 1] = detail::calculateParity(dataBits, parityBit); + } + + // set the data bits, switch to 1 based to make things easier for isPowerOfTwo + std::size_t dataBitIndex = 0; + for (std::size_t bitIndex = 1; bitIndex <= numEncodedBits - 1; + ++bitIndex) // -1 to account for the overall parity bit + { + if (!detail::isPowerOfTwo(bitIndex)) { - // two errors found - return 2; + encodedBits[bitIndex - 1] = dataBits[dataBitIndex++]; } - else if (!overallCorrect && indexOfError != 0) + } + + // compute and set overall parity for the entire encoded data (not including the overall parity bit itself) + encodedBits[numEncodedBits - 1] = detail::calculateParity(encodedBits); // overall parity at the end + + // all done! + return encodedBits; + } + + //============================================================================ + // Method Description: + /// Returns the Hamming SECDED decoded bits for the enocoded bits + /// https://en.wikipedia.org/wiki/Hamming_code + /// + /// @param encodedBits the encoded bits to decode + /// @param decodedBits the output decoded bits + /// @return int status (0=no errors, 1=1 corrected error, 2=2 errors detected) + /// @exception std::runtime_error if DataBits and EncodedBits are not consistent + /// @exception std::runtime_error if the number of data bits does not represent a valid Hamming SECDED code + /// + template, int> = 0> + int decode(std::bitset encodedBits, std::bitset& decodedBits) + { + const auto numParityBits = detail::checkBitsConsistent(); + + // the data bits, which may be corrupted + decodedBits = detail::extractData(encodedBits); + + // check the overall parity bit + const auto overallExpected = detail::calculateParity(encodedBits); + const auto overallActual = encodedBits[EncodedBits - 1]; + const auto overallCorrect = overallExpected == overallActual; + + // check individual parities - each parity bit's index (besides overall parity) is a power of two + std::size_t indexOfError = 0; + for (const auto parityBit : detail::powersOfTwo(numParityBits - 1)) + { + const auto expected = detail::calculateParity(decodedBits, parityBit); + const auto actual = encodedBits[parityBit - 1]; // -1 because parityBit is 1 based + if (expected != actual) { - // one error found, flip the bit in error and we're good - encodedBits.flip(indexOfError - 1); - decodedBits = detail::extractData(encodedBits); - return 1; + indexOfError += parityBit; } + } - return 0; + // attempt to repair a single flipped bit or throw exception if more than one + if (overallCorrect && indexOfError != 0) + { + // two errors found + return 2; } - } // namespace edac -} // namespace nc + else if (!overallCorrect && indexOfError != 0) + { + // one error found, flip the bit in error and we're good + encodedBits.flip(indexOfError - 1); + decodedBits = detail::extractData(encodedBits); + return 1; + } + + return 0; + } +} // namespace nc::edac #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/ImageProcessing/Centroid.hpp b/include/NumCpp/ImageProcessing/Centroid.hpp index 739ea56de..dc6ca8bd9 100644 --- a/include/NumCpp/ImageProcessing/Centroid.hpp +++ b/include/NumCpp/ImageProcessing/Centroid.hpp @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -40,304 +41,300 @@ #include "NumCpp/Utils/essentiallyEqual.hpp" #include "NumCpp/Utils/num2str.hpp" -namespace nc +namespace nc::imageProcessing { - namespace imageProcessing + //================================================================================ + // Class Description: + /// holds the information for a centroid + template + class Centroid { - //================================================================================ - // Class Description: - /// holds the information for a centroid - template - class Centroid + private: + STATIC_ASSERT_ARITHMETIC(dtype); + + public: + using accumulator_t = typename std::conditional::value, int64, double>::type; + + //============================================================================= + // Description: + /// defualt constructor needed by containers + /// + Centroid() = default; + + //============================================================================= + // Description: + /// constructor + /// + /// @param inCluster + /// + explicit Centroid(const Cluster& inCluster) : + intensity_(inCluster.intensity()), + eod_(inCluster.eod()) { - private: - STATIC_ASSERT_ARITHMETIC(dtype); - - public: - using accumulator_t = typename std::conditional::value, int64, double>::type; - - //============================================================================= - // Description: - /// defualt constructor needed by containers - /// - Centroid() = default; - - //============================================================================= - // Description: - /// constructor - /// - /// @param inCluster - /// - explicit Centroid(const Cluster& inCluster) : - intensity_(inCluster.intensity()), - eod_(inCluster.eod()) - { - centerOfMass(inCluster); - setEllipseProperties(inCluster); - } - - //============================================================================= - // Description: - /// gets the centroid row - /// - /// @return centroid row - /// - double row() const noexcept - { - return row_; - } - - //============================================================================= - // Description: - /// gets the centroid col - /// - /// @return centroid col - /// - double col() const noexcept - { - return col_; - } - - //============================================================================= - // Description: - /// gets the centroid intensity - /// - /// @return centroid intensity - /// - accumulator_t intensity() const noexcept - { - return intensity_; - } - - //============================================================================= - // Description: - /// returns the estimated eod of the centroid - /// - /// @return star id - /// - double eod() const noexcept - { - return eod_; - } - - //============================================================================= - // Description: - /// returns the ellipse semi-major axis a - /// - /// @return a - /// - double a() const noexcept - { - return a_; - } - - //============================================================================= - - // Description: - /// returns the ellipse semi-minor axis b - /// - /// @return b - /// - double b() const noexcept - { - return b_; - } - - //============================================================================= - // Description: - /// returns the ellipse eccentricity - /// - /// @return eccentricity - /// - double eccentricity() const noexcept - { - return eccentricity_; - } + centerOfMass(inCluster); + setEllipseProperties(inCluster); + } + + //============================================================================= + // Description: + /// gets the centroid row + /// + /// @return centroid row + /// + [[nodiscard]] double row() const noexcept + { + return row_; + } + + //============================================================================= + // Description: + /// gets the centroid col + /// + /// @return centroid col + /// + [[nodiscard]] double col() const noexcept + { + return col_; + } + + //============================================================================= + // Description: + /// gets the centroid intensity + /// + /// @return centroid intensity + /// + [[nodiscard]] accumulator_t intensity() const noexcept + { + return intensity_; + } + + //============================================================================= + // Description: + /// returns the estimated eod of the centroid + /// + /// @return star id + /// + [[nodiscard]] double eod() const noexcept + { + return eod_; + } + + //============================================================================= + // Description: + /// returns the ellipse semi-major axis a + /// + /// @return a + /// + [[nodiscard]] double a() const noexcept + { + return a_; + } - //============================================================================= + //============================================================================= - // Description: - /// returns the ellipse semi-minor axis orientation - /// - /// @return orientation - /// - double orientation() const noexcept - { - return orientation_; - } - - //============================================================================= - // Description: - /// returns the centroid as a string representation - /// - /// @return std::string - /// - std::string str() const - { - std::string out = "row = " + utils::num2str(row_) + " col = " + utils::num2str(col_) + - " intensity = " + utils::num2str(intensity_) + " eod = " + utils::num2str(eod_) + - " a = " + utils::num2str(a_) + " b = " + utils::num2str(b_) + - " eccentricity = " + utils::num2str(eccentricity_) + - " orientation = " + utils::num2str(orientation_) + '\n'; + // Description: + /// returns the ellipse semi-minor axis b + /// + /// @return b + /// + [[nodiscard]] double b() const noexcept + { + return b_; + } + + //============================================================================= + // Description: + /// returns the ellipse eccentricity + /// + /// @return eccentricity + /// + [[nodiscard]] double eccentricity() const noexcept + { + return eccentricity_; + } - return out; - } + //============================================================================= - //============================================================================ - /// Method Description: - /// prints the Centroid object to the console - /// - void print() const - { - std::cout << *this; - } + // Description: + /// returns the ellipse semi-minor axis orientation + /// + /// @return orientation + /// + [[nodiscard]] double orientation() const noexcept + { + return orientation_; + } + + //============================================================================= + // Description: + /// returns the centroid as a string representation + /// + /// @return std::string + /// + [[nodiscard]] std::string str() const + { + std::string out = "row = " + utils::num2str(row_) + " col = " + utils::num2str(col_) + + " intensity = " + utils::num2str(intensity_) + " eod = " + utils::num2str(eod_) + + " a = " + utils::num2str(a_) + " b = " + utils::num2str(b_) + + " eccentricity = " + utils::num2str(eccentricity_) + + " orientation = " + utils::num2str(orientation_) + '\n'; + + return out; + } + + //============================================================================ + /// Method Description: + /// prints the Centroid object to the console + /// + void print() const + { + std::cout << *this; + } + + //============================================================================= + // Description: + /// equality operator + /// + /// @param rhs + /// + /// @return bool + /// + bool operator==(const Centroid& rhs) const noexcept + { + return (row_ == rhs.row_ && col_ == rhs.col_ && intensity_ == rhs.intensity_ && + utils::essentiallyEqual(eod_, rhs.eod_) && utils::essentiallyEqual(a_, rhs.a_) && + utils::essentiallyEqual(b_, rhs.b_) && utils::essentiallyEqual(eccentricity_, rhs.eccentricity_) && + utils::essentiallyEqual(orientation_, rhs.orientation_)); + } + + //============================================================================= + // Description: + /// not equality operator + /// + /// @param rhs + /// + /// @return bool + /// + bool operator!=(const Centroid& rhs) const noexcept + { + return !(*this == rhs); + } + + //============================================================================= + // Description: + /// less than operator for std::sort algorithm; + /// NOTE: std::sort sorts in ascending order. Since I want to sort + /// the centroids in descensing order, I am purposefully defining + /// this operator backwards! + /// + /// @param rhs + /// + /// @return bool + /// + bool operator<(const Centroid& rhs) const noexcept + { + return intensity_ < rhs.intensity_ ? false : true; + } + + //============================================================================= + // Description: + /// ostream operator + /// + /// @param inStream + /// @param inCentriod + /// @return std::ostream + /// + friend std::ostream& operator<<(std::ostream& inStream, const Centroid& inCentriod) + { + inStream << inCentriod.str(); + return inStream; + } + + private: + //==================================Attributes================================/// + double row_{ 0. }; + double col_{ 0. }; + accumulator_t intensity_{ 0 }; + double eod_{ 0. }; + /// The ellipse semi-major axis a + double a_{}; + /// The ellipse semi-minor axis b + double b_{}; + /// The centriod eccentricity + double eccentricity_{}; + /// The centriod ellipse orientation in radians. Measured counter-clockwise from +x axis + double orientation_{}; + + //============================================================================= + // Description: + /// center of mass algorithm; + /// WARNING: if both positive and negative values are present in the cluster, + /// it can lead to an undefined COM. + /// + /// @param inCluster + /// + void centerOfMass(const Cluster& inCluster) + { + const Shape clusterShape(inCluster.height(), inCluster.width()); + NdArray clusterArray(clusterShape); + clusterArray.zeros(); - //============================================================================= - // Description: - /// equality operator - /// - /// @param rhs - /// - /// @return bool - /// - bool operator==(const Centroid& rhs) const noexcept - { - return (row_ == rhs.row_ && col_ == rhs.col_ && intensity_ == rhs.intensity_ && - utils::essentiallyEqual(eod_, rhs.eod_) && utils::essentiallyEqual(a_, rhs.a_) && - utils::essentiallyEqual(b_, rhs.b_) && - utils::essentiallyEqual(eccentricity_, rhs.eccentricity_) && - utils::essentiallyEqual(orientation_, rhs.orientation_)); - } + const uint32 rowMin = inCluster.rowMin(); + const uint32 colMin = inCluster.colMin(); - //============================================================================= - // Description: - /// not equality operator - /// - /// @param rhs - /// - /// @return bool - /// - bool operator!=(const Centroid& rhs) const noexcept + for (auto& pixel : inCluster) { - return !(*this == rhs); + clusterArray(pixel.row - rowMin, pixel.col - colMin) = pixel.intensity; } - //============================================================================= - // Description: - /// less than operator for std::sort algorithm; - /// NOTE: std::sort sorts in ascending order. Since I want to sort - /// the centroids in descensing order, I am purposefully defining - /// this operator backwards! - /// - /// @param rhs - /// - /// @return bool - /// - bool operator<(const Centroid& rhs) const noexcept - { - return intensity_ < rhs.intensity_ ? false : true; - } + const auto rowCol = nc::centerOfMass(clusterArray); + row_ = rowCol.front() + rowMin; + col_ = rowCol.back() + colMin; + } + + //============================================================================= + // Description: + /// Sets the cluster ellipse properties + /// + /// @param inCluster + /// + void setEllipseProperties(const Cluster& inCluster) noexcept + { + constexpr auto two = static_cast(2.); - //============================================================================= - // Description: - /// ostream operator - /// - /// @param inStream - /// @param inCentriod - /// @return std::ostream - /// - friend std::ostream& operator<<(std::ostream& inStream, const Centroid& inCentriod) - { - inStream << inCentriod.str(); - return inStream; - } + auto m20 = static_cast(0.); + auto m02 = static_cast(0.); + auto m11 = static_cast(0.); - private: - //==================================Attributes================================/// - double row_{ 0. }; - double col_{ 0. }; - accumulator_t intensity_{ 0 }; - double eod_{ 0. }; - /// The ellipse semi-major axis a - double a_; - /// The ellipse semi-minor axis b - double b_; - /// The centriod eccentricity - double eccentricity_; - /// The centriod ellipse orientation in radians. Measured counter-clockwise from +x axis - double orientation_; - - //============================================================================= - // Description: - /// center of mass algorithm; - /// WARNING: if both positive and negative values are present in the cluster, - /// it can lead to an undefined COM. - /// - /// @param inCluster - /// - void centerOfMass(const Cluster& inCluster) + for (typename Cluster::const_iterator iter = inCluster.begin(); iter != inCluster.end(); ++iter) { - const Shape clusterShape(inCluster.height(), inCluster.width()); - NdArray clusterArray(clusterShape); - clusterArray.zeros(); - - const uint32 rowMin = inCluster.rowMin(); - const uint32 colMin = inCluster.colMin(); + const auto& pixel = *iter; + const double deltaX = pixel.col - col_; + const double deltaY = pixel.row - row_; - for (auto& pixel : inCluster) - { - clusterArray(pixel.row - rowMin, pixel.col - colMin) = pixel.intensity; - } - - const auto rowCol = nc::centerOfMass(clusterArray); - row_ = rowCol.front() + rowMin; - col_ = rowCol.back() + colMin; + m11 += deltaX * deltaY; + m20 += utils::sqr(deltaX); + m02 += utils::sqr(deltaY); } - //============================================================================= - // Description: - /// Sets the cluster ellipse properties - /// - /// @param inCluster - /// - void setEllipseProperties(const Cluster& inCluster) noexcept - { - constexpr double two = static_cast(2.); - - double m20 = static_cast(0.); - double m02 = static_cast(0.); - double m11 = static_cast(0.); - - for (typename Cluster::const_iterator iter = inCluster.begin(); iter != inCluster.end(); ++iter) - { - const auto& pixel = *iter; - const double deltaX = pixel.col - col_; - const double deltaY = pixel.row - row_; + const double numPixels = static_cast(inCluster.size()); + m11 /= numPixels; + m20 /= numPixels; + m02 /= numPixels; - m11 += deltaX * deltaY; - m20 += utils::sqr(deltaX); - m02 += utils::sqr(deltaY); - } + double piece1 = m20 + m02; + piece1 /= two; - const double numPixels = static_cast(inCluster.size()); - m11 /= numPixels; - m20 /= numPixels; - m02 /= numPixels; + double piece2 = std::sqrt(static_cast(4.) * utils::sqr(m11) + utils::sqr(m20 - m02)); + piece2 /= two; - double piece1 = m20 + m02; - piece1 /= two; + const double lambda1 = piece1 - piece2; + const double lambda2 = piece1 + piece2; - double piece2 = std::sqrt(static_cast(4.) * utils::sqr(m11) + utils::sqr(m20 - m02)); - piece2 /= two; - - const double lambda1 = piece1 - piece2; - const double lambda2 = piece1 + piece2; - - eccentricity_ = std::sqrt(static_cast(1.) - lambda1 / lambda2); - orientation_ = static_cast(-0.5) * std::atan2(two * m11, m20 - m02); - a_ = two * std::sqrt(lambda2); - b_ = two * std::sqrt(lambda1); - } - }; - } // namespace imageProcessing -} // namespace nc + eccentricity_ = std::sqrt(static_cast(1.) - lambda1 / lambda2); + orientation_ = static_cast(-0.5) * std::atan2(two * m11, m20 - m02); + a_ = two * std::sqrt(lambda2); + b_ = two * std::sqrt(lambda1); + } + }; +} // namespace nc::imageProcessing diff --git a/include/NumCpp/ImageProcessing/Cluster.hpp b/include/NumCpp/ImageProcessing/Cluster.hpp index 2abb75831..8c2eeba93 100644 --- a/include/NumCpp/ImageProcessing/Cluster.hpp +++ b/include/NumCpp/ImageProcessing/Cluster.hpp @@ -43,332 +43,329 @@ #include "NumCpp/ImageProcessing/Pixel.hpp" #include "NumCpp/Utils/num2str.hpp" -namespace nc +namespace nc::imageProcessing { - namespace imageProcessing + //================================================================================ + // Class Description: + /// Holds the information for a cluster of pixels + template + class Cluster { - //================================================================================ - // Class Description: - /// Holds the information for a cluster of pixels - template - class Cluster + private: + STATIC_ASSERT_ARITHMETIC(dtype); + + public: + //================================Typedefs=============================== + using const_iterator = typename std::vector>::const_iterator; + using accumulator_t = typename std::conditional::value, int64, double>::type; + + //============================================================================= + // Description: + /// default constructor needed by containers + /// + Cluster() = default; + + //============================================================================= + // Description: + /// constructor + /// + /// @param inClusterId + /// + explicit Cluster(uint32 inClusterId) noexcept : + clusterId_(inClusterId) { - private: - STATIC_ASSERT_ARITHMETIC(dtype); - - public: - //================================Typedefs=============================== - using const_iterator = typename std::vector>::const_iterator; - using accumulator_t = typename std::conditional::value, int64, double>::type; - - //============================================================================= - // Description: - /// default constructor needed by containers - /// - Cluster() = default; - - //============================================================================= - // Description: - /// constructor - /// - /// @param inClusterId - /// - explicit Cluster(uint32 inClusterId) noexcept : - clusterId_(inClusterId) - { - } - - //============================================================================= - // Description: - /// equality operator - /// - /// @param rhs - /// - /// @return bool - /// - bool operator==(const Cluster& rhs) const noexcept - { - if (pixels_.size() != rhs.pixels_.size()) - { - return false; - } - - return stl_algorithms::equal(begin(), end(), rhs.begin()); - } - - //============================================================================= - // Description: - /// not equality operator - /// - /// @param rhs - /// - /// @return bool - /// - bool operator!=(const Cluster& rhs) const noexcept - { - return !(*this == rhs); - } - - //============================================================================= - // Description: - /// access operator, no bounds checking - /// - /// @param inIndex - /// - /// @return Pixel - /// - const Pixel& operator[](uint32 inIndex) const noexcept - { - return pixels_[inIndex]; - } - - //============================================================================= - // Description: - /// access method with bounds checking - /// - /// @param inIndex - /// - /// @return Pixel - /// - const Pixel& at(uint32 inIndex) const - { - if (inIndex >= pixels_.size()) - { - THROW_INVALID_ARGUMENT_ERROR("index exceeds cluster size."); - } - return pixels_[inIndex]; - } - - //============================================================================= - // Description: - /// returns in iterator to the beginning pixel of the cluster - /// - /// @return const_iterator - /// - const_iterator begin() const noexcept - { - return pixels_.cbegin(); - } - - //============================================================================= - // Description: - /// returns in iterator to the 1 past the end pixel of the cluster - /// - /// @return const_iterator - /// - const_iterator end() const noexcept - { - return pixels_.cend(); - } - - //============================================================================= - // Description: - /// returns the number of pixels in the cluster - /// - /// @return number of pixels in the cluster - /// - uint32 size() const noexcept - { - return static_cast(pixels_.size()); - } - - //============================================================================= - // Description: - /// returns the minimum row number of the cluster - /// - /// @return minimum row number of the cluster - /// - uint32 clusterId() const noexcept - { - return clusterId_; - } - - //============================================================================= - // Description: - /// returns the minimum row number of the cluster - /// - /// @return minimum row number of the cluster - /// - uint32 rowMin() const noexcept - { - return rowMin_; - } - - //============================================================================= - // Description: - /// returns the maximum row number of the cluster - /// - /// @return maximum row number of the cluster - /// - uint32 rowMax() const noexcept - { - return rowMax_; - } - - //============================================================================= - // Description: - /// returns the minimum column number of the cluster - /// - /// @return minimum column number of the cluster - /// - uint32 colMin() const noexcept - { - return colMin_; - } - - //============================================================================= - // Description: - /// returns the maximum column number of the cluster - /// - /// @return maximum column number of the cluster - /// - uint32 colMax() const noexcept - { - return colMax_; - } - - //============================================================================= - // Description: - /// returns the number of rows the cluster spans - /// - /// @return number of rows - /// - uint32 height() const noexcept - { - return rowMax_ - rowMin_ + 1; - } - - //============================================================================= - // Description: - /// returns the number of columns the cluster spans - /// - /// @return number of columns - /// - uint32 width() const noexcept - { - return colMax_ - colMin_ + 1; - } - - //============================================================================= - // Description: - /// returns the summed intensity of the cluster - /// - /// @return summed cluster intensity - /// - accumulator_t intensity() const noexcept - { - return intensity_; - } - - //============================================================================= - // Description: - /// returns the intensity of the peak pixel in the cluster - /// - /// @return peak pixel intensity - /// - dtype peakPixelIntensity() const noexcept - { - return peakPixelIntensity_; - } - - //============================================================================= - // Description: - /// returns the cluster estimated energy on detector (EOD) - /// - /// @return eod - /// - double eod() const noexcept - { - return eod_; - } - - //============================================================================= - // Description: - /// adds a pixel to the cluster - /// - /// @param inPixel - /// - void addPixel(const Pixel& inPixel) - { - pixels_.push_back(inPixel); - intensity_ += static_cast(inPixel.intensity); - - // adjust the cluster bounds - rowMin_ = std::min(rowMin_, inPixel.row); - rowMax_ = std::max(rowMax_, inPixel.row); - colMin_ = std::min(colMin_, inPixel.col); - colMax_ = std::max(colMax_, inPixel.col); - peakPixelIntensity_ = std::max(peakPixelIntensity_, inPixel.intensity); - - // calculate the energy on detector estimate - eod_ = static_cast(peakPixelIntensity_) / static_cast(intensity_); - } - - //============================================================================= - // Description: - /// returns a string representation of the cluster - /// - /// @return string - /// - std::string str() const - { - std::string out; - uint32 counter = 0; - std::for_each(begin(), - end(), - [&](const Pixel& pixel) - { out += "Pixel " + utils::num2str(counter++) + ":" + pixel.str(); }); - - return out; - } - - //============================================================================ - /// Method Description: - /// prints the Cluster object to the console - /// - void print() const + } + + //============================================================================= + // Description: + /// equality operator + /// + /// @param rhs + /// + /// @return bool + /// + bool operator==(const Cluster& rhs) const noexcept + { + if (pixels_.size() != rhs.pixels_.size()) { - std::cout << *this; + return false; } - //============================================================================= - // Description: - /// osstream operator - /// - /// @param inStream - /// @param inCluster - /// @return std::ostream - /// - friend std::ostream& operator<<(std::ostream& inStream, const Cluster& inCluster) + return stl_algorithms::equal(begin(), end(), rhs.begin()); + } + + //============================================================================= + // Description: + /// not equality operator + /// + /// @param rhs + /// + /// @return bool + /// + bool operator!=(const Cluster& rhs) const noexcept + { + return !(*this == rhs); + } + + //============================================================================= + // Description: + /// access operator, no bounds checking + /// + /// @param inIndex + /// + /// @return Pixel + /// + const Pixel& operator[](uint32 inIndex) const noexcept + { + return pixels_[inIndex]; + } + + //============================================================================= + // Description: + /// access method with bounds checking + /// + /// @param inIndex + /// + /// @return Pixel + /// + [[nodiscard]] const Pixel& at(uint32 inIndex) const + { + if (inIndex >= pixels_.size()) { - inStream << inCluster.str(); - return inStream; + THROW_INVALID_ARGUMENT_ERROR("index exceeds cluster size."); } - - private: - //================================Attributes=============================== - /// The cluster id - int32 clusterId_{ -1 }; - /// The pixels that make up the cluster - std::vector> pixels_{}; - /// The bounding box minimum row of the cluster. - uint32 rowMin_{ std::numeric_limits::max() }; // largest possible number - /// The bounding box maximum row of the cluster. - uint32 rowMax_{ 0 }; - /// The bounding box minimum col of the cluster. - uint32 colMin_{ std::numeric_limits::max() }; // largest possible number - /// The bounding box maximum row of the cluster. - uint32 colMax_{ 0 }; - /// The total summed intensity of the pixels in the cluster. - accumulator_t intensity_{ 0 }; - /// The peak pixel intensity of the cluster - dtype peakPixelIntensity_{ 0 }; - /// The minimum pixel count value of the cluster - dtype minPixel; - /// The maximum pixel count value of the cluster - dtype maxPixel; - /// The cluster energy on detector - double eod_{ 1. }; - }; - } // namespace imageProcessing -} // namespace nc + return pixels_[inIndex]; + } + + //============================================================================= + // Description: + /// returns in iterator to the beginning pixel of the cluster + /// + /// @return const_iterator + /// + [[nodiscard]] const_iterator begin() const noexcept + { + return pixels_.cbegin(); + } + + //============================================================================= + // Description: + /// returns in iterator to the 1 past the end pixel of the cluster + /// + /// @return const_iterator + /// + [[nodiscard]] const_iterator end() const noexcept + { + return pixels_.cend(); + } + + //============================================================================= + // Description: + /// returns the number of pixels in the cluster + /// + /// @return number of pixels in the cluster + /// + [[nodiscard]] uint32 size() const noexcept + { + return static_cast(pixels_.size()); + } + + //============================================================================= + // Description: + /// returns the minimum row number of the cluster + /// + /// @return minimum row number of the cluster + /// + [[nodiscard]] uint32 clusterId() const noexcept + { + return clusterId_; + } + + //============================================================================= + // Description: + /// returns the minimum row number of the cluster + /// + /// @return minimum row number of the cluster + /// + [[nodiscard]] uint32 rowMin() const noexcept + { + return rowMin_; + } + + //============================================================================= + // Description: + /// returns the maximum row number of the cluster + /// + /// @return maximum row number of the cluster + /// + [[nodiscard]] uint32 rowMax() const noexcept + { + return rowMax_; + } + + //============================================================================= + // Description: + /// returns the minimum column number of the cluster + /// + /// @return minimum column number of the cluster + /// + [[nodiscard]] uint32 colMin() const noexcept + { + return colMin_; + } + + //============================================================================= + // Description: + /// returns the maximum column number of the cluster + /// + /// @return maximum column number of the cluster + /// + [[nodiscard]] uint32 colMax() const noexcept + { + return colMax_; + } + + //============================================================================= + // Description: + /// returns the number of rows the cluster spans + /// + /// @return number of rows + /// + [[nodiscard]] uint32 height() const noexcept + { + return rowMax_ - rowMin_ + 1; + } + + //============================================================================= + // Description: + /// returns the number of columns the cluster spans + /// + /// @return number of columns + /// + [[nodiscard]] uint32 width() const noexcept + { + return colMax_ - colMin_ + 1; + } + + //============================================================================= + // Description: + /// returns the summed intensity of the cluster + /// + /// @return summed cluster intensity + /// + [[nodiscard]] accumulator_t intensity() const noexcept + { + return intensity_; + } + + //============================================================================= + // Description: + /// returns the intensity of the peak pixel in the cluster + /// + /// @return peak pixel intensity + /// + [[nodiscard]] dtype peakPixelIntensity() const noexcept + { + return peakPixelIntensity_; + } + + //============================================================================= + // Description: + /// returns the cluster estimated energy on detector (EOD) + /// + /// @return eod + /// + [[nodiscard]] double eod() const noexcept + { + return eod_; + } + + //============================================================================= + // Description: + /// adds a pixel to the cluster + /// + /// @param inPixel + /// + void addPixel(const Pixel& inPixel) + { + pixels_.push_back(inPixel); + intensity_ += static_cast(inPixel.intensity); + + // adjust the cluster bounds + rowMin_ = std::min(rowMin_, inPixel.row); + rowMax_ = std::max(rowMax_, inPixel.row); + colMin_ = std::min(colMin_, inPixel.col); + colMax_ = std::max(colMax_, inPixel.col); + peakPixelIntensity_ = std::max(peakPixelIntensity_, inPixel.intensity); + + // calculate the energy on detector estimate + eod_ = static_cast(peakPixelIntensity_) / static_cast(intensity_); + } + + //============================================================================= + // Description: + /// returns a string representation of the cluster + /// + /// @return string + /// + [[nodiscard]] std::string str() const + { + std::string out; + uint32 counter = 0; + std::for_each(begin(), + end(), + [&](const Pixel& pixel) + { out += "Pixel " + utils::num2str(counter++) + ":" + pixel.str(); }); + + return out; + } + + //============================================================================ + /// Method Description: + /// prints the Cluster object to the console + /// + void print() const + { + std::cout << *this; + } + + //============================================================================= + // Description: + /// osstream operator + /// + /// @param inStream + /// @param inCluster + /// @return std::ostream + /// + friend std::ostream& operator<<(std::ostream& inStream, const Cluster& inCluster) + { + inStream << inCluster.str(); + return inStream; + } + + private: + //================================Attributes=============================== + /// The cluster id + int32 clusterId_{ -1 }; + /// The pixels that make up the cluster + std::vector> pixels_{}; + /// The bounding box minimum row of the cluster. + uint32 rowMin_{ std::numeric_limits::max() }; // largest possible number + /// The bounding box maximum row of the cluster. + uint32 rowMax_{ 0 }; + /// The bounding box minimum col of the cluster. + uint32 colMin_{ std::numeric_limits::max() }; // largest possible number + /// The bounding box maximum row of the cluster. + uint32 colMax_{ 0 }; + /// The total summed intensity of the pixels in the cluster. + accumulator_t intensity_{ 0 }; + /// The peak pixel intensity of the cluster + dtype peakPixelIntensity_{ 0 }; + /// The minimum pixel count value of the cluster + dtype minPixel{}; + /// The maximum pixel count value of the cluster + dtype maxPixel{}; + /// The cluster energy on detector + double eod_{ 1. }; + }; +} // namespace nc::imageProcessing diff --git a/include/NumCpp/ImageProcessing/ClusterMaker.hpp b/include/NumCpp/ImageProcessing/ClusterMaker.hpp index ce5d920e8..7e9af0b42 100644 --- a/include/NumCpp/ImageProcessing/ClusterMaker.hpp +++ b/include/NumCpp/ImageProcessing/ClusterMaker.hpp @@ -41,329 +41,325 @@ #include "NumCpp/ImageProcessing/Cluster.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::imageProcessing { - namespace imageProcessing + //============================================================================= + // Class Description: + /// Clusters exceedance data into contiguous groups + template + class ClusterMaker { + private: + STATIC_ASSERT_ARITHMETIC(dtype); + + public: + //================================Typedefs===================================== + using const_iterator = typename std::vector>::const_iterator; + //============================================================================= - // Class Description: - /// Clusters exceedance data into contiguous groups - template - class ClusterMaker + // Description: + /// constructor + /// + /// @param inXcdArrayPtr: pointer to exceedance array + /// @param inIntensityArrayPtr: pointer to intensity array + /// @param inBorderWidth: border to apply around exceedance pixels post clustering (default 0) + /// + /// @return None + /// + ClusterMaker(const NdArray* const inXcdArrayPtr, + const NdArray* const inIntensityArrayPtr, + uint8 inBorderWidth = 0) : + xcds_(inXcdArrayPtr), + intensities_(inIntensityArrayPtr) { - private: - STATIC_ASSERT_ARITHMETIC(dtype); - - public: - //================================Typedefs===================================== - using const_iterator = typename std::vector>::const_iterator; - - //============================================================================= - // Description: - /// constructor - /// - /// @param inXcdArrayPtr: pointer to exceedance array - /// @param inIntensityArrayPtr: pointer to intensity array - /// @param inBorderWidth: border to apply around exceedance pixels post clustering (default 0) - /// - /// @return None - /// - ClusterMaker(const NdArray* const inXcdArrayPtr, - const NdArray* const inIntensityArrayPtr, - uint8 inBorderWidth = 0) : - xcds_(inXcdArrayPtr), - intensities_(inIntensityArrayPtr) + if (xcds_->shape() != intensities_->shape()) { - if (xcds_->shape() != intensities_->shape()) - { - THROW_INVALID_ARGUMENT_ERROR("input xcd and intensity arrays must be the same shape."); - } + THROW_INVALID_ARGUMENT_ERROR("input xcd and intensity arrays must be the same shape."); + } - shape_ = xcds_->shape(); + shape_ = xcds_->shape(); - // convert the NdArray of booleans to a vector of exceedances - for (uint32 row = 0; row < shape_.rows; ++row) + // convert the NdArray of booleans to a vector of exceedances + for (uint32 row = 0; row < shape_.rows; ++row) + { + for (uint32 col = 0; col < shape_.cols; ++col) { - for (uint32 col = 0; col < shape_.cols; ++col) + if (xcds_->operator()(row, col)) { - if (xcds_->operator()(row, col)) - { - const Pixel thePixel(row, col, intensities_->operator()(row, col)); - xcdsVec_.push_back(thePixel); - } + const Pixel thePixel(row, col, intensities_->operator()(row, col)); + xcdsVec_.push_back(thePixel); } } - - runClusterMaker(); - - for (uint8 i = 0; i < inBorderWidth; ++i) - { - expandClusters(); - } } - //============================================================================= - // Description: - /// returns the number of clusters in the frame - /// - /// @return number of clusters - /// - uint32 size() noexcept - { - return static_cast(clusters_.size()); - } + runClusterMaker(); - //============================================================================= - // Description: - /// access operator, no bounds checking - /// - /// @param inIndex - /// - /// @return Cluster - /// - const Cluster& operator[](uint32 inIndex) const noexcept + for (uint8 i = 0; i < inBorderWidth; ++i) { - return clusters_[inIndex]; + expandClusters(); } + } - //============================================================================= - // Description: - /// access method with bounds checking - /// - /// @param inIndex - /// - /// @return Cluster - /// - const Cluster& at(uint32 inIndex) const - { - if (inIndex >= clusters_.size()) - { - THROW_INVALID_ARGUMENT_ERROR("index exceeds cluster size."); - } - return clusters_[inIndex]; - } + //============================================================================= + // Description: + /// returns the number of clusters in the frame + /// + /// @return number of clusters + /// + uint32 size() noexcept + { + return static_cast(clusters_.size()); + } - //============================================================================= - // Description: - /// returns in iterator to the beginning cluster of the container - /// - /// @return const_iterator - /// - const_iterator begin() const noexcept - { - return clusters_.cbegin(); - } + //============================================================================= + // Description: + /// access operator, no bounds checking + /// + /// @param inIndex + /// + /// @return Cluster + /// + const Cluster& operator[](uint32 inIndex) const noexcept + { + return clusters_[inIndex]; + } - //============================================================================= - // Description: - /// returns in iterator to the 1 past the end cluster of the container - /// - /// @return const_iterator - /// - const_iterator end() const noexcept + //============================================================================= + // Description: + /// access method with bounds checking + /// + /// @param inIndex + /// + /// @return Cluster + /// + [[nodiscard]] const Cluster& at(uint32 inIndex) const + { + if (inIndex >= clusters_.size()) { - return clusters_.cend(); + THROW_INVALID_ARGUMENT_ERROR("index exceeds cluster size."); } + return clusters_[inIndex]; + } - private: - //==================================Attributes================================= - const NdArray* const xcds_; - const NdArray* const intensities_; - std::vector> xcdsVec_{}; - - Shape shape_{}; - - std::vector> clusters_{}; - - //============================================================================= - // Description: - /// checks that the input row and column have not fallen off of the edge - /// - /// @param inRow - /// @param inCol - /// - /// @return returns a pixel object clipped to the image boundaries - /// - Pixel makePixel(int32 inRow, int32 inCol) noexcept - { - // Make sure that on the edges after i've added or subtracted 1 from the row and col that - // i haven't gone over the edge - const uint32 row = std::min(static_cast(std::max(inRow, 0)), shape_.rows - 1); - const uint32 col = std::min(static_cast(std::max(inCol, 0)), shape_.cols - 1); - const dtype intensity = intensities_->operator()(row, col); + //============================================================================= + // Description: + /// returns in iterator to the beginning cluster of the container + /// + /// @return const_iterator + /// + [[nodiscard]] const_iterator begin() const noexcept + { + return clusters_.cbegin(); + } - return Pixel(row, col, intensity); - } + //============================================================================= + // Description: + /// returns in iterator to the 1 past the end cluster of the container + /// + /// @return const_iterator + /// + [[nodiscard]] const_iterator end() const noexcept + { + return clusters_.cend(); + } - //============================================================================= - // Description: - /// finds all of the neighboring pixels to the input pixel - /// - /// @param inPixel - /// @param outNeighbors - /// @return None - /// - void findNeighbors(const Pixel& inPixel, std::set>& outNeighbors) - { - // using a set will auto take care of adding duplicate pixels on the edges - - // the 8 surrounding neighbors - const auto row = static_cast(inPixel.row); - const auto col = static_cast(inPixel.col); - - outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col - 1)); - outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col)); - outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col + 1)); - outNeighbors.insert(outNeighbors.end(), makePixel(row, col - 1)); - outNeighbors.insert(outNeighbors.end(), makePixel(row, col + 1)); - outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col - 1)); - outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col)); - outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col + 1)); - } + private: + //==================================Attributes================================= + const NdArray* const xcds_{}; + const NdArray* const intensities_{}; + std::vector> xcdsVec_{}; - //============================================================================= - // Description: - /// finds all of the neighboring pixels to the input pixel that are NOT exceedances - /// - /// @param inPixel - /// @param outNeighbors - /// - /// @return vector of non exceedance neighboring pixels - /// - void findNeighborNotXcds(const Pixel& inPixel, std::vector>& outNeighbors) - { - std::set> neighbors; - findNeighbors(inPixel, neighbors); + Shape shape_{}; - // check if the neighboring pixels are exceedances and insert into the xcd vector - for (auto& pixel : neighbors) - { - if (!xcds_->operator()(pixel.row, pixel.col)) - { - outNeighbors.push_back(pixel); - } - } - } + std::vector> clusters_{}; - //============================================================================= - // Description: - /// finds the pixel index of neighboring pixels - /// - /// @param inPixel - /// @param outNeighbors - /// - /// @return vector of neighboring pixel indices - /// - void findNeighborXcds(const Pixel& inPixel, std::vector& outNeighbors) - { - std::set> neighbors; - findNeighbors(inPixel, neighbors); - std::vector> neighborXcds; + //============================================================================= + // Description: + /// checks that the input row and column have not fallen off of the edge + /// + /// @param inRow + /// @param inCol + /// + /// @return returns a pixel object clipped to the image boundaries + /// + Pixel makePixel(int32 inRow, int32 inCol) noexcept + { + // Make sure that on the edges after i've added or subtracted 1 from the row and col that + // i haven't gone over the edge + const uint32 row = std::min(static_cast(std::max(inRow, 0)), shape_.rows - 1); + const uint32 col = std::min(static_cast(std::max(inCol, 0)), shape_.cols - 1); + const dtype intensity = intensities_->operator()(row, col); + + return Pixel(row, col, intensity); + } + + //============================================================================= + // Description: + /// finds all of the neighboring pixels to the input pixel + /// + /// @param inPixel + /// @param outNeighbors + /// @return None + /// + void findNeighbors(const Pixel& inPixel, std::set>& outNeighbors) + { + // using a set will auto take care of adding duplicate pixels on the edges + + // the 8 surrounding neighbors + const auto row = static_cast(inPixel.row); + const auto col = static_cast(inPixel.col); + + outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col - 1)); + outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col)); + outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col + 1)); + outNeighbors.insert(outNeighbors.end(), makePixel(row, col - 1)); + outNeighbors.insert(outNeighbors.end(), makePixel(row, col + 1)); + outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col - 1)); + outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col)); + outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col + 1)); + } - // check if the neighboring pixels are exceedances and insert into the xcd vector - for (auto& pixel : neighbors) + //============================================================================= + // Description: + /// finds all of the neighboring pixels to the input pixel that are NOT exceedances + /// + /// @param inPixel + /// @param outNeighbors + /// + /// @return vector of non exceedance neighboring pixels + /// + void findNeighborNotXcds(const Pixel& inPixel, std::vector>& outNeighbors) + { + std::set> neighbors; + findNeighbors(inPixel, neighbors); + + // check if the neighboring pixels are exceedances and insert into the xcd vector + for (auto& pixel : neighbors) + { + if (!xcds_->operator()(pixel.row, pixel.col)) { - if (xcds_->operator()(pixel.row, pixel.col)) - { - neighborXcds.push_back(pixel); - } + outNeighbors.push_back(pixel); } + } + } - // loop through the neighbors and find the cooresponding index into exceedances_ - for (auto& pixel : neighborXcds) + //============================================================================= + // Description: + /// finds the pixel index of neighboring pixels + /// + /// @param inPixel + /// @param outNeighbors + /// + /// @return vector of neighboring pixel indices + /// + void findNeighborXcds(const Pixel& inPixel, std::vector& outNeighbors) + { + std::set> neighbors; + findNeighbors(inPixel, neighbors); + std::vector> neighborXcds; + + // check if the neighboring pixels are exceedances and insert into the xcd vector + for (auto& pixel : neighbors) + { + if (xcds_->operator()(pixel.row, pixel.col)) { - auto theExceedanceIter = std::find(xcdsVec_.begin(), xcdsVec_.end(), pixel); - outNeighbors.push_back(static_cast(theExceedanceIter - xcdsVec_.begin())); + neighborXcds.push_back(pixel); } } - //============================================================================= - // Description: - /// workhorse method that performs the clustering algorithm - /// - void runClusterMaker() + // loop through the neighbors and find the cooresponding index into exceedances_ + for (auto& pixel : neighborXcds) { - uint32 clusterId = 0; + auto theExceedanceIter = std::find(xcdsVec_.begin(), xcdsVec_.end(), pixel); + outNeighbors.push_back(static_cast(theExceedanceIter - xcdsVec_.begin())); + } + } - for (auto& currentPixel : xcdsVec_) + //============================================================================= + // Description: + /// workhorse method that performs the clustering algorithm + /// + void runClusterMaker() + { + uint32 clusterId = 0; + + for (auto& currentPixel : xcdsVec_) + { + // not already visited + if (currentPixel.clusterId == -1) { - // not already visited - if (currentPixel.clusterId == -1) + Cluster newCluster(clusterId); // a new cluster + currentPixel.clusterId = clusterId; + newCluster.addPixel(currentPixel); // assign pixel to cluster + + // get the neighbors + std::vector neighborIds; + findNeighborXcds(currentPixel, neighborIds); + if (neighborIds.empty()) { - Cluster newCluster(clusterId); // a new cluster - currentPixel.clusterId = clusterId; - newCluster.addPixel(currentPixel); // assign pixel to cluster - - // get the neighbors - std::vector neighborIds; - findNeighborXcds(currentPixel, neighborIds); - if (neighborIds.empty()) - { - clusters_.push_back(newCluster); - ++clusterId; - continue; - } - - // loop through the neighbors - for (uint32 neighborsIdx = 0; neighborsIdx < neighborIds.size(); ++neighborsIdx) - { - Pixel& currentNeighborPixel = xcdsVec_[neighborIds[neighborsIdx]]; + clusters_.push_back(newCluster); + ++clusterId; + continue; + } - // go to neighbors - std::vector newNeighborIds; - findNeighborXcds(currentNeighborPixel, newNeighborIds); + // loop through the neighbors + for (uint32 neighborsIdx = 0; neighborsIdx < neighborIds.size(); ++neighborsIdx) + { + Pixel& currentNeighborPixel = xcdsVec_[neighborIds[neighborsIdx]]; - // loop through the new neighbors and add them to neighbors - for (auto newNeighborId : newNeighborIds) - { - // not already in neighbors - if (std::find(neighborIds.begin(), neighborIds.end(), newNeighborId) == - neighborIds.end()) - { - neighborIds.push_back(newNeighborId); - } - } + // go to neighbors + std::vector newNeighborIds; + findNeighborXcds(currentNeighborPixel, newNeighborIds); - // not already assigned to a cluster - if (currentNeighborPixel.clusterId == -1) + // loop through the new neighbors and add them to neighbors + for (auto newNeighborId : newNeighborIds) + { + // not already in neighbors + if (std::find(neighborIds.begin(), neighborIds.end(), newNeighborId) == neighborIds.end()) { - currentNeighborPixel.clusterId = clusterId; - newCluster.addPixel(currentNeighborPixel); + neighborIds.push_back(newNeighborId); } } - clusters_.push_back(std::move(newCluster)); - ++clusterId; + // not already assigned to a cluster + if (currentNeighborPixel.clusterId == -1) + { + currentNeighborPixel.clusterId = clusterId; + newCluster.addPixel(currentNeighborPixel); + } } + + clusters_.push_back(std::move(newCluster)); + ++clusterId; } } + } - //============================================================================= - // Description: - /// 3x3 dialates the clusters - /// - void expandClusters() + //============================================================================= + // Description: + /// 3x3 dialates the clusters + /// + void expandClusters() + { + // loop through the clusters + for (auto& theCluster : clusters_) { - // loop through the clusters - for (auto& theCluster : clusters_) + // loop through the pixels of the cluster + for (auto& thePixel : theCluster) { - // loop through the pixels of the cluster - for (auto& thePixel : theCluster) - { - std::vector> neighborsNotXcds; - findNeighborNotXcds(thePixel, neighborsNotXcds); + std::vector> neighborsNotXcds; + findNeighborNotXcds(thePixel, neighborsNotXcds); - // loop through the neighbors and if they haven't already been added to the cluster, add them - for (auto& newPixel : neighborsNotXcds) + // loop through the neighbors and if they haven't already been added to the cluster, add them + for (auto& newPixel : neighborsNotXcds) + { + if (std::find(theCluster.begin(), theCluster.end(), newPixel) == theCluster.end()) { - if (std::find(theCluster.begin(), theCluster.end(), newPixel) == theCluster.end()) - { - theCluster.addPixel(newPixel); - } + theCluster.addPixel(newPixel); } } } } - }; - } // namespace imageProcessing -} // namespace nc + } + }; +} // namespace nc::imageProcessing diff --git a/include/NumCpp/ImageProcessing/Pixel.hpp b/include/NumCpp/ImageProcessing/Pixel.hpp index ebe165269..2b22134a8 100644 --- a/include/NumCpp/ImageProcessing/Pixel.hpp +++ b/include/NumCpp/ImageProcessing/Pixel.hpp @@ -35,133 +35,130 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/Utils/num2str.hpp" -namespace nc +namespace nc::imageProcessing { - namespace imageProcessing + //================================================================================ + // Class Description: + /// Holds the information for a single pixel + template + class Pixel { - //================================================================================ - // Class Description: - /// Holds the information for a single pixel - template - class Pixel - { - private: - STATIC_ASSERT_ARITHMETIC(dtype); + private: + STATIC_ASSERT_ARITHMETIC(dtype); - public: - //==================================Attributes================================ - mutable int32 clusterId{ -1 }; - uint32 row{ 0 }; - uint32 col{ 0 }; - dtype intensity{ 0 }; + public: + //==================================Attributes================================ + mutable int32 clusterId{ -1 }; + uint32 row{ 0 }; + uint32 col{ 0 }; + dtype intensity{ 0 }; - //============================================================================= - // Description: - /// defualt constructor needed by containers - /// - constexpr Pixel() = default; + //============================================================================= + // Description: + /// defualt constructor needed by containers + /// + constexpr Pixel() = default; - //============================================================================= - // Description: - /// constructor - /// - /// @param inRow: pixel row - /// @param inCol: pixel column - /// @param inIntensity: pixel intensity - /// - constexpr Pixel(uint32 inRow, uint32 inCol, dtype inIntensity) noexcept : - row(inRow), - col(inCol), - intensity(inIntensity) - { - } + //============================================================================= + // Description: + /// constructor + /// + /// @param inRow: pixel row + /// @param inCol: pixel column + /// @param inIntensity: pixel intensity + /// + constexpr Pixel(uint32 inRow, uint32 inCol, dtype inIntensity) noexcept : + row(inRow), + col(inCol), + intensity(inIntensity) + { + } - //============================================================================= - // Description: - /// equality operator - /// - /// @param rhs - /// - /// @return bool - /// - constexpr bool operator==(const Pixel& rhs) const noexcept - { - return row == rhs.row && col == rhs.col && intensity == rhs.intensity; - } + //============================================================================= + // Description: + /// equality operator + /// + /// @param rhs + /// + /// @return bool + /// + constexpr bool operator==(const Pixel& rhs) const noexcept + { + return row == rhs.row && col == rhs.col && intensity == rhs.intensity; + } - //============================================================================= - // Description: - /// not equality operator - /// - /// @param rhs - /// - /// @return bool - /// - constexpr bool operator!=(const Pixel& rhs) const noexcept - { - return !(*this == rhs); - } + //============================================================================= + // Description: + /// not equality operator + /// + /// @param rhs + /// + /// @return bool + /// + constexpr bool operator!=(const Pixel& rhs) const noexcept + { + return !(*this == rhs); + } - //============================================================================= - // Description: - /// less than operator for std::sort algorithm and std::set<>; - /// NOTE: std::sort sorts in ascending order. Since I want to sort - /// the centroids in descensing order, I am purposefully defining - /// this operator backwards! - /// - /// @param rhs - /// - /// @return bool - /// - bool operator<(const Pixel& rhs) const noexcept + //============================================================================= + // Description: + /// less than operator for std::sort algorithm and std::set<>; + /// NOTE: std::sort sorts in ascending order. Since I want to sort + /// the centroids in descensing order, I am purposefully defining + /// this operator backwards! + /// + /// @param rhs + /// + /// @return bool + /// + bool operator<(const Pixel& rhs) const noexcept + { + if (row < rhs.row) { - if (row < rhs.row) - { - return true; - } - if (row == rhs.row) - { - return static_cast(col < rhs.col); - } - - return false; + return true; } - - //============================================================================= - // Description: - /// returns the pixel information as a string - /// - /// @return std::string - /// - std::string str() const + if (row == rhs.row) { - std::string out = "row = " + utils::num2str(row) + " col = " + utils::num2str(col); - out += " intensity = " + utils::num2str(intensity) + '\n'; - return out; + return static_cast(col < rhs.col); } - //============================================================================ - /// Method Description: - /// prints the Pixel object to the console - /// - void print() const - { - std::cout << *this; - } + return false; + } - //============================================================================= - // Description: - /// osstream operator - /// - /// @param inStream - /// @param inPixel - /// @return std::ostream - /// - friend std::ostream& operator<<(std::ostream& inStream, const Pixel& inPixel) - { - inStream << inPixel.str(); - return inStream; - } - }; - } // namespace imageProcessing -} // namespace nc + //============================================================================= + // Description: + /// returns the pixel information as a string + /// + /// @return std::string + /// + [[nodiscard]] std::string str() const + { + std::string out = "row = " + utils::num2str(row) + " col = " + utils::num2str(col); + out += " intensity = " + utils::num2str(intensity) + '\n'; + return out; + } + + //============================================================================ + /// Method Description: + /// prints the Pixel object to the console + /// + void print() const + { + std::cout << *this; + } + + //============================================================================= + // Description: + /// osstream operator + /// + /// @param inStream + /// @param inPixel + /// @return std::ostream + /// + friend std::ostream& operator<<(std::ostream& inStream, const Pixel& inPixel) + { + inStream << inPixel.str(); + return inStream; + } + }; +} // namespace nc::imageProcessing diff --git a/include/NumCpp/ImageProcessing/applyThreshold.hpp b/include/NumCpp/ImageProcessing/applyThreshold.hpp index 71d326022..769cbaf46 100644 --- a/include/NumCpp/ImageProcessing/applyThreshold.hpp +++ b/include/NumCpp/ImageProcessing/applyThreshold.hpp @@ -30,24 +30,21 @@ #include "NumCpp/Core/Internal/StaticAsserts.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::imageProcessing { - namespace imageProcessing + //============================================================================ + // Method Description: + /// Applies a threshold to an image + /// + /// @param inImageArray + /// @param inThreshold + /// @return NdArray of booleans of pixels that exceeded the threshold + /// + template + NdArray applyThreshold(const NdArray& inImageArray, dtype inThreshold) { - //============================================================================ - // Method Description: - /// Applies a threshold to an image - /// - /// @param inImageArray - /// @param inThreshold - /// @return NdArray of booleans of pixels that exceeded the threshold - /// - template - NdArray applyThreshold(const NdArray& inImageArray, dtype inThreshold) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return inImageArray > inThreshold; - } - } // namespace imageProcessing -} // namespace nc + return inImageArray > inThreshold; + } +} // namespace nc::imageProcessing diff --git a/include/NumCpp/ImageProcessing/centroidClusters.hpp b/include/NumCpp/ImageProcessing/centroidClusters.hpp index 0494d61f0..13267b2a5 100644 --- a/include/NumCpp/ImageProcessing/centroidClusters.hpp +++ b/include/NumCpp/ImageProcessing/centroidClusters.hpp @@ -35,31 +35,28 @@ #include "NumCpp/ImageProcessing/Centroid.hpp" #include "NumCpp/ImageProcessing/Cluster.hpp" -namespace nc +namespace nc::imageProcessing { - namespace imageProcessing + //============================================================================ + // Method Description: + /// Center of Mass centroids clusters + /// + /// @param inClusters + /// @return std::vector + /// + template + std::vector> centroidClusters(const std::vector>& inClusters) { - //============================================================================ - // Method Description: - /// Center of Mass centroids clusters - /// - /// @param inClusters - /// @return std::vector - /// - template - std::vector> centroidClusters(const std::vector>& inClusters) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - std::vector> centroids; + STATIC_ASSERT_ARITHMETIC(dtype); - centroids.reserve(inClusters.size()); - for (auto& cluster : inClusters) - { - centroids.emplace_back(cluster); - } + std::vector> centroids; - return centroids; + centroids.reserve(inClusters.size()); + for (auto& cluster : inClusters) + { + centroids.emplace_back(cluster); } - } // namespace imageProcessing -} // namespace nc + + return centroids; + } +} // namespace nc::imageProcessing diff --git a/include/NumCpp/ImageProcessing/clusterPixels.hpp b/include/NumCpp/ImageProcessing/clusterPixels.hpp index d833ce4ef..99c239978 100644 --- a/include/NumCpp/ImageProcessing/clusterPixels.hpp +++ b/include/NumCpp/ImageProcessing/clusterPixels.hpp @@ -36,28 +36,24 @@ #include "NumCpp/ImageProcessing/ClusterMaker.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::imageProcessing { - namespace imageProcessing + //============================================================================ + // Method Description: + /// Clusters exceedance pixels from an image + /// + /// @param inImageArray + /// @param inExceedances + /// @param inBorderWidth: border to apply around exceedance pixels post clustering (default 0) + /// @return std::vector + /// + template + std::vector> + clusterPixels(const NdArray& inImageArray, const NdArray& inExceedances, uint8 inBorderWidth = 0) { - //============================================================================ - // Method Description: - /// Clusters exceedance pixels from an image - /// - /// @param inImageArray - /// @param inExceedances - /// @param inBorderWidth: border to apply around exceedance pixels post clustering (default 0) - /// @return std::vector - /// - template - std::vector> clusterPixels(const NdArray& inImageArray, - const NdArray& inExceedances, - uint8 inBorderWidth = 0) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - ClusterMaker clusterMaker(&inExceedances, &inImageArray, inBorderWidth); - return std::vector>(clusterMaker.begin(), clusterMaker.end()); - } - } // namespace imageProcessing -} // namespace nc + ClusterMaker clusterMaker(&inExceedances, &inImageArray, inBorderWidth); + return std::vector>(clusterMaker.begin(), clusterMaker.end()); + } +} // namespace nc::imageProcessing diff --git a/include/NumCpp/ImageProcessing/generateCentroids.hpp b/include/NumCpp/ImageProcessing/generateCentroids.hpp index f058a8a1d..ec5157e5e 100644 --- a/include/NumCpp/ImageProcessing/generateCentroids.hpp +++ b/include/NumCpp/ImageProcessing/generateCentroids.hpp @@ -41,61 +41,58 @@ #include "NumCpp/ImageProcessing/windowExceedances.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::imageProcessing { - namespace imageProcessing + //============================================================================ + // Method Description: + /// Generates a list of centroids givin an input exceedance + /// rate + /// + /// @param inImageArray + /// @param inRate: exceedance rate + /// @param inWindowType: (string "pre", or "post" for where to apply the exceedance windowing) + /// @param inBorderWidth: border to apply (default 0) + /// @return std::vector + /// + template + std::vector> generateCentroids(const NdArray& inImageArray, + double inRate, + const std::string& inWindowType, + uint8 inBorderWidth = 0) { - //============================================================================ - // Method Description: - /// Generates a list of centroids givin an input exceedance - /// rate - /// - /// @param inImageArray - /// @param inRate: exceedance rate - /// @param inWindowType: (string "pre", or "post" for where to apply the exceedance windowing) - /// @param inBorderWidth: border to apply (default 0) - /// @return std::vector - /// - template - std::vector> generateCentroids(const NdArray& inImageArray, - double inRate, - const std::string& inWindowType, - uint8 inBorderWidth = 0) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - uint8 borderWidthPre = 0; - uint8 borderWidthPost = 0; - if (inWindowType == "pre") - { - borderWidthPre = inBorderWidth; - } - else if (inWindowType == "post") - { - borderWidthPost = inBorderWidth; - } - else - { - THROW_INVALID_ARGUMENT_ERROR("input window type options are ['pre', 'post']"); - } + uint8 borderWidthPre = 0; + uint8 borderWidthPost = 0; + if (inWindowType == "pre") + { + borderWidthPre = inBorderWidth; + } + else if (inWindowType == "post") + { + borderWidthPost = inBorderWidth; + } + else + { + THROW_INVALID_ARGUMENT_ERROR("input window type options are ['pre', 'post']"); + } - // generate the threshold - dtype threshold = generateThreshold(inImageArray, inRate); + // generate the threshold + dtype threshold = generateThreshold(inImageArray, inRate); - // apply the threshold to get xcds - NdArray xcds = applyThreshold(inImageArray, threshold); + // apply the threshold to get xcds + NdArray xcds = applyThreshold(inImageArray, threshold); - // window around the xcds - if (borderWidthPre > 0) - { - xcds = windowExceedances(xcds, borderWidthPre); - } + // window around the xcds + if (borderWidthPre > 0) + { + xcds = windowExceedances(xcds, borderWidthPre); + } - // cluster the exceedances - std::vector> clusters = clusterPixels(inImageArray, xcds, borderWidthPost); + // cluster the exceedances + std::vector> clusters = clusterPixels(inImageArray, xcds, borderWidthPost); - // centroid the clusters - return centroidClusters(clusters); - } - } // namespace imageProcessing -} // namespace nc + // centroid the clusters + return centroidClusters(clusters); + } +} // namespace nc::imageProcessing diff --git a/include/NumCpp/ImageProcessing/generateThreshold.hpp b/include/NumCpp/ImageProcessing/generateThreshold.hpp index 17608a826..1113b95d7 100644 --- a/include/NumCpp/ImageProcessing/generateThreshold.hpp +++ b/include/NumCpp/ImageProcessing/generateThreshold.hpp @@ -38,108 +38,105 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Utils/essentiallyEqual.hpp" -namespace nc +namespace nc::imageProcessing { - namespace imageProcessing + //============================================================================ + // Method Description: + /// Calculates a threshold such that the input rate of pixels + /// exceeds the threshold. Really should only be used for integer + /// input array values. If using floating point data, user beware... + /// + /// @param inImageArray + /// @param inRate + /// @return dtype + /// + template + dtype generateThreshold(const NdArray& inImageArray, double inRate) { - //============================================================================ - // Method Description: - /// Calculates a threshold such that the input rate of pixels - /// exceeds the threshold. Really should only be used for integer - /// input array values. If using floating point data, user beware... - /// - /// @param inImageArray - /// @param inRate - /// @return dtype - /// - template - dtype generateThreshold(const NdArray& inImageArray, double inRate) + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inRate < 0. || inRate > 1.) { - STATIC_ASSERT_ARITHMETIC(dtype); + THROW_INVALID_ARGUMENT_ERROR("input rate must be of the range [0, 1]"); + } - if (inRate < 0. || inRate > 1.) - { - THROW_INVALID_ARGUMENT_ERROR("input rate must be of the range [0, 1]"); - } + // first build a histogram + auto minValue = static_cast(std::floor(inImageArray.min().item())); + auto maxValue = static_cast(std::floor(inImageArray.max().item())); - // first build a histogram - auto minValue = static_cast(std::floor(inImageArray.min().item())); - auto maxValue = static_cast(std::floor(inImageArray.max().item())); + if (utils::essentiallyEqual(inRate, 0.)) + { + return static_cast(maxValue); + } - if (utils::essentiallyEqual(inRate, 0.)) + if (utils::essentiallyEqual(inRate, 1.)) + { + if (DtypeInfo::isSigned()) { - return static_cast(maxValue); + return static_cast(minValue - 1); } - if (utils::essentiallyEqual(inRate, 1.)) - { - if (DtypeInfo::isSigned()) - { - return static_cast(minValue - 1); - } + return dtype{ 0 }; + } - return dtype{ 0 }; - } + const auto histSize = static_cast(maxValue - minValue + 1); + + NdArray histogram(1, histSize); + histogram.zeros(); + for (auto intensity : inImageArray) + { + const auto bin = static_cast(static_cast(std::floor(intensity)) - minValue); + ++histogram[bin]; + } + + // integrate the normalized histogram from right to left to make a survival function (1 - CDF) + const auto dNumPixels = static_cast(inImageArray.size()); + NdArray survivalFunction(1, histSize + 1); + survivalFunction[-1] = 0.; + for (int32 i = histSize - 1; i > -1; --i) + { + double histValue = histogram[i] / dNumPixels; + survivalFunction[i] = survivalFunction[i + 1] + histValue; + } - const auto histSize = static_cast(maxValue - minValue + 1); + // binary search through the survival function to find the rate + uint32 indexLow = 0; + uint32 indexHigh = histSize - 1; + uint32 index = indexHigh / 2; // integer division - NdArray histogram(1, histSize); - histogram.zeros(); - for (auto intensity : inImageArray) + constexpr bool keepGoing = true; + while (keepGoing) + { + const double value = survivalFunction[index]; + if (value < inRate) { - const auto bin = static_cast(static_cast(std::floor(intensity)) - minValue); - ++histogram[bin]; + indexHigh = index; } - - // integrate the normalized histogram from right to left to make a survival function (1 - CDF) - const auto dNumPixels = static_cast(inImageArray.size()); - NdArray survivalFunction(1, histSize + 1); - survivalFunction[-1] = 0.; - for (int32 i = histSize - 1; i > -1; --i) + else if (value > inRate) { - double histValue = histogram[i] / dNumPixels; - survivalFunction[i] = survivalFunction[i + 1] + histValue; + indexLow = index; } - - // binary search through the survival function to find the rate - uint32 indexLow = 0; - uint32 indexHigh = histSize - 1; - uint32 index = indexHigh / 2; // integer division - - constexpr bool keepGoing = true; - while (keepGoing) + else { - const double value = survivalFunction[index]; - if (value < inRate) - { - indexHigh = index; - } - else if (value > inRate) - { - indexLow = index; - } - else + const int32 thresh = static_cast(index) + minValue - 1; + if (DtypeInfo::isSigned()) { - const int32 thresh = static_cast(index) + minValue - 1; - if (DtypeInfo::isSigned()) - { - return static_cast(thresh); - } - - return thresh < 0 ? 0 : static_cast(thresh); + return static_cast(thresh); } - if (indexHigh - indexLow < 2) - { - return static_cast(static_cast(indexHigh) + minValue - 1); - } + return thresh < 0 ? 0 : static_cast(thresh); + } - index = indexLow + (indexHigh - indexLow) / 2; + if (indexHigh - indexLow < 2) + { + return static_cast(static_cast(indexHigh) + minValue - 1); } - // shouldn't ever get here but stop the compiler from throwing a warning - return static_cast(histSize - 1); + index = indexLow + (indexHigh - indexLow) / 2; } - } // namespace imageProcessing -} // namespace nc + // shouldn't ever get here but stop the compiler from throwing a warning + return static_cast(histSize - 1); + } + +} // namespace nc::imageProcessing diff --git a/include/NumCpp/ImageProcessing/windowExceedances.hpp b/include/NumCpp/ImageProcessing/windowExceedances.hpp index 98ed19f0b..2ce9f50a7 100644 --- a/include/NumCpp/ImageProcessing/windowExceedances.hpp +++ b/include/NumCpp/ImageProcessing/windowExceedances.hpp @@ -34,48 +34,45 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::imageProcessing { - namespace imageProcessing + //============================================================================ + // Method Description: + /// Window expand around exceedance pixels + /// + /// @param inExceedances + /// @param inBorderWidth + /// @return NdArray + /// + inline NdArray windowExceedances(const NdArray& inExceedances, uint8 inBorderWidth) noexcept { - //============================================================================ - // Method Description: - /// Window expand around exceedance pixels - /// - /// @param inExceedances - /// @param inBorderWidth - /// @return NdArray - /// - inline NdArray windowExceedances(const NdArray& inExceedances, uint8 inBorderWidth) noexcept + // not the most efficient way to do things, but the easist... + NdArray xcds(inExceedances); + const Shape inShape = xcds.shape(); + for (uint8 border = 0; border < inBorderWidth; ++border) { - // not the most efficient way to do things, but the easist... - NdArray xcds(inExceedances); - const Shape inShape = xcds.shape(); - for (uint8 border = 0; border < inBorderWidth; ++border) + for (int32 row = 0; row < static_cast(inShape.rows); ++row) { - for (int32 row = 0; row < static_cast(inShape.rows); ++row) + for (int32 col = 0; col < static_cast(inShape.cols); ++col) { - for (int32 col = 0; col < static_cast(inShape.cols); ++col) + if (inExceedances(row, col)) { - if (inExceedances(row, col)) - { - xcds(std::max(row - 1, 0), std::max(col - 1, 0)) = true; - xcds(std::max(row - 1, 0), col) = true; - xcds(std::max(row - 1, 0), std::min(col + 1, inShape.cols - 1)) = true; + xcds(std::max(row - 1, 0), std::max(col - 1, 0)) = true; + xcds(std::max(row - 1, 0), col) = true; + xcds(std::max(row - 1, 0), std::min(col + 1, inShape.cols - 1)) = true; - xcds(row, std::max(col - 1, 0)) = true; - xcds(row, std::min(col + 1, inShape.cols - 1)) = true; + xcds(row, std::max(col - 1, 0)) = true; + xcds(row, std::min(col + 1, inShape.cols - 1)) = true; - xcds(std::min(row + 1, inShape.rows - 1), std::max(col - 1, 0)) = true; - xcds(std::min(row + 1, inShape.rows - 1), col) = true; - xcds(std::min(row + 1, inShape.rows - 1), - std::min(col + 1, inShape.cols - 1)) = true; - } + xcds(std::min(row + 1, inShape.rows - 1), std::max(col - 1, 0)) = true; + xcds(std::min(row + 1, inShape.rows - 1), col) = true; + xcds(std::min(row + 1, inShape.rows - 1), std::min(col + 1, inShape.cols - 1)) = + true; } } } - - return xcds; } - } // namespace imageProcessing -} // namespace nc + + return xcds; + } +} // namespace nc::imageProcessing diff --git a/include/NumCpp/Integrate/gauss_legendre.hpp b/include/NumCpp/Integrate/gauss_legendre.hpp index 769581062..dc5b5f1df 100644 --- a/include/NumCpp/Integrate/gauss_legendre.hpp +++ b/include/NumCpp/Integrate/gauss_legendre.hpp @@ -39,166 +39,163 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/Utils/sqr.hpp" -namespace nc +namespace nc::integrate { - namespace integrate + //============================================================================ + // Class Description: + /// Legendre Polynomial class + /// + class LegendrePolynomial { + public: //============================================================================ - // Class Description: - /// Legendre Polynomial class + // Method Description: + /// Constructor + /// + /// @param numIterations: the number of iterations to perform /// - class LegendrePolynomial + explicit LegendrePolynomial(const uint32 numIterations) noexcept : + numIterations_(numIterations), + weight_(numIterations + 1), + root_(numIterations + 1) { - public: - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param numIterations: the number of iterations to perform - /// - explicit LegendrePolynomial(const uint32 numIterations) noexcept : - numIterations_(numIterations), - weight_(numIterations + 1), - root_(numIterations + 1) - { - calculateWeightAndRoot(); - } + calculateWeightAndRoot(); + } - //============================================================================ - // Method Description: - /// Returns the weights vector - /// - /// @return weights vector - /// - const std::vector& getWeight() const noexcept - { - return weight_; - } + //============================================================================ + // Method Description: + /// Returns the weights vector + /// + /// @return weights vector + /// + [[nodiscard]] const std::vector& getWeight() const noexcept + { + return weight_; + } - //============================================================================ - // Method Description: - /// Returns the roots vector - /// - /// @return roots vector - /// - const std::vector& getRoot() const noexcept - { - return root_; - } + //============================================================================ + // Method Description: + /// Returns the roots vector + /// + /// @return roots vector + /// + [[nodiscard]] const std::vector& getRoot() const noexcept + { + return root_; + } - private: - //============================================================================ - // Class Description: - /// Simple class to hold the results - /// - struct Result - { - double value{ 0. }; - double derivative{ 0. }; - - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param val: the value - /// @param deriv: the derivative - /// - Result(const double val, const double deriv) noexcept : - value(val), - derivative(deriv) - { - } - }; + private: + //============================================================================ + // Class Description: + /// Simple class to hold the results + /// + struct Result + { + double value{ 0. }; + double derivative{ 0. }; //============================================================================ // Method Description: - /// Calculates the weights and roots vectors + /// Constructor /// - void calculateWeightAndRoot() noexcept + /// @param val: the value + /// @param deriv: the derivative + /// + Result(const double val, const double deriv) noexcept : + value(val), + derivative(deriv) { - const auto numIterationsDouble = static_cast(numIterations_); - for (uint32 step = 0; step <= numIterations_; ++step) - { - double root = - std::cos(constants::pi * (static_cast(step) - 0.25) / (numIterationsDouble + 0.5)); - Result result = calculatePolynomialValueAndDerivative(root); - - double newtonRaphsonRatio; - do - { - newtonRaphsonRatio = result.value / result.derivative; - root -= newtonRaphsonRatio; - result = calculatePolynomialValueAndDerivative(root); - } while (std::fabs(newtonRaphsonRatio) > EPSILON); - - root_[step] = root; - weight_[step] = 2. / ((1. - utils::sqr(root)) * result.derivative * result.derivative); - } } + }; - //============================================================================ - // Method Description: - /// Calculates the weights and roots vectors - /// - /// @param x - /// @return Result - /// - Result calculatePolynomialValueAndDerivative(const double x) noexcept + //============================================================================ + // Method Description: + /// Calculates the weights and roots vectors + /// + void calculateWeightAndRoot() noexcept + { + const auto numIterationsDouble = static_cast(numIterations_); + for (uint32 step = 0; step <= numIterations_; ++step) { - Result result(x, 0.); + double root = + std::cos(constants::pi * (static_cast(step) - 0.25) / (numIterationsDouble + 0.5)); + Result result = calculatePolynomialValueAndDerivative(root); - double value_minus_1 = 1.; - const double f = 1. / (utils::sqr(x) - 1.); - for (uint32 step = 2; step <= numIterations_; ++step) + double newtonRaphsonRatio; + do { - const auto stepDouble = static_cast(step); - const double value = - ((2. * stepDouble - 1.) * x * result.value - (stepDouble - 1.) * value_minus_1) / stepDouble; - result.derivative = stepDouble * f * (x * value - result.value); - - value_minus_1 = result.value; - result.value = value; - } + newtonRaphsonRatio = result.value / result.derivative; + root -= newtonRaphsonRatio; + result = calculatePolynomialValueAndDerivative(root); + } while (std::fabs(newtonRaphsonRatio) > EPSILON); - return result; + root_[step] = root; + weight_[step] = 2. / ((1. - utils::sqr(root)) * result.derivative * result.derivative); } - - //===================================Attributes============================== - const double EPSILON{ 1e-15 }; - - const uint32 numIterations_; - std::vector weight_; - std::vector root_; - }; + } //============================================================================ // Method Description: - /// Performs Gauss-Legendre integration of the input function + /// Calculates the weights and roots vectors /// - /// @param low: the lower bound of the integration - /// @param high: the upper bound of the integration - /// @param n: the number of iterations to perform - /// @param f: the function to integrate over + /// @param x + /// @return Result /// - /// @return double - /// - inline double - gauss_legendre(const double low, const double high, const uint32 n, const std::function& f) + Result calculatePolynomialValueAndDerivative(const double x) noexcept { - const LegendrePolynomial legendrePolynomial(n); - const std::vector& weight = legendrePolynomial.getWeight(); - const std::vector& root = legendrePolynomial.getRoot(); - - const double width = 0.5 * (high - low); - const double mean = 0.5 * (low + high); + Result result(x, 0.); - double gaussLegendre = 0.; - for (uint32 step = 1; step <= n; ++step) + double value_minus_1 = 1.; + const double f = 1. / (utils::sqr(x) - 1.); + for (uint32 step = 2; step <= numIterations_; ++step) { - gaussLegendre += weight[step] * f(width * root[step] + mean); + const auto stepDouble = static_cast(step); + const double value = + ((2. * stepDouble - 1.) * x * result.value - (stepDouble - 1.) * value_minus_1) / stepDouble; + result.derivative = stepDouble * f * (x * value - result.value); + + value_minus_1 = result.value; + result.value = value; } - return gaussLegendre * width; + return result; + } + + //===================================Attributes============================== + const double EPSILON{ 1e-15 }; + + const uint32 numIterations_{}; + std::vector weight_{}; + std::vector root_{}; + }; + + //============================================================================ + // Method Description: + /// Performs Gauss-Legendre integration of the input function + /// + /// @param low: the lower bound of the integration + /// @param high: the upper bound of the integration + /// @param n: the number of iterations to perform + /// @param f: the function to integrate over + /// + /// @return double + /// + inline double + gauss_legendre(const double low, const double high, const uint32 n, const std::function& f) + { + const LegendrePolynomial legendrePolynomial(n); + const std::vector& weight = legendrePolynomial.getWeight(); + const std::vector& root = legendrePolynomial.getRoot(); + + const double width = 0.5 * (high - low); + const double mean = 0.5 * (low + high); + + double gaussLegendre = 0.; + for (uint32 step = 1; step <= n; ++step) + { + gaussLegendre += weight[step] * f(width * root[step] + mean); } - } // namespace integrate -} // namespace nc + + return gaussLegendre * width; + } +} // namespace nc::integrate diff --git a/include/NumCpp/Integrate/romberg.hpp b/include/NumCpp/Integrate/romberg.hpp index b5fb60920..26a0ca673 100644 --- a/include/NumCpp/Integrate/romberg.hpp +++ b/include/NumCpp/Integrate/romberg.hpp @@ -39,57 +39,53 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Utils/power.hpp" -namespace nc +namespace nc::integrate { - namespace integrate + //============================================================================ + // Method Description: + /// Performs Newton-Cotes Romberg integration of the input function + /// + /// @param low: the lower bound of the integration + /// @param high: the upper bound of the integration + /// @param n: the number of iterations + /// @param f: the function to integrate over + /// + /// @return double + /// + inline double romberg(const double low, const double high, const uint8 n, const std::function& f) { - //============================================================================ - // Method Description: - /// Performs Newton-Cotes Romberg integration of the input function - /// - /// @param low: the lower bound of the integration - /// @param high: the upper bound of the integration - /// @param n: the number of iterations - /// @param f: the function to integrate over - /// - /// @return double - /// - inline double - romberg(const double low, const double high, const uint8 n, const std::function& f) - { - NdArray rombergIntegral(n); + NdArray rombergIntegral(n); - // R(0,0) Start with trapezoidal integration with N = 1 - rombergIntegral(0, 0) = trapazoidal(low, high, 1, f); + // R(0,0) Start with trapezoidal integration with N = 1 + rombergIntegral(0, 0) = trapazoidal(low, high, 1, f); - double h = high - low; - for (uint8 step = 1; step < n; step++) - { - h *= 0.5; + double h = high - low; + for (uint8 step = 1; step < n; step++) + { + h *= 0.5; - // R(step, 0) Improve trapezoidal integration with decreasing h - double trapezoidal_integration = 0.; - const uint32 stepEnd = utils::power(2, step - 1); - for (uint32 tzStep = 1; tzStep <= stepEnd; ++tzStep) - { - const double deltaX = (2. * static_cast(tzStep - 1)) * h; - trapezoidal_integration += f(low + deltaX); - } + // R(step, 0) Improve trapezoidal integration with decreasing h + double trapezoidal_integration = 0.; + const uint32 stepEnd = utils::power(2, step - 1); + for (uint32 tzStep = 1; tzStep <= stepEnd; ++tzStep) + { + const double deltaX = (2. * static_cast(tzStep - 1)) * h; + trapezoidal_integration += f(low + deltaX); + } - rombergIntegral(step, 0) = 0.5 * rombergIntegral(step - 1, 0); - rombergIntegral(step, 0) += trapezoidal_integration * h; + rombergIntegral(step, 0) = 0.5 * rombergIntegral(step - 1, 0); + rombergIntegral(step, 0) += trapezoidal_integration * h; - // R(m,n) Romberg integration with R(m,1) -> Simpson rule, R(m,2) -> Boole's rule - for (uint8 rbStep = 1; rbStep <= step; ++rbStep) - { - const double k = utils::power(4, rbStep); - rombergIntegral(step, rbStep) = k * rombergIntegral(step, rbStep - 1); - rombergIntegral(step, rbStep) -= rombergIntegral(step - 1, rbStep - 1); - rombergIntegral(step, rbStep) /= (k - 1.); - } + // R(m,n) Romberg integration with R(m,1) -> Simpson rule, R(m,2) -> Boole's rule + for (uint8 rbStep = 1; rbStep <= step; ++rbStep) + { + const double k = utils::power(4, rbStep); + rombergIntegral(step, rbStep) = k * rombergIntegral(step, rbStep - 1); + rombergIntegral(step, rbStep) -= rombergIntegral(step - 1, rbStep - 1); + rombergIntegral(step, rbStep) /= (k - 1.); } - - return rombergIntegral.back(); } - } // namespace integrate -} // namespace nc + + return rombergIntegral.back(); + } +} // namespace nc::integrate diff --git a/include/NumCpp/Integrate/simpson.hpp b/include/NumCpp/Integrate/simpson.hpp index 32c7fa0c4..ab655069d 100644 --- a/include/NumCpp/Integrate/simpson.hpp +++ b/include/NumCpp/Integrate/simpson.hpp @@ -35,38 +35,33 @@ #include "NumCpp/Core/Types.hpp" -namespace nc +namespace nc::integrate { - namespace integrate + //============================================================================ + // Method Description: + /// Performs Newton-Cotes Simpson integration of the input function + /// + /// @param low: the lower bound of the integration + /// @param high: the upper bound of the integration + /// @param n: the number of subdivisions + /// @param f: the function to integrate over + /// + /// @return double + /// + inline double + simpson(const double low, const double high, const uint32 n, const std::function& f) noexcept { - //============================================================================ - // Method Description: - /// Performs Newton-Cotes Simpson integration of the input function - /// - /// @param low: the lower bound of the integration - /// @param high: the upper bound of the integration - /// @param n: the number of subdivisions - /// @param f: the function to integrate over - /// - /// @return double - /// - inline double simpson(const double low, - const double high, - const uint32 n, - const std::function& f) noexcept - { - const double width = (high - low) / static_cast(n); - - double simpson_integral = 0.; - for (uint32 step = 0; step < n; ++step) - { - const double x1 = low + static_cast(step) * width; - const double x2 = low + static_cast(step + 1) * width; + const double width = (high - low) / static_cast(n); - simpson_integral += (x2 - x1) / 6. * (f(x1) + 4. * f(0.5 * (x1 + x2)) + f(x2)); - } + double simpson_integral = 0.; + for (uint32 step = 0; step < n; ++step) + { + const double x1 = low + static_cast(step) * width; + const double x2 = low + static_cast(step + 1) * width; - return simpson_integral; + simpson_integral += (x2 - x1) / 6. * (f(x1) + 4. * f(0.5 * (x1 + x2)) + f(x2)); } - } // namespace integrate -} // namespace nc + + return simpson_integral; + } +} // namespace nc::integrate diff --git a/include/NumCpp/Integrate/trapazoidal.hpp b/include/NumCpp/Integrate/trapazoidal.hpp index 95f654b4c..6cd98d3f7 100644 --- a/include/NumCpp/Integrate/trapazoidal.hpp +++ b/include/NumCpp/Integrate/trapazoidal.hpp @@ -35,38 +35,35 @@ #include "NumCpp/Core/Types.hpp" -namespace nc +namespace nc::integrate { - namespace integrate + //============================================================================ + // Method Description: + /// Performs Newton-Cotes trapazoidal integration of the input function + /// + /// @param low: the lower bound of the integration + /// @param high: the upper bound of the integration + /// @param n: the number of subdivisions + /// @param f: the function to integrate over + /// + /// @return double + /// + inline double trapazoidal(const double low, + const double high, + const uint32 n, + const std::function& f) noexcept { - //============================================================================ - // Method Description: - /// Performs Newton-Cotes trapazoidal integration of the input function - /// - /// @param low: the lower bound of the integration - /// @param high: the upper bound of the integration - /// @param n: the number of subdivisions - /// @param f: the function to integrate over - /// - /// @return double - /// - inline double trapazoidal(const double low, - const double high, - const uint32 n, - const std::function& f) noexcept - { - const double width = (high - low) / static_cast(n); - - double trapezoidal_integral = 0.; - for (uint32 step = 0; step < n; ++step) - { - const double x1 = low + static_cast(step) * width; - const double x2 = low + static_cast(step + 1) * width; + const double width = (high - low) / static_cast(n); - trapezoidal_integral += 0.5 * (x2 - x1) * (f(x1) + f(x2)); - } + double trapezoidal_integral = 0.; + for (uint32 step = 0; step < n; ++step) + { + const double x1 = low + static_cast(step) * width; + const double x2 = low + static_cast(step + 1) * width; - return trapezoidal_integral; + trapezoidal_integral += 0.5 * (x2 - x1) * (f(x1) + f(x2)); } - } // namespace integrate -} // namespace nc + + return trapezoidal_integral; + } +} // namespace nc::integrate diff --git a/include/NumCpp/Linalg/cholesky.hpp b/include/NumCpp/Linalg/cholesky.hpp index c18eea436..92d719208 100644 --- a/include/NumCpp/Linalg/cholesky.hpp +++ b/include/NumCpp/Linalg/cholesky.hpp @@ -37,67 +37,64 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::linalg { - namespace linalg + //============================================================================ + // Method Description: + /// matrix cholesky decomposition A = L * L.transpose() + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.cholesky.html#numpy.linalg.cholesky + /// + /// @param inMatrix: NdArray to be decomposed + /// + /// @return NdArray of the decomposed L matrix + /// + template + NdArray cholesky(const NdArray& inMatrix) { - //============================================================================ - // Method Description: - /// matrix cholesky decomposition A = L * L.transpose() - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.cholesky.html#numpy.linalg.cholesky - /// - /// @param inMatrix: NdArray to be decomposed - /// - /// @return NdArray of the decomposed L matrix - /// - template - NdArray cholesky(const NdArray& inMatrix) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - const auto shape = inMatrix.shape(); - if (!shape.issquare()) - { - THROW_RUNTIME_ERROR("Input matrix should be square."); - } + const auto shape = inMatrix.shape(); + if (!shape.issquare()) + { + THROW_RUNTIME_ERROR("Input matrix should be square."); + } - auto lMatrix = inMatrix.template astype(); + auto lMatrix = inMatrix.template astype(); - for (uint32 row = 0; row < shape.rows; ++row) + for (uint32 row = 0; row < shape.rows; ++row) + { + for (uint32 col = row + 1; col < shape.cols; ++col) { - for (uint32 col = row + 1; col < shape.cols; ++col) - { - lMatrix(row, col) = 0.; - } + lMatrix(row, col) = 0.; } + } - for (uint32 k = 0; k < shape.cols; ++k) + for (uint32 k = 0; k < shape.cols; ++k) + { + const double& a_kk = lMatrix(k, k); + + if (a_kk > 0.) { - const double& a_kk = lMatrix(k, k); + lMatrix(k, k) = std::sqrt(a_kk); - if (a_kk > 0.) + for (uint32 i = k + 1; i < shape.rows; ++i) { - lMatrix(k, k) = std::sqrt(a_kk); + lMatrix(i, k) /= lMatrix(k, k); - for (uint32 i = k + 1; i < shape.rows; ++i) + for (uint32 j = k + 1; j <= i; ++j) { - lMatrix(i, k) /= lMatrix(k, k); - - for (uint32 j = k + 1; j <= i; ++j) - { - lMatrix(i, j) -= lMatrix(i, k) * lMatrix(j, k); - } + lMatrix(i, j) -= lMatrix(i, k) * lMatrix(j, k); } } - else - { - THROW_RUNTIME_ERROR("Matrix is not positive definite."); - } } - - return lMatrix; + else + { + THROW_RUNTIME_ERROR("Matrix is not positive definite."); + } } - } // namespace linalg -} // namespace nc + + return lMatrix; + } +} // namespace nc::linalg diff --git a/include/NumCpp/Linalg/det.hpp b/include/NumCpp/Linalg/det.hpp index d71f9d4c9..5fe04589f 100644 --- a/include/NumCpp/Linalg/det.hpp +++ b/include/NumCpp/Linalg/det.hpp @@ -38,109 +38,106 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::linalg { - namespace linalg + namespace detail { - namespace detail + //============================================================================ + // Method Description: + /// matrix determinant. + /// + /// @param inArray + /// @param order + /// @return matrix determinant + /// + template + auto det(const NdArray& inArray, uint32 order) + -> std::conditional_t, int64, double> { - //============================================================================ - // Method Description: - /// matrix determinant. - /// - /// @param inArray - /// @param order - /// @return matrix determinant - /// - template - auto det(const NdArray& inArray, uint32 order) - -> std::conditional_t, int64, double> - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - using ReturnType = std::conditional_t, int64, double>; + using ReturnType = std::conditional_t, int64, double>; - if (order == 1) - { - return static_cast(inArray.front()); - } + if (order == 1) + { + return static_cast(inArray.front()); + } - if (order == 2) - { - return static_cast(inArray(0, 0)) * static_cast(inArray(1, 1)) - - static_cast(inArray(0, 1)) * static_cast(inArray(1, 0)); - } + if (order == 2) + { + return static_cast(inArray(0, 0)) * static_cast(inArray(1, 1)) - + static_cast(inArray(0, 1)) * static_cast(inArray(1, 0)); + } - if (order == 3) - { - const auto aei = static_cast(inArray(0, 0)) * static_cast(inArray(1, 1)) * - static_cast(inArray(2, 2)); - const auto bfg = static_cast(inArray(0, 1)) * static_cast(inArray(1, 2)) * - static_cast(inArray(2, 0)); - const auto cdh = static_cast(inArray(0, 2)) * static_cast(inArray(1, 0)) * - static_cast(inArray(2, 1)); - const auto ceg = static_cast(inArray(0, 2)) * static_cast(inArray(1, 1)) * - static_cast(inArray(2, 0)); - const auto bdi = static_cast(inArray(0, 1)) * static_cast(inArray(1, 0)) * - static_cast(inArray(2, 2)); - const auto afh = static_cast(inArray(0, 0)) * static_cast(inArray(1, 2)) * - static_cast(inArray(2, 1)); - - return aei + bfg + cdh - ceg - bdi - afh; - } + if (order == 3) + { + const auto aei = static_cast(inArray(0, 0)) * static_cast(inArray(1, 1)) * + static_cast(inArray(2, 2)); + const auto bfg = static_cast(inArray(0, 1)) * static_cast(inArray(1, 2)) * + static_cast(inArray(2, 0)); + const auto cdh = static_cast(inArray(0, 2)) * static_cast(inArray(1, 0)) * + static_cast(inArray(2, 1)); + const auto ceg = static_cast(inArray(0, 2)) * static_cast(inArray(1, 1)) * + static_cast(inArray(2, 0)); + const auto bdi = static_cast(inArray(0, 1)) * static_cast(inArray(1, 0)) * + static_cast(inArray(2, 2)); + const auto afh = static_cast(inArray(0, 0)) * static_cast(inArray(1, 2)) * + static_cast(inArray(2, 1)); + + return aei + bfg + cdh - ceg - bdi - afh; + } - ReturnType determinant = 0; - ReturnType sign = 1; - NdArray submat(order - 1); + ReturnType determinant = 0; + ReturnType sign = 1; + NdArray submat(order - 1); - for (uint32 c = 0; c < order; ++c) + for (uint32 c = 0; c < order; ++c) + { + uint32 subi = 0; + for (uint32 i = 1; i < order; ++i) { - uint32 subi = 0; - for (uint32 i = 1; i < order; ++i) + uint32 subj = 0; + for (uint32 j = 0; j < order; ++j) { - uint32 subj = 0; - for (uint32 j = 0; j < order; ++j) + if (j == c) { - if (j == c) - { - continue; - } - - submat(subi, subj++) = inArray(i, j); + continue; } - ++subi; - } - determinant += (sign * static_cast(inArray(0, c)) * det(submat, order - 1)); - sign *= -1; + submat(subi, subj++) = inArray(i, j); + } + ++subi; } - return determinant; + determinant += (sign * static_cast(inArray(0, c)) * det(submat, order - 1)); + sign *= -1; } - } // namespace detail - - //============================================================================ - // Method Description: - /// matrix determinant. - /// NOTE: can get verrrrry slow for large matrices (order > 10) - /// - /// SciPy Reference: https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.det.html#scipy.linalg.det - /// - /// @param inArray - /// @return matrix determinant - /// - template - auto det(const NdArray& inArray) - { - STATIC_ASSERT_ARITHMETIC(dtype); - const Shape inShape = inArray.shape(); - if (!inShape.issquare()) - { - THROW_INVALID_ARGUMENT_ERROR("input array must be square."); - } + return determinant; + } + } // namespace detail + + //============================================================================ + // Method Description: + /// matrix determinant. + /// NOTE: can get verrrrry slow for large matrices (order > 10) + /// + /// SciPy Reference: https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.det.html#scipy.linalg.det + /// + /// @param inArray + /// @return matrix determinant + /// + template + auto det(const NdArray& inArray) + { + STATIC_ASSERT_ARITHMETIC(dtype); - return detail::det(inArray, inShape.rows); + const Shape inShape = inArray.shape(); + if (!inShape.issquare()) + { + THROW_INVALID_ARGUMENT_ERROR("input array must be square."); } - } // namespace linalg -} // namespace nc + + return detail::det(inArray, inShape.rows); + } +} // namespace nc::linalg diff --git a/include/NumCpp/Linalg/gaussNewtonNlls.hpp b/include/NumCpp/Linalg/gaussNewtonNlls.hpp index 9e23225d0..07901b5b7 100644 --- a/include/NumCpp/Linalg/gaussNewtonNlls.hpp +++ b/include/NumCpp/Linalg/gaussNewtonNlls.hpp @@ -43,95 +43,92 @@ #include "NumCpp/Linalg/inv.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::linalg { - namespace linalg + //============================================================================ + // Method Description: + /// The Gauss�Newton algorithm is used to solve non-linear least squares problems. + /// It is a modification of Newton's method for finding a minimum of a function. + /// https://en.wikipedia.org/wiki/Gauss%E2%80%93Newton_algorithm + /// + /// @param numIterations: the number of iterations to perform + /// @param coordinates: the coordinate values. The shape needs to be [n x d], where d is + /// the number of diminsions of the fit function (f(x) is one dimensional, + /// f(x, y) is two dimensions, etc), and n is the number of observations + /// that are being fit to. + /// @param measurements: the measured values that are being fit + /// @param function: a std::function of the function that is being fit. The function takes as + /// inputs an NdArray of a single set of the coordinate values, and an NdArray + /// of the current values of the fit parameters + /// @param derivatives: array of std::functions to calculate the function + /// derivatives. The function that is being fit. The function takes as + /// inputs an NdArray of a single set of the coordinate values, and an NdArray + /// of the current values of the fit parameters + /// @param initialGuess: the initial guess of the parameters to be solved for + /// + /// @return std::pair of NdArray of solved parameter values, and rms of the residuals value + /// + template, int> = 0, + std::enable_if_t, int> = 0, + std::enable_if_t, int> = 0> + std::pair, double> + gaussNewtonNlls(const uint32 numIterations, + const NdArray& coordinates, + const NdArray& measurements, + const std::function&, const NdArray&)>& function, + const std::array&, const NdArray&)>, + sizeof...(Params)>& derivatives, + Params... initialGuess) { - //============================================================================ - // Method Description: - /// The Gauss�Newton algorithm is used to solve non-linear least squares problems. - /// It is a modification of Newton's method for finding a minimum of a function. - /// https://en.wikipedia.org/wiki/Gauss%E2%80%93Newton_algorithm - /// - /// @param numIterations: the number of iterations to perform - /// @param coordinates: the coordinate values. The shape needs to be [n x d], where d is - /// the number of diminsions of the fit function (f(x) is one dimensional, - /// f(x, y) is two dimensions, etc), and n is the number of observations - /// that are being fit to. - /// @param measurements: the measured values that are being fit - /// @param function: a std::function of the function that is being fit. The function takes as - /// inputs an NdArray of a single set of the coordinate values, and an NdArray - /// of the current values of the fit parameters - /// @param derivatives: array of std::functions to calculate the function - /// derivatives. The function that is being fit. The function takes as - /// inputs an NdArray of a single set of the coordinate values, and an NdArray - /// of the current values of the fit parameters - /// @param initialGuess: the initial guess of the parameters to be solved for - /// - /// @return std::pair of NdArray of solved parameter values, and rms of the residuals value - /// - template, int> = 0, - std::enable_if_t, int> = 0, - std::enable_if_t, int> = 0> - std::pair, double> - gaussNewtonNlls(const uint32 numIterations, - const NdArray& coordinates, - const NdArray& measurements, - const std::function&, const NdArray&)>& function, - const std::array&, const NdArray&)>, - sizeof...(Params)>& derivatives, - Params... initialGuess) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - const auto coordinatesShape = coordinates.shape(); - - if (coordinatesShape.rows != measurements.size()) - { - THROW_INVALID_ARGUMENT_ERROR("coordinates number of rows, and measurements size must be the same."); - } + STATIC_ASSERT_ARITHMETIC(dtype); - NdArray beta = NdArray({ initialGuess... }).template astype().transpose(); - NdArray residuals(coordinatesShape.rows, 1); - NdArray jacobian(coordinatesShape.rows, sizeof...(Params)); + const auto coordinatesShape = coordinates.shape(); - const auto colSlice = coordinates.cSlice(); - for (uint32 iteration = 1; iteration <= numIterations; ++iteration) - { - for (uint32 measIdx = 0; measIdx < coordinatesShape.rows; ++measIdx) - { - const auto coordinate = coordinates(measIdx, colSlice); - - residuals[measIdx] = - static_cast(measurements[measIdx]) - static_cast(function(coordinate, beta)); - - for (uint32 paramIdx = 0; paramIdx < sizeof...(Params); ++paramIdx) - { - const auto& derivative = derivatives[paramIdx]; - jacobian(measIdx, paramIdx) = static_cast(derivative(coordinate, beta)); - } - } + if (coordinatesShape.rows != measurements.size()) + { + THROW_INVALID_ARGUMENT_ERROR("coordinates number of rows, and measurements size must be the same."); + } - // perform the gauss-newton linear algebra - const auto jacobianT = jacobian.transpose(); - const auto jacobianPsuedoInverse = linalg::inv(jacobianT.dot(jacobian)); - const auto intermediate = jacobianPsuedoInverse.dot(jacobianT); - const auto deltaBeta = intermediate.dot(residuals); - beta += deltaBeta; - } + NdArray beta = NdArray({ initialGuess... }).template astype().transpose(); + NdArray residuals(coordinatesShape.rows, 1); + NdArray jacobian(coordinatesShape.rows, sizeof...(Params)); - // calculate the final rms of the residuals + const auto colSlice = coordinates.cSlice(); + for (uint32 iteration = 1; iteration <= numIterations; ++iteration) + { for (uint32 measIdx = 0; measIdx < coordinatesShape.rows; ++measIdx) { const auto coordinate = coordinates(measIdx, colSlice); residuals[measIdx] = static_cast(measurements[measIdx]) - static_cast(function(coordinate, beta)); + + for (uint32 paramIdx = 0; paramIdx < sizeof...(Params); ++paramIdx) + { + const auto& derivative = derivatives[paramIdx]; + jacobian(measIdx, paramIdx) = static_cast(derivative(coordinate, beta)); + } } - return std::make_pair(beta.flatten(), rms(residuals).item()); + // perform the gauss-newton linear algebra + const auto jacobianT = jacobian.transpose(); + const auto jacobianPsuedoInverse = linalg::inv(jacobianT.dot(jacobian)); + const auto intermediate = jacobianPsuedoInverse.dot(jacobianT); + const auto deltaBeta = intermediate.dot(residuals); + beta += deltaBeta; } - } // namespace linalg -} // namespace nc + + // calculate the final rms of the residuals + for (uint32 measIdx = 0; measIdx < coordinatesShape.rows; ++measIdx) + { + const auto coordinate = coordinates(measIdx, colSlice); + + residuals[measIdx] = + static_cast(measurements[measIdx]) - static_cast(function(coordinate, beta)); + } + + return std::make_pair(beta.flatten(), rms(residuals).item()); + } +} // namespace nc::linalg diff --git a/include/NumCpp/Linalg/hat.hpp b/include/NumCpp/Linalg/hat.hpp index a37734449..f68f3f639 100644 --- a/include/NumCpp/Linalg/hat.hpp +++ b/include/NumCpp/Linalg/hat.hpp @@ -34,68 +34,65 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Vector/Vec3.hpp" -namespace nc +namespace nc::linalg { - namespace linalg + //============================================================================ + // Method Description: + /// vector hat operator + /// + /// @param inX + /// @param inY + /// @param inZ + /// @return 3x3 NdArray + /// + template + NdArray hat(dtype inX, dtype inY, dtype inZ) { - //============================================================================ - // Method Description: - /// vector hat operator - /// - /// @param inX - /// @param inY - /// @param inZ - /// @return 3x3 NdArray - /// - template - NdArray hat(dtype inX, dtype inY, dtype inZ) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - NdArray returnArray(3); - returnArray(0, 0) = 0.; - returnArray(0, 1) = -inZ; - returnArray(0, 2) = inY; - returnArray(1, 0) = inZ; - returnArray(1, 1) = 0.; - returnArray(1, 2) = -inX; - returnArray(2, 0) = -inY; - returnArray(2, 1) = inX; - returnArray(2, 2) = 0.; + STATIC_ASSERT_ARITHMETIC(dtype); - return returnArray; - } - - //============================================================================ - // Method Description: - /// vector hat operator - /// - /// @param inVec (3x1, or 1x3 cartesian vector) - /// @return 3x3 NdArray - /// - template - NdArray hat(const NdArray& inVec) - { - STATIC_ASSERT_ARITHMETIC(dtype); + NdArray returnArray(3); + returnArray(0, 0) = 0.; + returnArray(0, 1) = -inZ; + returnArray(0, 2) = inY; + returnArray(1, 0) = inZ; + returnArray(1, 1) = 0.; + returnArray(1, 2) = -inX; + returnArray(2, 0) = -inY; + returnArray(2, 1) = inX; + returnArray(2, 2) = 0.; - if (inVec.size() != 3) - { - THROW_INVALID_ARGUMENT_ERROR("input vector must be a length 3 cartesian vector."); - } + return returnArray; + } - return hat(inVec[0], inVec[1], inVec[2]); - } + //============================================================================ + // Method Description: + /// vector hat operator + /// + /// @param inVec (3x1, or 1x3 cartesian vector) + /// @return 3x3 NdArray + /// + template + NdArray hat(const NdArray& inVec) + { + STATIC_ASSERT_ARITHMETIC(dtype); - //============================================================================ - // Method Description: - /// vector hat operator - /// - /// @param inVec - /// @return 3x3 NdArray - /// - inline NdArray hat(const Vec3& inVec) + if (inVec.size() != 3) { - return hat(inVec.x, inVec.y, inVec.z); + THROW_INVALID_ARGUMENT_ERROR("input vector must be a length 3 cartesian vector."); } - } // namespace linalg -} // namespace nc + + return hat(inVec[0], inVec[1], inVec[2]); + } + + //============================================================================ + // Method Description: + /// vector hat operator + /// + /// @param inVec + /// @return 3x3 NdArray + /// + inline NdArray hat(const Vec3& inVec) + { + return hat(inVec.x, inVec.y, inVec.z); + } +} // namespace nc::linalg diff --git a/include/NumCpp/Linalg/inv.hpp b/include/NumCpp/Linalg/inv.hpp index d8256dcf6..806356313 100644 --- a/include/NumCpp/Linalg/inv.hpp +++ b/include/NumCpp/Linalg/inv.hpp @@ -39,98 +39,94 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Utils/essentiallyEqual.hpp" -namespace nc +namespace nc::linalg { - namespace linalg + //============================================================================ + // Method Description: + /// matrix inverse + /// + /// SciPy Reference: https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.inv.html#scipy.linalg.inv + /// + /// @param inArray + /// @return NdArray + /// + template + NdArray inv(const NdArray& inArray) { - //============================================================================ - // Method Description: - /// matrix inverse - /// - /// SciPy Reference: https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.inv.html#scipy.linalg.inv - /// - /// @param inArray - /// @return NdArray - /// - template - NdArray inv(const NdArray& inArray) - { - STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); + STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); - const Shape inShape = inArray.shape(); - if (inShape.rows != inShape.cols) - { - THROW_INVALID_ARGUMENT_ERROR("input array must be square."); - } + const Shape inShape = inArray.shape(); + if (inShape.rows != inShape.cols) + { + THROW_INVALID_ARGUMENT_ERROR("input array must be square."); + } - NdArray inArrayDouble = inArray.template astype(); - NdArray incidence = nc::zeros(inShape); + NdArray inArrayDouble = inArray.template astype(); + NdArray incidence = nc::zeros(inShape); - for (uint32 k = 0; k < inShape.rows - 1; ++k) + for (uint32 k = 0; k < inShape.rows - 1; ++k) + { + if (utils::essentiallyEqual(inArrayDouble(k, k), 0.)) { - if (utils::essentiallyEqual(inArrayDouble(k, k), 0.)) + uint32 l = k; + while (l < inShape.cols && utils::essentiallyEqual(inArrayDouble(k, l), 0.)) { - uint32 l = k; - while (l < inShape.cols && utils::essentiallyEqual(inArrayDouble(k, l), 0.)) - { - ++l; - } - - inArrayDouble.swapRows(k, l); - incidence(k, k) = 1; - incidence(k, l) = 1; + ++l; } + + inArrayDouble.swapRows(k, l); + incidence(k, k) = 1; + incidence(k, l) = 1; } + } - NdArray result(inShape); + NdArray result(inShape); - for (uint32 k = 0; k < inShape.rows; ++k) + for (uint32 k = 0; k < inShape.rows; ++k) + { + result(k, k) = -1. / inArrayDouble(k, k); + for (uint32 i = 0; i < inShape.rows; ++i) { - result(k, k) = -1. / inArrayDouble(k, k); - for (uint32 i = 0; i < inShape.rows; ++i) + for (uint32 j = 0; j < inShape.cols; ++j) { - for (uint32 j = 0; j < inShape.cols; ++j) + if ((i - k) && (j - k)) + { + result(i, j) = inArrayDouble(i, j) + inArrayDouble(k, j) * inArrayDouble(i, k) * result(k, k); + } + else if ((i - k) && !(j - k)) { - if ((i - k) && (j - k)) - { - result(i, j) = - inArrayDouble(i, j) + inArrayDouble(k, j) * inArrayDouble(i, k) * result(k, k); - } - else if ((i - k) && !(j - k)) - { - result(i, k) = inArrayDouble(i, k) * result(k, k); - } - else if (!(i - k) && (j - k)) - { - result(k, j) = inArrayDouble(k, j) * result(k, k); - } + result(i, k) = inArrayDouble(i, k) * result(k, k); + } + else if (!(i - k) && (j - k)) + { + result(k, j) = inArrayDouble(k, j) * result(k, k); } } - - inArrayDouble = result; } - result *= -1.; + inArrayDouble = result; + } - for (int i = static_cast(inShape.rows) - 1; i >= 0; --i) + result *= -1.; + + for (int i = static_cast(inShape.rows) - 1; i >= 0; --i) + { + if (incidence(i, i) != 1) { - if (incidence(i, i) != 1) - { - continue; - } + continue; + } - int k = 0; - for (; k < static_cast(inShape.cols); ++k) + int k = 0; + for (; k < static_cast(inShape.cols); ++k) + { + if ((k - i) && incidence(i, k) != 0) { - if ((k - i) && incidence(i, k) != 0) - { - result.swapCols(i, k); - break; - } + result.swapCols(i, k); + break; } } - - return result; } - } // namespace linalg -} // namespace nc + + return result; + } +} // namespace nc::linalg diff --git a/include/NumCpp/Linalg/lstsq.hpp b/include/NumCpp/Linalg/lstsq.hpp index 769878b80..2bf7956e0 100644 --- a/include/NumCpp/Linalg/lstsq.hpp +++ b/include/NumCpp/Linalg/lstsq.hpp @@ -31,39 +31,36 @@ #include "NumCpp/Linalg/svd/SVDClass.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::linalg { - namespace linalg + //============================================================================ + // Method Description: + /// Solves the equation a x = b by computing a vector x + /// that minimizes the Euclidean 2-norm || b - a x ||^2. + /// The equation may be under-, well-, or over- determined + /// (i.e., the number of linearly independent rows of a can + /// be less than, equal to, or greater than its number of + /// linearly independent columns). If a is square and of + /// full rank, then x (but for round-off error) is the + /// "exact" solution of the equation. + /// + /// SciPy Reference: + /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.lstsq.html#scipy.linalg.lstsq + /// + /// @param inA: coefficient matrix + /// @param inB: Ordinate or "dependent variable" values + /// @param inTolerance (default 1e-12) + /// + /// @return NdArray + /// + template + NdArray lstsq(const NdArray& inA, const NdArray& inB, double inTolerance = 1e-12) { - //============================================================================ - // Method Description: - /// Solves the equation a x = b by computing a vector x - /// that minimizes the Euclidean 2-norm || b - a x ||^2. - /// The equation may be under-, well-, or over- determined - /// (i.e., the number of linearly independent rows of a can - /// be less than, equal to, or greater than its number of - /// linearly independent columns). If a is square and of - /// full rank, then x (but for round-off error) is the - /// "exact" solution of the equation. - /// - /// SciPy Reference: - /// https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.lstsq.html#scipy.linalg.lstsq - /// - /// @param inA: coefficient matrix - /// @param inB: Ordinate or "dependent variable" values - /// @param inTolerance (default 1e-12) - /// - /// @return NdArray - /// - template - NdArray lstsq(const NdArray& inA, const NdArray& inB, double inTolerance = 1e-12) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - SVD svdSolver(inA.template astype()); - const double threshold = inTolerance * svdSolver.s().front(); + SVD svdSolver(inA.template astype()); + const double threshold = inTolerance * svdSolver.s().front(); - return svdSolver.solve(inB.template astype(), threshold); - } - } // namespace linalg -} // namespace nc + return svdSolver.solve(inB.template astype(), threshold); + } +} // namespace nc::linalg diff --git a/include/NumCpp/Linalg/lu_decomposition.hpp b/include/NumCpp/Linalg/lu_decomposition.hpp index 55176c1f2..e8542b9da 100644 --- a/include/NumCpp/Linalg/lu_decomposition.hpp +++ b/include/NumCpp/Linalg/lu_decomposition.hpp @@ -42,54 +42,51 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Utils/essentiallyEqual.hpp" -namespace nc +namespace nc::linalg { - namespace linalg + //============================================================================ + // Method Description: + /// matrix LU decomposition A = LU + /// + /// @param inMatrix: NdArray to be decomposed + /// + /// @return std::pair of the decomposed L and U matrices + /// + template + std::pair, NdArray> lu_decomposition(const NdArray& inMatrix) { - //============================================================================ - // Method Description: - /// matrix LU decomposition A = LU - /// - /// @param inMatrix: NdArray to be decomposed - /// - /// @return std::pair of the decomposed L and U matrices - /// - template - std::pair, NdArray> lu_decomposition(const NdArray& inMatrix) + STATIC_ASSERT_ARITHMETIC(dtype); + + const auto shape = inMatrix.shape(); + if (!shape.issquare()) { - STATIC_ASSERT_ARITHMETIC(dtype); + THROW_RUNTIME_ERROR("Input matrix should be square."); + } - const auto shape = inMatrix.shape(); - if (!shape.issquare()) - { - THROW_RUNTIME_ERROR("Input matrix should be square."); - } + NdArray lMatrix = zeros_like(inMatrix); + NdArray uMatrix = inMatrix.template astype(); - NdArray lMatrix = zeros_like(inMatrix); - NdArray uMatrix = inMatrix.template astype(); + for (uint32 col = 0; col < shape.cols; ++col) + { + lMatrix(col, col) = 1; - for (uint32 col = 0; col < shape.cols; ++col) + for (uint32 row = col + 1; row < shape.rows; ++row) { - lMatrix(col, col) = 1; - - for (uint32 row = col + 1; row < shape.rows; ++row) + const double& divisor = uMatrix(col, col); + if (utils::essentiallyEqual(divisor, double{ 0. })) { - const double& divisor = uMatrix(col, col); - if (utils::essentiallyEqual(divisor, double{ 0. })) - { - THROW_RUNTIME_ERROR("Division by 0."); - } + THROW_RUNTIME_ERROR("Division by 0."); + } - lMatrix(row, col) = uMatrix(row, col) / divisor; + lMatrix(row, col) = uMatrix(row, col) / divisor; - for (uint32 col2 = col; col2 < shape.cols; ++col2) - { - uMatrix(row, col2) -= lMatrix(row, col) * uMatrix(col, col2); - } + for (uint32 col2 = col; col2 < shape.cols; ++col2) + { + uMatrix(row, col2) -= lMatrix(row, col) * uMatrix(col, col2); } } - - return std::make_pair(lMatrix, uMatrix); } - } // namespace linalg -} // namespace nc + + return std::make_pair(lMatrix, uMatrix); + } +} // namespace nc::linalg diff --git a/include/NumCpp/Linalg/matrix_power.hpp b/include/NumCpp/Linalg/matrix_power.hpp index f81591c48..511a8596f 100644 --- a/include/NumCpp/Linalg/matrix_power.hpp +++ b/include/NumCpp/Linalg/matrix_power.hpp @@ -37,72 +37,69 @@ #include "NumCpp/Functions/identity.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::linalg { - namespace linalg + //============================================================================ + // Method Description: + /// Raise a square matrix to the (integer) power n. + /// + /// For positive integers n, the power is computed by repeated + /// matrix squarings and matrix multiplications. If n == 0, + /// the identity matrix of the same shape as M is returned. + /// If n < 0, the inverse is computed and then raised to the abs(n). + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.matrix_power.html#numpy.linalg.matrix_power + /// + /// @param inArray + /// @param inPower + /// + /// @return NdArray + /// + template + NdArray matrix_power(const NdArray& inArray, int16 inPower) { - //============================================================================ - // Method Description: - /// Raise a square matrix to the (integer) power n. - /// - /// For positive integers n, the power is computed by repeated - /// matrix squarings and matrix multiplications. If n == 0, - /// the identity matrix of the same shape as M is returned. - /// If n < 0, the inverse is computed and then raised to the abs(n). - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.matrix_power.html#numpy.linalg.matrix_power - /// - /// @param inArray - /// @param inPower - /// - /// @return NdArray - /// - template - NdArray matrix_power(const NdArray& inArray, int16 inPower) - { - STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); - - const Shape inShape = inArray.shape(); - if (inShape.rows != inShape.cols) - { - THROW_INVALID_ARGUMENT_ERROR("input matrix must be square."); - } + STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); - if (inPower == 0) - { - return identity(inShape.rows); - } + const Shape inShape = inArray.shape(); + if (inShape.rows != inShape.cols) + { + THROW_INVALID_ARGUMENT_ERROR("input matrix must be square."); + } - if (inPower == 1) - { - return inArray.template astype(); - } + if (inPower == 0) + { + return identity(inShape.rows); + } - if (inPower == -1) - { - return inv(inArray); - } + if (inPower == 1) + { + return inArray.template astype(); + } - if (inPower > 1) - { - NdArray inArrayDouble = inArray.template astype(); - NdArray returnArray = dot(inArrayDouble, inArrayDouble); - for (int16 i = 2; i < inPower; ++i) - { - returnArray = dot(returnArray, inArrayDouble); - } - return returnArray; - } + if (inPower == -1) + { + return inv(inArray); + } - NdArray inverse = inv(inArray); - NdArray returnArray = dot(inverse, inverse); - inPower *= -1; + if (inPower > 1) + { + NdArray inArrayDouble = inArray.template astype(); + NdArray returnArray = dot(inArrayDouble, inArrayDouble); for (int16 i = 2; i < inPower; ++i) { - returnArray = dot(returnArray, inverse); + returnArray = dot(returnArray, inArrayDouble); } return returnArray; } - } // namespace linalg -} // namespace nc + + NdArray inverse = inv(inArray); + NdArray returnArray = dot(inverse, inverse); + inPower *= -1; + for (int16 i = 2; i < inPower; ++i) + { + returnArray = dot(returnArray, inverse); + } + return returnArray; + } +} // namespace nc::linalg diff --git a/include/NumCpp/Linalg/multi_dot.hpp b/include/NumCpp/Linalg/multi_dot.hpp index 8623b6fb3..68167d03a 100644 --- a/include/NumCpp/Linalg/multi_dot.hpp +++ b/include/NumCpp/Linalg/multi_dot.hpp @@ -35,46 +35,43 @@ #include "NumCpp/Functions/dot.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::linalg { - namespace linalg + //============================================================================ + // Method Description: + /// Compute the dot product of two or more arrays in a single + /// function call. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.multi_dot.html#numpy.linalg.multi_dot + /// + /// @param inList: list of arrays + /// + /// @return NdArray + /// + template + NdArray multi_dot(const std::initializer_list>& inList) { - //============================================================================ - // Method Description: - /// Compute the dot product of two or more arrays in a single - /// function call. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.multi_dot.html#numpy.linalg.multi_dot - /// - /// @param inList: list of arrays - /// - /// @return NdArray - /// - template - NdArray multi_dot(const std::initializer_list>& inList) - { - STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); - - typename std::initializer_list>::iterator iter = inList.begin(); + STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); - if (inList.size() == 0) - { - THROW_INVALID_ARGUMENT_ERROR("input empty list of arrays."); - } - else if (inList.size() == 1) - { - return iter->copy(); - } + typename std::initializer_list>::iterator iter = inList.begin(); - NdArray returnArray = dot(*iter, *(iter + 1)); - iter += 2; - for (; iter < inList.end(); ++iter) - { - returnArray = dot(returnArray, *iter); - } + if (inList.size() == 0) + { + THROW_INVALID_ARGUMENT_ERROR("input empty list of arrays."); + } + else if (inList.size() == 1) + { + return iter->copy(); + } - return returnArray; + NdArray returnArray = dot(*iter, *(iter + 1)); + iter += 2; + for (; iter < inList.end(); ++iter) + { + returnArray = dot(returnArray, *iter); } - } // namespace linalg -} // namespace nc + + return returnArray; + } +} // namespace nc::linalg diff --git a/include/NumCpp/Linalg/pinv.hpp b/include/NumCpp/Linalg/pinv.hpp index 23c33bbda..d58306903 100644 --- a/include/NumCpp/Linalg/pinv.hpp +++ b/include/NumCpp/Linalg/pinv.hpp @@ -35,38 +35,35 @@ #include "NumCpp/Linalg/svd.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::linalg { - namespace linalg + //============================================================================ + // Method Description: + /// matrix psuedo-inverse + /// + /// NumPy Reference: https://numpy.org/doc/stable/reference/generated/numpy.linalg.pinv.html + /// + /// @param inArray + /// @return NdArray + /// + template + NdArray pinv(const NdArray& inArray) { - //============================================================================ - // Method Description: - /// matrix psuedo-inverse - /// - /// NumPy Reference: https://numpy.org/doc/stable/reference/generated/numpy.linalg.pinv.html - /// - /// @param inArray - /// @return NdArray - /// - template - NdArray pinv(const NdArray& inArray) - { - STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); - - NdArray u; - NdArray d; - NdArray v; - svd(inArray, u, d, v); + STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); - const auto inShape = inArray.shape(); - auto dPlus = nc::zeros(inShape.cols, inShape.rows); // transpose + NdArray u; + NdArray d; + NdArray v; + svd(inArray, u, d, v); - for (uint32 i = 0; i < d.shape().rows; ++i) - { - dPlus(i, i) = 1. / d(i, i); - } + const auto inShape = inArray.shape(); + auto dPlus = nc::zeros(inShape.cols, inShape.rows); // transpose - return v.transpose().dot(dPlus).dot(u.transpose()); + for (uint32 i = 0; i < d.shape().rows; ++i) + { + dPlus(i, i) = 1. / d(i, i); } - } // namespace linalg -} // namespace nc \ No newline at end of file + + return v.transpose().dot(dPlus).dot(u.transpose()); + } +} // namespace nc::linalg \ No newline at end of file diff --git a/include/NumCpp/Linalg/pivotLU_decomposition.hpp b/include/NumCpp/Linalg/pivotLU_decomposition.hpp index e29c319c4..80dbb6785 100644 --- a/include/NumCpp/Linalg/pivotLU_decomposition.hpp +++ b/include/NumCpp/Linalg/pivotLU_decomposition.hpp @@ -43,87 +43,83 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Utils/essentiallyEqual.hpp" -namespace nc +namespace nc::linalg { - namespace linalg + //============================================================================ + // Method Description: + /// matrix pivot LU decomposition PA = LU + /// + /// @param inMatrix: NdArray to be decomposed + /// + /// @return std::tuple of the decomposed L, U, and P matrices + /// + template + std::tuple, NdArray, NdArray> pivotLU_decomposition(const NdArray& inMatrix) { - //============================================================================ - // Method Description: - /// matrix pivot LU decomposition PA = LU - /// - /// @param inMatrix: NdArray to be decomposed - /// - /// @return std::tuple of the decomposed L, U, and P matrices - /// - template - std::tuple, NdArray, NdArray> - pivotLU_decomposition(const NdArray& inMatrix) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - const auto shape = inMatrix.shape(); + const auto shape = inMatrix.shape(); - if (!shape.issquare()) - { - THROW_RUNTIME_ERROR("Input matrix should be square."); - } + if (!shape.issquare()) + { + THROW_RUNTIME_ERROR("Input matrix should be square."); + } - NdArray lMatrix = zeros_like(inMatrix); - NdArray uMatrix = inMatrix.template astype(); - NdArray pMatrix = eye(shape.rows); + NdArray lMatrix = zeros_like(inMatrix); + NdArray uMatrix = inMatrix.template astype(); + NdArray pMatrix = eye(shape.rows); - for (uint32 k = 0; k < shape.rows; ++k) + for (uint32 k = 0; k < shape.rows; ++k) + { + double max = 0.; + uint32 pk = 0; + for (uint32 i = k; i < shape.rows; ++i) { - double max = 0.; - uint32 pk = 0; - for (uint32 i = k; i < shape.rows; ++i) + double s = 0.; + for (uint32 j = k; j < shape.cols; ++j) { - double s = 0.; - for (uint32 j = k; j < shape.cols; ++j) - { - s += std::fabs(uMatrix(i, j)); - } - - const double q = std::fabs(uMatrix(i, k)) / s; - if (q > max) - { - max = q; - pk = i; - } + s += std::fabs(uMatrix(i, j)); } - if (utils::essentiallyEqual(max, double{ 0. })) + const double q = std::fabs(uMatrix(i, k)) / s; + if (q > max) { - THROW_RUNTIME_ERROR("Division by 0."); + max = q; + pk = i; } + } - if (pk != k) - { - for (uint32 j = 0; j < shape.cols; ++j) - { - std::swap(pMatrix(k, j), pMatrix(pk, j)); - std::swap(lMatrix(k, j), lMatrix(pk, j)); - std::swap(uMatrix(k, j), uMatrix(pk, j)); - } - } + if (utils::essentiallyEqual(max, double{ 0. })) + { + THROW_RUNTIME_ERROR("Division by 0."); + } - for (uint32 i = k + 1; i < shape.rows; ++i) + if (pk != k) + { + for (uint32 j = 0; j < shape.cols; ++j) { - lMatrix(i, k) = uMatrix(i, k) / uMatrix(k, k); - - for (uint32 j = k; j < shape.cols; ++j) - { - uMatrix(i, j) = uMatrix(i, j) - lMatrix(i, k) * uMatrix(k, j); - } + std::swap(pMatrix(k, j), pMatrix(pk, j)); + std::swap(lMatrix(k, j), lMatrix(pk, j)); + std::swap(uMatrix(k, j), uMatrix(pk, j)); } } - for (uint32 k = 0; k < shape.rows; ++k) + for (uint32 i = k + 1; i < shape.rows; ++i) { - lMatrix(k, k) = 1.; + lMatrix(i, k) = uMatrix(i, k) / uMatrix(k, k); + + for (uint32 j = k; j < shape.cols; ++j) + { + uMatrix(i, j) = uMatrix(i, j) - lMatrix(i, k) * uMatrix(k, j); + } } + } - return std::make_tuple(lMatrix, uMatrix, pMatrix); + for (uint32 k = 0; k < shape.rows; ++k) + { + lMatrix(k, k) = 1.; } - } // namespace linalg -} // namespace nc + + return std::make_tuple(lMatrix, uMatrix, pMatrix); + } +} // namespace nc::linalg diff --git a/include/NumCpp/Linalg/solve.hpp b/include/NumCpp/Linalg/solve.hpp index cf6bd3bec..34e69f8aa 100644 --- a/include/NumCpp/Linalg/solve.hpp +++ b/include/NumCpp/Linalg/solve.hpp @@ -33,43 +33,40 @@ #include "NumCpp/Linalg/inv.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::linalg { - namespace linalg + //============================================================================ + // Method Description: + /// Solve a linear matrix equation, or system of linear scalar equations. + /// Computes the “exact” solution, x, of the well-determined, i.e., full rank, + /// linear matrix equation ax = b. + /// + /// https://numpy.org/doc/stable/reference/generated/numpy.linalg.solve.html + /// + /// @param inA + /// @param inB + /// @return NdArray Solution to the system a x = b. Returned shape is identical to b + /// + template + NdArray solve(const NdArray& inA, const NdArray& inB) { - //============================================================================ - // Method Description: - /// Solve a linear matrix equation, or system of linear scalar equations. - /// Computes the “exact” solution, x, of the well-determined, i.e., full rank, - /// linear matrix equation ax = b. - /// - /// https://numpy.org/doc/stable/reference/generated/numpy.linalg.solve.html - /// - /// @param inA - /// @param inB - /// @return NdArray Solution to the system a x = b. Returned shape is identical to b - /// - template - NdArray solve(const NdArray& inA, const NdArray& inB) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (!inA.issquare()) - { - THROW_INVALID_ARGUMENT_ERROR("input array a must be square."); - } + STATIC_ASSERT_ARITHMETIC(dtype); - if (!inB.isflat()) - { - THROW_INVALID_ARGUMENT_ERROR("input array b must be flat."); - } + if (!inA.issquare()) + { + THROW_INVALID_ARGUMENT_ERROR("input array a must be square."); + } - if (inA.numCols() != inB.size()) - { - THROW_INVALID_ARGUMENT_ERROR("input array b size must be the same as the square size of a."); - } + if (!inB.isflat()) + { + THROW_INVALID_ARGUMENT_ERROR("input array b must be flat."); + } - return dot(inv(inA), inB.template astype().reshape(inB.size(), 1)).reshape(inB.shape()); + if (inA.numCols() != inB.size()) + { + THROW_INVALID_ARGUMENT_ERROR("input array b size must be the same as the square size of a."); } - } // namespace linalg -} // namespace nc + + return dot(inv(inA), inB.template astype().reshape(inB.size(), 1)).reshape(inB.shape()); + } +} // namespace nc::linalg diff --git a/include/NumCpp/Linalg/svd.hpp b/include/NumCpp/Linalg/svd.hpp index 0eb50588a..5996c0f65 100644 --- a/include/NumCpp/Linalg/svd.hpp +++ b/include/NumCpp/Linalg/svd.hpp @@ -34,34 +34,31 @@ #include "NumCpp/Linalg/svd/SVDClass.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::linalg { - namespace linalg + //============================================================================ + // Method Description: + /// matrix svd + /// + /// NumPy Reference: https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.svd.html#numpy.linalg.svd + /// + /// @param inArray: NdArray to be SVDed + /// @param outU: NdArray output U + /// @param outS: NdArray output S + /// @param outVt: NdArray output V transpose + /// + template + void svd(const NdArray& inArray, NdArray& outU, NdArray& outS, NdArray& outVt) { - //============================================================================ - // Method Description: - /// matrix svd - /// - /// NumPy Reference: https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.svd.html#numpy.linalg.svd - /// - /// @param inArray: NdArray to be SVDed - /// @param outU: NdArray output U - /// @param outS: NdArray output S - /// @param outVt: NdArray output V transpose - /// - template - void svd(const NdArray& inArray, NdArray& outU, NdArray& outS, NdArray& outVt) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - SVD svdSolver(inArray.template astype()); - outU = svdSolver.u(); + SVD svdSolver(inArray.template astype()); + outU = svdSolver.u(); - NdArray vt = svdSolver.v().transpose(); - outVt = std::move(vt); + NdArray vt = svdSolver.v().transpose(); + outVt = std::move(vt); - NdArray s = diagflat(svdSolver.s(), 0); - outS = std::move(s); - } - } // namespace linalg -} // namespace nc + NdArray s = diagflat(svdSolver.s(), 0); + outS = std::move(s); + } +} // namespace nc::linalg diff --git a/include/NumCpp/Linalg/svd/SVDClass.hpp b/include/NumCpp/Linalg/svd/SVDClass.hpp index ceada22bf..bd73dd91e 100644 --- a/include/NumCpp/Linalg/svd/SVDClass.hpp +++ b/include/NumCpp/Linalg/svd/SVDClass.hpp @@ -36,612 +36,609 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::linalg { - namespace linalg + // ============================================================================= + // Class Description: + /// performs the singular value decomposition of a general matrix, + /// taken and adapted from Numerical Recipes Third Edition svd.h + class SVD { + public: // ============================================================================= - // Class Description: - /// performs the singular value decomposition of a general matrix, - /// taken and adapted from Numerical Recipes Third Edition svd.h - class SVD + // Description: + /// Constructor + /// + /// @param inMatrix: matrix to perform SVD on + /// + explicit SVD(const NdArray& inMatrix) : + m_(inMatrix.shape().rows), + n_(inMatrix.shape().cols), + u_(inMatrix), + v_(n_, n_), + s_(1, n_), + eps_(std::numeric_limits::epsilon()) { - public: - // ============================================================================= - // Description: - /// Constructor - /// - /// @param inMatrix: matrix to perform SVD on - /// - explicit SVD(const NdArray& inMatrix) : - m_(inMatrix.shape().rows), - n_(inMatrix.shape().cols), - u_(inMatrix), - v_(n_, n_), - s_(1, n_), - eps_(std::numeric_limits::epsilon()) - { - decompose(); - reorder(); - tsh_ = 0.5 * std::sqrt(m_ + n_ + 1.) * s_.front() * eps_; - } + decompose(); + reorder(); + tsh_ = 0.5 * std::sqrt(m_ + n_ + 1.) * s_.front() * eps_; + } - // ============================================================================= - // Description: - /// the resultant u matrix - /// - /// @return u matrix - /// - const NdArray& u() noexcept - { - return u_; - } + // ============================================================================= + // Description: + /// the resultant u matrix + /// + /// @return u matrix + /// + const NdArray& u() noexcept + { + return u_; + } - // ============================================================================= - // Description: - /// the resultant v matrix - /// - /// @return v matrix - /// - const NdArray& v() noexcept - { - return v_; - } + // ============================================================================= + // Description: + /// the resultant v matrix + /// + /// @return v matrix + /// + const NdArray& v() noexcept + { + return v_; + } - // ============================================================================= - // Description: - /// the resultant w matrix - /// - /// @return s matrix - /// - const NdArray& s() noexcept - { - return s_; - } + // ============================================================================= + // Description: + /// the resultant w matrix + /// + /// @return s matrix + /// + const NdArray& s() noexcept + { + return s_; + } - // ============================================================================= - // Description: - /// solves the linear least squares problem - /// - /// @param inInput - /// @param inThresh (default -1.) - /// - /// @return NdArray - /// - NdArray solve(const NdArray& inInput, double inThresh = -1.) - { - double ss = 0.; + // ============================================================================= + // Description: + /// solves the linear least squares problem + /// + /// @param inInput + /// @param inThresh (default -1.) + /// + /// @return NdArray + /// + NdArray solve(const NdArray& inInput, double inThresh = -1.) + { + double ss = 0.; - if (inInput.size() != m_) - { - THROW_INVALID_ARGUMENT_ERROR("bad sizes."); - } + if (inInput.size() != m_) + { + THROW_INVALID_ARGUMENT_ERROR("bad sizes."); + } - NdArray returnArray(1, n_); + NdArray returnArray(1, n_); - NdArray tmp(1, n_); + NdArray tmp(1, n_); - tsh_ = (inThresh >= 0. ? inThresh : 0.5 * sqrt(m_ + n_ + 1.) * s_.front() * eps_); + tsh_ = (inThresh >= 0. ? inThresh : 0.5 * sqrt(m_ + n_ + 1.) * s_.front() * eps_); - for (uint32 j = 0; j < n_; j++) + for (uint32 j = 0; j < n_; j++) + { + ss = 0.; + if (s_[j] > tsh_) { - ss = 0.; - if (s_[j] > tsh_) + for (uint32 i = 0; i < m_; i++) { - for (uint32 i = 0; i < m_; i++) - { - ss += u_(i, j) * inInput[i]; - } - ss /= s_[j]; + ss += u_(i, j) * inInput[i]; } - tmp[j] = ss; + ss /= s_[j]; } + tmp[j] = ss; + } - for (uint32 j = 0; j < n_; j++) + for (uint32 j = 0; j < n_; j++) + { + ss = 0.; + for (uint32 jj = 0; jj < n_; jj++) { - ss = 0.; - for (uint32 jj = 0; jj < n_; jj++) - { - ss += v_(j, jj) * tmp[jj]; - } - - returnArray[j] = ss; + ss += v_(j, jj) * tmp[jj]; } - return returnArray; + returnArray[j] = ss; } - private: - // ============================================================================= - // Description: - /// returns the SIGN of two values - /// - /// @param inA - /// @param inB - /// - /// @return value - /// - static double SIGN(double inA, double inB) noexcept - { - return inB >= 0 ? (inA >= 0 ? inA : -inA) : (inA >= 0 ? -inA : inA); - } + return returnArray; + } - // ============================================================================= - // Description: - /// decomposes the input matrix - /// - void decompose() + private: + // ============================================================================= + // Description: + /// returns the SIGN of two values + /// + /// @param inA + /// @param inB + /// + /// @return value + /// + static double SIGN(double inA, double inB) noexcept + { + return inB >= 0 ? (inA >= 0 ? inA : -inA) : (inA >= 0 ? -inA : inA); + } + + // ============================================================================= + // Description: + /// decomposes the input matrix + /// + void decompose() + { + bool flag = true; + uint32 i = 0; + uint32 its = 0; + uint32 j = 0; + uint32 jj = 0; + uint32 k = 0; + uint32 l = 0; + uint32 nm = 0; + + double anorm = 0.; + double c = 0.; + double f = 0.; + double g = 0.; + double h = 0.; + double ss = 0.; + double scale = 0.; + double x = 0.; + double y = 0.; + double z = 0.; + + NdArray rv1(n_, 1); + + for (i = 0; i < n_; ++i) { - bool flag = true; - uint32 i = 0; - uint32 its = 0; - uint32 j = 0; - uint32 jj = 0; - uint32 k = 0; - uint32 l = 0; - uint32 nm = 0; - - double anorm = 0.; - double c = 0.; - double f = 0.; - double g = 0.; - double h = 0.; - double ss = 0.; - double scale = 0.; - double x = 0.; - double y = 0.; - double z = 0.; - - NdArray rv1(n_, 1); - - for (i = 0; i < n_; ++i) + l = i + 2; + rv1[i] = scale * g; + g = ss = scale = 0.; + + if (i < m_) { - l = i + 2; - rv1[i] = scale * g; - g = ss = scale = 0.; + for (k = i; k < m_; ++k) + { + scale += std::abs(u_(k, i)); + } - if (i < m_) + if (scale != 0.) { for (k = i; k < m_; ++k) { - scale += std::abs(u_(k, i)); + u_(k, i) /= scale; + ss += u_(k, i) * u_(k, i); } - if (scale != 0.) + f = u_(i, i); + g = -SIGN(std::sqrt(ss), f); + h = f * g - ss; + u_(i, i) = f - g; + + for (j = l - 1; j < n_; ++j) { - for (k = i; k < m_; ++k) + for (ss = 0., k = i; k < m_; ++k) { - u_(k, i) /= scale; - ss += u_(k, i) * u_(k, i); + ss += u_(k, i) * u_(k, j); } - f = u_(i, i); - g = -SIGN(std::sqrt(ss), f); - h = f * g - ss; - u_(i, i) = f - g; - - for (j = l - 1; j < n_; ++j) - { - for (ss = 0., k = i; k < m_; ++k) - { - ss += u_(k, i) * u_(k, j); - } - - f = ss / h; - - for (k = i; k < m_; ++k) - { - u_(k, j) += f * u_(k, i); - } - } + f = ss / h; for (k = i; k < m_; ++k) { - u_(k, i) *= scale; + u_(k, j) += f * u_(k, i); } } + + for (k = i; k < m_; ++k) + { + u_(k, i) *= scale; + } } + } - s_[i] = scale * g; - g = ss = scale = 0.; + s_[i] = scale * g; + g = ss = scale = 0.; - if (i + 1 <= m_ && i + 1 != n_) + if (i + 1 <= m_ && i + 1 != n_) + { + for (k = l - 1; k < n_; ++k) + { + scale += std::abs(u_(i, k)); + } + + if (scale != 0.) { for (k = l - 1; k < n_; ++k) { - scale += std::abs(u_(i, k)); + u_(i, k) /= scale; + ss += u_(i, k) * u_(i, k); } - if (scale != 0.) - { - for (k = l - 1; k < n_; ++k) - { - u_(i, k) /= scale; - ss += u_(i, k) * u_(i, k); - } - - f = u_(i, l - 1); - g = -SIGN(std::sqrt(ss), f); - h = f * g - ss; - u_(i, l - 1) = f - g; + f = u_(i, l - 1); + g = -SIGN(std::sqrt(ss), f); + h = f * g - ss; + u_(i, l - 1) = f - g; - for (k = l - 1; k < n_; ++k) - { - rv1[k] = u_(i, k) / h; - } + for (k = l - 1; k < n_; ++k) + { + rv1[k] = u_(i, k) / h; + } - for (j = l - 1; j < m_; ++j) + for (j = l - 1; j < m_; ++j) + { + for (ss = 0., k = l - 1; k < n_; ++k) { - for (ss = 0., k = l - 1; k < n_; ++k) - { - ss += u_(j, k) * u_(i, k); - } - - for (k = l - 1; k < n_; ++k) - { - u_(j, k) += ss * rv1[k]; - } + ss += u_(j, k) * u_(i, k); } for (k = l - 1; k < n_; ++k) { - u_(i, k) *= scale; + u_(j, k) += ss * rv1[k]; } } - } - anorm = std::max(anorm, (std::abs(s_[i]) + std::abs(rv1[i]))); + for (k = l - 1; k < n_; ++k) + { + u_(i, k) *= scale; + } + } } - for (i = n_ - 1; i != static_cast(-1); --i) + anorm = std::max(anorm, (std::abs(s_[i]) + std::abs(rv1[i]))); + } + + for (i = n_ - 1; i != static_cast(-1); --i) + { + if (i < n_ - 1) { - if (i < n_ - 1) + if (g != 0.) { - if (g != 0.) + for (j = l; j < n_; ++j) + { + v_(j, i) = (u_(i, j) / u_(i, l)) / g; + } + + for (j = l; j < n_; ++j) { - for (j = l; j < n_; ++j) + for (ss = 0., k = l; k < n_; ++k) { - v_(j, i) = (u_(i, j) / u_(i, l)) / g; + ss += u_(i, k) * v_(k, j); } - for (j = l; j < n_; ++j) + for (k = l; k < n_; ++k) { - for (ss = 0., k = l; k < n_; ++k) - { - ss += u_(i, k) * v_(k, j); - } - - for (k = l; k < n_; ++k) - { - v_(k, j) += ss * v_(k, i); - } + v_(k, j) += ss * v_(k, i); } } + } - for (j = l; j < n_; ++j) - { - v_(i, j) = v_(j, i) = 0.; - } + for (j = l; j < n_; ++j) + { + v_(i, j) = v_(j, i) = 0.; } + } + + v_(i, i) = 1.; + g = rv1[i]; + l = i; + } - v_(i, i) = 1.; - g = rv1[i]; - l = i; + for (i = std::min(m_, n_) - 1; i != static_cast(-1); --i) + { + l = i + 1; + g = s_[i]; + + for (j = l; j < n_; ++j) + { + u_(i, j) = 0.; } - for (i = std::min(m_, n_) - 1; i != static_cast(-1); --i) + if (g != 0.) { - l = i + 1; - g = s_[i]; + g = 1. / g; for (j = l; j < n_; ++j) { - u_(i, j) = 0.; - } - - if (g != 0.) - { - g = 1. / g; - - for (j = l; j < n_; ++j) + for (ss = 0., k = l; k < m_; ++k) { - for (ss = 0., k = l; k < m_; ++k) - { - ss += u_(k, i) * u_(k, j); - } - - f = (ss / u_(i, i)) * g; - - for (k = i; k < m_; ++k) - { - u_(k, j) += f * u_(k, i); - } + ss += u_(k, i) * u_(k, j); } - for (j = i; j < m_; ++j) + f = (ss / u_(i, i)) * g; + + for (k = i; k < m_; ++k) { - u_(j, i) *= g; + u_(k, j) += f * u_(k, i); } } - else + + for (j = i; j < m_; ++j) { - for (j = i; j < m_; ++j) - { - u_(j, i) = 0.; - } + u_(j, i) *= g; } - - ++u_(i, i); } - - for (k = n_ - 1; k != static_cast(-1); --k) + else { - for (its = 0; its < 30; ++its) + for (j = i; j < m_; ++j) { - flag = true; - for (l = k; l != static_cast(-1); --l) - { - nm = l - 1; - if (l == 0 || std::abs(rv1[l]) <= eps_ * anorm) - { - flag = false; - break; - } + u_(j, i) = 0.; + } + } - if (std::abs(s_[nm]) <= eps_ * anorm) - { - break; - } - } + ++u_(i, i); + } - if (flag) + for (k = n_ - 1; k != static_cast(-1); --k) + { + for (its = 0; its < 30; ++its) + { + flag = true; + for (l = k; l != static_cast(-1); --l) + { + nm = l - 1; + if (l == 0 || std::abs(rv1[l]) <= eps_ * anorm) { - c = 0.; - ss = 1.; - for (i = l; i < k + 1; ++i) - { - f = ss * rv1[i]; - rv1[i] = c * rv1[i]; - - if (std::abs(f) <= eps_ * anorm) - { - break; - } - - g = s_[i]; - h = pythag(f, g); - s_[i] = h; - h = 1. / h; - c = g * h; - ss = -f * h; - - for (j = 0; j < m_; ++j) - { - y = u_(j, nm); - z = u_(j, i); - u_(j, nm) = y * c + z * ss; - u_(j, i) = z * c - y * ss; - } - } + flag = false; + break; } - z = s_[k]; - if (l == k) + if (std::abs(s_[nm]) <= eps_ * anorm) { - if (z < 0.) - { - s_[k] = -z; - for (j = 0; j < n_; ++j) - { - v_(j, k) = -v_(j, k); - } - } break; } + } - if (its == 29) + if (flag) + { + c = 0.; + ss = 1.; + for (i = l; i < k + 1; ++i) { - THROW_INVALID_ARGUMENT_ERROR("no convergence in 30 svdcmp iterations"); - } + f = ss * rv1[i]; + rv1[i] = c * rv1[i]; - x = s_[l]; - nm = k - 1; - y = s_[nm]; - g = rv1[nm]; - h = rv1[k]; - f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2. * h * y); - g = pythag(f, 1.); - f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x; - c = ss = 1.; - - for (j = l; j <= nm; j++) - { - i = j + 1; - g = rv1[i]; - y = s_[i]; - h = ss * g; - g = c * g; - z = pythag(f, h); - rv1[j] = z; - c = f / z; - ss = h / z; - f = x * c + g * ss; - g = g * c - x * ss; - h = y * ss; - y *= c; - - for (jj = 0; jj < n_; ++jj) + if (std::abs(f) <= eps_ * anorm) { - x = v_(jj, j); - z = v_(jj, i); - v_(jj, j) = x * c + z * ss; - v_(jj, i) = z * c - x * ss; + break; } - z = pythag(f, h); - s_[j] = z; + g = s_[i]; + h = pythag(f, g); + s_[i] = h; + h = 1. / h; + c = g * h; + ss = -f * h; - if (z != 0.) + for (j = 0; j < m_; ++j) { - z = 1. / z; - c = f * z; - ss = h * z; + y = u_(j, nm); + z = u_(j, i); + u_(j, nm) = y * c + z * ss; + u_(j, i) = z * c - y * ss; } + } + } - f = c * g + ss * y; - x = c * y - ss * g; - - for (jj = 0; jj < m_; ++jj) + z = s_[k]; + if (l == k) + { + if (z < 0.) + { + s_[k] = -z; + for (j = 0; j < n_; ++j) { - y = u_(jj, j); - z = u_(jj, i); - u_(jj, j) = y * c + z * ss; - u_(jj, i) = z * c - y * ss; + v_(j, k) = -v_(j, k); } } - rv1[l] = 0.; - rv1[k] = f; - s_[k] = x; + break; } - } - } - - // ============================================================================= - // Description: - /// reorders the input matrix - /// - void reorder() - { - uint32 i = 0; - uint32 j = 0; - uint32 k = 0; - uint32 ss = 0; - uint32 inc = 1; - double sw = 0.; - NdArray su(m_, 1); - NdArray sv(n_, 1); - - do - { - inc *= 3; - ++inc; - } while (inc <= n_); + if (its == 29) + { + THROW_INVALID_ARGUMENT_ERROR("no convergence in 30 svdcmp iterations"); + } - do - { - inc /= 3; - for (i = inc; i < n_; ++i) + x = s_[l]; + nm = k - 1; + y = s_[nm]; + g = rv1[nm]; + h = rv1[k]; + f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2. * h * y); + g = pythag(f, 1.); + f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x; + c = ss = 1.; + + for (j = l; j <= nm; j++) { - sw = s_[i]; - for (k = 0; k < m_; ++k) + i = j + 1; + g = rv1[i]; + y = s_[i]; + h = ss * g; + g = c * g; + z = pythag(f, h); + rv1[j] = z; + c = f / z; + ss = h / z; + f = x * c + g * ss; + g = g * c - x * ss; + h = y * ss; + y *= c; + + for (jj = 0; jj < n_; ++jj) { - su[k] = u_(k, i); + x = v_(jj, j); + z = v_(jj, i); + v_(jj, j) = x * c + z * ss; + v_(jj, i) = z * c - x * ss; } - for (k = 0; k < n_; ++k) + z = pythag(f, h); + s_[j] = z; + + if (z != 0.) { - sv[k] = v_(k, i); + z = 1. / z; + c = f * z; + ss = h * z; } - j = i; - while (s_[j - inc] < sw) + f = c * g + ss * y; + x = c * y - ss * g; + + for (jj = 0; jj < m_; ++jj) { - s_[j] = s_[j - inc]; + y = u_(jj, j); + z = u_(jj, i); + u_(jj, j) = y * c + z * ss; + u_(jj, i) = z * c - y * ss; + } + } + rv1[l] = 0.; + rv1[k] = f; + s_[k] = x; + } + } + } - for (k = 0; k < m_; ++k) - { - u_(k, j) = u_(k, j - inc); - } + // ============================================================================= + // Description: + /// reorders the input matrix + /// + void reorder() + { + uint32 i = 0; + uint32 j = 0; + uint32 k = 0; + uint32 ss = 0; + uint32 inc = 1; - for (k = 0; k < n_; ++k) - { - v_(k, j) = v_(k, j - inc); - } + double sw = 0.; + NdArray su(m_, 1); + NdArray sv(n_, 1); - j -= inc; + do + { + inc *= 3; + ++inc; + } while (inc <= n_); - if (j < inc) - { - break; - } - } + do + { + inc /= 3; + for (i = inc; i < n_; ++i) + { + sw = s_[i]; + for (k = 0; k < m_; ++k) + { + su[k] = u_(k, i); + } - s_[j] = sw; + for (k = 0; k < n_; ++k) + { + sv[k] = v_(k, i); + } + + j = i; + while (s_[j - inc] < sw) + { + s_[j] = s_[j - inc]; for (k = 0; k < m_; ++k) { - u_(k, j) = su[k]; + u_(k, j) = u_(k, j - inc); } for (k = 0; k < n_; ++k) { - v_(k, j) = sv[k]; + v_(k, j) = v_(k, j - inc); } - } - } while (inc > 1); - for (k = 0; k < n_; ++k) - { - ss = 0; + j -= inc; - for (i = 0; i < m_; i++) - { - if (u_(i, k) < 0.) + if (j < inc) { - ss++; + break; } } - for (j = 0; j < n_; ++j) + s_[j] = sw; + + for (k = 0; k < m_; ++k) { - if (v_(j, k) < 0.) - { - ss++; - } + u_(k, j) = su[k]; } - if (ss > (m_ + n_) / 2) + for (k = 0; k < n_; ++k) { - for (i = 0; i < m_; ++i) - { - u_(i, k) = -u_(i, k); - } - - for (j = 0; j < n_; ++j) - { - v_(j, k) = -v_(j, k); - } + v_(k, j) = sv[k]; } } - } + } while (inc > 1); - // ============================================================================= - // Description: - /// performs pythag of input values - /// - /// @param inA - /// @param inB - /// - /// @return resultant value - /// - static double pythag(double inA, double inB) noexcept + for (k = 0; k < n_; ++k) { - const double absa = std::abs(inA); - const double absb = std::abs(inB); - return (absa > absb ? absa * std::sqrt(1. + utils::sqr(absb / absa)) - : (absb == 0. ? 0. : absb * std::sqrt(1. + utils::sqr(absa / absb)))); + ss = 0; + + for (i = 0; i < m_; i++) + { + if (u_(i, k) < 0.) + { + ss++; + } + } + + for (j = 0; j < n_; ++j) + { + if (v_(j, k) < 0.) + { + ss++; + } + } + + if (ss > (m_ + n_) / 2) + { + for (i = 0; i < m_; ++i) + { + u_(i, k) = -u_(i, k); + } + + for (j = 0; j < n_; ++j) + { + v_(j, k) = -v_(j, k); + } + } } + } - private: - // ===============================Attributes==================================== - const uint32 m_; - const uint32 n_; - NdArray u_; - NdArray v_; - NdArray s_; - double eps_; - double tsh_; - }; - } // namespace linalg -} // namespace nc + // ============================================================================= + // Description: + /// performs pythag of input values + /// + /// @param inA + /// @param inB + /// + /// @return resultant value + /// + static double pythag(double inA, double inB) noexcept + { + const double absa = std::abs(inA); + const double absb = std::abs(inB); + return (absa > absb ? absa * std::sqrt(1. + utils::sqr(absb / absa)) + : (absb == 0. ? 0. : absb * std::sqrt(1. + utils::sqr(absa / absb)))); + } + + private: + // ===============================Attributes==================================== + const uint32 m_{}; + const uint32 n_{}; + NdArray u_{}; + NdArray v_{}; + NdArray s_{}; + double eps_{}; + double tsh_{}; + }; +} // namespace nc::linalg diff --git a/include/NumCpp/NdArray/NdArrayCore.hpp b/include/NumCpp/NdArray/NdArrayCore.hpp index bbf0da608..25f60c820 100644 --- a/include/NumCpp/NdArray/NdArrayCore.hpp +++ b/include/NumCpp/NdArray/NdArrayCore.hpp @@ -979,7 +979,7 @@ namespace nc /// @param inStepSize (default 1) /// @return Slice /// - Slice cSlice(index_type inStartIdx = 0, uint32 inStepSize = 1) const noexcept + [[nodiscard]] Slice cSlice(index_type inStartIdx = 0, uint32 inStepSize = 1) const noexcept { return Slice(inStartIdx, shape_.cols, inStepSize); } @@ -993,7 +993,7 @@ namespace nc /// @param inStepSize (default 1) /// @return Slice /// - Slice rSlice(index_type inStartIdx = 0, uint32 inStepSize = 1) const noexcept + [[nodiscard]] Slice rSlice(index_type inStartIdx = 0, uint32 inStepSize = 1) const noexcept { return Slice(inStartIdx, shape_.rows, inStepSize); } @@ -1026,7 +1026,7 @@ namespace nc /// @param inIndex /// @return value /// - const_reference at(index_type inIndex) const + [[nodiscard]] const_reference at(index_type inIndex) const { // this doesn't allow for calling the first element as -size_... // but why would you really want to do that anyway? @@ -1079,7 +1079,7 @@ namespace nc /// @param inColIndex /// @return value /// - const_reference at(index_type inRowIndex, index_type inColIndex) const + [[nodiscard]] const_reference at(index_type inRowIndex, index_type inColIndex) const { // this doesn't allow for calling the first element as -size_... // but why would you really want to do that anyway? @@ -1109,7 +1109,7 @@ namespace nc /// @param inSlice /// @return Ndarray /// - NdArray at(const Slice& inSlice) const + [[nodiscard]] NdArray at(const Slice& inSlice) const { // the slice operator already provides bounds checking. just including // the at method for completeness @@ -1124,7 +1124,7 @@ namespace nc /// @param inColSlice /// @return Ndarray /// - NdArray at(const Slice& inRowSlice, const Slice& inColSlice) const + [[nodiscard]] NdArray at(const Slice& inRowSlice, const Slice& inColSlice) const { // the slice operator already provides bounds checking. just including // the at method for completeness @@ -1139,7 +1139,7 @@ namespace nc /// @param inColIndex /// @return Ndarray /// - NdArray at(const Slice& inRowSlice, index_type inColIndex) const + [[nodiscard]] NdArray at(const Slice& inRowSlice, index_type inColIndex) const { // the slice operator already provides bounds checking. just including // the at method for completeness @@ -1154,7 +1154,7 @@ namespace nc /// @param inColSlice /// @return Ndarray /// - NdArray at(index_type inRowIndex, const Slice& inColSlice) const + [[nodiscard]] NdArray at(index_type inRowIndex, const Slice& inColSlice) const { // the slice operator already provides bounds checking. just including // the at method for completeness @@ -1169,7 +1169,8 @@ namespace nc /// @param colIndices /// @return Ndarray /// - NdArray at(const NdArray& rowIndices, const NdArray& colIndices) const + [[nodiscard]] NdArray at(const NdArray& rowIndices, + const NdArray& colIndices) const { // the slice operator already provides bounds checking. just including // the at method for completeness @@ -1208,7 +1209,7 @@ namespace nc /// const iterator to the beginning of the flattened array /// @return const_iterator /// - const_iterator begin() const noexcept + [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } @@ -1220,7 +1221,7 @@ namespace nc /// @param inRow /// @return const_iterator /// - const_iterator begin(size_type inRow) const + [[nodiscard]] const_iterator begin(size_type inRow) const { return cbegin(inRow); } @@ -1231,7 +1232,7 @@ namespace nc /// /// @return const_iterator /// - const_iterator cbegin() const noexcept + [[nodiscard]] const_iterator cbegin() const noexcept { return const_iterator(array_); } @@ -1243,7 +1244,7 @@ namespace nc /// @param inRow /// @return const_iterator /// - const_iterator cbegin(size_type inRow) const + [[nodiscard]] const_iterator cbegin(size_type inRow) const { if (inRow >= shape_.rows) { @@ -1285,7 +1286,7 @@ namespace nc /// const column_iterator to the beginning of the flattened array /// @return const_column_iterator /// - const_column_iterator colbegin() const noexcept + [[nodiscard]] const_column_iterator colbegin() const noexcept { return ccolbegin(); } @@ -1297,7 +1298,7 @@ namespace nc /// @param inCol /// @return const_column_iterator /// - const_column_iterator colbegin(size_type inCol) const + [[nodiscard]] const_column_iterator colbegin(size_type inCol) const { return ccolbegin(inCol); } @@ -1308,7 +1309,7 @@ namespace nc /// /// @return const_column_iterator /// - const_column_iterator ccolbegin() const noexcept + [[nodiscard]] const_column_iterator ccolbegin() const noexcept { return const_column_iterator(array_, shape_.rows, shape_.cols); } @@ -1320,7 +1321,7 @@ namespace nc /// @param inCol /// @return const_column_iterator /// - const_column_iterator ccolbegin(size_type inCol) const + [[nodiscard]] const_column_iterator ccolbegin(size_type inCol) const { if (inCol >= shape_.cols) { @@ -1362,7 +1363,7 @@ namespace nc /// const iterator to the beginning of the flattened array /// @return const_iterator /// - const_reverse_iterator rbegin() const noexcept + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return crbegin(); } @@ -1374,7 +1375,7 @@ namespace nc /// @param inRow /// @return const_iterator /// - const_reverse_iterator rbegin(size_type inRow) const + [[nodiscard]] const_reverse_iterator rbegin(size_type inRow) const { return crbegin(inRow); } @@ -1385,7 +1386,7 @@ namespace nc /// /// @return const_reverse_iterator /// - const_reverse_iterator crbegin() const noexcept + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } @@ -1397,7 +1398,7 @@ namespace nc /// @param inRow /// @return const_reverse_iterator /// - const_reverse_iterator crbegin(size_type inRow) const + [[nodiscard]] const_reverse_iterator crbegin(size_type inRow) const { if (inRow >= shape_.rows) { @@ -1439,7 +1440,7 @@ namespace nc /// const iterator to the beginning of the flattened array /// @return const_iterator /// - const_reverse_column_iterator rcolbegin() const noexcept + [[nodiscard]] const_reverse_column_iterator rcolbegin() const noexcept { return crcolbegin(); } @@ -1451,7 +1452,7 @@ namespace nc /// @param inCol /// @return const_iterator /// - const_reverse_column_iterator rcolbegin(size_type inCol) const + [[nodiscard]] const_reverse_column_iterator rcolbegin(size_type inCol) const { return crcolbegin(inCol); } @@ -1462,7 +1463,7 @@ namespace nc /// /// @return const_reverse_column_iterator /// - const_reverse_column_iterator crcolbegin() const noexcept + [[nodiscard]] const_reverse_column_iterator crcolbegin() const noexcept { return const_reverse_column_iterator(ccolend()); } @@ -1474,7 +1475,7 @@ namespace nc /// @param inCol /// @return const_reverse_column_iterator /// - const_reverse_column_iterator crcolbegin(size_type inCol) const + [[nodiscard]] const_reverse_column_iterator crcolbegin(size_type inCol) const { if (inCol >= shape_.cols) { @@ -1516,7 +1517,7 @@ namespace nc /// const iterator to 1 past the end of the flattened array /// @return const_iterator /// - const_iterator end() const noexcept + [[nodiscard]] const_iterator end() const noexcept { return cend(); } @@ -1528,7 +1529,7 @@ namespace nc /// @param inRow /// @return const_iterator /// - const_iterator end(size_type inRow) const + [[nodiscard]] const_iterator end(size_type inRow) const { return cend(inRow); } @@ -1539,7 +1540,7 @@ namespace nc /// /// @return const_iterator /// - const_iterator cend() const noexcept + [[nodiscard]] const_iterator cend() const noexcept { return cbegin() += size_; } @@ -1551,7 +1552,7 @@ namespace nc /// @param inRow /// @return const_iterator /// - const_iterator cend(size_type inRow) const + [[nodiscard]] const_iterator cend(size_type inRow) const { if (inRow >= shape_.rows) { @@ -1593,7 +1594,7 @@ namespace nc /// const_reverse_iterator to 1 past the end of the flattened array /// @return const_reverse_iterator /// - const_reverse_iterator rend() const noexcept + [[nodiscard]] const_reverse_iterator rend() const noexcept { return crend(); } @@ -1605,7 +1606,7 @@ namespace nc /// @param inRow /// @return const_reverse_iterator /// - const_reverse_iterator rend(size_type inRow) const + [[nodiscard]] const_reverse_iterator rend(size_type inRow) const { return crend(inRow); } @@ -1616,7 +1617,7 @@ namespace nc /// /// @return const_reverse_iterator /// - const_reverse_iterator crend() const noexcept + [[nodiscard]] const_reverse_iterator crend() const noexcept { return crbegin() += size_; } @@ -1628,7 +1629,7 @@ namespace nc /// @param inRow /// @return const_reverse_iterator /// - const_reverse_iterator crend(size_type inRow) const + [[nodiscard]] const_reverse_iterator crend(size_type inRow) const { if (inRow >= shape_.rows) { @@ -1670,7 +1671,7 @@ namespace nc /// const column_iterator to 1 past the end of the flattened array /// @return const_column_iterator /// - const_column_iterator colend() const noexcept + [[nodiscard]] const_column_iterator colend() const noexcept { return ccolend(); } @@ -1682,7 +1683,7 @@ namespace nc /// @param inCol /// @return const_column_iterator /// - const_column_iterator colend(size_type inCol) const + [[nodiscard]] const_column_iterator colend(size_type inCol) const { return ccolend(inCol); } @@ -1693,7 +1694,7 @@ namespace nc /// /// @return const_column_iterator /// - const_column_iterator ccolend() const noexcept + [[nodiscard]] const_column_iterator ccolend() const noexcept { return ccolbegin() += size_; } @@ -1705,7 +1706,7 @@ namespace nc /// @param inCol /// @return const_column_iterator /// - const_column_iterator ccolend(size_type inCol) const + [[nodiscard]] const_column_iterator ccolend(size_type inCol) const { if (inCol >= shape_.cols) { @@ -1747,7 +1748,7 @@ namespace nc /// const_reverse_column_iterator to 1 past the end of the flattened array /// @return const_reverse_column_iterator /// - const_reverse_column_iterator rcolend() const noexcept + [[nodiscard]] const_reverse_column_iterator rcolend() const noexcept { return crcolend(); } @@ -1759,7 +1760,7 @@ namespace nc /// @param inCol /// @return const_reverse_column_iterator /// - const_reverse_column_iterator rcolend(size_type inCol) const + [[nodiscard]] const_reverse_column_iterator rcolend(size_type inCol) const { return crcolend(inCol); } @@ -1770,7 +1771,7 @@ namespace nc /// /// @return const_reverse_column_iterator /// - const_reverse_column_iterator crcolend() const noexcept + [[nodiscard]] const_reverse_column_iterator crcolend() const noexcept { return crcolbegin() += size_; } @@ -1782,7 +1783,7 @@ namespace nc /// @param inCol /// @return const_reverse_column_iterator /// - const_reverse_column_iterator crcolend(size_type inCol) const + [[nodiscard]] const_reverse_column_iterator crcolend(size_type inCol) const { if (inCol >= shape_.cols) { @@ -1801,7 +1802,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return NdArray /// - NdArray all(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray all(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -1845,7 +1846,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return NdArray /// - NdArray any(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray any(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -1890,7 +1891,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return NdArray /// - NdArray argmax(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray argmax(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -1937,7 +1938,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return NdArray /// - NdArray argmin(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray argmin(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -1983,7 +1984,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return NdArray /// - NdArray argsort(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray argsort(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -2047,7 +2048,7 @@ namespace nc std::enable_if_t, int> = 0, std::enable_if_t, int> = 0, std::enable_if_t, int> = 0> - NdArray astype() const + [[nodiscard]] NdArray astype() const { NdArray outArray(shape_); stl_algorithms::transform(cbegin(), @@ -2072,7 +2073,7 @@ namespace nc std::enable_if_t, int> = 0, std::enable_if_t, int> = 0, std::enable_if_t, int> = 0> - NdArray astype() const + [[nodiscard]] NdArray astype() const { NdArray outArray(shape_); @@ -2098,7 +2099,7 @@ namespace nc std::enable_if_t, int> = 0, std::enable_if_t, int> = 0, std::enable_if_t, int> = 0> - NdArray astype() const + [[nodiscard]] NdArray astype() const { NdArray outArray(shape_); @@ -2131,7 +2132,7 @@ namespace nc std::enable_if_t, int> = 0, std::enable_if_t, int> = 0, std::enable_if_t, int> = 0> - NdArray astype() const + [[nodiscard]] NdArray astype() const { NdArray outArray(shape_); @@ -2148,7 +2149,7 @@ namespace nc /// /// @return dtype /// - const_reference back() const noexcept + [[nodiscard]] const_reference back() const noexcept { return *(cend() - 1); } @@ -2170,7 +2171,7 @@ namespace nc /// /// @return dtype /// - const_reference back(size_type row) const + [[nodiscard]] const_reference back(size_type row) const { return *(cend(row) - 1); } @@ -2234,7 +2235,7 @@ namespace nc /// @param inMax: max value to clip to /// @return clipped value /// - NdArray clip(value_type inMin, value_type inMax) const + [[nodiscard]] NdArray clip(value_type inMin, value_type inMax) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -2286,7 +2287,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return bool /// - NdArray contains(value_type inValue, Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray contains(value_type inValue, Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -2327,7 +2328,7 @@ namespace nc /// /// @return NdArray /// - NdArray copy() const + [[nodiscard]] NdArray copy() const { return NdArray(*this); } @@ -2341,7 +2342,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return NdArray /// - NdArray cumprod(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray cumprod(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -2393,7 +2394,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return NdArray /// - NdArray cumsum(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray cumsum(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -2451,7 +2452,7 @@ namespace nc /// Returns the raw pointer to the underlying data /// @return const_pointer /// - const_pointer data() const noexcept + [[nodiscard]] const_pointer data() const noexcept { return array_; } @@ -2480,7 +2481,7 @@ namespace nc /// @param inAxis: (Optional, default ROW) axis the offset is applied to /// @return NdArray /// - NdArray diagonal(index_type inOffset = 0, Axis inAxis = Axis::ROW) const + [[nodiscard]] NdArray diagonal(index_type inOffset = 0, Axis inAxis = Axis::ROW) const { switch (inAxis) { @@ -2530,7 +2531,7 @@ namespace nc /// @param inOtherArray /// @return dot product /// - NdArray dot(const NdArray& inOtherArray) const + [[nodiscard]] NdArray dot(const NdArray& inOtherArray) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -2603,7 +2604,7 @@ namespace nc /// /// @return Endian /// - Endian endianess() const noexcept + [[nodiscard]] Endian endianess() const noexcept { STATIC_ASSERT_ARITHMETIC(dtype); @@ -2632,7 +2633,7 @@ namespace nc /// /// @return NdArray /// - NdArray flatnonzero() const + [[nodiscard]] NdArray flatnonzero() const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -2658,7 +2659,7 @@ namespace nc /// /// @return NdArray /// - NdArray flatten() const + [[nodiscard]] NdArray flatten() const { NdArray outArray(1, size_); stl_algorithms::copy(cbegin(), cend(), outArray.begin()); @@ -2671,7 +2672,7 @@ namespace nc /// /// @return dtype /// - const_reference front() const noexcept + [[nodiscard]] const_reference front() const noexcept { return *cbegin(); } @@ -2693,7 +2694,7 @@ namespace nc /// /// @return dtype /// - const_reference front(size_type row) const + [[nodiscard]] const_reference front(size_type row) const { return *cbegin(row); } @@ -2716,7 +2717,7 @@ namespace nc /// @param inIndices /// @return values /// - NdArray getByIndices(const NdArray& inIndices) const + [[nodiscard]] NdArray getByIndices(const NdArray& inIndices) const { return operator[](inIndices); } @@ -2730,7 +2731,7 @@ namespace nc /// @param inMask /// @return values /// - NdArray getByMask(const NdArray& inMask) const + [[nodiscard]] NdArray getByMask(const NdArray& inMask) const { return operator[](inMask); } @@ -2766,7 +2767,7 @@ namespace nc /// @param inAxis /// @return boolean /// - NdArray issorted(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray issorted(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -2819,7 +2820,7 @@ namespace nc /// /// @return array element /// - value_type item() const + [[nodiscard]] value_type item() const { if (size_ != 1) { @@ -2838,7 +2839,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return NdArray /// - NdArray max(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray max(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -2882,7 +2883,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return NdArray /// - NdArray min(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray min(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -2928,7 +2929,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return NdArray /// - NdArray median(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray median(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -3030,7 +3031,7 @@ namespace nc /// /// @return number of bytes /// - uint64 nbytes() const noexcept + [[nodiscard]] uint64 nbytes() const noexcept { return static_cast(sizeof(dtype) * size_); } @@ -3045,7 +3046,7 @@ namespace nc /// @param inEndianess /// @return NdArray /// - NdArray newbyteorder(Endian inEndianess) const + [[nodiscard]] NdArray newbyteorder(Endian inEndianess) const { STATIC_ASSERT_INTEGER(dtype); @@ -3208,7 +3209,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return NdArray /// - NdArray none(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray none(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -3262,7 +3263,7 @@ namespace nc /// /// @return uint32 /// - uint32 numCols() const noexcept + [[nodiscard]] uint32 numCols() const noexcept { return shape_.cols; } @@ -3274,7 +3275,7 @@ namespace nc /// /// @return uint32 /// - uint32 numRows() const noexcept + [[nodiscard]] uint32 numRows() const noexcept { return shape_.rows; } @@ -3399,7 +3400,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return NdArray /// - NdArray prod(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray prod(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -3443,7 +3444,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return NdArray /// - NdArray ptp(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray ptp(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -3819,7 +3820,7 @@ namespace nc /// @param inNumCols /// @return NdArray /// - NdArray repeat(uint32 inNumRows, uint32 inNumCols) const + [[nodiscard]] NdArray repeat(uint32 inNumRows, uint32 inNumCols) const { NdArray returnArray(shape_.rows * inNumRows, shape_.cols * inNumCols); @@ -3860,7 +3861,7 @@ namespace nc /// @param inRepeatShape /// @return NdArray /// - NdArray repeat(const Shape& inRepeatShape) const + [[nodiscard]] NdArray repeat(const Shape& inRepeatShape) const { return repeat(inRepeatShape.rows, inRepeatShape.cols); } @@ -4068,7 +4069,7 @@ namespace nc /// @param inNumDecimals (default 0) /// @return NdArray /// - NdArray round(uint8 inNumDecimals = 0) const + [[nodiscard]] NdArray round(uint8 inNumDecimals = 0) const { STATIC_ASSERT_FLOAT(dtype); @@ -4102,7 +4103,7 @@ namespace nc /// /// @return Shape /// - Shape shape() const noexcept + [[nodiscard]] Shape shape() const noexcept { return shape_; } @@ -4115,7 +4116,7 @@ namespace nc /// /// @return size /// - size_type size() const noexcept + [[nodiscard]] size_type size() const noexcept { return size_; } @@ -4172,7 +4173,7 @@ namespace nc /// /// @return string /// - std::string str() const + [[nodiscard]] std::string str() const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -4208,7 +4209,7 @@ namespace nc /// @param inAxis (Optional, default NONE) /// @return NdArray /// - NdArray sum(Axis inAxis = Axis::NONE) const + [[nodiscard]] NdArray sum(Axis inAxis = Axis::NONE) const { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -4249,7 +4250,7 @@ namespace nc /// /// @return NdArray /// - NdArray swapaxes() const + [[nodiscard]] NdArray swapaxes() const { return transpose(); } @@ -4349,7 +4350,7 @@ namespace nc /// /// @return NdArray /// - NdArray toIndices(Slice inSlice, Axis inAxis = Axis::ROW) const + [[nodiscard]] NdArray toIndices(Slice inSlice, Axis inAxis = Axis::ROW) const { uint32 numElements = 0; switch (inAxis) @@ -4397,7 +4398,7 @@ namespace nc /// /// @return std::vector /// - std::vector toStlVector() const + [[nodiscard]] std::vector toStlVector() const { return std::vector(cbegin(), cend()); } @@ -4414,7 +4415,7 @@ namespace nc /// /// @return value /// - value_type trace(uint32 inOffset = 0, Axis inAxis = Axis::ROW) const noexcept + [[nodiscard]] value_type trace(uint32 inOffset = 0, Axis inAxis = Axis::ROW) const noexcept { STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); @@ -4467,7 +4468,7 @@ namespace nc /// /// @return NdArray /// - NdArray transpose() const + [[nodiscard]] NdArray transpose() const { NdArray transArray(shape_.cols, shape_.rows); for (uint32 row = 0; row < shape_.rows; ++row) diff --git a/include/NumCpp/Polynomial/Poly1d.hpp b/include/NumCpp/Polynomial/Poly1d.hpp index d35ea77b9..b0c9dc994 100644 --- a/include/NumCpp/Polynomial/Poly1d.hpp +++ b/include/NumCpp/Polynomial/Poly1d.hpp @@ -45,581 +45,573 @@ #include "NumCpp/Utils/num2str.hpp" #include "NumCpp/Utils/power.hpp" -namespace nc +namespace nc::polynomial { - namespace polynomial + //================================================================================ + /// A one-dimensional polynomial class. + /// A convenience class, used to encapsulate "natural" + /// operations on polynomials + template + class Poly1d { - //================================================================================ - /// A one-dimensional polynomial class. - /// A convenience class, used to encapsulate "natural" - /// operations on polynomials - template - class Poly1d + private: + STATIC_ASSERT_ARITHMETIC(dtype); + + public: + //============================================================================ + // Method Description: + /// Default Constructor (not very usefull, but needed for other + /// containers. + /// + Poly1d() = default; + + //============================================================================ + // Method Description: + /// Constructor + /// + /// @param inValues: (polynomial coefficients in ascending order of power if second input is false, + /// polynomial roots if second input is true) + /// @param isRoots + /// + Poly1d(const NdArray& inValues, bool isRoots = false) { - private: - STATIC_ASSERT_ARITHMETIC(dtype); - - public: - //============================================================================ - // Method Description: - /// Default Constructor (not very usefull, but needed for other - /// containers. - /// - Poly1d() = default; - - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param inValues: (polynomial coefficients in ascending order of power if second input is false, - /// polynomial roots if second input is true) - /// @param isRoots - /// - Poly1d(const NdArray& inValues, bool isRoots = false) - { - if (inValues.size() > DtypeInfo::max()) - { - THROW_INVALID_ARGUMENT_ERROR("can only make a polynomial of order " + - utils::num2str(DtypeInfo::max())); - } + if (inValues.size() > DtypeInfo::max()) + { + THROW_INVALID_ARGUMENT_ERROR("can only make a polynomial of order " + + utils::num2str(DtypeInfo::max())); + } - if (isRoots) - { - coefficients_.push_back(1); - for (auto value : inValues) - { - NdArray coeffs = { -(value), static_cast(1) }; - *this *= Poly1d(coeffs, !isRoots); - } - } - else + if (isRoots) + { + coefficients_.push_back(1); + for (auto value : inValues) { - for (auto value : inValues) - { - coefficients_.push_back(value); - } + NdArray coeffs = { -(value), static_cast(1) }; + *this *= Poly1d(coeffs, !isRoots); } } - - //============================================================================ - // Method Description: - /// Returns the area under the curve between the two bounds - /// - /// @param a: the lower bound - /// @param b: the upper bound - /// @return double - /// - double area(double a, double b) const + else { - if (a > b) + for (auto value : inValues) { - std::swap(a, b); + coefficients_.push_back(value); } - - auto polyIntegral = integ(); - return polyIntegral(b) - polyIntegral(a); } - - //============================================================================ - // Method Description: - /// Returns a copy of the polynomial of the new type - /// - /// @return Poly1d - /// - template - Poly1d astype() const + } + + //============================================================================ + // Method Description: + /// Returns the area under the curve between the two bounds + /// + /// @param a: the lower bound + /// @param b: the upper bound + /// @return double + /// + [[nodiscard]] double area(double a, double b) const + { + if (a > b) { - auto newCoefficients = NdArray(1, static_cast(coefficients_.size())); + std::swap(a, b); + } - const auto function = [](dtype value) -> dtypeOut { return static_cast(value); }; + auto polyIntegral = integ(); + return polyIntegral(b) - polyIntegral(a); + } + + //============================================================================ + // Method Description: + /// Returns a copy of the polynomial of the new type + /// + /// @return Poly1d + /// + template + Poly1d astype() const + { + auto newCoefficients = NdArray(1, static_cast(coefficients_.size())); - stl_algorithms::transform(coefficients_.begin(), - coefficients_.end(), - newCoefficients.begin(), - function); + const auto function = [](dtype value) -> dtypeOut { return static_cast(value); }; - return Poly1d(newCoefficients); - } + stl_algorithms::transform(coefficients_.begin(), coefficients_.end(), newCoefficients.begin(), function); + + return Poly1d(newCoefficients); + } - //============================================================================ - // Method Description: - /// Returns the Poly1d coefficients - /// - /// @return NdArray - /// - NdArray coefficients() const + //============================================================================ + // Method Description: + /// Returns the Poly1d coefficients + /// + /// @return NdArray + /// + [[nodiscard]] NdArray coefficients() const + { + auto coefficientsCopy = coefficients_; + return NdArray(coefficientsCopy); + } + + //============================================================================ + // Method Description: + /// Takes the derivative of the polynomial + /// + /// @return Poly1d + [[nodiscard]] Poly1d deriv() const + { + const auto numCoefficients = static_cast(coefficients_.size()); + if (numCoefficients == 0) { - auto coefficientsCopy = coefficients_; - return NdArray(coefficientsCopy); + return {}; } - - //============================================================================ - // Method Description: - /// Takes the derivative of the polynomial - /// - /// @return Poly1d - Poly1d deriv() const + if (numCoefficients == 1) { - const auto numCoefficients = static_cast(coefficients_.size()); - if (numCoefficients == 0) - { - return {}; - } - if (numCoefficients == 1) - { - return Poly1d({ 0 }); - } + return Poly1d({ 0 }); + } - NdArray derivativeCofficients(1, numCoefficients - 1); + NdArray derivativeCofficients(1, numCoefficients - 1); - uint32 counter = 0; - for (uint32 i = 1; i < numCoefficients; ++i) - { - derivativeCofficients[counter++] = coefficients_[i] * i; - } - - return Poly1d(derivativeCofficients); + uint32 counter = 0; + for (uint32 i = 1; i < numCoefficients; ++i) + { + derivativeCofficients[counter++] = coefficients_[i] * i; } - //============================================================================ - // Method Description: - /// Polynomial linear least squares regression: Ax = b - /// - /// @param xValues: the x measurements [1, n] or [n, 1] array - /// @param yValues: the y measurements [n, 1] array - /// @param polyOrder: the order of the poly nomial to fit - /// @return Poly1d - static Poly1d fit(const NdArray& xValues, const NdArray& yValues, uint8 polyOrder) + return Poly1d(derivativeCofficients); + } + + //============================================================================ + // Method Description: + /// Polynomial linear least squares regression: Ax = b + /// + /// @param xValues: the x measurements [1, n] or [n, 1] array + /// @param yValues: the y measurements [n, 1] array + /// @param polyOrder: the order of the poly nomial to fit + /// @return Poly1d + static Poly1d fit(const NdArray& xValues, const NdArray& yValues, uint8 polyOrder) + { + const auto numMeasurements = xValues.size(); + + if (yValues.size() != numMeasurements) { - const auto numMeasurements = xValues.size(); + THROW_INVALID_ARGUMENT_ERROR("Input x and y arrays must be of equal size."); + } - if (yValues.size() != numMeasurements) - { - THROW_INVALID_ARGUMENT_ERROR("Input x and y arrays must be of equal size."); - } + if (!xValues.isflat()) + { + THROW_INVALID_ARGUMENT_ERROR("Input x must be a flattened [1, n] or [n, 1] array."); + } - if (!xValues.isflat()) - { - THROW_INVALID_ARGUMENT_ERROR("Input x must be a flattened [1, n] or [n, 1] array."); - } + if (!yValues.isflat()) + { + THROW_INVALID_ARGUMENT_ERROR("Input y must be a flattened [n, 1] array."); + } - if (!yValues.isflat()) + NdArray a(numMeasurements, polyOrder + 1); + for (uint32 measIdx = 0; measIdx < numMeasurements; ++measIdx) + { + const auto xDouble = static_cast(xValues[measIdx]); + for (uint8 order = 0; order < a.numCols(); ++order) { - THROW_INVALID_ARGUMENT_ERROR("Input y must be a flattened [n, 1] array."); + a(measIdx, order) = utils::power(xDouble, order); } + } - NdArray a(numMeasurements, polyOrder + 1); - for (uint32 measIdx = 0; measIdx < numMeasurements; ++measIdx) - { - const auto xDouble = static_cast(xValues[measIdx]); - for (uint8 order = 0; order < a.numCols(); ++order) - { - a(measIdx, order) = utils::power(xDouble, order); - } - } + NdArray aInv; + if (a.issquare()) + { + aInv = linalg::inv(a); + } + else + { + // psuedo-inverse + auto aT = a.transpose(); + auto aTaInv = linalg::inv(aT.dot(a)); + aInv = aTaInv.dot(aT); + } - NdArray aInv; - if (a.issquare()) - { - aInv = linalg::inv(a); - } - else - { - // psuedo-inverse - auto aT = a.transpose(); - auto aTaInv = linalg::inv(aT.dot(a)); - aInv = aTaInv.dot(aT); - } + auto x = aInv.dot(yValues.template astype()); + return Poly1d(x); + } + + //============================================================================ + // Method Description: + /// Polynomial linear least squares regression: Ax = b + /// + /// @param xValues: the x measurements [1, n] or [n, 1] array + /// @param yValues: the y measurements [n, 1] array + /// @param weights: the measurement weights [1, n] or [n, 1] array + /// @param polyOrder: the order of the poly nomial to fit + /// @return Poly1d + static Poly1d fit(const NdArray& xValues, + const NdArray& yValues, + const NdArray& weights, + uint8 polyOrder) + { + const auto numMeasurements = xValues.size(); - auto x = aInv.dot(yValues.template astype()); - return Poly1d(x); + if (yValues.size() != numMeasurements) + { + THROW_INVALID_ARGUMENT_ERROR("Input x and y arrays must be of equal size."); } - //============================================================================ - // Method Description: - /// Polynomial linear least squares regression: Ax = b - /// - /// @param xValues: the x measurements [1, n] or [n, 1] array - /// @param yValues: the y measurements [n, 1] array - /// @param weights: the measurement weights [1, n] or [n, 1] array - /// @param polyOrder: the order of the poly nomial to fit - /// @return Poly1d - static Poly1d fit(const NdArray& xValues, - const NdArray& yValues, - const NdArray& weights, - uint8 polyOrder) + if (weights.size() != numMeasurements) { - const auto numMeasurements = xValues.size(); - - if (yValues.size() != numMeasurements) - { - THROW_INVALID_ARGUMENT_ERROR("Input x and y arrays must be of equal size."); - } - - if (weights.size() != numMeasurements) - { - THROW_INVALID_ARGUMENT_ERROR("Input x and weights arrays must be of equal size."); - } + THROW_INVALID_ARGUMENT_ERROR("Input x and weights arrays must be of equal size."); + } - if (!xValues.isflat()) - { - THROW_INVALID_ARGUMENT_ERROR("Input x must be a flattened [1, n] or [n, 1] array."); - } + if (!xValues.isflat()) + { + THROW_INVALID_ARGUMENT_ERROR("Input x must be a flattened [1, n] or [n, 1] array."); + } - if (!yValues.isflat()) - { - THROW_INVALID_ARGUMENT_ERROR("Input y must be a flattened [n, 1] array."); - } + if (!yValues.isflat()) + { + THROW_INVALID_ARGUMENT_ERROR("Input y must be a flattened [n, 1] array."); + } - if (!weights.isflat()) - { - THROW_INVALID_ARGUMENT_ERROR("Input weights must be a flattened [1, n] or [n, 1] array."); - } + if (!weights.isflat()) + { + THROW_INVALID_ARGUMENT_ERROR("Input weights must be a flattened [1, n] or [n, 1] array."); + } - NdArray a(numMeasurements, polyOrder + 1); - for (uint32 measIdx = 0; measIdx < numMeasurements; ++measIdx) + NdArray a(numMeasurements, polyOrder + 1); + for (uint32 measIdx = 0; measIdx < numMeasurements; ++measIdx) + { + const auto xDouble = static_cast(xValues[measIdx]); + for (uint8 order = 0; order < a.numCols(); ++order) { - const auto xDouble = static_cast(xValues[measIdx]); - for (uint8 order = 0; order < a.numCols(); ++order) - { - a(measIdx, order) = utils::power(xDouble, order); - } + a(measIdx, order) = utils::power(xDouble, order); } + } - NdArray aWeighted(a.shape()); - NdArray yWeighted(yValues.shape()); + NdArray aWeighted(a.shape()); + NdArray yWeighted(yValues.shape()); - for (uint32 measIdx = 0; measIdx < numMeasurements; ++measIdx) - { - const auto weight = static_cast(weights[measIdx]); - - yWeighted[measIdx] = yValues[measIdx] * weight; - for (uint8 order = 0; order < a.numCols(); ++order) - { - aWeighted(measIdx, order) = a(measIdx, order) * weight; - } - } + for (uint32 measIdx = 0; measIdx < numMeasurements; ++measIdx) + { + const auto weight = static_cast(weights[measIdx]); - NdArray aInv; - if (aWeighted.issquare()) + yWeighted[measIdx] = yValues[measIdx] * weight; + for (uint8 order = 0; order < a.numCols(); ++order) { - aInv = linalg::inv(aWeighted); + aWeighted(measIdx, order) = a(measIdx, order) * weight; } - else - { - // psuedo-inverse - auto aT = a.transpose(); - auto aTaInv = linalg::inv(aT.dot(aWeighted)); - aInv = aTaInv.dot(aT); - } - - auto x = aInv.dot(yWeighted); - return Poly1d(x); } - //============================================================================ - // Method Description: - /// Calculates the integral of the polynomial - /// - /// @return Poly1d - Poly1d integ() const + NdArray aInv; + if (aWeighted.issquare()) { - const auto numCoefficients = static_cast(coefficients_.size()); - if (numCoefficients == 0) - { - return {}; - } - - NdArray integralCofficients(1, numCoefficients + 1); - integralCofficients[0] = 0.; - - for (uint32 i = 0; i < numCoefficients; ++i) - { - integralCofficients[i + 1] = static_cast(coefficients_[i]) / static_cast(i + 1); - } - - return Poly1d(integralCofficients); + aInv = linalg::inv(aWeighted); } - - //============================================================================ - // Method Description: - /// Returns the order of the Poly1d - /// - /// @return NdArray - /// - uint32 order() const noexcept + else { - return static_cast(coefficients_.size() - 1); + // psuedo-inverse + auto aT = a.transpose(); + auto aTaInv = linalg::inv(aT.dot(aWeighted)); + aInv = aTaInv.dot(aT); } - //============================================================================ - // Method Description: - /// Prints the string representation of the Poly1d object - /// to the console - /// - void print() const + auto x = aInv.dot(yWeighted); + return Poly1d(x); + } + + //============================================================================ + // Method Description: + /// Calculates the integral of the polynomial + /// + /// @return Poly1d + [[nodiscard]] Poly1d integ() const + { + const auto numCoefficients = static_cast(coefficients_.size()); + if (numCoefficients == 0) { - std::cout << *this << std::endl; + return {}; } - //============================================================================ - // Method Description: - /// Converts the polynomial to a string representation - /// - /// @return Poly1d - /// - std::string str() const + NdArray integralCofficients(1, numCoefficients + 1); + integralCofficients[0] = 0.; + + for (uint32 i = 0; i < numCoefficients; ++i) { - const auto numCoeffients = static_cast(coefficients_.size()); + integralCofficients[i + 1] = static_cast(coefficients_[i]) / static_cast(i + 1); + } - std::string repr = "Poly1d<"; - uint32 power = 0; - for (auto& coefficient : coefficients_) - { - if (DtypeInfo::isInteger()) - { - if (coefficient == 0) - { - ++power; - continue; - } - } - else - { - if (utils::essentiallyEqual(coefficient, static_cast(0.))) - { - ++power; - continue; - } - } + return Poly1d(integralCofficients); + } - repr += utils::num2str(coefficient); + //============================================================================ + // Method Description: + /// Returns the order of the Poly1d + /// + /// @return NdArray + /// + [[nodiscard]] uint32 order() const noexcept + { + return static_cast(coefficients_.size() - 1); + } + + //============================================================================ + // Method Description: + /// Prints the string representation of the Poly1d object + /// to the console + /// + void print() const + { + std::cout << *this << std::endl; + } + + //============================================================================ + // Method Description: + /// Converts the polynomial to a string representation + /// + /// @return Poly1d + /// + [[nodiscard]] std::string str() const + { + const auto numCoeffients = static_cast(coefficients_.size()); - if (power > 1) + std::string repr = "Poly1d<"; + uint32 power = 0; + for (auto& coefficient : coefficients_) + { + if (DtypeInfo::isInteger()) + { + if (coefficient == 0) { - repr += "x^" + utils::num2str(power); + ++power; + continue; } - else if (power == 1) + } + else + { + if (utils::essentiallyEqual(coefficient, static_cast(0.))) { - repr += "x"; + ++power; + continue; } + } - ++power; + repr += utils::num2str(coefficient); - if (power < numCoeffients) - { - repr += " + "; - } + if (power > 1) + { + repr += "x^" + utils::num2str(power); + } + else if (power == 1) + { + repr += "x"; } - return repr + ">"; - } + ++power; - //============================================================================ - // Method Description: - /// Evaluates the Poly1D object for the input value - /// - /// @param inValue - /// @return Poly1d - /// - dtype operator()(dtype inValue) const noexcept - { - dtype polyValue = 0; - uint8 power = 0; - for (auto& coefficient : coefficients_) + if (power < numCoeffients) { - polyValue += coefficient * utils::power(inValue, power++); + repr += " + "; } - - return polyValue; } - //============================================================================ - // Method Description: - /// Adds the two Poly1d objects - /// - /// @param inOtherPoly - /// @return Poly1d - /// - Poly1d operator+(const Poly1d& inOtherPoly) const + return repr + ">"; + } + + //============================================================================ + // Method Description: + /// Evaluates the Poly1D object for the input value + /// + /// @param inValue + /// @return Poly1d + /// + dtype operator()(dtype inValue) const noexcept + { + dtype polyValue = 0; + uint8 power = 0; + for (auto& coefficient : coefficients_) { - return Poly1d(*this) += inOtherPoly; + polyValue += coefficient * utils::power(inValue, power++); } - //============================================================================ - // Method Description: - /// Adds the two Poly1d objects - /// - /// @param inOtherPoly - /// @return Poly1d - /// - Poly1d& operator+=(const Poly1d& inOtherPoly) + return polyValue; + } + + //============================================================================ + // Method Description: + /// Adds the two Poly1d objects + /// + /// @param inOtherPoly + /// @return Poly1d + /// + Poly1d operator+(const Poly1d& inOtherPoly) const + { + return Poly1d(*this) += inOtherPoly; + } + + //============================================================================ + // Method Description: + /// Adds the two Poly1d objects + /// + /// @param inOtherPoly + /// @return Poly1d + /// + Poly1d& operator+=(const Poly1d& inOtherPoly) + { + if (this->coefficients_.size() < inOtherPoly.coefficients_.size()) { - if (this->coefficients_.size() < inOtherPoly.coefficients_.size()) + for (size_t i = 0; i < coefficients_.size(); ++i) { - for (size_t i = 0; i < coefficients_.size(); ++i) - { - coefficients_[i] += inOtherPoly.coefficients_[i]; - } - for (size_t i = coefficients_.size(); i < inOtherPoly.coefficients_.size(); ++i) - { - coefficients_.push_back(inOtherPoly.coefficients_[i]); - } + coefficients_[i] += inOtherPoly.coefficients_[i]; } - else + for (size_t i = coefficients_.size(); i < inOtherPoly.coefficients_.size(); ++i) { - for (size_t i = 0; i < inOtherPoly.coefficients_.size(); ++i) - { - coefficients_[i] += inOtherPoly.coefficients_[i]; - } + coefficients_.push_back(inOtherPoly.coefficients_[i]); } - - return *this; } - - //============================================================================ - // Method Description: - /// Subtracts the two Poly1d objects - /// - /// @param inOtherPoly - /// @return Poly1d - /// - Poly1d operator-(const Poly1d& inOtherPoly) const + else { - return Poly1d(*this) -= inOtherPoly; + for (size_t i = 0; i < inOtherPoly.coefficients_.size(); ++i) + { + coefficients_[i] += inOtherPoly.coefficients_[i]; + } } - //============================================================================ - // Method Description: - /// Subtracts the two Poly1d objects - /// - /// @param inOtherPoly - /// @return Poly1d - /// - Poly1d& operator-=(const Poly1d& inOtherPoly) + return *this; + } + + //============================================================================ + // Method Description: + /// Subtracts the two Poly1d objects + /// + /// @param inOtherPoly + /// @return Poly1d + /// + Poly1d operator-(const Poly1d& inOtherPoly) const + { + return Poly1d(*this) -= inOtherPoly; + } + + //============================================================================ + // Method Description: + /// Subtracts the two Poly1d objects + /// + /// @param inOtherPoly + /// @return Poly1d + /// + Poly1d& operator-=(const Poly1d& inOtherPoly) + { + if (this->coefficients_.size() < inOtherPoly.coefficients_.size()) { - if (this->coefficients_.size() < inOtherPoly.coefficients_.size()) + for (size_t i = 0; i < coefficients_.size(); ++i) { - for (size_t i = 0; i < coefficients_.size(); ++i) - { - coefficients_[i] -= inOtherPoly.coefficients_[i]; - } - for (size_t i = coefficients_.size(); i < inOtherPoly.coefficients_.size(); ++i) - { - coefficients_.push_back(-inOtherPoly.coefficients_[i]); - } + coefficients_[i] -= inOtherPoly.coefficients_[i]; } - else + for (size_t i = coefficients_.size(); i < inOtherPoly.coefficients_.size(); ++i) { - for (size_t i = 0; i < inOtherPoly.coefficients_.size(); ++i) - { - coefficients_[i] -= inOtherPoly.coefficients_[i]; - } + coefficients_.push_back(-inOtherPoly.coefficients_[i]); } - - return *this; } - - //============================================================================ - // Method Description: - /// Multiplies the two Poly1d objects - /// - /// @param inOtherPoly - /// @return Poly1d - /// - Poly1d operator*(const Poly1d& inOtherPoly) const - { - return Poly1d(*this) *= inOtherPoly; - } - - //============================================================================ - // Method Description: - /// Multiplies the two Poly1d objects - /// - /// @param inOtherPoly - /// @return Poly1d - /// - Poly1d& operator*=(const Poly1d& inOtherPoly) - { - const uint32 finalCoefficientsSize = order() + inOtherPoly.order() + 1; - std::vector coeffsA(finalCoefficientsSize, 0); - std::vector coeffsB(finalCoefficientsSize, 0); - stl_algorithms::copy(coefficients_.begin(), coefficients_.end(), coeffsA.begin()); - stl_algorithms::copy(inOtherPoly.coefficients_.cbegin(), - inOtherPoly.coefficients_.cend(), - coeffsB.begin()); - - // now multiply out the coefficients - std::vector finalCoefficients(finalCoefficientsSize, 0); - for (uint32 i = 0; i < finalCoefficientsSize; ++i) + else + { + for (size_t i = 0; i < inOtherPoly.coefficients_.size(); ++i) { - for (uint32 k = 0; k <= i; ++k) - { - finalCoefficients[i] += coeffsA[k] * coeffsB[i - k]; - } + coefficients_[i] -= inOtherPoly.coefficients_[i]; } - - this->coefficients_ = finalCoefficients; - return *this; } - //============================================================================ - // Method Description: - /// Raise the Poly1d to an integer power - /// - /// @param inPower - /// @return Poly1d - /// - Poly1d operator^(uint32 inPower) const - { - return Poly1d(*this) ^= inPower; - } - - //============================================================================ - // Method Description: - /// Raise the Poly1d to an integer power - /// - /// @param inPower - /// @return Poly1d - /// - Poly1d& operator^=(uint32 inPower) + return *this; + } + + //============================================================================ + // Method Description: + /// Multiplies the two Poly1d objects + /// + /// @param inOtherPoly + /// @return Poly1d + /// + Poly1d operator*(const Poly1d& inOtherPoly) const + { + return Poly1d(*this) *= inOtherPoly; + } + + //============================================================================ + // Method Description: + /// Multiplies the two Poly1d objects + /// + /// @param inOtherPoly + /// @return Poly1d + /// + Poly1d& operator*=(const Poly1d& inOtherPoly) + { + const uint32 finalCoefficientsSize = order() + inOtherPoly.order() + 1; + std::vector coeffsA(finalCoefficientsSize, 0); + std::vector coeffsB(finalCoefficientsSize, 0); + stl_algorithms::copy(coefficients_.begin(), coefficients_.end(), coeffsA.begin()); + stl_algorithms::copy(inOtherPoly.coefficients_.cbegin(), inOtherPoly.coefficients_.cend(), coeffsB.begin()); + + // now multiply out the coefficients + std::vector finalCoefficients(finalCoefficientsSize, 0); + for (uint32 i = 0; i < finalCoefficientsSize; ++i) { - if (inPower == 0) - { - coefficients_.clear(); - coefficients_.push_back(1); - return *this; - } - if (inPower == 1) + for (uint32 k = 0; k <= i; ++k) { - return *this; - } - - auto thisPoly(*this); - for (uint32 power = 1; power < inPower; ++power) - { - *this *= thisPoly; + finalCoefficients[i] += coeffsA[k] * coeffsB[i - k]; } + } + this->coefficients_ = finalCoefficients; + return *this; + } + + //============================================================================ + // Method Description: + /// Raise the Poly1d to an integer power + /// + /// @param inPower + /// @return Poly1d + /// + Poly1d operator^(uint32 inPower) const + { + return Poly1d(*this) ^= inPower; + } + + //============================================================================ + // Method Description: + /// Raise the Poly1d to an integer power + /// + /// @param inPower + /// @return Poly1d + /// + Poly1d& operator^=(uint32 inPower) + { + if (inPower == 0) + { + coefficients_.clear(); + coefficients_.push_back(1); + return *this; + } + if (inPower == 1) + { return *this; } - //============================================================================ - // Method Description: - /// io operator for the Poly1d class - /// - /// @param inOStream - /// @param inPoly - /// @return std::ostream - /// - friend std::ostream& operator<<(std::ostream& inOStream, const Poly1d& inPoly) + auto thisPoly(*this); + for (uint32 power = 1; power < inPower; ++power) { - inOStream << inPoly.str() << std::endl; - return inOStream; + *this *= thisPoly; } - private: - std::vector coefficients_{}; - }; - } // namespace polynomial -} // namespace nc + return *this; + } + + //============================================================================ + // Method Description: + /// io operator for the Poly1d class + /// + /// @param inOStream + /// @param inPoly + /// @return std::ostream + /// + friend std::ostream& operator<<(std::ostream& inOStream, const Poly1d& inPoly) + { + inOStream << inPoly.str() << std::endl; + return inOStream; + } + + private: + std::vector coefficients_{}; + }; +} // namespace nc::polynomial diff --git a/include/NumCpp/Polynomial/chebyshev_t.hpp b/include/NumCpp/Polynomial/chebyshev_t.hpp index ce7edbd47..d79f6ce5c 100644 --- a/include/NumCpp/Polynomial/chebyshev_t.hpp +++ b/include/NumCpp/Polynomial/chebyshev_t.hpp @@ -35,48 +35,45 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::polynomial { - namespace polynomial + //============================================================================ + // Method Description: + /// Chebyshev Polynomial of the first kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param n: the order of the chebyshev polynomial + /// @param x: the input value + /// @return double + /// + template + double chebyshev_t(uint32 n, dtype x) { - //============================================================================ - // Method Description: - /// Chebyshev Polynomial of the first kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param n: the order of the chebyshev polynomial - /// @param x: the input value - /// @return double - /// - template - double chebyshev_t(uint32 n, dtype x) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::chebyshev_t(n, static_cast(x)); - } + return boost::math::chebyshev_t(n, static_cast(x)); + } - //============================================================================ - // Method Description: - /// Chebyshev Polynomial of the first kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param n: the order of the chebyshev polynomial - /// @param inArrayX: the input value - /// @return NdArray - /// - template - NdArray chebyshev_t(uint32 n, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + //============================================================================ + // Method Description: + /// Chebyshev Polynomial of the first kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param n: the order of the chebyshev polynomial + /// @param inArrayX: the input value + /// @return NdArray + /// + template + NdArray chebyshev_t(uint32 n, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - const auto function = [n](dtype x) -> double { return chebyshev_t(n, x); }; + const auto function = [n](dtype x) -> double { return chebyshev_t(n, x); }; - stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); + stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); - return returnArray; - } - } // namespace polynomial -} // namespace nc + return returnArray; + } +} // namespace nc::polynomial #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Polynomial/chebyshev_u.hpp b/include/NumCpp/Polynomial/chebyshev_u.hpp index 85e97770d..db34aa0a1 100644 --- a/include/NumCpp/Polynomial/chebyshev_u.hpp +++ b/include/NumCpp/Polynomial/chebyshev_u.hpp @@ -35,48 +35,45 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::polynomial { - namespace polynomial + //============================================================================ + // Method Description: + /// Chebyshev Polynomial of the second kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param n: the order of the chebyshev polynomial + /// @param x: the input value + /// @return double + /// + template + double chebyshev_u(uint32 n, dtype x) { - //============================================================================ - // Method Description: - /// Chebyshev Polynomial of the second kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param n: the order of the chebyshev polynomial - /// @param x: the input value - /// @return double - /// - template - double chebyshev_u(uint32 n, dtype x) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::chebyshev_u(n, static_cast(x)); - } + return boost::math::chebyshev_u(n, static_cast(x)); + } - //============================================================================ - // Method Description: - /// Chebyshev Polynomial of the second kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param n: the order of the chebyshev polynomial - /// @param inArrayX: the input value - /// @return NdArray - /// - template - NdArray chebyshev_u(uint32 n, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + //============================================================================ + // Method Description: + /// Chebyshev Polynomial of the second kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param n: the order of the chebyshev polynomial + /// @param inArrayX: the input value + /// @return NdArray + /// + template + NdArray chebyshev_u(uint32 n, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - const auto function = [n](dtype x) -> double { return chebyshev_u(n, x); }; + const auto function = [n](dtype x) -> double { return chebyshev_u(n, x); }; - stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); + stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); - return returnArray; - } - } // namespace polynomial -} // namespace nc + return returnArray; + } +} // namespace nc::polynomial #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Polynomial/hermite.hpp b/include/NumCpp/Polynomial/hermite.hpp index 74d558a10..f46423d9f 100644 --- a/include/NumCpp/Polynomial/hermite.hpp +++ b/include/NumCpp/Polynomial/hermite.hpp @@ -39,54 +39,51 @@ #include "boost/math/special_functions/hermite.hpp" #endif -namespace nc +namespace nc::polynomial { - namespace polynomial + //============================================================================ + // Method Description: + /// Hermite Polynomial + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param n: the order of the hermite polynomial + /// @param x: the input value + /// @return double + /// + template + double hermite(uint32 n, dtype x) { - //============================================================================ - // Method Description: - /// Hermite Polynomial - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param n: the order of the hermite polynomial - /// @param x: the input value - /// @return double - /// - template - double hermite(uint32 n, dtype x) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); #ifdef __cpp_lib_math_special_functions - return std::hermite(n, static_cast(x)); + return std::hermite(n, static_cast(x)); #else - return boost::math::hermite(n, static_cast(x)); + return boost::math::hermite(n, static_cast(x)); #endif - } + } - //============================================================================ - // Method Description: - /// Hermite Polynomial. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param n: the order of the hermite polynomial - /// @param inArrayX: the input value - /// @return NdArray - /// - template - NdArray hermite(uint32 n, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + //============================================================================ + // Method Description: + /// Hermite Polynomial. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param n: the order of the hermite polynomial + /// @param inArrayX: the input value + /// @return NdArray + /// + template + NdArray hermite(uint32 n, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - const auto function = [n](dtype x) -> double { return hermite(n, x); }; + const auto function = [n](dtype x) -> double { return hermite(n, x); }; - stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); + stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); - return returnArray; - } - } // namespace polynomial -} // namespace nc + return returnArray; + } +} // namespace nc::polynomial #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Polynomial/laguerre.hpp b/include/NumCpp/Polynomial/laguerre.hpp index f3e3a5e1c..cb0334013 100644 --- a/include/NumCpp/Polynomial/laguerre.hpp +++ b/include/NumCpp/Polynomial/laguerre.hpp @@ -39,100 +39,97 @@ #include "boost/math/special_functions/laguerre.hpp" #endif -namespace nc +namespace nc::polynomial { - namespace polynomial + //============================================================================ + // Method Description: + /// Laguerre Polynomial. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param n: the order of the leguerre polynomial + /// @param x: the input value + /// @return double + /// + template + double laguerre(uint32 n, dtype x) { - //============================================================================ - // Method Description: - /// Laguerre Polynomial. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param n: the order of the leguerre polynomial - /// @param x: the input value - /// @return double - /// - template - double laguerre(uint32 n, dtype x) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); #ifdef __cpp_lib_math_special_functions - return std::laguerre(n, static_cast(x)); + return std::laguerre(n, static_cast(x)); #else - return boost::math::laguerre(n, static_cast(x)); + return boost::math::laguerre(n, static_cast(x)); #endif - } - - //============================================================================ - // Method Description: - /// Associated Laguerre Polynomial. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param n: the order of the leguerre polynomial - /// @param m: the degree of the legendre polynomial - /// @param x: the input value - /// @return double - /// - template - double laguerre(uint32 n, uint32 m, dtype x) - { - STATIC_ASSERT_ARITHMETIC(dtype); + } + + //============================================================================ + // Method Description: + /// Associated Laguerre Polynomial. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param n: the order of the leguerre polynomial + /// @param m: the degree of the legendre polynomial + /// @param x: the input value + /// @return double + /// + template + double laguerre(uint32 n, uint32 m, dtype x) + { + STATIC_ASSERT_ARITHMETIC(dtype); #ifdef __cpp_lib_math_special_functions - return std::assoc_laguerre(m, n, static_cast(x)); + return std::assoc_laguerre(m, n, static_cast(x)); #else - return boost::math::laguerre(m, n, static_cast(x)); + return boost::math::laguerre(m, n, static_cast(x)); #endif - } - - //============================================================================ - // Method Description: - /// Laguerre Polynomial. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param n: the order of the leguerre polynomial - /// @param inArrayX: the input value - /// @return NdArray - /// - template - NdArray laguerre(uint32 n, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); - - const auto function = [n](dtype x) -> double { return laguerre(n, x); }; - - stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); - - return returnArray; - } - - //============================================================================ - // Method Description: - /// Associated Laguerre Polynomial. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param n: the order of the leguerre polynomial - /// @param m: the degree of the legendre polynomial - /// @param inArrayX: the input value - /// @return NdArray - /// - template - NdArray laguerre(uint32 n, uint32 m, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); - - const auto function = [n, m](dtype x) -> double { return laguerre(n, m, x); }; - - stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); - - return returnArray; - } - } // namespace polynomial -} // namespace nc + } + + //============================================================================ + // Method Description: + /// Laguerre Polynomial. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param n: the order of the leguerre polynomial + /// @param inArrayX: the input value + /// @return NdArray + /// + template + NdArray laguerre(uint32 n, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); + + const auto function = [n](dtype x) -> double { return laguerre(n, x); }; + + stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); + + return returnArray; + } + + //============================================================================ + // Method Description: + /// Associated Laguerre Polynomial. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param n: the order of the leguerre polynomial + /// @param m: the degree of the legendre polynomial + /// @param inArrayX: the input value + /// @return NdArray + /// + template + NdArray laguerre(uint32 n, uint32 m, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); + + const auto function = [n, m](dtype x) -> double { return laguerre(n, m, x); }; + + stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); + + return returnArray; + } +} // namespace nc::polynomial #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Polynomial/legendre_p.hpp b/include/NumCpp/Polynomial/legendre_p.hpp index 4def88221..ab609a6bc 100644 --- a/include/NumCpp/Polynomial/legendre_p.hpp +++ b/include/NumCpp/Polynomial/legendre_p.hpp @@ -40,67 +40,65 @@ #include "boost/math/special_functions/legendre.hpp" #endif -namespace nc +namespace nc::polynomial { - namespace polynomial + //============================================================================ + // Method Description: + /// Legendre Polynomial of the first kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param n: the degree of the legendre polynomial + /// @param x: the input value. Requires -1 <= x <= 1 + /// @return double + /// + template + double legendre_p(uint32 n, dtype x) { - //============================================================================ - // Method Description: - /// Legendre Polynomial of the first kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param n: the degree of the legendre polynomial - /// @param x: the input value. Requires -1 <= x <= 1 - /// @return double - /// - template - double legendre_p(uint32 n, dtype x) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - if (x < -1. || x > 1.) - { - THROW_INVALID_ARGUMENT_ERROR("input x must be of the range [-1, 1]."); - } + if (x < -1. || x > 1.) + { + THROW_INVALID_ARGUMENT_ERROR("input x must be of the range [-1, 1]."); + } #ifdef __cpp_lib_math_special_functions - return std::legendre(n, static_cast(x)); + return std::legendre(n, static_cast(x)); #else - return boost::math::legendre_p(n, static_cast(x)); + return boost::math::legendre_p(n, static_cast(x)); #endif - } + } + + //============================================================================ + // Method Description: + /// Associated Legendre Polynomial of the first kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param m: the order of the legendre polynomial + /// @param n: the degree of the legendre polynomial + /// @param x: the input value. Requires -1 <= x <= 1 + /// @return double + /// + template + double legendre_p(uint32 m, uint32 n, dtype x) + { + STATIC_ASSERT_ARITHMETIC(dtype); - //============================================================================ - // Method Description: - /// Associated Legendre Polynomial of the first kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param m: the order of the legendre polynomial - /// @param n: the degree of the legendre polynomial - /// @param x: the input value. Requires -1 <= x <= 1 - /// @return double - /// - template - double legendre_p(uint32 m, uint32 n, dtype x) + if (x < -1. || x > 1.) { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (x < -1. || x > 1.) - { - THROW_INVALID_ARGUMENT_ERROR("input x must be of the range [-1, 1]."); - } + THROW_INVALID_ARGUMENT_ERROR("input x must be of the range [-1, 1]."); + } #ifdef __cpp_lib_math_special_functions - auto value = std::assoc_legendre(n, m, static_cast(x)); + auto value = std::assoc_legendre(n, m, static_cast(x)); #ifdef __GNUC__ #if __GNUC__ != 7 && __GNUC__ != 8 - // gcc std::assoc_legendre is not standard compliant - value *= n % 2 == 0 ? 1 : -1; + // gcc std::assoc_legendre is not standard compliant + value *= n % 2 == 0 ? 1 : -1; #endif // #if __GNUC__ != 7 && __GNUC__ != 8 #endif // #ifdef __GNUC__ @@ -108,72 +106,71 @@ namespace nc #ifdef __clang__ #if __clang_major__ != 7 && __clang_major__ != 8 - // clang uses gcc headers where std::assoc_legendre is not standard compliant - value *= n % 2 == 0 ? 1 : -1; + // clang uses gcc headers where std::assoc_legendre is not standard compliant + value *= n % 2 == 0 ? 1 : -1; #endif // #if __clang_major__ != 6 && __clang_major__ != 7 && __clang_major__ != 8 #endif // #ifdef __clang__ #ifdef _MSC_VER - value *= n % 2 == 0 ? 1 : -1; + value *= n % 2 == 0 ? 1 : -1; #endif // #ifdef _MSC_VER - return value; + return value; #else // #ifdef __cpp_lib_math_special_functions - return boost::math::legendre_p(n, m, static_cast(x)); + return boost::math::legendre_p(n, m, static_cast(x)); #endif // #ifdef __cpp_lib_math_special_functions - } - - //============================================================================ - // Method Description: - /// Legendre Polynomial of the first kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param n: the degree of the legendre polynomial - /// @param inArrayX: the input value. Requires -1 <= x <= 1 - /// @return NdArray - /// - template - NdArray legendre_p(uint32 n, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); - - const auto function = [n](dtype x) -> double { return legendre_p(n, x); }; - - stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); - - return returnArray; - } - - //============================================================================ - // Method Description: - /// Associated Legendre Polynomial of the first kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param m: the order of the legendre polynomial - /// @param n: the degree of the legendre polynomial - /// @param inArrayX: the input value. Requires -1 <= x <= 1 - /// @return NdArray - /// - template - NdArray legendre_p(uint32 m, uint32 n, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + } + + //============================================================================ + // Method Description: + /// Legendre Polynomial of the first kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param n: the degree of the legendre polynomial + /// @param inArrayX: the input value. Requires -1 <= x <= 1 + /// @return NdArray + /// + template + NdArray legendre_p(uint32 n, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); + + const auto function = [n](dtype x) -> double { return legendre_p(n, x); }; + + stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); + + return returnArray; + } + + //============================================================================ + // Method Description: + /// Associated Legendre Polynomial of the first kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param m: the order of the legendre polynomial + /// @param n: the degree of the legendre polynomial + /// @param inArrayX: the input value. Requires -1 <= x <= 1 + /// @return NdArray + /// + template + NdArray legendre_p(uint32 m, uint32 n, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - const auto function = [m, n](dtype x) -> double { return legendre_p(m, n, x); }; + const auto function = [m, n](dtype x) -> double { return legendre_p(m, n, x); }; - stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); + stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); - return returnArray; - } - } // namespace polynomial -} // namespace nc + return returnArray; + } +} // namespace nc::polynomial #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Polynomial/legendre_q.hpp b/include/NumCpp/Polynomial/legendre_q.hpp index 58d96555d..d62f5ed6c 100644 --- a/include/NumCpp/Polynomial/legendre_q.hpp +++ b/include/NumCpp/Polynomial/legendre_q.hpp @@ -35,53 +35,50 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::polynomial { - namespace polynomial + //============================================================================ + // Method Description: + /// Legendre Polynomial of the second kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param n: the order of the legendre polynomial + /// @param x: the input value. Requires -1 <= x <= 1 + /// @return double + /// + template + double legendre_q(int32 n, dtype x) { - //============================================================================ - // Method Description: - /// Legendre Polynomial of the second kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param n: the order of the legendre polynomial - /// @param x: the input value. Requires -1 <= x <= 1 - /// @return double - /// - template - double legendre_q(int32 n, dtype x) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (x < -1. || x > 1.) - { - THROW_INVALID_ARGUMENT_ERROR("input x must be of the range [-1, 1]."); - } + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::legendre_q(n, static_cast(x)); + if (x < -1. || x > 1.) + { + THROW_INVALID_ARGUMENT_ERROR("input x must be of the range [-1, 1]."); } - //============================================================================ - // Method Description: - /// Legendre Polynomial of the second kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param n: the order of the legendre polynomial - /// @param inArrayX: the input value. Requires -1 <= x <= 1 - /// @return NdArray - /// - template - NdArray legendre_q(int32 n, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + return boost::math::legendre_q(n, static_cast(x)); + } - const auto function = [n](dtype x) -> double { return legendre_q(n, x); }; + //============================================================================ + // Method Description: + /// Legendre Polynomial of the second kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param n: the order of the legendre polynomial + /// @param inArrayX: the input value. Requires -1 <= x <= 1 + /// @return NdArray + /// + template + NdArray legendre_q(int32 n, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); + const auto function = [n](dtype x) -> double { return legendre_q(n, x); }; - return returnArray; - } - } // namespace polynomial -} // namespace nc + stl_algorithms::transform(inArrayX.cbegin(), inArrayX.cend(), returnArray.begin(), function); + + return returnArray; + } +} // namespace nc::polynomial #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Polynomial/spherical_harmonic.hpp b/include/NumCpp/Polynomial/spherical_harmonic.hpp index 8ad5dd1d4..25f0b3f67 100644 --- a/include/NumCpp/Polynomial/spherical_harmonic.hpp +++ b/include/NumCpp/Polynomial/spherical_harmonic.hpp @@ -36,79 +36,76 @@ #include "NumCpp/Core/Internal/StaticAsserts.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::polynomial { - namespace polynomial + //============================================================================ + // Method Description: + /// Returns the value of the Spherical Harmonic Ynm(theta, phi). + /// The spherical harmonics Ynm(theta, phi) are the angular portion of the + /// solution to Laplace's equation in spherical coordinates where azimuthal + /// symmetry is not present. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param n: order of the harmonic + /// @param m: degree of the harmonic + /// @param theta: Azimuthal (longitudinal) coordinate; must be in [0, 2*pi]. + /// @param phi: Polar (colatitudinal) coordinate; must be in [0, pi]. + /// @return double + /// + template + std::complex spherical_harmonic(uint32 n, int32 m, dtype1 theta, dtype2 phi) { - //============================================================================ - // Method Description: - /// Returns the value of the Spherical Harmonic Ynm(theta, phi). - /// The spherical harmonics Ynm(theta, phi) are the angular portion of the - /// solution to Laplace's equation in spherical coordinates where azimuthal - /// symmetry is not present. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param n: order of the harmonic - /// @param m: degree of the harmonic - /// @param theta: Azimuthal (longitudinal) coordinate; must be in [0, 2*pi]. - /// @param phi: Polar (colatitudinal) coordinate; must be in [0, pi]. - /// @return double - /// - template - std::complex spherical_harmonic(uint32 n, int32 m, dtype1 theta, dtype2 phi) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); - return boost::math::spherical_harmonic(m, n, static_cast(phi), static_cast(theta)); - } + return boost::math::spherical_harmonic(m, n, static_cast(phi), static_cast(theta)); + } - //============================================================================ - // Method Description: - /// Returns the real part of the Spherical Harmonic Ynm(theta, phi). - /// The spherical harmonics Ynm(theta, phi) are the angular portion of the - /// solution to Laplace's equation in spherical coordinates where azimuthal - /// symmetry is not present. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param n: order of the harmonic - /// @param m: degree of the harmonic - /// @param theta: Azimuthal (longitudinal) coordinate; must be in [0, 2*pi]. - /// @param phi: Polar (colatitudinal) coordinate; must be in [0, pi]. - /// @return double - /// - template - double spherical_harmonic_r(uint32 n, int32 m, dtype1 theta, dtype2 phi) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + //============================================================================ + // Method Description: + /// Returns the real part of the Spherical Harmonic Ynm(theta, phi). + /// The spherical harmonics Ynm(theta, phi) are the angular portion of the + /// solution to Laplace's equation in spherical coordinates where azimuthal + /// symmetry is not present. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param n: order of the harmonic + /// @param m: degree of the harmonic + /// @param theta: Azimuthal (longitudinal) coordinate; must be in [0, 2*pi]. + /// @param phi: Polar (colatitudinal) coordinate; must be in [0, pi]. + /// @return double + /// + template + double spherical_harmonic_r(uint32 n, int32 m, dtype1 theta, dtype2 phi) + { + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); - return boost::math::spherical_harmonic_r(m, n, static_cast(phi), static_cast(theta)); - } + return boost::math::spherical_harmonic_r(m, n, static_cast(phi), static_cast(theta)); + } - //============================================================================ - // Method Description: - /// Returns the imaginary part of the Spherical Harmonic Ynm(theta, phi). - /// The spherical harmonics Ynm(theta, phi) are the angular portion of the - /// solution to Laplace's equation in spherical coordinates where azimuthal - /// symmetry is not present. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param n: order of the harmonic - /// @param m: degree of the harmonic - /// @param theta: Azimuthal (longitudinal) coordinate; must be in [0, 2*pi]. - /// @param phi: Polar (colatitudinal) coordinate; must be in [0, pi]. - /// @return double - /// - template - double spherical_harmonic_i(uint32 n, int32 m, dtype1 theta, dtype2 phi) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + //============================================================================ + // Method Description: + /// Returns the imaginary part of the Spherical Harmonic Ynm(theta, phi). + /// The spherical harmonics Ynm(theta, phi) are the angular portion of the + /// solution to Laplace's equation in spherical coordinates where azimuthal + /// symmetry is not present. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param n: order of the harmonic + /// @param m: degree of the harmonic + /// @param theta: Azimuthal (longitudinal) coordinate; must be in [0, 2*pi]. + /// @param phi: Polar (colatitudinal) coordinate; must be in [0, pi]. + /// @return double + /// + template + double spherical_harmonic_i(uint32 n, int32 m, dtype1 theta, dtype2 phi) + { + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); - return boost::math::spherical_harmonic_i(m, n, static_cast(phi), static_cast(theta)); - } - } // namespace polynomial -} // namespace nc + return boost::math::spherical_harmonic_i(m, n, static_cast(phi), static_cast(theta)); + } +} // namespace nc::polynomial #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Random/RNG.hpp b/include/NumCpp/Random/RNG.hpp index 76061041c..e25d7dd7e 100644 --- a/include/NumCpp/Random/RNG.hpp +++ b/include/NumCpp/Random/RNG.hpp @@ -61,1059 +61,1056 @@ #include "NumCpp/Random/uniformOnSphere.hpp" #include "NumCpp/Random/weibull.hpp" -namespace nc +namespace nc::random { - namespace random + //============================================================================ + // Class Description: + /// Random Number Generater Class with non-global state + /// + template + class RNG { + public: //============================================================================ - // Class Description: - /// Random Number Generater Class with non-global state + // Method Description: + /// Defualt Constructor /// - template - class RNG + RNG() = default; + + //============================================================================ + // Method Description: + /// Seed Constructor + /// + /// @param seed: the seed value + /// + explicit RNG(int seed) : + generator_(seed){}; + + //============================================================================ + // Method Description: + /// Single random value sampled from the "bernoulli" distribution. + /// + /// @param inP (probability of success [0, 1]). Default 0.5 + /// @return NdArray + /// + bool bernoulli(double inP = 0.5) { - public: - //============================================================================ - // Method Description: - /// Defualt Constructor - /// - RNG() = default; - - //============================================================================ - // Method Description: - /// Seed Constructor - /// - /// @param seed: the seed value - /// - explicit RNG(int seed) : - generator_(seed){}; - - //============================================================================ - // Method Description: - /// Single random value sampled from the "bernoulli" distribution. - /// - /// @param inP (probability of success [0, 1]). Default 0.5 - /// @return NdArray - /// - bool bernoulli(double inP = 0.5) - { - return detail::bernoulli(generator_, inP); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "bernoulli" distribution. - /// - /// @param inShape - /// @param inP (probability of success [0, 1]). Default 0.5 - /// @return NdArray - /// - NdArray bernoulli(const Shape& inShape, double inP = 0.5) - { - return detail::bernoulli(generator_, inShape, inP); - } + return detail::bernoulli(generator_, inP); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "bernoulli" distribution. + /// + /// @param inShape + /// @param inP (probability of success [0, 1]). Default 0.5 + /// @return NdArray + /// + NdArray bernoulli(const Shape& inShape, double inP = 0.5) + { + return detail::bernoulli(generator_, inShape, inP); + } #ifndef NUMCPP_NO_USE_BOOST - //============================================================================ - // Method Description: - /// Single random value sampled from the from the "beta" distribution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.beta.html#numpy.random.beta - /// - /// @param inAlpha - /// @param inBeta - /// @return NdArray - /// - template - dtype beta(dtype inAlpha, dtype inBeta) - { - return detail::beta(generator_, inAlpha, inBeta); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "beta" distribution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.beta.html#numpy.random.beta - /// - /// @param inShape - /// @param inAlpha - /// @param inBeta - /// @return NdArray - /// - template - NdArray beta(const Shape& inShape, dtype inAlpha, dtype inBeta) - { - return detail::beta(generator_, inShape, inAlpha, inBeta); - } + //============================================================================ + // Method Description: + /// Single random value sampled from the from the "beta" distribution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.beta.html#numpy.random.beta + /// + /// @param inAlpha + /// @param inBeta + /// @return NdArray + /// + template + dtype beta(dtype inAlpha, dtype inBeta) + { + return detail::beta(generator_, inAlpha, inBeta); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "beta" distribution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.beta.html#numpy.random.beta + /// + /// @param inShape + /// @param inAlpha + /// @param inBeta + /// @return NdArray + /// + template + NdArray beta(const Shape& inShape, dtype inAlpha, dtype inBeta) + { + return detail::beta(generator_, inShape, inAlpha, inBeta); + } #endif - //============================================================================ - // Method Description: - /// Single random value sampled from the from the "binomial" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.binomial.html#numpy.random.binomial - /// - /// @param inN (number of trials) - /// @param inP (probablity of success [0, 1]) - /// @return NdArray - /// - template - dtype binomial(dtype inN, double inP = 0.5) - { - return detail::binomial(generator_, inN, inP); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "binomial" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.binomial.html#numpy.random.binomial - /// - /// @param inShape - /// @param inN (number of trials) - /// @param inP (probablity of success [0, 1]) - /// @return NdArray - /// - template - NdArray binomial(const Shape& inShape, dtype inN, double inP = 0.5) - { - return detail::binomial(generator_, inShape, inN, inP); - } - - //============================================================================ - // Method Description: - /// Single random value sampled from the from the "cauchy" distrubution. - /// - /// @param inMean: Mean value of the underlying normal distribution. Default is 0. - /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. - /// Default is 1. - /// @return NdArray - /// - template - dtype cauchy(dtype inMean = 0, dtype inSigma = 1) - { - return detail::cauchy(generator_, inMean, inSigma); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "cauchy" distrubution. - /// - /// @param inShape - /// @param inMean: Mean value of the underlying normal distribution. Default is 0. - /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. - /// Default is 1. - /// @return NdArray - /// - template - NdArray cauchy(const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) - { - return detail::cauchy(generator_, inShape, inMean, inSigma); - } - - //============================================================================ - // Method Description: - /// Single random value sampled from the from the "chi square" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.chisquare.html#numpy.random.chisquare - /// - /// @param inDof (independent random variables) - /// @return NdArray - /// - template - dtype chiSquare(dtype inDof) - { - return detail::chiSquare(generator_, inDof); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "chi square" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.chisquare.html#numpy.random.chisquare - /// - /// @param inShape - /// @param inDof (independent random variables) - /// @return NdArray - /// - template - NdArray chiSquare(const Shape& inShape, dtype inDof) - { - return detail::chiSquare(generator_, inShape, inDof); - } - - //============================================================================ - // Method Description: - /// Chooses a random sample from an input array. - /// - /// @param inArray - /// @return NdArray - /// - template - dtype choice(const NdArray& inArray) - { - return detail::choice(generator_, inArray); - } - - //============================================================================ - // Method Description: - /// Chooses inNum random samples from an input array. - /// - /// @param inArray - /// @param inNum - /// @param replace: Whether the sample is with or without replacement - /// @return NdArray - /// - template - NdArray choice(const NdArray& inArray, uint32 inNum, bool replace = true) - { - return detail::choice(generator_, inArray, inNum, replace); - } - - //============================================================================ - // Method Description: - /// Single random value sampled from the from the - /// "discrete" distrubution. It produces integers in the - /// range [0, n) with the probability of producing each value - /// is specified by the parameters of the distribution. - /// - /// @param inWeights - /// @return NdArray - /// - template - dtype discrete(const NdArray& inWeights) - { - return detail::discrete(generator_, inWeights); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "discrete" distrubution. It produces - /// integers in the range [0, n) with the probability of - /// producing each value is specified by the parameters - /// of the distribution. - /// - /// @param inShape - /// @param inWeights - /// @return NdArray - /// - template - NdArray discrete(const Shape& inShape, const NdArray& inWeights) - { - return detail::discrete(generator_, inShape, inWeights); - } - - //============================================================================ - // Method Description: - /// Single random value sampled from the "exponential" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.exponential.html#numpy.random.exponential - /// - /// @param inScaleValue (default 1) - /// @return NdArray - /// - template - dtype exponential(dtype inScaleValue = 1) - { - return detail::exponential(generator_, inScaleValue); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "exponential" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.exponential.html#numpy.random.exponential - /// - /// @param inShape - /// @param inScaleValue (default 1) - /// @return NdArray - /// - template - NdArray exponential(const Shape& inShape, dtype inScaleValue = 1) - { - return detail::exponential(generator_, inShape, inScaleValue); - } - - //============================================================================ - // Method Description: - /// Single random value sampled from the "extreme value" distrubution. - /// - /// @param inA (default 1) - /// @param inB (default 1) - /// @return NdArray - /// - template - dtype extremeValue(dtype inA = 1, dtype inB = 1) - { - return detail::extremeValue(generator_, inA, inB); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "extreme value" distrubution. - /// - /// @param inShape - /// @param inA (default 1) - /// @param inB (default 1) - /// @return NdArray - /// - template - NdArray extremeValue(const Shape& inShape, dtype inA = 1, dtype inB = 1) - { - return detail::extremeValue(generator_, inShape, inA, inB); - } - - //============================================================================ - // Method Description: - /// Single random value sampled from the "F" distrubution. - /// - /// NumPy Reference: https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.f.html#numpy.random.f - /// - /// @param inDofN: Degrees of freedom in numerator. Should be greater than zero. - /// @param inDofD: Degrees of freedom in denominator. Should be greater than zero. - /// @return NdArray - /// - template - dtype f(dtype inDofN, dtype inDofD) - { - return detail::f(generator_, inDofN, inDofD); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "F" distrubution. - /// - /// NumPy Reference: https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.f.html#numpy.random.f - /// - /// @param inShape - /// @param inDofN: Degrees of freedom in numerator. Should be greater than zero. - /// @param inDofD: Degrees of freedom in denominator. Should be greater than zero. - /// @return NdArray - /// - template - NdArray f(const Shape& inShape, dtype inDofN, dtype inDofD) - { - return detail::f(generator_, inShape, inDofN, inDofD); - } - - //============================================================================ - // Method Description: - /// Single random value sampled from the "gamma" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.gamma.html#numpy.random.gamma - /// - /// @param inGammaShape - /// @param inScaleValue (default 1) - /// @return NdArray - /// - template - dtype gamma(dtype inGammaShape, dtype inScaleValue = 1) - { - return detail::gamma(generator_, inGammaShape, inScaleValue); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "gamma" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.gamma.html#numpy.random.gamma - /// - /// @param inShape - /// @param inGammaShape - /// @param inScaleValue (default 1) - /// @return NdArray - /// - template - NdArray gamma(const Shape& inShape, dtype inGammaShape, dtype inScaleValue = 1) - { - return detail::gamma(generator_, inShape, inGammaShape, inScaleValue); - } - - //============================================================================ - // Method Description: - /// Single random value sampled from the "geometric" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.geometric.html#numpy.random.geometric - /// - /// @param inP (probablity of success [0, 1]) - /// @return NdArray - /// - template - dtype geometric(double inP = 0.5) - { - return detail::geometric(generator_, inP); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "geometric" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.geometric.html#numpy.random.geometric - /// - /// @param inShape - /// @param inP (probablity of success [0, 1]) - /// @return NdArray - /// - template - NdArray geometric(const Shape& inShape, double inP = 0.5) - { - return detail::geometric(generator_, inShape, inP); - } + //============================================================================ + // Method Description: + /// Single random value sampled from the from the "binomial" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.binomial.html#numpy.random.binomial + /// + /// @param inN (number of trials) + /// @param inP (probablity of success [0, 1]) + /// @return NdArray + /// + template + dtype binomial(dtype inN, double inP = 0.5) + { + return detail::binomial(generator_, inN, inP); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "binomial" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.binomial.html#numpy.random.binomial + /// + /// @param inShape + /// @param inN (number of trials) + /// @param inP (probablity of success [0, 1]) + /// @return NdArray + /// + template + NdArray binomial(const Shape& inShape, dtype inN, double inP = 0.5) + { + return detail::binomial(generator_, inShape, inN, inP); + } + + //============================================================================ + // Method Description: + /// Single random value sampled from the from the "cauchy" distrubution. + /// + /// @param inMean: Mean value of the underlying normal distribution. Default is 0. + /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. + /// Default is 1. + /// @return NdArray + /// + template + dtype cauchy(dtype inMean = 0, dtype inSigma = 1) + { + return detail::cauchy(generator_, inMean, inSigma); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "cauchy" distrubution. + /// + /// @param inShape + /// @param inMean: Mean value of the underlying normal distribution. Default is 0. + /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. + /// Default is 1. + /// @return NdArray + /// + template + NdArray cauchy(const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) + { + return detail::cauchy(generator_, inShape, inMean, inSigma); + } + + //============================================================================ + // Method Description: + /// Single random value sampled from the from the "chi square" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.chisquare.html#numpy.random.chisquare + /// + /// @param inDof (independent random variables) + /// @return NdArray + /// + template + dtype chiSquare(dtype inDof) + { + return detail::chiSquare(generator_, inDof); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "chi square" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.chisquare.html#numpy.random.chisquare + /// + /// @param inShape + /// @param inDof (independent random variables) + /// @return NdArray + /// + template + NdArray chiSquare(const Shape& inShape, dtype inDof) + { + return detail::chiSquare(generator_, inShape, inDof); + } + + //============================================================================ + // Method Description: + /// Chooses a random sample from an input array. + /// + /// @param inArray + /// @return NdArray + /// + template + dtype choice(const NdArray& inArray) + { + return detail::choice(generator_, inArray); + } + + //============================================================================ + // Method Description: + /// Chooses inNum random samples from an input array. + /// + /// @param inArray + /// @param inNum + /// @param replace: Whether the sample is with or without replacement + /// @return NdArray + /// + template + NdArray choice(const NdArray& inArray, uint32 inNum, bool replace = true) + { + return detail::choice(generator_, inArray, inNum, replace); + } + + //============================================================================ + // Method Description: + /// Single random value sampled from the from the + /// "discrete" distrubution. It produces integers in the + /// range [0, n) with the probability of producing each value + /// is specified by the parameters of the distribution. + /// + /// @param inWeights + /// @return NdArray + /// + template + dtype discrete(const NdArray& inWeights) + { + return detail::discrete(generator_, inWeights); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "discrete" distrubution. It produces + /// integers in the range [0, n) with the probability of + /// producing each value is specified by the parameters + /// of the distribution. + /// + /// @param inShape + /// @param inWeights + /// @return NdArray + /// + template + NdArray discrete(const Shape& inShape, const NdArray& inWeights) + { + return detail::discrete(generator_, inShape, inWeights); + } + + //============================================================================ + // Method Description: + /// Single random value sampled from the "exponential" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.exponential.html#numpy.random.exponential + /// + /// @param inScaleValue (default 1) + /// @return NdArray + /// + template + dtype exponential(dtype inScaleValue = 1) + { + return detail::exponential(generator_, inScaleValue); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "exponential" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.exponential.html#numpy.random.exponential + /// + /// @param inShape + /// @param inScaleValue (default 1) + /// @return NdArray + /// + template + NdArray exponential(const Shape& inShape, dtype inScaleValue = 1) + { + return detail::exponential(generator_, inShape, inScaleValue); + } + + //============================================================================ + // Method Description: + /// Single random value sampled from the "extreme value" distrubution. + /// + /// @param inA (default 1) + /// @param inB (default 1) + /// @return NdArray + /// + template + dtype extremeValue(dtype inA = 1, dtype inB = 1) + { + return detail::extremeValue(generator_, inA, inB); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "extreme value" distrubution. + /// + /// @param inShape + /// @param inA (default 1) + /// @param inB (default 1) + /// @return NdArray + /// + template + NdArray extremeValue(const Shape& inShape, dtype inA = 1, dtype inB = 1) + { + return detail::extremeValue(generator_, inShape, inA, inB); + } + + //============================================================================ + // Method Description: + /// Single random value sampled from the "F" distrubution. + /// + /// NumPy Reference: https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.f.html#numpy.random.f + /// + /// @param inDofN: Degrees of freedom in numerator. Should be greater than zero. + /// @param inDofD: Degrees of freedom in denominator. Should be greater than zero. + /// @return NdArray + /// + template + dtype f(dtype inDofN, dtype inDofD) + { + return detail::f(generator_, inDofN, inDofD); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "F" distrubution. + /// + /// NumPy Reference: https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.f.html#numpy.random.f + /// + /// @param inShape + /// @param inDofN: Degrees of freedom in numerator. Should be greater than zero. + /// @param inDofD: Degrees of freedom in denominator. Should be greater than zero. + /// @return NdArray + /// + template + NdArray f(const Shape& inShape, dtype inDofN, dtype inDofD) + { + return detail::f(generator_, inShape, inDofN, inDofD); + } + + //============================================================================ + // Method Description: + /// Single random value sampled from the "gamma" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.gamma.html#numpy.random.gamma + /// + /// @param inGammaShape + /// @param inScaleValue (default 1) + /// @return NdArray + /// + template + dtype gamma(dtype inGammaShape, dtype inScaleValue = 1) + { + return detail::gamma(generator_, inGammaShape, inScaleValue); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "gamma" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.gamma.html#numpy.random.gamma + /// + /// @param inShape + /// @param inGammaShape + /// @param inScaleValue (default 1) + /// @return NdArray + /// + template + NdArray gamma(const Shape& inShape, dtype inGammaShape, dtype inScaleValue = 1) + { + return detail::gamma(generator_, inShape, inGammaShape, inScaleValue); + } + + //============================================================================ + // Method Description: + /// Single random value sampled from the "geometric" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.geometric.html#numpy.random.geometric + /// + /// @param inP (probablity of success [0, 1]) + /// @return NdArray + /// + template + dtype geometric(double inP = 0.5) + { + return detail::geometric(generator_, inP); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "geometric" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.geometric.html#numpy.random.geometric + /// + /// @param inShape + /// @param inP (probablity of success [0, 1]) + /// @return NdArray + /// + template + NdArray geometric(const Shape& inShape, double inP = 0.5) + { + return detail::geometric(generator_, inShape, inP); + } #ifndef NUMCPP_NO_USE_BOOST - //============================================================================ - // Method Description: - /// Single random value sampled from the "laplace" distrubution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.laplace.html#numpy.random.laplace - /// - /// @param inLoc: (The position, mu, of the distribution peak. Default is 0) - /// @param inScale: (float optional the exponential decay. Default is 1) - /// @return NdArray - /// - template - dtype laplace(dtype inLoc = 0, dtype inScale = 1) - { - return detail::laplace(generator_, inLoc, inScale); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "laplace" distrubution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.laplace.html#numpy.random.laplace - /// - /// @param inShape - /// @param inLoc: (The position, mu, of the distribution peak. Default is 0) - /// @param inScale: (float optional the exponential decay. Default is 1) - /// @return NdArray - /// - template - NdArray laplace(const Shape& inShape, dtype inLoc = 0, dtype inScale = 1) - { - return detail::laplace(generator_, inShape, inLoc, inScale); - } + //============================================================================ + // Method Description: + /// Single random value sampled from the "laplace" distrubution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.laplace.html#numpy.random.laplace + /// + /// @param inLoc: (The position, mu, of the distribution peak. Default is 0) + /// @param inScale: (float optional the exponential decay. Default is 1) + /// @return NdArray + /// + template + dtype laplace(dtype inLoc = 0, dtype inScale = 1) + { + return detail::laplace(generator_, inLoc, inScale); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "laplace" distrubution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.laplace.html#numpy.random.laplace + /// + /// @param inShape + /// @param inLoc: (The position, mu, of the distribution peak. Default is 0) + /// @param inScale: (float optional the exponential decay. Default is 1) + /// @return NdArray + /// + template + NdArray laplace(const Shape& inShape, dtype inLoc = 0, dtype inScale = 1) + { + return detail::laplace(generator_, inShape, inLoc, inScale); + } #endif - //============================================================================ - // Method Description: - /// Single random value sampled from the "lognormal" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.lognormal.html#numpy.random.lognormal - /// - /// @param inMean: Mean value of the underlying normal distribution. Default is 0. - /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. - /// Default is 1. - /// @return NdArray - /// - template - dtype lognormal(dtype inMean = 0, dtype inSigma = 1) - { - return detail::lognormal(generator_, inMean, inSigma); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "lognormal" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.lognormal.html#numpy.random.lognormal - /// - /// @param inShape - /// @param inMean: Mean value of the underlying normal distribution. Default is 0. - /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. - /// Default is 1. - /// @return NdArray - /// - template - NdArray lognormal(const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) - { - return detail::lognormal(generator_, inShape, inMean, inSigma); - } - - //============================================================================ - // Method Description: - /// Single random value sampled from the "negative Binomial" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.negative_binomial.html#numpy.random.negative_binomial - /// - /// @param inN: number of trials - /// @param inP: probablity of success [0, 1] - /// @return NdArray - /// - template - dtype negativeBinomial(dtype inN, double inP = 0.5) - { - return detail::negativeBinomial(generator_, inN, inP); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "negative Binomial" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.negative_binomial.html#numpy.random.negative_binomial - /// - /// @param inShape - /// @param inN: number of trials - /// @param inP: probablity of success [0, 1] - /// @return NdArray - /// - template - NdArray negativeBinomial(const Shape& inShape, dtype inN, double inP = 0.5) - { - return detail::negativeBinomial(generator_, inShape, inN, inP); - } + //============================================================================ + // Method Description: + /// Single random value sampled from the "lognormal" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.lognormal.html#numpy.random.lognormal + /// + /// @param inMean: Mean value of the underlying normal distribution. Default is 0. + /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. + /// Default is 1. + /// @return NdArray + /// + template + dtype lognormal(dtype inMean = 0, dtype inSigma = 1) + { + return detail::lognormal(generator_, inMean, inSigma); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "lognormal" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.lognormal.html#numpy.random.lognormal + /// + /// @param inShape + /// @param inMean: Mean value of the underlying normal distribution. Default is 0. + /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. + /// Default is 1. + /// @return NdArray + /// + template + NdArray lognormal(const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) + { + return detail::lognormal(generator_, inShape, inMean, inSigma); + } + + //============================================================================ + // Method Description: + /// Single random value sampled from the "negative Binomial" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.negative_binomial.html#numpy.random.negative_binomial + /// + /// @param inN: number of trials + /// @param inP: probablity of success [0, 1] + /// @return NdArray + /// + template + dtype negativeBinomial(dtype inN, double inP = 0.5) + { + return detail::negativeBinomial(generator_, inN, inP); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "negative Binomial" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.negative_binomial.html#numpy.random.negative_binomial + /// + /// @param inShape + /// @param inN: number of trials + /// @param inP: probablity of success [0, 1] + /// @return NdArray + /// + template + NdArray negativeBinomial(const Shape& inShape, dtype inN, double inP = 0.5) + { + return detail::negativeBinomial(generator_, inShape, inN, inP); + } #ifndef NUMCPP_NO_USE_BOOST - //============================================================================ - // Method Description: - /// Single random value sampled from the "non central chi squared" distrubution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.noncentral_chisquare.html#numpy.random.noncentral_chisquare - /// - /// @param inK (default 1) - /// @param inLambda (default 1) - /// @return NdArray - /// - template - dtype nonCentralChiSquared(dtype inK = 1, dtype inLambda = 1) - { - return detail::nonCentralChiSquared(generator_, inK, inLambda); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "non central chi squared" distrubution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.noncentral_chisquare.html#numpy.random.noncentral_chisquare - /// - /// @param inShape - /// @param inK (default 1) - /// @param inLambda (default 1) - /// @return NdArray - /// - template - NdArray nonCentralChiSquared(const Shape& inShape, dtype inK = 1, dtype inLambda = 1) - { - return detail::nonCentralChiSquared(generator_, inShape, inK, inLambda); - } + //============================================================================ + // Method Description: + /// Single random value sampled from the "non central chi squared" distrubution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.noncentral_chisquare.html#numpy.random.noncentral_chisquare + /// + /// @param inK (default 1) + /// @param inLambda (default 1) + /// @return NdArray + /// + template + dtype nonCentralChiSquared(dtype inK = 1, dtype inLambda = 1) + { + return detail::nonCentralChiSquared(generator_, inK, inLambda); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "non central chi squared" distrubution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.noncentral_chisquare.html#numpy.random.noncentral_chisquare + /// + /// @param inShape + /// @param inK (default 1) + /// @param inLambda (default 1) + /// @return NdArray + /// + template + NdArray nonCentralChiSquared(const Shape& inShape, dtype inK = 1, dtype inLambda = 1) + { + return detail::nonCentralChiSquared(generator_, inShape, inK, inLambda); + } #endif - //============================================================================ - // Method Description: - /// Single random value sampled from the "normal" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.normal.html#numpy.random.normal - /// - /// @param inMean: Mean value of the underlying normal distribution. Default is 0. - /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. - /// Default is 1. - /// @return NdArray - /// - template - dtype normal(dtype inMean = 0, dtype inSigma = 1) - { - return detail::normal(generator_, inMean, inSigma); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "normal" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.normal.html#numpy.random.normal - /// - /// @param inShape - /// @param inMean: Mean value of the underlying normal distribution. Default is 0. - /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. - /// Default is 1. - /// @return NdArray - /// - template - NdArray normal(const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) - { - return detail::normal(generator_, inShape, inMean, inSigma); - } - - //============================================================================ - // Method Description: - /// Randomly permute a sequence, or return a permuted range. - /// If x is an integer, randomly permute np.arange(x). - /// If x is an array, make a copy and shuffle the elements randomly. - /// - /// @param inValue - /// @return NdArray - /// - template - NdArray permutation(dtype inValue) - { - return detail::permutation(generator_, inValue); - } - - //============================================================================ - // Method Description: - /// Randomly permute a sequence, or return a permuted range. - /// If x is an integer, randomly permute np.arange(x). - /// If x is an array, make a copy and shuffle the elements randomly. - /// - /// @param inArray - /// @return NdArray - /// - template - NdArray permutation(const NdArray& inArray) - { - return detail::permutation(generator_, inArray); - } - - //============================================================================ - // Method Description: - /// Single random value sampled from the "poisson" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.poisson.html#numpy.random.poisson - /// - /// @param inMean (default 1) - /// @return NdArray - /// - template - dtype poisson(double inMean = 1) - { - return detail::poisson(generator_, inMean); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "poisson" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.poisson.html#numpy.random.poisson - /// - /// @param inShape - /// @param inMean (default 1) - /// @return NdArray - /// - template - NdArray poisson(const Shape& inShape, double inMean = 1) - { - return detail::poisson(generator_, inShape, inMean); - } - - //============================================================================ - // Method Description: - /// Single random value sampled from the uniform distribution over [0, 1). - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.rand.html#numpy.random.rand - /// - /// @return NdArray - /// - template - dtype rand() - { - return detail::rand(generator_); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a uniform distribution over [0, 1). - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.rand.html#numpy.random.rand - /// - /// @param inShape - /// @return NdArray - /// - template - NdArray rand(const Shape& inShape) - { - return detail::rand(generator_, inShape); - } - - //============================================================================ - // Method Description: - /// Return a single random float from low (inclusive) to high (exclusive), - /// with the given shape. If no high value is input then the range will - /// go from [0, low). - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.ranf.html#numpy.random.ranf - /// - /// @param inLow - /// @param inHigh default 0. - /// @return NdArray - /// - template - dtype randFloat(dtype inLow, dtype inHigh = 0.) - { - return detail::randFloat(generator_, inLow, inHigh); - } - - //============================================================================ - // Method Description: - /// Return random floats from low (inclusive) to high (exclusive), - /// with the given shape. If no high value is input then the range will - /// go from [0, low). - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.ranf.html#numpy.random.ranf - /// - /// @param inShape - /// @param inLow - /// @param inHigh default 0. - /// @return NdArray - /// - template - NdArray randFloat(const Shape& inShape, dtype inLow, dtype inHigh = 0.) - { - return detail::randFloat(generator_, inShape, inLow, inHigh); - } - - //============================================================================ - // Method Description: - /// Return random integer from low (inclusive) to high (exclusive), - /// with the given shape. If no high value is input then the range will - /// go from [0, low). - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randint.html#numpy.random.randint - /// - /// @param inLow - /// @param inHigh default 0. - /// @return NdArray - /// - template - dtype randInt(dtype inLow, dtype inHigh = 0) - { - return detail::randInt(generator_, inLow, inHigh); - } - - //============================================================================ - // Method Description: - /// Return random integers from low (inclusive) to high (exclusive), - /// with the given shape. If no high value is input then the range will - /// go from [0, low). - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randint.html#numpy.random.randint - /// - /// @param inShape - /// @param inLow - /// @param inHigh default 0. - /// @return NdArray - /// - template - NdArray randInt(const Shape& inShape, dtype inLow, dtype inHigh = 0) - { - return detail::randInt(generator_, inShape, inLow, inHigh); - } - - //============================================================================ - // Method Description: - /// Returns a single random value sampled from the "standard normal" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randn.html#numpy.random.randn - /// - /// @return dtype - /// - template - dtype randN() - { - return detail::randN(generator_); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "standard normal" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randn.html#numpy.random.randn - /// - /// @param inShape - /// @return NdArray - /// - template - NdArray randN(const Shape& inShape) - { - return detail::randN(generator_, inShape); - } - - //============================================================================ - // Method Description: - /// Seed Constructor - /// - /// @param value: the seed value - /// - void seed(int value) noexcept - { - generator_.seed(value); - } - - //============================================================================ - // Method Description: - /// Modify a sequence in-place by shuffling its contents. - /// - /// @param inArray - /// - template - void shuffle(NdArray& inArray) - { - return detail::shuffle(generator_, inArray); - } - - //============================================================================ - // Method Description: - /// Single random value sampled from the "standard normal" distrubution with - /// mean = 0 and std = 1 - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_normal.html#numpy.random.standard_normal - /// - /// @return NdArray - /// - template - dtype standardNormal() - { - return detail::standardNormal(generator_); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "standard normal" distrubution with - /// mean = 0 and std = 1 - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_normal.html#numpy.random.standard_normal - /// - /// @param inShape - /// @return NdArray - /// - template - NdArray standardNormal(const Shape& inShape) - { - return detail::standardNormal(generator_, inShape); - } - - //============================================================================ - // Method Description: - /// Single random value sampled from the "student-T" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_t.html#numpy.random.standard_t - /// - /// @param inDof independent random variables - /// @return NdArray - /// - template - dtype studentT(dtype inDof) - { - return detail::studentT(generator_, inDof); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "student-T" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_t.html#numpy.random.standard_t - /// - /// @param inShape - /// @param inDof independent random variables - /// @return NdArray - /// - template - NdArray studentT(const Shape& inShape, dtype inDof) - { - return detail::studentT(generator_, inShape, inDof); - } + //============================================================================ + // Method Description: + /// Single random value sampled from the "normal" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.normal.html#numpy.random.normal + /// + /// @param inMean: Mean value of the underlying normal distribution. Default is 0. + /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. + /// Default is 1. + /// @return NdArray + /// + template + dtype normal(dtype inMean = 0, dtype inSigma = 1) + { + return detail::normal(generator_, inMean, inSigma); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "normal" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.normal.html#numpy.random.normal + /// + /// @param inShape + /// @param inMean: Mean value of the underlying normal distribution. Default is 0. + /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. + /// Default is 1. + /// @return NdArray + /// + template + NdArray normal(const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) + { + return detail::normal(generator_, inShape, inMean, inSigma); + } + + //============================================================================ + // Method Description: + /// Randomly permute a sequence, or return a permuted range. + /// If x is an integer, randomly permute np.arange(x). + /// If x is an array, make a copy and shuffle the elements randomly. + /// + /// @param inValue + /// @return NdArray + /// + template + NdArray permutation(dtype inValue) + { + return detail::permutation(generator_, inValue); + } + + //============================================================================ + // Method Description: + /// Randomly permute a sequence, or return a permuted range. + /// If x is an integer, randomly permute np.arange(x). + /// If x is an array, make a copy and shuffle the elements randomly. + /// + /// @param inArray + /// @return NdArray + /// + template + NdArray permutation(const NdArray& inArray) + { + return detail::permutation(generator_, inArray); + } + + //============================================================================ + // Method Description: + /// Single random value sampled from the "poisson" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.poisson.html#numpy.random.poisson + /// + /// @param inMean (default 1) + /// @return NdArray + /// + template + dtype poisson(double inMean = 1) + { + return detail::poisson(generator_, inMean); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "poisson" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.poisson.html#numpy.random.poisson + /// + /// @param inShape + /// @param inMean (default 1) + /// @return NdArray + /// + template + NdArray poisson(const Shape& inShape, double inMean = 1) + { + return detail::poisson(generator_, inShape, inMean); + } + + //============================================================================ + // Method Description: + /// Single random value sampled from the uniform distribution over [0, 1). + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.rand.html#numpy.random.rand + /// + /// @return NdArray + /// + template + dtype rand() + { + return detail::rand(generator_); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a uniform distribution over [0, 1). + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.rand.html#numpy.random.rand + /// + /// @param inShape + /// @return NdArray + /// + template + NdArray rand(const Shape& inShape) + { + return detail::rand(generator_, inShape); + } + + //============================================================================ + // Method Description: + /// Return a single random float from low (inclusive) to high (exclusive), + /// with the given shape. If no high value is input then the range will + /// go from [0, low). + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.ranf.html#numpy.random.ranf + /// + /// @param inLow + /// @param inHigh default 0. + /// @return NdArray + /// + template + dtype randFloat(dtype inLow, dtype inHigh = 0.) + { + return detail::randFloat(generator_, inLow, inHigh); + } + + //============================================================================ + // Method Description: + /// Return random floats from low (inclusive) to high (exclusive), + /// with the given shape. If no high value is input then the range will + /// go from [0, low). + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.ranf.html#numpy.random.ranf + /// + /// @param inShape + /// @param inLow + /// @param inHigh default 0. + /// @return NdArray + /// + template + NdArray randFloat(const Shape& inShape, dtype inLow, dtype inHigh = 0.) + { + return detail::randFloat(generator_, inShape, inLow, inHigh); + } + + //============================================================================ + // Method Description: + /// Return random integer from low (inclusive) to high (exclusive), + /// with the given shape. If no high value is input then the range will + /// go from [0, low). + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randint.html#numpy.random.randint + /// + /// @param inLow + /// @param inHigh default 0. + /// @return NdArray + /// + template + dtype randInt(dtype inLow, dtype inHigh = 0) + { + return detail::randInt(generator_, inLow, inHigh); + } + + //============================================================================ + // Method Description: + /// Return random integers from low (inclusive) to high (exclusive), + /// with the given shape. If no high value is input then the range will + /// go from [0, low). + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randint.html#numpy.random.randint + /// + /// @param inShape + /// @param inLow + /// @param inHigh default 0. + /// @return NdArray + /// + template + NdArray randInt(const Shape& inShape, dtype inLow, dtype inHigh = 0) + { + return detail::randInt(generator_, inShape, inLow, inHigh); + } + + //============================================================================ + // Method Description: + /// Returns a single random value sampled from the "standard normal" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randn.html#numpy.random.randn + /// + /// @return dtype + /// + template + dtype randN() + { + return detail::randN(generator_); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "standard normal" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randn.html#numpy.random.randn + /// + /// @param inShape + /// @return NdArray + /// + template + NdArray randN(const Shape& inShape) + { + return detail::randN(generator_, inShape); + } + + //============================================================================ + // Method Description: + /// Seed Constructor + /// + /// @param value: the seed value + /// + void seed(int value) noexcept + { + generator_.seed(value); + } + + //============================================================================ + // Method Description: + /// Modify a sequence in-place by shuffling its contents. + /// + /// @param inArray + /// + template + void shuffle(NdArray& inArray) + { + return detail::shuffle(generator_, inArray); + } + + //============================================================================ + // Method Description: + /// Single random value sampled from the "standard normal" distrubution with + /// mean = 0 and std = 1 + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_normal.html#numpy.random.standard_normal + /// + /// @return NdArray + /// + template + dtype standardNormal() + { + return detail::standardNormal(generator_); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "standard normal" distrubution with + /// mean = 0 and std = 1 + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_normal.html#numpy.random.standard_normal + /// + /// @param inShape + /// @return NdArray + /// + template + NdArray standardNormal(const Shape& inShape) + { + return detail::standardNormal(generator_, inShape); + } + + //============================================================================ + // Method Description: + /// Single random value sampled from the "student-T" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_t.html#numpy.random.standard_t + /// + /// @param inDof independent random variables + /// @return NdArray + /// + template + dtype studentT(dtype inDof) + { + return detail::studentT(generator_, inDof); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "student-T" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_t.html#numpy.random.standard_t + /// + /// @param inShape + /// @param inDof independent random variables + /// @return NdArray + /// + template + NdArray studentT(const Shape& inShape, dtype inDof) + { + return detail::studentT(generator_, inShape, inDof); + } #ifndef NUMCPP_NO_USE_BOOST - //============================================================================ - // Method Description: - /// Single random value sampled from the "triangle" distribution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.triangular.html#numpy.random.triangular - /// - /// @param inA - /// @param inB - /// @param inC - /// @return NdArray - /// - template - dtype triangle(dtype inA = 0, dtype inB = 0.5, dtype inC = 1) - { - return detail::triangle(generator_, inA, inB, inC); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "triangle" distribution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.triangular.html#numpy.random.triangular - /// - /// @param inShape - /// @param inA - /// @param inB - /// @param inC - /// @return NdArray - /// - template - NdArray triangle(const Shape& inShape, dtype inA = 0, dtype inB = 0.5, dtype inC = 1) - { - return detail::triangle(generator_, inShape, inA, inB, inC); - } + //============================================================================ + // Method Description: + /// Single random value sampled from the "triangle" distribution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.triangular.html#numpy.random.triangular + /// + /// @param inA + /// @param inB + /// @param inC + /// @return NdArray + /// + template + dtype triangle(dtype inA = 0, dtype inB = 0.5, dtype inC = 1) + { + return detail::triangle(generator_, inA, inB, inC); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "triangle" distribution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.triangular.html#numpy.random.triangular + /// + /// @param inShape + /// @param inA + /// @param inB + /// @param inC + /// @return NdArray + /// + template + NdArray triangle(const Shape& inShape, dtype inA = 0, dtype inB = 0.5, dtype inC = 1) + { + return detail::triangle(generator_, inShape, inA, inB, inC); + } #endif - //============================================================================ - // Method Description: - /// Draw sample from a uniform distribution. - /// - /// Samples are uniformly distributed over the half - - /// open interval[low, high) (includes low, but excludes high) - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.uniform.html#numpy.random.uniform - /// - /// @param inLow - /// @param inHigh - /// @return NdArray - /// - template - dtype uniform(dtype inLow, dtype inHigh) - { - return detail::uniform(generator_, inLow, inHigh); - } - - //============================================================================ - // Method Description: - /// Draw samples from a uniform distribution. - /// - /// Samples are uniformly distributed over the half - - /// open interval[low, high) (includes low, but excludes high) - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.uniform.html#numpy.random.uniform - /// - /// @param inShape - /// @param inLow - /// @param inHigh - /// @return NdArray - /// - template - NdArray uniform(const Shape& inShape, dtype inLow, dtype inHigh) - { - return detail::uniform(generator_, inShape, inLow, inHigh); - } + //============================================================================ + // Method Description: + /// Draw sample from a uniform distribution. + /// + /// Samples are uniformly distributed over the half - + /// open interval[low, high) (includes low, but excludes high) + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.uniform.html#numpy.random.uniform + /// + /// @param inLow + /// @param inHigh + /// @return NdArray + /// + template + dtype uniform(dtype inLow, dtype inHigh) + { + return detail::uniform(generator_, inLow, inHigh); + } + + //============================================================================ + // Method Description: + /// Draw samples from a uniform distribution. + /// + /// Samples are uniformly distributed over the half - + /// open interval[low, high) (includes low, but excludes high) + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.uniform.html#numpy.random.uniform + /// + /// @param inShape + /// @param inLow + /// @param inHigh + /// @return NdArray + /// + template + NdArray uniform(const Shape& inShape, dtype inLow, dtype inHigh) + { + return detail::uniform(generator_, inShape, inLow, inHigh); + } #ifndef NUMCPP_NO_USE_BOOST - //============================================================================ - // Method Description: - /// Such a distribution produces random numbers uniformly - /// distributed on the unit sphere of arbitrary dimension dim. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inNumPoints - /// @param inDims: dimension of the sphere (default 2) - /// @return NdArray - /// - template - NdArray uniformOnSphere(uint32 inNumPoints, uint32 inDims = 2) - { - return detail::uniformOnSphere(generator_, inNumPoints, inDims); - } + //============================================================================ + // Method Description: + /// Such a distribution produces random numbers uniformly + /// distributed on the unit sphere of arbitrary dimension dim. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inNumPoints + /// @param inDims: dimension of the sphere (default 2) + /// @return NdArray + /// + template + NdArray uniformOnSphere(uint32 inNumPoints, uint32 inDims = 2) + { + return detail::uniformOnSphere(generator_, inNumPoints, inDims); + } #endif - //============================================================================ - // Method Description: - /// Single random value sampled from the "weibull" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.weibull.html#numpy.random.weibull - /// - /// @param inA (default 1) - /// @param inB (default 1) - /// @return NdArray - /// - template - dtype weibull(dtype inA = 1, dtype inB = 1) - { - return detail::weibull(generator_, inA, inB); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "weibull" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.weibull.html#numpy.random.weibull - /// - /// @param inShape - /// @param inA (default 1) - /// @param inB (default 1) - /// @return NdArray - /// - template - NdArray weibull(const Shape& inShape, dtype inA = 1, dtype inB = 1) - { - return detail::weibull(generator_, inShape, inA, inB); - } - - private: - GeneratorType generator_{}; - }; - } // namespace random -} // namespace nc + //============================================================================ + // Method Description: + /// Single random value sampled from the "weibull" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.weibull.html#numpy.random.weibull + /// + /// @param inA (default 1) + /// @param inB (default 1) + /// @return NdArray + /// + template + dtype weibull(dtype inA = 1, dtype inB = 1) + { + return detail::weibull(generator_, inA, inB); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "weibull" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.weibull.html#numpy.random.weibull + /// + /// @param inShape + /// @param inA (default 1) + /// @param inB (default 1) + /// @return NdArray + /// + template + NdArray weibull(const Shape& inShape, dtype inA = 1, dtype inB = 1) + { + return detail::weibull(generator_, inShape, inA, inB); + } + + private: + GeneratorType generator_{}; + }; +} // namespace nc::random diff --git a/include/NumCpp/Random/bernoulli.hpp b/include/NumCpp/Random/bernoulli.hpp index 61bac992d..7717fb2d7 100644 --- a/include/NumCpp/Random/bernoulli.hpp +++ b/include/NumCpp/Random/bernoulli.hpp @@ -37,72 +37,28 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the "bernoulli" distribution. - /// - /// @param generator: instance of a random number generator - /// @param inP (probability of success [0, 1]). Default 0.5 - /// @return NdArray - /// - template - bool bernoulli(GeneratorType& generator, double inP = 0.5) - { - if (inP < 0 || inP > 1) - { - THROW_INVALID_ARGUMENT_ERROR("input probability of success must be of the range [0, 1]."); - } - - std::bernoulli_distribution dist(inP); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "bernoulli" distribution. - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inP (probability of success [0, 1]). Default 0.5 - /// @return NdArray - /// - template - NdArray bernoulli(GeneratorType& generator, const Shape& inShape, double inP = 0.5) - { - if (inP < 0 || inP > 1) - { - THROW_INVALID_ARGUMENT_ERROR("input probability of success must be of the range [0, 1]."); - } - - NdArray returnArray(inShape); - - std::bernoulli_distribution dist(inP); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](bool& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the "bernoulli" distribution. /// + /// @param generator: instance of a random number generator /// @param inP (probability of success [0, 1]). Default 0.5 /// @return NdArray /// - inline bool bernoulli(double inP = 0.5) + template + bool bernoulli(GeneratorType& generator, double inP = 0.5) { - return detail::bernoulli(generator_, inP); + if (inP < 0 || inP > 1) + { + THROW_INVALID_ARGUMENT_ERROR("input probability of success must be of the range [0, 1]."); + } + + std::bernoulli_distribution dist(inP); + return dist(generator); } //============================================================================ @@ -110,13 +66,54 @@ namespace nc /// Create an array of the given shape and populate it with /// random samples from the "bernoulli" distribution. /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inP (probability of success [0, 1]). Default 0.5 /// @return NdArray /// - inline NdArray bernoulli(const Shape& inShape, double inP = 0.5) + template + NdArray bernoulli(GeneratorType& generator, const Shape& inShape, double inP = 0.5) { - return detail::bernoulli(generator_, inShape, inP); + if (inP < 0 || inP > 1) + { + THROW_INVALID_ARGUMENT_ERROR("input probability of success must be of the range [0, 1]."); + } + + NdArray returnArray(inShape); + + std::bernoulli_distribution dist(inP); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](bool& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "bernoulli" distribution. + /// + /// @param inP (probability of success [0, 1]). Default 0.5 + /// @return NdArray + /// + inline bool bernoulli(double inP = 0.5) + { + return detail::bernoulli(generator_, inP); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "bernoulli" distribution. + /// + /// @param inShape + /// @param inP (probability of success [0, 1]). Default 0.5 + /// @return NdArray + /// + inline NdArray bernoulli(const Shape& inShape, double inP = 0.5) + { + return detail::bernoulli(generator_, inShape, inP); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/beta.hpp b/include/NumCpp/Random/beta.hpp index a5856a094..d31405876 100644 --- a/include/NumCpp/Random/beta.hpp +++ b/include/NumCpp/Random/beta.hpp @@ -40,86 +40,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the from the "beta" distribution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.beta.html#numpy.random.beta - /// - /// @param generator: instance of a random number generator - /// @param inAlpha - /// @param inBeta - /// @return NdArray - /// - template - dtype beta(GeneratorType& generator, dtype inAlpha, dtype inBeta) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inAlpha < 0) - { - THROW_INVALID_ARGUMENT_ERROR("input alpha must be greater than zero."); - } - - if (inBeta < 0) - { - THROW_INVALID_ARGUMENT_ERROR("input beta must be greater than zero."); - } - - boost::random::beta_distribution dist(inAlpha, inBeta); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "beta" distribution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.beta.html#numpy.random.beta - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inAlpha - /// @param inBeta - /// @return NdArray - /// - template - NdArray beta(GeneratorType& generator, const Shape& inShape, dtype inAlpha, dtype inBeta) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inAlpha < 0) - { - THROW_INVALID_ARGUMENT_ERROR("input alpha must be greater than zero."); - } - - if (inBeta < 0) - { - THROW_INVALID_ARGUMENT_ERROR("input beta must be greater than zero."); - } - - NdArray returnArray(inShape); - - boost::random::beta_distribution dist(inAlpha, inBeta); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the from the "beta" distribution. @@ -128,14 +52,28 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.beta.html#numpy.random.beta /// + /// @param generator: instance of a random number generator /// @param inAlpha /// @param inBeta /// @return NdArray /// - template - dtype beta(dtype inAlpha, dtype inBeta) + template + dtype beta(GeneratorType& generator, dtype inAlpha, dtype inBeta) { - return detail::beta(generator_, inAlpha, inBeta); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inAlpha < 0) + { + THROW_INVALID_ARGUMENT_ERROR("input alpha must be greater than zero."); + } + + if (inBeta < 0) + { + THROW_INVALID_ARGUMENT_ERROR("input beta must be greater than zero."); + } + + boost::random::beta_distribution dist(inAlpha, inBeta); + return dist(generator); } //============================================================================ @@ -147,17 +85,76 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.beta.html#numpy.random.beta /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inAlpha /// @param inBeta /// @return NdArray /// - template - NdArray beta(const Shape& inShape, dtype inAlpha, dtype inBeta) + template + NdArray beta(GeneratorType& generator, const Shape& inShape, dtype inAlpha, dtype inBeta) { - return detail::beta(generator_, inShape, inAlpha, inBeta); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inAlpha < 0) + { + THROW_INVALID_ARGUMENT_ERROR("input alpha must be greater than zero."); + } + + if (inBeta < 0) + { + THROW_INVALID_ARGUMENT_ERROR("input beta must be greater than zero."); + } + + NdArray returnArray(inShape); + + boost::random::beta_distribution dist(inAlpha, inBeta); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the from the "beta" distribution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.beta.html#numpy.random.beta + /// + /// @param inAlpha + /// @param inBeta + /// @return NdArray + /// + template + dtype beta(dtype inAlpha, dtype inBeta) + { + return detail::beta(generator_, inAlpha, inBeta); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "beta" distribution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.beta.html#numpy.random.beta + /// + /// @param inShape + /// @param inAlpha + /// @param inBeta + /// @return NdArray + /// + template + NdArray beta(const Shape& inShape, dtype inAlpha, dtype inBeta) + { + return detail::beta(generator_, inShape, inAlpha, inBeta); + } +} // namespace nc::random #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Random/binomial.hpp b/include/NumCpp/Random/binomial.hpp index 17fcb0d17..9d391f17a 100644 --- a/include/NumCpp/Random/binomial.hpp +++ b/include/NumCpp/Random/binomial.hpp @@ -37,84 +37,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the from the "binomial" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.binomial.html#numpy.random.binomial - /// - /// @param generator: instance of a random number generator - /// @param inN (number of trials) - /// @param inP (probablity of success [0, 1]) - /// @return NdArray - /// - template - dtype binomial(GeneratorType& generator, dtype inN, double inP = 0.5) - { - STATIC_ASSERT_INTEGER(dtype); - - if (inN < 0) - { - THROW_INVALID_ARGUMENT_ERROR("input number of trials must be greater than or equal to zero."); - } - - if (inP < 0 || inP > 1) - { - THROW_INVALID_ARGUMENT_ERROR("input probability of sucess must be of the range [0, 1]."); - } - - std::binomial_distribution dist(inN, inP); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "binomial" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.binomial.html#numpy.random.binomial - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inN (number of trials) - /// @param inP (probablity of success [0, 1]) - /// @return NdArray - /// - template - NdArray binomial(GeneratorType& generator, const Shape& inShape, dtype inN, double inP = 0.5) - { - STATIC_ASSERT_INTEGER(dtype); - - if (inN < 0) - { - THROW_INVALID_ARGUMENT_ERROR("input number of trials must be greater than or equal to zero."); - } - - if (inP < 0 || inP > 1) - { - THROW_INVALID_ARGUMENT_ERROR("input probability of sucess must be of the range [0, 1]."); - } - - NdArray returnArray(inShape); - - std::binomial_distribution dist(inN, inP); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the from the "binomial" distribution. @@ -122,14 +48,28 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.binomial.html#numpy.random.binomial /// + /// @param generator: instance of a random number generator /// @param inN (number of trials) /// @param inP (probablity of success [0, 1]) /// @return NdArray /// - template - dtype binomial(dtype inN, double inP = 0.5) + template + dtype binomial(GeneratorType& generator, dtype inN, double inP = 0.5) { - return detail::binomial(generator_, inN, inP); + STATIC_ASSERT_INTEGER(dtype); + + if (inN < 0) + { + THROW_INVALID_ARGUMENT_ERROR("input number of trials must be greater than or equal to zero."); + } + + if (inP < 0 || inP > 1) + { + THROW_INVALID_ARGUMENT_ERROR("input probability of sucess must be of the range [0, 1]."); + } + + std::binomial_distribution dist(inN, inP); + return dist(generator); } //============================================================================ @@ -140,15 +80,72 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.binomial.html#numpy.random.binomial /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inN (number of trials) /// @param inP (probablity of success [0, 1]) /// @return NdArray /// - template - NdArray binomial(const Shape& inShape, dtype inN, double inP = 0.5) + template + NdArray binomial(GeneratorType& generator, const Shape& inShape, dtype inN, double inP = 0.5) { - return detail::binomial(generator_, inShape, inN, inP); + STATIC_ASSERT_INTEGER(dtype); + + if (inN < 0) + { + THROW_INVALID_ARGUMENT_ERROR("input number of trials must be greater than or equal to zero."); + } + + if (inP < 0 || inP > 1) + { + THROW_INVALID_ARGUMENT_ERROR("input probability of sucess must be of the range [0, 1]."); + } + + NdArray returnArray(inShape); + + std::binomial_distribution dist(inN, inP); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the from the "binomial" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.binomial.html#numpy.random.binomial + /// + /// @param inN (number of trials) + /// @param inP (probablity of success [0, 1]) + /// @return NdArray + /// + template + dtype binomial(dtype inN, double inP = 0.5) + { + return detail::binomial(generator_, inN, inP); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "binomial" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.binomial.html#numpy.random.binomial + /// + /// @param inShape + /// @param inN (number of trials) + /// @param inP (probablity of success [0, 1]) + /// @return NdArray + /// + template + NdArray binomial(const Shape& inShape, dtype inN, double inP = 0.5) + { + return detail::binomial(generator_, inShape, inN, inP); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/cauchy.hpp b/include/NumCpp/Random/cauchy.hpp index 3bcb0f876..ab7f7a7dd 100644 --- a/include/NumCpp/Random/cauchy.hpp +++ b/include/NumCpp/Random/cauchy.hpp @@ -37,83 +37,32 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the from the "cauchy" distrubution. - /// - /// @param generator: instance of a random number generator - /// @param inMean: Mean value of the underlying normal distribution. Default is 0. - /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. - /// Default is 1. - /// @return NdArray - /// - template - dtype cauchy(GeneratorType& generator, dtype inMean = 0, dtype inSigma = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inSigma <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input sigma must be greater than zero."); - } - - std::cauchy_distribution dist(inMean, inSigma); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "cauchy" distrubution. - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inMean: Mean value of the underlying normal distribution. Default is 0. - /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. - /// Default is 1. - /// @return NdArray - /// - template - NdArray cauchy(GeneratorType& generator, const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inSigma <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input sigma must be greater than zero."); - } - - NdArray returnArray(inShape); - - std::cauchy_distribution dist(inMean, inSigma); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the from the "cauchy" distrubution. /// + /// @param generator: instance of a random number generator /// @param inMean: Mean value of the underlying normal distribution. Default is 0. /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. /// Default is 1. /// @return NdArray /// - template - dtype cauchy(dtype inMean = 0, dtype inSigma = 1) + template + dtype cauchy(GeneratorType& generator, dtype inMean = 0, dtype inSigma = 1) { - return detail::cauchy(generator_, inMean, inSigma); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inSigma <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input sigma must be greater than zero."); + } + + std::cauchy_distribution dist(inMean, inSigma); + return dist(generator); } //============================================================================ @@ -121,16 +70,64 @@ namespace nc /// Create an array of the given shape and populate it with /// random samples from a "cauchy" distrubution. /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inMean: Mean value of the underlying normal distribution. Default is 0. /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. /// Default is 1. /// @return NdArray /// - template - NdArray cauchy(const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) + template + NdArray cauchy(GeneratorType& generator, const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) { - return detail::cauchy(generator_, inShape, inMean, inSigma); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inSigma <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input sigma must be greater than zero."); + } + + NdArray returnArray(inShape); + + std::cauchy_distribution dist(inMean, inSigma); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the from the "cauchy" distrubution. + /// + /// @param inMean: Mean value of the underlying normal distribution. Default is 0. + /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. + /// Default is 1. + /// @return NdArray + /// + template + dtype cauchy(dtype inMean = 0, dtype inSigma = 1) + { + return detail::cauchy(generator_, inMean, inSigma); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "cauchy" distrubution. + /// + /// @param inShape + /// @param inMean: Mean value of the underlying normal distribution. Default is 0. + /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. + /// Default is 1. + /// @return NdArray + /// + template + NdArray cauchy(const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) + { + return detail::cauchy(generator_, inShape, inMean, inSigma); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/chiSquare.hpp b/include/NumCpp/Random/chiSquare.hpp index 9b62d1683..188262d2b 100644 --- a/include/NumCpp/Random/chiSquare.hpp +++ b/include/NumCpp/Random/chiSquare.hpp @@ -37,72 +37,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the from the "chi square" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.chisquare.html#numpy.random.chisquare - /// - /// @param generator: instance of a random number generator - /// @param inDof (independent random variables) - /// @return NdArray - /// - template - dtype chiSquare(GeneratorType& generator, dtype inDof) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inDof <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("numerator degrees of freedom must be greater than zero."); - } - - std::chi_squared_distribution dist(inDof); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "chi square" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.chisquare.html#numpy.random.chisquare - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inDof (independent random variables) - /// @return NdArray - /// - template - NdArray chiSquare(GeneratorType& generator, const Shape& inShape, dtype inDof) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inDof <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("numerator degrees of freedom must be greater than zero."); - } - - NdArray returnArray(inShape); - - std::chi_squared_distribution dist(inDof); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the from the "chi square" distribution. @@ -110,13 +48,22 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.chisquare.html#numpy.random.chisquare /// + /// @param generator: instance of a random number generator /// @param inDof (independent random variables) /// @return NdArray /// - template - dtype chiSquare(dtype inDof) + template + dtype chiSquare(GeneratorType& generator, dtype inDof) { - return detail::chiSquare(generator_, inDof); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inDof <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("numerator degrees of freedom must be greater than zero."); + } + + std::chi_squared_distribution dist(inDof); + return dist(generator); } //============================================================================ @@ -127,14 +74,64 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.chisquare.html#numpy.random.chisquare /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inDof (independent random variables) /// @return NdArray /// - template - NdArray chiSquare(const Shape& inShape, dtype inDof) + template + NdArray chiSquare(GeneratorType& generator, const Shape& inShape, dtype inDof) { - return detail::chiSquare(generator_, inShape, inDof); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inDof <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("numerator degrees of freedom must be greater than zero."); + } + + NdArray returnArray(inShape); + + std::chi_squared_distribution dist(inDof); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the from the "chi square" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.chisquare.html#numpy.random.chisquare + /// + /// @param inDof (independent random variables) + /// @return NdArray + /// + template + dtype chiSquare(dtype inDof) + { + return detail::chiSquare(generator_, inDof); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "chi square" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.chisquare.html#numpy.random.chisquare + /// + /// @param inShape + /// @param inDof (independent random variables) + /// @return NdArray + /// + template + NdArray chiSquare(const Shape& inShape, dtype inDof) + { + return detail::chiSquare(generator_, inShape, inDof); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/choice.hpp b/include/NumCpp/Random/choice.hpp index f2d11d47c..c9f5a7cd4 100644 --- a/include/NumCpp/Random/choice.hpp +++ b/include/NumCpp/Random/choice.hpp @@ -36,86 +36,83 @@ #include "NumCpp/Random/permutation.hpp" #include "NumCpp/Random/randInt.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Chooses a random sample from an input array. - /// - /// @param generator: instance of a random number generator - /// @param inArray - /// @return NdArray - /// - template - dtype choice(GeneratorType& generator, const NdArray& inArray) - { - uint32 randIdx = detail::randInt(generator, inArray.size()); - return inArray[randIdx]; - } - - //============================================================================ - // Method Description: - /// Chooses inNum random samples from an input array. - /// - /// @param generator: instance of a random number generator - /// @param inArray - /// @param inNum - /// @param replace: Whether the sample is with or without replacement - /// @return NdArray - /// - template - NdArray - choice(GeneratorType& generator, const NdArray& inArray, uint32 inNum, bool replace = true) - { - if (!replace && inNum > inArray.size()) - { - THROW_INVALID_ARGUMENT_ERROR("when 'replace' == false 'inNum' must be <= inArray.size()"); - } - - if (replace) - { - NdArray outArray(1, inNum); - std::for_each(outArray.begin(), - outArray.end(), - [&generator, &inArray](dtype& value) -> void { value = choice(generator, inArray); }); - - return outArray; - } - - return detail::permutation(generator, inArray)[Slice(inNum)]; - } - } // namespace detail - //============================================================================ // Method Description: /// Chooses a random sample from an input array. /// + /// @param generator: instance of a random number generator /// @param inArray /// @return NdArray /// - template - dtype choice(const NdArray& inArray) + template + dtype choice(GeneratorType& generator, const NdArray& inArray) { - return detail::choice(generator_, inArray); + uint32 randIdx = detail::randInt(generator, inArray.size()); + return inArray[randIdx]; } //============================================================================ // Method Description: /// Chooses inNum random samples from an input array. /// + /// @param generator: instance of a random number generator /// @param inArray /// @param inNum /// @param replace: Whether the sample is with or without replacement /// @return NdArray /// - template - NdArray choice(const NdArray& inArray, uint32 inNum, bool replace = true) + template + NdArray + choice(GeneratorType& generator, const NdArray& inArray, uint32 inNum, bool replace = true) { - return detail::choice(generator_, inArray, inNum, replace = true); + if (!replace && inNum > inArray.size()) + { + THROW_INVALID_ARGUMENT_ERROR("when 'replace' == false 'inNum' must be <= inArray.size()"); + } + + if (replace) + { + NdArray outArray(1, inNum); + std::for_each(outArray.begin(), + outArray.end(), + [&generator, &inArray](dtype& value) -> void { value = choice(generator, inArray); }); + + return outArray; + } + + return detail::permutation(generator, inArray)[Slice(inNum)]; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Chooses a random sample from an input array. + /// + /// @param inArray + /// @return NdArray + /// + template + dtype choice(const NdArray& inArray) + { + return detail::choice(generator_, inArray); + } + + //============================================================================ + // Method Description: + /// Chooses inNum random samples from an input array. + /// + /// @param inArray + /// @param inNum + /// @param replace: Whether the sample is with or without replacement + /// @return NdArray + /// + template + NdArray choice(const NdArray& inArray, uint32 inNum, bool replace = true) + { + return detail::choice(generator_, inArray, inNum, replace = true); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/discrete.hpp b/include/NumCpp/Random/discrete.hpp index 329cc636b..07843d6d9 100644 --- a/include/NumCpp/Random/discrete.hpp +++ b/include/NumCpp/Random/discrete.hpp @@ -36,62 +36,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the from the - /// "discrete" distrubution. It produces integers in the - /// range [0, n) with the probability of producing each value - /// is specified by the parameters of the distribution. - /// - /// @param generator: instance of a random number generator - /// @param inWeights - /// @return NdArray - /// - template - dtype discrete(GeneratorType& generator, const NdArray& inWeights) - { - STATIC_ASSERT_INTEGER(dtype); - - std::discrete_distribution dist(inWeights.cbegin(), inWeights.cend()); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "discrete" distrubution. It produces - /// integers in the range [0, n) with the probability of - /// producing each value is specified by the parameters - /// of the distribution. - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inWeights - /// @return NdArray - /// - template - NdArray discrete(GeneratorType& generator, const Shape& inShape, const NdArray& inWeights) - { - STATIC_ASSERT_INTEGER(dtype); - - NdArray returnArray(inShape); - - std::discrete_distribution dist(inWeights.cbegin(), inWeights.cend()); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the from the @@ -99,13 +47,17 @@ namespace nc /// range [0, n) with the probability of producing each value /// is specified by the parameters of the distribution. /// + /// @param generator: instance of a random number generator /// @param inWeights /// @return NdArray /// - template - dtype discrete(const NdArray& inWeights) + template + dtype discrete(GeneratorType& generator, const NdArray& inWeights) { - return detail::discrete(generator_, inWeights); + STATIC_ASSERT_INTEGER(dtype); + + std::discrete_distribution dist(inWeights.cbegin(), inWeights.cend()); + return dist(generator); } //============================================================================ @@ -116,14 +68,59 @@ namespace nc /// producing each value is specified by the parameters /// of the distribution. /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inWeights /// @return NdArray /// - template - NdArray discrete(const Shape& inShape, const NdArray& inWeights) + template + NdArray discrete(GeneratorType& generator, const Shape& inShape, const NdArray& inWeights) { - return detail::discrete(generator_, inShape, inWeights); + STATIC_ASSERT_INTEGER(dtype); + + NdArray returnArray(inShape); + + std::discrete_distribution dist(inWeights.cbegin(), inWeights.cend()); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the from the + /// "discrete" distrubution. It produces integers in the + /// range [0, n) with the probability of producing each value + /// is specified by the parameters of the distribution. + /// + /// @param inWeights + /// @return NdArray + /// + template + dtype discrete(const NdArray& inWeights) + { + return detail::discrete(generator_, inWeights); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "discrete" distrubution. It produces + /// integers in the range [0, n) with the probability of + /// producing each value is specified by the parameters + /// of the distribution. + /// + /// @param inShape + /// @param inWeights + /// @return NdArray + /// + template + NdArray discrete(const Shape& inShape, const NdArray& inWeights) + { + return detail::discrete(generator_, inShape, inWeights); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/exponential.hpp b/include/NumCpp/Random/exponential.hpp index 5d0c3067b..b04b39135 100644 --- a/include/NumCpp/Random/exponential.hpp +++ b/include/NumCpp/Random/exponential.hpp @@ -35,62 +35,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the "exponential" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.exponential.html#numpy.random.exponential - /// - /// @param generator: instance of a random number generator - /// @param inScaleValue (default 1) - /// @return NdArray - /// - template - dtype exponential(GeneratorType& generator, dtype inScaleValue = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - std::exponential_distribution dist(inScaleValue); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "exponential" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.exponential.html#numpy.random.exponential - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inScaleValue (default 1) - /// @return NdArray - /// - template - NdArray exponential(GeneratorType& generator, const Shape& inShape, dtype inScaleValue = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - NdArray returnArray(inShape); - - std::exponential_distribution dist(inScaleValue); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the "exponential" distrubution. @@ -98,13 +46,17 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.exponential.html#numpy.random.exponential /// + /// @param generator: instance of a random number generator /// @param inScaleValue (default 1) /// @return NdArray /// - template - dtype exponential(dtype inScaleValue = 1) + template + dtype exponential(GeneratorType& generator, dtype inScaleValue = 1) { - return detail::exponential(generator_, inScaleValue); + STATIC_ASSERT_ARITHMETIC(dtype); + + std::exponential_distribution dist(inScaleValue); + return dist(generator); } //============================================================================ @@ -115,14 +67,59 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.exponential.html#numpy.random.exponential /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inScaleValue (default 1) /// @return NdArray /// - template - NdArray exponential(const Shape& inShape, dtype inScaleValue = 1) + template + NdArray exponential(GeneratorType& generator, const Shape& inShape, dtype inScaleValue = 1) { - return detail::exponential(generator_, inShape, inScaleValue); + STATIC_ASSERT_ARITHMETIC(dtype); + + NdArray returnArray(inShape); + + std::exponential_distribution dist(inScaleValue); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "exponential" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.exponential.html#numpy.random.exponential + /// + /// @param inScaleValue (default 1) + /// @return NdArray + /// + template + dtype exponential(dtype inScaleValue = 1) + { + return detail::exponential(generator_, inScaleValue); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "exponential" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.exponential.html#numpy.random.exponential + /// + /// @param inShape + /// @param inScaleValue (default 1) + /// @return NdArray + /// + template + NdArray exponential(const Shape& inShape, dtype inScaleValue = 1) + { + return detail::exponential(generator_, inShape, inScaleValue); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/extremeValue.hpp b/include/NumCpp/Random/extremeValue.hpp index c95e1df8a..5113c7f8f 100644 --- a/include/NumCpp/Random/extremeValue.hpp +++ b/include/NumCpp/Random/extremeValue.hpp @@ -37,90 +37,36 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the "extreme value" distrubution. - /// - /// @param generator: instance of a random number generator - /// @param inA (default 1) - /// @param inB (default 1) - /// @return NdArray - /// - template - dtype extremeValue(GeneratorType& generator, dtype inA = 1, dtype inB = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inA <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input a must be greater than zero."); - } - - if (inB <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input b must be greater than zero."); - } - - std::extreme_value_distribution dist(inA, inB); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "extreme value" distrubution. - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inA (default 1) - /// @param inB (default 1) - /// @return NdArray - /// - template - NdArray extremeValue(GeneratorType& generator, const Shape& inShape, dtype inA = 1, dtype inB = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inA <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input a must be greater than zero."); - } - - if (inB <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input b must be greater than zero."); - } - - NdArray returnArray(inShape); - - std::extreme_value_distribution dist(inA, inB); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the "extreme value" distrubution. /// + /// @param generator: instance of a random number generator /// @param inA (default 1) /// @param inB (default 1) /// @return NdArray /// - template - dtype extremeValue(dtype inA = 1, dtype inB = 1) + template + dtype extremeValue(GeneratorType& generator, dtype inA = 1, dtype inB = 1) { - return detail::extremeValue(generator_, inA, inB); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inA <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input a must be greater than zero."); + } + + if (inB <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input b must be greater than zero."); + } + + std::extreme_value_distribution dist(inA, inB); + return dist(generator); } //============================================================================ @@ -128,15 +74,66 @@ namespace nc /// Create an array of the given shape and populate it with /// random samples from a "extreme value" distrubution. /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inA (default 1) /// @param inB (default 1) /// @return NdArray /// - template - NdArray extremeValue(const Shape& inShape, dtype inA = 1, dtype inB = 1) + template + NdArray extremeValue(GeneratorType& generator, const Shape& inShape, dtype inA = 1, dtype inB = 1) { - return detail::extremeValue(generator_, inShape, inA, inB); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inA <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input a must be greater than zero."); + } + + if (inB <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input b must be greater than zero."); + } + + NdArray returnArray(inShape); + + std::extreme_value_distribution dist(inA, inB); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "extreme value" distrubution. + /// + /// @param inA (default 1) + /// @param inB (default 1) + /// @return NdArray + /// + template + dtype extremeValue(dtype inA = 1, dtype inB = 1) + { + return detail::extremeValue(generator_, inA, inB); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "extreme value" distrubution. + /// + /// @param inShape + /// @param inA (default 1) + /// @param inB (default 1) + /// @return NdArray + /// + template + NdArray extremeValue(const Shape& inShape, dtype inA = 1, dtype inB = 1) + { + return detail::extremeValue(generator_, inShape, inA, inB); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/f.hpp b/include/NumCpp/Random/f.hpp index 876f1c17e..c88e1aa71 100644 --- a/include/NumCpp/Random/f.hpp +++ b/include/NumCpp/Random/f.hpp @@ -37,96 +37,38 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the "F" distrubution. - /// - /// NumPy Reference: https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.f.html#numpy.random.f - /// - /// @param generator: instance of a random number generator - /// @param inDofN: Degrees of freedom in numerator. Should be greater than zero. - /// @param inDofD: Degrees of freedom in denominator. Should be greater than zero. - /// @return NdArray - /// - template - dtype f(GeneratorType& generator, dtype inDofN, dtype inDofD) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inDofN <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("numerator degrees of freedom should be greater than zero."); - } - - if (inDofD <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("denominator degrees of freedom should be greater than zero."); - } - - std::fisher_f_distribution dist(inDofN, inDofD); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "F" distrubution. - /// - /// NumPy Reference: https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.f.html#numpy.random.f - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inDofN: Degrees of freedom in numerator. Should be greater than zero. - /// @param inDofD: Degrees of freedom in denominator. Should be greater than zero. - /// @return NdArray - /// - template - NdArray f(GeneratorType& generator, const Shape& inShape, dtype inDofN, dtype inDofD) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inDofN <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("numerator degrees of freedom should be greater than zero."); - } - - if (inDofD <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("denominator degrees of freedom should be greater than zero."); - } - - NdArray returnArray(inShape); - - std::fisher_f_distribution dist(inDofN, inDofD); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the "F" distrubution. /// /// NumPy Reference: https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.f.html#numpy.random.f /// + /// @param generator: instance of a random number generator /// @param inDofN: Degrees of freedom in numerator. Should be greater than zero. /// @param inDofD: Degrees of freedom in denominator. Should be greater than zero. /// @return NdArray /// - template - dtype f(dtype inDofN, dtype inDofD) + template + dtype f(GeneratorType& generator, dtype inDofN, dtype inDofD) { - return detail::f(generator_, inDofN, inDofD); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inDofN <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("numerator degrees of freedom should be greater than zero."); + } + + if (inDofD <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("denominator degrees of freedom should be greater than zero."); + } + + std::fisher_f_distribution dist(inDofN, inDofD); + return dist(generator); } //============================================================================ @@ -136,15 +78,70 @@ namespace nc /// /// NumPy Reference: https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.f.html#numpy.random.f /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inDofN: Degrees of freedom in numerator. Should be greater than zero. /// @param inDofD: Degrees of freedom in denominator. Should be greater than zero. /// @return NdArray /// - template - NdArray f(const Shape& inShape, dtype inDofN, dtype inDofD) + template + NdArray f(GeneratorType& generator, const Shape& inShape, dtype inDofN, dtype inDofD) { - return detail::f(generator_, inShape, inDofN, inDofD); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inDofN <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("numerator degrees of freedom should be greater than zero."); + } + + if (inDofD <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("denominator degrees of freedom should be greater than zero."); + } + + NdArray returnArray(inShape); + + std::fisher_f_distribution dist(inDofN, inDofD); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "F" distrubution. + /// + /// NumPy Reference: https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.f.html#numpy.random.f + /// + /// @param inDofN: Degrees of freedom in numerator. Should be greater than zero. + /// @param inDofD: Degrees of freedom in denominator. Should be greater than zero. + /// @return NdArray + /// + template + dtype f(dtype inDofN, dtype inDofD) + { + return detail::f(generator_, inDofN, inDofD); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "F" distrubution. + /// + /// NumPy Reference: https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.f.html#numpy.random.f + /// + /// @param inShape + /// @param inDofN: Degrees of freedom in numerator. Should be greater than zero. + /// @param inDofD: Degrees of freedom in denominator. Should be greater than zero. + /// @return NdArray + /// + template + NdArray f(const Shape& inShape, dtype inDofN, dtype inDofD) + { + return detail::f(generator_, inShape, inDofN, inDofD); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/gamma.hpp b/include/NumCpp/Random/gamma.hpp index 222062e03..f98ee9a01 100644 --- a/include/NumCpp/Random/gamma.hpp +++ b/include/NumCpp/Random/gamma.hpp @@ -37,85 +37,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the "gamma" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.gamma.html#numpy.random.gamma - /// - /// @param generator: instance of a random number generator - /// @param inGammaShape - /// @param inScaleValue (default 1) - /// @return NdArray - /// - template - dtype gamma(GeneratorType& generator, dtype inGammaShape, dtype inScaleValue = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inGammaShape <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input gamma shape should be greater than zero."); - } - - if (inScaleValue <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input scale should be greater than zero."); - } - - std::gamma_distribution dist(inGammaShape, inScaleValue); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "gamma" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.gamma.html#numpy.random.gamma - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inGammaShape - /// @param inScaleValue (default 1) - /// @return NdArray - /// - template - NdArray - gamma(GeneratorType& generator, const Shape& inShape, dtype inGammaShape, dtype inScaleValue = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inGammaShape <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input gamma shape should be greater than zero."); - } - - if (inScaleValue <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input scale should be greater than zero."); - } - - NdArray returnArray(inShape); - - std::gamma_distribution dist(inGammaShape, inScaleValue); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the "gamma" distrubution. @@ -123,14 +48,28 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.gamma.html#numpy.random.gamma /// + /// @param generator: instance of a random number generator /// @param inGammaShape /// @param inScaleValue (default 1) /// @return NdArray /// - template - dtype gamma(dtype inGammaShape, dtype inScaleValue = 1) + template + dtype gamma(GeneratorType& generator, dtype inGammaShape, dtype inScaleValue = 1) { - return detail::gamma(generator_, inGammaShape, inScaleValue); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inGammaShape <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input gamma shape should be greater than zero."); + } + + if (inScaleValue <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input scale should be greater than zero."); + } + + std::gamma_distribution dist(inGammaShape, inScaleValue); + return dist(generator); } //============================================================================ @@ -141,15 +80,72 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.gamma.html#numpy.random.gamma /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inGammaShape /// @param inScaleValue (default 1) /// @return NdArray /// - template - NdArray gamma(const Shape& inShape, dtype inGammaShape, dtype inScaleValue = 1) + template + NdArray gamma(GeneratorType& generator, const Shape& inShape, dtype inGammaShape, dtype inScaleValue = 1) { - return detail::gamma(generator_, inShape, inGammaShape, inScaleValue); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inGammaShape <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input gamma shape should be greater than zero."); + } + + if (inScaleValue <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input scale should be greater than zero."); + } + + NdArray returnArray(inShape); + + std::gamma_distribution dist(inGammaShape, inScaleValue); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "gamma" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.gamma.html#numpy.random.gamma + /// + /// @param inGammaShape + /// @param inScaleValue (default 1) + /// @return NdArray + /// + template + dtype gamma(dtype inGammaShape, dtype inScaleValue = 1) + { + return detail::gamma(generator_, inGammaShape, inScaleValue); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "gamma" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.gamma.html#numpy.random.gamma + /// + /// @param inShape + /// @param inGammaShape + /// @param inScaleValue (default 1) + /// @return NdArray + /// + template + NdArray gamma(const Shape& inShape, dtype inGammaShape, dtype inScaleValue = 1) + { + return detail::gamma(generator_, inShape, inGammaShape, inScaleValue); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/generator.hpp b/include/NumCpp/Random/generator.hpp index 9208f40c7..6dba1c463 100644 --- a/include/NumCpp/Random/generator.hpp +++ b/include/NumCpp/Random/generator.hpp @@ -29,25 +29,22 @@ #include -namespace nc +namespace nc::random { - namespace random - { - /// generator function - static std::mt19937_64 generator_; + /// generator function + static std::mt19937_64 generator_; - //============================================================================ - // Method Description: - /// Seeds the random number generator - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.seed.html#numpy.random.seed - /// - /// @param inSeed - /// - inline void seed(int inSeed) - { - generator_.seed(inSeed); - } - } // namespace random -} // namespace nc + //============================================================================ + // Method Description: + /// Seeds the random number generator + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.seed.html#numpy.random.seed + /// + /// @param inSeed + /// + inline void seed(int inSeed) + { + generator_.seed(inSeed); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/geometric.hpp b/include/NumCpp/Random/geometric.hpp index 3a83db37a..f4c0b64d9 100644 --- a/include/NumCpp/Random/geometric.hpp +++ b/include/NumCpp/Random/geometric.hpp @@ -37,72 +37,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the "geometric" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.geometric.html#numpy.random.geometric - /// - /// @param generator: instance of a random number generator - /// @param inP (probablity of success [0, 1]) - /// @return NdArray - /// - template - dtype geometric(GeneratorType& generator, double inP = 0.5) - { - STATIC_ASSERT_INTEGER(dtype); - - if (inP < 0 || inP > 1) - { - THROW_INVALID_ARGUMENT_ERROR("input probability of sucess must be of the range [0, 1]."); - } - - std::geometric_distribution dist(inP); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "geometric" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.geometric.html#numpy.random.geometric - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inP (probablity of success [0, 1]) - /// @return NdArray - /// - template - NdArray geometric(GeneratorType& generator, const Shape& inShape, double inP = 0.5) - { - STATIC_ASSERT_INTEGER(dtype); - - if (inP < 0 || inP > 1) - { - THROW_INVALID_ARGUMENT_ERROR("input probability of sucess must be of the range [0, 1]."); - } - - NdArray returnArray(inShape); - - std::geometric_distribution dist(inP); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the "geometric" distrubution. @@ -110,13 +48,22 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.geometric.html#numpy.random.geometric /// + /// @param generator: instance of a random number generator /// @param inP (probablity of success [0, 1]) /// @return NdArray /// - template - dtype geometric(double inP = 0.5) + template + dtype geometric(GeneratorType& generator, double inP = 0.5) { - return detail::geometric(generator_, inP); + STATIC_ASSERT_INTEGER(dtype); + + if (inP < 0 || inP > 1) + { + THROW_INVALID_ARGUMENT_ERROR("input probability of sucess must be of the range [0, 1]."); + } + + std::geometric_distribution dist(inP); + return dist(generator); } //============================================================================ @@ -127,14 +74,64 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.geometric.html#numpy.random.geometric /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inP (probablity of success [0, 1]) /// @return NdArray /// - template - NdArray geometric(const Shape& inShape, double inP = 0.5) + template + NdArray geometric(GeneratorType& generator, const Shape& inShape, double inP = 0.5) { - return detail::geometric(generator_, inShape, inP); + STATIC_ASSERT_INTEGER(dtype); + + if (inP < 0 || inP > 1) + { + THROW_INVALID_ARGUMENT_ERROR("input probability of sucess must be of the range [0, 1]."); + } + + NdArray returnArray(inShape); + + std::geometric_distribution dist(inP); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "geometric" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.geometric.html#numpy.random.geometric + /// + /// @param inP (probablity of success [0, 1]) + /// @return NdArray + /// + template + dtype geometric(double inP = 0.5) + { + return detail::geometric(generator_, inP); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "geometric" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.geometric.html#numpy.random.geometric + /// + /// @param inShape + /// @param inP (probablity of success [0, 1]) + /// @return NdArray + /// + template + NdArray geometric(const Shape& inShape, double inP = 0.5) + { + return detail::geometric(generator_, inShape, inP); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/laplace.hpp b/include/NumCpp/Random/laplace.hpp index 54816e067..d23e4a0a8 100644 --- a/include/NumCpp/Random/laplace.hpp +++ b/include/NumCpp/Random/laplace.hpp @@ -38,66 +38,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the "laplace" distrubution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.laplace.html#numpy.random.laplace - /// - /// @param generator: instance of a random number generator - /// @param inLoc: (The position, mu, of the distribution peak. Default is 0) - /// @param inScale: (float optional the exponential decay. Default is 1) - /// @return NdArray - /// - template - dtype laplace(GeneratorType& generator, dtype inLoc = 0, dtype inScale = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - boost::random::laplace_distribution dist(inLoc, inScale); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "laplace" distrubution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.laplace.html#numpy.random.laplace - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inLoc: (The position, mu, of the distribution peak. Default is 0) - /// @param inScale: (float optional the exponential decay. Default is 1) - /// @return NdArray - /// - template - NdArray laplace(GeneratorType& generator, const Shape& inShape, dtype inLoc = 0, dtype inScale = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - NdArray returnArray(inShape); - - boost::random::laplace_distribution dist(inLoc, inScale); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the "laplace" distrubution. @@ -106,14 +50,18 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.laplace.html#numpy.random.laplace /// + /// @param generator: instance of a random number generator /// @param inLoc: (The position, mu, of the distribution peak. Default is 0) /// @param inScale: (float optional the exponential decay. Default is 1) /// @return NdArray /// - template - dtype laplace(dtype inLoc = 0, dtype inScale = 1) + template + dtype laplace(GeneratorType& generator, dtype inLoc = 0, dtype inScale = 1) { - return detail::laplace(generator_, inLoc, inScale); + STATIC_ASSERT_ARITHMETIC(dtype); + + boost::random::laplace_distribution dist(inLoc, inScale); + return dist(generator); } //============================================================================ @@ -125,17 +73,66 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.laplace.html#numpy.random.laplace /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inLoc: (The position, mu, of the distribution peak. Default is 0) /// @param inScale: (float optional the exponential decay. Default is 1) /// @return NdArray /// - template - NdArray laplace(const Shape& inShape, dtype inLoc = 0, dtype inScale = 1) + template + NdArray laplace(GeneratorType& generator, const Shape& inShape, dtype inLoc = 0, dtype inScale = 1) { - return detail::laplace(generator_, inShape, inLoc, inScale); + STATIC_ASSERT_ARITHMETIC(dtype); + + NdArray returnArray(inShape); + + boost::random::laplace_distribution dist(inLoc, inScale); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "laplace" distrubution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.laplace.html#numpy.random.laplace + /// + /// @param inLoc: (The position, mu, of the distribution peak. Default is 0) + /// @param inScale: (float optional the exponential decay. Default is 1) + /// @return NdArray + /// + template + dtype laplace(dtype inLoc = 0, dtype inScale = 1) + { + return detail::laplace(generator_, inLoc, inScale); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "laplace" distrubution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.laplace.html#numpy.random.laplace + /// + /// @param inShape + /// @param inLoc: (The position, mu, of the distribution peak. Default is 0) + /// @param inScale: (float optional the exponential decay. Default is 1) + /// @return NdArray + /// + template + NdArray laplace(const Shape& inShape, dtype inLoc = 0, dtype inScale = 1) + { + return detail::laplace(generator_, inShape, inLoc, inScale); + } +} // namespace nc::random #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Random/lognormal.hpp b/include/NumCpp/Random/lognormal.hpp index e97e5cdb7..d223ff69e 100644 --- a/include/NumCpp/Random/lognormal.hpp +++ b/include/NumCpp/Random/lognormal.hpp @@ -37,77 +37,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the "lognormal" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.lognormal.html#numpy.random.lognormal - /// - /// @param generator: instance of a random number generator - /// @param inMean: Mean value of the underlying normal distribution. Default is 0. - /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. - /// Default is 1. - /// @return NdArray - /// - template - dtype lognormal(GeneratorType& generator, dtype inMean = 0, dtype inSigma = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inSigma <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input sigma must be greater than zero."); - } - - std::lognormal_distribution dist(inMean, inSigma); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "lognormal" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.lognormal.html#numpy.random.lognormal - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inMean: Mean value of the underlying normal distribution. Default is 0. - /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. - /// Default is 1. - /// @return NdArray - /// - template - NdArray - lognormal(GeneratorType& generator, const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inSigma <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input sigma must be greater than zero."); - } - - NdArray returnArray(inShape); - - std::lognormal_distribution dist(inMean, inSigma); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the "lognormal" distrubution. @@ -115,15 +48,24 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.lognormal.html#numpy.random.lognormal /// + /// @param generator: instance of a random number generator /// @param inMean: Mean value of the underlying normal distribution. Default is 0. /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. /// Default is 1. /// @return NdArray /// - template - dtype lognormal(dtype inMean = 0, dtype inSigma = 1) + template + dtype lognormal(GeneratorType& generator, dtype inMean = 0, dtype inSigma = 1) { - return detail::lognormal(generator_, inMean, inSigma); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inSigma <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input sigma must be greater than zero."); + } + + std::lognormal_distribution dist(inMean, inSigma); + return dist(generator); } //============================================================================ @@ -134,16 +76,70 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.lognormal.html#numpy.random.lognormal /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inMean: Mean value of the underlying normal distribution. Default is 0. /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. /// Default is 1. /// @return NdArray /// - template - NdArray lognormal(const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) + template + NdArray lognormal(GeneratorType& generator, const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) { - return detail::lognormal(generator_, inShape, inMean, inSigma); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inSigma <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input sigma must be greater than zero."); + } + + NdArray returnArray(inShape); + + std::lognormal_distribution dist(inMean, inSigma); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "lognormal" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.lognormal.html#numpy.random.lognormal + /// + /// @param inMean: Mean value of the underlying normal distribution. Default is 0. + /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. + /// Default is 1. + /// @return NdArray + /// + template + dtype lognormal(dtype inMean = 0, dtype inSigma = 1) + { + return detail::lognormal(generator_, inMean, inSigma); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "lognormal" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.lognormal.html#numpy.random.lognormal + /// + /// @param inShape + /// @param inMean: Mean value of the underlying normal distribution. Default is 0. + /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. + /// Default is 1. + /// @return NdArray + /// + template + NdArray lognormal(const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) + { + return detail::lognormal(generator_, inShape, inMean, inSigma); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/negativeBinomial.hpp b/include/NumCpp/Random/negativeBinomial.hpp index d0d185647..25cbd989d 100644 --- a/include/NumCpp/Random/negativeBinomial.hpp +++ b/include/NumCpp/Random/negativeBinomial.hpp @@ -37,84 +37,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the "negative Binomial" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.negative_binomial.html#numpy.random.negative_binomial - /// - /// @param generator: instance of a random number generator - /// @param inN: number of trials - /// @param inP: probablity of success [0, 1] - /// @return NdArray - /// - template - dtype negativeBinomial(GeneratorType& generator, dtype inN, double inP = 0.5) - { - STATIC_ASSERT_INTEGER(dtype); - - if (inN < 0) - { - THROW_INVALID_ARGUMENT_ERROR("input number of trials must be greater than or equal to zero."); - } - - if (inP < 0 || inP > 1) - { - THROW_INVALID_ARGUMENT_ERROR("input probability of sucess must be of the range [0, 1]."); - } - - std::negative_binomial_distribution dist(inN, inP); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "negative Binomial" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.negative_binomial.html#numpy.random.negative_binomial - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inN: number of trials - /// @param inP: probablity of success [0, 1] - /// @return NdArray - /// - template - NdArray negativeBinomial(GeneratorType& generator, const Shape& inShape, dtype inN, double inP = 0.5) - { - STATIC_ASSERT_INTEGER(dtype); - - if (inN < 0) - { - THROW_INVALID_ARGUMENT_ERROR("input number of trials must be greater than or equal to zero."); - } - - if (inP < 0 || inP > 1) - { - THROW_INVALID_ARGUMENT_ERROR("input probability of sucess must be of the range [0, 1]."); - } - - NdArray returnArray(inShape); - - std::negative_binomial_distribution dist(inN, inP); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the "negative Binomial" distribution. @@ -122,14 +48,28 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.negative_binomial.html#numpy.random.negative_binomial /// + /// @param generator: instance of a random number generator /// @param inN: number of trials /// @param inP: probablity of success [0, 1] /// @return NdArray /// - template - dtype negativeBinomial(dtype inN, double inP = 0.5) + template + dtype negativeBinomial(GeneratorType& generator, dtype inN, double inP = 0.5) { - return detail::negativeBinomial(generator_, inN, inP); + STATIC_ASSERT_INTEGER(dtype); + + if (inN < 0) + { + THROW_INVALID_ARGUMENT_ERROR("input number of trials must be greater than or equal to zero."); + } + + if (inP < 0 || inP > 1) + { + THROW_INVALID_ARGUMENT_ERROR("input probability of sucess must be of the range [0, 1]."); + } + + std::negative_binomial_distribution dist(inN, inP); + return dist(generator); } //============================================================================ @@ -140,15 +80,72 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.negative_binomial.html#numpy.random.negative_binomial /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inN: number of trials /// @param inP: probablity of success [0, 1] /// @return NdArray /// - template - NdArray negativeBinomial(const Shape& inShape, dtype inN, double inP = 0.5) + template + NdArray negativeBinomial(GeneratorType& generator, const Shape& inShape, dtype inN, double inP = 0.5) { - return detail::negativeBinomial(generator_, inShape, inN, inP); + STATIC_ASSERT_INTEGER(dtype); + + if (inN < 0) + { + THROW_INVALID_ARGUMENT_ERROR("input number of trials must be greater than or equal to zero."); + } + + if (inP < 0 || inP > 1) + { + THROW_INVALID_ARGUMENT_ERROR("input probability of sucess must be of the range [0, 1]."); + } + + NdArray returnArray(inShape); + + std::negative_binomial_distribution dist(inN, inP); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "negative Binomial" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.negative_binomial.html#numpy.random.negative_binomial + /// + /// @param inN: number of trials + /// @param inP: probablity of success [0, 1] + /// @return NdArray + /// + template + dtype negativeBinomial(dtype inN, double inP = 0.5) + { + return detail::negativeBinomial(generator_, inN, inP); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "negative Binomial" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.negative_binomial.html#numpy.random.negative_binomial + /// + /// @param inShape + /// @param inN: number of trials + /// @param inP: probablity of success [0, 1] + /// @return NdArray + /// + template + NdArray negativeBinomial(const Shape& inShape, dtype inN, double inP = 0.5) + { + return detail::negativeBinomial(generator_, inShape, inN, inP); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/nonCentralChiSquared.hpp b/include/NumCpp/Random/nonCentralChiSquared.hpp index e82b4566d..aad5b5b23 100644 --- a/include/NumCpp/Random/nonCentralChiSquared.hpp +++ b/include/NumCpp/Random/nonCentralChiSquared.hpp @@ -40,87 +40,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the "non central chi squared" distrubution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.noncentral_chisquare.html#numpy.random.noncentral_chisquare - /// - /// @param generator: instance of a random number generator - /// @param inK (default 1) - /// @param inLambda (default 1) - /// @return NdArray - /// - template - dtype nonCentralChiSquared(GeneratorType& generator, dtype inK = 1, dtype inLambda = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inK <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input k must be greater than zero."); - } - - if (inLambda <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input lambda must be greater than zero."); - } - - boost::random::non_central_chi_squared_distribution dist(inK, inLambda); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "non central chi squared" distrubution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.noncentral_chisquare.html#numpy.random.noncentral_chisquare - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inK (default 1) - /// @param inLambda (default 1) - /// @return NdArray - /// - template - NdArray - nonCentralChiSquared(GeneratorType& generator, const Shape& inShape, dtype inK = 1, dtype inLambda = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inK <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input k must be greater than zero."); - } - - if (inLambda <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input lambda must be greater than zero."); - } - - NdArray returnArray(inShape); - - boost::random::non_central_chi_squared_distribution dist(inK, inLambda); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the "non central chi squared" distrubution. @@ -129,14 +52,28 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.noncentral_chisquare.html#numpy.random.noncentral_chisquare /// + /// @param generator: instance of a random number generator /// @param inK (default 1) /// @param inLambda (default 1) /// @return NdArray /// - template - dtype nonCentralChiSquared(dtype inK = 1, dtype inLambda = 1) + template + dtype nonCentralChiSquared(GeneratorType& generator, dtype inK = 1, dtype inLambda = 1) { - return detail::nonCentralChiSquared(generator_, inK, inLambda); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inK <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input k must be greater than zero."); + } + + if (inLambda <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input lambda must be greater than zero."); + } + + boost::random::non_central_chi_squared_distribution dist(inK, inLambda); + return dist(generator); } //============================================================================ @@ -148,17 +85,77 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.noncentral_chisquare.html#numpy.random.noncentral_chisquare /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inK (default 1) /// @param inLambda (default 1) /// @return NdArray /// - template - NdArray nonCentralChiSquared(const Shape& inShape, dtype inK = 1, dtype inLambda = 1) + template + NdArray + nonCentralChiSquared(GeneratorType& generator, const Shape& inShape, dtype inK = 1, dtype inLambda = 1) { - return detail::nonCentralChiSquared(generator_, inShape, inK, inLambda); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inK <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input k must be greater than zero."); + } + + if (inLambda <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input lambda must be greater than zero."); + } + + NdArray returnArray(inShape); + + boost::random::non_central_chi_squared_distribution dist(inK, inLambda); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "non central chi squared" distrubution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.noncentral_chisquare.html#numpy.random.noncentral_chisquare + /// + /// @param inK (default 1) + /// @param inLambda (default 1) + /// @return NdArray + /// + template + dtype nonCentralChiSquared(dtype inK = 1, dtype inLambda = 1) + { + return detail::nonCentralChiSquared(generator_, inK, inLambda); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "non central chi squared" distrubution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.noncentral_chisquare.html#numpy.random.noncentral_chisquare + /// + /// @param inShape + /// @param inK (default 1) + /// @param inLambda (default 1) + /// @return NdArray + /// + template + NdArray nonCentralChiSquared(const Shape& inShape, dtype inK = 1, dtype inLambda = 1) + { + return detail::nonCentralChiSquared(generator_, inShape, inK, inLambda); + } +} // namespace nc::random #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Random/normal.hpp b/include/NumCpp/Random/normal.hpp index ed1835a30..1e60ae3c5 100644 --- a/include/NumCpp/Random/normal.hpp +++ b/include/NumCpp/Random/normal.hpp @@ -37,76 +37,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the "normal" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.normal.html#numpy.random.normal - /// - /// @param generator: instance of a random number generator - /// @param inMean: Mean value of the underlying normal distribution. Default is 0. - /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. - /// Default is 1. - /// @return NdArray - /// - template - dtype normal(GeneratorType& generator, dtype inMean = 0, dtype inSigma = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inSigma <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input sigma must be greater than zero."); - } - - std::normal_distribution dist(inMean, inSigma); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "normal" distrubution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.normal.html#numpy.random.normal - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inMean: Mean value of the underlying normal distribution. Default is 0. - /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. - /// Default is 1. - /// @return NdArray - /// - template - NdArray normal(GeneratorType& generator, const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inSigma <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input sigma must be greater than zero."); - } - - NdArray returnArray(inShape); - - std::normal_distribution dist(inMean, inSigma); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the "normal" distrubution. @@ -114,15 +48,24 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.normal.html#numpy.random.normal /// + /// @param generator: instance of a random number generator /// @param inMean: Mean value of the underlying normal distribution. Default is 0. /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. /// Default is 1. /// @return NdArray /// - template - dtype normal(dtype inMean = 0, dtype inSigma = 1) + template + dtype normal(GeneratorType& generator, dtype inMean = 0, dtype inSigma = 1) { - return detail::normal(generator_, inMean, inSigma); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inSigma <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input sigma must be greater than zero."); + } + + std::normal_distribution dist(inMean, inSigma); + return dist(generator); } //============================================================================ @@ -133,16 +76,70 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.normal.html#numpy.random.normal /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inMean: Mean value of the underlying normal distribution. Default is 0. /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. /// Default is 1. /// @return NdArray /// - template - NdArray normal(const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) + template + NdArray normal(GeneratorType& generator, const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) { - return detail::normal(generator_, inShape, inMean, inSigma); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inSigma <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input sigma must be greater than zero."); + } + + NdArray returnArray(inShape); + + std::normal_distribution dist(inMean, inSigma); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "normal" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.normal.html#numpy.random.normal + /// + /// @param inMean: Mean value of the underlying normal distribution. Default is 0. + /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. + /// Default is 1. + /// @return NdArray + /// + template + dtype normal(dtype inMean = 0, dtype inSigma = 1) + { + return detail::normal(generator_, inMean, inSigma); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "normal" distrubution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.normal.html#numpy.random.normal + /// + /// @param inShape + /// @param inMean: Mean value of the underlying normal distribution. Default is 0. + /// @param inSigma: Standard deviation of the underlying normal distribution. Should be greater than zero. + /// Default is 1. + /// @return NdArray + /// + template + NdArray normal(const Shape& inShape, dtype inMean = 0, dtype inSigma = 1) + { + return detail::normal(generator_, inShape, inMean, inSigma); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/permutation.hpp b/include/NumCpp/Random/permutation.hpp index 0af1d5400..793d689b8 100644 --- a/include/NumCpp/Random/permutation.hpp +++ b/include/NumCpp/Random/permutation.hpp @@ -34,66 +34,28 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Randomly permute a sequence, or return a permuted range. - /// If x is an integer, randomly permute np.arange(x). - /// If x is an array, make a copy and shuffle the elements randomly. - /// - /// @param generator: instance of a random number generator - /// @param inValue - /// @return NdArray - /// - template - NdArray permutation(GeneratorType& generator, dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - NdArray returnArray = arange(inValue); - std::shuffle(returnArray.begin(), returnArray.end(), generator); - return returnArray; - } - - //============================================================================ - // Method Description: - /// Randomly permute a sequence, or return a permuted range. - /// If x is an integer, randomly permute np.arange(x). - /// If x is an array, make a copy and shuffle the elements randomly. - /// - /// @param generator: instance of a random number generator - /// @param inArray - /// @return NdArray - /// - template - NdArray permutation(GeneratorType& generator, const NdArray& inArray) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - NdArray returnArray(inArray); - std::shuffle(returnArray.begin(), returnArray.end(), generator); - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Randomly permute a sequence, or return a permuted range. /// If x is an integer, randomly permute np.arange(x). /// If x is an array, make a copy and shuffle the elements randomly. /// + /// @param generator: instance of a random number generator /// @param inValue /// @return NdArray /// - template - NdArray permutation(dtype inValue) + template + NdArray permutation(GeneratorType& generator, dtype inValue) { - return detail::permutation(generator_, inValue); + STATIC_ASSERT_ARITHMETIC(dtype); + + NdArray returnArray = arange(inValue); + std::shuffle(returnArray.begin(), returnArray.end(), generator); + return returnArray; } //============================================================================ @@ -102,13 +64,48 @@ namespace nc /// If x is an integer, randomly permute np.arange(x). /// If x is an array, make a copy and shuffle the elements randomly. /// + /// @param generator: instance of a random number generator /// @param inArray /// @return NdArray /// - template - NdArray permutation(const NdArray& inArray) + template + NdArray permutation(GeneratorType& generator, const NdArray& inArray) { - return detail::permutation(generator_, inArray); + STATIC_ASSERT_ARITHMETIC(dtype); + + NdArray returnArray(inArray); + std::shuffle(returnArray.begin(), returnArray.end(), generator); + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Randomly permute a sequence, or return a permuted range. + /// If x is an integer, randomly permute np.arange(x). + /// If x is an array, make a copy and shuffle the elements randomly. + /// + /// @param inValue + /// @return NdArray + /// + template + NdArray permutation(dtype inValue) + { + return detail::permutation(generator_, inValue); + } + + //============================================================================ + // Method Description: + /// Randomly permute a sequence, or return a permuted range. + /// If x is an integer, randomly permute np.arange(x). + /// If x is an array, make a copy and shuffle the elements randomly. + /// + /// @param inArray + /// @return NdArray + /// + template + NdArray permutation(const NdArray& inArray) + { + return detail::permutation(generator_, inArray); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/poisson.hpp b/include/NumCpp/Random/poisson.hpp index 61dfd15a1..0749eb5d6 100644 --- a/include/NumCpp/Random/poisson.hpp +++ b/include/NumCpp/Random/poisson.hpp @@ -37,72 +37,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the "poisson" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.poisson.html#numpy.random.poisson - /// - /// @param generator: instance of a random number generator - /// @param inMean (default 1) - /// @return NdArray - /// - template - dtype poisson(GeneratorType& generator, double inMean = 1) - { - STATIC_ASSERT_INTEGER(dtype); - - if (inMean <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input mean must be greater than zero."); - } - - std::poisson_distribution dist(inMean); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "poisson" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.poisson.html#numpy.random.poisson - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inMean (default 1) - /// @return NdArray - /// - template - NdArray poisson(GeneratorType& generator, const Shape& inShape, double inMean = 1) - { - STATIC_ASSERT_INTEGER(dtype); - - if (inMean <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input mean must be greater than zero."); - } - - NdArray returnArray(inShape); - - std::poisson_distribution dist(inMean); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the "poisson" distribution. @@ -110,13 +48,22 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.poisson.html#numpy.random.poisson /// + /// @param generator: instance of a random number generator /// @param inMean (default 1) /// @return NdArray /// - template - dtype poisson(double inMean = 1) + template + dtype poisson(GeneratorType& generator, double inMean = 1) { - return detail::poisson(generator_, inMean); + STATIC_ASSERT_INTEGER(dtype); + + if (inMean <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input mean must be greater than zero."); + } + + std::poisson_distribution dist(inMean); + return dist(generator); } //============================================================================ @@ -127,14 +74,64 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.poisson.html#numpy.random.poisson /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inMean (default 1) /// @return NdArray /// - template - NdArray poisson(const Shape& inShape, double inMean = 1) + template + NdArray poisson(GeneratorType& generator, const Shape& inShape, double inMean = 1) { - return detail::poisson(generator_, inShape, inMean); + STATIC_ASSERT_INTEGER(dtype); + + if (inMean <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input mean must be greater than zero."); + } + + NdArray returnArray(inShape); + + std::poisson_distribution dist(inMean); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "poisson" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.poisson.html#numpy.random.poisson + /// + /// @param inMean (default 1) + /// @return NdArray + /// + template + dtype poisson(double inMean = 1) + { + return detail::poisson(generator_, inMean); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "poisson" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.poisson.html#numpy.random.poisson + /// + /// @param inShape + /// @param inMean (default 1) + /// @return NdArray + /// + template + NdArray poisson(const Shape& inShape, double inMean = 1) + { + return detail::poisson(generator_, inShape, inMean); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/rand.hpp b/include/NumCpp/Random/rand.hpp index 3b6a05983..b74c95d26 100644 --- a/include/NumCpp/Random/rand.hpp +++ b/include/NumCpp/Random/rand.hpp @@ -36,62 +36,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the uniform distribution over [0, 1). - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.rand.html#numpy.random.rand - /// - /// @param generator: instance of a random number generator - /// @return NdArray - /// - template - dtype rand(GeneratorType& generator) - { - STATIC_ASSERT_FLOAT(dtype); - - std::uniform_real_distribution dist(static_cast(0.), - static_cast(1.) - DtypeInfo::epsilon()); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a uniform distribution over [0, 1). - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.rand.html#numpy.random.rand - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @return NdArray - /// - template - NdArray rand(GeneratorType& generator, const Shape& inShape) - { - STATIC_ASSERT_FLOAT(dtype); - - NdArray returnArray(inShape); - - std::uniform_real_distribution dist(static_cast(0.), - static_cast(1.) - DtypeInfo::epsilon()); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the uniform distribution over [0, 1). @@ -99,12 +47,17 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.rand.html#numpy.random.rand /// + /// @param generator: instance of a random number generator /// @return NdArray /// - template - dtype rand() + template + dtype rand(GeneratorType& generator) { - return detail::rand(generator_); + STATIC_ASSERT_FLOAT(dtype); + + std::uniform_real_distribution dist(static_cast(0.), + static_cast(1.) - DtypeInfo::epsilon()); + return dist(generator); } //============================================================================ @@ -115,13 +68,57 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.rand.html#numpy.random.rand /// + /// @param generator: instance of a random number generator /// @param inShape /// @return NdArray /// - template - NdArray rand(const Shape& inShape) + template + NdArray rand(GeneratorType& generator, const Shape& inShape) { - return detail::rand(generator_, inShape); + STATIC_ASSERT_FLOAT(dtype); + + NdArray returnArray(inShape); + + std::uniform_real_distribution dist(static_cast(0.), + static_cast(1.) - DtypeInfo::epsilon()); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the uniform distribution over [0, 1). + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.rand.html#numpy.random.rand + /// + /// @return NdArray + /// + template + dtype rand() + { + return detail::rand(generator_); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a uniform distribution over [0, 1). + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.rand.html#numpy.random.rand + /// + /// @param inShape + /// @return NdArray + /// + template + NdArray rand(const Shape& inShape) + { + return detail::rand(generator_, inShape); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/randFloat.hpp b/include/NumCpp/Random/randFloat.hpp index 54db43385..f3665f28f 100644 --- a/include/NumCpp/Random/randFloat.hpp +++ b/include/NumCpp/Random/randFloat.hpp @@ -39,85 +39,10 @@ #include "NumCpp/Random/generator.hpp" #include "NumCpp/Utils/essentiallyEqual.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Return a single random float from low (inclusive) to high (exclusive), - /// with the given shape. If no high value is input then the range will - /// go from [0, low). - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.ranf.html#numpy.random.ranf - /// - /// @param generator: instance of a random number generator - /// @param inLow - /// @param inHigh default 0. - /// @return NdArray - /// - template - dtype randFloat(GeneratorType& generator, dtype inLow, dtype inHigh = 0.) - { - STATIC_ASSERT_FLOAT(dtype); - - if (utils::essentiallyEqual(inLow, inHigh)) - { - THROW_INVALID_ARGUMENT_ERROR("input low value must be less than the input high value."); - } - else if (inLow > inHigh) - { - std::swap(inLow, inHigh); - } - - std::uniform_real_distribution dist(inLow, inHigh - DtypeInfo::epsilon()); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Return random floats from low (inclusive) to high (exclusive), - /// with the given shape. If no high value is input then the range will - /// go from [0, low). - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.ranf.html#numpy.random.ranf - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inLow - /// @param inHigh default 0. - /// @return NdArray - /// - template - NdArray randFloat(GeneratorType& generator, const Shape& inShape, dtype inLow, dtype inHigh = 0.) - { - STATIC_ASSERT_FLOAT(dtype); - - if (utils::essentiallyEqual(inLow, inHigh)) - { - THROW_INVALID_ARGUMENT_ERROR("input low value must be less than the input high value."); - } - else if (inLow > inHigh) - { - std::swap(inLow, inHigh); - } - - NdArray returnArray(inShape); - - std::uniform_real_distribution dist(inLow, inHigh - DtypeInfo::epsilon()); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Return a single random float from low (inclusive) to high (exclusive), @@ -127,14 +52,27 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.ranf.html#numpy.random.ranf /// + /// @param generator: instance of a random number generator /// @param inLow /// @param inHigh default 0. /// @return NdArray /// - template - dtype randFloat(dtype inLow, dtype inHigh = 0.) + template + dtype randFloat(GeneratorType& generator, dtype inLow, dtype inHigh = 0.) { - return detail::randFloat(generator_, inLow, inHigh); + STATIC_ASSERT_FLOAT(dtype); + + if (utils::essentiallyEqual(inLow, inHigh)) + { + THROW_INVALID_ARGUMENT_ERROR("input low value must be less than the input high value."); + } + else if (inLow > inHigh) + { + std::swap(inLow, inHigh); + } + + std::uniform_real_distribution dist(inLow, inHigh - DtypeInfo::epsilon()); + return dist(generator); } //============================================================================ @@ -146,15 +84,74 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.ranf.html#numpy.random.ranf /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inLow /// @param inHigh default 0. /// @return NdArray /// - template - NdArray randFloat(const Shape& inShape, dtype inLow, dtype inHigh = 0.) + template + NdArray randFloat(GeneratorType& generator, const Shape& inShape, dtype inLow, dtype inHigh = 0.) { - return detail::randFloat(generator_, inShape, inLow, inHigh); + STATIC_ASSERT_FLOAT(dtype); + + if (utils::essentiallyEqual(inLow, inHigh)) + { + THROW_INVALID_ARGUMENT_ERROR("input low value must be less than the input high value."); + } + else if (inLow > inHigh) + { + std::swap(inLow, inHigh); + } + + NdArray returnArray(inShape); + + std::uniform_real_distribution dist(inLow, inHigh - DtypeInfo::epsilon()); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Return a single random float from low (inclusive) to high (exclusive), + /// with the given shape. If no high value is input then the range will + /// go from [0, low). + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.ranf.html#numpy.random.ranf + /// + /// @param inLow + /// @param inHigh default 0. + /// @return NdArray + /// + template + dtype randFloat(dtype inLow, dtype inHigh = 0.) + { + return detail::randFloat(generator_, inLow, inHigh); + } + + //============================================================================ + // Method Description: + /// Return random floats from low (inclusive) to high (exclusive), + /// with the given shape. If no high value is input then the range will + /// go from [0, low). + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.ranf.html#numpy.random.ranf + /// + /// @param inShape + /// @param inLow + /// @param inHigh default 0. + /// @return NdArray + /// + template + NdArray randFloat(const Shape& inShape, dtype inLow, dtype inHigh = 0.) + { + return detail::randFloat(generator_, inShape, inLow, inHigh); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/randInt.hpp b/include/NumCpp/Random/randInt.hpp index 51d4a5992..5c2bbe4de 100644 --- a/include/NumCpp/Random/randInt.hpp +++ b/include/NumCpp/Random/randInt.hpp @@ -39,85 +39,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Return random integer from low (inclusive) to high (exclusive), - /// with the given shape. If no high value is input then the range will - /// go from [0, low). - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randint.html#numpy.random.randint - /// - /// @param generator: instance of a random number generator - /// @param inLow - /// @param inHigh default 0. - /// @return NdArray - /// - template - dtype randInt(GeneratorType& generator, dtype inLow, dtype inHigh = 0) - { - STATIC_ASSERT_INTEGER(dtype); - - if (inLow == inHigh) - { - THROW_INVALID_ARGUMENT_ERROR("input low value must be less than the input high value."); - } - else if (inLow > inHigh) - { - std::swap(inLow, inHigh); - } - - std::uniform_int_distribution dist(inLow, inHigh - 1); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Return random integers from low (inclusive) to high (exclusive), - /// with the given shape. If no high value is input then the range will - /// go from [0, low). - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randint.html#numpy.random.randint - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inLow - /// @param inHigh default 0. - /// @return NdArray - /// - template - NdArray randInt(GeneratorType& generator, const Shape& inShape, dtype inLow, dtype inHigh = 0) - { - STATIC_ASSERT_INTEGER(dtype); - - if (inLow == inHigh) - { - THROW_INVALID_ARGUMENT_ERROR("input low value must be less than the input high value."); - } - else if (inLow > inHigh - 1) - { - std::swap(inLow, inHigh); - } - - NdArray returnArray(inShape); - - std::uniform_int_distribution dist(inLow, inHigh - 1); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&dist, &generator](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Return random integer from low (inclusive) to high (exclusive), @@ -127,14 +52,27 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randint.html#numpy.random.randint /// + /// @param generator: instance of a random number generator /// @param inLow /// @param inHigh default 0. /// @return NdArray /// - template - dtype randInt(dtype inLow, dtype inHigh = 0) + template + dtype randInt(GeneratorType& generator, dtype inLow, dtype inHigh = 0) { - return detail::randInt(generator_, inLow, inHigh); + STATIC_ASSERT_INTEGER(dtype); + + if (inLow == inHigh) + { + THROW_INVALID_ARGUMENT_ERROR("input low value must be less than the input high value."); + } + else if (inLow > inHigh) + { + std::swap(inLow, inHigh); + } + + std::uniform_int_distribution dist(inLow, inHigh - 1); + return dist(generator); } //============================================================================ @@ -146,15 +84,74 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randint.html#numpy.random.randint /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inLow /// @param inHigh default 0. /// @return NdArray /// - template - NdArray randInt(const Shape& inShape, dtype inLow, dtype inHigh = 0) + template + NdArray randInt(GeneratorType& generator, const Shape& inShape, dtype inLow, dtype inHigh = 0) { - return detail::randInt(generator_, inShape, inLow, inHigh); + STATIC_ASSERT_INTEGER(dtype); + + if (inLow == inHigh) + { + THROW_INVALID_ARGUMENT_ERROR("input low value must be less than the input high value."); + } + else if (inLow > inHigh - 1) + { + std::swap(inLow, inHigh); + } + + NdArray returnArray(inShape); + + std::uniform_int_distribution dist(inLow, inHigh - 1); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&dist, &generator](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Return random integer from low (inclusive) to high (exclusive), + /// with the given shape. If no high value is input then the range will + /// go from [0, low). + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randint.html#numpy.random.randint + /// + /// @param inLow + /// @param inHigh default 0. + /// @return NdArray + /// + template + dtype randInt(dtype inLow, dtype inHigh = 0) + { + return detail::randInt(generator_, inLow, inHigh); + } + + //============================================================================ + // Method Description: + /// Return random integers from low (inclusive) to high (exclusive), + /// with the given shape. If no high value is input then the range will + /// go from [0, low). + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randint.html#numpy.random.randint + /// + /// @param inShape + /// @param inLow + /// @param inHigh default 0. + /// @return NdArray + /// + template + NdArray randInt(const Shape& inShape, dtype inLow, dtype inHigh = 0) + { + return detail::randInt(generator_, inShape, inLow, inHigh); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/randN.hpp b/include/NumCpp/Random/randN.hpp index fed051d41..8f16b7c2d 100644 --- a/include/NumCpp/Random/randN.hpp +++ b/include/NumCpp/Random/randN.hpp @@ -35,60 +35,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Returns a single random value sampled from the "standard normal" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randn.html#numpy.random.randn - /// - /// @param generator: instance of a random number generator - /// @return dtype - /// - template - dtype randN(GeneratorType& generator) - { - STATIC_ASSERT_FLOAT(dtype); - - std::normal_distribution dist; - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "standard normal" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randn.html#numpy.random.randn - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @return NdArray - /// - template - NdArray randN(GeneratorType& generator, const Shape& inShape) - { - STATIC_ASSERT_FLOAT(dtype); - - NdArray returnArray(inShape); - - std::normal_distribution dist; - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Returns a single random value sampled from the "standard normal" distribution. @@ -96,12 +46,16 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randn.html#numpy.random.randn /// + /// @param generator: instance of a random number generator /// @return dtype /// - template - dtype randN() + template + dtype randN(GeneratorType& generator) { - return detail::randN(generator_); + STATIC_ASSERT_FLOAT(dtype); + + std::normal_distribution dist; + return dist(generator); } //============================================================================ @@ -112,13 +66,56 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randn.html#numpy.random.randn /// + /// @param generator: instance of a random number generator /// @param inShape /// @return NdArray /// - template - NdArray randN(const Shape& inShape) + template + NdArray randN(GeneratorType& generator, const Shape& inShape) { - return detail::randN(generator_, inShape); + STATIC_ASSERT_FLOAT(dtype); + + NdArray returnArray(inShape); + + std::normal_distribution dist; + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Returns a single random value sampled from the "standard normal" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randn.html#numpy.random.randn + /// + /// @return dtype + /// + template + dtype randN() + { + return detail::randN(generator_); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "standard normal" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randn.html#numpy.random.randn + /// + /// @param inShape + /// @return NdArray + /// + template + NdArray randN(const Shape& inShape) + { + return detail::randN(generator_, inShape); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/shuffle.hpp b/include/NumCpp/Random/shuffle.hpp index 065f36c31..033723d30 100644 --- a/include/NumCpp/Random/shuffle.hpp +++ b/include/NumCpp/Random/shuffle.hpp @@ -33,36 +33,33 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Modify a sequence in-place by shuffling its contents. - /// - /// @param generator: instance of a random number generator - /// @param inArray - /// - template - void shuffle(GeneratorType& generator, NdArray& inArray) - { - std::shuffle(inArray.begin(), inArray.end(), generator); - } - } // namespace detail - //============================================================================ // Method Description: /// Modify a sequence in-place by shuffling its contents. /// + /// @param generator: instance of a random number generator /// @param inArray /// - template - void shuffle(NdArray& inArray) + template + void shuffle(GeneratorType& generator, NdArray& inArray) { - return detail::shuffle(generator_, inArray); + std::shuffle(inArray.begin(), inArray.end(), generator); } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Modify a sequence in-place by shuffling its contents. + /// + /// @param inArray + /// + template + void shuffle(NdArray& inArray) + { + return detail::shuffle(generator_, inArray); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/standardNormal.hpp b/include/NumCpp/Random/standardNormal.hpp index 83a67bc5e..d4377cd6e 100644 --- a/include/NumCpp/Random/standardNormal.hpp +++ b/include/NumCpp/Random/standardNormal.hpp @@ -32,53 +32,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/normal.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the "standard normal" distrubution with - /// mean = 0 and std = 1 - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_normal.html#numpy.random.standard_normal - /// - /// @param generator: instance of a random number generator - /// @return NdArray - /// - template - dtype standardNormal(GeneratorType& generator) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - return detail::normal(generator, 0, 1); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from a "standard normal" distrubution with - /// mean = 0 and std = 1 - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_normal.html#numpy.random.standard_normal - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @return NdArray - /// - template - NdArray standardNormal(GeneratorType& generator, const Shape& inShape) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - return detail::normal(generator, inShape, 0, 1); - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the "standard normal" distrubution with @@ -87,12 +44,15 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_normal.html#numpy.random.standard_normal /// + /// @param generator: instance of a random number generator /// @return NdArray /// - template - dtype standardNormal() + template + dtype standardNormal(GeneratorType& generator) { - return detail::standardNormal(generator_); + STATIC_ASSERT_ARITHMETIC(dtype); + + return detail::normal(generator, 0, 1); } //============================================================================ @@ -104,13 +64,50 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_normal.html#numpy.random.standard_normal /// + /// @param generator: instance of a random number generator /// @param inShape /// @return NdArray /// - template - NdArray standardNormal(const Shape& inShape) + template + NdArray standardNormal(GeneratorType& generator, const Shape& inShape) { - return detail::standardNormal(generator_, inShape); + STATIC_ASSERT_ARITHMETIC(dtype); + + return detail::normal(generator, inShape, 0, 1); } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "standard normal" distrubution with + /// mean = 0 and std = 1 + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_normal.html#numpy.random.standard_normal + /// + /// @return NdArray + /// + template + dtype standardNormal() + { + return detail::standardNormal(generator_); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from a "standard normal" distrubution with + /// mean = 0 and std = 1 + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_normal.html#numpy.random.standard_normal + /// + /// @param inShape + /// @return NdArray + /// + template + NdArray standardNormal(const Shape& inShape) + { + return detail::standardNormal(generator_, inShape); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/studentT.hpp b/include/NumCpp/Random/studentT.hpp index efa7c613e..7a3ed4f6d 100644 --- a/include/NumCpp/Random/studentT.hpp +++ b/include/NumCpp/Random/studentT.hpp @@ -37,85 +37,32 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - // Method Description: - /// Single random value sampled from the "student-T" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_t.html#numpy.random.standard_t - /// - /// @param generator: instance of a random number generator - /// @param inDof independent random variables - /// @return NdArray - /// - template - dtype studentT(GeneratorType& generator, dtype inDof) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inDof <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("degrees of freedom must be greater than zero."); - } - - std::student_t_distribution dist(inDof); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "student-T" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_t.html#numpy.random.standard_t - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inDof independent random variables - /// @return NdArray - /// - template - NdArray studentT(GeneratorType& generator, const Shape& inShape, dtype inDof) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inDof <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("degrees of freedom must be greater than zero."); - } - - NdArray returnArray(inShape); - - std::student_t_distribution dist(inDof); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - - //============================================================================ // Method Description: /// Single random value sampled from the "student-T" distribution. /// /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_t.html#numpy.random.standard_t /// + /// @param generator: instance of a random number generator /// @param inDof independent random variables /// @return NdArray /// - template - dtype studentT(dtype inDof) + template + dtype studentT(GeneratorType& generator, dtype inDof) { - return detail::studentT(generator_, inDof); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inDof <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("degrees of freedom must be greater than zero."); + } + + std::student_t_distribution dist(inDof); + return dist(generator); } //============================================================================ @@ -126,14 +73,64 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_t.html#numpy.random.standard_t /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inDof independent random variables /// @return NdArray /// - template - NdArray studentT(const Shape& inShape, dtype inDof) + template + NdArray studentT(GeneratorType& generator, const Shape& inShape, dtype inDof) { - return detail::studentT(generator_, inShape, inDof); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inDof <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("degrees of freedom must be greater than zero."); + } + + NdArray returnArray(inShape); + + std::student_t_distribution dist(inDof); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "student-T" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_t.html#numpy.random.standard_t + /// + /// @param inDof independent random variables + /// @return NdArray + /// + template + dtype studentT(dtype inDof) + { + return detail::studentT(generator_, inDof); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "student-T" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.standard_t.html#numpy.random.standard_t + /// + /// @param inShape + /// @param inDof independent random variables + /// @return NdArray + /// + template + NdArray studentT(const Shape& inShape, dtype inDof) + { + return detail::studentT(generator_, inShape, inDof); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/triangle.hpp b/include/NumCpp/Random/triangle.hpp index 84cc8213a..9a226f9e3 100644 --- a/include/NumCpp/Random/triangle.hpp +++ b/include/NumCpp/Random/triangle.hpp @@ -41,113 +41,10 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Single random value sampled from the "triangle" distribution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.triangular.html#numpy.random.triangular - /// - /// @param generator: instance of a random number generator - /// @param inA - /// @param inB - /// @param inC - /// @return NdArray - /// - template - dtype triangle(GeneratorType& generator, dtype inA = 0, dtype inB = 0.5, dtype inC = 1) - { - STATIC_ASSERT_FLOAT(dtype); - - if (inA < 0) - { - THROW_INVALID_ARGUMENT_ERROR("input A must be greater than or equal to zero."); - } - - if (inB < 0) - { - THROW_INVALID_ARGUMENT_ERROR("input B must be greater than or equal to zero."); - } - - if (inC < 0) - { - THROW_INVALID_ARGUMENT_ERROR("input C must be greater than or equal to zero."); - } - - const bool aLessB = inA <= inB; - const bool bLessC = inB <= inC; - if (!(aLessB && bLessC)) - { - THROW_INVALID_ARGUMENT_ERROR("inputs must be a <= b <= c."); - } - - boost::random::triangle_distribution dist(inA, inB, inC); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "triangle" distribution. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.triangular.html#numpy.random.triangular - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inA - /// @param inB - /// @param inC - /// @return NdArray - /// - template - NdArray - triangle(GeneratorType& generator, const Shape& inShape, dtype inA = 0, dtype inB = 0.5, dtype inC = 1) - { - STATIC_ASSERT_FLOAT(dtype); - - if (inA < 0) - { - THROW_INVALID_ARGUMENT_ERROR("input A must be greater than or equal to zero."); - } - - if (inB < 0) - { - THROW_INVALID_ARGUMENT_ERROR("input B must be greater than or equal to zero."); - } - - if (inC < 0) - { - THROW_INVALID_ARGUMENT_ERROR("input C must be greater than or equal to zero."); - } - - const bool aLessB = inA <= inB; - const bool bLessC = inB <= inC; - if (!(aLessB && bLessC)) - { - THROW_INVALID_ARGUMENT_ERROR("inputs must be a <= b <= c."); - } - - NdArray returnArray(inShape); - - boost::random::triangle_distribution dist(inA, inB, inC); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Single random value sampled from the "triangle" distribution. @@ -156,15 +53,41 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.triangular.html#numpy.random.triangular /// + /// @param generator: instance of a random number generator /// @param inA /// @param inB /// @param inC /// @return NdArray /// - template - dtype triangle(dtype inA = 0, dtype inB = 0.5, dtype inC = 1) + template + dtype triangle(GeneratorType& generator, dtype inA = 0, dtype inB = 0.5, dtype inC = 1) { - return detail::triangle(generator_, inA, inB, inC); + STATIC_ASSERT_FLOAT(dtype); + + if (inA < 0) + { + THROW_INVALID_ARGUMENT_ERROR("input A must be greater than or equal to zero."); + } + + if (inB < 0) + { + THROW_INVALID_ARGUMENT_ERROR("input B must be greater than or equal to zero."); + } + + if (inC < 0) + { + THROW_INVALID_ARGUMENT_ERROR("input C must be greater than or equal to zero."); + } + + const bool aLessB = inA <= inB; + const bool bLessC = inB <= inC; + if (!(aLessB && bLessC)) + { + THROW_INVALID_ARGUMENT_ERROR("inputs must be a <= b <= c."); + } + + boost::random::triangle_distribution dist(inA, inB, inC); + return dist(generator); } //============================================================================ @@ -176,18 +99,92 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.triangular.html#numpy.random.triangular /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inA /// @param inB /// @param inC /// @return NdArray /// - template - NdArray triangle(const Shape& inShape, dtype inA = 0, dtype inB = 0.5, dtype inC = 1) + template + NdArray + triangle(GeneratorType& generator, const Shape& inShape, dtype inA = 0, dtype inB = 0.5, dtype inC = 1) { - return detail::triangle(generator_, inShape, inA, inB, inC); + STATIC_ASSERT_FLOAT(dtype); + + if (inA < 0) + { + THROW_INVALID_ARGUMENT_ERROR("input A must be greater than or equal to zero."); + } + + if (inB < 0) + { + THROW_INVALID_ARGUMENT_ERROR("input B must be greater than or equal to zero."); + } + + if (inC < 0) + { + THROW_INVALID_ARGUMENT_ERROR("input C must be greater than or equal to zero."); + } + + const bool aLessB = inA <= inB; + const bool bLessC = inB <= inC; + if (!(aLessB && bLessC)) + { + THROW_INVALID_ARGUMENT_ERROR("inputs must be a <= b <= c."); + } + + NdArray returnArray(inShape); + + boost::random::triangle_distribution dist(inA, inB, inC); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "triangle" distribution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.triangular.html#numpy.random.triangular + /// + /// @param inA + /// @param inB + /// @param inC + /// @return NdArray + /// + template + dtype triangle(dtype inA = 0, dtype inB = 0.5, dtype inC = 1) + { + return detail::triangle(generator_, inA, inB, inC); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "triangle" distribution. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.triangular.html#numpy.random.triangular + /// + /// @param inShape + /// @param inA + /// @param inB + /// @param inC + /// @return NdArray + /// + template + NdArray triangle(const Shape& inShape, dtype inA = 0, dtype inB = 0.5, dtype inC = 1) + { + return detail::triangle(generator_, inShape, inA, inB, inC); + } +} // namespace nc::random #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Random/uniform.hpp b/include/NumCpp/Random/uniform.hpp index fea45eaaa..4904d6cb3 100644 --- a/include/NumCpp/Random/uniform.hpp +++ b/include/NumCpp/Random/uniform.hpp @@ -31,60 +31,10 @@ #include "NumCpp/Core/Internal/StaticAsserts.hpp" #include "NumCpp/Random/randFloat.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Draw sample from a uniform distribution. - /// - /// Samples are uniformly distributed over the half - - /// open interval[low, high) (includes low, but excludes high) - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.uniform.html#numpy.random.uniform - /// - /// @param generator: instance of a random number generator - /// @param inLow - /// @param inHigh - /// @return NdArray - /// - template - dtype uniform(GeneratorType& generator, dtype inLow, dtype inHigh) - { - STATIC_ASSERT_FLOAT(dtype); - - return detail::randFloat(generator, inLow, inHigh); - } - - //============================================================================ - // Method Description: - /// Draw samples from a uniform distribution. - /// - /// Samples are uniformly distributed over the half - - /// open interval[low, high) (includes low, but excludes high) - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.uniform.html#numpy.random.uniform - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inLow - /// @param inHigh - /// @return NdArray - /// - template - NdArray uniform(GeneratorType& generator, const Shape& inShape, dtype inLow, dtype inHigh) - { - STATIC_ASSERT_FLOAT(dtype); - - return detail::randFloat(generator, inShape, inLow, inHigh); - } - } // namespace detail - //============================================================================ // Method Description: /// Draw sample from a uniform distribution. @@ -95,14 +45,17 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.uniform.html#numpy.random.uniform /// + /// @param generator: instance of a random number generator /// @param inLow /// @param inHigh /// @return NdArray /// - template - dtype uniform(dtype inLow, dtype inHigh) + template + dtype uniform(GeneratorType& generator, dtype inLow, dtype inHigh) { - return detail::uniform(generator_, inLow, inHigh); + STATIC_ASSERT_FLOAT(dtype); + + return detail::randFloat(generator, inLow, inHigh); } //============================================================================ @@ -115,15 +68,59 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.uniform.html#numpy.random.uniform /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inLow /// @param inHigh /// @return NdArray /// - template - NdArray uniform(const Shape& inShape, dtype inLow, dtype inHigh) + template + NdArray uniform(GeneratorType& generator, const Shape& inShape, dtype inLow, dtype inHigh) { - return detail::uniform(generator_, inShape, inLow, inHigh); + STATIC_ASSERT_FLOAT(dtype); + + return detail::randFloat(generator, inShape, inLow, inHigh); } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Draw sample from a uniform distribution. + /// + /// Samples are uniformly distributed over the half - + /// open interval[low, high) (includes low, but excludes high) + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.uniform.html#numpy.random.uniform + /// + /// @param inLow + /// @param inHigh + /// @return NdArray + /// + template + dtype uniform(dtype inLow, dtype inHigh) + { + return detail::uniform(generator_, inLow, inHigh); + } + + //============================================================================ + // Method Description: + /// Draw samples from a uniform distribution. + /// + /// Samples are uniformly distributed over the half - + /// open interval[low, high) (includes low, but excludes high) + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.uniform.html#numpy.random.uniform + /// + /// @param inShape + /// @param inLow + /// @param inHigh + /// @return NdArray + /// + template + NdArray uniform(const Shape& inShape, dtype inLow, dtype inHigh) + { + return detail::uniform(generator_, inShape, inLow, inHigh); + } +} // namespace nc::random diff --git a/include/NumCpp/Random/uniformOnSphere.hpp b/include/NumCpp/Random/uniformOnSphere.hpp index ad0a3c76f..140e3a09c 100644 --- a/include/NumCpp/Random/uniformOnSphere.hpp +++ b/include/NumCpp/Random/uniformOnSphere.hpp @@ -41,62 +41,59 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - //============================================================================ - // Method Description: - /// Such a distribution produces random numbers uniformly - /// distributed on the unit sphere of arbitrary dimension dim. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param generator: instance of a random number generator - /// @param inNumPoints - /// @param inDims: dimension of the sphere (default 2) - /// @return NdArray - /// - template - NdArray uniformOnSphere(GeneratorType& generator, uint32 inNumPoints, uint32 inDims = 2) - { - STATIC_ASSERT_FLOAT(dtype); - - if (inNumPoints == 0) - { - return {}; - } - - boost::random::uniform_on_sphere dist(static_cast(inDims)); - - NdArray returnArray(inNumPoints, inDims); - for (uint32 row = 0; row < inNumPoints; ++row) - { - const auto& point = dist(generator); - std::copy(point.begin(), point.end(), returnArray.begin(row)); - } - - return returnArray; - } - } // namespace detail - //============================================================================ // Method Description: /// Such a distribution produces random numbers uniformly /// distributed on the unit sphere of arbitrary dimension dim. /// NOTE: Use of this function requires using the Boost includes. /// + /// @param generator: instance of a random number generator /// @param inNumPoints /// @param inDims: dimension of the sphere (default 2) /// @return NdArray /// - template - NdArray uniformOnSphere(uint32 inNumPoints, uint32 inDims = 2) + template + NdArray uniformOnSphere(GeneratorType& generator, uint32 inNumPoints, uint32 inDims = 2) { - return detail::uniformOnSphere(generator_, inNumPoints, inDims); + STATIC_ASSERT_FLOAT(dtype); + + if (inNumPoints == 0) + { + return {}; + } + + boost::random::uniform_on_sphere dist(static_cast(inDims)); + + NdArray returnArray(inNumPoints, inDims); + for (uint32 row = 0; row < inNumPoints; ++row) + { + const auto& point = dist(generator); + std::copy(point.begin(), point.end(), returnArray.begin(row)); + } + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Such a distribution produces random numbers uniformly + /// distributed on the unit sphere of arbitrary dimension dim. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inNumPoints + /// @param inDims: dimension of the sphere (default 2) + /// @return NdArray + /// + template + NdArray uniformOnSphere(uint32 inNumPoints, uint32 inDims = 2) + { + return detail::uniformOnSphere(generator_, inNumPoints, inDims); + } +} // namespace nc::random #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Random/weibull.hpp b/include/NumCpp/Random/weibull.hpp index f5a814819..706629767 100644 --- a/include/NumCpp/Random/weibull.hpp +++ b/include/NumCpp/Random/weibull.hpp @@ -37,98 +37,38 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Random/generator.hpp" -namespace nc +namespace nc::random { - namespace random + namespace detail { - namespace detail - { - // Method Description: - /// Single random value sampled from the "weibull" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.weibull.html#numpy.random.weibull - /// - /// @param generator: instance of a random number generator - /// @param inA (default 1) - /// @param inB (default 1) - /// @return NdArray - /// - template - dtype weibull(GeneratorType& generator, dtype inA = 1, dtype inB = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inA <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input a must be greater than zero."); - } - - if (inB <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input b must be greater than zero."); - } - - std::weibull_distribution dist(inA, inB); - return dist(generator); - } - - //============================================================================ - // Method Description: - /// Create an array of the given shape and populate it with - /// random samples from the "weibull" distribution. - /// - /// NumPy Reference: - /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.weibull.html#numpy.random.weibull - /// - /// @param generator: instance of a random number generator - /// @param inShape - /// @param inA (default 1) - /// @param inB (default 1) - /// @return NdArray - /// - template - NdArray weibull(GeneratorType& generator, const Shape& inShape, dtype inA = 1, dtype inB = 1) - { - STATIC_ASSERT_ARITHMETIC(dtype); - - if (inA <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input a must be greater than zero."); - } - - if (inB <= 0) - { - THROW_INVALID_ARGUMENT_ERROR("input b must be greater than zero."); - } - - NdArray returnArray(inShape); - - std::weibull_distribution dist(inA, inB); - - std::for_each(returnArray.begin(), - returnArray.end(), - [&generator, &dist](dtype& value) -> void { value = dist(generator); }); - - return returnArray; - } - } // namespace detail - - //============================================================================ // Method Description: /// Single random value sampled from the "weibull" distribution. /// /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.weibull.html#numpy.random.weibull /// + /// @param generator: instance of a random number generator /// @param inA (default 1) /// @param inB (default 1) /// @return NdArray /// - template - dtype weibull(dtype inA = 1, dtype inB = 1) + template + dtype weibull(GeneratorType& generator, dtype inA = 1, dtype inB = 1) { - return detail::weibull(generator_, inA, inB); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inA <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input a must be greater than zero."); + } + + if (inB <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input b must be greater than zero."); + } + + std::weibull_distribution dist(inA, inB); + return dist(generator); } //============================================================================ @@ -139,15 +79,72 @@ namespace nc /// NumPy Reference: /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.weibull.html#numpy.random.weibull /// + /// @param generator: instance of a random number generator /// @param inShape /// @param inA (default 1) /// @param inB (default 1) /// @return NdArray /// - template - NdArray weibull(const Shape& inShape, dtype inA = 1, dtype inB = 1) + template + NdArray weibull(GeneratorType& generator, const Shape& inShape, dtype inA = 1, dtype inB = 1) { - return detail::weibull(generator_, inShape, inA, inB); + STATIC_ASSERT_ARITHMETIC(dtype); + + if (inA <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input a must be greater than zero."); + } + + if (inB <= 0) + { + THROW_INVALID_ARGUMENT_ERROR("input b must be greater than zero."); + } + + NdArray returnArray(inShape); + + std::weibull_distribution dist(inA, inB); + + std::for_each(returnArray.begin(), + returnArray.end(), + [&generator, &dist](dtype& value) -> void { value = dist(generator); }); + + return returnArray; } - } // namespace random -} // namespace nc + } // namespace detail + + //============================================================================ + // Method Description: + /// Single random value sampled from the "weibull" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.weibull.html#numpy.random.weibull + /// + /// @param inA (default 1) + /// @param inB (default 1) + /// @return NdArray + /// + template + dtype weibull(dtype inA = 1, dtype inB = 1) + { + return detail::weibull(generator_, inA, inB); + } + + //============================================================================ + // Method Description: + /// Create an array of the given shape and populate it with + /// random samples from the "weibull" distribution. + /// + /// NumPy Reference: + /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.weibull.html#numpy.random.weibull + /// + /// @param inShape + /// @param inA (default 1) + /// @param inB (default 1) + /// @return NdArray + /// + template + NdArray weibull(const Shape& inShape, dtype inA = 1, dtype inB = 1) + { + return detail::weibull(generator_, inShape, inA, inB); + } +} // namespace nc::random diff --git a/include/NumCpp/Roots/Bisection.hpp b/include/NumCpp/Roots/Bisection.hpp index 1c65fa5c5..332382745 100644 --- a/include/NumCpp/Roots/Bisection.hpp +++ b/include/NumCpp/Roots/Bisection.hpp @@ -39,120 +39,117 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/Roots/Iteration.hpp" -namespace nc +namespace nc::roots { - namespace roots + //================================================================================ + // Class Description: + /// Bisection root finding method + /// + class Bisection : public Iteration { - //================================================================================ - // Class Description: - /// Bisection root finding method + public: + //============================================================================ + // Method Description: + /// Constructor /// - class Bisection : public Iteration + /// @param epsilon: the epsilon value + /// @param f: the function + /// + Bisection(const double epsilon, std::function f) noexcept : + Iteration(epsilon), + f_(std::move(f)) { - public: - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param epsilon: the epsilon value - /// @param f: the function - /// - Bisection(const double epsilon, std::function f) noexcept : - Iteration(epsilon), - f_(std::move(f)) - { - } + } - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param epsilon: the epsilon value - /// @param maxNumIterations: the maximum number of iterations to perform - /// @param f: the function - /// - Bisection(const double epsilon, const uint32 maxNumIterations, std::function f) noexcept : - Iteration(epsilon, maxNumIterations), - f_(std::move(f)) - { - } - - //============================================================================ - // Method Description: - /// Destructor - /// - ~Bisection() override = default; + //============================================================================ + // Method Description: + /// Constructor + /// + /// @param epsilon: the epsilon value + /// @param maxNumIterations: the maximum number of iterations to perform + /// @param f: the function + /// + Bisection(const double epsilon, const uint32 maxNumIterations, std::function f) noexcept : + Iteration(epsilon, maxNumIterations), + f_(std::move(f)) + { + } - //============================================================================ - // Method Description: - /// Solves for the root in the range [a, b] - /// - /// @param a: the lower bound - /// @param b: the upper bound - /// @return root between the bound - /// - double solve(double a, double b) - { - resetNumberOfIterations(); - checkAndFixAlgorithmCriteria(a, b); + //============================================================================ + // Method Description: + /// Destructor + /// + ~Bisection() override = default; - double x = 0.5 * (a + b); - double fx = f_(x); + //============================================================================ + // Method Description: + /// Solves for the root in the range [a, b] + /// + /// @param a: the lower bound + /// @param b: the upper bound + /// @return root between the bound + /// + double solve(double a, double b) + { + resetNumberOfIterations(); + checkAndFixAlgorithmCriteria(a, b); - while (std::fabs(fx) >= epsilon_) - { - x = calculateX(x, a, b, fx); - fx = f_(x); + double x = 0.5 * (a + b); + double fx = f_(x); - incrementNumberOfIterations(); - } + while (std::fabs(fx) >= epsilon_) + { + x = calculateX(x, a, b, fx); + fx = f_(x); - return x; + incrementNumberOfIterations(); } - private: - //============================================================================ - const std::function f_; + return x; + } - //============================================================================ - // Method Description: - /// Checks the bounds criteria - /// - /// @param a: the lower bound - /// @param b: the upper bound - /// - void checkAndFixAlgorithmCriteria(double &a, double &b) const noexcept + private: + //============================================================================ + const std::function f_; + + //============================================================================ + // Method Description: + /// Checks the bounds criteria + /// + /// @param a: the lower bound + /// @param b: the upper bound + /// + void checkAndFixAlgorithmCriteria(double &a, double &b) const noexcept + { + // Algorithm works in range [a,b] if criteria f(a)*f(b) < 0 and f(a) > f(b) is fulfilled + if (f_(a) < f_(b)) { - // Algorithm works in range [a,b] if criteria f(a)*f(b) < 0 and f(a) > f(b) is fulfilled - if (f_(a) < f_(b)) - { - std::swap(a, b); - } + std::swap(a, b); } + } - //============================================================================ - // Method Description: - /// Calculates the bisection point - /// - /// @param x: the evaluation point - /// @param a: the lower bound - /// @param b: the upper bound - /// @param fx: the function evaluated at x - /// @return x - /// - static double calculateX(double x, double &a, double &b, double fx) noexcept + //============================================================================ + // Method Description: + /// Calculates the bisection point + /// + /// @param x: the evaluation point + /// @param a: the lower bound + /// @param b: the upper bound + /// @param fx: the function evaluated at x + /// @return x + /// + static double calculateX(double x, double &a, double &b, double fx) noexcept + { + if (fx < 0) { - if (fx < 0) - { - b = x; - } - else - { - a = x; - } - - return 0.5 * (a + b); + b = x; } - }; - } // namespace roots -} // namespace nc + else + { + a = x; + } + + return 0.5 * (a + b); + } + }; +} // namespace nc::roots diff --git a/include/NumCpp/Roots/Brent.hpp b/include/NumCpp/Roots/Brent.hpp index cf50ab53e..3f405a322 100644 --- a/include/NumCpp/Roots/Brent.hpp +++ b/include/NumCpp/Roots/Brent.hpp @@ -40,245 +40,240 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/Roots/Iteration.hpp" -namespace nc +namespace nc::roots { - namespace roots + //================================================================================ + // Class Description: + /// Brent root finding method + /// + class Brent : public Iteration { - //================================================================================ - // Class Description: - /// Brent root finding method + public: + //============================================================================ + // Method Description: + /// Constructor /// - class Brent : public Iteration + /// @param epsilon: the epsilon value + /// @param f: the function + /// + Brent(const double epsilon, std::function f) noexcept : + Iteration(epsilon), + f_(std::move(f)) { - public: - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param epsilon: the epsilon value - /// @param f: the function - /// - Brent(const double epsilon, std::function f) noexcept : - Iteration(epsilon), - f_(std::move(f)) - { - } + } - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param epsilon: the epsilon value - /// @param maxNumIterations: the maximum number of iterations to perform - /// @param f: the function - /// - Brent(const double epsilon, const uint32 maxNumIterations, std::function f) noexcept : - Iteration(epsilon, maxNumIterations), - f_(std::move(f)) - { - } + //============================================================================ + // Method Description: + /// Constructor + /// + /// @param epsilon: the epsilon value + /// @param maxNumIterations: the maximum number of iterations to perform + /// @param f: the function + /// + Brent(const double epsilon, const uint32 maxNumIterations, std::function f) noexcept : + Iteration(epsilon, maxNumIterations), + f_(std::move(f)) + { + } - //============================================================================ - // Method Description: - /// Destructor - /// - ~Brent() override = default; + //============================================================================ + // Method Description: + /// Destructor + /// + ~Brent() override = default; - //============================================================================ - // Method Description: - /// Solves for the root in the range [a, b] - /// - /// @param a: the lower bound - /// @param b: the upper bound - /// @return root between the bound - /// - double solve(double a, double b) - { - resetNumberOfIterations(); + //============================================================================ + // Method Description: + /// Solves for the root in the range [a, b] + /// + /// @param a: the lower bound + /// @param b: the upper bound + /// @return root between the bound + /// + double solve(double a, double b) + { + resetNumberOfIterations(); - double fa = f_(a); - double fb = f_(b); + double fa = f_(a); + double fb = f_(b); - checkAndFixAlgorithmCriteria(a, b, fa, fb); + checkAndFixAlgorithmCriteria(a, b, fa, fb); - double lastB = a; // b_{k-1} - double lastFb = fa; - double s = DtypeInfo::max(); - double fs = DtypeInfo::max(); - double penultimateB = a; // b_{k-2} + double lastB = a; // b_{k-1} + double lastFb = fa; + double s = DtypeInfo::max(); + double fs = DtypeInfo::max(); + double penultimateB = a; // b_{k-2} - bool bisection = true; - while (std::fabs(fb) > epsilon_ && std::fabs(fs) > epsilon_ && std::fabs(b - a) > epsilon_) + bool bisection = true; + while (std::fabs(fb) > epsilon_ && std::fabs(fs) > epsilon_ && std::fabs(b - a) > epsilon_) + { + if (useInverseQuadraticInterpolation(fa, fb, lastFb)) { - if (useInverseQuadraticInterpolation(fa, fb, lastFb)) - { - s = calculateInverseQuadraticInterpolation(a, b, lastB, fa, fb, lastFb); - } - else - { - s = calculateSecant(a, b, fa, fb); - } - - if (useBisection(bisection, b, lastB, penultimateB, s)) - { - s = calculateBisection(a, b); - bisection = true; - } - else - { - bisection = false; - } - - fs = f_(s); - penultimateB = lastB; - lastB = b; + s = calculateInverseQuadraticInterpolation(a, b, lastB, fa, fb, lastFb); + } + else + { + s = calculateSecant(a, b, fa, fb); + } - if (fa * fs < 0) - { - b = s; - } - else - { - a = s; - } + if (useBisection(bisection, b, lastB, penultimateB, s)) + { + s = calculateBisection(a, b); + bisection = true; + } + else + { + bisection = false; + } - fa = f_(a); - lastFb = fb; - fb = f_(b); - checkAndFixAlgorithmCriteria(a, b, fa, fb); + fs = f_(s); + penultimateB = lastB; + lastB = b; - incrementNumberOfIterations(); + if (fa * fs < 0) + { + b = s; + } + else + { + a = s; } - return fb < fs ? b : s; + fa = f_(a); + lastFb = fb; + fb = f_(b); + checkAndFixAlgorithmCriteria(a, b, fa, fb); + + incrementNumberOfIterations(); } - private: - //============================================================================ - const std::function f_; + return fb < fs ? b : s; + } - //============================================================================ - // Method Description: - /// Calculates the bisection point - /// - /// @param a: the lower bound - /// @param b: the upper bound - /// @return x - /// - static double calculateBisection(const double a, const double b) noexcept - { - return 0.5 * (a + b); - } + private: + //============================================================================ + const std::function f_; - //============================================================================ - // Method Description: - /// Calculates the secant point - /// - /// @param a: the lower bound - /// @param b: the upper bound - /// @param fa: the function evaluated at a - /// @param fb: the function evaluated at b - /// @return the secant point - /// - static double calculateSecant(const double a, const double b, const double fa, const double fb) noexcept - { - // No need to check division by 0, in this case the method returns NAN which is taken care by - // useSecantMethod method - return b - fb * (b - a) / (fb - fa); - } + //============================================================================ + // Method Description: + /// Calculates the bisection point + /// + /// @param a: the lower bound + /// @param b: the upper bound + /// @return x + /// + static double calculateBisection(const double a, const double b) noexcept + { + return 0.5 * (a + b); + } - //============================================================================ - // Method Description: - /// Calculates the inverse quadratic interpolation - /// - /// @param a: the lower bound - /// @param b: the upper bound - /// @param lastB: the previous upper bound - /// @param fa: the function evaluated at a - /// @param fb: the function evaluated at b - /// @param lastFb: the previous function evaluated at the upper bound - /// @return the inverse quadratic interpolation - /// - static double calculateInverseQuadraticInterpolation(const double a, - const double b, - const double lastB, - const double fa, - const double fb, - const double lastFb) noexcept - { - return a * fb * lastFb / ((fa - fb) * (fa - lastFb)) + b * fa * lastFb / ((fb - fa) * (fb - lastFb)) + - lastB * fa * fb / ((lastFb - fa) * (lastFb - fb)); - } + //============================================================================ + // Method Description: + /// Calculates the secant point + /// + /// @param a: the lower bound + /// @param b: the upper bound + /// @param fa: the function evaluated at a + /// @param fb: the function evaluated at b + /// @return the secant point + /// + static double calculateSecant(const double a, const double b, const double fa, const double fb) noexcept + { + // No need to check division by 0, in this case the method returns NAN which is taken care by + // useSecantMethod method + return b - fb * (b - a) / (fb - fa); + } - //============================================================================ - // Method Description: - /// Uses the inverse quadratic interpolation - /// - /// @param fa: the function evaluated at a - /// @param fb: the function evaluated at b - /// @param lastFb: the previous function evaluated at the upper bound - /// @return bool - /// - static bool useInverseQuadraticInterpolation(const double fa, const double fb, const double lastFb) noexcept - { - return fa != lastFb && fb != lastFb; - } + //============================================================================ + // Method Description: + /// Calculates the inverse quadratic interpolation + /// + /// @param a: the lower bound + /// @param b: the upper bound + /// @param lastB: the previous upper bound + /// @param fa: the function evaluated at a + /// @param fb: the function evaluated at b + /// @param lastFb: the previous function evaluated at the upper bound + /// @return the inverse quadratic interpolation + /// + static double calculateInverseQuadraticInterpolation(const double a, + const double b, + const double lastB, + const double fa, + const double fb, + const double lastFb) noexcept + { + return a * fb * lastFb / ((fa - fb) * (fa - lastFb)) + b * fa * lastFb / ((fb - fa) * (fb - lastFb)) + + lastB * fa * fb / ((lastFb - fa) * (lastFb - fb)); + } + + //============================================================================ + // Method Description: + /// Uses the inverse quadratic interpolation + /// + /// @param fa: the function evaluated at a + /// @param fb: the function evaluated at b + /// @param lastFb: the previous function evaluated at the upper bound + /// @return bool + /// + static bool useInverseQuadraticInterpolation(const double fa, const double fb, const double lastFb) noexcept + { + return fa != lastFb && fb != lastFb; + } - //============================================================================ - // Method Description: - /// Checks the algorithm criteria - /// - /// @param a: the lower bound - /// @param b: the upper bound - /// @param fa: the function evaluated at a - /// @param fb: the function evaluated at b - /// - static void checkAndFixAlgorithmCriteria(double &a, double &b, double &fa, double &fb) noexcept + //============================================================================ + // Method Description: + /// Checks the algorithm criteria + /// + /// @param a: the lower bound + /// @param b: the upper bound + /// @param fa: the function evaluated at a + /// @param fb: the function evaluated at b + /// + static void checkAndFixAlgorithmCriteria(double &a, double &b, double &fa, double &fb) noexcept + { + // Algorithm works in range [a,b] if criteria f(a)*f(b) < 0 and f(a) > f(b) is fulfilled + if (std::fabs(fa) < std::fabs(fb)) { - // Algorithm works in range [a,b] if criteria f(a)*f(b) < 0 and f(a) > f(b) is fulfilled - if (std::fabs(fa) < std::fabs(fb)) - { - std::swap(a, b); - std::swap(fa, fb); - } + std::swap(a, b); + std::swap(fa, fb); } + } - //============================================================================ - // Method Description: - /// Uses the bisection - /// - /// @param bisection: the bisection point - /// @param b: the upper bound - /// @param lastB: the previous upper bound - /// @param penultimateB: - /// @param s: - /// @return bool - /// - bool useBisection(const bool bisection, - const double b, - const double lastB, - const double penultimateB, - const double s) const noexcept - { - const double DELTA = epsilon_ + std::numeric_limits::min(); + //============================================================================ + // Method Description: + /// Uses the bisection + /// + /// @param bisection: the bisection point + /// @param b: the upper bound + /// @param lastB: the previous upper bound + /// @param penultimateB: + /// @param s: + /// @return bool + /// + [[nodiscard]] bool useBisection(const bool bisection, + const double b, + const double lastB, + const double penultimateB, + const double s) const noexcept + { + const double DELTA = epsilon_ + std::numeric_limits::min(); - return (bisection && - std::fabs(s - b) >= - 0.5 * - std::fabs(b - lastB)) || // Bisection was used in last step but |s-b|>=|b-lastB|/2 <- - // Interpolation step would be to rough, so still use bisection - (!bisection && - std::fabs(s - b) >= - 0.5 * std::fabs(lastB - penultimateB)) || // Interpolation was used in last step but - // |s-b|>=|lastB-penultimateB|/2 <- Interpolation - // step would be to small - (bisection && - std::fabs(b - lastB) < DELTA) || // If last iteration was using bisection and difference between - // b and lastB is < delta use bisection for next iteration - (!bisection && std::fabs(lastB - penultimateB) < - DELTA); // If last iteration was using interpolation but difference between - // lastB ond penultimateB is < delta use biscetion for next iteration - } - }; - } // namespace roots -} // namespace nc + return (bisection && + std::fabs(s - b) >= + 0.5 * std::fabs(b - lastB)) || // Bisection was used in last step but |s-b|>=|b-lastB|/2 <- + // Interpolation step would be to rough, so still use bisection + (!bisection && std::fabs(s - b) >= + 0.5 * std::fabs(lastB - penultimateB)) || // Interpolation was used in last step + // but |s-b|>=|lastB-penultimateB|/2 <- + // Interpolation step would be to small + (bisection && + std::fabs(b - lastB) < DELTA) || // If last iteration was using bisection and difference between + // b and lastB is < delta use bisection for next iteration + (!bisection && std::fabs(lastB - penultimateB) < + DELTA); // If last iteration was using interpolation but difference between + // lastB ond penultimateB is < delta use biscetion for next iteration + } + }; +} // namespace nc::roots diff --git a/include/NumCpp/Roots/Dekker.hpp b/include/NumCpp/Roots/Dekker.hpp index 7c0688a07..f24a0ed8f 100644 --- a/include/NumCpp/Roots/Dekker.hpp +++ b/include/NumCpp/Roots/Dekker.hpp @@ -39,163 +39,160 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/Roots/Iteration.hpp" -namespace nc +namespace nc::roots { - namespace roots + //================================================================================ + // Class Description: + /// Dekker root finding method + /// + class Dekker : public Iteration { - //================================================================================ - // Class Description: - /// Dekker root finding method + public: + //============================================================================ + // Method Description: + /// Constructor /// - class Dekker : public Iteration + /// @param epsilon: the epsilon value + /// @param f: the function + /// + Dekker(const double epsilon, std::function f) noexcept : + Iteration(epsilon), + f_(std::move(f)) { - public: - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param epsilon: the epsilon value - /// @param f: the function - /// - Dekker(const double epsilon, std::function f) noexcept : - Iteration(epsilon), - f_(std::move(f)) - { - } - - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param epsilon: the epsilon value - /// @param maxNumIterations: the maximum number of iterations to perform - /// @param f: the function - /// - Dekker(const double epsilon, const uint32 maxNumIterations, std::function f) noexcept : - Iteration(epsilon, maxNumIterations), - f_(std::move(f)) - { - } + } - //============================================================================ - // Method Description: - /// Destructor - /// - ~Dekker() override = default; - - //============================================================================ - // Method Description: - /// Solves for the root in the range [a, b] - /// - /// @param a: the lower bound - /// @param b: the upper bound - /// @return root between the bound - /// - double solve(double a, double b) - { - resetNumberOfIterations(); + //============================================================================ + // Method Description: + /// Constructor + /// + /// @param epsilon: the epsilon value + /// @param maxNumIterations: the maximum number of iterations to perform + /// @param f: the function + /// + Dekker(const double epsilon, const uint32 maxNumIterations, std::function f) noexcept : + Iteration(epsilon, maxNumIterations), + f_(std::move(f)) + { + } - double fa = f_(a); - double fb = f_(b); + //============================================================================ + // Method Description: + /// Destructor + /// + ~Dekker() override = default; - checkAndFixAlgorithmCriteria(a, b, fa, fb); + //============================================================================ + // Method Description: + /// Solves for the root in the range [a, b] + /// + /// @param a: the lower bound + /// @param b: the upper bound + /// @return root between the bound + /// + double solve(double a, double b) + { + resetNumberOfIterations(); - double lastB = a; - double lastFb = fa; + double fa = f_(a); + double fb = f_(b); - while (std::fabs(fb) > epsilon_ && std::fabs(b - a) > epsilon_) - { - const double s = calculateSecant(b, fb, lastB, lastFb); - const double m = calculateBisection(a, b); + checkAndFixAlgorithmCriteria(a, b, fa, fb); - lastB = b; + double lastB = a; + double lastFb = fa; - b = useSecantMethod(b, s, m) ? s : m; + while (std::fabs(fb) > epsilon_ && std::fabs(b - a) > epsilon_) + { + const double s = calculateSecant(b, fb, lastB, lastFb); + const double m = calculateBisection(a, b); - lastFb = fb; - fb = f_(b); + lastB = b; - if (fa * fb > 0 && fb * lastFb < 0) - { - a = lastB; - } + b = useSecantMethod(b, s, m) ? s : m; - fa = f_(a); - checkAndFixAlgorithmCriteria(a, b, fa, fb); + lastFb = fb; + fb = f_(b); - incrementNumberOfIterations(); + if (fa * fb > 0 && fb * lastFb < 0) + { + a = lastB; } - return b; - } + fa = f_(a); + checkAndFixAlgorithmCriteria(a, b, fa, fb); - private: - //============================================================================ - const std::function f_; - - //============================================================================ - // Method Description: - /// Checks the bounds criteria - /// - /// @param a: the lower bound - /// @param b: the upper bound - /// @param fa: the function evalulated at the lower bound - /// @param fb: the function evalulated at the upper bound - /// - static void checkAndFixAlgorithmCriteria(double &a, double &b, double &fa, double &fb) noexcept - { - // Algorithm works in range [a,b] if criteria f(a)*f(b) < 0 and f(a) > f(b) is fulfilled - if (std::fabs(fa) < std::fabs(fb)) - { - std::swap(a, b); - std::swap(fa, fb); - } + incrementNumberOfIterations(); } - //============================================================================ - // Method Description: - /// Calculates secant - /// - /// @param b: the upper bound - /// @param fb: the function evalulated at the upper bound - /// @param lastB: the last upper bound - /// @param lastFb: the function evalulated at the last upper bound - /// @ return secant value - /// - static double calculateSecant(double b, double fb, double lastB, double lastFb) noexcept - { - // No need to check division by 0, in this case the method returns NAN which is taken care by - // useSecantMethod method - return b - fb * (b - lastB) / (fb - lastFb); - } + return b; + } - //============================================================================ - // Method Description: - /// Calculate the bisection point - /// - /// @param a: the lower bound - /// @param b: the upper bound - /// @return bisection point - /// - static double calculateBisection(double a, double b) noexcept - { - return 0.5 * (a + b); - } + private: + //============================================================================ + const std::function f_; - //============================================================================ - // Method Description: - /// Whether or not to use the secant method - /// - /// @param b: the upper bound - /// @param s: - /// @param m: - /// @ return bool - /// - static bool useSecantMethod(double b, double s, double m) noexcept + //============================================================================ + // Method Description: + /// Checks the bounds criteria + /// + /// @param a: the lower bound + /// @param b: the upper bound + /// @param fa: the function evalulated at the lower bound + /// @param fb: the function evalulated at the upper bound + /// + static void checkAndFixAlgorithmCriteria(double &a, double &b, double &fa, double &fb) noexcept + { + // Algorithm works in range [a,b] if criteria f(a)*f(b) < 0 and f(a) > f(b) is fulfilled + if (std::fabs(fa) < std::fabs(fb)) { - // Value s calculated by secant method has to be between m and b - return (b > m && s > m && s < b) || (b < m && s > b && s < m); + std::swap(a, b); + std::swap(fa, fb); } - }; - } // namespace roots -} // namespace nc + } + + //============================================================================ + // Method Description: + /// Calculates secant + /// + /// @param b: the upper bound + /// @param fb: the function evalulated at the upper bound + /// @param lastB: the last upper bound + /// @param lastFb: the function evalulated at the last upper bound + /// @ return secant value + /// + static double calculateSecant(double b, double fb, double lastB, double lastFb) noexcept + { + // No need to check division by 0, in this case the method returns NAN which is taken care by + // useSecantMethod method + return b - fb * (b - lastB) / (fb - lastFb); + } + + //============================================================================ + // Method Description: + /// Calculate the bisection point + /// + /// @param a: the lower bound + /// @param b: the upper bound + /// @return bisection point + /// + static double calculateBisection(double a, double b) noexcept + { + return 0.5 * (a + b); + } + + //============================================================================ + // Method Description: + /// Whether or not to use the secant method + /// + /// @param b: the upper bound + /// @param s: + /// @param m: + /// @ return bool + /// + static bool useSecantMethod(double b, double s, double m) noexcept + { + // Value s calculated by secant method has to be between m and b + return (b > m && s > m && s < b) || (b < m && s > b && s < m); + } + }; +} // namespace nc::roots diff --git a/include/NumCpp/Roots/Iteration.hpp b/include/NumCpp/Roots/Iteration.hpp index 510daaa1b..f0268e494 100644 --- a/include/NumCpp/Roots/Iteration.hpp +++ b/include/NumCpp/Roots/Iteration.hpp @@ -37,87 +37,84 @@ #include "NumCpp/Core/Internal/Error.hpp" #include "NumCpp/Core/Types.hpp" -namespace nc +namespace nc::roots { - namespace roots + //================================================================================ + // Class Description: + /// ABC for iteration classes to derive from + class Iteration { - //================================================================================ - // Class Description: - /// ABC for iteration classes to derive from - class Iteration + public: + //============================================================================ + // Method Description: + /// Constructor + /// + /// @param epsilon: the epsilon value + /// + explicit Iteration(double epsilon) noexcept : + epsilon_(epsilon) { - public: - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param epsilon: the epsilon value - /// - explicit Iteration(double epsilon) noexcept : - epsilon_(epsilon) - { - } + } - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param epsilon: the epsilon value - /// @param maxNumIterations: the maximum number of iterations to perform - /// - Iteration(double epsilon, uint32 maxNumIterations) noexcept : - epsilon_(epsilon), - maxNumIterations_(maxNumIterations) - { - } + //============================================================================ + // Method Description: + /// Constructor + /// + /// @param epsilon: the epsilon value + /// @param maxNumIterations: the maximum number of iterations to perform + /// + Iteration(double epsilon, uint32 maxNumIterations) noexcept : + epsilon_(epsilon), + maxNumIterations_(maxNumIterations) + { + } - //============================================================================ - // Method Description: - /// Destructor - /// - virtual ~Iteration() noexcept = default; + //============================================================================ + // Method Description: + /// Destructor + /// + virtual ~Iteration() noexcept = default; - //============================================================================ - // Method Description: - /// Returns the number of iterations - /// - /// @return: number of iterations - /// - uint32 numIterations() const noexcept - { - return numIterations_; - } + //============================================================================ + // Method Description: + /// Returns the number of iterations + /// + /// @return: number of iterations + /// + [[nodiscard]] uint32 numIterations() const noexcept + { + return numIterations_; + } - protected: - //============================================================================ - // Method Description: - /// Resets the number of iterations - /// - void resetNumberOfIterations() noexcept - { - numIterations_ = 0; - } + protected: + //============================================================================ + // Method Description: + /// Resets the number of iterations + /// + void resetNumberOfIterations() noexcept + { + numIterations_ = 0; + } - //============================================================================ - // Method Description: - /// Incraments the number of iterations - /// - /// @return the number of iterations prior to incramenting - /// - void incrementNumberOfIterations() + //============================================================================ + // Method Description: + /// Incraments the number of iterations + /// + /// @return the number of iterations prior to incramenting + /// + void incrementNumberOfIterations() + { + ++numIterations_; + if (numIterations_ > maxNumIterations_) { - ++numIterations_; - if (numIterations_ > maxNumIterations_) - { - THROW_RUNTIME_ERROR( - "Maximum number of iterations has been reached; no root has been found within epsilon."); - } + THROW_RUNTIME_ERROR( + "Maximum number of iterations has been reached; no root has been found within epsilon."); } + } - //====================================Attributes============================== - const double epsilon_; - uint32 maxNumIterations_{ 1000 }; - uint32 numIterations_{ 0 }; - }; - } // namespace roots -} // namespace nc + //====================================Attributes============================== + const double epsilon_; + uint32 maxNumIterations_{ 1000 }; + uint32 numIterations_{ 0 }; + }; +} // namespace nc::roots diff --git a/include/NumCpp/Roots/Newton.hpp b/include/NumCpp/Roots/Newton.hpp index 66eceebbf..b4ebdf3fc 100644 --- a/include/NumCpp/Roots/Newton.hpp +++ b/include/NumCpp/Roots/Newton.hpp @@ -39,103 +39,99 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/Roots/Iteration.hpp" -namespace nc +namespace nc::roots { - namespace roots + //================================================================================ + // Class Description: + /// Newton root finding method + /// + class Newton : public Iteration { - //================================================================================ - // Class Description: - /// Newton root finding method + public: + //============================================================================ + // Method Description: + /// Constructor /// - class Newton : public Iteration + /// @param epsilon: the epsilon value + /// @param f: the function + /// @param fPrime: the derivative of the function + /// + Newton(const double epsilon, std::function f, std::function fPrime) noexcept : + Iteration(epsilon), + f_(std::move(f)), + fPrime_(std::move(fPrime)) { - public: - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param epsilon: the epsilon value - /// @param f: the function - /// @param fPrime: the derivative of the function - /// - Newton(const double epsilon, std::function f, std::function fPrime) noexcept - : - Iteration(epsilon), - f_(std::move(f)), - fPrime_(std::move(fPrime)) - { - } - - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param epsilon: the epsilon value - /// @param maxNumIterations: the maximum number of iterations to perform - /// @param f: the function - /// @param fPrime: the derivative of the function - /// - Newton(const double epsilon, - const uint32 maxNumIterations, - std::function f, - std::function fPrime) noexcept : - Iteration(epsilon, maxNumIterations), - f_(std::move(f)), - fPrime_(std::move(fPrime)) - { - } + } - //============================================================================ - // Method Description: - /// Destructor - /// - ~Newton() noexcept override = default; + //============================================================================ + // Method Description: + /// Constructor + /// + /// @param epsilon: the epsilon value + /// @param maxNumIterations: the maximum number of iterations to perform + /// @param f: the function + /// @param fPrime: the derivative of the function + /// + Newton(const double epsilon, + const uint32 maxNumIterations, + std::function f, + std::function fPrime) noexcept : + Iteration(epsilon, maxNumIterations), + f_(std::move(f)), + fPrime_(std::move(fPrime)) + { + } - //============================================================================ - // Method Description: - /// Solves for the root in the range [a, b] - /// - /// @param x: the starting point - /// @return root nearest the starting point - /// - double solve(double x) - { - resetNumberOfIterations(); + //============================================================================ + // Method Description: + /// Destructor + /// + ~Newton() noexcept override = default; - double fx = f_(x); - double fxPrime = fPrime_(x); + //============================================================================ + // Method Description: + /// Solves for the root in the range [a, b] + /// + /// @param x: the starting point + /// @return root nearest the starting point + /// + double solve(double x) + { + resetNumberOfIterations(); - while (std::fabs(fx) >= epsilon_) - { - x = calculateX(x, fx, fxPrime); + double fx = f_(x); + double fxPrime = fPrime_(x); - fx = f_(x); - fxPrime = fPrime_(x); + while (std::fabs(fx) >= epsilon_) + { + x = calculateX(x, fx, fxPrime); - incrementNumberOfIterations(); - } + fx = f_(x); + fxPrime = fPrime_(x); - return x; + incrementNumberOfIterations(); } - private: - //============================================================================ - const std::function f_; - const std::function fPrime_; + return x; + } - //============================================================================ - // Method Description: - /// Calculates x - /// - /// @param x: the current x value - /// @param fx: the function evaluated at the current x value - /// @param fxPrime: the derivate of the function evaluated at the current x value - /// @return x - /// - static double calculateX(double x, double fx, double fxPrime) noexcept - { - return x - fx / fxPrime; - } - }; - } // namespace roots -} // namespace nc + private: + //============================================================================ + const std::function f_; + const std::function fPrime_; + + //============================================================================ + // Method Description: + /// Calculates x + /// + /// @param x: the current x value + /// @param fx: the function evaluated at the current x value + /// @param fxPrime: the derivate of the function evaluated at the current x value + /// @return x + /// + static double calculateX(double x, double fx, double fxPrime) noexcept + { + return x - fx / fxPrime; + } + }; +} // namespace nc::roots diff --git a/include/NumCpp/Roots/Secant.hpp b/include/NumCpp/Roots/Secant.hpp index 452cb61a0..41f43bc93 100644 --- a/include/NumCpp/Roots/Secant.hpp +++ b/include/NumCpp/Roots/Secant.hpp @@ -40,107 +40,104 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/Roots/Iteration.hpp" -namespace nc +namespace nc::roots { - namespace roots + //================================================================================ + // Class Description: + /// Secant root finding method + /// + class Secant : public Iteration { - //================================================================================ - // Class Description: - /// Secant root finding method + public: + //============================================================================ + // Method Description: + /// Constructor /// - class Secant : public Iteration + /// @param epsilon: the epsilon value + /// @param f: the function + /// + Secant(const double epsilon, std::function f) noexcept : + Iteration(epsilon), + f_(std::move(f)) { - public: - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param epsilon: the epsilon value - /// @param f: the function - /// - Secant(const double epsilon, std::function f) noexcept : - Iteration(epsilon), - f_(std::move(f)) - { - } - - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param epsilon: the epsilon value - /// @param maxNumIterations: the maximum number of iterations to perform - /// @param f: the function - /// - Secant(const double epsilon, const uint32 maxNumIterations, std::function f) noexcept : - Iteration(epsilon, maxNumIterations), - f_(std::move(f)) - { - } + } - //============================================================================ - // Method Description: - /// Destructor - /// - ~Secant() override = default; + //============================================================================ + // Method Description: + /// Constructor + /// + /// @param epsilon: the epsilon value + /// @param maxNumIterations: the maximum number of iterations to perform + /// @param f: the function + /// + Secant(const double epsilon, const uint32 maxNumIterations, std::function f) noexcept : + Iteration(epsilon, maxNumIterations), + f_(std::move(f)) + { + } - //============================================================================ - // Method Description: - /// Solves for the root in the range [a, b] - /// - /// @param a: the lower bound - /// @param b: the upper bound - /// @return root between the bound - /// - double solve(double a, double b) - { - resetNumberOfIterations(); + //============================================================================ + // Method Description: + /// Destructor + /// + ~Secant() override = default; - if (f_(a) > f_(b)) - { - std::swap(a, b); - } + //============================================================================ + // Method Description: + /// Solves for the root in the range [a, b] + /// + /// @param a: the lower bound + /// @param b: the upper bound + /// @return root between the bound + /// + double solve(double a, double b) + { + resetNumberOfIterations(); - double x = b; - double lastX = a; - double fx = f_(b); - double lastFx = f_(a); + if (f_(a) > f_(b)) + { + std::swap(a, b); + } - while (std::fabs(fx) >= epsilon_) - { - const double x_tmp = calculateX(x, lastX, fx, lastFx); + double x = b; + double lastX = a; + double fx = f_(b); + double lastFx = f_(a); - lastFx = fx; - lastX = x; - x = x_tmp; + while (std::fabs(fx) >= epsilon_) + { + const double x_tmp = calculateX(x, lastX, fx, lastFx); - fx = f_(x); + lastFx = fx; + lastX = x; + x = x_tmp; - incrementNumberOfIterations(); - } + fx = f_(x); - return x; + incrementNumberOfIterations(); } - private: - //============================================================================ - const std::function f_; + return x; + } - //============================================================================ - // Method Description: - /// Calculates x - /// - /// @param x: the current x value - /// @param lastX: the previous x value - /// @param fx: the function evaluated at the current x value - /// @param lastFx: the function evaluated at the previous x value - /// @return x - /// - static double calculateX(double x, double lastX, double fx, double lastFx) noexcept - { - const double functionDifference = fx - lastFx; - return x - fx * (x - lastX) / functionDifference; - } - }; - } // namespace roots -} // namespace nc + private: + //============================================================================ + const std::function f_; + + //============================================================================ + // Method Description: + /// Calculates x + /// + /// @param x: the current x value + /// @param lastX: the previous x value + /// @param fx: the function evaluated at the current x value + /// @param lastFx: the function evaluated at the previous x value + /// @return x + /// + static double calculateX(double x, double lastX, double fx, double lastFx) noexcept + { + const double functionDifference = fx - lastFx; + return x - fx * (x - lastX) / functionDifference; + } + }; +} // namespace nc::roots diff --git a/include/NumCpp/Rotations/DCM.hpp b/include/NumCpp/Rotations/DCM.hpp index f97679488..0cabd3f28 100644 --- a/include/NumCpp/Rotations/DCM.hpp +++ b/include/NumCpp/Rotations/DCM.hpp @@ -35,161 +35,158 @@ #include "NumCpp/Utils/essentiallyEqual.hpp" #include "NumCpp/Vector/Vec3.hpp" -namespace nc +namespace nc::rotations { - namespace rotations + //================================================================================ + /// Factory methods for generating direction cosine matrices and vectors + class DCM { - //================================================================================ - /// Factory methods for generating direction cosine matrices and vectors - class DCM + public: + //============================================================================ + // Method Description: + /// returns a direction cosine matrix that rotates according + /// to the input euler angles + /// + /// @param roll: euler roll angle in radians + /// @param pitch: euler pitch angle in radians + /// @param yaw: euler yaw angle in radians + /// @return NdArray + /// + static NdArray eulerAngles(double roll, double pitch, double yaw) { - public: - //============================================================================ - // Method Description: - /// returns a direction cosine matrix that rotates according - /// to the input euler angles - /// - /// @param roll: euler roll angle in radians - /// @param pitch: euler pitch angle in radians - /// @param yaw: euler yaw angle in radians - /// @return NdArray - /// - static NdArray eulerAngles(double roll, double pitch, double yaw) - { - return Quaternion(roll, pitch, yaw).toDCM(); - } + return Quaternion(roll, pitch, yaw).toDCM(); + } - //============================================================================ - // Method Description: - /// returns a direction cosine matrix that rotates according - /// to the input euler angles - /// - /// @param angles: euler roll, pitch, angles - /// @return NdArray - /// - static NdArray eulerAngles(const NdArray& angles) - { - return Quaternion(angles).toDCM(); - } + //============================================================================ + // Method Description: + /// returns a direction cosine matrix that rotates according + /// to the input euler angles + /// + /// @param angles: euler roll, pitch, angles + /// @return NdArray + /// + static NdArray eulerAngles(const NdArray& angles) + { + return Quaternion(angles).toDCM(); + } - //============================================================================ - // Method Description: - /// returns a direction cosine matrix that rotates about - /// the input axis by the input angle - /// - /// @param inAxis: euler axis cartesian vector with x,y,z components - /// @param inAngle: euler angle in radians - /// @return NdArray - /// - static NdArray eulerAxisAngle(const NdArray& inAxis, double inAngle) - { - return Quaternion(inAxis, inAngle).toDCM(); - } + //============================================================================ + // Method Description: + /// returns a direction cosine matrix that rotates about + /// the input axis by the input angle + /// + /// @param inAxis: euler axis cartesian vector with x,y,z components + /// @param inAngle: euler angle in radians + /// @return NdArray + /// + static NdArray eulerAxisAngle(const NdArray& inAxis, double inAngle) + { + return Quaternion(inAxis, inAngle).toDCM(); + } - //============================================================================ - // Method Description: - /// returns a direction cosine matrix that rotates about - /// the input axis by the input angle - /// - /// @param inAxis: euler axis cartesian vector with x,y,z components - /// @param inAngle: euler angle in radians - /// @return NdArray - /// - static NdArray eulerAxisAngle(const Vec3& inAxis, double inAngle) - { - return Quaternion(inAxis, inAngle).toDCM(); - } + //============================================================================ + // Method Description: + /// returns a direction cosine matrix that rotates about + /// the input axis by the input angle + /// + /// @param inAxis: euler axis cartesian vector with x,y,z components + /// @param inAngle: euler angle in radians + /// @return NdArray + /// + static NdArray eulerAxisAngle(const Vec3& inAxis, double inAngle) + { + return Quaternion(inAxis, inAngle).toDCM(); + } - //============================================================================ - // Method Description: - /// returns whether the input array is a direction cosine - /// matrix - /// - /// @param inArray - /// @return bool - /// - static bool isValid(const NdArray& inArray) - { - const Shape inShape = inArray.shape(); - return inShape.rows == inShape.cols && - utils::essentiallyEqual(round(linalg::det(inArray), 2), 1.) && - utils::essentiallyEqual(round(linalg::det(inArray.transpose()), 2), 1.); - } + //============================================================================ + // Method Description: + /// returns whether the input array is a direction cosine + /// matrix + /// + /// @param inArray + /// @return bool + /// + static bool isValid(const NdArray& inArray) + { + const Shape inShape = inArray.shape(); + return inShape.rows == inShape.cols && + utils::essentiallyEqual(round(linalg::det(inArray), 2), 1.) && + utils::essentiallyEqual(round(linalg::det(inArray.transpose()), 2), 1.); + } - //============================================================================ - // Method Description: - /// The euler roll angle in radians - /// - /// @param dcm: a valid direction cosine matrix - /// @return euler roll angle in radians - /// - static double roll(const NdArray& dcm) - { - return Quaternion(dcm).roll(); - } + //============================================================================ + // Method Description: + /// The euler roll angle in radians + /// + /// @param dcm: a valid direction cosine matrix + /// @return euler roll angle in radians + /// + static double roll(const NdArray& dcm) + { + return Quaternion(dcm).roll(); + } - //============================================================================ - // Method Description: - /// The euler pitch angle in radians - /// - /// @param dcm: a valid direction cosine matrix - /// @return euler pitch angle in radians - /// - static double pitch(const NdArray& dcm) - { - return Quaternion(dcm).pitch(); - } + //============================================================================ + // Method Description: + /// The euler pitch angle in radians + /// + /// @param dcm: a valid direction cosine matrix + /// @return euler pitch angle in radians + /// + static double pitch(const NdArray& dcm) + { + return Quaternion(dcm).pitch(); + } - //============================================================================ - // Method Description: - /// The euler yaw angle in radians - /// - /// @param dcm: a valid direction cosine matrix - /// @return euler yaw angle in radians - /// - static double yaw(const NdArray& dcm) - { - return Quaternion(dcm).yaw(); - } + //============================================================================ + // Method Description: + /// The euler yaw angle in radians + /// + /// @param dcm: a valid direction cosine matrix + /// @return euler yaw angle in radians + /// + static double yaw(const NdArray& dcm) + { + return Quaternion(dcm).yaw(); + } - //============================================================================ - // Method Description: - /// returns a direction cosine matrix that rotates about - /// the x axis by the input angle - /// - /// @param inAngle (in radians) - /// @return NdArray - /// - static NdArray xRotation(double inAngle) - { - return DCM::eulerAxisAngle(Vec3{ 1., 0., 0. }, inAngle); - } + //============================================================================ + // Method Description: + /// returns a direction cosine matrix that rotates about + /// the x axis by the input angle + /// + /// @param inAngle (in radians) + /// @return NdArray + /// + static NdArray xRotation(double inAngle) + { + return DCM::eulerAxisAngle(Vec3{ 1., 0., 0. }, inAngle); + } - //============================================================================ - // Method Description: - /// returns a direction cosine matrix that rotates about - /// the x axis by the input angle - /// - /// @param inAngle (in radians) - /// @return NdArray - /// - static NdArray yRotation(double inAngle) - { - return DCM::eulerAxisAngle(Vec3{ 0., 1., 0. }, inAngle); - } + //============================================================================ + // Method Description: + /// returns a direction cosine matrix that rotates about + /// the x axis by the input angle + /// + /// @param inAngle (in radians) + /// @return NdArray + /// + static NdArray yRotation(double inAngle) + { + return DCM::eulerAxisAngle(Vec3{ 0., 1., 0. }, inAngle); + } - //============================================================================ - // Method Description: - /// returns a direction cosine matrix that rotates about - /// the x axis by the input angle - /// - /// @param inAngle (in radians) - /// @return NdArray - /// - static NdArray zRotation(double inAngle) - { - return DCM::eulerAxisAngle(Vec3{ 0., 0., 1. }, inAngle); - } - }; - } // namespace rotations -} // namespace nc + //============================================================================ + // Method Description: + /// returns a direction cosine matrix that rotates about + /// the x axis by the input angle + /// + /// @param inAngle (in radians) + /// @return NdArray + /// + static NdArray zRotation(double inAngle) + { + return DCM::eulerAxisAngle(Vec3{ 0., 0., 1. }, inAngle); + } + }; +} // namespace nc::rotations diff --git a/include/NumCpp/Rotations/Quaternion.hpp b/include/NumCpp/Rotations/Quaternion.hpp index 8b90d9563..36f269126 100644 --- a/include/NumCpp/Rotations/Quaternion.hpp +++ b/include/NumCpp/Rotations/Quaternion.hpp @@ -47,990 +47,982 @@ #include "NumCpp/Utils/sqr.hpp" #include "NumCpp/Vector/Vec3.hpp" -namespace nc +namespace nc::rotations { - namespace rotations + //================================================================================ + // Class Description: + /// Holds a unit quaternion + class Quaternion { - //================================================================================ - // Class Description: - /// Holds a unit quaternion - class Quaternion + public: + //============================================================================ + // Method Description: + /// Default Constructor + /// + Quaternion() = default; + + //============================================================================ + // Method Description: + /// Constructor + /// + /// @param roll: euler roll angle in radians + /// @param pitch: euler pitch angle in radians + /// @param yaw: euler yaw angle in radians + /// + Quaternion(double roll, double pitch, double yaw) noexcept { - public: - //============================================================================ - // Method Description: - /// Default Constructor - /// - Quaternion() = default; - - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param roll: euler roll angle in radians - /// @param pitch: euler pitch angle in radians - /// @param yaw: euler yaw angle in radians - /// - Quaternion(double roll, double pitch, double yaw) noexcept + eulerToQuat(roll, pitch, yaw); + } + + //============================================================================ + // Method Description: + /// Constructor + /// + /// @param inI + /// @param inJ + /// @param inK + /// @param inS + /// + Quaternion(double inI, double inJ, double inK, double inS) noexcept : + components_{ inI, inJ, inK, inS } + { + normalize(); + } + + //============================================================================ + // Method Description: + /// Constructor + /// + /// @param components + /// + Quaternion(const std::array& components) noexcept : + components_{ components } + { + normalize(); + } + + //============================================================================ + // Method Description: + /// Constructor + /// + /// @param inArray: if size = 3 the roll, pitch, yaw euler angles + /// if size = 4 the i, j, k, s components + /// if shape = [3, 3] then direction cosine matrix + /// + Quaternion(const NdArray& inArray) : + components_{ 0., 0., 0., 0. } + { + if (inArray.size() == 3) { - eulerToQuat(roll, pitch, yaw); + // euler angles + eulerToQuat(inArray[0], inArray[1], inArray[2]); } - - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param inI - /// @param inJ - /// @param inK - /// @param inS - /// - Quaternion(double inI, double inJ, double inK, double inS) noexcept : - components_{ inI, inJ, inK, inS } + else if (inArray.size() == 4) { + // quaternion i, j, k, s components + stl_algorithms::copy(inArray.cbegin(), inArray.cend(), components_.begin()); normalize(); } - - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param components - /// - Quaternion(const std::array& components) noexcept : - components_{ components } + else if (inArray.size() == 9) { - normalize(); + // direction cosine matrix + dcmToQuat(inArray); } - - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param inArray: if size = 3 the roll, pitch, yaw euler angles - /// if size = 4 the i, j, k, s components - /// if shape = [3, 3] then direction cosine matrix - /// - Quaternion(const NdArray& inArray) : - components_{ 0., 0., 0., 0. } + else { - if (inArray.size() == 3) - { - // euler angles - eulerToQuat(inArray[0], inArray[1], inArray[2]); - } - else if (inArray.size() == 4) - { - // quaternion i, j, k, s components - stl_algorithms::copy(inArray.cbegin(), inArray.cend(), components_.begin()); - normalize(); - } - else if (inArray.size() == 9) - { - // direction cosine matrix - dcmToQuat(inArray); - } - else - { - THROW_INVALID_ARGUMENT_ERROR("input array is not a valid size."); - } + THROW_INVALID_ARGUMENT_ERROR("input array is not a valid size."); } - - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param inAxis: Euler axis - /// @param inAngle: Euler angle in radians - /// - Quaternion(const Vec3& inAxis, double inAngle) noexcept - { - // normalize the input vector - Vec3 normAxis = inAxis.normalize(); - - const double halfAngle = inAngle / 2.; - const double sinHalfAngle = std::sin(halfAngle); - - components_[0] = normAxis.x * sinHalfAngle; - components_[1] = normAxis.y * sinHalfAngle; - components_[2] = normAxis.z * sinHalfAngle; - components_[3] = std::cos(halfAngle); - } - - //============================================================================ - // Method Description: - /// Constructor - /// - /// @param inAxis: Euler axis x,y,z vector components - /// @param inAngle: Euler angle in radians - /// - Quaternion(const NdArray& inAxis, double inAngle) : - Quaternion(Vec3(inAxis), inAngle) + } + + //============================================================================ + // Method Description: + /// Constructor + /// + /// @param inAxis: Euler axis + /// @param inAngle: Euler angle in radians + /// + Quaternion(const Vec3& inAxis, double inAngle) noexcept + { + // normalize the input vector + Vec3 normAxis = inAxis.normalize(); + + const double halfAngle = inAngle / 2.; + const double sinHalfAngle = std::sin(halfAngle); + + components_[0] = normAxis.x * sinHalfAngle; + components_[1] = normAxis.y * sinHalfAngle; + components_[2] = normAxis.z * sinHalfAngle; + components_[3] = std::cos(halfAngle); + } + + //============================================================================ + // Method Description: + /// Constructor + /// + /// @param inAxis: Euler axis x,y,z vector components + /// @param inAngle: Euler angle in radians + /// + Quaternion(const NdArray& inAxis, double inAngle) : + Quaternion(Vec3(inAxis), inAngle) + { + } + + //============================================================================ + // Method Description: + /// the angle of rotation around the rotation axis that is described by the quaternion + /// + /// @return radians + /// + [[nodiscard]] double angleOfRotation() const noexcept + { + return 2. * std::acos(s()); + } + + //============================================================================ + // Method Description: + /// angular velocity vector between the two quaternions. The norm + /// of the array is the magnitude + /// + /// @param inQuat1 + /// @param inQuat2 + /// @param inTime (seperation time) + /// @return NdArray + /// + static NdArray angularVelocity(const Quaternion& inQuat1, const Quaternion& inQuat2, double inTime) + { + NdArray q0 = inQuat1.toNdArray(); + NdArray q1 = inQuat2.toNdArray(); + + NdArray qDot = q1 - q0; + qDot /= inTime; + + NdArray eyeTimesScalar(3); + eyeTimesScalar.zeros(); + eyeTimesScalar(0, 0) = inQuat2.s(); + eyeTimesScalar(1, 1) = inQuat2.s(); + eyeTimesScalar(2, 2) = inQuat2.s(); + + NdArray epsilonHat = linalg::hat(inQuat2.i(), inQuat2.j(), inQuat2.k()); + NdArray q(4, 3); + q.put(Slice(0, 3), Slice(0, 3), eyeTimesScalar + epsilonHat); + q(3, 0) = -inQuat2.i(); + q(3, 1) = -inQuat2.j(); + q(3, 2) = -inQuat2.k(); + + NdArray omega = q.transpose().dot(qDot.transpose()); + return omega *= 2.; + } + + //============================================================================ + // Method Description: + /// angular velocity vector between the two quaternions. The norm + /// of the array is the magnitude + /// + /// @param inQuat2 + /// @param inTime (seperation time) + /// @return NdArray + /// + [[nodiscard]] NdArray angularVelocity(const Quaternion& inQuat2, double inTime) const + { + return angularVelocity(*this, inQuat2, inTime); + } + + //============================================================================ + // Method Description: + /// the axis of rotation described by the quaternion + /// + /// @return Vec3 + /// + [[nodiscard]] Vec3 axisOfRotation() const noexcept + { + const auto halfAngle = angleOfRotation() / 2.; + const auto sinHalfAngle = std::sin(halfAngle); + auto axis = Vec3(i() / sinHalfAngle, j() / sinHalfAngle, k() / sinHalfAngle); + + // shouldn't be necessary, but let's be pedantic + return axis.normalize(); + } + + //============================================================================ + // Method Description: + /// quaternion conjugate + /// + /// @return Quaternion + /// + [[nodiscard]] Quaternion conjugate() const noexcept + { + return { -i(), -j(), -k(), s() }; + } + + //============================================================================ + // Method Description: + /// returns the i component + /// + /// @return double + /// + [[nodiscard]] double i() const noexcept + { + return components_[0]; + } + + //============================================================================ + // Method Description: + /// quaternion identity (0,0,0,1) + /// + /// @return Quaternion + /// + static Quaternion identity() noexcept + { + return {}; + } + + //============================================================================ + // Method Description: + /// quaternion inverse + /// + /// @return Quaterion + /// + [[nodiscard]] Quaternion inverse() const noexcept + { + /// for unit quaternions the inverse is equal to the conjugate + return conjugate(); + } + + //============================================================================ + // Method Description: + /// returns the j component + /// + /// @return double + /// + [[nodiscard]] double j() const noexcept + { + return components_[1]; + } + + //============================================================================ + // Method Description: + /// returns the k component + /// + /// @return double + /// + [[nodiscard]] double k() const noexcept + { + return components_[2]; + } + + //============================================================================ + // Method Description: + /// linearly interpolates between the two quaternions + /// + /// @param inQuat1 + /// @param inQuat2 + /// @param inPercent [0, 1] + /// @return Quaternion + /// + static Quaternion nlerp(const Quaternion& inQuat1, const Quaternion& inQuat2, double inPercent) + { + if (inPercent < 0. || inPercent > 1.) { + THROW_INVALID_ARGUMENT_ERROR("input percent must be of the range [0,1]."); } - //============================================================================ - // Method Description: - /// the angle of rotation around the rotation axis that is described by the quaternion - /// - /// @return radians - /// - double angleOfRotation() const noexcept + if (utils::essentiallyEqual(inPercent, 0.)) { - return 2. * std::acos(s()); + return inQuat1; } - - //============================================================================ - // Method Description: - /// angular velocity vector between the two quaternions. The norm - /// of the array is the magnitude - /// - /// @param inQuat1 - /// @param inQuat2 - /// @param inTime (seperation time) - /// @return NdArray - /// - static NdArray angularVelocity(const Quaternion& inQuat1, const Quaternion& inQuat2, double inTime) + if (utils::essentiallyEqual(inPercent, 1.)) { - NdArray q0 = inQuat1.toNdArray(); - NdArray q1 = inQuat2.toNdArray(); - - NdArray qDot = q1 - q0; - qDot /= inTime; - - NdArray eyeTimesScalar(3); - eyeTimesScalar.zeros(); - eyeTimesScalar(0, 0) = inQuat2.s(); - eyeTimesScalar(1, 1) = inQuat2.s(); - eyeTimesScalar(2, 2) = inQuat2.s(); - - NdArray epsilonHat = linalg::hat(inQuat2.i(), inQuat2.j(), inQuat2.k()); - NdArray q(4, 3); - q.put(Slice(0, 3), Slice(0, 3), eyeTimesScalar + epsilonHat); - q(3, 0) = -inQuat2.i(); - q(3, 1) = -inQuat2.j(); - q(3, 2) = -inQuat2.k(); - - NdArray omega = q.transpose().dot(qDot.transpose()); - return omega *= 2.; + return inQuat2; } - //============================================================================ - // Method Description: - /// angular velocity vector between the two quaternions. The norm - /// of the array is the magnitude - /// - /// @param inQuat2 - /// @param inTime (seperation time) - /// @return NdArray - /// - NdArray angularVelocity(const Quaternion& inQuat2, double inTime) const + const double oneMinus = 1. - inPercent; + std::array newComponents{}; + + stl_algorithms::transform(inQuat1.components_.begin(), + inQuat1.components_.end(), + inQuat2.components_.begin(), + newComponents.begin(), + [inPercent, oneMinus](double component1, double component2) -> double + { return oneMinus * component1 + inPercent * component2; }); + + return { newComponents }; + } + + //============================================================================ + // Method Description: + /// linearly interpolates between the two quaternions + /// + /// @param inQuat2 + /// @param inPercent (0, 1) + /// @return Quaternion + /// + [[nodiscard]] Quaternion nlerp(const Quaternion& inQuat2, double inPercent) const + { + return nlerp(*this, inQuat2, inPercent); + } + + //============================================================================ + // Method Description: + /// The euler pitch angle in radians + /// + /// @return euler pitch angle in radians + /// + [[nodiscard]] double pitch() const noexcept + { + return std::asin(2 * (s() * j() - k() * i())); + } + + //============================================================================ + // Method Description: + /// returns a quaternion to rotate about the pitch axis + /// + /// @param inAngle (radians) + /// @return Quaternion + /// + static Quaternion pitchRotation(double inAngle) noexcept + { + return { 0., inAngle, 0. }; + } + + //============================================================================ + // Method Description: + /// prints the Quaternion to the console + /// + void print() const + { + std::cout << *this; + } + + //============================================================================ + // Method Description: + /// The euler roll angle in radians + /// + /// @return euler roll angle in radians + /// + [[nodiscard]] double roll() const noexcept + { + return std::atan2(2. * (s() * i() + j() * k()), 1. - 2. * (utils::sqr(i()) + utils::sqr(j()))); + } + + //============================================================================ + // Method Description: + /// returns a quaternion to rotate about the roll axis + /// + /// @param inAngle (radians) + /// @return Quaternion + /// + static Quaternion rollRotation(double inAngle) noexcept + { + return { inAngle, 0., 0. }; + } + + //============================================================================ + // Method Description: + /// rotate a vector using the quaternion + /// + /// @param inVector (cartesian vector with x,y,z components) + /// @return NdArray (cartesian vector with x,y,z components) + /// + [[nodiscard]] NdArray rotate(const NdArray& inVector) const + { + if (inVector.size() != 3) { - return angularVelocity(*this, inQuat2, inTime); + THROW_INVALID_ARGUMENT_ERROR("input inVector must be a cartesion vector of length = 3."); } - //============================================================================ - // Method Description: - /// the axis of rotation described by the quaternion - /// - /// @return Vec3 - /// - Vec3 axisOfRotation() const noexcept + return *this * inVector; + } + + //============================================================================ + // Method Description: + /// rotate a vector using the quaternion + /// + /// @param inVec3 + /// @return Vec3 + /// + [[nodiscard]] Vec3 rotate(const Vec3& inVec3) const + { + return *this * inVec3; + } + + //============================================================================ + // Method Description: + /// returns the s component + /// + /// @return double + /// + [[nodiscard]] double s() const noexcept + { + return components_[3]; + } + + //============================================================================ + // Method Description: + /// spherical linear interpolates between the two quaternions + /// + /// @param inQuat1 + /// @param inQuat2 + /// @param inPercent (0, 1) + /// @return Quaternion + /// + static Quaternion slerp(const Quaternion& inQuat1, const Quaternion& inQuat2, double inPercent) + { + if (inPercent < 0 || inPercent > 1) { - const auto halfAngle = angleOfRotation() / 2.; - const auto sinHalfAngle = std::sin(halfAngle); - auto axis = Vec3(i() / sinHalfAngle, j() / sinHalfAngle, k() / sinHalfAngle); - - // shouldn't be necessary, but let's be pedantic - axis.normalize(); - - return axis; + THROW_INVALID_ARGUMENT_ERROR("input percent must be of the range [0, 1]"); } - //============================================================================ - // Method Description: - /// quaternion conjugate - /// - /// @return Quaternion - /// - Quaternion conjugate() const noexcept + if (inPercent == 0) { - return { -i(), -j(), -k(), s() }; + return inQuat1; } - - //============================================================================ - // Method Description: - /// returns the i component - /// - /// @return double - /// - double i() const noexcept + if (inPercent == 1) { - return components_[0]; + return inQuat2; } - //============================================================================ - // Method Description: - /// quaternion identity (0,0,0,1) - /// - /// @return Quaternion - /// - static Quaternion identity() noexcept - { - return {}; - } + double dotProduct = dot(inQuat1.toNdArray(), inQuat2.toNdArray()).item(); - //============================================================================ - // Method Description: - /// quaternion inverse - /// - /// @return Quaterion - /// - Quaternion inverse() const noexcept + // If the dot product is negative, the quaternions + // have opposite handed-ness and slerp won't take + // the shorter path. Fix by reversing one quaternion. + Quaternion quat1Copy(inQuat1); + if (dotProduct < 0.) { - /// for unit quaternions the inverse is equal to the conjugate - return conjugate(); + quat1Copy *= -1.; + dotProduct *= -1.; } - //============================================================================ - // Method Description: - /// returns the j component - /// - /// @return double - /// - double j() const noexcept + constexpr double DOT_THRESHOLD = 0.9995; + if (dotProduct > DOT_THRESHOLD) { - return components_[1]; + // If the inputs are too close for comfort, linearly interpolate + // and normalize the result. + return nlerp(inQuat1, inQuat2, inPercent); } - //============================================================================ - // Method Description: - /// returns the k component - /// - /// @return double - /// - double k() const noexcept + dotProduct = clip(dotProduct, -1., 1.); // Robustness: Stay within domain of acos() + const double theta0 = std::acos(dotProduct); // angle between input vectors + const double theta = theta0 * inPercent; // angle between v0 and result + + const double s0 = std::cos(theta) - + dotProduct * std::sin(theta) / std::sin(theta0); // == sin(theta_0 - theta) / sin(theta_0) + const double s1 = std::sin(theta) / std::sin(theta0); + + NdArray interpQuat = (quat1Copy.toNdArray() * s0) + (inQuat2.toNdArray() * s1); + return Quaternion(interpQuat); + } + + //============================================================================ + // Method Description: + /// spherical linear interpolates between the two quaternions + /// + /// @param inQuat2 + /// @param inPercent (0, 1) + /// @return Quaternion + /// + [[nodiscard]] Quaternion slerp(const Quaternion& inQuat2, double inPercent) const + { + return slerp(*this, inQuat2, inPercent); + } + + //============================================================================ + // Method Description: + /// returns the quaternion as a string representation + /// + /// @return std::string + /// + [[nodiscard]] std::string str() const + { + std::string output = "[" + utils::num2str(i()) + ", " + utils::num2str(j()) + ", " + utils::num2str(k()) + + ", " + utils::num2str(s()) + "]\n"; + + return output; + } + + //============================================================================ + // Method Description: + /// returns the direction cosine matrix + /// + /// @return NdArray + /// + [[nodiscard]] NdArray toDCM() const + { + NdArray dcm(3); + + const double q0 = i(); + const double q1 = j(); + const double q2 = k(); + const double q3 = s(); + + const double q0sqr = utils::sqr(q0); + const double q1sqr = utils::sqr(q1); + const double q2sqr = utils::sqr(q2); + const double q3sqr = utils::sqr(q3); + + dcm(0, 0) = q3sqr + q0sqr - q1sqr - q2sqr; + dcm(0, 1) = 2. * (q0 * q1 - q3 * q2); + dcm(0, 2) = 2. * (q0 * q2 + q3 * q1); + dcm(1, 0) = 2. * (q0 * q1 + q3 * q2); + dcm(1, 1) = q3sqr + q1sqr - q0sqr - q2sqr; + dcm(1, 2) = 2. * (q1 * q2 - q3 * q0); + dcm(2, 0) = 2. * (q0 * q2 - q3 * q1); + dcm(2, 1) = 2. * (q1 * q2 + q3 * q0); + dcm(2, 2) = q3sqr + q2sqr - q0sqr - q1sqr; + + return dcm; + } + + //============================================================================ + // Method Description: + /// returns the quaternion as an NdArray + /// + /// @return NdArray + /// + [[nodiscard]] NdArray toNdArray() const + { + auto componentsCopy = components_; + return NdArray(componentsCopy); + } + + //============================================================================ + // Method Description: + /// returns a quaternion to rotate about the x-axis by the input angle + /// + /// @param inAngle (radians) + /// @return Quaternion + /// + static Quaternion xRotation(double inAngle) noexcept + { + const Vec3 eulerAxis = { 1., 0., 0. }; + return Quaternion(eulerAxis, inAngle); + } + + //============================================================================ + // Method Description: + /// The euler yaw angle in radians + /// + /// @return euler yaw angle in radians + /// + [[nodiscard]] double yaw() const noexcept + { + return std::atan2(2. * (s() * k() + i() * j()), 1. - 2. * (utils::sqr(j()) + utils::sqr(k()))); + } + + //============================================================================ + // Method Description: + /// returns a quaternion to rotate about the yaw axis + /// + /// @param inAngle (radians) + /// @return Quaternion + /// + static Quaternion yawRotation(double inAngle) noexcept + { + return { 0., 0., inAngle }; + } + + //============================================================================ + // Method Description: + /// returns a quaternion to rotate about the y-axis by the input angle + /// + /// @param inAngle (radians) + /// @return Quaternion + /// + static Quaternion yRotation(double inAngle) noexcept + { + const Vec3 eulerAxis = { 0., 1., 0. }; + return Quaternion(eulerAxis, inAngle); + } + + //============================================================================ + // Method Description: + /// returns a quaternion to rotate about the y-axis by the input angle + /// + /// @param inAngle (radians) + /// @return Quaternion + /// + static Quaternion zRotation(double inAngle) noexcept + { + const Vec3 eulerAxis = { 0., 0., 1. }; + return Quaternion(eulerAxis, inAngle); + } + + //============================================================================ + // Method Description: + /// equality operator + /// + /// @param inRhs + /// @return bool + /// + bool operator==(const Quaternion& inRhs) const noexcept + { + const auto comparitor = [](double value1, double value2) noexcept -> bool + { return utils::essentiallyEqual(value1, value2); }; + + return stl_algorithms::equal(components_.begin(), components_.end(), inRhs.components_.begin(), comparitor); + } + + //============================================================================ + // Method Description: + /// equality operator + /// + /// @param inRhs + /// @return bool + /// + bool operator!=(const Quaternion& inRhs) const noexcept + { + return !(*this == inRhs); + } + + //============================================================================ + // Method Description: + /// addition assignment operator + /// + /// @param inRhs + /// @return Quaternion + /// + Quaternion& operator+=(const Quaternion& inRhs) noexcept + { + stl_algorithms::transform(components_.begin(), + components_.end(), + inRhs.components_.begin(), + components_.begin(), + std::plus()); + + normalize(); + + return *this; + } + + //============================================================================ + // Method Description: + /// addition operator + /// + /// @param inRhs + /// @return Quaternion + /// + Quaternion operator+(const Quaternion& inRhs) const noexcept + { + return Quaternion(*this) += inRhs; + } + + //============================================================================ + // Method Description: + /// subtraction assignment operator + /// + /// @param inRhs + /// @return Quaternion + /// + Quaternion& operator-=(const Quaternion& inRhs) noexcept + { + stl_algorithms::transform(components_.begin(), + components_.end(), + inRhs.components_.begin(), + components_.begin(), + std::minus()); + + normalize(); + + return *this; + } + + //============================================================================ + // Method Description: + /// subtraction operator + /// + /// @param inRhs + /// @return Quaternion + /// + Quaternion operator-(const Quaternion& inRhs) const noexcept + { + return Quaternion(*this) -= inRhs; + } + + //============================================================================ + // Method Description: + /// negative operator + /// + /// @return Quaternion + /// + Quaternion operator-() const noexcept + { + return Quaternion(*this) *= -1.; + } + + //============================================================================ + // Method Description: + /// multiplication assignment operator + /// + /// @param inRhs + /// @return Quaternion + /// + Quaternion& operator*=(const Quaternion& inRhs) noexcept + { + double q0 = inRhs.s() * i(); + q0 += inRhs.i() * s(); + q0 -= inRhs.j() * k(); + q0 += inRhs.k() * j(); + + double q1 = inRhs.s() * j(); + q1 += inRhs.i() * k(); + q1 += inRhs.j() * s(); + q1 -= inRhs.k() * i(); + + double q2 = inRhs.s() * k(); + q2 -= inRhs.i() * j(); + q2 += inRhs.j() * i(); + q2 += inRhs.k() * s(); + + double q3 = inRhs.s() * s(); + q3 -= inRhs.i() * i(); + q3 -= inRhs.j() * j(); + q3 -= inRhs.k() * k(); + + components_[0] = q0; + components_[1] = q1; + components_[2] = q2; + components_[3] = q3; + + normalize(); + + return *this; + } + + //============================================================================ + // Method Description: + /// multiplication operator, only useful for multiplying + /// by negative 1, all others will be renormalized back out + /// + /// @param inScalar + /// @return Quaternion + /// + Quaternion& operator*=(double inScalar) noexcept + { + stl_algorithms::for_each(components_.begin(), + components_.end(), + [inScalar](double& component) { component *= inScalar; }); + + normalize(); + + return *this; + } + + //============================================================================ + // Method Description: + /// multiplication operator + /// + /// @param inRhs + /// @return Quaternion + /// + Quaternion operator*(const Quaternion& inRhs) const noexcept + { + return Quaternion(*this) *= inRhs; + } + + //============================================================================ + // Method Description: + /// multiplication operator, only useful for multiplying + /// by negative 1, all others will be renormalized back out + /// + /// @param inScalar + /// @return Quaternion + /// + Quaternion operator*(double inScalar) const noexcept + { + return Quaternion(*this) *= inScalar; + } + + //============================================================================ + // Method Description: + /// multiplication operator + /// + /// @param inVec + /// @return NdArray + /// + NdArray operator*(const NdArray& inVec) const + { + if (inVec.size() != 3) { - return components_[2]; + THROW_INVALID_ARGUMENT_ERROR("input vector must be a cartesion vector of length = 3."); } - //============================================================================ - // Method Description: - /// linearly interpolates between the two quaternions - /// - /// @param inQuat1 - /// @param inQuat2 - /// @param inPercent [0, 1] - /// @return Quaternion - /// - static Quaternion nlerp(const Quaternion& inQuat1, const Quaternion& inQuat2, double inPercent) - { - if (inPercent < 0. || inPercent > 1.) - { - THROW_INVALID_ARGUMENT_ERROR("input percent must be of the range [0,1]."); - } + const auto p = Quaternion(inVec[0], inVec[1], inVec[2], 0.); + const auto pPrime = *this * p * this->inverse(); + + NdArray rotatedVec = { pPrime.i(), pPrime.j(), pPrime.k() }; + rotatedVec *= norm(inVec).item(); + return rotatedVec; + } + + //============================================================================ + // Method Description: + /// multiplication operator + /// + /// @param inVec3 + /// @return Vec3 + /// + Vec3 operator*(const Vec3& inVec3) const + { + return *this * inVec3.toNdArray(); + } + + //============================================================================ + // Method Description: + /// division assignment operator + /// + /// @param inRhs + /// @return Quaternion + /// + Quaternion& operator/=(const Quaternion& inRhs) noexcept + { + return *this *= inRhs.conjugate(); + } + + //============================================================================ + // Method Description: + /// division operator + /// + /// @param inRhs + /// @return Quaternion + /// + Quaternion operator/(const Quaternion& inRhs) const noexcept + { + return Quaternion(*this) /= inRhs; + } + + //============================================================================ + // Method Description: + /// IO operator for the Quaternion class + /// + /// @param inOStream + /// @param inQuat + /// @return std::ostream& + /// + friend std::ostream& operator<<(std::ostream& inOStream, const Quaternion& inQuat) + { + inOStream << inQuat.str(); + return inOStream; + } + + private: + //====================================Attributes============================== + std::array components_{ { 0., 0., 0., 1. } }; + + //============================================================================ + // Method Description: + /// renormalizes the quaternion + /// + void normalize() noexcept + { + double sumOfSquares = 0.; + std::for_each(components_.begin(), + components_.end(), + [&sumOfSquares](double component) noexcept -> void + { sumOfSquares += utils::sqr(component); }); + + const double norm = std::sqrt(sumOfSquares); + stl_algorithms::for_each(components_.begin(), + components_.end(), + [norm](double& component) noexcept -> void { component /= norm; }); + } + + //============================================================================ + // Method Description: + /// Converts the euler roll, pitch, yaw angles to quaternion components + /// + /// @ param roll: the euler roll angle in radians + /// @ param pitch: the euler pitch angle in radians + /// @ param yaw: the euler yaw angle in radians + /// + void eulerToQuat(double roll, double pitch, double yaw) noexcept + { + const auto halfPhi = roll / 2.; + const auto halfTheta = pitch / 2.; + const auto halfPsi = yaw / 2.; - if (utils::essentiallyEqual(inPercent, 0.)) - { - return inQuat1; - } - if (utils::essentiallyEqual(inPercent, 1.)) - { - return inQuat2; - } + const auto sinHalfPhi = std::sin(halfPhi); + const auto cosHalfPhi = std::cos(halfPhi); - const double oneMinus = 1. - inPercent; - std::array newComponents{}; + const auto sinHalfTheta = std::sin(halfTheta); + const auto cosHalfTheta = std::cos(halfTheta); - stl_algorithms::transform(inQuat1.components_.begin(), - inQuat1.components_.end(), - inQuat2.components_.begin(), - newComponents.begin(), - [inPercent, oneMinus](double component1, double component2) -> double - { return oneMinus * component1 + inPercent * component2; }); + const auto sinHalfPsi = std::sin(halfPsi); + const auto cosHalfPsi = std::cos(halfPsi); - return { newComponents }; - } + components_[0] = sinHalfPhi * cosHalfTheta * cosHalfPsi; + components_[0] -= cosHalfPhi * sinHalfTheta * sinHalfPsi; - //============================================================================ - // Method Description: - /// linearly interpolates between the two quaternions - /// - /// @param inQuat2 - /// @param inPercent (0, 1) - /// @return Quaternion - /// - Quaternion nlerp(const Quaternion& inQuat2, double inPercent) const - { - return nlerp(*this, inQuat2, inPercent); - } + components_[1] = cosHalfPhi * sinHalfTheta * cosHalfPsi; + components_[1] += sinHalfPhi * cosHalfTheta * sinHalfPsi; - //============================================================================ - // Method Description: - /// The euler pitch angle in radians - /// - /// @return euler pitch angle in radians - /// - double pitch() const noexcept - { - return std::asin(2 * (s() * j() - k() * i())); - } - - //============================================================================ - // Method Description: - /// returns a quaternion to rotate about the pitch axis - /// - /// @param inAngle (radians) - /// @return Quaternion - /// - static Quaternion pitchRotation(double inAngle) noexcept - { - return Quaternion(0., inAngle, 0.); - } - - //============================================================================ - // Method Description: - /// prints the Quaternion to the console - /// - void print() const - { - std::cout << *this; - } + components_[2] = cosHalfPhi * cosHalfTheta * sinHalfPsi; + components_[2] -= sinHalfPhi * sinHalfTheta * cosHalfPsi; - //============================================================================ - // Method Description: - /// The euler roll angle in radians - /// - /// @return euler roll angle in radians - /// - double roll() const noexcept - { - return std::atan2(2. * (s() * i() + j() * k()), 1. - 2. * (utils::sqr(i()) + utils::sqr(j()))); - } + components_[3] = cosHalfPhi * cosHalfTheta * cosHalfPsi; + components_[3] += sinHalfPhi * sinHalfTheta * sinHalfPsi; + } - //============================================================================ - // Method Description: - /// returns a quaternion to rotate about the roll axis - /// - /// @param inAngle (radians) - /// @return Quaternion - /// - static Quaternion rollRotation(double inAngle) noexcept - { - return Quaternion(inAngle, 0., 0.); - } - - //============================================================================ - // Method Description: - /// rotate a vector using the quaternion - /// - /// @param inVector (cartesian vector with x,y,z components) - /// @return NdArray (cartesian vector with x,y,z components) - /// - NdArray rotate(const NdArray& inVector) const + //============================================================================ + // Method Description: + /// Converts the direction cosine matrix to quaternion components + /// + /// @ param dcm: the direction cosine matrix + /// + void dcmToQuat(const NdArray& dcm) + { + const Shape inShape = dcm.shape(); + if (!(inShape.rows == 3 && inShape.cols == 3)) { - if (inVector.size() != 3) - { - THROW_INVALID_ARGUMENT_ERROR("input inVector must be a cartesion vector of length = 3."); - } - - return *this * inVector; + THROW_INVALID_ARGUMENT_ERROR("input direction cosine matrix must have shape = (3,3)."); } - //============================================================================ - // Method Description: - /// rotate a vector using the quaternion - /// - /// @param inVec3 - /// @return Vec3 - /// - Vec3 rotate(const Vec3& inVec3) const - { - return *this * inVec3; - } + NdArray checks(1, 4); + checks[0] = 1 + dcm(0, 0) + dcm(1, 1) + dcm(2, 2); + checks[1] = 1 + dcm(0, 0) - dcm(1, 1) - dcm(2, 2); + checks[2] = 1 - dcm(0, 0) + dcm(1, 1) - dcm(2, 2); + checks[3] = 1 - dcm(0, 0) - dcm(1, 1) + dcm(2, 2); - //============================================================================ - // Method Description: - /// returns the s component - /// - /// @return double - /// - double s() const noexcept - { - return components_[3]; - } + const uint32 maxIdx = argmax(checks).item(); - //============================================================================ - // Method Description: - /// spherical linear interpolates between the two quaternions - /// - /// @param inQuat1 - /// @param inQuat2 - /// @param inPercent (0, 1) - /// @return Quaternion - /// - static Quaternion slerp(const Quaternion& inQuat1, const Quaternion& inQuat2, double inPercent) + switch (maxIdx) { - if (inPercent < 0 || inPercent > 1) + case 0: { - THROW_INVALID_ARGUMENT_ERROR("input percent must be of the range [0, 1]"); - } + components_[3] = 0.5 * std::sqrt(1 + dcm(0, 0) + dcm(1, 1) + dcm(2, 2)); + components_[0] = (dcm(2, 1) - dcm(1, 2)) / (4 * components_[3]); + components_[1] = (dcm(0, 2) - dcm(2, 0)) / (4 * components_[3]); + components_[2] = (dcm(1, 0) - dcm(0, 1)) / (4 * components_[3]); - if (inPercent == 0) - { - return inQuat1; + break; } - if (inPercent == 1) + case 1: { - return inQuat2; - } - - double dotProduct = dot(inQuat1.toNdArray(), inQuat2.toNdArray()).item(); + components_[0] = 0.5 * std::sqrt(1 + dcm(0, 0) - dcm(1, 1) - dcm(2, 2)); + components_[1] = (dcm(1, 0) + dcm(0, 1)) / (4 * components_[0]); + components_[2] = (dcm(2, 0) + dcm(0, 2)) / (4 * components_[0]); + components_[3] = (dcm(2, 1) - dcm(1, 2)) / (4 * components_[0]); - // If the dot product is negative, the quaternions - // have opposite handed-ness and slerp won't take - // the shorter path. Fix by reversing one quaternion. - Quaternion quat1Copy(inQuat1); - if (dotProduct < 0.) - { - quat1Copy *= -1.; - dotProduct *= -1.; + break; } - - constexpr double DOT_THRESHOLD = 0.9995; - if (dotProduct > DOT_THRESHOLD) + case 2: { - // If the inputs are too close for comfort, linearly interpolate - // and normalize the result. - return nlerp(inQuat1, inQuat2, inPercent); - } + components_[1] = 0.5 * std::sqrt(1 - dcm(0, 0) + dcm(1, 1) - dcm(2, 2)); + components_[0] = (dcm(1, 0) + dcm(0, 1)) / (4 * components_[1]); + components_[2] = (dcm(2, 1) + dcm(1, 2)) / (4 * components_[1]); + components_[3] = (dcm(0, 2) - dcm(2, 0)) / (4 * components_[1]); - dotProduct = clip(dotProduct, -1., 1.); // Robustness: Stay within domain of acos() - const double theta0 = std::acos(dotProduct); // angle between input vectors - const double theta = theta0 * inPercent; // angle between v0 and result - - const double s0 = std::cos(theta) - dotProduct * std::sin(theta) / - std::sin(theta0); // == sin(theta_0 - theta) / sin(theta_0) - const double s1 = std::sin(theta) / std::sin(theta0); - - NdArray interpQuat = (quat1Copy.toNdArray() * s0) + (inQuat2.toNdArray() * s1); - return Quaternion(interpQuat); - } - - //============================================================================ - // Method Description: - /// spherical linear interpolates between the two quaternions - /// - /// @param inQuat2 - /// @param inPercent (0, 1) - /// @return Quaternion - /// - Quaternion slerp(const Quaternion& inQuat2, double inPercent) const - { - return slerp(*this, inQuat2, inPercent); - } - - //============================================================================ - // Method Description: - /// returns the quaternion as a string representation - /// - /// @return std::string - /// - std::string str() const - { - std::string output = "[" + utils::num2str(i()) + ", " + utils::num2str(j()) + ", " + - utils::num2str(k()) + ", " + utils::num2str(s()) + "]\n"; - - return output; - } - - //============================================================================ - // Method Description: - /// returns the direction cosine matrix - /// - /// @return NdArray - /// - NdArray toDCM() const - { - NdArray dcm(3); - - const double q0 = i(); - const double q1 = j(); - const double q2 = k(); - const double q3 = s(); - - const double q0sqr = utils::sqr(q0); - const double q1sqr = utils::sqr(q1); - const double q2sqr = utils::sqr(q2); - const double q3sqr = utils::sqr(q3); - - dcm(0, 0) = q3sqr + q0sqr - q1sqr - q2sqr; - dcm(0, 1) = 2. * (q0 * q1 - q3 * q2); - dcm(0, 2) = 2. * (q0 * q2 + q3 * q1); - dcm(1, 0) = 2. * (q0 * q1 + q3 * q2); - dcm(1, 1) = q3sqr + q1sqr - q0sqr - q2sqr; - dcm(1, 2) = 2. * (q1 * q2 - q3 * q0); - dcm(2, 0) = 2. * (q0 * q2 - q3 * q1); - dcm(2, 1) = 2. * (q1 * q2 + q3 * q0); - dcm(2, 2) = q3sqr + q2sqr - q0sqr - q1sqr; - - return dcm; - } - - //============================================================================ - // Method Description: - /// returns the quaternion as an NdArray - /// - /// @return NdArray - /// - NdArray toNdArray() const - { - auto componentsCopy = components_; - return NdArray(componentsCopy); - } - - //============================================================================ - // Method Description: - /// returns a quaternion to rotate about the x-axis by the input angle - /// - /// @param inAngle (radians) - /// @return Quaternion - /// - static Quaternion xRotation(double inAngle) noexcept - { - const Vec3 eulerAxis = { 1., 0., 0. }; - return Quaternion(eulerAxis, inAngle); - } - - //============================================================================ - // Method Description: - /// The euler yaw angle in radians - /// - /// @return euler yaw angle in radians - /// - double yaw() const noexcept - { - return std::atan2(2. * (s() * k() + i() * j()), 1. - 2. * (utils::sqr(j()) + utils::sqr(k()))); - } - - //============================================================================ - // Method Description: - /// returns a quaternion to rotate about the yaw axis - /// - /// @param inAngle (radians) - /// @return Quaternion - /// - static Quaternion yawRotation(double inAngle) noexcept - { - return Quaternion(0., 0., inAngle); - } - - //============================================================================ - // Method Description: - /// returns a quaternion to rotate about the y-axis by the input angle - /// - /// @param inAngle (radians) - /// @return Quaternion - /// - static Quaternion yRotation(double inAngle) noexcept - { - const Vec3 eulerAxis = { 0., 1., 0. }; - return Quaternion(eulerAxis, inAngle); - } - - //============================================================================ - // Method Description: - /// returns a quaternion to rotate about the y-axis by the input angle - /// - /// @param inAngle (radians) - /// @return Quaternion - /// - static Quaternion zRotation(double inAngle) noexcept - { - const Vec3 eulerAxis = { 0., 0., 1. }; - return Quaternion(eulerAxis, inAngle); - } - - //============================================================================ - // Method Description: - /// equality operator - /// - /// @param inRhs - /// @return bool - /// - bool operator==(const Quaternion& inRhs) const noexcept - { - const auto comparitor = [](double value1, double value2) noexcept -> bool - { return utils::essentiallyEqual(value1, value2); }; - - return stl_algorithms::equal(components_.begin(), - components_.end(), - inRhs.components_.begin(), - comparitor); - } - - //============================================================================ - // Method Description: - /// equality operator - /// - /// @param inRhs - /// @return bool - /// - bool operator!=(const Quaternion& inRhs) const noexcept - { - return !(*this == inRhs); - } - - //============================================================================ - // Method Description: - /// addition assignment operator - /// - /// @param inRhs - /// @return Quaternion - /// - Quaternion& operator+=(const Quaternion& inRhs) noexcept - { - stl_algorithms::transform(components_.begin(), - components_.end(), - inRhs.components_.begin(), - components_.begin(), - std::plus()); - - normalize(); - - return *this; - } - - //============================================================================ - // Method Description: - /// addition operator - /// - /// @param inRhs - /// @return Quaternion - /// - Quaternion operator+(const Quaternion& inRhs) const noexcept - { - return Quaternion(*this) += inRhs; - } - - //============================================================================ - // Method Description: - /// subtraction assignment operator - /// - /// @param inRhs - /// @return Quaternion - /// - Quaternion& operator-=(const Quaternion& inRhs) noexcept - { - stl_algorithms::transform(components_.begin(), - components_.end(), - inRhs.components_.begin(), - components_.begin(), - std::minus()); - - normalize(); - - return *this; - } - - //============================================================================ - // Method Description: - /// subtraction operator - /// - /// @param inRhs - /// @return Quaternion - /// - Quaternion operator-(const Quaternion& inRhs) const noexcept - { - return Quaternion(*this) -= inRhs; - } - - //============================================================================ - // Method Description: - /// negative operator - /// - /// @return Quaternion - /// - Quaternion operator-() const noexcept - { - return Quaternion(*this) *= -1.; - } - - //============================================================================ - // Method Description: - /// multiplication assignment operator - /// - /// @param inRhs - /// @return Quaternion - /// - Quaternion& operator*=(const Quaternion& inRhs) noexcept - { - double q0 = inRhs.s() * i(); - q0 += inRhs.i() * s(); - q0 -= inRhs.j() * k(); - q0 += inRhs.k() * j(); - - double q1 = inRhs.s() * j(); - q1 += inRhs.i() * k(); - q1 += inRhs.j() * s(); - q1 -= inRhs.k() * i(); - - double q2 = inRhs.s() * k(); - q2 -= inRhs.i() * j(); - q2 += inRhs.j() * i(); - q2 += inRhs.k() * s(); - - double q3 = inRhs.s() * s(); - q3 -= inRhs.i() * i(); - q3 -= inRhs.j() * j(); - q3 -= inRhs.k() * k(); - - components_[0] = q0; - components_[1] = q1; - components_[2] = q2; - components_[3] = q3; - - normalize(); - - return *this; - } - - //============================================================================ - // Method Description: - /// multiplication operator, only useful for multiplying - /// by negative 1, all others will be renormalized back out - /// - /// @param inScalar - /// @return Quaternion - /// - Quaternion& operator*=(double inScalar) noexcept - { - stl_algorithms::for_each(components_.begin(), - components_.end(), - [inScalar](double& component) { component *= inScalar; }); - - normalize(); - - return *this; - } - - //============================================================================ - // Method Description: - /// multiplication operator - /// - /// @param inRhs - /// @return Quaternion - /// - Quaternion operator*(const Quaternion& inRhs) const noexcept - { - return Quaternion(*this) *= inRhs; - } - - //============================================================================ - // Method Description: - /// multiplication operator, only useful for multiplying - /// by negative 1, all others will be renormalized back out - /// - /// @param inScalar - /// @return Quaternion - /// - Quaternion operator*(double inScalar) const noexcept - { - return Quaternion(*this) *= inScalar; - } - - //============================================================================ - // Method Description: - /// multiplication operator - /// - /// @param inVec - /// @return NdArray - /// - NdArray operator*(const NdArray& inVec) const - { - if (inVec.size() != 3) - { - THROW_INVALID_ARGUMENT_ERROR("input vector must be a cartesion vector of length = 3."); + break; } - - const auto p = Quaternion(inVec[0], inVec[1], inVec[2], 0.); - const auto pPrime = *this * p * this->inverse(); - - NdArray rotatedVec = { pPrime.i(), pPrime.j(), pPrime.k() }; - rotatedVec *= norm(inVec).item(); - return rotatedVec; - } - - //============================================================================ - // Method Description: - /// multiplication operator - /// - /// @param inVec3 - /// @return Vec3 - /// - Vec3 operator*(const Vec3& inVec3) const - { - return *this * inVec3.toNdArray(); - } - - //============================================================================ - // Method Description: - /// division assignment operator - /// - /// @param inRhs - /// @return Quaternion - /// - Quaternion& operator/=(const Quaternion& inRhs) noexcept - { - return *this *= inRhs.conjugate(); - } - - //============================================================================ - // Method Description: - /// division operator - /// - /// @param inRhs - /// @return Quaternion - /// - Quaternion operator/(const Quaternion& inRhs) const noexcept - { - return Quaternion(*this) /= inRhs; - } - - //============================================================================ - // Method Description: - /// IO operator for the Quaternion class - /// - /// @param inOStream - /// @param inQuat - /// @return std::ostream& - /// - friend std::ostream& operator<<(std::ostream& inOStream, const Quaternion& inQuat) - { - inOStream << inQuat.str(); - return inOStream; - } - - private: - //====================================Attributes============================== - std::array components_{ { 0., 0., 0., 1. } }; - - //============================================================================ - // Method Description: - /// renormalizes the quaternion - /// - void normalize() noexcept - { - double sumOfSquares = 0.; - std::for_each(components_.begin(), - components_.end(), - [&sumOfSquares](double component) noexcept -> void - { sumOfSquares += utils::sqr(component); }); - - const double norm = std::sqrt(sumOfSquares); - stl_algorithms::for_each(components_.begin(), - components_.end(), - [norm](double& component) noexcept -> void { component /= norm; }); - } - - //============================================================================ - // Method Description: - /// Converts the euler roll, pitch, yaw angles to quaternion components - /// - /// @ param roll: the euler roll angle in radians - /// @ param pitch: the euler pitch angle in radians - /// @ param yaw: the euler yaw angle in radians - /// - void eulerToQuat(double roll, double pitch, double yaw) noexcept - { - const auto halfPhi = roll / 2.; - const auto halfTheta = pitch / 2.; - const auto halfPsi = yaw / 2.; - - const auto sinHalfPhi = std::sin(halfPhi); - const auto cosHalfPhi = std::cos(halfPhi); - - const auto sinHalfTheta = std::sin(halfTheta); - const auto cosHalfTheta = std::cos(halfTheta); - - const auto sinHalfPsi = std::sin(halfPsi); - const auto cosHalfPsi = std::cos(halfPsi); - - components_[0] = sinHalfPhi * cosHalfTheta * cosHalfPsi; - components_[0] -= cosHalfPhi * sinHalfTheta * sinHalfPsi; - - components_[1] = cosHalfPhi * sinHalfTheta * cosHalfPsi; - components_[1] += sinHalfPhi * cosHalfTheta * sinHalfPsi; - - components_[2] = cosHalfPhi * cosHalfTheta * sinHalfPsi; - components_[2] -= sinHalfPhi * sinHalfTheta * cosHalfPsi; - - components_[3] = cosHalfPhi * cosHalfTheta * cosHalfPsi; - components_[3] += sinHalfPhi * sinHalfTheta * sinHalfPsi; - } - - //============================================================================ - // Method Description: - /// Converts the direction cosine matrix to quaternion components - /// - /// @ param dcm: the direction cosine matrix - /// - void dcmToQuat(const NdArray& dcm) - { - const Shape inShape = dcm.shape(); - if (!(inShape.rows == 3 && inShape.cols == 3)) + case 3: { - THROW_INVALID_ARGUMENT_ERROR("input direction cosine matrix must have shape = (3,3)."); - } + components_[2] = 0.5 * std::sqrt(1 - dcm(0, 0) - dcm(1, 1) + dcm(2, 2)); + components_[0] = (dcm(2, 0) + dcm(0, 2)) / (4 * components_[2]); + components_[1] = (dcm(2, 1) + dcm(1, 2)) / (4 * components_[2]); + components_[3] = (dcm(1, 0) - dcm(0, 1)) / (4 * components_[2]); - NdArray checks(1, 4); - checks[0] = 1 + dcm(0, 0) + dcm(1, 1) + dcm(2, 2); - checks[1] = 1 + dcm(0, 0) - dcm(1, 1) - dcm(2, 2); - checks[2] = 1 - dcm(0, 0) + dcm(1, 1) - dcm(2, 2); - checks[3] = 1 - dcm(0, 0) - dcm(1, 1) + dcm(2, 2); - - const uint32 maxIdx = argmax(checks).item(); - - switch (maxIdx) - { - case 0: - { - components_[3] = 0.5 * std::sqrt(1 + dcm(0, 0) + dcm(1, 1) + dcm(2, 2)); - components_[0] = (dcm(2, 1) - dcm(1, 2)) / (4 * components_[3]); - components_[1] = (dcm(0, 2) - dcm(2, 0)) / (4 * components_[3]); - components_[2] = (dcm(1, 0) - dcm(0, 1)) / (4 * components_[3]); - - break; - } - case 1: - { - components_[0] = 0.5 * std::sqrt(1 + dcm(0, 0) - dcm(1, 1) - dcm(2, 2)); - components_[1] = (dcm(1, 0) + dcm(0, 1)) / (4 * components_[0]); - components_[2] = (dcm(2, 0) + dcm(0, 2)) / (4 * components_[0]); - components_[3] = (dcm(2, 1) - dcm(1, 2)) / (4 * components_[0]); - - break; - } - case 2: - { - components_[1] = 0.5 * std::sqrt(1 - dcm(0, 0) + dcm(1, 1) - dcm(2, 2)); - components_[0] = (dcm(1, 0) + dcm(0, 1)) / (4 * components_[1]); - components_[2] = (dcm(2, 1) + dcm(1, 2)) / (4 * components_[1]); - components_[3] = (dcm(0, 2) - dcm(2, 0)) / (4 * components_[1]); - - break; - } - case 3: - { - components_[2] = 0.5 * std::sqrt(1 - dcm(0, 0) - dcm(1, 1) + dcm(2, 2)); - components_[0] = (dcm(2, 0) + dcm(0, 2)) / (4 * components_[2]); - components_[1] = (dcm(2, 1) + dcm(1, 2)) / (4 * components_[2]); - components_[3] = (dcm(1, 0) - dcm(0, 1)) / (4 * components_[2]); - - break; - } + break; } } - }; - } // namespace rotations -} // namespace nc + } + }; +} // namespace nc::rotations diff --git a/include/NumCpp/Rotations/rodriguesRotation.hpp b/include/NumCpp/Rotations/rodriguesRotation.hpp index 954541708..b6ab6dd31 100644 --- a/include/NumCpp/Rotations/rodriguesRotation.hpp +++ b/include/NumCpp/Rotations/rodriguesRotation.hpp @@ -33,55 +33,52 @@ #include "NumCpp/NdArray.hpp" #include "NumCpp/Vector/Vec3.hpp" -namespace nc +namespace nc::rotations { - namespace rotations + //============================================================================ + // Method Description: + /// Performs Rodriques' rotation formula + /// https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula + /// + /// @param k: the axis to rotate around + /// @param theta: the angle in radians to rotate + /// @param v: the vector to rotate + /// + /// @return Vec3 + /// + inline Vec3 rodriguesRotation(const Vec3& k, double theta, const Vec3& v) noexcept { - //============================================================================ - // Method Description: - /// Performs Rodriques' rotation formula - /// https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula - /// - /// @param k: the axis to rotate around - /// @param theta: the angle in radians to rotate - /// @param v: the vector to rotate - /// - /// @return Vec3 - /// - inline Vec3 rodriguesRotation(const Vec3& k, double theta, const Vec3& v) noexcept - { - const auto kUnit = k.normalize(); + const auto kUnit = k.normalize(); - const auto vCosTheta = v * std::cos(theta); + const auto vCosTheta = v * std::cos(theta); - auto kCrossV = kUnit.cross(v); - kCrossV *= std::sin(theta); + auto kCrossV = kUnit.cross(v); + kCrossV *= std::sin(theta); - const auto kDotV = kUnit.dot(v); - auto kkDotV = kUnit * kDotV; - kkDotV *= 1 - std::cos(theta); + const auto kDotV = kUnit.dot(v); + auto kkDotV = kUnit * kDotV; + kkDotV *= 1 - std::cos(theta); - auto vec = vCosTheta + kCrossV; - vec += kkDotV; + auto vec = vCosTheta + kCrossV; + vec += kkDotV; - return vec; - } + return vec; + } - //============================================================================ - // Method Description: - /// Performs Rodriques' rotation formula - /// https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula - /// - /// @param k: the axis to rotate around - /// @param theta: the angle in radians to rotate - /// @param v: the vector to rotate - /// - /// @return NdArray - /// - template - NdArray rodriguesRotation(const NdArray& k, double theta, const NdArray& v) - { - return rodriguesRotation(Vec3(k), theta, Vec3(v)).toNdArray(); - } - } // namespace rotations -} // namespace nc \ No newline at end of file + //============================================================================ + // Method Description: + /// Performs Rodriques' rotation formula + /// https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula + /// + /// @param k: the axis to rotate around + /// @param theta: the angle in radians to rotate + /// @param v: the vector to rotate + /// + /// @return NdArray + /// + template + NdArray rodriguesRotation(const NdArray& k, double theta, const NdArray& v) + { + return rodriguesRotation(Vec3(k), theta, Vec3(v)).toNdArray(); + } +} // namespace nc::rotations \ No newline at end of file diff --git a/include/NumCpp/Rotations/wahbasProblem.hpp b/include/NumCpp/Rotations/wahbasProblem.hpp index eac739431..4fcf74b79 100644 --- a/include/NumCpp/Rotations/wahbasProblem.hpp +++ b/include/NumCpp/Rotations/wahbasProblem.hpp @@ -42,92 +42,89 @@ #include "NumCpp/Linalg/svd.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::rotations { - namespace rotations + //============================================================================ + // Method Description: + /// Finds a rotation matrix (special orthogonal matrix) between two coordinate + /// systems from a set of (weighted) vector observations. Solutions to Wahba's + /// problem are often used in satellite attitude determination utilising sensors + /// such as magnetometers and multi-antenna GPS receivers + /// https://en.wikipedia.org/wiki/Wahba%27s_problem + /// + /// @param wk: k-th 3-vector measurement in the reference frame (n x 3 matrix) + /// @param vk: corresponding k-th 3-vector measurement in the body frame (n x 3 matrix) + /// @param ak: set of weights for each observation (1 x n or n x 1 matrix) + /// + /// @return NdArray rotation matrix + /// + template + NdArray wahbasProblem(const NdArray& wk, const NdArray& vk, const NdArray& ak) { - //============================================================================ - // Method Description: - /// Finds a rotation matrix (special orthogonal matrix) between two coordinate - /// systems from a set of (weighted) vector observations. Solutions to Wahba's - /// problem are often used in satellite attitude determination utilising sensors - /// such as magnetometers and multi-antenna GPS receivers - /// https://en.wikipedia.org/wiki/Wahba%27s_problem - /// - /// @param wk: k-th 3-vector measurement in the reference frame (n x 3 matrix) - /// @param vk: corresponding k-th 3-vector measurement in the body frame (n x 3 matrix) - /// @param ak: set of weights for each observation (1 x n or n x 1 matrix) - /// - /// @return NdArray rotation matrix - /// - template - NdArray wahbasProblem(const NdArray& wk, const NdArray& vk, const NdArray& ak) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - const auto wkShape = wk.shape(); - if (wkShape.cols != 3) - { - THROW_INVALID_ARGUMENT_ERROR("wk matrix must be of shape [n, 3]"); - } + const auto wkShape = wk.shape(); + if (wkShape.cols != 3) + { + THROW_INVALID_ARGUMENT_ERROR("wk matrix must be of shape [n, 3]"); + } - const auto vkShape = vk.shape(); - if (vkShape.cols != 3) - { - THROW_INVALID_ARGUMENT_ERROR("vk matrix must be of shape [n, 3]"); - } + const auto vkShape = vk.shape(); + if (vkShape.cols != 3) + { + THROW_INVALID_ARGUMENT_ERROR("vk matrix must be of shape [n, 3]"); + } - if (wkShape.rows != vkShape.rows) - { - THROW_INVALID_ARGUMENT_ERROR("wk and vk matrices must have the same number of rows"); - } + if (wkShape.rows != vkShape.rows) + { + THROW_INVALID_ARGUMENT_ERROR("wk and vk matrices must have the same number of rows"); + } - if (ak.size() != wkShape.rows) - { - THROW_INVALID_ARGUMENT_ERROR("ak matrix must have the same number of elements as wk and vk rows"); - } + if (ak.size() != wkShape.rows) + { + THROW_INVALID_ARGUMENT_ERROR("ak matrix must have the same number of elements as wk and vk rows"); + } - auto b = zeros(3, 3); - const auto cSlice = wk.cSlice(); - for (uint32 row = 0; row < wkShape.rows; ++row) - { - const auto wkVec = wk(row, cSlice); - const auto vkVec = vk(row, cSlice); - b += ak[row] * dot(wkVec.transpose(), vkVec); - } + auto b = zeros(3, 3); + const auto cSlice = wk.cSlice(); + for (uint32 row = 0; row < wkShape.rows; ++row) + { + const auto wkVec = wk(row, cSlice); + const auto vkVec = vk(row, cSlice); + b += ak[row] * dot(wkVec.transpose(), vkVec); + } - NdArray u; - NdArray s; - NdArray vt; + NdArray u; + NdArray s; + NdArray vt; - linalg::svd(b, u, s, vt); + linalg::svd(b, u, s, vt); - auto m = eye(3, 3); - m(0, 0) = 1.; - m(1, 1) = 1.; - m(2, 2) = linalg::det(u) * linalg::det(vt.transpose()); + auto m = eye(3, 3); + m(0, 0) = 1.; + m(1, 1) = 1.; + m(2, 2) = linalg::det(u) * linalg::det(vt.transpose()); - return dot(u, dot(m, vt)); - } + return dot(u, dot(m, vt)); + } - //============================================================================ - // Method Description: - /// Finds a rotation matrix (special orthogonal matrix) between two coordinate - /// systems from a set of (weighted) vector observations. Solutions to Wahba's - /// problem are often used in satellite attitude determination utilising sensors - /// such as magnetometers and multi-antenna GPS receivers - /// https://en.wikipedia.org/wiki/Wahba%27s_problem - /// - /// @param wk: k-th 3-vector measurement in the reference frame - /// @param vk: corresponding k-th 3-vector measurement in the body frame - /// - /// @return NdArray rotation matrix - /// - template - NdArray wahbasProblem(const NdArray& wk, const NdArray& vk) - { - const auto ak = ones({ 1, wk.shape().rows }); - return wahbasProblem(wk, vk, ak); - } - } // namespace rotations -} // namespace nc + //============================================================================ + // Method Description: + /// Finds a rotation matrix (special orthogonal matrix) between two coordinate + /// systems from a set of (weighted) vector observations. Solutions to Wahba's + /// problem are often used in satellite attitude determination utilising sensors + /// such as magnetometers and multi-antenna GPS receivers + /// https://en.wikipedia.org/wiki/Wahba%27s_problem + /// + /// @param wk: k-th 3-vector measurement in the reference frame + /// @param vk: corresponding k-th 3-vector measurement in the body frame + /// + /// @return NdArray rotation matrix + /// + template + NdArray wahbasProblem(const NdArray& wk, const NdArray& vk) + { + const auto ak = ones({ 1, wk.shape().rows }); + return wahbasProblem(wk, vk, ak); + } +} // namespace nc::rotations diff --git a/include/NumCpp/Special/airy_ai.hpp b/include/NumCpp/Special/airy_ai.hpp index bf833b572..9c4be2352 100644 --- a/include/NumCpp/Special/airy_ai.hpp +++ b/include/NumCpp/Special/airy_ai.hpp @@ -35,50 +35,47 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// The first linearly independent solution to the differential equation y'' - yz = 0. + /// http://mathworld.wolfram.com/AiryFunctions.html + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inValue + /// @return calculated-result-type + /// + template + auto airy_ai(dtype inValue) { - //============================================================================ - // Method Description: - /// The first linearly independent solution to the differential equation y'' - yz = 0. - /// http://mathworld.wolfram.com/AiryFunctions.html - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inValue - /// @return calculated-result-type - /// - template - auto airy_ai(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::airy_ai(inValue); - } + return boost::math::airy_ai(inValue); + } - //============================================================================ - // Method Description: - /// The first linearly independent solution to the differential equation y'' - yz = 0. - /// http://mathworld.wolfram.com/AiryFunctions.html - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - template - auto airy_ai(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// The first linearly independent solution to the differential equation y'' - yz = 0. + /// http://mathworld.wolfram.com/AiryFunctions.html + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + template + auto airy_ai(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](dtype inValue) -> auto{ return airy_ai(inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](dtype inValue) -> auto{ return airy_ai(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/airy_ai_prime.hpp b/include/NumCpp/Special/airy_ai_prime.hpp index e9a9f2aad..95bb3622b 100644 --- a/include/NumCpp/Special/airy_ai_prime.hpp +++ b/include/NumCpp/Special/airy_ai_prime.hpp @@ -35,50 +35,47 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// The derivative of the first linearly independent solution to the differential equation y'' - yz = 0. + /// http://mathworld.wolfram.com/AiryFunctions.html + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inValue + /// @return calculated-result-type + /// + template + auto airy_ai_prime(dtype inValue) { - //============================================================================ - // Method Description: - /// The derivative of the first linearly independent solution to the differential equation y'' - yz = 0. - /// http://mathworld.wolfram.com/AiryFunctions.html - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inValue - /// @return calculated-result-type - /// - template - auto airy_ai_prime(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::airy_ai_prime(inValue); - } + return boost::math::airy_ai_prime(inValue); + } - //============================================================================ - // Method Description: - /// The derivative of the first linearly independent solution to the differential equation y'' - yz = 0. - /// http://mathworld.wolfram.com/AiryFunctions.html - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - template - auto airy_ai_prime(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// The derivative of the first linearly independent solution to the differential equation y'' - yz = 0. + /// http://mathworld.wolfram.com/AiryFunctions.html + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + template + auto airy_ai_prime(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](dtype inValue) -> auto{ return airy_ai_prime(inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](dtype inValue) -> auto{ return airy_ai_prime(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/airy_bi.hpp b/include/NumCpp/Special/airy_bi.hpp index 01b01c9fb..6efa8f9cd 100644 --- a/include/NumCpp/Special/airy_bi.hpp +++ b/include/NumCpp/Special/airy_bi.hpp @@ -35,50 +35,47 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// The second linearly independent solution to the differential equation y'' - yz = 0. + /// http://mathworld.wolfram.com/AiryFunctions.html + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inValue + /// @return calculated-result-type + /// + template + auto airy_bi(dtype inValue) { - //============================================================================ - // Method Description: - /// The second linearly independent solution to the differential equation y'' - yz = 0. - /// http://mathworld.wolfram.com/AiryFunctions.html - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inValue - /// @return calculated-result-type - /// - template - auto airy_bi(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::airy_bi(inValue); - } + return boost::math::airy_bi(inValue); + } - //============================================================================ - // Method Description: - /// The second linearly independent solution to the differential equation y'' - yz = 0. - /// http://mathworld.wolfram.com/AiryFunctions.html - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - template - auto airy_bi(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// The second linearly independent solution to the differential equation y'' - yz = 0. + /// http://mathworld.wolfram.com/AiryFunctions.html + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + template + auto airy_bi(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](dtype inValue) -> auto{ return airy_bi(inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](dtype inValue) -> auto{ return airy_bi(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/airy_bi_prime.hpp b/include/NumCpp/Special/airy_bi_prime.hpp index d3600d747..0787b7837 100644 --- a/include/NumCpp/Special/airy_bi_prime.hpp +++ b/include/NumCpp/Special/airy_bi_prime.hpp @@ -35,50 +35,47 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// The derivative of the second linearly independent solution to the differential equation y'' - yz = 0. + /// http://mathworld.wolfram.com/AiryFunctions.html + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inValue + /// @return calculated-result-type + /// + template + auto airy_bi_prime(dtype inValue) { - //============================================================================ - // Method Description: - /// The derivative of the second linearly independent solution to the differential equation y'' - yz = 0. - /// http://mathworld.wolfram.com/AiryFunctions.html - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inValue - /// @return calculated-result-type - /// - template - auto airy_bi_prime(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::airy_bi_prime(inValue); - } + return boost::math::airy_bi_prime(inValue); + } - //============================================================================ - // Method Description: - /// The derivative of the second linearly independent solution to the differential equation y'' - yz = 0. - /// http://mathworld.wolfram.com/AiryFunctions.html - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - template - auto airy_bi_prime(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// The derivative of the second linearly independent solution to the differential equation y'' - yz = 0. + /// http://mathworld.wolfram.com/AiryFunctions.html + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + template + auto airy_bi_prime(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](dtype inValue) -> auto{ return airy_bi_prime(inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](dtype inValue) -> auto{ return airy_bi_prime(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/bernoulli.hpp b/include/NumCpp/Special/bernoulli.hpp index 61998f540..9abb0377d 100644 --- a/include/NumCpp/Special/bernoulli.hpp +++ b/include/NumCpp/Special/bernoulli.hpp @@ -35,52 +35,49 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Both return the nth Bernoulli number B2n. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param n + /// @return double + /// + inline double bernoilli(uint32 n) { - //============================================================================ - // Method Description: - /// Both return the nth Bernoulli number B2n. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param n - /// @return double - /// - inline double bernoilli(uint32 n) + if (n == 1) { - if (n == 1) - { - return 0.5; - } - if (n % 2 != 0) - { - return 0.; - } - - return boost::math::bernoulli_b2n(n / 2); + return 0.5; } - - //============================================================================ - // Method Description: - /// Both return the nth Bernoulli number B2n. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - inline NdArray bernoilli(const NdArray& inArray) + if (n % 2 != 0) { - NdArray returnArray(inArray.shape()); + return 0.; + } - stl_algorithms::transform(inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](uint32 inValue) -> double { return bernoilli(inValue); }); + return boost::math::bernoulli_b2n(n / 2); + } - return returnArray; - } - } // namespace special -} // namespace nc + //============================================================================ + // Method Description: + /// Both return the nth Bernoulli number B2n. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + inline NdArray bernoilli(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); + + stl_algorithms::transform(inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](uint32 inValue) -> double { return bernoilli(inValue); }); + + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/bessel_in.hpp b/include/NumCpp/Special/bessel_in.hpp index cf320177b..b75b3564e 100644 --- a/include/NumCpp/Special/bessel_in.hpp +++ b/include/NumCpp/Special/bessel_in.hpp @@ -41,57 +41,54 @@ #include -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Modified Cylindrical Bessel function of the first kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input value + /// @return calculated-result-type + /// + template + auto bessel_in(dtype1 inV, dtype2 inX) { - //============================================================================ - // Method Description: - /// Modified Cylindrical Bessel function of the first kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input value - /// @return calculated-result-type - /// - template - auto bessel_in(dtype1 inV, dtype2 inX) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); #ifdef __cpp_lib_math_special_functions - return std::cyl_bessel_i(static_cast(inV), static_cast(inX)); + return std::cyl_bessel_i(static_cast(inV), static_cast(inX)); #else - return boost::math::cyl_bessel_i(static_cast(inV), static_cast(inX)); + return boost::math::cyl_bessel_i(static_cast(inV), static_cast(inX)); #endif - } + } - //============================================================================ - // Method Description: - /// Modified Cylindrical Bessel function of the first kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inV: the order of the bessel function - /// @param inArrayX: the input values - /// @return NdArray - /// - template - auto bessel_in(dtype1 inV, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + //============================================================================ + // Method Description: + /// Modified Cylindrical Bessel function of the first kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inV: the order of the bessel function + /// @param inArrayX: the input values + /// @return NdArray + /// + template + auto bessel_in(dtype1 inV, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - stl_algorithms::transform( - inArrayX.cbegin(), - inArrayX.cend(), - returnArray.begin(), - [inV](dtype2 inX) -> auto{ return bessel_in(inV, inX); }); + stl_algorithms::transform( + inArrayX.cbegin(), + inArrayX.cend(), + returnArray.begin(), + [inV](dtype2 inX) -> auto{ return bessel_in(inV, inX); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/bessel_in_prime.hpp b/include/NumCpp/Special/bessel_in_prime.hpp index ce8941567..eb7a99c3f 100644 --- a/include/NumCpp/Special/bessel_in_prime.hpp +++ b/include/NumCpp/Special/bessel_in_prime.hpp @@ -37,51 +37,48 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Derivcative of the Modified Cylindrical Bessel function of the first kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input value + /// @return calculated-result-type + /// + template + auto bessel_in_prime(dtype1 inV, dtype2 inX) { - //============================================================================ - // Method Description: - /// Derivcative of the Modified Cylindrical Bessel function of the first kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input value - /// @return calculated-result-type - /// - template - auto bessel_in_prime(dtype1 inV, dtype2 inX) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); - return boost::math::cyl_bessel_i_prime(inV, inX); - } + return boost::math::cyl_bessel_i_prime(inV, inX); + } - //============================================================================ - // Method Description: - /// Derivcative of the Modified Cylindrical Bessel function of the first kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inArrayX: the input values - /// @return NdArray - /// - template - auto bessel_in_prime(dtype1 inV, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + //============================================================================ + // Method Description: + /// Derivcative of the Modified Cylindrical Bessel function of the first kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inArrayX: the input values + /// @return NdArray + /// + template + auto bessel_in_prime(dtype1 inV, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - stl_algorithms::transform( - inArrayX.cbegin(), - inArrayX.cend(), - returnArray.begin(), - [inV](dtype2 inX) -> auto{ return bessel_in_prime(inV, inX); }); + stl_algorithms::transform( + inArrayX.cbegin(), + inArrayX.cend(), + returnArray.begin(), + [inV](dtype2 inX) -> auto{ return bessel_in_prime(inV, inX); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/bessel_jn.hpp b/include/NumCpp/Special/bessel_jn.hpp index 4faa280af..f23171ff5 100644 --- a/include/NumCpp/Special/bessel_jn.hpp +++ b/include/NumCpp/Special/bessel_jn.hpp @@ -41,57 +41,54 @@ #include -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Cylindrical Bessel function of the first kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input value + /// @return calculated-result-type + /// + template + auto bessel_jn(dtype1 inV, dtype2 inX) { - //============================================================================ - // Method Description: - /// Cylindrical Bessel function of the first kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input value - /// @return calculated-result-type - /// - template - auto bessel_jn(dtype1 inV, dtype2 inX) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); #ifdef __cpp_lib_math_special_functions - return std::cyl_bessel_j(static_cast(inV), static_cast(inX)); + return std::cyl_bessel_j(static_cast(inV), static_cast(inX)); #else - return boost::math::cyl_bessel_j(static_cast(inV), static_cast(inX)); + return boost::math::cyl_bessel_j(static_cast(inV), static_cast(inX)); #endif - } + } - //============================================================================ - // Method Description: - /// Cylindrical Bessel function of the first kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inV: the order of the bessel function - /// @param inArrayX: the input values - /// @return NdArray - /// - template - auto bessel_jn(dtype1 inV, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + //============================================================================ + // Method Description: + /// Cylindrical Bessel function of the first kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inV: the order of the bessel function + /// @param inArrayX: the input values + /// @return NdArray + /// + template + auto bessel_jn(dtype1 inV, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - stl_algorithms::transform( - inArrayX.cbegin(), - inArrayX.cend(), - returnArray.begin(), - [inV](dtype2 inX) -> auto{ return bessel_jn(inV, inX); }); + stl_algorithms::transform( + inArrayX.cbegin(), + inArrayX.cend(), + returnArray.begin(), + [inV](dtype2 inX) -> auto{ return bessel_jn(inV, inX); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/bessel_jn_prime.hpp b/include/NumCpp/Special/bessel_jn_prime.hpp index 738827f70..b4db65fe7 100644 --- a/include/NumCpp/Special/bessel_jn_prime.hpp +++ b/include/NumCpp/Special/bessel_jn_prime.hpp @@ -37,51 +37,48 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Derivcative of the Cylindrical Bessel function of the first kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input value + /// @return calculated-result-type + /// + template + auto bessel_jn_prime(dtype1 inV, dtype2 inX) { - //============================================================================ - // Method Description: - /// Derivcative of the Cylindrical Bessel function of the first kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input value - /// @return calculated-result-type - /// - template - auto bessel_jn_prime(dtype1 inV, dtype2 inX) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); - return boost::math::cyl_bessel_j_prime(inV, inX); - } + return boost::math::cyl_bessel_j_prime(inV, inX); + } - //============================================================================ - // Method Description: - /// Derivcative of the Cylindrical Bessel function of the first kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inArrayX: the input values - /// @return NdArray - /// - template - auto bessel_jn_prime(dtype1 inV, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + //============================================================================ + // Method Description: + /// Derivcative of the Cylindrical Bessel function of the first kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inArrayX: the input values + /// @return NdArray + /// + template + auto bessel_jn_prime(dtype1 inV, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - stl_algorithms::transform( - inArrayX.cbegin(), - inArrayX.cend(), - returnArray.begin(), - [inV](dtype2 inX) -> auto{ return bessel_jn_prime(inV, inX); }); + stl_algorithms::transform( + inArrayX.cbegin(), + inArrayX.cend(), + returnArray.begin(), + [inV](dtype2 inX) -> auto{ return bessel_jn_prime(inV, inX); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/bessel_kn.hpp b/include/NumCpp/Special/bessel_kn.hpp index 164b0cea4..8b81fb79b 100644 --- a/include/NumCpp/Special/bessel_kn.hpp +++ b/include/NumCpp/Special/bessel_kn.hpp @@ -41,57 +41,54 @@ #include -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Modified Cylindrical Bessel function of the second kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input value + /// @return calculated-result-type + /// + template + auto bessel_kn(dtype1 inV, dtype2 inX) { - //============================================================================ - // Method Description: - /// Modified Cylindrical Bessel function of the second kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input value - /// @return calculated-result-type - /// - template - auto bessel_kn(dtype1 inV, dtype2 inX) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); #ifdef __cpp_lib_math_special_functions - return std::cyl_bessel_k(static_cast(inV), static_cast(inX)); + return std::cyl_bessel_k(static_cast(inV), static_cast(inX)); #else - return boost::math::cyl_bessel_k(static_cast(inV), static_cast(inX)); + return boost::math::cyl_bessel_k(static_cast(inV), static_cast(inX)); #endif - } + } - //============================================================================ - // Method Description: - /// Modified Cylindrical Bessel function of the second kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inV: the order of the bessel function - /// @param inArrayX: the input values - /// @return NdArray - /// - template - auto bessel_kn(dtype1 inV, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + //============================================================================ + // Method Description: + /// Modified Cylindrical Bessel function of the second kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inV: the order of the bessel function + /// @param inArrayX: the input values + /// @return NdArray + /// + template + auto bessel_kn(dtype1 inV, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - stl_algorithms::transform( - inArrayX.cbegin(), - inArrayX.cend(), - returnArray.begin(), - [inV](dtype2 inX) -> auto{ return bessel_kn(inV, inX); }); + stl_algorithms::transform( + inArrayX.cbegin(), + inArrayX.cend(), + returnArray.begin(), + [inV](dtype2 inX) -> auto{ return bessel_kn(inV, inX); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/bessel_kn_prime.hpp b/include/NumCpp/Special/bessel_kn_prime.hpp index 7c83e0b70..248a980b0 100644 --- a/include/NumCpp/Special/bessel_kn_prime.hpp +++ b/include/NumCpp/Special/bessel_kn_prime.hpp @@ -37,51 +37,48 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Derivcative of the Modified Cylindrical Bessel function of the second kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input value + /// @return calculated-result-type + /// + template + auto bessel_kn_prime(dtype1 inV, dtype2 inX) { - //============================================================================ - // Method Description: - /// Derivcative of the Modified Cylindrical Bessel function of the second kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input value - /// @return calculated-result-type - /// - template - auto bessel_kn_prime(dtype1 inV, dtype2 inX) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); - return boost::math::cyl_bessel_k_prime(inV, inX); - } + return boost::math::cyl_bessel_k_prime(inV, inX); + } - //============================================================================ - // Method Description: - /// Derivcative of the Modified Cylindrical Bessel function of the second kind - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inArrayX: the input values - /// @return NdArray - /// - template - auto bessel_kn_prime(dtype1 inV, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + //============================================================================ + // Method Description: + /// Derivcative of the Modified Cylindrical Bessel function of the second kind + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inArrayX: the input values + /// @return NdArray + /// + template + auto bessel_kn_prime(dtype1 inV, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - stl_algorithms::transform( - inArrayX.cbegin(), - inArrayX.cend(), - returnArray.begin(), - [inV](dtype2 inX) -> auto{ return bessel_kn_prime(inV, inX); }); + stl_algorithms::transform( + inArrayX.cbegin(), + inArrayX.cend(), + returnArray.begin(), + [inV](dtype2 inX) -> auto{ return bessel_kn_prime(inV, inX); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/bessel_yn.hpp b/include/NumCpp/Special/bessel_yn.hpp index c0cf8ee57..e8ce29c94 100644 --- a/include/NumCpp/Special/bessel_yn.hpp +++ b/include/NumCpp/Special/bessel_yn.hpp @@ -41,57 +41,54 @@ #include -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Cylindrical Bessel function of the second kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input value + /// @return calculated-result-type + /// + template + auto bessel_yn(dtype1 inV, dtype2 inX) { - //============================================================================ - // Method Description: - /// Cylindrical Bessel function of the second kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input value - /// @return calculated-result-type - /// - template - auto bessel_yn(dtype1 inV, dtype2 inX) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); #ifdef __cpp_lib_math_special_functions - return std::cyl_neumann(static_cast(inV), static_cast(inX)); + return std::cyl_neumann(static_cast(inV), static_cast(inX)); #else - return boost::math::cyl_neumann(static_cast(inV), static_cast(inX)); + return boost::math::cyl_neumann(static_cast(inV), static_cast(inX)); #endif - } + } - //============================================================================ - // Method Description: - /// Cylindrical Bessel function of the second kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inV: the order of the bessel function - /// @param inArrayX: the input values - /// @return NdArray - /// - template - auto bessel_yn(dtype1 inV, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + //============================================================================ + // Method Description: + /// Cylindrical Bessel function of the second kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inV: the order of the bessel function + /// @param inArrayX: the input values + /// @return NdArray + /// + template + auto bessel_yn(dtype1 inV, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - stl_algorithms::transform( - inArrayX.cbegin(), - inArrayX.cend(), - returnArray.begin(), - [inV](dtype2 inX) -> auto{ return bessel_yn(inV, inX); }); + stl_algorithms::transform( + inArrayX.cbegin(), + inArrayX.cend(), + returnArray.begin(), + [inV](dtype2 inX) -> auto{ return bessel_yn(inV, inX); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/bessel_yn_prime.hpp b/include/NumCpp/Special/bessel_yn_prime.hpp index 56ed10512..41fe1f648 100644 --- a/include/NumCpp/Special/bessel_yn_prime.hpp +++ b/include/NumCpp/Special/bessel_yn_prime.hpp @@ -37,51 +37,48 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Derivcative of the Cylindrical Bessel function of the second kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input value + /// @return calculated-result-type + /// + template + auto bessel_yn_prime(dtype1 inV, dtype2 inX) { - //============================================================================ - // Method Description: - /// Derivcative of the Cylindrical Bessel function of the second kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input value - /// @return calculated-result-type - /// - template - auto bessel_yn_prime(dtype1 inV, dtype2 inX) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); - return boost::math::cyl_neumann_prime(inV, inX); - } + return boost::math::cyl_neumann_prime(inV, inX); + } - //============================================================================ - // Method Description: - /// Derivcative of the Cylindrical Bessel function of the second kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inArrayX: the input values - /// @return NdArray - /// - template - auto bessel_yn_prime(dtype1 inV, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + //============================================================================ + // Method Description: + /// Derivcative of the Cylindrical Bessel function of the second kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inArrayX: the input values + /// @return NdArray + /// + template + auto bessel_yn_prime(dtype1 inV, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - stl_algorithms::transform( - inArrayX.cbegin(), - inArrayX.cend(), - returnArray.begin(), - [inV](dtype2 inX) -> auto{ return bessel_yn_prime(inV, inX); }); + stl_algorithms::transform( + inArrayX.cbegin(), + inArrayX.cend(), + returnArray.begin(), + [inV](dtype2 inX) -> auto{ return bessel_yn_prime(inV, inX); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/beta.hpp b/include/NumCpp/Special/beta.hpp index d9f6db6e7..f9df74983 100644 --- a/include/NumCpp/Special/beta.hpp +++ b/include/NumCpp/Special/beta.hpp @@ -41,58 +41,55 @@ #include -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// The beta function. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param a + /// @param b + /// @return calculated-result-type + /// + template + auto beta(dtype1 a, dtype2 b) { - //============================================================================ - // Method Description: - /// The beta function. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param a - /// @param b - /// @return calculated-result-type - /// - template - auto beta(dtype1 a, dtype2 b) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); #ifdef __cpp_lib_math_special_functions - return std::beta(a, b); + return std::beta(a, b); #else - return boost::math::beta(a, b); + return boost::math::beta(a, b); #endif - } + } - //============================================================================ - // Method Description: - /// The beta function. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inArrayA - /// @param inArrayB - /// @return NdArray - /// - template - auto beta(const NdArray& inArrayA, const NdArray& inArrayB) - { - NdArray returnArray(inArrayB.shape()); + //============================================================================ + // Method Description: + /// The beta function. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inArrayA + /// @param inArrayB + /// @return NdArray + /// + template + auto beta(const NdArray& inArrayA, const NdArray& inArrayB) + { + NdArray returnArray(inArrayB.shape()); - stl_algorithms::transform( - inArrayA.cbegin(), - inArrayA.cend(), - inArrayB.cbegin(), - returnArray.begin(), - [](dtype1 a, dtype2 b) -> auto{ return beta(a, b); }); + stl_algorithms::transform( + inArrayA.cbegin(), + inArrayA.cend(), + inArrayB.cbegin(), + returnArray.begin(), + [](dtype1 a, dtype2 b) -> auto{ return beta(a, b); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/cnr.hpp b/include/NumCpp/Special/cnr.hpp index c19be1aed..5a1f7e61e 100644 --- a/include/NumCpp/Special/cnr.hpp +++ b/include/NumCpp/Special/cnr.hpp @@ -35,21 +35,18 @@ #include "NumCpp/Special/factorial.hpp" #include "NumCpp/Special/pnr.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Returns the number of combinations of n choose r. C(n, r) + /// + /// @param n: the total number of items + /// @param r: the number of items taken + /// @return double + /// + inline double cnr(uint32 n, uint32 r) { - //============================================================================ - // Method Description: - /// Returns the number of combinations of n choose r. C(n, r) - /// - /// @param n: the total number of items - /// @param r: the number of items taken - /// @return double - /// - inline double cnr(uint32 n, uint32 r) - { - return pnr(n, r) / factorial(r); - } - } // namespace special -} // namespace nc + return pnr(n, r) / factorial(r); + } +} // namespace nc::special diff --git a/include/NumCpp/Special/comp_ellint_1.hpp b/include/NumCpp/Special/comp_ellint_1.hpp index 347392b9d..8b9802a6b 100644 --- a/include/NumCpp/Special/comp_ellint_1.hpp +++ b/include/NumCpp/Special/comp_ellint_1.hpp @@ -41,54 +41,51 @@ #include -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Computes the complete elliptic integral of the first kind of k. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inK: elliptic modulus or eccentricity + /// @return calculated-result-type + /// + template + auto comp_ellint_1(dtype inK) { - //============================================================================ - // Method Description: - /// Computes the complete elliptic integral of the first kind of k. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inK: elliptic modulus or eccentricity - /// @return calculated-result-type - /// - template - auto comp_ellint_1(dtype inK) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); #ifdef __cpp_lib_math_special_functions - return std::comp_ellint_1(inK); + return std::comp_ellint_1(inK); #else - return boost::math::ellint_1(inK); + return boost::math::ellint_1(inK); #endif - } + } - //============================================================================ - // Method Description: - /// Computes the complete elliptic integral of the first kind of k. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inArrayK: elliptic modulus or eccentricity - /// @return NdArray - /// - template - auto comp_ellint_1(const NdArray& inArrayK) - { - NdArray returnArray(inArrayK.shape()); + //============================================================================ + // Method Description: + /// Computes the complete elliptic integral of the first kind of k. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inArrayK: elliptic modulus or eccentricity + /// @return NdArray + /// + template + auto comp_ellint_1(const NdArray& inArrayK) + { + NdArray returnArray(inArrayK.shape()); - stl_algorithms::transform( - inArrayK.cbegin(), - inArrayK.cend(), - returnArray.begin(), - [](dtype inK) -> auto{ return comp_ellint_1(inK); }); + stl_algorithms::transform( + inArrayK.cbegin(), + inArrayK.cend(), + returnArray.begin(), + [](dtype inK) -> auto{ return comp_ellint_1(inK); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/comp_ellint_2.hpp b/include/NumCpp/Special/comp_ellint_2.hpp index 69f585731..cd28df573 100644 --- a/include/NumCpp/Special/comp_ellint_2.hpp +++ b/include/NumCpp/Special/comp_ellint_2.hpp @@ -41,54 +41,51 @@ #include -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Computes the complete elliptic integral of the second kind of k. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inK: elliptic modulus or eccentricity + /// @return calculated-result-type + /// + template + auto comp_ellint_2(dtype inK) { - //============================================================================ - // Method Description: - /// Computes the complete elliptic integral of the second kind of k. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inK: elliptic modulus or eccentricity - /// @return calculated-result-type - /// - template - auto comp_ellint_2(dtype inK) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); #ifdef __cpp_lib_math_special_functions - return std::comp_ellint_2(inK); + return std::comp_ellint_2(inK); #else - return boost::math::ellint_2(inK); + return boost::math::ellint_2(inK); #endif - } + } - //============================================================================ - // Method Description: - /// Computes the complete elliptic integral of the second kind of k. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inArrayK: elliptic modulus or eccentricity - /// @return NdArray - /// - template - auto comp_ellint_2(const NdArray& inArrayK) - { - NdArray returnArray(inArrayK.shape()); + //============================================================================ + // Method Description: + /// Computes the complete elliptic integral of the second kind of k. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inArrayK: elliptic modulus or eccentricity + /// @return NdArray + /// + template + auto comp_ellint_2(const NdArray& inArrayK) + { + NdArray returnArray(inArrayK.shape()); - stl_algorithms::transform( - inArrayK.cbegin(), - inArrayK.cend(), - returnArray.begin(), - [](dtype inK) -> auto{ return comp_ellint_2(inK); }); + stl_algorithms::transform( + inArrayK.cbegin(), + inArrayK.cend(), + returnArray.begin(), + [](dtype inK) -> auto{ return comp_ellint_2(inK); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/comp_ellint_3.hpp b/include/NumCpp/Special/comp_ellint_3.hpp index dce8d4216..8b649ec58 100644 --- a/include/NumCpp/Special/comp_ellint_3.hpp +++ b/include/NumCpp/Special/comp_ellint_3.hpp @@ -42,63 +42,60 @@ #include -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Computes the complete elliptic integral of the third kind of k and v. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inK: elliptic modulus or eccentricity + /// @param inV: elliptic characteristic + /// @return calculated-result-type + /// + template + auto comp_ellint_3(dtype1 inK, dtype2 inV) { - //============================================================================ - // Method Description: - /// Computes the complete elliptic integral of the third kind of k and v. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inK: elliptic modulus or eccentricity - /// @param inV: elliptic characteristic - /// @return calculated-result-type - /// - template - auto comp_ellint_3(dtype1 inK, dtype2 inV) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); #ifdef __cpp_lib_math_special_functions - return std::comp_ellint_3(inK, inV); + return std::comp_ellint_3(inK, inV); #else - return boost::math::ellint_3(inK, inV); + return boost::math::ellint_3(inK, inV); #endif - } + } - //============================================================================ - // Method Description: - /// Computes the complete elliptic integral of the third kind of k and p. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inArrayK: the order of the bessel function - /// @param inArrayV: elliptic characteristic - /// @return NdArray - /// - template - auto comp_ellint_3(const NdArray& inArrayK, const NdArray& inArrayV) + //============================================================================ + // Method Description: + /// Computes the complete elliptic integral of the third kind of k and p. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inArrayK: the order of the bessel function + /// @param inArrayV: elliptic characteristic + /// @return NdArray + /// + template + auto comp_ellint_3(const NdArray& inArrayK, const NdArray& inArrayV) + { + if (inArrayK.size() != inArrayV.size()) { - if (inArrayK.size() != inArrayV.size()) - { - THROW_INVALID_ARGUMENT_ERROR("Shapes of inArrayk and inArrayV must match."); - } + THROW_INVALID_ARGUMENT_ERROR("Shapes of inArrayk and inArrayV must match."); + } - NdArray returnArray(inArrayK.shape()); + NdArray returnArray(inArrayK.shape()); - stl_algorithms::transform( - inArrayK.cbegin(), - inArrayK.cend(), - inArrayV.cbegin(), - returnArray.begin(), - [](dtype1 inK, dtype2 inV) -> auto{ return comp_ellint_3(inK, inV); }); + stl_algorithms::transform( + inArrayK.cbegin(), + inArrayK.cend(), + inArrayV.cbegin(), + returnArray.begin(), + [](dtype1 inK, dtype2 inV) -> auto{ return comp_ellint_3(inK, inV); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/cyclic_hankel_1.hpp b/include/NumCpp/Special/cyclic_hankel_1.hpp index 0892245d1..03c4a3a3d 100644 --- a/include/NumCpp/Special/cyclic_hankel_1.hpp +++ b/include/NumCpp/Special/cyclic_hankel_1.hpp @@ -37,51 +37,48 @@ #include "NumCpp/Core/Internal/StaticAsserts.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Hankel funcion of the first kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input value + /// @return std::complex + /// + template + auto cyclic_hankel_1(dtype1 inV, dtype2 inX) { - //============================================================================ - // Method Description: - /// Hankel funcion of the first kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input value - /// @return std::complex - /// - template - auto cyclic_hankel_1(dtype1 inV, dtype2 inX) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); - return boost::math::cyl_hankel_1(inV, inX); - } + return boost::math::cyl_hankel_1(inV, inX); + } - //============================================================================ - // Method Description: - /// Hankel funcion of the first kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input array - /// @return NdArray - /// - template - auto cyclic_hankel_1(dtype1 inV, const NdArray& inX) - { - NdArray returnArray(inX.shape()); + //============================================================================ + // Method Description: + /// Hankel funcion of the first kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input array + /// @return NdArray + /// + template + auto cyclic_hankel_1(dtype1 inV, const NdArray& inX) + { + NdArray returnArray(inX.shape()); - stl_algorithms::transform( - inX.cbegin(), - inX.cend(), - returnArray.begin(), - [inV](dtype2 x) -> auto{ return cyclic_hankel_1(inV, x); }); + stl_algorithms::transform( + inX.cbegin(), + inX.cend(), + returnArray.begin(), + [inV](dtype2 x) -> auto{ return cyclic_hankel_1(inV, x); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/cyclic_hankel_2.hpp b/include/NumCpp/Special/cyclic_hankel_2.hpp index 9027e7ddd..5b39c5910 100644 --- a/include/NumCpp/Special/cyclic_hankel_2.hpp +++ b/include/NumCpp/Special/cyclic_hankel_2.hpp @@ -37,51 +37,48 @@ #include "NumCpp/Core/Internal/StaticAsserts.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Hankel funcion of the second kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input value + /// @return std::complex<> + /// + template + auto cyclic_hankel_2(dtype1 inV, dtype2 inX) { - //============================================================================ - // Method Description: - /// Hankel funcion of the second kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input value - /// @return std::complex<> - /// - template - auto cyclic_hankel_2(dtype1 inV, dtype2 inX) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); - return boost::math::cyl_hankel_2(inV, inX); - } + return boost::math::cyl_hankel_2(inV, inX); + } - //============================================================================ - // Method Description: - /// Hankel funcion of the second kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input array - /// @return NdArray - /// - template - auto cyclic_hankel_2(dtype1 inV, const NdArray& inX) - { - NdArray returnArray(inX.shape()); + //============================================================================ + // Method Description: + /// Hankel funcion of the second kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input array + /// @return NdArray + /// + template + auto cyclic_hankel_2(dtype1 inV, const NdArray& inX) + { + NdArray returnArray(inX.shape()); - stl_algorithms::transform( - inX.cbegin(), - inX.cend(), - returnArray.begin(), - [inV](dtype2 x) -> auto{ return cyclic_hankel_2(inV, x); }); + stl_algorithms::transform( + inX.cbegin(), + inX.cend(), + returnArray.begin(), + [inV](dtype2 x) -> auto{ return cyclic_hankel_2(inV, x); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/digamma.hpp b/include/NumCpp/Special/digamma.hpp index 97f4bb5a4..ceace1357 100644 --- a/include/NumCpp/Special/digamma.hpp +++ b/include/NumCpp/Special/digamma.hpp @@ -35,50 +35,47 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Returns the digamma or psi function of inValue. Digamma is defined as the + /// logarithmic derivative of the gamma function. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inValue + /// @return calculated-result-type + /// + template + auto digamma(dtype inValue) { - //============================================================================ - // Method Description: - /// Returns the digamma or psi function of inValue. Digamma is defined as the - /// logarithmic derivative of the gamma function. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inValue - /// @return calculated-result-type - /// - template - auto digamma(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::digamma(inValue); - } + return boost::math::digamma(inValue); + } - //============================================================================ - // Method Description: - /// Returns the digamma or psi function of values in inArray. Digamma is defined as the - /// logarithmic derivative of the gamma function. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - template - auto digamma(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// Returns the digamma or psi function of values in inArray. Digamma is defined as the + /// logarithmic derivative of the gamma function. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + template + auto digamma(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](dtype inValue) -> auto{ return digamma(inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](dtype inValue) -> auto{ return digamma(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/ellint_1.hpp b/include/NumCpp/Special/ellint_1.hpp index b0f7cb9ec..ca26b599b 100644 --- a/include/NumCpp/Special/ellint_1.hpp +++ b/include/NumCpp/Special/ellint_1.hpp @@ -42,63 +42,60 @@ #include -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Computes the incomplete elliptic integral of the first kind of k and p. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inK: elliptic modulus or eccentricity + /// @param inP: Jacobi amplitude (measured in radians) + /// @return calculated-result-type + /// + template + auto ellint_1(dtype1 inK, dtype2 inP) { - //============================================================================ - // Method Description: - /// Computes the incomplete elliptic integral of the first kind of k and p. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inK: elliptic modulus or eccentricity - /// @param inP: Jacobi amplitude (measured in radians) - /// @return calculated-result-type - /// - template - auto ellint_1(dtype1 inK, dtype2 inP) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); #ifdef __cpp_lib_math_special_functions - return std::ellint_1(inK, inP); + return std::ellint_1(inK, inP); #else - return boost::math::ellint_1(inK, inP); + return boost::math::ellint_1(inK, inP); #endif - } + } - //============================================================================ - // Method Description: - /// Computes the incomplete elliptic integral of the first kind of k and p. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inArrayK: elliptic modulus or eccentricity - /// @param inArrayP: Jacobi amplitude (measured in radians) - /// @return NdArray - /// - template - auto ellint_1(const NdArray& inArrayK, const NdArray& inArrayP) + //============================================================================ + // Method Description: + /// Computes the incomplete elliptic integral of the first kind of k and p. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inArrayK: elliptic modulus or eccentricity + /// @param inArrayP: Jacobi amplitude (measured in radians) + /// @return NdArray + /// + template + auto ellint_1(const NdArray& inArrayK, const NdArray& inArrayP) + { + if (inArrayK.size() != inArrayP.size()) { - if (inArrayK.size() != inArrayP.size()) - { - THROW_INVALID_ARGUMENT_ERROR("Shapes of inArrayK and inArrayP must match."); - } + THROW_INVALID_ARGUMENT_ERROR("Shapes of inArrayK and inArrayP must match."); + } - NdArray returnArray(inArrayK.shape()); + NdArray returnArray(inArrayK.shape()); - stl_algorithms::transform( - inArrayK.cbegin(), - inArrayK.cend(), - inArrayP.cbegin(), - returnArray.begin(), - [](dtype1 inK, dtype2 inP) -> auto{ return ellint_1(inK, inP); }); + stl_algorithms::transform( + inArrayK.cbegin(), + inArrayK.cend(), + inArrayP.cbegin(), + returnArray.begin(), + [](dtype1 inK, dtype2 inP) -> auto{ return ellint_1(inK, inP); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/ellint_2.hpp b/include/NumCpp/Special/ellint_2.hpp index d72b79741..7c1a8c27b 100644 --- a/include/NumCpp/Special/ellint_2.hpp +++ b/include/NumCpp/Special/ellint_2.hpp @@ -42,63 +42,60 @@ #include -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Computes the incomplete elliptic integral of the second kind of k and p. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inK: elliptic modulus or eccentricity + /// @param inP: Jacobi amplitude (measured in radians) + /// @return calculated-result-type + /// + template + auto ellint_2(dtype1 inK, dtype2 inP) { - //============================================================================ - // Method Description: - /// Computes the incomplete elliptic integral of the second kind of k and p. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inK: elliptic modulus or eccentricity - /// @param inP: Jacobi amplitude (measured in radians) - /// @return calculated-result-type - /// - template - auto ellint_2(dtype1 inK, dtype2 inP) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); #ifdef __cpp_lib_math_special_functions - return std::ellint_2(inK, inP); + return std::ellint_2(inK, inP); #else - return boost::math::ellint_2(inK, inP); + return boost::math::ellint_2(inK, inP); #endif - } + } - //============================================================================ - // Method Description: - /// Computes the incomplete elliptic integral of the second kind of k and p. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inArrayK: elliptic modulus or eccentricity - /// @param inArrayP: Jacobi amplitude (measured in radians) - /// @return NdArray - /// - template - auto ellint_2(const NdArray& inArrayK, const NdArray& inArrayP) + //============================================================================ + // Method Description: + /// Computes the incomplete elliptic integral of the second kind of k and p. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inArrayK: elliptic modulus or eccentricity + /// @param inArrayP: Jacobi amplitude (measured in radians) + /// @return NdArray + /// + template + auto ellint_2(const NdArray& inArrayK, const NdArray& inArrayP) + { + if (inArrayK.size() != inArrayP.size()) { - if (inArrayK.size() != inArrayP.size()) - { - THROW_INVALID_ARGUMENT_ERROR("Shapes of inArrayK and inArrayP must match."); - } + THROW_INVALID_ARGUMENT_ERROR("Shapes of inArrayK and inArrayP must match."); + } - NdArray returnArray(inArrayK.shape()); + NdArray returnArray(inArrayK.shape()); - stl_algorithms::transform( - inArrayK.cbegin(), - inArrayK.cend(), - inArrayP.cbegin(), - returnArray.begin(), - [](dtype1 inK, dtype2 inP) -> auto{ return ellint_2(inK, inP); }); + stl_algorithms::transform( + inArrayK.cbegin(), + inArrayK.cend(), + inArrayP.cbegin(), + returnArray.begin(), + [](dtype1 inK, dtype2 inP) -> auto{ return ellint_2(inK, inP); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/ellint_3.hpp b/include/NumCpp/Special/ellint_3.hpp index 25830a1bd..795817378 100644 --- a/include/NumCpp/Special/ellint_3.hpp +++ b/include/NumCpp/Special/ellint_3.hpp @@ -42,64 +42,61 @@ #include -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Computes the incomplete elliptic integral of the second kind of k, v, and p. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inK: elliptic modulus or eccentricity + /// @param inV: elliptic characteristic + /// @param inP: Jacobi amplitude (measured in radians) + /// @return calculated-result-type + /// + template + auto ellint_3(dtype1 inK, dtype2 inV, dtype3 inP) { - //============================================================================ - // Method Description: - /// Computes the incomplete elliptic integral of the second kind of k, v, and p. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inK: elliptic modulus or eccentricity - /// @param inV: elliptic characteristic - /// @param inP: Jacobi amplitude (measured in radians) - /// @return calculated-result-type - /// - template - auto ellint_3(dtype1 inK, dtype2 inV, dtype3 inP) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); - STATIC_ASSERT_ARITHMETIC(dtype3); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype3); #ifdef __cpp_lib_math_special_functions - return std::ellint_3(inK, inV, inP); + return std::ellint_3(inK, inV, inP); #else - return boost::math::ellint_3(inK, inV, inP); + return boost::math::ellint_3(inK, inV, inP); #endif - } + } - //============================================================================ - // Method Description: - /// Computes the incomplete elliptic integral of the second kind of k, v, and p. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inArrayK: the order of the bessel function - /// @param inArrayV: elliptic characteristic - /// @param inArrayP: Jacobi amplitude (measured in radians) - /// @return NdArray - /// - template - auto ellint_3(const NdArray& inArrayK, const NdArray& inArrayV, const NdArray& inArrayP) + //============================================================================ + // Method Description: + /// Computes the incomplete elliptic integral of the second kind of k, v, and p. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inArrayK: the order of the bessel function + /// @param inArrayV: elliptic characteristic + /// @param inArrayP: Jacobi amplitude (measured in radians) + /// @return NdArray + /// + template + auto ellint_3(const NdArray& inArrayK, const NdArray& inArrayV, const NdArray& inArrayP) + { + if (inArrayK.size() != inArrayV.size() || inArrayK.size() != inArrayP.size()) { - if (inArrayK.size() != inArrayV.size() || inArrayK.size() != inArrayP.size()) - { - THROW_INVALID_ARGUMENT_ERROR("Shapes of inArrayK, inArrayV, and inArrayP must match."); - } - - NdArray returnArray(inArrayK.shape()); + THROW_INVALID_ARGUMENT_ERROR("Shapes of inArrayK, inArrayV, and inArrayP must match."); + } - for (uint32 i = 0; i < inArrayK.size(); ++i) - { - returnArray[i] = ellint_3(inArrayK[i], inArrayV[i], inArrayP[i]); - } + NdArray returnArray(inArrayK.shape()); - return returnArray; + for (uint32 i = 0; i < inArrayK.size(); ++i) + { + returnArray[i] = ellint_3(inArrayK[i], inArrayV[i], inArrayP[i]); } - } // namespace special -} // namespace nc + + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/erf.hpp b/include/NumCpp/Special/erf.hpp index de4676268..60941690b 100644 --- a/include/NumCpp/Special/erf.hpp +++ b/include/NumCpp/Special/erf.hpp @@ -35,50 +35,47 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Calculate the error function of all elements in the input array. + /// Integral (from [-x, x]) of np.exp(np.power(-t, 2)) dt, multiplied by 1/np.pi. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inValue + /// @return calculated-result-type + /// + template + auto erf(dtype inValue) { - //============================================================================ - // Method Description: - /// Calculate the error function of all elements in the input array. - /// Integral (from [-x, x]) of np.exp(np.power(-t, 2)) dt, multiplied by 1/np.pi. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inValue - /// @return calculated-result-type - /// - template - auto erf(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::erf(inValue); - } + return boost::math::erf(inValue); + } - //============================================================================ - // Method Description: - /// Calculate the error function of all elements in the input array. - /// Integral (from [-x, x]) of np.exp(np.power(-t, 2)) dt, multiplied by 1/np.pi. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - template - auto erf(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// Calculate the error function of all elements in the input array. + /// Integral (from [-x, x]) of np.exp(np.power(-t, 2)) dt, multiplied by 1/np.pi. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + template + auto erf(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](dtype inValue) -> auto{ return erf(inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](dtype inValue) -> auto{ return erf(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/erf_inv.hpp b/include/NumCpp/Special/erf_inv.hpp index 92eb57d8b..8ca9f883b 100644 --- a/include/NumCpp/Special/erf_inv.hpp +++ b/include/NumCpp/Special/erf_inv.hpp @@ -35,50 +35,47 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Returns the inverse error function of z, that is a value x such that: + /// z = erf(x). + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inValue + /// @return calculated-result-type + /// + template + auto erf_inv(dtype inValue) { - //============================================================================ - // Method Description: - /// Returns the inverse error function of z, that is a value x such that: - /// z = erf(x). - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inValue - /// @return calculated-result-type - /// - template - auto erf_inv(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::erf_inv(inValue); - } + return boost::math::erf_inv(inValue); + } - //============================================================================ - // Method Description: - /// Returns the inverse error function of z, that is a value x such that: - /// z = erf(x). - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - template - auto erf_inv(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// Returns the inverse error function of z, that is a value x such that: + /// z = erf(x). + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + template + auto erf_inv(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](dtype inValue) -> auto{ return erf_inv(inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](dtype inValue) -> auto{ return erf_inv(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/erfc.hpp b/include/NumCpp/Special/erfc.hpp index c1a5cf63b..9d78d57fa 100644 --- a/include/NumCpp/Special/erfc.hpp +++ b/include/NumCpp/Special/erfc.hpp @@ -35,49 +35,46 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Returns the complement of the error function of inValue. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inValue + /// @return calculated-result-type + /// + template + auto erfc(dtype inValue) { - //============================================================================ - // Method Description: - /// Returns the complement of the error function of inValue. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inValue - /// @return calculated-result-type - /// - template - auto erfc(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::erfc(inValue); - } + return boost::math::erfc(inValue); + } - //============================================================================ - // Method Description: - /// Returns the element-wise complement of the error - /// function of inValue. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - template - auto erfc(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// Returns the element-wise complement of the error + /// function of inValue. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + template + auto erfc(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](dtype inValue) -> auto{ return erfc(inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](dtype inValue) -> auto{ return erfc(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/erfc_inv.hpp b/include/NumCpp/Special/erfc_inv.hpp index 14eba5b65..21374199a 100644 --- a/include/NumCpp/Special/erfc_inv.hpp +++ b/include/NumCpp/Special/erfc_inv.hpp @@ -35,50 +35,47 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Returns the inverse complentary error function of z, that is a value x such that: + /// z = erfc(x). + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inValue + /// @return calculated-result-type + /// + template + auto erfc_inv(dtype inValue) { - //============================================================================ - // Method Description: - /// Returns the inverse complentary error function of z, that is a value x such that: - /// z = erfc(x). - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inValue - /// @return calculated-result-type - /// - template - auto erfc_inv(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::erfc_inv(inValue); - } + return boost::math::erfc_inv(inValue); + } - //============================================================================ - // Method Description: - /// Returns the inverse complementary error function of z, that is a value x such that: - /// z = erfc(x). - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - template - auto erfc_inv(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// Returns the inverse complementary error function of z, that is a value x such that: + /// z = erfc(x). + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + template + auto erfc_inv(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](dtype inValue) -> auto{ return erfc_inv(inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](dtype inValue) -> auto{ return erfc_inv(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/expint.hpp b/include/NumCpp/Special/expint.hpp index 53dae5568..8a69e3370 100644 --- a/include/NumCpp/Special/expint.hpp +++ b/include/NumCpp/Special/expint.hpp @@ -41,54 +41,51 @@ #include -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Exponential integral Ei. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inX: value + /// @return calculated-result-type + /// + template + auto expint(dtype inX) { - //============================================================================ - // Method Description: - /// Exponential integral Ei. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inX: value - /// @return calculated-result-type - /// - template - auto expint(dtype inX) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); #ifdef __cpp_lib_math_special_functions - return std::expint(inX); + return std::expint(inX); #else - return boost::math::expint(inX); + return boost::math::expint(inX); #endif - } + } - //============================================================================ - // Method Description: - /// Exponential integral Ei. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inArrayX: value - /// @return NdArray - /// - template - auto expint(const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + //============================================================================ + // Method Description: + /// Exponential integral Ei. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inArrayX: value + /// @return NdArray + /// + template + auto expint(const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - stl_algorithms::transform( - inArrayX.cbegin(), - inArrayX.cend(), - returnArray.begin(), - [](dtype inX) -> auto{ return expint(inX); }); + stl_algorithms::transform( + inArrayX.cbegin(), + inArrayX.cend(), + returnArray.begin(), + [](dtype inX) -> auto{ return expint(inX); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/factorial.hpp b/include/NumCpp/Special/factorial.hpp index 608e75683..70c1b8be8 100644 --- a/include/NumCpp/Special/factorial.hpp +++ b/include/NumCpp/Special/factorial.hpp @@ -37,54 +37,51 @@ #include -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Returns the factorial of the input value + /// + /// @param inValue + /// @return double + /// + inline double factorial(uint32 inValue) { - //============================================================================ - // Method Description: - /// Returns the factorial of the input value - /// - /// @param inValue - /// @return double - /// - inline double factorial(uint32 inValue) - { #ifndef NUMCPP_NO_USE_BOOST - if (inValue <= boost::math::max_factorial::value) - { - return boost::math::factorial(inValue); - } + if (inValue <= boost::math::max_factorial::value) + { + return boost::math::factorial(inValue); + } - return std::numeric_limits::infinity(); + return std::numeric_limits::infinity(); #else - double result = 1.; - for (uint32 i = 2; i <= inValue; ++i) - { - result *= static_cast(i); - } + double result = 1.; + for (uint32 i = 2; i <= inValue; ++i) + { + result *= static_cast(i); + } - return result; + return result; #endif - } + } - //============================================================================ - // Method Description: - /// Returns the factorial of the input value - /// - /// @param inArray - /// @return NdArray - /// - inline NdArray factorial(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// Returns the factorial of the input value + /// + /// @param inArray + /// @return NdArray + /// + inline NdArray factorial(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform(inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](uint32 inValue) -> double { return factorial(inValue); }); + stl_algorithms::transform(inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](uint32 inValue) -> double { return factorial(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special diff --git a/include/NumCpp/Special/gamma.hpp b/include/NumCpp/Special/gamma.hpp index aa641e2ed..ef9fc96ba 100644 --- a/include/NumCpp/Special/gamma.hpp +++ b/include/NumCpp/Special/gamma.hpp @@ -35,48 +35,45 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Returns the "true gamma" of value z. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inValue + /// @return calculated-result-type + /// + template + auto gamma(dtype inValue) { - //============================================================================ - // Method Description: - /// Returns the "true gamma" of value z. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inValue - /// @return calculated-result-type - /// - template - auto gamma(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::tgamma(inValue); - } + return boost::math::tgamma(inValue); + } - //============================================================================ - // Method Description: - /// Returns the "true gamma" of values in array. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - template - auto gamma(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// Returns the "true gamma" of values in array. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + template + auto gamma(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](dtype inValue) -> auto{ return gamma(inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](dtype inValue) -> auto{ return gamma(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/gamma1pm1.hpp b/include/NumCpp/Special/gamma1pm1.hpp index 20f81b495..7b4748b6e 100644 --- a/include/NumCpp/Special/gamma1pm1.hpp +++ b/include/NumCpp/Special/gamma1pm1.hpp @@ -35,48 +35,45 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Returns the true gamma(dz + 1) - 1 of value z. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inValue + /// @return calculated-result-type + /// + template + auto gamma1pm1(dtype inValue) { - //============================================================================ - // Method Description: - /// Returns the true gamma(dz + 1) - 1 of value z. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inValue - /// @return calculated-result-type - /// - template - auto gamma1pm1(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::tgamma1pm1(inValue); - } + return boost::math::tgamma1pm1(inValue); + } - //============================================================================ - // Method Description: - /// Returns the true gamma(dz + 1) - 1 of values in array. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - template - auto gamma1pm1(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// Returns the true gamma(dz + 1) - 1 of values in array. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + template + auto gamma1pm1(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](dtype inValue) -> auto{ return gamma1pm1(inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](dtype inValue) -> auto{ return gamma1pm1(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/log_gamma.hpp b/include/NumCpp/Special/log_gamma.hpp index 3ddfcd0cc..d9830e3ac 100644 --- a/include/NumCpp/Special/log_gamma.hpp +++ b/include/NumCpp/Special/log_gamma.hpp @@ -35,48 +35,45 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Returns natural log of the true gamma of value z. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inValue + /// @return calculated-result-type + /// + template + auto log_gamma(dtype inValue) { - //============================================================================ - // Method Description: - /// Returns natural log of the true gamma of value z. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inValue - /// @return calculated-result-type - /// - template - auto log_gamma(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::lgamma(inValue); - } + return boost::math::lgamma(inValue); + } - //============================================================================ - // Method Description: - /// Returns natural log of the true gamma of values in array. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - template - auto log_gamma(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// Returns natural log of the true gamma of values in array. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + template + auto log_gamma(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](dtype inValue) -> auto{ return log_gamma(inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](dtype inValue) -> auto{ return log_gamma(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/pnr.hpp b/include/NumCpp/Special/pnr.hpp index 7e54bd160..2ee697e83 100644 --- a/include/NumCpp/Special/pnr.hpp +++ b/include/NumCpp/Special/pnr.hpp @@ -35,53 +35,50 @@ #include "boost/math/special_functions/factorials.hpp" #endif -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Returns the number of permutaions of n choose r. P(n, r) + /// + /// @param n: the total number of items + /// @param r: the number of items taken + /// @return double + /// + inline double pnr(uint32 n, uint32 r) { - //============================================================================ - // Method Description: - /// Returns the number of permutaions of n choose r. P(n, r) - /// - /// @param n: the total number of items - /// @param r: the number of items taken - /// @return double - /// - inline double pnr(uint32 n, uint32 r) + if (r > n) { - if (r > n) - { - return 0.; - } - else if (r == n) - { - return factorial(n); - } + return 0.; + } + else if (r == n) + { + return factorial(n); + } - double combinations = 1.; + double combinations = 1.; #ifndef NUMCPP_NO_USE_BOOST - if (n <= boost::math::max_factorial::value) - { - const double nFactorial = factorial(n); - const double nMinusRFactoral = factorial(n - r); + if (n <= boost::math::max_factorial::value) + { + const double nFactorial = factorial(n); + const double nMinusRFactoral = factorial(n - r); - combinations = nFactorial / nMinusRFactoral; - } - else - { + combinations = nFactorial / nMinusRFactoral; + } + else + { #endif - const uint32 lower = n - r + 1; - combinations = static_cast(lower); - for (uint32 i = lower + 1; i <= n; ++i) - { - combinations *= static_cast(i); - } -#ifndef NUMCPP_NO_USE_BOOST + const uint32 lower = n - r + 1; + combinations = static_cast(lower); + for (uint32 i = lower + 1; i <= n; ++i) + { + combinations *= static_cast(i); } +#ifndef NUMCPP_NO_USE_BOOST + } #endif - return combinations; - } - } // namespace special -} // namespace nc + return combinations; + } +} // namespace nc::special diff --git a/include/NumCpp/Special/polygamma.hpp b/include/NumCpp/Special/polygamma.hpp index 4d6f7929a..4d244aae0 100644 --- a/include/NumCpp/Special/polygamma.hpp +++ b/include/NumCpp/Special/polygamma.hpp @@ -35,52 +35,49 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Returns the polygamma function of inValue. Polygamma is defined as the + /// n'th derivative of the digamma function. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param n: the nth derivative + /// @param inValue + /// @return calculated-result-type + /// + template + auto polygamma(uint32 n, dtype inValue) { - //============================================================================ - // Method Description: - /// Returns the polygamma function of inValue. Polygamma is defined as the - /// n'th derivative of the digamma function. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param n: the nth derivative - /// @param inValue - /// @return calculated-result-type - /// - template - auto polygamma(uint32 n, dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::polygamma(n, inValue); - } + return boost::math::polygamma(n, inValue); + } - //============================================================================ - // Method Description: - /// Returns the polygamma function of the values in inArray. Polygamma is defined as the - /// n'th derivative of the digamma function. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param n: the nth derivative - /// @param inArray - /// @return NdArray - /// - template - auto polygamma(uint32 n, const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// Returns the polygamma function of the values in inArray. Polygamma is defined as the + /// n'th derivative of the digamma function. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param n: the nth derivative + /// @param inArray + /// @return NdArray + /// + template + auto polygamma(uint32 n, const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [n](dtype inValue) -> auto{ return polygamma(n, inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [n](dtype inValue) -> auto{ return polygamma(n, inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/prime.hpp b/include/NumCpp/Special/prime.hpp index 3cd5ac7e1..269a8ce94 100644 --- a/include/NumCpp/Special/prime.hpp +++ b/include/NumCpp/Special/prime.hpp @@ -38,51 +38,48 @@ #include "NumCpp/Core/Types.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// The function prime provides fast table lookup to the first 10000 prime numbers + /// (starting from 2 as the zeroth prime: as 1 isn't terribly useful in practice). + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param n: the nth prime number to return + /// @return uint32 + /// + inline uint32 prime(uint32 n) { - //============================================================================ - // Method Description: - /// The function prime provides fast table lookup to the first 10000 prime numbers - /// (starting from 2 as the zeroth prime: as 1 isn't terribly useful in practice). - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param n: the nth prime number to return - /// @return uint32 - /// - inline uint32 prime(uint32 n) + if (n > boost::math::max_prime) { - if (n > boost::math::max_prime) - { - THROW_INVALID_ARGUMENT_ERROR("input n must be less than or equal to " + - std::to_string(boost::math::max_prime)); - } - - return boost::math::prime(n); + THROW_INVALID_ARGUMENT_ERROR("input n must be less than or equal to " + + std::to_string(boost::math::max_prime)); } - //============================================================================ - // Method Description: - /// The function prime provides fast table lookup to the first 10000 prime numbers - /// (starting from 2 as the zeroth prime: as 1 isn't terribly useful in practice). - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - inline NdArray prime(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + return boost::math::prime(n); + } - stl_algorithms::transform(inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](uint32 inValue) -> uint32 { return prime(inValue); }); + //============================================================================ + // Method Description: + /// The function prime provides fast table lookup to the first 10000 prime numbers + /// (starting from 2 as the zeroth prime: as 1 isn't terribly useful in practice). + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + inline NdArray prime(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - return returnArray; - } - } // namespace special -} // namespace nc + stl_algorithms::transform(inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](uint32 inValue) -> uint32 { return prime(inValue); }); + + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/riemann_zeta.hpp b/include/NumCpp/Special/riemann_zeta.hpp index ba79b044f..40cf19d8c 100644 --- a/include/NumCpp/Special/riemann_zeta.hpp +++ b/include/NumCpp/Special/riemann_zeta.hpp @@ -39,56 +39,53 @@ #include "boost/math/special_functions/zeta.hpp" #endif -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// The Riemann Zeta function + /// https://en.wikipedia.org/wiki/Riemann_zeta_function + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inValue + /// @return calculated-result-type + /// + template + auto riemann_zeta(dtype inValue) { - //============================================================================ - // Method Description: - /// The Riemann Zeta function - /// https://en.wikipedia.org/wiki/Riemann_zeta_function - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inValue - /// @return calculated-result-type - /// - template - auto riemann_zeta(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); #ifdef __cpp_lib_math_special_functions - return std::riemann_zeta(inValue); + return std::riemann_zeta(inValue); #else - return boost::math::zeta(inValue); + return boost::math::zeta(inValue); #endif - } + } - //============================================================================ - // Method Description: - /// The Riemann Zeta function - /// https://en.wikipedia.org/wiki/Riemann_zeta_function - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inArray - /// @return NdArray - /// - template - auto riemann_zeta(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// The Riemann Zeta function + /// https://en.wikipedia.org/wiki/Riemann_zeta_function + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inArray + /// @return NdArray + /// + template + auto riemann_zeta(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](dtype inValue) -> auto{ return riemann_zeta(inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](dtype inValue) -> auto{ return riemann_zeta(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/softmax.hpp b/include/NumCpp/Special/softmax.hpp index 8f2422126..3f1489682 100644 --- a/include/NumCpp/Special/softmax.hpp +++ b/include/NumCpp/Special/softmax.hpp @@ -33,70 +33,67 @@ #include "NumCpp/Functions/exp.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// The softmax function transforms each element of a collection by computing + /// the exponential of each element divided by the sum of the exponentials of all + /// the elements. That is, if x is a one-dimensional numpy array: + /// softmax(x) = np.exp(x)/sum(np.exp(x)) + /// + /// @param inArray + /// @param inAxis (Optional, default NONE) + /// @return NdArray + /// + template + NdArray softmax(const NdArray& inArray, Axis inAxis = Axis::NONE) { - //============================================================================ - // Method Description: - /// The softmax function transforms each element of a collection by computing - /// the exponential of each element divided by the sum of the exponentials of all - /// the elements. That is, if x is a one-dimensional numpy array: - /// softmax(x) = np.exp(x)/sum(np.exp(x)) - /// - /// @param inArray - /// @param inAxis (Optional, default NONE) - /// @return NdArray - /// - template - NdArray softmax(const NdArray& inArray, Axis inAxis = Axis::NONE) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - switch (inAxis) + switch (inAxis) + { + case Axis::NONE: { - case Axis::NONE: - { - auto returnArray = exp(inArray).template astype(); - returnArray /= static_cast(returnArray.sum().item()); - return returnArray; - } - case Axis::COL: - { - auto returnArray = exp(inArray).template astype(); - auto expSums = returnArray.sum(inAxis); - - for (uint32 row = 0; row < returnArray.shape().rows; ++row) - { - const auto rowExpSum = static_cast(expSums[row]); - stl_algorithms::for_each(returnArray.begin(row), - returnArray.end(row), - [rowExpSum](double& value) { value /= rowExpSum; }); - } + auto returnArray = exp(inArray).template astype(); + returnArray /= static_cast(returnArray.sum().item()); + return returnArray; + } + case Axis::COL: + { + auto returnArray = exp(inArray).template astype(); + auto expSums = returnArray.sum(inAxis); - return returnArray; - } - case Axis::ROW: + for (uint32 row = 0; row < returnArray.shape().rows; ++row) { - auto returnArray = exp(inArray.transpose()).template astype(); - auto expSums = returnArray.sum(Axis::COL); + const auto rowExpSum = static_cast(expSums[row]); + stl_algorithms::for_each(returnArray.begin(row), + returnArray.end(row), + [rowExpSum](double& value) { value /= rowExpSum; }); + } - for (uint32 row = 0; row < returnArray.shape().rows; ++row) - { - const auto rowExpSum = static_cast(expSums[row]); - stl_algorithms::for_each(returnArray.begin(row), - returnArray.end(row), - [rowExpSum](double& value) { value /= rowExpSum; }); - } + return returnArray; + } + case Axis::ROW: + { + auto returnArray = exp(inArray.transpose()).template astype(); + auto expSums = returnArray.sum(Axis::COL); - return returnArray.transpose(); - } - default: + for (uint32 row = 0; row < returnArray.shape().rows; ++row) { - THROW_INVALID_ARGUMENT_ERROR("Unimplemented axis type."); - return {}; // get rid of compiler warning + const auto rowExpSum = static_cast(expSums[row]); + stl_algorithms::for_each(returnArray.begin(row), + returnArray.end(row), + [rowExpSum](double& value) { value /= rowExpSum; }); } + + return returnArray.transpose(); + } + default: + { + THROW_INVALID_ARGUMENT_ERROR("Unimplemented axis type."); + return {}; // get rid of compiler warning } } - } // namespace special -} // namespace nc + } +} // namespace nc::special diff --git a/include/NumCpp/Special/spherical_bessel_jn.hpp b/include/NumCpp/Special/spherical_bessel_jn.hpp index 21e7b0657..d0e07cb67 100644 --- a/include/NumCpp/Special/spherical_bessel_jn.hpp +++ b/include/NumCpp/Special/spherical_bessel_jn.hpp @@ -39,56 +39,53 @@ #include "boost/math/special_functions/bessel.hpp" #endif -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Spherical Bessel function of the first kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input value + /// @return calculated-result-type + /// + template + auto spherical_bessel_jn(uint32 inV, dtype inX) { - //============================================================================ - // Method Description: - /// Spherical Bessel function of the first kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input value - /// @return calculated-result-type - /// - template - auto spherical_bessel_jn(uint32 inV, dtype inX) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); #ifdef __cpp_lib_math_special_functions - return std::sph_bessel(inV, inX); + return std::sph_bessel(inV, inX); #else - return boost::math::sph_bessel(inV, inX); + return boost::math::sph_bessel(inV, inX); #endif - } + } - //============================================================================ - // Method Description: - /// Spherical Bessel function of the first kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inV: the order of the bessel function - /// @param inArrayX: the input values - /// @return NdArray - /// - template - auto spherical_bessel_jn(uint32 inV, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + //============================================================================ + // Method Description: + /// Spherical Bessel function of the first kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inV: the order of the bessel function + /// @param inArrayX: the input values + /// @return NdArray + /// + template + auto spherical_bessel_jn(uint32 inV, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - stl_algorithms::transform( - inArrayX.cbegin(), - inArrayX.cend(), - returnArray.begin(), - [inV](dtype inX) -> auto{ return spherical_bessel_jn(inV, inX); }); + stl_algorithms::transform( + inArrayX.cbegin(), + inArrayX.cend(), + returnArray.begin(), + [inV](dtype inX) -> auto{ return spherical_bessel_jn(inV, inX); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/spherical_bessel_yn.hpp b/include/NumCpp/Special/spherical_bessel_yn.hpp index 8eb3e8da8..50ffb2613 100644 --- a/include/NumCpp/Special/spherical_bessel_yn.hpp +++ b/include/NumCpp/Special/spherical_bessel_yn.hpp @@ -39,56 +39,53 @@ #include "boost/math/special_functions/bessel.hpp" #endif -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Spherical Bessel function of the second kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input value + /// @return calculated-result-type + /// + template + auto spherical_bessel_yn(uint32 inV, dtype inX) { - //============================================================================ - // Method Description: - /// Spherical Bessel function of the second kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input value - /// @return calculated-result-type - /// - template - auto spherical_bessel_yn(uint32 inV, dtype inX) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); #ifdef __cpp_lib_math_special_functions - return std::sph_neumann(inV, inX); + return std::sph_neumann(inV, inX); #else - return boost::math::sph_neumann(inV, inX); + return boost::math::sph_neumann(inV, inX); #endif - } + } - //============================================================================ - // Method Description: - /// Spherical Bessel function of the second kind. - /// NOTE: Use of this function requires either using the Boost - /// includes or a C++17 compliant compiler. - /// - /// @param inV: the order of the bessel function - /// @param inArrayX: the input values - /// @return NdArray - /// - template - auto spherical_bessel_yn(uint32 inV, const NdArray& inArrayX) - { - NdArray returnArray(inArrayX.shape()); + //============================================================================ + // Method Description: + /// Spherical Bessel function of the second kind. + /// NOTE: Use of this function requires either using the Boost + /// includes or a C++17 compliant compiler. + /// + /// @param inV: the order of the bessel function + /// @param inArrayX: the input values + /// @return NdArray + /// + template + auto spherical_bessel_yn(uint32 inV, const NdArray& inArrayX) + { + NdArray returnArray(inArrayX.shape()); - stl_algorithms::transform( - inArrayX.cbegin(), - inArrayX.cend(), - returnArray.begin(), - [inV](dtype inX) -> auto{ return spherical_bessel_yn(inV, inX); }); + stl_algorithms::transform( + inArrayX.cbegin(), + inArrayX.cend(), + returnArray.begin(), + [inV](dtype inX) -> auto{ return spherical_bessel_yn(inV, inX); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #if defined(__cpp_lib_math_special_functions) || !defined(NUMCPP_NO_USE_BOOST) diff --git a/include/NumCpp/Special/spherical_hankel_1.hpp b/include/NumCpp/Special/spherical_hankel_1.hpp index 8b14b828b..33d9293e5 100644 --- a/include/NumCpp/Special/spherical_hankel_1.hpp +++ b/include/NumCpp/Special/spherical_hankel_1.hpp @@ -37,51 +37,48 @@ #include "NumCpp/Core/Internal/StaticAsserts.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Spherical Hankel funcion of the first kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input value + /// @return calculated-result-type + /// + template + auto spherical_hankel_1(dtype1 inV, dtype2 inX) { - //============================================================================ - // Method Description: - /// Spherical Hankel funcion of the first kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input value - /// @return calculated-result-type - /// - template - auto spherical_hankel_1(dtype1 inV, dtype2 inX) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); - return boost::math::sph_hankel_1(inV, inX); - } + return boost::math::sph_hankel_1(inV, inX); + } - //============================================================================ - // Method Description: - /// Spherical Hankel funcion of the first kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inArray: the input values - /// @return NdArray - /// - template - auto spherical_hankel_1(dtype1 inV, const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// Spherical Hankel funcion of the first kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inArray: the input values + /// @return NdArray + /// + template + auto spherical_hankel_1(dtype1 inV, const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [inV](dtype2 inValue) -> auto{ return spherical_hankel_1(inV, inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [inV](dtype2 inValue) -> auto{ return spherical_hankel_1(inV, inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/spherical_hankel_2.hpp b/include/NumCpp/Special/spherical_hankel_2.hpp index 479b719e6..264d5ab68 100644 --- a/include/NumCpp/Special/spherical_hankel_2.hpp +++ b/include/NumCpp/Special/spherical_hankel_2.hpp @@ -37,51 +37,48 @@ #include "NumCpp/Core/Internal/StaticAsserts.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Spherical Hankel funcion of the second kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inX: the input value + /// @return double + /// + template + std::complex spherical_hankel_2(dtype1 inV, dtype2 inX) { - //============================================================================ - // Method Description: - /// Spherical Hankel funcion of the second kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inX: the input value - /// @return double - /// - template - std::complex spherical_hankel_2(dtype1 inV, dtype2 inX) - { - STATIC_ASSERT_ARITHMETIC(dtype1); - STATIC_ASSERT_ARITHMETIC(dtype2); + STATIC_ASSERT_ARITHMETIC(dtype1); + STATIC_ASSERT_ARITHMETIC(dtype2); - return boost::math::sph_hankel_2(inV, inX); - } + return boost::math::sph_hankel_2(inV, inX); + } - //============================================================================ - // Method Description: - /// Spherical Hankel funcion of the second kind. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inV: the order of the bessel function - /// @param inArray: the input value - /// @return NdArray - /// - template - auto spherical_hankel_2(dtype1 inV, const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// Spherical Hankel funcion of the second kind. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inV: the order of the bessel function + /// @param inArray: the input value + /// @return NdArray + /// + template + auto spherical_hankel_2(dtype1 inV, const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [inV](dtype2 inValue) -> auto{ return spherical_hankel_2(inV, inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [inV](dtype2 inValue) -> auto{ return spherical_hankel_2(inV, inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Special/trigamma.hpp b/include/NumCpp/Special/trigamma.hpp index c4d4413a9..f0a16368c 100644 --- a/include/NumCpp/Special/trigamma.hpp +++ b/include/NumCpp/Special/trigamma.hpp @@ -35,50 +35,47 @@ #include "NumCpp/Core/Internal/StlAlgorithms.hpp" #include "NumCpp/NdArray.hpp" -namespace nc +namespace nc::special { - namespace special + //============================================================================ + // Method Description: + /// Returns the trigamma function of x. Trigamma is defined as the derivative + /// of the digamma function. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inValue + /// @return calculated-result-type + /// + template + auto trigamma(dtype inValue) { - //============================================================================ - // Method Description: - /// Returns the trigamma function of x. Trigamma is defined as the derivative - /// of the digamma function. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inValue - /// @return calculated-result-type - /// - template - auto trigamma(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return boost::math::trigamma(inValue); - } + return boost::math::trigamma(inValue); + } - //============================================================================ - // Method Description: - /// Returns the trigamma function of x. Trigamma is defined as the derivative - /// of the digamma function. - /// NOTE: Use of this function requires using the Boost includes. - /// - /// @param inArray - /// @return NdArray - /// - template - auto trigamma(const NdArray& inArray) - { - NdArray returnArray(inArray.shape()); + //============================================================================ + // Method Description: + /// Returns the trigamma function of x. Trigamma is defined as the derivative + /// of the digamma function. + /// NOTE: Use of this function requires using the Boost includes. + /// + /// @param inArray + /// @return NdArray + /// + template + auto trigamma(const NdArray& inArray) + { + NdArray returnArray(inArray.shape()); - stl_algorithms::transform( - inArray.cbegin(), - inArray.cend(), - returnArray.begin(), - [](dtype inValue) -> auto{ return trigamma(inValue); }); + stl_algorithms::transform( + inArray.cbegin(), + inArray.cend(), + returnArray.begin(), + [](dtype inValue) -> auto{ return trigamma(inValue); }); - return returnArray; - } - } // namespace special -} // namespace nc + return returnArray; + } +} // namespace nc::special #endif // #ifndef NUMCPP_NO_USE_BOOST diff --git a/include/NumCpp/Utils/cube.hpp b/include/NumCpp/Utils/cube.hpp index 3f8cf1224..00d31efe1 100644 --- a/include/NumCpp/Utils/cube.hpp +++ b/include/NumCpp/Utils/cube.hpp @@ -29,23 +29,20 @@ #include "NumCpp/Core/Internal/StaticAsserts.hpp" -namespace nc +namespace nc::utils { - namespace utils + //============================================================================ + /// Cubes in input value + /// + /// @param inValue + /// + /// @return cubed value + /// + template + constexpr dtype cube(dtype inValue) noexcept { - //============================================================================ - /// Cubes in input value - /// - /// @param inValue - /// - /// @return cubed value - /// - template - constexpr dtype cube(dtype inValue) noexcept - { - STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); + STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); - return inValue * inValue * inValue; - } - } // namespace utils -} // namespace nc + return inValue * inValue * inValue; + } +} // namespace nc::utils diff --git a/include/NumCpp/Utils/essentiallyEqual.hpp b/include/NumCpp/Utils/essentiallyEqual.hpp index b82e951a6..12a96ce3a 100644 --- a/include/NumCpp/Utils/essentiallyEqual.hpp +++ b/include/NumCpp/Utils/essentiallyEqual.hpp @@ -35,102 +35,97 @@ #include "NumCpp/Core/Internal/StdComplexOperators.hpp" #include "NumCpp/Core/Internal/TypeTraits.hpp" -namespace nc +namespace nc::utils { - namespace utils + //============================================================================ + /// tests that 2 integer values are "essentially equal" + /// + /// @param inValue1 + /// @param inValue2 + /// + /// @return bool + /// + template::value, int> = 0> + bool essentiallyEqual(dtype inValue1, dtype inValue2) noexcept { - //============================================================================ - /// tests that 2 integer values are "essentially equal" - /// - /// @param inValue1 - /// @param inValue2 - /// - /// @return bool - /// - template::value, int> = 0> - bool essentiallyEqual(dtype inValue1, dtype inValue2) noexcept - { - return inValue1 == inValue2; - } + return inValue1 == inValue2; + } - //============================================================================ - /// tests that 2 floating point values are "essentially equal" - /// - /// @param inValue1 - /// @param inValue2 - /// @param inEpsilon - /// - /// @return bool - /// - template::value, int> = 0> - bool essentiallyEqual(dtype inValue1, dtype inValue2, dtype inEpsilon) noexcept - { - const auto absValue1 = std::abs(inValue1); - const auto absValue2 = std::abs(inValue2); - return std::abs(inValue1 - inValue2) <= - ((absValue1 > absValue2 ? absValue2 : absValue1) * std::abs(inEpsilon)); - } + //============================================================================ + /// tests that 2 floating point values are "essentially equal" + /// + /// @param inValue1 + /// @param inValue2 + /// @param inEpsilon + /// + /// @return bool + /// + template::value, int> = 0> + bool essentiallyEqual(dtype inValue1, dtype inValue2, dtype inEpsilon) noexcept + { + const auto absValue1 = std::abs(inValue1); + const auto absValue2 = std::abs(inValue2); + return std::abs(inValue1 - inValue2) <= ((absValue1 > absValue2 ? absValue2 : absValue1) * std::abs(inEpsilon)); + } - //============================================================================ - /// tests that 2 complex values are "essentially equal" - /// - /// @param inValue1 - /// @param inValue2 - /// - /// @return bool - /// - template::value, int> = 0> - bool essentiallyEqual(const std::complex& inValue1, const std::complex& inValue2) noexcept - { - return inValue1 == inValue2; - } + //============================================================================ + /// tests that 2 complex values are "essentially equal" + /// + /// @param inValue1 + /// @param inValue2 + /// + /// @return bool + /// + template::value, int> = 0> + bool essentiallyEqual(const std::complex& inValue1, const std::complex& inValue2) noexcept + { + return inValue1 == inValue2; + } - //============================================================================ - /// tests that 2 complex values are "essentially equal" - /// - /// @param inValue1 - /// @param inValue2 - /// @param inEpsilon - /// - /// @return bool - /// - template::value, int> = 0> - bool essentiallyEqual(const std::complex& inValue1, - const std::complex& inValue2, - const std::complex& inEpsilon) noexcept - { - const auto absValue1 = std::abs(inValue1); - const auto absValue2 = std::abs(inValue2); - return std::abs(inValue1 - inValue2) <= - ((absValue1 > absValue2 ? absValue2 : absValue1) * std::abs(inEpsilon)); - } + //============================================================================ + /// tests that 2 complex values are "essentially equal" + /// + /// @param inValue1 + /// @param inValue2 + /// @param inEpsilon + /// + /// @return bool + /// + template::value, int> = 0> + bool essentiallyEqual(const std::complex& inValue1, + const std::complex& inValue2, + const std::complex& inEpsilon) noexcept + { + const auto absValue1 = std::abs(inValue1); + const auto absValue2 = std::abs(inValue2); + return std::abs(inValue1 - inValue2) <= ((absValue1 > absValue2 ? absValue2 : absValue1) * std::abs(inEpsilon)); + } - //============================================================================ - /// tests that 2 floating point values are "essentially equal" - /// - /// @param inValue1 - /// @param inValue2 - /// - /// @return bool - /// - template::value, int> = 0> - bool essentiallyEqual(dtype inValue1, dtype inValue2) noexcept - { - return essentiallyEqual(inValue1, inValue2, DtypeInfo::epsilon()); - } + //============================================================================ + /// tests that 2 floating point values are "essentially equal" + /// + /// @param inValue1 + /// @param inValue2 + /// + /// @return bool + /// + template::value, int> = 0> + bool essentiallyEqual(dtype inValue1, dtype inValue2) noexcept + { + return essentiallyEqual(inValue1, inValue2, DtypeInfo::epsilon()); + } - //============================================================================ - /// tests that 2 floating point values are "essentially equal" - /// - /// @param inValue1 - /// @param inValue2 - /// - /// @return bool - /// - template::value, int> = 0> - bool essentiallyEqual(const std::complex& inValue1, const std::complex& inValue2) noexcept - { - return essentiallyEqual(inValue1, inValue2, DtypeInfo>::epsilon()); - } - } // namespace utils -} // namespace nc + //============================================================================ + /// tests that 2 floating point values are "essentially equal" + /// + /// @param inValue1 + /// @param inValue2 + /// + /// @return bool + /// + template::value, int> = 0> + bool essentiallyEqual(const std::complex& inValue1, const std::complex& inValue2) noexcept + { + return essentiallyEqual(inValue1, inValue2, DtypeInfo>::epsilon()); + } +} // namespace nc::utils diff --git a/include/NumCpp/Utils/gaussian.hpp b/include/NumCpp/Utils/gaussian.hpp index f4c99be4d..53a3b56c9 100644 --- a/include/NumCpp/Utils/gaussian.hpp +++ b/include/NumCpp/Utils/gaussian.hpp @@ -31,26 +31,23 @@ #include "NumCpp/Utils/sqr.hpp" -namespace nc +namespace nc::utils { - namespace utils + //============================================================================ + // Method Description: + /// samples a 2D gaussian of mean zero and input STD sigma + /// + /// @param inX + /// @param inY + /// @param inSigma + /// + /// @return dtype + /// + inline double gaussian(double inX, double inY, double inSigma) noexcept { - //============================================================================ - // Method Description: - /// samples a 2D gaussian of mean zero and input STD sigma - /// - /// @param inX - /// @param inY - /// @param inSigma - /// - /// @return dtype - /// - inline double gaussian(double inX, double inY, double inSigma) noexcept - { - double exponent = sqr(inX) + sqr(inY); - exponent /= 2; - exponent /= sqr(inSigma); - return std::exp(-exponent); - } - } // namespace utils -} // namespace nc + double exponent = sqr(inX) + sqr(inY); + exponent /= 2; + exponent /= sqr(inSigma); + return std::exp(-exponent); + } +} // namespace nc::utils diff --git a/include/NumCpp/Utils/gaussian1d.hpp b/include/NumCpp/Utils/gaussian1d.hpp index 267586573..543545a57 100644 --- a/include/NumCpp/Utils/gaussian1d.hpp +++ b/include/NumCpp/Utils/gaussian1d.hpp @@ -31,26 +31,23 @@ #include "NumCpp/Utils/sqr.hpp" -namespace nc +namespace nc::utils { - namespace utils + //============================================================================ + // Method Description: + /// samples a 1D gaussian of input mean and sigma + /// + /// @param inX + /// @param inMu + /// @param inSigma + /// + /// @return dtype + /// + inline double gaussian1d(double inX, double inMu, double inSigma) noexcept { - //============================================================================ - // Method Description: - /// samples a 1D gaussian of input mean and sigma - /// - /// @param inX - /// @param inMu - /// @param inSigma - /// - /// @return dtype - /// - inline double gaussian1d(double inX, double inMu, double inSigma) noexcept - { - double exponent = sqr(inX - inMu); - exponent /= 2; - exponent /= sqr(inSigma); - return std::exp(-exponent); - } - } // namespace utils -} // namespace nc + double exponent = sqr(inX - inMu); + exponent /= 2; + exponent /= sqr(inSigma); + return std::exp(-exponent); + } +} // namespace nc::utils diff --git a/include/NumCpp/Utils/interp.hpp b/include/NumCpp/Utils/interp.hpp index 3784a368d..87ef6891b 100644 --- a/include/NumCpp/Utils/interp.hpp +++ b/include/NumCpp/Utils/interp.hpp @@ -27,22 +27,19 @@ /// #pragma once -namespace nc +namespace nc::utils { - namespace utils + //============================================================================ + /// Returns the linear interpolation between two points + /// + /// @param inValue1 + /// @param inValue2 + /// @param inPercent + /// + /// @return linear interpolated point + /// + constexpr double interp(double inValue1, double inValue2, double inPercent) noexcept { - //============================================================================ - /// Returns the linear interpolation between two points - /// - /// @param inValue1 - /// @param inValue2 - /// @param inPercent - /// - /// @return linear interpolated point - /// - constexpr double interp(double inValue1, double inValue2, double inPercent) noexcept - { - return inValue1 * (1. - inPercent) + inValue2 * inPercent; - } - } // namespace utils -} // namespace nc + return inValue1 * (1. - inPercent) + inValue2 * inPercent; + } +} // namespace nc::utils diff --git a/include/NumCpp/Utils/num2str.hpp b/include/NumCpp/Utils/num2str.hpp index 3bc04aaea..00f0ccc88 100644 --- a/include/NumCpp/Utils/num2str.hpp +++ b/include/NumCpp/Utils/num2str.hpp @@ -31,23 +31,20 @@ #include "NumCpp/Core/Internal/StaticAsserts.hpp" -namespace nc +namespace nc::utils { - namespace utils + //============================================================================ + /// Converts the number into a string + /// + /// @param inNumber + /// + /// @return std::string + /// + template + std::string num2str(dtype inNumber) { - //============================================================================ - /// Converts the number into a string - /// - /// @param inNumber - /// - /// @return std::string - /// - template - std::string num2str(dtype inNumber) - { - STATIC_ASSERT_ARITHMETIC(dtype); + STATIC_ASSERT_ARITHMETIC(dtype); - return std::to_string(inNumber); - } - } // namespace utils -} // namespace nc + return std::to_string(inNumber); + } +} // namespace nc::utils diff --git a/include/NumCpp/Utils/power.hpp b/include/NumCpp/Utils/power.hpp index 2493c0387..8d4295535 100644 --- a/include/NumCpp/Utils/power.hpp +++ b/include/NumCpp/Utils/power.hpp @@ -32,34 +32,31 @@ #include "NumCpp/Core/Internal/StaticAsserts.hpp" #include "NumCpp/Core/Types.hpp" -namespace nc +namespace nc::utils { - namespace utils + //============================================================================ + /// Raises the input value to an integer power + /// + /// @param inValue + /// @param inPower + /// + /// @return inValue raised to inPower + /// + template + dtype power(dtype inValue, uint8 inPower) noexcept { - //============================================================================ - /// Raises the input value to an integer power - /// - /// @param inValue - /// @param inPower - /// - /// @return inValue raised to inPower - /// - template - dtype power(dtype inValue, uint8 inPower) noexcept - { - STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); + STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); - if (inPower == 0) - { - return static_cast(1); - } + if (inPower == 0) + { + return static_cast(1); + } - dtype returnVal = inValue; - for (uint8 exponent = 1; exponent < inPower; ++exponent) - { - returnVal *= inValue; - } - return returnVal; + dtype returnVal = inValue; + for (uint8 exponent = 1; exponent < inPower; ++exponent) + { + returnVal *= inValue; } - } // namespace utils -} // namespace nc + return returnVal; + } +} // namespace nc::utils diff --git a/include/NumCpp/Utils/powerf.hpp b/include/NumCpp/Utils/powerf.hpp index 498ce6b20..73e36e6cf 100644 --- a/include/NumCpp/Utils/powerf.hpp +++ b/include/NumCpp/Utils/powerf.hpp @@ -33,24 +33,21 @@ #include "NumCpp/Core/Internal/StaticAsserts.hpp" #include "NumCpp/Core/Internal/TypeTraits.hpp" -namespace nc +namespace nc::utils { - namespace utils + //============================================================================ + /// Raises the input value to a floating point power + /// + /// @param inValue + /// @param inPower + /// + /// @return inValue raised to inPower + /// + template + auto powerf(dtype1 inValue, const dtype2 inPower) noexcept { - //============================================================================ - /// Raises the input value to a floating point power - /// - /// @param inValue - /// @param inPower - /// - /// @return inValue raised to inPower - /// - template - auto powerf(dtype1 inValue, const dtype2 inPower) noexcept - { - STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype1); + STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype1); - return std::pow(inValue, inPower); - } - } // namespace utils -} // namespace nc + return std::pow(inValue, inPower); + } +} // namespace nc::utils diff --git a/include/NumCpp/Utils/sqr.hpp b/include/NumCpp/Utils/sqr.hpp index 72fb5e2d2..7d7bd0462 100644 --- a/include/NumCpp/Utils/sqr.hpp +++ b/include/NumCpp/Utils/sqr.hpp @@ -29,23 +29,20 @@ #include "NumCpp/Core/Internal/StaticAsserts.hpp" -namespace nc +namespace nc::utils { - namespace utils + //============================================================================ + /// Squares in input value + /// + /// @param inValue + /// + /// @return squared value + /// + template + constexpr dtype sqr(dtype inValue) noexcept { - //============================================================================ - /// Squares in input value - /// - /// @param inValue - /// - /// @return squared value - /// - template - constexpr dtype sqr(dtype inValue) noexcept - { - STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); + STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); - return inValue * inValue; - } - } // namespace utils -} // namespace nc + return inValue * inValue; + } +} // namespace nc::utils diff --git a/include/NumCpp/Utils/value2str.hpp b/include/NumCpp/Utils/value2str.hpp index 7a1a1c217..71a4d0648 100644 --- a/include/NumCpp/Utils/value2str.hpp +++ b/include/NumCpp/Utils/value2str.hpp @@ -33,25 +33,22 @@ #include "NumCpp/Core/Internal/StaticAsserts.hpp" -namespace nc +namespace nc::utils { - namespace utils + //============================================================================ + /// Converts the value into a string + /// + /// @param inValue + /// + /// @return std::string + /// + template + std::string value2str(dtype inValue) { - //============================================================================ - /// Converts the value into a string - /// - /// @param inValue - /// - /// @return std::string - /// - template - std::string value2str(dtype inValue) - { - STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); + STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype); - std::stringstream ss; - ss << inValue; - return ss.str(); - } - } // namespace utils -} // namespace nc + std::stringstream ss; + ss << inValue; + return ss.str(); + } +} // namespace nc::utils diff --git a/include/NumCpp/Vector/Vec2.hpp b/include/NumCpp/Vector/Vec2.hpp index 5ceedfceb..301cd2e44 100644 --- a/include/NumCpp/Vector/Vec2.hpp +++ b/include/NumCpp/Vector/Vec2.hpp @@ -112,7 +112,7 @@ namespace nc /// @param otherVec /// @return the angle in radians /// - double angle(const Vec2& otherVec) const noexcept + [[nodiscard]] double angle(const Vec2& otherVec) const noexcept { double dotProduct = dot(otherVec); dotProduct /= norm(); @@ -132,7 +132,7 @@ namespace nc /// @param maxLength /// @return Vec2 /// - Vec2 clampMagnitude(double maxLength) const noexcept + [[nodiscard]] Vec2 clampMagnitude(double maxLength) const noexcept { const double magnitude = norm(); if (magnitude <= maxLength) @@ -152,7 +152,7 @@ namespace nc /// @param otherVec /// @return the distance (equivalent to (a - b).norm() /// - double distance(const Vec2& otherVec) const noexcept + [[nodiscard]] double distance(const Vec2& otherVec) const noexcept { return (Vec2(*this) -= otherVec).norm(); } @@ -164,7 +164,7 @@ namespace nc /// @param otherVec /// @return the dot product /// - double dot(const Vec2& otherVec) const noexcept + [[nodiscard]] double dot(const Vec2& otherVec) const noexcept { return x * otherVec.x + y * otherVec.y; } @@ -199,7 +199,7 @@ namespace nc /// @param t the amount to interpolate by (clamped from [0, 1]); /// @return Vec2 /// - Vec2 lerp(const Vec2& otherVec, double t) const noexcept + [[nodiscard]] Vec2 lerp(const Vec2& otherVec, double t) const noexcept { t = std::max(std::min(t, 1.), 0.); @@ -217,7 +217,7 @@ namespace nc /// /// @return magnitude of the vector /// - double norm() const noexcept + [[nodiscard]] double norm() const noexcept { return std::hypot(x, y); } @@ -228,7 +228,7 @@ namespace nc /// /// @return Vec2 /// - Vec2 normalize() const noexcept + [[nodiscard]] Vec2 normalize() const noexcept { return Vec2(*this) /= norm(); } @@ -240,7 +240,7 @@ namespace nc /// @param otherVec /// @return Vec2 /// - Vec2 project(const Vec2& otherVec) const noexcept + [[nodiscard]] Vec2 project(const Vec2& otherVec) const noexcept { const double projectedMagnitude = norm() * std::cos(angle(otherVec)); return otherVec.normalize() *= projectedMagnitude; @@ -263,7 +263,7 @@ namespace nc /// /// @return std::string /// - std::string toString() const + [[nodiscard]] std::string toString() const { std::stringstream stream; stream << "Vec2[" << x << ", " << y << "]"; @@ -276,7 +276,7 @@ namespace nc /// /// @return NdArray /// - NdArray toNdArray() const + [[nodiscard]] NdArray toNdArray() const { NdArray returnArray = { x, y }; return returnArray.transpose(); @@ -449,7 +449,7 @@ namespace nc /// inline Vec2 operator-(const Vec2& vec) noexcept { - return { -vec.x, -vec.y }; + return Vec2(-vec.x, -vec.y); } //============================================================================ diff --git a/include/NumCpp/Vector/Vec3.hpp b/include/NumCpp/Vector/Vec3.hpp index be483b8f7..e667fc4c2 100644 --- a/include/NumCpp/Vector/Vec3.hpp +++ b/include/NumCpp/Vector/Vec3.hpp @@ -118,7 +118,7 @@ namespace nc /// @param otherVec /// @return the angle in radians /// - double angle(const Vec3& otherVec) const noexcept + [[nodiscard]] double angle(const Vec3& otherVec) const noexcept { double dotProduct = dot(otherVec); dotProduct /= norm(); @@ -149,7 +149,7 @@ namespace nc /// @param maxLength /// @return Vec3 /// - Vec3 clampMagnitude(double maxLength) const noexcept + [[nodiscard]] Vec3 clampMagnitude(double maxLength) const noexcept { const double magnitude = norm(); if (magnitude <= maxLength) @@ -169,13 +169,13 @@ namespace nc /// @param otherVec /// @return the dot product /// - Vec3 cross(const Vec3& otherVec) const noexcept + [[nodiscard]] Vec3 cross(const Vec3& otherVec) const noexcept { const double crossX = y * otherVec.z - z * otherVec.y; const double crossY = -(x * otherVec.z - z * otherVec.x); const double crossZ = x * otherVec.y - y * otherVec.x; - return { crossX, crossY, crossZ }; + return Vec3(crossX, crossY, crossZ); } //============================================================================ @@ -185,7 +185,7 @@ namespace nc /// @param otherVec /// @return the distance (equivalent to (a - b).norm() /// - double distance(const Vec3& otherVec) const noexcept + [[nodiscard]] double distance(const Vec3& otherVec) const noexcept { return (Vec3(*this) -= otherVec).norm(); } @@ -197,7 +197,7 @@ namespace nc /// @param otherVec /// @return the dot product /// - double dot(const Vec3& otherVec) const noexcept + [[nodiscard]] double dot(const Vec3& otherVec) const noexcept { return x * otherVec.x + y * otherVec.y + z * otherVec.z; } @@ -243,7 +243,7 @@ namespace nc /// @param t the amount to interpolate by (clamped from [0, 1]); /// @return Vec3 /// - Vec3 lerp(const Vec3& otherVec, double t) const noexcept + [[nodiscard]] Vec3 lerp(const Vec3& otherVec, double t) const noexcept { t = std::max(std::min(t, 1.), 0.); @@ -262,7 +262,7 @@ namespace nc /// /// @return magnitude of the vector /// - double norm() const noexcept + [[nodiscard]] double norm() const noexcept { return hypot(x, y, z); } @@ -273,7 +273,7 @@ namespace nc /// /// @return Vec3 /// - Vec3 normalize() const noexcept + [[nodiscard]] Vec3 normalize() const noexcept { return Vec3(*this) /= norm(); } @@ -285,7 +285,7 @@ namespace nc /// @param otherVec /// @return Vec3 /// - Vec3 project(const Vec3& otherVec) const noexcept + [[nodiscard]] Vec3 project(const Vec3& otherVec) const noexcept { const double projectedMagnitude = norm() * std::cos(angle(otherVec)); return otherVec.normalize() *= projectedMagnitude; @@ -308,7 +308,7 @@ namespace nc /// /// @return std::string /// - std::string toString() const + [[nodiscard]] std::string toString() const { std::stringstream stream; stream << "Vec3[" << x << ", " << y << ", " << z << "]"; @@ -321,7 +321,7 @@ namespace nc /// /// @return NdArray /// - NdArray toNdArray() const + [[nodiscard]] NdArray toNdArray() const { NdArray returnArray = { x, y, z }; return returnArray.transpose(); @@ -501,7 +501,7 @@ namespace nc /// inline Vec3 operator-(const Vec3& vec) noexcept { - return { -vec.x, -vec.y, -vec.z }; + return Vec3(-vec.x, -vec.y, -vec.z); } //============================================================================ diff --git a/static_analysis/clang-format-all.sh b/static_analysis/clang-format-all.sh new file mode 100755 index 000000000..9a766b1cb --- /dev/null +++ b/static_analysis/clang-format-all.sh @@ -0,0 +1 @@ +find include -type f \( -name "*.hpp" \) -exec clang-format -style=file --fallback-style=none -i {} \; \ No newline at end of file diff --git a/supressions.txt b/supressions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/test/src/CMakeLists.txt b/test/src/CMakeLists.txt index 17c3424d5..1bf5b8c80 100644 --- a/test/src/CMakeLists.txt +++ b/test/src/CMakeLists.txt @@ -56,14 +56,45 @@ find_package(Python 3.8 REQUIRED target_link_libraries(${TARGET_NAME} PRIVATE Python::Module + ${ALL_INTERFACE_TARGET} ) target_compile_definitions(${TARGET_NAME} PRIVATE -DNUMCPP_INCLUDE_PYBIND_PYTHON_INTERFACE ) -target_link_libraries(${TARGET_NAME} PRIVATE - ${ALL_INTERFACE_TARGET} +target_compile_options(${TARGET_NAME} PRIVATE + $<$,$>:-W> + $<$,$>:-Wall> + $<$,$>:-Wextra> + $<$,$>:-Werror> + $<$,$>:-Wdouble-promotion> + $<$,$>:-Wunused> + # $<$,$>:-Wshadow> + # $<$,$>:-Wconversion> + $<$,$>:-Wpedantic> + $<$,$>:-pedantic-errors> + $<$,$>:-Waggregate-return> + $<$,$>:-Wbad-function-cast> + $<$,$>:-Wcast-align> + $<$,$>:-Wcast-qual> + $<$,$>:-Wdeclaration-after-statement> + # $<$,$>:-Wfloat-equal> + $<$,$>:-Wformat=2> + $<$,$>:-Wmissing-declarations> + $<$,$>:-Wmissing-include-dirs> + $<$,$>:-Wnested-externs> + $<$,$>:-Wpointer-arith> + $<$,$>:-Wredundant-decls> + $<$,$>:-Wsequence-point> + $<$,$>:-Wstrict-prototypes> + $<$,$>:-Wswitch> + $<$,$>:-Wundef> + $<$,$>:-Wunreachable-code> + $<$,$>:-Wunused-but-set-parameter> + $<$,$>:-Wwrite-strings> + $<$,$>:-Wunused-parameter> + $<$:/W4> ) target_link_options(${TARGET_NAME} PRIVATE diff --git a/test/src/NdArray.cpp b/test/src/NdArray.cpp index 08d19caa6..11bbf2fd6 100644 --- a/test/src/NdArray.cpp +++ b/test/src/NdArray.cpp @@ -481,18 +481,18 @@ namespace NdArrayInterface test2.end(0); test2.resizeFast({ 10, 10 }); - test2 = TestStruct{ 666, 357, 3.14519, true }; - test2.rSlice(); - test2.cSlice(); + test2 = TestStruct{ 666, 357, 3.14519, true }; + [[maybe_unused]] const auto slice1 = test2.rSlice(); + [[maybe_unused]] const auto slice2 = test2.cSlice(); test2.back(); test2.column(0); - test2.copy(); + [[maybe_unused]] const auto c = test2.copy(); test2.data(); - test2.diagonal(); + [[maybe_unused]] const auto d = test2.diagonal(); test2.dump("test.bin"); remove("test.bin"); test2.fill(TestStruct{ 0, 1, 6.5, false }); - test2.flatten(); + [[maybe_unused]] const auto f = test2.flatten(); test2.front(); test2[0]; test2(0, 0); @@ -501,20 +501,20 @@ namespace NdArrayInterface test2.isempty(); test2.isflat(); test2.issquare(); - test2.nbytes(); - test2.numRows(); - test2.numCols(); + [[maybe_unused]] const auto nb = test2.nbytes(); + [[maybe_unused]] const auto rows = test2.numRows(); + [[maybe_unused]] const auto cols = test2.numCols(); test2.put(0, TestStruct{ 0, 1, 6.5, false }); test2.ravel(); - test2.repeat({ 2, 2 }); + [[maybe_unused]] const auto r = test2.repeat({ 2, 2 }); test2.reshape(test2.size(), 1); test2.resizeFast(1, 1); test2.resizeSlow(10, 10); test2.row(0); - test2.shape(); - test2.size(); - test2.swapaxes(); - test2.transpose(); + [[maybe_unused]] const auto s1 = test2.shape(); + [[maybe_unused]] const auto s2 = test2.size(); + [[maybe_unused]] const auto s3 = test2.swapaxes(); + [[maybe_unused]] const auto t = test2.transpose(); return true; } From 22d8caa1aba127a2dd85fbac31c1f44b3ccac934 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sun, 19 Feb 2023 09:47:02 -0700 Subject: [PATCH 07/65] adding pybind11 as system lib in bindings --- test/src/CMakeLists.txt | 7 +- test/src/pybind11/buffer_info.h | 146 - test/src/pybind11/cast.h | 2231 ------------- test/src/pybind11/chrono.h | 191 -- test/src/pybind11/detail/common.h | 855 ----- test/src/pybind11/detail/descr.h | 100 - test/src/pybind11/detail/internals.h | 363 --- test/src/pybind11/embed.h | 201 -- test/src/pybind11/functional.h | 104 - test/src/pybind11/iostream.h | 218 -- test/src/pybind11/numpy.h | 1693 ---------- test/src/pybind11/operators.h | 173 - test/src/pybind11/options.h | 65 - test/src/pybind11/pybind11.h | 2401 -------------- test/src/pybind11/{ => pybind11}/attr.h | 357 +- test/src/pybind11/pybind11/buffer_info.h | 193 ++ test/src/pybind11/pybind11/cast.h | 1672 ++++++++++ test/src/pybind11/pybind11/chrono.h | 225 ++ test/src/pybind11/{ => pybind11}/common.h | 0 test/src/pybind11/{ => pybind11}/complex.h | 31 +- .../pybind11/{ => pybind11}/detail/class.h | 339 +- test/src/pybind11/pybind11/detail/common.h | 1236 +++++++ test/src/pybind11/pybind11/detail/descr.h | 158 + .../src/pybind11/{ => pybind11}/detail/init.h | 294 +- test/src/pybind11/pybind11/detail/internals.h | 614 ++++ .../pybind11/detail/type_caster_base.h | 1019 ++++++ .../pybind11/{ => pybind11}/detail/typeid.h | 30 +- test/src/pybind11/pybind11/eigen.h | 12 + .../{eigen.h => pybind11/eigen/matrix.h} | 540 +-- test/src/pybind11/pybind11/eigen/tensor.h | 511 +++ test/src/pybind11/pybind11/embed.h | 318 ++ test/src/pybind11/{ => pybind11}/eval.h | 112 +- test/src/pybind11/pybind11/functional.h | 137 + test/src/pybind11/pybind11/gil.h | 239 ++ test/src/pybind11/pybind11/iostream.h | 265 ++ test/src/pybind11/pybind11/numpy.h | 1992 ++++++++++++ test/src/pybind11/pybind11/operators.h | 202 ++ test/src/pybind11/pybind11/options.h | 92 + test/src/pybind11/pybind11/pybind11.h | 2882 +++++++++++++++++ test/src/pybind11/pybind11/pytypes.h | 2487 ++++++++++++++ test/src/pybind11/{ => pybind11}/stl.h | 301 +- test/src/pybind11/pybind11/stl/filesystem.h | 116 + test/src/pybind11/pybind11/stl_bind.h | 849 +++++ test/src/pybind11/pytypes.h | 1659 ---------- test/src/pybind11/stl_bind.h | 661 ---- 45 files changed, 16442 insertions(+), 11849 deletions(-) delete mode 100644 test/src/pybind11/buffer_info.h delete mode 100644 test/src/pybind11/cast.h delete mode 100644 test/src/pybind11/chrono.h delete mode 100644 test/src/pybind11/detail/common.h delete mode 100644 test/src/pybind11/detail/descr.h delete mode 100644 test/src/pybind11/detail/internals.h delete mode 100644 test/src/pybind11/embed.h delete mode 100644 test/src/pybind11/functional.h delete mode 100644 test/src/pybind11/iostream.h delete mode 100644 test/src/pybind11/numpy.h delete mode 100644 test/src/pybind11/operators.h delete mode 100644 test/src/pybind11/options.h delete mode 100644 test/src/pybind11/pybind11.h rename test/src/pybind11/{ => pybind11}/attr.h (60%) create mode 100644 test/src/pybind11/pybind11/buffer_info.h create mode 100644 test/src/pybind11/pybind11/cast.h create mode 100644 test/src/pybind11/pybind11/chrono.h rename test/src/pybind11/{ => pybind11}/common.h (100%) rename test/src/pybind11/{ => pybind11}/complex.h (61%) rename test/src/pybind11/{ => pybind11}/detail/class.h (74%) create mode 100644 test/src/pybind11/pybind11/detail/common.h create mode 100644 test/src/pybind11/pybind11/detail/descr.h rename test/src/pybind11/{ => pybind11}/detail/init.h (58%) create mode 100644 test/src/pybind11/pybind11/detail/internals.h create mode 100644 test/src/pybind11/pybind11/detail/type_caster_base.h rename test/src/pybind11/{ => pybind11}/detail/typeid.h (72%) create mode 100644 test/src/pybind11/pybind11/eigen.h rename test/src/pybind11/{eigen.h => pybind11/eigen/matrix.h} (51%) create mode 100644 test/src/pybind11/pybind11/eigen/tensor.h create mode 100644 test/src/pybind11/pybind11/embed.h rename test/src/pybind11/{ => pybind11}/eval.h (54%) create mode 100644 test/src/pybind11/pybind11/functional.h create mode 100644 test/src/pybind11/pybind11/gil.h create mode 100644 test/src/pybind11/pybind11/iostream.h create mode 100644 test/src/pybind11/pybind11/numpy.h create mode 100644 test/src/pybind11/pybind11/operators.h create mode 100644 test/src/pybind11/pybind11/options.h create mode 100644 test/src/pybind11/pybind11/pybind11.h create mode 100644 test/src/pybind11/pybind11/pytypes.h rename test/src/pybind11/{ => pybind11}/stl.h (51%) create mode 100644 test/src/pybind11/pybind11/stl/filesystem.h create mode 100644 test/src/pybind11/pybind11/stl_bind.h delete mode 100644 test/src/pybind11/pytypes.h delete mode 100644 test/src/pybind11/stl_bind.h diff --git a/test/src/CMakeLists.txt b/test/src/CMakeLists.txt index 1bf5b8c80..ef6d39b2f 100644 --- a/test/src/CMakeLists.txt +++ b/test/src/CMakeLists.txt @@ -43,8 +43,9 @@ add_library(${TARGET_NAME} SHARED ${_source_list} ) -target_include_directories(${TARGET_NAME} SYSTEM PRIVATE .) +target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/pybind11) target_include_directories(${TARGET_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} ${NUMCPP_INCLUDES} ) @@ -70,18 +71,16 @@ target_compile_options(${TARGET_NAME} PRIVATE $<$,$>:-Werror> $<$,$>:-Wdouble-promotion> $<$,$>:-Wunused> - # $<$,$>:-Wshadow> + $<$,$>:-Wshadow> # $<$,$>:-Wconversion> $<$,$>:-Wpedantic> $<$,$>:-pedantic-errors> $<$,$>:-Waggregate-return> - $<$,$>:-Wbad-function-cast> $<$,$>:-Wcast-align> $<$,$>:-Wcast-qual> $<$,$>:-Wdeclaration-after-statement> # $<$,$>:-Wfloat-equal> $<$,$>:-Wformat=2> - $<$,$>:-Wmissing-declarations> $<$,$>:-Wmissing-include-dirs> $<$,$>:-Wnested-externs> $<$,$>:-Wpointer-arith> diff --git a/test/src/pybind11/buffer_info.h b/test/src/pybind11/buffer_info.h deleted file mode 100644 index d803004a1..000000000 --- a/test/src/pybind11/buffer_info.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - pybind11/buffer_info.h: Python buffer object interface - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "detail/common.h" - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -PYBIND11_NAMESPACE_BEGIN(detail) - -// Default, C-style strides -inline std::vector c_strides(const std::vector &shape, ssize_t itemsize) { - auto ndim = shape.size(); - std::vector strides(ndim, itemsize); - if (ndim > 0) - for (size_t i = ndim - 1; i > 0; --i) - strides[i - 1] = strides[i] * shape[i]; - return strides; -} - -// F-style strides; default when constructing an array_t with `ExtraFlags & f_style` -inline std::vector f_strides(const std::vector &shape, ssize_t itemsize) { - auto ndim = shape.size(); - std::vector strides(ndim, itemsize); - for (size_t i = 1; i < ndim; ++i) - strides[i] = strides[i - 1] * shape[i - 1]; - return strides; -} - -PYBIND11_NAMESPACE_END(detail) - -/// Information record describing a Python buffer object -struct buffer_info { - void *ptr = nullptr; // Pointer to the underlying storage - ssize_t itemsize = 0; // Size of individual items in bytes - ssize_t size = 0; // Total number of entries - std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() - ssize_t ndim = 0; // Number of dimensions - std::vector shape; // Shape of the tensor (1 entry per dimension) - std::vector strides; // Number of bytes between adjacent entries (for each per dimension) - bool readonly = false; // flag to indicate if the underlying storage may be written to - - buffer_info() = default; - - buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container shape_in, detail::any_container strides_in, bool readonly=false) - : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), - shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { - if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) - pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); - for (size_t i = 0; i < (size_t) ndim; ++i) - size *= shape[i]; - } - - template - buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in, bool readonly=false) - : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in), readonly) { } - - buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size, bool readonly=false) - : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) { } - - template - buffer_info(T *ptr, ssize_t size, bool readonly=false) - : buffer_info(ptr, sizeof(T), format_descriptor::format(), size, readonly) { } - - template - buffer_info(const T *ptr, ssize_t size, bool readonly=true) - : buffer_info(const_cast(ptr), sizeof(T), format_descriptor::format(), size, readonly) { } - - explicit buffer_info(Py_buffer *view, bool ownview = true) - : buffer_info(view->buf, view->itemsize, view->format, view->ndim, - {view->shape, view->shape + view->ndim}, - /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects - * ignore this flag and return a view with NULL strides. - * When strides are NULL, build them manually. */ - view->strides - ? std::vector(view->strides, view->strides + view->ndim) - : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), - view->readonly) { - this->m_view = view; - this->ownview = ownview; - } - - buffer_info(const buffer_info &) = delete; - buffer_info& operator=(const buffer_info &) = delete; - - buffer_info(buffer_info &&other) { - (*this) = std::move(other); - } - - buffer_info& operator=(buffer_info &&rhs) { - ptr = rhs.ptr; - itemsize = rhs.itemsize; - size = rhs.size; - format = std::move(rhs.format); - ndim = rhs.ndim; - shape = std::move(rhs.shape); - strides = std::move(rhs.strides); - std::swap(m_view, rhs.m_view); - std::swap(ownview, rhs.ownview); - readonly = rhs.readonly; - return *this; - } - - ~buffer_info() { - if (m_view && ownview) { PyBuffer_Release(m_view); delete m_view; } - } - - Py_buffer *view() const { return m_view; } - Py_buffer *&view() { return m_view; } -private: - struct private_ctr_tag { }; - - buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container &&shape_in, detail::any_container &&strides_in, bool readonly) - : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { } - - Py_buffer *m_view = nullptr; - bool ownview = false; -}; - -PYBIND11_NAMESPACE_BEGIN(detail) - -template struct compare_buffer_info { - static bool compare(const buffer_info& b) { - return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); - } -}; - -template struct compare_buffer_info::value>> { - static bool compare(const buffer_info& b) { - return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor::value || - ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || - ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); - } -}; - -PYBIND11_NAMESPACE_END(detail) -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/test/src/pybind11/cast.h b/test/src/pybind11/cast.h deleted file mode 100644 index d2869264c..000000000 --- a/test/src/pybind11/cast.h +++ /dev/null @@ -1,2231 +0,0 @@ -/* - pybind11/cast.h: Partial template specializations to cast between - C++ and Python types - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pytypes.h" -#include "detail/typeid.h" -#include "detail/descr.h" -#include "detail/internals.h" -#include -#include -#include -#include - -#if defined(PYBIND11_CPP17) -# if defined(__has_include) -# if __has_include() -# define PYBIND11_HAS_STRING_VIEW -# endif -# elif defined(_MSC_VER) -# define PYBIND11_HAS_STRING_VIEW -# endif -#endif -#ifdef PYBIND11_HAS_STRING_VIEW -#include -#endif - -#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L -# define PYBIND11_HAS_U8STRING -#endif - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -PYBIND11_NAMESPACE_BEGIN(detail) - -/// A life support system for temporary objects created by `type_caster::load()`. -/// Adding a patient will keep it alive up until the enclosing function returns. -class loader_life_support { -public: - /// A new patient frame is created when a function is entered - loader_life_support() { - get_internals().loader_patient_stack.push_back(nullptr); - } - - /// ... and destroyed after it returns - ~loader_life_support() { - auto &stack = get_internals().loader_patient_stack; - if (stack.empty()) - pybind11_fail("loader_life_support: internal error"); - - auto ptr = stack.back(); - stack.pop_back(); - Py_CLEAR(ptr); - - // A heuristic to reduce the stack's capacity (e.g. after long recursive calls) - if (stack.capacity() > 16 && !stack.empty() && stack.capacity() / stack.size() > 2) - stack.shrink_to_fit(); - } - - /// This can only be used inside a pybind11-bound function, either by `argument_loader` - /// at argument preparation time or by `py::cast()` at execution time. - PYBIND11_NOINLINE static void add_patient(handle h) { - auto &stack = get_internals().loader_patient_stack; - if (stack.empty()) - throw cast_error("When called outside a bound function, py::cast() cannot " - "do Python -> C++ conversions which require the creation " - "of temporary values"); - - auto &list_ptr = stack.back(); - if (list_ptr == nullptr) { - list_ptr = PyList_New(1); - if (!list_ptr) - pybind11_fail("loader_life_support: error allocating list"); - PyList_SET_ITEM(list_ptr, 0, h.inc_ref().ptr()); - } else { - auto result = PyList_Append(list_ptr, h.ptr()); - if (result == -1) - pybind11_fail("loader_life_support: error adding patient"); - } - } -}; - -// Gets the cache entry for the given type, creating it if necessary. The return value is the pair -// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was -// just created. -inline std::pair all_type_info_get_cache(PyTypeObject *type); - -// Populates a just-created cache entry. -PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector &bases) { - std::vector check; - for (handle parent : reinterpret_borrow(t->tp_bases)) - check.push_back((PyTypeObject *) parent.ptr()); - - auto const &type_dict = get_internals().registered_types_py; - for (size_t i = 0; i < check.size(); i++) { - auto type = check[i]; - // Ignore Python2 old-style class super type: - if (!PyType_Check((PyObject *) type)) continue; - - // Check `type` in the current set of registered python types: - auto it = type_dict.find(type); - if (it != type_dict.end()) { - // We found a cache entry for it, so it's either pybind-registered or has pre-computed - // pybind bases, but we have to make sure we haven't already seen the type(s) before: we - // want to follow Python/virtual C++ rules that there should only be one instance of a - // common base. - for (auto *tinfo : it->second) { - // NB: Could use a second set here, rather than doing a linear search, but since - // having a large number of immediate pybind11-registered types seems fairly - // unlikely, that probably isn't worthwhile. - bool found = false; - for (auto *known : bases) { - if (known == tinfo) { found = true; break; } - } - if (!found) bases.push_back(tinfo); - } - } - else if (type->tp_bases) { - // It's some python type, so keep follow its bases classes to look for one or more - // registered types - if (i + 1 == check.size()) { - // When we're at the end, we can pop off the current element to avoid growing - // `check` when adding just one base (which is typical--i.e. when there is no - // multiple inheritance) - check.pop_back(); - i--; - } - for (handle parent : reinterpret_borrow(type->tp_bases)) - check.push_back((PyTypeObject *) parent.ptr()); - } - } -} - -/** - * Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will - * be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side - * derived class that uses single inheritance. Will contain as many types as required for a Python - * class that uses multiple inheritance to inherit (directly or indirectly) from multiple - * pybind-registered classes. Will be empty if neither the type nor any base classes are - * pybind-registered. - * - * The value is cached for the lifetime of the Python type. - */ -inline const std::vector &all_type_info(PyTypeObject *type) { - auto ins = all_type_info_get_cache(type); - if (ins.second) - // New cache entry: populate it - all_type_info_populate(type, ins.first->second); - - return ins.first->second; -} - -/** - * Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any - * ancestors are pybind11-registered. Throws an exception if there are multiple bases--use - * `all_type_info` instead if you want to support multiple bases. - */ -PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { - auto &bases = all_type_info(type); - if (bases.empty()) - return nullptr; - if (bases.size() > 1) - pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); - return bases.front(); -} - -inline detail::type_info *get_local_type_info(const std::type_index &tp) { - auto &locals = registered_local_types_cpp(); - auto it = locals.find(tp); - if (it != locals.end()) - return it->second; - return nullptr; -} - -inline detail::type_info *get_global_type_info(const std::type_index &tp) { - auto &types = get_internals().registered_types_cpp; - auto it = types.find(tp); - if (it != types.end()) - return it->second; - return nullptr; -} - -/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr. -PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_index &tp, - bool throw_if_missing = false) { - if (auto ltype = get_local_type_info(tp)) - return ltype; - if (auto gtype = get_global_type_info(tp)) - return gtype; - - if (throw_if_missing) { - std::string tname = tp.name(); - detail::clean_type_id(tname); - pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\""); - } - return nullptr; -} - -PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) { - detail::type_info *type_info = get_type_info(tp, throw_if_missing); - return handle(type_info ? ((PyObject *) type_info->type) : nullptr); -} - -struct value_and_holder { - instance *inst = nullptr; - size_t index = 0u; - const detail::type_info *type = nullptr; - void **vh = nullptr; - - // Main constructor for a found value/holder: - value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : - inst{i}, index{index}, type{type}, - vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]} - {} - - // Default constructor (used to signal a value-and-holder not found by get_value_and_holder()) - value_and_holder() = default; - - // Used for past-the-end iterator - value_and_holder(size_t index) : index{index} {} - - template V *&value_ptr() const { - return reinterpret_cast(vh[0]); - } - // True if this `value_and_holder` has a non-null value pointer - explicit operator bool() const { return value_ptr(); } - - template H &holder() const { - return reinterpret_cast(vh[1]); - } - bool holder_constructed() const { - return inst->simple_layout - ? inst->simple_holder_constructed - : inst->nonsimple.status[index] & instance::status_holder_constructed; - } - void set_holder_constructed(bool v = true) { - if (inst->simple_layout) - inst->simple_holder_constructed = v; - else if (v) - inst->nonsimple.status[index] |= instance::status_holder_constructed; - else - inst->nonsimple.status[index] &= (uint8_t) ~instance::status_holder_constructed; - } - bool instance_registered() const { - return inst->simple_layout - ? inst->simple_instance_registered - : inst->nonsimple.status[index] & instance::status_instance_registered; - } - void set_instance_registered(bool v = true) { - if (inst->simple_layout) - inst->simple_instance_registered = v; - else if (v) - inst->nonsimple.status[index] |= instance::status_instance_registered; - else - inst->nonsimple.status[index] &= (uint8_t) ~instance::status_instance_registered; - } -}; - -// Container for accessing and iterating over an instance's values/holders -struct values_and_holders { -private: - instance *inst; - using type_vec = std::vector; - const type_vec &tinfo; - -public: - values_and_holders(instance *inst) : inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {} - - struct iterator { - private: - instance *inst = nullptr; - const type_vec *types = nullptr; - value_and_holder curr; - friend struct values_and_holders; - iterator(instance *inst, const type_vec *tinfo) - : inst{inst}, types{tinfo}, - curr(inst /* instance */, - types->empty() ? nullptr : (*types)[0] /* type info */, - 0, /* vpos: (non-simple types only): the first vptr comes first */ - 0 /* index */) - {} - // Past-the-end iterator: - iterator(size_t end) : curr(end) {} - public: - bool operator==(const iterator &other) const { return curr.index == other.curr.index; } - bool operator!=(const iterator &other) const { return curr.index != other.curr.index; } - iterator &operator++() { - if (!inst->simple_layout) - curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; - ++curr.index; - curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr; - return *this; - } - value_and_holder &operator*() { return curr; } - value_and_holder *operator->() { return &curr; } - }; - - iterator begin() { return iterator(inst, &tinfo); } - iterator end() { return iterator(tinfo.size()); } - - iterator find(const type_info *find_type) { - auto it = begin(), endit = end(); - while (it != endit && it->type != find_type) ++it; - return it; - } - - size_t size() { return tinfo.size(); } -}; - -/** - * Extracts C++ value and holder pointer references from an instance (which may contain multiple - * values/holders for python-side multiple inheritance) that match the given type. Throws an error - * if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If - * `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned, - * regardless of type (and the resulting .type will be nullptr). - * - * The returned object should be short-lived: in particular, it must not outlive the called-upon - * instance. - */ -PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) { - // Optimize common case: - if (!find_type || Py_TYPE(this) == find_type->type) - return value_and_holder(this, find_type, 0, 0); - - detail::values_and_holders vhs(this); - auto it = vhs.find(find_type); - if (it != vhs.end()) - return *it; - - if (!throw_if_missing) - return value_and_holder(); - -#if defined(NDEBUG) - pybind11_fail("pybind11::detail::instance::get_value_and_holder: " - "type is not a pybind11 base of the given instance " - "(compile in debug mode for type details)"); -#else - pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + - get_fully_qualified_tp_name(find_type->type) + "' is not a pybind11 base of the given `" + - get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance"); -#endif -} - -PYBIND11_NOINLINE inline void instance::allocate_layout() { - auto &tinfo = all_type_info(Py_TYPE(this)); - - const size_t n_types = tinfo.size(); - - if (n_types == 0) - pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types"); - - simple_layout = - n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); - - // Simple path: no python-side multiple inheritance, and a small-enough holder - if (simple_layout) { - simple_value_holder[0] = nullptr; - simple_holder_constructed = false; - simple_instance_registered = false; - } - else { // multiple base types or a too-large holder - // Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer, - // [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool - // values that tracks whether each associated holder has been initialized. Each [block] is - // padded, if necessary, to an integer multiple of sizeof(void *). - size_t space = 0; - for (auto t : tinfo) { - space += 1; // value pointer - space += t->holder_size_in_ptrs; // holder instance - } - size_t flags_at = space; - space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered) - - // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values, - // in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6 - // they default to using pymalloc, which is designed to be efficient for small allocations - // like the one we're doing here; in earlier versions (and for larger allocations) they are - // just wrappers around malloc. -#if PY_VERSION_HEX >= 0x03050000 - nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); - if (!nonsimple.values_and_holders) throw std::bad_alloc(); -#else - nonsimple.values_and_holders = (void **) PyMem_New(void *, space); - if (!nonsimple.values_and_holders) throw std::bad_alloc(); - std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); -#endif - nonsimple.status = reinterpret_cast(&nonsimple.values_and_holders[flags_at]); - } - owned = true; -} - -PYBIND11_NOINLINE inline void instance::deallocate_layout() { - if (!simple_layout) - PyMem_Free(nonsimple.values_and_holders); -} - -PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) { - handle type = detail::get_type_handle(tp, false); - if (!type) - return false; - return isinstance(obj, type); -} - -PYBIND11_NOINLINE inline std::string error_string() { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); - return "Unknown internal error occurred"; - } - - error_scope scope; // Preserve error state - - std::string errorString; - if (scope.type) { - errorString += handle(scope.type).attr("__name__").cast(); - errorString += ": "; - } - if (scope.value) - errorString += (std::string) str(scope.value); - - PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); - -#if PY_MAJOR_VERSION >= 3 - if (scope.trace != nullptr) - PyException_SetTraceback(scope.value, scope.trace); -#endif - -#if !defined(PYPY_VERSION) - if (scope.trace) { - auto *trace = (PyTracebackObject *) scope.trace; - - /* Get the deepest trace possible */ - while (trace->tb_next) - trace = trace->tb_next; - - PyFrameObject *frame = trace->tb_frame; - errorString += "\n\nAt:\n"; - while (frame) { - int lineno = PyFrame_GetLineNumber(frame); - errorString += - " " + handle(frame->f_code->co_filename).cast() + - "(" + std::to_string(lineno) + "): " + - handle(frame->f_code->co_name).cast() + "\n"; - frame = frame->f_back; - } - } -#endif - - return errorString; -} - -PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail::type_info *type ) { - auto &instances = get_internals().registered_instances; - auto range = instances.equal_range(ptr); - for (auto it = range.first; it != range.second; ++it) { - for (const auto &vh : values_and_holders(it->second)) { - if (vh.type == type) - return handle((PyObject *) it->second); - } - } - return handle(); -} - -inline PyThreadState *get_thread_state_unchecked() { -#if defined(PYPY_VERSION) - return PyThreadState_GET(); -#elif PY_VERSION_HEX < 0x03000000 - return _PyThreadState_Current; -#elif PY_VERSION_HEX < 0x03050000 - return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current); -#elif PY_VERSION_HEX < 0x03050200 - return (PyThreadState*) _PyThreadState_Current.value; -#else - return _PyThreadState_UncheckedGet(); -#endif -} - -// Forward declarations -inline void keep_alive_impl(handle nurse, handle patient); -inline PyObject *make_new_instance(PyTypeObject *type); - -class type_caster_generic { -public: - PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) - : typeinfo(get_type_info(type_info)), cpptype(&type_info) { } - - type_caster_generic(const type_info *typeinfo) - : typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) { } - - bool load(handle src, bool convert) { - return load_impl(src, convert); - } - - PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, - const detail::type_info *tinfo, - void *(*copy_constructor)(const void *), - void *(*move_constructor)(const void *), - const void *existing_holder = nullptr) { - if (!tinfo) // no type info: error will be set already - return handle(); - - void *src = const_cast(_src); - if (src == nullptr) - return none().release(); - - auto it_instances = get_internals().registered_instances.equal_range(src); - for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { - for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { - if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) - return handle((PyObject *) it_i->second).inc_ref(); - } - } - - auto inst = reinterpret_steal(make_new_instance(tinfo->type)); - auto wrapper = reinterpret_cast(inst.ptr()); - wrapper->owned = false; - void *&valueptr = values_and_holders(wrapper).begin()->value_ptr(); - - switch (policy) { - case return_value_policy::automatic: - case return_value_policy::take_ownership: - valueptr = src; - wrapper->owned = true; - break; - - case return_value_policy::automatic_reference: - case return_value_policy::reference: - valueptr = src; - wrapper->owned = false; - break; - - case return_value_policy::copy: - if (copy_constructor) - valueptr = copy_constructor(src); - else { -#if defined(NDEBUG) - throw cast_error("return_value_policy = copy, but type is " - "non-copyable! (compile in debug mode for details)"); -#else - std::string type_name(tinfo->cpptype->name()); - detail::clean_type_id(type_name); - throw cast_error("return_value_policy = copy, but type " + - type_name + " is non-copyable!"); -#endif - } - wrapper->owned = true; - break; - - case return_value_policy::move: - if (move_constructor) - valueptr = move_constructor(src); - else if (copy_constructor) - valueptr = copy_constructor(src); - else { -#if defined(NDEBUG) - throw cast_error("return_value_policy = move, but type is neither " - "movable nor copyable! " - "(compile in debug mode for details)"); -#else - std::string type_name(tinfo->cpptype->name()); - detail::clean_type_id(type_name); - throw cast_error("return_value_policy = move, but type " + - type_name + " is neither movable nor copyable!"); -#endif - } - wrapper->owned = true; - break; - - case return_value_policy::reference_internal: - valueptr = src; - wrapper->owned = false; - keep_alive_impl(inst, parent); - break; - - default: - throw cast_error("unhandled return_value_policy: should not happen!"); - } - - tinfo->init_instance(wrapper, existing_holder); - - return inst.release(); - } - - // Base methods for generic caster; there are overridden in copyable_holder_caster - void load_value(value_and_holder &&v_h) { - auto *&vptr = v_h.value_ptr(); - // Lazy allocation for unallocated values: - if (vptr == nullptr) { - auto *type = v_h.type ? v_h.type : typeinfo; - if (type->operator_new) { - vptr = type->operator_new(type->type_size); - } else { - #if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912) - if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - vptr = ::operator new(type->type_size, - std::align_val_t(type->type_align)); - else - #endif - vptr = ::operator new(type->type_size); - } - } - value = vptr; - } - bool try_implicit_casts(handle src, bool convert) { - for (auto &cast : typeinfo->implicit_casts) { - type_caster_generic sub_caster(*cast.first); - if (sub_caster.load(src, convert)) { - value = cast.second(sub_caster.value); - return true; - } - } - return false; - } - bool try_direct_conversions(handle src) { - for (auto &converter : *typeinfo->direct_conversions) { - if (converter(src.ptr(), value)) - return true; - } - return false; - } - void check_holder_compat() {} - - PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { - auto caster = type_caster_generic(ti); - if (caster.load(src, false)) - return caster.value; - return nullptr; - } - - /// Try to load with foreign typeinfo, if available. Used when there is no - /// native typeinfo, or when the native one wasn't able to produce a value. - PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) { - constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID; - const auto pytype = type::handle_of(src); - if (!hasattr(pytype, local_key)) - return false; - - type_info *foreign_typeinfo = reinterpret_borrow(getattr(pytype, local_key)); - // Only consider this foreign loader if actually foreign and is a loader of the correct cpp type - if (foreign_typeinfo->module_local_load == &local_load - || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) - return false; - - if (auto result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) { - value = result; - return true; - } - return false; - } - - // Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant - // bits of code between here and copyable_holder_caster where the two classes need different - // logic (without having to resort to virtual inheritance). - template - PYBIND11_NOINLINE bool load_impl(handle src, bool convert) { - if (!src) return false; - if (!typeinfo) return try_load_foreign_module_local(src); - if (src.is_none()) { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; - value = nullptr; - return true; - } - - auto &this_ = static_cast(*this); - this_.check_holder_compat(); - - PyTypeObject *srctype = Py_TYPE(src.ptr()); - - // Case 1: If src is an exact type match for the target type then we can reinterpret_cast - // the instance's value pointer to the target type: - if (srctype == typeinfo->type) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); - return true; - } - // Case 2: We have a derived class - else if (PyType_IsSubtype(srctype, typeinfo->type)) { - auto &bases = all_type_info(srctype); - bool no_cpp_mi = typeinfo->simple_type; - - // Case 2a: the python type is a Python-inherited derived class that inherits from just - // one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of - // the right type and we can use reinterpret_cast. - // (This is essentially the same as case 2b, but because not using multiple inheritance - // is extremely common, we handle it specially to avoid the loop iterator and type - // pointer lookup overhead) - if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); - return true; - } - // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if - // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we - // can safely reinterpret_cast to the relevant pointer. - else if (bases.size() > 1) { - for (auto base : bases) { - if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder(base)); - return true; - } - } - } - - // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match - // in the registered bases, above, so try implicit casting (needed for proper C++ casting - // when MI is involved). - if (this_.try_implicit_casts(src, convert)) - return true; - } - - // Perform an implicit conversion - if (convert) { - for (auto &converter : typeinfo->implicit_conversions) { - auto temp = reinterpret_steal(converter(src.ptr(), typeinfo->type)); - if (load_impl(temp, false)) { - loader_life_support::add_patient(temp); - return true; - } - } - if (this_.try_direct_conversions(src)) - return true; - } - - // Failed to match local typeinfo. Try again with global. - if (typeinfo->module_local) { - if (auto gtype = get_global_type_info(*typeinfo->cpptype)) { - typeinfo = gtype; - return load(src, false); - } - } - - // Global typeinfo has precedence over foreign module_local - return try_load_foreign_module_local(src); - } - - - // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast - // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair - // with .second = nullptr. (p.first = nullptr is not an error: it becomes None). - PYBIND11_NOINLINE static std::pair src_and_type( - const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { - if (auto *tpi = get_type_info(cast_type)) - return {src, const_cast(tpi)}; - - // Not found, set error: - std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); - detail::clean_type_id(tname); - std::string msg = "Unregistered type : " + tname; - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return {nullptr, nullptr}; - } - - const type_info *typeinfo = nullptr; - const std::type_info *cpptype = nullptr; - void *value = nullptr; -}; - -/** - * Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster - * needs to provide `operator T*()` and `operator T&()` operators. - * - * If the type supports moving the value away via an `operator T&&() &&` method, it should use - * `movable_cast_op_type` instead. - */ -template -using cast_op_type = - conditional_t>::value, - typename std::add_pointer>::type, - typename std::add_lvalue_reference>::type>; - -/** - * Determine suitable casting operator for a type caster with a movable value. Such a type caster - * needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be - * called in appropriate contexts where the value can be moved rather than copied. - * - * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. - */ -template -using movable_cast_op_type = - conditional_t::type>::value, - typename std::add_pointer>::type, - conditional_t::value, - typename std::add_rvalue_reference>::type, - typename std::add_lvalue_reference>::type>>; - -// std::is_copy_constructible isn't quite enough: it lets std::vector (and similar) through when -// T is non-copyable, but code containing such a copy constructor fails to actually compile. -template struct is_copy_constructible : std::is_copy_constructible {}; - -// Specialization for types that appear to be copy constructible but also look like stl containers -// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if -// so, copy constructability depends on whether the value_type is copy constructible. -template struct is_copy_constructible, - std::is_same, - // Avoid infinite recursion - negation> - >::value>> : is_copy_constructible {}; - -// Likewise for std::pair -// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't themselves -// copy constructible, but this can not be relied upon when T1 or T2 are themselves containers). -template struct is_copy_constructible> - : all_of, is_copy_constructible> {}; - -// The same problems arise with std::is_copy_assignable, so we use the same workaround. -template struct is_copy_assignable : std::is_copy_assignable {}; -template struct is_copy_assignable, - std::is_same - >::value>> : is_copy_assignable {}; -template struct is_copy_assignable> - : all_of, is_copy_assignable> {}; - -PYBIND11_NAMESPACE_END(detail) - -// polymorphic_type_hook::get(src, tinfo) determines whether the object pointed -// to by `src` actually is an instance of some class derived from `itype`. -// If so, it sets `tinfo` to point to the std::type_info representing that derived -// type, and returns a pointer to the start of the most-derived object of that type -// (in which `src` is a subobject; this will be the same address as `src` in most -// single inheritance cases). If not, or if `src` is nullptr, it simply returns `src` -// and leaves `tinfo` at its default value of nullptr. -// -// The default polymorphic_type_hook just returns src. A specialization for polymorphic -// types determines the runtime type of the passed object and adjusts the this-pointer -// appropriately via dynamic_cast. This is what enables a C++ Animal* to appear -// to Python as a Dog (if Dog inherits from Animal, Animal is polymorphic, Dog is -// registered with pybind11, and this Animal is in fact a Dog). -// -// You may specialize polymorphic_type_hook yourself for types that want to appear -// polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern -// in performance-sensitive applications, used most notably in LLVM.) -// -// polymorphic_type_hook_base allows users to specialize polymorphic_type_hook with -// std::enable_if. User provided specializations will always have higher priority than -// the default implementation and specialization provided in polymorphic_type_hook_base. -template -struct polymorphic_type_hook_base -{ - static const void *get(const itype *src, const std::type_info*&) { return src; } -}; -template -struct polymorphic_type_hook_base::value>> -{ - static const void *get(const itype *src, const std::type_info*& type) { - type = src ? &typeid(*src) : nullptr; - return dynamic_cast(src); - } -}; -template -struct polymorphic_type_hook : public polymorphic_type_hook_base {}; - -PYBIND11_NAMESPACE_BEGIN(detail) - -/// Generic type caster for objects stored on the heap -template class type_caster_base : public type_caster_generic { - using itype = intrinsic_t; - -public: - static constexpr auto name = _(); - - type_caster_base() : type_caster_base(typeid(type)) { } - explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } - - static handle cast(const itype &src, return_value_policy policy, handle parent) { - if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) - policy = return_value_policy::copy; - return cast(&src, policy, parent); - } - - static handle cast(itype &&src, return_value_policy, handle parent) { - return cast(&src, return_value_policy::move, parent); - } - - // Returns a (pointer, type_info) pair taking care of necessary type lookup for a - // polymorphic type (using RTTI by default, but can be overridden by specializing - // polymorphic_type_hook). If the instance isn't derived, returns the base version. - static std::pair src_and_type(const itype *src) { - auto &cast_type = typeid(itype); - const std::type_info *instance_type = nullptr; - const void *vsrc = polymorphic_type_hook::get(src, instance_type); - if (instance_type && !same_type(cast_type, *instance_type)) { - // This is a base pointer to a derived type. If the derived type is registered - // with pybind11, we want to make the full derived object available. - // In the typical case where itype is polymorphic, we get the correct - // derived pointer (which may be != base pointer) by a dynamic_cast to - // most derived type. If itype is not polymorphic, we won't get here - // except via a user-provided specialization of polymorphic_type_hook, - // and the user has promised that no this-pointer adjustment is - // required in that case, so it's OK to use static_cast. - if (const auto *tpi = get_type_info(*instance_type)) - return {vsrc, tpi}; - } - // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so - // don't do a cast - return type_caster_generic::src_and_type(src, cast_type, instance_type); - } - - static handle cast(const itype *src, return_value_policy policy, handle parent) { - auto st = src_and_type(src); - return type_caster_generic::cast( - st.first, policy, parent, st.second, - make_copy_constructor(src), make_move_constructor(src)); - } - - static handle cast_holder(const itype *src, const void *holder) { - auto st = src_and_type(src); - return type_caster_generic::cast( - st.first, return_value_policy::take_ownership, {}, st.second, - nullptr, nullptr, holder); - } - - template using cast_op_type = detail::cast_op_type; - - operator itype*() { return (type *) value; } - operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); } - -protected: - using Constructor = void *(*)(const void *); - - /* Only enabled when the types are {copy,move}-constructible *and* when the type - does not have a private operator new implementation. */ - template ::value>> - static auto make_copy_constructor(const T *x) -> decltype(new T(*x), Constructor{}) { - return [](const void *arg) -> void * { - return new T(*reinterpret_cast(arg)); - }; - } - - template ::value>> - static auto make_move_constructor(const T *x) -> decltype(new T(std::move(*const_cast(x))), Constructor{}) { - return [](const void *arg) -> void * { - return new T(std::move(*const_cast(reinterpret_cast(arg)))); - }; - } - - static Constructor make_copy_constructor(...) { return nullptr; } - static Constructor make_move_constructor(...) { return nullptr; } -}; - -template class type_caster : public type_caster_base { }; -template using make_caster = type_caster>; - -// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T -template typename make_caster::template cast_op_type cast_op(make_caster &caster) { - return caster.operator typename make_caster::template cast_op_type(); -} -template typename make_caster::template cast_op_type::type> -cast_op(make_caster &&caster) { - return std::move(caster).operator - typename make_caster::template cast_op_type::type>(); -} - -template class type_caster> { -private: - using caster_t = make_caster; - caster_t subcaster; - using reference_t = type&; - using subcaster_cast_op_type = - typename caster_t::template cast_op_type; - - static_assert(std::is_same::type &, subcaster_cast_op_type>::value || - std::is_same::value, - "std::reference_wrapper caster requires T to have a caster with an " - "`operator T &()` or `operator const T &()`"); -public: - bool load(handle src, bool convert) { return subcaster.load(src, convert); } - static constexpr auto name = caster_t::name; - static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { - // It is definitely wrong to take ownership of this pointer, so mask that rvp - if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) - policy = return_value_policy::automatic_reference; - return caster_t::cast(&src.get(), policy, parent); - } - template using cast_op_type = std::reference_wrapper; - operator std::reference_wrapper() { return cast_op(subcaster); } -}; - -#define PYBIND11_TYPE_CASTER(type, py_name) \ - protected: \ - type value; \ - public: \ - static constexpr auto name = py_name; \ - template >::value, int> = 0> \ - static handle cast(T_ *src, return_value_policy policy, handle parent) { \ - if (!src) return none().release(); \ - if (policy == return_value_policy::take_ownership) { \ - auto h = cast(std::move(*src), policy, parent); delete src; return h; \ - } else { \ - return cast(*src, policy, parent); \ - } \ - } \ - operator type*() { return &value; } \ - operator type&() { return value; } \ - operator type&&() && { return std::move(value); } \ - template using cast_op_type = pybind11::detail::movable_cast_op_type - - -template using is_std_char_type = any_of< - std::is_same, /* std::string */ -#if defined(PYBIND11_HAS_U8STRING) - std::is_same, /* std::u8string */ -#endif - std::is_same, /* std::u16string */ - std::is_same, /* std::u32string */ - std::is_same /* std::wstring */ ->; - - -template -struct type_caster::value && !is_std_char_type::value>> { - using _py_type_0 = conditional_t; - using _py_type_1 = conditional_t::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>; - using py_type = conditional_t::value, double, _py_type_1>; -public: - - bool load(handle src, bool convert) { - py_type py_value; - - if (!src) - return false; - - if (std::is_floating_point::value) { - if (convert || PyFloat_Check(src.ptr())) - py_value = (py_type) PyFloat_AsDouble(src.ptr()); - else - return false; - } else if (PyFloat_Check(src.ptr())) { - return false; - } else if (std::is_unsigned::value) { - py_value = as_unsigned(src.ptr()); - } else { // signed integer: - py_value = sizeof(T) <= sizeof(long) - ? (py_type) PyLong_AsLong(src.ptr()) - : (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr()); - } - - // Python API reported an error - bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); - - // Check to see if the conversion is valid (integers should match exactly) - // Signed/unsigned checks happen elsewhere - if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && py_value != (py_type) (T) py_value)) { - bool type_error = py_err && PyErr_ExceptionMatches( -#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) - PyExc_SystemError -#else - PyExc_TypeError -#endif - ); - PyErr_Clear(); - if (type_error && convert && PyNumber_Check(src.ptr())) { - auto tmp = reinterpret_steal(std::is_floating_point::value - ? PyNumber_Float(src.ptr()) - : PyNumber_Long(src.ptr())); - PyErr_Clear(); - return load(tmp, false); - } - return false; - } - - value = (T) py_value; - return true; - } - - template - static typename std::enable_if::value, handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PyFloat_FromDouble((double) src); - } - - template - static typename std::enable_if::value && std::is_signed::value && (sizeof(U) <= sizeof(long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PYBIND11_LONG_FROM_SIGNED((long) src); - } - - template - static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) <= sizeof(unsigned long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); - } - - template - static typename std::enable_if::value && std::is_signed::value && (sizeof(U) > sizeof(long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PyLong_FromLongLong((long long) src); - } - - template - static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) > sizeof(unsigned long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PyLong_FromUnsignedLongLong((unsigned long long) src); - } - - PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); -}; - -template struct void_caster { -public: - bool load(handle src, bool) { - if (src && src.is_none()) - return true; - return false; - } - static handle cast(T, return_value_policy /* policy */, handle /* parent */) { - return none().inc_ref(); - } - PYBIND11_TYPE_CASTER(T, _("None")); -}; - -template <> class type_caster : public void_caster {}; - -template <> class type_caster : public type_caster { -public: - using type_caster::cast; - - bool load(handle h, bool) { - if (!h) { - return false; - } else if (h.is_none()) { - value = nullptr; - return true; - } - - /* Check if this is a capsule */ - if (isinstance(h)) { - value = reinterpret_borrow(h); - return true; - } - - /* Check if this is a C++ type */ - auto &bases = all_type_info((PyTypeObject *) type::handle_of(h).ptr()); - if (bases.size() == 1) { // Only allowing loading from a single-value type - value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); - return true; - } - - /* Fail */ - return false; - } - - static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { - if (ptr) - return capsule(ptr).release(); - else - return none().inc_ref(); - } - - template using cast_op_type = void*&; - operator void *&() { return value; } - static constexpr auto name = _("capsule"); -private: - void *value = nullptr; -}; - -template <> class type_caster : public void_caster { }; - -template <> class type_caster { -public: - bool load(handle src, bool convert) { - if (!src) return false; - else if (src.ptr() == Py_True) { value = true; return true; } - else if (src.ptr() == Py_False) { value = false; return true; } - else if (convert || !strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) { - // (allow non-implicit conversion for numpy booleans) - - Py_ssize_t res = -1; - if (src.is_none()) { - res = 0; // None is implicitly converted to False - } - #if defined(PYPY_VERSION) - // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists - else if (hasattr(src, PYBIND11_BOOL_ATTR)) { - res = PyObject_IsTrue(src.ptr()); - } - #else - // Alternate approach for CPython: this does the same as the above, but optimized - // using the CPython API so as to avoid an unneeded attribute lookup. - else if (auto tp_as_number = src.ptr()->ob_type->tp_as_number) { - if (PYBIND11_NB_BOOL(tp_as_number)) { - res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); - } - } - #endif - if (res == 0 || res == 1) { - value = (bool) res; - return true; - } else { - PyErr_Clear(); - } - } - return false; - } - static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { - return handle(src ? Py_True : Py_False).inc_ref(); - } - PYBIND11_TYPE_CASTER(bool, _("bool")); -}; - -// Helper class for UTF-{8,16,32} C++ stl strings: -template struct string_caster { - using CharT = typename StringType::value_type; - - // Simplify life by being able to assume standard char sizes (the standard only guarantees - // minimums, but Python requires exact sizes) - static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char size != 1"); -#if defined(PYBIND11_HAS_U8STRING) - static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char8_t size != 1"); -#endif - static_assert(!std::is_same::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2"); - static_assert(!std::is_same::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4"); - // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) - static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, - "Unsupported wchar_t size != 2/4"); - static constexpr size_t UTF_N = 8 * sizeof(CharT); - - bool load(handle src, bool) { -#if PY_MAJOR_VERSION < 3 - object temp; -#endif - handle load_src = src; - if (!src) { - return false; - } else if (!PyUnicode_Check(load_src.ptr())) { -#if PY_MAJOR_VERSION >= 3 - return load_bytes(load_src); -#else - if (std::is_same::value) { - return load_bytes(load_src); - } - - // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false - if (!PYBIND11_BYTES_CHECK(load_src.ptr())) - return false; - - temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); - if (!temp) { PyErr_Clear(); return false; } - load_src = temp; -#endif - } - - auto utfNbytes = reinterpret_steal(PyUnicode_AsEncodedString( - load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr)); - if (!utfNbytes) { PyErr_Clear(); return false; } - - const auto *buffer = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); - size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); - if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32 - value = StringType(buffer, length); - - // If we're loading a string_view we need to keep the encoded Python object alive: - if (IsView) - loader_life_support::add_patient(utfNbytes); - - return true; - } - - static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { - const char *buffer = reinterpret_cast(src.data()); - auto nbytes = ssize_t(src.size() * sizeof(CharT)); - handle s = decode_utfN(buffer, nbytes); - if (!s) throw error_already_set(); - return s; - } - - PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME)); - -private: - static handle decode_utfN(const char *buffer, ssize_t nbytes) { -#if !defined(PYPY_VERSION) - return - UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) : - UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) : - PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); -#else - // PyPy segfaults when on PyUnicode_DecodeUTF16 (and possibly on PyUnicode_DecodeUTF32 as well), - // so bypass the whole thing by just passing the encoding as a string value, which works properly: - return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr); -#endif - } - - // When loading into a std::string or char*, accept a bytes object as-is (i.e. - // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. - // which supports loading a unicode from a str, doesn't take this path. - template - bool load_bytes(enable_if_t::value, handle> src) { - if (PYBIND11_BYTES_CHECK(src.ptr())) { - // We were passed a Python 3 raw bytes; accept it into a std::string or char* - // without any encoding attempt. - const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); - if (bytes) { - value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); - return true; - } - } - - return false; - } - - template - bool load_bytes(enable_if_t::value, handle>) { return false; } -}; - -template -struct type_caster, enable_if_t::value>> - : string_caster> {}; - -#ifdef PYBIND11_HAS_STRING_VIEW -template -struct type_caster, enable_if_t::value>> - : string_caster, true> {}; -#endif - -// Type caster for C-style strings. We basically use a std::string type caster, but also add the -// ability to use None as a nullptr char* (which the string caster doesn't allow). -template struct type_caster::value>> { - using StringType = std::basic_string; - using StringCaster = type_caster; - StringCaster str_caster; - bool none = false; - CharT one_char = 0; -public: - bool load(handle src, bool convert) { - if (!src) return false; - if (src.is_none()) { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; - none = true; - return true; - } - return str_caster.load(src, convert); - } - - static handle cast(const CharT *src, return_value_policy policy, handle parent) { - if (src == nullptr) return pybind11::none().inc_ref(); - return StringCaster::cast(StringType(src), policy, parent); - } - - static handle cast(CharT src, return_value_policy policy, handle parent) { - if (std::is_same::value) { - handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); - if (!s) throw error_already_set(); - return s; - } - return StringCaster::cast(StringType(1, src), policy, parent); - } - - operator CharT*() { return none ? nullptr : const_cast(static_cast(str_caster).c_str()); } - operator CharT&() { - if (none) - throw value_error("Cannot convert None to a character"); - - auto &value = static_cast(str_caster); - size_t str_len = value.size(); - if (str_len == 0) - throw value_error("Cannot convert empty string to a character"); - - // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that - // is too high, and one for multiple unicode characters (caught later), so we need to figure - // out how long the first encoded character is in bytes to distinguish between these two - // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those - // can fit into a single char value. - if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { - auto v0 = static_cast(value[0]); - size_t char0_bytes = !(v0 & 0x80) ? 1 : // low bits only: 0-127 - (v0 & 0xE0) == 0xC0 ? 2 : // 0b110xxxxx - start of 2-byte sequence - (v0 & 0xF0) == 0xE0 ? 3 : // 0b1110xxxx - start of 3-byte sequence - 4; // 0b11110xxx - start of 4-byte sequence - - if (char0_bytes == str_len) { - // If we have a 128-255 value, we can decode it into a single char: - if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx - one_char = static_cast(((v0 & 3) << 6) + (static_cast(value[1]) & 0x3F)); - return one_char; - } - // Otherwise we have a single character, but it's > U+00FF - throw value_error("Character code point not in range(0x100)"); - } - } - - // UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a - // surrogate pair with total length 2 instantly indicates a range error (but not a "your - // string was too long" error). - else if (StringCaster::UTF_N == 16 && str_len == 2) { - one_char = static_cast(value[0]); - if (one_char >= 0xD800 && one_char < 0xE000) - throw value_error("Character code point not in range(0x10000)"); - } - - if (str_len != 1) - throw value_error("Expected a character, but multi-character string found"); - - one_char = value[0]; - return one_char; - } - - static constexpr auto name = _(PYBIND11_STRING_NAME); - template using cast_op_type = pybind11::detail::cast_op_type<_T>; -}; - -// Base implementation for std::tuple and std::pair -template class Tuple, typename... Ts> class tuple_caster { - using type = Tuple; - static constexpr auto size = sizeof...(Ts); - using indices = make_index_sequence; -public: - - bool load(handle src, bool convert) { - if (!isinstance(src)) - return false; - const auto seq = reinterpret_borrow(src); - if (seq.size() != size) - return false; - return load_impl(seq, convert, indices{}); - } - - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - return cast_impl(std::forward(src), policy, parent, indices{}); - } - - // copied from the PYBIND11_TYPE_CASTER macro - template - static handle cast(T *src, return_value_policy policy, handle parent) { - if (!src) return none().release(); - if (policy == return_value_policy::take_ownership) { - auto h = cast(std::move(*src), policy, parent); delete src; return h; - } else { - return cast(*src, policy, parent); - } - } - - static constexpr auto name = _("Tuple[") + concat(make_caster::name...) + _("]"); - - template using cast_op_type = type; - - operator type() & { return implicit_cast(indices{}); } - operator type() && { return std::move(*this).implicit_cast(indices{}); } - -protected: - template - type implicit_cast(index_sequence) & { return type(cast_op(std::get(subcasters))...); } - template - type implicit_cast(index_sequence) && { return type(cast_op(std::move(std::get(subcasters)))...); } - - static constexpr bool load_impl(const sequence &, bool, index_sequence<>) { return true; } - - template - bool load_impl(const sequence &seq, bool convert, index_sequence) { -#ifdef __cpp_fold_expressions - if ((... || !std::get(subcasters).load(seq[Is], convert))) - return false; -#else - for (bool r : {std::get(subcasters).load(seq[Is], convert)...}) - if (!r) - return false; -#endif - return true; - } - - /* Implementation: Convert a C++ tuple into a Python tuple */ - template - static handle cast_impl(T &&src, return_value_policy policy, handle parent, index_sequence) { - std::array entries{{ - reinterpret_steal(make_caster::cast(std::get(std::forward(src)), policy, parent))... - }}; - for (const auto &entry: entries) - if (!entry) - return handle(); - tuple result(size); - int counter = 0; - for (auto & entry: entries) - PyTuple_SET_ITEM(result.ptr(), counter++, entry.release().ptr()); - return result.release(); - } - - Tuple...> subcasters; -}; - -template class type_caster> - : public tuple_caster {}; - -template class type_caster> - : public tuple_caster {}; - -/// Helper class which abstracts away certain actions. Users can provide specializations for -/// custom holders, but it's only necessary if the type has a non-standard interface. -template -struct holder_helper { - static auto get(const T &p) -> decltype(p.get()) { return p.get(); } -}; - -/// Type caster for holder types like std::shared_ptr, etc. -template -struct copyable_holder_caster : public type_caster_base { -public: - using base = type_caster_base; - static_assert(std::is_base_of>::value, - "Holder classes are only supported for custom types"); - using base::base; - using base::cast; - using base::typeinfo; - using base::value; - - bool load(handle src, bool convert) { - return base::template load_impl>(src, convert); - } - - explicit operator type*() { return this->value; } - // static_cast works around compiler error with MSVC 17 and CUDA 10.2 - // see issue #2180 - explicit operator type&() { return *(static_cast(this->value)); } - explicit operator holder_type*() { return std::addressof(holder); } - explicit operator holder_type&() { return holder; } - - static handle cast(const holder_type &src, return_value_policy, handle) { - const auto *ptr = holder_helper::get(src); - return type_caster_base::cast_holder(ptr, &src); - } - -protected: - friend class type_caster_generic; - void check_holder_compat() { - if (typeinfo->default_holder) - throw cast_error("Unable to load a custom holder type from a default-holder instance"); - } - - bool load_value(value_and_holder &&v_h) { - if (v_h.holder_constructed()) { - value = v_h.value_ptr(); - holder = v_h.template holder(); - return true; - } else { - throw cast_error("Unable to cast from non-held to held instance (T& to Holder) " -#if defined(NDEBUG) - "(compile in debug mode for type information)"); -#else - "of type '" + type_id() + "''"); -#endif - } - } - - template ::value, int> = 0> - bool try_implicit_casts(handle, bool) { return false; } - - template ::value, int> = 0> - bool try_implicit_casts(handle src, bool convert) { - for (auto &cast : typeinfo->implicit_casts) { - copyable_holder_caster sub_caster(*cast.first); - if (sub_caster.load(src, convert)) { - value = cast.second(sub_caster.value); - holder = holder_type(sub_caster.holder, (type *) value); - return true; - } - } - return false; - } - - static bool try_direct_conversions(handle) { return false; } - - - holder_type holder; -}; - -/// Specialize for the common std::shared_ptr, so users don't need to -template -class type_caster> : public copyable_holder_caster> { }; - -template -struct move_only_holder_caster { - static_assert(std::is_base_of, type_caster>::value, - "Holder classes are only supported for custom types"); - - static handle cast(holder_type &&src, return_value_policy, handle) { - auto *ptr = holder_helper::get(src); - return type_caster_base::cast_holder(ptr, std::addressof(src)); - } - static constexpr auto name = type_caster_base::name; -}; - -template -class type_caster> - : public move_only_holder_caster> { }; - -template -using type_caster_holder = conditional_t::value, - copyable_holder_caster, - move_only_holder_caster>; - -template struct always_construct_holder { static constexpr bool value = Value; }; - -/// Create a specialization for custom holder types (silently ignores std::shared_ptr) -#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type, ...) \ - namespace pybind11 { namespace detail { \ - template \ - struct always_construct_holder : always_construct_holder { }; \ - template \ - class type_caster::value>> \ - : public type_caster_holder { }; \ - }} - -// PYBIND11_DECLARE_HOLDER_TYPE holder types: -template struct is_holder_type : - std::is_base_of, detail::type_caster> {}; -// Specialization for always-supported unique_ptr holders: -template struct is_holder_type> : - std::true_type {}; - -template struct handle_type_name { static constexpr auto name = _(); }; -template <> struct handle_type_name { static constexpr auto name = _(PYBIND11_BYTES_NAME); }; -template <> struct handle_type_name { static constexpr auto name = _("int"); }; -template <> struct handle_type_name { static constexpr auto name = _("Iterable"); }; -template <> struct handle_type_name { static constexpr auto name = _("Iterator"); }; -template <> struct handle_type_name { static constexpr auto name = _("None"); }; -template <> struct handle_type_name { static constexpr auto name = _("*args"); }; -template <> struct handle_type_name { static constexpr auto name = _("**kwargs"); }; - -template -struct pyobject_caster { - template ::value, int> = 0> - bool load(handle src, bool /* convert */) { value = src; return static_cast(value); } - - template ::value, int> = 0> - bool load(handle src, bool /* convert */) { - if (!isinstance(src)) - return false; - value = reinterpret_borrow(src); - return true; - } - - static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { - return src.inc_ref(); - } - PYBIND11_TYPE_CASTER(type, handle_type_name::name); -}; - -template -class type_caster::value>> : public pyobject_caster { }; - -// Our conditions for enabling moving are quite restrictive: -// At compile time: -// - T needs to be a non-const, non-pointer, non-reference type -// - type_caster::operator T&() must exist -// - the type must be move constructible (obviously) -// At run-time: -// - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it -// must have ref_count() == 1)h -// If any of the above are not satisfied, we fall back to copying. -template using move_is_plain_type = satisfies_none_of; -template struct move_always : std::false_type {}; -template struct move_always, - negation>, - std::is_move_constructible, - std::is_same>().operator T&()), T&> ->::value>> : std::true_type {}; -template struct move_if_unreferenced : std::false_type {}; -template struct move_if_unreferenced, - negation>, - std::is_move_constructible, - std::is_same>().operator T&()), T&> ->::value>> : std::true_type {}; -template using move_never = none_of, move_if_unreferenced>; - -// Detect whether returning a `type` from a cast on type's type_caster is going to result in a -// reference or pointer to a local variable of the type_caster. Basically, only -// non-reference/pointer `type`s and reference/pointers from a type_caster_generic are safe; -// everything else returns a reference/pointer to a local variable. -template using cast_is_temporary_value_reference = bool_constant< - (std::is_reference::value || std::is_pointer::value) && - !std::is_base_of>::value && - !std::is_same, void>::value ->; - -// When a value returned from a C++ function is being cast back to Python, we almost always want to -// force `policy = move`, regardless of the return value policy the function/method was declared -// with. -template struct return_value_policy_override { - static return_value_policy policy(return_value_policy p) { return p; } -}; - -template struct return_value_policy_override>::value, void>> { - static return_value_policy policy(return_value_policy p) { - return !std::is_lvalue_reference::value && - !std::is_pointer::value - ? return_value_policy::move : p; - } -}; - -// Basic python -> C++ casting; throws if casting fails -template type_caster &load_type(type_caster &conv, const handle &handle) { - if (!conv.load(handle, true)) { -#if defined(NDEBUG) - throw cast_error("Unable to cast Python instance to C++ type (compile in debug mode for details)"); -#else - throw cast_error("Unable to cast Python instance of type " + - (std::string) str(type::handle_of(handle)) + " to C++ type '" + type_id() + "'"); -#endif - } - return conv; -} -// Wrapper around the above that also constructs and returns a type_caster -template make_caster load_type(const handle &handle) { - make_caster conv; - load_type(conv, handle); - return conv; -} - -PYBIND11_NAMESPACE_END(detail) - -// pytype -> C++ type -template ::value, int> = 0> -T cast(const handle &handle) { - using namespace detail; - static_assert(!cast_is_temporary_value_reference::value, - "Unable to cast type to reference: value is local to type caster"); - return cast_op(load_type(handle)); -} - -// pytype -> pytype (calls converting constructor) -template ::value, int> = 0> -T cast(const handle &handle) { return T(reinterpret_borrow(handle)); } - -// C++ type -> py::object -template ::value, int> = 0> -object cast(T &&value, return_value_policy policy = return_value_policy::automatic_reference, - handle parent = handle()) { - using no_ref_T = typename std::remove_reference::type; - if (policy == return_value_policy::automatic) - policy = std::is_pointer::value ? return_value_policy::take_ownership : - std::is_lvalue_reference::value ? return_value_policy::copy : return_value_policy::move; - else if (policy == return_value_policy::automatic_reference) - policy = std::is_pointer::value ? return_value_policy::reference : - std::is_lvalue_reference::value ? return_value_policy::copy : return_value_policy::move; - return reinterpret_steal(detail::make_caster::cast(std::forward(value), policy, parent)); -} - -template T handle::cast() const { return pybind11::cast(*this); } -template <> inline void handle::cast() const { return; } - -template -detail::enable_if_t::value, T> move(object &&obj) { - if (obj.ref_count() > 1) -#if defined(NDEBUG) - throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references" - " (compile in debug mode for details)"); -#else - throw cast_error("Unable to move from Python " + (std::string) str(type::handle_of(obj)) + - " instance to C++ " + type_id() + " instance: instance has multiple references"); -#endif - - // Move into a temporary and return that, because the reference may be a local value of `conv` - T ret = std::move(detail::load_type(obj).operator T&()); - return ret; -} - -// Calling cast() on an rvalue calls pybind11::cast with the object rvalue, which does: -// - If we have to move (because T has no copy constructor), do it. This will fail if the moved -// object has multiple references, but trying to copy will fail to compile. -// - If both movable and copyable, check ref count: if 1, move; otherwise copy -// - Otherwise (not movable), copy. -template detail::enable_if_t::value, T> cast(object &&object) { - return move(std::move(object)); -} -template detail::enable_if_t::value, T> cast(object &&object) { - if (object.ref_count() > 1) - return cast(object); - else - return move(std::move(object)); -} -template detail::enable_if_t::value, T> cast(object &&object) { - return cast(object); -} - -template T object::cast() const & { return pybind11::cast(*this); } -template T object::cast() && { return pybind11::cast(std::move(*this)); } -template <> inline void object::cast() const & { return; } -template <> inline void object::cast() && { return; } - -PYBIND11_NAMESPACE_BEGIN(detail) - -// Declared in pytypes.h: -template ::value, int>> -object object_or_cast(T &&o) { return pybind11::cast(std::forward(o)); } - -struct override_unused {}; // Placeholder type for the unneeded (and dead code) static variable in the PYBIND11_OVERRIDE_OVERRIDE macro -template using override_caster_t = conditional_t< - cast_is_temporary_value_reference::value, make_caster, override_unused>; - -// Trampoline use: for reference/pointer types to value-converted values, we do a value cast, then -// store the result in the given variable. For other types, this is a no-op. -template enable_if_t::value, T> cast_ref(object &&o, make_caster &caster) { - return cast_op(load_type(caster, o)); -} -template enable_if_t::value, T> cast_ref(object &&, override_unused &) { - pybind11_fail("Internal error: cast_ref fallback invoked"); } - -// Trampoline use: Having a pybind11::cast with an invalid reference type is going to static_assert, even -// though if it's in dead code, so we provide a "trampoline" to pybind11::cast that only does anything in -// cases where pybind11::cast is valid. -template enable_if_t::value, T> cast_safe(object &&o) { - return pybind11::cast(std::move(o)); } -template enable_if_t::value, T> cast_safe(object &&) { - pybind11_fail("Internal error: cast_safe fallback invoked"); } -template <> inline void cast_safe(object &&) {} - -PYBIND11_NAMESPACE_END(detail) - -template -tuple make_tuple() { return tuple(0); } - -template tuple make_tuple(Args&&... args_) { - constexpr size_t size = sizeof...(Args); - std::array args { - { reinterpret_steal(detail::make_caster::cast( - std::forward(args_), policy, nullptr))... } - }; - for (size_t i = 0; i < args.size(); i++) { - if (!args[i]) { -#if defined(NDEBUG) - throw cast_error("make_tuple(): unable to convert arguments to Python object (compile in debug mode for details)"); -#else - std::array argtypes { {type_id()...} }; - throw cast_error("make_tuple(): unable to convert argument of type '" + - argtypes[i] + "' to Python object"); -#endif - } - } - tuple result(size); - int counter = 0; - for (auto &arg_value : args) - PyTuple_SET_ITEM(result.ptr(), counter++, arg_value.release().ptr()); - return result; -} - -/// \ingroup annotations -/// Annotation for arguments -struct arg { - /// Constructs an argument with the name of the argument; if null or omitted, this is a positional argument. - constexpr explicit arg(const char *name = nullptr) : name(name), flag_noconvert(false), flag_none(true) { } - /// Assign a value to this argument - template arg_v operator=(T &&value) const; - /// Indicate that the type should not be converted in the type caster - arg &noconvert(bool flag = true) { flag_noconvert = flag; return *this; } - /// Indicates that the argument should/shouldn't allow None (e.g. for nullable pointer args) - arg &none(bool flag = true) { flag_none = flag; return *this; } - - const char *name; ///< If non-null, this is a named kwargs argument - bool flag_noconvert : 1; ///< If set, do not allow conversion (requires a supporting type caster!) - bool flag_none : 1; ///< If set (the default), allow None to be passed to this argument -}; - -/// \ingroup annotations -/// Annotation for arguments with values -struct arg_v : arg { -private: - template - arg_v(arg &&base, T &&x, const char *descr = nullptr) - : arg(base), - value(reinterpret_steal( - detail::make_caster::cast(x, return_value_policy::automatic, {}) - )), - descr(descr) -#if !defined(NDEBUG) - , type(type_id()) -#endif - { - // Workaround! See: - // https://github.com/pybind/pybind11/issues/2336 - // https://github.com/pybind/pybind11/pull/2685#issuecomment-731286700 - if (PyErr_Occurred()) { - PyErr_Clear(); - } - } - -public: - /// Direct construction with name, default, and description - template - arg_v(const char *name, T &&x, const char *descr = nullptr) - : arg_v(arg(name), std::forward(x), descr) { } - - /// Called internally when invoking `py::arg("a") = value` - template - arg_v(const arg &base, T &&x, const char *descr = nullptr) - : arg_v(arg(base), std::forward(x), descr) { } - - /// Same as `arg::noconvert()`, but returns *this as arg_v&, not arg& - arg_v &noconvert(bool flag = true) { arg::noconvert(flag); return *this; } - - /// Same as `arg::nonone()`, but returns *this as arg_v&, not arg& - arg_v &none(bool flag = true) { arg::none(flag); return *this; } - - /// The default value - object value; - /// The (optional) description of the default value - const char *descr; -#if !defined(NDEBUG) - /// The C++ type name of the default value (only available when compiled in debug mode) - std::string type; -#endif -}; - -/// \ingroup annotations -/// Annotation indicating that all following arguments are keyword-only; the is the equivalent of an -/// unnamed '*' argument (in Python 3) -struct kw_only {}; - -/// \ingroup annotations -/// Annotation indicating that all previous arguments are positional-only; the is the equivalent of an -/// unnamed '/' argument (in Python 3.8) -struct pos_only {}; - -template -arg_v arg::operator=(T &&value) const { return {std::move(*this), std::forward(value)}; } - -/// Alias for backward compatibility -- to be removed in version 2.0 -template using arg_t = arg_v; - -inline namespace literals { -/** \rst - String literal version of `arg` - \endrst */ -constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } -} // namespace literals - -PYBIND11_NAMESPACE_BEGIN(detail) - -// forward declaration (definition in attr.h) -struct function_record; - -/// Internal data associated with a single function call -struct function_call { - function_call(const function_record &f, handle p); // Implementation in attr.h - - /// The function data: - const function_record &func; - - /// Arguments passed to the function: - std::vector args; - - /// The `convert` value the arguments should be loaded with - std::vector args_convert; - - /// Extra references for the optional `py::args` and/or `py::kwargs` arguments (which, if - /// present, are also in `args` but without a reference). - object args_ref, kwargs_ref; - - /// The parent, if any - handle parent; - - /// If this is a call to an initializer, this argument contains `self` - handle init_self; -}; - - -/// Helper class which loads arguments for C++ functions called from Python -template -class argument_loader { - using indices = make_index_sequence; - - template using argument_is_args = std::is_same, args>; - template using argument_is_kwargs = std::is_same, kwargs>; - // Get args/kwargs argument positions relative to the end of the argument list: - static constexpr auto args_pos = constexpr_first() - (int) sizeof...(Args), - kwargs_pos = constexpr_first() - (int) sizeof...(Args); - - static constexpr bool args_kwargs_are_last = kwargs_pos >= - 1 && args_pos >= kwargs_pos - 1; - - static_assert(args_kwargs_are_last, "py::args/py::kwargs are only permitted as the last argument(s) of a function"); - -public: - static constexpr bool has_kwargs = kwargs_pos < 0; - static constexpr bool has_args = args_pos < 0; - - static constexpr auto arg_names = concat(type_descr(make_caster::name)...); - - bool load_args(function_call &call) { - return load_impl_sequence(call, indices{}); - } - - template - enable_if_t::value, Return> call(Func &&f) && { - return std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); - } - - template - enable_if_t::value, void_type> call(Func &&f) && { - std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); - return void_type(); - } - -private: - - static bool load_impl_sequence(function_call &, index_sequence<>) { return true; } - - template - bool load_impl_sequence(function_call &call, index_sequence) { -#ifdef __cpp_fold_expressions - if ((... || !std::get(argcasters).load(call.args[Is], call.args_convert[Is]))) - return false; -#else - for (bool r : {std::get(argcasters).load(call.args[Is], call.args_convert[Is])...}) - if (!r) - return false; -#endif - return true; - } - - template - Return call_impl(Func &&f, index_sequence, Guard &&) && { - return std::forward(f)(cast_op(std::move(std::get(argcasters)))...); - } - - std::tuple...> argcasters; -}; - -/// Helper class which collects only positional arguments for a Python function call. -/// A fancier version below can collect any argument, but this one is optimal for simple calls. -template -class simple_collector { -public: - template - explicit simple_collector(Ts &&...values) - : m_args(pybind11::make_tuple(std::forward(values)...)) { } - - const tuple &args() const & { return m_args; } - dict kwargs() const { return {}; } - - tuple args() && { return std::move(m_args); } - - /// Call a Python function and pass the collected arguments - object call(PyObject *ptr) const { - PyObject *result = PyObject_CallObject(ptr, m_args.ptr()); - if (!result) - throw error_already_set(); - return reinterpret_steal(result); - } - -private: - tuple m_args; -}; - -/// Helper class which collects positional, keyword, * and ** arguments for a Python function call -template -class unpacking_collector { -public: - template - explicit unpacking_collector(Ts &&...values) { - // Tuples aren't (easily) resizable so a list is needed for collection, - // but the actual function call strictly requires a tuple. - auto args_list = list(); - int _[] = { 0, (process(args_list, std::forward(values)), 0)... }; - ignore_unused(_); - - m_args = std::move(args_list); - } - - const tuple &args() const & { return m_args; } - const dict &kwargs() const & { return m_kwargs; } - - tuple args() && { return std::move(m_args); } - dict kwargs() && { return std::move(m_kwargs); } - - /// Call a Python function and pass the collected arguments - object call(PyObject *ptr) const { - PyObject *result = PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr()); - if (!result) - throw error_already_set(); - return reinterpret_steal(result); - } - -private: - template - void process(list &args_list, T &&x) { - auto o = reinterpret_steal(detail::make_caster::cast(std::forward(x), policy, {})); - if (!o) { -#if defined(NDEBUG) - argument_cast_error(); -#else - argument_cast_error(std::to_string(args_list.size()), type_id()); -#endif - } - args_list.append(o); - } - - void process(list &args_list, detail::args_proxy ap) { - for (auto a : ap) - args_list.append(a); - } - - void process(list &/*args_list*/, arg_v a) { - if (!a.name) -#if defined(NDEBUG) - nameless_argument_error(); -#else - nameless_argument_error(a.type); -#endif - - if (m_kwargs.contains(a.name)) { -#if defined(NDEBUG) - multiple_values_error(); -#else - multiple_values_error(a.name); -#endif - } - if (!a.value) { -#if defined(NDEBUG) - argument_cast_error(); -#else - argument_cast_error(a.name, a.type); -#endif - } - m_kwargs[a.name] = a.value; - } - - void process(list &/*args_list*/, detail::kwargs_proxy kp) { - if (!kp) - return; - for (auto k : reinterpret_borrow(kp)) { - if (m_kwargs.contains(k.first)) { -#if defined(NDEBUG) - multiple_values_error(); -#else - multiple_values_error(str(k.first)); -#endif - } - m_kwargs[k.first] = k.second; - } - } - - [[noreturn]] static void nameless_argument_error() { - throw type_error("Got kwargs without a name; only named arguments " - "may be passed via py::arg() to a python function call. " - "(compile in debug mode for details)"); - } - [[noreturn]] static void nameless_argument_error(std::string type) { - throw type_error("Got kwargs without a name of type '" + type + "'; only named " - "arguments may be passed via py::arg() to a python function call. "); - } - [[noreturn]] static void multiple_values_error() { - throw type_error("Got multiple values for keyword argument " - "(compile in debug mode for details)"); - } - - [[noreturn]] static void multiple_values_error(std::string name) { - throw type_error("Got multiple values for keyword argument '" + name + "'"); - } - - [[noreturn]] static void argument_cast_error() { - throw cast_error("Unable to convert call argument to Python object " - "(compile in debug mode for details)"); - } - - [[noreturn]] static void argument_cast_error(std::string name, std::string type) { - throw cast_error("Unable to convert call argument '" + name - + "' of type '" + type + "' to Python object"); - } - -private: - tuple m_args; - dict m_kwargs; -}; - -/// Collect only positional arguments for a Python function call -template ...>::value>> -simple_collector collect_arguments(Args &&...args) { - return simple_collector(std::forward(args)...); -} - -/// Collect all arguments, including keywords and unpacking (only instantiated when needed) -template ...>::value>> -unpacking_collector collect_arguments(Args &&...args) { - // Following argument order rules for generalized unpacking according to PEP 448 - static_assert( - constexpr_last() < constexpr_first() - && constexpr_last() < constexpr_first(), - "Invalid function call: positional args must precede keywords and ** unpacking; " - "* unpacking must precede ** unpacking" - ); - return unpacking_collector(std::forward(args)...); -} - -template -template -object object_api::operator()(Args &&...args) const { - return detail::collect_arguments(std::forward(args)...).call(derived().ptr()); -} - -template -template -object object_api::call(Args &&...args) const { - return operator()(std::forward(args)...); -} - -PYBIND11_NAMESPACE_END(detail) - - -template -handle type::handle_of() { - static_assert( - std::is_base_of>::value, - "py::type::of only supports the case where T is a registered C++ types." - ); - - return detail::get_type_handle(typeid(T), true); -} - - -#define PYBIND11_MAKE_OPAQUE(...) \ - namespace pybind11 { namespace detail { \ - template<> class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> { }; \ - }} - -/// Lets you pass a type containing a `,` through a macro parameter without needing a separate -/// typedef, e.g.: `PYBIND11_OVERRIDE(PYBIND11_TYPE(ReturnType), PYBIND11_TYPE(Parent), f, arg)` -#define PYBIND11_TYPE(...) __VA_ARGS__ - -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/test/src/pybind11/chrono.h b/test/src/pybind11/chrono.h deleted file mode 100644 index c3681102d..000000000 --- a/test/src/pybind11/chrono.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime - - Copyright (c) 2016 Trent Houliston and - Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include -#include -#include -#include - -// Backport the PyDateTime_DELTA functions from Python3.3 if required -#ifndef PyDateTime_DELTA_GET_DAYS -#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) -#endif -#ifndef PyDateTime_DELTA_GET_SECONDS -#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) -#endif -#ifndef PyDateTime_DELTA_GET_MICROSECONDS -#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) -#endif - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -PYBIND11_NAMESPACE_BEGIN(detail) - -template class duration_caster { -public: - using rep = typename type::rep; - using period = typename type::period; - - using days = std::chrono::duration>; - - bool load(handle src, bool) { - using namespace std::chrono; - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - if (!src) return false; - // If invoked with datetime.delta object - if (PyDelta_Check(src.ptr())) { - value = type(duration_cast>( - days(PyDateTime_DELTA_GET_DAYS(src.ptr())) - + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr())) - + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr())))); - return true; - } - // If invoked with a float we assume it is seconds and convert - else if (PyFloat_Check(src.ptr())) { - value = type(duration_cast>(duration(PyFloat_AsDouble(src.ptr())))); - return true; - } - else return false; - } - - // If this is a duration just return it back - static const std::chrono::duration& get_duration(const std::chrono::duration &src) { - return src; - } - - // If this is a time_point get the time_since_epoch - template static std::chrono::duration get_duration(const std::chrono::time_point> &src) { - return src.time_since_epoch(); - } - - static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) { - using namespace std::chrono; - - // Use overloaded function to get our duration from our source - // Works out if it is a duration or time_point and get the duration - auto d = get_duration(src); - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - // Declare these special duration types so the conversions happen with the correct primitive types (int) - using dd_t = duration>; - using ss_t = duration>; - using us_t = duration; - - auto dd = duration_cast(d); - auto subd = d - dd; - auto ss = duration_cast(subd); - auto us = duration_cast(subd - ss); - return PyDelta_FromDSU(dd.count(), ss.count(), us.count()); - } - - PYBIND11_TYPE_CASTER(type, _("datetime.timedelta")); -}; - -// This is for casting times on the system clock into datetime.datetime instances -template class type_caster> { -public: - using type = std::chrono::time_point; - bool load(handle src, bool) { - using namespace std::chrono; - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - if (!src) return false; - - std::tm cal; - microseconds msecs; - - if (PyDateTime_Check(src.ptr())) { - cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr()); - cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr()); - cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr()); - cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); - cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; - cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; - cal.tm_isdst = -1; - msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr())); - } else if (PyDate_Check(src.ptr())) { - cal.tm_sec = 0; - cal.tm_min = 0; - cal.tm_hour = 0; - cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); - cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; - cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; - cal.tm_isdst = -1; - msecs = microseconds(0); - } else if (PyTime_Check(src.ptr())) { - cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr()); - cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr()); - cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr()); - cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70) - cal.tm_mon = 0; // represents 1-Jan-1970, which is the first - cal.tm_year = 70; // earliest available date for Python's datetime - cal.tm_isdst = -1; - msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr())); - } - else return false; - - value = time_point_cast(system_clock::from_time_t(std::mktime(&cal)) + msecs); - return true; - } - - static handle cast(const std::chrono::time_point &src, return_value_policy /* policy */, handle /* parent */) { - using namespace std::chrono; - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - // Get out microseconds, and make sure they are positive, to avoid bug in eastern hemisphere time zones - // (cfr. https://github.com/pybind/pybind11/issues/2417) - using us_t = duration; - auto us = duration_cast(src.time_since_epoch() % seconds(1)); - if (us.count() < 0) - us += seconds(1); - - // Subtract microseconds BEFORE `system_clock::to_time_t`, because: - // > If std::time_t has lower precision, it is implementation-defined whether the value is rounded or truncated. - // (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t) - std::time_t tt = system_clock::to_time_t(time_point_cast(src - us)); - // this function uses static memory so it's best to copy it out asap just in case - // otherwise other code that is using localtime may break this (not just python code) - std::tm localtime = *std::localtime(&tt); - - return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, - localtime.tm_mon + 1, - localtime.tm_mday, - localtime.tm_hour, - localtime.tm_min, - localtime.tm_sec, - us.count()); - } - PYBIND11_TYPE_CASTER(type, _("datetime.datetime")); -}; - -// Other clocks that are not the system clock are not measured as datetime.datetime objects -// since they are not measured on calendar time. So instead we just make them timedeltas -// Or if they have passed us a time as a float we convert that -template class type_caster> -: public duration_caster> { -}; - -template class type_caster> -: public duration_caster> { -}; - -PYBIND11_NAMESPACE_END(detail) -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/test/src/pybind11/detail/common.h b/test/src/pybind11/detail/common.h deleted file mode 100644 index cb05bd14d..000000000 --- a/test/src/pybind11/detail/common.h +++ /dev/null @@ -1,855 +0,0 @@ -/* - pybind11/detail/common.h -- Basic macros - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#define PYBIND11_VERSION_MAJOR 2 -#define PYBIND11_VERSION_MINOR 6 -#define PYBIND11_VERSION_PATCH 2.dev1 - -#define PYBIND11_NAMESPACE_BEGIN(name) namespace name { -#define PYBIND11_NAMESPACE_END(name) } - -// Robust support for some features and loading modules compiled against different pybind versions -// requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute on -// the main `pybind11` namespace. -#if !defined(PYBIND11_NAMESPACE) -# ifdef __GNUG__ -# define PYBIND11_NAMESPACE pybind11 __attribute__((visibility("hidden"))) -# else -# define PYBIND11_NAMESPACE pybind11 -# endif -#endif - -#if !(defined(_MSC_VER) && __cplusplus == 199711L) -# if __cplusplus >= 201402L -# define PYBIND11_CPP14 -# if __cplusplus >= 201703L -# define PYBIND11_CPP17 -# endif -# endif -#elif defined(_MSC_VER) && __cplusplus == 199711L -// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) -// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer -# if _MSVC_LANG >= 201402L -# define PYBIND11_CPP14 -# if _MSVC_LANG > 201402L && _MSC_VER >= 1910 -# define PYBIND11_CPP17 -# endif -# endif -#endif - -// Compiler version assertions -#if defined(__INTEL_COMPILER) -# if __INTEL_COMPILER < 1800 -# error pybind11 requires Intel C++ compiler v18 or newer -# elif __INTEL_COMPILER < 1900 && defined(PYBIND11_CPP14) -# error pybind11 supports only C++11 with Intel C++ compiler v18. Use v19 or newer for C++14. -# endif -#elif defined(__clang__) && !defined(__apple_build_version__) -# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3) -# error pybind11 requires clang 3.3 or newer -# endif -#elif defined(__clang__) -// Apple changes clang version macros to its Xcode version; the first Xcode release based on -// (upstream) clang 3.3 was Xcode 5: -# if __clang_major__ < 5 -# error pybind11 requires Xcode/clang 5.0 or newer -# endif -#elif defined(__GNUG__) -# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) -# error pybind11 requires gcc 4.8 or newer -# endif -#elif defined(_MSC_VER) -// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features -// (e.g. std::negation) added in 2015u3: -# if _MSC_FULL_VER < 190024210 -# error pybind11 requires MSVC 2015 update 3 or newer -# endif -#endif - -#if !defined(PYBIND11_EXPORT) -# if defined(WIN32) || defined(_WIN32) -# define PYBIND11_EXPORT __declspec(dllexport) -# else -# define PYBIND11_EXPORT __attribute__ ((visibility("default"))) -# endif -#endif - -#if defined(_MSC_VER) -# define PYBIND11_NOINLINE __declspec(noinline) -#else -# define PYBIND11_NOINLINE __attribute__ ((noinline)) -#endif - -#if defined(PYBIND11_CPP14) -# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]] -#else -# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason))) -#endif - -#if defined(PYBIND11_CPP17) -# define PYBIND11_MAYBE_UNUSED [[maybe_unused]] -#elif defined(_MSC_VER) && !defined(__clang__) -# define PYBIND11_MAYBE_UNUSED -#else -# define PYBIND11_MAYBE_UNUSED __attribute__ ((__unused__)) -#endif - -/* Don't let Python.h #define (v)snprintf as macro because they are implemented - properly in Visual Studio since 2015. */ -#if defined(_MSC_VER) && _MSC_VER >= 1900 -# define HAVE_SNPRINTF 1 -#endif - -/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode -#if defined(_MSC_VER) -# if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4) -# define HAVE_ROUND 1 -# endif -# pragma warning(push) -# pragma warning(disable: 4510 4610 4512 4005) -# if defined(_DEBUG) && !defined(Py_DEBUG) -# define PYBIND11_DEBUG_MARKER -# undef _DEBUG -# endif -#endif - -#include -#include -#include - -/* Python #defines overrides on all sorts of core functions, which - tends to weak havok in C++ codebases that expect these to work - like regular functions (potentially with several overloads) */ -#if defined(isalnum) -# undef isalnum -# undef isalpha -# undef islower -# undef isspace -# undef isupper -# undef tolower -# undef toupper -#endif - -#if defined(copysign) -# undef copysign -#endif - -#if defined(_MSC_VER) -# if defined(PYBIND11_DEBUG_MARKER) -# define _DEBUG -# undef PYBIND11_DEBUG_MARKER -# endif -# pragma warning(pop) -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions -#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) -#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check -#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION -#define PYBIND11_BYTES_CHECK PyBytes_Check -#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString -#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize -#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize -#define PYBIND11_BYTES_AS_STRING PyBytes_AsString -#define PYBIND11_BYTES_SIZE PyBytes_Size -#define PYBIND11_LONG_CHECK(o) PyLong_Check(o) -#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) -#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) o) -#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) o) -#define PYBIND11_BYTES_NAME "bytes" -#define PYBIND11_STRING_NAME "str" -#define PYBIND11_SLICE_OBJECT PyObject -#define PYBIND11_FROM_STRING PyUnicode_FromString -#define PYBIND11_STR_TYPE ::pybind11::str -#define PYBIND11_BOOL_ATTR "__bool__" -#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) -#define PYBIND11_BUILTINS_MODULE "builtins" -// Providing a separate declaration to make Clang's -Wmissing-prototypes happy. -// See comment for PYBIND11_MODULE below for why this is marked "maybe unused". -#define PYBIND11_PLUGIN_IMPL(name) \ - extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT PyObject *PyInit_##name(); \ - extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() - -#else -#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_) -#define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check -#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION -#define PYBIND11_BYTES_CHECK PyString_Check -#define PYBIND11_BYTES_FROM_STRING PyString_FromString -#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize -#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize -#define PYBIND11_BYTES_AS_STRING PyString_AsString -#define PYBIND11_BYTES_SIZE PyString_Size -#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o)) -#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o)) -#define PYBIND11_LONG_FROM_SIGNED(o) PyInt_FromSsize_t((ssize_t) o) // Returns long if needed. -#define PYBIND11_LONG_FROM_UNSIGNED(o) PyInt_FromSize_t((size_t) o) // Returns long if needed. -#define PYBIND11_BYTES_NAME "str" -#define PYBIND11_STRING_NAME "unicode" -#define PYBIND11_SLICE_OBJECT PySliceObject -#define PYBIND11_FROM_STRING PyString_FromString -#define PYBIND11_STR_TYPE ::pybind11::bytes -#define PYBIND11_BOOL_ATTR "__nonzero__" -#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero) -#define PYBIND11_BUILTINS_MODULE "__builtin__" -// Providing a separate PyInit decl to make Clang's -Wmissing-prototypes happy. -// See comment for PYBIND11_MODULE below for why this is marked "maybe unused". -#define PYBIND11_PLUGIN_IMPL(name) \ - static PyObject *pybind11_init_wrapper(); \ - extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT void init##name(); \ - extern "C" PYBIND11_EXPORT void init##name() { \ - (void)pybind11_init_wrapper(); \ - } \ - PyObject *pybind11_init_wrapper() -#endif - -#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200 -extern "C" { - struct _Py_atomic_address { void *value; }; - PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current; -} -#endif - -#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code -#define PYBIND11_STRINGIFY(x) #x -#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) -#define PYBIND11_CONCAT(first, second) first##second -#define PYBIND11_ENSURE_INTERNALS_READY \ - pybind11::detail::get_internals(); - -#define PYBIND11_CHECK_PYTHON_VERSION \ - { \ - const char *compiled_ver = PYBIND11_TOSTRING(PY_MAJOR_VERSION) \ - "." PYBIND11_TOSTRING(PY_MINOR_VERSION); \ - const char *runtime_ver = Py_GetVersion(); \ - size_t len = std::strlen(compiled_ver); \ - if (std::strncmp(runtime_ver, compiled_ver, len) != 0 \ - || (runtime_ver[len] >= '0' && runtime_ver[len] <= '9')) { \ - PyErr_Format(PyExc_ImportError, \ - "Python version mismatch: module was compiled for Python %s, " \ - "but the interpreter version is incompatible: %s.", \ - compiled_ver, runtime_ver); \ - return nullptr; \ - } \ - } - -#define PYBIND11_CATCH_INIT_EXCEPTIONS \ - catch (pybind11::error_already_set &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } catch (const std::exception &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } \ - -/** \rst - ***Deprecated in favor of PYBIND11_MODULE*** - - This macro creates the entry point that will be invoked when the Python interpreter - imports a plugin library. Please create a `module_` in the function body and return - the pointer to its underlying Python object at the end. - - .. code-block:: cpp - - PYBIND11_PLUGIN(example) { - pybind11::module_ m("example", "pybind11 example plugin"); - /// Set up bindings here - return m.ptr(); - } -\endrst */ -#define PYBIND11_PLUGIN(name) \ - PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \ - static PyObject *pybind11_init(); \ - PYBIND11_PLUGIN_IMPL(name) { \ - PYBIND11_CHECK_PYTHON_VERSION \ - PYBIND11_ENSURE_INTERNALS_READY \ - try { \ - return pybind11_init(); \ - } PYBIND11_CATCH_INIT_EXCEPTIONS \ - } \ - PyObject *pybind11_init() - -/** \rst - This macro creates the entry point that will be invoked when the Python interpreter - imports an extension module. The module name is given as the fist argument and it - should not be in quotes. The second macro argument defines a variable of type - `py::module_` which can be used to initialize the module. - - The entry point is marked as "maybe unused" to aid dead-code detection analysis: - since the entry point is typically only looked up at runtime and not referenced - during translation, it would otherwise appear as unused ("dead") code. - - .. code-block:: cpp - - PYBIND11_MODULE(example, m) { - m.doc() = "pybind11 example module"; - - // Add bindings here - m.def("foo", []() { - return "Hello, World!"; - }); - } -\endrst */ -#define PYBIND11_MODULE(name, variable) \ - static ::pybind11::module_::module_def \ - PYBIND11_CONCAT(pybind11_module_def_, name) PYBIND11_MAYBE_UNUSED; \ - PYBIND11_MAYBE_UNUSED \ - static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \ - PYBIND11_PLUGIN_IMPL(name) { \ - PYBIND11_CHECK_PYTHON_VERSION \ - PYBIND11_ENSURE_INTERNALS_READY \ - auto m = ::pybind11::module_::create_extension_module( \ - PYBIND11_TOSTRING(name), nullptr, \ - &PYBIND11_CONCAT(pybind11_module_def_, name)); \ - try { \ - PYBIND11_CONCAT(pybind11_init_, name)(m); \ - return m.ptr(); \ - } PYBIND11_CATCH_INIT_EXCEPTIONS \ - } \ - void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &variable) - - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -using ssize_t = Py_ssize_t; -using size_t = std::size_t; - -/// Approach used to cast a previously unknown C++ instance into a Python object -enum class return_value_policy : uint8_t { - /** This is the default return value policy, which falls back to the policy - return_value_policy::take_ownership when the return value is a pointer. - Otherwise, it uses return_value::move or return_value::copy for rvalue - and lvalue references, respectively. See below for a description of what - all of these different policies do. */ - automatic = 0, - - /** As above, but use policy return_value_policy::reference when the return - value is a pointer. This is the default conversion policy for function - arguments when calling Python functions manually from C++ code (i.e. via - handle::operator()). You probably won't need to use this. */ - automatic_reference, - - /** Reference an existing object (i.e. do not create a new copy) and take - ownership. Python will call the destructor and delete operator when the - object’s reference count reaches zero. Undefined behavior ensues when - the C++ side does the same.. */ - take_ownership, - - /** Create a new copy of the returned object, which will be owned by - Python. This policy is comparably safe because the lifetimes of the two - instances are decoupled. */ - copy, - - /** Use std::move to move the return value contents into a new instance - that will be owned by Python. This policy is comparably safe because the - lifetimes of the two instances (move source and destination) are - decoupled. */ - move, - - /** Reference an existing object, but do not take ownership. The C++ side - is responsible for managing the object’s lifetime and deallocating it - when it is no longer used. Warning: undefined behavior will ensue when - the C++ side deletes an object that is still referenced and used by - Python. */ - reference, - - /** This policy only applies to methods and properties. It references the - object without taking ownership similar to the above - return_value_policy::reference policy. In contrast to that policy, the - function or property’s implicit this argument (called the parent) is - considered to be the the owner of the return value (the child). - pybind11 then couples the lifetime of the parent to the child via a - reference relationship that ensures that the parent cannot be garbage - collected while Python is still using the child. More advanced - variations of this scheme are also possible using combinations of - return_value_policy::reference and the keep_alive call policy */ - reference_internal -}; - -PYBIND11_NAMESPACE_BEGIN(detail) - -inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); } - -// Returns the size as a multiple of sizeof(void *), rounded up. -inline static constexpr size_t size_in_ptrs(size_t s) { return 1 + ((s - 1) >> log2(sizeof(void *))); } - -/** - * The space to allocate for simple layout instance holders (see below) in multiple of the size of - * a pointer (e.g. 2 means 16 bytes on 64-bit architectures). The default is the minimum required - * to holder either a std::unique_ptr or std::shared_ptr (which is almost always - * sizeof(std::shared_ptr)). - */ -constexpr size_t instance_simple_holder_in_ptrs() { - static_assert(sizeof(std::shared_ptr) >= sizeof(std::unique_ptr), - "pybind assumes std::shared_ptrs are at least as big as std::unique_ptrs"); - return size_in_ptrs(sizeof(std::shared_ptr)); -} - -// Forward declarations -struct type_info; -struct value_and_holder; - -struct nonsimple_values_and_holders { - void **values_and_holders; - uint8_t *status; -}; - -/// The 'instance' type which needs to be standard layout (need to be able to use 'offsetof') -struct instance { - PyObject_HEAD - /// Storage for pointers and holder; see simple_layout, below, for a description - union { - void *simple_value_holder[1 + instance_simple_holder_in_ptrs()]; - nonsimple_values_and_holders nonsimple; - }; - /// Weak references - PyObject *weakrefs; - /// If true, the pointer is owned which means we're free to manage it with a holder. - bool owned : 1; - /** - * An instance has two possible value/holder layouts. - * - * Simple layout (when this flag is true), means the `simple_value_holder` is set with a pointer - * and the holder object governing that pointer, i.e. [val1*][holder]. This layout is applied - * whenever there is no python-side multiple inheritance of bound C++ types *and* the type's - * holder will fit in the default space (which is large enough to hold either a std::unique_ptr - * or std::shared_ptr). - * - * Non-simple layout applies when using custom holders that require more space than `shared_ptr` - * (which is typically the size of two pointers), or when multiple inheritance is used on the - * python side. Non-simple layout allocates the required amount of memory to have multiple - * bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a - * pointer to allocated space of the required space to hold a sequence of value pointers and - * holders followed `status`, a set of bit flags (1 byte each), i.e. - * [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of - * `sizeof(void *)`. `nonsimple.status` is, for convenience, a pointer to the - * beginning of the [bb...] block (but not independently allocated). - * - * Status bits indicate whether the associated holder is constructed (& - * status_holder_constructed) and whether the value pointer is registered (& - * status_instance_registered) in `registered_instances`. - */ - bool simple_layout : 1; - /// For simple layout, tracks whether the holder has been constructed - bool simple_holder_constructed : 1; - /// For simple layout, tracks whether the instance is registered in `registered_instances` - bool simple_instance_registered : 1; - /// If true, get_internals().patients has an entry for this object - bool has_patients : 1; - - /// Initializes all of the above type/values/holders data (but not the instance values themselves) - void allocate_layout(); - - /// Destroys/deallocates all of the above - void deallocate_layout(); - - /// Returns the value_and_holder wrapper for the given type (or the first, if `find_type` - /// omitted). Returns a default-constructed (with `.inst = nullptr`) object on failure if - /// `throw_if_missing` is false. - value_and_holder get_value_and_holder(const type_info *find_type = nullptr, bool throw_if_missing = true); - - /// Bit values for the non-simple status flags - static constexpr uint8_t status_holder_constructed = 1; - static constexpr uint8_t status_instance_registered = 2; -}; - -static_assert(std::is_standard_layout::value, "Internal error: `pybind11::detail::instance` is not standard layout!"); - -/// from __cpp_future__ import (convenient aliases from C++14/17) -#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910) -using std::enable_if_t; -using std::conditional_t; -using std::remove_cv_t; -using std::remove_reference_t; -#else -template using enable_if_t = typename std::enable_if::type; -template using conditional_t = typename std::conditional::type; -template using remove_cv_t = typename std::remove_cv::type; -template using remove_reference_t = typename std::remove_reference::type; -#endif - -/// Index sequences -#if defined(PYBIND11_CPP14) -using std::index_sequence; -using std::make_index_sequence; -#else -template struct index_sequence { }; -template struct make_index_sequence_impl : make_index_sequence_impl { }; -template struct make_index_sequence_impl <0, S...> { using type = index_sequence; }; -template using make_index_sequence = typename make_index_sequence_impl::type; -#endif - -/// Make an index sequence of the indices of true arguments -template struct select_indices_impl { using type = ISeq; }; -template struct select_indices_impl, I, B, Bs...> - : select_indices_impl, index_sequence>, I + 1, Bs...> {}; -template using select_indices = typename select_indices_impl, 0, Bs...>::type; - -/// Backports of std::bool_constant and std::negation to accommodate older compilers -template using bool_constant = std::integral_constant; -template struct negation : bool_constant { }; - -// PGI/Intel cannot detect operator delete with the "compatible" void_t impl, so -// using the new one (C++14 defect, so generally works on newer compilers, even -// if not in C++17 mode) -#if defined(__PGIC__) || defined(__INTEL_COMPILER) -template using void_t = void; -#else -template struct void_t_impl { using type = void; }; -template using void_t = typename void_t_impl::type; -#endif - - -/// Compile-time all/any/none of that check the boolean value of all template types -#if defined(__cpp_fold_expressions) && !(defined(_MSC_VER) && (_MSC_VER < 1916)) -template using all_of = bool_constant<(Ts::value && ...)>; -template using any_of = bool_constant<(Ts::value || ...)>; -#elif !defined(_MSC_VER) -template struct bools {}; -template using all_of = std::is_same< - bools, - bools>; -template using any_of = negation...>>; -#else -// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit -// at a slight loss of compilation efficiency). -template using all_of = std::conjunction; -template using any_of = std::disjunction; -#endif -template using none_of = negation>; - -template class... Predicates> using satisfies_all_of = all_of...>; -template class... Predicates> using satisfies_any_of = any_of...>; -template class... Predicates> using satisfies_none_of = none_of...>; - -/// Strip the class from a method type -template struct remove_class { }; -template struct remove_class { using type = R (A...); }; -template struct remove_class { using type = R (A...); }; - -/// Helper template to strip away type modifiers -template struct intrinsic_type { using type = T; }; -template struct intrinsic_type { using type = typename intrinsic_type::type; }; -template struct intrinsic_type { using type = typename intrinsic_type::type; }; -template struct intrinsic_type { using type = typename intrinsic_type::type; }; -template struct intrinsic_type { using type = typename intrinsic_type::type; }; -template struct intrinsic_type { using type = typename intrinsic_type::type; }; -template struct intrinsic_type { using type = typename intrinsic_type::type; }; -template using intrinsic_t = typename intrinsic_type::type; - -/// Helper type to replace 'void' in some expressions -struct void_type { }; - -/// Helper template which holds a list of types -template struct type_list { }; - -/// Compile-time integer sum -#ifdef __cpp_fold_expressions -template constexpr size_t constexpr_sum(Ts... ns) { return (0 + ... + size_t{ns}); } -#else -constexpr size_t constexpr_sum() { return 0; } -template -constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); } -#endif - -PYBIND11_NAMESPACE_BEGIN(constexpr_impl) -/// Implementation details for constexpr functions -constexpr int first(int i) { return i; } -template -constexpr int first(int i, T v, Ts... vs) { return v ? i : first(i + 1, vs...); } - -constexpr int last(int /*i*/, int result) { return result; } -template -constexpr int last(int i, int result, T v, Ts... vs) { return last(i + 1, v ? i : result, vs...); } -PYBIND11_NAMESPACE_END(constexpr_impl) - -/// Return the index of the first type in Ts which satisfies Predicate. Returns sizeof...(Ts) if -/// none match. -template class Predicate, typename... Ts> -constexpr int constexpr_first() { return constexpr_impl::first(0, Predicate::value...); } - -/// Return the index of the last type in Ts which satisfies Predicate, or -1 if none match. -template class Predicate, typename... Ts> -constexpr int constexpr_last() { return constexpr_impl::last(0, -1, Predicate::value...); } - -/// Return the Nth element from the parameter pack -template -struct pack_element { using type = typename pack_element::type; }; -template -struct pack_element<0, T, Ts...> { using type = T; }; - -/// Return the one and only type which matches the predicate, or Default if none match. -/// If more than one type matches the predicate, fail at compile-time. -template class Predicate, typename Default, typename... Ts> -struct exactly_one { - static constexpr auto found = constexpr_sum(Predicate::value...); - static_assert(found <= 1, "Found more than one type matching the predicate"); - - static constexpr auto index = found ? constexpr_first() : 0; - using type = conditional_t::type, Default>; -}; -template class P, typename Default> -struct exactly_one { using type = Default; }; - -template class Predicate, typename Default, typename... Ts> -using exactly_one_t = typename exactly_one::type; - -/// Defer the evaluation of type T until types Us are instantiated -template struct deferred_type { using type = T; }; -template using deferred_t = typename deferred_type::type; - -/// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of::value == false`, -/// unlike `std::is_base_of`) -template using is_strict_base_of = bool_constant< - std::is_base_of::value && !std::is_same::value>; - -/// Like is_base_of, but also requires that the base type is accessible (i.e. that a Derived pointer -/// can be converted to a Base pointer) -/// For unions, `is_base_of::value` is False, so we need to check `is_same` as well. -template using is_accessible_base_of = bool_constant< - (std::is_same::value || std::is_base_of::value) && std::is_convertible::value>; - -template class Base> -struct is_template_base_of_impl { - template static std::true_type check(Base *); - static std::false_type check(...); -}; - -/// Check if a template is the base of a type. For example: -/// `is_template_base_of` is true if `struct T : Base {}` where U can be anything -template class Base, typename T> -#if !defined(_MSC_VER) -using is_template_base_of = decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)); -#else // MSVC2015 has trouble with decltype in template aliases -struct is_template_base_of : decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)) { }; -#endif - -/// Check if T is an instantiation of the template `Class`. For example: -/// `is_instantiation` is true if `T == shared_ptr` where U can be anything. -template class Class, typename T> -struct is_instantiation : std::false_type { }; -template class Class, typename... Us> -struct is_instantiation> : std::true_type { }; - -/// Check if T is std::shared_ptr where U can be anything -template using is_shared_ptr = is_instantiation; - -/// Check if T looks like an input iterator -template struct is_input_iterator : std::false_type {}; -template -struct is_input_iterator()), decltype(++std::declval())>> - : std::true_type {}; - -template using is_function_pointer = bool_constant< - std::is_pointer::value && std::is_function::type>::value>; - -template struct strip_function_object { - using type = typename remove_class::type; -}; - -// Extracts the function signature from a function, function pointer or lambda. -template > -using function_signature_t = conditional_t< - std::is_function::value, - F, - typename conditional_t< - std::is_pointer::value || std::is_member_pointer::value, - std::remove_pointer, - strip_function_object - >::type ->; - -/// Returns true if the type looks like a lambda: that is, isn't a function, pointer or member -/// pointer. Note that this can catch all sorts of other things, too; this is intended to be used -/// in a place where passing a lambda makes sense. -template using is_lambda = satisfies_none_of, - std::is_function, std::is_pointer, std::is_member_pointer>; - -/// Ignore that a variable is unused in compiler warnings -inline void ignore_unused(const int *) { } - -/// Apply a function over each element of a parameter pack -#ifdef __cpp_fold_expressions -#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...) -#else -using expand_side_effects = bool[]; -#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (void)pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false } -#endif - -PYBIND11_NAMESPACE_END(detail) - -/// C++ bindings of builtin Python exceptions -class builtin_exception : public std::runtime_error { -public: - using std::runtime_error::runtime_error; - /// Set the error using the Python C API - virtual void set_error() const = 0; -}; - -#define PYBIND11_RUNTIME_EXCEPTION(name, type) \ - class name : public builtin_exception { public: \ - using builtin_exception::builtin_exception; \ - name() : name("") { } \ - void set_error() const override { PyErr_SetString(type, what()); } \ - }; - -PYBIND11_RUNTIME_EXCEPTION(stop_iteration, PyExc_StopIteration) -PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError) -PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError) -PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError) -PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError) -PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError) -PYBIND11_RUNTIME_EXCEPTION(import_error, PyExc_ImportError) -PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error -PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally - -[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); } -[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); } - -template struct format_descriptor { }; - -PYBIND11_NAMESPACE_BEGIN(detail) -// Returns the index of the given type in the type char array below, and in the list in numpy.h -// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double; -// complex float,double,long double. Note that the long double types only participate when long -// double is actually longer than double (it isn't under MSVC). -// NB: not only the string below but also complex.h and numpy.h rely on this order. -template struct is_fmt_numeric { static constexpr bool value = false; }; -template struct is_fmt_numeric::value>> { - static constexpr bool value = true; - static constexpr int index = std::is_same::value ? 0 : 1 + ( - std::is_integral::value ? detail::log2(sizeof(T))*2 + std::is_unsigned::value : 8 + ( - std::is_same::value ? 1 : std::is_same::value ? 2 : 0)); -}; -PYBIND11_NAMESPACE_END(detail) - -template struct format_descriptor::value>> { - static constexpr const char c = "?bBhHiIqQfdg"[detail::is_fmt_numeric::index]; - static constexpr const char value[2] = { c, '\0' }; - static std::string format() { return std::string(1, c); } -}; - -#if !defined(PYBIND11_CPP17) - -template constexpr const char format_descriptor< - T, detail::enable_if_t::value>>::value[2]; - -#endif - -/// RAII wrapper that temporarily clears any Python error state -struct error_scope { - PyObject *type, *value, *trace; - error_scope() { PyErr_Fetch(&type, &value, &trace); } - ~error_scope() { PyErr_Restore(type, value, trace); } -}; - -/// Dummy destructor wrapper that can be used to expose classes with a private destructor -struct nodelete { template void operator()(T*) { } }; - -PYBIND11_NAMESPACE_BEGIN(detail) -template -struct overload_cast_impl { - constexpr overload_cast_impl() {}; // NOLINT(modernize-use-equals-default): MSVC 2015 needs this - - template - constexpr auto operator()(Return (*pf)(Args...)) const noexcept - -> decltype(pf) { return pf; } - - template - constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept - -> decltype(pmf) { return pmf; } - - template - constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept - -> decltype(pmf) { return pmf; } -}; -PYBIND11_NAMESPACE_END(detail) - -// overload_cast requires variable templates: C++14 -#if defined(PYBIND11_CPP14) -#define PYBIND11_OVERLOAD_CAST 1 -/// Syntax sugar for resolving overloaded function pointers: -/// - regular: static_cast(&Class::func) -/// - sweet: overload_cast(&Class::func) -template -static constexpr detail::overload_cast_impl overload_cast = {}; -// MSVC 2015 only accepts this particular initialization syntax for this variable template. -#endif - -/// Const member function selector for overload_cast -/// - regular: static_cast(&Class::func) -/// - sweet: overload_cast(&Class::func, const_) -static constexpr auto const_ = std::true_type{}; - -#if !defined(PYBIND11_CPP14) // no overload_cast: providing something that static_assert-fails: -template struct overload_cast { - static_assert(detail::deferred_t::value, - "pybind11::overload_cast<...> requires compiling in C++14 mode"); -}; -#endif // overload_cast - -PYBIND11_NAMESPACE_BEGIN(detail) - -// Adaptor for converting arbitrary container arguments into a vector; implicitly convertible from -// any standard container (or C-style array) supporting std::begin/std::end, any singleton -// arithmetic type (if T is arithmetic), or explicitly constructible from an iterator pair. -template -class any_container { - std::vector v; -public: - any_container() = default; - - // Can construct from a pair of iterators - template ::value>> - any_container(It first, It last) : v(first, last) { } - - // Implicit conversion constructor from any arbitrary container type with values convertible to T - template ())), T>::value>> - any_container(const Container &c) : any_container(std::begin(c), std::end(c)) { } - - // initializer_list's aren't deducible, so don't get matched by the above template; we need this - // to explicitly allow implicit conversion from one: - template ::value>> - any_container(const std::initializer_list &c) : any_container(c.begin(), c.end()) { } - - // Avoid copying if given an rvalue vector of the correct type. - any_container(std::vector &&v) : v(std::move(v)) { } - - // Moves the vector out of an rvalue any_container - operator std::vector &&() && { return std::move(v); } - - // Dereferencing obtains a reference to the underlying vector - std::vector &operator*() { return v; } - const std::vector &operator*() const { return v; } - - // -> lets you call methods on the underlying vector - std::vector *operator->() { return &v; } - const std::vector *operator->() const { return &v; } -}; - -// Forward-declaration; see detail/class.h -std::string get_fully_qualified_tp_name(PyTypeObject*); - -PYBIND11_NAMESPACE_END(detail) -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/test/src/pybind11/detail/descr.h b/test/src/pybind11/detail/descr.h deleted file mode 100644 index 92720cd56..000000000 --- a/test/src/pybind11/detail/descr.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - pybind11/detail/descr.h: Helper type for concatenating type signatures at compile time - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "common.h" - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -PYBIND11_NAMESPACE_BEGIN(detail) - -#if !defined(_MSC_VER) -# define PYBIND11_DESCR_CONSTEXPR static constexpr -#else -# define PYBIND11_DESCR_CONSTEXPR const -#endif - -/* Concatenate type signatures at compile time */ -template -struct descr { - char text[N + 1]; - - constexpr descr() : text{'\0'} { } - constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence()) { } - - template - constexpr descr(char const (&s)[N+1], index_sequence) : text{s[Is]..., '\0'} { } - - template - constexpr descr(char c, Chars... cs) : text{c, static_cast(cs)..., '\0'} { } - - static constexpr std::array types() { - return {{&typeid(Ts)..., nullptr}}; - } -}; - -template -constexpr descr plus_impl(const descr &a, const descr &b, - index_sequence, index_sequence) { - return {a.text[Is1]..., b.text[Is2]...}; -} - -template -constexpr descr operator+(const descr &a, const descr &b) { - return plus_impl(a, b, make_index_sequence(), make_index_sequence()); -} - -template -constexpr descr _(char const(&text)[N]) { return descr(text); } -constexpr descr<0> _(char const(&)[1]) { return {}; } - -template struct int_to_str : int_to_str { }; -template struct int_to_str<0, Digits...> { - static constexpr auto digits = descr(('0' + Digits)...); -}; - -// Ternary description (like std::conditional) -template -constexpr enable_if_t> _(char const(&text1)[N1], char const(&)[N2]) { - return _(text1); -} -template -constexpr enable_if_t> _(char const(&)[N1], char const(&text2)[N2]) { - return _(text2); -} - -template -constexpr enable_if_t _(const T1 &d, const T2 &) { return d; } -template -constexpr enable_if_t _(const T1 &, const T2 &d) { return d; } - -template auto constexpr _() -> decltype(int_to_str::digits) { - return int_to_str::digits; -} - -template constexpr descr<1, Type> _() { return {'%'}; } - -constexpr descr<0> concat() { return {}; } - -template -constexpr descr concat(const descr &descr) { return descr; } - -template -constexpr auto concat(const descr &d, const Args &...args) - -> decltype(std::declval>() + concat(args...)) { - return d + _(", ") + concat(args...); -} - -template -constexpr descr type_descr(const descr &descr) { - return _("{") + descr + _("}"); -} - -PYBIND11_NAMESPACE_END(detail) -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/test/src/pybind11/detail/internals.h b/test/src/pybind11/detail/internals.h deleted file mode 100644 index a455715bf..000000000 --- a/test/src/pybind11/detail/internals.h +++ /dev/null @@ -1,363 +0,0 @@ -/* - pybind11/detail/internals.h: Internal data structure and related functions - - Copyright (c) 2017 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "../pytypes.h" - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -PYBIND11_NAMESPACE_BEGIN(detail) -// Forward declarations -inline PyTypeObject *make_static_property_type(); -inline PyTypeObject *make_default_metaclass(); -inline PyObject *make_object_base_type(PyTypeObject *metaclass); - -// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new -// Thread Specific Storage (TSS) API. -#if PY_VERSION_HEX >= 0x03070000 -# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr -# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key)) -# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value)) -# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr) -# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key) -#else - // Usually an int but a long on Cygwin64 with Python 3.x -# define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0 -# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) -# if PY_MAJOR_VERSION < 3 -# define PYBIND11_TLS_DELETE_VALUE(key) \ - PyThread_delete_key_value(key) -# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ - do { \ - PyThread_delete_key_value((key)); \ - PyThread_set_key_value((key), (value)); \ - } while (false) -# else -# define PYBIND11_TLS_DELETE_VALUE(key) \ - PyThread_set_key_value((key), nullptr) -# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ - PyThread_set_key_value((key), (value)) -# endif -# define PYBIND11_TLS_FREE(key) (void)key -#endif - -// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly -// other STLs, this means `typeid(A)` from one module won't equal `typeid(A)` from another module -// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under -// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name, -// which works. If not under a known-good stl, provide our own name-based hash and equality -// functions that use the type name. -#if defined(__GLIBCXX__) -inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; } -using type_hash = std::hash; -using type_equal_to = std::equal_to; -#else -inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { - return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; -} - -struct type_hash { - size_t operator()(const std::type_index &t) const { - size_t hash = 5381; - const char *ptr = t.name(); - while (auto c = static_cast(*ptr++)) - hash = (hash * 33) ^ c; - return hash; - } -}; - -struct type_equal_to { - bool operator()(const std::type_index &lhs, const std::type_index &rhs) const { - return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; - } -}; -#endif - -template -using type_map = std::unordered_map; - -struct override_hash { - inline size_t operator()(const std::pair& v) const { - size_t value = std::hash()(v.first); - value ^= std::hash()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2); - return value; - } -}; - -/// Internal data structure used to track registered instances and types. -/// Whenever binary incompatible changes are made to this structure, -/// `PYBIND11_INTERNALS_VERSION` must be incremented. -struct internals { - type_map registered_types_cpp; // std::type_index -> pybind11's type information - std::unordered_map> registered_types_py; // PyTypeObject* -> base type_info(s) - std::unordered_multimap registered_instances; // void * -> instance* - std::unordered_set, override_hash> inactive_override_cache; - type_map> direct_conversions; - std::unordered_map> patients; - std::forward_list registered_exception_translators; - std::unordered_map shared_data; // Custom data to be shared across extensions - std::vector loader_patient_stack; // Used by `loader_life_support` - std::forward_list static_strings; // Stores the std::strings backing detail::c_str() - PyTypeObject *static_property_type; - PyTypeObject *default_metaclass; - PyObject *instance_base; -#if defined(WITH_THREAD) - PYBIND11_TLS_KEY_INIT(tstate); - PyInterpreterState *istate = nullptr; - ~internals() { - // This destructor is called *after* Py_Finalize() in finalize_interpreter(). - // That *SHOULD BE* fine. The following details what happens whe PyThread_tss_free is called. - // PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does nothing. - // PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree. - // PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX). Neither - // of those have anything to do with CPython internals. - // PyMem_RawFree *requires* that the `tstate` be allocated with the CPython allocator. - PYBIND11_TLS_FREE(tstate); - } -#endif -}; - -/// Additional type information which does not fit into the PyTypeObject. -/// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`. -struct type_info { - PyTypeObject *type; - const std::type_info *cpptype; - size_t type_size, type_align, holder_size_in_ptrs; - void *(*operator_new)(size_t); - void (*init_instance)(instance *, const void *); - void (*dealloc)(value_and_holder &v_h); - std::vector implicit_conversions; - std::vector> implicit_casts; - std::vector *direct_conversions; - buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; - void *get_buffer_data = nullptr; - void *(*module_local_load)(PyObject *, const type_info *) = nullptr; - /* A simple type never occurs as a (direct or indirect) parent - * of a class that makes use of multiple inheritance */ - bool simple_type : 1; - /* True if there is no multiple inheritance in this type's inheritance tree */ - bool simple_ancestors : 1; - /* for base vs derived holder_type checks */ - bool default_holder : 1; - /* true if this is a type registered with py::module_local */ - bool module_local : 1; -}; - -/// Tracks the `internals` and `type_info` ABI version independent of the main library version -#define PYBIND11_INTERNALS_VERSION 4 - -/// On MSVC, debug and release builds are not ABI-compatible! -#if defined(_MSC_VER) && defined(_DEBUG) -# define PYBIND11_BUILD_TYPE "_debug" -#else -# define PYBIND11_BUILD_TYPE "" -#endif - -/// Let's assume that different compilers are ABI-incompatible. -/// A user can manually set this string if they know their -/// compiler is compatible. -#ifndef PYBIND11_COMPILER_TYPE -# if defined(_MSC_VER) -# define PYBIND11_COMPILER_TYPE "_msvc" -# elif defined(__INTEL_COMPILER) -# define PYBIND11_COMPILER_TYPE "_icc" -# elif defined(__clang__) -# define PYBIND11_COMPILER_TYPE "_clang" -# elif defined(__PGI) -# define PYBIND11_COMPILER_TYPE "_pgi" -# elif defined(__MINGW32__) -# define PYBIND11_COMPILER_TYPE "_mingw" -# elif defined(__CYGWIN__) -# define PYBIND11_COMPILER_TYPE "_gcc_cygwin" -# elif defined(__GNUC__) -# define PYBIND11_COMPILER_TYPE "_gcc" -# else -# define PYBIND11_COMPILER_TYPE "_unknown" -# endif -#endif - -/// Also standard libs -#ifndef PYBIND11_STDLIB -# if defined(_LIBCPP_VERSION) -# define PYBIND11_STDLIB "_libcpp" -# elif defined(__GLIBCXX__) || defined(__GLIBCPP__) -# define PYBIND11_STDLIB "_libstdcpp" -# else -# define PYBIND11_STDLIB "" -# endif -#endif - -/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility. -#ifndef PYBIND11_BUILD_ABI -# if defined(__GXX_ABI_VERSION) -# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION) -# else -# define PYBIND11_BUILD_ABI "" -# endif -#endif - -#ifndef PYBIND11_INTERNALS_KIND -# if defined(WITH_THREAD) -# define PYBIND11_INTERNALS_KIND "" -# else -# define PYBIND11_INTERNALS_KIND "_without_thread" -# endif -#endif - -#define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \ - PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" - -#define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \ - PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" - -/// Each module locally stores a pointer to the `internals` data. The data -/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. -inline internals **&get_internals_pp() { - static internals **internals_pp = nullptr; - return internals_pp; -} - -inline void translate_exception(std::exception_ptr p) { - try { - if (p) std::rethrow_exception(p); - } catch (error_already_set &e) { e.restore(); return; - } catch (const builtin_exception &e) { e.set_error(); return; - } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return; - } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return; - } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::overflow_error &e) { PyErr_SetString(PyExc_OverflowError, e.what()); return; - } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return; - } catch (...) { - PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); - return; - } -} - -#if !defined(__GLIBCXX__) -inline void translate_local_exception(std::exception_ptr p) { - try { - if (p) std::rethrow_exception(p); - } catch (error_already_set &e) { e.restore(); return; - } catch (const builtin_exception &e) { e.set_error(); return; - } -} -#endif - -/// Return a reference to the current `internals` data -PYBIND11_NOINLINE inline internals &get_internals() { - auto **&internals_pp = get_internals_pp(); - if (internals_pp && *internals_pp) - return **internals_pp; - - // Ensure that the GIL is held since we will need to make Python calls. - // Cannot use py::gil_scoped_acquire here since that constructor calls get_internals. - struct gil_scoped_acquire_local { - gil_scoped_acquire_local() : state (PyGILState_Ensure()) {} - ~gil_scoped_acquire_local() { PyGILState_Release(state); } - const PyGILState_STATE state; - } gil; - - constexpr auto *id = PYBIND11_INTERNALS_ID; - auto builtins = handle(PyEval_GetBuiltins()); - if (builtins.contains(id) && isinstance(builtins[id])) { - internals_pp = static_cast(capsule(builtins[id])); - - // We loaded builtins through python's builtins, which means that our `error_already_set` - // and `builtin_exception` may be different local classes than the ones set up in the - // initial exception translator, below, so add another for our local exception classes. - // - // libstdc++ doesn't require this (types there are identified only by name) -#if !defined(__GLIBCXX__) - (*internals_pp)->registered_exception_translators.push_front(&translate_local_exception); -#endif - } else { - if (!internals_pp) internals_pp = new internals*(); - auto *&internals_ptr = *internals_pp; - internals_ptr = new internals(); -#if defined(WITH_THREAD) - - #if PY_VERSION_HEX < 0x03090000 - PyEval_InitThreads(); - #endif - PyThreadState *tstate = PyThreadState_Get(); - #if PY_VERSION_HEX >= 0x03070000 - internals_ptr->tstate = PyThread_tss_alloc(); - if (!internals_ptr->tstate || PyThread_tss_create(internals_ptr->tstate)) - pybind11_fail("get_internals: could not successfully initialize the TSS key!"); - PyThread_tss_set(internals_ptr->tstate, tstate); - #else - internals_ptr->tstate = PyThread_create_key(); - if (internals_ptr->tstate == -1) - pybind11_fail("get_internals: could not successfully initialize the TLS key!"); - PyThread_set_key_value(internals_ptr->tstate, tstate); - #endif - internals_ptr->istate = tstate->interp; -#endif - builtins[id] = capsule(internals_pp); - internals_ptr->registered_exception_translators.push_front(&translate_exception); - internals_ptr->static_property_type = make_static_property_type(); - internals_ptr->default_metaclass = make_default_metaclass(); - internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass); - } - return **internals_pp; -} - -/// Works like `internals.registered_types_cpp`, but for module-local registered types: -inline type_map ®istered_local_types_cpp() { - static type_map locals{}; - return locals; -} - -/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its -/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only -/// cleared when the program exits or after interpreter shutdown (when embedding), and so are -/// suitable for c-style strings needed by Python internals (such as PyTypeObject's tp_name). -template -const char *c_str(Args &&...args) { - auto &strings = get_internals().static_strings; - strings.emplace_front(std::forward(args)...); - return strings.front().c_str(); -} - -PYBIND11_NAMESPACE_END(detail) - -/// Returns a named pointer that is shared among all extension modules (using the same -/// pybind11 version) running in the current interpreter. Names starting with underscores -/// are reserved for internal usage. Returns `nullptr` if no matching entry was found. -inline PYBIND11_NOINLINE void *get_shared_data(const std::string &name) { - auto &internals = detail::get_internals(); - auto it = internals.shared_data.find(name); - return it != internals.shared_data.end() ? it->second : nullptr; -} - -/// Set the shared data that can be later recovered by `get_shared_data()`. -inline PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) { - detail::get_internals().shared_data[name] = data; - return data; -} - -/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if -/// such entry exists. Otherwise, a new object of default-constructible type `T` is -/// added to the shared data under the given name and a reference to it is returned. -template -T &get_or_create_shared_data(const std::string &name) { - auto &internals = detail::get_internals(); - auto it = internals.shared_data.find(name); - T *ptr = (T *) (it != internals.shared_data.end() ? it->second : nullptr); - if (!ptr) { - ptr = new T(); - internals.shared_data[name] = ptr; - } - return *ptr; -} - -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/test/src/pybind11/embed.h b/test/src/pybind11/embed.h deleted file mode 100644 index 204aaf989..000000000 --- a/test/src/pybind11/embed.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - pybind11/embed.h: Support for embedding the interpreter - - Copyright (c) 2017 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include "eval.h" - -#if defined(PYPY_VERSION) -# error Embedding the interpreter is not supported with PyPy -#endif - -#if PY_MAJOR_VERSION >= 3 -# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - extern "C" PyObject *pybind11_init_impl_##name(); \ - extern "C" PyObject *pybind11_init_impl_##name() { \ - return pybind11_init_wrapper_##name(); \ - } -#else -# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - extern "C" void pybind11_init_impl_##name(); \ - extern "C" void pybind11_init_impl_##name() { \ - pybind11_init_wrapper_##name(); \ - } -#endif - -/** \rst - Add a new module to the table of builtins for the interpreter. Must be - defined in global scope. The first macro parameter is the name of the - module (without quotes). The second parameter is the variable which will - be used as the interface to add functions and classes to the module. - - .. code-block:: cpp - - PYBIND11_EMBEDDED_MODULE(example, m) { - // ... initialize functions and classes here - m.def("foo", []() { - return "Hello, World!"; - }); - } - \endrst */ -#define PYBIND11_EMBEDDED_MODULE(name, variable) \ - static ::pybind11::module_::module_def \ - PYBIND11_CONCAT(pybind11_module_def_, name); \ - static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \ - static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \ - auto m = ::pybind11::module_::create_extension_module( \ - PYBIND11_TOSTRING(name), nullptr, \ - &PYBIND11_CONCAT(pybind11_module_def_, name)); \ - try { \ - PYBIND11_CONCAT(pybind11_init_, name)(m); \ - return m.ptr(); \ - } PYBIND11_CATCH_INIT_EXCEPTIONS \ - } \ - PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - ::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name) \ - (PYBIND11_TOSTRING(name), \ - PYBIND11_CONCAT(pybind11_init_impl_, name)); \ - void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &variable) - - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -PYBIND11_NAMESPACE_BEGIN(detail) - -/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. -struct embedded_module { -#if PY_MAJOR_VERSION >= 3 - using init_t = PyObject *(*)(); -#else - using init_t = void (*)(); -#endif - embedded_module(const char *name, init_t init) { - if (Py_IsInitialized()) - pybind11_fail("Can't add new modules after the interpreter has been initialized"); - - auto result = PyImport_AppendInittab(name, init); - if (result == -1) - pybind11_fail("Insufficient memory to add a new module"); - } -}; - -PYBIND11_NAMESPACE_END(detail) - -/** \rst - Initialize the Python interpreter. No other pybind11 or CPython API functions can be - called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The - optional parameter can be used to skip the registration of signal handlers (see the - `Python documentation`_ for details). Calling this function again after the interpreter - has already been initialized is a fatal error. - - If initializing the Python interpreter fails, then the program is terminated. (This - is controlled by the CPython runtime and is an exception to pybind11's normal behavior - of throwing exceptions on errors.) - - .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx - \endrst */ -inline void initialize_interpreter(bool init_signal_handlers = true) { - if (Py_IsInitialized()) - pybind11_fail("The interpreter is already running"); - - Py_InitializeEx(init_signal_handlers ? 1 : 0); - - // Make .py files in the working directory available by default - module_::import("sys").attr("path").cast().append("."); -} - -/** \rst - Shut down the Python interpreter. No pybind11 or CPython API functions can be called - after this. In addition, pybind11 objects must not outlive the interpreter: - - .. code-block:: cpp - - { // BAD - py::initialize_interpreter(); - auto hello = py::str("Hello, World!"); - py::finalize_interpreter(); - } // <-- BOOM, hello's destructor is called after interpreter shutdown - - { // GOOD - py::initialize_interpreter(); - { // scoped - auto hello = py::str("Hello, World!"); - } // <-- OK, hello is cleaned up properly - py::finalize_interpreter(); - } - - { // BETTER - py::scoped_interpreter guard{}; - auto hello = py::str("Hello, World!"); - } - - .. warning:: - - The interpreter can be restarted by calling `initialize_interpreter` again. - Modules created using pybind11 can be safely re-initialized. However, Python - itself cannot completely unload binary extension modules and there are several - caveats with regard to interpreter restarting. All the details can be found - in the CPython documentation. In short, not all interpreter memory may be - freed, either due to reference cycles or user-created global data. - - \endrst */ -inline void finalize_interpreter() { - handle builtins(PyEval_GetBuiltins()); - const char *id = PYBIND11_INTERNALS_ID; - - // Get the internals pointer (without creating it if it doesn't exist). It's possible for the - // internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()` - // during destruction), so we get the pointer-pointer here and check it after Py_Finalize(). - detail::internals **internals_ptr_ptr = detail::get_internals_pp(); - // It could also be stashed in builtins, so look there too: - if (builtins.contains(id) && isinstance(builtins[id])) - internals_ptr_ptr = capsule(builtins[id]); - - Py_Finalize(); - - if (internals_ptr_ptr) { - delete *internals_ptr_ptr; - *internals_ptr_ptr = nullptr; - } -} - -/** \rst - Scope guard version of `initialize_interpreter` and `finalize_interpreter`. - This a move-only guard and only a single instance can exist. - - .. code-block:: cpp - - #include - - int main() { - py::scoped_interpreter guard{}; - py::print(Hello, World!); - } // <-- interpreter shutdown - \endrst */ -class scoped_interpreter { -public: - scoped_interpreter(bool init_signal_handlers = true) { - initialize_interpreter(init_signal_handlers); - } - - scoped_interpreter(const scoped_interpreter &) = delete; - scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; } - scoped_interpreter &operator=(const scoped_interpreter &) = delete; - scoped_interpreter &operator=(scoped_interpreter &&) = delete; - - ~scoped_interpreter() { - if (is_valid) - finalize_interpreter(); - } - -private: - bool is_valid = true; -}; - -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/test/src/pybind11/functional.h b/test/src/pybind11/functional.h deleted file mode 100644 index 92c17dc22..000000000 --- a/test/src/pybind11/functional.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - pybind11/functional.h: std::function<> support - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -PYBIND11_NAMESPACE_BEGIN(detail) - -template -struct type_caster> { - using type = std::function; - using retval_type = conditional_t::value, void_type, Return>; - using function_type = Return (*) (Args...); - -public: - bool load(handle src, bool convert) { - if (src.is_none()) { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; - return true; - } - - if (!isinstance(src)) - return false; - - auto func = reinterpret_borrow(src); - - /* - When passing a C++ function as an argument to another C++ - function via Python, every function call would normally involve - a full C++ -> Python -> C++ roundtrip, which can be prohibitive. - Here, we try to at least detect the case where the function is - stateless (i.e. function pointer or lambda function without - captured variables), in which case the roundtrip can be avoided. - */ - if (auto cfunc = func.cpp_function()) { - auto c = reinterpret_borrow(PyCFunction_GET_SELF(cfunc.ptr())); - auto rec = (function_record *) c; - - if (rec && rec->is_stateless && - same_type(typeid(function_type), *reinterpret_cast(rec->data[1]))) { - struct capture { function_type f; }; - value = ((capture *) &rec->data)->f; - return true; - } - } - - // ensure GIL is held during functor destruction - struct func_handle { - function f; - func_handle(function&& f_) : f(std::move(f_)) {} - func_handle(const func_handle& f_) { - gil_scoped_acquire acq; - f = f_.f; - } - ~func_handle() { - gil_scoped_acquire acq; - function kill_f(std::move(f)); - } - }; - - // to emulate 'move initialization capture' in C++11 - struct func_wrapper { - func_handle hfunc; - func_wrapper(func_handle&& hf): hfunc(std::move(hf)) {} - Return operator()(Args... args) const { - gil_scoped_acquire acq; - object retval(hfunc.f(std::forward(args)...)); - /* Visual studio 2015 parser issue: need parentheses around this expression */ - return (retval.template cast()); - } - }; - - value = func_wrapper(func_handle(std::move(func))); - return true; - } - - template - static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { - if (!f_) - return none().inc_ref(); - - auto result = f_.template target(); - if (result) - return cpp_function(*result, policy).release(); - else - return cpp_function(std::forward(f_), policy).release(); - } - - PYBIND11_TYPE_CASTER(type, _("Callable[[") + concat(make_caster::name...) + _("], ") - + make_caster::name + _("]")); -}; - -PYBIND11_NAMESPACE_END(detail) -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/test/src/pybind11/iostream.h b/test/src/pybind11/iostream.h deleted file mode 100644 index 816e38f10..000000000 --- a/test/src/pybind11/iostream.h +++ /dev/null @@ -1,218 +0,0 @@ -/* - pybind11/iostream.h -- Tools to assist with redirecting cout and cerr to Python - - Copyright (c) 2017 Henry F. Schreiner - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" - -#include -#include -#include -#include -#include - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -PYBIND11_NAMESPACE_BEGIN(detail) - -// Buffer that writes to Python instead of C++ -class pythonbuf : public std::streambuf { -private: - using traits_type = std::streambuf::traits_type; - - const size_t buf_size; - std::unique_ptr d_buffer; - object pywrite; - object pyflush; - - int overflow(int c) override { - if (!traits_type::eq_int_type(c, traits_type::eof())) { - *pptr() = traits_type::to_char_type(c); - pbump(1); - } - return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof(); - } - - // This function must be non-virtual to be called in a destructor. If the - // rare MSVC test failure shows up with this version, then this should be - // simplified to a fully qualified call. - int _sync() { - if (pbase() != pptr()) { - - { - gil_scoped_acquire tmp; - - // This subtraction cannot be negative, so dropping the sign. - str line(pbase(), static_cast(pptr() - pbase())); - - pywrite(line); - pyflush(); - } - - setp(pbase(), epptr()); - } - return 0; - } - - int sync() override { - return _sync(); - } - -public: - - pythonbuf(object pyostream, size_t buffer_size = 1024) - : buf_size(buffer_size), - d_buffer(new char[buf_size]), - pywrite(pyostream.attr("write")), - pyflush(pyostream.attr("flush")) { - setp(d_buffer.get(), d_buffer.get() + buf_size - 1); - } - - pythonbuf(pythonbuf&&) = default; - - /// Sync before destroy - ~pythonbuf() override { - _sync(); - } -}; - -PYBIND11_NAMESPACE_END(detail) - - -/** \rst - This a move-only guard that redirects output. - - .. code-block:: cpp - - #include - - ... - - { - py::scoped_ostream_redirect output; - std::cout << "Hello, World!"; // Python stdout - } // <-- return std::cout to normal - - You can explicitly pass the c++ stream and the python object, - for example to guard stderr instead. - - .. code-block:: cpp - - { - py::scoped_ostream_redirect output{std::cerr, py::module_::import("sys").attr("stderr")}; - std::cerr << "Hello, World!"; - } - \endrst */ -class scoped_ostream_redirect { -protected: - std::streambuf *old; - std::ostream &costream; - detail::pythonbuf buffer; - -public: - scoped_ostream_redirect( - std::ostream &costream = std::cout, - object pyostream = module_::import("sys").attr("stdout")) - : costream(costream), buffer(pyostream) { - old = costream.rdbuf(&buffer); - } - - ~scoped_ostream_redirect() { - costream.rdbuf(old); - } - - scoped_ostream_redirect(const scoped_ostream_redirect &) = delete; - scoped_ostream_redirect(scoped_ostream_redirect &&other) = default; - scoped_ostream_redirect &operator=(const scoped_ostream_redirect &) = delete; - scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete; -}; - - -/** \rst - Like `scoped_ostream_redirect`, but redirects cerr by default. This class - is provided primary to make ``py::call_guard`` easier to make. - - .. code-block:: cpp - - m.def("noisy_func", &noisy_func, - py::call_guard()); - -\endrst */ -class scoped_estream_redirect : public scoped_ostream_redirect { -public: - scoped_estream_redirect( - std::ostream &costream = std::cerr, - object pyostream = module_::import("sys").attr("stderr")) - : scoped_ostream_redirect(costream,pyostream) {} -}; - - -PYBIND11_NAMESPACE_BEGIN(detail) - -// Class to redirect output as a context manager. C++ backend. -class OstreamRedirect { - bool do_stdout_; - bool do_stderr_; - std::unique_ptr redirect_stdout; - std::unique_ptr redirect_stderr; - -public: - OstreamRedirect(bool do_stdout = true, bool do_stderr = true) - : do_stdout_(do_stdout), do_stderr_(do_stderr) {} - - void enter() { - if (do_stdout_) - redirect_stdout.reset(new scoped_ostream_redirect()); - if (do_stderr_) - redirect_stderr.reset(new scoped_estream_redirect()); - } - - void exit() { - redirect_stdout.reset(); - redirect_stderr.reset(); - } -}; - -PYBIND11_NAMESPACE_END(detail) - -/** \rst - This is a helper function to add a C++ redirect context manager to Python - instead of using a C++ guard. To use it, add the following to your binding code: - - .. code-block:: cpp - - #include - - ... - - py::add_ostream_redirect(m, "ostream_redirect"); - - You now have a Python context manager that redirects your output: - - .. code-block:: python - - with m.ostream_redirect(): - m.print_to_cout_function() - - This manager can optionally be told which streams to operate on: - - .. code-block:: python - - with m.ostream_redirect(stdout=true, stderr=true): - m.noisy_function_with_error_printing() - - \endrst */ -inline class_ add_ostream_redirect(module_ m, std::string name = "ostream_redirect") { - return class_(m, name.c_str(), module_local()) - .def(init(), arg("stdout")=true, arg("stderr")=true) - .def("__enter__", &detail::OstreamRedirect::enter) - .def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); }); -} - -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/test/src/pybind11/numpy.h b/test/src/pybind11/numpy.h deleted file mode 100644 index 019f5688e..000000000 --- a/test/src/pybind11/numpy.h +++ /dev/null @@ -1,1693 +0,0 @@ -/* - pybind11/numpy.h: Basic NumPy support, vectorize() wrapper - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include "complex.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#endif - -/* This will be true on all flat address space platforms and allows us to reduce the - whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size - and dimension types (e.g. shape, strides, indexing), instead of inflicting this - upon the library user. */ -static_assert(sizeof(::pybind11::ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t"); -static_assert(std::is_signed::value, "Py_intptr_t must be signed"); -// We now can reinterpret_cast between py::ssize_t and Py_intptr_t (MSVC + PyPy cares) - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -class array; // Forward declaration - -PYBIND11_NAMESPACE_BEGIN(detail) - -template <> struct handle_type_name { static constexpr auto name = _("numpy.ndarray"); }; - -template struct npy_format_descriptor; - -struct PyArrayDescr_Proxy { - PyObject_HEAD - PyObject *typeobj; - char kind; - char type; - char byteorder; - char flags; - int type_num; - int elsize; - int alignment; - char *subarray; - PyObject *fields; - PyObject *names; -}; - -struct PyArray_Proxy { - PyObject_HEAD - char *data; - int nd; - ssize_t *dimensions; - ssize_t *strides; - PyObject *base; - PyObject *descr; - int flags; -}; - -struct PyVoidScalarObject_Proxy { - PyObject_VAR_HEAD - char *obval; - PyArrayDescr_Proxy *descr; - int flags; - PyObject *base; -}; - -struct numpy_type_info { - PyObject* dtype_ptr; - std::string format_str; -}; - -struct numpy_internals { - std::unordered_map registered_dtypes; - - numpy_type_info *get_type_info(const std::type_info& tinfo, bool throw_if_missing = true) { - auto it = registered_dtypes.find(std::type_index(tinfo)); - if (it != registered_dtypes.end()) - return &(it->second); - if (throw_if_missing) - pybind11_fail(std::string("NumPy type info missing for ") + tinfo.name()); - return nullptr; - } - - template numpy_type_info *get_type_info(bool throw_if_missing = true) { - return get_type_info(typeid(typename std::remove_cv::type), throw_if_missing); - } -}; - -inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) { - ptr = &get_or_create_shared_data("_numpy_internals"); -} - -inline numpy_internals& get_numpy_internals() { - static numpy_internals* ptr = nullptr; - if (!ptr) - load_numpy_internals(ptr); - return *ptr; -} - -template struct same_size { - template using as = bool_constant; -}; - -template constexpr int platform_lookup() { return -1; } - -// Lookup a type according to its size, and return a value corresponding to the NumPy typenum. -template -constexpr int platform_lookup(int I, Ints... Is) { - return sizeof(Concrete) == sizeof(T) ? I : platform_lookup(Is...); -} - -struct npy_api { - enum constants { - NPY_ARRAY_C_CONTIGUOUS_ = 0x0001, - NPY_ARRAY_F_CONTIGUOUS_ = 0x0002, - NPY_ARRAY_OWNDATA_ = 0x0004, - NPY_ARRAY_FORCECAST_ = 0x0010, - NPY_ARRAY_ENSUREARRAY_ = 0x0040, - NPY_ARRAY_ALIGNED_ = 0x0100, - NPY_ARRAY_WRITEABLE_ = 0x0400, - NPY_BOOL_ = 0, - NPY_BYTE_, NPY_UBYTE_, - NPY_SHORT_, NPY_USHORT_, - NPY_INT_, NPY_UINT_, - NPY_LONG_, NPY_ULONG_, - NPY_LONGLONG_, NPY_ULONGLONG_, - NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_, - NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_, - NPY_OBJECT_ = 17, - NPY_STRING_, NPY_UNICODE_, NPY_VOID_, - // Platform-dependent normalization - NPY_INT8_ = NPY_BYTE_, - NPY_UINT8_ = NPY_UBYTE_, - NPY_INT16_ = NPY_SHORT_, - NPY_UINT16_ = NPY_USHORT_, - // `npy_common.h` defines the integer aliases. In order, it checks: - // NPY_BITSOF_LONG, NPY_BITSOF_LONGLONG, NPY_BITSOF_INT, NPY_BITSOF_SHORT, NPY_BITSOF_CHAR - // and assigns the alias to the first matching size, so we should check in this order. - NPY_INT32_ = platform_lookup( - NPY_LONG_, NPY_INT_, NPY_SHORT_), - NPY_UINT32_ = platform_lookup( - NPY_ULONG_, NPY_UINT_, NPY_USHORT_), - NPY_INT64_ = platform_lookup( - NPY_LONG_, NPY_LONGLONG_, NPY_INT_), - NPY_UINT64_ = platform_lookup( - NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_), - }; - - typedef struct { - Py_intptr_t *ptr; - int len; - } PyArray_Dims; - - static npy_api& get() { - static npy_api api = lookup(); - return api; - } - - bool PyArray_Check_(PyObject *obj) const { - return (bool) PyObject_TypeCheck(obj, PyArray_Type_); - } - bool PyArrayDescr_Check_(PyObject *obj) const { - return (bool) PyObject_TypeCheck(obj, PyArrayDescr_Type_); - } - - unsigned int (*PyArray_GetNDArrayCFeatureVersion_)(); - PyObject *(*PyArray_DescrFromType_)(int); - PyObject *(*PyArray_NewFromDescr_) - (PyTypeObject *, PyObject *, int, Py_intptr_t const *, - Py_intptr_t const *, void *, int, PyObject *); - // Unused. Not removed because that affects ABI of the class. - PyObject *(*PyArray_DescrNewFromType_)(int); - int (*PyArray_CopyInto_)(PyObject *, PyObject *); - PyObject *(*PyArray_NewCopy_)(PyObject *, int); - PyTypeObject *PyArray_Type_; - PyTypeObject *PyVoidArrType_Type_; - PyTypeObject *PyArrayDescr_Type_; - PyObject *(*PyArray_DescrFromScalar_)(PyObject *); - PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *); - int (*PyArray_DescrConverter_) (PyObject *, PyObject **); - bool (*PyArray_EquivTypes_) (PyObject *, PyObject *); - int (*PyArray_GetArrayParamsFromObject_)(PyObject *, PyObject *, unsigned char, PyObject **, int *, - Py_intptr_t *, PyObject **, PyObject *); - PyObject *(*PyArray_Squeeze_)(PyObject *); - // Unused. Not removed because that affects ABI of the class. - int (*PyArray_SetBaseObject_)(PyObject *, PyObject *); - PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int); -private: - enum functions { - API_PyArray_GetNDArrayCFeatureVersion = 211, - API_PyArray_Type = 2, - API_PyArrayDescr_Type = 3, - API_PyVoidArrType_Type = 39, - API_PyArray_DescrFromType = 45, - API_PyArray_DescrFromScalar = 57, - API_PyArray_FromAny = 69, - API_PyArray_Resize = 80, - API_PyArray_CopyInto = 82, - API_PyArray_NewCopy = 85, - API_PyArray_NewFromDescr = 94, - API_PyArray_DescrNewFromType = 96, - API_PyArray_DescrConverter = 174, - API_PyArray_EquivTypes = 182, - API_PyArray_GetArrayParamsFromObject = 278, - API_PyArray_Squeeze = 136, - API_PyArray_SetBaseObject = 282 - }; - - static npy_api lookup() { - module_ m = module_::import("numpy.core.multiarray"); - auto c = m.attr("_ARRAY_API"); -#if PY_MAJOR_VERSION >= 3 - void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); -#else - void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr()); -#endif - npy_api api; -#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; - DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion); - if (api.PyArray_GetNDArrayCFeatureVersion_() < 0x7) - pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0"); - DECL_NPY_API(PyArray_Type); - DECL_NPY_API(PyVoidArrType_Type); - DECL_NPY_API(PyArrayDescr_Type); - DECL_NPY_API(PyArray_DescrFromType); - DECL_NPY_API(PyArray_DescrFromScalar); - DECL_NPY_API(PyArray_FromAny); - DECL_NPY_API(PyArray_Resize); - DECL_NPY_API(PyArray_CopyInto); - DECL_NPY_API(PyArray_NewCopy); - DECL_NPY_API(PyArray_NewFromDescr); - DECL_NPY_API(PyArray_DescrNewFromType); - DECL_NPY_API(PyArray_DescrConverter); - DECL_NPY_API(PyArray_EquivTypes); - DECL_NPY_API(PyArray_GetArrayParamsFromObject); - DECL_NPY_API(PyArray_Squeeze); - DECL_NPY_API(PyArray_SetBaseObject); -#undef DECL_NPY_API - return api; - } -}; - -inline PyArray_Proxy* array_proxy(void* ptr) { - return reinterpret_cast(ptr); -} - -inline const PyArray_Proxy* array_proxy(const void* ptr) { - return reinterpret_cast(ptr); -} - -inline PyArrayDescr_Proxy* array_descriptor_proxy(PyObject* ptr) { - return reinterpret_cast(ptr); -} - -inline const PyArrayDescr_Proxy* array_descriptor_proxy(const PyObject* ptr) { - return reinterpret_cast(ptr); -} - -inline bool check_flags(const void* ptr, int flag) { - return (flag == (array_proxy(ptr)->flags & flag)); -} - -template struct is_std_array : std::false_type { }; -template struct is_std_array> : std::true_type { }; -template struct is_complex : std::false_type { }; -template struct is_complex> : std::true_type { }; - -template struct array_info_scalar { - using type = T; - static constexpr bool is_array = false; - static constexpr bool is_empty = false; - static constexpr auto extents = _(""); - static void append_extents(list& /* shape */) { } -}; -// Computes underlying type and a comma-separated list of extents for array -// types (any mix of std::array and built-in arrays). An array of char is -// treated as scalar because it gets special handling. -template struct array_info : array_info_scalar { }; -template struct array_info> { - using type = typename array_info::type; - static constexpr bool is_array = true; - static constexpr bool is_empty = (N == 0) || array_info::is_empty; - static constexpr size_t extent = N; - - // appends the extents to shape - static void append_extents(list& shape) { - shape.append(N); - array_info::append_extents(shape); - } - - static constexpr auto extents = _::is_array>( - concat(_(), array_info::extents), _() - ); -}; -// For numpy we have special handling for arrays of characters, so we don't include -// the size in the array extents. -template struct array_info : array_info_scalar { }; -template struct array_info> : array_info_scalar> { }; -template struct array_info : array_info> { }; -template using remove_all_extents_t = typename array_info::type; - -template using is_pod_struct = all_of< - std::is_standard_layout, // since we're accessing directly in memory we need a standard layout type -#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(_GLIBCXX_USE_CXX11_ABI) - // _GLIBCXX_USE_CXX11_ABI indicates that we're using libstdc++ from GCC 5 or newer, independent - // of the actual compiler (Clang can also use libstdc++, but it always defines __GNUC__ == 4). - std::is_trivially_copyable, -#else - // GCC 4 doesn't implement is_trivially_copyable, so approximate it - std::is_trivially_destructible, - satisfies_any_of, -#endif - satisfies_none_of ->; - -// Replacement for std::is_pod (deprecated in C++20) -template using is_pod = all_of< - std::is_standard_layout, - std::is_trivial ->; - -template ssize_t byte_offset_unsafe(const Strides &) { return 0; } -template -ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) { - return i * strides[Dim] + byte_offset_unsafe(strides, index...); -} - -/** - * Proxy class providing unsafe, unchecked const access to array data. This is constructed through - * the `unchecked()` method of `array` or the `unchecked()` method of `array_t`. `Dims` - * will be -1 for dimensions determined at runtime. - */ -template -class unchecked_reference { -protected: - static constexpr bool Dynamic = Dims < 0; - const unsigned char *data_; - // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to - // make large performance gains on big, nested loops, but requires compile-time dimensions - conditional_t> - shape_, strides_; - const ssize_t dims_; - - friend class pybind11::array; - // Constructor for compile-time dimensions: - template - unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t) - : data_{reinterpret_cast(data)}, dims_{Dims} { - for (size_t i = 0; i < (size_t) dims_; i++) { - shape_[i] = shape[i]; - strides_[i] = strides[i]; - } - } - // Constructor for runtime dimensions: - template - unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t dims) - : data_{reinterpret_cast(data)}, shape_{shape}, strides_{strides}, dims_{dims} {} - -public: - /** - * Unchecked const reference access to data at the given indices. For a compile-time known - * number of dimensions, this requires the correct number of arguments; for run-time - * dimensionality, this is not checked (and so is up to the caller to use safely). - */ - template const T &operator()(Ix... index) const { - static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, - "Invalid number of indices for unchecked array reference"); - return *reinterpret_cast(data_ + byte_offset_unsafe(strides_, ssize_t(index)...)); - } - /** - * Unchecked const reference access to data; this operator only participates if the reference - * is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`. - */ - template > - const T &operator[](ssize_t index) const { return operator()(index); } - - /// Pointer access to the data at the given indices. - template const T *data(Ix... ix) const { return &operator()(ssize_t(ix)...); } - - /// Returns the item size, i.e. sizeof(T) - constexpr static ssize_t itemsize() { return sizeof(T); } - - /// Returns the shape (i.e. size) of dimension `dim` - ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; } - - /// Returns the number of dimensions of the array - ssize_t ndim() const { return dims_; } - - /// Returns the total number of elements in the referenced array, i.e. the product of the shapes - template - enable_if_t size() const { - return std::accumulate(shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies()); - } - template - enable_if_t size() const { - return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies()); - } - - /// Returns the total number of bytes used by the referenced data. Note that the actual span in - /// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice). - ssize_t nbytes() const { - return size() * itemsize(); - } -}; - -template -class unchecked_mutable_reference : public unchecked_reference { - friend class pybind11::array; - using ConstBase = unchecked_reference; - using ConstBase::ConstBase; - using ConstBase::Dynamic; -public: - // Bring in const-qualified versions from base class - using ConstBase::operator(); - using ConstBase::operator[]; - - /// Mutable, unchecked access to data at the given indices. - template T& operator()(Ix... index) { - static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, - "Invalid number of indices for unchecked array reference"); - return const_cast(ConstBase::operator()(index...)); - } - /** - * Mutable, unchecked access data at the given index; this operator only participates if the - * reference is to a 1-dimensional array (or has runtime dimensions). When present, this is - * exactly equivalent to `obj(index)`. - */ - template > - T &operator[](ssize_t index) { return operator()(index); } - - /// Mutable pointer access to the data at the given indices. - template T *mutable_data(Ix... ix) { return &operator()(ssize_t(ix)...); } -}; - -template -struct type_caster> { - static_assert(Dim == 0 && Dim > 0 /* always fail */, "unchecked array proxy object is not castable"); -}; -template -struct type_caster> : type_caster> {}; - -PYBIND11_NAMESPACE_END(detail) - -class dtype : public object { -public: - PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_); - - explicit dtype(const buffer_info &info) { - dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format))); - // If info.itemsize == 0, use the value calculated from the format string - m_ptr = descr.strip_padding(info.itemsize ? info.itemsize : descr.itemsize()).release().ptr(); - } - - explicit dtype(const std::string &format) { - m_ptr = from_args(pybind11::str(format)).release().ptr(); - } - - dtype(const char *format) : dtype(std::string(format)) { } - - dtype(list names, list formats, list offsets, ssize_t itemsize) { - dict args; - args["names"] = names; - args["formats"] = formats; - args["offsets"] = offsets; - args["itemsize"] = pybind11::int_(itemsize); - m_ptr = from_args(args).release().ptr(); - } - - /// This is essentially the same as calling numpy.dtype(args) in Python. - static dtype from_args(object args) { - PyObject *ptr = nullptr; - if (!detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) || !ptr) - throw error_already_set(); - return reinterpret_steal(ptr); - } - - /// Return dtype associated with a C++ type. - template static dtype of() { - return detail::npy_format_descriptor::type>::dtype(); - } - - /// Size of the data type in bytes. - ssize_t itemsize() const { - return detail::array_descriptor_proxy(m_ptr)->elsize; - } - - /// Returns true for structured data types. - bool has_fields() const { - return detail::array_descriptor_proxy(m_ptr)->names != nullptr; - } - - /// Single-character type code. - char kind() const { - return detail::array_descriptor_proxy(m_ptr)->kind; - } - -private: - static object _dtype_from_pep3118() { - static PyObject *obj = module_::import("numpy.core._internal") - .attr("_dtype_from_pep3118").cast().release().ptr(); - return reinterpret_borrow(obj); - } - - dtype strip_padding(ssize_t itemsize) { - // Recursively strip all void fields with empty names that are generated for - // padding fields (as of NumPy v1.11). - if (!has_fields()) - return *this; - - struct field_descr { PYBIND11_STR_TYPE name; object format; pybind11::int_ offset; }; - std::vector field_descriptors; - - for (auto field : attr("fields").attr("items")()) { - auto spec = field.cast(); - auto name = spec[0].cast(); - auto format = spec[1].cast()[0].cast(); - auto offset = spec[1].cast()[1].cast(); - if (!len(name) && format.kind() == 'V') - continue; - field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset}); - } - - std::sort(field_descriptors.begin(), field_descriptors.end(), - [](const field_descr& a, const field_descr& b) { - return a.offset.cast() < b.offset.cast(); - }); - - list names, formats, offsets; - for (auto& descr : field_descriptors) { - names.append(descr.name); - formats.append(descr.format); - offsets.append(descr.offset); - } - return dtype(names, formats, offsets, itemsize); - } -}; - -class array : public buffer { -public: - PYBIND11_OBJECT_CVT(array, buffer, detail::npy_api::get().PyArray_Check_, raw_array) - - enum { - c_style = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_, - f_style = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_, - forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_ - }; - - array() : array(0, static_cast(nullptr)) {} - - using ShapeContainer = detail::any_container; - using StridesContainer = detail::any_container; - - // Constructs an array taking shape/strides from arbitrary container types - array(const pybind11::dtype &dt, ShapeContainer shape, StridesContainer strides, - const void *ptr = nullptr, handle base = handle()) { - - if (strides->empty()) - *strides = detail::c_strides(*shape, dt.itemsize()); - - auto ndim = shape->size(); - if (ndim != strides->size()) - pybind11_fail("NumPy: shape ndim doesn't match strides ndim"); - auto descr = dt; - - int flags = 0; - if (base && ptr) { - if (isinstance(base)) - /* Copy flags from base (except ownership bit) */ - flags = reinterpret_borrow(base).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_; - else - /* Writable by default, easy to downgrade later on if needed */ - flags = detail::npy_api::NPY_ARRAY_WRITEABLE_; - } - - auto &api = detail::npy_api::get(); - auto tmp = reinterpret_steal(api.PyArray_NewFromDescr_( - api.PyArray_Type_, descr.release().ptr(), (int) ndim, - // Use reinterpret_cast for PyPy on Windows (remove if fixed, checked on 7.3.1) - reinterpret_cast(shape->data()), - reinterpret_cast(strides->data()), - const_cast(ptr), flags, nullptr)); - if (!tmp) - throw error_already_set(); - if (ptr) { - if (base) { - api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr()); - } else { - tmp = reinterpret_steal(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */)); - } - } - m_ptr = tmp.release().ptr(); - } - - array(const pybind11::dtype &dt, ShapeContainer shape, const void *ptr = nullptr, handle base = handle()) - : array(dt, std::move(shape), {}, ptr, base) { } - - template ::value && !std::is_same::value>> - array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle()) - : array(dt, {{count}}, ptr, base) { } - - template - array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle()) - : array(pybind11::dtype::of(), std::move(shape), std::move(strides), ptr, base) { } - - template - array(ShapeContainer shape, const T *ptr, handle base = handle()) - : array(std::move(shape), {}, ptr, base) { } - - template - explicit array(ssize_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { } - - explicit array(const buffer_info &info, handle base = handle()) - : array(pybind11::dtype(info), info.shape, info.strides, info.ptr, base) { } - - /// Array descriptor (dtype) - pybind11::dtype dtype() const { - return reinterpret_borrow(detail::array_proxy(m_ptr)->descr); - } - - /// Total number of elements - ssize_t size() const { - return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies()); - } - - /// Byte size of a single element - ssize_t itemsize() const { - return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize; - } - - /// Total number of bytes - ssize_t nbytes() const { - return size() * itemsize(); - } - - /// Number of dimensions - ssize_t ndim() const { - return detail::array_proxy(m_ptr)->nd; - } - - /// Base object - object base() const { - return reinterpret_borrow(detail::array_proxy(m_ptr)->base); - } - - /// Dimensions of the array - const ssize_t* shape() const { - return detail::array_proxy(m_ptr)->dimensions; - } - - /// Dimension along a given axis - ssize_t shape(ssize_t dim) const { - if (dim >= ndim()) - fail_dim_check(dim, "invalid axis"); - return shape()[dim]; - } - - /// Strides of the array - const ssize_t* strides() const { - return detail::array_proxy(m_ptr)->strides; - } - - /// Stride along a given axis - ssize_t strides(ssize_t dim) const { - if (dim >= ndim()) - fail_dim_check(dim, "invalid axis"); - return strides()[dim]; - } - - /// Return the NumPy array flags - int flags() const { - return detail::array_proxy(m_ptr)->flags; - } - - /// If set, the array is writeable (otherwise the buffer is read-only) - bool writeable() const { - return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_WRITEABLE_); - } - - /// If set, the array owns the data (will be freed when the array is deleted) - bool owndata() const { - return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_OWNDATA_); - } - - /// Pointer to the contained data. If index is not provided, points to the - /// beginning of the buffer. May throw if the index would lead to out of bounds access. - template const void* data(Ix... index) const { - return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); - } - - /// Mutable pointer to the contained data. If index is not provided, points to the - /// beginning of the buffer. May throw if the index would lead to out of bounds access. - /// May throw if the array is not writeable. - template void* mutable_data(Ix... index) { - check_writeable(); - return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); - } - - /// Byte offset from beginning of the array to a given index (full or partial). - /// May throw if the index would lead to out of bounds access. - template ssize_t offset_at(Ix... index) const { - if ((ssize_t) sizeof...(index) > ndim()) - fail_dim_check(sizeof...(index), "too many indices for an array"); - return byte_offset(ssize_t(index)...); - } - - ssize_t offset_at() const { return 0; } - - /// Item count from beginning of the array to a given index (full or partial). - /// May throw if the index would lead to out of bounds access. - template ssize_t index_at(Ix... index) const { - return offset_at(index...) / itemsize(); - } - - /** - * Returns a proxy object that provides access to the array's data without bounds or - * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with - * care: the array must not be destroyed or reshaped for the duration of the returned object, - * and the caller must take care not to access invalid dimensions or dimension indices. - */ - template detail::unchecked_mutable_reference mutable_unchecked() & { - if (Dims >= 0 && ndim() != Dims) - throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + - "; expected " + std::to_string(Dims)); - return detail::unchecked_mutable_reference(mutable_data(), shape(), strides(), ndim()); - } - - /** - * Returns a proxy object that provides const access to the array's data without bounds or - * dimensionality checking. Unlike `mutable_unchecked()`, this does not require that the - * underlying array have the `writable` flag. Use with care: the array must not be destroyed or - * reshaped for the duration of the returned object, and the caller must take care not to access - * invalid dimensions or dimension indices. - */ - template detail::unchecked_reference unchecked() const & { - if (Dims >= 0 && ndim() != Dims) - throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + - "; expected " + std::to_string(Dims)); - return detail::unchecked_reference(data(), shape(), strides(), ndim()); - } - - /// Return a new view with all of the dimensions of length 1 removed - array squeeze() { - auto& api = detail::npy_api::get(); - return reinterpret_steal(api.PyArray_Squeeze_(m_ptr)); - } - - /// Resize array to given shape - /// If refcheck is true and more that one reference exist to this array - /// then resize will succeed only if it makes a reshape, i.e. original size doesn't change - void resize(ShapeContainer new_shape, bool refcheck = true) { - detail::npy_api::PyArray_Dims d = { - // Use reinterpret_cast for PyPy on Windows (remove if fixed, checked on 7.3.1) - reinterpret_cast(new_shape->data()), - int(new_shape->size()) - }; - // try to resize, set ordering param to -1 cause it's not used anyway - auto new_array = reinterpret_steal( - detail::npy_api::get().PyArray_Resize_(m_ptr, &d, int(refcheck), -1) - ); - if (!new_array) throw error_already_set(); - if (isinstance(new_array)) { *this = std::move(new_array); } - } - - /// Ensure that the argument is a NumPy array - /// In case of an error, nullptr is returned and the Python error is cleared. - static array ensure(handle h, int ExtraFlags = 0) { - auto result = reinterpret_steal(raw_array(h.ptr(), ExtraFlags)); - if (!result) - PyErr_Clear(); - return result; - } - -protected: - template friend struct detail::npy_format_descriptor; - - void fail_dim_check(ssize_t dim, const std::string& msg) const { - throw index_error(msg + ": " + std::to_string(dim) + - " (ndim = " + std::to_string(ndim()) + ")"); - } - - template ssize_t byte_offset(Ix... index) const { - check_dimensions(index...); - return detail::byte_offset_unsafe(strides(), ssize_t(index)...); - } - - void check_writeable() const { - if (!writeable()) - throw std::domain_error("array is not writeable"); - } - - template void check_dimensions(Ix... index) const { - check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...); - } - - void check_dimensions_impl(ssize_t, const ssize_t*) const { } - - template void check_dimensions_impl(ssize_t axis, const ssize_t* shape, ssize_t i, Ix... index) const { - if (i >= *shape) { - throw index_error(std::string("index ") + std::to_string(i) + - " is out of bounds for axis " + std::to_string(axis) + - " with size " + std::to_string(*shape)); - } - check_dimensions_impl(axis + 1, shape + 1, index...); - } - - /// Create array from any object -- always returns a new reference - static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) { - if (ptr == nullptr) { - PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array from a nullptr"); - return nullptr; - } - return detail::npy_api::get().PyArray_FromAny_( - ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); - } -}; - -template class array_t : public array { -private: - struct private_ctor {}; - // Delegating constructor needed when both moving and accessing in the same constructor - array_t(private_ctor, ShapeContainer &&shape, StridesContainer &&strides, const T *ptr, handle base) - : array(std::move(shape), std::move(strides), ptr, base) {} -public: - static_assert(!detail::array_info::is_array, "Array types cannot be used with array_t"); - - using value_type = T; - - array_t() : array(0, static_cast(nullptr)) {} - array_t(handle h, borrowed_t) : array(h, borrowed_t{}) { } - array_t(handle h, stolen_t) : array(h, stolen_t{}) { } - - PYBIND11_DEPRECATED("Use array_t::ensure() instead") - array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) { - if (!m_ptr) PyErr_Clear(); - if (!is_borrowed) Py_XDECREF(h.ptr()); - } - - array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) { - if (!m_ptr) throw error_already_set(); - } - - explicit array_t(const buffer_info& info, handle base = handle()) : array(info, base) { } - - array_t(ShapeContainer shape, StridesContainer strides, const T *ptr = nullptr, handle base = handle()) - : array(std::move(shape), std::move(strides), ptr, base) { } - - explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle()) - : array_t(private_ctor{}, std::move(shape), - ExtraFlags & f_style - ? detail::f_strides(*shape, itemsize()) - : detail::c_strides(*shape, itemsize()), - ptr, base) { } - - explicit array_t(ssize_t count, const T *ptr = nullptr, handle base = handle()) - : array({count}, {}, ptr, base) { } - - constexpr ssize_t itemsize() const { - return sizeof(T); - } - - template ssize_t index_at(Ix... index) const { - return offset_at(index...) / itemsize(); - } - - template const T* data(Ix... index) const { - return static_cast(array::data(index...)); - } - - template T* mutable_data(Ix... index) { - return static_cast(array::mutable_data(index...)); - } - - // Reference to element at a given index - template const T& at(Ix... index) const { - if ((ssize_t) sizeof...(index) != ndim()) - fail_dim_check(sizeof...(index), "index dimension mismatch"); - return *(static_cast(array::data()) + byte_offset(ssize_t(index)...) / itemsize()); - } - - // Mutable reference to element at a given index - template T& mutable_at(Ix... index) { - if ((ssize_t) sizeof...(index) != ndim()) - fail_dim_check(sizeof...(index), "index dimension mismatch"); - return *(static_cast(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize()); - } - - /** - * Returns a proxy object that provides access to the array's data without bounds or - * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with - * care: the array must not be destroyed or reshaped for the duration of the returned object, - * and the caller must take care not to access invalid dimensions or dimension indices. - */ - template detail::unchecked_mutable_reference mutable_unchecked() & { - return array::mutable_unchecked(); - } - - /** - * Returns a proxy object that provides const access to the array's data without bounds or - * dimensionality checking. Unlike `unchecked()`, this does not require that the underlying - * array have the `writable` flag. Use with care: the array must not be destroyed or reshaped - * for the duration of the returned object, and the caller must take care not to access invalid - * dimensions or dimension indices. - */ - template detail::unchecked_reference unchecked() const & { - return array::unchecked(); - } - - /// Ensure that the argument is a NumPy array of the correct dtype (and if not, try to convert - /// it). In case of an error, nullptr is returned and the Python error is cleared. - static array_t ensure(handle h) { - auto result = reinterpret_steal(raw_array_t(h.ptr())); - if (!result) - PyErr_Clear(); - return result; - } - - static bool check_(handle h) { - const auto &api = detail::npy_api::get(); - return api.PyArray_Check_(h.ptr()) - && api.PyArray_EquivTypes_(detail::array_proxy(h.ptr())->descr, dtype::of().ptr()) - && detail::check_flags(h.ptr(), ExtraFlags & (array::c_style | array::f_style)); - } - -protected: - /// Create array from any object -- always returns a new reference - static PyObject *raw_array_t(PyObject *ptr) { - if (ptr == nullptr) { - PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr"); - return nullptr; - } - return detail::npy_api::get().PyArray_FromAny_( - ptr, dtype::of().release().ptr(), 0, 0, - detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); - } -}; - -template -struct format_descriptor::value>> { - static std::string format() { - return detail::npy_format_descriptor::type>::format(); - } -}; - -template struct format_descriptor { - static std::string format() { return std::to_string(N) + "s"; } -}; -template struct format_descriptor> { - static std::string format() { return std::to_string(N) + "s"; } -}; - -template -struct format_descriptor::value>> { - static std::string format() { - return format_descriptor< - typename std::remove_cv::type>::type>::format(); - } -}; - -template -struct format_descriptor::is_array>> { - static std::string format() { - using namespace detail; - static constexpr auto extents = _("(") + array_info::extents + _(")"); - return extents.text + format_descriptor>::format(); - } -}; - -PYBIND11_NAMESPACE_BEGIN(detail) -template -struct pyobject_caster> { - using type = array_t; - - bool load(handle src, bool convert) { - if (!convert && !type::check_(src)) - return false; - value = type::ensure(src); - return static_cast(value); - } - - static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { - return src.inc_ref(); - } - PYBIND11_TYPE_CASTER(type, handle_type_name::name); -}; - -template -struct compare_buffer_info::value>> { - static bool compare(const buffer_info& b) { - return npy_api::get().PyArray_EquivTypes_(dtype::of().ptr(), dtype(b).ptr()); - } -}; - -template -struct npy_format_descriptor_name; - -template -struct npy_format_descriptor_name::value>> { - static constexpr auto name = _::value>( - _("bool"), _::value>("numpy.int", "numpy.uint") + _() - ); -}; - -template -struct npy_format_descriptor_name::value>> { - static constexpr auto name = _::value || std::is_same::value>( - _("numpy.float") + _(), _("numpy.longdouble") - ); -}; - -template -struct npy_format_descriptor_name::value>> { - static constexpr auto name = _::value - || std::is_same::value>( - _("numpy.complex") + _(), _("numpy.longcomplex") - ); -}; - -template -struct npy_format_descriptor::value>> - : npy_format_descriptor_name { -private: - // NB: the order here must match the one in common.h - constexpr static const int values[15] = { - npy_api::NPY_BOOL_, - npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_INT16_, npy_api::NPY_UINT16_, - npy_api::NPY_INT32_, npy_api::NPY_UINT32_, npy_api::NPY_INT64_, npy_api::NPY_UINT64_, - npy_api::NPY_FLOAT_, npy_api::NPY_DOUBLE_, npy_api::NPY_LONGDOUBLE_, - npy_api::NPY_CFLOAT_, npy_api::NPY_CDOUBLE_, npy_api::NPY_CLONGDOUBLE_ - }; - -public: - static constexpr int value = values[detail::is_fmt_numeric::index]; - - static pybind11::dtype dtype() { - if (auto ptr = npy_api::get().PyArray_DescrFromType_(value)) - return reinterpret_steal(ptr); - pybind11_fail("Unsupported buffer format!"); - } -}; - -#define PYBIND11_DECL_CHAR_FMT \ - static constexpr auto name = _("S") + _(); \ - static pybind11::dtype dtype() { return pybind11::dtype(std::string("S") + std::to_string(N)); } -template struct npy_format_descriptor { PYBIND11_DECL_CHAR_FMT }; -template struct npy_format_descriptor> { PYBIND11_DECL_CHAR_FMT }; -#undef PYBIND11_DECL_CHAR_FMT - -template struct npy_format_descriptor::is_array>> { -private: - using base_descr = npy_format_descriptor::type>; -public: - static_assert(!array_info::is_empty, "Zero-sized arrays are not supported"); - - static constexpr auto name = _("(") + array_info::extents + _(")") + base_descr::name; - static pybind11::dtype dtype() { - list shape; - array_info::append_extents(shape); - return pybind11::dtype::from_args(pybind11::make_tuple(base_descr::dtype(), shape)); - } -}; - -template struct npy_format_descriptor::value>> { -private: - using base_descr = npy_format_descriptor::type>; -public: - static constexpr auto name = base_descr::name; - static pybind11::dtype dtype() { return base_descr::dtype(); } -}; - -struct field_descriptor { - const char *name; - ssize_t offset; - ssize_t size; - std::string format; - dtype descr; -}; - -inline PYBIND11_NOINLINE void register_structured_dtype( - any_container fields, - const std::type_info& tinfo, ssize_t itemsize, - bool (*direct_converter)(PyObject *, void *&)) { - - auto& numpy_internals = get_numpy_internals(); - if (numpy_internals.get_type_info(tinfo, false)) - pybind11_fail("NumPy: dtype is already registered"); - - // Use ordered fields because order matters as of NumPy 1.14: - // https://docs.scipy.org/doc/numpy/release.html#multiple-field-indexing-assignment-of-structured-arrays - std::vector ordered_fields(std::move(fields)); - std::sort(ordered_fields.begin(), ordered_fields.end(), - [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); - - list names, formats, offsets; - for (auto& field : ordered_fields) { - if (!field.descr) - pybind11_fail(std::string("NumPy: unsupported field dtype: `") + - field.name + "` @ " + tinfo.name()); - names.append(PYBIND11_STR_TYPE(field.name)); - formats.append(field.descr); - offsets.append(pybind11::int_(field.offset)); - } - auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr(); - - // There is an existing bug in NumPy (as of v1.11): trailing bytes are - // not encoded explicitly into the format string. This will supposedly - // get fixed in v1.12; for further details, see these: - // - https://github.com/numpy/numpy/issues/7797 - // - https://github.com/numpy/numpy/pull/7798 - // Because of this, we won't use numpy's logic to generate buffer format - // strings and will just do it ourselves. - ssize_t offset = 0; - std::ostringstream oss; - // mark the structure as unaligned with '^', because numpy and C++ don't - // always agree about alignment (particularly for complex), and we're - // explicitly listing all our padding. This depends on none of the fields - // overriding the endianness. Putting the ^ in front of individual fields - // isn't guaranteed to work due to https://github.com/numpy/numpy/issues/9049 - oss << "^T{"; - for (auto& field : ordered_fields) { - if (field.offset > offset) - oss << (field.offset - offset) << 'x'; - oss << field.format << ':' << field.name << ':'; - offset = field.offset + field.size; - } - if (itemsize > offset) - oss << (itemsize - offset) << 'x'; - oss << '}'; - auto format_str = oss.str(); - - // Sanity check: verify that NumPy properly parses our buffer format string - auto& api = npy_api::get(); - auto arr = array(buffer_info(nullptr, itemsize, format_str, 1)); - if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) - pybind11_fail("NumPy: invalid buffer descriptor!"); - - auto tindex = std::type_index(tinfo); - numpy_internals.registered_dtypes[tindex] = { dtype_ptr, format_str }; - get_internals().direct_conversions[tindex].push_back(direct_converter); -} - -template struct npy_format_descriptor { - static_assert(is_pod_struct::value, "Attempt to use a non-POD or unimplemented POD type as a numpy dtype"); - - static constexpr auto name = make_caster::name; - - static pybind11::dtype dtype() { - return reinterpret_borrow(dtype_ptr()); - } - - static std::string format() { - static auto format_str = get_numpy_internals().get_type_info(true)->format_str; - return format_str; - } - - static void register_dtype(any_container fields) { - register_structured_dtype(std::move(fields), typeid(typename std::remove_cv::type), - sizeof(T), &direct_converter); - } - -private: - static PyObject* dtype_ptr() { - static PyObject* ptr = get_numpy_internals().get_type_info(true)->dtype_ptr; - return ptr; - } - - static bool direct_converter(PyObject *obj, void*& value) { - auto& api = npy_api::get(); - if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_)) - return false; - if (auto descr = reinterpret_steal(api.PyArray_DescrFromScalar_(obj))) { - if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) { - value = ((PyVoidScalarObject_Proxy *) obj)->obval; - return true; - } - } - return false; - } -}; - -#ifdef __CLION_IDE__ // replace heavy macro with dummy code for the IDE (doesn't affect code) -# define PYBIND11_NUMPY_DTYPE(Type, ...) ((void)0) -# define PYBIND11_NUMPY_DTYPE_EX(Type, ...) ((void)0) -#else - -#define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name) \ - ::pybind11::detail::field_descriptor { \ - Name, offsetof(T, Field), sizeof(decltype(std::declval().Field)), \ - ::pybind11::format_descriptor().Field)>::format(), \ - ::pybind11::detail::npy_format_descriptor().Field)>::dtype() \ - } - -// Extract name, offset and format descriptor for a struct field -#define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field) - -// The main idea of this macro is borrowed from https://github.com/swansontec/map-macro -// (C) William Swanson, Paul Fultz -#define PYBIND11_EVAL0(...) __VA_ARGS__ -#define PYBIND11_EVAL1(...) PYBIND11_EVAL0 (PYBIND11_EVAL0 (PYBIND11_EVAL0 (__VA_ARGS__))) -#define PYBIND11_EVAL2(...) PYBIND11_EVAL1 (PYBIND11_EVAL1 (PYBIND11_EVAL1 (__VA_ARGS__))) -#define PYBIND11_EVAL3(...) PYBIND11_EVAL2 (PYBIND11_EVAL2 (PYBIND11_EVAL2 (__VA_ARGS__))) -#define PYBIND11_EVAL4(...) PYBIND11_EVAL3 (PYBIND11_EVAL3 (PYBIND11_EVAL3 (__VA_ARGS__))) -#define PYBIND11_EVAL(...) PYBIND11_EVAL4 (PYBIND11_EVAL4 (PYBIND11_EVAL4 (__VA_ARGS__))) -#define PYBIND11_MAP_END(...) -#define PYBIND11_MAP_OUT -#define PYBIND11_MAP_COMMA , -#define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END -#define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT -#define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0 (test, next, 0) -#define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1 (PYBIND11_MAP_GET_END test, next) -#if defined(_MSC_VER) && !defined(__clang__) // MSVC is not as eager to expand macros, hence this workaround -#define PYBIND11_MAP_LIST_NEXT1(test, next) \ - PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) -#else -#define PYBIND11_MAP_LIST_NEXT1(test, next) \ - PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) -#endif -#define PYBIND11_MAP_LIST_NEXT(test, next) \ - PYBIND11_MAP_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) -#define PYBIND11_MAP_LIST0(f, t, x, peek, ...) \ - f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST1) (f, t, peek, __VA_ARGS__) -#define PYBIND11_MAP_LIST1(f, t, x, peek, ...) \ - f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST0) (f, t, peek, __VA_ARGS__) -// PYBIND11_MAP_LIST(f, t, a1, a2, ...) expands to f(t, a1), f(t, a2), ... -#define PYBIND11_MAP_LIST(f, t, ...) \ - PYBIND11_EVAL (PYBIND11_MAP_LIST1 (f, t, __VA_ARGS__, (), 0)) - -#define PYBIND11_NUMPY_DTYPE(Type, ...) \ - ::pybind11::detail::npy_format_descriptor::register_dtype \ - (::std::vector<::pybind11::detail::field_descriptor> \ - {PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)}) - -#if defined(_MSC_VER) && !defined(__clang__) -#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ - PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) -#else -#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ - PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) -#endif -#define PYBIND11_MAP2_LIST_NEXT(test, next) \ - PYBIND11_MAP2_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) -#define PYBIND11_MAP2_LIST0(f, t, x1, x2, peek, ...) \ - f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST1) (f, t, peek, __VA_ARGS__) -#define PYBIND11_MAP2_LIST1(f, t, x1, x2, peek, ...) \ - f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST0) (f, t, peek, __VA_ARGS__) -// PYBIND11_MAP2_LIST(f, t, a1, a2, ...) expands to f(t, a1, a2), f(t, a3, a4), ... -#define PYBIND11_MAP2_LIST(f, t, ...) \ - PYBIND11_EVAL (PYBIND11_MAP2_LIST1 (f, t, __VA_ARGS__, (), 0)) - -#define PYBIND11_NUMPY_DTYPE_EX(Type, ...) \ - ::pybind11::detail::npy_format_descriptor::register_dtype \ - (::std::vector<::pybind11::detail::field_descriptor> \ - {PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)}) - -#endif // __CLION_IDE__ - -class common_iterator { -public: - using container_type = std::vector; - using value_type = container_type::value_type; - using size_type = container_type::size_type; - - common_iterator() : p_ptr(0), m_strides() {} - - common_iterator(void* ptr, const container_type& strides, const container_type& shape) - : p_ptr(reinterpret_cast(ptr)), m_strides(strides.size()) { - m_strides.back() = static_cast(strides.back()); - for (size_type i = m_strides.size() - 1; i != 0; --i) { - size_type j = i - 1; - auto s = static_cast(shape[i]); - m_strides[j] = strides[j] + m_strides[i] - strides[i] * s; - } - } - - void increment(size_type dim) { - p_ptr += m_strides[dim]; - } - - void* data() const { - return p_ptr; - } - -private: - char* p_ptr; - container_type m_strides; -}; - -template class multi_array_iterator { -public: - using container_type = std::vector; - - multi_array_iterator(const std::array &buffers, - const container_type &shape) - : m_shape(shape.size()), m_index(shape.size(), 0), - m_common_iterator() { - - // Manual copy to avoid conversion warning if using std::copy - for (size_t i = 0; i < shape.size(); ++i) - m_shape[i] = shape[i]; - - container_type strides(shape.size()); - for (size_t i = 0; i < N; ++i) - init_common_iterator(buffers[i], shape, m_common_iterator[i], strides); - } - - multi_array_iterator& operator++() { - for (size_t j = m_index.size(); j != 0; --j) { - size_t i = j - 1; - if (++m_index[i] != m_shape[i]) { - increment_common_iterator(i); - break; - } else { - m_index[i] = 0; - } - } - return *this; - } - - template T* data() const { - return reinterpret_cast(m_common_iterator[K].data()); - } - -private: - - using common_iter = common_iterator; - - void init_common_iterator(const buffer_info &buffer, - const container_type &shape, - common_iter &iterator, - container_type &strides) { - auto buffer_shape_iter = buffer.shape.rbegin(); - auto buffer_strides_iter = buffer.strides.rbegin(); - auto shape_iter = shape.rbegin(); - auto strides_iter = strides.rbegin(); - - while (buffer_shape_iter != buffer.shape.rend()) { - if (*shape_iter == *buffer_shape_iter) - *strides_iter = *buffer_strides_iter; - else - *strides_iter = 0; - - ++buffer_shape_iter; - ++buffer_strides_iter; - ++shape_iter; - ++strides_iter; - } - - std::fill(strides_iter, strides.rend(), 0); - iterator = common_iter(buffer.ptr, strides, shape); - } - - void increment_common_iterator(size_t dim) { - for (auto &iter : m_common_iterator) - iter.increment(dim); - } - - container_type m_shape; - container_type m_index; - std::array m_common_iterator; -}; - -enum class broadcast_trivial { non_trivial, c_trivial, f_trivial }; - -// Populates the shape and number of dimensions for the set of buffers. Returns a broadcast_trivial -// enum value indicating whether the broadcast is "trivial"--that is, has each buffer being either a -// singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage -// buffer; returns `non_trivial` otherwise. -template -broadcast_trivial broadcast(const std::array &buffers, ssize_t &ndim, std::vector &shape) { - ndim = std::accumulate(buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info &buf) { - return std::max(res, buf.ndim); - }); - - shape.clear(); - shape.resize((size_t) ndim, 1); - - // Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or - // the full size). - for (size_t i = 0; i < N; ++i) { - auto res_iter = shape.rbegin(); - auto end = buffers[i].shape.rend(); - for (auto shape_iter = buffers[i].shape.rbegin(); shape_iter != end; ++shape_iter, ++res_iter) { - const auto &dim_size_in = *shape_iter; - auto &dim_size_out = *res_iter; - - // Each input dimension can either be 1 or `n`, but `n` values must match across buffers - if (dim_size_out == 1) - dim_size_out = dim_size_in; - else if (dim_size_in != 1 && dim_size_in != dim_size_out) - pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!"); - } - } - - bool trivial_broadcast_c = true; - bool trivial_broadcast_f = true; - for (size_t i = 0; i < N && (trivial_broadcast_c || trivial_broadcast_f); ++i) { - if (buffers[i].size == 1) - continue; - - // Require the same number of dimensions: - if (buffers[i].ndim != ndim) - return broadcast_trivial::non_trivial; - - // Require all dimensions be full-size: - if (!std::equal(buffers[i].shape.cbegin(), buffers[i].shape.cend(), shape.cbegin())) - return broadcast_trivial::non_trivial; - - // Check for C contiguity (but only if previous inputs were also C contiguous) - if (trivial_broadcast_c) { - ssize_t expect_stride = buffers[i].itemsize; - auto end = buffers[i].shape.crend(); - for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin(); - trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) { - if (expect_stride == *stride_iter) - expect_stride *= *shape_iter; - else - trivial_broadcast_c = false; - } - } - - // Check for Fortran contiguity (if previous inputs were also F contiguous) - if (trivial_broadcast_f) { - ssize_t expect_stride = buffers[i].itemsize; - auto end = buffers[i].shape.cend(); - for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin(); - trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) { - if (expect_stride == *stride_iter) - expect_stride *= *shape_iter; - else - trivial_broadcast_f = false; - } - } - } - - return - trivial_broadcast_c ? broadcast_trivial::c_trivial : - trivial_broadcast_f ? broadcast_trivial::f_trivial : - broadcast_trivial::non_trivial; -} - -template -struct vectorize_arg { - static_assert(!std::is_rvalue_reference::value, "Functions with rvalue reference arguments cannot be vectorized"); - // The wrapped function gets called with this type: - using call_type = remove_reference_t; - // Is this a vectorized argument? - static constexpr bool vectorize = - satisfies_any_of::value && - satisfies_none_of::value && - (!std::is_reference::value || - (std::is_lvalue_reference::value && std::is_const::value)); - // Accept this type: an array for vectorized types, otherwise the type as-is: - using type = conditional_t, array::forcecast>, T>; -}; - - -// py::vectorize when a return type is present -template -struct vectorize_returned_array { - using Type = array_t; - - static Type create(broadcast_trivial trivial, const std::vector &shape) { - if (trivial == broadcast_trivial::f_trivial) - return array_t(shape); - else - return array_t(shape); - } - - static Return *mutable_data(Type &array) { - return array.mutable_data(); - } - - static Return call(Func &f, Args &... args) { - return f(args...); - } - - static void call(Return *out, size_t i, Func &f, Args &... args) { - out[i] = f(args...); - } -}; - -// py::vectorize when a return type is not present -template -struct vectorize_returned_array { - using Type = none; - - static Type create(broadcast_trivial, const std::vector &) { - return none(); - } - - static void *mutable_data(Type &) { - return nullptr; - } - - static detail::void_type call(Func &f, Args &... args) { - f(args...); - return {}; - } - - static void call(void *, size_t, Func &f, Args &... args) { - f(args...); - } -}; - - -template -struct vectorize_helper { - -// NVCC for some reason breaks if NVectorized is private -#ifdef __CUDACC__ -public: -#else -private: -#endif - - static constexpr size_t N = sizeof...(Args); - static constexpr size_t NVectorized = constexpr_sum(vectorize_arg::vectorize...); - static_assert(NVectorized >= 1, - "pybind11::vectorize(...) requires a function with at least one vectorizable argument"); - -public: - template - explicit vectorize_helper(T &&f) : f(std::forward(f)) { } - - object operator()(typename vectorize_arg::type... args) { - return run(args..., - make_index_sequence(), - select_indices::vectorize...>(), - make_index_sequence()); - } - -private: - remove_reference_t f; - - // Internal compiler error in MSVC 19.16.27025.1 (Visual Studio 2017 15.9.4), when compiling with "/permissive-" flag - // when arg_call_types is manually inlined. - using arg_call_types = std::tuple::call_type...>; - template using param_n_t = typename std::tuple_element::type; - - using returned_array = vectorize_returned_array; - - // Runs a vectorized function given arguments tuple and three index sequences: - // - Index is the full set of 0 ... (N-1) argument indices; - // - VIndex is the subset of argument indices with vectorized parameters, letting us access - // vectorized arguments (anything not in this sequence is passed through) - // - BIndex is a incremental sequence (beginning at 0) of the same size as VIndex, so that - // we can store vectorized buffer_infos in an array (argument VIndex has its buffer at - // index BIndex in the array). - template object run( - typename vectorize_arg::type &...args, - index_sequence i_seq, index_sequence vi_seq, index_sequence bi_seq) { - - // Pointers to values the function was called with; the vectorized ones set here will start - // out as array_t pointers, but they will be changed them to T pointers before we make - // call the wrapped function. Non-vectorized pointers are left as-is. - std::array params{{ &args... }}; - - // The array of `buffer_info`s of vectorized arguments: - std::array buffers{{ reinterpret_cast(params[VIndex])->request()... }}; - - /* Determine dimensions parameters of output array */ - ssize_t nd = 0; - std::vector shape(0); - auto trivial = broadcast(buffers, nd, shape); - auto ndim = (size_t) nd; - - size_t size = std::accumulate(shape.begin(), shape.end(), (size_t) 1, std::multiplies()); - - // If all arguments are 0-dimension arrays (i.e. single values) return a plain value (i.e. - // not wrapped in an array). - if (size == 1 && ndim == 0) { - PYBIND11_EXPAND_SIDE_EFFECTS(params[VIndex] = buffers[BIndex].ptr); - return cast(returned_array::call(f, *reinterpret_cast *>(params[Index])...)); - } - - auto result = returned_array::create(trivial, shape); - - if (size == 0) return std::move(result); - - /* Call the function */ - auto mutable_data = returned_array::mutable_data(result); - if (trivial == broadcast_trivial::non_trivial) - apply_broadcast(buffers, params, mutable_data, size, shape, i_seq, vi_seq, bi_seq); - else - apply_trivial(buffers, params, mutable_data, size, i_seq, vi_seq, bi_seq); - - return std::move(result); - } - - template - void apply_trivial(std::array &buffers, - std::array ¶ms, - Return *out, - size_t size, - index_sequence, index_sequence, index_sequence) { - - // Initialize an array of mutable byte references and sizes with references set to the - // appropriate pointer in `params`; as we iterate, we'll increment each pointer by its size - // (except for singletons, which get an increment of 0). - std::array, NVectorized> vecparams{{ - std::pair( - reinterpret_cast(params[VIndex] = buffers[BIndex].ptr), - buffers[BIndex].size == 1 ? 0 : sizeof(param_n_t) - )... - }}; - - for (size_t i = 0; i < size; ++i) { - returned_array::call(out, i, f, *reinterpret_cast *>(params[Index])...); - for (auto &x : vecparams) x.first += x.second; - } - } - - template - void apply_broadcast(std::array &buffers, - std::array ¶ms, - Return *out, - size_t size, - const std::vector &output_shape, - index_sequence, index_sequence, index_sequence) { - - multi_array_iterator input_iter(buffers, output_shape); - - for (size_t i = 0; i < size; ++i, ++input_iter) { - PYBIND11_EXPAND_SIDE_EFFECTS(( - params[VIndex] = input_iter.template data() - )); - returned_array::call(out, i, f, *reinterpret_cast *>(std::get(params))...); - } - } -}; - -template -vectorize_helper -vectorize_extractor(const Func &f, Return (*) (Args ...)) { - return detail::vectorize_helper(f); -} - -template struct handle_type_name> { - static constexpr auto name = _("numpy.ndarray[") + npy_format_descriptor::name + _("]"); -}; - -PYBIND11_NAMESPACE_END(detail) - -// Vanilla pointer vectorizer: -template -detail::vectorize_helper -vectorize(Return (*f) (Args ...)) { - return detail::vectorize_helper(f); -} - -// lambda vectorizer: -template ::value, int> = 0> -auto vectorize(Func &&f) -> decltype( - detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr)) { - return detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr); -} - -// Vectorize a class method (non-const): -template ())), Return, Class *, Args...>> -Helper vectorize(Return (Class::*f)(Args...)) { - return Helper(std::mem_fn(f)); -} - -// Vectorize a class method (const): -template ())), Return, const Class *, Args...>> -Helper vectorize(Return (Class::*f)(Args...) const) { - return Helper(std::mem_fn(f)); -} - -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/test/src/pybind11/operators.h b/test/src/pybind11/operators.h deleted file mode 100644 index 086cb4cfd..000000000 --- a/test/src/pybind11/operators.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - pybind11/operator.h: Metatemplates for operator overloading - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" - -#if defined(__clang__) && !defined(__INTEL_COMPILER) -# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type())) -#elif defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#endif - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -PYBIND11_NAMESPACE_BEGIN(detail) - -/// Enumeration with all supported operator types -enum op_id : int { - op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, - op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, - op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, - op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, - op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, - op_repr, op_truediv, op_itruediv, op_hash -}; - -enum op_type : int { - op_l, /* base type on left */ - op_r, /* base type on right */ - op_u /* unary operator */ -}; - -struct self_t { }; -static const self_t self = self_t(); - -/// Type for an unused type slot -struct undefined_t { }; - -/// Don't warn about an unused variable -inline self_t __self() { return self; } - -/// base template of operator implementations -template struct op_impl { }; - -/// Operator implementation generator -template struct op_ { - template void execute(Class &cl, const Extra&... extra) const { - using Base = typename Class::type; - using L_type = conditional_t::value, Base, L>; - using R_type = conditional_t::value, Base, R>; - using op = op_impl; - cl.def(op::name(), &op::execute, is_operator(), extra...); - #if PY_MAJOR_VERSION < 3 - if (id == op_truediv || id == op_itruediv) - cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", - &op::execute, is_operator(), extra...); - #endif - } - template void execute_cast(Class &cl, const Extra&... extra) const { - using Base = typename Class::type; - using L_type = conditional_t::value, Base, L>; - using R_type = conditional_t::value, Base, R>; - using op = op_impl; - cl.def(op::name(), &op::execute_cast, is_operator(), extra...); - #if PY_MAJOR_VERSION < 3 - if (id == op_truediv || id == op_itruediv) - cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", - &op::execute, is_operator(), extra...); - #endif - } -}; - -#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ -template struct op_impl { \ - static char const* name() { return "__" #id "__"; } \ - static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ - static B execute_cast(const L &l, const R &r) { return B(expr); } \ -}; \ -template struct op_impl { \ - static char const* name() { return "__" #rid "__"; } \ - static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ - static B execute_cast(const R &r, const L &l) { return B(expr); } \ -}; \ -inline op_ op(const self_t &, const self_t &) { \ - return op_(); \ -} \ -template op_ op(const self_t &, const T &) { \ - return op_(); \ -} \ -template op_ op(const T &, const self_t &) { \ - return op_(); \ -} - -#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ -template struct op_impl { \ - static char const* name() { return "__" #id "__"; } \ - static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \ - static B execute_cast(L &l, const R &r) { return B(expr); } \ -}; \ -template op_ op(const self_t &, const T &) { \ - return op_(); \ -} - -#define PYBIND11_UNARY_OPERATOR(id, op, expr) \ -template struct op_impl { \ - static char const* name() { return "__" #id "__"; } \ - static auto execute(const L &l) -> decltype(expr) { return expr; } \ - static B execute_cast(const L &l) { return B(expr); } \ -}; \ -inline op_ op(const self_t &) { \ - return op_(); \ -} - -PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) -PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) -PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r) -PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) -PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) -PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) -PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) -PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r) -PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) -PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) -PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) -PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r) -PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) -PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) -PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) -PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) -//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) -PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) -PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) -PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) -PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r) -PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) -PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) -PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) -PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) -PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) -PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) -PYBIND11_UNARY_OPERATOR(neg, operator-, -l) -PYBIND11_UNARY_OPERATOR(pos, operator+, +l) -// WARNING: This usage of `abs` should only be done for existing STL overloads. -// Adding overloads directly in to the `std::` namespace is advised against: -// https://en.cppreference.com/w/cpp/language/extending_std -PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) -PYBIND11_UNARY_OPERATOR(hash, hash, std::hash()(l)) -PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) -PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) -PYBIND11_UNARY_OPERATOR(int, int_, (int) l) -PYBIND11_UNARY_OPERATOR(float, float_, (double) l) - -#undef PYBIND11_BINARY_OPERATOR -#undef PYBIND11_INPLACE_OPERATOR -#undef PYBIND11_UNARY_OPERATOR -PYBIND11_NAMESPACE_END(detail) - -using detail::self; -// Add named operators so that they are accessible via `py::`. -using detail::hash; - -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) - -#if defined(_MSC_VER) -# pragma warning(pop) -#endif diff --git a/test/src/pybind11/options.h b/test/src/pybind11/options.h deleted file mode 100644 index d74db1c68..000000000 --- a/test/src/pybind11/options.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - pybind11/options.h: global settings that are configurable at runtime. - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "detail/common.h" - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -class options { -public: - - // Default RAII constructor, which leaves settings as they currently are. - options() : previous_state(global_state()) {} - - // Class is non-copyable. - options(const options&) = delete; - options& operator=(const options&) = delete; - - // Destructor, which restores settings that were in effect before. - ~options() { - global_state() = previous_state; - } - - // Setter methods (affect the global state): - - options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; } - - options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; } - - options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; } - - options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } - - // Getter methods (return the global state): - - static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } - - static bool show_function_signatures() { return global_state().show_function_signatures; } - - // This type is not meant to be allocated on the heap. - void* operator new(size_t) = delete; - -private: - - struct state { - bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. - bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings. - }; - - static state &global_state() { - static state instance; - return instance; - } - - state previous_state; -}; - -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/test/src/pybind11/pybind11.h b/test/src/pybind11/pybind11.h deleted file mode 100644 index 82003a909..000000000 --- a/test/src/pybind11/pybind11.h +++ /dev/null @@ -1,2401 +0,0 @@ -/* - pybind11/pybind11.h: Main header file of the C++11 python - binding generator library - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#if defined(__INTEL_COMPILER) -# pragma warning push -# pragma warning disable 68 // integer conversion resulted in a change of sign -# pragma warning disable 186 // pointless comparison of unsigned integer with zero -# pragma warning disable 878 // incompatible exception specifications -# pragma warning disable 1334 // the "template" keyword used for syntactic disambiguation may only be used within a template -# pragma warning disable 1682 // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) -# pragma warning disable 1786 // function "strdup" was declared deprecated -# pragma warning disable 1875 // offsetof applied to non-POD (Plain Old Data) types is nonstandard -# pragma warning disable 2196 // warning #2196: routine is both "inline" and "noinline" -#elif defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted -# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) -# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name -# pragma warning(disable: 4702) // warning C4702: unreachable code -# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified -# pragma warning(disable: 4505) // warning C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed (PyPy only) -#elif defined(__GNUG__) && !defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-but-set-parameter" -# pragma GCC diagnostic ignored "-Wunused-but-set-variable" -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -# pragma GCC diagnostic ignored "-Wstrict-aliasing" -# pragma GCC diagnostic ignored "-Wattributes" -# if __GNUC__ >= 7 -# pragma GCC diagnostic ignored "-Wnoexcept-type" -# endif -#endif - -#include "attr.h" -#include "options.h" -#include "detail/class.h" -#include "detail/init.h" - -#include -#include -#include -#include - -#if defined(__GNUG__) && !defined(__clang__) -# include -#endif - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object -class cpp_function : public function { -public: - cpp_function() = default; - cpp_function(std::nullptr_t) { } - - /// Construct a cpp_function from a vanilla function pointer - template - cpp_function(Return (*f)(Args...), const Extra&... extra) { - initialize(f, f, extra...); - } - - /// Construct a cpp_function from a lambda function (possibly with internal state) - template ::value>> - cpp_function(Func &&f, const Extra&... extra) { - initialize(std::forward(f), - (detail::function_signature_t *) nullptr, extra...); - } - - /// Construct a cpp_function from a class method (non-const, no ref-qualifier) - template - cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { - initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(std::forward(args)...); }, - (Return (*) (Class *, Arg...)) nullptr, extra...); - } - - /// Construct a cpp_function from a class method (non-const, lvalue ref-qualifier) - /// A copy of the overload for non-const functions without explicit ref-qualifier - /// but with an added `&`. - template - cpp_function(Return (Class::*f)(Arg...)&, const Extra&... extra) { - initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, - (Return (*) (Class *, Arg...)) nullptr, extra...); - } - - /// Construct a cpp_function from a class method (const, no ref-qualifier) - template - cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { - initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(std::forward(args)...); }, - (Return (*)(const Class *, Arg ...)) nullptr, extra...); - } - - /// Construct a cpp_function from a class method (const, lvalue ref-qualifier) - /// A copy of the overload for const functions without explicit ref-qualifier - /// but with an added `&`. - template - cpp_function(Return (Class::*f)(Arg...) const&, const Extra&... extra) { - initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, - (Return (*)(const Class *, Arg ...)) nullptr, extra...); - } - - /// Return the function name - object name() const { return attr("__name__"); } - -protected: - /// Space optimization: don't inline this frequently instantiated fragment - PYBIND11_NOINLINE detail::function_record *make_function_record() { - return new detail::function_record(); - } - - /// Special internal constructor for functors, lambda functions, etc. - template - void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) { - using namespace detail; - struct capture { remove_reference_t f; }; - - /* Store the function including any extra state it might have (e.g. a lambda capture object) */ - auto rec = make_function_record(); - - /* Store the capture object directly in the function record if there is enough space */ - if (sizeof(capture) <= sizeof(rec->data)) { - /* Without these pragmas, GCC warns that there might not be - enough space to use the placement new operator. However, the - 'if' statement above ensures that this is the case. */ -#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wplacement-new" -#endif - new ((capture *) &rec->data) capture { std::forward(f) }; -#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 -# pragma GCC diagnostic pop -#endif - if (!std::is_trivially_destructible::value) - rec->free_data = [](function_record *r) { ((capture *) &r->data)->~capture(); }; - } else { - rec->data[0] = new capture { std::forward(f) }; - rec->free_data = [](function_record *r) { delete ((capture *) r->data[0]); }; - } - - /* Type casters for the function arguments and return value */ - using cast_in = argument_loader; - using cast_out = make_caster< - conditional_t::value, void_type, Return> - >; - - static_assert(expected_num_args(sizeof...(Args), cast_in::has_args, cast_in::has_kwargs), - "The number of argument annotations does not match the number of function arguments"); - - /* Dispatch code which converts function arguments and performs the actual function call */ - rec->impl = [](function_call &call) -> handle { - cast_in args_converter; - - /* Try to cast the function arguments into the C++ domain */ - if (!args_converter.load_args(call)) - return PYBIND11_TRY_NEXT_OVERLOAD; - - /* Invoke call policy pre-call hook */ - process_attributes::precall(call); - - /* Get a pointer to the capture object */ - auto data = (sizeof(capture) <= sizeof(call.func.data) - ? &call.func.data : call.func.data[0]); - auto *cap = const_cast(reinterpret_cast(data)); - - /* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */ - return_value_policy policy = return_value_policy_override::policy(call.func.policy); - - /* Function scope guard -- defaults to the compile-to-nothing `void_type` */ - using Guard = extract_guard_t; - - /* Perform the function call */ - handle result = cast_out::cast( - std::move(args_converter).template call(cap->f), policy, call.parent); - - /* Invoke call policy post-call hook */ - process_attributes::postcall(call, result); - - return result; - }; - - /* Process any user-provided function attributes */ - process_attributes::init(extra..., rec); - - { - constexpr bool has_kw_only_args = any_of...>::value, - has_pos_only_args = any_of...>::value, - has_args = any_of...>::value, - has_arg_annotations = any_of...>::value; - static_assert(has_arg_annotations || !has_kw_only_args, "py::kw_only requires the use of argument annotations"); - static_assert(has_arg_annotations || !has_pos_only_args, "py::pos_only requires the use of argument annotations (for docstrings and aligning the annotations to the argument)"); - static_assert(!(has_args && has_kw_only_args), "py::kw_only cannot be combined with a py::args argument"); - } - - /* Generate a readable signature describing the function's arguments and return value types */ - static constexpr auto signature = _("(") + cast_in::arg_names + _(") -> ") + cast_out::name; - PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types(); - - /* Register the function with Python from generic (non-templated) code */ - initialize_generic(rec, signature.text, types.data(), sizeof...(Args)); - - if (cast_in::has_args) rec->has_args = true; - if (cast_in::has_kwargs) rec->has_kwargs = true; - - /* Stash some additional information used by an important optimization in 'functional.h' */ - using FunctionType = Return (*)(Args...); - constexpr bool is_function_ptr = - std::is_convertible::value && - sizeof(capture) == sizeof(void *); - if (is_function_ptr) { - rec->is_stateless = true; - rec->data[1] = const_cast(reinterpret_cast(&typeid(FunctionType))); - } - } - - /// Register a function call with Python (generic non-templated code goes here) - void initialize_generic(detail::function_record *rec, const char *text, - const std::type_info *const *types, size_t args) { - - /* Create copies of all referenced C-style strings */ - rec->name = strdup(rec->name ? rec->name : ""); - if (rec->doc) rec->doc = strdup(rec->doc); - for (auto &a: rec->args) { - if (a.name) - a.name = strdup(a.name); - if (a.descr) - a.descr = strdup(a.descr); - else if (a.value) - a.descr = strdup(repr(a.value).cast().c_str()); - } - - rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__"); - -#if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING) - if (rec->is_constructor && !rec->is_new_style_constructor) { - const auto class_name = detail::get_fully_qualified_tp_name((PyTypeObject *) rec->scope.ptr()); - const auto func_name = std::string(rec->name); - PyErr_WarnEx( - PyExc_FutureWarning, - ("pybind11-bound class '" + class_name + "' is using an old-style " - "placement-new '" + func_name + "' which has been deprecated. See " - "the upgrade guide in pybind11's docs. This message is only visible " - "when compiled in debug mode.").c_str(), 0 - ); - } -#endif - - /* Generate a proper function signature */ - std::string signature; - size_t type_index = 0, arg_index = 0; - for (auto *pc = text; *pc != '\0'; ++pc) { - const auto c = *pc; - - if (c == '{') { - // Write arg name for everything except *args and **kwargs. - if (*(pc + 1) == '*') - continue; - // Separator for keyword-only arguments, placed before the kw - // arguments start - if (rec->nargs_kw_only > 0 && arg_index + rec->nargs_kw_only == args) - signature += "*, "; - if (arg_index < rec->args.size() && rec->args[arg_index].name) { - signature += rec->args[arg_index].name; - } else if (arg_index == 0 && rec->is_method) { - signature += "self"; - } else { - signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0)); - } - signature += ": "; - } else if (c == '}') { - // Write default value if available. - if (arg_index < rec->args.size() && rec->args[arg_index].descr) { - signature += " = "; - signature += rec->args[arg_index].descr; - } - // Separator for positional-only arguments (placed after the - // argument, rather than before like * - if (rec->nargs_pos_only > 0 && (arg_index + 1) == rec->nargs_pos_only) - signature += ", /"; - arg_index++; - } else if (c == '%') { - const std::type_info *t = types[type_index++]; - if (!t) - pybind11_fail("Internal error while parsing type signature (1)"); - if (auto tinfo = detail::get_type_info(*t)) { - handle th((PyObject *) tinfo->type); - signature += - th.attr("__module__").cast() + "." + - th.attr("__qualname__").cast(); // Python 3.3+, but we backport it to earlier versions - } else if (rec->is_new_style_constructor && arg_index == 0) { - // A new-style `__init__` takes `self` as `value_and_holder`. - // Rewrite it to the proper class type. - signature += - rec->scope.attr("__module__").cast() + "." + - rec->scope.attr("__qualname__").cast(); - } else { - std::string tname(t->name()); - detail::clean_type_id(tname); - signature += tname; - } - } else { - signature += c; - } - } - - if (arg_index != args || types[type_index] != nullptr) - pybind11_fail("Internal error while parsing type signature (2)"); - -#if PY_MAJOR_VERSION < 3 - if (strcmp(rec->name, "__next__") == 0) { - std::free(rec->name); - rec->name = strdup("next"); - } else if (strcmp(rec->name, "__bool__") == 0) { - std::free(rec->name); - rec->name = strdup("__nonzero__"); - } -#endif - rec->signature = strdup(signature.c_str()); - rec->args.shrink_to_fit(); - rec->nargs = (std::uint16_t) args; - - if (rec->sibling && PYBIND11_INSTANCE_METHOD_CHECK(rec->sibling.ptr())) - rec->sibling = PYBIND11_INSTANCE_METHOD_GET_FUNCTION(rec->sibling.ptr()); - - detail::function_record *chain = nullptr, *chain_start = rec; - if (rec->sibling) { - if (PyCFunction_Check(rec->sibling.ptr())) { - auto rec_capsule = reinterpret_borrow(PyCFunction_GET_SELF(rec->sibling.ptr())); - chain = (detail::function_record *) rec_capsule; - /* Never append a method to an overload chain of a parent class; - instead, hide the parent's overloads in this case */ - if (!chain->scope.is(rec->scope)) - chain = nullptr; - } - // Don't trigger for things like the default __init__, which are wrapper_descriptors that we are intentionally replacing - else if (!rec->sibling.is_none() && rec->name[0] != '_') - pybind11_fail("Cannot overload existing non-function object \"" + std::string(rec->name) + - "\" with a function of the same name"); - } - - if (!chain) { - /* No existing overload was found, create a new function object */ - rec->def = new PyMethodDef(); - std::memset(rec->def, 0, sizeof(PyMethodDef)); - rec->def->ml_name = rec->name; - rec->def->ml_meth = reinterpret_cast(reinterpret_cast(*dispatcher)); - rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; - - capsule rec_capsule(rec, [](void *ptr) { - destruct((detail::function_record *) ptr); - }); - - object scope_module; - if (rec->scope) { - if (hasattr(rec->scope, "__module__")) { - scope_module = rec->scope.attr("__module__"); - } else if (hasattr(rec->scope, "__name__")) { - scope_module = rec->scope.attr("__name__"); - } - } - - m_ptr = PyCFunction_NewEx(rec->def, rec_capsule.ptr(), scope_module.ptr()); - if (!m_ptr) - pybind11_fail("cpp_function::cpp_function(): Could not allocate function object"); - } else { - /* Append at the beginning or end of the overload chain */ - m_ptr = rec->sibling.ptr(); - inc_ref(); - if (chain->is_method != rec->is_method) - pybind11_fail("overloading a method with both static and instance methods is not supported; " - #if defined(NDEBUG) - "compile in debug mode for more details" - #else - "error while attempting to bind " + std::string(rec->is_method ? "instance" : "static") + " method " + - std::string(pybind11::str(rec->scope.attr("__name__"))) + "." + std::string(rec->name) + signature - #endif - ); - - if (rec->prepend) { - // Beginning of chain; we need to replace the capsule's current head-of-the-chain - // pointer with this one, then make this one point to the previous head of the - // chain. - chain_start = rec; - rec->next = chain; - auto rec_capsule = reinterpret_borrow(((PyCFunctionObject *) m_ptr)->m_self); - rec_capsule.set_pointer(rec); - } else { - // Or end of chain (normal behavior) - chain_start = chain; - while (chain->next) - chain = chain->next; - chain->next = rec; - } - } - - std::string signatures; - int index = 0; - /* Create a nice pydoc rec including all signatures and - docstrings of the functions in the overload chain */ - if (chain && options::show_function_signatures()) { - // First a generic signature - signatures += rec->name; - signatures += "(*args, **kwargs)\n"; - signatures += "Overloaded function.\n\n"; - } - // Then specific overload signatures - bool first_user_def = true; - for (auto it = chain_start; it != nullptr; it = it->next) { - if (options::show_function_signatures()) { - if (index > 0) signatures += "\n"; - if (chain) - signatures += std::to_string(++index) + ". "; - signatures += rec->name; - signatures += it->signature; - signatures += "\n"; - } - if (it->doc && strlen(it->doc) > 0 && options::show_user_defined_docstrings()) { - // If we're appending another docstring, and aren't printing function signatures, we - // need to append a newline first: - if (!options::show_function_signatures()) { - if (first_user_def) first_user_def = false; - else signatures += "\n"; - } - if (options::show_function_signatures()) signatures += "\n"; - signatures += it->doc; - if (options::show_function_signatures()) signatures += "\n"; - } - } - - /* Install docstring */ - auto *func = (PyCFunctionObject *) m_ptr; - std::free(const_cast(func->m_ml->ml_doc)); - // Install docstring if it's non-empty (when at least one option is enabled) - func->m_ml->ml_doc = signatures.empty() ? nullptr : strdup(signatures.c_str()); - - if (rec->is_method) { - m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); - if (!m_ptr) - pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object"); - Py_DECREF(func); - } - } - - /// When a cpp_function is GCed, release any memory allocated by pybind11 - static void destruct(detail::function_record *rec) { - // If on Python 3.9, check the interpreter "MICRO" (patch) version. - // If this is running on 3.9.0, we have to work around a bug. - #if !defined(PYPY_VERSION) && PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 9 - static bool is_zero = Py_GetVersion()[4] == '0'; - #endif - - while (rec) { - detail::function_record *next = rec->next; - if (rec->free_data) - rec->free_data(rec); - std::free((char *) rec->name); - std::free((char *) rec->doc); - std::free((char *) rec->signature); - for (auto &arg: rec->args) { - std::free(const_cast(arg.name)); - std::free(const_cast(arg.descr)); - arg.value.dec_ref(); - } - if (rec->def) { - std::free(const_cast(rec->def->ml_doc)); - // Python 3.9.0 decref's these in the wrong order; rec->def - // If loaded on 3.9.0, let these leak (use Python 3.9.1 at runtime to fix) - // See https://github.com/python/cpython/pull/22670 - #if !defined(PYPY_VERSION) && PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 9 - if (!is_zero) - delete rec->def; - #else - delete rec->def; - #endif - } - delete rec; - rec = next; - } - } - - /// Main dispatch logic for calls to functions bound using pybind11 - static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) { - using namespace detail; - - /* Iterator over the list of potentially admissible overloads */ - const function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr), - *it = overloads; - - /* Need to know how many arguments + keyword arguments there are to pick the right overload */ - const auto n_args_in = (size_t) PyTuple_GET_SIZE(args_in); - - handle parent = n_args_in > 0 ? PyTuple_GET_ITEM(args_in, 0) : nullptr, - result = PYBIND11_TRY_NEXT_OVERLOAD; - - auto self_value_and_holder = value_and_holder(); - if (overloads->is_constructor) { - if (!PyObject_TypeCheck(parent.ptr(), (PyTypeObject *) overloads->scope.ptr())) { - PyErr_SetString(PyExc_TypeError, "__init__(self, ...) called with invalid `self` argument"); - return nullptr; - } - - const auto tinfo = get_type_info((PyTypeObject *) overloads->scope.ptr()); - const auto pi = reinterpret_cast(parent.ptr()); - self_value_and_holder = pi->get_value_and_holder(tinfo, true); - - // If this value is already registered it must mean __init__ is invoked multiple times; - // we really can't support that in C++, so just ignore the second __init__. - if (self_value_and_holder.instance_registered()) - return none().release().ptr(); - } - - try { - // We do this in two passes: in the first pass, we load arguments with `convert=false`; - // in the second, we allow conversion (except for arguments with an explicit - // py::arg().noconvert()). This lets us prefer calls without conversion, with - // conversion as a fallback. - std::vector second_pass; - - // However, if there are no overloads, we can just skip the no-convert pass entirely - const bool overloaded = it != nullptr && it->next != nullptr; - - for (; it != nullptr; it = it->next) { - - /* For each overload: - 1. Copy all positional arguments we were given, also checking to make sure that - named positional arguments weren't *also* specified via kwarg. - 2. If we weren't given enough, try to make up the omitted ones by checking - whether they were provided by a kwarg matching the `py::arg("name")` name. If - so, use it (and remove it from kwargs; if not, see if the function binding - provided a default that we can use. - 3. Ensure that either all keyword arguments were "consumed", or that the function - takes a kwargs argument to accept unconsumed kwargs. - 4. Any positional arguments still left get put into a tuple (for args), and any - leftover kwargs get put into a dict. - 5. Pack everything into a vector; if we have py::args or py::kwargs, they are an - extra tuple or dict at the end of the positional arguments. - 6. Call the function call dispatcher (function_record::impl) - - If one of these fail, move on to the next overload and keep trying until we get a - result other than PYBIND11_TRY_NEXT_OVERLOAD. - */ - - const function_record &func = *it; - size_t num_args = func.nargs; // Number of positional arguments that we need - if (func.has_args) --num_args; // (but don't count py::args - if (func.has_kwargs) --num_args; // or py::kwargs) - size_t pos_args = num_args - func.nargs_kw_only; - - if (!func.has_args && n_args_in > pos_args) - continue; // Too many positional arguments for this overload - - if (n_args_in < pos_args && func.args.size() < pos_args) - continue; // Not enough positional arguments given, and not enough defaults to fill in the blanks - - function_call call(func, parent); - - size_t args_to_copy = (std::min)(pos_args, n_args_in); // Protect std::min with parentheses - size_t args_copied = 0; - - // 0. Inject new-style `self` argument - if (func.is_new_style_constructor) { - // The `value` may have been preallocated by an old-style `__init__` - // if it was a preceding candidate for overload resolution. - if (self_value_and_holder) - self_value_and_holder.type->dealloc(self_value_and_holder); - - call.init_self = PyTuple_GET_ITEM(args_in, 0); - call.args.emplace_back(reinterpret_cast(&self_value_and_holder)); - call.args_convert.push_back(false); - ++args_copied; - } - - // 1. Copy any position arguments given. - bool bad_arg = false; - for (; args_copied < args_to_copy; ++args_copied) { - const argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr; - if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) { - bad_arg = true; - break; - } - - handle arg(PyTuple_GET_ITEM(args_in, args_copied)); - if (arg_rec && !arg_rec->none && arg.is_none()) { - bad_arg = true; - break; - } - call.args.push_back(arg); - call.args_convert.push_back(arg_rec ? arg_rec->convert : true); - } - if (bad_arg) - continue; // Maybe it was meant for another overload (issue #688) - - // We'll need to copy this if we steal some kwargs for defaults - dict kwargs = reinterpret_borrow(kwargs_in); - - // 1.5. Fill in any missing pos_only args from defaults if they exist - if (args_copied < func.nargs_pos_only) { - for (; args_copied < func.nargs_pos_only; ++args_copied) { - const auto &arg_rec = func.args[args_copied]; - handle value; - - if (arg_rec.value) { - value = arg_rec.value; - } - if (value) { - call.args.push_back(value); - call.args_convert.push_back(arg_rec.convert); - } else - break; - } - - if (args_copied < func.nargs_pos_only) - continue; // Not enough defaults to fill the positional arguments - } - - // 2. Check kwargs and, failing that, defaults that may help complete the list - if (args_copied < num_args) { - bool copied_kwargs = false; - - for (; args_copied < num_args; ++args_copied) { - const auto &arg_rec = func.args[args_copied]; - - handle value; - if (kwargs_in && arg_rec.name) - value = PyDict_GetItemString(kwargs.ptr(), arg_rec.name); - - if (value) { - // Consume a kwargs value - if (!copied_kwargs) { - kwargs = reinterpret_steal(PyDict_Copy(kwargs.ptr())); - copied_kwargs = true; - } - PyDict_DelItemString(kwargs.ptr(), arg_rec.name); - } else if (arg_rec.value) { - value = arg_rec.value; - } - - if (!arg_rec.none && value.is_none()) { - break; - } - - if (value) { - call.args.push_back(value); - call.args_convert.push_back(arg_rec.convert); - } - else - break; - } - - if (args_copied < num_args) - continue; // Not enough arguments, defaults, or kwargs to fill the positional arguments - } - - // 3. Check everything was consumed (unless we have a kwargs arg) - if (kwargs && !kwargs.empty() && !func.has_kwargs) - continue; // Unconsumed kwargs, but no py::kwargs argument to accept them - - // 4a. If we have a py::args argument, create a new tuple with leftovers - if (func.has_args) { - tuple extra_args; - if (args_to_copy == 0) { - // We didn't copy out any position arguments from the args_in tuple, so we - // can reuse it directly without copying: - extra_args = reinterpret_borrow(args_in); - } else if (args_copied >= n_args_in) { - extra_args = tuple(0); - } else { - size_t args_size = n_args_in - args_copied; - extra_args = tuple(args_size); - for (size_t i = 0; i < args_size; ++i) { - extra_args[i] = PyTuple_GET_ITEM(args_in, args_copied + i); - } - } - call.args.push_back(extra_args); - call.args_convert.push_back(false); - call.args_ref = std::move(extra_args); - } - - // 4b. If we have a py::kwargs, pass on any remaining kwargs - if (func.has_kwargs) { - if (!kwargs.ptr()) - kwargs = dict(); // If we didn't get one, send an empty one - call.args.push_back(kwargs); - call.args_convert.push_back(false); - call.kwargs_ref = std::move(kwargs); - } - - // 5. Put everything in a vector. Not technically step 5, we've been building it - // in `call.args` all along. - #if !defined(NDEBUG) - if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) - pybind11_fail("Internal error: function call dispatcher inserted wrong number of arguments!"); - #endif - - std::vector second_pass_convert; - if (overloaded) { - // We're in the first no-convert pass, so swap out the conversion flags for a - // set of all-false flags. If the call fails, we'll swap the flags back in for - // the conversion-allowed call below. - second_pass_convert.resize(func.nargs, false); - call.args_convert.swap(second_pass_convert); - } - - // 6. Call the function. - try { - loader_life_support guard{}; - result = func.impl(call); - } catch (reference_cast_error &) { - result = PYBIND11_TRY_NEXT_OVERLOAD; - } - - if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) - break; - - if (overloaded) { - // The (overloaded) call failed; if the call has at least one argument that - // permits conversion (i.e. it hasn't been explicitly specified `.noconvert()`) - // then add this call to the list of second pass overloads to try. - for (size_t i = func.is_method ? 1 : 0; i < pos_args; i++) { - if (second_pass_convert[i]) { - // Found one: swap the converting flags back in and store the call for - // the second pass. - call.args_convert.swap(second_pass_convert); - second_pass.push_back(std::move(call)); - break; - } - } - } - } - - if (overloaded && !second_pass.empty() && result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { - // The no-conversion pass finished without success, try again with conversion allowed - for (auto &call : second_pass) { - try { - loader_life_support guard{}; - result = call.func.impl(call); - } catch (reference_cast_error &) { - result = PYBIND11_TRY_NEXT_OVERLOAD; - } - - if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) { - // The error reporting logic below expects 'it' to be valid, as it would be - // if we'd encountered this failure in the first-pass loop. - if (!result) - it = &call.func; - break; - } - } - } - } catch (error_already_set &e) { - e.restore(); - return nullptr; -#ifdef __GLIBCXX__ - } catch ( abi::__forced_unwind& ) { - throw; -#endif - } catch (...) { - /* When an exception is caught, give each registered exception - translator a chance to translate it to a Python exception - in reverse order of registration. - - A translator may choose to do one of the following: - - - catch the exception and call PyErr_SetString or PyErr_SetObject - to set a standard (or custom) Python exception, or - - do nothing and let the exception fall through to the next translator, or - - delegate translation to the next translator by throwing a new type of exception. */ - - auto last_exception = std::current_exception(); - auto ®istered_exception_translators = get_internals().registered_exception_translators; - for (auto& translator : registered_exception_translators) { - try { - translator(last_exception); - } catch (...) { - last_exception = std::current_exception(); - continue; - } - return nullptr; - } - PyErr_SetString(PyExc_SystemError, "Exception escaped from default exception translator!"); - return nullptr; - } - - auto append_note_if_missing_header_is_suspected = [](std::string &msg) { - if (msg.find("std::") != std::string::npos) { - msg += "\n\n" - "Did you forget to `#include `? Or ,\n" - ", , etc. Some automatic\n" - "conversions are optional and require extra headers to be included\n" - "when compiling your pybind11 module."; - } - }; - - if (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { - if (overloads->is_operator) - return handle(Py_NotImplemented).inc_ref().ptr(); - - std::string msg = std::string(overloads->name) + "(): incompatible " + - std::string(overloads->is_constructor ? "constructor" : "function") + - " arguments. The following argument types are supported:\n"; - - int ctr = 0; - for (const function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) { - msg += " "+ std::to_string(++ctr) + ". "; - - bool wrote_sig = false; - if (overloads->is_constructor) { - // For a constructor, rewrite `(self: Object, arg0, ...) -> NoneType` as `Object(arg0, ...)` - std::string sig = it2->signature; - size_t start = sig.find('(') + 7; // skip "(self: " - if (start < sig.size()) { - // End at the , for the next argument - size_t end = sig.find(", "), next = end + 2; - size_t ret = sig.rfind(" -> "); - // Or the ), if there is no comma: - if (end >= sig.size()) next = end = sig.find(')'); - if (start < end && next < sig.size()) { - msg.append(sig, start, end - start); - msg += '('; - msg.append(sig, next, ret - next); - wrote_sig = true; - } - } - } - if (!wrote_sig) msg += it2->signature; - - msg += "\n"; - } - msg += "\nInvoked with: "; - auto args_ = reinterpret_borrow(args_in); - bool some_args = false; - for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) { - if (!some_args) some_args = true; - else msg += ", "; - try { - msg += pybind11::repr(args_[ti]); - } catch (const error_already_set&) { - msg += ""; - } - } - if (kwargs_in) { - auto kwargs = reinterpret_borrow(kwargs_in); - if (!kwargs.empty()) { - if (some_args) msg += "; "; - msg += "kwargs: "; - bool first = true; - for (auto kwarg : kwargs) { - if (first) first = false; - else msg += ", "; - msg += pybind11::str("{}=").format(kwarg.first); - try { - msg += pybind11::repr(kwarg.second); - } catch (const error_already_set&) { - msg += ""; - } - } - } - } - - append_note_if_missing_header_is_suspected(msg); - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return nullptr; - } else if (!result) { - std::string msg = "Unable to convert function return value to a " - "Python type! The signature was\n\t"; - msg += it->signature; - append_note_if_missing_header_is_suspected(msg); - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return nullptr; - } else { - if (overloads->is_constructor && !self_value_and_holder.holder_constructed()) { - auto *pi = reinterpret_cast(parent.ptr()); - self_value_and_holder.type->init_instance(pi, nullptr); - } - return result.ptr(); - } - } -}; - -/// Wrapper for Python extension modules -class module_ : public object { -public: - PYBIND11_OBJECT_DEFAULT(module_, object, PyModule_Check) - - /// Create a new top-level Python module with the given name and docstring - PYBIND11_DEPRECATED("Use PYBIND11_MODULE or module_::create_extension_module instead") - explicit module_(const char *name, const char *doc = nullptr) { -#if PY_MAJOR_VERSION >= 3 - *this = create_extension_module(name, doc, new PyModuleDef()); -#else - *this = create_extension_module(name, doc, nullptr); -#endif - } - - /** \rst - Create Python binding for a new function within the module scope. ``Func`` - can be a plain C++ function, a function pointer, or a lambda function. For - details on the ``Extra&& ... extra`` argument, see section :ref:`extras`. - \endrst */ - template - module_ &def(const char *name_, Func &&f, const Extra& ... extra) { - cpp_function func(std::forward(f), name(name_), scope(*this), - sibling(getattr(*this, name_, none())), extra...); - // NB: allow overwriting here because cpp_function sets up a chain with the intention of - // overwriting (and has already checked internally that it isn't overwriting non-functions). - add_object(name_, func, true /* overwrite */); - return *this; - } - - /** \rst - Create and return a new Python submodule with the given name and docstring. - This also works recursively, i.e. - - .. code-block:: cpp - - py::module_ m("example", "pybind11 example plugin"); - py::module_ m2 = m.def_submodule("sub", "A submodule of 'example'"); - py::module_ m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'"); - \endrst */ - module_ def_submodule(const char *name, const char *doc = nullptr) { - std::string full_name = std::string(PyModule_GetName(m_ptr)) - + std::string(".") + std::string(name); - auto result = reinterpret_borrow(PyImport_AddModule(full_name.c_str())); - if (doc && options::show_user_defined_docstrings()) - result.attr("__doc__") = pybind11::str(doc); - attr(name) = result; - return result; - } - - /// Import and return a module or throws `error_already_set`. - static module_ import(const char *name) { - PyObject *obj = PyImport_ImportModule(name); - if (!obj) - throw error_already_set(); - return reinterpret_steal(obj); - } - - /// Reload the module or throws `error_already_set`. - void reload() { - PyObject *obj = PyImport_ReloadModule(ptr()); - if (!obj) - throw error_already_set(); - *this = reinterpret_steal(obj); - } - - /** \rst - Adds an object to the module using the given name. Throws if an object with the given name - already exists. - - ``overwrite`` should almost always be false: attempting to overwrite objects that pybind11 has - established will, in most cases, break things. - \endrst */ - PYBIND11_NOINLINE void add_object(const char *name, handle obj, bool overwrite = false) { - if (!overwrite && hasattr(*this, name)) - pybind11_fail("Error during initialization: multiple incompatible definitions with name \"" + - std::string(name) + "\""); - - PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); - } - -#if PY_MAJOR_VERSION >= 3 - using module_def = PyModuleDef; -#else - struct module_def {}; -#endif - - /** \rst - Create a new top-level module that can be used as the main module of a C extension. - - For Python 3, ``def`` should point to a staticly allocated module_def. - For Python 2, ``def`` can be a nullptr and is completely ignored. - \endrst */ - static module_ create_extension_module(const char *name, const char *doc, module_def *def) { -#if PY_MAJOR_VERSION >= 3 - // module_def is PyModuleDef - def = new (def) PyModuleDef { // Placement new (not an allocation). - /* m_base */ PyModuleDef_HEAD_INIT, - /* m_name */ name, - /* m_doc */ options::show_user_defined_docstrings() ? doc : nullptr, - /* m_size */ -1, - /* m_methods */ nullptr, - /* m_slots */ nullptr, - /* m_traverse */ nullptr, - /* m_clear */ nullptr, - /* m_free */ nullptr - }; - auto m = PyModule_Create(def); -#else - // Ignore module_def *def; only necessary for Python 3 - (void) def; - auto m = Py_InitModule3(name, nullptr, options::show_user_defined_docstrings() ? doc : nullptr); -#endif - if (m == nullptr) { - if (PyErr_Occurred()) - throw error_already_set(); - pybind11_fail("Internal error in module_::create_extension_module()"); - } - // TODO: Sould be reinterpret_steal for Python 3, but Python also steals it again when returned from PyInit_... - // For Python 2, reinterpret_borrow is correct. - return reinterpret_borrow(m); - } -}; - -// When inside a namespace (or anywhere as long as it's not the first item on a line), -// C++20 allows "module" to be used. This is provided for backward compatibility, and for -// simplicity, if someone wants to use py::module for example, that is perfectly safe. -using module = module_; - -/// \ingroup python_builtins -/// Return a dictionary representing the global variables in the current execution frame, -/// or ``__main__.__dict__`` if there is no frame (usually when the interpreter is embedded). -inline dict globals() { - PyObject *p = PyEval_GetGlobals(); - return reinterpret_borrow(p ? p : module_::import("__main__").attr("__dict__").ptr()); -} - -PYBIND11_NAMESPACE_BEGIN(detail) -/// Generic support for creating new Python heap types -class generic_type : public object { -public: - PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) -protected: - void initialize(const type_record &rec) { - if (rec.scope && hasattr(rec.scope, "__dict__") && rec.scope.attr("__dict__").contains(rec.name)) - pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) + - "\": an object with that name is already defined"); - - if (rec.module_local ? get_local_type_info(*rec.type) : get_global_type_info(*rec.type)) - pybind11_fail("generic_type: type \"" + std::string(rec.name) + - "\" is already registered!"); - - m_ptr = make_new_python_type(rec); - - /* Register supplemental type information in C++ dict */ - auto *tinfo = new detail::type_info(); - tinfo->type = (PyTypeObject *) m_ptr; - tinfo->cpptype = rec.type; - tinfo->type_size = rec.type_size; - tinfo->type_align = rec.type_align; - tinfo->operator_new = rec.operator_new; - tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); - tinfo->init_instance = rec.init_instance; - tinfo->dealloc = rec.dealloc; - tinfo->simple_type = true; - tinfo->simple_ancestors = true; - tinfo->default_holder = rec.default_holder; - tinfo->module_local = rec.module_local; - - auto &internals = get_internals(); - auto tindex = std::type_index(*rec.type); - tinfo->direct_conversions = &internals.direct_conversions[tindex]; - if (rec.module_local) - registered_local_types_cpp()[tindex] = tinfo; - else - internals.registered_types_cpp[tindex] = tinfo; - internals.registered_types_py[(PyTypeObject *) m_ptr] = { tinfo }; - - if (rec.bases.size() > 1 || rec.multiple_inheritance) { - mark_parents_nonsimple(tinfo->type); - tinfo->simple_ancestors = false; - } - else if (rec.bases.size() == 1) { - auto parent_tinfo = get_type_info((PyTypeObject *) rec.bases[0].ptr()); - tinfo->simple_ancestors = parent_tinfo->simple_ancestors; - } - - if (rec.module_local) { - // Stash the local typeinfo and loader so that external modules can access it. - tinfo->module_local_load = &type_caster_generic::local_load; - setattr(m_ptr, PYBIND11_MODULE_LOCAL_ID, capsule(tinfo)); - } - } - - /// Helper function which tags all parents of a type using mult. inheritance - void mark_parents_nonsimple(PyTypeObject *value) { - auto t = reinterpret_borrow(value->tp_bases); - for (handle h : t) { - auto tinfo2 = get_type_info((PyTypeObject *) h.ptr()); - if (tinfo2) - tinfo2->simple_type = false; - mark_parents_nonsimple((PyTypeObject *) h.ptr()); - } - } - - void install_buffer_funcs( - buffer_info *(*get_buffer)(PyObject *, void *), - void *get_buffer_data) { - auto *type = (PyHeapTypeObject*) m_ptr; - auto tinfo = detail::get_type_info(&type->ht_type); - - if (!type->ht_type.tp_as_buffer) - pybind11_fail( - "To be able to register buffer protocol support for the type '" + - get_fully_qualified_tp_name(tinfo->type) + - "' the associated class<>(..) invocation must " - "include the pybind11::buffer_protocol() annotation!"); - - tinfo->get_buffer = get_buffer; - tinfo->get_buffer_data = get_buffer_data; - } - - // rec_func must be set for either fget or fset. - void def_property_static_impl(const char *name, - handle fget, handle fset, - detail::function_record *rec_func) { - const auto is_static = rec_func && !(rec_func->is_method && rec_func->scope); - const auto has_doc = rec_func && rec_func->doc && pybind11::options::show_user_defined_docstrings(); - auto property = handle((PyObject *) (is_static ? get_internals().static_property_type - : &PyProperty_Type)); - attr(name) = property(fget.ptr() ? fget : none(), - fset.ptr() ? fset : none(), - /*deleter*/none(), - pybind11::str(has_doc ? rec_func->doc : "")); - } -}; - -/// Set the pointer to operator new if it exists. The cast is needed because it can be overloaded. -template (T::operator new))>> -void set_operator_new(type_record *r) { r->operator_new = &T::operator new; } - -template void set_operator_new(...) { } - -template struct has_operator_delete : std::false_type { }; -template struct has_operator_delete(T::operator delete))>> - : std::true_type { }; -template struct has_operator_delete_size : std::false_type { }; -template struct has_operator_delete_size(T::operator delete))>> - : std::true_type { }; -/// Call class-specific delete if it exists or global otherwise. Can also be an overload set. -template ::value, int> = 0> -void call_operator_delete(T *p, size_t, size_t) { T::operator delete(p); } -template ::value && has_operator_delete_size::value, int> = 0> -void call_operator_delete(T *p, size_t s, size_t) { T::operator delete(p, s); } - -inline void call_operator_delete(void *p, size_t s, size_t a) { - (void)s; (void)a; - #if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912) - if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { - #ifdef __cpp_sized_deallocation - ::operator delete(p, s, std::align_val_t(a)); - #else - ::operator delete(p, std::align_val_t(a)); - #endif - return; - } - #endif - #ifdef __cpp_sized_deallocation - ::operator delete(p, s); - #else - ::operator delete(p); - #endif -} - -inline void add_class_method(object& cls, const char *name_, const cpp_function &cf) { - cls.attr(cf.name()) = cf; - if (strcmp(name_, "__eq__") == 0 && !cls.attr("__dict__").contains("__hash__")) { - cls.attr("__hash__") = none(); - } -} - -PYBIND11_NAMESPACE_END(detail) - -/// Given a pointer to a member function, cast it to its `Derived` version. -/// Forward everything else unchanged. -template -auto method_adaptor(F &&f) -> decltype(std::forward(f)) { return std::forward(f); } - -template -auto method_adaptor(Return (Class::*pmf)(Args...)) -> Return (Derived::*)(Args...) { - static_assert(detail::is_accessible_base_of::value, - "Cannot bind an inaccessible base class method; use a lambda definition instead"); - return pmf; -} - -template -auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(Args...) const { - static_assert(detail::is_accessible_base_of::value, - "Cannot bind an inaccessible base class method; use a lambda definition instead"); - return pmf; -} - -template -class class_ : public detail::generic_type { - template using is_holder = detail::is_holder_type; - template using is_subtype = detail::is_strict_base_of; - template using is_base = detail::is_strict_base_of; - // struct instead of using here to help MSVC: - template struct is_valid_class_option : - detail::any_of, is_subtype, is_base> {}; - -public: - using type = type_; - using type_alias = detail::exactly_one_t; - constexpr static bool has_alias = !std::is_void::value; - using holder_type = detail::exactly_one_t, options...>; - - static_assert(detail::all_of...>::value, - "Unknown/invalid class_ template parameters provided"); - - static_assert(!has_alias || std::is_polymorphic::value, - "Cannot use an alias class with a non-polymorphic type"); - - PYBIND11_OBJECT(class_, generic_type, PyType_Check) - - template - class_(handle scope, const char *name, const Extra &... extra) { - using namespace detail; - - // MI can only be specified via class_ template options, not constructor parameters - static_assert( - none_of...>::value || // no base class arguments, or: - ( constexpr_sum(is_pyobject::value...) == 1 && // Exactly one base - constexpr_sum(is_base::value...) == 0 && // no template option bases - none_of...>::value), // no multiple_inheritance attr - "Error: multiple inheritance bases must be specified via class_ template options"); - - type_record record; - record.scope = scope; - record.name = name; - record.type = &typeid(type); - record.type_size = sizeof(conditional_t); - record.type_align = alignof(conditional_t&); - record.holder_size = sizeof(holder_type); - record.init_instance = init_instance; - record.dealloc = dealloc; - record.default_holder = detail::is_instantiation::value; - - set_operator_new(&record); - - /* Register base classes specified via template arguments to class_, if any */ - PYBIND11_EXPAND_SIDE_EFFECTS(add_base(record)); - - /* Process optional arguments, if any */ - process_attributes::init(extra..., &record); - - generic_type::initialize(record); - - if (has_alias) { - auto &instances = record.module_local ? registered_local_types_cpp() : get_internals().registered_types_cpp; - instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))]; - } - } - - template ::value, int> = 0> - static void add_base(detail::type_record &rec) { - rec.add_base(typeid(Base), [](void *src) -> void * { - return static_cast(reinterpret_cast(src)); - }); - } - - template ::value, int> = 0> - static void add_base(detail::type_record &) { } - - template - class_ &def(const char *name_, Func&& f, const Extra&... extra) { - cpp_function cf(method_adaptor(std::forward(f)), name(name_), is_method(*this), - sibling(getattr(*this, name_, none())), extra...); - add_class_method(*this, name_, cf); - return *this; - } - - template class_ & - def_static(const char *name_, Func &&f, const Extra&... extra) { - static_assert(!std::is_member_function_pointer::value, - "def_static(...) called with a non-static member function pointer"); - cpp_function cf(std::forward(f), name(name_), scope(*this), - sibling(getattr(*this, name_, none())), extra...); - attr(cf.name()) = staticmethod(cf); - return *this; - } - - template - class_ &def(const detail::op_ &op, const Extra&... extra) { - op.execute(*this, extra...); - return *this; - } - - template - class_ & def_cast(const detail::op_ &op, const Extra&... extra) { - op.execute_cast(*this, extra...); - return *this; - } - - template - class_ &def(const detail::initimpl::constructor &init, const Extra&... extra) { - init.execute(*this, extra...); - return *this; - } - - template - class_ &def(const detail::initimpl::alias_constructor &init, const Extra&... extra) { - init.execute(*this, extra...); - return *this; - } - - template - class_ &def(detail::initimpl::factory &&init, const Extra&... extra) { - std::move(init).execute(*this, extra...); - return *this; - } - - template - class_ &def(detail::initimpl::pickle_factory &&pf, const Extra &...extra) { - std::move(pf).execute(*this, extra...); - return *this; - } - - template - class_& def_buffer(Func &&func) { - struct capture { Func func; }; - auto *ptr = new capture { std::forward(func) }; - install_buffer_funcs([](PyObject *obj, void *ptr) -> buffer_info* { - detail::make_caster caster; - if (!caster.load(obj, false)) - return nullptr; - return new buffer_info(((capture *) ptr)->func(caster)); - }, ptr); - weakref(m_ptr, cpp_function([ptr](handle wr) { - delete ptr; - wr.dec_ref(); - })).release(); - return *this; - } - - template - class_ &def_buffer(Return (Class::*func)(Args...)) { - return def_buffer([func] (type &obj) { return (obj.*func)(); }); - } - - template - class_ &def_buffer(Return (Class::*func)(Args...) const) { - return def_buffer([func] (const type &obj) { return (obj.*func)(); }); - } - - template - class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) { - static_assert(std::is_same::value || std::is_base_of::value, "def_readwrite() requires a class member (or base class member)"); - cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)), - fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this)); - def_property(name, fget, fset, return_value_policy::reference_internal, extra...); - return *this; - } - - template - class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) { - static_assert(std::is_same::value || std::is_base_of::value, "def_readonly() requires a class member (or base class member)"); - cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)); - def_property_readonly(name, fget, return_value_policy::reference_internal, extra...); - return *this; - } - - template - class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) { - cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)), - fset([pm](object, const D &value) { *pm = value; }, scope(*this)); - def_property_static(name, fget, fset, return_value_policy::reference, extra...); - return *this; - } - - template - class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) { - cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)); - def_property_readonly_static(name, fget, return_value_policy::reference, extra...); - return *this; - } - - /// Uses return_value_policy::reference_internal by default - template - class_ &def_property_readonly(const char *name, const Getter &fget, const Extra& ...extra) { - return def_property_readonly(name, cpp_function(method_adaptor(fget)), - return_value_policy::reference_internal, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) { - return def_property(name, fget, nullptr, extra...); - } - - /// Uses return_value_policy::reference by default - template - class_ &def_property_readonly_static(const char *name, const Getter &fget, const Extra& ...extra) { - return def_property_readonly_static(name, cpp_function(fget), return_value_policy::reference, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) { - return def_property_static(name, fget, nullptr, extra...); - } - - /// Uses return_value_policy::reference_internal by default - template - class_ &def_property(const char *name, const Getter &fget, const Setter &fset, const Extra& ...extra) { - return def_property(name, fget, cpp_function(method_adaptor(fset)), extra...); - } - template - class_ &def_property(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { - return def_property(name, cpp_function(method_adaptor(fget)), fset, - return_value_policy::reference_internal, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { - return def_property_static(name, fget, fset, is_method(*this), extra...); - } - - /// Uses return_value_policy::reference by default - template - class_ &def_property_static(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { - return def_property_static(name, cpp_function(fget), fset, return_value_policy::reference, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { - static_assert( 0 == detail::constexpr_sum(std::is_base_of::value...), - "Argument annotations are not allowed for properties"); - auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset); - auto *rec_active = rec_fget; - if (rec_fget) { - char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */ - detail::process_attributes::init(extra..., rec_fget); - if (rec_fget->doc && rec_fget->doc != doc_prev) { - free(doc_prev); - rec_fget->doc = strdup(rec_fget->doc); - } - } - if (rec_fset) { - char *doc_prev = rec_fset->doc; - detail::process_attributes::init(extra..., rec_fset); - if (rec_fset->doc && rec_fset->doc != doc_prev) { - free(doc_prev); - rec_fset->doc = strdup(rec_fset->doc); - } - if (! rec_active) rec_active = rec_fset; - } - def_property_static_impl(name, fget, fset, rec_active); - return *this; - } - -private: - /// Initialize holder object, variant 1: object derives from enable_shared_from_this - template - static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, - const holder_type * /* unused */, const std::enable_shared_from_this * /* dummy */) { - try { - auto sh = std::dynamic_pointer_cast( - v_h.value_ptr()->shared_from_this()); - if (sh) { - new (std::addressof(v_h.holder())) holder_type(std::move(sh)); - v_h.set_holder_constructed(); - } - } catch (const std::bad_weak_ptr &) {} - - if (!v_h.holder_constructed() && inst->owned) { - new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); - v_h.set_holder_constructed(); - } - } - - static void init_holder_from_existing(const detail::value_and_holder &v_h, - const holder_type *holder_ptr, std::true_type /*is_copy_constructible*/) { - new (std::addressof(v_h.holder())) holder_type(*reinterpret_cast(holder_ptr)); - } - - static void init_holder_from_existing(const detail::value_and_holder &v_h, - const holder_type *holder_ptr, std::false_type /*is_copy_constructible*/) { - new (std::addressof(v_h.holder())) holder_type(std::move(*const_cast(holder_ptr))); - } - - /// Initialize holder object, variant 2: try to construct from existing holder object, if possible - static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, - const holder_type *holder_ptr, const void * /* dummy -- not enable_shared_from_this) */) { - if (holder_ptr) { - init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible()); - v_h.set_holder_constructed(); - } else if (inst->owned || detail::always_construct_holder::value) { - new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); - v_h.set_holder_constructed(); - } - } - - /// Performs instance initialization including constructing a holder and registering the known - /// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes an - /// optional pointer to an existing holder to use; if not specified and the instance is - /// `.owned`, a new holder will be constructed to manage the value pointer. - static void init_instance(detail::instance *inst, const void *holder_ptr) { - auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type))); - if (!v_h.instance_registered()) { - register_instance(inst, v_h.value_ptr(), v_h.type); - v_h.set_instance_registered(); - } - init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr()); - } - - /// Deallocates an instance; via holder, if constructed; otherwise via operator delete. - static void dealloc(detail::value_and_holder &v_h) { - // We could be deallocating because we are cleaning up after a Python exception. - // If so, the Python error indicator will be set. We need to clear that before - // running the destructor, in case the destructor code calls more Python. - // If we don't, the Python API will exit with an exception, and pybind11 will - // throw error_already_set from the C++ destructor which is forbidden and triggers - // std::terminate(). - error_scope scope; - if (v_h.holder_constructed()) { - v_h.holder().~holder_type(); - v_h.set_holder_constructed(false); - } - else { - detail::call_operator_delete(v_h.value_ptr(), - v_h.type->type_size, - v_h.type->type_align - ); - } - v_h.value_ptr() = nullptr; - } - - static detail::function_record *get_function_record(handle h) { - h = detail::get_function(h); - return h ? (detail::function_record *) reinterpret_borrow(PyCFunction_GET_SELF(h.ptr())) - : nullptr; - } -}; - -/// Binds an existing constructor taking arguments Args... -template detail::initimpl::constructor init() { return {}; } -/// Like `init()`, but the instance is always constructed through the alias class (even -/// when not inheriting on the Python side). -template detail::initimpl::alias_constructor init_alias() { return {}; } - -/// Binds a factory function as a constructor -template > -Ret init(Func &&f) { return {std::forward(f)}; } - -/// Dual-argument factory function: the first function is called when no alias is needed, the second -/// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical. -template > -Ret init(CFunc &&c, AFunc &&a) { - return {std::forward(c), std::forward(a)}; -} - -/// Binds pickling functions `__getstate__` and `__setstate__` and ensures that the type -/// returned by `__getstate__` is the same as the argument accepted by `__setstate__`. -template -detail::initimpl::pickle_factory pickle(GetState &&g, SetState &&s) { - return {std::forward(g), std::forward(s)}; -} - -PYBIND11_NAMESPACE_BEGIN(detail) - -inline str enum_name(handle arg) { - dict entries = arg.get_type().attr("__entries"); - for (auto kv : entries) { - if (handle(kv.second[int_(0)]).equal(arg)) - return pybind11::str(kv.first); - } - return "???"; -} - -struct enum_base { - enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { } - - PYBIND11_NOINLINE void init(bool is_arithmetic, bool is_convertible) { - m_base.attr("__entries") = dict(); - auto property = handle((PyObject *) &PyProperty_Type); - auto static_property = handle((PyObject *) get_internals().static_property_type); - - m_base.attr("__repr__") = cpp_function( - [](object arg) -> str { - handle type = type::handle_of(arg); - object type_name = type.attr("__name__"); - return pybind11::str("<{}.{}: {}>").format(type_name, enum_name(arg), int_(arg)); - }, name("__repr__"), is_method(m_base) - ); - - m_base.attr("name") = property(cpp_function(&enum_name, name("name"), is_method(m_base))); - - m_base.attr("__str__") = cpp_function( - [](handle arg) -> str { - object type_name = type::handle_of(arg).attr("__name__"); - return pybind11::str("{}.{}").format(type_name, enum_name(arg)); - }, name("name"), is_method(m_base) - ); - - m_base.attr("__doc__") = static_property(cpp_function( - [](handle arg) -> std::string { - std::string docstring; - dict entries = arg.attr("__entries"); - if (((PyTypeObject *) arg.ptr())->tp_doc) - docstring += std::string(((PyTypeObject *) arg.ptr())->tp_doc) + "\n\n"; - docstring += "Members:"; - for (auto kv : entries) { - auto key = std::string(pybind11::str(kv.first)); - auto comment = kv.second[int_(1)]; - docstring += "\n\n " + key; - if (!comment.is_none()) - docstring += " : " + (std::string) pybind11::str(comment); - } - return docstring; - }, name("__doc__") - ), none(), none(), ""); - - m_base.attr("__members__") = static_property(cpp_function( - [](handle arg) -> dict { - dict entries = arg.attr("__entries"), m; - for (auto kv : entries) - m[kv.first] = kv.second[int_(0)]; - return m; - }, name("__members__")), none(), none(), "" - ); - - #define PYBIND11_ENUM_OP_STRICT(op, expr, strict_behavior) \ - m_base.attr(op) = cpp_function( \ - [](object a, object b) { \ - if (!type::handle_of(a).is(type::handle_of(b))) \ - strict_behavior; \ - return expr; \ - }, \ - name(op), is_method(m_base), arg("other")) - - #define PYBIND11_ENUM_OP_CONV(op, expr) \ - m_base.attr(op) = cpp_function( \ - [](object a_, object b_) { \ - int_ a(a_), b(b_); \ - return expr; \ - }, \ - name(op), is_method(m_base), arg("other")) - - #define PYBIND11_ENUM_OP_CONV_LHS(op, expr) \ - m_base.attr(op) = cpp_function( \ - [](object a_, object b) { \ - int_ a(a_); \ - return expr; \ - }, \ - name(op), is_method(m_base), arg("other")) - - if (is_convertible) { - PYBIND11_ENUM_OP_CONV_LHS("__eq__", !b.is_none() && a.equal(b)); - PYBIND11_ENUM_OP_CONV_LHS("__ne__", b.is_none() || !a.equal(b)); - - if (is_arithmetic) { - PYBIND11_ENUM_OP_CONV("__lt__", a < b); - PYBIND11_ENUM_OP_CONV("__gt__", a > b); - PYBIND11_ENUM_OP_CONV("__le__", a <= b); - PYBIND11_ENUM_OP_CONV("__ge__", a >= b); - PYBIND11_ENUM_OP_CONV("__and__", a & b); - PYBIND11_ENUM_OP_CONV("__rand__", a & b); - PYBIND11_ENUM_OP_CONV("__or__", a | b); - PYBIND11_ENUM_OP_CONV("__ror__", a | b); - PYBIND11_ENUM_OP_CONV("__xor__", a ^ b); - PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b); - m_base.attr("__invert__") = cpp_function( - [](object arg) { return ~(int_(arg)); }, name("__invert__"), is_method(m_base)); - } - } else { - PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false); - PYBIND11_ENUM_OP_STRICT("__ne__", !int_(a).equal(int_(b)), return true); - - if (is_arithmetic) { - #define PYBIND11_THROW throw type_error("Expected an enumeration of matching type!"); - PYBIND11_ENUM_OP_STRICT("__lt__", int_(a) < int_(b), PYBIND11_THROW); - PYBIND11_ENUM_OP_STRICT("__gt__", int_(a) > int_(b), PYBIND11_THROW); - PYBIND11_ENUM_OP_STRICT("__le__", int_(a) <= int_(b), PYBIND11_THROW); - PYBIND11_ENUM_OP_STRICT("__ge__", int_(a) >= int_(b), PYBIND11_THROW); - #undef PYBIND11_THROW - } - } - - #undef PYBIND11_ENUM_OP_CONV_LHS - #undef PYBIND11_ENUM_OP_CONV - #undef PYBIND11_ENUM_OP_STRICT - - m_base.attr("__getstate__") = cpp_function( - [](object arg) { return int_(arg); }, name("__getstate__"), is_method(m_base)); - - m_base.attr("__hash__") = cpp_function( - [](object arg) { return int_(arg); }, name("__hash__"), is_method(m_base)); - } - - PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) { - dict entries = m_base.attr("__entries"); - str name(name_); - if (entries.contains(name)) { - std::string type_name = (std::string) str(m_base.attr("__name__")); - throw value_error(type_name + ": element \"" + std::string(name_) + "\" already exists!"); - } - - entries[name] = std::make_pair(value, doc); - m_base.attr(name) = value; - } - - PYBIND11_NOINLINE void export_values() { - dict entries = m_base.attr("__entries"); - for (auto kv : entries) - m_parent.attr(kv.first) = kv.second[int_(0)]; - } - - handle m_base; - handle m_parent; -}; - -PYBIND11_NAMESPACE_END(detail) - -/// Binds C++ enumerations and enumeration classes to Python -template class enum_ : public class_ { -public: - using Base = class_; - using Base::def; - using Base::attr; - using Base::def_property_readonly; - using Base::def_property_readonly_static; - using Scalar = typename std::underlying_type::type; - - template - enum_(const handle &scope, const char *name, const Extra&... extra) - : class_(scope, name, extra...), m_base(*this, scope) { - constexpr bool is_arithmetic = detail::any_of...>::value; - constexpr bool is_convertible = std::is_convertible::value; - m_base.init(is_arithmetic, is_convertible); - - def(init([](Scalar i) { return static_cast(i); }), arg("value")); - def_property_readonly("value", [](Type value) { return (Scalar) value; }); - def("__int__", [](Type value) { return (Scalar) value; }); - #if PY_MAJOR_VERSION < 3 - def("__long__", [](Type value) { return (Scalar) value; }); - #endif - #if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 8) - def("__index__", [](Type value) { return (Scalar) value; }); - #endif - - attr("__setstate__") = cpp_function( - [](detail::value_and_holder &v_h, Scalar arg) { - detail::initimpl::setstate(v_h, static_cast(arg), - Py_TYPE(v_h.inst) != v_h.type->type); }, - detail::is_new_style_constructor(), - pybind11::name("__setstate__"), is_method(*this), arg("state")); - } - - /// Export enumeration entries into the parent scope - enum_& export_values() { - m_base.export_values(); - return *this; - } - - /// Add an enumeration entry - enum_& value(char const* name, Type value, const char *doc = nullptr) { - m_base.value(name, pybind11::cast(value, return_value_policy::copy), doc); - return *this; - } - -private: - detail::enum_base m_base; -}; - -PYBIND11_NAMESPACE_BEGIN(detail) - - -inline void keep_alive_impl(handle nurse, handle patient) { - if (!nurse || !patient) - pybind11_fail("Could not activate keep_alive!"); - - if (patient.is_none() || nurse.is_none()) - return; /* Nothing to keep alive or nothing to be kept alive by */ - - auto tinfo = all_type_info(Py_TYPE(nurse.ptr())); - if (!tinfo.empty()) { - /* It's a pybind-registered type, so we can store the patient in the - * internal list. */ - add_patient(nurse.ptr(), patient.ptr()); - } - else { - /* Fall back to clever approach based on weak references taken from - * Boost.Python. This is not used for pybind-registered types because - * the objects can be destroyed out-of-order in a GC pass. */ - cpp_function disable_lifesupport( - [patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); }); - - weakref wr(nurse, disable_lifesupport); - - patient.inc_ref(); /* reference patient and leak the weak reference */ - (void) wr.release(); - } -} - -PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) { - auto get_arg = [&](size_t n) { - if (n == 0) - return ret; - else if (n == 1 && call.init_self) - return call.init_self; - else if (n <= call.args.size()) - return call.args[n - 1]; - return handle(); - }; - - keep_alive_impl(get_arg(Nurse), get_arg(Patient)); -} - -inline std::pair all_type_info_get_cache(PyTypeObject *type) { - auto res = get_internals().registered_types_py -#ifdef __cpp_lib_unordered_map_try_emplace - .try_emplace(type); -#else - .emplace(type, std::vector()); -#endif - if (res.second) { - // New cache entry created; set up a weak reference to automatically remove it if the type - // gets destroyed: - weakref((PyObject *) type, cpp_function([type](handle wr) { - get_internals().registered_types_py.erase(type); - wr.dec_ref(); - })).release(); - } - - return res; -} - -template -struct iterator_state { - Iterator it; - Sentinel end; - bool first_or_done; -}; - -PYBIND11_NAMESPACE_END(detail) - -/// Makes a python iterator from a first and past-the-end C++ InputIterator. -template ()), - typename... Extra> -iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { - using state = detail::iterator_state; - - if (!detail::get_type_info(typeid(state), false)) { - class_(handle(), "iterator", pybind11::module_local()) - .def("__iter__", [](state &s) -> state& { return s; }) - .def("__next__", [](state &s) -> ValueType { - if (!s.first_or_done) - ++s.it; - else - s.first_or_done = false; - if (s.it == s.end) { - s.first_or_done = true; - throw stop_iteration(); - } - return *s.it; - }, std::forward(extra)..., Policy); - } - - return cast(state{first, last, true}); -} - -/// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a -/// first and past-the-end InputIterator. -template ()).first), - typename... Extra> -iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { - using state = detail::iterator_state; - - if (!detail::get_type_info(typeid(state), false)) { - class_(handle(), "iterator", pybind11::module_local()) - .def("__iter__", [](state &s) -> state& { return s; }) - .def("__next__", [](state &s) -> KeyType { - if (!s.first_or_done) - ++s.it; - else - s.first_or_done = false; - if (s.it == s.end) { - s.first_or_done = true; - throw stop_iteration(); - } - return (*s.it).first; - }, std::forward(extra)..., Policy); - } - - return cast(state{first, last, true}); -} - -/// Makes an iterator over values of an stl container or other container supporting -/// `std::begin()`/`std::end()` -template iterator make_iterator(Type &value, Extra&&... extra) { - return make_iterator(std::begin(value), std::end(value), extra...); -} - -/// Makes an iterator over the keys (`.first`) of a stl map-like container supporting -/// `std::begin()`/`std::end()` -template iterator make_key_iterator(Type &value, Extra&&... extra) { - return make_key_iterator(std::begin(value), std::end(value), extra...); -} - -template void implicitly_convertible() { - struct set_flag { - bool &flag; - set_flag(bool &flag) : flag(flag) { flag = true; } - ~set_flag() { flag = false; } - }; - auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * { - static bool currently_used = false; - if (currently_used) // implicit conversions are non-reentrant - return nullptr; - set_flag flag_helper(currently_used); - if (!detail::make_caster().load(obj, false)) - return nullptr; - tuple args(1); - args[0] = obj; - PyObject *result = PyObject_Call((PyObject *) type, args.ptr(), nullptr); - if (result == nullptr) - PyErr_Clear(); - return result; - }; - - if (auto tinfo = detail::get_type_info(typeid(OutputType))) - tinfo->implicit_conversions.push_back(implicit_caster); - else - pybind11_fail("implicitly_convertible: Unable to find type " + type_id()); -} - -template -void register_exception_translator(ExceptionTranslator&& translator) { - detail::get_internals().registered_exception_translators.push_front( - std::forward(translator)); -} - -/** - * Wrapper to generate a new Python exception type. - * - * This should only be used with PyErr_SetString for now. - * It is not (yet) possible to use as a py::base. - * Template type argument is reserved for future use. - */ -template -class exception : public object { -public: - exception() = default; - exception(handle scope, const char *name, handle base = PyExc_Exception) { - std::string full_name = scope.attr("__name__").cast() + - std::string(".") + name; - m_ptr = PyErr_NewException(const_cast(full_name.c_str()), base.ptr(), NULL); - if (hasattr(scope, "__dict__") && scope.attr("__dict__").contains(name)) - pybind11_fail("Error during initialization: multiple incompatible " - "definitions with name \"" + std::string(name) + "\""); - scope.attr(name) = *this; - } - - // Sets the current python exception to this exception object with the given message - void operator()(const char *message) { - PyErr_SetString(m_ptr, message); - } -}; - -PYBIND11_NAMESPACE_BEGIN(detail) -// Returns a reference to a function-local static exception object used in the simple -// register_exception approach below. (It would be simpler to have the static local variable -// directly in register_exception, but that makes clang <3.5 segfault - issue #1349). -template -exception &get_exception_object() { static exception ex; return ex; } -PYBIND11_NAMESPACE_END(detail) - -/** - * Registers a Python exception in `m` of the given `name` and installs an exception translator to - * translate the C++ exception to the created Python exception using the exceptions what() method. - * This is intended for simple exception translations; for more complex translation, register the - * exception object and translator directly. - */ -template -exception ®ister_exception(handle scope, - const char *name, - handle base = PyExc_Exception) { - auto &ex = detail::get_exception_object(); - if (!ex) ex = exception(scope, name, base); - - register_exception_translator([](std::exception_ptr p) { - if (!p) return; - try { - std::rethrow_exception(p); - } catch (const CppException &e) { - detail::get_exception_object()(e.what()); - } - }); - return ex; -} - -PYBIND11_NAMESPACE_BEGIN(detail) -PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { - auto strings = tuple(args.size()); - for (size_t i = 0; i < args.size(); ++i) { - strings[i] = str(args[i]); - } - auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" "); - auto line = sep.attr("join")(strings); - - object file; - if (kwargs.contains("file")) { - file = kwargs["file"].cast(); - } else { - try { - file = module_::import("sys").attr("stdout"); - } catch (const error_already_set &) { - /* If print() is called from code that is executed as - part of garbage collection during interpreter shutdown, - importing 'sys' can fail. Give up rather than crashing the - interpreter in this case. */ - return; - } - } - - auto write = file.attr("write"); - write(line); - write(kwargs.contains("end") ? kwargs["end"] : cast("\n")); - - if (kwargs.contains("flush") && kwargs["flush"].cast()) - file.attr("flush")(); -} -PYBIND11_NAMESPACE_END(detail) - -template -void print(Args &&...args) { - auto c = detail::collect_arguments(std::forward(args)...); - detail::print(c.args(), c.kwargs()); -} - -#if defined(WITH_THREAD) && !defined(PYPY_VERSION) - -/* The functions below essentially reproduce the PyGILState_* API using a RAII - * pattern, but there are a few important differences: - * - * 1. When acquiring the GIL from an non-main thread during the finalization - * phase, the GILState API blindly terminates the calling thread, which - * is often not what is wanted. This API does not do this. - * - * 2. The gil_scoped_release function can optionally cut the relationship - * of a PyThreadState and its associated thread, which allows moving it to - * another thread (this is a fairly rare/advanced use case). - * - * 3. The reference count of an acquired thread state can be controlled. This - * can be handy to prevent cases where callbacks issued from an external - * thread would otherwise constantly construct and destroy thread state data - * structures. - * - * See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an - * example which uses features 2 and 3 to migrate the Python thread of - * execution to another thread (to run the event loop on the original thread, - * in this case). - */ - -class gil_scoped_acquire { -public: - PYBIND11_NOINLINE gil_scoped_acquire() { - auto const &internals = detail::get_internals(); - tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate); - - if (!tstate) { - /* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if - calling from a Python thread). Since we use a different key, this ensures - we don't create a new thread state and deadlock in PyEval_AcquireThread - below. Note we don't save this state with internals.tstate, since we don't - create it we would fail to clear it (its reference count should be > 0). */ - tstate = PyGILState_GetThisThreadState(); - } - - if (!tstate) { - tstate = PyThreadState_New(internals.istate); - #if !defined(NDEBUG) - if (!tstate) - pybind11_fail("scoped_acquire: could not create thread state!"); - #endif - tstate->gilstate_counter = 0; - PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate); - } else { - release = detail::get_thread_state_unchecked() != tstate; - } - - if (release) { - PyEval_AcquireThread(tstate); - } - - inc_ref(); - } - - void inc_ref() { - ++tstate->gilstate_counter; - } - - PYBIND11_NOINLINE void dec_ref() { - --tstate->gilstate_counter; - #if !defined(NDEBUG) - if (detail::get_thread_state_unchecked() != tstate) - pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); - if (tstate->gilstate_counter < 0) - pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); - #endif - if (tstate->gilstate_counter == 0) { - #if !defined(NDEBUG) - if (!release) - pybind11_fail("scoped_acquire::dec_ref(): internal error!"); - #endif - PyThreadState_Clear(tstate); - if (active) - PyThreadState_DeleteCurrent(); - PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate); - release = false; - } - } - - /// This method will disable the PyThreadState_DeleteCurrent call and the - /// GIL won't be acquired. This method should be used if the interpreter - /// could be shutting down when this is called, as thread deletion is not - /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and - /// protect subsequent code. - PYBIND11_NOINLINE void disarm() { - active = false; - } - - PYBIND11_NOINLINE ~gil_scoped_acquire() { - dec_ref(); - if (release) - PyEval_SaveThread(); - } -private: - PyThreadState *tstate = nullptr; - bool release = true; - bool active = true; -}; - -class gil_scoped_release { -public: - explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) { - // `get_internals()` must be called here unconditionally in order to initialize - // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an - // initialization race could occur as multiple threads try `gil_scoped_acquire`. - const auto &internals = detail::get_internals(); - tstate = PyEval_SaveThread(); - if (disassoc) { - auto key = internals.tstate; - PYBIND11_TLS_DELETE_VALUE(key); - } - } - - /// This method will disable the PyThreadState_DeleteCurrent call and the - /// GIL won't be acquired. This method should be used if the interpreter - /// could be shutting down when this is called, as thread deletion is not - /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and - /// protect subsequent code. - PYBIND11_NOINLINE void disarm() { - active = false; - } - - ~gil_scoped_release() { - if (!tstate) - return; - // `PyEval_RestoreThread()` should not be called if runtime is finalizing - if (active) - PyEval_RestoreThread(tstate); - if (disassoc) { - auto key = detail::get_internals().tstate; - PYBIND11_TLS_REPLACE_VALUE(key, tstate); - } - } -private: - PyThreadState *tstate; - bool disassoc; - bool active = true; -}; -#elif defined(PYPY_VERSION) -class gil_scoped_acquire { - PyGILState_STATE state; -public: - gil_scoped_acquire() { state = PyGILState_Ensure(); } - ~gil_scoped_acquire() { PyGILState_Release(state); } - void disarm() {} -}; - -class gil_scoped_release { - PyThreadState *state; -public: - gil_scoped_release() { state = PyEval_SaveThread(); } - ~gil_scoped_release() { PyEval_RestoreThread(state); } - void disarm() {} -}; -#else -class gil_scoped_acquire { - void disarm() {} -}; -class gil_scoped_release { - void disarm() {} -}; -#endif - -error_already_set::~error_already_set() { - if (m_type) { - gil_scoped_acquire gil; - error_scope scope; - m_type.release().dec_ref(); - m_value.release().dec_ref(); - m_trace.release().dec_ref(); - } -} - -PYBIND11_NAMESPACE_BEGIN(detail) -inline function get_type_override(const void *this_ptr, const type_info *this_type, const char *name) { - handle self = get_object_handle(this_ptr, this_type); - if (!self) - return function(); - handle type = type::handle_of(self); - auto key = std::make_pair(type.ptr(), name); - - /* Cache functions that aren't overridden in Python to avoid - many costly Python dictionary lookups below */ - auto &cache = get_internals().inactive_override_cache; - if (cache.find(key) != cache.end()) - return function(); - - function override = getattr(self, name, function()); - if (override.is_cpp_function()) { - cache.insert(key); - return function(); - } - - /* Don't call dispatch code if invoked from overridden function. - Unfortunately this doesn't work on PyPy. */ -#if !defined(PYPY_VERSION) - PyFrameObject *frame = PyThreadState_Get()->frame; - if (frame && (std::string) str(frame->f_code->co_name) == name && - frame->f_code->co_argcount > 0) { - PyFrame_FastToLocals(frame); - PyObject *self_caller = PyDict_GetItem( - frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); - if (self_caller == self.ptr()) - return function(); - } -#else - /* PyPy currently doesn't provide a detailed cpyext emulation of - frame objects, so we have to emulate this using Python. This - is going to be slow..*/ - dict d; d["self"] = self; d["name"] = pybind11::str(name); - PyObject *result = PyRun_String( - "import inspect\n" - "frame = inspect.currentframe()\n" - "if frame is not None:\n" - " frame = frame.f_back\n" - " if frame is not None and str(frame.f_code.co_name) == name and " - "frame.f_code.co_argcount > 0:\n" - " self_caller = frame.f_locals[frame.f_code.co_varnames[0]]\n" - " if self_caller == self:\n" - " self = None\n", - Py_file_input, d.ptr(), d.ptr()); - if (result == nullptr) - throw error_already_set(); - if (d["self"].is_none()) - return function(); - Py_DECREF(result); -#endif - - return override; -} -PYBIND11_NAMESPACE_END(detail) - -/** \rst - Try to retrieve a python method by the provided name from the instance pointed to by the this_ptr. - - :this_ptr: The pointer to the object the overriden method should be retrieved for. This should be - the first non-trampoline class encountered in the inheritance chain. - :name: The name of the overridden Python method to retrieve. - :return: The Python method by this name from the object or an empty function wrapper. - \endrst */ -template function get_override(const T *this_ptr, const char *name) { - auto tinfo = detail::get_type_info(typeid(T)); - return tinfo ? detail::get_type_override(this_ptr, tinfo, name) : function(); -} - -#define PYBIND11_OVERRIDE_IMPL(ret_type, cname, name, ...) \ - do { \ - pybind11::gil_scoped_acquire gil; \ - pybind11::function override = pybind11::get_override(static_cast(this), name); \ - if (override) { \ - auto o = override(__VA_ARGS__); \ - if (pybind11::detail::cast_is_temporary_value_reference::value) { \ - static pybind11::detail::override_caster_t caster; \ - return pybind11::detail::cast_ref(std::move(o), caster); \ - } \ - else return pybind11::detail::cast_safe(std::move(o)); \ - } \ - } while (false) - -/** \rst - Macro to populate the virtual method in the trampoline class. This macro tries to look up a method named 'fn' - from the Python side, deals with the :ref:`gil` and necessary argument conversions to call this method and return - the appropriate type. See :ref:`overriding_virtuals` for more information. This macro should be used when the method - name in C is not the same as the method name in Python. For example with `__str__`. - - .. code-block:: cpp - - std::string toString() override { - PYBIND11_OVERRIDE_NAME( - std::string, // Return type (ret_type) - Animal, // Parent class (cname) - "__str__", // Name of method in Python (name) - toString, // Name of function in C++ (fn) - ); - } -\endrst */ -#define PYBIND11_OVERRIDE_NAME(ret_type, cname, name, fn, ...) \ - do { \ - PYBIND11_OVERRIDE_IMPL(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__); \ - return cname::fn(__VA_ARGS__); \ - } while (false) - -/** \rst - Macro for pure virtual functions, this function is identical to :c:macro:`PYBIND11_OVERRIDE_NAME`, except that it - throws if no override can be found. -\endrst */ -#define PYBIND11_OVERRIDE_PURE_NAME(ret_type, cname, name, fn, ...) \ - do { \ - PYBIND11_OVERRIDE_IMPL(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__); \ - pybind11::pybind11_fail("Tried to call pure virtual function \"" PYBIND11_STRINGIFY(cname) "::" name "\""); \ - } while (false) - -/** \rst - Macro to populate the virtual method in the trampoline class. This macro tries to look up the method - from the Python side, deals with the :ref:`gil` and necessary argument conversions to call this method and return - the appropriate type. This macro should be used if the method name in C and in Python are identical. - See :ref:`overriding_virtuals` for more information. - - .. code-block:: cpp - - class PyAnimal : public Animal { - public: - // Inherit the constructors - using Animal::Animal; - - // Trampoline (need one for each virtual function) - std::string go(int n_times) override { - PYBIND11_OVERRIDE_PURE( - std::string, // Return type (ret_type) - Animal, // Parent class (cname) - go, // Name of function in C++ (must match Python name) (fn) - n_times // Argument(s) (...) - ); - } - }; -\endrst */ -#define PYBIND11_OVERRIDE(ret_type, cname, fn, ...) \ - PYBIND11_OVERRIDE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) - -/** \rst - Macro for pure virtual functions, this function is identical to :c:macro:`PYBIND11_OVERRIDE`, except that it throws - if no override can be found. -\endrst */ -#define PYBIND11_OVERRIDE_PURE(ret_type, cname, fn, ...) \ - PYBIND11_OVERRIDE_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) - - -// Deprecated versions - -PYBIND11_DEPRECATED("get_type_overload has been deprecated") -inline function get_type_overload(const void *this_ptr, const detail::type_info *this_type, const char *name) { - return detail::get_type_override(this_ptr, this_type, name); -} - -template -inline function get_overload(const T *this_ptr, const char *name) { - return get_override(this_ptr, name); -} - -#define PYBIND11_OVERLOAD_INT(ret_type, cname, name, ...) \ - PYBIND11_OVERRIDE_IMPL(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) -#define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \ - PYBIND11_OVERRIDE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, fn, __VA_ARGS__) -#define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ - PYBIND11_OVERRIDE_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, fn, __VA_ARGS__); -#define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ - PYBIND11_OVERRIDE(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), fn, __VA_ARGS__) -#define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ - PYBIND11_OVERRIDE_PURE(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), fn, __VA_ARGS__); - -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) -# pragma warning(pop) -#elif defined(__GNUG__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif diff --git a/test/src/pybind11/attr.h b/test/src/pybind11/pybind11/attr.h similarity index 60% rename from test/src/pybind11/attr.h rename to test/src/pybind11/pybind11/attr.h index 0c4167092..b5e3b7b22 100644 --- a/test/src/pybind11/attr.h +++ b/test/src/pybind11/pybind11/attr.h @@ -10,72 +10,113 @@ #pragma once +#include "detail/common.h" #include "cast.h" +#include + PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) /// \addtogroup annotations /// @{ /// Annotation for methods -struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; +struct is_method { + handle class_; + explicit is_method(const handle &c) : class_(c) {} +}; /// Annotation for operators -struct is_operator { }; +struct is_operator {}; /// Annotation for classes that cannot be subclassed -struct is_final { }; +struct is_final {}; /// Annotation for parent scope -struct scope { handle value; scope(const handle &s) : value(s) { } }; +struct scope { + handle value; + explicit scope(const handle &s) : value(s) {} +}; /// Annotation for documentation -struct doc { const char *value; doc(const char *value) : value(value) { } }; +struct doc { + const char *value; + explicit doc(const char *value) : value(value) {} +}; /// Annotation for function names -struct name { const char *value; name(const char *value) : value(value) { } }; +struct name { + const char *value; + explicit name(const char *value) : value(value) {} +}; /// Annotation indicating that a function is an overload associated with a given "sibling" -struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; +struct sibling { + handle value; + explicit sibling(const handle &value) : value(value.ptr()) {} +}; /// Annotation indicating that a class derives from another given type -template struct base { +template +struct base { - PYBIND11_DEPRECATED("base() was deprecated in favor of specifying 'T' as a template argument to class_") - base() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute + PYBIND11_DEPRECATED( + "base() was deprecated in favor of specifying 'T' as a template argument to class_") + base() = default; }; /// Keep patient alive while nurse lives -template struct keep_alive { }; +template +struct keep_alive {}; /// Annotation indicating that a class is involved in a multiple inheritance relationship -struct multiple_inheritance { }; +struct multiple_inheritance {}; /// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class -struct dynamic_attr { }; +struct dynamic_attr {}; /// Annotation which enables the buffer protocol for a type -struct buffer_protocol { }; +struct buffer_protocol {}; /// Annotation which requests that a special metaclass is created for a type struct metaclass { handle value; PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") - metaclass() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute + metaclass() = default; /// Override pybind11's default metaclass - explicit metaclass(handle value) : value(value) { } + explicit metaclass(handle value) : value(value) {} +}; + +/// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that +/// may be used to customize the Python type. +/// +/// The callback is invoked immediately before `PyType_Ready`. +/// +/// Note: This is an advanced interface, and uses of it may require changes to +/// work with later versions of pybind11. You may wish to consult the +/// implementation of `make_new_python_type` in `detail/classes.h` to understand +/// the context in which the callback will be run. +struct custom_type_setup { + using callback = std::function; + + explicit custom_type_setup(callback value) : value(std::move(value)) {} + + callback value; }; /// Annotation that marks a class as local to the module: -struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } }; +struct module_local { + const bool value; + constexpr explicit module_local(bool v = true) : value(v) {} +}; /// Annotation to mark enums as an arithmetic type -struct arithmetic { }; +struct arithmetic {}; /// Mark a function for addition at the beginning of the existing overload chain instead of the end -struct prepend { }; +struct prepend {}; /** \rst A call policy which places one or more guard variables (``Ts...``) around the function call. @@ -95,9 +136,13 @@ struct prepend { }; return foo(args...); // forwarded arguments }); \endrst */ -template struct call_guard; +template +struct call_guard; -template <> struct call_guard<> { using type = detail::void_type; }; +template <> +struct call_guard<> { + using type = detail::void_type; +}; template struct call_guard { @@ -122,8 +167,9 @@ PYBIND11_NAMESPACE_BEGIN(detail) enum op_id : int; enum op_type : int; struct undefined_t; -template struct op_; -inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); +template +struct op_; +void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); /// Internal data structure which holds metadata about a keyword argument struct argument_record { @@ -134,15 +180,16 @@ struct argument_record { bool none : 1; ///< True if None is allowed when loading argument_record(const char *name, const char *descr, handle value, bool convert, bool none) - : name(name), descr(descr), value(value), convert(convert), none(none) { } + : name(name), descr(descr), value(value), convert(convert), none(none) {} }; -/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) +/// Internal data structure which holds metadata about a bound function (signature, overloads, +/// etc.) struct function_record { function_record() : is_constructor(false), is_new_style_constructor(false), is_stateless(false), - is_operator(false), is_method(false), has_args(false), - has_kwargs(false), has_kw_only_args(false), prepend(false) { } + is_operator(false), is_method(false), has_args(false), has_kwargs(false), + prepend(false) {} /// Function name char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ @@ -157,13 +204,13 @@ struct function_record { std::vector args; /// Pointer to lambda function which converts arguments and performs the actual call - handle (*impl) (function_call &) = nullptr; + handle (*impl)(function_call &) = nullptr; /// Storage for the wrapped function pointer and captured data, if any - void *data[3] = { }; + void *data[3] = {}; /// Pointer to custom destructor for 'data' (if needed) - void (*free_data) (function_record *ptr) = nullptr; + void (*free_data)(function_record *ptr) = nullptr; /// Return value policy associated with this function return_value_policy policy = return_value_policy::automatic; @@ -189,17 +236,15 @@ struct function_record { /// True if the function has a '**kwargs' argument bool has_kwargs : 1; - /// True once a 'py::kw_only' is encountered (any following args are keyword-only) - bool has_kw_only_args : 1; - /// True if this function is to be inserted at the beginning of the overload resolution chain bool prepend : 1; /// Number of arguments (including py::args and/or py::kwargs, if present) std::uint16_t nargs; - /// Number of trailing arguments (counted in `nargs`) that are keyword-only - std::uint16_t nargs_kw_only = 0; + /// Number of leading positional arguments, which are terminated by a py::args or py::kwargs + /// argument or by a py::kw_only annotation. + std::uint16_t nargs_pos = 0; /// Number of leading arguments (counted in `nargs`) that are positional-only std::uint16_t nargs_pos_only = 0; @@ -221,7 +266,7 @@ struct function_record { struct type_record { PYBIND11_NOINLINE type_record() : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), - default_holder(true), module_local(false), is_final(false) { } + default_holder(true), module_local(false), is_final(false) {} /// Handle to the parent scope handle scope; @@ -259,6 +304,9 @@ struct type_record { /// Custom metaclass (optional) handle metaclass; + /// Custom type setup. + custom_type_setup::callback custom_type_setup_callback; + /// Multiple inheritance marker bool multiple_inheritance : 1; @@ -277,42 +325,45 @@ struct type_record { /// Is the class inheritable from python classes? bool is_final : 1; - PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { - auto base_info = detail::get_type_info(base, false); + PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) { + auto *base_info = detail::get_type_info(base, false); if (!base_info) { std::string tname(base.name()); detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + - "\" referenced unknown base type \"" + tname + "\""); + pybind11_fail("generic_type: type \"" + std::string(name) + + "\" referenced unknown base type \"" + tname + "\""); } if (default_holder != base_info->default_holder) { std::string tname(base.name()); detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + - (default_holder ? "does not have" : "has") + - " a non-default holder type while its base \"" + tname + "\" " + - (base_info->default_holder ? "does not" : "does")); + pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + + (default_holder ? "does not have" : "has") + + " a non-default holder type while its base \"" + tname + "\" " + + (base_info->default_holder ? "does not" : "does")); } bases.append((PyObject *) base_info->type); - if (base_info->type->tp_dictoffset != 0) - dynamic_attr = true; +#if PY_VERSION_HEX < 0x030B0000 + dynamic_attr |= base_info->type->tp_dictoffset != 0; +#else + dynamic_attr |= (base_info->type->tp_flags & Py_TPFLAGS_MANAGED_DICT) != 0; +#endif - if (caster) + if (caster) { base_info->implicit_casts.emplace_back(type, caster); + } } }; -inline function_call::function_call(const function_record &f, handle p) : - func(f), parent(p) { +inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) { args.reserve(f.nargs); args_convert.reserve(f.nargs); } /// Tag for a new-style `__init__` defined in `detail/init.h` -struct is_new_style_constructor { }; +struct is_new_style_constructor {}; /** * Partial template specializations to process custom attributes provided to @@ -320,129 +371,177 @@ struct is_new_style_constructor { }; * fields in the type_record and function_record data structures or executed at * runtime to deal with custom call policies (e.g. keep_alive). */ -template struct process_attribute; +template +struct process_attribute; -template struct process_attribute_default { +template +struct process_attribute_default { /// Default implementation: do nothing - static void init(const T &, function_record *) { } - static void init(const T &, type_record *) { } - static void precall(function_call &) { } - static void postcall(function_call &, handle) { } + static void init(const T &, function_record *) {} + static void init(const T &, type_record *) {} + static void precall(function_call &) {} + static void postcall(function_call &, handle) {} }; /// Process an attribute specifying the function's name -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } }; /// Process an attribute specifying the function's docstring -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } }; /// Process an attribute specifying the function's docstring (provided as a C-style string) -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const char *d, function_record *r) { r->doc = const_cast(d); } - static void init(const char *d, type_record *r) { r->doc = const_cast(d); } + static void init(const char *d, type_record *r) { r->doc = d; } }; -template <> struct process_attribute : process_attribute { }; +template <> +struct process_attribute : process_attribute {}; /// Process an attribute indicating the function's return value policy -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const return_value_policy &p, function_record *r) { r->policy = p; } }; -/// Process an attribute which indicates that this is an overloaded function associated with a given sibling -template <> struct process_attribute : process_attribute_default { +/// Process an attribute which indicates that this is an overloaded function associated with a +/// given sibling +template <> +struct process_attribute : process_attribute_default { static void init(const sibling &s, function_record *r) { r->sibling = s.value; } }; /// Process an attribute which indicates that this function is a method -template <> struct process_attribute : process_attribute_default { - static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } +template <> +struct process_attribute : process_attribute_default { + static void init(const is_method &s, function_record *r) { + r->is_method = true; + r->scope = s.class_; + } }; /// Process an attribute which indicates the parent scope of a method -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const scope &s, function_record *r) { r->scope = s.value; } }; /// Process an attribute which indicates that this function is an operator -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const is_operator &, function_record *r) { r->is_operator = true; } }; -template <> struct process_attribute : process_attribute_default { - static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } +template <> +struct process_attribute + : process_attribute_default { + static void init(const is_new_style_constructor &, function_record *r) { + r->is_new_style_constructor = true; + } }; -inline void process_kw_only_arg(const arg &a, function_record *r) { - if (!a.name || strlen(a.name) == 0) - pybind11_fail("arg(): cannot specify an unnamed argument after an kw_only() annotation"); - ++r->nargs_kw_only; +inline void check_kw_only_arg(const arg &a, function_record *r) { + if (r->args.size() > r->nargs_pos && (!a.name || a.name[0] == '\0')) { + pybind11_fail("arg(): cannot specify an unnamed argument after a kw_only() annotation or " + "args() argument"); + } +} + +inline void append_self_arg_if_needed(function_record *r) { + if (r->is_method && r->args.empty()) { + r->args.emplace_back("self", nullptr, handle(), /*convert=*/true, /*none=*/false); + } } /// Process a keyword argument attribute (*without* a default value) -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const arg &a, function_record *r) { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); + append_self_arg_if_needed(r); r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); - if (r->has_kw_only_args) process_kw_only_arg(a, r); + check_kw_only_arg(a, r); } }; /// Process a keyword argument attribute (*with* a default value) -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const arg_v &a, function_record *r) { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); + if (r->is_method && r->args.empty()) { + r->args.emplace_back( + "self", /*descr=*/nullptr, /*parent=*/handle(), /*convert=*/true, /*none=*/false); + } if (!a.value) { -#if !defined(NDEBUG) +#if defined(PYBIND11_DETAILED_ERROR_MESSAGES) std::string descr("'"); - if (a.name) descr += std::string(a.name) + ": "; + if (a.name) { + descr += std::string(a.name) + ": "; + } descr += a.type + "'"; if (r->is_method) { - if (r->name) - descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; - else + if (r->name) { + descr += " in method '" + (std::string) str(r->scope) + "." + + (std::string) r->name + "'"; + } else { descr += " in method of '" + (std::string) str(r->scope) + "'"; + } } else if (r->name) { descr += " in function '" + (std::string) r->name + "'"; } - pybind11_fail("arg(): could not convert default argument " - + descr + " into a Python object (type not registered yet?)"); + pybind11_fail("arg(): could not convert default argument " + descr + + " into a Python object (type not registered yet?)"); #else pybind11_fail("arg(): could not convert default argument " "into a Python object (type not registered yet?). " - "Compile in debug mode for more information."); + "#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for " + "more information."); #endif } r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); - if (r->has_kw_only_args) process_kw_only_arg(a, r); + check_kw_only_arg(a, r); } }; /// Process a keyword-only-arguments-follow pseudo argument -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const kw_only &, function_record *r) { - r->has_kw_only_args = true; + append_self_arg_if_needed(r); + if (r->has_args && r->nargs_pos != static_cast(r->args.size())) { + pybind11_fail("Mismatched args() and kw_only(): they must occur at the same relative " + "argument location (or omit kw_only() entirely)"); + } + r->nargs_pos = static_cast(r->args.size()); } }; /// Process a positional-only-argument maker -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const pos_only &, function_record *r) { + append_self_arg_if_needed(r); r->nargs_pos_only = static_cast(r->args.size()); + if (r->nargs_pos_only > r->nargs_pos) { + pybind11_fail("pos_only(): cannot follow a py::args() argument"); + } + // It also can't follow a kw_only, but a static_assert in pybind11.h checks that } }; -/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) +/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees +/// that) template -struct process_attribute::value>> : process_attribute_default { +struct process_attribute::value>> + : process_attribute_default { static void init(const handle &h, type_record *r) { r->bases.append(h); } }; @@ -455,7 +554,9 @@ struct process_attribute> : process_attribute_default> { /// Process a multiple inheritance attribute template <> struct process_attribute : process_attribute_default { - static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } + static void init(const multiple_inheritance &, type_record *r) { + r->multiple_inheritance = true; + } }; template <> @@ -463,6 +564,13 @@ struct process_attribute : process_attribute_default static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } }; +template <> +struct process_attribute { + static void init(const custom_type_setup &value, type_record *r) { + r->custom_type_setup_callback = value.value; + } +}; + template <> struct process_attribute : process_attribute_default { static void init(const is_final &, type_record *r) { r->is_final = true; } @@ -494,41 +602,59 @@ template <> struct process_attribute : process_attribute_default {}; template -struct process_attribute> : process_attribute_default> { }; +struct process_attribute> : process_attribute_default> {}; /** * Process a keep_alive call policy -- invokes keep_alive_impl during the * pre-call handler if both Nurse, Patient != 0 and use the post-call handler * otherwise */ -template struct process_attribute> : public process_attribute_default> { +template +struct process_attribute> + : public process_attribute_default> { template = 0> - static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } + static void precall(function_call &call) { + keep_alive_impl(Nurse, Patient, call, handle()); + } template = 0> - static void postcall(function_call &, handle) { } + static void postcall(function_call &, handle) {} template = 0> - static void precall(function_call &) { } + static void precall(function_call &) {} template = 0> - static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } + static void postcall(function_call &call, handle ret) { + keep_alive_impl(Nurse, Patient, call, ret); + } }; /// Recursively iterate over variadic template arguments -template struct process_attributes { - static void init(const Args&... args, function_record *r) { - int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; - ignore_unused(unused); +template +struct process_attributes { + static void init(const Args &...args, function_record *r) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); + using expander = int[]; + (void) expander{ + 0, ((void) process_attribute::type>::init(args, r), 0)...}; } - static void init(const Args&... args, type_record *r) { - int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; - ignore_unused(unused); + static void init(const Args &...args, type_record *r) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); + using expander = int[]; + (void) expander{0, + (process_attribute::type>::init(args, r), 0)...}; } static void precall(function_call &call) { - int unused[] = { 0, (process_attribute::type>::precall(call), 0) ... }; - ignore_unused(unused); + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call); + using expander = int[]; + (void) expander{0, + (process_attribute::type>::precall(call), 0)...}; } static void postcall(function_call &call, handle fn_ret) { - int unused[] = { 0, (process_attribute::type>::postcall(call, fn_ret), 0) ... }; - ignore_unused(unused); + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call, fn_ret); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(fn_ret); + using expander = int[]; + (void) expander{ + 0, (process_attribute::type>::postcall(call, fn_ret), 0)...}; } }; @@ -542,9 +668,10 @@ using extract_guard_t = typename exactly_one_t, Extr /// Check the number of named arguments at compile time template ::value...), - size_t self = constexpr_sum(std::is_same::value...)> + size_t self = constexpr_sum(std::is_same::value...)> constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { - return named == 0 || (self + named + has_args + has_kwargs) == nargs; + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs); + return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs; } PYBIND11_NAMESPACE_END(detail) diff --git a/test/src/pybind11/pybind11/buffer_info.h b/test/src/pybind11/pybind11/buffer_info.h new file mode 100644 index 000000000..06120d556 --- /dev/null +++ b/test/src/pybind11/pybind11/buffer_info.h @@ -0,0 +1,193 @@ +/* + pybind11/buffer_info.h: Python buffer object interface + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +PYBIND11_NAMESPACE_BEGIN(detail) + +// Default, C-style strides +inline std::vector c_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + if (ndim > 0) { + for (size_t i = ndim - 1; i > 0; --i) { + strides[i - 1] = strides[i] * shape[i]; + } + } + return strides; +} + +// F-style strides; default when constructing an array_t with `ExtraFlags & f_style` +inline std::vector f_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + for (size_t i = 1; i < ndim; ++i) { + strides[i] = strides[i - 1] * shape[i - 1]; + } + return strides; +} + +PYBIND11_NAMESPACE_END(detail) + +/// Information record describing a Python buffer object +struct buffer_info { + void *ptr = nullptr; // Pointer to the underlying storage + ssize_t itemsize = 0; // Size of individual items in bytes + ssize_t size = 0; // Total number of entries + std::string format; // For homogeneous buffers, this should be set to + // format_descriptor::format() + ssize_t ndim = 0; // Number of dimensions + std::vector shape; // Shape of the tensor (1 entry per dimension) + std::vector strides; // Number of bytes between adjacent entries + // (for each per dimension) + bool readonly = false; // flag to indicate if the underlying storage may be written to + + buffer_info() = default; + + buffer_info(void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t ndim, + detail::any_container shape_in, + detail::any_container strides_in, + bool readonly = false) + : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), + shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { + if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) { + pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); + } + for (size_t i = 0; i < (size_t) ndim; ++i) { + size *= shape[i]; + } + } + + template + buffer_info(T *ptr, + detail::any_container shape_in, + detail::any_container strides_in, + bool readonly = false) + : buffer_info(private_ctr_tag(), + ptr, + sizeof(T), + format_descriptor::format(), + static_cast(shape_in->size()), + std::move(shape_in), + std::move(strides_in), + readonly) {} + + buffer_info(void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t size, + bool readonly = false) + : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) {} + + template + buffer_info(T *ptr, ssize_t size, bool readonly = false) + : buffer_info(ptr, sizeof(T), format_descriptor::format(), size, readonly) {} + + template + buffer_info(const T *ptr, ssize_t size, bool readonly = true) + : buffer_info( + const_cast(ptr), sizeof(T), format_descriptor::format(), size, readonly) {} + + explicit buffer_info(Py_buffer *view, bool ownview = true) + : buffer_info( + view->buf, + view->itemsize, + view->format, + view->ndim, + {view->shape, view->shape + view->ndim}, + /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects + * ignore this flag and return a view with NULL strides. + * When strides are NULL, build them manually. */ + view->strides + ? std::vector(view->strides, view->strides + view->ndim) + : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), + (view->readonly != 0)) { + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + this->m_view = view; + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + this->ownview = ownview; + } + + buffer_info(const buffer_info &) = delete; + buffer_info &operator=(const buffer_info &) = delete; + + buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); } + + buffer_info &operator=(buffer_info &&rhs) noexcept { + ptr = rhs.ptr; + itemsize = rhs.itemsize; + size = rhs.size; + format = std::move(rhs.format); + ndim = rhs.ndim; + shape = std::move(rhs.shape); + strides = std::move(rhs.strides); + std::swap(m_view, rhs.m_view); + std::swap(ownview, rhs.ownview); + readonly = rhs.readonly; + return *this; + } + + ~buffer_info() { + if (m_view && ownview) { + PyBuffer_Release(m_view); + delete m_view; + } + } + + Py_buffer *view() const { return m_view; } + Py_buffer *&view() { return m_view; } + +private: + struct private_ctr_tag {}; + + buffer_info(private_ctr_tag, + void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t ndim, + detail::any_container &&shape_in, + detail::any_container &&strides_in, + bool readonly) + : buffer_info( + ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) {} + + Py_buffer *m_view = nullptr; + bool ownview = false; +}; + +PYBIND11_NAMESPACE_BEGIN(detail) + +template +struct compare_buffer_info { + static bool compare(const buffer_info &b) { + return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); + } +}; + +template +struct compare_buffer_info::value>> { + static bool compare(const buffer_info &b) { + return (size_t) b.itemsize == sizeof(T) + && (b.format == format_descriptor::value + || ((sizeof(T) == sizeof(long)) + && b.format == (std::is_unsigned::value ? "L" : "l")) + || ((sizeof(T) == sizeof(size_t)) + && b.format == (std::is_unsigned::value ? "N" : "n"))); + } +}; + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/test/src/pybind11/pybind11/cast.h b/test/src/pybind11/pybind11/cast.h new file mode 100644 index 000000000..9b013bc39 --- /dev/null +++ b/test/src/pybind11/pybind11/cast.h @@ -0,0 +1,1672 @@ +/* + pybind11/cast.h: Partial template specializations to cast between + C++ and Python types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" +#include "detail/descr.h" +#include "detail/type_caster_base.h" +#include "detail/typeid.h" +#include "pytypes.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +PYBIND11_WARNING_DISABLE_MSVC(4127) + +PYBIND11_NAMESPACE_BEGIN(detail) + +template +class type_caster : public type_caster_base {}; +template +using make_caster = type_caster>; + +// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T +template +typename make_caster::template cast_op_type cast_op(make_caster &caster) { + return caster.operator typename make_caster::template cast_op_type(); +} +template +typename make_caster::template cast_op_type::type> +cast_op(make_caster &&caster) { + return std::move(caster).operator typename make_caster:: + template cast_op_type::type>(); +} + +template +class type_caster> { +private: + using caster_t = make_caster; + caster_t subcaster; + using reference_t = type &; + using subcaster_cast_op_type = typename caster_t::template cast_op_type; + + static_assert( + std::is_same::type &, subcaster_cast_op_type>::value + || std::is_same::value, + "std::reference_wrapper caster requires T to have a caster with an " + "`operator T &()` or `operator const T &()`"); + +public: + bool load(handle src, bool convert) { return subcaster.load(src, convert); } + static constexpr auto name = caster_t::name; + static handle + cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { + // It is definitely wrong to take ownership of this pointer, so mask that rvp + if (policy == return_value_policy::take_ownership + || policy == return_value_policy::automatic) { + policy = return_value_policy::automatic_reference; + } + return caster_t::cast(&src.get(), policy, parent); + } + template + using cast_op_type = std::reference_wrapper; + explicit operator std::reference_wrapper() { return cast_op(subcaster); } +}; + +#define PYBIND11_TYPE_CASTER(type, py_name) \ +protected: \ + type value; \ + \ +public: \ + static constexpr auto name = py_name; \ + template >::value, \ + int> \ + = 0> \ + static ::pybind11::handle cast( \ + T_ *src, ::pybind11::return_value_policy policy, ::pybind11::handle parent) { \ + if (!src) \ + return ::pybind11::none().release(); \ + if (policy == ::pybind11::return_value_policy::take_ownership) { \ + auto h = cast(std::move(*src), policy, parent); \ + delete src; \ + return h; \ + } \ + return cast(*src, policy, parent); \ + } \ + operator type *() { return &value; } /* NOLINT(bugprone-macro-parentheses) */ \ + operator type &() { return value; } /* NOLINT(bugprone-macro-parentheses) */ \ + operator type &&() && { return std::move(value); } /* NOLINT(bugprone-macro-parentheses) */ \ + template \ + using cast_op_type = ::pybind11::detail::movable_cast_op_type + +template +using is_std_char_type = any_of, /* std::string */ +#if defined(PYBIND11_HAS_U8STRING) + std::is_same, /* std::u8string */ +#endif + std::is_same, /* std::u16string */ + std::is_same, /* std::u32string */ + std::is_same /* std::wstring */ + >; + +template +struct type_caster::value && !is_std_char_type::value>> { + using _py_type_0 = conditional_t; + using _py_type_1 = conditional_t::value, + _py_type_0, + typename std::make_unsigned<_py_type_0>::type>; + using py_type = conditional_t::value, double, _py_type_1>; + +public: + bool load(handle src, bool convert) { + py_type py_value; + + if (!src) { + return false; + } + +#if !defined(PYPY_VERSION) + auto index_check = [](PyObject *o) { return PyIndex_Check(o); }; +#else + // In PyPy 7.3.3, `PyIndex_Check` is implemented by calling `__index__`, + // while CPython only considers the existence of `nb_index`/`__index__`. + auto index_check = [](PyObject *o) { return hasattr(o, "__index__"); }; +#endif + + if (std::is_floating_point::value) { + if (convert || PyFloat_Check(src.ptr())) { + py_value = (py_type) PyFloat_AsDouble(src.ptr()); + } else { + return false; + } + } else if (PyFloat_Check(src.ptr()) + || (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr()))) { + return false; + } else { + handle src_or_index = src; + // PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls. +#if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) + object index; + if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr()) + index = reinterpret_steal(PyNumber_Index(src.ptr())); + if (!index) { + PyErr_Clear(); + if (!convert) + return false; + } else { + src_or_index = index; + } + } +#endif + if (std::is_unsigned::value) { + py_value = as_unsigned(src_or_index.ptr()); + } else { // signed integer: + py_value = sizeof(T) <= sizeof(long) + ? (py_type) PyLong_AsLong(src_or_index.ptr()) + : (py_type) PYBIND11_LONG_AS_LONGLONG(src_or_index.ptr()); + } + } + + // Python API reported an error + bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); + + // Check to see if the conversion is valid (integers should match exactly) + // Signed/unsigned checks happen elsewhere + if (py_err + || (std::is_integral::value && sizeof(py_type) != sizeof(T) + && py_value != (py_type) (T) py_value)) { + PyErr_Clear(); + if (py_err && convert && (PyNumber_Check(src.ptr()) != 0)) { + auto tmp = reinterpret_steal(std::is_floating_point::value + ? PyNumber_Float(src.ptr()) + : PyNumber_Long(src.ptr())); + PyErr_Clear(); + return load(tmp, false); + } + return false; + } + + value = (T) py_value; + return true; + } + + template + static typename std::enable_if::value, handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyFloat_FromDouble((double) src); + } + + template + static typename std::enable_if::value && std::is_signed::value + && (sizeof(U) <= sizeof(long)), + handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_SIGNED((long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value + && (sizeof(U) <= sizeof(unsigned long)), + handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); + } + + template + static typename std::enable_if::value && std::is_signed::value + && (sizeof(U) > sizeof(long)), + handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromLongLong((long long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value + && (sizeof(U) > sizeof(unsigned long)), + handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromUnsignedLongLong((unsigned long long) src); + } + + PYBIND11_TYPE_CASTER(T, const_name::value>("int", "float")); +}; + +template +struct void_caster { +public: + bool load(handle src, bool) { + if (src && src.is_none()) { + return true; + } + return false; + } + static handle cast(T, return_value_policy /* policy */, handle /* parent */) { + return none().release(); + } + PYBIND11_TYPE_CASTER(T, const_name("None")); +}; + +template <> +class type_caster : public void_caster {}; + +template <> +class type_caster : public type_caster { +public: + using type_caster::cast; + + bool load(handle h, bool) { + if (!h) { + return false; + } + if (h.is_none()) { + value = nullptr; + return true; + } + + /* Check if this is a capsule */ + if (isinstance(h)) { + value = reinterpret_borrow(h); + return true; + } + + /* Check if this is a C++ type */ + const auto &bases = all_type_info((PyTypeObject *) type::handle_of(h).ptr()); + if (bases.size() == 1) { // Only allowing loading from a single-value type + value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); + return true; + } + + /* Fail */ + return false; + } + + static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { + if (ptr) { + return capsule(ptr).release(); + } + return none().release(); + } + + template + using cast_op_type = void *&; + explicit operator void *&() { return value; } + static constexpr auto name = const_name("capsule"); + +private: + void *value = nullptr; +}; + +template <> +class type_caster : public void_caster {}; + +template <> +class type_caster { +public: + bool load(handle src, bool convert) { + if (!src) { + return false; + } + if (src.ptr() == Py_True) { + value = true; + return true; + } + if (src.ptr() == Py_False) { + value = false; + return true; + } + if (convert || (std::strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name) == 0)) { + // (allow non-implicit conversion for numpy booleans) + + Py_ssize_t res = -1; + if (src.is_none()) { + res = 0; // None is implicitly converted to False + } +#if defined(PYPY_VERSION) + // On PyPy, check that "__bool__" attr exists + else if (hasattr(src, PYBIND11_BOOL_ATTR)) { + res = PyObject_IsTrue(src.ptr()); + } +#else + // Alternate approach for CPython: this does the same as the above, but optimized + // using the CPython API so as to avoid an unneeded attribute lookup. + else if (auto *tp_as_number = src.ptr()->ob_type->tp_as_number) { + if (PYBIND11_NB_BOOL(tp_as_number)) { + res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); + } + } +#endif + if (res == 0 || res == 1) { + value = (res != 0); + return true; + } + PyErr_Clear(); + } + return false; + } + static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { + return handle(src ? Py_True : Py_False).inc_ref(); + } + PYBIND11_TYPE_CASTER(bool, const_name("bool")); +}; + +// Helper class for UTF-{8,16,32} C++ stl strings: +template +struct string_caster { + using CharT = typename StringType::value_type; + + // Simplify life by being able to assume standard char sizes (the standard only guarantees + // minimums, but Python requires exact sizes) + static_assert(!std::is_same::value || sizeof(CharT) == 1, + "Unsupported char size != 1"); +#if defined(PYBIND11_HAS_U8STRING) + static_assert(!std::is_same::value || sizeof(CharT) == 1, + "Unsupported char8_t size != 1"); +#endif + static_assert(!std::is_same::value || sizeof(CharT) == 2, + "Unsupported char16_t size != 2"); + static_assert(!std::is_same::value || sizeof(CharT) == 4, + "Unsupported char32_t size != 4"); + // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) + static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, + "Unsupported wchar_t size != 2/4"); + static constexpr size_t UTF_N = 8 * sizeof(CharT); + + bool load(handle src, bool) { + handle load_src = src; + if (!src) { + return false; + } + if (!PyUnicode_Check(load_src.ptr())) { + return load_raw(load_src); + } + + // For UTF-8 we avoid the need for a temporary `bytes` object by using + // `PyUnicode_AsUTF8AndSize`. + if (UTF_N == 8) { + Py_ssize_t size = -1; + const auto *buffer + = reinterpret_cast(PyUnicode_AsUTF8AndSize(load_src.ptr(), &size)); + if (!buffer) { + PyErr_Clear(); + return false; + } + value = StringType(buffer, static_cast(size)); + return true; + } + + auto utfNbytes + = reinterpret_steal(PyUnicode_AsEncodedString(load_src.ptr(), + UTF_N == 8 ? "utf-8" + : UTF_N == 16 ? "utf-16" + : "utf-32", + nullptr)); + if (!utfNbytes) { + PyErr_Clear(); + return false; + } + + const auto *buffer + = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); + size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); + // Skip BOM for UTF-16/32 + if (UTF_N > 8) { + buffer++; + length--; + } + value = StringType(buffer, length); + + // If we're loading a string_view we need to keep the encoded Python object alive: + if (IsView) { + loader_life_support::add_patient(utfNbytes); + } + + return true; + } + + static handle + cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { + const char *buffer = reinterpret_cast(src.data()); + auto nbytes = ssize_t(src.size() * sizeof(CharT)); + handle s = decode_utfN(buffer, nbytes); + if (!s) { + throw error_already_set(); + } + return s; + } + + PYBIND11_TYPE_CASTER(StringType, const_name(PYBIND11_STRING_NAME)); + +private: + static handle decode_utfN(const char *buffer, ssize_t nbytes) { +#if !defined(PYPY_VERSION) + return UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) + : UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) + : PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); +#else + // PyPy segfaults when on PyUnicode_DecodeUTF16 (and possibly on PyUnicode_DecodeUTF32 as + // well), so bypass the whole thing by just passing the encoding as a string value, which + // works properly: + return PyUnicode_Decode(buffer, + nbytes, + UTF_N == 8 ? "utf-8" + : UTF_N == 16 ? "utf-16" + : "utf-32", + nullptr); +#endif + } + + // When loading into a std::string or char*, accept a bytes/bytearray object as-is (i.e. + // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. + // which supports loading a unicode from a str, doesn't take this path. + template + bool load_raw(enable_if_t::value, handle> src) { + if (PYBIND11_BYTES_CHECK(src.ptr())) { + // We were passed raw bytes; accept it into a std::string or char* + // without any encoding attempt. + const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); + if (!bytes) { + pybind11_fail("Unexpected PYBIND11_BYTES_AS_STRING() failure."); + } + value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); + return true; + } + if (PyByteArray_Check(src.ptr())) { + // We were passed a bytearray; accept it into a std::string or char* + // without any encoding attempt. + const char *bytearray = PyByteArray_AsString(src.ptr()); + if (!bytearray) { + pybind11_fail("Unexpected PyByteArray_AsString() failure."); + } + value = StringType(bytearray, (size_t) PyByteArray_Size(src.ptr())); + return true; + } + + return false; + } + + template + bool load_raw(enable_if_t::value, handle>) { + return false; + } +}; + +template +struct type_caster, + enable_if_t::value>> + : string_caster> {}; + +#ifdef PYBIND11_HAS_STRING_VIEW +template +struct type_caster, + enable_if_t::value>> + : string_caster, true> {}; +#endif + +// Type caster for C-style strings. We basically use a std::string type caster, but also add the +// ability to use None as a nullptr char* (which the string caster doesn't allow). +template +struct type_caster::value>> { + using StringType = std::basic_string; + using StringCaster = make_caster; + StringCaster str_caster; + bool none = false; + CharT one_char = 0; + +public: + bool load(handle src, bool convert) { + if (!src) { + return false; + } + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) { + return false; + } + none = true; + return true; + } + return str_caster.load(src, convert); + } + + static handle cast(const CharT *src, return_value_policy policy, handle parent) { + if (src == nullptr) { + return pybind11::none().release(); + } + return StringCaster::cast(StringType(src), policy, parent); + } + + static handle cast(CharT src, return_value_policy policy, handle parent) { + if (std::is_same::value) { + handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); + if (!s) { + throw error_already_set(); + } + return s; + } + return StringCaster::cast(StringType(1, src), policy, parent); + } + + explicit operator CharT *() { + return none ? nullptr : const_cast(static_cast(str_caster).c_str()); + } + explicit operator CharT &() { + if (none) { + throw value_error("Cannot convert None to a character"); + } + + auto &value = static_cast(str_caster); + size_t str_len = value.size(); + if (str_len == 0) { + throw value_error("Cannot convert empty string to a character"); + } + + // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that + // is too high, and one for multiple unicode characters (caught later), so we need to + // figure out how long the first encoded character is in bytes to distinguish between these + // two errors. We also allow want to allow unicode characters U+0080 through U+00FF, as + // those can fit into a single char value. + if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { + auto v0 = static_cast(value[0]); + // low bits only: 0-127 + // 0b110xxxxx - start of 2-byte sequence + // 0b1110xxxx - start of 3-byte sequence + // 0b11110xxx - start of 4-byte sequence + size_t char0_bytes = (v0 & 0x80) == 0 ? 1 + : (v0 & 0xE0) == 0xC0 ? 2 + : (v0 & 0xF0) == 0xE0 ? 3 + : 4; + + if (char0_bytes == str_len) { + // If we have a 128-255 value, we can decode it into a single char: + if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx + one_char = static_cast(((v0 & 3) << 6) + + (static_cast(value[1]) & 0x3F)); + return one_char; + } + // Otherwise we have a single character, but it's > U+00FF + throw value_error("Character code point not in range(0x100)"); + } + } + + // UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a + // surrogate pair with total length 2 instantly indicates a range error (but not a "your + // string was too long" error). + else if (StringCaster::UTF_N == 16 && str_len == 2) { + one_char = static_cast(value[0]); + if (one_char >= 0xD800 && one_char < 0xE000) { + throw value_error("Character code point not in range(0x10000)"); + } + } + + if (str_len != 1) { + throw value_error("Expected a character, but multi-character string found"); + } + + one_char = value[0]; + return one_char; + } + + static constexpr auto name = const_name(PYBIND11_STRING_NAME); + template + using cast_op_type = pybind11::detail::cast_op_type<_T>; +}; + +// Base implementation for std::tuple and std::pair +template