summaryrefslogtreecommitdiffstats
path: root/include/mm
diff options
context:
space:
mode:
Diffstat (limited to 'include/mm')
-rw-r--r--include/mm/mm.hpp (renamed from include/mm)0
-rw-r--r--include/mm/mmmatrix.hpp374
-rw-r--r--include/mm/mmvec.hpp319
3 files changed, 693 insertions, 0 deletions
diff --git a/include/mm b/include/mm/mm.hpp
index 7e9f02b..7e9f02b 100644
--- a/include/mm
+++ b/include/mm/mm.hpp
diff --git a/include/mm/mmmatrix.hpp b/include/mm/mmmatrix.hpp
new file mode 100644
index 0000000..5285429
--- /dev/null
+++ b/include/mm/mmmatrix.hpp
@@ -0,0 +1,374 @@
+/* mmmatrix.hpp
+ * Part of Mathematical library built (ab)using Modern C++ 17 abstractions.
+ *
+ * This library is not intended to be _performant_, it does not contain
+ * hand written SMID / SSE / AVX optimizations. It is instead an example
+ * of highly inefficient (but abstract!) code, where matrices can contain any
+ * data type.
+ *
+ * Naoki Pross <naopross@thearcway.org>
+ * 2018 ~ 2019
+ */
+#pragma once
+
+#include <iostream>
+#include <iomanip>
+#include <cstring>
+#include <cassert>
+#include <initializer_list>
+#include <array>
+
+namespace mm {
+ template<typename T, std::size_t Rows, std::size_t Cols>
+ class basic_matrix;
+
+ /* specialization of basic_matrx for Cols = 1 */
+ template<typename T, std::size_t Rows>
+ class row_vec;
+
+ /* specialization of basic_matrx for Rows = 1 */
+ template<typename T, std::size_t Cols>
+ class col_vec;
+
+ /* shorter name for basic_matrix */
+ template<typename T, std::size_t Rows, std::size_t Cols>
+ class matrix;
+
+ /* specialization of basic_matrix for Rows == Cols */
+ template<typename T, std::size_t N>
+ class square_matrix;
+
+ // template<typename T, std::size_t N>
+ // class diag_matrix;
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+class mm::basic_matrix {
+public:
+ using type = T;
+
+ template<typename U, std::size_t ORows, std::size_t OCols>
+ friend class mm::basic_matrix;
+
+ static constexpr std::size_t rows = Rows;
+ static constexpr std::size_t cols = Cols;
+
+ basic_matrix();
+
+ // from initializer_list
+ basic_matrix(std::initializer_list<std::initializer_list<T>> l);
+
+ // copyable and movable
+ basic_matrix(const basic_matrix<T, Rows, Cols>& other);
+ basic_matrix(basic_matrix<T, Rows, Cols>&& other);
+
+ // copy from another matrix
+ template<std::size_t ORows, std::size_t OCols>
+ basic_matrix(const basic_matrix<T, ORows, OCols>& other);
+
+ // access data
+ T& at(std::size_t row, std::size_t col);
+ const T& at(std::size_t row, std::size_t col) const;
+ // allows to access a matrix M at row j col k with M[j][k]
+ auto operator[](std::size_t index);
+
+ void swap_rows(std::size_t x, std::size_t y);
+ void swap_cols(std::size_t x, std::size_t y);
+
+ // mathematical operations
+ virtual basic_matrix<T, Cols, Rows> transposed() const;
+ inline basic_matrix<T, Cols, Rows> td() const { return transposed(); }
+
+ // bool is_invertible() const;
+ // basic_matrix<T, Rows, Cols> inverse() const;
+
+
+ /// downcast to square matrix
+ static inline constexpr bool is_square() { return (Rows == Cols); }
+ inline constexpr square_matrix<T, Rows> to_square() const {
+ static_assert(is_square());
+ return static_cast<square_matrix<T, Rows>>(*this);
+ }
+
+
+ /// downcast to row_vector
+ static inline constexpr bool is_row_vec() { return (Cols == 1); }
+ inline constexpr row_vec<T, Rows> to_row_vec() const {
+ static_assert(is_row_vec());
+ return static_cast<row_vec<T, Rows>>(*this);
+ }
+
+ /// downcast to col_vector
+ static inline constexpr bool is_col_vec() { return (Rows == 1); }
+ inline constexpr col_vec<T, Cols> to_col_vec() const {
+ static_assert(is_col_vec());
+ return static_cast<col_vec<T, Cols>>(*this);
+ }
+
+protected:
+ template<typename ConstIterator>
+ basic_matrix(ConstIterator begin, ConstIterator end);
+
+private:
+ std::array<T, Rows * Cols> data;
+};
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols>::basic_matrix() {
+ std::fill(data.begin(), data.end(), 0);
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols>::basic_matrix(
+ std::initializer_list<std::initializer_list<T>> l
+) {
+ assert(l.size() == Rows);
+ auto data_it = data.begin();
+
+ for (auto&& row : l) {
+ data_it = std::copy(row.begin(), row.end(), data_it);
+ }
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols>::basic_matrix(
+ const mm::basic_matrix<T, Rows, Cols>& other
+) : data(other.data) {}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols>::basic_matrix(
+ mm::basic_matrix<T, Rows, Cols>&& other
+) : data(std::forward<decltype(other.data)>(other.data)) {}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+template<std::size_t ORows, std::size_t OCols>
+mm::basic_matrix<T, Rows, Cols>::basic_matrix(
+ const mm::basic_matrix<T, ORows, OCols>& other
+) {
+ static_assert((ORows <= Rows),
+ "cannot copy a taller matrix into a smaller one"
+ );
+
+ static_assert((OCols <= Cols),
+ "cannot copy a larger matrix into a smaller one"
+ );
+
+ std::fill(data.begin(), data.end(), 0);
+ for (unsigned row = 0; row < Rows; row++)
+ for (unsigned col = 0; col < Cols; col++)
+ this->at(row, col) = other.at(row, col);
+}
+
+/* protected construtor */
+template<typename T, std::size_t Rows, std::size_t Cols>
+template<typename ConstIterator>
+mm::basic_matrix<T, Rows, Cols>::basic_matrix(
+ ConstIterator begin, ConstIterator end
+) {
+ assert(static_cast<unsigned>(std::distance(begin, end)) >= ((Rows * Cols)));
+ std::copy(begin, end, data.begin());
+}
+
+
+/* member functions */
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+T& mm::basic_matrix<T, Rows, Cols>::at(std::size_t row, std::size_t col) {
+ assert(row < Rows); // "out of row bound"
+ assert(col < Cols); // "out of column bound"
+
+ return data[row * Cols + col];
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+const T& mm::basic_matrix<T, Rows, Cols>::at(std::size_t row, std::size_t col) const {
+ assert(row < Rows); // "out of row bound"
+ assert(col < Cols); // "out of column bound"
+
+ return data[row * Cols + col];
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+auto mm::basic_matrix<T, Rows, Cols>::operator[](std::size_t index) {
+ if constexpr (is_row_vec() || is_col_vec()) {
+ return data.at(index);
+ } else {
+ return row_vec<T, Rows>(
+ data.cbegin() + (index * Cols),
+ data.cbegin() + ((index + 1) * Cols) + 1
+ );
+ }
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+void mm::basic_matrix<T, Rows, Cols>::swap_rows(std::size_t x, std::size_t y) {
+ if (x == y)
+ return;
+
+ for (unsigned col = 0; col < Cols; col++)
+ std::swap(this->at(x, col), this->at(y, col));
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+void mm::basic_matrix<T, Rows, Cols>::swap_cols(std::size_t x, std::size_t y) {
+ if (x == y)
+ return;
+
+ for (unsigned row = 0; row < rows; row++)
+ std::swap(this->at(row, x), this->at(row, y));
+}
+
+template<typename T, std::size_t M, std::size_t N>
+mm::basic_matrix<T, N, M> mm::basic_matrix<T, M, N>::transposed() const {
+ mm::basic_matrix<T, N, M> result;
+
+ for (unsigned row = 0; row < M; row++)
+ for (unsigned col = 0; col < N; col++)
+ result.at(col, row) = this->at(row, col);
+
+ return result;
+}
+
+
+/* operator overloading */
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols> operator+(
+ const mm::basic_matrix<T, Rows, Cols>& a,
+ const mm::basic_matrix<T, Rows, Cols>& b
+) {
+ mm::basic_matrix<T, Rows, Cols> result;
+
+ for (unsigned row = 0; row < Rows; row++)
+ for (unsigned col = 0; col < Cols; col++)
+ result.at(row, col) = a.at(row, col) + b.at(row, col);
+
+ return result;
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols> operator*(
+ const mm::basic_matrix<T, Rows, Cols>& m,
+ const T& scalar
+) {
+ mm::basic_matrix<T, Rows, Cols> result;
+ for (unsigned row = 0; row < Rows; row++)
+ for (unsigned col = 0; col < Cols; col++)
+ result.at(row, col) = m.at(row, col) * scalar;
+
+ return result;
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols> operator*(
+ const T& scalar,
+ const mm::basic_matrix<T, Rows, Cols>& m
+) {
+ return m * scalar;
+}
+
+template<typename T, std::size_t M, std::size_t P1, std::size_t P2, std::size_t N>
+mm::basic_matrix<T, M, N> operator*(
+ const mm::basic_matrix<T, M, P1>& a,
+ const mm::basic_matrix<T, P2, N>& b
+) {
+ static_assert(P1 == P2, "invalid matrix multiplication");
+ mm::basic_matrix<T, M, N> result;
+
+ // TODO: use a more efficient algorithm
+ for (unsigned row = 0; row < M; row++)
+ for (unsigned col = 0; col < N; col++)
+ for (unsigned k = 0; k < P1; k++)
+ result.at(row, col) = a.at(row, k) * b.at(k, col);
+
+ return result;
+}
+
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols> operator-(
+ const mm::basic_matrix<T, Rows, Cols>& a,
+ const mm::basic_matrix<T, Rows, Cols>& b
+) {
+ return a + (static_cast<T>(-1) * b);
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols, unsigned NumW = 3>
+std::ostream& operator<<(std::ostream& os, const mm::basic_matrix<T, Rows, Cols>& m) {
+ for (unsigned row = 0; row < Rows; row++) {
+ os << "[ ";
+ for (unsigned col = 0; col < (Cols -1); col++) {
+ os << std::setw(NumW) << m.at(row, col) << ", ";
+ }
+ os << std::setw(NumW) << m.at(row, (Cols -1)) << " ]\n";
+ }
+
+ return os;
+}
+
+
+
+
+/* row vector specialization */
+template<typename T, std::size_t Rows>
+class mm::row_vec : public mm::basic_matrix<T, Rows, 1> {
+public:
+ using mm::basic_matrix<T, Rows, 1>::basic_matrix;
+};
+
+/* column vector specialization */
+template<typename T, std::size_t Cols>
+class mm::col_vec : public mm::basic_matrix<T, 1, Cols> {
+public:
+ using mm::basic_matrix<T, 1, Cols>::basic_matrix;
+};
+
+/* general specialization (alias) */
+template<typename T, std::size_t Rows, std::size_t Cols>
+class mm::matrix : public mm::basic_matrix<T, Rows, Cols> {
+public:
+ using mm::basic_matrix<T, Rows, Cols>::basic_matrix;
+};
+
+/* square matrix specializaiton */
+template<typename T, std::size_t N>
+class mm::square_matrix : public mm::basic_matrix<T, N, N> {
+public:
+ using mm::basic_matrix<T, N, N>::basic_matrix;
+
+ /// in place transpose
+ void transpose();
+ inline void t() { transpose(); }
+
+ T trace();
+ inline T tr() { return trace(); }
+
+ /// in place inverse
+ void invert();
+
+ // get the identity of size N
+ static inline constexpr square_matrix<T, N> identity() {
+ square_matrix<T, N> i;
+ for (unsigned row = 0; row < N; row++)
+ for (unsigned col = 0; col < N; col++)
+ i.at(row, col) = (row == col) ? 1 : 0;
+
+ return i;
+ }
+};
+
+
+template<typename T, std::size_t N>
+void mm::square_matrix<T, N>::transpose() {
+ for (unsigned row = 0; row < N; row++)
+ for (unsigned col = 0; col < row; col++)
+ std::swap(this->at(row, col), this->at(col, row));
+}
+
+template<typename T, std::size_t N>
+T mm::square_matrix<T, N>::trace() {
+ T sum = 0;
+ for (unsigned i = 0; i < N; i++)
+ sum += this->at(i, i);
+
+ return sum;
+}
diff --git a/include/mm/mmvec.hpp b/include/mm/mmvec.hpp
new file mode 100644
index 0000000..1939388
--- /dev/null
+++ b/include/mm/mmvec.hpp
@@ -0,0 +1,319 @@
+/* mmvec.hpp
+ * Part of Mathematical library built (ab)using Modern C++ 17 abstractions.
+ *
+ * This library is not intended to be _performant_, it does not contain
+ * hand written SMID / SSE / AVX optimizations. It is instead an example
+ * of highly abstracted code, where Vectors can contain any data type.
+ *
+ * As a challenge, the vector data structure has been built on a container
+ * of static capacity. But if a dynamic base container is needed, the code
+ * should be easily modifiable to add further abstraction, by templating
+ * the container, and by consequence the allocator.
+ *
+ * Naoki Pross <naopross@thearcway.org>
+ * 2018 ~ 2019
+ */
+#pragma once
+
+#include <cassert>
+#include <cmath>
+
+#include <iostream>
+#include <array>
+#include <algorithm>
+#include <numeric>
+#include <complex>
+#include <initializer_list>
+
+namespace mm {
+ // generic implementation
+ template<typename T, std::size_t d>
+ struct basic_vec;
+
+ // usable specializations
+ template<typename T, std::size_t d>
+ struct vec;
+ template<typename T>
+ struct vec3;
+ template<typename T>
+ struct vec2;
+}
+
+template<typename T, std::size_t d>
+struct mm::basic_vec : public std::array<T, d> {
+ using type = T;
+ static constexpr std::size_t dimensions = d;
+
+ // TODO: template away these
+ static constexpr T null_element = static_cast<T>(0);
+ static constexpr T unit_element = static_cast<T>(1);
+ static constexpr T unit_additive_inverse_element = static_cast<T>(-1);
+
+ basic_vec();
+ basic_vec(const std::initializer_list<T> l);
+
+ // copyable to a vector of size n <= d
+ template<std::size_t n> basic_vec(const basic_vec<T, n>& other);
+ // movable to a vector of size n <= d
+ template<std::size_t n> basic_vec(basic_vec<T, n>&& other);
+
+ T length() const;
+
+ // copy operator=
+ template<std::size_t n>
+ basic_vec<T, d>& operator=(const mm::basic_vec<T, n>& other);
+
+ // move operator=
+ template<std::size_t n>
+ basic_vec<T, d>& operator=(mm::basic_vec<T, n>&& other);
+
+ template<std::size_t n>
+ basic_vec<T, d>& operator+=(const mm::basic_vec<T, n>& other);
+
+ template<std::size_t n>
+ basic_vec<T, d>& operator-=(const mm::basic_vec<T, n>& other);
+
+
+ basic_vec<T, d>& operator*=(const T& scalar);
+};
+
+
+// member functions for basic_vec
+
+template<typename T, std::size_t d>
+mm::basic_vec<T, d>::basic_vec() : std::array<T, d>() {
+ this->fill(basic_vec<T, d>::null_element);
+}
+
+template<typename T, std::size_t d>
+mm::basic_vec<T, d>::basic_vec(const std::initializer_list<T> l) {
+ // why can't this sh*t be a constexpr with static_assert???
+ assert(l.size() <= d);
+ std::copy(l.begin(), l.end(), this->begin());
+}
+
+template<typename T, std::size_t d>
+template<std::size_t n>
+mm::basic_vec<T, d>::basic_vec(const mm::basic_vec<T, n>& other) {
+ *this = other;
+}
+
+template<typename T, std::size_t d>
+template<std::size_t n>
+mm::basic_vec<T, d>::basic_vec(basic_vec<T, n>&& other) {
+ *this = other;
+}
+
+template<typename T, std::size_t d>
+T mm::basic_vec<T, d>::length() const {
+ return std::sqrt(std::accumulate(this->begin(), this->end(),
+ basic_vec<T, d>::null_element,
+ [](const T& init, const T& val) -> T {
+ return init + val * val;
+ }
+ ));
+}
+
+
+// memeber operator overloads for basic_vec
+
+template<typename T, std::size_t d>
+template<std::size_t n>
+mm::basic_vec<T, d>& mm::basic_vec<T, d>::operator=(const mm::basic_vec<T, n>& other) {
+ static_assert(
+ d >= n, "cannot copy higher dimensional vector into a smaller one"
+ );
+
+ std::copy(other.begin(), other.end(), this->begin());
+
+ return *this;
+}
+
+template<typename T, std::size_t d>
+template<std::size_t n>
+mm::basic_vec<T, d>& mm::basic_vec<T, d>::operator=(mm::basic_vec<T, n>&& other) {
+ static_assert(
+ d >= n, "cannot move a higher dimensional vector into a smaller one"
+ );
+
+ std::move(other.begin(), other.end(), this->begin());
+
+ return *this;
+}
+
+
+template<typename T, std::size_t d>
+template<std::size_t n>
+mm::basic_vec<T, d>& mm::basic_vec<T, d>::operator+=(const mm::basic_vec<T, n>& other) {
+ *this = *this + other;
+ return *this;
+}
+
+template<typename T, std::size_t d>
+template<std::size_t n>
+mm::basic_vec<T, d>& mm::basic_vec<T, d>::operator-=(const mm::basic_vec<T, n>& other) {
+ *this = *this - other;
+ return *this;
+}
+
+template<typename T, std::size_t d>
+mm::basic_vec<T, d>& mm::basic_vec<T, d>::operator*=(const T& scalar) {
+ *this = *this * scalar;
+ return *this;
+}
+
+
+// operator overloads for basic_vec
+
+template<typename T, std::size_t d>
+mm::basic_vec<T, d> operator+(const mm::basic_vec<T, d>& rhs, const mm::basic_vec<T, d>& lhs) {
+ mm::basic_vec<T, d> out;
+
+ std::transform(rhs.begin(), rhs.end(), lhs.begin(), out.begin(),
+ [](const T& r, const T& l) -> T {
+ return r + l;
+ }
+ );
+
+ return out;
+}
+
+template<typename T, std::size_t d>
+mm::basic_vec<T, d> operator*(const mm::basic_vec<T, d>& rhs, const T& lhs) {
+ return lhs * rhs;
+}
+
+template<typename T, std::size_t d>
+mm::basic_vec<T, d> operator*(const T& rhs, const mm::basic_vec<T, d>& lhs) {
+ mm::basic_vec<T, d> out;
+
+ std::transform(lhs.begin(), lhs.end(), out.begin(),
+ [rhs](const T& t) -> T {
+ return t * rhs;
+ });
+
+ return out;
+}
+
+template<typename T, std::size_t d>
+mm::basic_vec<T, d> operator-(const mm::basic_vec<T, d>& rhs, const mm::basic_vec<T, d>& lhs) {
+ return rhs + mm::basic_vec<T, d>::unit_additive_inverse_element * lhs;
+}
+
+template<typename T, std::size_t d>
+T operator*(const mm::basic_vec<T, d>& rhs, const mm::basic_vec<T, d>& lhs) {
+ return std::inner_product(rhs.begin(), rhs.end(), lhs.begin(), 0);
+}
+
+template<typename T, std::size_t d>
+std::ostream& operator<<(std::ostream& os, const mm::basic_vec<T, d>& v) {
+ os << "<";
+ std::for_each(v.begin(), v.end() -1, [&](const T& el) {
+ os << el << ", ";
+ });
+ os << v.back() << ">";
+
+ return os;
+}
+
+
+// actual vectors to use in your code
+
+template<typename T, std::size_t d>
+class mm::vec: public mm::basic_vec<T, d> {
+public:
+ using mm::basic_vec<T, d>::basic_vec;
+};
+
+
+// three dimensional specialization with a static cross product
+// TODO: specialize operator+ for spherical coordinates
+
+template<typename T>
+class mm::vec3 : public mm::basic_vec<T, 3> {
+public:
+ using mm::basic_vec<T, 3>::basic_vec;
+
+ T& x() { return this->at(0); }
+ T& y() { return this->at(1); }
+ T& z() { return this->at(2); }
+
+ const T& x() const { return this->at(0); }
+ const T& y() const { return this->at(1); }
+ const T& z() const { return this->at(2); }
+
+ T zenith() const;
+ T azimuth() const;
+ vec3<T> spherical() const;
+
+ static vec3<T> cross(const vec3<T>& rhs, const vec3<T>& lhs);
+};
+
+template<typename T>
+T mm::vec3<T>::zenith() const {
+ return std::acos(this->z() / this->length());
+}
+
+template<typename T>
+T mm::vec3<T>::azimuth() const {
+ return std::atan(this->y() / this->x());
+}
+
+template<typename T>
+mm::vec3<T> mm::vec3<T>::spherical() const {
+ return mm::vec3<T> {
+ this->length(),
+ this->zenith(),
+ this->azimuth(),
+ };
+}
+
+template<typename T>
+mm::vec3<T> mm::vec3<T>::cross(const vec3<T>& rhs, const vec3<T>& lhs) {
+ mm::vec3<T> res;
+
+ res.x() = (rhs.y() * lhs.z()) - (rhs.z() * lhs.y());
+ res.y() = (rhs.z() * lhs.x()) - (rhs.x() * lhs.z());
+ res.z() = (rhs.x() * lhs.y()) - (rhs.y() * lhs.x());
+
+ return res;
+}
+
+
+// two dimensional specialization with a polar conversion
+// TODO: specialize operator+ for polar coordinates
+
+template<typename T>
+class mm::vec2: public mm::basic_vec<T, 2> {
+public:
+ using mm::basic_vec<T, 2>::basic_vec;
+
+ T& x() { return this->at(0); }
+ T& y() { return this->at(1); }
+
+ const T& x() const { return this->at(0); }
+ const T& y() const { return this->at(1); }
+
+ T angle() const;
+ vec2<T> polar() const;
+
+ static vec3<T> cross(const vec2<T>& rhs, const vec2<T>& lhs);
+};
+
+template<typename T>
+T mm::vec2<T>::angle() const {
+ return std::atan(this->y() / this->x());
+}
+
+template<typename T>
+mm::vec2<T> mm::vec2<T>::polar() const {
+ return {
+ this->length(),
+ this->angle()
+ };
+}
+
+template<typename T>
+mm::vec3<T> mm::vec2<T>::cross(const mm::vec2<T>& rhs, const mm::vec2<T>& lhs) {
+ return mm::vec3<T>::cross(mm::vec3<T>(rhs), mm::vec3<T>(lhs));
+}