    // Copyright (C) 2011  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #include <sstream>
    #include <string>
    #include <cstdlib>
    #include <ctime>
    #include <dlib/optimization.h>
    #include <dlib/rand.h>

    #include "tester.h"

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

        logger dlog("test.max_sum_submatrix");

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

        bool order_rects (
            const rectangle& a,
            const rectangle& b
        )
        {
            if (a.left() < b.left()) return true;
            else if (a.left() > b.left()) return false;

            if (a.right() < b.right()) return true;
            else if (a.right() > b.right()) return false;

            if (a.top() < b.top()) return true;
            else if (a.top() > b.top()) return false;

            if (a.bottom() < b.bottom()) return true;
            else if (a.bottom() > b.bottom()) return false;

            return false;
        }

        void run_test(
            const int num
        )
        {
            static dlib::rand rnd;

            matrix<int> mat, mask;

            mat.set_size(rnd.get_random_32bit_number()%1000 + 1,
                         rnd.get_random_32bit_number()%1000 + 1);
            mask.set_size(mat.nr(), mat.nc());
            mask = 0;

            mat = -10000;

            std::vector<rectangle> true_rects;

            for (int i = 0; i < num; ++i)
            {
                const int width = rnd.get_random_32bit_number()%100 + 1;
                const int height = rnd.get_random_32bit_number()%100 + 1;

                rectangle rect = centered_rect(rnd.get_random_16bit_number()%mat.nc(),
                                               rnd.get_random_16bit_number()%mat.nr(),
                                               width,height);
                rect = get_rect(mat).intersect(rect);

                // make sure this new rectangle doesn't overlap or abut any others
                if (sum(subm(mask,grow_rect(rect,1).intersect(get_rect(mask)))) == 0)
                {
                    set_subm(mat, rect) = rnd.get_random_8bit_number()%100 + 1;
                    set_subm(mask, rect) = 1;
                    true_rects.push_back(rect);
                }
            }

            std::vector<rectangle> res;
            res = max_sum_submatrix(mat, true_rects.size()+10, 0);

            DLIB_TEST(res.size() == true_rects.size());

            // make sure big rectangles come first
            for (unsigned long i = 0; i+1 < res.size(); ++i)
            {
                DLIB_TEST(sum(subm(mat,res[i])) >= sum(subm(mat,res[i+1])));
            }

            // make sure rectangles match
            sort(true_rects.begin(), true_rects.end(), order_rects);
            sort(res.begin(), res.end(), order_rects);
            for (unsigned long i = 0; i < res.size(); ++i)
            {
                DLIB_TEST_MSG(res[i] == true_rects[i],
                              "i: " << i << "  res[i]: " << res[i] << "  true_rects[i]: " <<  true_rects[i]);
            }

        }

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

        template <typename T>
        void run_test2()
        {
            matrix<T> mat(100,100);
            mat = 1;
            std::vector<rectangle> res = max_sum_submatrix(mat, 0, 0);

            DLIB_TEST(res.size() == 0);
            res = max_sum_submatrix(mat, 1, 0);
            DLIB_TEST(res.size() == 1);
            DLIB_TEST(res[0] == get_rect(mat));
            res = max_sum_submatrix(mat, 3, 0);
            DLIB_TEST(res.size() == 1);
            DLIB_TEST(res[0] == get_rect(mat));
            res = max_sum_submatrix(mat, 3, 10);
            DLIB_TEST(res.size() == 1);
            DLIB_TEST(res[0] == get_rect(mat));

            res = max_sum_submatrix(mat, 3, mat.size());
            DLIB_TEST(res.size() == 0);

            mat = -1;
            res = max_sum_submatrix(mat, 1, 0);
            DLIB_TEST(res.size() == 0);

            const rectangle rect1 = rectangle(10,10,40,40);
            const rectangle rect2 = rectangle(35,35,80,80);

            set_subm(mat, rect1) = 2;
            set_subm(mat, rect2) = 1;
            res = max_sum_submatrix(mat, 3, 0);
            DLIB_TEST(res.size() == 2);
            DLIB_TEST(res[0] == rect2);
            DLIB_TEST(res[1] == rect1);

            res = max_sum_submatrix(mat, 3, 2*rect1.area() - 2*(rect1.intersect(rect2)).area());
            DLIB_TEST(res.size() == 1);
            DLIB_TEST(res[0] == rect2);
        }

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

        class test_max_sum_submatrix : public tester
        {
        public:
            test_max_sum_submatrix (
            ) :
                tester ("test_max_sum_submatrix",
                        "Runs tests on the max_sum_submatrix() function.")
            {}

            void perform_test (
            )
            {
                for (int j = 0; j < 5; ++j)
                {
                    print_spinner();
                    for (int i = 0; i < 40; ++i)
                        run_test(i);
                }

                run_test2<int>();
                run_test2<short>();
                run_test2<signed char>();
                run_test2<float>();
                run_test2<double>();
            }
        } a;

    }