diff --git a/main.py b/main.py new file mode 100644 index 0000000..f3fcb9d --- /dev/null +++ b/main.py @@ -0,0 +1,327 @@ +import itertools +import ast +import sys + + +class Poly: + + def __init__(self, int_mod, elements): + self.elements = {} + self.int_mod = int_mod + + elements = [x % self.int_mod for x in elements] + + 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 + 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(poly.int_mod, list(reversed(list(poly.elements.values())))) + + 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 __eq__(self, other): + return self.int_mod == other.int_mod and self.elements == other.elements + + 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}"] + + if poly.is_empty(): + break + + 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(poly.int_mod, list(reversed(list(poly.elements.values())))) + + # TODO + @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())))) + + if len(self.elements) < len(other.elements): + dividened, divisor = divisor, dividened + + # div_result = dividened % divisor + + # if math.gcd(self.int_mod, + # dividened.elements[f"x{len(dividened.elements) - 1}"]) == 1: + # if dividened.elements[f"x{len(dividened.elements) - 1}"] == 1: + # raise Exception("Leading dividened coefficient not invertible") + 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 div_result.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): + return self == Poly(self.int_mod, [0]) + + +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, self.int_modulo)], + repeat=len(self.poly_modulo.elements) - 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)) + + # FIXME + def invertibles(self): + invertibles = [] + + one = Poly(self.int_modulo, [1]) + + for e in self.elements: + for f in self.elements: + if e * f % self.poly_modulo == one: + invertibles.append( + list(reversed(list(e.elements.values())))) + break + + return invertibles + + def zero_divisors(self): + zero_divisors = [[0]] + + zero = Poly(self.int_modulo, [0]) + + for e in self.elements: + for f in self.elements: + if e * f % self.poly_modulo == zero and e != zero and f != zero: + zero_divisors.append( + list(reversed(list(e.elements.values())))) + break + + return zero_divisors + + def nilpotents(self): + nilpotents = [] + + zero = Poly(self.int_modulo, [0]) + + for e in self.elements: + for n in range(1, self.int_modulo): + if e ** n % self.poly_modulo == zero: + nilpotents.append(list(reversed(list(e.elements.values())))) + break + + nilpotents[nilpotents.index([])] = [0] + return nilpotents + + def idempotents(self): + idempotents = [] + + for e in self.elements: + if e ** 2 % self.poly_modulo == e: + idempotents.append(list(reversed(list(e.elements.values())))) + + idempotents[idempotents.index([])] = [0] + return idempotents + + def __str__(self): + str_form = "[\n\t" + str_form += str(self.invertibles()) + ", # odwracalne\n\t" + str_form += str(self.zero_divisors()) + ", # dzielniki zera\n\t" + str_form += str(self.nilpotents()) + ", # nilpotenty\n\t" + str_form += str(self.idempotents()) + " # idempotenty\n" + str_form += "]\n" + return str_form + + +if __name__ == "__main__": + + if len(sys.argv) < 3: + print("Niepoprawny input") + exit(1) + + if sys.argv[1].find(",") != -1: + print(f"Proszę użyć spacji, nie przecinków: '{sys.argv[1]}'") + exit(1) + + field = PolyIntField(int(sys.argv[1]), ast.literal_eval(sys.argv[2])) + print(field) \ No newline at end of file