/*
*/
// Gaussian Elimination
// Using UTF-8 (Unicode) encoding of Russian characters
//
// Приведение матрицы к ступенчатому виду
// методом Гаусса
//
#include 
#include 

const double EPS = 1e-8;

int gauss(double *a, int m, int n);

void swapRows(double *a, int m, int n, int i1, int i2);
void addRows(double *a, int m, int n, 
    int i, int k, double lambda);
void multRow(double *a, int m, int n, int i, double lambda);

double det(const double *a, int m, int n);
bool readMatrix(const char *path, double **a, int *m, int *n);
bool writeMatrix(
    const char *path, const double *a, int m, int n
);
void printMatrix(const double *a, int m, int n);

// Читаем матрицу из файла "input.txt",
// приводим ее к ступенчатому виду и записываем
// результат в файл "output.txt".
// Формат файла:
//     m n
//     элементы матрицы по строкам
// где m - число строк матрицы, n - число столбцов.
// Например, матрица размера 2x3 записывается как
//     2 3
//     a11 a12 a13
//     a21 a22 a23
// Программа также печатает ранг матрицы и для
// квадратной матрицы ее определитель.
int main() {
    int m, n;
    double *a;
    if (!readMatrix("input.txt", &a, &m, &n)) {
        printf("Cannot read a matrix.\n");
        return (-1);
    }
    printMatrix(a, m, n);
    printf("----\n");  
  
    int rank = gauss(a, m, n);
    printMatrix(a, m, n);
    printf("----\n");  

    printf("rank = %d\n", rank);
    if (n == m) {
        double d = det(a, n, m);
        printf("determinant = %.6f\n", d);
    }
    
    if (!writeMatrix("output.txt", a, m, n)) {
        printf("Cannot write a matrix.\n");
        return (-1);
    }

    return 0;
}

int gauss(double *a, int m, int n) {
    int i = 0;
    int j = 0;
    
    while (i < m && j < n) {
        // 1. Ищем макс. ненулевой эл-т в j-м столбце,
        //    начиная со строки i
        double amax = (-1.);
        int kmax = j;
        for (int k = i; k < m; ++k) {
            if (fabs(a[k*n + j]) > amax) {
                amax = fabs(a[k*n + j]);
                kmax = k;
            }
        }
        if (amax <= EPS) {
            // Нулевой столбец
            for (int k = i; k < m; ++k) {
                a[k*n + j] = 0.;
            }
            ++j;
            continue;
        }
        if (kmax != i) {
            // Меняем местами строки i и kmax
            swapRows(a, m, n, i, kmax);
        }
        
        // Зануляем j-й столбец, начиная со строки i+1
        for (int k = i+1; k < m; ++k) {
            // От строки k вычитаем строку i, умноженную
            // на коэффициент lambda
            double lambda = a[k*n + j]/a[i*n + j];
            addRows(a, m, n, k, i, -lambda);
            a[k*n + j] = 0.;
        }
        ++i; ++j;    
    }
    return i;
}

bool writeMatrix(
    const char *path, const double *a, int m, int n
) {
    FILE *f = fopen(path, "wt");
    if (f == NULL) {
        return false;
    }
    fprintf(f, "%d %d\n", m, n);
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            if (j > 0)
                fprintf(f, " "); // Разделитель
            fprintf(f, "%16.6f", a[i*n + j]);    
        }
        fprintf(f, "\n");
    }
    fclose(f);
    return true;
}

void printMatrix(const double *a, int m, int n) {
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            if (j > 0)
                printf(" ");
            printf("%16.6f", a[i*n + j]);
        }
        printf("\n");
    }
}

bool readMatrix(
    const char *path, double **a, int *m, int *n
) {
    FILE *f = fopen(path, "rt");
    if (f == NULL) {
        return false;
    }
    
    if (fscanf(f, "%d%d", m, n) < 2) {
        fclose(f);
        return false;
    }
    
    int rows = *m;
    int cols = *n;
    
    double *matr = new double[rows*cols];
    *a = matr;
    
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            if (fscanf(f, "%lf", &(matr[i*cols + j])) < 1) {
                fclose(f);
                return false;
            }    
        }
    }
    fclose(f);
    return true;
}

double det(const double *a, int m, int n) {
    double d = 1.;
    for (int i = 0; i < n && i < m; ++i) {
        d *= a[i*n + i];
    }
    return d;
}


void swapRows(double *a, int m, int n, int i1, int i2) {
    for (int j = 0; j < n; ++ j) {
        double tmp = a[i1*n + j];
        a[i1*n + j] = a[i2*n + j];
        a[i2*n + j] = (-tmp);
    }
}

void addRows(double *a, int m, int n, 
    int i, int k, double lambda) {
    for (int j = 0; j < n; ++ j) {
        a[i*n + j] += a[k*n + j]*lambda;
    }
}

void multRow(double *a, int m, int n, int i, double lambda) {
    for (int j = 0; j < n; ++ j) {
        a[i*n + j] *= lambda;
    }
}
/*
*/