/**
 * @example waveq3d/test/proploss_test.cc
 */
#include <boost/test/unit_test.hpp>
#include <usml/waveq3d/waveq3d.h>
#include <usml/netcdf/netcdf_files.h>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <time.h>

BOOST_AUTO_TEST_SUITE(proploss_test)

using namespace boost::unit_test;
using namespace usml::waveq3d;

/**
 * @ingroup waveq3d_test
 * @{
 */

static const double time_step = 0.100 ;
static const double f0 = 2000 ;
static const double bot_depth = 1e5 ;

/**
 * This test demonstrates that the  basic functions of the spreading loss model
 * are working correctly.  It computes propagation loss in a spherical spreading
 * in an isovelocity ocean with no boundary interaction.
 *
 *      - Source:       15000 meters deep
 *      - Target:       15000 meters deep, latitude change is 0.01-18.01 deg
 *      - Frequency:    10, 100, 1000, & 10000 Hz
 *      - Sound Speed:  1500 m/s
 *      - Time Step:    100 msec
 *	- Source D/E:	-10 deg to 10 deg in 1 deg increments
 *	- Source AZ:	-10 deg to 10 deg in 2 deg increments
 *
 * BOOST errors are automatically generated if the modeled eigenrays
 * deviate from the 20*log10(R) model by more than 0.2 dB.
 *
 * The eigenrays for all targets are written to the proploss_basic.csv
 * spreadsheet file.  A comparison of the model vs. theory is printed to
 * standard output.
 */
BOOST_AUTO_TEST_CASE(proploss_basic)
{
    cout << "=== proploss_test: proploss_basic ===" << endl;
    const char* csvname = USML_TEST_DIR "/waveq3d/test/proploss_basic.csv";
    const double c0 = 1500.0;
    const double src_lat = 45.0;
    const double src_lng = -45.0;
    const double src_alt = -15000.0;
    const double time_max = 8.0;

    // initialize propagation model

    wposition::compute_earth_radius(src_lat);
    attenuation_model* attn = new attenuation_constant(0.0);
    profile_model* profile = new profile_linear(c0, attn);
    boundary_model* surface = new boundary_flat();
    boundary_model* bottom = new boundary_flat(bot_depth);
    ocean_model ocean(surface, bottom, profile);

    seq_log freq(10.0, 10.0, 4);
    cout << "frequencies: " << freq << endl;
    wposition1 pos(src_lat, src_lng, src_alt);
    seq_linear de(-10.7, 1.0, 10.0);
    seq_linear az(-10.5, 2.0, 10.0);

    // build a series of targets at different ranges

    wposition target(10, 1, src_lat, src_lng, src_alt);
    for (size_t n = 0; n < target.size1(); ++n)
    {
        target.latitude(n, 0, src_lat + 0.01 * (n + 2.0));
    }

    eigenray_collection loss(freq, pos, de, az, time_step, &target);
    wave_queue wave( ocean, freq, pos, de, az, time_step, &target, 0, wave_queue::CLASSIC_RAY ) ;
    wave.add_eigenray_listener(&loss);

    // propagate rays & record to log file

    cout << "propagate wavefronts" << endl;
    while (wave.time() < time_max)
    {
        wave.step();
    }
    loss.sum_eigenrays();

    // save results to spreadsheet and compare to analytic results

    std::ofstream os(csvname);
    os << "target,time,intensity,phase,src de,src az,trg de,trg az,surf,bot" << endl;
    os << std::setprecision(18);

    cout << "writing spreadsheets to " << csvname << endl;
    for (size_t n = 0; n < target.size1(); ++n)
    {
        const eigenray_list *raylist = loss.eigenrays(n, 0);
        for (eigenray_list::const_iterator iter = raylist->begin();
                iter != raylist->end(); ++n, ++iter)
        {
            const eigenray &ray = *iter;
            os << n
            << "," << ray.time
            << "," << ray.intensity(0)
            << "," << ray.phase(0)
            << "," << ray.source_de
            << "," << ray.source_az
            << "," << ray.target_de
            << "," << ray.target_az
            << "," << ray.surface
            << "," << ray.bottom
            << endl;
            double range = c0 * ray.time;
            double pl = 20.0 * log10(range - 2.0);
            cout << "range=" << range
                 << " theory=" << pl
                 << " model=" << ray.intensity << endl;
            for (size_t f = 0; f < freq.size(); ++f)
            {
                BOOST_CHECK(fabs(ray.intensity(f) - pl) < 0.2);
            }
        }
    }
}

/**
 * This test demonstrates that the frequency based accuracy of the model.
 * - Scenario parameters
 *   - Profile: constant 1500 m/s sound speed, no absorption
 *   - Bottom: 3000 meters
 *   - Source: 45N, 45W, -1000 meters
 *   - Target: 45.02N, 45W, -1000 meters
 *   - Time Step: 100 msec
 *   - Launch D/E: 1 degree linear spacing from -60 to 60 degrees
 *
 * - Analytic Results
 *   - Direct Path: 1.484018789 sec, -0.01 deg launch, 66.95 dB
 *   - Surface Bounce: 1.995102731 sec, 41.93623171 deg launch, 69.52 dB
 *   - Bottom Bounce: 3.051676949 sec, -60.91257162 deg launch, 73.21 dB
 */
BOOST_AUTO_TEST_CASE( proploss_freq )
{
    cout << "=== proploss_test: proploss_freq ===" << endl ;
    const char* ncwave = USML_TEST_DIR "/waveq3d/test/proploss_freq_wave.nc" ;
    const char* ncproploss = USML_TEST_DIR "/waveq3d/test/proploss_freq_proploss.nc" ;
    const char* csvname = USML_TEST_DIR "/waveq3d/test/proploss_freq.csv";
    const double c0 = 1500.0 ;
    const double src_lat = 45.0 ;
    const double src_lng = -45.0 ;
    const double src_alt = -1000.0 ;
    const double trg_lat = 45.02 ;
    const double time_max = 3.5 ;

    // initialize propagation model

    wposition::compute_earth_radius( src_lat ) ;
    attenuation_model* attn = new attenuation_constant(0.0) ;
    profile_model* profile = new profile_linear(c0,attn) ;
    boundary_model* surface = new boundary_flat() ;
    boundary_model* bottom = new boundary_flat(3000.0) ;
    ocean_model ocean( surface, bottom, profile ) ;

    seq_linear freq( 10.0, 10.0, 1000 ) ;
    wposition1 pos( src_lat, src_lng, src_alt ) ;
    seq_linear de( -90.0, 1.0, 90.0 ) ;
    seq_linear az( -4.0, 1.0, 4.0 ) ;

    // build a single target

    wposition target( 1, 1, trg_lat, src_lng, src_alt );

    eigenray_collection loss(freq, pos, de, az, time_step, &target);
    wave_queue wave( ocean, freq, pos, de, az, time_step, &target) ;
    wave.add_eigenray_listener(&loss) ;

    // propagate rays & record to log file

    cout << "propagate wavefront for " << time_max << endl ;
    cout << "writing wavefronts to " << ncwave << endl ;

    wave.init_netcdf( ncwave ) ;
    wave.save_netcdf() ;
    while (wave.time() < time_max)
    {
        wave.step() ;
        wave.save_netcdf() ;
    }
    wave.close_netcdf() ;

    loss.sum_eigenrays() ;
    cout << "writing eigenray_collection to " << ncproploss << endl ;
    loss.write_netcdf(ncproploss,"proploss_freq test") ;

    // save results to spreadsheet and compare to analytic results

    cout << "writing data to " << csvname << endl ;
    std::ofstream os(csvname) ;
    os << "frequency,theory_direct,model_direct,theory_surface,model_surface,theory_bottom,theory_surface" << endl ;
    os << std::setprecision(18) ;

    const eigenray_list *raylist = loss.eigenrays(0, 0);
    eigenray direct_ray, surface_ray, bottom_ray ;
    for (eigenray_list::const_iterator iter = raylist->begin();
            iter != raylist->end(); ++iter)
    {
        if( iter->bottom == 1 && iter->surface == 0 ) bottom_ray = *iter ;
        else if( iter->bottom == 0 && iter->surface == 1 ) surface_ray = *iter ;
        else direct_ray = *iter ;
    }
    for(size_t j=0; j<freq.size(); ++j) {
        os << freq(j)
           << "," << 66.95
           << "," << direct_ray.intensity(j)
           << "," << 69.52
           << "," << surface_ray.intensity(j)
           << "," << 73.21
           << "," << bottom_ray.intensity(j)
           << endl ;
    }
}

/**
 * Compares modeled propagation loss as a function of range to the Lloyd's
 * mirror analytic expression for surface reflection in an isovelocity ocean.
 *
 *      - Source:       25 meters deep
 *      - Target:       200 meters deep, range is 200-10,000 m
 *      - Frequency:    2000 Hz
 *      - Sound Speed:  1500 m/s
 *      - Time Step:    50 msec
 *	- Source D/E:	-90 deg to 90 deg, 181 rays with tangental spacing
 *	- Source AZ:	-2 deg to 2 deg in 1 deg increments
 *
 * The Lloyd's analytic analytic result is:
 * \f[
 *      p(r,z) = \frac{ e^{ikR_1} }{ R_1 } - \frac{ e^{ikR_2} }{ R_2 }
 * \f]\f[
 *      R_1 = \sqrt{ r^2 + ( z - z_s )^2 }
 * \f]\f[
 *      R_2 = \sqrt{ r^2 + ( z + z_s )^2 }
 * \f]\f[
 *      PL = -10 log_{10}( |p(r,z)|^2 )
 * \f]\f[
 *      \theta = arg( p(r,z) )
 * \f]
 * where:
 *      - \f$ r         \f$ = target range
 *      - \f$ z         \f$ = target depth
 *      - \f$ z_s       \f$ = source depth
 *      - \f$ R_1       \f$ = slant range to source
 *      - \f$ R_2       \f$ = slant range to image of source above water
 *      - \f$ k         \f$ = acoustic wave number = \f$ 2 \pi f / c \f$
 *      - \f$ p(r,z)    \f$ = complex pressure
 *      - \f$ PL        \f$ = propagation loss in dB.
 *      - \f$ \theta    \f$ = phase in radians
 *
 * This test computes three statistics to measure the difference between
 * the model and the analytic solution.
 *
 * - "bias" is the mean difference and it measures offsets in level.
 * - "dev" is an estimate of the sqrt of the variance and it is a measure of
 *   the difference in flucuations of between the models.
 * - "detcoef" is the coefficient of determination and its measure of the
 *   fraction of the model that predicts the analytic solution.
 *
 * An automatic error is thrown if abs(bias) > 0.5 dB, dev > 4 db, or
 * detcoef < 80%.
 *
 * @xref F.B. Jensen, W.A. Kuperman, M.B. Porter, H. Schmidt,
 * "Computational Ocean Acoustics", pp. 16-19.
 */
BOOST_AUTO_TEST_CASE(proploss_lloyds_range)
{
    cout << "=== proploss_test: proploss_lloyds_range ===" << endl;
    const char* csvname = USML_TEST_DIR "/waveq3d/test/proploss_lloyds_range.csv";
    const char* ncname = USML_TEST_DIR "/waveq3d/test/proploss_lloyds_range.nc";
    const char* ncname_wave = USML_TEST_DIR "/waveq3d/test/proploss_lloyds_range_wave.nc";

    const double c0 = 1500.0;
    const double src_lat = 45.0;
    const double src_lng = -45.0;
    const double src_alt = -25.0;
    const double trg_alt = -200.0;
    const double time_max = 8.0;

    // initialize propagation model

    wposition::compute_earth_radius(src_lat);
    attenuation_model* attn = new attenuation_constant(0.0);
    profile_model* profile = new profile_linear(c0, attn);
    boundary_model* surface = new boundary_flat();
    boundary_model* bottom = new boundary_flat(bot_depth);
    ocean_model ocean(surface, bottom, profile);
    profile->flat_earth(true);

    seq_log freq(f0, 1.0, 1);  // 2000 Hz
    const double wavenum = TWO_PI * freq(0) / c0;

    wposition1 pos(src_lat, src_lng, src_alt);
    seq_rayfan de ;
    seq_linear az( -4.0, 1.0, 4.0 );

    // build a series of targets at different ranges

    seq_linear range(200.0, 10.0, 10e3); // range in meters
    wposition target(range.size(), 1, src_lat, src_lng, trg_alt);
    for (size_t n = 0; n < target.size1(); ++n)
    {
        double degrees = src_lat + range(n) / (1852.0 * 60.0); // range in latitude
        target.latitude(n, 0, degrees);
    }

    eigenray_collection loss(freq, pos, de, az, time_step, &target);
    wave_queue wave( ocean, freq, pos, de, az, time_step, &target) ;
    wave.add_eigenray_listener(&loss);

    // propagate rays & record to log file

    cout << "propagate wavefronts" << endl;
    cout << "writing wavefronts to " << ncname_wave << endl;
    wave.init_netcdf(ncname_wave);  // open a log file for wavefront data
    wave.save_netcdf();             // write ray data to log file
    while (wave.time() < time_max)
    {
        wave.step();
        wave.save_netcdf();         // write ray data to log file
    }
    wave.close_netcdf();            // close log file for wavefront data
    cout << "writing eigenrays to " << ncname << endl;
    loss.sum_eigenrays();
    loss.write_netcdf(ncname);

    // save results to spreadsheet and compare to analytic results

    cout << "writing spreadsheets to " << csvname << endl;
    std::ofstream os(csvname);
    os << "range,model,theory,m1amp,m1time,t1amp,t1time,m2amp,m2time,t2amp,t2time" << endl;
    os << std::setprecision(18);

    vector<double> tl_model(range.size());
    vector<double> tl_analytic(range.size());
    double mean_model = 0.0;
    double mean_analytic = 0.0;

    const double z1 = trg_alt - src_alt;
    const double z2 = trg_alt + src_alt;

    for (size_t n = 0; n < range.size(); ++n)
    {
        tl_model[n] = -loss.total(n, 0)->intensity(0);

        // compute analytic solution

        const double R1 = sqrt(range(n) * range(n) + z1 * z1);
        const double R2 = sqrt(range(n) * range(n) + z2 * z2);
        complex<double> p1(0.0, wavenum * R1);
        complex<double> p2(0.0, wavenum * R2);
        p1 = exp(p1) / R1;
        p2 = -exp(p2) / R2;
        tl_analytic[n] = 10.0 * log10(norm(p1 + p2));

        // print to log file

        eigenray_list::const_iterator iter = loss.eigenrays(n, 0)->begin();
        if (iter != loss.eigenrays(n, 0)->end())
        {
            os << range(n)
            << "," << tl_model[n]
            << "," << tl_analytic[n]
            << "," << -(*iter).intensity(0)
            << "," << (*iter).time
            << "," << 10.0 * log10(norm(p1))
            << "," << R1 / c0;
            ++iter;
            if (iter != loss.eigenrays(n, 0)->end())
            {
                os << "," << -(*iter).intensity(0)
                << "," << (*iter).time
                << "," << 10.0 * log10(norm(p2))
                << "," << R2 / c0;
            }
        }
        os << endl;
        mean_model += tl_model[n];
        mean_analytic += tl_analytic[n];
    }
    mean_model /= range.size();
    mean_analytic /= range.size();

    // compute statistics of difference between curves

    double bias = 0.0;
    double dev = 0.0;
    double Sxx = 0.0;
    double Syy = 0.0;
    double Sxy = 0.0;
    for (size_t n = 0; n < range.size(); ++n)
    {
        const double diff = (tl_model[n] - tl_analytic[n]);
        bias += diff ;
        dev += (diff * diff);
        const double diff_analytic = (tl_analytic[n] - mean_analytic);
        Sxx += (diff_analytic * diff_analytic);
        const double diff_model = (tl_model[n] - mean_model);
        Syy += (diff_model * diff_model);
        Sxy += (diff_analytic * diff_model);
    }
    bias /= range.size() ;
    dev = sqrt( dev / range.size() - bias*bias ) ;
    double detcoef = Sxy * Sxy / (Sxx * Syy) * 100.0;

    cout << std::setprecision(4);
    cout << "bias = " << bias << " dB"
         << " dev = " << dev << " dB"
         << " detcoef = " << detcoef << "%" << endl ;

    BOOST_CHECK( abs(bias) <= 0.5 );
    BOOST_CHECK( dev <= 4.0 );
    BOOST_CHECK( detcoef >= 80.0 );
}

/**
 * Compares modeled propagation loss as a function of range and frequency to the Lloyd's
 * mirror analytic expression for surface reflection in an isovelocity ocean.
 *
 *      - Source:       25 meters deep
 *      - Target:       200 meters deep, range is 200-10,000 m
 *      - Frequency:    10-10000 Hz
 *      - Sound Speed:  1500 m/s
 *      - Time Step:    50 msec
 *	- Source D/E:	-90 deg to 90 deg, 181 rays with tangent spacing
 *	- Source AZ:	-2 deg to 2 deg in 1 deg increments
 *
 * The Lloyd's analytic analytic result is:
 * \f[
 *      p(r,z) = \frac{ e^{ikR_1} }{ R_1 } - \frac{ e^{ikR_2} }{ R_2 }
 * \f]\f[
 *      R_1 = \sqrt{ r^2 + ( z - z_s )^2 }
 * \f]\f[
 *      R_2 = \sqrt{ r^2 + ( z + z_s )^2 }
 * \f]\f[
 *      PL = -10 log_{10}( |p(r,z)|^2 )
 * \f]\f[
 *      \theta = arg( p(r,z) )
 * \f]
 * where:
 *      - \f$ r         \f$ = target range
 *      - \f$ z         \f$ = target depth
 *      - \f$ z_s       \f$ = source depth
 *      - \f$ R_1       \f$ = slant range to source
 *      - \f$ R_2       \f$ = slant range to image of source above water
 *      - \f$ k         \f$ = acoustic wave number = \f$ 2 \pi f / c \f$
 *      - \f$ p(r,z)    \f$ = complex pressure
 *      - \f$ PL        \f$ = propagation loss in dB.
 *      - \f$ \theta    \f$ = phase in radians
 *
 * This test computes three statistics to measure the difference between
 * the model and the analytic solution.
 *
 * - "bias" is the mean difference and it measures offsets in level.
 * - "dev" is an estimate of the sqrt of the variance and it is a measure of
 *   the difference in flucuations between the models.
 * - "detcoef" is the coefficient of determination and it measure of the
 *   fraction of the model that predicts the analytic solution.
 *
 * At 1000 Hz, an automatic error is thrown if abs(bias) > 1 dB, dev > 4 dB,
 * or detcoef < 90%. Extremely low and high freq can have a greater deviations.
 *
 * @xref F.B. Jensen, W.A. Kuperman, M.B. Porter, H. Schmidt,
 * "Computational Ocean Acoustics", pp. 16-19.
 */
BOOST_AUTO_TEST_CASE(proploss_lloyds_range_freq)
{
    cout << "=== proploss_test: proploss_lloyds_range_freq ===" << endl;

    const char* csvname = USML_TEST_DIR "/waveq3d/test/proploss_lloyds_range_freq.csv";
    const char* ncname = USML_TEST_DIR "/waveq3d/test/proploss_lloyds_range_freq.nc";
    const char* ncname_wave = USML_TEST_DIR "/waveq3d/test/proploss_lloyds_range_freq_wave.nc";

    const double c0 = 1500.0;
    const double src_lat = 45.0;
    const double src_lng = -45.0;
    const double src_alt = -25.0;
    const double trg_alt = -200.0;
    const double time_max = 8.0;

    // initialize propagation model

    wposition::compute_earth_radius(src_lat);
    attenuation_model* attn = new attenuation_constant(0.0);
    profile_model* profile = new profile_linear(c0, attn);
    boundary_model* surface = new boundary_flat();
    boundary_model* bottom = new boundary_flat(bot_depth);
    ocean_model ocean(surface, bottom, profile);
    profile->flat_earth(true);

    seq_log freq(10.0, 10.0, 4);
    cout << "frequencies: " << freq << endl;

    wposition1 pos(src_lat, src_lng, src_alt);
    seq_rayfan de ;
    seq_linear az( -4.0, 1.0, 4.0 );

    // build a series of targets at different ranges

    seq_linear range(200.0, 10.0, 10e3); // range in meters
    wposition target(range.size(), 1, src_lat, src_lng, trg_alt);
    for (size_t n = 0; n < target.size1(); ++n)
    {
        double degrees = src_lat + range(n) / (1852.0 * 60.0); // range in latitude
        target.latitude(n, 0, degrees);
    }

    eigenray_collection loss(freq, pos, de, az, time_step, &target);
    wave_queue wave( ocean, freq, pos, de, az, time_step, &target) ;
    wave.add_eigenray_listener(&loss);

    // propagate rays & record to log file

    cout << "propagate wavefronts" << endl;
    cout << "writing wavefronts to " << ncname_wave << endl;
    wave.init_netcdf(ncname_wave);  // open a log file for wavefront data
    wave.save_netcdf();             // write ray data to log file
    while (wave.time() < time_max)
    {
        wave.step();
        wave.save_netcdf();         // write ray data to log file
    }
    wave.close_netcdf();            // close log file for wavefront data
    cout << "writing eigenrays to " << ncname << endl;
    loss.sum_eigenrays();
    loss.write_netcdf(ncname);

    // save results to spreadsheet and compare to analytic results

    cout << "writing spreadsheets to " << csvname << endl;
    std::ofstream os(csvname);
    os << "freq,range,model,theory,m1amp,m1time,t1amp,t1time,m2amp,m2time,t2amp,t2time" << endl;
    os << std::setprecision(18);

    vector<double> tl_model(range.size());
    vector<double> tl_analytic(range.size());
    double mean_model = 0.0;
    double mean_analytic = 0.0;

    const double z1 = trg_alt - src_alt;
    const double z2 = trg_alt + src_alt;

    for (size_t f=0; f < freq.size(); ++f)
    {
        const double wavenum = TWO_PI * freq(f) / c0 ;

        for (size_t n = 0; n < range.size(); ++n)
        {
            os << freq(f) << "," ;
            tl_model[n] = -loss.total(n, 0)->intensity(f);

            // compute analytic solution

            const double R1 = sqrt(range(n) * range(n) + z1 * z1);
            const double R2 = sqrt(range(n) * range(n) + z2 * z2);
            complex<double> p1(0.0, wavenum * R1);
            complex<double> p2(0.0, wavenum * R2);
            p1 = exp(p1) / R1;
            p2 = -exp(p2) / R2;
            tl_analytic[n] = 10.0 * log10(norm(p1 + p2));

            // print to log file

            eigenray_list::const_iterator iter = loss.eigenrays(n, 0)->begin();
            if (iter != loss.eigenrays(n, 0)->end())
            {
                os << range(n)                     // range
                << "," << tl_model[n]              // model
                << "," << tl_analytic[n]           // theory
                << "," << -(*iter).intensity(f)    // m1amp
                << "," << (*iter).time             // m1time
                << "," << 10.0 * log10(norm(p1))   // t1amp
                << "," << R1 / c0;                 // t1time
                ++iter;
                if (iter != loss.eigenrays(n, 0)->end())
                {
                    os << "," << -(*iter).intensity(f)  // m2amp
                    << "," << (*iter).time              // m2time
                    << "," << 10.0 * log10(norm(p2))    // t2amp
                    << "," << R2 / c0;                  // t2time
                }
            }
            os << endl;
            mean_model += tl_model[n];
            mean_analytic += tl_analytic[n];
        }
        mean_model /= range.size();
        mean_analytic /= range.size();

        // compute statistics of difference between curves for each freq

        double bias = 0.0;
        double dev = 0.0;
        double Sxx = 0.0;
        double Syy = 0.0;
        double Sxy = 0.0;
        for (size_t n = 0; n < range.size(); ++n)
        {
            const double diff = (tl_model[n] - tl_analytic[n]);
            bias += diff ;
            dev += (diff * diff);
            const double diff_analytic = (tl_analytic[n] - mean_analytic);
            Sxx += (diff_analytic * diff_analytic);
            const double diff_model = (tl_model[n] - mean_model);
            Syy += (diff_model * diff_model);
            Sxy += (diff_analytic * diff_model);
        }
        bias /= range.size() ;
        dev = sqrt( dev / range.size() - bias*bias ) ;
        double detcoef = Sxy * Sxy / (Sxx * Syy) * 100.0;

        cout << std::setprecision(4);
        cout << " freq =" << freq(f) << "Hz: bias = " << bias << " dB"
         << " dev = " << dev << " dB"
         << " detcoef = " << detcoef << "%" << endl ;

        // extremely low and high freq can have a greater dev

        int iFreq = (int)freq(f);
        switch (iFreq) {
            case 10:
                BOOST_CHECK( abs(bias) <= 10.0 );
                BOOST_CHECK( dev <= 5.0 );
                BOOST_CHECK( detcoef >= 90.0 );
                break;
            case 100:
                BOOST_CHECK( abs(bias) <= 2.0 );
                BOOST_CHECK( dev <= 4.0 );
                BOOST_CHECK( detcoef >= 90.0 );
                break;
            case 10000:
                BOOST_CHECK( dev <= 6.0 );
                BOOST_CHECK( detcoef >= 75.0 );
                break;
            default:
                BOOST_CHECK( abs(bias) <= 1.0 );
                BOOST_CHECK( dev <= 4.0 );
                BOOST_CHECK( detcoef >= 90 );
        }

    } // end Freq
}

/**
 * Compares modeled propagation loss as a function of depth to the Lloyd's
 * mirror analytic expression for surface reflection in an isovelocity ocean.
 * This forces the model to deal with target points near the surface where the
 * up-going and down-going wavefronts must be extrapolated from two ray
 * families that have different numbers of surface bounces.
 *
 *      - Source:       25 meters deep
 *      - Target:       Range 2000 meters, depth is 1-50 meters
 *      - Frequency:    2000 Hz
 *      - Sound Speed:  1500 m/s
 *      - Time Step:    50 msec
 *	- Source D/E:	-90 deg to 90 deg, 181 rays with tangent spacing
 *	- Source AZ:	-2 deg to 2 deg in 1 deg increments
 *
 * The analytic result is the same as that for proploss_lloyds_range().
 *
 * Targets near the surface must be extrapolated from the wavefronts below
 * them.  Because the Gaussian profile rolls off at edge of a ray family,
 * it is important to test the impact of that phenomena on propagation loss at
 * the interface.
 *
 * This test computes three statistics to measure the difference between
 * the model and the analytic solution.
 *
 * - "bias" is the mean difference and it measures offsets in level.
 * - "dev" is an estimate of the sqrt of the variance and it is a measure of
 *   the difference in fluctuations of between the models.
 * - "detcoef" is the coefficient of determination and it measure of the
 *   fraction of the model that is predicts the analytic solution.
 *
 * An automatic error is thrown if abs(bias) > 0.5 dB, dev > 4 db, or
 * detcoef < 80%.
 *
 * @xref F.B. Jensen, W.A. Kuperman, M.B. Porter, H. Schmidt,
 * "Computational Ocean Acoustics", pp. 16-19.
 */
BOOST_AUTO_TEST_CASE(proploss_lloyds_depth)
{
    cout << "=== proploss_test: proploss_lloyds_depth ===" << endl;
    const char* csvname = USML_TEST_DIR "/waveq3d/test/proploss_lloyds_depth.csv";
    const char* ncname = USML_TEST_DIR "/waveq3d/test/proploss_lloyds_depth.nc";
    const char* ncname_wave = USML_TEST_DIR "/waveq3d/test/proploss_lloyds_depth_wave.nc";
    const double c0 = 1500.0;
    const double src_lat = 45.0;
    const double src_lng = -45.0;
    const double src_alt = -25.0;
    const double range = 10e3;
    const double time_max = 8.0;

    // initialize propagation model

    wposition::compute_earth_radius(src_lat);
    attenuation_model* attn = new attenuation_constant(0.0);
    profile_model* profile = new profile_linear(c0, attn);
    boundary_model* surface = new boundary_flat();
    boundary_model* bottom = new boundary_flat(bot_depth);
    ocean_model ocean(surface, bottom, profile);
    profile->flat_earth(true);

    seq_log freq(f0, 1.0, 1);
    const double wavenum = TWO_PI * freq(0) / c0;

    wposition1 pos(src_lat, src_lng, src_alt);
    seq_rayfan de ;
    seq_linear az( -4.0, 1.0, 4.0 );

    // build a series of targets at different depths

    double degrees = src_lat + to_degrees(range / (wposition::earth_radius+src_alt)); // range in latitude
    seq_linear depth(-0.1, -0.5, -40.1); // depth in meters
    wposition target(depth.size(), 1, degrees, src_lng, 0.0);
    for (size_t n = 0; n < target.size1(); ++n)
    {
        target.altitude(n, 0, depth(n));
    }

    eigenray_collection loss(freq, pos, de, az, time_step, &target);
    wave_queue wave( ocean, freq, pos, de, az, time_step, &target) ;
    wave.add_eigenray_listener(&loss);

    // propagate rays & record to log file

    cout << "propagate wavefronts" << endl;
    cout << "writing wavefronts to " << ncname_wave << endl;
    wave.init_netcdf(ncname_wave); // open a log file for wavefront data
    wave.save_netcdf(); // write ray data to log file
    while (wave.time() < time_max)
    {
        wave.step();
        wave.save_netcdf(); // write ray data to log file
    }
    wave.close_netcdf(); // close log file for wavefront data
    cout << "writing eigenrays to " << ncname << endl;
    loss.sum_eigenrays();
    loss.write_netcdf(ncname);

    // save results to spreadsheet and compare to analytic results

    cout << "writing spreadsheets to " << csvname << endl;
    std::ofstream os(csvname);
    os << "depth,model,theory,m1amp,t1amp,m1time,t1time,time1-diff,m2amp,t2amp,m2time,t2time,time2-diff,phase"
    << endl;
    os << std::setprecision(18);

    vector<double> tl_model(depth.size());
    vector<double> tl_analytic(depth.size());
    double mean_model = 0.0;
    double mean_analytic = 0.0;

    for (size_t n = 0; n < depth.size(); ++n)
    {
        const double z1 = depth(n) - src_alt;
        const double z2 = depth(n) + src_alt;

        tl_model[n] = -loss.total(n, 0)->intensity(0);

        // compute analytic solution

        const double R1 = sqrt(range * range + z1 * z1);
        const double R2 = sqrt(range * range + z2 * z2);
        complex<double> p1(0.0, wavenum * R1);
        complex<double> p2(0.0, wavenum * R2);
        p1 = exp(p1) / R1;
        p2 = -exp(p2) / R2;
        tl_analytic[n] = 10.0 * log10(norm(p1 + p2));

        // print to log file

        eigenray_list::const_iterator iter = loss.eigenrays(n, 0)->begin();
        if (iter != loss.eigenrays(n, 0)->end())
        {
            os << depth(n)
            << "," << tl_model[n]
            << "," << tl_analytic[n]
            << "," << -(*iter).intensity(0)
            << "," << 10.0 * log10(norm(p1))
            << "," << (*iter).time
            << "," << R1 / c0
            << "," << (*iter).time-R1 / c0;
            ++iter;
            if (iter != loss.eigenrays(n, 0)->end())
            {
                os << "," << -(*iter).intensity(0)
                << "," << 10.0 * log10(norm(p2))
                << "," << (*iter).time
                << "," << R2 / c0
                << "," << (*iter).time-R2 / c0;
            }
            os << "," << (*iter).phase(0);
        }
        os << endl;
        mean_model += tl_model[n];
        mean_analytic += tl_analytic[n];
    }
    mean_model /= depth.size();
    mean_analytic /= depth.size();

    // compute statistics of difference between curves

    double bias = 0.0;
    double dev = 0.0;
    double Sxx = 0.0;
    double Syy = 0.0;
    double Sxy = 0.0;
    for (size_t n = 0; n < depth.size(); ++n)
    {
        const double diff = (tl_model[n] - tl_analytic[n]);
        bias += diff ;
        dev += (diff * diff);
        const double diff_analytic = (tl_analytic[n] - mean_analytic);
        Sxx += (diff_analytic * diff_analytic);
        const double diff_model = (tl_model[n] - mean_model);
        Syy += (diff_model * diff_model);
        Sxy += (diff_analytic * diff_model);
    }
    bias /= depth.size() ;
    dev = sqrt( dev / depth.size() - bias*bias ) ;
    double detcoef = Sxy * Sxy / (Sxx * Syy) * 100.0;

    cout << std::setprecision(4);
    cout << "bias = " << bias << " dB"
         << " dev = " << dev << " dB"
         << " detcoef = " << detcoef << "%" << endl ;

    BOOST_CHECK( abs(bias) <= 0.7 );
    BOOST_CHECK( dev <= 5.0 );
    BOOST_CHECK( detcoef >= 80.0 );
}

/**
 * This test demonstrates ability to adjust source position if it is within
 * 0.1 meters of being above the ocean surface or below the ocean bottom.
 * The boundary reflection logic does not perform correctly if the
 * wavefront starts on the wrong side of either boundary.
 */
BOOST_AUTO_TEST_CASE(proploss_limits)
{
    cout << "=== proploss_test: proploss_limits ===" << endl;
    const double c0 = 1500.0;
    const double src_lat = 45.0;
    const double src_lng = -45.0;
    const double src_alt = 0.0;
    const double depth = -1000 ;

    // initialize propagation model

    profile_model* profile = new profile_linear(c0);
    boundary_model* surface = new boundary_flat();
    boundary_model* bottom = new boundary_flat(depth);
    ocean_model ocean(surface, bottom, profile);

    wposition1 pos(src_lat, src_lng, src_alt);
    seq_linear de(-10.7, 1.0, 10.0);
    seq_linear az(-10.5, 2.0, 10.0);
    seq_log freq(f0, 1.0, 1);  // 2000 Hz

    // try building a source above ocean surface

    wave_queue wave1( ocean, freq, pos, de, az, time_step ) ;
    BOOST_CHECK_CLOSE( wave1.source_pos().altitude(), -0.1, 1e-6 );

    // try building a source above ocean surface

    pos.altitude( depth-10.0 ) ;
    wave_queue wave2( ocean, freq, pos, de, az, time_step ) ;
    BOOST_CHECK_CLOSE( wave2.source_pos().altitude(), depth+0.1, 1e-6 );
}

/// @}

BOOST_AUTO_TEST_SUITE_END()


/*
Copyright 2013 Rogier van Dalen.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#define BOOST_TEST_MODULE test_rime_sign
#include "utility/test/boost_unit_test.hpp"

#include "rime/sign.hpp"

#include <string>
#include <type_traits>

#include <boost/mpl/assert.hpp>
#include <boost/mpl/int.hpp>

#include "rime/core.hpp"

#include "rime/check/check_equal.hpp"

BOOST_AUTO_TEST_SUITE(test_rime_sign)

BOOST_AUTO_TEST_CASE (test_rime_sign_types) {
    // is_signed.
    static_assert (rime::types::is_signed <int>::value, "");
    static_assert (rime::types::is_signed <short>::value, "");
    static_assert (rime::types::is_signed <rime::int_ <-1>>::value, "");
    static_assert (rime::types::is_signed <boost::mpl::int_ <0>>::value, "");
    static_assert (rime::types::is_signed
        <std::integral_constant <int, 1>>::value, "");

    static_assert (!rime::types::is_signed <unsigned>::value, "");
    static_assert (!rime::types::is_signed <unsigned short>::value, "");
    static_assert (!rime::types::is_signed <std::size_t>::value, "");
    static_assert (!rime::types::is_signed <rime::size_t <0>>::value, "");
    static_assert (!rime::types::is_signed <rime::size_t <2>>::value, "");
    static_assert (!rime::types::is_signed <void>::value, "");
    static_assert (!rime::types::is_signed <std::string>::value, "");

    // is_unsigned.
    static_assert (rime::types::is_unsigned <unsigned>::value, "");
    static_assert (rime::types::is_unsigned <unsigned short>::value, "");
    static_assert (rime::types::is_unsigned <std::size_t>::value, "");
    static_assert (rime::types::is_unsigned <rime::size_t <0>>::value, "");
    static_assert (rime::types::is_unsigned <rime::size_t <2>>::value, "");

    static_assert (!rime::types::is_unsigned <int>::value, "");
    static_assert (!rime::types::is_unsigned <short>::value, "");
    static_assert (!rime::types::is_unsigned <rime::int_ <-1>>::value, "");
    static_assert (!rime::types::is_unsigned <boost::mpl::int_ <0>>::value, "");
    static_assert (!rime::types::is_unsigned <
        std::integral_constant <int, 1>>::value, "");

    static_assert (!rime::types::is_unsigned <void>::value, "");
    static_assert (!rime::types::is_unsigned <std::string>::value, "");

    // make_signed.
    BOOST_MPL_ASSERT ((std::is_same <
        typename rime::types::make_signed <int>::type, int>));
    BOOST_MPL_ASSERT ((std::is_same <
        typename rime::types::make_signed <unsigned>::type, int>));
    BOOST_MPL_ASSERT ((std::is_same <
        typename rime::types::make_signed <short>::type, short>));
    BOOST_MPL_ASSERT ((std::is_same <
        typename rime::types::make_signed <unsigned short>::type, short>));

    BOOST_MPL_ASSERT ((rime::same_constant <
        rime::types::make_signed <rime::int_ <1>>::type, rime::int_ <1>>));
    BOOST_MPL_ASSERT ((rime::same_constant <
        rime::types::make_signed <rime::constant <unsigned, 1>>::type,
        rime::int_ <1>>));
    BOOST_MPL_ASSERT ((rime::same_constant <
        rime::types::make_signed <std::integral_constant <unsigned, 2>>::type,
        rime::int_ <2>>));

    // make_unsigned.
    BOOST_MPL_ASSERT ((std::is_same <
        typename rime::types::make_unsigned <int>::type, unsigned>));
    BOOST_MPL_ASSERT ((std::is_same <
        typename rime::types::make_unsigned <unsigned>::type, unsigned>));
    BOOST_MPL_ASSERT ((std::is_same <
        typename rime::types::make_unsigned <short>::type, short unsigned>));
    BOOST_MPL_ASSERT ((std::is_same <
        typename rime::types::make_unsigned <unsigned short>::type,
        short unsigned>));

    BOOST_MPL_ASSERT ((rime::same_constant <
        rime::types::make_unsigned <rime::int_ <1>>::type,
        rime::constant <unsigned, 1u>>));
    BOOST_MPL_ASSERT ((rime::same_constant <
        rime::types::make_unsigned <rime::constant <int, 1>>::type,
        rime::constant <unsigned, 1u>>));
    BOOST_MPL_ASSERT ((rime::same_constant <
        rime::types::make_unsigned <std::integral_constant <int, 2>>::type,
        rime::constant <unsigned, 2u>>));

    // make_zero.
    RIME_CHECK_EQUAL (rime::types::make_zero <rime::int_ <1>>::type(),
        rime::int_ <0>());
    RIME_CHECK_EQUAL (rime::types::make_zero <rime::int_ <-5>>::type(),
        rime::int_ <0>());
    RIME_CHECK_EQUAL (
        (rime::types::make_zero <rime::constant <unsigned, 1>>::type()),
        (rime::constant <unsigned, 0>()));
}

BOOST_AUTO_TEST_CASE (test_rime_sign_convert) {
    // make_signed.
    RIME_CHECK_EQUAL (rime::make_signed (1), 1);
    RIME_CHECK_EQUAL (rime::make_signed (1u), 1);

    RIME_CHECK_EQUAL (rime::make_signed (rime::int_ <1>()), rime::int_ <1>());
    RIME_CHECK_EQUAL (
        rime::make_signed (rime::constant <unsigned, 1u>()), rime::int_ <1>());

    // make_unsigned.
    RIME_CHECK_EQUAL (rime::make_unsigned (1), 1u);
    RIME_CHECK_EQUAL (rime::make_unsigned (1u), 1u);

    RIME_CHECK_EQUAL (rime::make_unsigned (rime::int_ <3>()),
        (rime::constant <unsigned, 3u>()));
    RIME_CHECK_EQUAL (rime::make_unsigned (rime::constant <unsigned, 2u>()),
        (rime::constant <unsigned, 2u>()));

    // make_zero.
    RIME_CHECK_EQUAL (rime::make_zero (1), 0);
    RIME_CHECK_EQUAL (rime::make_zero (1u), 0u);

    RIME_CHECK_EQUAL (rime::make_zero (rime::int_ <1>()), rime::int_ <0>());
    RIME_CHECK_EQUAL (
        rime::make_zero (rime::constant <unsigned, 1u>()),
        (rime::constant <unsigned, 0u>()));

}

// Signed constants.
typedef rime::int_ <int (unsigned (-1)/2 + 1)> very_negative_int;
very_negative_int i1;
boost::mpl::int_ <-4> i2;
std::integral_constant <int, 0> i3;
rime::int_ <30> i4;

rime::constant <unsigned, 0> u3;
rime::constant <unsigned, 30> u4;
typedef rime::constant <unsigned, unsigned (-1)> very_great_unsigned;
very_great_unsigned u5;

// Sanity check for constants.
static_assert (very_negative_int::value < 0, "Should be negative");
static_assert (int (very_great_unsigned::value) < 0,
    "Unsigned should wrongly convert to negative");

template <bool reference, class Left, class Right>
    void check_less_sign_safe (Left const & left, Right const & right)
{
    // Known at compile time.
    RIME_CHECK_EQUAL (rime::less_sign_safe (left, right),
        rime::bool_ <reference>());

    // Known only at run time.
    RIME_CHECK_EQUAL (rime::less_sign_safe (Left::value, right), reference);
    RIME_CHECK_EQUAL (rime::less_sign_safe (left, Right::value), reference);
    RIME_CHECK_EQUAL (rime::less_sign_safe (Left::value, Right::value),
        reference);
}

// The case where if the left operand is constant, the result is known.
// I.e. -1 < u for any unsigned u.
template <bool reference, class Left, class Right>
    void check_less_sign_safe_left_constant (
        Left const & left, Right const & right)
{
    // Known at compile time.
    RIME_CHECK_EQUAL (rime::less_sign_safe (left, right),
        rime::bool_ <reference>());
    RIME_CHECK_EQUAL (rime::less_sign_safe (left, Right::value),
        rime::bool_ <reference>());

    // Known only at run time.
    RIME_CHECK_EQUAL (rime::less_sign_safe (Left::value, right), reference);
    RIME_CHECK_EQUAL (rime::less_sign_safe (Left::value, Right::value),
        reference);
}

// The case where if the right operand is constant, the result is known.
// I.e. u < -1 for any unsigned u.
template <bool reference, class Left, class Right>
    void check_less_sign_safe_right_constant (
        Left const & left, Right const & right)
{
    // Known at compile time.
    RIME_CHECK_EQUAL (rime::less_sign_safe (left, right),
        rime::bool_ <reference>());
    RIME_CHECK_EQUAL (rime::less_sign_safe (Left::value, right),
        rime::bool_ <reference>());

    // Known only at run time.
    RIME_CHECK_EQUAL (rime::less_sign_safe (left, Right::value), reference);
    RIME_CHECK_EQUAL (rime::less_sign_safe (Left::value, Right::value),
        reference);
}

struct not_an_int {};

bool operator < (not_an_int, not_an_int) { return false; }

BOOST_AUTO_TEST_CASE (test_rime_less_sign_safe) {
    check_less_sign_safe <false> (i1, i1);
    check_less_sign_safe <true> (i1, i2);
    check_less_sign_safe <true> (i1, i3);
    check_less_sign_safe_left_constant <true> (i1, u3);
    check_less_sign_safe <true> (i1, i4);
    check_less_sign_safe_left_constant <true> (i1, u4);
    check_less_sign_safe_left_constant <true> (i1, u5);

    check_less_sign_safe <false> (i2, i1);
    check_less_sign_safe <false> (i2, i2);
    check_less_sign_safe <true> (i2, i3);
    check_less_sign_safe_left_constant <true> (i2, u3);
    check_less_sign_safe <true> (i2, i4);
    check_less_sign_safe_left_constant <true> (i2, u4);
    check_less_sign_safe_left_constant <true> (i2, u5);

    check_less_sign_safe <false> (i3, i1);
    check_less_sign_safe <false> (i3, i2);
    check_less_sign_safe <false> (i3, i3);
    check_less_sign_safe <false> (i3, u3);
    check_less_sign_safe <true> (i3, i4);
    check_less_sign_safe <true> (i3, u4);
    check_less_sign_safe <true> (i3, u5);

    check_less_sign_safe <false> (i4, i1);
    check_less_sign_safe <false> (i4, i2);
    check_less_sign_safe <false> (i4, i3);
    check_less_sign_safe <false> (i4, u3);
    check_less_sign_safe <false> (i4, i4);
    check_less_sign_safe <false> (i4, u4);
    check_less_sign_safe <true> (i4, u5);

    check_less_sign_safe_right_constant  <false> (u3, i1);
    check_less_sign_safe_right_constant  <false> (u3, i2);
    check_less_sign_safe <false> (u3, i3);
    check_less_sign_safe <false> (u3, u3);
    check_less_sign_safe <true> (u3, i4);
    check_less_sign_safe <true> (u3, u4);
    check_less_sign_safe <true> (u3, u5);

    check_less_sign_safe_right_constant <false> (u4, i1);
    check_less_sign_safe_right_constant <false> (u4, i2);
    check_less_sign_safe <false> (u4, i3);
    check_less_sign_safe <false> (u4, u3);
    check_less_sign_safe <false> (u4, i4);
    check_less_sign_safe <false> (u4, u4);
    check_less_sign_safe <true> (u4, u5);

    check_less_sign_safe_right_constant <false> (u5, i1);
    check_less_sign_safe_right_constant <false> (u5, i2);
    check_less_sign_safe <false> (u5, i3);
    check_less_sign_safe <false> (u5, u3);
    check_less_sign_safe <false> (u5, i4);
    check_less_sign_safe <false> (u5, u4);
    check_less_sign_safe <false> (u5, u5);

    RIME_CHECK_EQUAL (rime::less_sign_safe (not_an_int(), not_an_int()), false);
}

BOOST_AUTO_TEST_SUITE_END()


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

#pragma hdrstop

#include <algorithm>
#include <numeric>
#include <iterator>

#include <boost/tuple/tuple.hpp>

#include "GridMapper.h"

using std::lower_bound;
using std::iota;
using std::for_each;
using std::stable_sort;
using std::begin;
using std::end;

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

#pragma package(smart_init)

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

void ViewRowGridMapper::ClearMap()
{
    mapped_ = false;
    view2Model_.clear();
    model2view_.clear();
}
//---------------------------------------------------------------------------

int ViewRowGridMapper::Map( int RowCount )
{
    ViewToModelCont View2Model( RowCount );
    iota( begin( View2Model ), end( View2Model ), 0 );

    BuildModelToView( View2Model );

    view2Model_.swap( View2Model );
    mapped_ = true;
    return GetCount();
}
//---------------------------------------------------------------------------

template<size_t TupleFieldIdx>
struct ModelIndexCompare {
    template<typename T, typename U>
    bool operator()( T const & Tuple, U Value ) const {
        return Tuple.template get<TupleFieldIdx>() < Value;
    }
};

int ViewRowGridMapper::DoIdx2Num( int Idx ) const
{
    if ( mapped_ ) {
        ModelToViewCont::const_iterator It =
            lower_bound(
                begin( model2view_ ), end( model2view_ ), Idx,
                ModelIndexCompare<(size_t)ModelToRowField::ModelIdx>()
            );
        if ( It != end( model2view_ ) &&
             !( Idx < It->get<(int)ModelToRowField::ModelIdx>() ) )
        {
            return It->get<(size_t)ModelToRowField::ViewIdx>();
        }
        return -1;
    }
    else {
        return Idx;
    }
}
//---------------------------------------------------------------------------

int ViewRowGridMapper::DoNum2Idx( int ARow ) const
{
    return mapped_ && ARow < static_cast<int>( view2Model_.size() ) ?
             view2Model_[ARow]
           :
             ARow;
}
//---------------------------------------------------------------------------

template<typename C>
class CopyIotaType {
public:
    explicit CopyIotaType( C& Cont ) : n_( 0 ), cont_( Cont ) {}

    template<typename T>
    void operator()( T const & Val ) {
        cont_.push_back( boost::make_tuple( Val, n_++ ) );
    }
private:
    int n_;
    C& cont_;
};

template<typename C>
inline CopyIotaType<C> CopyIota( C& Cont ) { return CopyIotaType<C>( Cont ); }

void ViewRowGridMapper::BuildModelToView( ViewToModelCont const & View2Model )
{
    ModelToViewCont Model2View;
    Model2View.reserve( View2Model.size() );
    for_each( begin( View2Model ), end( View2Model ), CopyIota( Model2View ) );
    stable_sort( begin( Model2View ), end( Model2View ) );
    model2view_.swap( Model2View );
}
//---------------------------------------------------------------------------


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef NT2_SWAR_FUNCTIONS_COMPLEX_SIMD_COMMON_SPLIT_HPP_INCLUDED
#define NT2_SWAR_FUNCTIONS_COMPLEX_SIMD_COMMON_SPLIT_HPP_INCLUDED

#include <nt2/swar/functions/split.hpp>
#include <nt2/include/functions/imag.hpp>
#include <nt2/include/functions/real.hpp>
#include <nt2/include/functions/bitwise_cast.hpp>
#include <nt2/sdk/complex/meta/as_real.hpp>
#include <nt2/sdk/complex/hierarchy.hpp>
#include <boost/dispatch/meta/upgrade.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT  ( split_, tag::cpu_,
                              (A0)(A1)(X),
                              ((simd_<complex_<arithmetic_<A0> >,X>))
                              ((simd_<complex_<arithmetic_<A1> >,X>))
                              ((simd_<complex_<arithmetic_<A1> >,X>))
                            )
  {
    typedef void result_type;
    inline result_type operator()(A0 const& a0,A1 & a1, A1 & a2) const
    {
      typedef typename nt2::meta::as_real<A1>::type rA1;
      rA1 ar0, ar1, ai0, ai1;
      split(nt2::real(a0), ar0, ar1);
      split(nt2::imag(a0), ai0, ai1);
      a1 = A1(ar0, ai0);
      a2 = A1(ar1, ai1);
    }
  };

  BOOST_DISPATCH_IMPLEMENT  ( split_, tag::cpu_,
                              (A0)(A1)(X),
                              ((simd_<dry_<arithmetic_<A0> >,X>))
                              ((simd_<dry_<arithmetic_<A1> >,X>))
                              ((simd_<dry_<arithmetic_<A1> >,X>))
                            )
  {
    typedef void result_type;
    inline result_type operator()(A0 const& a0,A1 & a1, A1 & a2) const
    {
      typedef typename nt2::meta::as_real<A1>::type rA1;
      rA1 ai0, ai1;
      split(nt2::imag(a0), ai0, ai1);
      a1 = bitwise_cast<A1>(ai0);
      a2 = bitwise_cast<A1>(ai1);
    }
  };

  BOOST_DISPATCH_IMPLEMENT  ( split_, tag::cpu_,
                              (A0)(X),
                              ((simd_<complex_<arithmetic_<A0> >,X>))
                            )
  {
    typedef typename boost::dispatch::meta::upgrade<typename A0::value_type>::type base_t;
    typedef typename boost::simd::meta::vector_of< base_t
                                                 , A0::static_size
                                                 >::type result_type;
    inline result_type operator()(A0 const& a0) const
    {
      return result_type(split(nt2::real(a0)), split(nt2::imag(a0)));
    }
  };

  BOOST_DISPATCH_IMPLEMENT  ( split_, tag::cpu_,
                              (A0)(X),
                              ((simd_<dry_<arithmetic_<A0> >,X>))
                            )
  {
    typedef typename boost::dispatch::meta::upgrade<typename A0::value_type>::type base_t;
    typedef typename boost::simd::meta::vector_of< base_t
                                                 , A0::static_size
                                                 >::type result_type;
    inline result_type operator()(A0 const& a0) const
    {
      return bitwise_cast<result_type>(split(nt2::real(a0)));
    }
  };

} } }

#endif


#include "say.h"
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(zero)
{
    BOOST_REQUIRE_EQUAL("zero", say::in_english(0ULL));
}

#if defined(EXERCISM_RUN_ALL_TESTS)
BOOST_AUTO_TEST_CASE(one)
{
    BOOST_REQUIRE_EQUAL("one", say::in_english(1ULL));
}

BOOST_AUTO_TEST_CASE(fourteen)
{
    BOOST_REQUIRE_EQUAL("fourteen", say::in_english(14ULL));
}

BOOST_AUTO_TEST_CASE(twenty)
{
    BOOST_REQUIRE_EQUAL("twenty", say::in_english(20ULL));
}

BOOST_AUTO_TEST_CASE(twenty_two)
{
    BOOST_REQUIRE_EQUAL("twenty-two", say::in_english(22ULL));
}

BOOST_AUTO_TEST_CASE(sixty_nine)
{
    BOOST_REQUIRE_EQUAL("sixty-nine", say::in_english(69ULL));
}

BOOST_AUTO_TEST_CASE(one_hundred)
{
    BOOST_REQUIRE_EQUAL("one hundred", say::in_english(100ULL));
}

BOOST_AUTO_TEST_CASE(one_hundred_twenty_three)
{
    BOOST_REQUIRE_EQUAL("one hundred twenty-three", say::in_english(123ULL));
}

BOOST_AUTO_TEST_CASE(one_thousand)
{
    BOOST_REQUIRE_EQUAL("one thousand", say::in_english(1000ULL));
}

BOOST_AUTO_TEST_CASE(one_thousand_two_hundred_thirty_four)
{
    BOOST_REQUIRE_EQUAL("one thousand two hundred thirty-four", say::in_english(1234ULL));
}

BOOST_AUTO_TEST_CASE(one_million)
{
    BOOST_REQUIRE_EQUAL("one million", say::in_english(1000ULL*1000ULL));
}

BOOST_AUTO_TEST_CASE(one_million_two)
{
    BOOST_REQUIRE_EQUAL("one million two", say::in_english(1000ULL*1000ULL + 2ULL));
}

BOOST_AUTO_TEST_CASE(one_million_two_thousand_three_hundred_forty_five)
{
    BOOST_REQUIRE_EQUAL("one million two thousand three hundred forty-five", say::in_english(1002345ULL));
}

BOOST_AUTO_TEST_CASE(one_billion)
{
    BOOST_REQUIRE_EQUAL("one billion", say::in_english(1000ULL*1000ULL*1000ULL));
}

BOOST_AUTO_TEST_CASE(a_really_big_number)
{
    BOOST_REQUIRE_EQUAL(
            "nine hundred eighty-seven billion"
            " six hundred fifty-four million"
            " three hundred twenty-one thousand"
            " one hundred twenty-three",
        say::in_english(987654321123ULL));
}

BOOST_AUTO_TEST_CASE(raises_an_error_below_zero)
{
    BOOST_REQUIRE_THROW(say::in_english(-1), std::domain_error);
}

BOOST_AUTO_TEST_CASE(raises_an_error_for_one_trillion)
{
    BOOST_REQUIRE_THROW(say::in_english(1000ULL*1000ULL*1000ULL*1000ULL), std::domain_error);
}
#endif


#include <iostream>
#include <algorithm>
#include <boost/lambda/lambda.hpp>

#define SizeOf(array) (sizeof(array)/sizeof(array[0]))

int main()
{
	int array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, };

	int label = 4;
	const int* isCL = std::find_if(array, array + SizeOf(array), boost::lambda::_1 == label);

	if (isCL != &array[10]) {
		std::cout << *isCL << std::endl;
		std::cout << SizeOf(array) << std::endl;
	} else {
		std::cout << "UnFound" << std::endl;
	}
}


#include <iostream>
#include <vector>
#include <memory>
#include <string>
using namespace std;
#include "pulse.hpp"

#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>

#define NAME_(x) {x->name(#x);}

#define NAME__(x) {x.name(#x);}

/*
int main() {
    boost::log::core::get()->set_filter (
                                         boost::log::trivial::severity >= boost::log::trivial::trace
                                         );
  shared_ptr<WatorAudioWaveL> audio = make_shared<WatorAudioWaveL>();
  NAME_(audio);

  shared_ptr<WatorHiddenL> hide1 = make_shared<WatorHiddenL>();
  NAME_(hide1);
  shared_ptr<WatorHiddenL> hide2 = make_shared<WatorHiddenL>();
  NAME_(hide2);
  shared_ptr<WatorHiddenL> hide3 = make_shared<WatorHiddenL>();
  NAME_(hide3);
    shared_ptr<WatorHiddenL> hide4 = make_shared<WatorHiddenL>();
    NAME_(hide4);
    shared_ptr<WatorHiddenL> hide5 = make_shared<WatorHiddenL>();
    NAME_(hide5);
    shared_ptr<WatorHiddenL> hide6 = make_shared<WatorHiddenL>();
    NAME_(hide6);

    shared_ptr<WatorHiddenL> hide7 = make_shared<WatorHiddenL>();
    NAME_(hide7);

    shared_ptr<WatorHiddenL> hide8 = make_shared<WatorHiddenL>();
    NAME_(hide8);

    shared_ptr<WatorHiddenL> hide9 = make_shared<WatorHiddenL>();
    NAME_(hide9);

    shared_ptr<WatorHiddenL> hide10 = make_shared<WatorHiddenL>();
    NAME_(hide10);

  shared_ptr<WatorOutputL> out = make_shared<WatorOutputL>();
  NAME_(out);

  audio->addTop(hide1);
  hide1->addTop(hide2);
  hide2->addTop(hide3);
    hide3->addTop(hide4);
    hide4->addTop(hide5);
    hide5->addTop(hide6);
    hide6->addTop(hide7);
    hide7->addTop(hide8);
    hide8->addTop(hide9);
    hide9->addTop(hide10);
    hide10->addTop(out);

  hide1->addButtom(audio);
  hide2->addButtom(hide1);
    hide3->addButtom(hide2);
    hide4->addButtom(hide3);
    hide5->addButtom(hide4);
    hide6->addButtom(hide5);
    hide7->addButtom(hide6);
    hide8->addButtom(hide7);
    hide9->addButtom(hide8);
    hide10->addButtom(hide9);
  out->addButtom(hide10);

    audio->setDAF(0.01);
    hide1->setDAF(0.01);
    hide2->setDAF(0.02);
    hide3->setDAF(0.02);
    hide4->setDAF(0.02);
    hide5->setDAF(0.2);
    hide6->setDAF(0.5);
    hide7->setDAF(0.5);
    hide8->setDAF(0.5);
    hide9->setDAF(0.5);
    hide10->setDAF(0.5);
    shared_ptr<WatorAudioWave2L> audio = make_shared<WatorAudioWave2L>();
    NAME_(audio);
    shared_ptr<HalfSinCurveL> halfSin = make_shared<HalfSinCurveL>();
    NAME_(halfSin);
    shared_ptr<FullSinCurveL> fullSin = make_shared<FullSinCurveL>();
    NAME_(fullSin);
    shared_ptr<WatorOutputL> out = make_shared<WatorOutputL>();
    NAME_(out);

    audio->addTop(halfSin);
    halfSin->addTop(fullSin);
    fullSin->addTop(out);

    halfSin->addButtom(audio);
    fullSin->addButtom(halfSin);
    out->addButtom(fullSin);

  WatorNet net(audio);
  net.build();
  net.train();
  net.snapshot();
  return 0;
}
 */

int main() {
    boost::log::core::get()->set_filter (
                                         boost::log::trivial::severity >= boost::log::trivial::trace
                                         );
    using namespace WatorVapor;

    shared_ptr<AudioWaveLayer> audio = make_shared<AudioWaveLayer>();
    NAME_(audio);
    shared_ptr<Peak2PeakLayer> peak = make_shared<Peak2PeakLayer>();
    NAME_(peak);
    shared_ptr<TextOutputLayer> text = make_shared<TextOutputLayer>();
    NAME_(text);

    audio->addTop(peak);
    peak->addTop(text);

    peak->addButtom(audio);
    text->addButtom(peak);

    Net net(audio);
    net.build();
    net.train();
    net.snapshot();
    return 0;
}


/*
 [auto_generated]
 boost/numeric/odeint/algebra/array_algebra.hpp

 [begin_description]
 Algebra for boost::array. Highly specialized for odeint. Const arguments are introduce to work with odeint.
 [end_description]

 Copyright 2009-2011 Karsten Ahnert
 Copyright 2009-2011 Mario Mulansky

 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_NUMERIC_ODEINT_ALGEBRA_ARRAY_ALGEBRA_HPP_INCLUDED
#define BOOST_NUMERIC_ODEINT_ALGEBRA_ARRAY_ALGEBRA_HPP_INCLUDED

#include <algorithm>
#include <boost/array.hpp>

#include <boost/numeric/odeint/algebra/norm_result_type.hpp>

namespace boost {
namespace numeric {
namespace odeint {

struct array_algebra
{
    template< typename T , size_t dim , class Op >
    static void for_each1( boost::array< T , dim > &s1 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] );
    }

    template< typename T1 , typename T2 , size_t dim , class Op >
    static void for_each2( boost::array< T1 , dim > &s1 ,
            const boost::array< T2 , dim > &s2 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] );
    }

    template< typename T , size_t dim , class Op >
    static void for_each3( boost::array< T , dim > &s1 ,
            const boost::array< T , dim > &s2 ,
            const boost::array< T , dim > &s3 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] , s3[i] );
    }

    /* different const signature - required for the scale_sum_swap2 operation */
    template< typename T , size_t dim , class Op >
    static void for_each3( boost::array< T , dim > &s1 ,
            boost::array< T , dim > &s2 ,
            const boost::array< T , dim > &s3 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] , s3[i] );
    }

    template< typename T , size_t dim , class Op >
    static void for_each4( boost::array< T , dim > &s1 ,
            const boost::array< T , dim > &s2 ,
            const boost::array< T , dim > &s3 ,
            const boost::array< T , dim > &s4 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] , s3[i] , s4[i] );
    }

    template< typename T , size_t dim , class Op >
    static void for_each5( boost::array< T , dim > &s1 ,
            const boost::array< T , dim > &s2 ,
            const boost::array< T , dim > &s3 ,
            const boost::array< T , dim > &s4 ,
            const boost::array< T , dim > &s5 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] , s3[i] , s4[i] , s5[i] );
    }

    template< typename T , size_t dim , class Op >
    static void for_each6( boost::array< T , dim > &s1 ,
            const boost::array< T , dim > &s2 ,
            const boost::array< T , dim > &s3 ,
            const boost::array< T , dim > &s4 ,
            const boost::array< T , dim > &s5 ,
            const boost::array< T , dim > &s6 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] , s3[i] , s4[i] , s5[i] , s6[i] );
    }

    template< typename T , size_t dim , class Op >
    static void for_each7( boost::array< T , dim > &s1 ,
            const boost::array< T , dim > &s2 ,
            const boost::array< T , dim > &s3 ,
            const boost::array< T , dim > &s4 ,
            const boost::array< T , dim > &s5 ,
            const boost::array< T , dim > &s6 ,
            const boost::array< T , dim > &s7 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] , s3[i] , s4[i] , s5[i] , s6[i] , s7[i] );
    }

    template< typename T , size_t dim , class Op >
    static void for_each8( boost::array< T , dim > &s1 ,
            const boost::array< T , dim > &s2 ,
            const boost::array< T , dim > &s3 ,
            const boost::array< T , dim > &s4 ,
            const boost::array< T , dim > &s5 ,
            const boost::array< T , dim > &s6 ,
            const boost::array< T , dim > &s7 ,
            const boost::array< T , dim > &s8 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] , s3[i] , s4[i] , s5[i] , s6[i] , s7[i] , s8[i] );
    }

    template< typename T , size_t dim , class Op >
    static void for_each9( boost::array< T , dim > &s1 ,
            const boost::array< T , dim > &s2 ,
            const boost::array< T , dim > &s3 ,
            const boost::array< T , dim > &s4 ,
            const boost::array< T , dim > &s5 ,
            const boost::array< T , dim > &s6 ,
            const boost::array< T , dim > &s7 ,
            const boost::array< T , dim > &s8 ,
            const boost::array< T , dim > &s9 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] , s3[i] , s4[i] , s5[i] , s6[i] , s7[i] , s8[i] , s9[i] );
    }

    template< typename T , size_t dim , class Op >
    static void for_each10( boost::array< T , dim > &s1 ,
            const boost::array< T , dim > &s2 ,
            const boost::array< T , dim > &s3 ,
            const boost::array< T , dim > &s4 ,
            const boost::array< T , dim > &s5 ,
            const boost::array< T , dim > &s6 ,
            const boost::array< T , dim > &s7 ,
            const boost::array< T , dim > &s8 ,
            const boost::array< T , dim > &s9 ,
            const boost::array< T , dim > &s10 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] , s3[i] , s4[i] , s5[i] , s6[i] , s7[i] , s8[i] , s9[i] , s10[i] );
    }

    template< typename T , size_t dim , class Op >
    static void for_each11( boost::array< T , dim > &s1 ,
            const boost::array< T , dim > &s2 ,
            const boost::array< T , dim > &s3 ,
            const boost::array< T , dim > &s4 ,
            const boost::array< T , dim > &s5 ,
            const boost::array< T , dim > &s6 ,
            const boost::array< T , dim > &s7 ,
            const boost::array< T , dim > &s8 ,
            const boost::array< T , dim > &s9 ,
            const boost::array< T , dim > &s10 ,
            const boost::array< T , dim > &s11 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] , s3[i] , s4[i] , s5[i] , s6[i] , s7[i] , s8[i] , s9[i] , s10[i] , s11[i] );
    }

    template< typename T , size_t dim , class Op >
    static void for_each12( boost::array< T , dim > &s1 ,
            const boost::array< T , dim > &s2 ,
            const boost::array< T , dim > &s3 ,
            const boost::array< T , dim > &s4 ,
            const boost::array< T , dim > &s5 ,
            const boost::array< T , dim > &s6 ,
            const boost::array< T , dim > &s7 ,
            const boost::array< T , dim > &s8 ,
            const boost::array< T , dim > &s9 ,
            const boost::array< T , dim > &s10 ,
            const boost::array< T , dim > &s11 ,
            const boost::array< T , dim > &s12 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] , s3[i] , s4[i] , s5[i] , s6[i] , s7[i] , s8[i] , s9[i] , s10[i] , s11[i] , s12[i] );
    }

    template< typename T , size_t dim , class Op >
    static void for_each13( boost::array< T , dim > &s1 ,
            const boost::array< T , dim > &s2 ,
            const boost::array< T , dim > &s3 ,
            const boost::array< T , dim > &s4 ,
            const boost::array< T , dim > &s5 ,
            const boost::array< T , dim > &s6 ,
            const boost::array< T , dim > &s7 ,
            const boost::array< T , dim > &s8 ,
            const boost::array< T , dim > &s9 ,
            const boost::array< T , dim > &s10 ,
            const boost::array< T , dim > &s11 ,
            const boost::array< T , dim > &s12 ,
            const boost::array< T , dim > &s13 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] , s3[i] , s4[i] , s5[i] , s6[i] , s7[i] , s8[i] , s9[i] , s10[i] , s11[i] , s12[i] , s13[i] );
    }

    template< typename T , size_t dim , class Op >
    static void for_each14( boost::array< T , dim > &s1 ,
            const boost::array< T , dim > &s2 ,
            const boost::array< T , dim > &s3 ,
            const boost::array< T , dim > &s4 ,
            const boost::array< T , dim > &s5 ,
            const boost::array< T , dim > &s6 ,
            const boost::array< T , dim > &s7 ,
            const boost::array< T , dim > &s8 ,
            const boost::array< T , dim > &s9 ,
            const boost::array< T , dim > &s10 ,
            const boost::array< T , dim > &s11 ,
            const boost::array< T , dim > &s12 ,
            const boost::array< T , dim > &s13 ,
            const boost::array< T , dim > &s14 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] , s3[i] , s4[i] , s5[i] , s6[i] , s7[i] , s8[i] , s9[i] , s10[i] , s11[i] , s12[i] , s13[i] , s14[i] );
    }

    template< typename T , size_t dim , class Op >
    static void for_each15( boost::array< T , dim > &s1 ,
            const boost::array< T , dim > &s2 ,
            const boost::array< T , dim > &s3 ,
            const boost::array< T , dim > &s4 ,
            const boost::array< T , dim > &s5 ,
            const boost::array< T , dim > &s6 ,
            const boost::array< T , dim > &s7 ,
            const boost::array< T , dim > &s8 ,
            const boost::array< T , dim > &s9 ,
            const boost::array< T , dim > &s10 ,
            const boost::array< T , dim > &s11 ,
            const boost::array< T , dim > &s12 ,
            const boost::array< T , dim > &s13 ,
            const boost::array< T , dim > &s14 ,
            const boost::array< T , dim > &s15 , Op op )
    {
        for( size_t i=0 ; i<dim ; ++i )
            op( s1[i] , s2[i] , s3[i] , s4[i] , s5[i] , s6[i] , s7[i] , s8[i] , s9[i] , s10[i] , s11[i] , s12[i] , s13[i] , s14[i] , s15[i] );
    }

    template< typename T , size_t dim >
    static typename norm_result_type< boost::array< T , dim > >::type norm_inf( const boost::array< T , dim > &s )
    {
        BOOST_USING_STD_MAX();
        using std::abs;
        typedef typename norm_result_type< boost::array< T , dim > >::type result_type;
        result_type init = static_cast< result_type >( 0 );
        for( size_t i=0 ; i<dim ; ++i )
            init = max BOOST_PREVENT_MACRO_SUBSTITUTION ( init , static_cast< result_type >(abs(s[i])) );
        return init;
    }

};

}
}
}

#endif // BOOST_NUMERIC_ODEINT_ALGEBRA_ARRAY_ALGEBRA_HPP_INCLUDED


// ------------------------------------------------------------------------
//  Copyright (C)
//  Jordi Pont-Tuset <jponttuset@vision.ee.ethz.ch>
//  July 2015
// ------------------------------------------------------------------------

#ifndef SIMPLIFY_HPP
#define SIMPLIFY_HPP

#include <vector>
#include <list>

#include <cpp/containers.hpp>
#include <psimpl/psimpl.hpp>
#include <Eigen/Dense>

namespace PolyCont
{
    void simplify_one(ContContainer& all_cont_pieces, /* Vector containing the finished contour pieces */
                      double simplify_tol             /* Tolerance of the line simplification algorithm */
                      )
    {
        /* Sweep all contour pieces */
        for (std::size_t ii=0; ii<all_cont_pieces.size(); ++ii)
        {
            /* Copy the data to the structure needed to simplify */
            std::list<double> to_simpl;
            for (std::size_t jj=0; jj<all_cont_pieces[ii].size(); ++jj)
            {
                /* Coordinates in the form x1,y1,x2,y2, etc. */
                to_simpl.push_back(all_cont_pieces[ii][jj].X);
                to_simpl.push_back(all_cont_pieces[ii][jj].Y);
            }
            std::list<double> simplified(to_simpl.size());
            std::fill(simplified.begin(), simplified.end(), -1);

            /* Simplify */
            psimpl::simplify_douglas_peucker<2>(to_simpl.begin(), to_simpl.end(), simplify_tol, simplified.begin());

            /* Copy back to the Coord vector */
            all_cont_pieces[ii].resize(0);
            for(std::list<double>::iterator it=simplified.begin(); it!=simplified.end(); ++it)
            {
                if(*it==-1)
                    break;
                double tmp_x = *it;
                ++it;
                double tmp_y = *it;

                all_cont_pieces[ii].push_back(Point(tmp_x, tmp_y));
            }
        }
    }

    void simplify(ContContainer& all_cont_pieces, /* Vector containing the finished contour pieces */
                  double simplify_tol             /* Tolerance of the line simplification algorithm */
                  )
    {
        // Do nothing if simplify_tol==0
        if (simplify_tol>0)
        {
            // Do a first simplification of the 'straight' lines
            if (simplify_tol>0.1)
                simplify_one(all_cont_pieces, 0.1);

            // Then do the real simplification
            // (works better in practice, but it can be removed for efficiency)
            simplify_one(all_cont_pieces, simplify_tol);
        }
    }
}
#endif


/* Copyright (C) 2012-2020 IBM Corp.
 * This program is Licensed under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. See accompanying LICENSE file.
 */
/**
 * @file matmul.h
 * @brief some matrix / linear algenra stuff
 */
#include <helib/matmul.h>
#include <helib/fhe_stats.h>
#include <NTL/BasicThreadPool.h>

#if (defined(__unix__) || defined(__unix) || defined(unix))
#include <sys/time.h>
#include <sys/resource.h>
#endif

// Implementation of the various random matrices is found here
#include <helib/randomMatrices.h>
/*
 * Defined in this file are the following class templates:
 *
 *   class RandomMatrix: public MatMul1D_derived<type>
 *   class RandomMultiMatrix: public MatMul1D_derived<type>
 *   class RandomBlockMatrix: public BlockMatMul1D_derived<type>
 *   class RandomMultiBlockMatrix: public BlockMatMul1D_derived<type>
 *   class RandomFullMatrix: public MatMulFull_derived<type>
 *   class RandomFullBlockMatrix : public BlockMatMulFull_derived<type>
 *
 * Each of them has a corresponding build function, namely:
 *
 *   MatMul1D* buildRandomMatrix(const EncryptedArray& ea, long dim);
 *   MatMul1D* buildRandomMultiMatrix(const EncryptedArray& ea, long dim);
 test_common.h   BlockMatMul1D* buildRandomBlockMatrix(const EncryptedArray& ea,
 long dim);
 *   BlockMatMul1D* buildRandomMultiBlockMatrix(const EncryptedArray& ea, long
 dim);
 *   MatMulFull* buildRandomFullMatrix(const EncryptedArray& ea);
 *   BlockMatMulFull* buildRandomFullBlockMatrix(const EncryptedArray& ea);
 */

#include <helib/debugging.h>
#include "gtest/gtest.h"
#include "test_common.h"

namespace {

struct Parameters
{
  Parameters(long m,
             long p,
             long r,
             long L,
             long dim,
             long nt,
             long full,
             long block,
             std::vector<long> gens,
             std::vector<long> ords,
             int ks_strategy,
             int force_bsgs,
             int force_hoist) :
      m(m),
      p(p),
      r(r),
      L(L),
      dim(dim),
      nt(nt),
      full(full),
      block(block),
      gens(gens),
      ords(ords),
      ks_strategy(ks_strategy),
      force_bsgs(force_bsgs),
      force_hoist(force_hoist){};

  long m;                 // defines the cyclotomic polynomial Phi_m(X)
  long p;                 // plaintext base
  long r;                 // lifting
  long L;                 // # of levels in the modulus chain
  long dim;               // dimension along which to multiply
  long nt;                // # threads
  long full;              // 0: 1D, 1: full
  long block;             // 0: normal, 1: block
  std::vector<long> gens; // use specified vector of generators
                          // e.g., gens='[420 1105 1425]'
  std::vector<long> ords; // use specified vector of orders
                          // e.g., ords='[11 8 2]', negative means 'bad'
  int ks_strategy;        // 0: default, 1:full, 2:bsgs, 3:minimal
  int force_bsgs;         // 1 to force on, -1 to force off
  int force_hoist;        // -1 to force off

  // tell google test how to print Parameters
  // Removed as not used by templated tests
  // friend std::ostream& operator<<(std::ostream& os, const Parameters& params)
  //{
  //    return os << "{" <<
  //        "m=" << params.m << "," <<
  //        "p=" << params.p << "," <<
  //        "r=" << params.r << "," <<
  //        "L=" << params.L << "," <<
  //        "dim=" << params.dim << "," <<
  //        "nt=" << params.nt << "," <<
  //        "full=" << params.full <<  "," <<
  //        "block=" << params.block <<  "," <<
  //        "gens=" << helib::vecToStr(params.gens) << "," <<
  //        "ords=" << helib::vecToStr(params.ords) << "," <<
  //        "ks_strategy=" << params.ks_strategy << "," <<
  //        "force_bsgs=" << params.force_bsgs << "," <<
  //        "force_hoist=" << params.force_hoist <<
  //        "}";
  //};
};

template <class Mat>
std::unique_ptr<Mat> buildMat(const helib::EncryptedArray& ea, long dim);

template <>
std::unique_ptr<helib::MatMul1D> buildMat(const helib::EncryptedArray& ea,
                                          long dim)
{
  return std::unique_ptr<helib::MatMul1D>{buildRandomMatrix(ea, dim)};
};
// template<> std::unique_ptr<helib::BlockMatMul1D> buildMat(const
// helib::EncryptedArray &ea, long dim)
//{
//    return std::unique_ptr<helib::BlockMatMul1D>{buildRandomBlockMatrix(ea,
//    dim)};
//};
// template<> std::unique_ptr<helib::MatMulFull> buildMat(const
// helib::EncryptedArray &ea, long dim)
//{
//    return std::unique_ptr<helib::MatMulFull>{buildRandomFullMatrix(ea)};
//};
// template<> std::unique_ptr<helib::BlockMatMulFull> buildMat(const
// helib::EncryptedArray &ea, long dim)
//{
//    return
//    std::unique_ptr<helib::BlockMatMulFull>{buildRandomFullBlockMatrix(ea)};
//};

template <typename T>
class GTestMatmul : public ::testing::Test
{
protected:
  static void setGlobals(long force_bsgs, long force_hoist)
  {
    helib::fhe_test_force_bsgs = force_bsgs;
    helib::fhe_test_force_hoist = force_hoist;
  };

  bool minimal;
  long m;
  long p;
  long r;
  long L;
  long dim;
  long nt;

  long full;
  long block;
  std::vector<long> gens;
  std::vector<long> ords;

  long ks_strategy;

  helib::Context context;
  helib::SecKey secretKey;
  const helib::PubKey& publicKey;
  helib::EncryptedArray ea;
  std::unique_ptr<typename T::MatrixType> matrixPtr;

  static helib::Context& setupContext(helib::Context& context, long L)
  {
    buildModChain(context, L, /*c=*/3);
    if (helib_test::verbose) {
      context.zMStar.printout();
      std::cout << "# small primes = " << context.smallPrimes.card() << "\n";
      std::cout << "# ctxt primes = " << context.ctxtPrimes.card() << "\n";
      std::cout << "# bits in ctxt primes = "
                << long(context.logOfProduct(context.ctxtPrimes) / log(2.0) +
                        0.5)
                << "\n";
      std::cout << "# special primes = " << context.specialPrimes.card()
                << "\n";
      std::cout << "# bits in special primes = "
                << long(context.logOfProduct(context.specialPrimes) / log(2.0) +
                        0.5)
                << "\n";
      helib::fhe_stats = true;
    }
    return context;
  }

  GTestMatmul() :
      minimal((setGlobals(T::parameters.force_bsgs, T::parameters.force_hoist),
               T::parameters.ks_strategy == 3)),
      m(T::parameters.m),
      p(T::parameters.p),
      r(T::parameters.r),
      L(T::parameters.L),
      dim(T::parameters.dim),
      nt((NTL::SetNumThreads(T::parameters.nt), T::parameters.nt)),
      full(T::parameters.full),
      block(T::parameters.block),
      gens(T::parameters.gens),
      ords(T::parameters.ords),
      ks_strategy(T::parameters.ks_strategy),
      context((helib::setTimersOn(), m), p, r, gens, ords),
      secretKey(setupContext(context, L)),
      publicKey((secretKey.GenSecKey(), secretKey)),
      ea(context, context.alMod), // encrypted array with "full slots"
      matrixPtr(buildMat<typename T::MatrixType>(ea, dim)){};

  virtual void SetUp()
  {
    // we call addSomeFrbMatrices for all strategies except minimal
    switch (ks_strategy) {
    case 0:
      addSome1DMatrices(secretKey);
      addSomeFrbMatrices(secretKey);
      break;
    case 1:
      add1DMatrices(secretKey);
      addSomeFrbMatrices(secretKey);
      break;
    case 2:
      addBSGS1DMatrices(secretKey);
      addSomeFrbMatrices(secretKey);
      break;
    case 3:
      addMinimal1DMatrices(secretKey);
      addMinimalFrbMatrices(secretKey);
      break;
    default:
      throw std::invalid_argument("bad ks_strategy");
    }

    if (helib_test::verbose) {
      {
        std::stringstream ss;
        ss << publicKey;
        std::cout << "\n  |pubKey|=" << ss.tellp() << " bytes";
      }
      std::cout << ", security=" << context.securityLevel() << std::endl;
      std::cout << "  #threads=" << NTL::AvailableThreads();
      if (block)
        std::cout << "block-size=" << (ea.getPAlgebra().getOrdP());
      std::cout << ", vector-dimension="
                << (full ? ea.size() : ea.sizeOfDimension(dim)) << ", ";
      if (!full)
        std::cout << (ea.size() / ea.sizeOfDimension(dim))
                  << " products in parallel, ";
    }
  };

  virtual void TearDown()
  {
    if (helib_test::verbose) {
      helib::printAllTimers(std::cout);
#if (defined(__unix__) || defined(__unix) || defined(unix))
      struct rusage rusage;
      getrusage(RUSAGE_SELF, &rusage);
      std::cout << "  rusage.ru_maxrss=" << rusage.ru_maxrss << std::endl;
#endif
      helib::print_stats(std::cout);
    }
    helib::cleanupGlobals();
  };
};

// This is how we pack a matrix type and a reference to a Parameters object into
// a single type for gtest
template <class T, const Parameters& params>
struct MatrixTypeAndParams
{
  typedef T MatrixType;
  constexpr const static Parameters& parameters = params;
};

// These need to be at namespace scope, and cannot be anonymous since one cannot
// provide rvalue references as template non-type parameters.
// clang-format off
//SLOW
Parameters oneDimensionalMatrixParams     (18631, 2, 1, 300, 0, 1, 0, 0, std::vector<long>{}            , std::vector<long>{}      , 0, 0, 0);
Parameters oneDimensionalBlockMatrixParams(24295, 2, 1, 300, 0, 1, 0, 1, std::vector<long>{16386, 16427}, std::vector<long>{42, 16}, 0, 0, 0);

// The above commented-out parameters were used before the newnoise was brought in - it is too slow now.
//FAST
// Parameters oneDimensionalMatrixParams(91, 2, 1, 300, 0, 1, 0, 0, std::vector<long>{9, 3}, std::vector<long>{3, -2}, 0, 0, 0);
// clang-format on

using TypesToTest = ::testing::Types<
    MatrixTypeAndParams<helib::MatMul1D, oneDimensionalMatrixParams>,
    MatrixTypeAndParams<helib::MatMul1D, oneDimensionalBlockMatrixParams>>;

// Currently gtest does not intend on supporting the -Wall and -Wextra flags so
// this does not conform to the C++ standard. Until gtest changes, we need a
// pragma to ignore this warning.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
TYPED_TEST_SUITE(GTestMatmul, TypesToTest);
#pragma GCC diagnostic pop

TYPED_TEST(GTestMatmul, multipliesWithoutErrors)
{
  FHE_NTIMER_START(EncodeMartix_MatMul);
  // Templated class so explicit 'this' necessary
  const typename TypeParam::MatrixType& mat = *(this->matrixPtr);
  typename TypeParam::MatrixType::ExecType mat_exec(mat, (this->minimal));
  mat_exec.upgrade();
  FHE_NTIMER_STOP(EncodeMartix_MatMul);

  // choose a random plaintext vector and encrypt it
  helib::PlaintextArray v(this->ea);
  random(this->ea, v);

  // encrypt the random vector
  helib::Ctxt ctxt(this->secretKey);
  this->ea.encrypt(ctxt, this->secretKey, v);
  helib::Ctxt ctxt2 = ctxt;

  mat_exec.mul(ctxt);

  mul(v, mat); // multiply the plaintext vector

  helib::PlaintextArray v1(this->ea);
  this->ea.decrypt(ctxt, this->secretKey, v1); // decrypt the ciphertext vector

  EXPECT_TRUE(equals(this->ea, v, v1)); // check that we've got the right answer
};

} // namespace


/**
 * @file centroid.hh
 *
 * Finds centroid of submap and caches result for future lookup as
 * the centroid search is expensive.
 *
 * Signficant history (date, user, job code, action):
 *   2021-09-07, erandtke@carnegierobotics.com, 2036, Created file.
 */
#pragma once

#include <map> // centroid_cache_

#include <Eigen/Eigen> // Array2i

#include "cartographer/mapping/2d/submap_2d.h" // submap 2d

namespace cartographer {
namespace mapping {
namespace constraints {

class CentroidFinder
{
public:
    Eigen::Array2i FindCentroid(const SubmapId& submap_id, const Submap2D& submap);

private:
    std::map<SubmapId,Eigen::Array2i> centroid_cache_;
};

} // constraints
} // mapping
} // cartographer


// Copyright 2004 The Trustees of Indiana University.

// 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)

//  Authors: Douglas Gregor
//           Andrew Lumsdaine

// This program performs betweenness centrality (BC) clustering on the
// actor collaboration graph available at
// http://www.nd.edu/~networks/database/index.html and outputs the
// result of clustering in Pajek format.
//
// This program mimics the BC clustering algorithm program implemented
// by Shashikant Penumarthy for JUNG, so that we may compare results
// and timings.
#include <boost/graph/bc_clustering.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_traits.hpp>
#include <fstream>
#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>
#include <boost/lexical_cast.hpp>
#include <map>

using namespace boost;

struct Actor
{
  Actor(int id = -1) : id(id) {}

  int id;
};

typedef adjacency_list<vecS, vecS, undirectedS, Actor,
                       property<edge_centrality_t, double> > ActorGraph;
typedef graph_traits<ActorGraph>::vertex_descriptor Vertex;
typedef graph_traits<ActorGraph>::edge_descriptor Edge;

void load_actor_graph(std::istream& in, ActorGraph& g)
{
  std::map<int, Vertex> actors;

  std::string line;
  while (getline(in, line)) {
    std::vector<Vertex> actors_in_movie;

    // Map from the actor numbers on this line to the actor vertices
    typedef tokenizer<char_separator<char> > Tok;
    Tok tok(line, char_separator<char>(" "));
    for (Tok::iterator id = tok.begin(); id != tok.end(); ++id) {
      int actor_id = lexical_cast<int>(*id);
      std::map<int, Vertex>::iterator v = actors.find(actor_id);
      if (v == actors.end()) {
        Vertex new_vertex = add_vertex(Actor(actor_id), g);
        actors[actor_id] = new_vertex;
        actors_in_movie.push_back(new_vertex);
      } else {
        actors_in_movie.push_back(v->second);
      }
    }

    for (std::vector<Vertex>::iterator i = actors_in_movie.begin();
         i != actors_in_movie.end(); ++i) {
      for (std::vector<Vertex>::iterator j = i + 1;
           j != actors_in_movie.end(); ++j) {
        if (!edge(*i, *j, g).second) add_edge(*i, *j, g);
      }
    }
  }
}

template<typename Graph, typename VertexIndexMap, typename VertexNameMap>
std::ostream&
write_pajek_graph(std::ostream& out, const Graph& g,
                  VertexIndexMap vertex_index, VertexNameMap vertex_name)
{
  out << "*Vertices " << num_vertices(g) << '\n';
  typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
  for (vertex_iterator v = vertices(g).first; v != vertices(g).second; ++v) {
    out << get(vertex_index, *v)+1 << " \"" << get(vertex_name, *v) << "\"\n";
  }

  out << "*Edges\n";
  typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
  for (edge_iterator e = edges(g).first; e != edges(g).second; ++e) {
    out << get(vertex_index, source(*e, g))+1 << ' '
        << get(vertex_index, target(*e, g))+1 << " 1.0\n"; // HACK!
  }
  return out;
}

class actor_clustering_threshold : public bc_clustering_threshold<double>
{
  typedef bc_clustering_threshold<double> inherited;

 public:
  actor_clustering_threshold(double threshold, const ActorGraph& g,
                             bool normalize)
    : inherited(threshold, g, normalize), iter(1) { }

  bool operator()(double max_centrality, Edge e, const ActorGraph& g)
  {
    std::cout << "Iter: " << iter << " Max Centrality: "
              << (max_centrality / dividend) << std::endl;
    ++iter;
    return inherited::operator()(max_centrality, e, g);
  }

 private:
  unsigned int iter;
};

int main(int argc, char* argv[])
{
  std::string in_file;
  std::string out_file;
  double threshold = -1.0;
  bool normalize = false;

  // Parse command-line options
  {
    int on_arg = 1;
    while (on_arg < argc) {
      std::string arg(argv[on_arg]);
      if (arg == "-in") {
        ++on_arg; assert(on_arg < argc);
        in_file = argv[on_arg];
      } else if (arg == "-out") {
        ++on_arg; assert(on_arg < argc);
        out_file = argv[on_arg];
      } else if (arg == "-threshold") {
        ++on_arg; assert(on_arg < argc);
        threshold = lexical_cast<double>(argv[on_arg]);
      } else if (arg == "-normalize") {
        normalize = true;
      } else {
        std::cerr << "Unrecognized parameter \"" << arg << "\".\n";
        return -1;
      }
      ++on_arg;
    }

    if (in_file.empty() || out_file.empty() || threshold < 0) {
      std::cerr << "error: syntax is actor_clustering [options]\n\n"
                << "options are:\n"
                << "\t-in <infile>\tInput file\n"
                << "\t-out <outfile>\tOutput file\n"
                << "\t-threshold <value>\tA threshold value\n"
                << "\t-normalize\tNormalize edge centrality scores\n";
      return -1;
    }
  }

  ActorGraph g;

  // Load the actor graph
  {
    std::cout << "Building graph." << std::endl;
    std::ifstream in(in_file.c_str());
    if (!in) {
      std::cerr << "Unable to open file \"" << in_file << "\" for input.\n";
      return -2;
    }
    load_actor_graph(in, g);
  }

  // Run the algorithm
  std::cout << "Clusting..." << std::endl;
  betweenness_centrality_clustering(g,
    actor_clustering_threshold(threshold, g, normalize),
    get(edge_centrality, g));

  // Output the graph
  {
    std::cout << "Writing graph to file: " << out_file << std::endl;
    std::ofstream out(out_file.c_str());
    if (!out) {
      std::cerr << "Unable to open file \"" << out_file << "\" for output.\n";
      return -3;
    }
    write_pajek_graph(out, g, get(vertex_index, g), get(&Actor::id, g));
  }
  return 0;
}


// floop3 generated by makeloops.py Thu Jun 30 16:44:56 2011

#include <blitz/vector2.h>
#include <blitz/array.h>
#include <random/uniform.h>
#include <blitz/benchext.h>

#ifdef BZ_HAVE_VALARRAY
 #define BENCHMARK_VALARRAY
#endif

#ifdef BENCHMARK_VALARRAY
#include <valarray>
#endif

BZ_NAMESPACE(blitz)
extern void sink();
BZ_NAMESPACE_END

BZ_USING_NAMESPACE(blitz)
BZ_USING_NAMESPACE(std)

#if defined(BZ_FORTRAN_SYMBOLS_WITH_TRAILING_UNDERSCORES)
 #define floop3_f77 floop3_f77_
 #define floop3_f77overhead floop3_f77overhead_
 #define floop3_f90 floop3_f90_
 #define floop3_f90overhead floop3_f90overhead_
#elif defined(BZ_FORTRAN_SYMBOLS_WITH_DOUBLE_TRAILING_UNDERSCORES)
 #define floop3_f77 floop3_f77__
 #define floop3_f77overhead floop3_f77overhead__
 #define floop3_f90 floop3_f90__
 #define floop3_f90overhead floop3_f90overhead__
#elif defined(BZ_FORTRAN_SYMBOLS_CAPS)
 #define floop3_f77 FLOOP3_F77
 #define floop3_f77overhead FLOOP3_F77OVERHEAD
 #define floop3_f90 FLOOP3_F90
 #define floop3_f90overhead FLOOP3_F90OVERHEAD
#endif

extern "C" {
  void floop3_f77(const int& N, float* x, float* y, const float& a);
  void floop3_f77overhead(const int& N, float* x, float* y, const float& a);
  void floop3_f90(const int& N, float* x, float* y, const float& a);
  void floop3_f90overhead(const int& N, float* x, float* y, const float& a);

}

void VectorVersion(BenchmarkExt<int>& bench, float a);
void ArrayVersion(BenchmarkExt<int>& bench, float a);
void ArrayVersion_unaligned(BenchmarkExt<int>& bench, float a);
void ArrayVersion_misaligned(BenchmarkExt<int>& bench, float a);
void ArrayVersion_index(BenchmarkExt<int>& bench, float a);
void doTinyVectorVersion(BenchmarkExt<int>& bench, float a);
void F77Version(BenchmarkExt<int>& bench, float a);
#ifdef FORTRAN_90
void F90Version(BenchmarkExt<int>& bench, float a);
#endif
#ifdef BENCHMARK_VALARRAY
void ValarrayVersion(BenchmarkExt<int>& bench, float a);
#endif

const int numSizes = 80;
const bool runvector=false; // no point as long as Vector is Array<1>

int main()
{
    int numBenchmarks = 5;
    if (runvector) numBenchmarks++;
#ifdef BENCHMARK_VALARRAY
    numBenchmarks++;
#endif
#ifdef FORTRAN_90
    numBenchmarks++;
#endif

    BenchmarkExt<int> bench("floop3: $y = $y + a*$x", numBenchmarks);

    bench.setNumParameters(numSizes);

    Array<int,1> parameters(numSizes);
    Array<long,1> iters(numSizes);
    Array<double,1> flops(numSizes);

    parameters=pow(pow(2.,0.25),tensor::i)+tensor::i;
    flops = 2 * parameters;
    iters = 100000000L / flops;
    iters = where(iters<2, 2, iters);
    cout << iters << endl;

    bench.setParameterVector(parameters);
    bench.setIterations(iters);
    bench.setOpsPerIteration(flops);
    bench.setDependentVariable("flops");
    bench.beginBenchmarking();

    float a = 0.39123982498157938742;

    ArrayVersion(bench, a);
    ArrayVersion_unaligned(bench, a);
    ArrayVersion_misaligned(bench, a);
    ArrayVersion_index(bench, a);
    //doTinyVectorVersion(bench, a);
    F77Version(bench, a);
#ifdef FORTRAN_90
    F90Version(bench, a);
#endif
#ifdef BENCHMARK_VALARRAY
    ValarrayVersion(bench, a);
#endif

    if(runvector)
      VectorVersion(bench, a);

    bench.endBenchmarking();

    bench.saveMatlabGraph("floop3.m");
    return 0;
}

template<class T>
void initializeRandomDouble(T* data, int numElements, int stride = 1)
{
    ranlib::Uniform<T> rnd;

    for (int i=0; i < numElements; ++i)
        data[size_t(i*stride)] = rnd.random();
}

template<class T>
void initializeRandomDouble(valarray<T>& data, int numElements, int stride = 1)
{
    ranlib::Uniform<T> rnd;

    for (int i=0; i < numElements; ++i)
        data[size_t(i*stride)] = rnd.random();
}

void VectorVersion(BenchmarkExt<int>& bench, float a)
{
    bench.beginImplementation("Vector<T>");

    while (!bench.doneImplementationBenchmark())
    {
        int N = bench.getParameter();
        long iters = bench.getIterations();

        cout << bench.currentImplementation() << ": N = " << N << endl;

        Vector<float> x(N);
        initializeRandomDouble(x.data(), N);
        Vector<float> y(N);
        initializeRandomDouble(y.data(), N);

        bench.start();
        for (long i=0; i < iters; ++i)
        {
            y = y + a*x;
            sink();
        }
        bench.stop();

        bench.startOverhead();
        for (long i=0; i < iters; ++i) {
            sink();
	}

        bench.stopOverhead();
    }

    bench.endImplementation();
}

  void ArrayVersion(BenchmarkExt<int>& bench, float a)
{
    bench.beginImplementation("Array<T,1>");

    while (!bench.doneImplementationBenchmark())
    {
        int N = bench.getParameter();
        long iters = bench.getIterations();

        cout << bench.currentImplementation() << ": N = " << N << endl;

        Array<float,1> x(N);
        initializeRandomDouble(x.dataFirst(), N);
        Array<float,1> y(N);
        initializeRandomDouble(y.dataFirst(), N);

        bench.start();
        for (long i=0; i < iters; ++i)
        {
            y = y + a*x;
            sink();
        }
        bench.stop();

        bench.startOverhead();
        for (long i=0; i < iters; ++i) {
            sink();
	}

        bench.stopOverhead();
    }

    bench.endImplementation();
}

  void ArrayVersion_index(BenchmarkExt<int>& bench, float a)
{
    bench.beginImplementation("Array<T,1> (indexexpr.)");

    while (!bench.doneImplementationBenchmark())
    {
        int N = bench.getParameter();
        long iters = bench.getIterations();

        cout << bench.currentImplementation() << ": N = " << N << endl;

        Array<float,1> x(N);
        initializeRandomDouble(x.dataFirst(), N);
        Array<float,1> y(N);
        initializeRandomDouble(y.dataFirst(), N);

        bench.start();
        for (long i=0; i < iters; ++i)
        {
            y = y(tensor::i) + a*x(tensor::i);;
            sink();
        }
        bench.stop();

        bench.startOverhead();
        for (long i=0; i < iters; ++i) {
            sink();
	}

        bench.stopOverhead();
    }

    bench.endImplementation();
}

  void ArrayVersion_unaligned(BenchmarkExt<int>& bench, float a)
{
    bench.beginImplementation("Array<T,1> (unal.)");

    while (!bench.doneImplementationBenchmark())
    {
        int N = bench.getParameter();
        long iters = bench.getIterations();

        cout << bench.currentImplementation() << ": N = " << N << endl;

    Array<float,1> xfill(N+1);
    Array<float,1> x(xfill(Range(1,N)));
    initializeRandomDouble(x.dataFirst(), N);

    Array<float,1> yfill(N+1);
    Array<float,1> y(yfill(Range(1,N)));
    initializeRandomDouble(y.dataFirst(), N);

        bench.start();
        for (long i=0; i < iters; ++i)
        {
            y = y + a*x;
            sink();
        }
        bench.stop();

        bench.startOverhead();
        for (long i=0; i < iters; ++i) {
            sink();
	}

        bench.stopOverhead();
    }

    bench.endImplementation();
}

  void ArrayVersion_misaligned(BenchmarkExt<int>& bench, float a)
{
    bench.beginImplementation("Array<T,1> (misal.)");

    while (!bench.doneImplementationBenchmark())
    {
        int N = bench.getParameter();
        long iters = bench.getIterations();

        cout << bench.currentImplementation() << ": N = " << N << endl;

    Array<float,1> xfill(N+2);
    Array<float,1> x(xfill(Range(0,N+0-1)));
    initializeRandomDouble(x.dataFirst(), N);

    Array<float,1> yfill(N+2);
    Array<float,1> y(yfill(Range(1,N+1-1)));
    initializeRandomDouble(y.dataFirst(), N);

        bench.start();
        for (long i=0; i < iters; ++i)
        {
            y = y + a*x;
            sink();
        }
        bench.stop();

        bench.startOverhead();
        for (long i=0; i < iters; ++i) {
            sink();
	}

        bench.stopOverhead();
    }

    bench.endImplementation();
}

#ifdef BENCHMARK_VALARRAY
void ValarrayVersion(BenchmarkExt<int>& bench, float a)
{
    bench.beginImplementation("valarray<T>");

    while (!bench.doneImplementationBenchmark())
    {
        int N = bench.getParameter();
        cout << bench.currentImplementation() << ": N = " << N << endl;

        long iters = bench.getIterations();

        valarray<float> x(N);
        initializeRandomDouble(x, N);
        valarray<float> y(N);
        initializeRandomDouble(y, N);

        bench.start();
        for (long i=0; i < iters; ++i)
        {
            y = y + a*x;
            sink();
        }
        bench.stop();

        bench.startOverhead();
        for (long i=0; i < iters; ++i) {
	  sink();
	}
        bench.stopOverhead();
    }

    bench.endImplementation();
}
#endif

void F77Version(BenchmarkExt<int>& bench, float a)
{
    bench.beginImplementation("Fortran 77");

    while (!bench.doneImplementationBenchmark())
    {
        int N = bench.getParameter();
        cout << bench.currentImplementation() << ": N = " << N << endl;

        int iters = bench.getIterations();

        float* x = new float[N];
        initializeRandomDouble(x, N);
        float* y = new float[N];
        initializeRandomDouble(y, N);

        bench.start();
        for (int iter=0; iter < iters; ++iter)
            floop3_f77(N, x, y, a);
        bench.stop();

        bench.startOverhead();
        for (int iter=0; iter < iters; ++iter)
            floop3_f77overhead(N, x, y, a);

        bench.stopOverhead();

        delete [] x;
        delete [] y;

    }

    bench.endImplementation();
}

#ifdef FORTRAN_90
void F90Version(BenchmarkExt<int>& bench, float a)
{
    bench.beginImplementation("Fortran 90");

    while (!bench.doneImplementationBenchmark())
    {
        int N = bench.getParameter();
        cout << bench.currentImplementation() << ": N = " << N << endl;

        int iters = bench.getIterations();

        float* x = new float[N];
        initializeRandomDouble(x, N);
        float* y = new float[N];
        initializeRandomDouble(y, N);

        bench.start();
        for (int iter=0; iter < iters; ++iter)
            floop3_f90(N, x, y, a);
        bench.stop();

        bench.startOverhead();
        for (int iter=0; iter < iters; ++iter)
            floop3_f90overhead(N, x, y, a);

        bench.stopOverhead();
        delete [] x;
        delete [] y;

    }

    bench.endImplementation();
}
#endif