commit aed9381e0a2e7dec03900d148fc833f53a509f83 Author: Andrea Bontempi Date: Thu Apr 4 15:26:31 2019 +0200 First commit diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9f3d6ad --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 2.6) +project(quaternion) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") + +find_package(Boost COMPONENTS unit_test_framework system REQUIRED) +include_directories (${Boost_INCLUDE_DIRS}) + +add_executable(quaternion_example example.cpp Quaternion.h) + +add_executable(quaternion_test test.cpp Quaternion.h) + +target_link_libraries(quaternion_test ${Boost_LIBRARIES}) + +add_test(NAME quaternion_test WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND ${PROJECT_BINARY_DIR}/quaternion_test) + +enable_testing() diff --git a/Quaternion.h b/Quaternion.h new file mode 100644 index 0000000..b768893 --- /dev/null +++ b/Quaternion.h @@ -0,0 +1,384 @@ +/* + * Copyright © 2019 Andrea Bontempi All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * - Neither the name of Andrea Bontempi nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef QUATER_H +#define QUATER_H + +#include +#include +#include + +template +class Quaternion { + +private: + + T n, ni, nj, nk; + +public: + + using value_type = T; ///< value_type trait for STL compatibility + + /** + * Default constuctor + */ + Quaternion(const T& n = static_cast(0), const T& ni = static_cast(0), const T& nj = static_cast(0), const T& nk = static_cast(0)) + : n(n), ni(ni), nj(nj), nk(nk) {} + + /** + * Specialized constructor for std::complex + */ + Quaternion(const std::complex& complex_a, const std::complex& complex_b = std::complex(static_cast(0), static_cast(0))) + : n(complex_a.real()), ni(complex_a.imag()), nj(complex_b.real()), nk(complex_b.imag()) {} + + /** + * Copy constructor + */ + template + Quaternion(const Quaternion& rhs) + : n(rhs.a()), ni(rhs.b()), nj(rhs.c()), nk(rhs.d()) {} + + /** + * Default copy assignment operator + */ + template + Quaternion& operator=(const Quaternion& rhs) { + this->n = rhs.a(); + this->ni = rhs.b(); + this->nj = rhs.c(); + this->nk = rhs.d(); + } + + /** + * Specialized copy assignment operator for std::complex + */ + template + Quaternion& operator=(const std::complex rhs) { + this->n = rhs.real(); + this->ni = rhs.imag(); + this->nj = static_cast(0); + this->nk = static_cast(0); + } + + T a() const { + return this->n; + } + + T b() const { + return this->ni; + } + + T c() const { + return this->nj; + } + + T d() const { + return this->nk; + } + + std::complex complex_a() const { + return {this->n, this->ni}; + } + + std::complex complex_b() const { + return {this->nj, this->nk}; + } + + T real() const { + return this->n; + } + + Quaternion unreal() const { + return {static_cast(0), this->ni, this->nj, this->nk}; + } + +}; + +namespace std { + + /** + * Norm of quaternion + */ + template + T norm(const Quaternion& quat) { + return (quat.a() * quat.a()) + (quat.b() * quat.b()) + (quat.c() * quat.c()) + (quat.d() * quat.d()); + } + + /** + * Implementation of abs with float sqrt + */ + float abs(const Quaternion& quat) { + return std::sqrt(std::norm(quat)); + } + + /** + * Implementation of abs with double sqrt + */ + double abs(const Quaternion& quat) { + return std::sqrt(std::norm(quat)); + } + + /** + * Implementation of abs with long double sqrt + */ + long double abs(const Quaternion& quat) { + return std::sqrt(std::norm(quat)); + } + + /** + * The conjugate of quaternion + */ + template + Quaternion conj(const Quaternion& quat) { + return {quat.a(), quat.b() * -1, quat.c() * -1, quat.d() * -1}; + } + + /** + * Is not a number? + */ + template + bool isnan(Quaternion quat) { + return std::isnan(quat.a()) || std::isnan(quat.b()) || std::isnan(quat.c()) || std::isnan(quat.d()); + } + + /** + * Is infinite? + */ + template + bool isinf(Quaternion quat) { + return std::isinf(quat.a()) || std::isinf(quat.b()) || std::isinf(quat.c()) || std::isinf(quat.d()); + } + + /** + * Is finite? + */ + template + bool isfinite(Quaternion quat) { + return std::isfinite(quat.a()) && std::isfinite(quat.b()) && std::isfinite(quat.c()) && std::isfinite(quat.d()); + } + +} + +/** + * Add operator between two quaternios. + */ +template +auto operator+(const Quaternion<_tA>& lhs, const Quaternion<_tB>& rhs) -> Quaternion { + return {lhs.a() + rhs.a(), lhs.b() + rhs.b(), lhs.c() + rhs.c(), lhs.d() + rhs.d()}; +} + +/** + * Add operator between quaternion and std::complex. + */ +template +auto operator+(const Quaternion<_tA>& lhs, const std::complex<_tB>& rhs) -> Quaternion { + return {lhs.a() + rhs.real(), lhs.b() + rhs.imag(), lhs.c(), lhs.d()}; +} + +/** + * Add operator between std::complex and quaternion. + */ +template +auto operator+(const std::complex<_tB>& lhs, const Quaternion<_tA>& rhs) -> Quaternion { + return operator+(rhs, lhs); +} + +/** + * Add operator between quaternion and scalar. + */ +template +auto operator+(const Quaternion<_tA>& lhs, const _tB& rhs) -> Quaternion { + return {lhs.a() + rhs, lhs.b(), lhs.c(), lhs.d()}; +} + +/** + * Add operator between scalar and quaternion. + */ +template +auto operator+(const _tA& lhs, const Quaternion<_tB>& rhs) -> Quaternion { + return operator+(rhs, lhs); +} + +/** + * Sub operator between two quaternions. + */ +template +auto operator-(const Quaternion<_tA>& lhs, const Quaternion<_tB>& rhs) -> Quaternion { + return {lhs.a() - rhs.a(), lhs.b() - rhs.b(), lhs.c() - rhs.c(), lhs.d() - rhs.d()}; +} + +/** + * Sub operator between quaternion and std:complex. + */ +template +auto operator-(const Quaternion<_tA>& lhs, const std::complex<_tB>& rhs) -> Quaternion { + return {lhs.a() - rhs.real(), lhs.b() - rhs.imag(), lhs.c(), lhs.d()}; +} + +/** + * Sub operator between std:complex and quaternion. + */ +template +auto operator-(const std::complex<_tB>& lhs, const Quaternion<_tA>& rhs) -> Quaternion { + return {lhs.real() - rhs.a(), lhs.imag() - rhs.b(), -rhs.c(), -rhs.d()}; +} + +/** + * Sub operator between quaternion and scalar. + */ +template +auto operator-(const Quaternion<_tA>& lhs, const _tB& rhs) -> Quaternion { + return {lhs.a() - rhs, lhs.b(), lhs.c(), lhs.d()}; +} + +/** + * Sub operator between scalar and quaternion. + */ +template +auto operator-(const _tA& lhs, const Quaternion<_tB>& rhs) -> Quaternion { + return {lhs - rhs.a(), -rhs.b(), -rhs.c(), -rhs.d()}; +} + +/** + * Mul operator between two quaternions. + */ +template +auto operator*(const Quaternion<_tA>& lhs, const Quaternion<_tB>& rhs) -> Quaternion { + decltype(lhs.a() * rhs.a()) tn = (lhs.a() * rhs.a()) - (lhs.b() * rhs.b()) - (lhs.c() * rhs.c()) - (lhs.d() * rhs.d()); + decltype(lhs.a() * rhs.a()) tni = (lhs.a() * rhs.b()) + (lhs.b() * rhs.a()) + (lhs.c() * rhs.d()) - (lhs.d() * rhs.c()); + decltype(lhs.a() * rhs.a()) tnj = (lhs.a() * rhs.c()) + (lhs.c() * rhs.a()) + (lhs.d() * rhs.b()) - (lhs.b() * rhs.d()); + decltype(lhs.a() * rhs.a()) tnk = (lhs.a() * rhs.d()) + (lhs.d() * rhs.a()) + (lhs.b() * rhs.c()) - (lhs.c() * rhs.b()); + return {tn, tni, tnj, tnk}; +} + +/** + * Mul operator between quaternion and std:complex. + */ +template +auto operator*(const Quaternion<_tA>& lhs, const std::complex<_tB>& rhs) -> Quaternion { + decltype(lhs.a() * rhs.real()) tn = (lhs.a() * rhs.real()) - (lhs.b() * rhs.imag()); + decltype(lhs.a() * rhs.real()) tni = (lhs.a() * rhs.imag()) + (lhs.b() * rhs.real()); + decltype(lhs.a() * rhs.real()) tnj = (lhs.c() * rhs.real()) + (lhs.d() * rhs.imag()); + decltype(lhs.a() * rhs.real()) tnk = (lhs.d() * rhs.real()) - (lhs.c() * rhs.imag()); + return {tn, tni, tnj, tnk}; +} + +/** + * Mul operator between std:complex and quaternion. + */ +template +auto operator*(const std::complex<_tB>& lhs, const Quaternion<_tA>& rhs) -> Quaternion { + decltype(lhs.real() * rhs.a()) tn = (lhs.real() * rhs.a()) - (lhs.imag() * rhs.b()); + decltype(lhs.real() * rhs.a()) tni = (lhs.real() * rhs.b()) + (lhs.imag() * rhs.a()); + decltype(lhs.real() * rhs.a()) tnj = (lhs.real() * rhs.c()) - (lhs.imag() * rhs.d()); + decltype(lhs.real() * rhs.a()) tnk = (lhs.real() * rhs.d()) + (lhs.imag() * rhs.c()); + return {tn, tni, tnj, tnk}; +} + +/** + * Mul operator between quaternion and scalar. + */ +template +auto operator*(const Quaternion<_tA>& lhs, const _tB& rhs) -> Quaternion { + return {lhs.a() * rhs, lhs.b() * rhs, lhs.c() * rhs, lhs.d() * rhs}; +} + +/** + * Mul operator between scalar and quaternion. + */ +template +auto operator*(const _tA& lhs, const Quaternion<_tB>& rhs) -> Quaternion { + return operator*(rhs, lhs); +} + +/** + * Div operator between two quaternions. + */ +template +auto operator/(const Quaternion<_tA>& lhs, const Quaternion<_tB>& rhs) -> Quaternion { + decltype(lhs.a() * rhs.a()) tn = (lhs.a() * rhs.a()) + (lhs.b() * rhs.b()) + (lhs.c() * rhs.c()) + (lhs.d() * rhs.d()); + decltype(lhs.a() * rhs.a()) tni = - (lhs.a() * rhs.b()) + (lhs.b() * rhs.a()) - (lhs.c() * rhs.d()) + (lhs.d() * rhs.c()); + decltype(lhs.a() * rhs.a()) tnj = - (lhs.a() * rhs.c()) + (lhs.c() * rhs.a()) - (lhs.d() * rhs.b()) + (lhs.b() * rhs.d()); + decltype(lhs.a() * rhs.a()) tnk = - (lhs.a() * rhs.d()) + (lhs.d() * rhs.a()) - (lhs.b() * rhs.c()) + (lhs.c() * rhs.b()); + decltype(rhs.a()) norm = std::norm(rhs); + return {tn / norm, tni / norm, tnj / norm, tnk / norm}; +} + +/** + * Div operator between quaternion and scalar. + */ +template +auto operator/(const Quaternion<_tA>& lhs, const _tB& rhs) -> Quaternion { + return {lhs.a() / rhs, lhs.b() / rhs, lhs.c() / rhs, lhs.d() / rhs}; +} + +/** + * Div operator between scalar and quaternion. + */ +template +auto operator/(const _tB& lhs, const Quaternion<_tA>& rhs) -> Quaternion { + decltype(rhs.a()) norm = std::norm(rhs); + return {(rhs.a() * lhs) / norm, -(rhs.b() * lhs) / norm, -(rhs.c() * lhs) / norm, -(rhs.d() * lhs) / norm}; +} + +/** + * Trivial comparison operator between quaternions. + */ +template +bool operator==(const _tA& lhs, const _tB& rhs) { + return lhs.a() == rhs.a() && lhs.b() == rhs.b() && lhs.c() == rhs.c() && lhs.d() == rhs.d(); +} + +/** + * Stream operator for quaternion. + */ +template +std::ostream& operator<< (std::ostream& os, const Quaternion& obj) { + os << "(" << obj.a() << "," << obj.b() << "," << obj.c() << "," << obj.d() << ")"; + return os; +} + +/** + * Normalization function + */ +template +auto normalized(const Quaternion& quat) -> Quaternion { + return quat / std::abs(quat); +} + +/** + * Inverse function + */ +template +auto inverse(const Quaternion& quat) { + return std::conj(quat) / std::norm(quat); +} + +#endif // QUATER_H diff --git a/README.md b/README.md new file mode 100644 index 0000000..69a661c --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +A simple header-only generic C++ quaternion class. +========================================== + +* Header-only. +* No dynamic memory allocation. +* Supports operations on mixed types. +* Modern C++. +* Compatible with Standard Template Library. + +### Run test suite +``` +mkdir build +cd build +cmake .. +make +make test + +``` diff --git a/example.cpp b/example.cpp new file mode 100644 index 0000000..27ecf8d --- /dev/null +++ b/example.cpp @@ -0,0 +1,91 @@ +/* + * Copyright © 2019 Andrea Bontempi All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * - Neither the name of Andrea Bontempi nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include "Quaternion.h" + +int main(int argc, char **argv) { + + Quaternion a(1,0,1,0); + Quaternion b(1,0.5,0.5,0.75); + Quaternion c(3,0.5,1.5,0.75); + + std::complex ca(1,2); + std::complex cb(3,4); + + std::cout << "Real part of " << a << " is " << a.real() << std::endl; + + std::cout << "Unreal part of " << b << " is " << b.unreal() << std::endl; + + std::cout << "Component of " << a << " is " << a.a() << ", " << a.b() << ", " << a.c() << ", " << a.d() << std::endl; + + std::cout << "Norm of " << a << " is " << std::norm(a) << std::endl; + + std::cout << "Modulus of " << c << " is " << std::abs(c) << std::endl; + + std::cout << "Conjugate of " << b << " is " << std::conj(b) << std::endl; + + std::cout << "Normalization of " << b << " is " << normalized(b) << std::endl; + + std::cout << "Inverse of " << b << " is " << inverse(b) << std::endl; + + std::cout << a << " + " << b << " = " << a + b << std::endl; + + std::cout << a << " + " << 3 << " = " << a + 3 << std::endl; + + std::cout << b << " + complex " << ca << " = " << b + ca << std::endl; + + std::cout << a << " - " << b << " = " << a - b << std::endl; + + std::cout << a << " * " << b << " = " << a * b << std::endl; + + std::cout << a << " * " << 2.2 << " = " << a * 2.2 << std::endl; + + std::cout << a << " / " << b << " = " << a / b << std::endl; + + std::cout << a << " / " << 2.2 << " = " << a / 2.2 << std::endl; + + std::cout << 2.2 << " / " << b << " = " << 2.2 / b << std::endl; + + Quaternion component (ca,cb); + + std::cout << "Construct quaternion from complex " << ca << " and " << cb << " = " << component << std::endl; + + std::cout << "Quaternion " << component << " has complex component " << component.complex_a() << " and " << component.complex_b() << std::endl; + + std::cout << a << " is NaN? " << std::boolalpha << std::isnan(a) << std::endl; + + std::cout << a << " is infinite? " << std::boolalpha << std::isinf(a) << std::endl; + + std::cout << a << " is finite? " << std::boolalpha << std::isfinite(a) << std::endl; + + return 0; + +} diff --git a/test.cpp b/test.cpp new file mode 100644 index 0000000..bca2788 --- /dev/null +++ b/test.cpp @@ -0,0 +1,246 @@ +/* + * Copyright © 2019 Andrea Bontempi All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * - Neither the name of Andrea Bontempi nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE "Quaternion tests" + +#include +#include "Quaternion.h" +#include //VERY IMPORTANT - include this last + + +/** + * Comparison operator for test + */ + +const double epsilon = 1e-05; + +bool operator==(const Quaternion& lhs, const Quaternion& rhs) { + bool check_a = std::abs(lhs.a() - rhs.a()) <= ((std::abs(lhs.a()) < std::abs(rhs.a()) ? std::abs(rhs.a()) : std::abs(lhs.a())) * epsilon); + bool check_b = std::abs(lhs.b() - rhs.b()) <= ((std::abs(lhs.b()) < std::abs(rhs.b()) ? std::abs(rhs.b()) : std::abs(lhs.b())) * epsilon); + bool check_c = std::abs(lhs.c() - rhs.c()) <= ((std::abs(lhs.c()) < std::abs(rhs.c()) ? std::abs(rhs.c()) : std::abs(lhs.c())) * epsilon); + bool check_d = std::abs(lhs.d() - rhs.d()) <= ((std::abs(lhs.d()) < std::abs(rhs.d()) ? std::abs(rhs.d()) : std::abs(lhs.d())) * epsilon); + return check_a && check_b && check_c && check_d; +} + +bool compare_double(const double& a, const double& b) { + return std::abs(a - b) <= ((std::abs(a) < std::abs(b) ? std::abs(b) : std::abs(a)) * epsilon); + +} + +/** CONSTRUCTION OF A QUATERNION **/ + +BOOST_AUTO_TEST_CASE(quaternion_costruction_from_components) { + Quaternion test(0.1,0.5,0.9,1); + BOOST_CHECK_EQUAL(test.a(), 0.1); + BOOST_CHECK_EQUAL(test.b(), 0.5); + BOOST_CHECK_EQUAL(test.c(), 0.9); + BOOST_CHECK_EQUAL(test.d(), 1); +} + +BOOST_AUTO_TEST_CASE(quaternion_costruction_from_complex) { + std::complex a(0.1,0.5); + std::complex b(0.9,1); + Quaternion test(a,b); + BOOST_CHECK_EQUAL(test.complex_a(), a); + BOOST_CHECK_EQUAL(test.complex_b(), b); +} + +BOOST_AUTO_TEST_CASE(quaternion_costruction_from_copy) { + Quaternion a(0.1,0.5,0.9,1); + Quaternion b(a); + BOOST_CHECK_EQUAL(a, b); +} + +/** UNARY OPERATORS **/ + +BOOST_AUTO_TEST_CASE(quaternion_conjugation) { + Quaternion a(0.1,0.5,0.9,1); + Quaternion b(0.1,-0.5,-0.9,-1); + BOOST_CHECK_EQUAL(std::conj(a), b); +} + +BOOST_AUTO_TEST_CASE(quaternion_norm) { + Quaternion a(0.1,0.5,0.9,1); + double b = 2.07000; + BOOST_CHECK_EQUAL(compare_double(std::norm(a), b), true); +} + +BOOST_AUTO_TEST_CASE(quaternion_abs) { + Quaternion a(0.1,0.5,0.9,1); + double b = 1.43875; + BOOST_CHECK_EQUAL(compare_double(std::abs(a), b), true); +} + +BOOST_AUTO_TEST_CASE(quaternion_normalization) { + Quaternion a(0.1,0.5,0.9,1); + Quaternion b(0.0695048,0.347524,0.625543,0.695048); + BOOST_CHECK_EQUAL(normalized(a), b); +} + +BOOST_AUTO_TEST_CASE(quaternion_inversion) { + Quaternion a(0.1,0.5,0.9,1); + Quaternion b(0.0483092,-0.241546,-0.434783,-0.483092); + BOOST_CHECK_EQUAL(inverse(a), b); +} + +/** BINARY OPERATOR: SUM **/ + +BOOST_AUTO_TEST_CASE(sum_between_quaternions) { + Quaternion a(0.1,0.5,0.9,1); + Quaternion b(0.9,0.5,0.1,0); + Quaternion c(1,1,1,1); + BOOST_CHECK_EQUAL(a + b, c); +} + +BOOST_AUTO_TEST_CASE(sum_between_scalar_and_quaternion) { + double a = 1; + Quaternion b(0.9,0.5,0.1,0); + Quaternion c(1.9,0.5,0.1,0); + BOOST_CHECK_EQUAL(a + b, c); +} + +BOOST_AUTO_TEST_CASE(sum_between_quaternion_and_scalar) { + Quaternion a(0.9,0.5,0.1,0); + double b = 1; + Quaternion c(1.9,0.5,0.1,0); + BOOST_CHECK_EQUAL(a + b, c); +} + +BOOST_AUTO_TEST_CASE(sum_between_complex_and_quaternion) { + std::complex a (1,1); + Quaternion b(0.9,0.5,0.1,0); + Quaternion c(1.9,1.5,0.1,0); + BOOST_CHECK_EQUAL(a + b, c); +} + +BOOST_AUTO_TEST_CASE(sum_between_quaternion_and_complex) { + Quaternion a(0.9,0.5,0.1,0); + std::complex b (1,1); + Quaternion c(1.9,1.5,0.1,0); + BOOST_CHECK_EQUAL(a + b, c); +} + +/** BINARY OPERATOR: DIFFERENCE **/ + +BOOST_AUTO_TEST_CASE(difference_between_quaternions) { + Quaternion a(1,1,1,1); + Quaternion b(0.1,0.5,0.9,1); + Quaternion c(0.9,0.5,0.1,0); + BOOST_CHECK_EQUAL(a - b, c); +} + +BOOST_AUTO_TEST_CASE(difference_between_scalar_and_quaternion) { + double a = 1; + Quaternion b(0.9,0.5,0.1,0); + Quaternion c(0.1,-0.5,-0.1,0); + BOOST_CHECK_EQUAL(a - b, c); +} + +BOOST_AUTO_TEST_CASE(difference_between_quaternion_and_scalar) { + Quaternion a(0.9,0.5,0.1,0); + double b = 1; + Quaternion c(-0.1,0.5,0.1,0); + BOOST_CHECK_EQUAL(a - b, c); +} + +BOOST_AUTO_TEST_CASE(difference_between_complex_and_quaternion) { + std::complex a (1,1); + Quaternion b(0.9,0.5,0.1,0); + Quaternion c(0.1,0.5,-0.1,0); + BOOST_CHECK_EQUAL(a - b, c); +} + +BOOST_AUTO_TEST_CASE(difference_between_quaternion_and_complex) { + Quaternion a(0.9,0.5,0.1,0); + std::complex b (1,1); + Quaternion c(-0.1,-0.5,0.1,0); + BOOST_CHECK_EQUAL(a - b, c); +} + +/** BINARY OPERATOR: MULTIPLICATION **/ + +BOOST_AUTO_TEST_CASE(multiplication_between_quaternions) { + Quaternion a(-1,1,-1,1); + Quaternion b(0.1,0.5,0.9,1); + Quaternion c(-0.7,-2.3,-1.5,0.5); + BOOST_CHECK_EQUAL(a * b, c); +} + +BOOST_AUTO_TEST_CASE(multiplication_between_scalar_and_quaternion) { + double a = 2; + Quaternion b(0.1,0.5,0.9,1); + Quaternion c(0.2,1,1.8,2); + BOOST_CHECK_EQUAL(a * b, c); +} + +BOOST_AUTO_TEST_CASE(multiplication_between_quaternion_and_scalar) { + Quaternion a(0.1,0.5,0.9,1); + double b = 2; + Quaternion c(0.2,1,1.8,2); + BOOST_CHECK_EQUAL(a * b, c); +} + +BOOST_AUTO_TEST_CASE(multiplication_between_complex_and_quaternion) { + std::complex a (1,1); + Quaternion b(0.1,0.5,0.9,1); + Quaternion c(-0.4,0.6,-0.1,1.9); + BOOST_CHECK_EQUAL(a * b, c); +} + +BOOST_AUTO_TEST_CASE(multiplication_between_quaternion_and_complex) { + Quaternion a(0.1,0.5,0.9,1); + std::complex b (1,1); + Quaternion c(-0.4,0.6,1.9,0.1); + BOOST_CHECK_EQUAL(a * b, c); +} + +/** BINARY OPERATOR: DIVISION **/ + +BOOST_AUTO_TEST_CASE(division_between_quaternions) { + Quaternion a(-1,1,-1,1); + Quaternion b(0.1,0.5,0.9,1); + Quaternion c(0.241546,1.207729,0.628019,-0.144928); + BOOST_CHECK_EQUAL(a / b, c); +} + +BOOST_AUTO_TEST_CASE(division_between_scalar_and_quaternion) { + double a = 2; + Quaternion b(0.1,0.5,0.9,1); + Quaternion c(0.0966184,-0.483092,-0.869565,-0.966184); + BOOST_CHECK_EQUAL(a / b, c); +} + +BOOST_AUTO_TEST_CASE(division_between_quaternion_and_scalar) { + Quaternion a(-1,1,-1,1); + double b = 2; + Quaternion c(-0.5,0.5,-0.5,0.5); + BOOST_CHECK_EQUAL(a / b, c); +}