You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1387 lines
48 KiB

//===-- 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
#include <cmath>
#include <ctgmath>
#include <limits>
#include <type_traits>
#include <complex>
#include <random>
#include <iostream>
#endif
#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<duals::dual<T>>`). It
can also be used to differentiate parts of complex functions as
`std::complex<duals::dual<T>>`. 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 <duals/dual>
using namespace duals::literals;
template <class T> T f(T x) { return pow(x,pow(x,x)); }
template <class T> T df(T x) { return pow(x,-1. + x + pow(x,x)) * (1. + x*log(x) + x*pow(log(x),2.)); }
template <class T> 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
<duals/dual>`, for extended [Eigen](http://eigen.tuxfamily.org)
support, instead include the header \ref duals/dual_eigen "#include <duals/dual_eigen>".
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<T>`. For example,
you can assign (or add/sub/mul/div) `duals::dual<float>` and
`duals::dual<double>`, but you can not assign
`duals::dual<duals::dual<float>>` to `duals::dual<float>`.
Here is a synopsis of the class:
```
namespace duals {
template<class T> class dual {
typedef T value_type;
dual(const & re = T(), const & du = T());
dual(const dual &);
template<class X> dual(const dual<X> &);
T rpart() const;
T dpart() const;
void rpart(T);
void dpart(T);
dual<T> operator-() const;
dual<T> operator+() const;
dual<T> & operator= (const T &);
dual<T> & operator+=(const T &);
dual<T> & operator-=(const T &);
dual<T> & operator*=(const T &);
dual<T> & operator/=(const T &);
dual<T> & operator=(const dual<T> &);
template<class X> dual<T> & operator= (const dual<X> &);
template<class X> dual<T> & operator+=(const dual<X> &);
template<class X> dual<T> & operator-=(const dual<X> &);
template<class X> dual<T> & operator*=(const dual<X> &);
template<class X> dual<T> & operator/=(const dual<X> &);
// 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<X> &b) const;
bool operator >(const dual<X> &b) const;
bool operator <=(const dual<X> &b) const;
bool operator >=(const dual<X> &b) const;
};
// Non-member functions:
T rpart(dual<T>) // Real part
T dpart(dual<T>) // Dual part
dual<T> dconj(dual<T>) // Dual-conjugate
dual<T> random(dual<T> low = {0,0}, dual<T> high = {1,0})
dual<T> exp(dual<T>)
dual<T> log(dual<T>)
dual<T> log10(dual<T>)
dual<T> pow(dual<T>, U)
dual<T> pow(U, dual<T>)
dual<T> pow(dual<T>, dual<T>)
dual<T> sqrt(dual<T>)
dual<T> cbrt(dual<T>)
dual<T> sin(dual<T>)
dual<T> cos(dual<T>)
dual<T> tan(dual<T>)
dual<T> asin(dual<T>)
dual<T> acos(dual<T>)
dual<T> atan(dual<T>)
// TODO:
dual<T> sinh(dual<T>)
dual<T> cosh(dual<T>)
dual<T> tanh(dual<T>)
dual<T> asinh(dual<T>)
dual<T> acosh(dual<T>)
dual<T> atanh(dual<T>)
// Non-differentiable operations on the real part.
T frexp(duals::dual<T> arg, int* exp );
duals::dual<T> ldexp(duals::dual<T> arg, int exp );
T trunc(duals::dual<T> d);
T floor(duals::dual<T> d);
T ceil(duals::dual<T> d);
T round(duals::dual<T> d);
int fpclassify(duals::dual<T> d);
bool isfinite(duals::dual<T> d);
bool isnormal(duals::dual<T> d);
bool isinf(duals::dual<T> d);
bool isnan(duals::dual<T> d);
// Stream IO
template<T> operator>>(basic_istream<charT, traits> &, dual<T> &);
template<T> operator<<(basic_ostream<charT, traits> &, const dual<T> &);
}
```
Some useful typedefs:
```
typedef dual<float> dualf;
typedef dual<double> duald;
typedef dual<long double> dualld;
typedef dual<dualf> hyperdualf;
typedef dual<duald> hyperduald;
typedef dual<dualld> hyperdualld;
typedef std::complex<dualf> cdualf;
typedef std::complex<duald> cduald;
typedef std::complex<dualld> 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 <duals/dual>
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 T> class dual;
#endif
/// Check if T is dual<>, match non-duals.
template <class T> struct is_dual : std::false_type {};
#ifndef PARSED_BY_DOXYGEN
/// Check if T is dual<>, match dual<>.
template <class T> struct is_dual<dual<T> > : std::true_type {};
#endif
/// Check if T is std::complex<>, match non- std::complex<>.
template <class T> struct is_complex : std::false_type {};
#ifndef PARSED_BY_DOXYGEN
/// Check if T is std::complex<>, match std::complex<>.
template <class T> struct is_complex<std::complex<T> > : std::true_type {};
#endif
/// Dual_traits helper class.
template <class T> struct dual_traits
{
/// Depth of T - for T=scalar this is 0. for dual_traits<double> 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 <class T> struct dual_traits<dual<T>>
{
/// 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<T>::depth + 1 };
/// The real storage type.
typedef typename dual_traits<T>::real_type real_type;
};
template <class T> struct dual_traits<std::complex<dual<T>>>
{
/// complex<dual<T>> have the same 'depth' as their dual.
enum { depth = dual_traits<T>::depth };
/// The real storage type.
typedef typename dual_traits<T>::real_type real_type;
};
namespace detail {
template<class T>
struct Void { typedef void type; };
template<class T, class U = void>
struct has_member_type : std::false_type {};
template<class T>
struct has_member_type<T, typename Void<typename T::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 <class T, class U, class V = void> struct promote : std::common_type<T,U> {};
/// Can types A and B be promoted to a common type?
template<class A, class B> using can_promote = detail::has_member_type<promote<A,B>>;
template <class T, class U>
struct promote<dual<T>, dual<U>,
typename std::enable_if<(can_promote<T,U>::value
&& (int)dual_traits<T>::depth == (int)dual_traits<U>::depth)>::type>
{
typedef dual<typename promote<U,T>::type> type;
};
template <class T, class U>
struct promote<dual<T>, U,
typename std::enable_if<(can_promote<T,U>::value
&& (int)dual_traits<T>::depth >= (int)dual_traits<U>::depth
&& !is_complex<U>::value)>::type>
{
typedef dual<typename promote<U,T>::type> type;
};
template <class T, class U>
struct promote<U, dual<T>,
typename std::enable_if<(can_promote<T,U>::value
&& (int)dual_traits<T>::depth >= (int)dual_traits<U>::depth
&& !is_complex<U>::value)>::type>
{
typedef dual<typename promote<U,T>::type> type;
};
// /////////////////////////////////////////////////
template <class T, class U>
struct promote<std::complex<T>, std::complex<U>,
typename std::enable_if<(can_promote<T,U>::value
&& (is_dual<T>::value || is_dual<U>::value))>::type>
{
typedef std::complex<typename promote<T,U>::type> type;
};
template <class T, class U>
struct promote<std::complex<T>, U,
typename std::enable_if<(can_promote<T,U>::value
&& (is_dual<T>::value || is_dual<U>::value)
&& !is_complex<U>::value)>::type>
{
typedef std::complex<typename promote<T,U>::type> type;
};
template <class T, class U>
struct promote<U, std::complex<T>,
typename std::enable_if<(can_promote<T,U>::value
&& (is_dual<T>::value || is_dual<U>::value)
&& !is_complex<U>::value)>::type>
{
typedef std::complex<typename promote<T,U>::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 <class T>
struct is_arithmetic<duals::dual<T>> : is_arithmetic<T> {};
#endif // CPPDUALS_ENABLE_IS_ARITHMETIC
/// Duals are compound types.
template <class T>
struct is_compound<duals::dual<T>> : true_type {};
// Modification of std::numeric_limits<> per
// C++03 17.4.3.1/1, and C++11 18.3.2.3/1.
template <class T>
struct numeric_limits<duals::dual<T>> : numeric_limits<T> {
static constexpr bool is_specialized = true;
static constexpr duals::dual<T> min NOMACRO () { return numeric_limits<T>::min NOMACRO (); }
static constexpr duals::dual<T> lowest() { return numeric_limits<T>::lowest(); }
static constexpr duals::dual<T> max NOMACRO () { return numeric_limits<T>::max NOMACRO (); }
static constexpr duals::dual<T> epsilon() { return numeric_limits<T>::epsilon(); }
static constexpr duals::dual<T> round_error() { return numeric_limits<T>::round_error(); }
static constexpr duals::dual<T> infinity() { return numeric_limits<T>::infinity(); }
static constexpr duals::dual<T> quiet_NaN() { return numeric_limits<T>::quiet_NaN(); }
static constexpr duals::dual<T> signaling_NaN() { return numeric_limits<T>::signaling_NaN(); }
static constexpr duals::dual<T> denorm_min() { return numeric_limits<T>::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<X>::depth == \
(int)duals::dual_traits<T>::depth, int>::type = 0, \
typename std::enable_if<can_promote<T,X>::value,int>::type = 0
// Both T and U are wrapped in a dual<>
#define CPPDUALS_ENABLE_SAME_DEPTH_AND_COMMON_T(T,U) \
typename std::enable_if<(int)duals::dual_traits<T>::depth == \
(int)duals::dual_traits<U>::depth, int>::type = 0, \
typename std::enable_if<can_promote<T,U>::value,int>::type = 0, \
typename common_t = dual<typename duals::promote<T,U>::type>
// T is wrapped in a dual<>, U may or may not be.
#define CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U) \
typename std::enable_if<((int)duals::dual_traits<T>::depth >= \
(int)duals::dual_traits<U>::depth), int>::type = 0, \
typename std::enable_if<can_promote<dual<T>,U>::value,int>::type = 0, \
typename common_t = typename duals::promote<dual<T>, U>::type
// T is wrapped in complex<dual<>>
#define CPPDUALS_ENABLE_LEQ_DEPTH_AND_CX_COMMON_T(T,U) \
typename std::enable_if<((int)duals::dual_traits<T>::depth >= \
(int)duals::dual_traits<U>::depth), int>::type = 0, \
typename std::enable_if<can_promote<std::complex<dual<T>>,U>::value,int>::type = 0, \
typename common_t = typename duals::promote<std::complex<dual<T> >,U>::type
#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 T>
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.
#pragma warning (disable : 4244) /* floats are not used in HICUM */
template<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X),
CPPDUALS_ENABLE_IF(!is_complex<X>::value)>
dual(const dual<X> & x)
: _real(x.rpart()), _dual(x.dpart()) {}
/// Cast to a complex<dual<>> with real part equal to *this.
operator std::complex<dual<T>>() { return std::complex<dual<T>>(*this); }
/// Explicit cast to an arithmetic type retains the rpart()
template <class X,
CPPDUALS_ENABLE_IF(std::is_arithmetic<X>::value && !is_dual<X>::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<T> operator-() const { return dual<T>(-_real, -_dual); }
/// Unary nothing
dual<T> operator+() const { return *this; }
/// Assignment of `value_type` assigns the real part and zeros the dual part.
dual<T> & operator= (const T & x) { _real = x; _dual = value_type(); return *this; }
/// Add a relatively-scalar to this dual.
dual<T> & operator+=(const T & x) { _real += x; return *this; }
/// Subtract a relatively-scalar from this dual.
dual<T> & operator-=(const T & x) { _real -= x; return *this; }
/// Multiply a relatively-scalar with this dual.
dual<T> & operator*=(const T & x) { _real *= x; _dual *= x; return *this; }
/// Divide this dual by relatively-scalar.
dual<T> & operator/=(const T & x) { _real /= x; _dual /= x; return *this; }
//dua & operator=(const dual & x) { _real = x.rpart(); _dual = x.dpart(); }
template<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X)>
dual<T> & operator= (const dual<X> & x) { _real = x.rpart(); _dual = x.dpart(); return *this; }
/// Add a dual of the same depth to this dual.
template<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X)>
dual<T> & operator+=(const dual<X> & x) { _real += x.rpart(); _dual += x.dpart(); return *this; }
/// Subtract a dual of the same depth from this dual.
template<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X)>
dual<T> & operator-=(const dual<X> & x) { _real -= x.rpart(); _dual -= x.dpart(); return *this; }
/// Multiply this dual with a dual of same of lower depth.
template<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X)>
dual<T> & operator*=(const dual<X> & 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<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X)>
dual<T> & operator/=(const dual<X> & 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<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X)>
bool operator ==(const dual<X> &b) const { return _real == b.rpart(); }
/// Compare this dual with another dual, comparing real parts for inequality.
template<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X)>
bool operator !=(const dual<X> &b) const { return _real != b.rpart(); }
/// Compare real part against real part of b
template<class X, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,X)>
bool operator <(const dual<X> &b) const { return _real < b.rpart(); }
/// Compare real part against real part of b
template<class X, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,X)>
bool operator >(const dual<X> &b) const { return _real > b.rpart(); }
/// Compare real part against real part of b
template<class X, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,X)>
bool operator <=(const dual<X> &b) const { return _real <= b.rpart(); }
/// Compare real part against real part of b
template<class X, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,X)>
bool operator >=(const dual<X> &b) const { return _real >= b.rpart(); }
#endif
};
/// Get the dual's real part.
template <class T> T rpart(const dual<T> & x) { return x.rpart(); }
/// Get the dual's dual part.
template <class T> T dpart(const dual<T> & x) { return x.dpart(); }
/// R-part of complex<dual<>> is non-dual complex<> (not to be confused with real())
template <class T> std::complex<T> rpart(const std::complex<dual<T>> & x)
{ return std::complex<T>(x.real().rpart(), x.imag().rpart()); }
/// Dual part of complex<dual<>> is complex<>
template <class T> std::complex<T> dpart(const std::complex<dual<T>> & x)
{ return std::complex<T>(x.real().dpart(), x.imag().dpart()); }
/// Get a non-dual's real part.
template <class T,
CPPDUALS_ENABLE_IF((std::is_arithmetic<T>::value &&
!std::is_compound<T>::value) || is_complex<T>::value)>
T rpart(const T & x) { return x; }
/// Get a non-dual's dual part := zero.
template <class T,
CPPDUALS_ENABLE_IF((std::is_arithmetic<T>::value &&
!std::is_compound<T>::value) || is_complex<T>::value)>
T dpart(const T & ) { return T(0); }
#ifndef PARSED_BY_DOXYGEN
/// Dual +-*/ ops with another entity
#define CPPDUALS_BINARY_OP(op) \
template<class T, class U, CPPDUALS_ENABLE_SAME_DEPTH_AND_COMMON_T(T,U)> \
common_t \
operator op(const dual<T> & z, const dual<U> & w) { \
common_t x(z); \
return x op##= w; \
} \
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U), \
CPPDUALS_ENABLE_IF(!std::is_same<U,std::complex<dual<T>>>::value)> \
common_t \
operator op(const dual<T> & z, const U & w) { \
common_t x(z); \
return x op##= w; \
} \
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U), \
CPPDUALS_ENABLE_IF(!std::is_same<U,std::complex<dual<T>>>::value)> \
common_t \
operator op(const U & z, const dual<T> & w) { \
common_t x(z); \
return x op##= w; \
} \
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_CX_COMMON_T(T,U), \
CPPDUALS_ENABLE_IF(!std::is_same<U,std::complex<dual<T>>>::value)> \
common_t \
operator op(const std::complex<dual<T>> & z, const U & w) { \
common_t x(z); \
return x op##= w; \
} \
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_CX_COMMON_T(T,U), \
CPPDUALS_ENABLE_IF(!std::is_same<U,std::complex<dual<T>>>::value)> \
common_t \
operator op(const U & z, const std::complex<dual<T>> & 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<class T, class U, CPPDUALS_ENABLE_SAME_DEPTH_AND_COMMON_T(T,U)> \
bool \
operator op(const dual<T> & a, const dual<U> & b) { \
return a.rpart() op b.rpart(); \
} \
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U), \
CPPDUALS_ENABLE_IF(!is_complex<U>::value)> \
bool \
operator op(const U & a, const dual<T> & b) { \
return a op b.rpart(); \
} \
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U), \
CPPDUALS_ENABLE_IF(!is_complex<U>::value)> \
bool \
operator op(const dual<T> & 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
namespace randos {
// Random real value between a and b.
template<class T,
typename dist = std::uniform_real_distribution<T>,
CPPDUALS_ENABLE_IF(!is_complex<T>::value && !is_dual<T>::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<class T,
typename dist = std::uniform_real_distribution<T>,
CPPDUALS_ENABLE_IF(!is_complex<T>::value && !is_dual<T>::value)>
T random2(T a = T(0), T b = T(1)) { return random<T,dist>(a,b); }
// Helper class for testing - also random value in dual part.
template <class DT,
CPPDUALS_ENABLE_IF(is_dual<DT>::value)>
DT random2(DT a = DT(0,0), DT b = DT(1,1)) {
using randos::random;
return DT(a.rpart() + random2<typename DT::value_type>() * (b.rpart() - a.rpart()),
a.dpart() + random2<typename DT::value_type>() * (b.dpart() - a.dpart()));
}
// Helper class for testing - also random value in dual part of the complex.
template<class CT,
CPPDUALS_ENABLE_IF(is_complex<CT>::value)>
CT random2(CT a = CT(0,0), CT b = CT(1,1)) {
return CT(a.real() + random2<typename CT::value_type>() * (b.real() - a.real()),
a.imag() + random2<typename CT::value_type>() * (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 <class DT,
typename dist = std::uniform_real_distribution<typename DT::value_type>,
CPPDUALS_ENABLE_IF(is_dual<DT>::value)>
DT random(DT a = DT(0,0), DT b = DT(1,0)) {
using randos::random;
return DT(random<typename DT::value_type,dist>(a.rpart(),b.rpart()),
random<typename DT::value_type,dist>(a.dpart(),b.dpart()));
}
/// Complex Conjugate of a dual is just the dual.
template<class T> dual<T> conj(const dual<T> & 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<T>.
template<class T, CPPDUALS_ENABLE_IF(!is_dual<T>::value &&
!is_complex<T>::value &&
std::is_arithmetic<T>::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<class T> dual<T> dconj(const dual<T> & x) {
return dual<T>(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<class T> std::complex<T> dconj(const std::complex<T> & x) {
return std::complex<T>(dconj(x.real()), dconj(x.imag()));
}
/// Conjugate a thing that's not dual and not complex.
template<class T, CPPDUALS_ENABLE_IF(!is_dual<T>::value &&
!is_complex<T>::value &&
std::is_arithmetic<T>::value)>
T dconj(const T & x) { return x; }
/// Exponential e^x
template<class T> dual<T> exp(const dual<T> & x) {
using std::exp;
T v = exp(x.rpart());
return dual<T>(v,
v * x.dpart());
}
/// Natural log ln(x)
template<class T> dual<T> log(const dual<T> & x) {
using std::log;
T v = log(x.rpart());
if (x.dpart() == T(0))
return v;
else
return dual<T>(v, x.dpart() / x.rpart());
}
template<class T> dual<T> log10(const dual<T> & x) {
using std::log;
return log(x) / log(static_cast<T>(10));
}
template<class T> dual<T> log2(const dual<T> & x) {
using std::log;
return log(x) / log(static_cast<T>(2));
}
template<class T, class U, CPPDUALS_ENABLE_SAME_DEPTH_AND_COMMON_T(T,U)>
common_t
pow(const dual<T> & x, const dual<U> & y) {
using std::pow;
using std::log;
T v = pow(x.rpart(), y.rpart());
return common_t(v,
x.dpart() * y.rpart() * pow(x.rpart(), y.rpart() - T(1)) +
y.dpart() * log(x.rpart()) * v);
}
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U)>
common_t
pow(const dual<T> & x, const U & y) {
using std::pow;
return common_t(pow(x.rpart(), y),
x.dpart() * y * pow(x.rpart(), y - U(1)));
}
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U)>
common_t
pow(const U & x, const dual<T> & y) {
return pow(common_t(x), y);
}
namespace utils {
template <typename T> int sgn(T val) {
return (T(0) < val) - (val < T(0));
}
}
template<class T> dual<T> abs(const dual<T> & x) {
using std::abs;
return dual<T>(abs(x.rpart()),
x.dpart() * utils::sgn(x.rpart()));
}
template<class T> dual<T> fabs(const dual<T> & x) {
using std::fabs;
return dual<T>(fabs(x.rpart()),
x.dpart() * utils::sgn(x.rpart()));
}
#if 0
template<class T> dual<T> abs2(const dual<T> & x) {
using std::abs;
return dual<T>(x.rpart() * x.rpart(),
xxx x.dpart() * utils::sgn(x.rpart()));
}
#endif
template<class T> duals::dual<T> copysign(const duals::dual<T> & x, const duals::dual<T> & y) {
using std::copysign;
T r = copysign(x.rpart(), y.rpart());
return duals::dual<T>(r, r == x.rpart() ? x.dpart() : -x.dpart());
}
template<class T> duals::dual<T> hypot(const duals::dual<T> & x, const duals::dual<T> & y) {
return sqrt(x*x + y*y);
}
template<class T> duals::dual<T> scalbn(const duals::dual<T> & arg, int ex) {
return arg * std::pow((T)2, ex);
}
template<class T> duals::dual<T> (fmax)(const duals::dual<T> & x, const duals::dual<T> & y) {
return x.rpart() > y.rpart() ? x : y;
}
template<class T> duals::dual<T> (fmin)(const duals::dual<T> & x, const duals::dual<T> & y) {
return x.rpart() <= y.rpart() ? x : y;
}
template<class T> duals::dual<T> logb(const duals::dual<T> & x) {
return duals::log2(x);
}
template<class T> int (fpclassify)(const duals::dual<T> & d) { using std::fpclassify; return (fpclassify)(d.rpart()); }
template<class T> bool (isfinite)(const duals::dual<T> & d) { using std::isfinite; return (isfinite)(d.rpart()); }
template<class T> bool (isnormal)(const duals::dual<T> & d) { using std::isnormal; return (isnormal)(d.rpart()); }
template<class T> bool (isinf)(const duals::dual<T> & d) { using std::isinf; return (isinf)(d.rpart()); }
template<class T> bool (isnan)(const duals::dual<T> & d) { using std::isnan; return (isnan)(d.rpart()); }
template<class T> bool (signbit)(const duals::dual<T> & d) { using std::signbit; return (signbit)(d.rpart()); }
template<class T> dual<T> sqrt(const dual<T> & x) {
using std::sqrt;
T v = sqrt(x.rpart());
if (x.dpart() == T(0))
return v;
else
return dual<T>(v, x.dpart() / (T(2) * v) );
}
template<class T> dual<T> cbrt(const dual<T> & x) {
using std::cbrt;
T v = cbrt(x.rpart());
if (x.dpart() == T(0))
return v;
else
return dual<T>(v, x.dpart() / (T(3) * v * v) );
}
template<class T> dual<T> sin(const dual<T> & x) {
using std::sin;
using std::cos;
return dual<T>(sin(x.rpart()),
x.dpart() * cos(x.rpart()));
}
template<class T> dual<T> cos(const dual<T> & x) {
using std::cos;
using std::sin;
return dual<T>(cos(x.rpart()),
-sin(x.rpart()) * x.dpart());
}
template<class T> dual<T> tan(const dual<T> & x) {
using std::tan;
T v = tan(x.rpart());
return dual<T>(v, x.dpart() * (v*v + 1));
}
template<class T> dual<T> asin(const dual<T> & x) {
using std::asin;
using std::sqrt;
T v = asin(x.rpart());
if (x.dpart() == T(0))
return v;
else
return dual<T>(v, x.dpart() / sqrt(1 - x.rpart()*x.rpart()));
}
template<class T> dual<T> acos(const dual<T> & x) {
using std::acos;
using std::sqrt;
T v = acos(x.rpart());
if (x.dpart() == T(0))
return v;
else
return dual<T>(v, -x.dpart() / sqrt(1 - x.rpart()*x.rpart()));
}
template<class T> dual<T> atan(const dual<T> & x) {
using std::atan;
T v = atan(x.rpart());
if (x.dpart() == T(0))
return v;
else
return dual<T>(v, x.dpart() / (1 + x.rpart()*x.rpart()));
}
template<class T> dual<T> atan2(const dual<T> & x, const dual<T> & y) {
using std::atan2;
T v = atan2(x.rpart(), y.rpart());
if (x.dpart() == T(0))
return v;
else
return dual<T>(v, x.dpart() / (1 + x.rpart()*x.rpart()));
}
// TODO
template<class T> dual<T> sinh(const dual<T> & x);
template<class T> dual<T> cosh(const dual<T> & x);
template<class T> dual<T> tanh(const dual<T> & x);
template<class T> dual<T> asinh(const dual<T> & x);
template<class T> dual<T> acosh(const dual<T> & x);
template<class T> dual<T> atanh(const dual<T> & x);
template<class T> dual<T> log1p(const dual<T> & x);
template<class T> dual<T> expm1(const dual<T> & x);
/// The error function. Make sure to `#include <math.h>` before
/// `#include <duals/dual>` to use this function.
template<class T> dual<T> erf(const dual<T> & x) {
using std::erf;
using std::sqrt;
using std::pow;
using std::exp;
return dual<T>(erf(x.rpart()),
x.dpart() * T(2)/sqrt(T(MY_PI)) * exp(-pow(x.rpart(),T(2))));
}
/// Error function complement (1 - erf()).
template<class T> dual<T> erfc(const dual<T> & x) {
using std::erfc;
using std::sqrt;
using std::pow;
using std::exp;
return dual<T>(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<class T> dual<T> tgamma(const dual<T> & 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<T>::digits / 3)));
T w((tgamma(x.rpart()+h) - tgamma(x.rpart()-h))/(2*h));
errno = errno_saved;
return dual<T>(v, x.dpart() * w);
}
}
/// Log of absolute value of gamma function. Approximation of the dual part.
template<class T> dual<T> lgamma(const dual<T> & 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<T>::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<T>(v, x.dpart() * w);
}
}
/// Putto operator
template<class T, class _CharT, class _Traits>
std::basic_ostream<_CharT, _Traits> &
operator<<(std::basic_ostream<_CharT, _Traits> & os, const dual<T> & 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<typename std::decay<T>::type,float>::value ? "f" :
std::is_same<typename std::decay<T>::type,double>::value ? "" :
std::is_same<typename std::decay<T>::type,long double>::value ? "l" : "")
<< ")";
return os << s.str();
}
/// Stream reader
template<class T, class CharT, class Traits>
std::basic_istream<CharT, Traits> &
operator>>(std::basic_istream<CharT, Traits> & is, dual<T> & 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<typename std::decay<T>::type,float>::value) ||
(c == 'l' && !std::is_same<typename std::decay<T>::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<T>(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<T>(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<T>(r, T(0));
else
is.setstate(std::ios_base::failbit);
}
}
else
is.setstate(std::ios_base::failbit);
return is;
}
#if __cpp_user_defined_literals >= 200809
/// Dual number literals in namespace duals::literals
inline namespace literals
{
using duals::dual;
constexpr dual<float> operator""_ef(long double du)
{
return { 0.0f, static_cast<float>(du) };
}
constexpr dual<float> operator""_ef(unsigned long long du)
{
return { 0.0f, static_cast<float>(du) };
}
constexpr dual<double> operator""_e(long double du)
{
return { 0.0, static_cast<double>(du) };
}
constexpr dual<double> operator""_e(unsigned long long du)
{
return { 0.0, static_cast<double>(du) };
}
constexpr dual<long double> operator""_el(long double du)
{
return { 0.0l, du };
}
constexpr dual<long double> operator""_el(unsigned long long du)
{
return { 0.0l, static_cast<long double>(du) };
}
}
#endif
typedef dual<float> dualf;
typedef dual<double> duald;
typedef dual<long double> dualld;
typedef dual<dualf> hyperdualf;
typedef dual<duald> hyperduald;
typedef dual<dualld> hyperdualld;
typedef std::complex<dualf> cdualf;
typedef std::complex<duald> cduald;
typedef std::complex<dualld> cdualld;
} // namespace duals
#ifdef CPPDUALS_LIBFMT
#include <fmt/format.h>
/// 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<double> 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 <typename T, typename Char>
struct fmt::formatter<duals::dual<T>,Char> : public fmt::formatter<T,Char>
{
typedef fmt::formatter<T,Char> base;
enum style { expr, star, pair } style_ = expr;
internal::dynamic_format_specs<Char> specs_;
FMT_CONSTEXPR auto parse(format_parse_context & ctx) -> decltype(ctx.begin()) {
using handler_type = internal::dynamic_specs_handler<format_parse_context>;
auto type = internal::type_constant<T, Char>::value;
internal::specs_checker<handler_type> handler(handler_type(specs_, ctx), type);
auto it = ctx.begin();
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;
}
parse_format_specs(ctx.begin(), ctx.end(), handler);
return base::parse(ctx);
}
template <typename FormatCtx>
auto format(const duals::dual<T> & x, FormatCtx & ctx) -> decltype(ctx.out()) {
format_to(ctx.out(), "(");
if (style_ == style::pair) {
base::format(x.rpart(), ctx);
format_to(ctx.out(), ",");
base::format(x.dpart(), ctx);
return format_to(ctx.out(), ")");
}
if (x.rpart() || !x.dpart())
base::format(x.rpart(), ctx);
if (x.dpart()) {
if (x.rpart() && x.dpart() >= 0 && specs_.sign != sign::plus)
format_to(ctx.out(), "+");
base::format(x.dpart(), ctx);
if (style_ == style::star)
format_to(ctx.out(), "*e");
else
format_to(ctx.out(), "_e");
if (std::is_same<typename std::decay<T>::type,float>::value) format_to(ctx.out(), "f");
if (std::is_same<typename std::decay<T>::type,long double>::value) format_to(ctx.out(), "l");
}
return format_to(ctx.out(), ")");
}
};
#endif
#ifdef CPPDUALS_LIBFMT_COMPLEX
#ifndef CPPDUALS_LIBFMT
#include <fmt/format.h>
#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<double> using the specs {:g}
/// | {:$g}, {:*g}, or {:,g}, respectively. (this implementation is a
/// bit hacky - glad for cleanups).
///
template <typename T, typename Char>
struct fmt::formatter<std::complex<T>,Char> : public fmt::formatter<T,Char>
{
typedef fmt::formatter<T,Char> base;
enum style { expr, star, pair } style_ = expr;
internal::dynamic_format_specs<Char> specs_;
FMT_CONSTEXPR auto parse(format_parse_context & ctx) -> decltype(ctx.begin()) {
using handler_type = internal::dynamic_specs_handler<format_parse_context>;
auto type = internal::type_constant<T, Char>::value;
internal::specs_checker<handler_type> handler(handler_type(specs_, ctx), type);
auto it = ctx.begin();
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;
}
parse_format_specs(ctx.begin(), ctx.end(), handler);
//todo: fixup alignment
return base::parse(ctx);
}
template <typename FormatCtx>
auto format(const std::complex<T> & x, FormatCtx & ctx) -> 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);
return format_to(ctx.out(), ")");
}
if (x.real() || !x.imag())
base::format(x.real(), ctx);
if (x.imag()) {
if (x.real() && x.imag() >= 0 && specs_.sign != sign::plus)
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<typename std::decay<T>::type,float>::value) format_to(ctx.out(), "f");
if (std::is_same<typename std::decay<T>::type,long double>::value) format_to(ctx.out(), "l");
}
return format_to(ctx.out(), ")");
}
};
#endif
#ifdef _LIBCPP_BEGIN_NAMESPACE_STD
_LIBCPP_BEGIN_NAMESPACE_STD
#else
namespace std {
#endif
#ifndef PARSED_BY_DOXYGEN
#define make_math(T) \
inline T (frexp)(const duals::dual<T> & arg, int* exp ) { return (frexp)(arg.rpart(), exp); } \
inline duals::dual<T> (ldexp)(const duals::dual<T> & arg, int exp ) { return arg * std::pow((T)2,exp); } \
inline T (trunc)(const duals::dual<T> & d) { return (trunc)(d.rpart()); } \
inline T (floor)(const duals::dual<T> & d) { return (floor)(d.rpart()); } \
inline T (ceil)(const duals::dual<T> & d) { return (ceil)(d.rpart()); } \
inline T (round)(const duals::dual<T> & d) { return (round)(d.rpart()); } \
inline int (fpclassify)(const duals::dual<T> & d) { return (fpclassify)(d.rpart()); } \
inline bool (isfinite)(const duals::dual<T> & d) { return (isfinite)(d.rpart()); } \
inline bool (isnormal)(const duals::dual<T> & d) { return (isnormal)(d.rpart()); } \
inline bool (isinf)(const duals::dual<T> & d) { return (isinf)(d.rpart()); } \
inline bool (isnan)(const duals::dual<T> & d) { return (isnan)(d.rpart()); } \
make_math(float)
make_math(double)
make_math(long double)
#undef make_math
#endif // PARSED_BY_DOXYGEN
#ifdef _LIBCPP_BEGIN_NAMESPACE_STD
_LIBCPP_END_NAMESPACE_STD
#else
} // namespace std
#endif
#endif // CPPDUALS_DUAL