vector<double> SimulatedAnnealing::FindOptimalParameters(const vector<double>& initial_parameters)
{
    vector<double> current_parameters = initial_parameters;

    for (size_t i = 1; i <= max_number_of_iterations_; ++i)
    {
        vector<double> neighbour = GetRandomNeighbour(current_parameters);
        double current_value = CountObjectiveFunctionValueToMinimize(current_parameters);
        double neighbour_value = CountObjectiveFunctionValueToMinimize(neighbour);

        double t = CountTemperature(i);
        double probability = CountAcceptanceProbability(current_value, neighbour_value, t);

        if (Random::Instance().CheckEventOutcome(probability))
        {
            current_parameters = neighbour;
        }
    }

    return current_parameters;
}
void 
EdgeLayer(QImage* img, QImage* canvas, int radius, double hue_distortion, double strength)
///
/// This final layer repaints over areas determined to be edges in order to bring smaller details
/// that have been covered by points back into the picture. The same color distortions are used
/// as in the main layer.
///
/// @param img
///  The reference image.
///
/// @param canvas
///  The canvas to be painted to.
///
/// @param radius
///  The radius of the points to be painted.
///
/// @param hue_distortion
///  Determines the strength of the hue distortion where 1.0 is very distorted and 0.0 is not distorted.
///
/// @param strength
///  The strength of the pointillistic filter, where 1.0 is very strong and 0.0 is very weak.
///
/// @return
///  Nothing.
///
{
	hue_distortion = hue_distortion*strength;
	if( strength < 0.5 ) 
	{
		int new_radius = (int)radius*strength*2;
		if( 1.0*new_radius < radius*strength*2 ) new_radius++;
		radius = new_radius;
		if( radius < 1 ) radius = 1;
	}
	
	uchar* edges = new uchar[img->width()*img->height()];
	ImageProcessing::CannyEdgeDetection( img->bits(), edges, img->width(), img->height(), 4 );

	uchar* gray = new uchar[img->width()*img->height()];
	ImageProcessing::ConvertToOneChannel( img->bits(), gray, img->width(), img->height());

	uchar* smoothed_gray = new uchar[img->width()*img->height()];
	int kernel = radius;
	if(kernel%2 == 0) kernel++;
	if(kernel < 3) kernel = 3;
	ImageProcessing::GaussianBlur( gray, smoothed_gray, img->width(), img->height(), 1, kernel );
	delete [] gray;

	// Clear the depth buffer ready for painting
	uchar* depth_buffer = new uchar[img->width()*img->height()];
	for( int i = 0; i < img->width()*img->height(); i++ )
	{
		depth_buffer[i] = 0;
	}

	// If there is an edge, find the greatest error in the edge's neighbourhood
	// and at a new stroke at this point.
	for( int y = 0; y < img->height(); y++ ) 
	{
		for( int x = 0; x < img->width(); x++ ) 
		{
			if( edges[y*img->width() + x] > 0 ) 
			{

				// Find the brightest and and darkest spots in the neighbourhood
				int brightest = 0;
				int darkest = 255;
				QPoint brightest_pos = QPoint(0, 0);
				QPoint darkest_pos = QPoint(0, 0);
				for( int j = y - radius; j <= y + radius; j++ ) {
					for( int i = x - radius; i <= x + radius; i++ ) {
						if( i >= 0 && j >= 0 && i < img->width() && j < img->height() ) {
							int intensity = smoothed_gray[j*img->width() + i];
							if(intensity > brightest) {
								brightest_pos = QPoint(i, j);
								brightest = intensity;
							}
							if(intensity < darkest) {
								darkest_pos = QPoint(i, j);
								darkest = intensity;
							}
						}
					}
				}

				// For each position in the neighbourhood, find if most spots
				// are closer to the brightest or darkest
				int dark = 0;
				int bright = 0;
				for( int j = y - radius; j <= y + radius; j++ ) 
				{
					for( int i = x - radius; i <= x + radius; i++ ) 
					{
						if( i >= 0 && j >= 0 && i < img->width() && j < img->height() ) {
							int intensity = smoothed_gray[j*img->width() + i];
							int bright_diff = brightest - intensity;
							int dark_diff = intensity - darkest;
							if(bright_diff < dark_diff) 
							{
								bright++;
							} 
							else 
							{
								dark++;
							}
						}
					}
				}

				// Paint at the side that needs defining
				QPoint new_point;
				if(bright < dark && bright != 0) 
				{
					new_point = brightest_pos;
				} 
				else if(dark != 0) 
				{
					new_point = darkest_pos;
				} 
				else 
				{
					new_point = QPoint(x, y);
				}

				// Paint circle at this position
				QColor hsv = QColor(img->pixel( new_point )).toHsv();
				int hue = hsv.hue();
				int val = hsv.value();
				int sat = hsv.saturation();

				// Find closest hue in palette
				int new_pos = GetPaletteHuePosition( hsv.hue() );

				if( ( rand()%100)/100.0 < strength ) 
				{
					hue = GetRandomNeighbour( new_pos );
				} 
				else 
				{
					hue = chevreul[new_pos];
				}

				// Hue distortion
				double r = ( rand()%100 )/100.0;
				if( ( r < hue_distortion && new_pos != 8 ) || r < hue_distortion/3 ) 
				{
					int n = ChangeHue(smoothed_gray[new_point.y()*img->width() + new_point.x()]/256.0)*2;
					if( n > -1 ) 
					{
						new_pos = n;
						if( sat < 70 && val < 0.3 ) sat = 70;
					}
					hue = chevreul[new_pos];
				}
				sat = ChangeSaturation( sat, val, 0.35*strength, strength );
				int z = rand()%256;
				hsv.setHsv( hue, sat, val );
				DrawRandomCircle( canvas, new_point, hsv.toRgb(), radius - 1, z, depth_buffer );
			}
		}
	}

	delete [] smoothed_gray;
	delete [] edges;
	delete [] depth_buffer;
}
void 
MainLayer( QImage* img, QImage* canvas, int radius, double strength )
///
/// Paint the main pointillism layer, adding smaller details and more color distortion.
/// Points are painted where the color error between the canvas and the original image
/// is high. Color difference is based on color intensity (i.e. brightness) to avoid
/// difference due to hue distortion. Saturation distortion and divisionism are applied
/// in addition to palette restriction.
///
/// @param img
///  The reference image.
///
/// @param canvas
///  The canvas to be painted to.
///
/// @param radius
///  The radius of the points being painted.
///
/// @param strength
///  The strength of the pointillistic filter, where 1.0 is very strong and 0.0 is very weak.
///
/// @return
///  Nothing.
///
{
	// Adjust the point radius based on the strength of the filter
	if(strength < 0.5) 
	{
		int new_radius = (int)radius*strength*2;
		if(1.0*new_radius < radius*strength*2) new_radius++;
		radius = new_radius;
		if(radius < 1) radius = 1;
	}

	// Get gray scale of original image
	uchar* gray = new uchar[img->width()*img->height()];
	ImageProcessing::ConvertToOneChannel( img->bits(), gray, img->width(), img->height() );

	// Blur the grayscale image
	uchar* smoothed_gray = new uchar[img->width()*img->height()];
	int kernel = radius;
	if(kernel%2 == 0) kernel++;
	if(kernel < 3) kernel = 3;
	ImageProcessing::GaussianBlur( gray, smoothed_gray, img->width(), img->height(), 1, kernel );
	delete [] gray;

	// Clear the depth buffer ready for painting
	uchar* depth_buffer = new uchar[img->width()*img->height()];
	for( int i = 0; i < img->width()*img->height(); i++ )
	{
		depth_buffer[i] = 0;
	}

	// At each grid point, find maximum error based on difference
	// between intensity at canvas and intensity of blurred image
	// Paint stroke at this location
	for( int y = (int)radius/2; y < img->height(); y += radius ) 
	{
		for( int x = (int)radius/2; x < img->width(); x += radius ) 
		{
			int total_error = 0;
			int max_error = 0;
			QPoint max_error_at = QPoint(0, 0);

			// Get error of each pixel in the neighbourhood
			int min_x = x - radius/2;
			int min_y = y - radius/2;
			int max_x = x + radius/2;
			int max_y = y + radius/2;

			if(min_x < 0) min_x = 0;
			if(min_y < 0) min_y = 0;
			if(max_x >= img->width()) max_x = img->width() - 1;
			if(max_y >= img->height()) max_y = img->height() - 1;

			for( int j = min_y; j <= max_y; j++ ) 
			{
				for( int i = min_x; i <= max_x; i++ ) 
				{
					
					// Get error at this pixel
					int intensity = QColor(canvas->pixel( i, j )).toHsv().value();
					int error = abs(intensity - smoothed_gray[j*img->width() + i]);

					// Update error stats
					total_error += error;
					if( error > max_error ) 
					{
						max_error = error;
						max_error_at = QPoint( i, j );
					}
				}
			}

			// If the total error is above a threshold
			// Paint a stroke at the area of max error
			if( total_error > 10*strength ) 
			{
				QColor hsv = QColor(img->pixel( x, y )).toHsv();
				int hue = hsv.hue();
				int sat = hsv.saturation();
				int v = hsv.value();

				// Find closest hue in palette, make a method that does this
				int new_pos = GetPaletteHuePosition( hsv.hue() );
				if( (rand()%100)/100.0 < strength ) 
				{
					hue = GetRandomNeighbour(new_pos);
				} 
				else 
				{
					hue = chevreul[new_pos];
				}
				
				sat = ChangeSaturation(sat, v, 0.35*strength, strength);
				hsv.setHsv( hue, sat, v );

				int z = rand()%256;
				DrawRandomCircle(canvas, max_error_at, hsv.toRgb(), radius, z, depth_buffer);
			}
		}
	}
	delete [] smoothed_gray;
	delete [] depth_buffer;
}