#pragma once #include "mm/mmmatrix.hpp" #include #include #include #include namespace mm { namespace algorithm { // does nothing struct visit { visit() = default; template void operator()(Matrix& m) {} }; struct transpose : public visit { /// does not work with non-square matrices template void operator()(Matrix& m) { static_assert(Matrix::rows == Matrix::cols); // naiive impl for (index r = 0; r < m.rows / 2; r++) for (index c = 0; c < m.cols; c++) if (c != r) std::swap(m.at(r, c), m.at(c, r)); } }; /// algorithm aliases using tr = transpose; } /// namespace alias namespace alg = algorithm; template struct clone { Matrix matrix; explicit clone(Matrix&& m) : matrix(m) {} explicit clone(const Matrix& m) : matrix(m) {} operator Matrix() { return std::move(matrix); } }; template struct mutate { Matrix& matrix; std::tuple visitors; explicit mutate(Matrix& m) : matrix(m) {} template explicit mutate(Matrix& m, std::tuple&& t, Alg&& v) : matrix(m) { /// append the new operator visitors = std::tuple_cat(t, std::make_tuple(v)); } ~mutate() { visit(); } void visit() { std::apply([this](auto&&... v) { (v(matrix),...); }, visitors); } operator Matrix() { return std::move(matrix); } }; template clone operator|(clone&& cl, Alg&& v) { static_assert(std::is_convertible::value); /// apply alg operator v(cl.matrix); /// forward to next alg return clone(std::move(cl)); } template mutate operator|(mutate&& mut, Alg&& v) { static_assert(std::is_convertible::value); /// append alg to the visitors tuple return mutate( mut.matrix, std::move(mut.visitors), v ); } template mutate operator|(Matrix& m, Alg&& v) { return mutate(m) | std::move(v); } }