I quickly wrote this program to transform quadratic expressions in general form into vertex form.
\$ax^2 + bx + c \;=\; a(x-h)^2 + k,\quad\text{where}\quad h = -\frac{b}{2a} \quad\text{and}\quad k = c - ah^2 = c - \frac{b^2}{4a}.\$
The actual conversion is easy, but with writing a lexer/parser came pain points:
I tokenize with Regex, and some of the regex patterns are built dynamically using f-strings (which seems suspect).
I use
eval
to compute terms in parentheses (to make fractions easy to input, for example).
I also feel like there's probably a library for working with ASCII-fied algebraic expressions, (lots of the same kind of code populates sites like Symbolab, Cymath and Desmos,) but I do not know of it.
## Tests:
# user_in = "(16/4)x^2 7x (3**2)" -> "Result is: 4.0(x + (0.875))^2 + (5.9375)" (Passes)
# user_in = "(41*4)x^2 41x 2" -> "Result is: 164.0(x + (0.125))^2 + (-0.5625)" (Passes)
# user_in = "x^2 4" -> "Result is: (x + (2.0))^2 + (-4.0)" (Fails)
# user_in = "x^2 4x 16" -> "Result is (x + (2.0))^2 + 12.0" (Fails)
import re
print("""~~~Square Completer!~~~
Enter the terms of your quadratic equation in standard form
Addition is implied, terms separated by spaces.
Only use one kind of single-letter variable in the whole expression.
Surround computations such as constant multiplication and fractions in parens.
DO NOT put variables inside parens!""")
user_in = input("> ")
# extract the variable
def extract_variable(expression):
letters = re.findall(r'[a-z]', expression) # all the letters
if all(match == letters[0] for match in letters): # if all letters == the first one
return letters[0] # we have our variable
else:
raise ValueError("Only use one kind of single-letter variable in the whole expression.")
variable = extract_variable(user_in)
# eval all things in parens and split string into terms
def split_into_terms(expression):
expression = re.sub(fr"\([^{variable}\)]+\)", lambda m: str(eval(m.group())), expression)
print(f"working with {expression}")
return expression.split(' ')
terms = split_into_terms(user_in)
print(terms)
# sort terms into bins
def bin_terms(terms):
squareds, x_es, constants = [], [], []
for term in terms:
term = term.strip().lower()
if term.endswith('^2'):
squareds.append(term[:-2]) # without trailing '^2'
elif term.endswith(variable):
x_es.append(term)
elif bool(re.search(r"^-?[0-9\.]+$", term)): # if the term contains only numbers
constants.append(term)
return squareds, x_es, constants
squareds, x_es, constants = bin_terms(terms)
print('unprocessed:', squareds, x_es, constants, sep='\n')
def sum_bins(squareds, x_es, constants):
squareds = [squared[:-1] for squared in squareds if squared.endswith(variable)] # 2x -> 2
print('squareds step1:', squareds, sep='\n')
squareds = ['1' if squared == variable else squared for squared in squareds] # x -> 1
print('step2:', squareds)
print('\nprocessed:', squareds, x_es, constants, sep='\n')
constant = sum(float(c) for c in constants)
print('\n ***EXES: ', x_es,'***')
x = sum(float(el[:-1]) for el in x_es)
print('\n ***SUMX: ', x,'***')
squared = sum(float(el) for el in squareds)
return squared, x, constant
squared, x, constant = sum_bins(squareds, x_es, constants)
##print('\ntotals:', squared, x, constant, sep='\n')
##constant = constant / squared
##x = x / squared
##squared = 1
##print('\nover square term:', squared, x, constant, sep='\n')
def complete_square(a,b,c):
"""
Complete the square, transforming a quadratic
of form
ax^2 + bx + c
to form
a(x + p)^2 - q
"""
p = b/(2*a)
q = c - (b**2)/(4*a)
return p, q
p, q = complete_square(squared, x, constant)
a = squared
print(f'Result is: {a}({variable} + ({p}))^2 + ({q})')
1 Answer 1
There is a library to help with this! Check out sympy
, particularly sympify
and parse_expr
.
Explore related questions
See similar questions with these tags.