#ifndef Magnum_Math_Matrix_h #define Magnum_Math_Matrix_h /* Copyright © 2010, 2011, 2012 Vladimír Vondruš This file is part of Magnum. Magnum is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3 only, as published by the Free Software Foundation. Magnum 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 Lesser General Public License version 3 for more details. */ /** @file * @brief Class Magnum::Math::Matrix */ #include "Vector.h" namespace Magnum { namespace Math { namespace Implementation { template class MatrixDeterminant; #ifndef DOXYGEN_GENERATING_OUTPUT template struct Sequence {}; template struct GenerateSequence: GenerateSequence {}; template struct GenerateSequence<0, S...> { typedef Sequence Type; }; #endif } /** * @brief %Matrix * * @todo @c PERFORMANCE - loop unrolling for Matrix and Matrix * @todo first col, then row (cache adjacency) */ template class Matrix { public: /** * @brief %Matrix from array * @return Reference to the data as if it was Matrix, thus doesn't * perform any copying. * * @attention Use with caution, the function doesn't check whether the * array is long enough. */ inline constexpr static Matrix& from(T* data) { return *reinterpret_cast*>(data); } /** @copydoc from(T*) */ inline constexpr static const Matrix& from(const T* data) { return *reinterpret_cast*>(data); } /** * @brief %Matrix from column vectors * @param first First column vector * @param next Next column vectors */ template inline constexpr static Matrix from(const Vector& first, const U&... next) { return from(typename Implementation::GenerateSequence::Type(), first, next...); } /** * @brief Default constructor * @param identity Create identity matrix instead of zero matrix. */ inline explicit Matrix(bool identity = true): _data() { /** @todo constexpr how? */ if(identity) for(size_t i = 0; i != size; ++i) _data[size*i+i] = static_cast(1); } /** * @brief Initializer-list constructor * @param first First value * @param next Next values * * Note that the values are in column-major order. */ #ifndef DOXYGEN_GENERATING_OUTPUT template inline constexpr Matrix(T first, U... next): _data{first, next...} {} #else template inline constexpr Matrix(T first, U... next); #endif /** @brief Copy constructor */ inline constexpr Matrix(const Matrix& other) = default; /** @brief Assignment operator */ inline Matrix& operator=(const Matrix& other) = default; /** * @brief Raw data * @return One-dimensional array of `size*size` length in column-major * order. */ inline T* data() { return _data; } inline constexpr const T* data() const { return _data; } /**< @copydoc data() */ /** @brief %Matrix column */ inline Vector& operator[](size_t col) { return Vector::from(_data+col*size); } /** @copydoc operator[]() */ inline constexpr const Vector& operator[](size_t col) const { return Vector::from(_data+col*size); } /** @brief Equality operator */ inline bool operator==(const Matrix& other) const { for(size_t row = 0; row != size; ++row) for(size_t col = 0; col != size; ++col) if(!TypeTraits::equals((*this)[col][row], other[col][row])) return false; return true; } /** @brief Non-equality operator */ inline constexpr bool operator!=(const Matrix& other) const { return !operator==(other); } /** @brief Multiply matrix operator */ Matrix operator*(const Matrix& other) const { Matrix out(false); for(size_t row = 0; row != size; ++row) for(size_t col = 0; col != size; ++col) for(size_t pos = 0; pos != size; ++pos) out[col][row] += (*this)[pos][row]*other[col][pos]; return out; } /** @brief Multiply and assign matrix operator */ inline Matrix& operator*=(const Matrix& other) { return (*this = *this*other); } /** @brief Multiply vector operator */ Vector operator*(const Vector& other) const { Vector out; for(size_t row = 0; row != size; ++row) for(size_t pos = 0; pos != size; ++pos) out[row] += (*this)[pos][row]*other[pos]; return out; } /** @brief Transposed matrix */ Matrix transposed() const { Matrix out(false); for(size_t row = 0; row != size; ++row) for(size_t col = 0; col != size; ++col) out[row][col] = (*this)[col][row]; return out; } /** @brief %Matrix without given column and row */ Matrix ij(size_t skipCol, size_t skipRow) const { Matrix out(false); for(size_t row = 0; row != size-1; ++row) for(size_t col = 0; col != size-1; ++col) out[col][row] = (*this)[col + (col >= skipCol)] [row + (row >= skipRow)]; return out; } /** * @brief Determinant * * Computed recursively using Laplace's formula expanded down to 2x2 * matrix, where the determinant is computed directly. Complexity is * O(n!), the same as when computing the determinant directly. */ inline T determinant() const { return Implementation::MatrixDeterminant()(*this); } /** * @brief Inverse matrix */ Matrix inversed() const { Matrix out(false); T _determinant = determinant(); for(size_t row = 0; row != size; ++row) for(size_t col = 0; col != size; ++col) out[col][row] = (((row+col) & 1) ? -1 : 1)*ij(row, col).determinant()/_determinant; return out; } private: template inline constexpr static Matrix from(Implementation::Sequence s, const Vector& first, U... next) { return from(s, next..., first[sequence]...); } template inline constexpr static Matrix from(Implementation::Sequence s, T first, U... next) { return Matrix(first, next...); } T _data[size*size]; }; namespace Implementation { /** @brief Matrix determinant implementation for >2x2 matrices */ template class MatrixDeterminant { public: /** @brief Functor */ T operator()(const Matrix& m) { T out(0); for(size_t col = 0; col != size; ++col) out += ((col & 1) ? -1 : 1)*m[col][0]*m.ij(col, 0).determinant(); return out; } }; /** @brief Matrix determinant implementation for 2x2 matrix */ template class MatrixDeterminant { public: /** @brief Functor */ inline constexpr T operator()(const Matrix& m) { return m[0][0]*m[1][1] - m[1][0]*m[0][1]; } }; /** @brief Matrix determinant implementation for 1x1 matrix */ template class MatrixDeterminant { public: /** @brief Functor */ inline constexpr T operator()(const Matrix& m) { return m[0][0]; } }; } #ifndef DOXYGEN_GENERATING_OUTPUT template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Math::Matrix& value) { debug << "Matrix("; debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, false); for(size_t row = 0; row != size; ++row) { if(row != 0) debug << ",\n "; for(size_t col = 0; col != size; ++col) { if(col != 0) debug << ", "; debug << value[col][row]; } } debug << ')'; debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, true); return debug; } #endif }} #endif