scalar_t Matrix4x4::Determinant() const {

    scalar_t det11 =    (m[1][1] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) -
                        (m[1][2] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) +
                        (m[1][3] * (m[2][1] * m[3][2] - m[3][1] * m[2][2]));

    scalar_t det12 =    (m[1][0] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) -
                        (m[1][2] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) +
                        (m[1][3] * (m[2][0] * m[3][2] - m[3][0] * m[2][2]));

    scalar_t det13 =    (m[1][0] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) -
                        (m[1][1] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) +
                        (m[1][3] * (m[2][0] * m[3][1] - m[3][0] * m[2][1]));

    scalar_t det14 =    (m[1][0] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])) -
                        (m[1][1] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])) +
                        (m[1][2] * (m[2][0] * m[3][1] - m[3][0] * m[2][1]));

    return m[0][0] * det11 - m[0][1] * det12 + m[0][2] * det13 - m[0][3] * det14;
}


Matrix4x4 Matrix4x4::Adjoint() const {

    Matrix4x4 coef;

    coef.m[0][0] =  (m[1][1] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) -
                    (m[1][2] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) +
                    (m[1][3] * (m[2][1] * m[3][2] - m[3][1] * m[2][2]));
    coef.m[0][1] =  (m[1][0] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) -
                    (m[1][2] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) +
                    (m[1][3] * (m[2][0] * m[3][2] - m[3][0] * m[2][2]));
    coef.m[0][2] =  (m[1][0] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) -
                    (m[1][1] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) +
                    (m[1][3] * (m[2][0] * m[3][1] - m[3][0] * m[2][1]));
    coef.m[0][3] =  (m[1][0] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])) -
                    (m[1][1] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])) +
                    (m[1][2] * (m[2][0] * m[3][1] - m[3][0] * m[2][1]));

    coef.m[1][0] =  (m[0][1] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) -
                    (m[0][2] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) +
                    (m[0][3] * (m[2][1] * m[3][2] - m[3][1] * m[2][2]));
    coef.m[1][1] =  (m[0][0] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) -
                    (m[0][2] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) +
                    (m[0][3] * (m[2][0] * m[3][2] - m[3][0] * m[2][2]));
    coef.m[1][2] =  (m[0][0] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) -
                    (m[0][1] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) +
                    (m[0][3] * (m[2][0] * m[3][1] - m[3][0] * m[2][1]));
    coef.m[1][3] =  (m[0][0] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])) -
                    (m[0][1] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])) +
                    (m[0][2] * (m[2][0] * m[3][1] - m[3][0] * m[2][1]));

    coef.m[2][0] =  (m[0][1] * (m[1][2] * m[3][3] - m[3][2] * m[1][3])) -
                    (m[0][2] * (m[1][1] * m[3][3] - m[3][1] * m[1][3])) +
                    (m[0][3] * (m[1][1] * m[3][2] - m[3][1] * m[1][2]));
    coef.m[2][1] =  (m[0][0] * (m[1][2] * m[3][3] - m[3][2] * m[1][3])) -
                    (m[0][2] * (m[1][0] * m[3][3] - m[3][0] * m[1][3])) +
                    (m[0][3] * (m[1][0] * m[3][2] - m[3][0] * m[1][2]));
    coef.m[2][2] =  (m[0][0] * (m[1][1] * m[3][3] - m[3][1] * m[1][3])) -
                    (m[0][1] * (m[1][0] * m[3][3] - m[3][0] * m[1][3])) +
                    (m[0][3] * (m[1][0] * m[3][1] - m[3][0] * m[1][1]));
    coef.m[2][3] =  (m[0][0] * (m[1][1] * m[3][2] - m[3][1] * m[1][2])) -
                    (m[0][1] * (m[1][0] * m[3][2] - m[3][0] * m[1][2])) +
                    (m[0][2] * (m[1][0] * m[3][1] - m[3][0] * m[1][1]));

    coef.m[3][0] =  (m[0][1] * (m[1][2] * m[2][3] - m[2][2] * m[1][3])) -
                    (m[0][2] * (m[1][1] * m[2][3] - m[2][1] * m[1][3])) +
                    (m[0][3] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]));
    coef.m[3][1] =  (m[0][0] * (m[1][2] * m[2][3] - m[2][2] * m[1][3])) -
                    (m[0][2] * (m[1][0] * m[2][3] - m[2][0] * m[1][3])) +
                    (m[0][3] * (m[1][0] * m[2][2] - m[2][0] * m[1][2]));
    coef.m[3][2] =  (m[0][0] * (m[1][1] * m[2][3] - m[2][1] * m[1][3])) -
                    (m[0][1] * (m[1][0] * m[2][3] - m[2][0] * m[1][3])) +
                    (m[0][3] * (m[1][0] * m[2][1] - m[2][0] * m[1][1]));
    coef.m[3][3] =  (m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])) -
                    (m[0][1] * (m[1][0] * m[2][2] - m[2][0] * m[1][2])) +
                    (m[0][2] * (m[1][0] * m[2][1] - m[2][0] * m[1][1]));

    coef.Transpose();

    for(int i=0; i<4; i++) {
        for(int j=0; j<4; j++) {
            coef.m[i][j] = j%2 ? -coef.m[i][j] : coef.m[i][j];
            if(i%2) coef.m[i][j] = -coef.m[i][j];
        }
    }

    return coef;
}

Matrix4x4 Matrix4x4::Inverse() const {

    Matrix4x4 AdjMat = Adjoint();

    return AdjMat * (1.0f / Determinant());
}