// Copyright (C) 2006  Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License   See LICENSE.txt for the full license.


#include <dlib/matrix.h>
#include <sstream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <vector>
#include "../stl_checked.h"
#include "../array.h"
#include "../rand.h"

#include "tester.h"
#include <dlib/memory_manager_stateless.h>
#include <dlib/array2d.h>

namespace  
{

    using namespace test;
    using namespace dlib;
    using namespace std;

    logger dlog("test.matrix");

    void matrix_test (
    )
    /*!
        ensures
            - runs tests on the matrix stuff compliance with the specs
    !*/
    {        
        typedef memory_manager_stateless<char>::kernel_2_2a MM;
        print_spinner();

        const double ident[] = {
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1 };

        const double uniform3[] = {
            3, 3, 3, 3,
            3, 3, 3, 3,
            3, 3, 3, 3,
            3, 3, 3, 3 
        };

        const double uniform1[] = {
            1, 1, 1, 1,
            1, 1, 1, 1,
            1, 1, 1, 1,
            1, 1, 1, 1 
        };

        const double uniform0[] = {
            0, 0, 0, 0,
            0, 0, 0, 0,
            0, 0, 0, 0,
            0, 0, 0, 0 
        };

        const int array[] = {
            42, 58, 9, 1,
            9, 5, 8, 2,
            98, 28, 4, 77, 
            9, 2, 44, 88 };

        const int array2[] = {
            1, 22, 3,
            4, 52, 6,
            7, 8, 9 };

        const int array2_r[] = {
            52, 6, 4,
            8, 9, 7,
            22, 3, 1
        };

        const double array_f[] = {
            -0.99, 
            0.99};


        matrix<double,2,1,MM> fm(array_f);

        DLIB_CASSERT(fm.size() == 2,"");
        matrix<double> dfm(fm);
        DLIB_CASSERT(round(fm)(0) == -1,"");
        DLIB_CASSERT(round(fm)(1) == 1,"");
        DLIB_CASSERT(round(dfm)(0) == -1,"");
        DLIB_CASSERT(round(dfm)(1) == 1,"");
        DLIB_CASSERT(round(dfm).size() == dfm.size(),"");


        const int array3[] = { 1, 2, 3, 4 };

        matrix<double,3,3,MM> m3(array2);
        matrix<double> dm3;
        DLIB_CASSERT(dm3.size() == 0,"");
        DLIB_CASSERT(dm3.nr() == 0,"");
        DLIB_CASSERT(dm3.nc() == 0,"");
        dm3.set_size(3,4);
        DLIB_CASSERT(dm3.nr() == 3,"");
        DLIB_CASSERT(dm3.nc() == 4,"");
        DLIB_CASSERT(dm3.size() == 3*4,"");
        dm3.set_size(3,3);
        DLIB_CASSERT(dm3.nr() == 3,"");
        DLIB_CASSERT(dm3.nc() == 3,"");
        dm3 = m3;
        dm3(0,0)++;
        DLIB_CASSERT( dm3 != m3,"");
        dm3 = m3;
        DLIB_CASSERT( dm3 == m3,"");
        DLIB_CASSERT( abs(sum(squared(normalize(dm3))) - 1.0) < 1e-10,"");

        matrix<double,3,4> mrc;
        mrc.set_size(3,4);

        set_all_elements(mrc,1);

        DLIB_CASSERT(diag(mrc) == uniform_matrix<double>(3,1,1),"");
        DLIB_CASSERT(diag(matrix<double>(mrc)) == uniform_matrix<double>(3,1,1),"");

        matrix<double,2,3> mrc2;
        set_all_elements(mrc2,1);
        DLIB_CASSERT((removerc<1,1>(mrc) == mrc2),"");
        DLIB_CASSERT((removerc(mrc,1,1) == mrc2),"");

        matrix<int,3,3> m4, m5, m6;
        set_all_elements(m4, 4);
        set_all_elements(m5, 4);
        set_all_elements(m6, 1);

        DLIB_CASSERT(squared(m4) == pointwise_multiply(m4,m4),"");
        DLIB_CASSERT(cubed(m4) == pointwise_multiply(m4,m4,m4),"");
        DLIB_CASSERT(pow(matrix_cast<double>(m4),2) == squared(matrix_cast<double>(m4)),"");
        DLIB_CASSERT(pow(matrix_cast<double>(m4),3) == cubed(matrix_cast<double>(m4)),"");

        matrix<int> dm4;
        matrix<int,0,0,memory_manager_stateless<char>::kernel_2_2a> dm5;
        dm4 = dm4;
        dm4 = dm5;
        DLIB_CASSERT(dm4.nr() == 0,"");
        dm4 = m4;
        dm5 = m5;
        DLIB_CASSERT(dm4 == dm5,"");


        DLIB_CASSERT(m4 == m5,"");
        DLIB_CASSERT(m6 != m5,"");
        m4.swap(m6);
        DLIB_CASSERT(m6 == m5,"");
        DLIB_CASSERT(m4 != m5,"");

        DLIB_CASSERT(m3.nr() == 3,"");
        DLIB_CASSERT(m3.nc() == 3,"");

        matrix<double,4,1> v(array3), v2;
        DLIB_CASSERT(v.nr() == 4,"");
        DLIB_CASSERT(v.nc() == 1,"");

        std::vector<double> stdv(4);
        std_vector_c<double> stdv_c(4);
        dlib::array<double>::expand_1a_c arr;
        arr.expand(4);
        for (long i = 0; i < 4; ++i)
            stdv[i] = stdv_c[i] = arr[i] = i+1;

        DLIB_CASSERT(vector_to_matrix(stdv)(0) == 1,"");
        DLIB_CASSERT(vector_to_matrix(stdv)(1) == 2,"");
        DLIB_CASSERT(vector_to_matrix(stdv)(2) == 3,"");
        DLIB_CASSERT(vector_to_matrix(stdv)(3) == 4,"");
        DLIB_CASSERT(vector_to_matrix(stdv).nr() == 4,"");
        DLIB_CASSERT(vector_to_matrix(stdv).nc() == 1,"");
        DLIB_CASSERT(vector_to_matrix(stdv).size() == 4,"");
        DLIB_CASSERT(equal(trans(vector_to_matrix(stdv))*vector_to_matrix(stdv), trans(v)*v), "");
        DLIB_CASSERT(equal(trans(vector_to_matrix(stdv))*vector_to_matrix(stdv), tmp(trans(v)*v)), "");

        DLIB_CASSERT(vector_to_matrix(stdv_c)(0) == 1,"");
        DLIB_CASSERT(vector_to_matrix(stdv_c)(1) == 2,"");
        DLIB_CASSERT(vector_to_matrix(stdv_c)(2) == 3,"");
        DLIB_CASSERT(vector_to_matrix(stdv_c)(3) == 4,"");
        DLIB_CASSERT(vector_to_matrix(stdv_c).nr() == 4,"");
        DLIB_CASSERT(vector_to_matrix(stdv_c).nc() == 1,"");
        DLIB_CASSERT(vector_to_matrix(stdv_c).size() == 4,"");
        DLIB_CASSERT(equal(trans(vector_to_matrix(stdv_c))*vector_to_matrix(stdv_c), trans(v)*v), "");

        DLIB_CASSERT(vector_to_matrix(arr)(0) == 1,"");
        DLIB_CASSERT(vector_to_matrix(arr)(1) == 2,"");
        DLIB_CASSERT(vector_to_matrix(arr)(2) == 3,"");
        DLIB_CASSERT(vector_to_matrix(arr)(3) == 4,"");
        DLIB_CASSERT(vector_to_matrix(arr).nr() == 4,"");
        DLIB_CASSERT(vector_to_matrix(arr).nc() == 1,"");
        DLIB_CASSERT(vector_to_matrix(arr).size() == 4,"");
        DLIB_CASSERT(equal(trans(vector_to_matrix(arr))*vector_to_matrix(arr), trans(v)*v), "");

        DLIB_CASSERT(v(0) == 1,"");
        DLIB_CASSERT(v(1) == 2,"");
        DLIB_CASSERT(v(2) == 3,"");
        DLIB_CASSERT(v(3) == 4,"");
        matrix<double> dv = v;
        DLIB_CASSERT((trans(v)*v).size() == 1,"");
        DLIB_CASSERT((trans(v)*v).nr() == 1,"");
        DLIB_CASSERT((trans(v)*dv).nr() == 1,"");
        DLIB_CASSERT((trans(dv)*dv).nr() == 1,"");
        DLIB_CASSERT((trans(dv)*v).nr() == 1,"");
        DLIB_CASSERT((trans(v)*v).nc() == 1,"");
        DLIB_CASSERT((trans(v)*dv).nc() == 1,"");
        DLIB_CASSERT((trans(dv)*dv).nc() == 1,"");
        DLIB_CASSERT((trans(dv)*v).nc() == 1,"");
        DLIB_CASSERT((trans(v)*v)(0) == 1*1 + 2*2 + 3*3 + 4*4,"");
        DLIB_CASSERT((trans(dv)*v)(0) == 1*1 + 2*2 + 3*3 + 4*4,"");
        DLIB_CASSERT((trans(dv)*dv)(0) == 1*1 + 2*2 + 3*3 + 4*4,"");
        DLIB_CASSERT((trans(v)*dv)(0) == 1*1 + 2*2 + 3*3 + 4*4,"");

        dv = trans(dv)*v;
        DLIB_CASSERT(dv.nr() == 1,"");
        DLIB_CASSERT(dv.nc() == 1,"");

        dm3 = m3;
        DLIB_CASSERT(floor(det(m3)+0.01) == -444,"");
        DLIB_CASSERT(floor(det(dm3)+0.01) == -444,"");
        DLIB_CASSERT(min(m3) == 1,"");
        DLIB_CASSERT(min(dm3) == 1,"");
        DLIB_CASSERT(max(m3) == 52,"");
        DLIB_CASSERT(max(dm3) == 52,"");
        DLIB_CASSERT(sum(m3) == 112,"");
        DLIB_CASSERT(sum(dm3) == 112,"");
        DLIB_CASSERT(prod(m3) == 41513472,"");
        DLIB_CASSERT(prod(dm3) == 41513472,"");
        DLIB_CASSERT(prod(diag(m3)) == 1*52*9,"");
        DLIB_CASSERT(prod(diag(dm3)) == 1*52*9,"");
        DLIB_CASSERT(sum(diag(m3)) == 1+52+9,"");
        DLIB_CASSERT(sum(diag(dm3)) == 1+52+9,"");
        DLIB_CASSERT((round(10000*m3*inv(m3))/10000 == identity_matrix<double,3>()),"");
        DLIB_CASSERT((round(10000*dm3*inv(m3))/10000 == identity_matrix<double,3>()),"");
        DLIB_CASSERT((round(10000*dm3*inv(dm3))/10000 == identity_matrix<double,3>()),"");
        DLIB_CASSERT((round(10000*m3*inv(dm3))/10000 == identity_matrix<double,3>()),"");
        DLIB_CASSERT((round(10000*tmp(m3*inv(m3)))/10000 == identity_matrix<double,3>()),"");
        DLIB_CASSERT((round(10000*tmp(dm3*inv(m3)))/10000 == identity_matrix<double,3>()),"");
        DLIB_CASSERT((round(10000*tmp(dm3*inv(dm3)))/10000 == identity_matrix<double,3>()),"");
        DLIB_CASSERT((round(10000*tmp(m3*inv(dm3)))/10000 == identity_matrix<double,3>()),"");
        DLIB_CASSERT(-1*m3 == -m3,"");
        DLIB_CASSERT(-1*dm3 == -m3,"");
        DLIB_CASSERT(-1*m3 == -dm3,"");
        DLIB_CASSERT(-1*dm3 == -dm3,"");

        DLIB_CASSERT(m3 == dm3,"");
        m3(1,1) = 99;
        DLIB_CASSERT(m3 != dm3,"");
        m3 = dm3;
        DLIB_CASSERT(m3 == dm3,"");

        matrix<double,4,4,MM> mident(ident);
        matrix<double,4,4> muniform0(uniform0);
        matrix<double,4,4> muniform1(uniform1);
        matrix<double,4,4> muniform3(uniform3);
        matrix<double,4,4> m1(array), m2;
        DLIB_CASSERT(m1.nr() == 4,"");
        DLIB_CASSERT(m1.nc() == 4,"");

        DLIB_CASSERT(muniform1 + muniform1 + muniform1 == muniform3,"");
        DLIB_CASSERT(muniform1*2 + muniform1 + muniform1 - muniform1 == muniform3,"");
        DLIB_CASSERT(2*muniform1 + muniform1 + muniform1 - muniform1 == muniform3,"");
        DLIB_CASSERT(muniform1 + muniform1 + muniform1 - muniform3 == muniform0,"");
        DLIB_CASSERT(muniform3/3 == muniform1,"");
        DLIB_CASSERT(v != m1,"");
        DLIB_CASSERT(v == v,"");
        DLIB_CASSERT(m1 == m1,"");

        muniform0.swap(muniform1);
        DLIB_CASSERT((muniform1 == matrix_cast<double>(uniform_matrix<long,4,4,0>())),"");
        DLIB_CASSERT((muniform0 == matrix_cast<double>(uniform_matrix<long,4,4,1>())),"");
        DLIB_CASSERT((muniform1 == matrix_cast<double>(uniform_matrix<long>(4,4,0))),"");
        DLIB_CASSERT((muniform0 == matrix_cast<double>(uniform_matrix<long>(4,4,1))),"");
        swap(muniform0,muniform1);

        DLIB_CASSERT((mident == identity_matrix<double,4>()),"");
        DLIB_CASSERT((muniform0 == matrix_cast<double>(uniform_matrix<long,4,4,0>())),"");
        DLIB_CASSERT((muniform1 == matrix_cast<double>(uniform_matrix<long,4,4,1>())),"");
        DLIB_CASSERT((muniform3 == matrix_cast<double>(uniform_matrix<long,4,4,3>())),"");
        DLIB_CASSERT((muniform1*8 == matrix_cast<double>(uniform_matrix<long,4,4,8>())),"");

        set_all_elements(m2,7);
        DLIB_CASSERT(m2 == muniform1*7,"");
        m2 = array;
        DLIB_CASSERT(m2 == m1,"");

        const double m1inv[] = {
            -0.00946427624, 0.0593272941,  0.00970564379,  -0.00973323731, 
            0.0249312057,   -0.0590122427, -0.00583102756, 0.00616002729, 
            -0.00575431149, 0.110081189,   -0.00806792253, 0.00462297692, 
            0.00327847478,  -0.0597669712, 0.00317386196,  0.00990759201 
        };

        m2 = m1inv;
        DLIB_CASSERT((round(m2*m1) == identity_matrix<double,4>()),"");
        DLIB_CASSERT((round(tmp(m2*m1)) == identity_matrix<double,4>()),"");

        DLIB_CASSERT(round(m2*10000) == round(inv(m1)*10000),"");
        DLIB_CASSERT(m1 == abs(-1*m1),"");
        DLIB_CASSERT(abs(m2) == abs(-1*m2),"");

        DLIB_CASSERT(floor(det(m1)+0.01) == 3297875,"\nm1: \n" << m1 << "\ndet(m1): " << det(m1));


        ostringstream sout;
        m1 = m2;
        serialize(m1,sout);
        set_all_elements(m1,0);
        istringstream sin(sout.str());
        deserialize(m1,sin);
        DLIB_CASSERT(round(100000*m1) == round(100000*m2),"m1: \n" << m1 << endl << "m2: \n" << m2);


        set_all_elements(v,2);
        v2 =  pointwise_multiply(v, v*2);
        set_all_elements(v,8);
        DLIB_CASSERT(v == v2,"");
        DLIB_CASSERT(v == tmp(v2),"");
        DLIB_CASSERT((v == rotate<2,0>(v)),""); 

        m4 = array2;
        m5 = array2_r;
        DLIB_CASSERT((m5 == rotate<1,1>(m4)),"");

        m5 = array2;
        DLIB_CASSERT((m5*2 == pointwise_multiply(m5,uniform_matrix<int,3,3,2>())),"");
        DLIB_CASSERT((tmp(m5*2) == tmp(pointwise_multiply(m5,uniform_matrix<int,3,3,2>()))),"");

        v = tmp(v);




        matrix<double> dm10(10,5);
        DLIB_CASSERT(dm10.nr() == 10,"");
        DLIB_CASSERT(dm10.nc() == 5,"");
        set_all_elements(dm10,4);
        DLIB_CASSERT(dm10.nr() == 10,"");
        DLIB_CASSERT(dm10.nc() == 5,"");
        matrix<double,10,5> m10;
        DLIB_CASSERT(m10.nr() == 10,"");
        DLIB_CASSERT(m10.nc() == 5,"");
        set_all_elements(m10,4);
        DLIB_CASSERT(dm10 == m10,"");
        DLIB_CASSERT((clamp<0,3>(dm10) == clamp<0,3>(m10)),"");
        DLIB_CASSERT((clamp<0,3>(dm10)(0,2) == 3),"");

        set_all_elements(dm10,1);
        set_all_elements(m10,4);
        DLIB_CASSERT(4*dm10 == m10,"");
        DLIB_CASSERT(5*dm10 - dm10 == m10,"");
        DLIB_CASSERT((16*dm10)/4 == m10,"");
        DLIB_CASSERT(dm10+dm10+2*dm10 == m10,"");
        DLIB_CASSERT(dm10+tmp(dm10+2*dm10) == m10,"");
        set_all_elements(dm10,4);
        DLIB_CASSERT(dm10 == m10,"");
        DLIB_CASSERT(sum(abs(sigmoid(dm10) -sigmoid(m10))) < 1e-10,sum(abs(sigmoid(dm10) -sigmoid(m10))) );


        matrix<double, 7, 7,MM> m7;
        matrix<double> dm7(7,7);
        for (long r= 0; r< dm7.nr(); ++r)
        {
            for (long c = 0; c < dm7.nc(); ++c)
            {
                dm7(r,c) = r*c/3.3;
            }
        }
        m7 = dm7;

        DLIB_CASSERT(inv(dm7) == inv(m7),"");
        DLIB_CASSERT(det(dm7) == det(m7),"");
        DLIB_CASSERT(min(dm7) == min(m7),"");
        DLIB_CASSERT(max(dm7) == max(m7),"");
        DLIB_CASSERT(abs(sum(dm7) - sum(m7)) < 1e-14,sum(dm7) - sum(m7));
        DLIB_CASSERT(prod(dm7) == prod(m7),"");
        DLIB_CASSERT(diag(dm7) == diag(m7),"");
        DLIB_CASSERT(trans(dm7) == trans(m7),"");
        DLIB_CASSERT(abs(dm7) == abs(m7),"");
        DLIB_CASSERT(round(dm7) == round(m7),"");
        DLIB_CASSERT(matrix_cast<int>(dm7) == matrix_cast<int>(m7),"");
        DLIB_CASSERT((rotate<2,3>(dm7) == rotate<2,3>(m7)),"");
        DLIB_CASSERT((sum(pointwise_multiply(dm7,dm7) - pointwise_multiply(m7,m7))) < 1e-10,"");
        DLIB_CASSERT((sum(pointwise_multiply(dm7,dm7,dm7) - pointwise_multiply(m7,m7,m7))) < 1e-10,"");
        DLIB_CASSERT((sum(pointwise_multiply(dm7,dm7,dm7,dm7) - pointwise_multiply(m7,m7,m7,m7))) < 1e-10,
                     (sum(pointwise_multiply(dm7,dm7,dm7,dm7) - pointwise_multiply(m7,m7,m7,m7)))
        );


        matrix<double> temp(5,5);
        matrix<double> dsm(5,5);
        matrix<double,5,5,MM> sm;

        set_all_elements(dsm,1);
        set_all_elements(sm,1);
        set_all_elements(temp,1);

        dsm += dsm;
        sm += sm;
        DLIB_CASSERT(dsm == 2*temp,"");
        DLIB_CASSERT(sm == 2*temp,"");
        temp = dsm*sm + dsm;
        dsm += dsm*sm;
        DLIB_CASSERT(temp == dsm,temp - dsm);

        set_all_elements(dsm,1);
        set_all_elements(sm,1);
        set_all_elements(temp,1);

        dsm += dsm;
        sm += sm;
        DLIB_CASSERT(dsm == 2*temp,"");
        DLIB_CASSERT(sm == 2*temp,"");
        temp = dsm*sm + dsm;
        sm += dsm*sm;
        DLIB_CASSERT(temp == sm,temp - sm);

        set_all_elements(dsm,1);
        set_all_elements(sm,1);
        set_all_elements(temp,1);

        dsm += dsm;
        sm += sm;
        DLIB_CASSERT(dsm == 2*temp,"");
        DLIB_CASSERT(sm == 2*temp,"");
        temp = sm - dsm*sm ;
        sm -= dsm*sm;
        DLIB_CASSERT(temp == sm,temp - sm);

        set_all_elements(dsm,1);
        set_all_elements(sm,1);
        set_all_elements(temp,1);

        dsm += dsm;
        sm += sm;
        DLIB_CASSERT(dsm == 2*temp,"");
        DLIB_CASSERT(sm == 2*temp,"");
        temp = dsm - dsm*sm ;
        dsm -= dsm*sm;
        DLIB_CASSERT(temp == dsm,temp - dsm);

        set_all_elements(dsm,1);
        set_all_elements(sm,1);
        set_all_elements(temp,2);

        dsm *= 2;
        sm *= 2;
        DLIB_CASSERT(dsm == temp,"");
        DLIB_CASSERT(sm == temp,"");
        dsm /= 2;
        sm /= 2;
        DLIB_CASSERT(dsm == temp/2,"");
        DLIB_CASSERT(sm == temp/2,"");

        dsm += dsm;
        sm += sm;
        DLIB_CASSERT(dsm == temp,"");
        DLIB_CASSERT(sm == temp,"");
        dsm += sm;
        sm += dsm;
        DLIB_CASSERT(dsm == 2*temp,"");
        DLIB_CASSERT(sm == temp*3,"");
        dsm -= sm;
        sm -= dsm;
        DLIB_CASSERT(dsm == -temp,"");
        DLIB_CASSERT(sm == 4*temp,"");
        sm -= sm;
        dsm -= dsm;
        DLIB_CASSERT(dsm == 0*temp,"");
        DLIB_CASSERT(sm == 0*temp,"");

        set_all_elements(dsm,1);
        set_all_elements(sm,1);
        set_all_elements(temp,3);
        dsm += sm+sm;
        DLIB_CASSERT(dsm == temp,"");

        set_all_elements(dsm,1);
        set_all_elements(sm,1);
        set_all_elements(temp,-1);
        dsm -= sm+sm;
        DLIB_CASSERT(dsm == temp,"");

        set_all_elements(dsm,1);
        set_all_elements(sm,1);
        set_all_elements(temp,-1);
        sm -= dsm+dsm;
        DLIB_CASSERT(sm == temp,"");

        set_all_elements(dsm,1);
        set_all_elements(sm,1);
        set_all_elements(temp,3);
        sm += dsm+dsm;
        DLIB_CASSERT(sm == temp,"");



        // test the implicit conversion to bool stuff
        {
            matrix<float> bt1(3,1);
            matrix<float,3,1> bt2;
            set_all_elements(bt1,2);
            set_all_elements(bt2,3);

            DLIB_CASSERT(trans(bt1)*bt2 == 18,"");
        }
        {
            matrix<float,3,1> bt1;
            matrix<float> bt2(3,1);
            set_all_elements(bt1,2);
            set_all_elements(bt2,3);

            DLIB_CASSERT(trans(bt1)*bt2 == 18,"");
        }
        {
            matrix<float> bt1(3,1);
            matrix<float> bt2(3,1);
            set_all_elements(bt1,2);
            set_all_elements(bt2,3);

            DLIB_CASSERT(trans(bt1)*bt2 == 18,"");
        }
        {
            matrix<float,3,1> bt1;
            matrix<float,3,1> bt2;
            set_all_elements(bt1,2);
            set_all_elements(bt2,3);

            DLIB_CASSERT(trans(bt1)*bt2 == 18,"");
        }




        {
            srand(423452);
            const long M = 50;
            const long N = 40;

            matrix<double> a(M,N);  
            for (long r = 0; r < a.nr(); ++r)
            {
                for (long c = 0; c < a.nc(); ++c)
                {
                    a(r,c) = 10*((double)::rand())/RAND_MAX;
                }
            }

            matrix<double> u, u2;  
            matrix<double> q, q2;
            matrix<double> v, v2;

            matrix<double> a2;  
            a2 = tmp(a/2);


            svd2(true,true,a2+a2,u,q,v);

            double err = sum(round(1e10*(a - subm(u,get_rect(a2+a2))*diagm(q)*trans(v))));
            DLIB_CASSERT(  err == 0,"err: " << err);
            DLIB_CASSERT((round(1e10*trans(u)*u)  == 1e10*identity_matrix<double,M>()),"");
            DLIB_CASSERT((round(1e10*trans(v)*v)  == 1e10*identity_matrix<double,N>()),"");

            svd2(false,true,a2+a2,u2,q2,v2);
            DLIB_CASSERT(equal(q2,q),"");
            DLIB_CASSERT(equal(v2,v),"");
            svd2(true,false,a2+a2,u2,q2,v2);
            DLIB_CASSERT(equal(q2,q),"");
            DLIB_CASSERT(equal(u2,u),"");
            svd2(false,false,a2+a2,u2,q2,v2);
            DLIB_CASSERT(equal(q2,q),"");

        }


        {
            srand(423452);
            const long M = 3;
            const long N = 3;

            matrix<double> a(M,N);  
            for (long r = 0; r < a.nr(); ++r)
            {
                for (long c = 0; c < a.nc(); ++c)
                {
                    a(r,c) = 10*((double)::rand())/RAND_MAX;
                }
            }

            matrix<double,M,M> u, u2;  
            matrix<double> q, q2;
            matrix<double,N,N> v, v2;

            matrix<double,M,N,MM> a2;  
            a2 = tmp(a/2);


            svd2(true,true,a2+a2,u,q,v);

            double err = sum(round(1e10*(a - subm(u,get_rect(a2+a2))*diagm(q)*trans(v))));
            DLIB_CASSERT(  err == 0,"err: " << err);
            DLIB_CASSERT((round(1e10*trans(u)*u)  == 1e10*identity_matrix<double,M>()),"");
            DLIB_CASSERT((round(1e10*trans(v)*v)  == 1e10*identity_matrix<double,N>()),"");

            svd2(false,true,a2+a2,u2,q2,v2);
            DLIB_CASSERT(equal(q2,q),"");
            DLIB_CASSERT(equal(v2,v),"");
            svd2(true,false,a2+a2,u2,q2,v2);
            DLIB_CASSERT(equal(q2,q),"");
            DLIB_CASSERT(equal(u2,u),"");
            svd2(false,false,a2+a2,u2,q2,v2);
            DLIB_CASSERT(equal(q2,q),"");

        }


        {
            srand(423452);
            const long M = 10;
            const long N = 7;

            matrix<double> a(M,N);  
            for (long r = 0; r < a.nr(); ++r)
            {
                for (long c = 0; c < a.nc(); ++c)
                {
                    a(r,c) = 10*((double)::rand())/RAND_MAX;
                }
            }

            matrix<double,M,M> u;  
            matrix<double> q;
            matrix<double,N,N> v;

            matrix<double,M,N,MM> a2;  
            a2 = tmp(a/2);


            svd2(true,true,a2+a2,u,q,v);

            double err = sum(round(1e10*(a - subm(u,get_rect(a2+a2))*diagm(q)*trans(v))));
            DLIB_CASSERT(  err == 0,"err: " << err);
            DLIB_CASSERT((round(1e10*trans(u)*u)  == 1e10*identity_matrix<double,M>()),"");
            DLIB_CASSERT((round(1e10*trans(v)*v)  == 1e10*identity_matrix<double,N>()),"");
        }


        {
            srand(423452);
            const long M = 10;
            const long N = 7;

            matrix<double> a(M,N);  
            for (long r = 0; r < a.nr(); ++r)
            {
                for (long c = 0; c < a.nc(); ++c)
                {
                    a(r,c) = 10*((double)::rand())/RAND_MAX;
                }
            }

            matrix<double,M> u(M,N);  
            matrix<double> w;
            matrix<double,N,N> v(N,N);

            matrix<double,M,N,MM> a2;  
            a2 = tmp(a/2);


            svd(a2+a2,u,w,v);

            DLIB_CASSERT(  sum(round(1e10*(a - u*w*trans(v)))) == 0,"");
            DLIB_CASSERT((round(1e10*trans(u)*u)  == 1e10*identity_matrix<double,N>()),"");
            DLIB_CASSERT((round(1e10*trans(v)*v)  == 1e10*identity_matrix<double,N>()),"");
        }

        {
            srand(423452);
            const long M = 1;
            const long N = 1;

            matrix<double> a(M,N);  
            for (long r = 0; r < a.nr(); ++r)
            {
                for (long c = 0; c < a.nc(); ++c)
                {
                    a(r,c) = 10*((double)::rand())/RAND_MAX;
                }
            }

            matrix<double,M,N> u;  
            matrix<double> w;
            matrix<double,N,N> v;

            matrix<double,M,N> a2;  
            a2 = tmp(a/2);


            svd(a2+a2,u,w,v);

            DLIB_CASSERT(  sum(round(1e10*(a - u*w*trans(v)))) == 0,"");
            DLIB_CASSERT((round(1e10*trans(u)*u)  == 1e10*identity_matrix<double,N>()),"");
            DLIB_CASSERT((round(1e10*trans(v)*v)  == 1e10*identity_matrix<double,N>()),"");
        }


        {
            srand(53434);
            const long M = 5;
            const long N = 5;

            matrix<double> a(M,N);  
            for (long r = 0; r < a.nr(); ++r)
            {
                for (long c = 0; c < a.nc(); ++c)
                {
                    a(r,c) = 10*((double)::rand())/RAND_MAX;
                }
            }

            matrix<double,0,N> u(M,N);  
            matrix<double,N,N> w;
            matrix<double> v;

            svd(a,u,w,v);

            DLIB_CASSERT(  sum(round(1e10*(a - u*w*trans(v)))) == 0,"");
            DLIB_CASSERT((round(1e10*trans(u)*u)  == 1e10*identity_matrix<double,N>()),"");
            DLIB_CASSERT((round(1e10*trans(v)*v)  == 1e10*identity_matrix<double,N>()),"");
        }


        {
            srand(11234);
            const long M = 9;
            const long N = 4;

            matrix<double,0,0,MM> a(M,N);  
            for (long r = 0; r < a.nr(); ++r)
            {
                for (long c = 0; c < a.nc(); ++c)
                {
                    a(r,c) = 10*((double)::rand())/RAND_MAX;
                }
            }

            matrix<double> u;  
            matrix<double,0,0,MM> w;
            matrix<double> v;

            svd(a,u,w,v);

            DLIB_CASSERT(  sum(round(1e10*(a - u*w*trans(v)))) == 0,"");
            DLIB_CASSERT((round(1e10*trans(u)*u)  == 1e10*identity_matrix<double,N>()),"");
            DLIB_CASSERT((round(1e10*trans(v)*v)  == 1e10*identity_matrix<double,N>()),"");
        }



        {
            srand(53934);
            const long M = 2;
            const long N = 4;

            matrix<double> a(M,N);  
            for (long r = 0; r < a.nr(); ++r)
            {
                for (long c = 0; c < a.nc(); ++c)
                {
                    a(r,c) = 10*((double)::rand())/RAND_MAX;
                }
            }

            matrix<double> u;  
            matrix<double> w;
            matrix<double> v;

            svd(a,u,w,v);

            DLIB_CASSERT(  sum(round(1e10*(a - u*w*trans(v)))) == 0,"");
        }


        {
            srand(53234);
            const long M = 9;
            const long N = 40;

            matrix<double> a(M,N);  
            for (long r = 0; r < a.nr(); ++r)
            {
                for (long c = 0; c < a.nc(); ++c)
                {
                    a(r,c) = 10*((double)::rand())/RAND_MAX;
                }
            }

            matrix<double> u;  
            matrix<double> w;
            matrix<double> v;

            svd(a,u,w,v);

            DLIB_CASSERT(  sum(round(1e10*(a - u*w*trans(v)))) == 0,"");
        }


        {
            matrix<double> a(3,3);
            matrix<double,3,3> b;
            set_all_elements(a,0);

            a(0,0) = 1;
            a(1,1) = 2;
            a(2,2) = 3;
            b = a;

            DLIB_CASSERT(diag(a)(0) == 1,"");
            DLIB_CASSERT(diag(a)(1) == 2,"");
            DLIB_CASSERT(diag(a)(2) == 3,"");
            DLIB_CASSERT(diag(a).nr() == 3,"");
            DLIB_CASSERT(diag(a).nc() == 1,"");

            DLIB_CASSERT(diag(b)(0) == 1,"");
            DLIB_CASSERT(diag(b)(1) == 2,"");
            DLIB_CASSERT(diag(b)(2) == 3,"");
            DLIB_CASSERT(diag(b).nr() == 3,"");
            DLIB_CASSERT(diag(b).nc() == 1,"");

            DLIB_CASSERT(pointwise_multiply(a,b)(0,0) == 1,"");
            DLIB_CASSERT(pointwise_multiply(a,b)(1,1) == 4,"");
            DLIB_CASSERT(pointwise_multiply(a,b)(2,2) == 9,"");
            DLIB_CASSERT(pointwise_multiply(a,b)(1,0) == 0,"");
            DLIB_CASSERT(pointwise_multiply(a,b,a)(1,0) == 0,"");
            DLIB_CASSERT(pointwise_multiply(a,b,a,b)(1,0) == 0,"");


            DLIB_CASSERT(complex_matrix(a,b)(0,0) == std::complex<double>(1,1),"");
            DLIB_CASSERT(complex_matrix(a,b)(2,2) == std::complex<double>(3,3),"");
            DLIB_CASSERT(complex_matrix(a,b)(2,1) == std::complex<double>(0,0),"");
        }

        {
            matrix<float,3,1> m1, m2;
            set_all_elements(m1,2.0);
            set_all_elements(m2,1.0/2.0);
            DLIB_CASSERT(reciprocal(m1) == m2,"");
            DLIB_CASSERT((reciprocal(uniform_matrix<float,3,1>(2.0)) == m2),"");
            DLIB_CASSERT((round_zeros(uniform_matrix<float,3,1>(1e-8f)) == uniform_matrix<float,3,1>(0)) ,"");
            set_all_elements(m1,2.0);
            m2 = m1;
            m1(1,0) = static_cast<float>(1e-8);
            m2(1,0) = 0;
            DLIB_CASSERT(round_zeros(m1) == m2,"");
            m1 = round_zeros(m1);
            DLIB_CASSERT(m1 == m2,"");
        }

        {
            matrix<matrix<double,2,2> > m;
            m.set_size(3,3);
            set_all_elements(m,uniform_matrix<double,2,2>(1));
            DLIB_CASSERT((sum(m) == uniform_matrix<double,2,2>(9)),"");
            DLIB_CASSERT((round_zeros(sqrt(sum(m)) - uniform_matrix<double,2,2>(3)) == uniform_matrix<double,2,2>(0)),"");
        }

        {
            matrix<int,2,2> m1;
            matrix<int> m2;
            m2.set_size(2,2);

            set_all_elements(m1,2);
            m2 = uniform_matrix<int,2,2>(2);

            m1 = m1 + m2;
            DLIB_CASSERT((m1 == uniform_matrix<int,2,2>(4)),"");

            set_all_elements(m1,2);
            set_all_elements(m2,2);
            m1 = m1*m1;
            DLIB_CASSERT((m1 == uniform_matrix<int,2,2>(8)),"");

            m1(1,0) = 1;
            set_all_elements(m2,8);
            m2(0,1) = 1;
            m1 = trans(m1);
            DLIB_CASSERT(m1 == m2,"");
        }

        {
            matrix<double,2,3> m;
            matrix<double> m2(2,3);

            set_all_elements(m,1);
            DLIB_CASSERT(mean(m) == 1,"");
            set_all_elements(m,2);
            DLIB_CASSERT(mean(m) == 2,"");
            m(0,0) = 1;
            m(0,1) = 1;
            m(0,2) = 1;
            DLIB_CASSERT(abs(mean(m) - 1.5) < 1e-10,"");
            DLIB_CASSERT(abs(variance(m) - 0.3) < 1e-10,"");

            set_all_elements(m2,1);
            DLIB_CASSERT(mean(m2) == 1,"");
            set_all_elements(m2,2);
            DLIB_CASSERT(mean(m2) == 2,"");
            m2(0,0) = 1;
            m2(0,1) = 1;
            m2(0,2) = 1;
            DLIB_CASSERT(abs(mean(m2) - 1.5) < 1e-10,"");
            DLIB_CASSERT(abs(variance(m2) - 0.3) < 1e-10,"");

            set_all_elements(m,0);
            DLIB_CASSERT(abs(variance(m)) < 1e-10,"");
            set_all_elements(m,1);
            DLIB_CASSERT(abs(variance(m)) < 1e-10,"");
            set_all_elements(m,23.4);
            DLIB_CASSERT(abs(variance(m)) < 1e-10,"");
        }

        {
            matrix<matrix<double,3,1,MM>,2,2,MM> m;
            set_all_elements(m,uniform_matrix<double,3,1>(1));
            DLIB_CASSERT((round_zeros(variance(m)) == uniform_matrix<double,3,1>(0)),"");
            DLIB_CASSERT((round_zeros(mean(m)) == uniform_matrix<double,3,1>(1)),"");
            m(0,0) = uniform_matrix<double,3,1>(9);
            DLIB_CASSERT((round_zeros(variance(m)) == uniform_matrix<double,3,1>(16)),"");
            DLIB_CASSERT((round_zeros(mean(m)) == uniform_matrix<double,3,1>(3)),"");

            matrix<matrix<double> > m2(2,2);
            set_all_elements(m2,uniform_matrix<double,3,1>(1));
            DLIB_CASSERT((round_zeros(variance(m2)) == uniform_matrix<double,3,1>(0)),"");
            DLIB_CASSERT((round_zeros(mean(m2)) == uniform_matrix<double,3,1>(1)),"");
            m2(0,0) = uniform_matrix<double,3,1>(9);
            DLIB_CASSERT((round_zeros(variance(m2)) == uniform_matrix<double,3,1>(16)),"");
            DLIB_CASSERT((round_zeros(mean(m2)) == uniform_matrix<double,3,1>(3)),"");
        }

        {
            matrix<complex<double>,2,2,MM> m;
            set_all_elements(m,complex<double>(1,2));
            DLIB_CASSERT((conj(m) == uniform_matrix<complex<double>,2,2>(conj(m(0,0)))),"");
            DLIB_CASSERT((real(m) == uniform_matrix<double,2,2>(1)),"");
            DLIB_CASSERT((imag(m) == uniform_matrix<double,2,2>(2)),"");
            DLIB_CASSERT((sum(abs(norm(m) - uniform_matrix<double,2,2>(5))) < 1e-10 ),norm(m));

        }

        {
            matrix<double,5,5> m(5,5);

            for (long r = 0; r < m.nr(); ++r)
            {
                for (long c = 0; c < m.nc(); ++c)
                {
                    m(r,c) = r*c; 
                }
            }

            m = cos(exp(m));


            matrix<double> mi = pinv(m ); 
            DLIB_CASSERT(mi.nr() == m.nc(),"");
            DLIB_CASSERT(mi.nc() == m.nr(),"");
            DLIB_CASSERT((equal(round_zeros(mi*m,0.000001) , identity_matrix<double,5>())),"");
            DLIB_CASSERT((equal(round_zeros(m*mi,0.000001) , identity_matrix<double,5>())),"");
        }
        {
            matrix<double,5,0,MM> m(5,5);

            for (long r = 0; r < m.nr(); ++r)
            {
                for (long c = 0; c < m.nc(); ++c)
                {
                    m(r,c) = r*c; 
                }
            }

            m = cos(exp(m));


            matrix<double> mi = pinv(m ); 
            DLIB_CASSERT((equal(round_zeros(mi*m,0.000001) , identity_matrix<double,5>())),"");
        }

        {
            matrix<double,0,5,MM> m(5,5);

            for (long r = 0; r < m.nr(); ++r)
            {
                for (long c = 0; c < m.nc(); ++c)
                {
                    m(r,c) = r*c; 
                }
            }

            m = cos(exp(m));


            matrix<double> mi = pinv(m ); 
            DLIB_CASSERT((equal(round_zeros(mi*m,0.000001) , identity_matrix<double,5>())),"");
        }


        {
            matrix<double> m(5,5);

            for (long r = 0; r < m.nr(); ++r)
            {
                for (long c = 0; c < m.nc(); ++c)
                {
                    m(r,c) = r*c; 
                }
            }

            m = cos(exp(m));


            matrix<double> mi = pinv(m ); 
            DLIB_CASSERT((equal(round_zeros(mi*m,0.000001) , identity_matrix<double,5>())),"");
        }


        {
            matrix<double,5,2> m;

            for (long r = 0; r < m.nr(); ++r)
            {
                for (long c = 0; c < m.nc(); ++c)
                {
                    m(r,c) = r*c; 
                }
            }

            m = cos(exp(m));


            matrix<double> mi = pinv(m ); 
            DLIB_CASSERT(mi.nr() == m.nc(),"");
            DLIB_CASSERT(mi.nc() == m.nr(),"");
            DLIB_CASSERT((equal(round_zeros(mi*m,0.000001) , identity_matrix<double,2>())),"");
        }

        {
            matrix<double> m(5,2);

            for (long r = 0; r < m.nr(); ++r)
            {
                for (long c = 0; c < m.nc(); ++c)
                {
                    m(r,c) = r*c; 
                }
            }

            m = cos(exp(m));


            matrix<double> mi = pinv(m ); 
            DLIB_CASSERT(mi.nr() == m.nc(),"");
            DLIB_CASSERT(mi.nc() == m.nr(),"");
            DLIB_CASSERT((equal(round_zeros(mi*m,0.000001) , identity_matrix<double,2>())),"");
        }

        {
            matrix<long> a1(5,1);
            matrix<long,0,0,MM> a2(1,5);
            matrix<long,5,1> b1(5,1);
            matrix<long,1,5> b2(1,5);
            matrix<long,0,1> c1(5,1);
            matrix<long,1,0> c2(1,5);
            matrix<long,0,1,MM> d1(5,1);
            matrix<long,1,0,MM> d2(1,5);

            for (long i = 0; i < 5; ++i)
            {
                a1(i) = i;
                a2(i) = i;
                b1(i) = i;
                b2(i) = i;
                c1(i) = i;
                c2(i) = i;
                d1(i) = i;
                d2(i) = i;
            }

            DLIB_CASSERT(a1 == trans(a2),"");
            DLIB_CASSERT(a1 == trans(b2),"");
            DLIB_CASSERT(a1 == trans(c2),"");
            DLIB_CASSERT(a1 == trans(d2),"");

            DLIB_CASSERT(a1 == b1,"");
            DLIB_CASSERT(a1 == c1,"");
            DLIB_CASSERT(a1 == d1,"");

            DLIB_CASSERT(trans(a1) == c2,"");
            DLIB_CASSERT(trans(b1) == c2,"");
            DLIB_CASSERT(trans(c1) == c2,"");
            DLIB_CASSERT(trans(d1) == c2,"");

            DLIB_CASSERT(sum(a1) == 10,"");
            DLIB_CASSERT(sum(a2) == 10,"");
            DLIB_CASSERT(sum(b1) == 10,"");
            DLIB_CASSERT(sum(b2) == 10,"");
            DLIB_CASSERT(sum(c1) == 10,"");
            DLIB_CASSERT(sum(c2) == 10,"");
            DLIB_CASSERT(sum(d1) == 10,"");
            DLIB_CASSERT(sum(d2) == 10,"");

            const matrix<long> orig1 = a1;
            const matrix<long> orig2 = a2;

            ostringstream sout;
            serialize(a1,sout);
            serialize(a2,sout);
            serialize(b1,sout);
            serialize(b2,sout);
            serialize(c1,sout);
            serialize(c2,sout);
            serialize(d1,sout);
            serialize(d2,sout);

            DLIB_CASSERT(a1 == orig1,"");
            DLIB_CASSERT(a2 == orig2,"");
            DLIB_CASSERT(b1 == orig1,"");
            DLIB_CASSERT(b2 == orig2,"");
            DLIB_CASSERT(c1 == orig1,"");
            DLIB_CASSERT(c2 == orig2,"");
            DLIB_CASSERT(d1 == orig1,"");
            DLIB_CASSERT(d2 == orig2,"");

            set_all_elements(a1,99);
            set_all_elements(a2,99);
            set_all_elements(b1,99);
            set_all_elements(b2,99);
            set_all_elements(c1,99);
            set_all_elements(c2,99);
            set_all_elements(d1,99);
            set_all_elements(d2,99);

            DLIB_CASSERT(a1 != orig1,"");
            DLIB_CASSERT(a2 != orig2,"");
            DLIB_CASSERT(b1 != orig1,"");
            DLIB_CASSERT(b2 != orig2,"");
            DLIB_CASSERT(c1 != orig1,"");
            DLIB_CASSERT(c2 != orig2,"");
            DLIB_CASSERT(d1 != orig1,"");
            DLIB_CASSERT(d2 != orig2,"");

            istringstream sin(sout.str());

            deserialize(a1,sin);
            deserialize(a2,sin);
            deserialize(b1,sin);
            deserialize(b2,sin);
            deserialize(c1,sin);
            deserialize(c2,sin);
            deserialize(d1,sin);
            deserialize(d2,sin);

            DLIB_CASSERT(a1 == orig1,"");
            DLIB_CASSERT(a2 == orig2,"");
            DLIB_CASSERT(b1 == orig1,"");
            DLIB_CASSERT(b2 == orig2,"");
            DLIB_CASSERT(c1 == orig1,"");
            DLIB_CASSERT(c2 == orig2,"");
            DLIB_CASSERT(d1 == orig1,"");
            DLIB_CASSERT(d2 == orig2,"");


        }

        {
            matrix<double,1,0> a(5);
            matrix<double,0,1> b(5);
            matrix<double,1,5> c(5);
            matrix<double,5,1> d(5);
            DLIB_CASSERT(a.nr() == 1,"");
            DLIB_CASSERT(a.nc() == 5,"");
            DLIB_CASSERT(c.nr() == 1,"");
            DLIB_CASSERT(c.nc() == 5,"");

            DLIB_CASSERT(b.nc() == 1,"");
            DLIB_CASSERT(b.nr() == 5,"");
            DLIB_CASSERT(d.nc() == 1,"");
            DLIB_CASSERT(d.nr() == 5,"");
        }

        {
            matrix<double,1,0> a;
            matrix<double,0,1> b;
            matrix<double,1,5> c;
            matrix<double,5,1> d;

            a.set_size(5);
            b.set_size(5);
            c.set_size(5);
            d.set_size(5);

            DLIB_CASSERT(a.nr() == 1,"");
            DLIB_CASSERT(a.nc() == 5,"");
            DLIB_CASSERT(c.nr() == 1,"");
            DLIB_CASSERT(c.nc() == 5,"");

            DLIB_CASSERT(b.nc() == 1,"");
            DLIB_CASSERT(b.nr() == 5,"");
            DLIB_CASSERT(d.nc() == 1,"");
            DLIB_CASSERT(d.nr() == 5,"");
        }

        {
            matrix<double> a(1,5);
            matrix<double> b(5,1);

            set_all_elements(a,1);
            set_all_elements(b,1);


            a = a*b;

            DLIB_CASSERT(a(0) == 5,"");
        }

        {
            matrix<double> a(6,7);

            for (long r = 0; r < a.nr(); ++r)
            {
                for (long c = 0; c < a.nc(); ++c)
                {
                    a(r,c) = r*a.nc() + c;
                }
            }



            DLIB_CASSERT(rowm(a,1).nr() == 1,"");
            DLIB_CASSERT(rowm(a,1).nc() == a.nc(),"");
            DLIB_CASSERT(colm(a,1).nr() == a.nr(),"");
            DLIB_CASSERT(colm(a,1).nc() == 1,"");

            for (long c = 0; c < a.nc(); ++c)
            {
                DLIB_CASSERT( rowm(a,1)(c) == 1*a.nc() + c,"");
            }

            for (long r = 0; r < a.nr(); ++r)
            {
                DLIB_CASSERT( colm(a,1)(r) == r*a.nc() + 1,"");
            }

            rectangle rect(2, 1, 3+2-1, 2+1-1);
            DLIB_CASSERT(get_rect(a).contains(get_rect(a)), "");
            DLIB_CASSERT(get_rect(a).contains(rect), "");
            for (long r = 0; r < 2; ++r)
            {
                for (long c = 0; c < 3; ++c)
                {
                    DLIB_CASSERT(subm(a,1,2,2,3)(r,c) == (r+1)*a.nc() + c+2,"");
                    DLIB_CASSERT(subm(a,1,2,2,3) == subm(a,rect),"");
                }
            }

            DLIB_CASSERT(subm(a,rectangle()).nr() == 0,"");
            DLIB_CASSERT(subm(a,rectangle()).nc() == 0,"");

        }

        {
            array2d<double>::kernel_1a_c a;
            a.set_size(6,7);


            for (long r = 0; r < a.nr(); ++r)
            {
                for (long c = 0; c < a.nc(); ++c)
                {
                    a[r][c] = r*a.nc() + c;
                }
            }



            DLIB_CASSERT(rowm(array_to_matrix(a),1).nr() == 1,"");
            DLIB_CASSERT(rowm(array_to_matrix(a),1).nc() == a.nc(),"");
            DLIB_CASSERT(colm(array_to_matrix(a),1).nr() == a.nr(),"");
            DLIB_CASSERT(colm(array_to_matrix(a),1).nc() == 1,"");

            for (long c = 0; c < a.nc(); ++c)
            {
                DLIB_CASSERT( rowm(array_to_matrix(a),1)(c) == 1*a.nc() + c,"");
            }

            for (long r = 0; r < a.nr(); ++r)
            {
                DLIB_CASSERT( colm(array_to_matrix(a),1)(r) == r*a.nc() + 1,"");
            }

            for (long r = 0; r < 2; ++r)
            {
                for (long c = 0; c < 3; ++c)
                {
                    DLIB_CASSERT(subm(array_to_matrix(a),1,2,2,3)(r,c) == (r+1)*a.nc() + c+2,"");
                }
            }


        }

        {
            array2d<double>::kernel_1a_c m;
            m.set_size(5,5);

            for (long r = 0; r < m.nr(); ++r)
            {
                for (long c = 0; c < m.nc(); ++c)
                {
                    m[r][c] = r*c; 
                }
            }


            matrix<double> mi = pinv(cos(exp(array_to_matrix(m))) ); 
            DLIB_CASSERT(mi.nr() == m.nc(),"");
            DLIB_CASSERT(mi.nc() == m.nr(),"");
            DLIB_CASSERT((equal(round_zeros(mi*cos(exp(array_to_matrix(m))),0.000001) , identity_matrix<double,5>())),"");
            DLIB_CASSERT((equal(round_zeros(cos(exp(array_to_matrix(m)))*mi,0.000001) , identity_matrix<double,5>())),"");
        }

        {
            matrix<long,5,5> m1, res;
            matrix<long,2,2> m2;

            set_all_elements(m1,0);


            long res_vals[] = {
                9, 9, 9, 9, 9,
                0, 1, 1, 0, 0,
                0, 1, 1, 0, 2,
                0, 0, 2, 2, 2,
                0, 0, 2, 2, 0
            };

            res = res_vals;

            set_all_elements(m2, 1);
            set_subm(m1, range(1,2), range(1,2)) = subm(m2,0,0,2,2);
            set_all_elements(m2, 2);
            set_subm(m1, 3,2,2,2) = m2;

            set_colm(m1,4) = trans(rowm(m1,4));
            set_rowm(m1,0) = 9;

            DLIB_CASSERT(m1 == res, "m1: \n" << m1 << "\nres: \n" << res);

            set_subm(m1,0,0,5,5) = m1*m1;
            DLIB_CASSERT(m1 == res*res, "m1: \n" << m1 << "\nres*res: \n" << res*res);

            m1 = res;
            set_subm(m1,1,1,2,2) = subm(m1,0,0,2,2);

            long res_vals2[] = {
                9, 9, 9, 9, 9,
                0, 9, 9, 0, 0,
                0, 0, 1, 0, 2,
                0, 0, 2, 2, 2,
                0, 0, 2, 2, 0
            };

            res = res_vals2;
            DLIB_CASSERT(m1 == res, "m1: \n" << m1 << "\nres: \n" << res);


        }

        {
            matrix<long,5,5> m1, res;
            matrix<long,2,2> m2;

            set_all_elements(m1,0);


            long res_vals[] = {
                9, 9, 9, 9, 9,
                0, 1, 1, 0, 0,
                0, 1, 1, 0, 2,
                0, 0, 2, 2, 2,
                0, 0, 2, 2, 0
            };

            res = res_vals;

            set_all_elements(m2, 1);
            set_subm(m1, rectangle(1,1,2,2)) = subm(m2,0,0,2,2);
            set_all_elements(m2, 2);
            set_subm(m1, 3,2,2,2) = m2;

            set_colm(m1,4) = trans(rowm(m1,4));
            set_rowm(m1,0) = 9;

            DLIB_CASSERT(m1 == res, "m1: \n" << m1 << "\nres: \n" << res);

            set_subm(m1,0,0,5,5) = m1*m1;
            DLIB_CASSERT(m1 == res*res, "m1: \n" << m1 << "\nres*res: \n" << res*res);

            m1 = res;
            set_subm(m1,1,1,2,2) = subm(m1,0,0,2,2);

            long res_vals2[] = {
                9, 9, 9, 9, 9,
                0, 9, 9, 0, 0,
                0, 0, 1, 0, 2,
                0, 0, 2, 2, 2,
                0, 0, 2, 2, 0
            };

            res = res_vals2;
            DLIB_CASSERT(m1 == res, "m1: \n" << m1 << "\nres: \n" << res);


        }

        {
            matrix<long,5,5> m1, res;
            matrix<long,2,2> m2;

            set_all_elements(m1,0);


            long res_vals[] = {
                9, 0, 3, 3, 0,
                9, 2, 2, 2, 0,
                9, 2, 2, 2, 0,
                4, 4, 4, 4, 4,
                9, 0, 3, 3, 0
            };
            long res_vals_c3[] = {
                9, 0, 3, 0,
                9, 2, 2, 0,
                9, 2, 2, 0,
                4, 4, 4, 4,
                9, 0, 3, 0
            };
            long res_vals_r2[] = {
                9, 0, 3, 3, 0,
                9, 2, 2, 2, 0,
                4, 4, 4, 4, 4,
                9, 0, 3, 3, 0
            };

            matrix<long> temp;

            res = res_vals;

            temp = matrix<long,4,5>(res_vals_r2);
            DLIB_CASSERT(remove_row<2>(res) == temp,"");
            DLIB_CASSERT(remove_row<2>(res)(3,3) == 3,"");
            DLIB_CASSERT(remove_row<2>(res).nr() == 4,"");
            DLIB_CASSERT(remove_row<2>(res).nc() == 5,"");
            DLIB_CASSERT(remove_row(res,2) == temp,"");
            DLIB_CASSERT(remove_row(res,2)(3,3) == 3,"");
            DLIB_CASSERT(remove_row(res,2).nr() == 4,"");
            DLIB_CASSERT(remove_row(res,2).nc() == 5,"");

            temp = matrix<long,5,5>(res_vals);
            temp = remove_row(res,2);
            DLIB_CASSERT((temp == matrix<long,4,5>(res_vals_r2)),"");
            temp = matrix<long,5,5>(res_vals);
            temp = remove_col(res,3);
            DLIB_CASSERT((temp == matrix<long,5,4>(res_vals_c3)),"");

            matrix<long,3,1> vect;
            set_all_elements(vect,1);
            temp = identity_matrix<long>(3);
            temp = temp*vect;
            DLIB_CASSERT(temp == vect,"");

            temp = matrix<long,5,4>(res_vals_c3);
            DLIB_CASSERT(remove_col(res,3) == temp,"");
            DLIB_CASSERT(remove_col(res,3)(2,3) == 0,"");
            DLIB_CASSERT(remove_col(res,3).nr() == 5,"");
            DLIB_CASSERT(remove_col(res,3).nc() == 4,"");

            set_all_elements(m2, 1);
            set_subm(m1, rectangle(1,1,3,2)) = 2;
            set_all_elements(m2, 2);
            set_subm(m1, 3,2,2,2) = 3;

            set_colm(m1,0) = 9;
            set_rowm(m1,0) = rowm(m1,4);
            set_rowm(m1,3) = 4;

            DLIB_CASSERT(m1 == res, "m1: \n" << m1 << "\nres: \n" << res);

        }


        {

            const double stuff[] = { 
                1, 2, 3,
                6, 3, 3, 
                7, 3, 9};

            matrix<double,3,3> m(stuff);

            // make m be symmetric
            m = m*trans(m);

            matrix<double> L = cholesky_decomposition(m);
            DLIB_CASSERT(equal(L*trans(L), m), "");

            DLIB_CASSERT(equal(inv(m), inv_upper_triangular(trans(L))*inv_lower_triangular((L))), "") 
            DLIB_CASSERT(equal(round_zeros(inv_upper_triangular(trans(L))*trans(L),1e-10), identity_matrix<double>(3), 1e-10),""); 
            DLIB_CASSERT(equal(round_zeros(inv_lower_triangular((L))*(L),1e-10) ,identity_matrix<double>(3),1e-10),""); 

        }

        {

            const double stuff[] = { 
                1, 2, 3, 6, 3, 4,
                6, 3, 3, 1, 2, 3,
                7, 3, 9, 54.3, 5, 3,
                -6, 3, -3, 1, 2, 3,
                1, 2, 3, 5, -3, 3,
                7, 3, -9, 54.3, 5, 3
                };

            matrix<double,6,6> m(stuff);

            // make m be symmetric
            m = m*trans(m);

            matrix<double> L = cholesky_decomposition(m);
            DLIB_CASSERT(equal(L*trans(L), m, 1e-10), L*trans(L)-m);

            DLIB_CASSERT(equal(inv(m), inv_upper_triangular(trans(L))*inv_lower_triangular((L))), "") 
            DLIB_CASSERT(equal(inv(m), trans(inv_lower_triangular(L))*inv_lower_triangular((L))), "") 
            DLIB_CASSERT(equal(inv(m), trans(inv_lower_triangular(L))*trans(inv_upper_triangular(trans(L)))), "") 
            DLIB_CASSERT(equal(round_zeros(inv_upper_triangular(trans(L))*trans(L),1e-10) , identity_matrix<double>(6), 1e-10),
                         round_zeros(inv_upper_triangular(trans(L))*trans(L),1e-10)); 
            DLIB_CASSERT(equal(round_zeros(inv_lower_triangular((L))*(L),1e-10) ,identity_matrix<double>(6), 1e-10),
                         round_zeros(inv_lower_triangular((L))*(L),1e-10)); 

        }

        {

            matrix<double,6,6> m(identity_matrix<double>(6)*4.5);

            matrix<double> L = cholesky_decomposition(m);
            DLIB_CASSERT(equal(L*trans(L), m, 1e-10), L*trans(L)-m);

            DLIB_CASSERT(equal(inv(m), inv_upper_triangular(trans(L))*inv_lower_triangular((L))), "") 
            DLIB_CASSERT(equal(round_zeros(inv_upper_triangular(trans(L))*trans(L),1e-10) , identity_matrix<double>(6), 1e-10),
                         round_zeros(inv_upper_triangular(trans(L))*trans(L),1e-10)); 
            DLIB_CASSERT(equal(round_zeros(inv_lower_triangular((L))*(L),1e-10) ,identity_matrix<double>(6), 1e-10),
                         round_zeros(inv_lower_triangular((L))*(L),1e-10)); 

        }

        {

            matrix<double,6,6> m(identity_matrix<double>(6)*4.5);
            m(1,4) = 2;

            DLIB_CASSERT(dlib::equal(inv_upper_triangular(m), inv(m),1e-10), inv_upper_triangular(m)-inv(m));
            DLIB_CASSERT(dlib::equal(inv_lower_triangular(trans(m)), inv(trans(m)),1e-10), inv_lower_triangular(trans(m))-inv(trans(m)));

        }

        {
            matrix<double> a;
            matrix<float> b;
            matrix<int> i;
            a.set_size(1000,10);
            b.set_size(1000,10);
            i.set_size(1000,10);
            dlib::rand::float_1a rnd;
            for (long r = 0; r < a.nr(); ++r)
            {
                for (long c = 0; c < a.nc(); ++c)
                {
                    a(r,c) = rnd.get_random_double();
                    b(r,c) = rnd.get_random_float();
                    i(r,c) = r+c*r;
                }
            }

            // make sure the multiply optimizations aren't messing things up
            DLIB_CASSERT(trans(i)*i == tmp(trans(i)*i),"");
            DLIB_CASSERT(equal(trans(a)*a , tmp(trans(a)*a), 1e-11),max(abs(trans(a)*a - tmp(trans(a)*a))));
            DLIB_CASSERT(equal(trans(b)*b , tmp(trans(b)*b), 1e-3f),max(abs(trans(b)*b - tmp(trans(b)*b))));
        }

        {
            matrix<int,4> i(4,1);
            i(0) = 1;
            i(1) = 2;
            i(2) = 3;
            i(3) = 4;
            matrix<int,4,4> m;
            set_all_elements(m,0);
            m(0,0) = 1;
            m(1,1) = 2;
            m(2,2) = 3;
            m(3,3) = 4;

            DLIB_CASSERT(diagm(i) == m,"");
        }

        {
            matrix<int,1,4> i;
            i(0) = 1;
            i(1) = 2;
            i(2) = 3;
            i(3) = 4;
            matrix<int,4,4> m;
            set_all_elements(m,0);
            m(0,0) = 1;
            m(1,1) = 2;
            m(2,2) = 3;
            m(3,3) = 4;

            DLIB_CASSERT(diagm(i) == m,"");
        }

        {
            matrix<int> i(4,1);
            i(0) = 1;
            i(1) = 2;
            i(2) = 3;
            i(3) = 4;
            matrix<int> m(4,4);
            set_all_elements(m,0);
            m(0,0) = 1;
            m(1,1) = 2;
            m(2,2) = 3;
            m(3,3) = 4;

            DLIB_CASSERT(diagm(i) == m,"");
        }

        {
            matrix<int> i(1,4);
            i(0) = 1;
            i(1) = 2;
            i(2) = 3;
            i(3) = 4;
            matrix<int> m(4,4);
            set_all_elements(m,0);
            m(0,0) = 1;
            m(1,1) = 2;
            m(2,2) = 3;
            m(3,3) = 4;

            DLIB_CASSERT(diagm(i) == m,"");
        }

        {
            DLIB_CASSERT(range(0,5).nr() == 6,"");
            DLIB_CASSERT(range(1,5).nr() == 5,"");
            DLIB_CASSERT(range(0,5).nc() == 1,"");
            DLIB_CASSERT(range(1,5).nc() == 1,"");
            DLIB_CASSERT(trans(range(0,5)).nc() == 6,"");
            DLIB_CASSERT(trans(range(1,5)).nc() == 5,"");
            DLIB_CASSERT(trans(range(0,5)).nr() == 1,"");
            DLIB_CASSERT(trans(range(1,5)).nr() == 1,"");

            DLIB_CASSERT(range(0,2,5).nr() == 3,"");
            DLIB_CASSERT(range(1,2,5).nr() == 3,"");
            DLIB_CASSERT(range(0,2,5).nc() == 1,"");
            DLIB_CASSERT(range(1,2,5).nc() == 1,"");
            DLIB_CASSERT(trans(range(0,2,5)).nc() == 3,"");
            DLIB_CASSERT(trans(range(1,2,5)).nc() == 3,"");
            DLIB_CASSERT(trans(range(0,2,5)).nr() == 1,"");
            DLIB_CASSERT(trans(range(1,2,5)).nr() == 1,"");

            DLIB_CASSERT(range(0,3,6).nr() == 3,"");
            DLIB_CASSERT(range(1,3,5).nr() == 2,"");
            DLIB_CASSERT(range(0,3,5).nc() == 1,"");
            DLIB_CASSERT(range(1,3,5).nc() == 1,"");
            DLIB_CASSERT(trans(range(0,3,6)).nc() == 3,"");
            DLIB_CASSERT(trans(range(1,3,5)).nc() == 2,"");
            DLIB_CASSERT(trans(range(0,3,5)).nr() == 1,"");
            DLIB_CASSERT(trans(range(1,3,5)).nr() == 1,"");

            DLIB_CASSERT(range(1,9,5).nr() == 1,"");
            DLIB_CASSERT(range(1,9,5).nc() == 1,"");

            DLIB_CASSERT(range(0,0).nr() == 1,"");
            DLIB_CASSERT(range(0,0).nc() == 1,"");

            DLIB_CASSERT(range(1,1)(0) == 1,"");

            DLIB_CASSERT(range(0,5)(0) == 0 && range(0,5)(1) == 1 && range(0,5)(5) == 5,"");
            DLIB_CASSERT(range(1,2,5)(0) == 1 && range(1,2,5)(1) == 3 && range(1,2,5)(2) == 5,"");
            DLIB_CASSERT((range<0,5>()(0) == 0 && range<0,5>()(1) == 1 && range<0,5>()(5) == 5),"");
            DLIB_CASSERT((range<1,2,5>()(0) == 1 && range<1,2,5>()(1) == 3 && range<1,2,5>()(2) == 5),"");


            DLIB_CASSERT((range<0,5>().nr() == 6),"");
            DLIB_CASSERT((range<1,5>().nr() == 5),"");
            DLIB_CASSERT((range<0,5>().nc() == 1),"");
            DLIB_CASSERT((range<1,5>().nc() == 1),"");
            DLIB_CASSERT((trans(range<0,5>()).nc() == 6),"");
            DLIB_CASSERT((trans(range<1,5>()).nc() == 5),"");
            DLIB_CASSERT((trans(range<0,5>()).nr() == 1),"");
            DLIB_CASSERT((trans(range<1,5>()).nr() == 1),"");

            DLIB_CASSERT((range<0,2,5>().nr() == 3),"");
            DLIB_CASSERT((range<1,2,5>().nr() == 3),"");
            DLIB_CASSERT((range<0,2,5>().nc() == 1),"");
            DLIB_CASSERT((range<1,2,5>().nc() == 1),"");
            DLIB_CASSERT((trans(range<0,2,5>()).nc() == 3),"");
            DLIB_CASSERT((trans(range<1,2,5>()).nc() == 3),"");
            DLIB_CASSERT((trans(range<0,2,5>()).nr() == 1),"");
            DLIB_CASSERT((trans(range<1,2,5>()).nr() == 1),"");

            DLIB_CASSERT((range<0,3,6>().nr() == 3),"");
            DLIB_CASSERT((range<1,3,5>().nr() == 2),"");
            DLIB_CASSERT((range<0,3,5>().nc() == 1),"");
            DLIB_CASSERT((range<1,3,5>().nc() == 1),"");
            DLIB_CASSERT((trans(range<0,3,6>()).nc() == 3),"");
            DLIB_CASSERT((trans(range<1,3,5>()).nc() == 2),"");
            DLIB_CASSERT((trans(range<0,3,5>()).nr() == 1),"");
            DLIB_CASSERT((trans(range<1,3,5>()).nr() == 1),"");
        }

        {
            matrix<double> m(4,3);
            for (long r = 0; r < m.nr(); ++r)
            {
                for (long c = 0; c < m.nc(); ++c)
                {
                    m(r,c) = r*c;
                }
            }

            DLIB_CASSERT(subm(m,range(0,3),range(0,0)) == colm(m,0),"");
            DLIB_CASSERT(subm(m,range(0,3),range(1,1)) == colm(m,1),"");
            DLIB_CASSERT(subm(m,range(0,3),range(2,2)) == colm(m,2),"");

            DLIB_CASSERT(subm(m,range(0,0),range(0,2)) == rowm(m,0),"");
            DLIB_CASSERT(subm(m,range(1,1),range(0,2)) == rowm(m,1),"");
            DLIB_CASSERT(subm(m,range(2,2),range(0,2)) == rowm(m,2),"");
            DLIB_CASSERT(subm(m,range(3,3),range(0,2)) == rowm(m,3),"");

            DLIB_CASSERT(subm(m,0,0,2,2) == subm(m,range(0,1),range(0,1)),"");
            DLIB_CASSERT(subm(m,1,1,2,2) == subm(m,range(1,2),range(1,2)),"");

            matrix<double,2,2> m2 = subm(m,range(0,2,2),range(0,2,2));

            DLIB_CASSERT(m2(0,0) == m(0,0),"");
            DLIB_CASSERT(m2(0,1) == m(0,2),"");
            DLIB_CASSERT(m2(1,0) == m(2,0),"");
            DLIB_CASSERT(m2(1,1) == m(2,2),"");


        }
        {
            matrix<double,4,3> m(4,3);
            for (long r = 0; r < m.nr(); ++r)
            {
                for (long c = 0; c < m.nc(); ++c)
                {
                    m(r,c) = r*c;
                }
            }

            DLIB_CASSERT(subm(m,range<0,3>(),range<0,0>()) == colm(m,0),"");
            DLIB_CASSERT(subm(m,range<0,3>(),range<1,1>()) == colm(m,1),"");
            DLIB_CASSERT(subm(m,range<0,3>(),range<2,2>()) == colm(m,2),"");

            DLIB_CASSERT(subm(m,range<0,0>(),range<0,2>()) == rowm(m,0),"");
            DLIB_CASSERT(subm(m,range<1,1>(),range<0,2>()) == rowm(m,1),"");
            DLIB_CASSERT(subm(m,range<2,2>(),range<0,2>()) == rowm(m,2),"");
            DLIB_CASSERT(subm(m,range<3,3>(),range<0,2>()) == rowm(m,3),"");

            DLIB_CASSERT(subm(m,0,0,2,2) == subm(m,range<0,1>(),range<0,1>()),"");
            DLIB_CASSERT(subm(m,1,1,2,2) == subm(m,range<1,2>(),range<1,2>()),"");

            matrix<double,2,2> m2 = subm(m,range<0,2,2>(),range<0,2,2>());

            DLIB_CASSERT(m2(0,0) == m(0,0),"");
            DLIB_CASSERT(m2(0,1) == m(0,2),"");
            DLIB_CASSERT(m2(1,0) == m(2,0),"");
            DLIB_CASSERT(m2(1,1) == m(2,2),"");


        }

        {
            matrix<double,4,5> m;
            set_subm(m, range(0,3), range(0,4)) = 4;
            DLIB_CASSERT(min(m) == max(m) && min(m) == 4,"");

            set_subm(m,range(1,1),range(0,4)) = 7;
            DLIB_CASSERT((rowm(m,0) == uniform_matrix<double>(1,5, 4)),"");
            DLIB_CASSERT((rowm(m,1) == uniform_matrix<double>(1,5, 7)),"");
            DLIB_CASSERT((rowm(m,2) == uniform_matrix<double>(1,5, 4)),"");
            DLIB_CASSERT((rowm(m,3) == uniform_matrix<double>(1,5, 4)),"");


            set_subm(m, range(0,2,3), range(0,2,4)) = trans(subm(m,0,0,3,2));


            DLIB_CASSERT(m(0,2) == 7,"");
            DLIB_CASSERT(m(2,2) == 7,"");

            DLIB_CASSERT(sum(m) == 7*5+ 7+7 +  4*(4*5 - 7),"");

        }

    }






    class matrix_tester : public tester
    {
    public:
        matrix_tester (
        ) :
            tester ("test_matrix",
                    "Runs tests on the matrix component.")
        {}

        void perform_test (
        )
        {
            matrix_test();
        }
    } a;

}


