#ifndef BOOST_GEOMETRY_PROJECTIONS_TPEQD_HPP
#define BOOST_GEOMETRY_PROJECTIONS_TPEQD_HPP

// Boost.Geometry - extensions-gis-projections (based on PROJ4)
// This file is automatically generated. DO NOT EDIT.

// Copyright (c) 2008-2015 Barend Gehrels, Amsterdam, the Netherlands.

// This file was modified by Oracle on 2017.
// Modifications copyright (c) 2017, Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

// This file is converted from PROJ4, http://trac.osgeo.org/proj
// PROJ4 is originally written by Gerald Evenden (then of the USGS)
// PROJ4 is maintained by Frank Warmerdam
// PROJ4 is converted to Boost.Geometry by Barend Gehrels

// Last updated version of proj: 4.9.1

// Original copyright notice:

// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#include <boost/geometry/util/math.hpp>
#include <boost/math/special_functions/hypot.hpp>

#include <boost/geometry/srs/projections/impl/base_static.hpp>
#include <boost/geometry/srs/projections/impl/base_dynamic.hpp>
#include <boost/geometry/srs/projections/impl/projects.hpp>
#include <boost/geometry/srs/projections/impl/factory_entry.hpp>
#include <boost/geometry/srs/projections/impl/aasincos.hpp>

namespace boost { namespace geometry
{

namespace srs { namespace par4
{
    struct tpeqd {};

}} //namespace srs::par4

namespace projections
{
    #ifndef DOXYGEN_NO_DETAIL
    namespace detail { namespace tpeqd
    {
            template <typename T>
            struct par_tpeqd
            {
                T cp1, sp1, cp2, sp2, ccs, cs, sc, r2z0, z02, dlam2;
                T hz0, thz0, rhshz0, ca, sa, lp, lamc;
            };

            // template class, using CRTP to implement forward/inverse
            template <typename CalculationType, typename Parameters>
            struct base_tpeqd_spheroid : public base_t_fi<base_tpeqd_spheroid<CalculationType, Parameters>,
                     CalculationType, Parameters>
            {

                typedef CalculationType geographic_type;
                typedef CalculationType cartesian_type;

                par_tpeqd<CalculationType> m_proj_parm;

                inline base_tpeqd_spheroid(const Parameters& par)
                    : base_t_fi<base_tpeqd_spheroid<CalculationType, Parameters>,
                     CalculationType, Parameters>(*this, par) {}

                // FORWARD(s_forward)  sphere
                // Project coordinates from geographic (lon, lat) to cartesian (x, y)
                inline void fwd(geographic_type& lp_lon, geographic_type& lp_lat, cartesian_type& xy_x, cartesian_type& xy_y) const
                {
                    CalculationType t, z1, z2, dl1, dl2, sp, cp;

                    sp = sin(lp_lat);
                    cp = cos(lp_lat);
                    z1 = aacos(this->m_proj_parm.sp1 * sp + this->m_proj_parm.cp1 * cp * cos(dl1 = lp_lon + this->m_proj_parm.dlam2));
                    z2 = aacos(this->m_proj_parm.sp2 * sp + this->m_proj_parm.cp2 * cp * cos(dl2 = lp_lon - this->m_proj_parm.dlam2));
                    z1 *= z1;
                    z2 *= z2;
                    xy_x = this->m_proj_parm.r2z0 * (t = z1 - z2);
                    t = this->m_proj_parm.z02 - t;
                    xy_y = this->m_proj_parm.r2z0 * asqrt(4. * this->m_proj_parm.z02 * z2 - t * t);
                    if ((this->m_proj_parm.ccs * sp - cp * (this->m_proj_parm.cs * sin(dl1) - this->m_proj_parm.sc * sin(dl2))) < 0.)
                        xy_y = -xy_y;
                }

                // INVERSE(s_inverse)  sphere
                // Project coordinates from cartesian (x, y) to geographic (lon, lat)
                inline void inv(cartesian_type& xy_x, cartesian_type& xy_y, geographic_type& lp_lon, geographic_type& lp_lat) const
                {
                    CalculationType cz1, cz2, s, d, cp, sp;

                    cz1 = cos(boost::math::hypot(xy_y, xy_x + this->m_proj_parm.hz0));
                    cz2 = cos(boost::math::hypot(xy_y, xy_x - this->m_proj_parm.hz0));
                    s = cz1 + cz2;
                    d = cz1 - cz2;
                    lp_lon = - atan2(d, (s * this->m_proj_parm.thz0));
                    lp_lat = aacos(boost::math::hypot(this->m_proj_parm.thz0 * s, d) * this->m_proj_parm.rhshz0);
                    if ( xy_y < 0. )
                        lp_lat = - lp_lat;
                    /* lam--phi now in system relative to P1--P2 base equator */
                    sp = sin(lp_lat);
                    cp = cos(lp_lat);
                    lp_lat = aasin(this->m_proj_parm.sa * sp + this->m_proj_parm.ca * cp * (s = cos(lp_lon -= this->m_proj_parm.lp)));
                    lp_lon = atan2(cp * sin(lp_lon), this->m_proj_parm.sa * cp * s - this->m_proj_parm.ca * sp) + this->m_proj_parm.lamc;
                }

                static inline std::string get_name()
                {
                    return "tpeqd_spheroid";
                }

            };

            // Two Point Equidistant
            template <typename Parameters, typename T>
            inline void setup_tpeqd(Parameters& par, par_tpeqd<T>& proj_parm)
            {
                T lam_1, lam_2, phi_1, phi_2, A12, pp;

                /* get control point locations */
                phi_1 = pj_param(par.params, "rlat_1").f;
                lam_1 = pj_param(par.params, "rlon_1").f;
                phi_2 = pj_param(par.params, "rlat_2").f;
                lam_2 = pj_param(par.params, "rlon_2").f;
                if (phi_1 == phi_2 && lam_1 == lam_2)
                    BOOST_THROW_EXCEPTION( projection_exception(-25) );
                par.lam0 = adjlon(0.5 * (lam_1 + lam_2));
                proj_parm.dlam2 = adjlon(lam_2 - lam_1);
                proj_parm.cp1 = cos(phi_1);
                proj_parm.cp2 = cos(phi_2);
                proj_parm.sp1 = sin(phi_1);
                proj_parm.sp2 = sin(phi_2);
                proj_parm.cs = proj_parm.cp1 * proj_parm.sp2;
                proj_parm.sc = proj_parm.sp1 * proj_parm.cp2;
                proj_parm.ccs = proj_parm.cp1 * proj_parm.cp2 * sin(proj_parm.dlam2);
                proj_parm.z02 = aacos(proj_parm.sp1 * proj_parm.sp2 + proj_parm.cp1 * proj_parm.cp2 * cos(proj_parm.dlam2));
                proj_parm.hz0 = .5 * proj_parm.z02;
                A12 = atan2(proj_parm.cp2 * sin(proj_parm.dlam2),
                    proj_parm.cp1 * proj_parm.sp2 - proj_parm.sp1 * proj_parm.cp2 * cos(proj_parm.dlam2));
                proj_parm.ca = cos(pp = aasin(proj_parm.cp1 * sin(A12)));
                proj_parm.sa = sin(pp);
                proj_parm.lp = adjlon(atan2(proj_parm.cp1 * cos(A12), proj_parm.sp1) - proj_parm.hz0);
                proj_parm.dlam2 *= .5;
                proj_parm.lamc = geometry::math::half_pi<T>() - atan2(sin(A12) * proj_parm.sp1, cos(A12)) - proj_parm.dlam2;
                proj_parm.thz0 = tan(proj_parm.hz0);
                proj_parm.rhshz0 = .5 / sin(proj_parm.hz0);
                proj_parm.r2z0 = 0.5 / proj_parm.z02;
                proj_parm.z02 *= proj_parm.z02;
                par.es = 0.;
            }

    }} // namespace detail::tpeqd
    #endif // doxygen

    /*!
        \brief Two Point Equidistant projection
        \ingroup projections
        \tparam Geographic latlong point type
        \tparam Cartesian xy point type
        \tparam Parameters parameter type
        \par Projection characteristics
         - Miscellaneous
         - Spheroid
        \par Projection parameters
         - lat_1: Latitude of first standard parallel (degrees)
         - lon_1 (degrees)
         - lat_2: Latitude of second standard parallel (degrees)
         - lon_2 (degrees)
        \par Example
        \image html ex_tpeqd.gif
    */
    template <typename CalculationType, typename Parameters>
    struct tpeqd_spheroid : public detail::tpeqd::base_tpeqd_spheroid<CalculationType, Parameters>
    {
        inline tpeqd_spheroid(const Parameters& par) : detail::tpeqd::base_tpeqd_spheroid<CalculationType, Parameters>(par)
        {
            detail::tpeqd::setup_tpeqd(this->m_par, this->m_proj_parm);
        }
    };

    #ifndef DOXYGEN_NO_DETAIL
    namespace detail
    {

        // Static projection
        BOOST_GEOMETRY_PROJECTIONS_DETAIL_STATIC_PROJECTION(srs::par4::tpeqd, tpeqd_spheroid, tpeqd_spheroid)

        // Factory entry(s)
        template <typename CalculationType, typename Parameters>
        class tpeqd_entry : public detail::factory_entry<CalculationType, Parameters>
        {
            public :
                virtual base_v<CalculationType, Parameters>* create_new(const Parameters& par) const
                {
                    return new base_v_fi<tpeqd_spheroid<CalculationType, Parameters>, CalculationType, Parameters>(par);
                }
        };

        template <typename CalculationType, typename Parameters>
        inline void tpeqd_init(detail::base_factory<CalculationType, Parameters>& factory)
        {
            factory.add_to_factory("tpeqd", new tpeqd_entry<CalculationType, Parameters>);
        }

    } // namespace detail
    #endif // doxygen

} // namespace projections

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_PROJECTIONS_TPEQD_HPP


///////////////////////////////////////////////////////////////////////////////
/// \file statistics.hpp
/// Includes all of the Statistical Accumulators Library
//
//  Copyright 2005 Eric Niebler. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_ACCUMULATORS_STATISTICS_HPP_EAN_01_17_2006
#define BOOST_ACCUMULATORS_STATISTICS_HPP_EAN_01_17_2006

#include <boost/accumulators/statistics/count.hpp>
#include <boost/accumulators/statistics/covariance.hpp>
#include <boost/accumulators/statistics/density.hpp>
#include <boost/accumulators/statistics/error_of.hpp>
#include <boost/accumulators/statistics/error_of_mean.hpp>
#include <boost/accumulators/statistics/extended_p_square.hpp>
#include <boost/accumulators/statistics/extended_p_square_quantile.hpp>
#include <boost/accumulators/statistics/kurtosis.hpp>
#include <boost/accumulators/statistics/max.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/median.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/moment.hpp>
#include <boost/accumulators/statistics/peaks_over_threshold.hpp>
#include <boost/accumulators/statistics/pot_tail_mean.hpp>
#include <boost/accumulators/statistics/pot_quantile.hpp>
#include <boost/accumulators/statistics/p_square_cumulative_distribution.hpp>
#include <boost/accumulators/statistics/p_square_quantile.hpp>
#include <boost/accumulators/statistics/skewness.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/sum.hpp>
#include <boost/accumulators/statistics/tail.hpp>
#include <boost/accumulators/statistics/tail_quantile.hpp>
#include <boost/accumulators/statistics/tail_mean.hpp>
#include <boost/accumulators/statistics/tail_variate.hpp>
#include <boost/accumulators/statistics/tail_variate_means.hpp>
#include <boost/accumulators/statistics/variance.hpp>
#include <boost/accumulators/statistics/weighted_covariance.hpp>
#include <boost/accumulators/statistics/weighted_density.hpp>
#include <boost/accumulators/statistics/weighted_kurtosis.hpp>
#include <boost/accumulators/statistics/weighted_extended_p_square.hpp>
#include <boost/accumulators/statistics/weighted_mean.hpp>
#include <boost/accumulators/statistics/weighted_median.hpp>
#include <boost/accumulators/statistics/weighted_moment.hpp>
#include <boost/accumulators/statistics/weighted_peaks_over_threshold.hpp>
#include <boost/accumulators/statistics/weighted_p_square_cumulative_distribution.hpp>
#include <boost/accumulators/statistics/weighted_p_square_quantile.hpp>
#include <boost/accumulators/statistics/weighted_skewness.hpp>
#include <boost/accumulators/statistics/weighted_sum.hpp>
#include <boost/accumulators/statistics/weighted_tail_quantile.hpp>
#include <boost/accumulators/statistics/weighted_tail_mean.hpp>
#include <boost/accumulators/statistics/weighted_tail_variate_means.hpp>
#include <boost/accumulators/statistics/weighted_variance.hpp>
#include <boost/accumulators/statistics/with_error.hpp>
#include <boost/accumulators/statistics/parameters/quantile_probability.hpp>
#include <boost/accumulators/statistics/variates/covariate.hpp>

#endif


/*
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <limits>

#include "oslexec_pvt.h"
#include "noiseimpl.h"
#include "OSL/dual_vec.h"
#include "OSL/Imathx.h"

#include <OpenImageIO/fmath.h>

using namespace OSL;

OSL_NAMESPACE_ENTER
namespace pvt {

#if 0 // only when testing the statistics of perlin noise to normalize the range

#include <boost/random.hpp>

void test_perlin(int d) {
    HashScalar h;
    float noise_min = +std::numeric_limits<float>::max();
    float noise_max = -std::numeric_limits<float>::max();
    float noise_avg = 0;
    float noise_avg2 = 0;
    float noise_stddev;
    boost::mt19937 rndgen;
    boost::uniform_01<boost::mt19937, float> rnd(rndgen);
    printf("Running perlin-%d noise test ...\n", d);
    const int n = 100000000;
    const float r = 1024;
    for (int i = 0; i < n; i++) {
        float noise;
        float nx = rnd(); nx = (2 * nx - 1) * r;
        float ny = rnd(); ny = (2 * ny - 1) * r;
        float nz = rnd(); nz = (2 * nz - 1) * r;
        float nw = rnd(); nw = (2 * nw - 1) * r;
        switch (d) {
            case 1: perlin(noise, h, nx); break;
            case 2: perlin(noise, h, nx, ny); break;
            case 3: perlin(noise, h, nx, ny, nz); break;
            case 4: perlin(noise, h, nx, ny, nz, nw); break;
        }
        if (noise_min > noise) noise_min = noise;
        if (noise_max < noise) noise_max = noise;
        noise_avg += noise;
        noise_avg2 += noise * noise;
    }
    noise_avg /= n;
    noise_stddev = std::sqrt((noise_avg2 - noise_avg * noise_avg * n) / n);
    printf("Result: perlin-%d noise stats:\n\tmin: %.17g\n\tmax: %.17g\n\tavg: %.17g\n\tdev: %.17g\n",
            d, noise_min, noise_max, noise_avg, noise_stddev);
    printf("Normalization: %.17g\n", 1.0f / std::max(fabsf(noise_min), fabsf(noise_max)));
}

#endif

/***********************************************************************
 * noise routines callable by the LLVM-generated code.
 */

#if 1

#define NOISE_IMPL(opname,implname)                                     \
OSL_SHADEOP float osl_ ##opname## _ff (float x) {                       \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, x);                                                        \
    return r;                                                           \
}                                                                       \
                                                                        \
OSL_SHADEOP float osl_ ##opname## _fff (float x, float y) {             \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, x, y);                                                     \
    return r;                                                           \
}                                                                       \
                                                                        \
OSL_SHADEOP float osl_ ##opname## _fv (char *x) {                       \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, VEC(x));                                                   \
    return r;                                                           \
}                                                                       \
                                                                        \
OSL_SHADEOP float osl_ ##opname## _fvf (char *x, float y) {             \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, VEC(x), y);                                                \
    return r;                                                           \
}                                                                       \
                                                                        \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vf (char *r, float x) {               \
    implname impl;                                                      \
    impl (VEC(r), x);                                                   \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vff (char *r, float x, float y) {     \
    implname impl;                                                      \
    impl (VEC(r), x, y);                                                \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vv (char *r, char *x) {               \
    implname impl;                                                      \
    impl (VEC(r), VEC(x));                                              \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vvf (char *r, char *x, float y) {     \
    implname impl;                                                      \
    impl (VEC(r), VEC(x), y);                                           \
}

#define NOISE_IMPL_DERIV(opname,implname)                               \
OSL_SHADEOP void osl_ ##opname## _dfdf (char *r, char *x) {             \
    implname impl;                                                      \
    impl (DFLOAT(r), DFLOAT(x));                                        \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdfdf (char *r, char *x, char *y) {  \
    implname impl;                                                      \
    impl (DFLOAT(r), DFLOAT(x), DFLOAT(y));                             \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdff (char *r, char *x, float y) {   \
    implname impl;                                                      \
    impl (DFLOAT(r), DFLOAT(x), Dual2<float>(y));                       \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dffdf (char *r, float x, char *y) {   \
    implname impl;                                                      \
    impl (DFLOAT(r), Dual2<float>(x), DFLOAT(y));                       \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdv (char *r, char *x) {             \
    implname impl;                                                      \
    impl (DFLOAT(r), DVEC(x));                                          \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvdf (char *r, char *x, char *y) {  \
    implname impl;                                                      \
    impl (DFLOAT(r), DVEC(x), DFLOAT(y));                               \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvf (char *r, char *x, float y) {   \
    implname impl;                                                      \
    impl (DFLOAT(r), DVEC(x), Dual2<float>(y));                         \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfvdf (char *r, char *x, char *y) {   \
    implname impl;                                                      \
    impl (DFLOAT(r), Dual2<Vec3>(VEC(x)), DFLOAT(y));                   \
}                                                                       \
                                                                        \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdf (char *r, char *x) {             \
    implname impl;                                                      \
    impl (DVEC(r), DFLOAT(x));                                          \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdfdf (char *r, char *x, char *y) {  \
    implname impl;                                                      \
    impl (DVEC(r), DFLOAT(x), DFLOAT(y));                               \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdff (char *r, char *x, float y) {   \
    implname impl;                                                      \
    impl (DVEC(r), DFLOAT(x), Dual2<float>(y));                         \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvfdf (char *r, float x, char *y) {   \
    implname impl;                                                      \
    impl (DVEC(r), Dual2<float>(x), DFLOAT(y));                         \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdv (char *r, char *x) {             \
    implname impl;                                                      \
    impl (DVEC(r), DVEC(x));                                            \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvdf (char *r, char *x, char *y) {  \
    implname impl;                                                      \
    impl (DVEC(r), DVEC(x), DFLOAT(y));                                 \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvf (char *r, char *x, float y) {   \
    implname impl;                                                      \
    impl (DVEC(r), DVEC(x), Dual2<float>(y));                           \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvvdf (char *r, char *x, char *y) {   \
    implname impl;                                                      \
    impl (DVEC(r), Dual2<Vec3>(VEC(x)), DFLOAT(y));                     \
}

#define NOISE_IMPL_DERIV_OPT(opname,implname)                           \
OSL_SHADEOP void osl_ ##opname## _dfdf (char *name, char *r, char *x, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DFLOAT(x), (ShaderGlobals *)sg, (NoiseParams *)opt);                                   \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdfdf (char *name, char *r, char *x, char *y, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DFLOAT(x), DFLOAT(y), (ShaderGlobals *)sg, (NoiseParams *)opt);                        \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdv (char *name, char *r, char *x, char *sg, char *opt) {  \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DVEC(x), (ShaderGlobals *)sg, (NoiseParams *)opt);                                     \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvdf (char *name, char *r, char *x, char *y, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DVEC(x), DFLOAT(y), (ShaderGlobals *)sg, (NoiseParams *)opt);                          \
}                                                                       \
                                                                        \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdf (char *name, char *r, char *x, char *sg, char *opt) {  \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DFLOAT(x), (ShaderGlobals *)sg, (NoiseParams *)opt);                                     \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdfdf (char *name, char *r, char *x, char *y, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DFLOAT(x), DFLOAT(y), (ShaderGlobals *)sg, (NoiseParams *)opt);                                     \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdv (char *name, char *r, char *x, char *sg, char *opt) {  \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DVEC(x), (ShaderGlobals *)sg, (NoiseParams *)opt);                                       \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvdf (char *name, char *r, char *x, char *y, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DVEC(x), DFLOAT(y), (ShaderGlobals *)sg, (NoiseParams *)opt);                            \
}

NOISE_IMPL (cellnoise, CellNoise)
NOISE_IMPL (noise, Noise)
NOISE_IMPL_DERIV (noise, Noise)
NOISE_IMPL (snoise, SNoise)
NOISE_IMPL_DERIV (snoise, SNoise)
NOISE_IMPL (simplexnoise, SimplexNoise)
NOISE_IMPL_DERIV (simplexnoise, SimplexNoise)
NOISE_IMPL (usimplexnoise, USimplexNoise)
NOISE_IMPL_DERIV (usimplexnoise, USimplexNoise)

#define PNOISE_IMPL(opname,implname)                                    \
    OSL_SHADEOP float osl_ ##opname## _fff (float x, float px) {        \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, x, px);                                                    \
    return r;                                                           \
}                                                                       \
                                                                        \
OSL_SHADEOP float osl_ ##opname## _fffff (float x, float y, float px, float py) { \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, x, y, px, py);                                             \
    return r;                                                           \
}                                                                       \
                                                                        \
OSL_SHADEOP float osl_ ##opname## _fvv (char *x, char *px) {            \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, VEC(x), VEC(px));                                          \
    return r;                                                           \
}                                                                       \
                                                                        \
OSL_SHADEOP float osl_ ##opname## _fvfvf (char *x, float y, char *px, float py) { \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, VEC(x), y, VEC(px), py);                                   \
    return r;                                                           \
}                                                                       \
                                                                        \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vff (char *r, float x, float px) {    \
    implname impl;                                                      \
    impl (VEC(r), x, px);                                               \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vffff (char *r, float x, float y, float px, float py) { \
    implname impl;                                                      \
    impl (VEC(r), x, y, px, py);                                        \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vvv (char *r, char *x, char *px) {    \
    implname impl;                                                      \
    impl (VEC(r), VEC(x), VEC(px));                                     \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vvfvf (char *r, char *x, float y, char *px, float py) { \
    implname impl;                                                      \
    impl (VEC(r), VEC(x), y, VEC(px), py);                              \
}

#define PNOISE_IMPL_DERIV(opname,implname)                              \
OSL_SHADEOP void osl_ ##opname## _dfdff (char *r, char *x, float px) {  \
    implname impl;                                                      \
    impl (DFLOAT(r), DFLOAT(x), px);                                    \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdfdfff (char *r, char *x, char *y, float px, float py) { \
    implname impl;                                                      \
    impl (DFLOAT(r), DFLOAT(x), DFLOAT(y), px, py);                     \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdffff (char *r, char *x, float y, float px, float py) { \
    implname impl;                                                      \
    impl (DFLOAT(r), DFLOAT(x), Dual2<float>(y), px, py);               \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dffdfff (char *r, float x, char *y, float px, float py) { \
    implname impl;                                                      \
    impl (DFLOAT(r), Dual2<float>(x), DFLOAT(y), px, py);               \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvv (char *r, char *x, char *px) {  \
    implname impl;                                                      \
    impl (DFLOAT(r), DVEC(x), VEC(px));                                 \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvdfvf (char *r, char *x, char *y, char *px, float py) { \
    implname impl;                                                      \
    impl (DFLOAT(r), DVEC(x), DFLOAT(y), VEC(px), py);                  \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvfvf (char *r, char *x, float y, char *px, float py) { \
    implname impl;                                                      \
    impl (DFLOAT(r), DVEC(x), Dual2<float>(y), VEC(px), py);            \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfvdfvf (char *r, char *x, char *y, char *px, float py) { \
    implname impl;                                                      \
    impl (DFLOAT(r), Dual2<Vec3>(VEC(x)), DFLOAT(y), VEC(px), py);      \
}                                                                       \
                                                                        \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdff (char *r, char *x, float px) {  \
    implname impl;                                                      \
    impl (DVEC(r), DFLOAT(x), px);                                      \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdfdfff (char *r, char *x, char *y, float px, float py) { \
    implname impl;                                                      \
    impl (DVEC(r), DFLOAT(x), DFLOAT(y), px, py);                       \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdffff (char *r, char *x, float y, float px, float py) { \
    implname impl;                                                      \
    impl (DVEC(r), DFLOAT(x), Dual2<float>(y), px, py);                 \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvfdfff (char *r, float x, char *y, float px, float py) { \
    implname impl;                                                      \
    impl (DVEC(r), Dual2<float>(x), DFLOAT(y), px, py);                 \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvv (char *r, char *x, char *px) {  \
    implname impl;                                                      \
    impl (DVEC(r), DVEC(x), VEC(px));                                   \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvdfvf (char *r, char *x, char *y, char *px, float py) { \
    implname impl;                                                      \
    impl (DVEC(r), DVEC(x), DFLOAT(y), VEC(px), py);                    \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvfvf (char *r, char *x, float y, float *px, float py) { \
    implname impl;                                                      \
    impl (DVEC(r), DVEC(x), Dual2<float>(y), VEC(px), py);              \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvvdfvf (char *r, char *x, char *px, char *y, float py) { \
    implname impl;                                                      \
    impl (DVEC(r), Dual2<Vec3>(VEC(x)), DFLOAT(y), VEC(px), py);        \
}

#define PNOISE_IMPL_DERIV_OPT(opname,implname)                          \
OSL_SHADEOP void osl_ ##opname## _dfdff (char *name, char *r, char *x, float px, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DFLOAT(x), px, (ShaderGlobals *)sg, (NoiseParams *)opt); \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdfdfff (char *name, char *r, char *x, char *y, float px, float py, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DFLOAT(x), DFLOAT(y), px, py, (ShaderGlobals *)sg, (NoiseParams *)opt); \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvv (char *name, char *r, char *x, char *px, char *sg, char *opt) {  \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DVEC(x), VEC(px), (ShaderGlobals *)sg, (NoiseParams *)opt); \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvdfvf (char *name, char *r, char *x, char *y, char *px, float py, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DVEC(x), DFLOAT(y), VEC(px), py, (ShaderGlobals *)sg, (NoiseParams *)opt); \
}                                                                       \
                                                                        \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdff (char *name, char *r, char *x, float px, char *sg, char *opt) {  \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DFLOAT(x), px, (ShaderGlobals *)sg, (NoiseParams *)opt); \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdfdfff (char *name, char *r, char *x, char *y, float px, float py, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DFLOAT(x), DFLOAT(y), px, py, (ShaderGlobals *)sg, (NoiseParams *)opt); \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvv (char *name, char *r, char *x, char *px, char *sg, char *opt) {  \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DVEC(x), VEC(px), (ShaderGlobals *)sg, (NoiseParams *)opt); \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvdfvf (char *name, char *r, char *x, char *y, char *px, float py, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DVEC(x), DFLOAT(y), VEC(px), py, (ShaderGlobals *)sg, (NoiseParams *)opt); \
}

PNOISE_IMPL (pcellnoise, PeriodicCellNoise)
PNOISE_IMPL (pnoise, PeriodicNoise)
PNOISE_IMPL_DERIV (pnoise, PeriodicNoise)
PNOISE_IMPL (psnoise, PeriodicSNoise)
PNOISE_IMPL_DERIV (psnoise, PeriodicSNoise)

struct GaborNoise {
    GaborNoise () { }

    // Gabor always uses derivatives, so dual versions only

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<float> &x,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = gabor (x, opt);
    }

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<float> &x, const Dual2<float> &y,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = gabor (x, y, opt);
    }

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<Vec3> &p,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = gabor (p, opt);
    }

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<Vec3> &p, const Dual2<float> &t,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        // FIXME -- This is very broken, we are ignoring 4D!
        result = gabor (p, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<float> &x,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = gabor3 (x, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<float> &x, const Dual2<float> &y,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = gabor3 (x, y, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<Vec3> &p,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = gabor3 (p, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<Vec3> &p, const Dual2<float> &t,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        // FIXME -- This is very broken, we are ignoring 4D!
        result = gabor3 (p, opt);
    }
};

struct GaborPNoise {
    GaborPNoise () { }

    // Gabor always uses derivatives, so dual versions only

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<float> &x, float px,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = pgabor (x, px, opt);
    }

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<float> &x, const Dual2<float> &y,
                            float px, float py,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = pgabor (x, y, px, py, opt);
    }

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<Vec3> &p, const Vec3 &pp,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = pgabor (p, pp, opt);
    }

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<Vec3> &p, const Dual2<float> &t,
                            const Vec3 &pp, float tp,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        // FIXME -- This is very broken, we are ignoring 4D!
        result = pgabor (p, pp, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<float> &x, float px,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = pgabor3 (x, px, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<float> &x, const Dual2<float> &y,
                            float px, float py,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = pgabor3 (x, y, px, py, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<Vec3> &p, const Vec3 &pp,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = pgabor3 (p, pp, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<Vec3> &p, const Dual2<float> &t,
                            const Vec3 &pp, float tp,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        // FIXME -- This is very broken, we are ignoring 4D!
        result = pgabor3 (p, pp, opt);
    }
};

NOISE_IMPL_DERIV_OPT (gabornoise, GaborNoise)
PNOISE_IMPL_DERIV_OPT (gaborpnoise, GaborPNoise)

struct GenericNoise {
    GenericNoise () { }

    // Template on R, S, and T to be either float or Vec3

    // dual versions -- this is always called with derivs

    template<class R, class S>
    inline void operator() (ustring name, Dual2<R> &result, const Dual2<S> &s,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        if (name == Strings::uperlin || name == Strings::noise) {
            Noise noise;
            noise(result, s);
        } else if (name == Strings::perlin || name == Strings::snoise) {
            SNoise snoise;
            snoise(result, s);
        } else if (name == Strings::simplexnoise || name == Strings::simplex) {
            SimplexNoise simplexnoise;
            simplexnoise(result, s);
        } else if (name == Strings::usimplexnoise || name == Strings::usimplex) {
            USimplexNoise usimplexnoise;
            usimplexnoise(result, s);
        } else if (name == Strings::cell) {
            CellNoise cellnoise;
            cellnoise(result.val(), s.val());
            result.clear_d();
        } else if (name == Strings::gabor) {
            GaborNoise gnoise;
            gnoise (name, result, s, sg, opt);
        } else {
            ((ShadingContext *)sg->context)->error ("Unknown noise type \"%s\"", name.c_str());
        }
    }

    template<class R, class S, class T>
    inline void operator() (ustring name, Dual2<R> &result,
                            const Dual2<S> &s, const Dual2<T> &t,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        if (name == Strings::uperlin || name == Strings::noise) {
            Noise noise;
            noise(result, s, t);
        } else if (name == Strings::perlin || name == Strings::snoise) {
            SNoise snoise;
            snoise(result, s, t);
        } else if (name == Strings::simplexnoise || name == Strings::simplex) {
            SimplexNoise simplexnoise;
            simplexnoise(result, s, t);
        } else if (name == Strings::usimplexnoise || name == Strings::usimplex) {
            USimplexNoise usimplexnoise;
            usimplexnoise(result, s, t);
        } else if (name == Strings::cell) {
            CellNoise cellnoise;
            cellnoise(result.val(), s.val(), t.val());
            result.clear_d();
        } else if (name == Strings::gabor) {
            GaborNoise gnoise;
            gnoise (name, result, s, t, sg, opt);
        } else {
            ((ShadingContext *)sg->context)->error ("Unknown noise type \"%s\"", name.c_str());
        }
    }
};

NOISE_IMPL_DERIV_OPT (genericnoise, GenericNoise)

struct GenericPNoise {
    GenericPNoise () { }

    // Template on R, S, and T to be either float or Vec3

    // dual versions -- this is always called with derivs

    template<class R, class S>
    inline void operator() (ustring name, Dual2<R> &result, const Dual2<S> &s,
                            const S &sp,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        if (name == Strings::uperlin || name == Strings::noise) {
            PeriodicNoise noise;
            noise(result, s, sp);
        } else if (name == Strings::perlin || name == Strings::snoise) {
            PeriodicSNoise snoise;
            snoise(result, s, sp);
        } else if (name == Strings::cell) {
            PeriodicCellNoise cellnoise;
            cellnoise(result.val(), s.val(), sp);
            result.clear_d();
        } else if (name == Strings::gabor) {
            GaborPNoise gnoise;
            gnoise (name, result, s, sp, sg, opt);
        } else {
            ((ShadingContext *)sg->context)->error ("Unknown noise type \"%s\"", name.c_str());
        }
    }

    template<class R, class S, class T>
    inline void operator() (ustring name, Dual2<R> &result,
                            const Dual2<S> &s, const Dual2<T> &t,
                            const S &sp, const T &tp,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        if (name == Strings::uperlin || name == Strings::noise) {
            PeriodicNoise noise;
            noise(result, s, t, sp, tp);
        } else if (name == Strings::perlin || name == Strings::snoise) {
            PeriodicSNoise snoise;
            snoise(result, s, t, sp, tp);
        } else if (name == Strings::cell) {
            PeriodicCellNoise cellnoise;
            cellnoise(result.val(), s.val(), t.val(), sp, tp);
            result.clear_d();
        } else if (name == Strings::gabor) {
            GaborPNoise gnoise;
            gnoise (name, result, s, t, sp, tp, sg, opt);
        } else {
            ((ShadingContext *)sg->context)->error ("Unknown noise type \"%s\"", name.c_str());
        }
    }
};

PNOISE_IMPL_DERIV_OPT (genericpnoise, GenericPNoise)

// Utility: retrieve a pointer to the ShadingContext's noise params
// struct, also re-initialize its contents.
OSL_SHADEOP void *
osl_get_noise_options (void *sg_)
{
    ShaderGlobals *sg = (ShaderGlobals *)sg_;
    RendererServices::NoiseOpt *opt = sg->context->noise_options_ptr ();
    new (opt) RendererServices::NoiseOpt;
    return opt;
}

OSL_SHADEOP void
osl_noiseparams_set_anisotropic (void *opt, int a)
{
    ((RendererServices::NoiseOpt *)opt)->anisotropic = a;
}

OSL_SHADEOP void
osl_noiseparams_set_do_filter (void *opt, int a)
{
    ((RendererServices::NoiseOpt *)opt)->do_filter = a;
}

OSL_SHADEOP void
osl_noiseparams_set_direction (void *opt, void *dir)
{
    ((RendererServices::NoiseOpt *)opt)->direction = VEC(dir);
}

OSL_SHADEOP void
osl_noiseparams_set_bandwidth (void *opt, float b)
{
    ((RendererServices::NoiseOpt *)opt)->bandwidth = b;
}

OSL_SHADEOP void
osl_noiseparams_set_impulses (void *opt, float i)
{
    ((RendererServices::NoiseOpt *)opt)->impulses = i;
}

} // namespace pvt
OSL_NAMESPACE_EXIT

#endif


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#ifndef MTL_DIM_SPLITTER_INCLUDE
#define MTL_DIM_SPLITTER_INCLUDE

#include <algorithm>
#include <boost/numeric/mtl/recursion/utility.hpp>

namespace mtl { namespace recursion {

// Splits dimensions of a matrix separately into halfs (first value rounded up)
template <typename Matrix>
struct half_splitter
{
    typedef typename Matrix::size_type                            size_type;

    explicit half_splitter(Matrix const& matrix)
    {
	size_type nr= matrix.num_rows(), nc= matrix.num_cols();
	nr-= nr / 2; // keep the part (by removind the down-rounded half)
	nc-= nc / 2;
	my_row_split= matrix.begin_row() + nr;
	my_col_split= matrix.begin_col() + nc;
    }

    // End of northern half and beginning of southern
    size_type row_split() const
    {
	return my_row_split;
    }

    // End of western half and beginning of eastern
    size_type col_split() const
    {
	return my_col_split;
    }

private:
    size_type             my_row_split, my_col_split;
};

// Splits dimensions of a matrix separately into a first part that
//   is the largest power of 2 smaller than m or n, plus rest;
//   doesn't yield empty submatrices if both dimension > 1
template <typename Matrix>
struct separate_dim_splitter
{
    typedef typename Matrix::size_type                            size_type;

    explicit separate_dim_splitter(Matrix const& matrix)
	: my_row_split(matrix.begin_row() + first_part(matrix.num_rows())),
	  my_col_split(matrix.begin_col() + first_part(matrix.num_cols()))
    {}

    // End of northern half and beginning of southern
    size_type row_split() const
    {
	return my_row_split;
    }

    // End of western half and beginning of eastern
    size_type col_split() const
    {
	return my_col_split;
    }

private:
    size_type             my_row_split, my_col_split;
};

// Splits dimensions of a matrix separately into a first part that
//   is the largest power of 2 smaller than the maximum of m and n;
//   can yield 1 or two empty submatrices if matrix is rather unproportional;
//   helps creating square submatrices
template <typename Matrix>
struct max_dim_splitter
{
    typedef typename Matrix::size_type                            size_type;

    explicit max_dim_splitter(Matrix const& matrix)
	: // matrix(matrix),
	  my_split(std::max(first_part(matrix.num_rows()), first_part(matrix.num_cols()))),
	  my_row_split(std::min(matrix.begin_row() + my_split, matrix.end_row())),
	  my_col_split(std::min(matrix.begin_col() + my_split, matrix.end_col()))
   {}

    // End of northern half and beginning of southern (limited to end_row)
    size_type row_split() const
    {
	return my_row_split;
    }

    // End of western half and beginning of eastern (limited to end_col)
    size_type col_split() const
    {
	return my_col_split;
    }

private:
    //    Matrix const&   matrix;
    size_type             my_split, // minimal 2^(k-1) such that 2^k >= max(num_rows, num_cols)
                    my_row_split, my_col_split;
};

// Splitting within bounding box of power of 2, using recursators
// For instance, the upper left part of a 530 x 17 matrix is
//   530 x 17   if the bound is 2048 or larger
//   512 x 17   if the bound is 1024
//                 bound of 512 or smaller is a wrong bound
template <typename Recursator>
struct outer_bound_splitter
{
    typedef typename Recursator::size_type                            size_type;

    explicit outer_bound_splitter(Recursator const& recursator)
    {
	typename Recursator::matrix_type const& matrix= recursator.get_value();
	my_row_split= std::min(matrix.begin_row() + recursator.bound() / 2, matrix.end_row());
	my_col_split= std::min(matrix.begin_col() + recursator.bound() / 2, matrix.end_col());
    }

    // End of northern half and beginning of southern (limited to end_row)
    size_type row_split() const
    {
	return my_row_split;
    }

    // End of western half and beginning of eastern (limited to end_col)
    size_type col_split() const
    {
	return my_col_split;
    }

private:
    size_type             my_row_split, my_col_split;
};

}} // namespace mtl::recursion

#endif // MTL_DIM_SPLITTER_INCLUDE


#include <iostream>
#include <fstream>
#include <algorithm>
#include <stdio.h>
#include <math.h>
#include <vector>
#include <numeric>
#include <strings.h>
#include <assert.h>
#include <sys/types.h>
#include <stdexcept>

#include <dirent.h>

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/adapted/c_array.hpp>

BOOST_GEOMETRY_REGISTER_C_ARRAY_CS(cs::cartesian)

typedef boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > Polygon;

using namespace std;

/*=======================================================================
STATIC EVALUATION PARAMETERS
=======================================================================*/

// easy and hard evaluation level
enum DIFFICULTY{EASY=0, HARD=1};

const int32_t N_TESTIMAGES = 27661;

const int32_t MIN_3D_N_POINTS = 10;
const double MAX_3D_DIST[2] = {15, 25};
const double MIN_2D_AREA[2] = {1600, 500};
const double MAX_2D_OCC = 2;
// evaluation metrics: image, ground or 3D
enum METRIC{IMAGE=0, GROUND=1, BOX3D=2};

// evaluated object classes
enum CLASSES{CAR=0, PEDESTRIAN=1, CYCLIST=2};
const int NUM_CLASS = 3;

// parameters varying per class
vector<string> CLASS_NAMES;
vector<string> CLASS_NAMES_CAP;
// the minimum overlap required for 2D evaluation on the image/ground plane and 3D evaluation
const double MIN_OVERLAP[3][3] = {{0.7, 0.5, 0.5}, {0.7, 0.5, 0.5}, {0.5, 0.3, 0.3}};

// no. of recall steps that should be evaluated (discretized)
const double N_SAMPLE_PTS = 41;

// initialize class names
void initGlobals () {
  CLASS_NAMES.push_back("car");
  CLASS_NAMES.push_back("pedestrian");
  CLASS_NAMES.push_back("cyclist");
  CLASS_NAMES_CAP.push_back("Car");
  CLASS_NAMES_CAP.push_back("Pedestrian");
  CLASS_NAMES_CAP.push_back("Cyclist");
}

/*=======================================================================
DATA TYPES FOR EVALUATION
=======================================================================*/

// holding data needed for precision-recall and precision-aos
struct tPrData {
  vector<double> v;           // detection score for computing score thresholds
  double         similarity;  // orientation similarity
  int32_t        tp;          // true positives
  int32_t        fp;          // false positives
  int32_t        fn;          // false negatives
  tPrData () :
    similarity(0), tp(0), fp(0), fn(0) {}
};

// holding bounding boxes for ground truth and detections
struct tBox {
  string  type;     // object type as car, pedestrian or cyclist,...
  double   x1;      // left corner
  double   y1;      // top corner
  double   x2;      // right corner
  double   y2;      // bottom corner
  double   alpha;   // image orientation
  tBox (string type, double x1,double y1,double x2,double y2,double alpha) :
    type(type),x1(x1),y1(y1),x2(x2),y2(y2),alpha(alpha) {}
};

// holding ground truth data
struct tGroundtruth {
  tBox    box;        // object type, box, orientation
  int32_t  truncation; // truncation 0..1
  int32_t occlusion;  // occlusion 0,1,2,3 (non, partly, partly, fully)
  int32_t num_points_3d;
  double ry;
  double  t1, t2, t3;
  double h, w, l;
  tGroundtruth () :
    box(tBox("invalild",-1,-1,-1,-1,-10)),truncation(-1),occlusion(-1) {}
  tGroundtruth (tBox box,int32_t truncation,int32_t occlusion) :
    box(box),truncation(truncation),occlusion(occlusion) {}
  tGroundtruth (string type,double x1,double y1,double x2,double y2,double alpha,int32_t truncation,int32_t occlusion) :
    box(tBox(type,x1,y1,x2,y2,alpha)),truncation(truncation),occlusion(occlusion) {}
};

// holding detection data
struct tDetection {
  tBox    box;    // object type, box, orientation
  double  thresh; // detection score
  double  ry;
  double  t1, t2, t3;
  double  h, w, l;
  tDetection ():
    box(tBox("invalid",-1,-1,-1,-1,-10)),thresh(-1000) {}
  tDetection (tBox box,double thresh) :
    box(box),thresh(thresh) {}
  tDetection (string type,double x1,double y1,double x2,double y2,double alpha,double thresh) :
    box(tBox(type,x1,y1,x2,y2,alpha)),thresh(thresh) {}
};

/*=======================================================================
FUNCTIONS TO LOAD DETECTION AND GROUND TRUTH DATA ONCE, SAVE RESULTS
=======================================================================*/
vector<tDetection> loadDetection(string file_name) {

  vector<tDetection> detections;
  FILE *fp = fopen(file_name.c_str(),"r");
  if (!fp)
    throw invalid_argument("cannot read detection file " + file_name);
  while (!feof(fp)) {
    tDetection d;
    int trash;
    char str[255];
    if (fscanf(fp, "%s %d %d %d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",
                   str, &trash, &trash, &trash, &d.box.alpha, &d.box.x1, &d.box.y1,
                   &d.box.x2, &d.box.y2, &d.h, &d.w, &d.l, &d.t1, &d.t2, &d.t3,
                   &d.ry, &d.thresh)==17) {
      d.box.type = str;
      detections.push_back(d);
    }
  }

  fclose(fp);
  return detections;
}

vector<tGroundtruth> loadGroundtruth(string file_name) {
  vector<tGroundtruth> groundtruth;
  FILE *fp = fopen(file_name.c_str(),"r");
  if (!fp)
    throw invalid_argument("cannot read ground truth file " + file_name);
  while (!feof(fp)) {
    tGroundtruth g;
    int trash;
    char str[255];
    if (fscanf(fp, "%s %d %d %d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %d %d",
                   str, &g.truncation, &g.occlusion, &g.num_points_3d,
                   &g.box.alpha, &g.box.x1, &g.box.y1, &g.box.x2, &g.box.y2,
                   &g.h, &g.w, &g.l, &g.t1, &g.t2, &g.t3, &g.ry, &trash)==17) {
      g.box.type = str;
      groundtruth.push_back(g);
    }
  }
  fclose(fp);
  return groundtruth;
}

vector<string> list_dir(const string path) {
  struct dirent *entry;
  DIR *dir = opendir(path.c_str());
  vector<string> entries;

  if (dir == NULL) {
    return entries;
  }
  while ((entry = readdir(dir)) != NULL) {
    if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0)
      entries.push_back(entry->d_name);
  }
  closedir(dir);
  return entries;
}

/*=======================================================================
EVALUATION HELPER FUNCTIONS
=======================================================================*/

// criterion defines whether the overlap is computed with respect to both areas (ground truth and detection)
// or with respect to box a or b (detection and "dontcare" areas)
inline double imageBoxOverlap(tBox a, tBox b, int32_t criterion=-1){

  // overlap is invalid in the beginning
  double o = -1;

  // get overlapping area
  double x1 = max(a.x1, b.x1);
  double y1 = max(a.y1, b.y1);
  double x2 = min(a.x2, b.x2);
  double y2 = min(a.y2, b.y2);

  // compute width and height of overlapping area
  double w = x2-x1;
  double h = y2-y1;

  // set invalid entries to 0 overlap
  if(w<=0 || h<=0)
    return 0;

  // get overlapping areas
  double inter = w*h;
  double a_area = (a.x2-a.x1) * (a.y2-a.y1);
  double b_area = (b.x2-b.x1) * (b.y2-b.y1);

  // intersection over union overlap depending on users choice
  if(criterion==-1)     // union
    o = inter / (a_area+b_area-inter);
  else if(criterion==0) // bbox_a
    o = inter / a_area;
  else if(criterion==1) // bbox_b
    o = inter / b_area;

  // overlap
  return o;
}

inline double imageBoxOverlap(tDetection a, tGroundtruth b, int32_t criterion=-1){
  return imageBoxOverlap(a.box, b.box, criterion);
}

// compute polygon of an oriented bounding box
template <typename T>
Polygon toPolygon(const T& g) {
    using namespace boost::numeric::ublas;
    using namespace boost::geometry;
    matrix<double> mref(2, 2);
    mref(0, 0) = cos(g.ry); mref(0, 1) = sin(g.ry);
    mref(1, 0) = -sin(g.ry); mref(1, 1) = cos(g.ry);

    static int count = 0;
    matrix<double> corners(2, 4);
    double data[] = {g.l / 2, g.l / 2, -g.l / 2, -g.l / 2,
                     g.w / 2, -g.w / 2, -g.w / 2, g.w / 2};
    std::copy(data, data + 8, corners.data().begin());
    matrix<double> gc = prod(mref, corners);
    for (int i = 0; i < 4; ++i) {
        gc(0, i) += g.t1;
        gc(1, i) += g.t3;
    }

    double points[][2] = {{gc(0, 0), gc(1, 0)},{gc(0, 1), gc(1, 1)},{gc(0, 2), gc(1, 2)},{gc(0, 3), gc(1, 3)},{gc(0, 0), gc(1, 0)}};
    Polygon poly;
    append(poly, points);
    return poly;
}

// measure overlap between bird's eye view bounding boxes, parametrized by (ry, l, w, tx, tz)
inline double groundBoxOverlap(tDetection d, tGroundtruth g, int32_t criterion = -1) {
    using namespace boost::geometry;
    Polygon gp = toPolygon(g);
    Polygon dp = toPolygon(d);

    std::vector<Polygon> in, un;
    intersection(gp, dp, in);
    union_(gp, dp, un);

    double inter_area = in.empty() ? 0 : area(in.front());
    double union_area = area(un.front());
    double o;
    if(criterion==-1)     // union
        o = inter_area / union_area;
    else if(criterion==0) // bbox_a
        o = inter_area / area(dp);
    else if(criterion==1) // bbox_b
        o = inter_area / area(gp);

    return o;
}

// measure overlap between 3D bounding boxes, parametrized by (ry, h, w, l, tx, ty, tz)
inline double box3DOverlap(tDetection d, tGroundtruth g, int32_t criterion = -1) {
    using namespace boost::geometry;
    Polygon gp = toPolygon(g);
    Polygon dp = toPolygon(d);

    std::vector<Polygon> in, un;
    intersection(gp, dp, in);
    union_(gp, dp, un);

    double ymax = min(d.t2, g.t2);
    double ymin = max(d.t2 - d.h, g.t2 - g.h);

    double inter_area = in.empty() ? 0 : area(in.front());
    double inter_vol = inter_area * max(0.0, ymax - ymin);

    double det_vol = d.h * d.l * d.w;
    double gt_vol = g.h * g.l * g.w;

    double o;
    if(criterion==-1)     // union
        o = inter_vol / (det_vol + gt_vol - inter_vol);
    else if(criterion==0) // bbox_a
        o = inter_vol / det_vol;
    else if(criterion==1) // bbox_b
        o = inter_vol / gt_vol;

    return o;
}

vector<double> getThresholds(vector<double> &v, double n_groundtruth){

  // holds scores needed to compute N_SAMPLE_PTS recall values
  vector<double> t;

  // sort scores in descending order
  // (highest score is assumed to give best/most confident detections)
  sort(v.begin(), v.end(), greater<double>());

  // get scores for linearly spaced recall
  double current_recall = 0;
  for(int32_t i=0; i<v.size(); i++){

    // check if right-hand-side recall with respect to current recall is close than left-hand-side one
    // in this case, skip the current detection score
    double l_recall, r_recall, recall;
    l_recall = (double)(i+1)/n_groundtruth;
    if(i<(v.size()-1))
      r_recall = (double)(i+2)/n_groundtruth;
    else
      r_recall = l_recall;

    if( (r_recall-current_recall) < (current_recall-l_recall) && i<(v.size()-1))
      continue;

    // left recall is the best approximation, so use this and goto next recall step for approximation
    recall = l_recall;

    // the next recall step was reached
    t.push_back(v[i]);
    current_recall += 1.0/(N_SAMPLE_PTS-1.0);
  }
  return t;
}

void cleanData(CLASSES current_class, const vector<tGroundtruth> &gt, const vector<tDetection> &det, vector<int32_t> &ignored_gt, vector<tGroundtruth> &dc, vector<int32_t> &ignored_det, int32_t &n_gt, DIFFICULTY difficulty, bool depth) {

  // extract ground truth bounding boxes for current evaluation class
  for(int32_t i=0;i<gt.size(); i++){

    // neighboring classes are ignored ("van" for "car" and "person_sitting" for "pedestrian")
    // (lower/upper cases are ignored)
    int32_t valid_class;

    // all classes without a neighboring class
    if(!strcasecmp(gt[i].box.type.c_str(), CLASS_NAMES[current_class].c_str()))
      valid_class = 1;

    // classes with a neighboring class
    else if(!strcasecmp(CLASS_NAMES[current_class].c_str(), "Pedestrian") && !strcasecmp("Person_sitting", gt[i].box.type.c_str()))
      valid_class = 0;
    else if(!strcasecmp(CLASS_NAMES[current_class].c_str(), "Car") && !strcasecmp("Van", gt[i].box.type.c_str()))
      valid_class = 0;

    // classes not used for evaluation
    else
      valid_class = -1;

    bool ignore = false;
    bool invalid = false;
    // 3D groundtruth filter criteria
    if (depth) {
      if (gt[i].num_points_3d < 0)
        invalid = true;
      if (gt[i].num_points_3d < MIN_3D_N_POINTS)
        ignore = true;
      if (gt[i].t1 * gt[i].t1 + gt[i].t3 * gt[i].t3 > MAX_3D_DIST[difficulty] * MAX_3D_DIST[difficulty])
        ignore = true;
    } else {
      double height = gt[i].box.y2 - gt[i].box.y1;
      double width = gt[i].box.x2 - gt[i].box.x1;
      double area = width * height;
      if (gt[i].box.x1 < 0)
        invalid = true;
      if (area < MIN_2D_AREA[difficulty])
        ignore = true;
      if (gt[i].occlusion > MAX_2D_OCC)
        ignore = true;
    }
    // set ignored vector for ground truth
    // current class and not ignored (total no. of ground truth is detected for recall denominator)
    if(invalid)
      ignored_gt.push_back(-1);
    else if(valid_class==1 && !ignore){
      ignored_gt.push_back(0);
      n_gt++;
    }
    else
      ignored_gt.push_back(1);
  }

  // extract dontcare areas
  for(int32_t i=0;i<gt.size(); i++)
    if(!strcasecmp("DontCare", gt[i].box.type.c_str()))
      dc.push_back(gt[i]);

  // extract detections bounding boxes of the current class
  for(int32_t i=0;i<det.size(); i++){

    // neighboring classes are not evaluated
    int32_t valid_class;
    if(!strcasecmp(det[i].box.type.c_str(), CLASS_NAMES[current_class].c_str()))
      valid_class = 1;
    else
      valid_class = -1;

    bool ignore = false;
    if (depth) {
      if (det[i].t1 * det[i].t1 + det[i].t3 * det[i].t3 > MAX_3D_DIST[difficulty] * MAX_3D_DIST[difficulty])
        ignore = true;
    } else {
      double height = det[i].box.y2 - det[i].box.y1;
      double width = det[i].box.x2 - det[i].box.x1;
      double area = width * height;
      if (area < MIN_2D_AREA[difficulty])
        ignore = true;
    }
    // set ignored vector for detections
    if(ignore)
      ignored_det.push_back(1);
    else if(valid_class==1)
      ignored_det.push_back(0);
    else
      ignored_det.push_back(-1);
  }
}

tPrData computeStatistics(CLASSES current_class, const vector<tGroundtruth> &gt,
        const vector<tDetection> &det, const vector<tGroundtruth> &dc,
        const vector<int32_t> &ignored_gt, const vector<int32_t>  &ignored_det,
        bool compute_fp, double (*boxoverlap)(tDetection, tGroundtruth, int32_t),
        METRIC metric, bool compute_aos=false, double thresh=0, bool debug=false){

  tPrData stat = tPrData();
  const double NO_DETECTION = -10000000;
  vector<double> delta;            // holds angular difference for TPs (needed for AOS evaluation)
  vector<bool> assigned_detection; // holds wether a detection was assigned to a valid or ignored ground truth
  assigned_detection.assign(det.size(), false);
  vector<bool> ignored_threshold;
  ignored_threshold.assign(det.size(), false); // holds detections with a threshold lower than thresh if FP are computed

  // detections with a low score are ignored for computing precision (needs FP)
  if(compute_fp)
    for(int32_t i=0; i<det.size(); i++)
      if(det[i].thresh<thresh)
        ignored_threshold[i] = true;

  // evaluate all ground truth boxes
  for(int32_t i=0; i<gt.size(); i++){

    // this ground truth is not of the current or a neighboring class and therefore ignored
    if(ignored_gt[i]==-1)
      continue;

    /*=======================================================================
    find candidates (overlap with ground truth > 0.5) (logical len(det))
    =======================================================================*/
    int32_t det_idx          = -1;
    double valid_detection = NO_DETECTION;
    double max_overlap     = 0;

    // search for a possible detection
    bool assigned_ignored_det = false;
    for(int32_t j=0; j<det.size(); j++){

      // detections not of the current class, already assigned or with a low threshold are ignored
      if(ignored_det[j]==-1)
        continue;
      if(assigned_detection[j])
        continue;
      if(ignored_threshold[j])
        continue;

      // find the maximum score for the candidates and get idx of respective detection
      double overlap = boxoverlap(det[j], gt[i], -1);

      // for computing recall thresholds, the candidate with highest score is considered
      if(!compute_fp && overlap>MIN_OVERLAP[metric][current_class] && det[j].thresh>valid_detection){
        det_idx         = j;
        valid_detection = det[j].thresh;
      }

      // for computing pr curve values, the candidate with the greatest overlap is considered
      // if the greatest overlap is an ignored detection, the overlapping detection is used
      else if(compute_fp && overlap>MIN_OVERLAP[metric][current_class] && (overlap>max_overlap || assigned_ignored_det) && ignored_det[j]==0){
        max_overlap     = overlap;
        det_idx         = j;
        valid_detection = 1;
        assigned_ignored_det = false;
      }
      else if(compute_fp && overlap>MIN_OVERLAP[metric][current_class] && valid_detection==NO_DETECTION && ignored_det[j]==1){
        det_idx              = j;
        valid_detection      = 1;
        assigned_ignored_det = true;
      }
    }

    /*=======================================================================
    compute TP, FP and FN
    =======================================================================*/

    // nothing was assigned to this valid ground truth
    if(valid_detection==NO_DETECTION && ignored_gt[i]==0) {
      stat.fn++;
    }

    // only evaluate valid ground truth <=> detection assignments
    else if(valid_detection!=NO_DETECTION && (ignored_gt[i]==1 || ignored_det[det_idx]==1))
      assigned_detection[det_idx] = true;

    // found a valid true positive
    else if(valid_detection!=NO_DETECTION){

      // write highest score to threshold vector
      stat.tp++;
      stat.v.push_back(det[det_idx].thresh);

      // compute angular difference of detection and ground truth if valid detection orientation was provided
      if(compute_aos)
        delta.push_back(gt[i].box.alpha - det[det_idx].box.alpha);

      // clean up
      assigned_detection[det_idx] = true;
    }
  }

  // if FP are requested, consider stuff area
  if(compute_fp){

    // count fp
    for(int32_t i=0; i<det.size(); i++){

      // count false positives if required (height smaller than required is ignored (ignored_det==1)
      if(!(assigned_detection[i] || ignored_det[i]==-1 || ignored_det[i]==1 || ignored_threshold[i]))
        stat.fp++;
    }

    // do not consider detections overlapping with stuff area
    int32_t nstuff = 0;
    for(int32_t i=0; i<dc.size(); i++){
      for(int32_t j=0; j<det.size(); j++){

        // detections not of the current class, already assigned, with a low threshold or a low minimum height are ignored
        if(assigned_detection[j])
          continue;
        if(ignored_det[j]==-1 || ignored_det[j]==1)
          continue;
        if(ignored_threshold[j])
          continue;

        // compute overlap and assign to stuff area, if overlap exceeds class specific value
        double overlap = boxoverlap(det[j], dc[i], 0);
        if(overlap>MIN_OVERLAP[metric][current_class]){
          assigned_detection[j] = true;
          nstuff++;
        }
      }
    }

    // FP = no. of all not to ground truth assigned detections - detections assigned to stuff areas
    stat.fp -= nstuff;

    // if all orientation values are valid, the AOS is computed
    if(compute_aos){
      vector<double> tmp;

      // FP have a similarity of 0, for all TP compute AOS
      tmp.assign(stat.fp, 0);
      for(int32_t i=0; i<delta.size(); i++)
        tmp.push_back((1.0+cos(delta[i]))/2.0);

      // be sure, that all orientation deltas are computed
      assert(tmp.size()==stat.fp+stat.tp);
      assert(delta.size()==stat.tp);

      // get the mean orientation similarity for this image
      if(stat.tp>0 || stat.fp>0)
        stat.similarity = accumulate(tmp.begin(), tmp.end(), 0.0);

      // there was neither a FP nor a TP, so the similarity is ignored in the evaluation
      else
        stat.similarity = -1;
    }
  }
  return stat;
}

/*=======================================================================
EVALUATE CLASS-WISE
=======================================================================*/

bool eval_class(CLASSES current_class,
        const vector< vector<tGroundtruth> > &groundtruth,
        const vector< vector<tDetection> > &detections, bool compute_aos,
        double (*boxoverlap)(tDetection, tGroundtruth, int32_t),
        vector<double> &precision,
        METRIC metric, DIFFICULTY difficulty, bool depth) {
  assert(groundtruth.size() == detections.size());

  // init
  int32_t n_gt=0;                                     // total no. of gt (denominator of recall)
  vector<double> v, thresholds;                       // detection scores, evaluated for recall discretization
  vector< vector<int32_t> > ignored_gt, ignored_det;  // index of ignored gt detection for current class
  vector< vector<tGroundtruth> > dontcare;            // index of dontcare areas, included in ground truth

  // for all test images do
  for (int32_t i=0; i<groundtruth.size(); i++){

    // holds ignored ground truth, ignored detections and dontcare areas for current frame
    vector<int32_t> i_gt, i_det;
    vector<tGroundtruth> dc;

    // only evaluate objects of current class and ignore occluded, truncated objects
    cleanData(current_class, groundtruth[i], detections[i], i_gt, dc, i_det, n_gt, difficulty, depth);
    ignored_gt.push_back(i_gt);
    ignored_det.push_back(i_det);
    dontcare.push_back(dc);

    // compute statistics to get recall values
    tPrData pr_tmp = tPrData();
    pr_tmp = computeStatistics(current_class, groundtruth[i], detections[i], dc, i_gt, i_det, false, boxoverlap, metric);

    // add detection scores to vector over all images
    for(int32_t j=0; j<pr_tmp.v.size(); j++)
      v.push_back(pr_tmp.v[j]);
  }

  // get scores that must be evaluated for recall discretization
  thresholds = getThresholds(v, n_gt);

  // compute TP,FP,FN for relevant scores
  vector<tPrData> pr;
  pr.assign(thresholds.size(),tPrData());
  for (int32_t i=0; i<groundtruth.size(); i++){

    // for all scores/recall thresholds do:
    for(int32_t t=0; t<thresholds.size(); t++){
      tPrData tmp = tPrData();
      tmp = computeStatistics(current_class, groundtruth[i], detections[i], dontcare[i],
                              ignored_gt[i], ignored_det[i], true, boxoverlap, metric,
                              compute_aos, thresholds[t], t==38);

      // add no. of TP, FP, FN, AOS for current frame to total evaluation for current threshold
      pr[t].tp += tmp.tp;
      pr[t].fp += tmp.fp;
      pr[t].fn += tmp.fn;
      if(tmp.similarity!=-1)
        pr[t].similarity += tmp.similarity;
    }
  }

  // compute recall, precision and AOS
  precision.assign(N_SAMPLE_PTS, 0);
  double r=0;
  for (int32_t i=0; i<thresholds.size(); i++){
    r = pr[i].tp/(double)(pr[i].tp + pr[i].fn);
    precision[i] = pr[i].tp/(double)(pr[i].tp + pr[i].fp);
  }

  // filter precision and AOS using max_{i..end}(precision)
  for (int32_t i=0; i<thresholds.size(); i++){
    precision[i] = *max_element(precision.begin()+i, precision.end());
  }

  return true;
}

void write_result(ofstream& outfile, string exp_name, vector<double> &precisions) {
  double ap = accumulate(precisions.begin() + 1, precisions.end(), 0.0) / (N_SAMPLE_PTS - 1);
  outfile << exp_name << "," << ap ;
  for (const double& prec : precisions) {
    outfile << ',' << prec;
  }
  outfile << endl;
}

void eval(string gt_dir, string result_dir, int c, bool depth, ofstream& outfile) {

  vector<vector<tGroundtruth>> groundtruths;
  vector<vector<tDetection>> detections;
  map<string, vector<vector<tGroundtruth>>> groundtruths_perseq;
  map<string, vector<vector<tDetection>>> detections_perseq;

  cout << "Loading data" << endl;
  string path;
  vector<string> sequences = list_dir(gt_dir);
  for (const auto& sequence : sequences) {
    vector<string> frames = list_dir(gt_dir + '/' + sequence);
    vector<vector<tGroundtruth>> groundtruths_seq;
    vector<vector<tDetection>> detections_seq;
    for (const auto& frame : frames) {
      string gt_path = gt_dir + '/' + sequence + '/' + frame;
      string result_path = "";
      if (depth) {
        result_path = result_dir + '/' + sequence + '/' + frame;
      } else {
        result_path = result_dir + '/' + sequence + "/image_stitched/" + frame;
      }
      vector<tGroundtruth> gt = loadGroundtruth(gt_path);
      vector<tDetection> det = loadDetection(result_path);
      groundtruths.push_back(gt);
      detections.push_back(det);
      groundtruths_seq.push_back(gt);
      detections_seq.push_back(det);
    }
    groundtruths_perseq[sequence] = groundtruths_seq;
    detections_perseq[sequence] = detections_seq;
  }
  // if (groundtruths.size() != N_TESTIMAGES)
  //   throw invalid_argument("Mismatch in number of ground truth files.");

  cout << "Loaded data" << endl;

  CLASSES cls = (CLASSES)c;

  // eval image 2D bounding boxes
  if (!depth) {
    cout << "Starting 2D evaluation (" << CLASS_NAMES[c].c_str() << ") ..." << endl;
    vector<double> precision_2d_hard;
    if (!eval_class(cls, groundtruths, detections, false, imageBoxOverlap, precision_2d_hard, IMAGE, HARD, depth)) {
      cout << CLASS_NAMES[c].c_str() << " evaluation failed." << endl;
    } else {
      write_result(outfile, "overall", precision_2d_hard);
    }
    for (auto const& groundtruths_seq : groundtruths_perseq) {
      cout << "Starting per-sequence 2D evaluation (" << groundtruths_seq.first << ", " << CLASS_NAMES[c].c_str() << ") ..." << endl;
      vector<double> precision_2d_seq;
      if (!eval_class(cls, groundtruths_seq.second, detections_perseq[groundtruths_seq.first], false, imageBoxOverlap, precision_2d_seq, IMAGE, HARD, depth)) {
        cout << CLASS_NAMES[c].c_str() << " evaluation failed." << endl;
      } else {
        write_result(outfile, groundtruths_seq.first, precision_2d_seq);
      }
    }
  } else {
    cout << "Starting 3D evaluation (" << CLASS_NAMES[c].c_str() << ") ..." << endl;
    vector<double> precision_3d_hard;
    if (!eval_class(cls, groundtruths, detections, false, box3DOverlap, precision_3d_hard, BOX3D, HARD, depth)) {
      cout << CLASS_NAMES[c].c_str() << " evaluation failed." << endl;
    } else {
      write_result(outfile, "overall", precision_3d_hard);
    }
    for (auto const& groundtruths_seq : groundtruths_perseq) {
      cout << "Starting per-sequence 3D evaluation (" << groundtruths_seq.first << ", " << CLASS_NAMES[c].c_str() << ") ..." << endl;
      vector<double> precision_3d_seq;
      if (!eval_class(cls, groundtruths_seq.second, detections_perseq[groundtruths_seq.first], false, box3DOverlap, precision_3d_seq, BOX3D, HARD, depth)) {
        cout << CLASS_NAMES[c].c_str() << " evaluation failed." << endl;
      } else {
        write_result(outfile, groundtruths_seq.first, precision_3d_seq);
      }
    }
  }
}

int32_t main (int32_t argc, char *argv[]) {
  if (argc != 5) {
    cout << "Usage: ./eval_detection gt_dir result_dir eval_type save_path" << endl;
    return 1;
  }
  initGlobals();

  vector<vector<tGroundtruth>> groundtruths;
  vector<vector<tDetection>> detections;

  bool depth = strcmp(argv[3], "0") != 0;

  // run evaluation
  ofstream outfile;
  outfile.open(argv[4]);

  eval(argv[1], argv[2], 1, depth, outfile);
  cout << "Finished evaluating" << endl;

  outfile.close();
  cout << "Saved metrics to " << argv[4] << endl;

  return 0;
}


/*******************************************************************************
*
*  Filename    : RooFit_CompareBackground.cc
*  Description : Control flow of background function comparison fitting and plotting
*  Author      : Yi-Mu "Enoch" Chen [ ensc@hep1.phys.ntu.edu.tw ]
*
*******************************************************************************/
#include "TstarAnalysis/Common/interface/PlotStyle.hpp"
#include "TstarAnalysis/LimitCalc/interface/Common.hpp"
#include "TstarAnalysis/LimitCalc/interface/SampleRooFitMgr.hpp"

#include "ManagerUtils/Maths/interface/RooFitExt.hpp"
#include "ManagerUtils/PlotUtils/interface/Common.hpp"
#include "ManagerUtils/PlotUtils/interface/RooFitUtils.hpp"

#include "RooDataSet.h"
#include "RooFitResult.h"
#include "RooMinimizer.h"
#include "RooNLLVar.h"
#include "TMath.h"

#include <boost/format.hpp>
#include <vector>

using namespace std;

static Color_t sequence[] = {
  KBLUE,
  KGREEN,
  KRED,
  KPURPLE
};

struct MyFitResult
{
  RooFitResult*      fitresult;
  TGraph*            fitplot;
  TGraphAsymmErrors* pullplot;
  double             ksvalue;
};

/******************************************************************************/

void
CompareFitFunc( SampleRooFitMgr* mgr )
{
  TCanvas* c        = mgr::NewCanvas();
  TPad* toppad      = mgr::NewTopPad();
  TPad* botpad      = mgr::NewBottomPad();
  RooPlot* frame    = SampleRooFitMgr::x().frame();
  const double xmin = SampleRooFitMgr::x().getMin();
  const double xmax = SampleRooFitMgr::x().getMax();

  vector<MyFitResult> fitlist;
  const vector<string> funclist = limnamer.GetInputList<string>( "fitfunc" );

  /*******************************************************************************
  *   Making the top plots
  *******************************************************************************/
  toppad->Draw();
  toppad->cd();

  TGraphAsymmErrors* setplot = (TGraphAsymmErrors*)mgr::PlotOn(
    frame, mgr->DataSet( "" ),
    RooFit::DrawOption( PGS_DATA )
    );

  vector<double> prevfitvaluelist = {280, 0.51};

  for( const auto& fitfunc : funclist ){
    RooAbsPdf* pdf  = mgr->NewPdf( fitfunc, fitfunc );
    RooDataSet* set = (RooDataSet*)( mgr->DataSet( "" ) );

    // Setting up fitting parameters with previous fit results for faster convertions
    vector<RooRealVar*> fitparams = mgr->VarContains( fitfunc );

    for( size_t i = 0; i < fitparams.size(); ++i ){
      if( i < prevfitvaluelist.size() ){
        *( fitparams.at( i ) ) = prevfitvaluelist.at( i );
      } else {
        *( fitparams.at( i ) ) = 0;
      }
    }

    // Manually calling NNL minizer functions
    static RooCmdArg min    = RooFit::Minimizer( "Minuit", "Migrad" );
    static RooCmdArg sumerr = RooFit::SumW2Error( kTRUE );
    static RooCmdArg minos  = RooFit::Minos( kTRUE );
    static RooCmdArg save   = RooFit::Save();
    // static RooCmdArg ncpu   = RooFit::NumCPU( 6 );
    static RooCmdArg verb   = RooFit::Verbose( kFALSE );
    static RooCmdArg printl = RooFit::PrintLevel( -1 );
    static RooCmdArg printe = RooFit::PrintEvalErrors( -1 );
    static RooCmdArg printw = RooFit::Warnings( kFALSE );

    RooLinkedList fitopt;
    fitopt.Add( &min    );
    fitopt.Add( &sumerr );
    fitopt.Add( &minos  );
    fitopt.Add( &save   );
    // fitopt.Add( &ncpu   );
    fitopt.Add( &verb   );
    fitopt.Add( &printl );
    fitopt.Add( &printe );
    fitopt.Add( &printw );

    RooFitResult* ans = NULL;
    const unsigned maxiter = 50;
    unsigned iter = 0;
    while( !ans ){
      ans = pdf->fitTo( *set, fitopt );
      if( ans->status() ){// Not properly converged
        ++iter;
        if( iter > maxiter ) { break; }
        delete ans;
        ans = NULL;
      }
    }

    MyFitResult x;

    // Saving fit results
    x.fitresult = ans;
    x.fitplot   = mgr::PlotOn( frame, pdf );
    x.ksvalue   = KSTest( *set, *pdf, SampleRooFitMgr::x() );
    x.pullplot  = mgr::DividedGraph( setplot, x.fitplot );

    // Small information dump after fit
    prevfitvaluelist.clear();
    cout << "Minimum NLL:" << x.fitresult->minNll() << endl;
    cout << "Parameter fit values: " << endl;

    for( int i = 0; i < x.fitresult->floatParsFinal().getSize(); ++i ){
      RooRealVar* var = (RooRealVar*)( x.fitresult->floatParsFinal().at( i ) );
      cout << var->GetName() << " " << var->getVal() << " " << var->getError() << endl;
      prevfitvaluelist.push_back( var->getVal() );
    }

    cout << "===" << endl;

    fitlist.push_back( x );
  }

  frame->Draw();
  frame->SetMinimum( 0.3 );
  mgr::SetTopPlotAxis( frame );
  frame->SetTitle( "" );
  c->cd();

  /*******************************************************************************
  *   Drawing bottom pad.
  *******************************************************************************/
  botpad->Draw();
  botpad->cd();

  TMultiGraph* mg = new TMultiGraph;

  for( const auto fitresult : fitlist ){
    mg->Add( fitresult.pullplot, "Z" );
  }

  TLine line( xmin, 1, xmax, 1 );
  TLine line_top( xmin, 1.5, xmax, 1.5 );
  TLine line_bot( xmin, 0.5, xmax, 0.5 );

  mg->Draw( "AZ" );
  line.Draw( "SAME" );
  line_top.Draw( "SAME" );
  line_bot.Draw( "SAME" );
  mg->GetXaxis()->SetTitle( frame->GetXaxis()->GetTitle() );
  mg->GetYaxis()->SetTitle( "Data/Fit" );
  mg->GetXaxis()->SetRangeUser( xmin, xmax );
  mgr::SetBottomPlotAxis( mg );
  mg->SetMaximum( 1.6 );
  mg->SetMinimum( 0.4 );
  c->cd();

  /*******************************************************************************
  *   Object styling
  *******************************************************************************/
  tstar::SetDataStyle( setplot );

  line.SetLineColor( KRED );
  line.SetLineStyle( 1 );
  line_top.SetLineColor( kBlack );
  line_bot.SetLineColor( kBlack );
  line_top.SetLineStyle( 3 );
  line_bot.SetLineStyle( 3 );

  // Drawing common
  mgr::DrawCMSLabel();
  mgr::DrawLuminosity( mgr::SampleMgr::TotalLuminosity() );

  // Drawing legends
  TLegend* l = mgr::NewLegend( 0.45, 0.6 );
  if( limnamer.GetInput<string>( "sample" ) == "Data" ){
    l->AddEntry( setplot, "Data", "lp" );
  } else {
    l->AddEntry( setplot, "MC background", "lp" );
  }

  for( size_t i = 0; i < fitlist.size(); ++i ){
    auto fitplot  = fitlist.at( i ).fitplot;
    auto pullplot = fitlist.at( i ).pullplot;
    fitplot->SetLineColor( sequence[i] );
    pullplot->SetLineColor( sequence[i] );
    fitplot->SetLineWidth( 2 );
    pullplot->SetLineWidth( 2 );
    auto funcname = funclist.at( i );

    boost::format fitentryfmt( "%s K=%.3lf" );
    const string fitentry = boost::str(
      fitentryfmt
      % limnamer.ExtQuery<string>( "fitfunc", funcname, "Full Name" )
      % fitlist.at( i ).ksvalue
      );
    l->AddEntry( fitplot, fitentry.c_str(), "l" );
  }

  l->Draw();

  mgr::LatexMgr latex;
  latex.SetOrigin( PLOT_X_TEXT_MIN, PLOT_Y_TEXT_MAX, TOP_LEFT )
  .WriteLine( limnamer.GetChannelEXT( "Root Name" ) );

  boost::format chi2testfmt( "LR Test_{%d/%d} = %.3lf" );

  // Writing LR test results
  for( size_t i = 0; i+1 < fitlist.size(); ++i ){
    const double thisnll  = fitlist[i].fitresult->minNll();
    const double nextnll  = fitlist[i+1].fitresult->minNll();
    const int thisorder   = fitlist[i].fitresult->floatParsFinal().getSize();
    const int nextorder   = fitlist[i+1].fitresult->floatParsFinal().getSize();
    const double chi2test = TMath::Prob( 2*( std::max( fabs( thisnll-nextnll ), 0. ) ), nextorder-thisorder );

    latex.WriteLine( boost::str( chi2testfmt % thisorder % nextorder % chi2test ) );
  }

  /*******************************************************************************
  *   Updating everything and saving
  *******************************************************************************/
  toppad->Update();
  botpad->Update();
  c->Update();

  frame->SetMaximum( mgr::GetYmax( setplot )*1.5 );
  mgr::SaveToPDF( c, limnamer.PlotFileName( "bkgcomp" ) );
  mgr::SaveToROOT( c, limnamer.PlotRootFile(), limnamer.PlotFileName( "bkgcomp" ) );
  toppad->SetLogy( kTRUE );
  frame->SetMaximum( mgr::GetYmax( setplot )*300 );
  mgr::SaveToPDF( c, limnamer.PlotFileName( "bkgcomp", "log" ) );

  /*******************************************************************************
  *   Printing results and leaving
  *******************************************************************************/
  for( auto fit : fitlist ){
    cout << boost::str( boost::format( "%.6lf" ) % fit.fitresult->minNll() ) << endl;

    for( int i = 0; i < fit.fitresult->floatParsFinal().getSize(); ++i ){
      RooRealVar* var = (RooRealVar*)( fit.fitresult->floatParsFinal().at( i ) );
      cout << var->GetName() << " " << var->getVal() << " " << var->getError() << endl;
    }

    cout << "===" << endl;
  }

  delete toppad;
  delete botpad;
  delete c;
  delete frame;
}