def try_polynomial(line): elements = {0: None, 1: None, 2: None} match = re.match(check_polynomial, line) if match: compute(fn.format_line(line), elements, 0) else: u.warn("Invalid Input.", "SyntaxError")
def __init__(self, function, param, data): self.function = function exp = u.check_unknown_vars(function, param, data) if exp is not None: self.formated = u.format_line(exp) else: u.warn("Too many unknown variables.", "SyntaxError") self.param = param
def draw(self, xMin, xMax): matrices = re.search(regex.checkMatrice, self.formated) complexes = re.search(regex.complex, self.formated) zeroDiv = re.search("\/\s*0", self.formated) if matrices is not None or complexes is not None: u.warn("Can't draw a function with matrices or complexes.", "DrawError") if zeroDiv is not None: u.warn("Division by 0.", "ComputeError") X = np.array(range(xMin, xMax)) y = eval(self.formated) plt.plot(X, y) plt.show()
def draw(self, x_min, x_max): matrices = re.search(regex.checkMatrice, self.formated) complexes = re.search( r"(?:\d+(?:\.\d+)?\s*[+-]\s*)?(?:(?!el)\d+(?:\.\d+)\s*\*\s*)?i", self.formated) zero_div = re.search(r"/\s*\(?\s*0\s*\)?", self.formated) if matrices is not None or complexes is not None: u.warn("Can't draw a function with matrices or complexes.", "DrawError") if zero_div is not None: u.warn("Division by 0.", "ComputeError") X = np.array(range(x_min, x_max)) y = eval(self.formated) plt.plot(X, y) plt.show()
def parse(self, exp): width = None height = 0 match = re.findall(regex.parseMatrice, exp) matrice = [[] for _ in range(len(match))] for m in match: elems = m.split(',') if width is None: width = len(elems) elif width != len(elems): u.warn("Matrice not well formated.", "syntaxError") for e in elems: matrice[height].append(Rational(u.int_float_cast(e))) height += 1 self.height = height self.width = width self.array = matrice self.str = exp return self
def get_inverse(self, m): determinant = self.get_determinant(m) if determinant == 0: u.warn("Can't compute inverse of matrice, determinant is 0.", "ComputeError") if len(m) == 2: return [[m[1][1] / determinant, -1 * m[0][1] / determinant], [-1 * m[1][0] / determinant, m[0][0] / determinant]] cofactors = [] for r in range(len(m)): cofactor_row = [] for c in range(len(m)): minor = self.get_minor(m, r, c) cofactor_row.append( ((-1)**(r + c)) * self.get_determinant(minor)) cofactors.append(cofactor_row) cofactors = self.transpose(cofactors, len(cofactors), len(cofactors[0])) for r in range(len(cofactors)): for c in range(len(cofactors)): cofactors[r][c] /= determinant return cofactors
def calc(self, operation, obj): type = obj.get_type() if operation == '+': if type == "rational": return Rational(self.value + obj.value) if type == "matrice" or type == "complex": return obj.calc('+', self) elif operation == '-': if type == "rational": return Rational(self.value - obj.value) elif type == "matrice": u.warn("Can't substract a matrice to a rational.", "ComputeError") elif type == "complex": ret = obj.negate() return ret.calc('+', self) elif operation == '*': if type == "rational": return Rational(self.value * obj.value) elif type == "matrice" or type == "complex": return obj.calc('*', self) elif operation == '/': frac = None if type == "complex": conj = obj.get_conj() div = self.calc('*', conj) den = obj.calc('*', conj) ret = div.calc('/', den) return ret elif type == "matrice": ret = copy.deepcopy(obj) if obj.width != obj.height: u.warn("Can't compute the inverse of an unsquare matrice", "ComputeError") if obj.array[0][0].get_type() == "complex": u.warn( "Can't divide by a matrice of complexes (Mat[[complex]])", "ComputeError") ret.array = ret.get_inverse(ret.array) return ret.calc('*', self) if obj.value == 0: u.warn("Division by 0.", "ComputeError") res = str(self.value / obj.value) if '.' in res and '.' not in self.str and '.' not in obj.str: cd = u.pgcd(self.value, obj.value) frac = str(self.value / cd) + "/" + str(obj.value / cd) if frac is not None: self.frac = frac return Rational(self.value / obj.value) elif operation == '%': if type != "rational": u.warn("Can't modulo a rational by a " + type + ".", "ComputeError") return Rational(self.value % obj.value) elif operation == '^': if type != "rational": u.warn("Can't elevate a rational to a " + type + ".", "ComputeError") return Rational(self.value**obj.value)
def calc(self, operation, obj): ret = copy.deepcopy(self) type = obj.get_type() if operation == "+": if type == "rational": ret.real += obj.value elif type == "complex": ret.real += obj.real ret.imaginary += obj.imaginary ret.imgIsNeg = ret.imaginary < 0 elif type == "matrice": u.warn("Can't add a matrice to a complex.", "ComputeError") elif operation == "-": if type == "rational": ret.real -= obj.value elif type == "complex": ret.real -= obj.real ret.imaginary -= obj.imaginary ret.imgIsNeg = ret.imaginary < 0 elif type == "matrice": u.warn("Can't substract a matrice to a complex.", "ComputeError") elif operation == "*": if type == "rational": ret.real *= obj.value ret.imaginary *= obj.value ret.imgIsNeg = ret.imaginary < 0 elif type == "complex": old = ret.real ret.real = ret.real * obj.real - ret.imaginary * obj.imaginary ret.imaginary = old * obj.imaginary + ret.imaginary * obj.real elif type == "matrice": ret = copy.deepcopy(obj) for i in range(obj.height): for j in range(obj.width): ret.array[i][j] = self.calc('*', obj.array[i][j]) elif operation == "/": if type == "rational": ret.real = round(ret.real / obj.value, 3) ret.imaginary = round(ret.imaginary / obj.value, 3) elif type == "complex": div = Rational(obj.real**2 + obj.imaginary**2) conj = obj.get_conj() ret = self.calc('*', conj) ret = ret.calc('/', div) elif type == "matrice": if obj.width != obj.height: u.warn("Can't compute the inverse of an unsquare matrice", "ComputeError") if obj.array[0][0].get_type() == "complex": u.warn( "Can't divide by a matrice of complexes (Mat[[complex]])", "ComputeError") ret = copy.deepcopy(obj) ret.array = ret.get_inverse(obj.to_int_tab(obj.array)) for i in range(ret.height): for j in range(ret.width): ret.array[i][j] = self.calc('*', Rational(ret.array[i][j])) elif operation == "%": if type == "rational": ret.real %= obj.value ret.imaginary %= obj.value ret.imgIsNeg = ret.imaginary < 0 elif type == "complex" or type == "matrice": u.warn("Can't modulo a complex by a " + type + ".", "ComputeError") elif operation == '^': if type != "rational": u.warn("Can't elevate a complex to a " + type + ".", "ComputeError") for i in range(obj.value): ret = copy.deepcopy(self) for j in range(obj.value - 1): ret = ret.calc('*', self) if ret.get_type() == "complex": ret.real = u.int_float_cast(str(ret.real)) ret.imaginary = u.int_float_cast(str(ret.imaginary)) ret.imgIsNeg = ret.imaginary < 0 ret.str = ( str(ret.real) + (" + " if not ret.imgIsNeg and ret.imaginary != 0 else " ") if ret.real != 0 else "") + ( (str(ret.imaginary) + "i") if ret.imaginary != 0 else "") return ret
def calc(self, operation, obj): ret = Matrice() new = copy.deepcopy(self.array) type = obj.get_type() if operation == "+": if type == "rational" or type == "complex": u.warn("Can't add a " + type + " to a matrice.", "ComputeError") elif type == "matrice": if self.height == obj.height and self.width == obj.width: for i in range(len(new)): for j in range(len(new[i])): new[i][j] = new[i][j].calc('+', obj.array[i][j]) else: u.warn("Can't add matrices of different dimensions.", "ComputeError") elif operation == "-": if type == "rational" or type == "complex": u.warn("Can't substract a " + type + " to a matrice.", "ComputeError") elif type == "complex": for i in range(self.height): for j in range(self.width): new[i][j] = new[i][j].calc('-', obj) elif type == "matrice" or type == "complex": if self.height == obj.height and self.width == obj.width: for i in range(len(new)): for j in range(len(new[i])): new[i][j] = new[i][j].calc('-', obj.array[i][j]) else: u.warn("Can't substract matrices of different dimensions.", "ComputeError") elif operation == "*": if type == "rational" or type == "complex": for i in range(len(new)): for j in range(len(new[i])): new[i][j] = new[i][j].calc('*', obj) elif type == "matrice": if self.width == obj.height: new = [] i = j = m = 0 while i < self.height or j < self.width: new.append([]) j = k = 0 while k < obj.width: j = l = 0 res = 0 while j < self.width: res += obj.array[l][k].calc( '*', self.array[i][j]).value j += 1 l += 1 new[m].append(Rational(res)) k += 1 m += 1 i += 1 else: u.warn( "Can't resolve m1 * m2 : Number of raws in m1 doesn't match number of columns in m2.", "ComputeError") elif operation == "/": if type == "rational" or type == "complex": for i in range(self.height): for j in range(self.width): new[i][j] = new[i][j].calc('/', obj) elif type == "matrice": if obj.width != obj.height: u.warn("Can't compute the inverse of an unsquare matrice", "ComputeError") if obj.array[0][0].get_type() == "complex": u.warn( "Can't divide by a matrice of complexes (Mat[[complex]])", "ComputeError") new = copy.deepcopy(obj.array) for i in range(obj.height): for j in range(obj.width): new[i][j] = obj.array[i][j].value new = self.get_inverse(new) for i in range(len(new)): for j in range(len(new)): new[i][j] = Rational(new[i][j]) ret.array = new ret.height = obj.height ret.width = obj.width ret = self.calc('*', ret) return ret elif operation == "%": if type == "rational": for i in range(self.height): for j in range(self.width): new[i][j] = new[i][j].calc('%', obj) else: u.warn("Can't modulo a " + type + " by a matrice", "ComputeError") elif operation == '^': if type != "rational": u.warn("Can't elevate a matrice to a " + type + ".", "ComputeError") tmp = copy.deepcopy(self) for i in range(obj.value - 1): tmp = tmp.calc('*', self) new = tmp.array ret.array = new.copy() ret.height = self.height ret.width = self.width ret.str = "[" first_raw = True for i in range(len(new)): if first_raw: first_raw = False else: ret.str += ';' ret.str += '[' first = True for j in range(len(new[0])): if first: first = False else: ret.str += "," ret.str += ret.array[i][j].str ret.str += ']' ret.str += ']' return ret
def parse(exp, data, i, tmp): """ Replace all the elements in the string by a key and stores the object in the dictionary tmp with the same key. :param exp : the expression to evaluate data : the program dictionary, containing all the assigned variables so far i : the index to make the key to store elements in the dictionnary (key = "el" + str(i)) tmp : the temporary dictinnary where we store all the parsed elements :order - find and evaluate brackets - parse Complexes - parse Matrices - parse Variables - parse negative Rationals - parse positive Rationals :return obj{exp, index, tmp}: exp : the formated expression index : the key index, to be used by compute tmp : the tmp dictionary filled with the parsed objects :e.g. parse("5 - [[1,2];[2,3]] * 5 + 3i") return: exp = "el0 - el1 * el2" index = 3 tmp = {"el0": Rational(5), "el1": Matrice([[1,2];[2,3]]), "el2": Complex(5 + 3i)} """ match = True brackets = True while brackets is not None: brackets = u.findBrackets(exp) if brackets is not None: ret = resolve(brackets, data) key = "el" + str(i) tmp[key] = ret i += 1 exp = re.sub("(\d+(?:\.\d+)?|[A-Za-z])\s*\(" + brackets + "\)", r"\1 * (" + brackets + ")", exp) exp = exp.replace("(" + brackets + ")", key, 1) while match: # find and replace complexes match = re.search(regex.complex, exp) if match: c = Complex() string = match.group(0) key = "el" + str(i) i += 1 exp = exp.replace(string, key, 1) tmp[key] = c.parse(string) match = True while match: # find and replace matrices match = re.search(regex.checkMatrice, exp) if match: m = Matrice() string = match.group() key = "el" + str(i) i += 1 exp = exp.replace(string, key, 1) tmp[key] = m.parse(string) match = True while match: # find and replace functions match = re.search("(fun[a-z]\(([a-z\d\s+\-/*%^]+)\))", exp, flags=re.IGNORECASE) if match: obj = match.group(1) if obj[0:4] in data.keys(): fn = data[obj[0:4]] param = u.intFloatCast(match.group(2)) if param: key = "el" + str(i) i += 1 ret = resolve(fn.formated.replace('X', match.group(2)), data) exp = exp.replace(obj, key) tmp[key] = ret elif match.group(2) in data.keys(): param = data[match.group(2)] ret = resolve(fn.formated.replace('X', param.str), data) key = "el" + str(i) i += 1 tmp[key] = ret exp = exp.replace(obj, key) else: param = resolve(match.group(2), data) if param: ret = resolve(fn.formated.replace('X', param.str), data) key = "el" + str(i) i += 1 tmp[key] = ret exp = exp.replace(obj, key) else: u.warn("The function " + obj[0:4] + " is not assigned.", "NameError") match = True while match: match = re.search("(?:[^a-z]|^)(?!el)([A-Z]+)", exp, flags=re.IGNORECASE) if match: obj = match.group(1) if obj in data.keys(): key = "el" + str(i) i += 1 tmp[key] = data[obj] exp = re.sub("([^a-z]|^)(?!el)([A-Za-z]+)", r"\1" + key, exp, 1) else: u.warn("The variable " + obj + " is not assigned.", "NameError") match = True while match: match = re.search("(?:[+\-*/%]|^)\s*(-\s*\d+(?:\.\d+)?)", exp) # find and replace negative rationals if match: key = "el" + str(i) var = Rational(u.intFloatCast(match.group(1).replace(" ", ""))) i += 1 tmp[key] = var exp = re.sub("([+\-*/%]|^)\s*(-\s*\d+(?:\.\d+)?)", r"\1 " + key, exp, 1) match = True while match: match = re.search("(?<!el)\d+(?:\.\d+)?", exp) # find and replace positive rationals if match: key = "el" + str(i) var = Rational(u.intFloatCast(match.group(0))) i += 1 tmp[key] = var exp = re.sub("(?<!el)\d+(?:\.\d+)?", key, exp, 1) return {"exp": exp, "index": i, "tmp": tmp}
def compute(parsed): """ Replace the calculations by a key and stores the object in the tmp dictionary with the same key. :param: obj{exp, index, tmp} exp : the parsed expression to evaluate index : the index to make the key to store elements in the dictionnary (key = "el" + str(i)) tmp : the temporary dictinnary where we store all the parsed elements :order: - evaluate powers - evaluate * / % - evaluate + - :return: The last object in exp: exp should now contain only one key as "el7" if True, then return the corresponding object from tmp """ i = parsed["index"] exp = parsed["exp"] tmp = parsed["tmp"] match = True while match: match = re.search("(el\d+)\s*(?:\*\*|\^)\s*(el\d+)", exp) if match: var1 = tmp[match.group(1)] var2 = tmp[match.group(2)] ret = var1.calc('^', var2) key = "el" + str(i) i += 1 tmp[key] = ret exp = re.sub("(el\d+)\s*(?:\*\*|\^)\s*(el\d+)", key, exp, 1) match = True while match: match = re.search("(el\d+)\s*([*/%])\s*(el\d+)", exp) if match: var1 = tmp[match.group(1)] var2 = tmp[match.group(3)] ope = match.group(2) ret = var1.calc(ope, var2) key = "el" + str(i) i += 1 tmp[key] = ret exp = re.sub("(el\d+)\s*([*/%])\s*(el\d+)", key, exp, 1) match = True while match: match = re.search("(el\d+)\s*([+-])\s*(el\d+)", exp) if match: var1 = tmp[match.group(1)] var2 = tmp[match.group(3)] ope = match.group(2) ret = var1.calc(ope, var2) key = "el" + str(i) i += 1 tmp[key] = ret exp = re.sub("(el\d+)\s*([+-])\s*(el\d+)", key, exp, 1) if exp.strip() in tmp.keys(): return tmp[exp.strip()] else: match = re.match("^-\s*(el\d+)$", exp.strip()) if match and match.group(1) in tmp.keys(): return tmp[match.group(1)].negate() elif "i" in exp.strip(): c = Complex() return c.parse(exp.strip()) else: u.warn("Invalid input.", "SyntaxError")
def compute(line, elements, side): if details: print("\n\033[0;32m[details]\033[0m natural parsing : " + line + "\n") line_split = line.split('=') reduced = "Reduced form: " detail_string = "\n\033[0;32m[details]\033[0m Simplified form : " res = 0 first = True detail_first = True degree = 0 count = 0 while side < 2: exp = re.findall(regex_pattern, line_split[side]) for i in range(len(exp)): pwr = int(exp[i][1].replace(' ', '')) num = float(exp[i][0].replace(' ', '')) try: if elements[pwr] is None: elements[pwr] = 0 elements[pwr] += num if side == 0 else -num except KeyError: elements[pwr] = num if side == 0 else -num side += 1 for key, value in elements.items(): if value is not None: if value == 0: count += 1 else: if key > degree: degree = key if first: reduced += fn.str_int_float(value) + " * X^" + str(key) first = False else: reduced += (" + " + fn.str_int_float(value) if value >= 0 else " - " + fn.str_int_float(-value)) \ + " * X^" + str(key) if detail_first: value_str = fn.str_int_float(value) if key != 0: detail_first = False else: value_str = (" + " + fn.str_int_float(value) if value >= 0 else " - " + fn.str_int_float(-value)) if key == 0: res -= value if key == 1: detail_string += value_str + "X" if key > 1: detail_string += value_str + "X^" + str(key) else: count += 1 elements[key] = 0 if degree == 0: if elements[0] == 0: print(reduced + " = 0") print("This equation accepts all real numbers as solution.") else: u.warn("Invalid input.", "SyntaxError") elif count == len(elements): print(reduced + " = 0") print("This equation accepts all real numbers as solution.") else: print(reduced + " = 0") print("Polynomial degree: " + str(degree)) if degree < 3 and details: print(detail_string + " = " + fn.str_int_float(res)) if degree <= 2: b = elements[1] c = elements[0] if degree == 1: if b == 0: u.warn("Division by 0.", "ComputeError") else: x = -c / b if details: print("\033[0;32m[details]\033[0m X = " + fn.str_int_float(-c) + "/" + fn.str_int_float(b) + "\n") print("The solution is:") fn.print_solution(x, -c, b) elif degree == 2: a = elements[2] delta = b * b - 4 * a * c if details: print( "\033[0;32m[details]\033[0m Calculating discriminant : " + fn.str_int_float(b) + "^2 - 4 * " + fn.str_int_float(a) + " * " + fn.str_int_float(c) + " = " + fn.str_int_float(delta) + "\n") if delta > 0: x1 = (-b - delta**0.5) / (2 * a) x2 = (-b + delta**0.5) / (2 * a) print( "Discriminant is strictly positive, the two solutions are:" ) if details: print( "\n\033[0;32m[details]\033[0m Calculating solution 1 : (" + fn.str_int_float(-b) + " - " + fn.str_int_float(delta) + "^0.5) / " + fn.str_int_float(2 * a)) print( "\033[0;32m[details]\033[0m Calculating solution 2 : (" + fn.str_int_float(-b) + " + " + fn.str_int_float(delta) + "^0.5) / " + fn.str_int_float(2 * a) + "\n") fn.print_solution(x1, (-b - delta**0.5), (2 * a)) fn.print_solution(x2, (-b + delta**0.5), (2 * a)) elif delta < 0: print( "The discriminant is strictly negative, the equation has no real solution," + "only 2 complex solutions:") print("(" + fn.str_int_float(-b) + " − i√" + fn.str_int_float(-delta) + ") / " + fn.str_int_float(2 * a)) print("(" + fn.str_int_float(-b) + " + i√" + fn.str_int_float(-delta) + ") / " + fn.str_int_float(2 * a)) else: if a == 0: u.warn("Division by 0.", "ComputeError") x = -b / (2 * a) print( "The discriminant is 0, the equation has one solution:" ) if details: print( "\033[0;32m\n[details]\033[0m Calculating solution : " + fn.str_int_float(-b) + "/" + fn.str_int_float(2 * a) + "\n") fn.print_solution(x, -b, c) else: u.warn( "The polynomial degree is strictly greater than 2, I can't solve.", "ComputeError") return True
def tryPolynomial(line): elements = {0: None, 1: None, 2: None} try: compute(fn.formatLine(line), elements, 0) except Exception: u.warn("Invalid input.", "SyntaxError")