/*
	1. Failist F1 sisestatakse ja paigutatakse pinusse reaalarvulise massiivi A elemendid.
	2. Failist F2 sisestatakse ja paigutatakse pinusse reaalarvulise massiivi A elemendid.
	3. Klaviatuurilt sisestatakse reaalarrv X.
	4. Moodustatakse ja väljastatakse faili F3 masiiv K elementidega:
		K_0 = 1
		K_i = K_i-1 * A_i * X^i / (A_i^2 + B_i^2), i = 1 ... min(N,M)

	Lisasin compileri build command -lm lõppu, <math.h> teegi jaoks.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef struct {
    size_t stackSize; //stacki maksimaalne suurus
    size_t stackPointer; //mitmenda elemendi peale stack näitab
    double *items; //stacki elementidele massiiv
} STACK;

// Prototypes
STACK *create(size_t st); //stackile mälu andmine
void file_check_existance(FILE *, char[], char[]);
void file_record_count(FILE *, int *, char[]);
void ask_int(int *, char[], int, int);
void ask_double(double *, double, double, char[]);
double pop(STACK *);
void push(STACK *, double);
void file_read_data(FILE*, STACK *, int);
void destroy(STACK *); //stacki mälust eemaldamine
void calc_array(STACK *, STACK *, double *, int, int, double);
void file_write_data(FILE *file, double *, int, int);

// Main cycle
int main() {
	int STACKSIZE = 2000;
	FILE *F1, *F2, *F3;
	STACK *A = create(STACKSIZE); //stacki deklamine ja mälu andmine
	STACK *B = create(STACKSIZE); //stacki deklamine ja mälu andmine
	char F1_name[10], F2_name[10], F3_name[10];
	int N, M, A_count, B_count;
	double X;
	
	// Ask for input file, also check, if it exists.
    file_check_existance(F1, F1_name, "massive A");
    file_check_existance(F2, F2_name, "massive B");
    file_check_existance(F3, F3_name, "output");
    
    // Ask for row count
    ask_int(&N, "Enter row count [2 =< N =< 10]: ", 2, 10);
    
    // Ask for column count
    ask_int(&M, "Enter column count [2 =< M =< 10]: ", 2, 10);
    
    // Declare massive K
    double K[N][M];
    
    // Ask for variable X
    ask_double(&X, 0.01, 1.50, "Enter X variable [0.01 =< X =< 1.50]: ");
    
    // Open files
	F1 = fopen(F1_name, "r");
    F2 = fopen(F2_name, "r");
    F3 = fopen(F3_name, "w");
    
    // Find record counts
    file_record_count(F1, &A_count, "F1 record count: ");
    file_record_count(F2, &B_count, "F2 record count: ");
    
    // Read massives into stacks
    file_read_data(F1, A, A_count);
    file_read_data(F2, B, B_count);
    
    // Calculate array K elements
    calc_array(A, B, &K[0][0], N, M, X);
    
	// Write data to file
	file_write_data(F3, &K[0][0], N, M);
    
	// Close files
	fclose(F1);
	fclose(F2);
	fclose(F3);
	
	// Free memory
	destroy(A);
	destroy(B);
	
	return 0;
}

// Create stack memory
STACK *create(size_t st){
    STACK *ps; //ajutine viit
    ps = (STACK*) malloc (sizeof(STACK)); //anname stackile mälu
    ps -> stackSize = st; //määrame ära stacki suuruse
    ps -> stackPointer = 0; //paneme näitama nullindale elemendile
    ps -> items = (double*)malloc(sizeof(double) * ps -> stackSize); //anname stacki elementidele mälu
    return ps;
}

// Free stack memory
void destroy(STACK *ps){
    free(ps->items); //vabastame esmalt stacki elementide mälu, sest muidu ei ole meil enam seda viita ja mälu vabastada ei saaks
    free(ps); //seejärel vabastame stacki enda mälu
}

// Save data into stack
void push(STACK *ps, double x) {
    if (ps->stackPointer == ps->stackSize)
        puts("Error: stack overflow\n");
    else
        *(ps -> items + (ps -> stackPointer++)) = x;
}

// Get data from stack
double pop(STACK *ps) {
    if (ps->stackPointer == 0) {
        puts("\nError: stack underflow\n");
        return 0;
    } else
        return ps->items[--ps->stackPointer];
}

// Ask for int variable
void ask_int(int *var, char prompt[], int low_limit, int high_limit) {
    do {
        printf("%s", prompt);
        scanf("%d", var);
    } while (*var < low_limit || *var > high_limit);
}

// Ask double variable
void ask_double(double *var, double lower_limit, double upper_limit, char prompt[]) {
	do {
		printf(prompt);
		scanf("%lf", var);
	} while (*var < lower_limit || *var > upper_limit);
}

// Ask for file name and check it's existance
void file_check_existance(FILE *file, char file_name[], char prompt[]) {
	int i = 3;
	
	do {
		if (i == 0) {
			printf("File not found! Exiting...");
			exit(0);
		}
		
		printf("Enter %s filename (%d tries remaining): ", prompt, i);
		scanf("%s", file_name);
		
		// Lisame .txt lõppu, kui see veel puudub
        if (strstr(file_name, ".txt") == NULL) {
            strcat(file_name, ".txt");
        }
		
		file = fopen(file_name, "r");
		i--;
	} while(file == NULL);
	
	fclose(file);
}

// Read data from file into stack
void file_read_data(FILE *file, STACK *ps, int index){
    int i;
    double value;

    for (i = 0; i < index; i++) {
		fscanf(file, "%lf", &value);
        push(ps, value);
    }
    
    rewind(file);
}

// Write data to file
void file_write_data(FILE *file, double *K, int N, int M){
	int i, j;
    for (i = 0; i < N; i++) {
		for (j = 0; j < M; j++) {
			fprintf(file, "%.12f\t", *(K+i*M+j));
    }
		fprintf(file, "\n");
	}
}

// Get record count from file
void file_record_count(FILE *file, int *value, char prompt[]) {
    char line[256];
    *value = 0;

    while (fgets(line, sizeof(line), file) != NULL) {
        (*value)++;
    }

    printf("%s%d\n", prompt, *value);
    rewind(file);
}

// Calculate array elements
void calc_array(STACK *psA, STACK *psB, double *K, int N, int M, double X) {
	double A, B, K_prev;
	int m, j;
	int i = 1;
	
    for (m = 0; m < N; m++) {
		for (j = 0; j < M; j++) {
			if (i == 1) {
				*(K+m*M+j) = 1;
				K_prev = *(K+m*M+j);
				i++;
			} else {
				A = pop(psA);
				B = pop(psB);
				*(K+m*M+j) = K_prev * A * pow(X, i) / (pow(A, 2) + pow(B, 2));
				K_prev = *(K+m*M+j);
				i++;
			}
		}
	}
}


