#include <boost/range/algorithm/nth_element.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/preprocessor/repeat.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/noncopyable.hpp>
#include <boost/timer/timer.hpp>
//#include <boost/timer.hpp>

#include <iostream>
#include <ostream>
#include <cstdlib>
#include <vector>
#include <ctime>

using namespace std;
using namespace boost;

// ______________________________ //

const int series = 33;
const int deepness = 5;
#define REPEATS 32

// ______________________________ //

bool error_check()
{
    //return time(0) < 1;
    static volatile bool t = false;
    return t;
}
void *mallocator()
{
    return malloc(4);
}

// ______________________________ //

class Holder : private noncopyable
{
    void *data;
public:
    Holder()
        : data(mallocator())
    {}
    ~Holder()
    {
        free(data);
    }
};
template<int iter> struct exceptions
{
    static void call()
    {
        #define EXP_REP(z, i, _) Holder h##i; exceptions<iter-1>::call();
            BOOST_PP_REPEAT(REPEATS,EXP_REP,t)
    }
};
template<> struct exceptions<0>
{
    static void call()
    {
        if(error_check()) throw false;
    }
};

// ______________________________ //

template<int iter> struct ifgoto
{
    static bool call()
    {
        bool result = true;

        #define IFG_REP0(z, i, _) void *d##i;
            BOOST_PP_REPEAT(REPEATS,IFG_REP0,t)

        #define IFG_REP1(z, i, _) d##i = mallocator(); if(! (result=ifgoto<iter-1>::call()) ) goto ERROR##i;
            BOOST_PP_REPEAT(REPEATS,IFG_REP1,t)

        #define IFG_REP_AUX(i) BOOST_PP_CAT(ERROR,i): free(BOOST_PP_CAT(d,i));
        #define IFG_REP2(z, i, _) IFG_REP_AUX( BOOST_PP_SUB(BOOST_PP_SUB(REPEATS,1),i) )
            BOOST_PP_REPEAT(REPEATS,IFG_REP2,t)

        return result;
    }
};
template<> struct ifgoto<0>
{
    static bool call()
    {
        return error_check() == false;
    }
};

// ______________________________ //

template<template<int> class test_case>
void test(const char *name)
{
    cout << name;
    vector<double> times;
    for(int i=0;i!=series;++i)
    {
        //timer t;
        timer::cpu_timer t;
        test_case<deepness>::call();
        times.push_back(double(t.elapsed().user));
    }
    vector<double>::iterator median = times.begin() + times.size()/2;
    nth_element(times,median);
    cout << "\tmedian is " << (*median)*1e-6 << "ms" << endl;
}

int main()
{
    try
    {
        for(int i=0;i!=2;++i)
        {
            test<exceptions>("exceptions");
            test<ifgoto>("if-error-goto");
        }
    }
    catch(...){}
}
