#pragma once #include namespace autoopt { template struct dual { T _x; T _dx; constexpr dual(T x = T{0}, T dx = T{0}) : _x(x), _dx(dx) {} // allow arbitrary deeply-nested dual construction template constexpr dual(U x) : _x(T(x)), _dx(T(0)) {} }; template constexpr dual operator+(const dual& a, const dual& b) { return dual(a._x + b._x, a._dx + b._dx); } template constexpr dual operator-(const dual& a) { return dual(-a._x, -a._dx); } template constexpr dual operator-(const dual& a, const dual& b) { return dual(a._x - b._x, a._dx - b._dx); } template constexpr dual operator*(const dual& a, const dual& b) { return dual(a._x * b._x, a._x * b._dx + a._dx * b._x); } template constexpr dual operator/(const dual& a, const dual& b) { return dual(a._x / b._x, (a._dx * b._x - a._x * b._dx) / (b._x * b._x)); } template constexpr bool operator==(const dual& a, const dual& b) { return a._x == b._x; } template constexpr bool operator!=(const dual& a, const dual& b) { return a._x != b._x; } template constexpr bool operator<(const dual& a, const dual& b) { return a._x < b._x; } template constexpr bool operator<=(const dual& a, const dual& b) { return a._x <= b._x; } template constexpr bool operator>(const dual& a, const dual& b) { return a._x > b._x; } template constexpr bool operator>=(const dual& a, const dual& b) { return a._x >= b._x; } } // namespace autoopt namespace std { using autoopt::dual; // forward declarations of standard functions template constexpr dual abs(const dual& a); template constexpr dual exp(const dual& a); template constexpr dual log(const dual& a); template constexpr dual pow(const dual& a, const dual& b); template constexpr dual sqrt(const dual& a); // forward declarations of trigonometric functions template constexpr dual sin(const dual& a); template constexpr dual cos(const dual& a); template constexpr dual tan(const dual& a); template constexpr dual asin(const dual& a); template constexpr dual acos(const dual& a); template constexpr dual atan(const dual& a); template constexpr dual atan2(const dual& y, const dual& x); // forward declarations of hyperbolic functions template constexpr dual sinh(const dual& a); template constexpr dual cosh(const dual& a); template constexpr dual tanh(const dual& a); template constexpr dual asinh(const dual& a); template constexpr dual acosh(const dual& a); template constexpr dual atanh(const dual& a); // standard functions template constexpr dual abs(const dual& a) { return dual(std::abs(a._x), (a._x >= T(0) ? T(1) : T(-1)) * a._dx); } template constexpr dual exp(const dual& a) { T exp_x = std::exp(a._x); return dual(exp_x, exp_x * a._dx); } template constexpr dual log(const dual& a) { return dual(std::log(a._x), (T(1) / a._x) * a._dx); } template constexpr dual pow(const dual& a, const dual& b) { return std::exp(b * std::log(a)); } template constexpr dual sqrt(const dual& a) { T sqrt_x = std::sqrt(a._x); return dual(sqrt_x, (T(1) / (T(2) * sqrt_x)) * a._dx); } // trigonometric functions template constexpr dual sin(const dual& a) { return dual(std::sin(a._x), std::cos(a._x) * a._dx); } template constexpr dual cos(const dual& a) { return dual(std::cos(a._x), -std::sin(a._x) * a._dx); } template constexpr dual tan(const dual& a) { T cos_x = std::cos(a._x); return dual(std::tan(a._x), (T(1) / (cos_x * cos_x)) * a._dx); } template constexpr dual asin(const dual& a) { return dual(std::asin(a._x), (T(1) / std::sqrt(T(1) - a._x * a._x)) * a._dx); } template constexpr dual acos(const dual& a) { return dual(std::acos(a._x), (-T(1) / std::sqrt(T(1) - a._x * a._x)) * a._dx); } template constexpr dual atan(const dual& a) { return dual(std::atan(a._x), (T(1) / (T(1) + a._x * a._x)) * a._dx); } template constexpr dual atan2(const dual& y, const dual& x) { return dual(std::atan2(y._x, x._x), (x._x * y._dx - y._x * x._dx) / (x._x * x._x + y._x * y._x)); } // hyperbolic functions template constexpr dual sinh(const dual& a) { return dual(std::sinh(a._x), std::cosh(a._x) * a._dx); } template constexpr dual cosh(const dual& a) { return dual(std::cosh(a._x), std::sinh(a._x) * a._dx); } template constexpr dual tanh(const dual& a) { T cosh_x = std::cosh(a._x); return dual(std::tanh(a._x), (T(1) / (cosh_x * cosh_x)) * a._dx); } template constexpr dual asinh(const dual& a) { return dual(std::asinh(a._x), (T(1) / std::sqrt(a._x * a._x + T(1))) * a._dx); } template constexpr dual acosh(const dual& a) { return dual(std::acosh(a._x), (T(1) / std::sqrt(a._x * a._x - T(1))) * a._dx); } template constexpr dual atanh(const dual& a) { return dual(std::atanh(a._x), (T(1) / (T(1) - a._x * a._x)) * a._dx); } } // namespace std