import itertools class Poly: def __init__(self, int_mod, elements): self.elements = {} self.int_mod = int_mod elements = list(reversed(elements)) z = 0 for i in elements: if i != 0: break z += 1 elements = elements[z:] i = len(elements) - 1 for e in elements: self.elements[f"x{i}"] = e % self.int_mod i -= 1 def __str__(self): str_form = "" deg = len(self.elements) - 1 for e in self.elements: if self.elements[e] >= 0: if e != f"x{deg}": str_form += "+ " else: str_form += "- " str_form += str(abs(self.elements[e])) if e != "x0": str_form += e[0] + "^" + e[1:] + " " return str_form def __mul__(self, other): assert self.int_mod == other.int_mod elements = {} for e in self.elements: for f in other.elements: coefficient = self.elements[e] * other.elements[f] degree = f"x{int(e[1:])+int(f[1:])}" if elements.get(f"{degree}") is None: elements[degree] = coefficient % self.int_mod else: elements[degree] += coefficient % self.int_mod els = {} for i in reversed(range(len(elements))): els[f"x{i}"] = elements[f"x{i}"] return Poly(self.int_mod, list(reversed(list(els.values())))) def __pow__(self, power, modulo=None): poly = Poly(self.int_mod, list(reversed(list(self.elements.values())))) for i in range(power - 1): poly *= poly return poly def __add__(self, other): assert self.int_mod == other.int_mod elements = self.elements.copy() for f in other.elements: if f in elements: elements[f] += other.elements[f] else: elements[f] = other.elements[f] els = {} for i in reversed(range(len(elements))): els[f"x{i}"] = elements[f"x{i}"] return Poly(self.int_mod, list(reversed(list(els.values())))) def __sub__(self, other): assert self.int_mod == other.int_mod elements = self.elements.copy() for e in other.elements: if e in elements: elements[e] -= other.elements[e] else: elements[e] = -other.elements[e] els = {} for i in reversed(range(len(elements))): els[f"x{i}"] = elements[f"x{i}"] return Poly(self.int_mod, list(reversed(list(els.values())))) def __truediv__(self, other): assert self.int_mod == other.int_mod if other.is_empty(): raise ZeroDivisionError("Polynomial is empty") poly = Poly(self.int_mod, list(reversed(list(self.elements.values())))) fdeg = len(poly.elements) - 1 sdeg = len(other.elements) - 1 els = {} while fdeg >= sdeg: coefficient = other.elements[f"x{sdeg}"] for i in range(1, poly.int_mod): if coefficient * i % poly.int_mod == poly.elements[f"x{fdeg}"]: coefficient = i break degree = fdeg - sdeg divpoly = [0 for x in range(degree + 1)] divpoly[degree] = coefficient divpoly = Poly(poly.int_mod, divpoly) els[f"x{degree}"] = coefficient mulpoly = divpoly * other poly -= mulpoly for i in poly.elements.keys(): if poly.elements[f"{i}"] != 0: fdeg = int(i[1:]) break return Poly(poly.int_mod, list(reversed(list(els.values())))) def __mod__(self, other): assert self.int_mod == other.int_mod if other.is_empty(): raise ZeroDivisionError("Polynomial is empty") poly = Poly(self.int_mod, list(reversed(list(self.elements.values())))) fdeg = len(poly.elements) - 1 sdeg = len(other.elements) - 1 while fdeg >= sdeg: coefficient = other.elements[f"x{sdeg}"] for i in range(1, poly.int_mod): if coefficient * i % poly.int_mod == poly.elements[f"x{fdeg}"]: coefficient = i break degree = fdeg - sdeg divpoly = [0 for x in range(degree + 1)] divpoly[degree] = coefficient divpoly = Poly(poly.int_mod, divpoly) mulpoly = divpoly * other poly -= mulpoly for i in poly.elements.keys(): if poly.elements[f"{i}"] != 0: fdeg = int(i[1:]) break return poly @staticmethod def gcd(self, other): dividened = Poly(self.int_mod, list(reversed(list(self.elements.values())))) divisor = Poly(self.int_mod, list(reversed(list(other.elements.values())))) div_result = dividened / divisor while True: xs_zero = True for e in div_result.elements: if e != "x0": if div_result.elements[e] != 0: xs_zero = False break # if xs_zero: # if div_result.elements["x0"] == 1: # break if dividened.is_empty(): break dividened = Poly(dividened.int_mod, list(reversed(list(divisor.elements.values())))) divisor = Poly(divisor.int_mod, list(reversed(list(div_result.elements.values())))) div_result = dividened / divisor return div_result def is_empty(self): for e in self.elements: if self.elements[e] != 0: return False return True class PolyIntField: def __init__(self, int_mod, poly_mod): self.int_modulo = int_mod self.poly_modulo = Poly(int_mod, poly_mod) product = list(itertools.product([x for x in range(0, int_mod)], repeat=len(poly_mod) - 1)) self.elements = [] for p in product: p = list(p) p = [x % int_mod for x in p] self.elements.append(Poly(int_mod, p)) def invertibles(self): invertibles = [] for e in self.elements: if Poly.gcd(e, self.poly_modulo) == 1: invertibles.append(e) return invertibles def nilpotents(self): nilpotents = [] for e in self.elements: if not e.is_empty(): for f in self.elements: if not f.is_empty(): if (e ** f) % self.poly_modulo == e: nilpotents.append(e) return nilpotents def idempotents(self): idempotents = [] for e in self.elements: if Poly.gcd((e ** 2), self.poly_modulo) == e: idempotents.append(e) return idempotents def zero_divisors(self): zero_divisors = [] for e in self.elements: if not e.is_empty(): for f in self.elements: if not f.is_empty(): if Poly.gcd((e * f), self.poly_modulo).is_empty(): zero_divisors.append(e) return zero_divisors if __name__ == "__main__": # poly_field = PolyIntField(3, [1, 1, 2, 2]) # print(poly_field.invertibles()) # for p in poly_field.elements: # print(p) # print(p.elements) # print(len(poly_field.elements)) # a = Poly(3, [0, 0, 0]) # b = Poly(3, [1, 2]) # print((a * b).is_empty()) # x = Poly(5, [1, 0, 4, 0, 2, 1]) # y = Poly(5, [4, 0, 0, 0, 1]) # print(x % y) # o = Poly(5, [1, 0, 1, 2, 2, 1]) # print(o) # # p = Poly(5, [4, 0, 0, 0, 1]) # print(p) # print(o / p) # n = Poly(5, [1, 0, 1]) # m = Poly(5, [2, 4]) # print(n / m) # a = Poly(5, [1, 0, 4, 0, 2, 1, 0, 0]) # # print(a) # b = Poly(5, [4, 0, 0, 0, 1]) # # print(b) # # print(a + b) # print(a % b) # d = Poly(5, [3, 1, 4]) # # print(d) # e = Poly(5, [4, 0, 0, 0, 1]) # # print(e) # print(e / d) # c = Poly(5, [4, 0, 0, 0, 1]) # d = Poly(5, [3, 1, 4, 0, 0, 0]) # print(c) # print(d) # print(c % d) e = Poly(10, [-4, 0, -2, 1]) f = Poly(10, [-3, 1]) print(e / f) print(e % f) print(f / e) print(f % e) # print((a % b).elements) # d = Poly(10, [2, 0, 6, 0, 1]) # e = Poly(10, [5, 0, 1]) # print(d / e) # print(a - Poly(10, [0, 0, -3, 1])) # p = Poly(5, [-4, 0, -2, 1]) # print(p) # c = p * p # print(c.elements) # print(c) # print(p ** 2) # print(p) # print(d) # print(p) # print(p + d) # d = Poly(5, [-3, 1]) # g = Poly(5, [1, 2, 1]) # print(d) # print(g) # print() # # # print(g) # print(g - d) # print(d - g) # # print() # print(d + g) # print(g + d) # # print() # print(d * g) # print(g * d) # print(d) # print(g) # print(g - d) # print(p / d) # print(p.elements[0]) # a = PolyIntField(3, [1, 1, 2, 2]) # for e in a.elements: # print(e.elements) # print() # print(a.elements[4]) # print(a.elements[8]) # print(a.elements[4] * a.elements[8]) # a.elements[3] / a.elements[1]