    // Copyright (C) 2006  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_MATRIx_DATA_LAYOUT_
    #define DLIB_MATRIx_DATA_LAYOUT_

    #include "../algs.h"
    #include "matrix_fwd.h"
    #include "matrix_data_layout_abstract.h"
    #ifdef MATLAB_MEX_FILE
    #include <mex.h>
    #endif

    // GCC 4.8 gives false alarms about some matrix operations going out of bounds.  Disable
    // these false warnings.
    #if defined(__GNUC__) && ((__GNUC__ >= 4 && __GNUC_MINOR__ >= 8) || (__GNUC__ > 4))
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Warray-bounds"
    #endif

    namespace dlib
    {

    // ----------------------------------------------------------------------------------------

        /*!
            A matrix layout object is any object that contains a templated class called "layout"
            with an interface identical to one below:
            (Note that all the template arguments are just the template arguments from the dlib::matrix
            object and the member functions are defined identically to the ones with the same
            signatures inside the matrix object.)

            struct matrix_layout
            {
                template <
                    typename T,
                    long num_rows,
                    long num_cols,
                    typename mem_manager
                    >
                class layout
                {
                public:

                    T& operator() (
                        long r,
                        long c
                    );

                    const T& operator() (
                        long r,
                        long c
                    );

                    T& operator() (
                        long i
                    );

                    const T& operator() (
                        long i
                    ) const;

                    void swap(
                        layout& item
                    );

                    long nr (
                    ) const;

                    long nc (
                    ) const;

                    void set_size (
                        long nr_,
                        long nc_
                    );
                };
            };
        !*/

    // ----------------------------------------------------------------------------------------

        struct row_major_layout
        {
            // if a matrix is bigger than this many bytes then don't put it on the stack
            const static size_t max_stack_based_size = 256;

            // this is a hack to avoid a compile time error in visual studio 8.  I would just
            // use sizeof(T) and be done with it but that won't compile.  The idea here
            // is to avoid using the stack allocation of the layout object if it
            // is going to contain another matrix and also avoid asking for the sizeof()
            // the contained matrix.
            template <typename T>
            struct get_sizeof_helper
            {
                const static std::size_t val = sizeof(T);
            };

            template <typename T, long NR, long NC, typename mm, typename l>
            struct get_sizeof_helper<matrix<T,NR,NC,mm,l> >
            {
                const static std::size_t val = 1000000;
            };

            template <
                typename T,
                long num_rows,
                long num_cols,
                typename mem_manager,
                int val = static_switch <
                    // when the sizes are all non zero and small
                    (num_rows*num_cols*get_sizeof_helper<T>::val <= max_stack_based_size) && (num_rows != 0 && num_cols != 0),
                // when the sizes are all non zero and big
                (num_rows*num_cols*get_sizeof_helper<T>::val >  max_stack_based_size) && (num_rows != 0 && num_cols != 0),
                num_rows == 0 && num_cols != 0,
                num_rows != 0 && num_cols == 0,
                num_rows == 0 && num_cols == 0
                >::value
                >
            class layout ;
            /*!
                WHAT THIS OBJECT REPRESENTS
                    This object represents the actual allocation of space for a matrix.
                    Small matrices allocate all their data on the stack and bigger ones
                    use a memory_manager to get their memory.
            !*/

        // ------------------------------------------------------------------------------------

            template <
                typename T,
                long num_rows,
                long num_cols,
                typename mem_manager
                >
            class layout<T,num_rows,num_cols,mem_manager,1> : noncopyable // when the sizes are all non zero and small
            {
            public:
                const static long NR = num_rows;
                const static long NC = num_cols;

                layout() {}

                T& operator() (
                    long r,
                    long c
                ) { return *(data+r*num_cols + c); }

                const T& operator() (
                    long r,
                    long c
                ) const { return *(data+r*num_cols + c); }

                T& operator() (
                    long i
                ) { return data[i]; }

                const T& operator() (
                    long i
                ) const { return data[i]; }

                void swap(
                    layout& item
                )
                {
                    for (long r = 0; r < num_rows; ++r)
                    {
                        for (long c = 0; c < num_cols; ++c)
                        {
                            exchange((*this)(r,c),item(r,c));
                        }
                    }
                }

                long nr (
                ) const { return num_rows; }

                long nc (
                ) const { return num_cols; }

                void set_size (
                    long ,
                    long
                )
                {
                }

    #ifdef MATLAB_MEX_FILE
                void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); }
                mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); }
                void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); }
                bool _private_is_owned_by_matlab() const { return false; }
    #endif

            private:
                T data[num_rows*num_cols];
            };

        // ------------------------------------------------------------------------------------

            template <
                typename T,
                long num_rows,
                long num_cols,
                typename mem_manager
                >
            class layout<T,num_rows,num_cols,mem_manager,2> : noncopyable // when the sizes are all non zero and big
            {
            public:
                const static long NR = num_rows;
                const static long NC = num_cols;

                layout (
                ) { data = pool.allocate_array(num_rows*num_cols); }

                ~layout ()
                { pool.deallocate_array(data); }

                T& operator() (
                    long r,
                    long c
                ) { return data[r*num_cols + c]; }

                const T& operator() (
                    long r,
                    long c
                ) const { return data[r*num_cols + c]; }

                T& operator() (
                    long i
                ) { return data[i]; }

                const T& operator() (
                    long i
                ) const { return data[i]; }

                void swap(
                    layout& item
                )
                {
                    std::swap(item.data,data);
                    pool.swap(item.pool);
                }

                long nr (
                ) const { return num_rows; }

                long nc (
                ) const { return num_cols; }

                void set_size (
                    long ,
                    long
                )
                {
                }

    #ifdef MATLAB_MEX_FILE
                void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); }
                mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); }
                void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); }
                bool _private_is_owned_by_matlab() const { return false; }
    #endif

            private:

                T* data;
                typename mem_manager::template rebind<T>::other pool;
                };

        // ------------------------------------------------------------------------------------

            template <
                typename T,
                long num_rows,
                long num_cols,
                typename mem_manager
                >
            class layout<T,num_rows,num_cols,mem_manager,3> : noncopyable // when num_rows == 0 && num_cols != 0,
            {
            public:
                const static long NR = num_rows;
                const static long NC = num_cols;

                layout (
                ):data(0), nr_(0) { }

                ~layout ()
                {
                    if (data)
                        pool.deallocate_array(data);
                }

                T& operator() (
                    long r,
                    long c
                ) { return data[r*num_cols + c]; }

                const T& operator() (
                    long r,
                    long c
                ) const { return data[r*num_cols + c]; }

                T& operator() (
                    long i
                ) { return data[i]; }

                const T& operator() (
                    long i
                ) const { return data[i]; }

                void swap(
                    layout& item
                )
                {
                    std::swap(item.data,data);
                    std::swap(item.nr_,nr_);
                    pool.swap(item.pool);
                }

                long nr (
                ) const { return nr_; }

                long nc (
                ) const { return num_cols; }

                void set_size (
                    long nr,
                    long nc
                )
                {
                    if (data)
                    {
                        pool.deallocate_array(data);
                    }
                    data = pool.allocate_array(nr*nc);
                    nr_ = nr;
                }

    #ifdef MATLAB_MEX_FILE
                void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); }
                mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); }
                void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); }
                bool _private_is_owned_by_matlab() const { return false; }
    #endif

            private:

                T* data;
                long nr_;
                typename mem_manager::template rebind<T>::other pool;
                };

        // ------------------------------------------------------------------------------------

            template <
                typename T,
                long num_rows,
                long num_cols,
                typename mem_manager
                >
            class layout<T,num_rows,num_cols,mem_manager,4> : noncopyable // when num_rows != 0 && num_cols == 0
            {
            public:
                const static long NR = num_rows;
                const static long NC = num_cols;

                layout (
                ):data(0), nc_(0) { }

                ~layout ()
                {
                    if (data)
                    {
                        pool.deallocate_array(data);
                    }
                }

                T& operator() (
                    long r,
                    long c
                ) { return data[r*nc_ + c]; }

                const T& operator() (
                    long r,
                    long c
                ) const { return data[r*nc_ + c]; }

                T& operator() (
                    long i
                ) { return data[i]; }

                const T& operator() (
                    long i
                ) const { return data[i]; }

                void swap(
                    layout& item
                )
                {
                    std::swap(item.data,data);
                    std::swap(item.nc_,nc_);
                    pool.swap(item.pool);
                }

                long nr (
                ) const { return num_rows; }

                long nc (
                ) const { return nc_; }

                void set_size (
                    long nr,
                    long nc
                )
                {
                    if (data)
                    {
                        pool.deallocate_array(data);
                    }
                    data = pool.allocate_array(nr*nc);
                    nc_ = nc;
                }

    #ifdef MATLAB_MEX_FILE
                void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); }
                mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); }
                void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); }
                bool _private_is_owned_by_matlab() const { return false; }
    #endif

            private:

                T* data;
                long nc_;
                typename mem_manager::template rebind<T>::other pool;
                };

        // ------------------------------------------------------------------------------------

            template <
                typename T,
                long num_rows,
                long num_cols,
                typename mem_manager
                >
            class layout<T,num_rows,num_cols,mem_manager,5> : noncopyable // when num_rows == 0 && num_cols == 0
            {
            public:
                const static long NR = num_rows;
                const static long NC = num_cols;

                layout (
                ):data(0), nr_(0), nc_(0) { }

                ~layout ()
                {
                    if (data)
                    {
                        pool.deallocate_array(data);
                    }
                }

                T& operator() (
                    long r,
                    long c
                ) { return data[r*nc_ + c]; }

                const T& operator() (
                    long r,
                    long c
                ) const { return data[r*nc_ + c]; }

                T& operator() (
                    long i
                ) { return data[i]; }

                const T& operator() (
                    long i
                ) const { return data[i]; }

                void swap(
                    layout& item
                )
                {
                    std::swap(item.data,data);
                    std::swap(item.nc_,nc_);
                    std::swap(item.nr_,nr_);
                    pool.swap(item.pool);
                }

                long nr (
                ) const { return nr_; }

                long nc (
                ) const { return nc_; }

                void set_size (
                    long nr,
                    long nc
                )
                {
                    if (data)
                    {
                        pool.deallocate_array(data);
                    }
                    data = pool.allocate_array(nr*nc);
                    nr_ = nr;
                    nc_ = nc;
                }

    #ifdef MATLAB_MEX_FILE
                void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); }
                mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); }
                void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); }
                bool _private_is_owned_by_matlab() const { return false; }
    #endif
            private:
                T* data;
                long nr_;
                long nc_;
                typename mem_manager::template rebind<T>::other pool;
                };

        };

    // ----------------------------------------------------------------------------------------

        struct column_major_layout
        {
            // if a matrix is bigger than this many bytes then don't put it on the stack
            const static size_t max_stack_based_size = 256;

            // this is a hack to avoid a compile time error in visual studio 8.  I would just
            // use sizeof(T) and be done with it but that won't compile.  The idea here
            // is to avoid using the stack allocation of the layout object if it
            // is going to contain another matrix and also avoid asking for the sizeof()
            // the contained matrix.
            template <typename T>
            struct get_sizeof_helper
            {
                const static std::size_t val = sizeof(T);
            };

            template <typename T, long NR, long NC, typename mm, typename l>
            struct get_sizeof_helper<matrix<T,NR,NC,mm,l> >
            {
                const static std::size_t val = 1000000;
            };

            template <
                typename T,
                long num_rows,
                long num_cols,
                typename mem_manager,
                int val = static_switch <
                    // when the sizes are all non zero and small
                    (num_rows*num_cols*get_sizeof_helper<T>::val <= max_stack_based_size) && (num_rows != 0 && num_cols != 0),
                // when the sizes are all non zero and big
                (num_rows*num_cols*get_sizeof_helper<T>::val > max_stack_based_size) && (num_rows != 0 && num_cols != 0),
                num_rows == 0 && num_cols != 0,
                num_rows != 0 && num_cols == 0,
                num_rows == 0 && num_cols == 0
                >::value
                >
            class layout ;
            /*!
                WHAT THIS OBJECT REPRESENTS
                    This object represents the actual allocation of space for a matrix.
                    Small matrices allocate all their data on the stack and bigger ones
                    use a memory_manager to get their memory.
            !*/

        // ------------------------------------------------------------------------------------

            template <
                typename T,
                long num_rows,
                long num_cols,
                typename mem_manager
                >
            class layout<T,num_rows,num_cols,mem_manager,1> : noncopyable // when the sizes are all non zero and small
            {
            public:
                const static long NR = num_rows;
                const static long NC = num_cols;

                layout() {}

                T& operator() (
                    long r,
                    long c
                ) { return *(data+c*num_rows + r); }

                const T& operator() (
                    long r,
                    long c
                ) const { return *(data+c*num_rows + r); }

                T& operator() (
                    long i
                ) { return data[i]; }

                const T& operator() (
                    long i
                ) const { return data[i]; }

                void swap(
                    layout& item
                )
                {
                    for (long r = 0; r < num_rows; ++r)
                    {
                        for (long c = 0; c < num_cols; ++c)
                        {
                            exchange((*this)(r,c),item(r,c));
                        }
                    }
                }

                long nr (
                ) const { return num_rows; }

                long nc (
                ) const { return num_cols; }

                void set_size (
                    long,
                    long
                )
                {
                }

    #ifdef MATLAB_MEX_FILE
                void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); }
                mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); }
                void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); }
                bool _private_is_owned_by_matlab() const { return false; }
    #endif

            private:
                T data[num_cols*num_rows];
            };

        // ------------------------------------------------------------------------------------

            template <
                typename T,
                long num_rows,
                long num_cols,
                typename mem_manager
                >
            class layout<T,num_rows,num_cols,mem_manager,2> : noncopyable // when the sizes are all non zero and big
            {
            public:
                const static long NR = num_rows;
                const static long NC = num_cols;

                layout (
                ) { data = pool.allocate_array(num_rows*num_cols); }

                ~layout ()
                { pool.deallocate_array(data); }

                T& operator() (
                    long r,
                    long c
                ) { return data[c*num_rows + r]; }

                const T& operator() (
                    long r,
                    long c
                ) const { return data[c*num_rows + r]; }

                T& operator() (
                    long i
                ) { return data[i]; }

                const T& operator() (
                    long i
                ) const { return data[i]; }

                void swap(
                    layout& item
                )
                {
                    std::swap(item.data,data);
                    pool.swap(item.pool);
                }

                long nr (
                ) const { return num_rows; }

                long nc (
                ) const { return num_cols; }

                void set_size (
                    long ,
                    long
                )
                {
                }

    #ifdef MATLAB_MEX_FILE
                void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); }
                mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); }
                void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); }
                bool _private_is_owned_by_matlab() const { return false; }
    #endif

            private:

                T* data;
                typename mem_manager::template rebind<T>::other pool;
                };

        // ------------------------------------------------------------------------------------

            template <
                typename T,
                long num_rows,
                long num_cols,
                typename mem_manager
                >
            class layout<T,num_rows,num_cols,mem_manager,3> : noncopyable // when num_rows == 0 && num_cols != 0,
            {
            public:
                const static long NR = num_rows;
                const static long NC = num_cols;

                layout (
                ):data(0), nr_(0) { }

                ~layout ()
                {
                    if (data)
                        pool.deallocate_array(data);
                }

                T& operator() (
                    long r,
                    long c
                ) { return data[c*nr_ + r]; }

                const T& operator() (
                    long r,
                    long c
                ) const { return data[c*nr_ + r]; }

                T& operator() (
                    long i
                ) { return data[i]; }

                const T& operator() (
                    long i
                ) const { return data[i]; }

                void swap(
                    layout& item
                )
                {
                    std::swap(item.data,data);
                    std::swap(item.nr_,nr_);
                    pool.swap(item.pool);
                }

                long nr (
                ) const { return nr_; }

                long nc (
                ) const { return num_cols; }

                void set_size (
                    long nr,
                    long nc
                )
                {
                    if (data)
                    {
                        pool.deallocate_array(data);
                    }
                    data = pool.allocate_array(nr*nc);
                    nr_ = nr;
                }

    #ifdef MATLAB_MEX_FILE
                void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); }
                mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); }
                void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); }
                bool _private_is_owned_by_matlab() const { return false; }
    #endif

            private:

                T* data;
                long nr_;
                typename mem_manager::template rebind<T>::other pool;
                };

        // ------------------------------------------------------------------------------------

            template <
                typename T,
                long num_rows,
                long num_cols,
                typename mem_manager
                >
            class layout<T,num_rows,num_cols,mem_manager,4> : noncopyable // when num_rows != 0 && num_cols == 0
            {
            public:
                const static long NR = num_rows;
                const static long NC = num_cols;

                layout (
                ):data(0), nc_(0) { }

                ~layout ()
                {
                    if (data)
                    {
                        pool.deallocate_array(data);
                    }
                }

                T& operator() (
                    long r,
                    long c
                ) { return data[c*num_rows + r]; }

                const T& operator() (
                    long r,
                    long c
                ) const { return data[c*num_rows + r]; }

                T& operator() (
                    long i
                ) { return data[i]; }

                const T& operator() (
                    long i
                ) const { return data[i]; }

                void swap(
                    layout& item
                )
                {
                    std::swap(item.data,data);
                    std::swap(item.nc_,nc_);
                    pool.swap(item.pool);
                }

                long nr (
                ) const { return num_rows; }

                long nc (
                ) const { return nc_; }

                void set_size (
                    long nr,
                    long nc
                )
                {
                    if (data)
                    {
                        pool.deallocate_array(data);
                    }
                    data = pool.allocate_array(nr*nc);
                    nc_ = nc;
                }

    #ifdef MATLAB_MEX_FILE
                void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); }
                mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); }
                void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); }
                bool _private_is_owned_by_matlab() const { return false; }
    #endif

            private:

                T* data;
                long nc_;
                typename mem_manager::template rebind<T>::other pool;
                };

        // ------------------------------------------------------------------------------------

            template <
                typename T,
                long num_rows,
                long num_cols,
                typename mem_manager
                >
            class layout<T,num_rows,num_cols,mem_manager,5> : noncopyable // when num_rows == 0 && num_cols == 0
            {
            public:
                const static long NR = num_rows;
                const static long NC = num_cols;

                layout (
                ):data(0), nr_(0), nc_(0) { }

                ~layout ()
                {
                    if (data)
                    {
                        pool.deallocate_array(data);
                    }
                }

                T& operator() (
                    long r,
                    long c
                ) { return data[c*nr_ + r]; }

                const T& operator() (
                    long r,
                    long c
                ) const { return data[c*nr_ + r]; }

                T& operator() (
                    long i
                ) { return data[i]; }

                const T& operator() (
                    long i
                ) const { return data[i]; }

                void swap(
                    layout& item
                )
                {
                    std::swap(item.data,data);
                    std::swap(item.nc_,nc_);
                    std::swap(item.nr_,nr_);
                    pool.swap(item.pool);
                }

    #ifdef MATLAB_MEX_FILE
                void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); }
                mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); }
                void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); }
                bool _private_is_owned_by_matlab() const { return false; }
    #endif

                long nr (
                ) const { return nr_; }

                long nc (
                ) const { return nc_; }

                void set_size (
                    long nr,
                    long nc
                )
                {
                    if (data)
                    {
                        pool.deallocate_array(data);
                    }
                    data = pool.allocate_array(nr*nc);
                    nr_ = nr;
                    nc_ = nc;
                }

            private:
                T* data;
                long nr_;
                long nc_;
                typename mem_manager::template rebind<T>::other pool;
            };

    #ifdef MATLAB_MEX_FILE
            template <
                long num_rows,
                long num_cols
                >
            class layout<double,num_rows,num_cols,default_memory_manager,5> : noncopyable // when num_rows == 0 && num_cols == 0
            {
            public:
                const static long NR = num_rows;
                const static long NC = num_cols;

                layout (
                ): data(0), nr_(0), nc_(0), owned_by_matlab(false),set_by_private_set_mxArray(false),mem(0) { }

                ~layout ()
                {
                    if (owned_by_matlab)
                    {
                        if (!set_by_private_set_mxArray && mem)
                        {
                            mxDestroyArray(mem);
                            mem = 0;
                            data = 0;
                        }
                    }
                    else if (data)
                    {
                        delete [] data;
                        data = 0;
                    }
                }

                double& operator() (
                    long r,
                    long c
                ) { return data[c*nr_ + r]; }

                const double& operator() (
                    long r,
                    long c
                ) const { return data[c*nr_ + r]; }

                double& operator() (
                    long i
                ) { return data[i]; }

                const double& operator() (
                    long i
                ) const { return data[i]; }

                void _private_set_mxArray (
                    mxArray* mem_
                )
                {
                    DLIB_CASSERT(mem == 0 && data == 0,"You can't call this function on an already allocated matrix.");
                    // We don't own the pointer, so make note of that so we won't try to free
                    // it.
                    set_by_private_set_mxArray = true;
                    owned_by_matlab = true;
                    mem = mem_;
                    data = mxGetPr(mem);
                    nr_ = mxGetM(mem);
                    nc_ = mxGetN(mem);
                }

                mxArray* _private_release_mxArray()
                {
                    DLIB_CASSERT(owned_by_matlab,"");
                    mxArray* temp = mem;
                    mem = 0;
                    set_by_private_set_mxArray = false;
                    data = 0;
                    nr_ = 0;
                    nc_ = 0;
                    return temp;
                }

                void _private_mark_owned_by_matlab()
                {
                    DLIB_CASSERT(mem == 0 && data == 0,"You can't say a matrix should be owned by matlab after it's been allocated.");
                    owned_by_matlab = true;
                }
                bool _private_is_owned_by_matlab() const
                {
                    return owned_by_matlab;
                }

                void swap(
                    layout& item
                )
                {
                    std::swap(item.owned_by_matlab,owned_by_matlab);
                    std::swap(item.set_by_private_set_mxArray,set_by_private_set_mxArray);
                    std::swap(item.mem,mem);
                    std::swap(item.data,data);
                    std::swap(item.nc_,nc_);
                    std::swap(item.nr_,nr_);
                }

                long nr (
                ) const { return nr_; }

                long nc (
                ) const { return nc_; }

                void set_size (
                    long nr,
                    long nc
                )
                {
                    if (owned_by_matlab)
                    {
                        if (!set_by_private_set_mxArray && mem)
                        {
                            mxDestroyArray(mem);
                            mem = 0;
                            data = 0;
                        }
                        set_by_private_set_mxArray = false;

                        mem = mxCreateDoubleMatrix(nr, nc, mxREAL);
                        if (mem == 0)
                            throw std::bad_alloc();
                        data = mxGetPr(mem);
                    }
                    else
                    {
                        if (data)
                            delete [] data;
                        data = new double[nr*nc];
                    }
                    nr_ = nr;
                    nc_ = nc;
                }

            private:
                double* data;
                long nr_;
                long nc_;
                bool owned_by_matlab;
                bool set_by_private_set_mxArray;
                mxArray* mem;
            };

            template <
                long num_rows,
                long num_cols
                >
            class layout<float,num_rows,num_cols,default_memory_manager,5> : noncopyable // when num_rows == 0 && num_cols == 0
            {
            public:
                const static long NR = num_rows;
                const static long NC = num_cols;

                layout (
                ): data(0), nr_(0), nc_(0), owned_by_matlab(false),set_by_private_set_mxArray(false),mem(0) { }

                ~layout ()
                {
                    if (owned_by_matlab)
                    {
                        if (!set_by_private_set_mxArray && mem)
                        {
                            mxDestroyArray(mem);
                            mem = 0;
                            data = 0;
                        }
                    }
                    else if (data)
                    {
                        delete [] data;
                        data = 0;
                    }
                }

                float& operator() (
                    long r,
                    long c
                ) { return data[c*nr_ + r]; }

                const float& operator() (
                    long r,
                    long c
                ) const { return data[c*nr_ + r]; }

                float& operator() (
                    long i
                ) { return data[i]; }

                const float& operator() (
                    long i
                ) const { return data[i]; }

                void _private_set_mxArray (
                    mxArray* mem_
                )
                {
                    DLIB_CASSERT(mem == 0 && data == 0,"You can't call this function on an already allocated matrix.");
                    // We don't own the pointer, so make note of that so we won't try to free
                    // it.
                    set_by_private_set_mxArray = true;
                    owned_by_matlab = true;
                    mem = mem_;
                    data = (float*)mxGetData(mem);
                    nr_ = mxGetM(mem);
                    nc_ = mxGetN(mem);
                }

                mxArray* _private_release_mxArray()
                {
                    DLIB_CASSERT(owned_by_matlab,"");
                    mxArray* temp = mem;
                    mem = 0;
                    set_by_private_set_mxArray = false;
                    data = 0;
                    nr_ = 0;
                    nc_ = 0;
                    return temp;
                }

                void _private_mark_owned_by_matlab()
                {
                    DLIB_CASSERT(mem == 0 && data == 0,"You can't say a matrix should be owned by matlab after it's been allocated.");
                    owned_by_matlab = true;
                }
                bool _private_is_owned_by_matlab() const
                {
                    return owned_by_matlab;
                }

                void swap(
                    layout& item
                )
                {
                    std::swap(item.owned_by_matlab,owned_by_matlab);
                    std::swap(item.set_by_private_set_mxArray,set_by_private_set_mxArray);
                    std::swap(item.mem,mem);
                    std::swap(item.data,data);
                    std::swap(item.nc_,nc_);
                    std::swap(item.nr_,nr_);
                }

                long nr (
                ) const { return nr_; }

                long nc (
                ) const { return nc_; }

                void set_size (
                    long nr,
                    long nc
                )
                {
                    if (owned_by_matlab)
                    {
                        if (!set_by_private_set_mxArray && mem)
                        {
                            mxDestroyArray(mem);
                            mem = 0;
                            data = 0;
                        }
                        set_by_private_set_mxArray = false;

                        mem = mxCreateNumericMatrix(nr, nc, mxSINGLE_CLASS, mxREAL);
                        if (mem == 0)
                            throw std::bad_alloc();
                        data = (float*)mxGetData(mem);
                    }
                    else
                    {
                        if (data)
                            delete [] data;
                        data = new float[nr*nc];
                    }
                    nr_ = nr;
                    nc_ = nc;
                }

            private:
                float* data;
                long nr_;
                long nc_;
                bool owned_by_matlab;
                bool set_by_private_set_mxArray;
                mxArray* mem;
            };
    #endif

        };

    // ----------------------------------------------------------------------------------------

    }

    #if defined(__GNUC__) && ((__GNUC__ >= 4 && __GNUC_MINOR__ >= 8) || (__GNUC__ > 4))
    #pragma GCC diagnostic pop
    #endif

    #endif // DLIB_MATRIx_DATA_LAYOUT_