diff --git a/src/Magnum/Math/Dual.h b/src/Magnum/Math/Dual.h index 39536e0e5..feb48b08d 100644 --- a/src/Magnum/Math/Dual.h +++ b/src/Magnum/Math/Dual.h @@ -37,6 +37,10 @@ namespace Magnum { namespace Math { +namespace Implementation { + CORRADE_HAS_TYPE(IsDual, decltype(std::declval().dual())); +} + /** @brief Dual number @tparam T Underlying data type @@ -165,22 +169,52 @@ template class Dual { * @f[ * \hat a \hat b = a_0 b_0 + \epsilon (a_0 b_\epsilon + a_\epsilon b_0) * @f] + * @see @ref operator*(const U&) const, + * @ref operator*(const T&, const Dual&) */ template auto operator*(const Dual& other) const -> Dual()*std::declval())> { return {_real*other._real, _real*other._dual + _dual*other._real}; } + /** + * @brief Multiply by real number + * + * Equivalent to the above assuming that @f$ b_\epsilon = 0 @f$. + * @f[ + * \hat a \hat b = a_0 b_0 + \epsilon (a_0 b_\epsilon + a_\epsilon b_0) = a_0 b_0 + \epsilon a_\epsilon b_0 + * @f] + * @see @ref operator*(const Dual&) const, + * @ref operator*(const T&, const Dual&) + */ + template::value, void>::type> Dual()*std::declval())> operator*(const U& other) const { + return {_real*other, _dual*other}; + } + /** * @brief Divide by dual number * * @f[ * \frac{\hat a}{\hat b} = \frac{a_0}{b_0} + \epsilon \frac{a_\epsilon b_0 - a_0 b_\epsilon}{b_0^2} * @f] + * @see @ref operator/(const U&) const */ template auto operator/(const Dual& other) const -> Dual()/std::declval())> { return {_real/other._real, (_dual*other._real - _real*other._dual)/(other._real*other._real)}; } + /** + * @brief Divide by real number + * + * Equivalent to the above assuming that @f$ b_\epsilon = 0 @f$. + * @f[ + * \frac{\hat a}{\hat b} = \frac{a_0}{b_0} + \epsilon \frac{a_\epsilon b_0 - a_0 b_\epsilon}{b_0^2} = \frac{a_0}{b_0} + \epsilon \frac{a_\epsilon}{b_0} + * @f] + * @see @ref operator/(const Dual&) const + */ + template::value, Dual()/std::declval())>>::type> V operator/(const U& other) const { + return {_real/other, _dual/other}; + } + /** * @brief Conjugated dual number * @@ -196,6 +230,19 @@ template class Dual { T _real, _dual; }; +/** @relates Dual +@brief Multiply real number by dual number + +Equivalent to @ref Dual::operator*(const Dual&) const assuming that +@f$ a_\epsilon = 0 @f$. +@f[ + \hat a \hat b = a_0 b_0 + \epsilon (a_0 b_\epsilon + a_\epsilon b_0) = a_0 b_0 + \epsilon a_0 b_\epsilon +@f] +*/ +template::value, Dual()*std::declval())>>::type> inline V operator*(const T& a, const Dual& b) { + return {a*b.real(), a*b.dual()}; +} + #ifndef DOXYGEN_GENERATING_OUTPUT #define MAGNUM_DUAL_SUBCLASS_IMPLEMENTATION(Type, Underlying, Multiplicable) \ Type operator-() const { \ @@ -218,8 +265,14 @@ template class Dual { Type operator*(const Math::Dual& other) const { \ return Math::Dual>::operator*(other); \ } \ + Type operator*(const Multiplicable& other) const { \ + return Math::Dual>::operator*(other); \ + } \ Type operator/(const Math::Dual& other) const { \ return Math::Dual>::operator/(other); \ + } \ + Type operator/(const Multiplicable& other) const { \ + return Math::Dual>::operator/(other); \ } /* DualComplex needs its own special implementation of multiplication/division */ @@ -241,6 +294,9 @@ template class Dual { template inline Type operator*(const Math::Dual& a, const Type& b) { \ return a*static_cast>&>(b); \ } \ + template inline Type operator*(const Multiplicable& a, const Type& b) { \ + return a*static_cast>&>(b); \ + } \ template inline Type operator/(const Math::Dual& a, const Type& b) { \ return a/static_cast>&>(b); \ } diff --git a/src/Magnum/Math/Test/DualTest.cpp b/src/Magnum/Math/Test/DualTest.cpp index 90ee2b9da..cff84eca2 100644 --- a/src/Magnum/Math/Test/DualTest.cpp +++ b/src/Magnum/Math/Test/DualTest.cpp @@ -45,6 +45,7 @@ struct DualTest: Corrade::TestSuite::Tester { void addSubtract(); void negated(); void multiplyDivide(); + void multiplyDivideScalar(); void multiplyDivideDifferentType(); void conjugated(); @@ -72,6 +73,7 @@ DualTest::DualTest() { &DualTest::addSubtract, &DualTest::negated, &DualTest::multiplyDivide, + &DualTest::multiplyDivideScalar, &DualTest::multiplyDivideDifferentType, &DualTest::conjugated, @@ -157,6 +159,15 @@ void DualTest::multiplyDivide() { CORRADE_COMPARE(c/b, a); } +void DualTest::multiplyDivideScalar() { + Dual a{1.5f, -4.0f}; + Dual b{-3.0f, 8.0f}; + + CORRADE_COMPARE(a*-2.0f, b); + CORRADE_COMPARE(-2.0f*a, b); + CORRADE_COMPARE(b/-2.0f, a); +} + void DualTest::multiplyDivideDifferentType() { DualVector2 a{{1.5f, 2.0f}, {-4.0f, 1.3f}}; Dual b{-2.0f, 0.5f}; @@ -211,6 +222,10 @@ void DualTest::subclassTypes() { CORRADE_VERIFY((std::is_same::value)); CORRADE_VERIFY((std::is_same::value)); CORRADE_VERIFY((std::is_same::value)); + + CORRADE_VERIFY((std::is_same::value)); + CORRADE_VERIFY((std::is_same::value)); + CORRADE_VERIFY((std::is_same::value)); } void DualTest::subclass() { @@ -235,6 +250,11 @@ void DualTest::subclass() { CORRADE_COMPARE(e*a, f); CORRADE_COMPARE(f/e, a); CORRADE_COMPARE(e/a, g); + + const DualVec2 h{Vector2{-3.0f, -4.0f}, Vector2{8.0f, -2.6f}}; + CORRADE_COMPARE(a*-2.0f, h); + CORRADE_COMPARE(-2.0f*a, h); + CORRADE_COMPARE(h/-2.0f, a); } void DualTest::debug() {