/*
Program: cramers rule.cpp
Author: Austin Semerad
Date: 2001 September 27
Purpose: The purpose of this program is to solve a linear system of size
         less than or equal to MAX_SIZE using Cramer's Rule.
*/

#include <iostream.h>
#include <string.h>

const int MAX_SIZE = 20;

// Level 1 Functions
void print_message();
void initialize(float &, float *, float *, float *);
void allocate_floats(int &, float *&, float *&, float *&);
void initialize_dynamic_memory(float *, int);
void get_matrices(float [][MAX_SIZE], float *, int);
void calculate_variable_matrix(float &, float *, float *, float [][MAX_SIZE], float *, int);
void print_results(float *, float *, int, float);
void return_memory(float *, float *, float *);

// Level 2 Functions
void calculate_determinant(float &, float [][MAX_SIZE], int);
void calculate_modified_determinant(float *, float [][MAX_SIZE], float [], int);
void calculate_variables(float *, float *, float, int);
void print_determinants(float *, int, float);
void print_variables(float *, int, float);

// Level 3 Functions
void copy_matrix(float[][MAX_SIZE], float [][MAX_SIZE], int);
bool swap_rows(float [][MAX_SIZE], int, int);

int main()
{
    int size;
    float coefficient[MAX_SIZE][MAX_SIZE], *variable, *constant, *determinant_modified, determinant_original;
    char quit[1];
    char yes[] = "y";

    print_message();
    do
    {
        initialize(determinant_original, variable, constant, determinant_modified);
        allocate_floats(size, variable, constant, determinant_modified);
        initialize_dynamic_memory(determinant_modified, size);
        get_matrices(coefficient, constant, size);
        calculate_variable_matrix(determinant_original, determinant_modified, variable, coefficient, constant, size);
        print_results(variable, determinant_modified, size, determinant_original);
        return_memory(variable, constant, determinant_modified);
        cout << "Do again (y/n)? ";
        cin >> quit;

    } while (strcmp(quit, yes) == 0);

    return 0;
}

void print_message()
{
    cout << " This program was written completely by Austin Semerad" << endl;
    cout << " for Dr. Ansari's MATH 220 class." << endl << endl;
    cout << "This program will solve a square linear system using Cramer's Rule." << endl;
    cout << "Ax = b, where A is a square (n x n) coefficient matrix," << endl;
    cout << "x is a variable (n x 1) matrix, and b is a constant (n x 1) matrix." << endl;

    return;
}

void initialize(float &i_determinant_original, float *i_variable, float *i_constant, float *i_determinant_modified)
{
    i_determinant_original = 1;
    i_variable = NULL;
    i_constant = NULL;
    i_determinant_modified = NULL;

    return;
}

void allocate_floats(int &af_size, float *&af_variable, float *&af_constant, float *&af_determinant_modified)
{
    cout << "First, enter n (the size of the square matrix): ";
    cin >> af_size;
    while ((af_size < 1) || (af_size > 20))
    {
        cout << "That is not a valid size. Numbers must be greater than 0 and less than 20." << endl;
        cout << "Enter n: ";
        cin >> af_size;
    }
    af_variable = new float[af_size];
    af_constant = new float[af_size];
    af_determinant_modified = new float[af_size];

    return;
}

void initialize_dynamic_memory(float *idm_determinant_modified, int idm_size)
{
    int i;

    for (i = 0; i < idm_size; i ++)
    idm_determinant_modified[i] = 1;

    return;
}

void get_matrices(float gm_coefficient[][MAX_SIZE], float *gm_constant, int gm_size)
{
    int i, j;

    cout << endl;
    cout << "Enter the coefficient matrix (A)." << endl;
    cout << "When entering the rows, put one space between each term in the row." << endl;
    for (i = 0; i < gm_size; i ++)
    {
        cout << "Enter row " << i + 1 << ": ";
        for (j = 0; j < gm_size; j ++)
            cin >> gm_coefficient[i][j];
    }
    cout << endl;
    cout << "Enter the constant matrix (b)." << endl;
    for (i = 0; i < gm_size; i ++)
    {
        cout << "Enter row " << i + 1 << ": ";
        cin >> gm_constant[i];
    }

    return;
}

void calculate_variable_matrix(float &cvm_determinant_original, float *cvm_determinant_modified, float *cvm_variable, float cvm_coefficient[][MAX_SIZE], float *cvm_constant, int cvm_size)
{
    if (cvm_size == 1)
    {
        cvm_determinant_original = cvm_coefficient[0][0];
        cvm_determinant_modified[0] = cvm_constant[0];
    }
    else
    {
        calculate_determinant(cvm_determinant_original, cvm_coefficient, cvm_size);
        calculate_modified_determinant(cvm_determinant_modified, cvm_coefficient, cvm_constant, cvm_size);
    }
    calculate_variables(cvm_variable, cvm_determinant_modified, cvm_determinant_original, cvm_size);

    return;
}

void print_results(float *pr_variable, float *pr_determinant_modified, int pr_size, float pr_determinant_original)
{
    print_determinants(pr_determinant_modified, pr_size, pr_determinant_original);
    print_variables(pr_variable, pr_size, pr_determinant_original);

    return;
}
 

void return_memory(float *rm_variable, float *rm_constant, float *rm_determinant_modified)
{
    delete [] rm_variable;
    delete [] rm_constant;
    delete [] rm_determinant_modified;

    return;
}

void calculate_determinant(float &cd_determinant, float cd_coefficient[][MAX_SIZE], int cd_size)
{
    int i, j, k, l;
    float temp_co, temp_mat[MAX_SIZE][MAX_SIZE];

    copy_matrix(cd_coefficient, temp_mat, cd_size);

    for (i = 0; i < cd_size; i ++)
    {
        if (temp_mat[i][i] == 0)
        {
            if (swap_rows(temp_mat, i, cd_size) == false)
            {
                cd_determinant = 0;
                return;
            }
            else
                cd_determinant *= -1;
        }
        if (temp_mat[i][i] != 1)
        {
            temp_co = temp_mat[i][i];
            cd_determinant *= temp_mat[i][i];
            for (j = i; j < cd_size; j ++)
                temp_mat[i][j] = temp_mat[i][j] / temp_co;
        }
        for (k = i + 1; k < cd_size; k ++)
            for (l = (cd_size - 1); l >= i; l --)
                temp_mat[k][l] = temp_mat[k][l] - (temp_mat[k][i] * temp_mat[i][l]);
    }

    return;
}

void calculate_modified_determinant(float cmd_determinant_modified[], float cmd_coefficient[][MAX_SIZE], float *cmd_constant, int cmd_size)
{
    int i, j;
    float temp_mat[MAX_SIZE][MAX_SIZE];

    for (i = 0; i < cmd_size; i ++)
    {
        copy_matrix(cmd_coefficient, temp_mat, cmd_size);
        for (j = 0; j < cmd_size; j ++)
            temp_mat[j][i] = cmd_constant[j];
        calculate_determinant(cmd_determinant_modified[i], temp_mat, cmd_size);
    }

    return;
}

void calculate_variables(float *cv_variable, float *cv_determinant_modified, float cv_determinant_original, int cv_size)
{
    int i;

    for (i = 0; i < cv_size; i ++)
        cv_variable[i] = cv_determinant_modified[i] / cv_determinant_original;

    return;
}

void print_determinants(float *pd_determinant_modified, int pd_size, float pd_determinant_original)
{
    int i;

    cout << endl << "Here is a list of all the determinants:" << endl;
    cout << "Det(A) = " << pd_determinant_original << endl;
    for (i = 0; i < pd_size; i ++)
        cout << "Det(A" << i + 1 << ") = " << pd_determinant_modified[i] << endl;

    return;
}

void print_variables(float *pv_variable, int pv_size, float pv_determinant_original)
{
    int i;

    cout << endl;
    if (pv_determinant_original == 0)
        cout << "Cramer's Rule is not applicable for this matrix because Det(A) = 0." << endl;
    else
    {
        cout << "Here is a list of the variables:" << endl;
        for (i = 0; i < pv_size; i ++)
            cout << "x" << i + 1 << " = " << pv_variable[i] << endl;
    }

    return;
}

void copy_matrix(float matrix1[][MAX_SIZE], float matrix2[][MAX_SIZE], int cm_size)
{
    int i, j;

    for (i = 0; i < cm_size; i ++)
        for (j = 0; j < cm_size; j ++)
            matrix2[i][j] = matrix1[i][j];

    return;
}

bool swap_rows(float matrix[][MAX_SIZE], int sr_i, int sr_size)
{
    int initRow = sr_i, initCol = sr_i, i, j;
    float temp;

    for (i = initRow; i < sr_size; i ++)
        if (matrix[i][initCol] != 0)
        {
            for (j = initCol; j < sr_size; j ++)
            {
                temp = matrix[initRow][j];
                matrix[initRow][j] = matrix[i][j];
                matrix[i][j] = temp;
            }
            return true;
        }
        return false;
}