from functools import reduce
from itertools import product


def euler(n):
    print(f"Вычисление количества взаимно простых с {n} чисел, меньших него")
    result = n
    p = 2
    while p * p <= n:
        if n % p == 0:
            result -= result // p
            while n % p == 0:
                n //= p
        p += 1
    if n > 1:
        result -= result // n
    return result


class Ring:
    # проверка на противоположный элемент
    def __init__(self, elements, add, mul):
        self.elements = elements
        self.add = add
        self.mul = mul
        self.zero = None
        self.one = None

    def is_commutative(self, operation): #проверяет аксиомы 4 и 9
        print(f"проверка коммутативности операции {operation}")
        for a, b in product(self.elements, repeat=2):
            if operation(a, b) != operation(b, a):
                return False
        return True

    def is_associative(self, operation): #аксиомы 1 и 7
        print(f"проверка ассоциативности операции {operation}")
        for a, b, c in product(self.elements, repeat=3):
            if operation(operation(a, b), c) != operation(a, operation(b, c)):
                return False
        return True

    def find_identity(self, operation): #восьмая аксиома 8 (mul) и 2 аксиома (add) ( в зависимости о топерации)
        print("Проверка существования")
        for e in self.elements:
            if all(operation(e, x) == x and operation(x, e) == x for x in self.elements):
                return e
        return None

    def has_inverses(self): #3 аксиома
        print("Проверка существования противоположного элементов")
        for a in self.elements:
            if not any(self.add(a, b) == self.zero for b in self.elements):
                return False
        return True

    def is_distributive(self): #проверка левой и правой дистрибутивности 5 и 6 аксиома
        print("Проверка правой и левой дистрибутивности")
        for a, b, c in product(self.elements, repeat=3):
            if self.mul(a, self.add(b, c)) != self.add(self.mul(a, b), self.mul(a, c)):
                return False
            if self.mul(self.add(b, c), a) != self.add(self.mul(b, a), self.mul(c, a)):
                return False
        return True

    def is_ring_with_one(self): # проверка всех аксиом
        print("Проверка на кольцо:")
        self.zero = self.find_identity(self.add)
        self.one = self.find_identity(self.mul)
        return (
            self.zero is not None and
            self.one is not None and
            self.is_commutative(self.add) and
            self.is_commutative(self.mul) and
            self.is_associative(self.add) and
            self.is_associative(self.mul) and
            self.has_inverses() and
            self.is_distributive()
        )

    def find_invertible_elements(self): # поиск обратимых элементов (обратимые элементы при произведении дают единицу)
        invertible = set()
        for a in self.elements:
            for b in self.elements:
                if self.mul(a, b) == self.one and self.mul(b, a) == self.one:
                    invertible.add(a)
                    print(f"Найден обратимый элемент: {a}^(-1) = {b}")
                    break
       #print("Проверка на существование обтаных элементов для всех элементов группы")
        return invertible #возвращает все уникальные обратимые элементы (предположительная группа)

    def is_multiplicative_group(self, invertible): # замкнутость = произведение любых двух ее элементов принадлежит группе
        print("Проверка на замкнутость по умножению")
        if self.one not in invertible:
            return False
        for a, b in product(invertible, repeat=2):
            if self.mul(a, b) not in invertible:
                return False
        return True
    # необходимо дополнительно проверить ассоциативность умножения для группы и существование единицы для группы 

    def is_multiplicative_group_with_one(self, invertible): # проверка на существование единицы и проверка ассоциативности
        print("Проверка на группу по умножению:")
        self.one = self.find_identity(self.mul)
        return (
            self.one is not None and
            self.is_associative(self.mul) and
            self.is_multiplicative_group(invertible)
        )

    def check_ring_properties(self):
        if not self.is_ring_with_one():
            print("Множество НЕ является ассоциативным кольцом с 1.")
            return
        print("Множество является ассоциативным кольцом с 1.")

        invertible = self.find_invertible_elements()
        if self.is_multiplicative_group_with_one(invertible):
            print(f"Обратимые элементы {invertible} образуют мультипликативную группу относительно умножения.")
        else:
            print("Обратимые элементы НЕ образуют мультипликативную группу.")


elements = list(input("Введите элементы множества через пробел: ").split())

print(f"Введите таблицу сложения размерности {len(elements)}x{len(elements)}, элементы строчки через пробел, каждую сточку с новой строки:")
add_table = [list(input().split()) for _ in range(len(elements))]

print(f"Введите таблицу умножения размерности {len(elements)}x{len(elements)}, элементы строчки через пробел, каждую сточку с новой строки:")
mul_table = [list(input().split()) for _ in range(len(elements))]


def add(a, b):
    for i in range(len(elements)):
        if a == elements[i]:
            i1 = i
        if b == elements[i]:
            i2 = i
    return add_table[i1][i2]


def mul(a, b):
    for i in range(len(elements)):
        if a == elements[i]:
            i1 = i
        if b == elements[i]:
            i2 = i
    return mul_table[i1][i2]


ring = Ring(elements, add, mul)
ring.check_ring_properties()
inputik = int(input('Введите n'))

print(f"Порядок (количество элементов) в мультипликативной группе Z{inputik} "
      f"равен значению функции Эйлера: phi({inputik}) = {euler(inputik)}")

'''
0 1 2 3 4 5 6 7

0 1 2 3 4 5 6 7
1 2 3 4 5 6 7 0
2 3 4 5 6 7 0 1
3 4 5 6 7 0 1 2
4 5 6 7 0 1 2 3
5 6 7 0 1 2 3 4
6 7 0 1 2 3 4 5
7 0 1 2 3 4 5 6

0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7
0 2 4 6 0 2 4 6
0 3 6 1 4 7 2 5
0 4 0 4 0 4 0 4
0 5 2 7 4 1 6 3
0 6 4 2 0 6 4 2
0 7 6 5 4 3 2 1

'''

Перепиши программу с использованием node js 
считыванием массива elements, а также таблиц умножения и сложения разнеси по отдельным файлам. считывание элемента inputik для функции эйлера тоже необходимо вынести в отдельный файл