import numpy as np

# create a n.n matrix
def createSquareMatrix(n):
    matrix = np.random.random((n, n))
    return matrix

# Calculate det() of a matrix
def determinant_recursive(matrix_numpy, total=0):
    # convert numpy array to list
    # np.array([[1, 2], [3, 4]]) => [[1, 2], [3, 4]]
    if type(matrix_numpy) is not list:
        matrix = matrix_numpy.tolist()

    # Store indices in list for row referencing
    indices = list(range(len(matrix)))
    print("i:", indices)
    # 2x2 matrix
    if len(matrix) == 2 and len(matrix[0]) == 2:
        val = matrix[0][0] * matrix[1][1] - matrix[1][0] * matrix[0][1]
        return val

    # submatrix for focus column (fc)
    for fc in indices:  # for each focus column, ...
        # find the submatrix ...
        s_matrix = matrix.copy()  # make a copy
        s_matrix = s_matrix[1:]  # remove the first row
        height = len(s_matrix)
        print("s_m:", s_matrix)
        print("h:", height)

        for i in range(height):
            # for each remaining row of submatrix ...
            # remove the focus column elements
            s_matrix[i] = s_matrix[i][0:fc] + s_matrix[i][fc + 1:]

        sign = (-1) ** (fc % 2)
        # pass submatrix recursively
        sub_det = determinant_recursive(s_matrix)
        # total all returns from recursion
        total += sign * matrix[0][fc] * sub_det

    return total

# Calculate inverse()
def inversion_matrix(matrix_np):
    AM = matrix_np.tolist()
    n = len(AM)
    IM = np.eye(n)

    indices = list(range(n))  # to allow flexible row referencing
    for fd in range(n):  # fd stands for focus diagonal
        fdScaler = 1.0 / AM[fd][fd]
        # FIRST: scale fd row with fd inverse. 
        for j in range(n):  # Use j to indicate column looping.
            AM[fd][j] *= fdScaler
            IM[fd][j] *= fdScaler

            # SECOND: operate on all rows except fd row.
        for i in indices[0:fd] + indices[fd + 1:]:  # *** skip row with fd in it.
            crScaler = AM[i][fd]  # cr stands for "current row".
            for j in range(n):  # cr - crScaler * fdRow, but one element at a time.
                AM[i][j] = AM[i][j] - crScaler * AM[fd][j]
                IM[i][j] = IM[i][j] - crScaler * IM[fd][j]

    return np.multiply(matrix_np, IM)

# Main
print()