/* draws bifurcation diagram of the iterator given in double calculate(...);*/

#include <iostream>
#include <fstream>
#include <math.h>
#include "plotter.h"

// target image size
#define XSIZE 1024
#define YSIZE 768

// source parameters
#define X_START -2.1
#define X_END 4.05
#define Y_START -0.5
#define Y_END 1.5

// warmup iterations
#define WARMUP 200
#define ATTRACTOR_LIMIT	80 // don't post any attractors past this limit

// "real" iterations
#define POINTS_PER_LINE YSIZE*10

// special values
#define BACKGROUND	1500
#define ATTRACTOR	2500

double calculate(double r, double val) {

	return (r*val*((double)1-val));
}

inline double convert(double min, double max, double current, double destmin,
		double destmax) {

	// converts a number in the range [min..max] to a number in the
	// range [destmin..destmax]
	return (destmin+((current-min)/(max-min)*(destmax-destmin)));
}

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

// yes, you can buffer overflow this. no, it doesn't use C++ strings. Sosumi :)
bool dumpToFile(char * file, double * array, int array_size, 
		double background_value) {

	int c_val;

	ofstream outputFile(file);

	if (!outputFile) return(false);

	for (int counter = 0; counter < array_size; counter++) {
		c_val = (int)rint(array[counter]);
		if (c_val != background_value) {
			if (c_val == ATTRACTOR) {
				outputFile << (char)100;
				outputFile << (char)0;
				outputFile << (char)0;
			} else {
				outputFile << (char)c_val;
				outputFile << (char)c_val;
				outputFile << (char)c_val;
			}
		} else {
			outputFile << (char)0;
			outputFile << (char)0;
			outputFile << (char)130;
		}
	}
		
	outputFile.close();
}


// Bifurcation plotting algorithm:

// for x axis = 0 to 4
	// initialize val by calculating N times [ val = calculate(r, val) ]
	// for f = 0 to iterations
		// val = calculate(r, val)
		// plot(x, y, f)

main() {

	double increment;
	double calculatedValue;
	int shade;

	// clean up the array first
	
	double * output = new double [XSIZE*YSIZE];

	for (int ix = 0; ix < (XSIZE*YSIZE); ix++) output[ix] = 1500;
	
	// plot.
	Plotter tool;

	tool.setSourceBoundaries(X_START, Y_START, X_END, Y_END);
	tool.setTargetBoundaries(0, 0, XSIZE, YSIZE);
	tool.setTargetArray(output);
	increment = tool.getStepsX(XSIZE);
	
	for (double counter = (double)X_START; counter < (double)X_END;
			counter += increment) {

		cout << counter << "\n";

		// warmup to get on attractor
		// plot "gravity fields" as well
		calculatedValue = 0.5;
		for (int sec = 0; sec < WARMUP; sec++) {
			calculatedValue = calculate(counter, calculatedValue);
			if ((sec < ATTRACTOR_LIMIT) && 
					(sec % (ATTRACTOR_LIMIT/10))) {
				tool.plot(counter, calculatedValue, ATTRACTOR);
			}
		}
		
		// start plotting
		for (int sec = 0; sec < POINTS_PER_LINE; sec++) {
			shade = (int)rint((double)sec/POINTS_PER_LINE*255);
			calculatedValue = calculate(counter, calculatedValue);
			tool.plot(counter, calculatedValue, shade);
		}
	}
	
	dumpToFile("output.raw", output, XSIZE*YSIZE, BACKGROUND);
}
