//===-- duals/dual - Dual number class --------------------------*- C++ -*-===// // // Part of the cppduals project. // https://tesch1.gitlab.io/cppduals // // (c)2019 Michael Tesch. tesch1@gmail.com // // See https://gitlab.com/tesch1/cppduals/blob/master/LICENSE.txt for // license information. // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. #ifndef CPPDUALS_DUAL #define CPPDUALS_DUAL #ifndef PARSED_BY_DOXYGEN #define _DUALS_CONSTEXPR constexpr ///// FOR LIBCPP ///// #include #if !defined(_LIBCPP_BEGIN_NAMESPACE_STD) namespace std { template class complex; } #else // LIBCPP _LIBCPP_BEGIN_NAMESPACE_STD template class complex; _LIBCPP_END_NAMESPACE_STD #endif // LIBCPP #include #include #endif // PARSED_BY_DOXYGEN #if !defined(CPPDUALS_IGNORE_COMPILER_VERSION) && !defined(_WIN32) #if __cplusplus < 201103L #error CPPDUALS needs at least a C++11 compliant compiler #endif #endif /// Configure whether system has POSIX extern int signgam; #ifndef CPPDUALS_HAVE_SIGNGAM #ifndef _WIN32 #ifndef __CYGWIN__ #define CPPDUALS_HAVE_SIGNGAM 1 #endif #endif #endif namespace duals { /** \file dual \brief Dual number class. \mainpage cppduals \ref duals/dual is a single-header [Dual number](https://en.wikipedia.org/wiki/Dual_number) C++ template library that implements numbers of the type \f$(a + b \cdot \epsilon)\f$ where \f$ \epsilon \ne 0\f$, and \f$\epsilon^2 = 0\f$. `duals::dual<>` can be used for "automatic" differentiation, it can also recursively nest with itself for higher orders of differentiation (ie for the second derivative use `duals::dual>`). It can also be used to differentiate parts of complex functions as `std::complex>`. This file can be used stand-alone for dual number support, but for Eigen vectorization support rather the file \ref duals/dual_eigen should be included. ``` #include using namespace duals::literals; template T f(T x) { return pow(x,pow(x,x)); } template T df(T x) { return pow(x,-1. + x + pow(x,x)) * (1. + x*log(x) + x*pow(log(x),2.)); } template T ddf(T x) { return (pow(x,pow(x,x)) * pow(pow(x,x - 1.) + pow(x,x)*log(x)*(log(x) + 1.), 2.) + pow(x,pow(x,x)) * (pow(x,x - 1.) * log(x) + pow(x,x - 1.) * (log(x) + 1.) + pow(x,x - 1.) * ((x - 1.)/x + log(x)) + pow(x,x) * log(x) * pow(log(x) + 1., 2.) )); } int main() { std::cout << " f(2.) = " << f(2.) << "\n"; std::cout << " df(2.) = " << df(2.) << "\n"; std::cout << "ddf(2.) = " << ddf(2.) << "\n"; std::cout << " f(2+1_e) = " << f(2+1_e) << "\n"; std::cout << " f(2+1_e).dpart() = " << f(2+1_e).dpart() << "\n"; duals::hyperduald x(2+1_e,1+0_e); std::cout << " f((2+1_e) + (1+0_e)_e).dpart().dpart() = " << f(x).dpart().dpart() << "\n"; } ``` Produces (notice the derivative in the dual-part): ``` f(2.) = 16 df(2.) = 107.11 ddf(2.) = 958.755 f(2+1_e) = (16+107.11_e) f(2+1_e).dpart() = 107.11 f((2+1_e) + (1+0_e)_e).dpart().dpart() = 958.755 ``` How this works can be seen by inspecting the infinite [Taylor series](https://en.wikipedia.org/wiki/Taylor_series) expansion of a function \f$ f(x) \f$ at \f$ a \f$ with \f$ x = a + b\epsilon \f$. The series truncates itself due to the property \f$ \epsilon^2 = 0 \f$, leaving the function's derivative at \f$ a \f$ in the "dual part" of the result (when \f$ b = 1 \f$): \f[ \begin{split} f(a + b \epsilon) &= f(a) + f'(a)(b \epsilon) + \frac{f''(a)}{2!}(b \epsilon)^2 + \frac{f'''(a)}{3!}(b \epsilon)^3 + \ldots \\ \ &= f(a) + f'(a)(b \epsilon) \end{split} \f] The class is contained in a single c++11 header `#include `, for extended [Eigen](http://eigen.tuxfamily.org) support, instead include the header \ref duals/dual_eigen "#include ". Type X in the templates below can be any value which can be assigned to value_type. Type X also indicates a limitation to dual numbers of the same depth but (possibly) different value_type as `duals::dual`. For example, you can assign (or add/sub/mul/div) `duals::dual` and `duals::dual`, but you can not assign `duals::dual>` to `duals::dual`. Here is a synopsis of the class: ``` namespace duals { template class dual { typedef T value_type; dual(const & re = T(), const & du = T()); dual(const dual &); template dual(const dual &); T rpart() const; T dpart() const; void rpart(T); void dpart(T); dual operator-() const; dual operator+() const; dual & operator= (const T &); dual & operator+=(const T &); dual & operator-=(const T &); dual & operator*=(const T &); dual & operator/=(const T &); dual & operator=(const dual &); template dual & operator= (const dual &); template dual & operator+=(const dual &); template dual & operator-=(const dual &); template dual & operator*=(const dual &); template dual & operator/=(const dual &); // The comparison operators are not strictly well-defined, // they are implemented as comparison of the real part. bool operator ==(const X &b) const; bool operator !=(const X &b) const; bool operator <(const X &b) const; bool operator >(const X &b) const; bool operator <=(const X &b) const; bool operator >=(const X &b) const; bool operator <(const dual &b) const; bool operator >(const dual &b) const; bool operator <=(const dual &b) const; bool operator >=(const dual &b) const; }; // Non-member functions: T rpart(dual) // Real part T dpart(dual) // Dual part dual dconj(dual) // Dual-conjugate // Transcendental functions dual exp(dual) dual log(dual) dual log10(dual) dual log2(dual) dual logb(dual x) dual pow(dual, U) dual pow(U, dual) dual pow(dual, dual) dual sqrt(dual) dual cbrt(dual) dual sin(dual) dual cos(dual) dual tan(dual) dual asin(dual) dual acos(dual) dual atan(dual) dual atan2(dual, dual) dual hypot(dual, dual) dual scalbn(dual arg, int ex) // TODO: dual sinh(dual) dual cosh(dual) dual tanh(dual) dual asinh(dual) dual acosh(dual) dual atanh(dual) dual erf(dual) dual erfc(dual) dual tgamma(dual) dual lgamma(dual) // Non-differentiable operations on the real part. dual abs(dual d) dual fabs(dual d) dual fmax(dual x, dual y) dual fmin(dual x, dual y) dual frexp(dual arg, int* exp ) dual ldexp(dual arg, int exp ) dual trunc(dual d) dual floor(dual d) dual ceil(dual d) dual round(dual d) // floating point functions int fpclassify(dual d) bool isfinite(dual d) bool isnormal(dual d) bool isinf(dual d) bool isnan(dual d) bool signbit(dual d) dual copysign(dual x, dual y) // Utility functions dual random(dual low = {0,0}, dual high = {1,0}) dual random2(dual low = {0,0}, dual high = {1,0}) // Stream IO template operator>>(basic_istream &, dual &); template operator<<(basic_ostream &, const dual &); } ``` Some useful typedefs: ``` typedef dual dualf; typedef dual duald; typedef dual dualld; typedef dual hyperdualf; typedef dual hyperduald; typedef dual hyperdualld; typedef std::complex cdualf; typedef std::complex cduald; typedef std::complex cdualld; ``` There are also literals for dual parts defined in the inline namespace duals::literals. They are available with `using namespace duals;` or `using namespace duals::literals`. ``` using namespace duals::literals; dualf x = 3 + 4_ef; duald y = 3 + 4_e; dualld z = 3 + 4_el; ``` And in case you dislike iostreams, there are some formatters for the [`{fmt}`](https://github.com/fmtlib/fmt) formatting library. These are disabled by default, but can be enabled by `#define CPPDUALS_LIBFMT` for the `dual<>` formatter, and/or `#define CPPDUALS_LIBFMT_COMPLEX` for the std::complex<> formatter. There are three custom formatting flags that control how the dual numbers are printed, these must come before the normal `{fmt}` formatting spec '$', '*', ','. For me these are about 3x faster than iostreams. ``` #define CPPDUALS_LIBFMT #define CPPDUALS_LIBFMT_COMPLEX #inlcude using namespace duals::literals; ... string s = fmt::format("{:}", 1 + 2_e); // s = "(1.0+2.0_e)" string s = fmt::format("{:g}", 1 + 2_e); // s = "(1+2_e)" string s = fmt::format("{:*}", 1 + 2_e); // s = "(1.0+2.0*e)" string s = fmt::format("{:,}", 1 + 2_e); // s = "(1.0,2.0)" string s = fmt::format("{:*,g}", complexd(1,2_e)); // s = "((1)+(0,2)*i)" ``` The "archaic Greek epsilon" logo is from [Wikimedia commons](https://commons.wikimedia.org/wiki/File:Greek_Epsilon_archaic.svg) Some casual reading material: - [ON QUATERNIONS, William Rowan Hamilton, Proceedings of the Royal Irish Academy, 3 (1847), pp. 1–16.](https://www.maths.tcd.ie/pub/HistMath/People/Hamilton/Quatern2/) - [Basic Space-Time Transformations Expressed by Means of Two-Component Number Systems](https://doi.org/10.12693%2Faphyspola.86.291) */ #ifndef PARSED_BY_DOXYGEN template class dual; #endif /// Check if T is dual<>, match non-duals. template struct is_dual : std::false_type {}; #ifndef PARSED_BY_DOXYGEN /// Check if T is dual<>, match dual<>. template struct is_dual > : std::true_type {}; #endif /// Check if T is std::complex<>, match non- std::complex<>. template struct is_complex : std::false_type {}; #ifndef PARSED_BY_DOXYGEN /// Check if T is std::complex<>, match std::complex<>. template struct is_complex > : std::true_type {}; #endif /// Dual_traits helper class. template struct dual_traits { /// Depth of T - for T=scalar this is 0. for dual_traits it /// is 1. enum { depth = 0 }; // -Wenum-compare /// The real storage type. typedef T real_type; }; #ifndef PARSED_BY_DOXYGEN /// dual_traits for dual<> types template struct dual_traits> { /// Depth to which this dual<> type is nested. One (1) is a /// first-level dual, whereas non-duals have a depth of 0. enum { depth = dual_traits::depth + 1 }; /// The real storage type. typedef typename dual_traits::real_type real_type; }; template struct dual_traits>> { /// complex> have the same 'depth' as their dual. enum { depth = dual_traits >::depth }; /// The real storage type. typedef typename dual_traits::real_type real_type; }; namespace detail { template struct Void { typedef void type; }; template struct has_member_type : std::false_type {}; template struct has_member_type::type > : std::true_type { struct wrap { typedef typename T::type type; typedef typename T::type ReturnType; }; }; } // namespace detail /// Promote two types - default according to common_type template struct promote : std::common_type {}; /// Can types A and B be promoted to a common type? template using can_promote = detail::has_member_type>; // dual dual // T == U template struct promote, dual, typename std::enable_if<(can_promote::value && (int)dual_traits::depth == (int)dual_traits::depth)>::type> { typedef dual::type> type; }; // dual U // T >= U // U is not complex template struct promote, U, typename std::enable_if<(can_promote::value && (int)dual_traits::depth >= (int)dual_traits::depth && !is_complex::value)>::type> { typedef dual::type> type; }; // U dual // T >= U // U is not complex template struct promote, typename std::enable_if<(can_promote::value && (int)dual_traits::depth >= (int)dual_traits::depth && !is_complex::value)>::type> { typedef dual::type> type; }; // ///////////////////////////////////////////////// // complex complex // T == U template struct promote, std::complex, typename std::enable_if<(can_promote::value && (is_dual::value || is_dual::value))>::type> { typedef std::complex::type> type; }; // complex U // T >= U // U is not complex template struct promote, U, typename std::enable_if<(can_promote::value && (is_dual::value || is_dual::value) && !is_complex::value)>::type> { typedef std::complex::type> type; }; // U complex // T >= U // U is not complex template struct promote, typename std::enable_if<(can_promote::value && (is_dual::value || is_dual::value) && !is_complex::value)>::type> { typedef std::complex::type> type; }; // ///////////////////////////////////////////////// #endif // PARSED_BY_DOXYGEN } // namespace duals #ifndef PARSED_BY_DOXYGEN #define NOMACRO // thwart macroification #ifdef EIGEN_PI #define MY_PI EIGEN_PI #else #define MY_PI M_PI #endif #endif // PARSED_BY_DOXYGEN #ifdef _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_BEGIN_NAMESPACE_STD #else namespace std { #endif #ifdef CPPDUALS_ENABLE_STD_IS_ARITHMETIC /// Duals are as arithmetic as their value_type is arithmetic. template struct is_arithmetic> : is_arithmetic {}; #endif // CPPDUALS_ENABLE_IS_ARITHMETIC /// Duals are compound types. template struct is_compound> : true_type {}; // Modification of std::numeric_limits<> per // C++03 17.4.3.1/1, and C++11 18.3.2.3/1. template struct numeric_limits > : public numeric_limits { static constexpr bool is_specialized = true; static constexpr duals::dual min NOMACRO () { return numeric_limits::min NOMACRO (); } static constexpr duals::dual lowest() { return numeric_limits::lowest(); } static constexpr duals::dual max NOMACRO () { return numeric_limits::max NOMACRO (); } static constexpr duals::dual epsilon() { return numeric_limits::epsilon(); } static constexpr duals::dual round_error() { return numeric_limits::round_error(); } static constexpr duals::dual infinity() { return numeric_limits::infinity(); } static constexpr duals::dual quiet_NaN() { return numeric_limits::quiet_NaN(); } static constexpr duals::dual signaling_NaN() { return numeric_limits::signaling_NaN(); } static constexpr duals::dual denorm_min() { return numeric_limits::denorm_min(); } }; #ifdef _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD #else } // namespace std #endif namespace duals { #ifndef PARSED_BY_DOXYGEN // T and X are wrapped in a dual<> #define CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X) \ typename std::enable_if<(int)duals::dual_traits::depth == \ (int)duals::dual_traits::depth, int>::type = 0, \ typename std::enable_if::value,int>::type = 0 // Both T and U are wrapped in a dual<> #define CPPDUALS_ENABLE_SAME_DEPTH_AND_COMMON_T(T,U) \ typename common_t = dual::type>, \ typename std::enable_if<(int)duals::dual_traits::depth == \ (int)duals::dual_traits::depth, int>::type = 0, \ typename std::enable_if::value,int>::type = 0 // T is wrapped in a dual<>, U may or may not be. #define CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U) \ typename common_t = typename duals::promote, U>::type, \ typename std::enable_if<((int)duals::dual_traits::depth >= \ (int)duals::dual_traits::depth), int>::type = 0, \ typename std::enable_if,U>::value,int>::type = 0 // T is wrapped in complex> #define CPPDUALS_ENABLE_LEQ_DEPTH_AND_CX_COMMON_T(T,U) \ typename common_t = typename duals::promote >,U>::type, \ typename std::enable_if<((int)duals::dual_traits::depth >= \ (int)duals::dual_traits::depth), int>::type = 0, \ typename std::enable_if>,U>::value,int>::type = 0 #define CPPDUALS_ENABLE_IF(...) typename std::enable_if< (__VA_ARGS__) , int>::type = 0 #endif /*! \page user Background TODO: Add text here... */ /// Abstract dual number class. Can nest with other dual numbers and /// complex numbers. template class dual { public: /// Class type of rpart() and dpart(). This type can be nested /// dual<> or std::complex<>. typedef T value_type; private: /// The real part. value_type _real; /// The dual part. value_type _dual; public: /// Construct dual from optional real and dual parts. constexpr dual(const value_type re = value_type(), const value_type du = value_type()) : _real(re), _dual(du) {} /// Copy construct from a dual of equal depth. template::value)> dual(const dual & x) : _real(x.rpart()), _dual(x.dpart()) {} /// Cast to a complex> with real part equal to *this. operator std::complex>() { return std::complex>(*this, (T)0); } /// Explicit cast to an arithmetic type retains the rpart() template ::value && !is_dual::value)> explicit operator X() const { return X(_real); } /// Get the real part. T rpart() const { return _real; } /// Get the dual part. T dpart() const { return _dual; } /// Set the real part. void rpart(value_type re) { _real = re; } /// Get the dual part. void dpart(value_type du) { _dual = du; } /// Unary negation dual operator-() const { return dual(-_real, -_dual); } /// Unary nothing dual operator+() const { return *this; } /// Assignment of `value_type` assigns the real part and zeros the dual part. dual & operator= (const T & x) { _real = x; _dual = value_type(); return *this; } /// Add a relatively-scalar to this dual. dual & operator+=(const T & x) { _real += x; return *this; } /// Subtract a relatively-scalar from this dual. dual & operator-=(const T & x) { _real -= x; return *this; } /// Multiply a relatively-scalar with this dual. dual & operator*=(const T & x) { _real *= x; _dual *= x; return *this; } /// Divide this dual by relatively-scalar. dual & operator/=(const T & x) { _real /= x; _dual /= x; return *this; } //dua & operator=(const dual & x) { _real = x.rpart(); _dual = x.dpart(); } template dual & operator= (const dual & x) { _real = x.rpart(); _dual = x.dpart(); return *this; } /// Add a dual of the same depth to this dual. template dual & operator+=(const dual & x) { _real += x.rpart(); _dual += x.dpart(); return *this; } /// Subtract a dual of the same depth from this dual. template dual & operator-=(const dual & x) { _real -= x.rpart(); _dual -= x.dpart(); return *this; } /// Multiply this dual with a dual of same depth. template dual & operator*=(const dual & x) { _dual = _real * x.dpart() + _dual * x.rpart(); _real = _real * x.rpart(); return *this; } /// Divide this dual by another dual of the same or lower depth. template dual & operator/=(const dual & x) { _dual = (_dual * x.rpart() - _real * x.dpart()) / (x.rpart() * x.rpart()); _real = _real / x.rpart(); return *this; } //The following comparison operators are not strictly well-defined, //they are implemented as comparison of the real parts. #if 0 /// Compare this dual with another dual, comparing real parts for equality. template bool operator ==(const dual &b) const { return _real == b.rpart(); } /// Compare this dual with another dual, comparing real parts for inequality. template bool operator !=(const dual &b) const { return _real != b.rpart(); } /// Compare real part against real part of b template bool operator <(const dual &b) const { return _real < b.rpart(); } /// Compare real part against real part of b template bool operator >(const dual &b) const { return _real > b.rpart(); } /// Compare real part against real part of b template bool operator <=(const dual &b) const { return _real <= b.rpart(); } /// Compare real part against real part of b template bool operator >=(const dual &b) const { return _real >= b.rpart(); } #endif }; /// Get the dual's real part. template T rpart(const dual & x) { return x.rpart(); } /// Get the dual's dual part. template T dpart(const dual & x) { return x.dpart(); } /// R-part of complex> is non-dual complex<> (not to be confused with real()) template std::complex rpart(const std::complex> & x) { return std::complex(x.real().rpart(), x.imag().rpart()); } /// Dual part of complex> is complex<> template std::complex dpart(const std::complex> & x) { return std::complex(x.real().dpart(), x.imag().dpart()); } /// Get a non-dual's real part. template ::value && !std::is_compound::value) || is_complex::value)> T rpart(const T & x) { return x; } /// Get a non-dual's dual part := zero. template ::value && !std::is_compound::value) || is_complex::value)> T dpart(const T & ) { return T(0); } #ifndef PARSED_BY_DOXYGEN /// Dual +-*/ ops with another entity #define CPPDUALS_BINARY_OP(op) \ template \ common_t \ operator op(const dual & z, const dual & w) { \ common_t x(z); \ return x op##= w; \ } \ template>>::value)> \ common_t \ operator op(const dual & z, const U & w) { \ common_t x(z); \ return x op##= w; \ } \ template>>::value)> \ common_t \ operator op(const U & z, const dual & w) { \ common_t x(z); \ return x op##= w; \ } \ template>>::value)> \ common_t \ operator op(const std::complex> & z, const U & w) { \ common_t x(z); \ return x op##= w; \ } \ template>>::value)> \ common_t \ operator op(const U & z, const std::complex> & w) { \ common_t x(z); \ return x op##= w; \ } CPPDUALS_BINARY_OP(+) CPPDUALS_BINARY_OP(-) CPPDUALS_BINARY_OP(*) CPPDUALS_BINARY_OP(/) /// Dual compared to a non-complex lower rank thing #define CPPDUALS_LHS_COMPARISON(op) \ template \ bool \ operator op(const dual & a, const dual & b) { \ return a.rpart() op b.rpart(); \ } \ template::value)> \ bool \ operator op(const U & a, const dual & b) { \ return a op b.rpart(); \ } \ template::value)> \ bool \ operator op(const dual & a, const U & b) { \ return a.rpart() op b; \ } CPPDUALS_LHS_COMPARISON(<) CPPDUALS_LHS_COMPARISON(>) CPPDUALS_LHS_COMPARISON(<=) CPPDUALS_LHS_COMPARISON(>=) CPPDUALS_LHS_COMPARISON(==) CPPDUALS_LHS_COMPARISON(!=) #endif // PARSED_BY_DOXYGEN /// Complex Conjugate of a dual is just the dual. template dual conj(const dual & x) { return x; } /// Conjugate a thing that's not dual and not complex -- it has no /// complex value so just return it. This is different from /// std::conj() which promotes the T to a std::complex. template::value && !is_complex::value && std::is_arithmetic::value)> T conj(const T & x) { return x; } /// Dual Conjugate, such that x*dconj(x) = rpart(x)^2. Different /// function name from complex conjugate conj(). template dual dconj(const dual & x) { return dual(x.rpart(), - x.dpart()); } /// Dual Conjugate of a complex, such that x*dconj(x) = rpart(x)^2. /// Different function name from complex conjugate conj(). template std::complex dconj(const std::complex & x) { return std::complex(dconj(x.real()), dconj(x.imag())); } /// Conjugate a thing that's not dual and not complex. template::value && !is_complex::value && std::is_arithmetic::value)> T dconj(const T & x) { return x; } namespace detail { template dual exp_impl(const dual & x); template dual log_impl(const dual & x); template dual log10_impl(const dual & x); template dual log2_impl(const dual & x); template dual logb_impl(const dual & x); template common_t pow_dual_scalar(const dual& x, const U& y); template common_t pow_scalar_dual(const U& x, const dual& y); template std::complex> pow_complex_dual(const std::complex>& x, const dual& y); template std::complex> pow_dual_complex(const dual& x, const std::complex>& y); template common_t pow_dual_dual(const dual& x, const dual& y); template dual sqrt_impl(const dual & x); template dual cbrt_impl(const dual & x); template dual sin_impl(const dual & x); template dual cos_impl(const dual & x); template dual tan_impl(const dual & x); template dual asin_impl(const dual & x); template dual acos_impl(const dual & x); template dual atan_impl(const dual & x); template common_t atan2_dual_scalar(const dual & y, const U & x); template common_t atan2_scalar_dual(const U & y, const dual & x); template dual atan2_dual_dual(const dual & y, const dual & x); template dual hypot_impl(const dual & x, const dual & y); template dual scalbn_impl(const dual & arg, int ex); template dual sinh_impl(const dual & x); template dual cosh_impl(const dual & x); template dual tanh_impl(const dual & x); template dual asinh_impl(const dual & x); template dual acosh_impl(const dual & x); template dual atanh_impl(const dual & x); template dual erf_impl(const dual & x); template dual erfc_impl(const dual & x); template dual tgamma_impl(const dual & x); template dual lgamma_impl(const dual & x); template dual abs_impl(const dual & x); template dual fabs_impl(const dual & x); template dual fmax_impl(const dual & x, const dual & y); template dual fmin_impl(const dual & x, const dual & y); template dual frexp_impl(const dual & x, int* exp); template dual ldexp_impl(const dual & x, int exp); template dual trunc_impl(const dual & x); template dual floor_impl(const dual & x); template dual ceil_impl(const dual & x); template dual round_impl(const dual & x); template int fpclassify_impl(const dual & x); template bool isfinite_impl(const dual & x); template bool isnormal_impl(const dual & x); template bool isinf_impl(const dual & x); template bool isnan_impl(const dual & x); template bool signbit_impl(const dual & x); template dual copysign_impl(const dual & x, const dual & y); template dual log1p_impl(const dual & x); template dual expm1_impl(const dual & x); template dual random_impl(const dual & low, const dual & high); template dual random2_impl(const dual & low, const dual & high); } /// /// /// /// /// /// /// /// /// Prototypes /// // Public interfaces that forward to implementations template dual exp(const dual & x) { return detail::exp_impl(x); } template dual log(const dual & x) { return detail::log_impl(x); } template dual log10(const dual & x) { return detail::log10_impl(x); } template dual log2(const dual & x) { return detail::log2_impl(x); } template dual logb(const dual & x) { return detail::logb_impl(x); } template::value)> common_t pow(const dual& x, const U& y) { return detail::pow_dual_scalar(x, y); } template::value)> common_t pow(const U& x, const dual& y) { return detail::pow_scalar_dual(x, y); } template std::complex> pow(const dual & x, const std::complex> & y) { return detail::pow_dual_complex(x, y); } template std::complex> pow(const std::complex> & x, const dual& y) { return detail::pow_complex_dual(x, y); } template common_t pow(const dual& x, const dual& y) { return detail::pow_dual_dual(x, y); } template dual sqrt(const dual & x) { return detail::sqrt_impl(x); } template dual cbrt(const dual & x) { return detail::cbrt_impl(x); } template dual sin(const dual & x) { return detail::sin_impl(x); } template dual cos(const dual & x) { return detail::cos_impl(x); } template dual tan(const dual & x) { return detail::tan_impl(x); } template dual asin(const dual & x) { return detail::asin_impl(x); } template dual acos(const dual & x) { return detail::acos_impl(x); } template dual atan(const dual & x) { return detail::atan_impl(x); } template common_t atan2(const dual & y, const U & x) { return detail::atan2_dual_scalar(y, x); } template common_t atan2(const U & y, const dual & x) { return detail::atan2_scalar_dual(y, x); } template dual atan2(const dual & y, const dual & x) { return detail::atan2_dual_dual(y, x); } template dual hypot(const dual & x, const dual & y) { return detail::hypot_impl(x, y); } template dual scalbn(const dual & arg, int ex) { return detail::scalbn_impl(arg, ex); } template dual sinh(const dual & x) { return detail::sinh_impl(x); } template dual cosh(const dual & x) { return detail::cosh_impl(x); } template dual tanh(const dual & x) { return detail::tanh_impl(x); } template dual asinh(const dual & x) { return detail::asinh_impl(x); } template dual acosh(const dual & x) { return detail::acosh_impl(x); } template dual atanh(const dual & x) { return detail::atanh_impl(x); } /// The error function. Make sure to `#include ` before /// `#include ` to use this function. template dual erf(const dual & x) { return detail::erf_impl(x); } template dual erfc(const dual & x) { return detail::erfc_impl(x); } template dual tgamma(const dual & x) { return detail::tgamma_impl(x); } template dual lgamma(const dual & x) { return detail::lgamma_impl(x); } template dual abs(const dual & x) { return detail::abs_impl(x); } template dual fabs(const dual & x) { return detail::fabs_impl(x); } template dual fmax(const dual & x, const dual & y) { return detail::fmax_impl(x, y); } template dual fmin(const dual & x, const dual & y) { return detail::fmin_impl(x, y); } template dual frexp(const dual & x, int* exp) { return detail::frexp_impl(x, exp); } template dual ldexp(const dual & x, int exp) { return detail::ldexp_impl(x, exp); } //template dual trunc(const dual & x) { return detail::trunc_impl(x); } template dual trunc(const dual & x) { return detail::trunc_impl(x); } template dual floor(const dual & x) { return detail::floor_impl(x); } template dual ceil(const dual & x) { return detail::ceil_impl(x); } template dual round(const dual & x) { return detail::round_impl(x); } template int fpclassify(const dual & x) { return detail::fpclassify_impl(x); } template bool isfinite(const dual & x) { return detail::isfinite_impl(x); } template bool isnormal(const dual & x) { return detail::isnormal_impl(x); } template bool isinf(const dual & x) { return detail::isinf_impl(x); } template bool isnan(const dual & x) { return detail::isnan_impl(x); } template bool signbit(const dual & x) { return detail::signbit_impl(x); } template dual copysign(const dual & x, const dual & y) { return detail::copysign_impl(x, y); } //template dual random(const dual & low, const dual & high) { return detail::random_impl(low, high); } //template dual random2(const dual & low, const dual & high) { return detail::random2_impl(low, high); } namespace utils { template int sgn(T val) { return (T(0) < val) - (val < T(0)); } } template dual log1p(const dual & x) {return detail::log1p_impl(x);} template dual expm1(const dual & x) {return detail::expm1_impl(x);} #if __cpp_user_defined_literals >= 200809 /// Dual number literals in namespace duals::literals inline namespace literals { using duals::dual; constexpr dual operator""_ef(long double du) { return { 0.0f, static_cast(du) }; } constexpr dual operator""_ef(unsigned long long du) { return { 0.0f, static_cast(du) }; } constexpr dual operator""_e(long double du) { return { 0.0, static_cast(du) }; } constexpr dual operator""_e(unsigned long long du) { return { 0.0, static_cast(du) }; } constexpr dual operator""_el(long double du) { return { 0.0l, du }; } constexpr dual operator""_el(unsigned long long du) { return { 0.0l, static_cast(du) }; } } // namespace literals #endif // shortcut type names typedef dual dualf; typedef dual duald; typedef dual dualld; typedef dual hyperdualf; typedef dual hyperduald; typedef dual hyperdualld; } // namespace duals /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// Insert some stuff into std:: /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// #ifdef _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_BEGIN_NAMESPACE_STD #else namespace std { #endif // special needs LIBCPP #if defined(_LIBCPP_BEGIN_NAMESPACE_STD) template _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI duals::dual
__constexpr_copysign(duals::dual
a, duals::dual
b); template _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI duals::dual
__constexpr_fabs(duals::dual
a); template _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI bool __constexpr_isfinite(duals::dual
a); template _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI bool __constexpr_isinf(duals::dual
a); template _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI duals::dual
__constexpr_fmax(duals::dual
a, duals::dual
b); template _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI duals::dual
__constexpr_scalbn(duals::dual
a, int exp); template _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI duals::dual
__constexpr_logb(duals::dual
a); #endif // export functions to the std namespace using duals::exp; using duals::log; using duals::log10; using duals::log2; using duals::logb; using duals::pow; using duals::sqrt; using duals::cbrt; using duals::sin; using duals::cos; using duals::tan; using duals::asin; using duals::acos; using duals::atan; using duals::atan2; using duals::hypot; using duals::scalbn; using duals::sinh; using duals::cosh; using duals::tanh; using duals::asinh; using duals::acosh; using duals::atanh; using duals::erf; using duals::erfc; using duals::tgamma; using duals::lgamma; using duals::abs; using duals::fabs; using duals::fmax; using duals::fmin; using duals::frexp; using duals::ldexp; using duals::trunc; using duals::floor; using duals::ceil; using duals::round; using duals::fpclassify; using duals::isfinite; using duals::isnormal; using duals::isinf; using duals::isnan; using duals::signbit; using duals::copysign; //using duals::random; //using duals::random2; // TODO: figure out if still needed namespace __math { using duals::isinf; } #ifdef _LIBCPP_END_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD #else } // namespace std #endif /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// Implementations /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// #include #include #ifdef _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_BEGIN_NAMESPACE_STD #else namespace std { #endif #if defined(_LIBCPP_BEGIN_NAMESPACE_STD) // special needs LIBCPP template _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI duals::dual
__constexpr_copysign(duals::dual
a, duals::dual
b) { return duals::copysign(a, b); } template _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI duals::dual
__constexpr_fabs(duals::dual
a) { return duals::fabs(a); } template _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI bool __constexpr_isfinite(duals::dual
a) { return duals::isfinite(a); } template _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI bool __constexpr_isinf(duals::dual
a) { return duals::isinf(a); } template _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI duals::dual
__constexpr_fmax(duals::dual
a, duals::dual
b) { return duals::fmax(a, b); } template _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI duals::dual
__constexpr_scalbn(duals::dual
a, int exp) { return duals::scalbn(a, exp); } template _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI duals::dual
__constexpr_logb(duals::dual
a) { return duals::logb(a); } #endif #ifdef _LIBCPP_END_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD #else } // namespace std #endif namespace duals { namespace detail { /// Exponential e^x template dual exp_impl(const dual & x) { using std::exp; T v = exp(x.rpart()); return dual(v, v * x.dpart()); } /// Natural log ln(x) template dual log_impl(const dual & x) { using std::log; T v = log(x.rpart()); if (x.dpart() == T(0)) return v; else return dual(v, x.dpart() / x.rpart()); } template dual log10_impl(const dual & x) { using std::log; return log(x) / log(static_cast(10)); } template dual log2_impl(const dual & x) { using std::log; return log(x) / log(static_cast(2)); } template duals::dual logb_impl(const duals::dual & x) { using std::log; using std::pow; using std::abs; using std::floor; T v = floor(log(abs(x.rpart())) / log(std::numeric_limits::radix)); return duals::dual(v, x.rpart() - pow(2,v) ? (T)0 : std::numeric_limits::infinity()); } // dual ^ U implementation template common_t pow_dual_scalar(const dual& x, const U& y) { using std::pow; typedef typename common_t::value_type V; return common_t( pow(x.rpart(), y), x.dpart() == V(0) ? V(0) : x.dpart() * y * pow(x.rpart(), y - U(1))); } // U ^ dual implementation template common_t pow_scalar_dual(const U& x, const dual& y) { return pow(common_t(x), y); } // U ^ dual implementation template std::complex> pow_complex_dual(const std::complex>& x, const dual& y) { using dual_t = duals::dual; // 1. Compute the magnitude (r = |x|). // r = sqrt( (Re x)^2 + (Im x)^2 ), where Re x and Im x are dual. dual_t rr = x.real() * x.real() + x.imag() * x.imag(); dual_t r = sqrt(rr); // Must have sqrt(dual_t) defined in your dual library // 2. Compute the argument (theta = arg(x)). // theta = atan2( Im x, Re x ) dual_t theta = atan2(x.imag(), x.real()); // dual-friendly atan2 // 3. Compute ln(x) = ln(r) + i * theta // Then multiply by y: y * ln(x) // realPart = y * ln(r) // imagPart = y * theta dual_t realPart = y * log(r); dual_t imagPart = y * theta; // 4. exponent = exp(realPart) // Then real/imag of x^y = exponent * cos(imagPart), exponent * // sin(imagPart) dual_t exponent = exp(realPart); dual_t realOut = exponent * cos(imagPart); dual_t imagOut = exponent * sin(imagPart); // 5. Return the result as a complex>. return std::complex(realOut, imagOut); } template std::complex> pow_dual_complex( const dual& x, const std::complex>& y) { // For clarity, define short aliases: using dual_t = duals::dual; // Extract the real and imaginary parts of y: both are dual. const dual_t& a = y.real(); // exponent's real part const dual_t& b = y.imag(); // exponent's imaginary part // 1. Compute ln(x). We assume x > 0 in the "real" sense. // (In forward-mode dual, x can carry derivatives, but rpart(x) should be > // 0 // or else you'd need branch handling for negative bases.) dual_t ln_x = log(x); // 2. Compute the real exponent = a * ln_x // and the imaginary exponent = b * ln_x dual_t realExp = a * ln_x; // a ln(x) dual_t imagExp = b * ln_x; // b ln(x) // 3. The magnitude is exp(a ln(x)): dual_t magnitude = exp(realExp); // 4. Then the final real and imaginary parts come from: // Re(x^y) = magnitude * cos(b ln(x)) // Im(x^y) = magnitude * sin(b ln(x)) dual_t realPart = magnitude * cos(imagExp); dual_t imagPart = magnitude * sin(imagExp); // 5. Return the complex result. return std::complex(realPart, imagPart); } // dual ^ dual implementation template common_t pow_dual_dual(const dual& f, const dual& g) { using std::log; using std::pow; #if 1 using std::floor; typedef typename common_t::value_type V; common_t result; if (f.rpart() == T(0) && g.rpart() >= U(1)) { if (g.rpart() > U(1)) { result = common_t(0); } else { result = f; } } else { if (f.rpart() < T(0) && g.rpart() == floor(g.rpart())) { V const tmp = g.rpart() * pow(f.rpart(), g.rpart() - U(1.0)); result = common_t(pow(f.rpart(), g.rpart()), f.dpart() == T(0) ? T(0) : f.dpart() * tmp); if (g.dpart() != U(0.0)) { // Return a NaN when g.dpart() != 0. result.dpart(std::numeric_limits::quiet_NaN()); } } else { // Handle the remaining cases. For cases 4,5,6,9 we allow the log() // function to generate -HUGE_VAL or NaN, since those cases result in // a nonfinite derivative. V const tmp1 = pow(f.rpart(), g.rpart()); V const tmp2 = g.rpart() * pow(f.rpart(), g.rpart() - T(1.0)); V const tmp3 = tmp1 * log(f.rpart()); result = common_t(tmp1, f.dpart() == T(0) && g.dpart() == U(0) ? V(0) : tmp2 * f.dpart() + tmp3 * g.dpart()); } } return result; #else T v = pow(f.rpart(), g.rpart()); return common_t( v, pow(f.rpart(), g.rpart() - T(1)) * (g.rpart() * f.dpart() + f.rpart() * log(f.rpart()) * g.dpart())); #endif } template dual sqrt_impl(const dual & x) { using std::sqrt; T v = sqrt(x.rpart()); if (x.dpart() == T(0)) return v; else return dual(v, x.dpart() / (T(2) * v) ); } template dual cbrt_impl(const dual & x) { using std::cbrt; T v = cbrt(x.rpart()); if (x.dpart() == T(0)) return v; else return dual(v, x.dpart() / (T(3) * v * v) ); } template dual sin_impl(const dual & x) { using std::sin; using std::cos; return dual(sin(x.rpart()), x.dpart() * cos(x.rpart())); } template dual cos_impl(const dual & x) { using std::cos; using std::sin; return dual(cos(x.rpart()), -sin(x.rpart()) * x.dpart()); } template dual tan_impl(const dual & x) { using std::tan; T v = tan(x.rpart()); return dual(v, x.dpart() * (v*v + 1)); } template dual asin_impl(const dual & x) { using std::asin; using std::sqrt; T v = asin(x.rpart()); if (x.dpart() == T(0)) return v; else return dual(v, x.dpart() / sqrt(1 - x.rpart()*x.rpart())); } template dual acos_impl(const dual & x) { using std::acos; using std::sqrt; T v = acos(x.rpart()); if (x.dpart() == T(0)) return v; else return dual(v, -x.dpart() / sqrt(1 - x.rpart()*x.rpart())); } template dual atan_impl(const dual & x) { using std::atan; T v = atan(x.rpart()); if (x.dpart() == T(0)) return v; else return dual(v, x.dpart() / (1 + x.rpart() * x.rpart())); } template dual atan2_dual_dual(const dual & y, const dual & x) { using std::atan2; T v = atan2(y.rpart(), x.rpart()); return dual(v, (x.rpart() * y.dpart() - y.rpart() * x.dpart()) / (y.rpart() * y.rpart() + x.rpart() * x.rpart())); } template common_t atan2_dual_scalar(const dual & y, const U & x) { using std::atan2; T v = atan2(y.rpart(), x); return dual(v, (x * y.dpart()) / (y.rpart() * y.rpart() + x * x)); } template common_t atan2_scalar_dual(const U & y, const dual & x) { using std::atan2; T v = atan2(y, x.rpart()); return dual(v, (-y * x.dpart()) / (y * y + x.rpart() * x.rpart())); } template duals::dual hypot_impl(const duals::dual & x, const duals::dual & y) { using std::hypot; return dual(hypot(x.rpart(), y.rpart()), (x.rpart() * y.dpart() + y.rpart() * x.dpart()) / hypot(x.rpart(), y.rpart())); } template duals::dual scalbn_impl(const duals::dual & arg, int ex) { return arg * std::pow((T)2, ex); } template dual sinh_impl(const dual & x) { using std::sinh; using std::cosh; return dual(sinh(x.rpart()), x.dpart() * cosh(x.rpart())); } template dual cosh_impl(const dual & x) { using std::cosh; using std::sinh; return dual(cosh(x.rpart()), x.dpart() * sinh(x.rpart())); } template dual tanh_impl(const dual & x) { using std::tanh; return dual(tanh(x.rpart()), x.dpart() * (1 - tanh(x.rpart()) * tanh(x.rpart()))); } template dual asinh_impl(const dual & x) { using std::asinh; using std::sqrt; return dual(asinh(x.rpart()), x.dpart() / sqrt(1 + x.rpart() * x.rpart())); } template dual acosh_impl(const dual & x) { using std::acosh; using std::sqrt; return dual(acosh(x.rpart()), x.dpart() / sqrt(x.rpart() * x.rpart() - 1)); } template dual atanh_impl(const dual & x) { using std::atanh; return dual(atanh(x.rpart()), x.dpart() / (1 - x.rpart() * x.rpart())); } template dual log1p_impl(const dual & x) { using std::log1p; return dual(log1p(x.rpart()), x.dpart() / (1 + x.rpart())); } template dual expm1_impl(const dual & x) { using std::expm1; using std::exp; return dual(expm1(x.rpart()), x.dpart() * exp(x.rpart())); } /// The error function. Make sure to `#include ` before /// `#include ` to use this function. template dual erf_impl(const dual & x) { using std::erf; using std::sqrt; using std::pow; using std::exp; return dual(erf(x.rpart()), x.dpart() * T(2)/sqrt(T(MY_PI)) * exp(-pow(x.rpart(),T(2)))); } /// Error function complement (1 - erf()). template dual erfc_impl(const dual & x) { using std::erfc; using std::sqrt; using std::pow; using std::exp; return dual(erfc(x.rpart()), x.dpart() * -T(2)/sqrt(T(MY_PI)) * exp(-pow(x.rpart(),T(2)))); } /// Gamma function. Approximation of the dual part. // TODO specialize for integers template dual tgamma_impl(const dual & x) { using std::tgamma; T v = tgamma(x.rpart()); if (x.dpart() == T(0)) return v; else { int errno_saved = errno; T h(T(1) / (1ull << (std::numeric_limits::digits / 3))); T w((tgamma(x.rpart()+h) - tgamma(x.rpart()-h))/(2*h)); errno = errno_saved; return dual(v, x.dpart() * w); } } /// Log of absolute value of gamma function. Approximation of the dual part. template dual lgamma_impl(const dual & x) { using std::lgamma; T v = lgamma(x.rpart()); if (x.dpart() == T(0)) return v; else { #if CPPDUALS_HAVE_SIGNGAM int signgam_saved = signgam; #endif int errno_saved = errno; T h(T(1) / (1ull << (std::numeric_limits::digits / 3))); T w((lgamma(x.rpart()+h) - lgamma(x.rpart()-h))/(2*h)); #if CPPDUALS_HAVE_SIGNGAM signgam = signgam_saved; #endif errno = errno_saved; return dual(v, x.dpart() * w); } } template dual abs_impl(const dual & x) { using std::abs; return dual(abs(x.rpart()), x.dpart() * utils::sgn(x.rpart())); } template dual fabs_impl(const dual & x) { using std::fabs; return dual(fabs(x.rpart()), x.dpart() * utils::sgn(x.rpart())); } template duals::dual fmax_impl(const duals::dual & x, const duals::dual & y) { return x.rpart() > y.rpart() ? x : y; } template duals::dual fmin_impl(const duals::dual & x, const duals::dual & y) { return x.rpart() <= y.rpart() ? x : y; } template duals::dual frexp_impl(const duals::dual & arg, int* exp ) { using std::frexp; return frexp(arg.rpart(), exp); } template duals::dual ldexp_impl(const duals::dual & arg, int exp ) { return arg * std::pow((T)2,exp); } template duals::dual trunc_impl(const duals::dual & d) { using std::trunc; return trunc(d.rpart()); } template dual floor_impl(const dual & x) { using std::floor; return dual(floor(x.rpart()), x.dpart() * utils::sgn(x.rpart()-floor(x.rpart()))); } template duals::dual ceil_impl(const duals::dual & d) { using std::ceil; return ceil(d.rpart()); } template duals::dual round_impl(const duals::dual & d) { using std::round; return round(d.rpart()); } template int fpclassify_impl(const duals::dual & d) { using std::fpclassify; return fpclassify(d.rpart()); } template bool isfinite_impl(const duals::dual & d) { using std::isfinite; return isfinite(d.rpart()); } template bool isnormal_impl(const duals::dual & d) { using std::isnormal; return isnormal(d.rpart()); } template bool isinf_impl(const duals::dual & d) { using std::isinf; return isinf(d.rpart()); } template bool isnan_impl(const duals::dual & d) { using std::isnan; return isnan(d.rpart()); } template bool signbit_impl(const duals::dual & d) { using std::signbit; return signbit(d.rpart()); } template duals::dual copysign_impl(const duals::dual & x, const duals::dual & y) { using std::copysign; T r = copysign(x.rpart(), y.rpart()); return duals::dual(r, r == x.rpart() ? x.dpart() : -x.dpart()); } } // namespace detail } // namespace duals #include namespace duals { namespace randos { // Random real value between a and b. template, CPPDUALS_ENABLE_IF(!is_complex::value && !is_dual::value)> T random(T a = T(0), T b = T(1)) { static std::default_random_engine generator; dist distribution(a, b); return distribution(generator); } template, CPPDUALS_ENABLE_IF(!is_complex::value && !is_dual::value)> T random2(T a = T(0), T b = T(1)) { return random(a,b); } // Helper class for testing - also random value in dual part. template ::value)> DT random2(DT a = DT(0,0), DT b = DT(1,1)) { using randos::random; return DT(a.rpart() + random2() * (b.rpart() - a.rpart()), a.dpart() + random2() * (b.dpart() - a.dpart())); } // Helper class for testing - also random value in dual part of the complex. template::value)> CT random2(CT a = CT(0,0), CT b = CT(1,1)) { return CT(a.real() + random2() * (b.real() - a.real()), a.imag() + random2() * (b.imag() - a.imag())); } } // randos /// Random real and dual parts, used by Eigen's Random(), by default // the returned value has zero dual part and is in the range [0+0_e, // 1+0_e]. template , CPPDUALS_ENABLE_IF(is_dual
::value)> DT random(DT a = DT(0,0), DT b = DT(1,0)) { using randos::random; return DT(random(a.rpart(),b.rpart()), random(a.dpart(),b.dpart())); } } // namespace duals #include namespace duals { typedef std::complex cdualf; typedef std::complex cduald; typedef std::complex cdualld; } #include namespace duals { /// Putto operator template std::basic_ostream<_CharT, _Traits> & operator<<(std::basic_ostream<_CharT, _Traits> & os, const dual & x) { std::basic_ostringstream<_CharT, _Traits> s; s.flags(os.flags()); s.imbue(os.getloc()); s.precision(os.precision()); s << '(' << x.rpart() << (x.dpart() < 0 ? "" : "+") << x.dpart() << "_e" << (std::is_same::type,float>::value ? "f" : std::is_same::type,double>::value ? "" : std::is_same::type,long double>::value ? "l" : "") << ")"; return os << s.str(); } /// Stream reader template std::basic_istream & operator>>(std::basic_istream & is, dual & x) { if (is.good()) { ws(is); if (is.peek() == CharT('(')) { is.get(); T r; is >> r; if (!is.fail()) { CharT c = is.peek(); if (c != CharT('_')) { ws(is); c = is.peek(); } if (c == CharT('+') || c == CharT('-') || c == CharT('_')) { if (c == CharT('+')) is.get(); T d; if (c == CharT('_')) { d = r; r = 0; } else is >> d; if (!is.fail()) { ws(is); c = is.peek(); if (c == CharT('_')) { is.get(); c = is.peek(); if (c == CharT('e')) { is.get(); c = is.peek(); if ((c == 'f' && !std::is_same::type,float>::value) || (c == 'l' && !std::is_same::type,long double>::value)) is.setstate(std::ios_base::failbit); else { if (c == 'f' || c == 'l') is.get(); ws(is); c = is.peek(); if (c == ')') { is.get(); x = dual(r, d); } else is.setstate(std::ios_base::failbit); } } else is.setstate(std::ios_base::failbit); } else is.setstate(std::ios_base::failbit); } else is.setstate(std::ios_base::failbit); } else if (c == CharT(')')) { is.get(); x = dual(r, T(0)); } else is.setstate(std::ios_base::failbit); } else is.setstate(std::ios_base::failbit); } else { T r; is >> r; if (!is.fail()) x = dual(r, T(0)); else is.setstate(std::ios_base::failbit); } } else is.setstate(std::ios_base::failbit); return is; } } // namespace duals #ifdef CPPDUALS_LIBFMT #include /// duals::dual<> Formatter for libfmt https://github.com/fmtlib/fmt /// /// Formats a dual number (r,d) as (r+d_e), offering the same /// formatting options as the underlying type - with the addition of /// three optional format options, only one of which may appear /// directly after the ':' in the format spec: '$', '*', and ','. The /// '*' flag changes the separating _ to a *, producing (r+d*e), where /// r and d are the formatted value_type values. The ',' flag simply /// prints the real and dual parts separated by a comma. As a /// concrete exmple, this formatter can produce either (3+5.4_e) or /// (3+5.4*e) or (3,5.4) for a dual using the specs {:g}, /// {:*g}, or {:,g}, respectively. When the '*' is NOT specified, the /// output should be compatible with the input operator>> and the dual /// literals below. (this implementation is a bit hacky - glad for /// cleanups). template struct fmt::formatter,Char> : public fmt::formatter { typedef fmt::formatter base; enum style { expr, star, pair } style_ = expr; detail::dynamic_format_specs specs_; FMT_CONSTEXPR auto parse(parse_context& ctx) -> decltype(ctx.begin()) { auto it = ctx.begin(); if (it != ctx.end()) { switch (*it) { case '$': style_ = style::expr; ctx.advance_to(++it); break; case '*': style_ = style::star; ctx.advance_to(++it); break; case ',': style_ = style::pair; ctx.advance_to(++it); break; default: break; } } return parse_format_specs(it, ctx.end(), specs_, ctx, detail::type_constant::value); } template auto format(const duals::dual & x, FormatCtx & ctx) const -> decltype(ctx.out()) { auto specs = specs_; if (specs.dynamic()) { detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, specs.width_ref, ctx); detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, specs.precision_ref, ctx); } auto out = ctx.out(); *out++ = '('; if (style_ == style::pair) { out = detail::write(out, x.rpart(), specs, ctx.locale()); *out++ = ','; out = detail::write(out, x.dpart(), specs, ctx.locale()); } else { if (x.rpart() || !x.dpart()) out = detail::write(out, x.rpart(), specs, ctx.locale()); if (x.dpart()) { if (x.rpart() && x.dpart() >= 0) specs.set_sign(sign::plus); out = detail::write(out, x.dpart(), specs, ctx.locale()); if (style_ == style::star) { *out++ = '*'; *out++ = 'e'; } else { *out++ = '_'; *out++ = 'e'; } if (std::is_same::type,float>::value) *out++ = 'f'; if (std::is_same::type,long double>::value) *out++ = 'l'; } } *out++ = ')'; return out; } }; #endif // CPPDUALS_LIBFMT #ifdef CPPDUALS_LIBFMT_COMPLEX #ifndef CPPDUALS_LIBFMT #include #endif /// std::complex<> Formatter for libfmt https://github.com/fmtlib/fmt /// /// libfmt does not provide a formatter for std::complex<>, although /// one is proposed for c++20. Anyway, at the expense of a k or two, /// you can define CPPDUALS_LIBFMT_COMPLEX and get this one. /// /// The standard iostreams formatting of complex numbers is (a,b), /// where a and b are the real and imaginary parts. This formats a /// complex number (a+bi) as (a+bi), offering the same formatting /// options as the underlying type - with the addition of three /// optional format options, only one of which may appear directly /// after the ':' in the format spec (before any fill or align): '$' /// (the default if no flag is specified), '*', and ','. The '*' flag /// adds a * before the 'i', producing (a+b*i), where a and b are the /// formatted value_type values. The ',' flag simply prints the real /// and complex parts separated by a comma (same as iostreams' format). /// As a concrete exmple, this formatter can produce either (3+5.4i) /// or (3+5.4*i) or (3,5.4) for a complex using the specs {:g} /// | {:$g}, {:*g}, or {:,g}, respectively. (this implementation is a /// bit hacky - glad for cleanups). /// template struct fmt::formatter,Char> : public fmt::formatter { typedef fmt::formatter base; enum style { expr, star, pair } style_ = expr; detail::dynamic_format_specs specs_; FMT_CONSTEXPR auto parse(parse_context& ctx) -> decltype(ctx.begin()) { auto it = ctx.begin(); if (it != ctx.end()) { switch (*it) { case '$': style_ = style::expr; ctx.advance_to(++it); break; case '*': style_ = style::star; ctx.advance_to(++it); break; case ',': style_ = style::pair; ctx.advance_to(++it); break; default: break; } } // Let base parser handle any nested format chars first auto end = base::parse(ctx); // Now parse the format specs for this formatter parse_format_specs(end, ctx.end(), specs_, ctx, detail::type_constant::value); return end; } template auto format(const std::complex & x, FormatCtx & ctx) const -> decltype(ctx.out()) { format_to(ctx.out(), "("); if (style_ == style::pair) { base::format(x.real(), ctx); format_to(ctx.out(), ","); base::format(x.imag(), ctx); } else { if (x.real() || !x.imag()) base::format(x.real(), ctx); if (x.imag()) { // The base formatter will handle the negative sign if (x.real() && x.imag() >= 0 && sign::plus != specs_.sign()) { format_to(ctx.out(), "+"); } base::format(x.imag(), ctx); if (style_ == style::star) format_to(ctx.out(), "*i"); else format_to(ctx.out(), "i"); if (std::is_same::type,float>::value) format_to(ctx.out(), "f"); if (std::is_same::type,long double>::value) format_to(ctx.out(), "l"); } } return format_to(ctx.out(), ")"); } }; #endif // CPPDUALS_LIBFMT_COMPLEX #endif // CPPDUALS_DUAL