Files
ngspice/src/include/cppduals/duals/dual
T

1390 lines
48 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//===-- duals/dual - Dual number class --------------------------*- C++ -*-===//
//
// Part of the cppduals project.
// https://tesch1.gitlab.io/cppduals
//
// (c)2019 Michael Tesch. [email protected]
//
// 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. 116.](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.
#if defined (_MSC_VER)
#pragma warning (disable : 4244) /* floats are not used in HICUM */
#endif
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