//AUTORE: Libera Longo
//Data: 29 dicembre 2022
//programma che scrive in automatico quello che dovremmo fare noi per risolvere
//l'algoritmo del simplesso
//per ovvi motivi non ragiona in modo grafico quindi è da verificare...
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>  //per i boolean
#include <math.h>			//per +infinito

#define human_num(n) (n+1) //serve pk gli umani contano da 1 gli indici degli array da 0

#define ENGLISH
//#define ITALIAN

int main(int argc, char* argv[]) {
	const int N_VARIABILI = 2; //se cambi questo alcuni printf sono da cambiare...

	//pochi argomenti
	if(argc < 2) {
#ifdef ENGLISH		
		printf("this simpless program work only in R^%d!\n", N_VARIABILI);
		printf("I should know how many constraint there is\n");
#endif
#ifdef ITALIAN
		printf("Questo programma del simplesso lavora solo in R^%d!\n", N_VARIABILI);
		printf("Dovresti dirmi quanti vincoli ci sono\n");
#endif
		return 42;
	}

	//dati
	const int SIZE = atoi(argv[1]);
	
	float C[N_VARIABILI];
	float A[N_VARIABILI][SIZE];
	float Noti[SIZE]; //termine noto
	int Base[N_VARIABILI];

	//inizio
#ifdef ENGLISH
	printf("You want to insert %d constraints\n", SIZE);
	printf("Insert the vector that represent c (the max objective function):\n");
#endif
#ifdef ITALIAN
	printf("Vuoi inserire %d vincoli\n", SIZE);
	printf("Inserisci il vettore che rappresenta c (la funzione obbiettivo da massimizzare):\n");
#endif
	//inserisci c (max c1*x1 + c2 * x2)
	printf("max c_1 * x_1 + c_2 * x_2\n");
	for(int v=0; v<N_VARIABILI; v++) {
		printf("c_%d = ", v); scanf("%f",&C[v]);
	}
	printf("max { (%.2f) * x_1 + (%.2f) * x_2 }\n", C[0], C[1]);

	//inserisci A x <= b
	for(int i = 0; i < SIZE; i++) {
		float b;
#ifdef ENGLISH
		printf("Insert the %d-th constraint\n", human_num(i));
#endif
#ifdef ITALIAN
		printf("Inserisci l'%d-esimo vincolo\n", human_num(i));
#endif
		//inserire vincolo i-esimo
		for(int v=0; v<N_VARIABILI; v++) {
			printf("a_%d = ", v); scanf("%f",&A[v][i]);
		}
		printf("b   = "); scanf("%f",&b); Noti[i] = b;
		printf("\t\t\t(%d)  :  (%.2f) * x_1 + (%.2f) * x_2 <= (%.2f) \n\n",
				human_num(i), A[0][i], A[1][i], b);
	}
	//inserire base B
#ifdef ENGLISH
	printf("wich constraints u want to start with (B)?\n");
#endif
#ifdef ITALIAN
	printf("da quali vincoli vuoi partire (base B)?\n");
#endif
	for(int v=0; v<N_VARIABILI; v++) {
		printf("B_%d = ", v); scanf("%d",&Base[v]);
	}
	printf("Base = { B_1 , B_2 } = { %d, %d }\n\n\n", Base[0], Base[1]);
	//riassunto
#ifdef ENGLISH
	printf("summary:\n");
#endif
#ifdef ITALIAN
	printf("riassumendo:\n");
#endif
	printf("c = [%.2f, %.2f]\n", C[0], C[1]);
	printf("A = [\t\t\tb = [\n");
	for(int i=0; i<SIZE; i++) {
		printf("[ ");
		for(int v=0; v<N_VARIABILI; v++)
			printf(" %.2f ", A[v][i]);
		printf("] \t[ %.2f ]\n", Noti[i]);
	}
	printf("]\t\t\t]\n");
	printf("B = { %d, %d }\n", Base[0], Base[1]);

	//ora decremento di uno gli indici della base così posso utilizzarli negli array...
	for( int v=0; v<N_VARIABILI; v++)
		Base[v]--;
	
	//ora che abbiamo finito di inserire i dati possiamo iniziare con l'algoritmo del simplesso
	//codice fatto nella convinzione che N_VARIABILI == 2 sia vero
	if( N_VARIABILI == 2) {
		int iterazione = 1;
		while(true) {
#ifdef ENGLISH
			printf("\n\n\n( iteration %d-th )\n", iterazione);
#endif
#ifdef ITALIAN
			printf("\n\n\n( iterazione %d-esima )\n", iterazione);
#endif
			//Base
			int vincolo0 = Base[0], vincolo1 = Base[1];
			printf("B = { %d, %d }\n", human_num(vincolo0), human_num(vincolo1));
			//A_B e A_B inversa
			float a = A[0][vincolo0], b = A[1][vincolo0], c = A[0][vincolo1], d = A[1][vincolo1];
			printf("A_B = \t[[ %.2f \t%.2f ]\n\t [ %.2f \t%.2f ]]\n", a, b, c, d);
			float det = a * d - b * c;
			printf("det = (%.2f)(%.2f) - (%.2f)(%.2f) = %.2f\n", a, d, b, c, det);
			float inv_a = d/det, inv_b = -b/det, inv_c = -c/det, inv_d = a/det;
			printf("(A_B)^-1 = [[ %.2f \t%.2f ]\n            [ %.2f \t%.2f ]]\n", inv_a, inv_b, inv_c, inv_d);
			//x
			float noto0 = Noti[vincolo0], noto1 = Noti[vincolo1];
			float x0 = inv_a * noto0 + inv_b * noto1, x1 = inv_c * noto0 + inv_d * noto1;
			printf("x = (A_B)^-1 * b_B = [[ %.2f\t%.2f ]  * [ %.2f   = [ %.2f  \n", inv_a, inv_b, noto0, x0);
			printf("                      [ %.2f\t%.2f ]]     %.2f ]     %.2f ]\n", inv_c, inv_d, noto1, x1);
			//y
			float C0 = C[0], C1 = C[1];
			float y0 = C0 * inv_a + C1 * inv_c, y1 = C0 * inv_b + C1 * inv_d;
			printf("y = c * (A_B)^-1   = [ %.2f\t%.2f] * [[ %.2f  \t%.2f ]  = [ %.2f\t%.2f ] \n", C0, C1, inv_a, inv_b, y0, y1);
			printf("                                    \t [ %.2f \t%.2f ]]\n",                  inv_c, inv_d);
			//successo?
			if (y0 >= 0 && y1 >= 0) {
#ifdef ENGLISH
				printf("If y_B >= 0 , then terminate with success and return x = [%.2f  %.2f] and y\n", x0, x1);
				printf("(to build y we know that y_B = [%.2f  %.2f], y_N = 0, where 0 is a vector)\n", y0, y1);
#endif
#ifdef ITALIAN
				printf("Se y_B >= 0 , allora termina con successo e restituisci x = [%.2f  %.2f] e y\n", x0, x1);
				printf("(per costruire y sappiamo che y_B = [%.2f  %.2f], y_N = 0, dove 0 è un vettore)\n", y0, y1);
#endif
				break; //uscita dal while(true)
			}		
			//h
			int h = ( y0 < 0 ? vincolo0 : vincolo1 );
			printf("h = min { i ∈ B | y_i < 0 } = %d\n", human_num(h));
			//ξ
			float epsilon0 = ( y0 < 0 ? -inv_a : -inv_b ), epsilon1 = ( y0 < 0 ? -inv_c : -inv_d ) ;
			printf("(A_B)^-1 = [[ %.2f \t%.2f ]\n            [ %.2f \t%.2f ]]\n", inv_a, inv_b, inv_c, inv_d);
#ifdef ENGLISH
			printf("ξ column of index h (%d) in -((A_B)^-1). ξ = [ %.2f  \n", human_num(h), epsilon0);
			printf("                                              %.2f ]\n", epsilon1);
#endif
#ifdef ITALIAN
			printf("ξ colonna di indice h (%d) in -((A_B)^-1). ξ = [ %.2f  \n", human_num(h), epsilon0);
			printf("                                                %.2f ]\n", epsilon1);
#endif
			//A_N ξ
#ifdef ENGLISH
			printf("(to calculate A_N ξ we should: for every i ∈ N we calculate A_i ξ as product row by column)\n");
#endif
#ifdef ITALIAN
			printf("(per calcolare A_N ξ dovremo: per ogni i ∈ N calcoliamo A_i ξ come prodotto riga per colonna)\n");
#endif
			bool problema_illimitato = true;
			float last_value = +INFINITY; //serve math.h
			int k;
			for(int i=0; i<SIZE; i++)
				if(i != vincolo0 && i != vincolo1) {
					float a_0 = A[0][i], a_1 = A[1][i];
					float result = a_0 * epsilon0 + a_1 * epsilon1;
#ifdef ENGLISH
					printf("A_%d ξ = row [ %.2f  %.2f ] * column [ %.2f  %.2f ] = %.2f\n", human_num(i), a_0, a_1, epsilon0, epsilon1, result);
#endif
#ifdef ITALIAN
					printf("A_%d ξ = riga [ %.2f  %.2f ] * colonna [ %.2f  %.2f ] = %.2f\n", human_num(i), a_0, a_1, epsilon0, epsilon1, result);
#endif
					if(result > 0) { //stampo le possibili scelte di k
						problema_illimitato = false;
						printf("A_%d ξ > 0\n", human_num(i));
						//scelta di k
						float nominatore = Noti[i] - ( a_0 * x0 + a_1 * x1 ) ;
						float k_value = nominatore / result ;
#ifdef ENGLISH
						printf(" b_%d - A_%d x    %.2f -  row [ %.2f  %.2f ] *  column [ %.2f  %.2f ]   %.2f\n", human_num(i), human_num(i), Noti[i], a_0, a_1, x0, x1, nominatore);
#endif
#ifdef ITALIAN
						printf(" b_%d - A_%d x    %.2f - riga [ %.2f  %.2f ] * colonna [ %.2f  %.2f ]   %.2f\n", human_num(i), human_num(i), Noti[i], a_0, a_1, x0, x1, nominatore);
#endif
						printf("------------- = ---------------------------------------------------- = ---- = %.2f\n", k_value);
						printf("    A_%d ξ                               %.2f                           %.2f\n\n", human_num(i), result, result);
						//choose if k_value is the minimum value until now.
						if(k_value < last_value) {
							last_value = k_value;
							k = i;
						}
					}
				}
			//problema illimitato?
			if(problema_illimitato) {
#ifdef ENGLISH
				printf("If A_N ξ ≤ 0, then terminate and return ξ = [ %.2f  %.2f ] : the problem is unlimited\n", epsilon0, epsilon1);
#endif
#ifdef ITALIAN
				printf("Se A_N ξ ≤ 0, allora termina e restituisci ξ = [ %.2f  %.2f ] : il problema è illimitato\n", epsilon0, epsilon1);
#endif
				break; //uscita dal while(true)
			}
			//k
			printf("k = argmin { ( b_i - A_i x ) / ( A_i ξ ) | A_i ξ > 0 ∧ i ∈ N } = %d\n", human_num(k));
			//nuova base
			int non_refused = ( h == Base[0] ? Base[1] : Base[0] );
			int min_vincolo = ( k < non_refused ? k : non_refused ) , max_vincolo = ( k > non_refused ? k : non_refused ) ;
			printf("B_%d = B_%d ∪ {k} − {h} = {%d, %d} ∪ {%d} − {%d} = {%d, %d}\n", iterazione+1, iterazione, human_num(Base[0]), human_num(Base[1]), human_num(k), human_num(h), human_num(min_vincolo), human_num(max_vincolo));
			Base[0] = min_vincolo; Base[1] = max_vincolo;
			iterazione ++;
		}
	} else
#ifdef ENGLISH
		printf("If u want to CHANGE N_VARIABILI in the code then u should understood how to do A^-1 (INVERSE MATRIX OF A)\n");
#endif
#ifdef ITALIAN
		printf("Se tu volessi cambiare N_VARIABILI nel codice allora dovresti capire come calcolare A^-1 (INVERSA DI A)\n");
#endif
}