#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
#include <iomanip>
#include <chrono>
using namespace std;
using namespace std::chrono;

class Calculation {
	private:
		int element_count;  // element_count should be updated inside class
		time_point<high_resolution_clock> start_time, end_time;
		duration<double> elapsed_duration;
		ofstream file;
	public:
		int ask_int(string, int);
		double ask_double(string, int, int);
		void calculate_elements(double, double, int, double[]);
		void output_to_console(double[]);
		void output_to_file(double[]);
		void create_file();
		void close_file();
		void start_timer();
		void end_timer();
};


int main() {
	Calculation calc;
	int L;
	double x, epsilon;
	
	// Create file
	calc.create_file();
	
	// Enter max element count
	L = calc.ask_int("Enter maximum element count [L > 0]: ", 0);
	
	// Declare array
	double A[L];
	
	// Enter parameter x
	x = calc.ask_double("Enter parameter X [|X| < 1]: ", -1, 1);
	
	// Enter parameter epsilon
	epsilon = calc.ask_double("Enter parameter epsilon [0 < eps < 1]: ", 0, 1);
	
	// Start timer
	calc.start_timer();
	
	// Calculate elements
	calc.calculate_elements(x, epsilon, L, A);
	
	// Stop timer
	calc.end_timer();
	
	// Output elements to console
	calc.output_to_console(A);
	
	// Output elements to file
	calc.output_to_file(A);
	
	// Close file
	calc.close_file();
	
	return 0;
}

// Output elements to file
void Calculation::output_to_file(double A[]) {
	int i;
	
	// Print calculation time
	file << endl;
	file << "Calculation time: " << fixed << setprecision(12) << elapsed_duration.count() << " seconds." << endl;
	file << endl;	
	
	// Output elements
	for (i = 0; i < element_count; i++) {
		file << "Element " << i + 1 << ": " << fixed << setprecision(12) << A[i] << endl;
	}
}

// Output elements to console
void Calculation::output_to_console(double A[]) {
	int i;

	// Print calculation time
	cout << endl;
	cout << "Calculation time: " << fixed << setprecision(12) << elapsed_duration.count() << " seconds." << endl;
	cout << endl;	
	
	// Output elements
	for (i = 0; i < element_count; i++) {
		cout << "Element " << i + 1 << ": " << fixed << setprecision(12) << A[i] << endl;
	}
}

// Calculate elements
void Calculation::calculate_elements(double x, double epsilon, int L, double A[]) {
	int i;
	A[0] = x;
	
	for (i = 1; i < L; i++) {
        if (i == 1) {
            A[i] = -(x * x) / 3.0;
        } else {
            A[i] = -(A[i - 1] * x * x * (2.0 * i - 1)) / (2.0 * i + 1);
        }

        if (i >= 1 && fabs(A[i] - A[i - 1]) <= epsilon) {
            break;
        }
    }
	
	element_count = i;
}

// Create file
void Calculation::create_file() {
	string output_file_name;
	
	cout << "Enter output filename: ";
	cin >> output_file_name;
	
	output_file_name += ".txt";
	
	file.open(output_file_name);
}

// Close file
void Calculation::close_file() {
	file.close();
}

// Ask for integer input
int Calculation::ask_int(string prompt, int low_limit) {
	int value;
	
	do {
		cout << prompt;
		cin >> value;
	} while(value <= low_limit); 
	
	return value;
}

// Ask for double input
double Calculation::ask_double(string prompt, int low_limit, int high_limit) {
	double value;
	
	do {
		cout << prompt;
		cin >> value;
	} while(value <= low_limit || value >= high_limit); 
	
	return value;	
}

// Start time
void Calculation::start_timer() {
	start_time = high_resolution_clock::now();
}

// End time and calculate the duration
void Calculation::end_timer() {
	end_time = high_resolution_clock::now();
	elapsed_duration = duration_cast<duration<double>>(end_time - start_time);
}


