diff --git a/.clangd b/.clangd index c139880..4675865 100644 --- a/.clangd +++ b/.clangd @@ -1,2 +1,12 @@ CompileFlags: - CompilationDatabase: .build \ No newline at end of file + CompilationDatabase: .build + Add: + - -Wno-c2y-extensions + - -Wno-double-promotion + +Diagnostics: + Suppress: + - c2y-extensions + - -Wc2y-extensions + - typecheck_expression_not_modifiable_lvalue + - pp_including_mainfile_in_preamble \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 63f4e75..584b49a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -33,7 +33,7 @@ "-i ${workspaceFolder}/external/Catch2/src" // adjust path as needed ], "files.associations": { - "*.tpp": "c++", + "*.tpp": "cpp", "type_traits": "cpp", "functional": "cpp", "array": "cpp", diff --git a/CHANGELOG.md b/CHANGELOG.md index e361c6f..a799bb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. ## Next Release +### Feature + +- add static Vector3d with mp-units support + ## [0.1.4](https://github.com/repo/owner/releases/tag/0.1.4) - 2026-07-26 diff --git a/include/mstd/linAlg/concepts/vector3dConcepts.hpp b/include/mstd/linAlg/concepts/vector3dConcepts.hpp new file mode 100644 index 0000000..9cc531f --- /dev/null +++ b/include/mstd/linAlg/concepts/vector3dConcepts.hpp @@ -0,0 +1,45 @@ +/***************************************************************************** + + + mstd library + Copyright (C) 2025-now Jakob Gamper + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + +******************************************************************************/ + +#ifndef __MSTD__LINALG__CONCEPTS__VECTOR3DCONCEPTS_HPP__ +#define __MSTD__LINALG__CONCEPTS__VECTOR3DCONCEPTS_HPP__ + +#include + +#include "vector3dDepth.hpp" + +namespace mstd +{ + template + class Vector3d; + + /** + * @brief Concept for Vector3d types + * + * @tparam T + */ + template + concept Vector3dConcept = std::same_as>; + +} // namespace mstd + +#endif // __MSTD__LINALG__CONCEPTS__VECTOR3DCONCEPTS_HPP__ diff --git a/include/mstd/linAlg/concepts/vector3dDepth.hpp b/include/mstd/linAlg/concepts/vector3dDepth.hpp new file mode 100644 index 0000000..3a48993 --- /dev/null +++ b/include/mstd/linAlg/concepts/vector3dDepth.hpp @@ -0,0 +1,75 @@ +/***************************************************************************** + + + mstd library + Copyright (C) 2025-now Jakob Gamper + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + +******************************************************************************/ + +#ifndef __MSTD__LINALG__CONCEPTS__VECTOR3DDEPTH_HPP__ +#define __MSTD__LINALG__CONCEPTS__VECTOR3DDEPTH_HPP__ + +namespace mstd +{ + template + class Vector3d; + + /** + * @brief type trait to determine the depth of a Vector3d + * + * @tparam T + */ + template + struct Vector3dDepth + { + static constexpr int value = 0; + }; + + /** + * @brief type trait to determine the depth of a Vector3d + * + * @details specialization for Vector3d + * + * @tparam T + */ + template + struct Vector3dDepth> + { + static constexpr int value = 1 + Vector3dDepth::value; + }; + + /** + * @brief constexpr variable to check the depth of a Vector3d + * + * @tparam T + */ + template + constexpr int Vector3dDepth_v = Vector3dDepth::value; + + /** + * @brief constexpr variable to check the depth difference of two Vector3ds + * + * @tparam T + * @tparam U + */ + template + constexpr int Vector3dDepthDifference_v = + Vector3dDepth_v - Vector3dDepth_v; + +} // namespace mstd + +#endif // __MSTD__LINALG__CONCEPTS__VECTOR3DDEPTH_HPP__ diff --git a/include/mstd/linAlg/vector3d.hpp b/include/mstd/linAlg/vector3d.hpp new file mode 100644 index 0000000..51ff12c --- /dev/null +++ b/include/mstd/linAlg/vector3d.hpp @@ -0,0 +1,210 @@ +/***************************************************************************** + + + mstd library + Copyright (C) 2025-now Jakob Gamper + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + +******************************************************************************/ + +#ifndef __MSTD__LINALG__VECTOR3D_HPP__ +#define __MSTD__LINALG__VECTOR3D_HPP__ + +#include // for mp_units::sqrt + +#include // for std:sqrt +#include +#include // for std::ostream +#include + +#include "concepts/vector3dConcepts.hpp" +#include "vector3dClass.hpp" + +namespace mstd +{ + /************************ + * comparison operators * + ************************/ + + template + requires std::equality_comparable_with + [[nodiscard]] constexpr bool operator==( + const Vector3d &lhs, + const Vector3d &rhs + ); + + template + requires std::equality_comparable_with + [[nodiscard]] constexpr bool operator!=( + const Vector3d &lhs, + const Vector3d &rhs + ); + + /********************* + * binary + operator * + *********************/ + + template + requires requires(const U &u, const V &v) { u + v; } && + (Vector3dDepthDifference_v == 0) + [[nodiscard]] constexpr auto operator+( + const Vector3d &lhs, + const Vector3d &rhs + ) -> Vector3d() + std::declval())>; + + template + requires requires(const U &u, const V &v) { u + v; } + [[nodiscard]] constexpr auto operator+( + const U &scalar, + const Vector3d &vector + ) -> Vector3d() + std::declval())>; + + template + requires requires(const U &u, const V &v) { u + v; } + [[nodiscard]] constexpr auto operator+( + const Vector3d &vector, + const V &scalar + ) -> Vector3d() + std::declval())>; + + /********************* + * binary - operator * + *********************/ + + template + requires requires(const U &u, const V &v) { u - v; } && + (Vector3dDepthDifference_v == 0) + [[nodiscard]] constexpr auto operator-( + const Vector3d &lhs, + const Vector3d &rhs + ) -> Vector3d() - std::declval())>; + + template + requires requires(const U &u, const V &v) { u - v; } + [[nodiscard]] constexpr auto operator-( + const Vector3d &vector, + const V &scalar + ) -> Vector3d() - std::declval())>; + + /********************* + * binary * operator * + *********************/ + + template + requires requires(const U &u, const V &v) { u * v; } && + (Vector3dDepthDifference_v == 0) + [[nodiscard]] constexpr auto operator*( + const Vector3d &lhs, + const Vector3d &rhs + ) -> Vector3d() * std::declval())>; + + template + requires requires(const U &u, const V &v) { u * v; } + [[nodiscard]] constexpr auto operator*( + const U &scalar, + const Vector3d &vector + ) -> Vector3d() * std::declval())>; + + template + requires requires(const U &u, const V &v) { u * v; } + [[nodiscard]] constexpr auto operator*( + const Vector3d &vector, + const V &scalar + ) -> Vector3d() * std::declval())>; + + /********************* + * binary / operator * + *********************/ + + template + requires requires(const U &u, const V &v) { u / v; } && + (Vector3dDepthDifference_v == 0) + [[nodiscard]] constexpr auto operator/( + const Vector3d &lhs, + const Vector3d &rhs + ) -> Vector3d() / std::declval())>; + + template + requires requires(const U &u, const V &v) { u / v; } + [[nodiscard]] constexpr auto operator/( + const Vector3d &vector, + const V &scalar + ) -> Vector3d() / std::declval())>; + + /****************** + * norm functions * + ******************/ + + template + requires requires(const U &u) { u * u + u * u; } + [[nodiscard]] constexpr auto normSquared(const Vector3d &vec) + -> decltype(std::declval() * std::declval()); + + template + requires requires(const U &vec) { std::sqrt(normSquared(vec)); } + [[nodiscard]] auto norm(const U &vec) + -> decltype(std::sqrt(normSquared(vec))); + + template + requires requires(const U &vec) { mp_units::sqrt(normSquared(vec)); } + [[nodiscard]] auto norm(const U &vec) + -> decltype(mp_units::sqrt(normSquared(vec))); + + template + requires requires(const Vector3d &vec) { vec[0] / norm(vec); } + [[nodiscard]] auto normalize(const Vector3d &vec) -> Vector3d< + decltype(std::declval() / norm(std::declval &>()))>; + + template + requires requires(Vector3d &vec) { vec /= norm(vec); } + void normalize(Vector3d &vec); + + /********************* + * product functions * + *********************/ + + template + requires requires(const U &u, const V &v) { u * v + u * v; } && + (Vector3dDepthDifference_v == 0) + [[nodiscard]] constexpr auto dot( + const Vector3d &lhs, + const Vector3d &rhs + ) -> decltype(std::declval() * std::declval() + std::declval() * std::declval()); + + template + requires requires(const U &u, const V &v) { u * v - u * v; } && + (Vector3dDepthDifference_v == 0) + [[nodiscard]] constexpr auto cross( + const Vector3d &lhs, + const Vector3d &rhs + ) + -> Vector3d< + decltype(std::declval() * std::declval() - std::declval() * std::declval())>; + + /************** + * ostream << * + **************/ + + template + requires requires(std::ostream &os, const U &u) { + { os << u } -> std::same_as; + } + std::ostream &operator<<(std::ostream &os, const Vector3d &vector); + +} // namespace mstd + +#include "vector3d.tpp" + +#endif // __MSTD__LINALG__VECTOR3D_HPP__ diff --git a/include/mstd/linAlg/vector3d.tpp b/include/mstd/linAlg/vector3d.tpp new file mode 100644 index 0000000..7a85510 --- /dev/null +++ b/include/mstd/linAlg/vector3d.tpp @@ -0,0 +1,324 @@ +/***************************************************************************** + + + mstd library + Copyright (C) 2025-now Jakob Gamper + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + +******************************************************************************/ + +#ifndef __MSTD__LINALG__VECTOR3D_TPP__ +#define __MSTD__LINALG__VECTOR3D_TPP__ + +#include + +#include +#include +#include + +#include "vector3d.hpp" + +namespace mstd +{ + /************************ + * comparison operators * + ************************/ + + template + requires std::equality_comparable_with + constexpr bool operator==(const Vector3d &lhs, const Vector3d &rhs) + { + return lhs[0] == rhs[0] && lhs[1] == rhs[1] && lhs[2] == rhs[2]; + } + + template + requires std::equality_comparable_with + constexpr bool operator!=(const Vector3d &lhs, const Vector3d &rhs) + { + return !(lhs == rhs); + } + + /********************* + * binary + operator * + *********************/ + + template + requires requires(const U &u, const V &v) { u + v; } && + (Vector3dDepthDifference_v == 0) + [[nodiscard]] constexpr auto operator+( + const Vector3d &lhs, + const Vector3d &rhs + ) -> Vector3d() + std::declval())> + { + using ResultType = decltype(std::declval() + std::declval()); + + return Vector3d( + lhs[0] + rhs[0], + lhs[1] + rhs[1], + lhs[2] + rhs[2] + ); + } + + template + requires requires(const U &u, const V &v) { u + v; } + constexpr auto operator+(const U &scalar, const Vector3d &vector) + -> Vector3d() + std::declval())> + { + using ResultType = decltype(std::declval() + std::declval()); + + return Vector3d( + scalar + vector[0], + scalar + vector[1], + scalar + vector[2] + ); + } + + template + requires requires(const U &u, const V &v) { u + v; } + constexpr auto operator+(const Vector3d &vector, const V &scalar) + -> Vector3d() + std::declval())> + { + using ResultType = decltype(std::declval() + std::declval()); + + return Vector3d( + vector[0] + scalar, + vector[1] + scalar, + vector[2] + scalar + ); + } + + /********************* + * binary - operator * + *********************/ + + template + requires requires(const U &u, const V &v) { u - v; } && + (Vector3dDepthDifference_v == 0) + [[nodiscard]] constexpr auto operator-( + const Vector3d &lhs, + const Vector3d &rhs + ) -> Vector3d() - std::declval())> + { + using ResultType = decltype(std::declval() - std::declval()); + + return Vector3d( + lhs[0] - rhs[0], + lhs[1] - rhs[1], + lhs[2] - rhs[2] + ); + } + + template + requires requires(const U &u, const V &v) { u - v; } + constexpr auto operator-(const Vector3d &vector, const V &scalar) + -> Vector3d() - std::declval())> + { + using ResultType = decltype(std::declval() - std::declval()); + + return Vector3d( + vector[0] - scalar, + vector[1] - scalar, + vector[2] - scalar + ); + } + + /********************* + * binary * operator * + *********************/ + + template + requires requires(const U &u, const V &v) { u * v; } && + (Vector3dDepthDifference_v == 0) + [[nodiscard]] constexpr auto operator*( + const Vector3d &lhs, + const Vector3d &rhs + ) -> Vector3d() * std::declval())> + { + using ResultType = decltype(std::declval() * std::declval()); + + return Vector3d( + lhs[0] * rhs[0], + lhs[1] * rhs[1], + lhs[2] * rhs[2] + ); + } + + template + requires requires(const U &u, const V &v) { u * v; } + constexpr auto operator*(const U &scalar, const Vector3d &vector) + -> Vector3d() * std::declval())> + { + using ResultType = decltype(std::declval() * std::declval()); + + return Vector3d( + scalar * vector[0], + scalar * vector[1], + scalar * vector[2] + ); + } + + template + requires requires(const U &u, const V &v) { u * v; } + constexpr auto operator*(const Vector3d &vector, const V &scalar) + -> Vector3d() * std::declval())> + { + using ResultType = decltype(std::declval() * std::declval()); + + return Vector3d( + vector[0] * scalar, + vector[1] * scalar, + vector[2] * scalar + ); + } + + /********************* + * binary / operator * + *********************/ + + template + requires requires(const U &u, const V &v) { u / v; } && + (Vector3dDepthDifference_v == 0) + [[nodiscard]] constexpr auto operator/( + const Vector3d &lhs, + const Vector3d &rhs + ) -> Vector3d() / std::declval())> + { + using ResultType = decltype(std::declval() / std::declval()); + + return Vector3d( + lhs[0] / rhs[0], + lhs[1] / rhs[1], + lhs[2] / rhs[2] + ); + } + + template + requires requires(const U &u, const V &v) { u / v; } + constexpr auto operator/(const Vector3d &vector, const V &scalar) + -> Vector3d() / std::declval())> + { + using ResultType = decltype(std::declval() / std::declval()); + + return Vector3d( + vector[0] / scalar, + vector[1] / scalar, + vector[2] / scalar + ); + } + + /****************** + * norm functions * + ******************/ + + template + requires requires(const U &u) { u * u + u * u; } + [[nodiscard]] constexpr auto normSquared(const Vector3d &vec) + -> decltype(std::declval() * std::declval()) + { + return vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]; + } + + template + requires requires(const U &vec) { std::sqrt(normSquared(vec)); } + [[nodiscard]] auto norm(const U &vec) + -> decltype(std::sqrt(normSquared(vec))) + { + return std::sqrt(normSquared(vec)); + } + + template + requires requires(const U &vec) { mp_units::sqrt(normSquared(vec)); } + [[nodiscard]] auto norm(const U &vec) + -> decltype(mp_units::sqrt(normSquared(vec))) + { + return mp_units::sqrt(normSquared(vec)); + } + + template + requires requires(const Vector3d &vec) { vec[0] / norm(vec); } + [[nodiscard]] auto normalize(const Vector3d &vec) -> Vector3d< + decltype(std::declval() / norm(std::declval &>()))> + { + using ResultType = + decltype(std::declval() / norm(std::declval &>())); + + const auto normValue = norm(vec); + + return Vector3d( + vec[0] / normValue, + vec[1] / normValue, + vec[2] / normValue + ); + } + + template + requires requires(Vector3d &vec) { vec /= norm(vec); } + void normalize(Vector3d &vec) + { + vec /= norm(vec); + } + + /********************* + * product functions * + *********************/ + + template + requires requires(const U &u, const V &v) { u * v + u * v; } && + (Vector3dDepthDifference_v == 0) + [[nodiscard]] constexpr auto dot( + const Vector3d &lhs, + const Vector3d &rhs + ) -> decltype(std::declval() * std::declval() + std::declval() * std::declval()) + { + return lhs[0] * rhs[0] + lhs[1] * rhs[1] + lhs[2] * rhs[2]; + } + + template + requires requires(const U &u, const V &v) { u * v - u * v; } && + (Vector3dDepthDifference_v == 0) + [[nodiscard]] constexpr auto cross( + const Vector3d &lhs, + const Vector3d &rhs + ) + -> Vector3d< + decltype(std::declval() * std::declval() - std::declval() * std::declval())> + { + using ResultType = + decltype(std::declval() * std::declval() - std::declval() * std::declval()); + return Vector3d( + lhs[1] * rhs[2] - lhs[2] * rhs[1], + lhs[2] * rhs[0] - lhs[0] * rhs[2], + lhs[0] * rhs[1] - lhs[1] * rhs[0] + + ); + } + + /************** + * ostream << * + **************/ + + template + requires requires(std::ostream &os, const U &u) { + { os << u } -> std::same_as; + } + std::ostream &operator<<(std::ostream &os, const Vector3d &vector) + { + return os << vector[0] << " " << vector[1] << " " << vector[2]; + } + +} // namespace mstd + +#endif // __MSTD__LINALG__VECTOR3D_TPP__ diff --git a/include/mstd/linAlg/vector3dClass.hpp b/include/mstd/linAlg/vector3dClass.hpp new file mode 100644 index 0000000..083435f --- /dev/null +++ b/include/mstd/linAlg/vector3dClass.hpp @@ -0,0 +1,147 @@ +/***************************************************************************** + + + mstd library + Copyright (C) 2025-now Jakob Gamper + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + +******************************************************************************/ + +#ifndef __MSTD__LINALG__VECTOR3DCLASS_HPP__ +#define __MSTD__LINALG__VECTOR3DCLASS_HPP__ + +#include // for std::array + +#include "concepts/vector3dConcepts.hpp" + +namespace mstd +{ + template + class Vector3d + { + private: + std::array _xyz{}; + + public: + // clang-format off + [[nodiscard]] constexpr Vector3d() = default; + constexpr ~Vector3d() = default; + [[nodiscard]] constexpr Vector3d(const Vector3d &) = default; + constexpr Vector3d &operator=(const Vector3d &) = default; + [[nodiscard]] constexpr Vector3d(Vector3d &&) = default; + constexpr Vector3d &operator=(Vector3d &&) = default; + + [[nodiscard]] constexpr explicit Vector3d(const T &xyz) : _xyz{xyz, xyz, xyz} {} + [[nodiscard]] constexpr Vector3d(const T &x, const T &y, const T &z) : _xyz{x, y, z} {} + // clang-format on + + using value_type = T; + + /*********************** + * unary +/- operators * + ***********************/ + + [[nodiscard]] constexpr Vector3d operator+() const; + [[nodiscard]] constexpr Vector3d operator-() const; + + /********************************* + * compound assignment operators * + *********************************/ + + template + requires requires(T &t, const U &u) { t += u; } && + (Vector3dDepthDifference_v == 0) + constexpr Vector3d &operator+=(const Vector3d &rhs); + + template + requires requires(T &t, const U &u) { t += u; } + constexpr Vector3d &operator+=(const U &rhs); + + template + requires requires(T &t, const U &u) { t -= u; } && + (Vector3dDepthDifference_v == 0) + constexpr Vector3d &operator-=(const Vector3d &rhs); + + template + requires requires(T &t, const U &u) { t -= u; } + constexpr Vector3d &operator-=(const U &rhs); + + template + requires requires(T &t, const U &u) { t *= u; } && + (Vector3dDepthDifference_v == 0) + constexpr Vector3d &operator*=(const Vector3d &rhs); + + template + requires requires(T &t, const U &u) { t *= u; } + constexpr Vector3d &operator*=(const U &rhs); + + template + requires requires(T &t, const U &u) { t /= u; } && + (Vector3dDepthDifference_v == 0) + constexpr Vector3d &operator/=(const Vector3d &rhs); + + template + requires requires(T &t, const U &u) { t /= u; } + constexpr Vector3d &operator/=(const U &rhs); + + /******************** + * unit conversions * + ********************/ + + template + requires requires(const T &v, const U &unit) { v.in(unit); } + [[nodiscard]] constexpr auto in(const U &unit) const; + + template + requires requires(const T &v, const U &unit) { v.force_in(unit); } + [[nodiscard]] constexpr auto force_in(const U &unit) const; + + template + requires requires(const T &v, const U &unit) { + v.numerical_value_in(unit); + } + [[nodiscard]] constexpr auto numerical_value_in(const U &unit) const; + + template + requires requires(const T &v, const U &unit) { + v.force_numerical_value_in(unit); + } + [[nodiscard]] constexpr auto force_numerical_value_in( + const U &unit + ) const; + + /********************** + * indexing operators * + **********************/ + + constexpr T &operator[](const std::size_t index); + [[nodiscard]] constexpr const T &operator[](const std::size_t index + ) const; + + /*********** + * casting * + ***********/ + + template + requires requires(const T &value) { static_cast(value); } + constexpr operator Vector3d() const; + }; + +} // namespace mstd + +#include "vector3dClass.tpp" + +#endif // __MSTD__LINALG__VECTOR3DCLASS_HPP__ diff --git a/include/mstd/linAlg/vector3dClass.tpp b/include/mstd/linAlg/vector3dClass.tpp new file mode 100644 index 0000000..623d796 --- /dev/null +++ b/include/mstd/linAlg/vector3dClass.tpp @@ -0,0 +1,259 @@ +/***************************************************************************** + + + mstd library + Copyright (C) 2025-now Jakob Gamper + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + +******************************************************************************/ + +#ifndef __MSTD__LINALG__VECTOR3DCLASS_TPP__ +#define __MSTD__LINALG__VECTOR3DCLASS_TPP__ + +#include // for std::declval + +#include "concepts/vector3dConcepts.hpp" +#include "vector3dClass.hpp" + +namespace mstd +{ + /*********************** + * * + * unary +/- operators * + * * + ***********************/ + + template + constexpr Vector3d Vector3d::operator+() const + { + return *this; + } + + template + constexpr Vector3d Vector3d::operator-() const + { + return Vector3d{-_xyz[0], -_xyz[1], -_xyz[2]}; + } + + /********************************* + * * + * compound assignment operators * + * * + *********************************/ + + template + template + requires requires(T &t, const U &u) { t += u; } && + (Vector3dDepthDifference_v == 0) + constexpr Vector3d &Vector3d::operator+=(const Vector3d &rhs) + { + _xyz[0] += rhs[0]; + _xyz[1] += rhs[1]; + _xyz[2] += rhs[2]; + + return *this; + } + + template + template + requires requires(T &t, const U &u) { t += u; } + constexpr Vector3d &Vector3d::operator+=(const U &rhs) + { + _xyz[0] += rhs; + _xyz[1] += rhs; + _xyz[2] += rhs; + + return *this; + } + + template + template + requires requires(T &t, const U &u) { t -= u; } && + (Vector3dDepthDifference_v == 0) + constexpr Vector3d &Vector3d::operator-=(const Vector3d &rhs) + { + _xyz[0] -= rhs[0]; + _xyz[1] -= rhs[1]; + _xyz[2] -= rhs[2]; + + return *this; + } + + template + template + requires requires(T &t, const U &u) { t -= u; } + constexpr Vector3d &Vector3d::operator-=(const U &rhs) + { + _xyz[0] -= rhs; + _xyz[1] -= rhs; + _xyz[2] -= rhs; + + return *this; + } + + template + template + requires requires(T &t, const U &u) { t *= u; } && + (Vector3dDepthDifference_v == 0) + constexpr Vector3d &Vector3d::operator*=(const Vector3d &rhs) + { + _xyz[0] *= rhs[0]; + _xyz[1] *= rhs[1]; + _xyz[2] *= rhs[2]; + + return *this; + } + + template + template + requires requires(T &t, const U &u) { t *= u; } + constexpr Vector3d &Vector3d::operator*=(const U &rhs) + { + _xyz[0] *= rhs; + _xyz[1] *= rhs; + _xyz[2] *= rhs; + + return *this; + } + + template + template + requires requires(T &t, const U &u) { t /= u; } && + (Vector3dDepthDifference_v == 0) + constexpr Vector3d &Vector3d::operator/=(const Vector3d &rhs) + { + _xyz[0] /= rhs[0]; + _xyz[1] /= rhs[1]; + _xyz[2] /= rhs[2]; + + return *this; + } + + template + template + requires requires(T &t, const U &u) { t /= u; } + constexpr Vector3d &Vector3d::operator/=(const U &rhs) + { + _xyz[0] /= rhs; + _xyz[1] /= rhs; + _xyz[2] /= rhs; + + return *this; + } + + /******************* + * unit conversion * + *******************/ + + template + template + requires requires(const T &v, const U &unit) { v.in(unit); } + constexpr auto Vector3d::in(const U &unit) const + { + using ResultType = decltype(std::declval().in(unit)); + + return Vector3d{ + _xyz[0].in(unit), + _xyz[1].in(unit), + _xyz[2].in(unit), + }; + } + + template + template + requires requires(const T &v, const U &unit) { v.force_in(unit); } + constexpr auto Vector3d::force_in(const U &unit) const + { + using ResultType = decltype(std::declval().force_in(unit)); + + return Vector3d{ + _xyz[0].force_in(unit), + _xyz[1].force_in(unit), + _xyz[2].force_in(unit), + }; + } + + template + template + requires requires(const T &v, const U &unit) { v.numerical_value_in(unit); } + constexpr auto Vector3d::numerical_value_in(const U &unit) const + { + using ResultType = + decltype(std::declval().numerical_value_in(unit)); + + return Vector3d{ + _xyz[0].numerical_value_in(unit), + _xyz[1].numerical_value_in(unit), + _xyz[2].numerical_value_in(unit), + }; + } + + template + template + requires requires(const T &v, const U &unit) { + v.force_numerical_value_in(unit); + } + constexpr auto Vector3d::force_numerical_value_in(const U &unit) const + { + using ResultType = + decltype(std::declval().force_numerical_value_in(unit)); + + return Vector3d{ + _xyz[0].force_numerical_value_in(unit), + _xyz[1].force_numerical_value_in(unit), + _xyz[2].force_numerical_value_in(unit), + }; + } + + /********************** + * * + * indexing operators * + * * + **********************/ + + template + constexpr T &Vector3d::operator[](const std::size_t index) + { + return _xyz[index]; + } + + template + constexpr const T &Vector3d::operator[](const std::size_t index) const + { + return _xyz[index]; + } + + /*********** + * * + * casting * + * * + ***********/ + + template + template + requires requires(const T &value) { static_cast(value); } + constexpr Vector3d::operator Vector3d() const + { + return Vector3d{ + static_cast(_xyz[0]), + static_cast(_xyz[1]), + static_cast(_xyz[2]), + }; + } + +} // namespace mstd + +#endif // __MSTD__LINALG__VECTOR3DCLASS_TPP__ diff --git a/test/linAlg/CMakeLists.txt b/test/linAlg/CMakeLists.txt new file mode 100644 index 0000000..ac5293b --- /dev/null +++ b/test/linAlg/CMakeLists.txt @@ -0,0 +1,68 @@ +if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + cmake_minimum_required(VERSION 3.20) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + project(mstd_tests_linAlg LANGUAGES CXX) + include(CTest) + enable_testing() + set(MSTD_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../..") +else() + set(MSTD_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/..") +endif() + +if(NOT TARGET mstd) + add_library(mstd INTERFACE) + target_include_directories(mstd + INTERFACE + "${MSTD_ROOT_DIR}/include" + ) + target_compile_features(mstd INTERFACE cxx_std_20) +endif() + +if(NOT TARGET Catch2::Catch2WithMain) + add_subdirectory( + "${MSTD_ROOT_DIR}/external/Catch2" + "${CMAKE_CURRENT_BINARY_DIR}/external/Catch2" + ) +endif() + +list(APPEND CMAKE_MODULE_PATH "${MSTD_ROOT_DIR}/external/Catch2/extras") + +if(TARGET mstd_test_support) + set(MSTD_TEST_LINK_TARGET mstd_test_support) +else() + add_library(mstd_test_support INTERFACE) + if(EXISTS "${MSTD_ROOT_DIR}/test/include") + target_include_directories(mstd_test_support + INTERFACE + "${MSTD_ROOT_DIR}/test/include" + ) + endif() + target_link_libraries(mstd_test_support + INTERFACE + mstd + Catch2::Catch2WithMain + ) + target_compile_features(mstd_test_support INTERFACE cxx_std_20) + set(MSTD_TEST_LINK_TARGET mstd_test_support) +endif() + +add_executable(mstd_tests_linAlg + vector3d.cpp + vector3d_mp-units.cpp +) + +target_link_libraries(mstd_tests_linAlg + PRIVATE + "${MSTD_TEST_LINK_TARGET}" + mp-units::mp-units +) + +target_compile_features(mstd_tests_linAlg PRIVATE cxx_std_20) + +include(Catch) +catch_discover_tests(mstd_tests_linAlg + TEST_PREFIX "mstd::linAlg::" + REPORTER compact +) + +set_property(GLOBAL APPEND PROPERTY MSTD_TEST_TARGETS mstd_tests_linAlg) diff --git a/test/linAlg/vector3d.cpp b/test/linAlg/vector3d.cpp new file mode 100644 index 0000000..f299da4 --- /dev/null +++ b/test/linAlg/vector3d.cpp @@ -0,0 +1,313 @@ +/***************************************************************************** + + + mstd library + Copyright (C) 2025-now Jakob Gamper + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + +******************************************************************************/ + +#include "mstd/linAlg/vector3d.hpp" + +#include +#include +#include +#include + +using namespace mstd; + +TEST_CASE("Vector3d - Default Constructor") +{ + constexpr Vector3d defaultConstructed{}; + + STATIC_REQUIRE(defaultConstructed == Vector3d{0, 0, 0}); +} + +TEST_CASE("Vector3d - Copy Constructors") +{ + constexpr Vector3d copySource{1, 2, 3}; + constexpr auto copyConstructed{copySource}; + constexpr auto copyAssigned = copyConstructed; + + STATIC_REQUIRE(copyConstructed == copySource); + STATIC_REQUIRE(copyAssigned == copySource); +} + +TEST_CASE("Vector3d - Move Constructors") +{ + constexpr Vector3d moveSource{4, 5, 6}; + constexpr auto moveConstructed{std::move(moveSource)}; + constexpr auto moveAssigned = std::move(moveConstructed); + + STATIC_REQUIRE(moveConstructed == moveSource); + STATIC_REQUIRE(moveAssigned == moveSource); +} + +TEST_CASE("Vector3d - Parametrized Constructors") +{ + constexpr auto valueConstructed = Vector3d{1, 2, 3}; + STATIC_REQUIRE(valueConstructed == Vector3d{1, 2, 3}); + + constexpr auto scalarConstructed = Vector3d{7}; + STATIC_REQUIRE(scalarConstructed == Vector3d{7, 7, 7}); +} + +TEST_CASE("Vector3d - Indexing Operators") +{ + constexpr auto distance = Vector3d{1.0, 2.0, 3.0}; + + STATIC_REQUIRE(distance[0] == 1.0); + STATIC_REQUIRE(distance[1] == 2.0); + STATIC_REQUIRE(distance[2] == 3.0); + + auto position = Vector3d{0.0}; + + position[0] = 1.0; + position[1] = 2.0; + position[2] = 3.0; + + REQUIRE(distance == position); +} + +TEST_CASE("Vector3d - Comparison Operators") +{ + constexpr auto distance = Vector3d{1.0, 2.0, 3.0}; + constexpr auto distanceInt = Vector3d{1, 2, 3}; + constexpr auto anotherDistance = Vector3d{1.1, 2.2, 3.3}; + + STATIC_REQUIRE(distance == distance); + STATIC_REQUIRE(distance == distanceInt); + STATIC_REQUIRE(distance != anotherDistance); +} + +TEST_CASE("Vector3d - Unary +/- Operators") +{ + constexpr auto distance = Vector3d{1.0, 2.0, 3.0}; + constexpr auto negativeDistance = Vector3d{-1.0, -2.0, -3.0}; + + STATIC_REQUIRE(+distance == distance); + STATIC_REQUIRE(-distance == negativeDistance); +} + +TEST_CASE("Vector3d - Compound Assignment Operators") +{ + auto distance = Vector3d{1.0, 2.0, 3.0}; + constexpr auto anotherDistance = Vector3d{5.0, 5.0, 5.0}; + constexpr auto scalarDistance = 5.0; + constexpr auto distanceSum = Vector3d{6.0, 7.0, 8.0}; + constexpr auto distanceProduct = Vector3d{5.0, 10.0, 15.0}; + + REQUIRE((distance += anotherDistance) == distanceSum); + REQUIRE((distance -= anotherDistance) == distance); + REQUIRE((distance += scalarDistance) == distanceSum); + REQUIRE((distance -= scalarDistance) == distance); + + REQUIRE((distance *= anotherDistance) == distanceProduct); + REQUIRE((distance /= anotherDistance) == distance); + REQUIRE((distance *= scalarDistance) == distanceProduct); + REQUIRE((distance /= scalarDistance) == distance); +} + +TEST_CASE("Vector3d - Static Cast") +{ + constexpr auto distance = Vector3d{1.1, 2.2, 3.3}; + constexpr auto distanceInt = Vector3d{1, 2, 3}; + constexpr auto distanceCasted = static_cast>(distance); + + STATIC_REQUIRE(distance != distanceInt); + STATIC_REQUIRE(distanceCasted == distanceInt); +} + +TEST_CASE("Vector3d - Binary + and - Operators") +{ + constexpr auto distance = Vector3d{1.0, 2.0, 3.0}; + constexpr auto distanceMeter = Vector3d{1000.0, 2000.0, 3000.0}; + constexpr auto distanceTwice = Vector3d{2.0, 4.0, 6.0}; + constexpr auto distanceDiff = Vector3d{999.0, 1999.0, 2999.0}; + constexpr auto scalarMeter = 1.0; + + // clang-format off + STATIC_REQUIRE(distance + distance == distanceTwice); + STATIC_REQUIRE(scalarMeter + distanceMeter == distanceMeter + scalarMeter); + STATIC_REQUIRE(distance - distance == Vector3d{0}); + STATIC_REQUIRE(distanceMeter - scalarMeter == distanceDiff); + // clang-format on +} + +TEST_CASE("Vector3d - Binary * and / Operators") +{ + constexpr auto distance = Vector3d{1.0, 2.0, 4.0}; + constexpr auto time = Vector3d{2.0, 1.0, 4.0}; + constexpr auto speed = Vector3d{0.5, 2.0, 1.0}; + constexpr auto area = Vector3d{1.0, 4.0, 16.0}; + + STATIC_REQUIRE(area == distance * distance); + STATIC_REQUIRE(2 * distance == distance * 2); + STATIC_REQUIRE(speed == distance / time); + STATIC_REQUIRE(distance / 2 == distance * 0.5); +} + +TEST_CASE("Vector3d - Norm Functions") +{ + constexpr auto time = Vector3d{3.0, 4.0, 0.0}; + auto distance = Vector3d{3.0, 4.0, 0.0}; + + STATIC_REQUIRE(normSquared(time) == (25.0)); + REQUIRE(norm(time) == (5.0)); + REQUIRE(normalize(time) == Vector3d{0.6, 0.8, 0.0}); + normalize(distance); + REQUIRE(distance == Vector3d{0.6, 0.8, 0.0}); +} + +TEST_CASE("Vector3d - Product Functions") +{ + constexpr auto length1 = Vector3d{1.0, 2.0, 3.0}; + constexpr auto length2 = Vector3d{4.0, 5.0, 6.0}; + constexpr auto length3 = Vector3d{-3.0, 6.0, -3.0}; + + STATIC_REQUIRE(dot(length1, length2) == (32.0)); + STATIC_REQUIRE(cross(length1, length2) == length3); +} + +TEST_CASE("Vector3d - ostream <<") +{ + constexpr auto distance = Vector3d{1, 2, 3}; + + std::ostringstream oss; + oss << distance; + + REQUIRE(oss.str() == "1 2 3"); +} + +TEST_CASE("Vector3d - Nested Vector3d Indexing Operator") +{ + constexpr auto pos1 = Vector3d{1, 2, 3}; + constexpr auto pos2 = Vector3d{4, 5, 6}; + constexpr auto pos3 = Vector3d{7, 8, 9}; + constexpr auto pos = Vector3d{pos1, pos2, pos3}; + + STATIC_REQUIRE(pos[0] == pos1); + STATIC_REQUIRE(pos[1] == pos2); + STATIC_REQUIRE(pos[2] == pos3); + + STATIC_REQUIRE(pos[0][0] == 1); + STATIC_REQUIRE(pos[0][1] == 2); + STATIC_REQUIRE(pos[0][2] == 3); + + STATIC_REQUIRE(pos[1][0] == 4); + STATIC_REQUIRE(pos[1][1] == 5); + STATIC_REQUIRE(pos[1][2] == 6); + + STATIC_REQUIRE(pos[2][0] == 7); + STATIC_REQUIRE(pos[2][1] == 8); + STATIC_REQUIRE(pos[2][2] == 9); +} + +TEST_CASE("Vector3d - Nested Vector3d Unary +/-") +{ + constexpr auto pos1 = Vector3d{1, 2, 3}; + constexpr auto pos2 = Vector3d{4, 5, 6}; + constexpr auto pos3 = Vector3d{7, 8, 9}; + constexpr auto pos = Vector3d{pos1, pos2, pos3}; + constexpr auto negativePos = Vector3d{-pos1, -pos2, -pos3}; + + STATIC_REQUIRE(+pos == pos); + STATIC_REQUIRE(-pos == negativePos); +} + +TEST_CASE("Vector3d - Nested Vector3d Binary +") +{ + constexpr auto pos1 = Vector3d{1, 2, 3}; + constexpr auto pos2 = Vector3d{4, 5, 6}; + constexpr auto pos3 = Vector3d{7, 8, 9}; + constexpr auto pos = Vector3d{pos1, pos2, pos3}; + + constexpr auto shift1 = Vector3d{10, 11, 12}; + constexpr auto shift2 = Vector3d{13, 14, 15}; + constexpr auto shift3 = Vector3d{16, 17, 18}; + constexpr auto shift = Vector3d{shift1, shift2, shift3}; + + constexpr auto pos1_shifted = Vector3d{11, 13, 15}; + constexpr auto pos2_shifted = Vector3d{17, 19, 21}; + constexpr auto pos3_shifted = Vector3d{23, 25, 27}; + constexpr auto pos_shifted = + Vector3d{pos1_shifted, pos2_shifted, pos3_shifted}; + + STATIC_REQUIRE(pos + shift == pos_shifted); + STATIC_REQUIRE(shift + pos == pos_shifted); +} + +TEST_CASE("Vector3d - Nested Vector3d Binary -") +{ + constexpr auto pos1 = Vector3d{10, 11, 12}; + constexpr auto pos2 = Vector3d{13, 14, 15}; + constexpr auto pos3 = Vector3d{16, 17, 18}; + constexpr auto pos = Vector3d{pos1, pos2, pos3}; + + constexpr auto shift1 = Vector3d{9, 8, 7}; + constexpr auto shift2 = Vector3d{6, 5, 4}; + constexpr auto shift3 = Vector3d{3, 2, 1}; + constexpr auto shift = Vector3d{shift1, shift2, shift3}; + + constexpr auto pos1_shifted = Vector3d{1, 3, 5}; + constexpr auto pos2_shifted = Vector3d{7, 9, 11}; + constexpr auto pos3_shifted = Vector3d{13, 15, 17}; + constexpr auto pos_shifted = + Vector3d{pos1_shifted, pos2_shifted, pos3_shifted}; + + STATIC_REQUIRE(pos - shift == pos_shifted); +} + +TEST_CASE("Vector3d - Nested Vector3d Binary *") +{ + constexpr auto pos1 = Vector3d{1, 2, 3}; + constexpr auto pos2 = Vector3d{4, 5, 6}; + constexpr auto pos3 = Vector3d{7, 8, 9}; + constexpr auto pos = Vector3d{pos1, pos2, pos3}; + + constexpr auto factor1 = Vector3d{10, 11, 12}; + constexpr auto factor2 = Vector3d{13, 14, 15}; + constexpr auto factor3 = Vector3d{16, 17, 18}; + constexpr auto factor = Vector3d{factor1, factor2, factor3}; + + constexpr auto pos1_scaled = Vector3d{10, 22, 36}; + constexpr auto pos2_scaled = Vector3d{52, 70, 90}; + constexpr auto pos3_scaled = Vector3d{112, 136, 162}; + constexpr auto pos_scaled = Vector3d{pos1_scaled, pos2_scaled, pos3_scaled}; + + STATIC_REQUIRE(pos * factor == pos_scaled); + STATIC_REQUIRE(factor * pos == pos_scaled); +} + +TEST_CASE("Vector3d - Nested Vector3d Binary /") +{ + constexpr auto num1 = Vector3d{1, 4, 9}; + constexpr auto num2 = Vector3d{16, 25, 36}; + constexpr auto num3 = Vector3d{49, 64, 81}; + constexpr auto num = Vector3d{num1, num2, num3}; + + constexpr auto den1 = Vector3d{1, 2, 3}; + constexpr auto den2 = Vector3d{4, 5, 6}; + constexpr auto den3 = Vector3d{7, 8, 9}; + constexpr auto den = Vector3d{den1, den2, den3}; + + constexpr auto quotient1 = Vector3d{1, 2, 3}; + constexpr auto quotient2 = Vector3d{4, 5, 6}; + constexpr auto quotient3 = Vector3d{7, 8, 9}; + constexpr auto quotient = Vector3d{quotient1, quotient2, quotient3}; + + STATIC_REQUIRE(num / den == quotient); +} diff --git a/test/linAlg/vector3d_mp-units.cpp b/test/linAlg/vector3d_mp-units.cpp new file mode 100644 index 0000000..fdc7cb9 --- /dev/null +++ b/test/linAlg/vector3d_mp-units.cpp @@ -0,0 +1,283 @@ +/***************************************************************************** + + + mstd library + Copyright (C) 2025-now Jakob Gamper + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + +******************************************************************************/ + +#include +#include + +#include +#include + +#include "mstd/linAlg/vector3d.hpp" + +using namespace mp_units; +using namespace mp_units::si::unit_symbols; +using namespace mstd; + +TEST_CASE("Vector3d mp-units - Parametrized Constructors") +{ + constexpr auto valueConstructed = Vector3d{1 * m, 2 * m, 3 * m}; + STATIC_REQUIRE(valueConstructed == Vector3d{1 * m, 2 * m, 3 * m}); + + constexpr auto scalarConstructed = Vector3d{7 * m}; + STATIC_REQUIRE(scalarConstructed == Vector3d{7 * m, 7 * m, 7 * m}); +} + +TEST_CASE("Vector3d mp-units - Indexing Operators") +{ + constexpr auto distance = Vector3d{1000 * m, 2000 * m, 3000 * m}; + + STATIC_REQUIRE(distance[0] == 1 * km); + STATIC_REQUIRE(distance[1] == 2 * km); + STATIC_REQUIRE(distance[2] == 3 * km); + + auto position = Vector3d{0 * km}; + + position[0] = 1 * km; + position[1] = 2 * km; + position[2] = 3 * km; + + REQUIRE(distance == position); +} + +TEST_CASE("Vector3d mp-units - Comparison Operators") +{ + constexpr auto distance = Vector3d{1.0 * km, 2.0 * km, 3.0 * km}; + constexpr auto distanceMeter = Vector3d{1000.0 * m, 2000.0 * m, 3000.0 * m}; + constexpr auto anotherDistance = Vector3d{1.1 * km, 2.2 * km, 3.3 * km}; + + STATIC_REQUIRE(distance == distanceMeter); + STATIC_REQUIRE(distance != anotherDistance); +} + +TEST_CASE("Vector3d mp-units Specific Functions") +{ + constexpr auto distance = Vector3d{1.0 * km, 2.0 * km, 3.0 * km}; + constexpr auto distanceMeter = Vector3d{1000.0 * m, 2000.0 * m, 3000.0 * m}; + constexpr auto distanceMeterInt = Vector3d{1 * m, 2 * m, 3 * m}; + + // clang-format off + STATIC_REQUIRE(distance.in(m) == distanceMeter); + STATIC_REQUIRE(distanceMeterInt.force_in(km) == Vector3d{0 * m}); + STATIC_REQUIRE(distance.numerical_value_in(m) == Vector3d{1000.0, 2000.0, 3000.0}); + STATIC_REQUIRE(distanceMeterInt.force_numerical_value_in(km) == Vector3d{0.0}); + // clang-format on +} + +TEST_CASE("Vector3d mp-units - Unary +/- Operators") +{ + constexpr auto distance = Vector3d{1.0 * km, 2.0 * km, 3.0 * km}; + constexpr auto negativeDistance = Vector3d{-1.0 * km, -2.0 * km, -3.0 * km}; + + STATIC_REQUIRE(+distance == distance); + STATIC_REQUIRE(-distance == negativeDistance); +} + +TEST_CASE("Vector3d mp-units - Compound Assignment Operators") +{ + auto distance = Vector3d{1.0 * km, 2.0 * km, 3.0 * km}; + constexpr auto anotherDistance = Vector3d{5000.0 * m}; + constexpr auto scalarDistance = 5000.0 * m; + constexpr auto distanceSum = Vector3d{6.0 * km, 7.0 * km, 8.0 * km}; + + REQUIRE((distance += anotherDistance) == distanceSum); + REQUIRE((distance -= anotherDistance) == distance); + REQUIRE((distance += scalarDistance) == distanceSum); + REQUIRE((distance -= scalarDistance) == distance); +} + +TEST_CASE("Vector3d mp-units - Binary + and - Operators") +{ + constexpr auto distance = Vector3d{1.0 * km, 2.0 * km, 3.0 * km}; + constexpr auto distanceMeter = Vector3d{1000.0 * m, 2000.0 * m, 3000.0 * m}; + constexpr auto distanceTwice = Vector3d{2.0 * km, 4.0 * km, 6.0 * km}; + constexpr auto scalarMeter = 1.0 * m; + constexpr auto distanceDiff = Vector3d{999.0 * m, 1999.0 * m, 2999.0 * m}; + + // clang-format off + STATIC_REQUIRE(distance + distanceMeter == distanceTwice); + STATIC_REQUIRE(scalarMeter + distanceMeter == distanceMeter + scalarMeter); + STATIC_REQUIRE(distance - distanceMeter == Vector3d{0 * m}); + STATIC_REQUIRE(distanceMeter - scalarMeter == distanceDiff); + // clang-format on +} + +TEST_CASE("Vector3d mp-units - Binary * and / Operators") +{ + constexpr auto distance = Vector3d{1.0 * km, 2.0 * km, 4.0 * km}; + constexpr auto time = Vector3d{2.0 * h, 1.0 * h, 4.0 * h}; + constexpr auto speed = Vector3d{0.5 * km / h, 2.0 * km / h, 1.0 * km / h}; + constexpr auto area = Vector3d{1e6 * m2, 4e6 * m2, 16e6 * m2}; + + STATIC_REQUIRE(area == distance * distance); + STATIC_REQUIRE(2 * distance == distance * 2); + STATIC_REQUIRE(speed == distance / time); + STATIC_REQUIRE(distance / 2 == distance * 0.5); +} + +TEST_CASE("Vector3d mp-units - Norm Functions") +{ + constexpr auto time = Vector3d{3.0 * h, 4.0 * h, 0.0 * h}; + + STATIC_REQUIRE(normSquared(time) == (25.0 * h * h)); + REQUIRE(norm(time) == (5.0 * h)); + REQUIRE(normalize(time) == Vector3d{0.6, 0.8, 0.0}); +} + +TEST_CASE("Vector3d mp-units - Product Functions") +{ + constexpr auto length1 = Vector3d{1.0 * m, 2.0 * m, 3.0 * m}; + constexpr auto length2 = Vector3d{4.0 * m, 5.0 * m, 6.0 * m}; + constexpr auto length3 = Vector3d{-3.0 * m2, 6.0 * m2, -3.0 * m2}; + + STATIC_REQUIRE(dot(length1, length2) == (32.0 * m2)); + STATIC_REQUIRE(cross(length1, length2) == length3); +} + +TEST_CASE("Vector3d mp-units - ostream <<") +{ + constexpr auto distance = Vector3d{1 * km, 2 * km, 3 * km}; + + std::ostringstream oss; + oss << distance; + + REQUIRE(oss.str() == "1 km 2 km 3 km"); +} + +TEST_CASE("Vector3d mp-units - Nested Vector3d Indexing Operator") +{ + constexpr auto pos1 = Vector3d{1000 * m, 2000 * m, 3000 * m}; + constexpr auto pos2 = Vector3d{4000 * m, 5000 * m, 6000 * m}; + constexpr auto pos3 = Vector3d{7000 * m, 8000 * m, 9000 * m}; + constexpr auto pos = Vector3d{pos1, pos2, pos3}; + + STATIC_REQUIRE(pos[0] == pos1); + STATIC_REQUIRE(pos[1] == pos2); + STATIC_REQUIRE(pos[2] == pos3); + + STATIC_REQUIRE(pos[0][0] == 1 * km); + STATIC_REQUIRE(pos[0][1] == 2 * km); + STATIC_REQUIRE(pos[0][2] == 3 * km); + + STATIC_REQUIRE(pos[1][0] == 4 * km); + STATIC_REQUIRE(pos[1][1] == 5 * km); + STATIC_REQUIRE(pos[1][2] == 6 * km); + + STATIC_REQUIRE(pos[2][0] == 7 * km); + STATIC_REQUIRE(pos[2][1] == 8 * km); + STATIC_REQUIRE(pos[2][2] == 9 * km); +} + +TEST_CASE("Vector3d mp-units - Nested Vector3d Unary +/-") +{ + constexpr auto pos1 = Vector3d{1 * m, 2 * m, 3 * m}; + constexpr auto pos2 = Vector3d{4 * m, 5 * m, 6 * m}; + constexpr auto pos3 = Vector3d{7 * m, 8 * m, 9 * m}; + constexpr auto pos = Vector3d{pos1, pos2, pos3}; + constexpr auto negativePos = Vector3d{-pos1, -pos2, -pos3}; + + STATIC_REQUIRE(+pos == pos); + STATIC_REQUIRE(-pos == negativePos); +} + +TEST_CASE("Vector3d mp-units - Nested Vector3d Binary +") +{ + constexpr auto pos1 = Vector3d{1 * m, 2 * m, 3 * m}; + constexpr auto pos2 = Vector3d{4 * m, 5 * m, 6 * m}; + constexpr auto pos3 = Vector3d{7 * m, 8 * m, 9 * m}; + constexpr auto pos = Vector3d{pos1, pos2, pos3}; + + constexpr auto shift1 = Vector3d{10 * m, 11 * m, 12 * m}; + constexpr auto shift2 = Vector3d{13 * m, 14 * m, 15 * m}; + constexpr auto shift3 = Vector3d{16 * m, 17 * m, 18 * m}; + constexpr auto shift = Vector3d{shift1, shift2, shift3}; + + constexpr auto pos1_shifted = Vector3d{11 * m, 13 * m, 15 * m}; + constexpr auto pos2_shifted = Vector3d{17 * m, 19 * m, 21 * m}; + constexpr auto pos3_shifted = Vector3d{23 * m, 25 * m, 27 * m}; + constexpr auto pos_shifted = + Vector3d{pos1_shifted, pos2_shifted, pos3_shifted}; + + STATIC_REQUIRE(pos + shift == pos_shifted); + STATIC_REQUIRE(shift + pos == pos_shifted); +} + +TEST_CASE("Vector3d mp-units - Nested Vector3d Binary -") +{ + constexpr auto pos1 = Vector3d{10 * m, 11 * m, 12 * m}; + constexpr auto pos2 = Vector3d{13 * m, 14 * m, 15 * m}; + constexpr auto pos3 = Vector3d{16 * m, 17 * m, 18 * m}; + constexpr auto pos = Vector3d{pos1, pos2, pos3}; + + constexpr auto shift1 = Vector3d{9 * m, 8 * m, 7 * m}; + constexpr auto shift2 = Vector3d{6 * m, 5 * m, 4 * m}; + constexpr auto shift3 = Vector3d{3 * m, 2 * m, 1 * m}; + constexpr auto shift = Vector3d{shift1, shift2, shift3}; + + constexpr auto pos1_shifted = Vector3d{1 * m, 3 * m, 5 * m}; + constexpr auto pos2_shifted = Vector3d{7 * m, 9 * m, 11 * m}; + constexpr auto pos3_shifted = Vector3d{13 * m, 15 * m, 17 * m}; + constexpr auto pos_shifted = + Vector3d{pos1_shifted, pos2_shifted, pos3_shifted}; + + STATIC_REQUIRE(pos - shift == pos_shifted); +} + +TEST_CASE("Vector3d mp-units - Nested Vector3d Binary *") +{ + constexpr auto pos1 = Vector3d{1 * m, 2 * m, 3 * m}; + constexpr auto pos2 = Vector3d{4 * m, 5 * m, 6 * m}; + constexpr auto pos3 = Vector3d{7 * m, 8 * m, 9 * m}; + constexpr auto pos = Vector3d{pos1, pos2, pos3}; + + constexpr auto factor1 = Vector3d{10 * m, 11 * m, 12 * m}; + constexpr auto factor2 = Vector3d{13 * m, 14 * m, 15 * m}; + constexpr auto factor3 = Vector3d{16 * m, 17 * m, 18 * m}; + constexpr auto factor = Vector3d{factor1, factor2, factor3}; + + constexpr auto pos1_scaled = Vector3d{10 * m2, 22 * m2, 36 * m2}; + constexpr auto pos2_scaled = Vector3d{52 * m2, 70 * m2, 90 * m2}; + constexpr auto pos3_scaled = Vector3d{112 * m2, 136 * m2, 162 * m2}; + constexpr auto pos_scaled = Vector3d{pos1_scaled, pos2_scaled, pos3_scaled}; + + STATIC_REQUIRE(pos * factor == pos_scaled); + STATIC_REQUIRE(factor * pos == pos_scaled); +} + +TEST_CASE("Vector3d mp-units - Nested Vector3d Binary /") +{ + constexpr auto num1 = Vector3d{1 * m2, 4 * m2, 9 * m2}; + constexpr auto num2 = Vector3d{16 * m2, 25 * m2, 36 * m2}; + constexpr auto num3 = Vector3d{49 * m2, 64 * m2, 81 * m2}; + constexpr auto num = Vector3d{num1, num2, num3}; + + constexpr auto den1 = Vector3d{1 * m, 2 * m, 3 * m}; + constexpr auto den2 = Vector3d{4 * m, 5 * m, 6 * m}; + constexpr auto den3 = Vector3d{7 * m, 8 * m, 9 * m}; + constexpr auto den = Vector3d{den1, den2, den3}; + + constexpr auto quotient1 = Vector3d{1 * m, 2 * m, 3 * m}; + constexpr auto quotient2 = Vector3d{4 * m, 5 * m, 6 * m}; + constexpr auto quotient3 = Vector3d{7 * m, 8 * m, 9 * m}; + constexpr auto quotient = Vector3d{quotient1, quotient2, quotient3}; + + STATIC_REQUIRE(num / den == quotient); +}