int main()
{
	Expression expr("(cos(x)^0.5*cos(200*x)+abs(x)^0.5-0.7)*(4-x^2)^0.01");

	const double from = -1.5;
	const double to = 1.5;
	const size_t steps = 1000000;

	const size_t bench_repeats = 15;

	double exprTime = avgTime([&]
	{
		const Parameters &parameters = expr.GetParameters();

		ValueType *xValue = parameters.at("x");
		sumInInterval(from, to, steps, [&expr, xValue](double value)
		{
			*xValue = value;
			return expr.Execute();
		});
	}, bench_repeats);

	std::cout << "Expression: " << exprTime << "ms" << std::endl;

	double nativeTime = avgTime([&from, &to, &steps]
	{
		sumInInterval(from, to, steps, [](double value)
		{
			return (pow(cos(value), 0.5) * cos(200 * value) + pow(abs(value), 0.5) - 0.7) * (4 - pow(pow(value, 2), 0.01));
		});
	}, bench_repeats);

	double percantage = (exprTime / nativeTime * 100.0) - 100;

	std::cout << "Native: " << nativeTime << "ms"
		<< " (" << abs(percantage) << "% " << (percantage > 0 ? "faster" : "slower") << ")"
		<< std::endl << std::endl;

	return 0;
}
int main(int argc, const char** argv) {
    
    boost::program_options::options_description desc("Allowed options");
    desc.add_options()
        ("help", "produce help message")
        ("input", boost::program_options::value<std::string>(), "the folder to process")
        ("lambda", boost::program_options::value<double>()->default_value(0.5), "lambda")
        ("sigma", boost::program_options::value<double>()->default_value(5.0), "sigma")
        ("four-connected", "use 4-connected")
        ("superpixels", boost::program_options::value<int>()->default_value(400), "number of superpixels")
        ("time", boost::program_options::value<std::string>(), "time the algorithm and save results to the given directory")
        ("process", "show additional information while processing")
        ("csv", "save segmentation as CSV file")
        ("contour", "save contour image of segmentation")
        ("mean", "save mean colored image of segmentation")
        ("output", boost::program_options::value<std::string>()->default_value("output"), "specify the output directory (default is ./output)");

    boost::program_options::positional_options_description positionals;
    positionals.add("input", 1);
    
    boost::program_options::variables_map parameters;
    boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(desc).positional(positionals).run(), parameters);
    boost::program_options::notify(parameters);

    if (parameters.find("help") != parameters.end()) {
        std::cout << desc << std::endl;
        return 1;
    }
    
    boost::filesystem::path outputDir(parameters["output"].as<std::string>());
    if (!boost::filesystem::is_directory(outputDir)) {
        boost::filesystem::create_directory(outputDir);
    }
    
    boost::filesystem::path inputDir(parameters["input"].as<std::string>());
    if (!boost::filesystem::is_directory(inputDir)) {
        std::cout << "Image directory not found ..." << std::endl;
        return 1;
    }
    
    bool process = false;
    if (parameters.find("process") != parameters.end()) {
        process = true;
    }
    
    std::vector<boost::filesystem::path> pathVector;
    std::vector<boost::filesystem::path> images;
    
    std::copy(boost::filesystem::directory_iterator(inputDir), boost::filesystem::directory_iterator(), std::back_inserter(pathVector));

    std::sort(pathVector.begin(), pathVector.end());
    
    std::string extension;
    int count = 0;
    
    for (std::vector<boost::filesystem::path>::const_iterator iterator (pathVector.begin()); iterator != pathVector.end(); ++iterator) {
        if (boost::filesystem::is_regular_file(*iterator)) {
            
            // Check supported file extensions.
            extension = iterator->extension().string();
            std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
            
            if (extension == ".png" || extension == ".jpg" || extension == ".jpeg") {
                images.push_back(*iterator);
                
                if (process == true) {
                    std::cout << "Found " << iterator->string() << " ..." << std::endl;
                }
                
                ++count;
            }
        }
    }
    
    std::cout << count << " images total ..." << std::endl;
    
    boost::timer timer;
    double totalTime = 0;
    
    int eightConnected = 1;
    if (parameters.find("four-connected") != parameters.end()) {
        eightConnected = 0;
    }
    
    int superpixels = parameters["superpixels"].as<int>();
    int kernel = 0;
    double lambda = parameters["lambda"].as<double>();
    double sigma = parameters["sigma"].as<double>();
    MERCLazyGreedy merc;
    
    cv::Mat time(images.size(), 2, cv::DataType<double>::type);
    for(std::vector<boost::filesystem::path>::iterator iterator = images.begin(); iterator != images.end(); ++iterator) {
        cv::Mat mat = cv::imread(iterator->string());
        
        Image<RGBMap> inputImage;
        MERCInputImage<RGBMap> input;

        inputImage.Resize(mat.cols, mat.rows, false);

        for (int i = 0; i < mat.rows; ++i) {
            for (int j = 0; j < mat.cols; ++j) {
                RGBMap color((int) mat.at<cv::Vec3b>(i, j)[2], (int) mat.at<cv::Vec3b>(i, j)[1], (int) mat.at<cv::Vec3b>(i, j)[0]);
                inputImage.Access(j, i) = color;
            }
        }

        input.ReadImage(&inputImage, eightConnected);
		
        timer.restart();
        int index = std::distance(images.begin(), iterator);
        
        merc.ClusteringTreeIF(input.nNodes_, input, kernel, sigma*mat.channels(), lambda*1.0*superpixels, superpixels);
        
        time.at<double>(index, 1) = timer.elapsed();
        time.at<double>(index, 0) = index + 1;
        totalTime += time.at<double>(index, 1);
        
	vector<int> label = MERCOutputImage::DisjointSetToLabel(merc.disjointSet_);
        
        int** labels = new int*[mat.rows];
        for (int i = 0; i < mat.rows; ++i) {
            labels[i] = new int[mat.cols];
            
            for (int j = 0; j < mat.cols; ++j) {
                labels[i][j] = label[j + i*mat.cols];
            }
        }
        
        Integrity::relabel(labels, mat.rows, mat.cols);
        
        boost::filesystem::path extension = iterator->filename().extension();
        int position = iterator->filename().string().find(extension.string());
        
        if (parameters.find("contour") != parameters.end()) {
            
            std::string store = outputDir.string() + DIRECTORY_SEPARATOR + iterator->filename().string().substr(0, position) + "_contours.png";
            
            int bgr[] = {0, 0, 204};
            cv::Mat contourImage = Draw::contourImage(labels, mat, bgr);
            cv::imwrite(store, contourImage);
            
            if (process == true) {
                std::cout << "Image " << iterator->string() << " with contours saved to " << store << " ..." << std::endl;
            }
        }

        if (parameters.find("mean") != parameters.end()) {
            
            std::string store = outputDir.string() + DIRECTORY_SEPARATOR + iterator->filename().string().substr(0, position) + "_mean.png";

            cv::Mat meanImage = Draw::meanImage(labels, mat);
            cv::imwrite(store, meanImage);

            if (process == true) {
                std::cout << "Image " << iterator->string() << " with mean colors saved to " << store << " ..." << std::endl;
            }
        }

        if (parameters.find("csv") != parameters.end()) {
            
            boost::filesystem::path csvFile(outputDir.string() + DIRECTORY_SEPARATOR + iterator->filename().string().substr(0, position) + ".csv");
            Export::CSV(labels, mat.rows, mat.cols, csvFile);

            if (process == true) {
                std::cout << "Labels for image " << iterator->string() << " saved in " << csvFile.string() << " ..." << std::endl;
            }
        }
        
        for (int i = 0; i < mat.rows; ++i) {
            delete[] labels[i];
        }
        
        delete[] labels;
    }
    
    if (parameters.find("time") != parameters.end()) {
        
        boost::filesystem::path timeDir(parameters["time"].as<std::string>());
        if (!boost::filesystem::is_directory(timeDir)) {
            boost::filesystem::create_directories(timeDir);
        }
        
        boost::filesystem::path timeImgFile(timeDir.string() + DIRECTORY_SEPARATOR + "eval_time_img.txt");
        boost::filesystem::path timeFile(timeDir.string() + DIRECTORY_SEPARATOR + "eval_time.txt");
        
        Export::BSDEvaluationFile<double>(time, 4, timeImgFile);
        
        cv::Mat avgTime(1, 1, cv::DataType<double>::type);
        avgTime.at<double>(0, 0) = totalTime/((double) images.size());
        Export::BSDEvaluationFile<double>(avgTime, 6, timeFile);
    }
    
    std::cout << "On average, " << totalTime/images.size() << " seconds needed ..." << std::endl;
    
    return 0;
}