#This is the code for finiding (row) Hermite normal form of a matrix A. 
#You can call this fuction as HNFORMS(A).
#In: A= m by n integer matrix (e.g., A=[[4, -1, 2, -5],
     [-3, 0, 1, -3],
     [2, 4, -3, 2]])
#Out: HNF of A



import numpy as np

def reduction(A,m,n, i, j):
    # Find maximum and minimum absolute values in column j
    m1 = np.max(np.abs(A[i:m, j]))  # Maximum of the absolute values in column j
    l = np.where(np.abs(A[i:m, j]) == m1)[0][0]  # First occurrence of max
    non_zero_elements = A[i:m, j][np.abs(A[i:m, j]) != 0]  # Omit zero values
    
    # Find minimum absolute value in non-zero elements
    if non_zero_elements.size > 0:
        m2 = np.min(np.abs(non_zero_elements))  # Minimum of the non-zero absolute values
        k = np.where(np.abs(A[i:m, j]) == m2)[0][0]  # First occurrence of min
    else:
        k = None  # Handle case if all elements in column are zero

    return k+i, l+i

def HNFORMS(A):
    A = np.array(A, dtype=int)
    m, n = A.shape
    i = 0
    j = 0
    while i< m and j<n :
        if np.all(A[:, j] == 0):
            j+=1
        else:
            k, l = reduction(A,m,n, i, j)
            while k != l:
                q= int(np.floor(A[l,j]/A[k,j]))
                A[l,:] -= q*A[k,:]
                k, l = reduction(A,m,n, i, j)
            if i != k:
                A[[i,k]]= A[[k,i]]
        
            if A[i,j]<0:
                A[i,:]= -A[i,:]
    
            if i>0:
                for p in range(i):
                    q1= int(np.floor(A[p,j]/A[i,j]))
                    A[p,:] -= q1*A[i,:] 
    
            i+=1
            j+=1

    
        
    return A
    
    


    