import itertools
import time

def operation(x,y,R):
    """
    :params:
        x: int
        y: int
        R: list of tuples
    :return:
        int
    
    This function returns the result of the operation R on x and y
    """
    for relation in R:
        if relation[0]==x and relation[1]==y:
            return relation[2]

def associativity_checker(n,R):
    """
    :params:
        n: int
        R: list of tuples
    :return:
        bool
    
    This function checks if the operation R is associative or not. It returns True if it is associative and False otherwise.
    """
    for i in range(n):
        for j in range(n):
            for k in range(n):
                if operation(operation(i,j,R),k,R)!=operation(i,operation(j,k,R),R):
                    return False
    return True

def generate_all_operations(n):
    """
    :params:
        n: int
    :return:
        list of lists of tuples
    
    This function generates all possible operations on a set of n elements. It returns a list of all possible operations.
    """
    R=[]
    values = set(range(n))
    operations_results = itertools.product(values,repeat=n**2)
    f = open(f"output_n_{n}.txt","w")
    for operation_result in operations_results:
        op=[]
        for i in values:
            for j in values:
                op.append((i,j,operation_result[i*n+j]))
        if associativity_checker(n,op):
            R.append(op)
            f.write("\n\n")
            f.write(' |')
            for i in range(n):
                f.write(str(i)+"|")
            for i in range(n):
                f.write("\n"+"-+"*(n+1)+"\n")
                f.write(str(i)+"|")
                for j in range(n):
                    f.write(str(operation(i,j,op))+"|") 
    f.close()
    return R


def main():
    print("Enter the number of elements in the set. Number must be smaller than 4")
    n=int(input())
    data = generate_all_operations(n)
    print(len(data))
    count=0
    for i in data:
        if associativity_checker(n,i):
            print(i)
            count+=1
    print(count)

def file_handle(n):
    """
    :params:
        n: int
    :return:
        None
    
    This function generates all possible operations on a set of n elements and writes the results to a file. It also writes the number of associative operations.
    """
    semi_groups = generate_all_operations(n)
    f = open(f"output_n_{n}.txt","w")
    f.write("Number of elements in the set: "+str(n)+"\n")
    f.write("Number of associative operations: "+str(len(semi_groups))+"\n")
    for op in semi_groups:
            f.write("\n\n")
            f.write(' |')
            for i in range(n):
                f.write(str(i)+"|")
            for i in range(n):
                f.write("\n"+"-+"*(n+1)+"\n")
                f.write(str(i)+"|")
                for j in range(n):
                    f.write(str(operation(i,j,op))+"|") 
    f.close()


if __name__=="__main__":
    # file_handle(1)
    # file_handle(2)
    # file_handle(3)
    # file_handle(4)
    main()


