#!/usr/bin/env python2 # -*- coding: utf-8 -*- # I, Danny Milosavljevic, place this file in the public domain. import data # trivial: # a+0=a # a⋅1=a # a⋅0=0 # a^0=1 # a⋅(1/a)=1 # a+(0-a)=0 # nontrivial: # a⋅(b+c)=a⋅b+a⋅c # (b+c)⋅a=b⋅a+c⋅a # (√a)⋅(√b)=(√(a⋅b)) # a+b=b+a # s⋅(t⋅a)=(s⋅t)⋅a # a⋅(b⋅c)=(a⋅b)⋅c # when solving for a variable, try to isolate that variable x into one y⋅x=b term. # then try to divide by y, if possible; or use the gauss algorithm or the eigenvalue algorithm if (y⋅x=b⋅x) # Lx=λ⋅x # expand all a⋅(b+c) and (b+c)⋅a. # sort operands (the inverses by their operand). # isolate the x by transforming it to (...)⋅x # try to keep x to the right of the left side. # try to multiply/subtract by inverses until the x is staying there on its own. # x^2=4 def expand_braces(expression): if data.equation_P(expression): return data.Equation([expand_braces(item) for item in expression.operands]) elif data.relation_P(expression): return data.Relation([expand_braces(item) for item in expression.operands]) else: # FIXME a⋅(b+c) => 7⋅x+7⋅2 # also for a=derivation. # FIXME a⨯(b+c) => a⨯b+a⨯c # also for a=Nabla. # FIXME a∙(b+c) => a∙b+a∙c # also for a=Nabla. return expression def homogenize(expression): """ given an equation A=B, returns one that says A-B=0 """ assert(data.relation_P(expression) or data.equation_P(expression)) assert(len(expression.operands) == 2) # FIXME support systems? return data.simplify_all(expression.__class__([data.Addition([expression.operands[0], data.simplify(data.negate(expression.operands[1]))]), 0])) def solve(for_variable, expression): # TODO eliminate common terms/factors/... expression = homogenize(expression) # => x+5-3=0 expression = expand_braces(expression) expression = filter_sides(for_variable, expression) expression = sort_names(expression) # maybe do that just for LHS? expression = remove_redundancy(expression) expression = isolate_left_name(for_variable, expression) return expression def filter_sides(for_variable, expression): # FIXME take care not to do invalid stuff for matrices. assert(data.relation_P(expression) or data.equation_P(expression)) assert(len(expression.operands) == 2) # FIXME support systems? LHS, RHS = expression.operands assert(data.addition_P(LHS)) assert(RHS == 0) new_LHS = [operand for operand in LHS.operands if data.contains_variable_P(for_variable, operand)] new_RHS = [operand for operand in LHS.operands if not data.contains_variable_P(for_variable, operand)] new_LHS = lift_out_once(for_variable, new_LHS) return expression.__class__([data.simplify(data.Addition(new_LHS)), data.negate(data.Addition(new_RHS))]) def sort_names(expression): return expression def remove_redundancy(expression): return expression def isolate_left_name(for_variable, expression): return expression def lift_out_once(for_variable, expression): # FIXME return expression # >>> a.sort(key=lambda obj: obj.eggs) if __name__ == "__main__": import StringIO import parsers import sys import formatters def parse_expression(text): text = text.replace("²", "^2") text = text.replace("³", "^3") expression = parsers.ExpressionParser(StringIO.StringIO(text)).parse() return expression def print_expression(expression): formatters.Formatter().format(expression, sys.stdout) sys.stdout.write("\n") print_expression(solve("x", parse_expression("x+5=3"))) print_expression(solve("x", parse_expression("7⋅(x+2)=3"))) print_expression(solve("x", parse_expression("x²=-1+x"))) print_expression(solve("x", parse_expression("5⋅x=10")))