-
Notifications
You must be signed in to change notification settings - Fork 127
Complex Overloads #620
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mborland
wants to merge
39
commits into
develop
Choose a base branch
from
complex
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Complex Overloads #620
Changes from 30 commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
5073b38
Add better cmake support
mborland 1f98503
Add test set
mborland 8e82bbb
Add class basis
mborland 2a688a5
Restrict to boost.mp types
mborland 564ce77
Add unary operators
mborland 2df271f
Specialize class
mborland f61a9e1
Have std::complex inherit from multiprecision number class
mborland b714bb2
Add equality
mborland dbbc941
Add the ostream operator
mborland 0b5bd14
Add the istream operator
mborland f50f134
Add overloads for non-member real and imag
mborland 612f4fe
Replace polar impl with the one from default ops
mborland 21d9b25
Add abs overload
mborland d76f19b
Add constexpr annotations
mborland e46424d
Add arg
mborland 20faba1
Add norm
mborland 8ee77ce
Add conj overload
mborland a5faa8d
Add proj overload and fix const correctness
mborland 618bdf3
Fix test for std::sqrt promoting float result to double
mborland 861c6bd
Add exp overload
mborland 1c95d3a
Add overload for log
mborland 4abb271
Add complex and scalar basic operations
mborland 4a35b0d
Add log10 overload
mborland aa01997
Make basic operations non-member functions
mborland 2a5d7bb
Add pow overload
mborland c560957
Add sqrt overload
mborland f1b88ea
Add sinh overload
mborland caf7058
Add cosh overload
mborland 17b815d
Add tanh overload
mborland f9b9071
Add MSVC workaround for test failure
mborland 9b3575b
Bootstrap from existing complex class
mborland 214a2d2
Add remaining special functions
mborland 7ec2760
Add tentative polar() support and tests.
jzmaddock 411d9ea
Remove MSVC 14.0 for boost 1.86
mborland 4b0d1e5
Fix usage of if constexpr
mborland b437d60
Use SFINAE to fix C++14 build errors
mborland b885279
Delete cruft
mborland ded45c0
Add nick's test set
mborland 903d23c
Eigen matrix doesn't support iterators
mborland File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,373 @@ | ||
| /////////////////////////////////////////////////////////////////////////////// | ||
| // Copyright 2024 Matt Borland. Distributed under the Boost | ||
| // Software License, Version 1.0. (See accompanying file | ||
| // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
|
|
||
| #ifndef BOOST_MP_COMPLEX_HPP | ||
| #define BOOST_MP_COMPLEX_HPP | ||
|
|
||
| #include <boost/multiprecision/detail/assert.hpp> | ||
| #include <boost/multiprecision/traits/is_backend.hpp> | ||
| #include <boost/multiprecision/cpp_bin_float.hpp> | ||
| #include <boost/multiprecision/fwd.hpp> | ||
| #include <ostream> | ||
| #include <istream> | ||
| #include <complex> | ||
| #include <cmath> | ||
|
|
||
| namespace boost { | ||
| namespace multiprecision { | ||
|
|
||
| template <typename T> | ||
| class complex | ||
| { | ||
| private: | ||
| T real_; | ||
| T imag_; | ||
|
|
||
| public: | ||
| BOOST_MP_CXX14_CONSTEXPR complex() noexcept = default; | ||
| BOOST_MP_CXX14_CONSTEXPR complex(T real) noexcept : real_ {real}, imag_ {T{0}} {} | ||
| BOOST_MP_CXX14_CONSTEXPR complex(T real, T imag) noexcept : real_ {real}, imag_ {imag} {} | ||
| BOOST_MP_CXX14_CONSTEXPR complex& operator=(const complex& rhs) | ||
| { | ||
| real_ = rhs.real_; | ||
| imag_ = rhs.imag_; | ||
| return *this; | ||
| } | ||
|
|
||
| BOOST_MP_CXX14_CONSTEXPR T real() const noexcept { return real_; } | ||
| BOOST_MP_CXX14_CONSTEXPR T imag() const noexcept { return imag_; } | ||
|
|
||
| BOOST_MP_CXX14_CONSTEXPR complex operator+() const { return *this; } | ||
| BOOST_MP_CXX14_CONSTEXPR complex operator-() const { return {-real_, -imag_}; } | ||
|
|
||
| friend BOOST_MP_CXX14_CONSTEXPR complex operator+(const complex& lhs, const complex& rhs) noexcept | ||
| { | ||
| return {lhs.real_ + rhs.real_, lhs.imag_ + rhs.imag_}; | ||
| } | ||
|
|
||
| friend BOOST_MP_CXX14_CONSTEXPR complex operator+(const complex& lhs, const T& rhs) noexcept | ||
| { | ||
| return {lhs.real_ + rhs, lhs.imag_}; | ||
| } | ||
|
|
||
| friend BOOST_MP_CXX14_CONSTEXPR complex operator+(const T& lhs, const complex& rhs) noexcept | ||
| { | ||
| return {lhs + rhs.real_, rhs.imag_}; | ||
| } | ||
|
|
||
| friend BOOST_MP_CXX14_CONSTEXPR complex operator-(const complex& lhs, const complex& rhs) noexcept | ||
| { | ||
| return {lhs.real_ - rhs.real_, lhs.imag_ - rhs.imag_}; | ||
| } | ||
|
|
||
| friend BOOST_MP_CXX14_CONSTEXPR complex operator-(const complex& lhs, const T& rhs) noexcept | ||
| { | ||
| return {lhs.real_ - rhs, lhs.imag_}; | ||
| } | ||
|
|
||
| friend BOOST_MP_CXX14_CONSTEXPR complex operator-(const T& lhs, const complex& rhs) noexcept | ||
| { | ||
| return {lhs - rhs.real_, -rhs.imag_}; | ||
| } | ||
|
|
||
| friend BOOST_MP_CXX14_CONSTEXPR complex operator*(const complex& lhs, const complex& rhs) noexcept | ||
| { | ||
| return {lhs.real_ * rhs.real_ - lhs.imag_ * rhs.imag_, lhs.imag_ * rhs.real_ + lhs.real_ * rhs.imag_}; | ||
| } | ||
|
|
||
| friend BOOST_MP_CXX14_CONSTEXPR complex operator*(const complex& lhs, const T& rhs) noexcept | ||
| { | ||
| return {lhs.real_ * rhs, lhs.imag_ * rhs}; | ||
| } | ||
|
|
||
| friend BOOST_MP_CXX14_CONSTEXPR complex operator*(const T& lhs, const complex& rhs) noexcept | ||
| { | ||
| return {lhs * rhs.real_, lhs * rhs.imag_}; | ||
| } | ||
|
|
||
| friend BOOST_MP_CXX14_CONSTEXPR complex operator/(const complex& lhs, const complex& rhs) noexcept | ||
| { | ||
| const T divisor = rhs.real_ * rhs.real_ + rhs.imag_ * rhs.imag_; | ||
| const T real_part = (lhs.real_ * rhs.real_ + lhs.imag_ * rhs.imag_) / divisor; | ||
| const T imag_part = (lhs.imag_ * rhs.real_ - lhs.real_ * rhs.imag_) / divisor; | ||
| return {real_part, imag_part}; | ||
| } | ||
|
|
||
| friend BOOST_MP_CXX14_CONSTEXPR complex operator/(const complex& lhs, const T& rhs) noexcept | ||
| { | ||
| const T divisor = rhs * rhs; | ||
| const T real_part = (lhs.real_ * rhs) / divisor; | ||
| const T imag_part = (lhs.imag_ * rhs) / divisor; | ||
| return {real_part, imag_part}; | ||
| } | ||
|
|
||
| friend BOOST_MP_CXX14_CONSTEXPR complex operator/(const T& lhs, const complex& rhs) noexcept | ||
| { | ||
| const T divisor = rhs.real_ * rhs.real_ + rhs.imag_ * rhs.imag_; | ||
| const T real_part = (lhs * rhs.real_) / divisor; | ||
| const T imag_part = -(lhs.real_ * rhs.imag_) / divisor; | ||
| return {real_part, imag_part}; | ||
| } | ||
|
|
||
| BOOST_MP_CXX14_CONSTEXPR complex& operator+=(const complex& rhs) noexcept | ||
| { | ||
| *this = *this + rhs; | ||
| return *this; | ||
| } | ||
|
|
||
| BOOST_MP_CXX14_CONSTEXPR complex& operator+=(const T& rhs) noexcept | ||
| { | ||
| *this = *this + rhs; | ||
| return *this; | ||
| } | ||
|
|
||
| BOOST_MP_CXX14_CONSTEXPR complex& operator-=(const complex& rhs) noexcept | ||
| { | ||
| *this = *this - rhs; | ||
| return *this; | ||
| } | ||
|
|
||
| BOOST_MP_CXX14_CONSTEXPR complex& operator-=(const T& rhs) noexcept | ||
| { | ||
| *this = *this - rhs; | ||
| return *this; | ||
| } | ||
|
|
||
| BOOST_MP_CXX14_CONSTEXPR complex& operator*=(const complex& rhs) noexcept | ||
| { | ||
| *this = *this * rhs; | ||
| return *this; | ||
| } | ||
|
|
||
| BOOST_MP_CXX14_CONSTEXPR complex& operator*=(const T& rhs) noexcept | ||
| { | ||
| *this = *this * rhs; | ||
| return *this; | ||
| } | ||
|
|
||
| BOOST_MP_CXX14_CONSTEXPR complex& operator/=(const complex& rhs) noexcept | ||
| { | ||
| *this = *this / rhs; | ||
| return *this; | ||
| } | ||
|
|
||
| BOOST_MP_CXX14_CONSTEXPR complex& operator/=(const T& rhs) noexcept | ||
| { | ||
| *this = *this / rhs; | ||
| return *this; | ||
| } | ||
|
|
||
| BOOST_MP_CXX14_CONSTEXPR bool operator==(const complex& rhs) const noexcept | ||
| { | ||
| return real_ == rhs.real_ && imag_ == rhs.imag_; | ||
| } | ||
|
|
||
| BOOST_MP_CXX14_CONSTEXPR bool operator!=(const complex& rhs) const noexcept | ||
| { | ||
| return !(*this == rhs); | ||
| } | ||
|
|
||
| BOOST_MP_CXX14_CONSTEXPR bool operator==(const T& rhs) const noexcept | ||
| { | ||
| return real_ == rhs && imag_ == T{0}; | ||
| } | ||
|
|
||
| BOOST_MP_CXX14_CONSTEXPR bool operator!=(const T& rhs) const noexcept | ||
| { | ||
| return !(*this == rhs); | ||
| } | ||
|
|
||
| template <typename CharT, typename Traits> | ||
| friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const complex& z) | ||
| { | ||
| std::basic_ostringstream<CharT, Traits> s; | ||
| s.flags(os.flags()); | ||
| s.imbue(os.getloc()); | ||
| s.precision(os.precision()); | ||
| s << '(' << z.real_ << ',' << z.imag_ << ')'; | ||
|
|
||
| return os << s.str(); | ||
| } | ||
|
|
||
| // Supported formats: | ||
| // 1) real | ||
| // 2) (real) | ||
| // 3) (real, imag) | ||
| template <typename CharT, typename Traits> | ||
| friend std::basic_istream<CharT, Traits>& operator>>(std::basic_istream<CharT, Traits>& is, complex& z) | ||
| { | ||
| CharT ch {}; | ||
| T real = T{0}; | ||
| T imag = T{0}; | ||
|
|
||
| is >> std::ws; | ||
| is.get(ch); | ||
|
|
||
| if (ch == '(') | ||
| { | ||
| // Expecting a format like 2 or 3 | ||
| is >> std::ws >> real; | ||
| is.get(ch); | ||
| if (ch == ',') | ||
| { | ||
| // A comma indicates it's 3 | ||
| is >> std::ws >> imag; | ||
| is.get(ch); // Should be ')' | ||
| } | ||
| else if (ch != ')') | ||
| { | ||
| // Syntax error: unexpected character | ||
| is.setstate(std::ios_base::failbit); | ||
| return is; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| // No parentheses, just a real number from format 1 | ||
| is.putback(ch); | ||
| is >> real; | ||
| } | ||
|
|
||
| if (!is.fail()) | ||
| { | ||
| z.real_ = real; | ||
| z.imag_ = imag; | ||
| } | ||
|
|
||
| return is; | ||
| } | ||
| }; | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR complex<boost::multiprecision::number<T, ET>> polar(const boost::multiprecision::number<T, ET>& rho, | ||
| const boost::multiprecision::number<T, ET>& theta) noexcept | ||
| { | ||
| BOOST_MP_ASSERT_MSG(rho >= 0, "Rho must be positive"); | ||
| return {rho * cos(theta), rho * sin(theta)}; | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR boost::multiprecision::number<T, ET> real(const complex<boost::multiprecision::number<T, ET>>& z) noexcept | ||
| { | ||
| return z.real(); | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR boost::multiprecision::number<T, ET> imag(const complex<boost::multiprecision::number<T, ET>>& z) noexcept | ||
| { | ||
| return z.imag(); | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR boost::multiprecision::number<T, ET> abs(const complex<boost::multiprecision::number<T, ET>>& z) noexcept | ||
| { | ||
| return hypot(z.real(), z.imag()); | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR boost::multiprecision::number<T, ET> arg(const complex<boost::multiprecision::number<T, ET>>& z) noexcept | ||
| { | ||
| return atan2(z.imag(), z.real()); | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR boost::multiprecision::number<T, ET> norm(const complex<boost::multiprecision::number<T, ET>>& z) noexcept | ||
| { | ||
| return z.real() * z.real() + z.imag() * z.imag(); | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR complex<boost::multiprecision::number<T, ET>> conj(const complex<boost::multiprecision::number<T, ET>>& z) noexcept | ||
| { | ||
| return {z.real(), -z.imag()}; | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR complex<boost::multiprecision::number<T, ET>> proj(const complex<boost::multiprecision::number<T, ET>>& z) noexcept | ||
| { | ||
| if (isinf(z.real()) || isinf(z.imag())) | ||
| { | ||
| return {std::numeric_limits<boost::multiprecision::number<T, ET>>::infinity(), copysign(boost::multiprecision::number<T, ET>{0}, z.imag())}; | ||
| } | ||
|
|
||
| return z; | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR complex<boost::multiprecision::number<T, ET>> exp(const complex<boost::multiprecision::number<T, ET>>& z) noexcept | ||
| { | ||
| return polar(exp(z.real()), z.imag()); | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR complex<boost::multiprecision::number<T, ET>> log(const complex<boost::multiprecision::number<T, ET>>& z) noexcept | ||
| { | ||
| return {log(abs(z)), arg(z)}; | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR complex<boost::multiprecision::number<T, ET>> log10(const complex<boost::multiprecision::number<T, ET>>& z) noexcept | ||
| { | ||
| return log(z) / log(boost::multiprecision::number<T, ET>{10}); | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR complex<boost::multiprecision::number<T, ET>> pow(const complex<boost::multiprecision::number<T, ET>>& x, | ||
| const complex<boost::multiprecision::number<T, ET>>& y) noexcept | ||
| { | ||
| return exp(y * log(x)); | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR complex<boost::multiprecision::number<T, ET>> pow(const boost::multiprecision::number<T, ET>& x, | ||
| const complex<boost::multiprecision::number<T, ET>>& y) noexcept | ||
| { | ||
| const complex<boost::multiprecision::number<T, ET>> new_x {x}; | ||
| return exp(y * log(new_x)); | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR complex<boost::multiprecision::number<T, ET>> pow(const complex<boost::multiprecision::number<T, ET>>& x, | ||
| const boost::multiprecision::number<T, ET>& y) noexcept | ||
| { | ||
| const complex<boost::multiprecision::number<T, ET>> new_y {y}; | ||
| return exp(new_y * log(x)); | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR complex<boost::multiprecision::number<T, ET>> sqrt(const complex<boost::multiprecision::number<T, ET>>& z) noexcept | ||
| { | ||
| return polar(sqrt(abs(z)), arg(z) / 2); | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR complex<boost::multiprecision::number<T, ET>> sinh(const complex<boost::multiprecision::number<T, ET>>& z) noexcept | ||
| { | ||
| return {sinh(z.real()) * cos(z.imag()), cosh(z.real()) * sin(z.imag())}; | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR complex<boost::multiprecision::number<T, ET>> cosh(const complex<boost::multiprecision::number<T, ET>>& z) noexcept | ||
| { | ||
| return {cosh(z.real()) * cos(z.imag()), sinh(z.real()) * sin(z.imag())}; | ||
| } | ||
|
|
||
| template <typename T, expression_template_option ET> | ||
| inline BOOST_MP_CXX14_CONSTEXPR complex<boost::multiprecision::number<T, ET>> tanh(const complex<boost::multiprecision::number<T, ET>>& z) noexcept | ||
| { | ||
| return {sinh(z) / cosh(z)}; | ||
| } | ||
|
|
||
| } // Namespace multiprecision | ||
| } // Namespace boost | ||
|
|
||
| namespace std { | ||
|
|
||
| template <typename T, boost::multiprecision::expression_template_option ET> | ||
| class complex<boost::multiprecision::number<T, ET>> : public boost::multiprecision::complex<boost::multiprecision::number<T, ET>> {}; | ||
|
|
||
| } // namespace std | ||
|
|
||
| #endif //BOOST_MP_COMPLEX_HPP | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # Copyright 2018, 2019 Peter Dimov | ||
| # Copyright 2024 Matt Borland | ||
| # Distributed under the Boost Software License, Version 1.0. | ||
| # See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt | ||
|
|
||
| file(GLOB SOURCES "*.cpp") | ||
| add_library(boost_mp_tests STATIC ${SOURCES}) | ||
| target_compile_features(boost_mp_tests PRIVATE cxx_std_17) | ||
| target_link_libraries(boost_mp_tests PUBLIC Boost::multiprecision Boost::assert Boost::config Boost::core Boost::integer Boost::lexical_cast Boost::math Boost::random) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mborland you're re-inventing the wheel here? All these complex number functions are already implemented, it's just that
number<whatever>is already a complex number type with the same interface asstd::complexfor a suitably definedwhatever. See https://www.boost.org/doc/libs/1_85_0/libs/multiprecision/doc/html/boost_multiprecision/tut/complex.htmlThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In @NAThompson's original issue he was using
cpp_bin_float_100as the number type. The templates could beenable_iffor the already complex types, but if we want to allow a wrapper of any floating-point multiprecision type I don't see any other way around this.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess my point is that adding a
boost::multiprecision::complexclass duplicates what is already there and adds confusion (and maintenance), what if you had (caution untested code ahead!!):Now there's no duplication, so bugs fixed in one place are fixed everywhere. Plus if the user has included mpc.hpp for example then mpc will be used for mpfr complex number types rather than synthesising our own type.
It's still a bind writing out all the forwarding functions, but at least they're trivial and improvements in one place propagate throughout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can give that a go tomorrow. Forwarding functions will be monotonous which is fine. What is non-obvious to me is how to overload say self_type + complex type like is here: https://github.com/boostorg/multiprecision/pull/620/files#diff-db4d91a1fc843b60d38c832c49b4b5ec9b8c22f772d5c5d0a56eea563ae59a5dR50. I'll play with it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you have the form of the function already there?
BTW those functions are definitely NOT noexcept in the multiprecision case: memory allocations usually are involved!