#include "../s21_matrix.h"

int s21_create_matrix(int rows, int columns, matrix_t *result) {
  if (result == NULL) return ERROR_INIT;

  int matrix_status = OK;

  if (rows < 1 || columns < 1) {
    matrix_status = ERROR_INIT;
    result->matrix = NULL;
  } else {
    result->rows = rows;
    result->columns = columns;
    result->matrix = (double **)calloc(rows, sizeof(double *));
    if (result->matrix == NULL) {
      matrix_status = ERROR_INIT;
    } else {
      for (int i = 0; i < rows; i++) {
        result->matrix[i] = (double *)calloc(columns, sizeof(double));
        if (result->matrix[i] == NULL) {
          matrix_status = ERROR_CALC;
          s21_remove_matrix(result);
        }
      }
    }
  }

  return matrix_status;
}
#include "../s21_matrix.h"

void s21_remove_matrix(matrix_t *A) {
  if (A != NULL && A->matrix != NULL) {
    for (int i = 0; i < A->rows; ++i) {
      free(A->matrix[i]);
    }
    free(A->matrix);
    A->matrix = NULL;
    A->columns = 0;
    A->rows = 0;
  }
}
#include "../s21_matrix.h"

int s21_inverse_matrix(matrix_t *A, matrix_t *result) {
  if (A == NULL || A->matrix == NULL || result == NULL) {
    return ERROR_INIT;
  }
  if (A->columns != A->rows) return ERROR_CALC;

  int status = OK;
  status = s21_create_matrix(A->rows, A->columns, result);

  if (status == OK) {
    double det;
    status = s21_determinant(A, &det);
    if (status == OK) {
      if (det == 0) return ERROR_CALC;
      matrix_t transpose_complements;
      status = s21_adj_matrix(A, &transpose_complements);
      if (status == OK) {
        status = s21_mult_number(&transpose_complements, 1 / det, result);
      }
      s21_remove_matrix(&transpose_complements);
    }
  }

  return status;
}
#include "../s21_matrix.h"

int s21_determinant(matrix_t *A, double *result) {
  if (A == NULL || A->matrix == NULL || result == NULL) {
    return ERROR_INIT;
  }

  if (A->columns != A->rows) {
    return ERROR_CALC;
  }

  int status = OK;
  if (A->rows == 1) {
    *result = A->matrix[0][0];
  } else if (A->rows == 2) {
    *result =
        A->matrix[0][0] * A->matrix[1][1] - A->matrix[0][1] * A->matrix[1][0];
  } else {
    double det = 0.0;
    for (int i = 0; i < A->rows; ++i) {
      matrix_t minor_matrix;
      status = s21_minor_matrix(A, 0, i, &minor_matrix);
      if (status != OK) {
        return ERROR_CALC;
      }
      double minor_det;
      status = s21_determinant(&minor_matrix, &minor_det);
      if (status != OK) {
        return ERROR_CALC;
      }
      if (i % 2 == 0) {
        det += A->matrix[0][i] * minor_det;
      } else {
        det -= A->matrix[0][i] * minor_det;
      }
      s21_remove_matrix(&minor_matrix);
    }
    *result = det;
  }
  return status;
}
#include "../s21_matrix.h"

void s21_initialize_matrix(matrix_t *A, double start_value,
                           double iteration_step) {
  if (A != NULL && A->matrix != NULL) {
    double value = start_value;
    for (int i = 0; i < A->rows; i++) {
      for (int j = 0; j < A->columns; j++) {
        A->matrix[i][j] = value;
        value += iteration_step;
      }
    }
  }
}

int s21_minor_matrix(matrix_t *A, int rows, int columns, matrix_t *result) {
  if (A == NULL || A->matrix == NULL || result == NULL) {
    return ERROR_INIT;
  }

  int status = OK;
  status = s21_create_matrix(A->rows - 1, A->columns - 1, result);

  if (status == OK) {
    int new_row = 0;
    for (int i = 0; i < A->rows; ++i) {
      if (i == rows) {
        continue;
      }

      int new_col = 0;
      for (int j = 0; j < A->columns; ++j) {
        if (j == columns) {
          continue;
        }

        result->matrix[new_row][new_col] = A->matrix[i][j];
        new_col++;
      }
      new_row++;
    }
  }
  return status;
}

int s21_adj_matrix(matrix_t *A, matrix_t *result) {
  if (A == NULL || A->matrix == NULL || result == NULL) {
    return ERROR_INIT;
  }

  int status = OK;
  status = s21_create_matrix(A->rows, A->columns, result);

  if (status == OK) {
    matrix_t complements_matrix;
    status = s21_calc_complements(A, &complements_matrix);
    if (status == OK) {
      status = s21_transpose(&complements_matrix, result);
    }
    s21_remove_matrix(&complements_matrix);
  }
  return status;
}
#include "../s21_matrix.h"

int s21_mult_number(matrix_t *A, double number, matrix_t *result) {
  if (A == NULL || A->matrix == NULL || result == NULL) {
    return ERROR_INIT;
  }

  if (A->rows < 1 || A->columns < 1 || !isfinite(number)) {
    return ERROR_CALC;
  }
  int status = OK;
  status = s21_create_matrix(A->rows, A->columns, result);
  if (!status) {
    for (int i = 0; i < A->rows && status == OK; ++i) {
      for (int j = 0; j < A->columns && status == OK; ++j) {
        result->matrix[i][j] = A->matrix[i][j] * number;
        if (!isfinite(result->matrix[i][j])) {
          status = ERROR_CALC;
        }
      }
    }
  }
  return status;
}
#include "s21_matrix_test.h"

START_TEST(test_inverse_matrix_0) {
  matrix_t A = {};
  matrix_t result = {};
  ck_assert_int_eq(s21_inverse_matrix(&A, &result), ERROR_INIT);
  s21_remove_matrix(&A);
  s21_remove_matrix(&result);
}
END_TEST

START_TEST(test_inverse_matrix_1) {
  matrix_t A = {};
  matrix_t result = {};
  s21_create_matrix(3, 3, &A);
  s21_initialize_matrix(&A, 1, 1);
  ck_assert_int_eq(s21_inverse_matrix(&A, &result), ERROR_CALC);
  s21_remove_matrix(&A);
  s21_remove_matrix(&result);
}
END_TEST

START_TEST(test_inverse_matrix_2) {
  matrix_t A = {};
  matrix_t result = {};
  s21_create_matrix(5, 3, &A);
  s21_initialize_matrix(&A, 1, 1);
  ck_assert_int_eq(s21_inverse_matrix(&A, &result), ERROR_CALC);
  s21_remove_matrix(&A);
  s21_remove_matrix(&result);
}
END_TEST

START_TEST(test_inverse_matrix_3) {
  matrix_t A = {};
  matrix_t result = {};
  matrix_t eq_matrix = {};
  s21_create_matrix(1, 1, &A);
  s21_create_matrix(1, 1, &eq_matrix);
  A.matrix[0][0] = 21;
  eq_matrix.matrix[0][0] = 1.0 / 21.0;
  ck_assert_int_eq(s21_inverse_matrix(&A, &result), OK);
  s21_remove_matrix(&A);
  ck_assert_int_eq(s21_eq_matrix(&result, &eq_matrix), SUCCESS);
  s21_remove_matrix(&result);
  s21_remove_matrix(&eq_matrix);
}
END_TEST

START_TEST(test_inverse_matrix_4) {
  matrix_t A = {};
  matrix_t result = {};
  matrix_t eq_matrix = {};
  s21_create_matrix(3, 3, &A);
  s21_create_matrix(3, 3, &eq_matrix);
  A.matrix[0][0] = 2, A.matrix[0][1] = 5, A.matrix[0][2] = 7;
  A.matrix[1][0] = 6, A.matrix[1][1] = 3, A.matrix[1][2] = 4;
  A.matrix[2][0] = 5, A.matrix[2][1] = -2, A.matrix[2][2] = -3;
  eq_matrix.matrix[0][0] = 1, eq_matrix.matrix[0][1] = -1,
  eq_matrix.matrix[0][2] = 1;
  eq_matrix.matrix[1][0] = -38, eq_matrix.matrix[1][1] = 41,
  eq_matrix.matrix[1][2] = -34;
  eq_matrix.matrix[2][0] = 27, eq_matrix.matrix[2][1] = -29,
  eq_matrix.matrix[2][2] = 24;
  ck_assert_int_eq(s21_inverse_matrix(&A, &result), OK);
  ck_assert_int_eq(s21_eq_matrix(&result, &eq_matrix), SUCCESS);
  s21_remove_matrix(&A);
  s21_remove_matrix(&result);
  s21_remove_matrix(&eq_matrix);
}
END_TEST

START_TEST(test_inverse_matrix_5) {
  int res = 0;
  matrix_t A = {0};
  matrix_t Z = {0};
  matrix_t X = {0};

  s21_create_matrix(4, 4, &A);
  A.matrix[0][0] = -1.0;
  A.matrix[0][1] = 2.0;
  A.matrix[0][2] = 7.0;
  A.matrix[0][3] = 9.0;
  A.matrix[1][0] = 1.0;
  A.matrix[1][1] = 0.0;
  A.matrix[1][2] = 0.0;
  A.matrix[1][3] = 0.0;
  A.matrix[2][0] = 47.0;
  A.matrix[2][1] = 13.0;
  A.matrix[2][2] = 17.0;
  A.matrix[2][3] = 21.0;
  A.matrix[3][0] = 22.0;
  A.matrix[3][1] = 7.0;
  A.matrix[3][2] = 1.0;
  A.matrix[3][3] = 3.0;

  s21_inverse_matrix(&A, &Z);
  s21_remove_matrix(&A);
  s21_create_matrix(4, 4, &X);

  X.matrix[0][0] = 0.0;
  X.matrix[0][1] = 1.0;
  X.matrix[0][2] = 0.0;
  X.matrix[0][3] = 0.0;
  X.matrix[1][0] = -5.0 / 23.0;
  X.matrix[1][1] = -121.0 / 23.0;
  X.matrix[1][2] = 2.0 / 23.0;
  X.matrix[1][3] = 1.0 / 23.0;
  X.matrix[2][0] = -18.0 / 23.0;
  X.matrix[2][1] = -379.0 / 46.0;
  X.matrix[2][2] = 19.0 / 46.0;
  X.matrix[2][3] = -25.0 / 46.0;
  X.matrix[3][0] = 53.0 / 69.0;
  X.matrix[3][1] = 1061.0 / 138.0;
  X.matrix[3][2] = -47.0 / 138.0;
  X.matrix[3][3] = 19.0 / 46.0;

  res = s21_eq_matrix(&X, &Z);

  
  s21_remove_matrix(&X);
  s21_remove_matrix(&Z);

  ck_assert_int_eq(res, SUCCESS);
}
END_TEST

START_TEST(test_inverse_matrix_6) {
  int res;
  matrix_t A = {0};
  matrix_t Z = {0};

  s21_create_matrix(1, 1, &A);
  A.matrix[0][0] = 2;

  res = s21_inverse_matrix(&A, &Z);

  ck_assert_int_eq(Z.matrix[0][0] == (1.0 / A.matrix[0][0]), 1);
  ck_assert_int_eq(res, OK);

  s21_remove_matrix(&A);
  s21_remove_matrix(&Z);
}
END_TEST

START_TEST(test_inverse_matrix_7) {
  int res = 0;
  matrix_t A = {0};
  matrix_t Z = {0};

  s21_create_matrix(3, 3, &A);
  s21_initialize_matrix(&A, 1.0, 1);

  res = s21_inverse_matrix(&A, &Z);
  ck_assert_int_eq(res, ERROR_CALC);
  s21_remove_matrix(&A);
  s21_remove_matrix(&Z);
}
END_TEST

START_TEST(test_inverse_matrix_8) {
  int res = 0;
  matrix_t A = {0};
  matrix_t Z = {0};

  s21_create_matrix(3, 2, &A);
  s21_initialize_matrix(&A, 1.0, 1);

  res = s21_inverse_matrix(&A, &Z);
  ck_assert_int_eq(res, ERROR_CALC);
  s21_remove_matrix(&A);
  s21_remove_matrix(&Z);
}
END_TEST

START_TEST(test_inverse_matrix_9) {
  int res = 0;
  matrix_t A = {0};
  matrix_t Z = {0};

  s21_create_matrix(3, 3, &A);
  s21_initialize_matrix(&A, 1.0, 1);
  s21_remove_matrix(&A);

  res = s21_inverse_matrix(&A, &Z);

  ck_assert_int_eq(res, ERROR_INIT);
  s21_remove_matrix(&Z);
}
END_TEST

START_TEST(test_inverse_matrix_10) {
  int res = 0;
  matrix_t A = {0};
  matrix_t Z = {0};
  matrix_t X = {0};

  s21_create_matrix(3, 3, &A);
  A.matrix[0][0] = 1.0;
  A.matrix[0][1] = 2.0;
  A.matrix[0][2] = 3.0;
  A.matrix[1][0] = 4.0;
  A.matrix[1][1] = 5.0;
  A.matrix[1][2] = 6.0;
  A.matrix[2][0] = 8.0;
  A.matrix[2][1] = 6.0;
  A.matrix[2][2] = 9.0;

  s21_inverse_matrix(&A, &Z);

  s21_create_matrix(3, 3, &X);

  X.matrix[0][0] = -3.0 / 5.0;
  X.matrix[0][1] = 0.0;
  X.matrix[0][2] = 1.0 / 5.0;
  X.matrix[1][0] = -4.0 / 5.0;
  X.matrix[1][1] = 1.0;
  X.matrix[1][2] = -2.0 / 5.0;
  X.matrix[2][0] = 16.0 / 15.0;
  X.matrix[2][1] = -2.0 / 3.0;
  X.matrix[2][2] = 1.0 / 5.0;

  res = s21_eq_matrix(&X, &Z);
  s21_remove_matrix(&A);
  s21_remove_matrix(&X);
  s21_remove_matrix(&Z);

  ck_assert_int_eq(res, SUCCESS);
}
END_TEST

Suite* suite_inverse_matrix(void) {
  Suite* s = suite_create("suite_inverse_matrix");
  TCase* tc = tcase_create("case_inverse_matrix");

  tcase_add_test(tc, test_inverse_matrix_0);
  tcase_add_test(tc, test_inverse_matrix_1);
  tcase_add_test(tc, test_inverse_matrix_2);
  tcase_add_test(tc, test_inverse_matrix_3);
  tcase_add_test(tc, test_inverse_matrix_4);
  tcase_add_test(tc, test_inverse_matrix_5);
  tcase_add_test(tc, test_inverse_matrix_6);
  tcase_add_test(tc, test_inverse_matrix_7);
  tcase_add_test(tc, test_inverse_matrix_8);
  tcase_add_test(tc, test_inverse_matrix_9);
  tcase_add_test(tc, test_inverse_matrix_10);

  suite_add_tcase(s, tc);
  return s;
}

==231467==
==231467== 16 (8 direct, 8 indirect) bytes in 1 blocks are definitely lost in loss record 5 of 22
==231467==    at 0x484DA83: calloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==231467==    by 0x11ECC1: s21_create_matrix (in /home/fratello/school_21/C6_s21_matrix-1/src/s21_test.out)
==231467==    by 0x11F3B0: s21_inverse_matrix (in /home/fratello/school_21/C6_s21_matrix-1/src/s21_test.out)
==231467==    by 0x1123EA: test_inverse_matrix_3_fn (in /home/fratello/school_21/C6_s21_matrix-1/src/s21_test.out)