- fixed some small bugs
authorAkiko <akiko@linux-addicted.net>
Fri, 25 Oct 2013 10:39:27 +0000 (12:39 +0200)
committerAkiko <akiko@linux-addicted.net>
Fri, 25 Oct 2013 10:39:27 +0000 (12:39 +0200)
- replaced default values by floats one (implicit converting from float to int is the better way)
- added some missing operators and methods

engine/Matrix.hxx

index e4c235c..186b481 100644 (file)
@@ -8,9 +8,6 @@
  *  ~ Akiko ~
  */
 
-#include <cmath>
-#include <iostream>
-#include <sstream>
 #include "Vector.hxx"
 
 // --- generic forwarder ---
@@ -23,7 +20,7 @@ class Matrix;
 template <typename MT, size_t C, size_t R>
 class Matrix {
 public:
-    // --- public data structures ---
+    // --- Generic Matrix: public data structures ---
 
     enum : size_t {
         columns = C,
@@ -36,7 +33,7 @@ public:
         MT m[C][R];
     };
 
-    // --- constructors and deconstructors ---
+    // --- Generic Matrix: constructors and deconstructors ---
 
     Matrix()
     {
@@ -59,7 +56,7 @@ public:
     {
     }
 
-    // --- default operators ---
+    // --- Generic Matrix: default operators ---
 
     Matrix& operator=(const Matrix& rhs)
     {
@@ -104,9 +101,210 @@ public:
         return ml[cell];
     }
 
-    // --- other operators ---
+    // --- Generic Matrix: other operators ---
 
-    // --- public methodes ---
+    Matrix& operator+=(const Matrix& rhs)
+    {
+        for (size_t cell = 0; cell < cells; ++cell)
+            ml[cell] += rhs.ml[cell];
+
+        return *this;
+    }
+
+    template <typename T>
+    Matrix& operator+=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        for (size_t cell = 0; cell < cells; ++cell)
+            ml[cell] += val;
+
+        return *this;
+    }
+
+    Matrix operator+(const Matrix& rhs) const
+    {
+        Matrix mat(*this);
+
+        for (size_t cell = 0; cell < cells; ++cell)
+            mat.ml[cell] += rhs.ml[cell];
+
+        return mat;
+    }
+
+    template <typename T>
+    Matrix operator+(const T& val) const
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        Matrix mat(*this);
+
+        for (size_t cell = 0; cell < cells; ++cell)
+            mat.ml[cell] += val;
+
+        return mat;
+    }
+
+    // ---
+
+    Matrix& operator-=(const Matrix& rhs)
+    {
+        for (size_t cell = 0; cell < cells; ++cell)
+            ml[cell] -= rhs.ml[cell];
+
+        return *this;
+    }
+
+    template <typename T>
+    Matrix& operator-=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        for (size_t cell = 0; cell < cells; ++cell)
+            ml[cell] -= val;
+
+        return *this;
+    }
+
+    Matrix operator-(const Matrix& rhs) const
+    {
+        Matrix mat(*this);
+
+        for (size_t cell = 0; cell < cells; ++cell)
+            mat.ml[cell] -= rhs.ml[cell];
+
+        return mat;
+    }
+
+    template <typename T>
+    Matrix operator-(const T& val) const
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        Matrix mat(*this);
+
+        for (size_t cell = 0; cell < cells; ++cell)
+            mat.ml[cell] -= val;
+
+        return *this;
+    }
+
+    // ---
+
+    // this one is very tricky, do not use it, it will fail in many ways
+    Matrix& operator*=(const Matrix& rhs)
+    {
+        const MT other_dim = columns;
+        Matrix mat(*this);
+
+        for (size_t row = 0; row < rows; ++row)
+        {
+            for (size_t col = 0; col < columns; ++col)
+            {
+                MT sum = 0.0;
+
+                for (size_t i = 0; i < other_dim; ++i)
+                    sum += mat.m[i][row] * rhs.m[col][i];
+
+                mat.m[col][row] = sum;
+            }
+        }
+        *this = mat;
+
+        return *this;
+    }
+
+    template <typename T>
+    Matrix& operator*=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        for (size_t cell = 0; cell < cells; ++cell)
+            ml[cell] *= val;
+
+        return *this;
+    }
+
+    // this one is very tricky, do not use it, it will fail in many ways
+    Matrix operator*(const Matrix& rhs) const
+    {
+        const MT other_dim = columns;
+        Matrix mat(*this);
+
+        for (size_t row = 0; row < rows; ++row)
+        {
+            for (size_t col = 0; col < columns; ++col)
+            {
+                MT sum = 0.0;
+
+                for (size_t i = 0; i < other_dim; ++i)
+                    sum += mat.m[i][row] * rhs.m[col][i];
+
+                mat.m[col][row] = sum;
+            }
+        }
+
+        return mat;
+    }
+
+    template <typename T>
+    Matrix operator*(const T& val) const
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        Matrix mat(*this);
+
+        for (size_t cell = 0; cell < cells; ++cell)
+            mat.ml[cell] *= val;
+
+        return mat;
+    }
+
+    // ---
+
+    template <typename T>
+    Matrix& operator/=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        for (size_t cell = 0; cell < cells; ++cell)
+            ml[cell] /= val;
+
+        return *this;
+    }
+
+    template <typename T>
+    Matrix operator/(const T& val) const
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        Matrix mat(*this);
+
+        for (size_t cell = 0; cell < cells; ++cell)
+            mat.ml[cell] /= val;
+
+        return mat;
+    }
+
+    // --- Generic Matrix: public methodes ---
+
+    template <typename T>
+    void fill(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        for (size_t cell = 0; cell < cells; ++cell)
+            ml[cell] = val;
+    }
 
     bool isIdentity() const
     {
@@ -143,14 +341,270 @@ public:
             }
         }
     }
+
+    std::string string() const
+    {
+        std::stringstream result;
+
+        result << "(";
+        for (size_t row = 0; row < rows; ++row)
+        {
+            result << "(";
+            for (size_t col = 0; col < columns; ++col)
+            {
+                result << m[col][row];
+                if (col < (columns - 1))
+                    result << ", ";
+            }
+            result << ")";
+            if (row < (rows - 1))
+                result << ", ";
+        }
+        result << ")";
+
+        return result.str();
+    }
 };
 
-// --- 4x4 matrix ---
+// --- Generic Matrix: global operators ---
+
+template <typename T, size_t C, size_t R>
+bool operator==(const Matrix<T, C, R>& mat1, const Matrix<T, C, R>& mat2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    bool result = true;
+
+    for (size_t cell = 0; cell < (C * R); ++cell)
+        result = result && mat1.ml[cell] == mat2.ml[cell];
+
+    return result;
+}
+
+template <typename T, size_t C, size_t R>
+bool operator!=(const Matrix<T, C, R>& mat1, const Matrix<T, C, R>& mat2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return !(mat1 == mat2);
+}
+
+template <typename T, size_t C, size_t R>
+std::ostream& operator<<(std::ostream& lhs, const Matrix<T, C, R>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    for (size_t cell = 0; cell < (C * R); ++cell)
+        lhs << rhs.ml[cell];
+
+    return lhs;
+}
+
+template <typename T, size_t C, size_t R>
+std::istream& operator<<(std::istream& lhs, Matrix<T, C, R>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    for (size_t cell = 0; cell < (C * R); ++cell)
+        lhs >> rhs.ml[cell];
+
+    return lhs;
+}
+
+// ---
+
+template <typename T, size_t C, size_t R>
+Matrix<T, C, R> operator+(const Matrix<T, C, R>& mat1, const Matrix<T, C, R>& mat2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    Matrix<T, C, R> mat(mat1);
+
+    for (size_t cell = 0; cell < mat.cells; ++cell)
+        mat.ml[cell] += mat2.ml[cell];
+
+    return mat;
+}
+
+template <typename Mat, size_t C, size_t R, typename Val>
+Matrix<Mat, C, R> operator+(const Matrix<Mat, C, R>& mat, const Val& val)
+{
+    static_assert (std::is_integral<Mat>::value || std::is_floating_point<Mat>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    Matrix<Mat, C, R> m(mat);
+
+    for (size_t cell = 0; cell < m.cells; ++cell)
+        m.ml[cell] += val;
+
+    return m;
+}
+
+template <typename Val, typename Mat, size_t C, size_t R>
+Matrix<Mat, C, R> operator+(const Val& val, const Matrix<Mat, C, R>& mat)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Mat>::value || std::is_floating_point<Mat>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    Matrix<Mat, C, R> m;
+
+    for (size_t cell = 0; cell < m.cells; ++cell)
+        m.ml[cell] = val + mat.ml[cell];
+
+    return m;
+}
+
+// ---
+
+template <typename T, size_t C, size_t R>
+Matrix<T, C, R> operator-(const Matrix<T, C, R>& mat1, const Matrix<T, C, R>& mat2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    Matrix<T, C, R> mat(mat1);
+
+    for (size_t cell = 0; cell < mat.cells; ++cell)
+        mat.ml[cell] -= mat2.ml[cell];
+
+    return mat;
+}
+
+template <typename Mat, size_t C, size_t R, typename Val>
+Matrix<Mat, C, R> operator-(const Matrix<Mat, C, R>& mat, const Val& val)
+{
+    static_assert (std::is_integral<Mat>::value || std::is_floating_point<Mat>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    Matrix<Mat, C, R> m(mat);
+
+    for (size_t cell = 0; cell < m.cells; ++cell)
+        m.ml[cell] -= val;
+
+    return m;
+}
+
+template <typename Val, typename Mat, size_t C, size_t R>
+Matrix<Mat, C, R> operator-(const Val& val, const Matrix<Mat, C, R>& mat)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Mat>::value || std::is_floating_point<Mat>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    Matrix<Mat, C, R> m;
+
+    for (size_t cell = 0; cell < m.cells; ++cell)
+        m.ml[cell] = val - mat.ml[cell];
+
+    return m;
+}
+
+// ---
+
+// this one is very tricky, do not use it, it will fail in many ways
+template <typename T, size_t C, size_t R>
+Matrix<T, C, R> operator*(const Matrix<T, C, R>& mat1, const Matrix<T, C, R>& mat2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    const T other_dim = C * R;
+    Matrix<T, C, R> mat;
+
+    for (size_t row = 0; row < mat.rows; ++row)
+    {
+        for (size_t col = 0; col < mat.columns; ++col)
+        {
+            T sum = 0.0;
+
+            for (size_t i = 0; i < other_dim; ++i)
+                sum += mat1.m[i][row] * mat2.m[col][i];
+
+            mat.m[col][row] = sum;
+        }
+    }
+
+    return mat;
+}
+
+template <typename Mat, size_t C, size_t R, typename Val>
+Matrix<Mat, C, R> operator*(const Matrix<Mat, C, R>& mat, const Val& val)
+{
+    static_assert (std::is_integral<Mat>::value || std::is_floating_point<Mat>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    Matrix<Mat, C, R> m(mat);
+
+    for (size_t cell = 0; cell < mat.cells; ++cell)
+        m.ml[cell] *= val;
+
+    return m;
+}
+
+template <typename Val, typename Mat, size_t C, size_t R>
+Matrix<Mat, C, R> operator*(const Val& val, const Matrix<Mat, C, R>& mat)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Mat>::value || std::is_floating_point<Mat>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    Matrix<Mat, C, R> m;
+
+    for (size_t cell = 0; cell < m.cells; ++cell)
+        m.ml[cell] = val * mat.ml[cell];
+
+    return m;
+}
+
+// ---
+
+template <typename Mat, size_t C, size_t R, typename Val>
+Matrix<Mat, C, R> operator/(const Matrix<Mat, C, R>& mat, const Val& val)
+{
+    static_assert (std::is_integral<Mat>::value || std::is_floating_point<Mat>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    Matrix<Mat, C, R> m(mat);
+
+    for (size_t cell = 0; cell < m.cells; ++cell)
+        m.ml[cell] /= val;
+
+    return m;
+}
+
+template <typename Val, typename Mat, size_t C, size_t R>
+Matrix<Mat, C, R> operator/(const Val& val, const Matrix<Mat, C, R>& mat)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Mat>::value || std::is_floating_point<Mat>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    Matrix<Mat, C, R> m;
+
+    for (size_t cell = 0; cell < m.cells; ++cell)
+        m.ml[cell] = val / mat.ml[cell];
+
+    return m;
+}
+
+
+
+// --- Matrix 4x4 ---
 
 template <typename MT>
 class Matrix<MT, 4, 4> {
 public:
-    // --- public constants ---
+    // --- Matrix 4x4: public constants ---
 
     enum : size_t {
         columns = 4,
@@ -158,7 +612,7 @@ public:
         cells = 4 * 4
     };
 
-    // --- public data structures ---
+    // --- Matrix 4x4: public data structures ---
 
     alignas (16) union {
         struct {
@@ -171,14 +625,14 @@ public:
         MT ml[cells];
     };
 
-    // --- constructors and deconstructors ---
+    // --- Matrix 4x4: constructors and deconstructors ---
 
     // default is an identity matrix
     Matrix()
-    : ml{1, 0, 0, 0,
-         0, 1, 0, 0,
-         0, 0, 1, 0,
-         0, 0, 0, 1}
+    : ml{1.0, 0.0, 0.0, 0.0,
+         0.0, 1.0, 0.0, 0.0,
+         0.0, 0.0, 1.0, 0.0,
+         0.0, 0.0, 0.0, 1.0}
     {
     }
 
@@ -237,7 +691,7 @@ public:
     {
     }
 
-    // --- default operators ---
+    // --- Matrix 4x4: default operators ---
 
     Matrix& operator=(const Matrix& rhs)
     {
@@ -296,7 +750,7 @@ public:
                 -m14, -m24, -m34, -m44};
     }
 
-    // --- other operators ---
+    // --- Matrix 4x4: other operators ---
 
     Matrix& operator+=(const Matrix& rhs)
     {
@@ -371,6 +825,9 @@ public:
     template <typename T>
     Matrix operator-(const T& val) const
     {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
         return {m11 - val, m21 - val, m31 - val, m41 - val,
                 m12 - val, m22 - val, m32 - val, m42 - val,
                 m13 - val, m23 - val, m33 - val, m43 - val,
@@ -493,7 +950,7 @@ public:
                 m14 / val, m24 / val, m34 / val, m44 / val};
     }
 
-    // --- public methods ---
+    // --- Matrix 4x4: public methods ---
 
     Vector4<MT> column(const size_t& index) const
     {
@@ -508,16 +965,19 @@ public:
     template <typename T>
     void fill(const T& val)
     {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
         for (size_t cell = 0; cell < cells; ++cell)
             ml[cell] = val;
     }
 
     void inverte(bool& invertible)
     {
-        Matrix im(0);
+        Matrix im(0.0);
         MT det = determinant(m);
 
-        if (det == static_cast<const MT>(0))
+        if (det == 0.0)
         {
             invertible = false;
             *this == Matrix(); // identity
@@ -525,7 +985,7 @@ public:
             return;
         }
 
-        det = static_cast<const MT>(1) / det;
+        det = 1.0 / det;
         invertible = true;
 
         im.m11 = determinant33(m, 1, 2, 3, 1, 2, 3) * det;
@@ -589,18 +1049,18 @@ public:
     }
 
 protected:
-    // --- protected methods ---
+    // --- Matrix 4x4: protected methods ---
 
     constexpr MT determinant22(const MT mm[4][4],
-                     const size_t& col1, const size_t& col2,
-                     const size_t& row1, const size_t& row2) const
+                               const size_t& col1, const size_t& col2,
+                               const size_t& row1, const size_t& row2) const
     {
         return mm[col1][row1] * mm[col2][row2] - mm[col1][row2] * mm[col2][row1];
     }
 
     constexpr MT determinant33(const MT mm[4][4],
-                     const size_t& col1, const size_t& col2, const size_t& col3,
-                     const size_t& row1, const size_t& row2, const size_t& row3) const
+                               const size_t& col1, const size_t& col2, const size_t& col3,
+                               const size_t& row1, const size_t& row2, const size_t& row3) const
     {
         return mm[col1][row1] * determinant22(mm, col2, col3, row2, row3) -
                mm[col2][row1] * determinant22(mm, col1, col3, row2, row3) +
@@ -615,3 +1075,217 @@ protected:
                mm[3][0] * determinant33(mm, 0, 1, 2, 1, 2, 3);
     }
 };
+
+// --- Matrix 4x4: global operators ---
+
+template <typename T>
+bool operator==(const Matrix<T, 4, 4>& lhs, const Matrix<T, 4, 4>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return lhs.m11 == rhs.m11 && lhs.m21 == rhs.m21 && lhs.m31 == rhs.m31 && lhs.m41 == rhs.m41 &&
+           lhs.m12 == rhs.m12 && lhs.m22 == rhs.m22 && lhs.m32 == rhs.m32 && lhs.m42 == rhs.m42 &&
+           lhs.m13 == rhs.m13 && lhs.m23 == rhs.m23 && lhs.m33 == rhs.m33 && lhs.m43 == rhs.m43 &&
+           lhs.m14 == rhs.m14 && lhs.m24 == rhs.m24 && lhs.m34 == rhs.m34 && lhs.m44 == rhs.m44;
+}
+
+template <typename T>
+bool operator!=(const Matrix<T, 4, 4>& lhs, const Matrix<T, 4, 4>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return !(lhs == rhs);
+}
+
+template <typename T>
+std::ostream& operator<<(std::ostream& lhs, const Matrix<T, 4, 4>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    lhs << rhs.m11 << rhs.m21 << rhs.m31 << rhs.m41
+        << rhs.m12 << rhs.m22 << rhs.m32 << rhs.m42
+        << rhs.m13 << rhs.m23 << rhs.m33 << rhs.m43
+        << rhs.m14 << rhs.m24 << rhs.m34 << rhs.m44;
+
+    return lhs;
+}
+
+template <typename T>
+std::istream& operator>>(std::istream& lhs, Matrix<T, 4, 4>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    lhs >> rhs.m11 >> rhs.m21 >> rhs.m31 >> rhs.m41
+        >> rhs.m12 >> rhs.m22 >> rhs.m32 >> rhs.m42
+        >> rhs.m13 >> rhs.m23 >> rhs.m33 >> rhs.m43
+        >> rhs.m14 >> rhs.m24 >> rhs.m34 >> rhs.m44;
+
+    return lhs;
+}
+
+// ---
+
+template <typename T>
+Matrix<T, 4, 4> operator+(const Matrix<T, 4, 4>& mat1, const Matrix<T, 4, 4>& mat2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {mat1.m11 + mat2.m11, mat1.m21 + mat2.m21, mat1.m31 + mat2.m31, mat1.m41 + mat2.m41,
+            mat1.m12 + mat2.m12, mat1.m22 + mat2.m22, mat1.m32 + mat2.m32, mat1.m42 + mat2.m42,
+            mat1.m13 + mat2.m13, mat1.m23 + mat2.m23, mat1.m33 + mat2.m33, mat1.m43 + mat2.m43,
+            mat1.m14 + mat2.m14, mat1.m24 + mat2.m24, mat1.m34 + mat2.m34, mat1.m44 + mat2.m44};
+}
+
+template <typename Mat, typename Val>
+Matrix<Mat, 4, 4> operator+(const Matrix<Mat, 4, 4>& mat, const Val& val)
+{
+    static_assert (std::is_integral<Mat>::value || std::is_floating_point<Mat>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {mat.m11 + val, mat.m21 + val, mat.m31 + val, mat.m41 + val,
+            mat.m12 + val, mat.m22 + val, mat.m32 + val, mat.m42 + val,
+            mat.m13 + val, mat.m23 + val, mat.m33 + val, mat.m43 + val,
+            mat.m14 + val, mat.m24 + val, mat.m34 + val, mat.m44 + val};
+}
+
+template <typename Val, typename Mat>
+Matrix<Mat, 4, 4> operator+(const Val& val, const Matrix<Mat, 4, 4>& mat)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Mat>::value || std::is_floating_point<Mat>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val + mat.m11, val + mat.m21, val + mat.m31, val + mat.m41,
+            val + mat.m12, val + mat.m22, val + mat.m32, val + mat.m42,
+            val + mat.m13, val + mat.m23, val + mat.m33, val + mat.m43,
+            val + mat.m14, val + mat.m24, val + mat.m34, val + mat.m44};
+}
+
+// ---
+
+template <typename T>
+Matrix<T, 4, 4> operator-(const Matrix<T, 4, 4>& mat1, const Matrix<T, 4, 4>& mat2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {mat1.m11 - mat2.m11, mat1.m21 - mat2.m21, mat1.m31 - mat2.m31, mat1.m41 - mat2.m41,
+            mat1.m12 - mat2.m12, mat1.m22 - mat2.m22, mat1.m32 - mat2.m32, mat1.m42 - mat2.m42,
+            mat1.m13 - mat2.m13, mat1.m23 - mat2.m23, mat1.m33 - mat2.m33, mat1.m43 - mat2.m43,
+            mat1.m14 - mat2.m14, mat1.m24 - mat2.m24, mat1.m34 - mat2.m34, mat1.m44 - mat2.m44};
+}
+
+template <typename Mat, typename Val>
+Matrix<Mat, 4, 4> operator-(const Matrix<Mat, 4, 4>& mat, const Val& val)
+{
+    static_assert (std::is_integral<Mat>::value || std::is_floating_point<Mat>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {mat.m11 - val, mat.m21 - val, mat.m31 - val, mat.m41 - val,
+            mat.m12 - val, mat.m22 - val, mat.m32 - val, mat.m42 - val,
+            mat.m13 - val, mat.m23 - val, mat.m33 - val, mat.m43 - val,
+            mat.m14 - val, mat.m24 - val, mat.m34 - val, mat.m44 - val};
+}
+
+template <typename Val, typename Mat>
+Matrix<Mat, 4, 4> operator-(const Val& val, const Matrix<Mat, 4, 4>& mat)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Mat>::value || std::is_floating_point<Mat>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val - mat.m11, val - mat.m21, val - mat.m31, val - mat.m41,
+            val - mat.m12, val - mat.m22, val - mat.m32, val - mat.m42,
+            val - mat.m13, val - mat.m23, val - mat.m33, val - mat.m43,
+            val - mat.m14, val - mat.m24, val - mat.m34, val - mat.m44};
+}
+
+// ---
+
+template <typename T>
+Matrix<T, 4, 4> operator*(const Matrix<T, 4, 4>& mat1, const Matrix<T, 4, 4>& mat2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {mat1.m11 * mat2.m11 + mat1.m21 * mat2.m12 + mat1.m31 * mat2.m13 + mat1.m41 * mat2.m14,
+            mat1.m11 * mat2.m21 + mat1.m21 * mat2.m22 + mat1.m31 * mat2.m23 + mat1.m41 * mat2.m24,
+            mat1.m11 * mat2.m31 + mat1.m21 * mat2.m32 + mat1.m31 * mat2.m33 + mat1.m41 * mat2.m34,
+            mat1.m11 * mat2.m41 + mat1.m21 * mat2.m42 + mat1.m31 * mat2.m43 + mat1.m41 * mat2.m44,
+
+            mat1.m12 * mat2.m11 + mat1.m22 * mat2.m12 + mat1.m32 * mat2.m13 + mat1.m42 * mat2.m14,
+            mat1.m12 * mat2.m21 + mat1.m22 * mat2.m22 + mat1.m32 * mat2.m23 + mat1.m42 * mat2.m24,
+            mat1.m12 * mat2.m31 + mat1.m22 * mat2.m32 + mat1.m32 * mat2.m33 + mat1.m42 * mat2.m34,
+            mat1.m12 * mat2.m41 + mat1.m22 * mat2.m42 + mat1.m32 * mat2.m43 + mat1.m42 * mat2.m44,
+
+            mat1.m13 * mat2.m11 + mat1.m23 * mat2.m12 + mat1.m33 * mat2.m13 + mat1.m43 * mat2.m14,
+            mat1.m13 * mat2.m21 + mat1.m23 * mat2.m22 + mat1.m33 * mat2.m23 + mat1.m43 * mat2.m24,
+            mat1.m13 * mat2.m31 + mat1.m23 * mat2.m32 + mat1.m33 * mat2.m33 + mat1.m43 * mat2.m34,
+            mat1.m13 * mat2.m41 + mat1.m23 * mat2.m42 + mat1.m33 * mat2.m43 + mat1.m43 * mat2.m44,
+
+            mat1.m14 * mat2.m11 + mat1.m24 * mat2.m12 + mat1.m34 * mat2.m13 + mat1.m44 * mat2.m14,
+            mat1.m14 * mat2.m21 + mat1.m24 * mat2.m22 + mat1.m34 * mat2.m23 + mat1.m44 * mat2.m24,
+            mat1.m14 * mat2.m31 + mat1.m24 * mat2.m32 + mat1.m34 * mat2.m33 + mat1.m44 * mat2.m34,
+            mat1.m14 * mat2.m41 + mat1.m24 * mat2.m42 + mat1.m34 * mat2.m43 + mat1.m44 * mat2.m44};
+}
+
+template <typename Mat, typename Val>
+Matrix<Mat, 4, 4> operator*(const Matrix<Mat, 4, 4>& mat, const Val& val)
+{
+    static_assert (std::is_integral<Mat>::value || std::is_floating_point<Mat>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {mat.m11 * val, mat.m21 * val, mat.m31 * val, mat.m41 * val,
+            mat.m12 * val, mat.m22 * val, mat.m32 * val, mat.m42 * val,
+            mat.m13 * val, mat.m23 * val, mat.m33 * val, mat.m43 * val,
+            mat.m14 * val, mat.m24 * val, mat.m34 * val, mat.m44 * val};
+}
+
+template <typename Val, typename Mat>
+Matrix<Mat, 4, 4> operator*(const Val& val, const Matrix<Mat, 4, 4>& mat)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Mat>::value || std::is_floating_point<Mat>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val * mat.m11, val * mat.m21, val * mat.m31, val * mat.m41,
+            val * mat.m12, val * mat.m22, val * mat.m32, val * mat.m42,
+            val * mat.m13, val * mat.m23, val * mat.m33, val * mat.m43,
+            val * mat.m14, val * mat.m24, val * mat.m34, val * mat.m44};
+}
+
+// ---
+
+template <typename Mat, typename Val>
+Matrix<Mat, 4, 4> operator/(const Matrix<Mat, 4, 4>& mat, const Val& val)
+{
+    static_assert (std::is_integral<Mat>::value || std::is_floating_point<Mat>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {mat.m11 / val, mat.m21 / val, mat.m31 / val, mat.m41 / val,
+            mat.m12 / val, mat.m22 / val, mat.m32 / val, mat.m42 / val,
+            mat.m13 / val, mat.m23 / val, mat.m33 / val, mat.m43 / val,
+            mat.m14 / val, mat.m24 / val, mat.m34 / val, mat.m44 / val};
+}
+
+template <typename Val, typename Mat>
+Matrix<Mat, 4, 4> operator/(const Val& val, const Matrix<Mat, 4, 4>& mat)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Mat>::value || std::is_floating_point<Mat>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val / mat.m11, val / mat.m21, val / mat.m31, val / mat.m41,
+            val / mat.m12, val / mat.m22, val / mat.m32, val / mat.m42,
+            val / mat.m13, val / mat.m23, val / mat.m33, val / mat.m43,
+            val / mat.m14, val / mat.m24, val / mat.m34, val / mat.m44};
+}