    // Copyright (C) 2013 Steve Taylor (steve98654@gmail.com)
    // License: Boost Software License  See LICENSE.txt for full license
    #ifndef DLIB_INTEGRATE_FUNCTION_ADAPT_SIMPSONh_
    #define DLIB_INTEGRATE_FUNCTION_ADAPT_SIMPSONh_

    #include "integrate_function_adapt_simpson_abstract.h"
    #include "../assert.h"

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

    namespace dlib
    {
        template <typename T, typename funct>
        T impl_adapt_simp_stop(const funct& f, T a, T b, T fa, T fm, T fb, T is, int cnt)
        {
            const int maxint = 500;

            T m   = (a + b)/2.0;
            T h   = (b - a)/4.0;
            T fml = f(a + h);
            T fmr = f(b - h);
            T i1 = h/1.5*(fa+4.0*fm+fb);
            T i2 = h/3.0*(fa+4.0*(fml+fmr)+2.0*fm+fb);
            i1 = (16.0*i2 - i1)/15.0;
            T Q = 0;

            if ((std::abs(i1-i2) <= std::abs(is)) || (m <= a) || (b <= m))
            {
                Q = i1;
            }
            else
            {
                if(cnt < maxint)
                {
                    cnt = cnt + 1;

                    Q = impl_adapt_simp_stop(f,a,m,fa,fml,fm,is,cnt)
                        + impl_adapt_simp_stop(f,m,b,fm,fmr,fb,is,cnt);
                }
            }

            return Q;
        }

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

        template <typename T, typename funct>
        T integrate_function_adapt_simp(
            const funct& f,
            T a,
            T b,
            T tol = 1e-10
        )
        {
            // make sure requires clause is not broken
            DLIB_ASSERT(b > a && tol > 0,
                "\t T integrate_function_adapt_simp()"
                << "\n\t Invalid arguments were given to this function."
                << "\n\t a:   " << a
                << "\n\t b:   " << b
                << "\n\t tol: " << tol
                );

            T eps = std::numeric_limits<T>::epsilon();
            if(tol < eps)
            {
                tol = eps;
            }

            const T ba = b-a;
            const T fa = f(a);
            const T fb = f(b);
            const T fm = f((a+b)/2);

            T is = ba/8*(fa+fb+fm+ f(a + 0.9501*ba) + f(a + 0.2311*ba) + f(a + 0.6068*ba)
                + f(a + 0.4860*ba) + f(a + 0.8913*ba));

            if(is == 0)
            {
                is = b-a;
            }

            is = is*tol;

            int cnt = 0;

            return impl_adapt_simp_stop(f, a, b, fa, fm, fb, is, cnt);
        }
    }

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

    #endif // DLIB_INTEGRATE_FUNCTION_ADAPT_SIMPSONh_