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

    #include "matrix_abstract.h"
    #include "../geometry/rectangle.h"

    namespace dlib
    {

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

        template <long start, long inc, long end>
        const matrix_exp range (
        );
        /*!
            requires
                - inc > 0
            ensures
                - returns a matrix R such that:
                    - R::type == long
                    - R.nr() == 1
                    - R.nc() == abs(end - start)/inc + 1
                    - if (start <= end) then
                        - R(i) == start + i*inc
                    - else
                        - R(i) == start - i*inc
        !*/

        template <long start, long end>
        const matrix_exp range (
        ) { return range<start,1,end>(); }

        const matrix_exp range (
            long start,
            long inc,
            long end
        );
        /*!
            requires
                - inc > 0
            ensures
                - returns a matrix R such that:
                    - R::type == long
                    - R.nr() == 1
                    - R.nc() == abs(end - start)/inc + 1
                    - if (start <= end) then
                        - R(i) == start + i*inc
                    - else
                        - R(i) == start - i*inc
        !*/

        const matrix_exp range (
            long start,
            long end
        ) { return range(start,1,end); }

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

        const matrix_exp subm (
            const matrix_exp& m,
            const matrix_exp& rows,
            const matrix_exp& cols,
        );
        /*!
            requires
                - rows and cols contain integral elements (e.g. int, long)
                - 0 <= min(rows) && max(rows) < m.nr()
                - 0 <= min(cols) && max(cols) < m.nc()
                - rows.nr() == 1 || rows.nc() == 1
                - cols.nr() == 1 || cols.nc() == 1
                  (i.e. rows and cols must be vectors)
            ensures
                - returns a matrix R such that:
                    - R::type == the same type that was in m
                    - R.nr() == rows.size()
                    - R.nc() == cols.size()
                    - for all valid r and c:
                      R(r,c) == m(rows(r),cols(c))
        !*/

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

        const matrix_exp subm (
            const matrix_exp& m,
            long row,
            long col,
            long nr,
            long nc
        );
        /*!
            requires
                - row >= 0
                - col >= 0
                - nr >= 0
                - nc >= 0
                - row + nr <= m.nr()
                - col + nc <= m.nc()
            ensures
                - returns a matrix R such that:
                    - R.nr() == nr
                    - R.nc() == nc
                    - for all valid r and c:
                      R(r, c) == m(r+row,c+col)
        !*/

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

        const matrix_exp subm (
            const matrix_exp& m,
            const rectangle& rect
        );
        /*!
            requires
                - get_rect(m).contains(rect) == true
                  (i.e. rect is a region inside the matrix m)
            ensures
                - returns a matrix R such that:
                    - R.nr() == rect.height()
                    - R.nc() == rect.width()
                    - for all valid r and c:
                      R(r, c) == m(r+rect.top(), c+rect.left())
        !*/

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

        const matrix_exp subm_clipped (
            const matrix_exp& m,
            long row,
            long col,
            long nr,
            long nc
        );
        /*!
            ensures
                - This function is just like subm() except that it will automatically clip the
                  indicated sub matrix window so that it does not extend outside m.
                  In particular:
                    - Let box = rectangle(col,row,col+nc-1,row+nr-1)
                      (i.e. the box that contains the indicated sub matrix)
                    - Let box_clipped = box.intersect(get_rect(m))
                    - Then this function returns a matrix R such that:
                        - R.nr() == box_clipped.height()
                        - R.nc() == box_clipped.width()
                        - for all valid r and c:
                          R(r, c) == m(r+box_clipped.top(),c+box_clipped.left())
        !*/

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

        const matrix_exp subm_clipped (
            const matrix_exp& m,
            const rectangle& rect
        );
        /*!
            ensures
                - Let box_clipped == rect.intersect(get_rect(m))
                - returns a matrix R such that:
                    - R.nr() == box_clipped.height()
                    - R.nc() == box_clipped.width()
                    - for all valid r and c:
                      R(r, c) == m(r+box_clipped.top(), c+box_clipped.left())
        !*/

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

        const matrix_exp rowm (
            const matrix_exp& m,
            long row
        );
        /*!
            requires
                - 0 <= row < m.nr()
            ensures
                - returns a matrix R such that:
                    - R.nr() == 1
                    - R.nc() == m.nc()
                    - for all valid i:
                      R(i) == m(row,i)
        !*/

        template <typename EXP>
        struct rowm_exp
        {
            /*!
                WHAT THIS OBJECT REPRESENTS
                    This struct allows you to determine the type of matrix expression
                    object returned from the rowm(m,row) function.  An example makes its
                    use clear:

                    template <typename EXP>
                    void do_something( const matrix_exp<EXP>& mat)
                    {
                        // r is a matrix expression that aliases mat.
                        typename rowm_exp<EXP>::type r = rowm(mat,0);

                        // Print the first row of mat.  So we see that by using
                        // rowm_exp we can save the object returned by rowm() in
                        // a local variable.
                        cout << r << endl;

                        // Note that you can only save the return value of rowm() to
                        // a local variable if the argument to rowm() has a lifetime
                        // beyond the rowm() expression.  The example shown above is
                        // OK but the following would result in undefined behavior:
                        typename rowm_exp<EXP>::type bad = rowm(mat + mat,0);
                    }
            !*/
            typedef type_of_expression_returned_by_rowm type;
        };

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

        const matrix_exp rowm (
            const matrix_exp& m,
            long row,
            long length
        );
        /*!
            requires
                - 0 <= row < m.nr()
                - 0 <= length <= m.nc()
            ensures
                - returns a matrix R such that:
                    - R.nr() == 1
                    - R.nc() == length
                    - for all valid i:
                      R(i) == m(row,i)
        !*/

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

        const matrix_exp rowm (
            const matrix_exp& m,
            const matrix_exp& rows
        );
        /*!
            requires
                - rows contains integral elements (e.g. int, long)
                - 0 <= min(rows) && max(rows) < m.nr()
                - rows.nr() == 1 || rows.nc() == 1 || rows.size() == 0
                  (i.e. rows must be a vector, or just empty)
            ensures
                - returns a matrix R such that:
                    - R::type == the same type that was in m
                    - R.nr() == rows.size()
                    - R.nc() == m.nc()
                    - for all valid r and c:
                      R(r,c) == m(rows(r),c)
        !*/

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

        const matrix_exp colm (
            const matrix_exp& m,
            long col
        );
        /*!
            requires
                - 0 <= col < m.nc()
            ensures
                - returns a matrix R such that:
                    - R.nr() == m.nr()
                    - R.nc() == 1
                    - for all valid i:
                      R(i) == m(i,col)
        !*/

        template <typename EXP>
        struct colm_exp
        {
            /*!
                WHAT THIS OBJECT REPRESENTS
                    This struct allows you to determine the type of matrix expression
                    object returned from the colm(m,col) function.  An example makes its
                    use clear:

                    template <typename EXP>
                    void do_something( const matrix_exp<EXP>& mat)
                    {
                        // c is a matrix expression that aliases mat.
                        typename colm_exp<EXP>::type c = colm(mat,0);

                        // Print the first column of mat.  So we see that by using
                        // colm_exp we can save the object returned by colm() in
                        // a local variable.
                        cout << c << endl;

                        // Note that you can only save the return value of colm() to
                        // a local variable if the argument to colm() has a lifetime
                        // beyond the colm() expression.  The example shown above is
                        // OK but the following would result in undefined behavior:
                        typename colm_exp<EXP>::type bad = colm(mat + mat,0);
                    }
            !*/
            typedef type_of_expression_returned_by_colm type;
        };

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

        const matrix_exp colm (
            const matrix_exp& m,
            long col,
            long length
        );
        /*!
            requires
                - 0 <= col < m.nc()
                - 0 <= length <= m.nr()
            ensures
                - returns a matrix R such that:
                    - R.nr() == length
                    - R.nc() == 1
                    - for all valid i:
                      R(i) == m(i,col)
        !*/

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

        const matrix_exp colm (
            const matrix_exp& m,
            const matrix_exp& cols
        );
        /*!
            requires
                - cols contains integral elements (e.g. int, long)
                - 0 <= min(cols) && max(cols) < m.nc()
                - cols.nr() == 1 || cols.nc() == 1 || cols.size() == 0
                  (i.e. cols must be a vector, or just empty)
            ensures
                - returns a matrix R such that:
                    - R::type == the same type that was in m
                    - R.nr() == m.nr()
                    - R.nc() == cols.size()
                    - for all valid r and c:
                      R(r,c) == m(r,cols(c))
        !*/

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

        template <typename T>
        assignable_matrix_expression set_ptrm (
            T* ptr,
            long nr,
            long nc = 1
        );
        /*!
            requires
                - ptr == a pointer to nr*nc elements of type T
                - nr >= 0
                - nc >= 0
            ensures
                - statements of the following form:
                    - set_ptrm(ptr,nr,nc) = some_matrix;
                  result in it being the case that:
                    - mat(ptr,nr,nc) == some_matrix.

                - statements of the following form:
                    - set_ptrm(ptr,nr,nc) = scalar_value;
                  result in it being the case that:
                    - mat(ptr,nr,nc) == uniform_matrix<matrix::type>(nr,nc,scalar_value).

                - In addition to the normal assignment statements using the = symbol, you may
                  also use the usual += and -= versions of the assignment operator.  In these
                  cases, they have their usual effect.
        !*/

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

        assignable_matrix_expression set_subm (
            matrix& m,
            long row,
            long col,
            long nr,
            long nc
        );
        /*!
            requires
                - row >= 0
                - col >= 0
                - nr >= 0
                - nc >= 0
                - row + nr <= m.nr()
                - col + nc <= m.nc()
            ensures
                - statements of the following form:
                    - set_subm(m,row,col,nr,nc) = some_matrix;
                  result in it being the case that:
                    - subm(m,row,col,nr,nc) == some_matrix.

                - statements of the following form:
                    - set_subm(m,row,col,nr,nc) = scalar_value;
                  result in it being the case that:
                    - subm(m,row,col,nr,nc) == uniform_matrix<matrix::type>(nr,nc,scalar_value).

                - In addition to the normal assignment statements using the = symbol, you may
                  also use the usual += and -= versions of the assignment operator.  In these
                  cases, they have their usual effect.
        !*/

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

        assignable_matrix_expression set_subm (
            matrix& m,
            const rectangle& rect
        );
        /*!
            requires
                - get_rect(m).contains(rect) == true
                  (i.e. rect is a region inside the matrix m)
            ensures
                - statements of the following form:
                    - set_subm(m,rect) = some_matrix;
                  result in it being the case that:
                    - subm(m,rect) == some_matrix.

                - statements of the following form:
                    - set_subm(m,rect) = scalar_value;
                  result in it being the case that:
                    - subm(m,rect) == uniform_matrix<matrix::type>(nr,nc,scalar_value).

                - In addition to the normal assignment statements using the = symbol, you may
                  also use the usual += and -= versions of the assignment operator.  In these
                  cases, they have their usual effect.
        !*/

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

        assignable_matrix_expression set_subm (
            matrix& m,
            const matrix_exp& rows,
            const matrix_exp& cols
        );
        /*!
            requires
                - rows and cols contain integral elements (e.g. int, long)
                - 0 <= min(rows) && max(rows) < m.nr()
                - 0 <= min(cols) && max(cols) < m.nc()
                - rows.nr() == 1 || rows.nc() == 1
                - cols.nr() == 1 || cols.nc() == 1
                  (i.e. rows and cols must be vectors)
            ensures
                - statements of the following form:
                    - set_subm(m,rows,cols) = some_matrix;
                  result in it being the case that:
                    - subm(m,rows,cols) == some_matrix.

                - statements of the following form:
                    - set_subm(m,rows,cols) = scalar_value;
                  result in it being the case that:
                    - subm(m,rows,cols) == uniform_matrix<matrix::type>(nr,nc,scalar_value).

                - In addition to the normal assignment statements using the = symbol, you may
                  also use the usual += and -= versions of the assignment operator.  In these
                  cases, they have their usual effect.
        !*/

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

        assignable_matrix_expression set_rowm (
            matrix& m,
            long row
        );
        /*!
            requires
                - 0 <= row < m.nr()
            ensures
                - statements of the following form:
                    - set_rowm(m,row) = some_matrix;
                  result in it being the case that:
                    - rowm(m,row) == some_matrix.

                - statements of the following form:
                    - set_rowm(m,row) = scalar_value;
                  result in it being the case that:
                    - rowm(m,row) == uniform_matrix<matrix::type>(1,nc,scalar_value).

                - In addition to the normal assignment statements using the = symbol, you may
                  also use the usual += and -= versions of the assignment operator.  In these
                  cases, they have their usual effect.
        !*/

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

        assignable_matrix_expression set_rowm (
            matrix& m,
            const matrix_exp& rows
        );
        /*!
            requires
                - rows contains integral elements (e.g. int, long)
                - 0 <= min(rows) && max(rows) < m.nr()
                - rows.nr() == 1 || rows.nc() == 1
                  (i.e. rows must be a vector)
            ensures
                - statements of the following form:
                    - set_rowm(m,rows) = some_matrix;
                  result in it being the case that:
                    - rowm(m,rows) == some_matrix.

                - statements of the following form:
                    - set_rowm(m,rows) = scalar_value;
                  result in it being the case that:
                    - rowm(m,rows) == uniform_matrix<matrix::type>(nr,nc,scalar_value).

                - In addition to the normal assignment statements using the = symbol, you may
                  also use the usual += and -= versions of the assignment operator.  In these
                  cases, they have their usual effect.
        !*/

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

        assignable_matrix_expression set_colm (
            matrix& m,
            long col
        );
        /*!
            requires
                - 0 <= col < m.nr()
            ensures
                - statements of the following form:
                    - set_colm(m,col) = some_matrix;
                  result in it being the case that:
                    - colm(m,col) == some_matrix.

                - statements of the following form:
                    - set_colm(m,col) = scalar_value;
                  result in it being the case that:
                    - colm(m,col) == uniform_matrix<matrix::type>(nr,1,scalar_value).

                - In addition to the normal assignment statements using the = symbol, you may
                  also use the usual += and -= versions of the assignment operator.  In these
                  cases, they have their usual effect.
        !*/

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

        assignable_matrix_expression set_colm (
            matrix& m,
            const matrix_exp& cols
        );
        /*!
            requires
                - cols contains integral elements (e.g. int, long)
                - 0 <= min(cols) && max(cols) < m.nc()
                - cols.nr() == 1 || cols.nc() == 1
                  (i.e. cols must be a vector)
            ensures
                - statements of the following form:
                    - set_colm(m,cols) = some_matrix;
                  result in it being the case that:
                    - colm(m,cols) == some_matrix.

                - statements of the following form:
                    - set_colm(m,cols) = scalar_value;
                  result in it being the case that:
                    - colm(m,cols) == uniform_matrix<matrix::type>(nr,nc,scalar_value).

                - In addition to the normal assignment statements using the = symbol, you may
                  also use the usual += and -= versions of the assignment operator.  In these
                  cases, they have their usual effect.
        !*/

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

    }

    #endif // DLIB_MATRIx_SUBEXP_ABSTRACT_