From 4e4440673b63dbc3463a0408d4eab0d4e7158f91 Mon Sep 17 00:00:00 2001 From: Accusedbold Date: Thu, 30 Apr 2026 00:39:44 -0400 Subject: [PATCH] print statements in places --- __pycache__/algebraic_steps.cpython-313.pyc | Bin 7283 -> 20669 bytes __pycache__/problem_generator.cpython-313.pyc | Bin 13194 -> 13516 bytes __pycache__/steps_generator.cpython-313.pyc | Bin 9240 -> 16155 bytes algebraic_steps.py | 347 +++++++++++++++++- main.py | 2 + problem_generator.py | 30 +- steps_generator.py | 139 ++++++- 7 files changed, 505 insertions(+), 13 deletions(-) diff --git a/__pycache__/algebraic_steps.cpython-313.pyc b/__pycache__/algebraic_steps.cpython-313.pyc index c77c17dd6c445c93f0727e6b0ea775b65ac30af6..b9b1f14d1ac089c1e8f72d3a7677073eed7ad55c 100644 GIT binary patch literal 20669 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 delta 1464 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 OdM!Mx_i(|&8ovQ-A?Jqx diff --git a/__pycache__/steps_generator.cpython-313.pyc b/__pycache__/steps_generator.cpython-313.pyc index 3cae9159c5eda905fad2cb5746ffab71ae804e9d..2449d1e3e69fc5eada7a3c573d98f131033e6e07 100644 GIT binary patch delta 4880 zcmb_gdrVu`8NUzP_u&@?U)$LDMR}QoM+gK0gak++*+`?}S|Mc%fhu z+AT(Dh0#eDx=K^J{nKfhmM+YMasVKUcY#y z(Y7P}-S3`rzjMEP&hPxr@33(6-PcSHjYg4z^2z&u8?UYZ!1NAFPZFbPknEDgJ4VeQ zE!0p{hzsgkFOfM-CHb-0M!u)D>y)Yi@))ZAY?sJ;+Ai8e{-pgjS$EjT*L6esI_Ok_ zU-S`0-m=sYEBCFMxIZqF$`#%?XP7nI7TaxzOzLb!S&L<1vC$+e`pxP$H@uM{8Pz6KT15eCL> z@14IEpJ2#$S%H3@e8yG#>3$X(Ho=0miX<{8h*~bh^_$2=vtQcwP2?lq!wfW&-)ihE*I!81 zc6{&ys~4am6$H1*q;_j2YF+>~%^(h(Qi9xmCai-jhl`3>ez2IViBcO}GwmTh^F1bN zFB_sWufhq)-KB>>*Y6@0jeCy}5`seOC5jqGXbVAB@gXF%!{CxOhldQ?Z1rX8;{y;{ zzVqXmt|p3bLGVo5m>lO^Js~DI__o;a|(g~mjIZ|2wvZ<#aAn&K}n_!E4|Oy>&6lY>q>VO(!9dMo+R6(E0c zRcaGb&oWQO-Oq>|^o@Xecp7kgj@i$UuNJw;IlhPwF+pt&PXSF7mBLTZ3V=s_29IuW z&~Fb*);MY;Nn zH)thpt(PLDyrB%&z`*$$J-oyKFUg``eKA@{`wws8|pd_PnD>@a~yTxbsY} zipJP*yz~0*nQnmlwO6mbx?(PjjmB75D7-#4{QBj1=gnjD$8JdpYen8JSGOe9-R2U~ zfn~m9rO>{>%m)^F7YoZ~xfMa2E1oM}HCf{R>*FzYOVGXNejz2KgUkG(mBQ&1Y5DV&=rTlDS9DIgWyw-QM?}qn*W%rV0_xmRkmZoLRzBPTpOrK01xyzNEXaWSnISrzc z#7(wBT)){>fV+o?y9fDD@Q_u>MsL_ENS){*2L#j*5+BqlON=A+ro$>B*n#BjNb=2a zh3Qbr7LregrAa=0r>;{{BKT=XdJnW60%!}{LR+QWES?qK1&D1?7r$~;oS=x;X&{rCQq|dWUJOlUtgkRKzxGP#L=-f=Lq{Zn$ zR(0p5rg_QIe2+_54lQdsVa`xBkvBX((q1Car%6xAPy>87^Q@9tfR#@{|H|T%2f>4i zoGwAR6h%VykxxD4?PtN0Kp;d>yvMO?R6>WcoWd^ zKkAexs{;chr<9-$F&NU3l*!rvDC9PvaC`Q@1nmDYus=$doiz?fmn?2=C~>^$T_^Ku zyymGcM4a?rik(fLlNCEITnE#_AYxJ}Th>8ZSE=jZ3nT{exdQqg73(Gy>p5b!qye78 zHp-5|jLybzWOmt0YAg;(Q6M95zX8#`USID^Q0YYsIlyEww91>~>~#a^ge;gq@8=-h zuw-et-JGzrEo<5Vb|7JAjg>{)iHr^|bb93pgtsbrZ-MNnh>5GdvRkLVZ@%xXzs=q~ ze9xI^JbZuek^AnRm^L;tE5s`od8f*32$dv%R)(!&eyyO(0O&eM%;p^bSpi1}Nrg7eeVoF5{6_9m1nzUf69jHLEX{rCJC*zLkfO`T zVnybE2Wi>_pCD!yNI$0MqN~Uw^M1CqngZEiCy&khNV!?swWA(L<0p?b;qc?sE0Q#g zjgK9rAq5-fVQ|!QkUo!ZHlNY(p$;@zhD@^IsS8PVbSxYNy_4ZepO5^~>wh5vHSLJ4 z7rqEv+bh?*-fak0)vf@m>Hi>rgSq~2xzXy#O`r36#v)T}m0c*ZlY`ZqZg1>rDxizDVul zccL$4NE0CGa|g(%uSp0n>2%4vzDn0+NG6O~newwzC;U_2A9de=>M5H1+&?p|EI~ZY z$%SZe`{Yg)K~_{0a*}Vq5E-4C z80(Oyp$?H0eGzPmrD^&hPtoLOrHy6>ZF$I2Oj(KnZ@rQHt8`%6uhvzi81UA6Xo_(z z>`KrT>r9#2Qkg<;y$@UNJ8Khk-8$n`o9a{Ot+!wdGz8WePHm`8p|@U$4S@t*mW?RS ii>OY}Jl&OQJCIrrn;r?<0b)RLlD z1^nXc#VMcrmHI>yRS`DbLD*E=;CH<>rD~AU}qm@|ZP-g&K~` z3XGbU8opYT2ex~*N2BXn=NGN><;}d-w`Mt&yI@(8SUcElE@aqtMe}0~r$Qbe1PLKn zv_)AIzS+7D_hEv1XJ#3Und9&$BExiDbMBfFHu>662-B+q|$OhS^7 zB18yLSZPeLlc2hi>{jlq>#k_6!Muc{GzSl(qoDP8nFKSQqt2jI)tfFNLpG50K>Z@k zP9PS9f-ESt3;kO#peNvYOADk8-SI-)E6s>h<@dK78p2>1M+x2<4e%+Tlh?`zF;zBs zB4K3&XYX+EVFXM@J^M|1%-eJkwGX={ZpUcD%*WGH6XP>5WW=g_er)q7<7oK2YX`O6 zy}xHim#(DIuvRg>={{h()1xbw(eS-u8ZZoz55j%JuS{cdyCSR_+wvUFeh9GKyD-1= z%jg;NIs!c%PH%|QU+3ot=LvK{oGyUV0Xc1()3!J*%V`NtUAcomr{)S`!_qr@HGN~| zQi@;4DZ`POL>45*Sji$Vc%AI1buvdu62#Vmh*2>CzmmiA)^aXT5HTtq5kyxuoM*nG zXetZ-0!2j|3ahSIp2aIlVo%AU5UdEHDj}F>trfvrC4}SN`d@kw B;g$dZ diff --git a/algebraic_steps.py b/algebraic_steps.py index 3ce887f..da05906 100644 --- a/algebraic_steps.py +++ b/algebraic_steps.py @@ -56,8 +56,8 @@ def subtract_both_sides(equation, value): left_expr = parse_expr(left, transformations=transformations, evaluate=False) right_expr = parse_expr(right, transformations=transformations, evaluate=False) - new_left_expr = left_expr - value - new_right_expr = right_expr - value + new_left_expr = clean(left_expr - value) + new_right_expr = clean(right_expr - value) step["after"] = f"{sstr(new_left_expr)} = {sstr(new_right_expr)}" step["step"] = f"Subtract both sides by {sstr(value)}" @@ -101,6 +101,44 @@ def multiply_both_sides(equation, value): step["step"] = f"Multiply both sides by {sstr(value)}" step["rule"] = "Multiplication Property of Equality" return step + +def square_root_both_sides(equation): + step = {} + + current = equation + step["before"] = current + left, right = current.split("=") + x_generic = symbols('x') + x_pos = symbols('x', positive=True) + + left_expr = parse_expr(left, transformations=transformations, evaluate=False) + right_expr = parse_expr(right, transformations=transformations, evaluate=False) + + new_left_expr = sqrt(left_expr.subs(x_generic, x_pos)) + new_right_expr = sqrt(right_expr) + step["after"] = f"{sstr(new_left_expr)} = {sstr(new_right_expr)}, {sstr(new_left_expr)} = -{sstr(new_right_expr)}" + + step["step"] = f"Take the square root of both sides" + step["rule"] = "Square Root Property of Equality" + return step + +def square_both_sides(equation): + step = {} + + current = equation + step["before"] = current + left, right = current.split("=") + + left_expr = parse_expr(left, transformations=transformations, evaluate=False) + right_expr = parse_expr(right, transformations=transformations, evaluate=False) + + new_left_expr = clean(left_expr * left_expr) + new_right_expr = clean(right_expr * right_expr) + step["after"] = f"{sstr(new_left_expr)} = {sstr(new_right_expr)}" + + step["step"] = f"Square both sides" + step["rule"] = "Multiplication property of equality" + return step def factor_collect(equation): step = {} @@ -122,6 +160,7 @@ def factor_collect(equation): return step def factor_form_collection(equation, factor): + # Collect factors of factor step = {} current = equation @@ -140,6 +179,180 @@ def factor_form_collection(equation, factor): step["rule"] = "Factor by grouping" return step +def factor_out(equation, factor): + step = {} + + current = equation + step["before"] = current + left, right = current.split("=") + x = symbols('x') + + left_expr = parse_expr(left, transformations=transformations, evaluate=False) + right_expr = parse_expr(right, transformations=transformations, evaluate=False) + + new_left_expr = clean(cancel(left_expr / factor)) + new_left_expr = Mul(factor, new_left_expr, evaluate = False) + step["after"] = f"{sstr(new_left_expr)} = {sstr(right_expr)}" + + step["step"] = f"Factor out the Greatest Common Factor, {sstr(factor)}" + step["rule"] = "Reverse Distributive Property" + return step + +def trinomial_by_grouping(equation, inner): + # expects n (ax**2+bx+c) = rhs : inner = (ax**2+bx+c), b != 0, c != 0 + + #4 steps + steps = [{}] + + current = equation + steps[-1]["before"] = current + left, right = current.split("=") + x = symbols('x') + + left_expr = parse_expr(left, transformations=transformations) + right_expr = parse_expr(right, transformations=transformations, evaluate=False) + n = simplify(left_expr / inner) + poly = inner.as_poly(x) + a = poly.coeff_monomial(x**2) + b = poly.coeff_monomial(x) + c = poly.coeff_monomial(1) + ac = Abs(a * c) + + ## Split Coeficients + factor1 = Integer(1) + factor2 = ac + while factor1 < factor2: + if ac % factor1 == 0: + factor2 = ac / factor1 + if c.is_negative: + if Abs(factor1 - factor2) == Abs(b): + break + else: + if factor1 + factor2 == Abs(b): + break + factor1 = factor1 + 1 + + if factor1 > factor2: + return [] + + if c.is_negative: + action = "differ by" + if b.is_negative: + left_expr = Add(a * x**2, factor1 * x, -factor2 * x, c, evaluate=False) + else: + left_expr = Add(a * x**2, -factor1 * x, factor2 * x, c, evaluate=False) + else: + action = "add up to" + if b.is_negative: + left_expr = Add(a * x**2, -factor1 * x, -factor2 * x, c, evaluate=False) + else: + left_expr = Add(a * x**2, factor1 * x, factor2 * x, c, evaluate=False) + if n != 1: + new_left_expr = Mul(n, left_expr, evaluate=False) + else: + new_left_expr = left_expr + + steps[-1]["after"] = f"{sstr(new_left_expr)} = {sstr(right_expr)}" + steps[-1]["step"] = f"Seperate the x coeficients equal to the factors of {sstr(ac)} that {action} {sstr(Abs(b))}" + steps[-1]["rule"] = "Split Coeficients" + + ## Factor Out X on left term + steps.append({}) + steps[-1]["before"] = steps[-2]["after"] + + terms = left_expr.as_ordered_terms() + t1, t2, t3, t4 = terms[0], terms[1], terms[2], terms[3] + factored_part1 = factor(t1 + t2) + factored_part2 = factor(t3 + t4) + rest = sum(terms[2:]) + new_left_expr = Add(factored_part1, rest, evaluate=False) + new_left_expr = Mul(n, new_left_expr, evaluate=False) + + steps[-1]["after"] = f"{sstr(new_left_expr)} = {sstr(right_expr)}" + steps[-1]["step"] = f"Factor out the x from the left two terms of the inner polynomial" + steps[-1]["rule"] = "Reverse Distributive Property" + + ## Factor Out GCD on right term + steps.append({}) + steps[-1]["before"] = steps[-2]["after"] + + left_expr = Add(factored_part1, factored_part2, evaluate=False) + new_left_expr = Mul(n, left_expr, evaluate=False) + + steps[-1]["after"] = f"{sstr(new_left_expr)} = {sstr(right_expr)}" + steps[-1]["step"] = f"Factor out the GCD from the right two terms of the inner polynomial" + steps[-1]["rule"] = "Reverse Distributive Property" + + ## Add Like Terms + if steps[-1]["before"] != steps[-1]["after"]: + steps.append({}) + steps[-1]["before"] = steps[-2]["after"] + terms = left_expr.as_ordered_terms() + factors = [set(t.as_ordered_factors()) for t in terms] + common = set.intersection(*factors) + base = list(common)[0] + coeffs = [t.coeff(base) for t in terms] + new_expr = sum(coeffs) * base + new_left_expr = flatten_mul(Mul(n, new_expr, evaluate=False)) + + steps[-1]["after"] = f"{sstr(new_left_expr)} = {sstr(right_expr)}" + steps[-1]["step"] = f"Collect the like terms" + steps[-1]["rule"] = "Combine the like terms" + + return steps + +def solve_roots(equation): + # expects n(ax+b)(x+c) = 0 + + #4 steps + steps = [] + + left, right = equation.split("=") + x = symbols('x') + + left_expr = parse_expr(left, transformations=transformations, evaluate=False) + factors = left_expr.as_ordered_factors() + x_factors = [f for f in factors if f.has(x)] + + solutions = "" + for factor in x_factors: + clean_factor = clean(factor) + steps.append({}) + if solutions: + solutions = solutions + ", " + steps[-1]["before"] = equation + steps[-1]["after"] = f"{sstr(clean_factor)} = 0" + steps[-1]["step"] = f"Focus on a root" + steps[-1]["rule"] = "Root of a polynomial" + + current = steps[-1]["after"] + a = clean_factor.coeff(x) + b = clean_factor.subs(x, 0) + if b.is_zero == False: + if b.is_negative: + steps.append(add_both_sides(current, -b)) + elif b.is_positive: + steps.append(subtract_both_sides(current, b)) + current = steps[-1]["after"] + left, right = current.split("=") + left_expr = parse_expr(left) + right_expr = parse_expr(right) + steps[-1]["after"] = f"{sstr(left_expr)} = {sstr(right_expr)}" + current = steps[-1]["after"] + if a != 1 and a != -1: + steps.append(divide_both_sides(current, a)) + elif a == -1: + steps.append(multiply_both_sides(current, a)) + solutions += steps[-1]["after"] + + steps.append({}) + steps[-1]["before"] = equation + steps[-1]["after"] = solutions + steps[-1]["step"] = f"Solved The Roots" + steps[-1]["rule"] = "Root of a polynomial" + + return steps + def combine_like_terms(equation): step = {} @@ -186,6 +399,110 @@ def combine_like_terms(equation): step["rule"] = "Combine the like terms" return step +def distribute_step(equation): + steps = [] + + current = equation + left, right = current.split("=") + left = left.replace("-(", "-1*(") + x = symbols('x') + + left_expr = parse_expr(left, transformations=transformations, evaluate=False) + right_expr = parse_expr(right, transformations=transformations, evaluate=False) + + print(f"calling distribute_once with expression: {sstr(left_expr)}") + new_left_expr, distributed = distribute_once(left_expr) + + if distributed != None: + steps.append({}) + steps[-1]["before"] = current + steps[-1]["after"] = f"{sstr(safe_format(new_left_expr))} = {sstr(right_expr)}" + steps[-1]["step"] = f"Distribute out {sstr(distributed)}" + steps[-1]["rule"] = "Distributive Law of Multiplication" + + return steps + +def build_ordered_add(args): + expr = args[0] + for a in args[1:]: + expr = Add(expr, a, evaluate=False) + return expr + +def distribute_once(expr): + expr = flatten_mul(expr) + + # ------------------------------------------------------------ + # STEP 1: ONLY HANDLE DIRECT DISTRIBUTION CASES + # (i.e. Mul where one factor is Add) + # ------------------------------------------------------------ + if expr.is_Mul: + + print(f"expr: {sstr(expr)}") + + add_part = None + other_parts = [] + + # extract Add factor + everything else + for arg in expr.args: + print(f"arg: {sstr(arg)}") + + if arg.is_Add and add_part is None: + add_part = arg + else: + other_parts.append(arg) + + # -------------------------------------------------------- + # DISTRIBUTION RULE + # -------------------------------------------------------- + if add_part is not None: + print(f"expr used: {sstr(expr)}, add used: {sstr(add_part)}") + + distributed_value = Mul(*other_parts) + + distributed_terms = [ + Mul(*other_parts, term) + for term in add_part.args + ] + + new_expr = build_ordered_add(distributed_terms) + + return new_expr, distributed_value + + # ------------------------------------------------------------ + # STEP 2: PRIORITY-BASED RECURSION (IMPORTANT FIX) + # ------------------------------------------------------------ + if expr.args: + print(f"step2 args:{expr.args}") + # PASS 1: ONLY distributable Mul(Add(...)) + for i, arg in enumerate(expr.args): + if arg.is_Mul and arg.has(Add): + + new_arg, distributed = distribute_once(arg) + + if new_arg != arg: + new_args = list(expr.args) + new_args[i] = new_arg + return build_ordered_add(new_args), distributed + + # PASS 2: ONLY recurse into Add or structured nodes + for i, arg in enumerate(expr.args): + + # IMPORTANT FILTER: skip pure Mul like -4*x + if arg.is_Mul and not any(a.is_Add for a in arg.args): + continue + + new_arg, distributed = distribute_once(arg) + + if new_arg != arg: + new_args = list(expr.args) + new_args[i] = new_arg + return build_ordered_add(new_args), distributed + + # ------------------------------------------------------------ + # STEP 3: NO CHANGE + # ------------------------------------------------------------ + return expr, None + def clean(expr): # remove explicit 1 multipliers @@ -194,4 +511,30 @@ def clean(expr): lambda e: Mul(*[arg for arg in e.args if arg != 1]) ) + return expr + +def safe_format(expr): + if expr.is_Mul: + args = [] + for a in expr.args: + a = safe_format(a) + if a == 1: + continue + args.append(a) + return Mul(*args, evaluate=False) + + if expr.is_Add: + return expr.func(*[safe_format(a) for a in expr.args], evaluate=False) + + return expr + +def flatten_mul(expr): + if expr.is_Mul: + args = [] + for arg in expr.args: + if arg.is_Mul: + args.extend(arg.args) + else: + args.append(arg) + return Mul(*args, evaluate=False) return expr \ No newline at end of file diff --git a/main.py b/main.py index b2d253d..6e1a396 100644 --- a/main.py +++ b/main.py @@ -3,9 +3,11 @@ from problem_generator import generate_problem from steps_generator import generate_steps +from sympy import init_printing #define the entry point to the programs def main(): + init_printing(order='none') problem = generate_problem() steps = generate_steps(problem); diff --git a/problem_generator.py b/problem_generator.py index 1e2af0a..50fdc25 100644 --- a/problem_generator.py +++ b/problem_generator.py @@ -110,18 +110,24 @@ def generate_like_terms (): @register_problem_generator("quadratic") def generate_quadratic (): #ax² + bx + c = 0 - r1 = random.choice([i for i in range(-10, 16)]) - r2 = random.choice([i for i in range(-10, 16)]) + r1 = 0 + r2 = 0 + while r1 == 0 and r2 == 0: + r1 = random.choice(range(-10, 13)) + r2 = random.choice(range(-10, 13)) n = random.choice([i for i in range(-5, 6) if i != 0]) s = random.choice([i for i in range(-5, 6) if i != 0]) x = symbols('x') - expr = n *(x - r1) * (x - r2) + expr = n *(s * x - r1) * (x - r2) + print(f"n:{n}, s:{s}") expr = expand(expr) - if r1 == r2: - solution = r1 + root1 = Rational(r1, s) + root2 = Integer(r2) + if root1 == root2: + solution = sstr(root1) else: - solution = [r1, r2] + solution = [sstr(root1), sstr(root2)] return { "type": "quadratic", @@ -210,8 +216,14 @@ def generate_binomial (): e = a * (ans + b) + c * (ans + d) x = symbols('x') - left_expr = Mul(a, x + b, evaluate=False) - right_expr = Mul(c, x + d, evaluate=False) + if a != 1: + left_expr = Mul(a, x + b, evaluate=False) + else: + left_expr = x+b + if c != 1: + right_expr = Mul(c, x + d, evaluate=False) + else: + right_expr = x+d expr = Add(left_expr, right_expr, evaluate=False) return { @@ -251,6 +263,6 @@ def generate_problem(): problem_type = random.choices(types, weights=weights)[0] template = TEMPLATES[problem_type] - return generate_like_terms() + return generate_binomial() #return template() \ No newline at end of file diff --git a/steps_generator.py b/steps_generator.py index 8472c2a..2ac1647 100644 --- a/steps_generator.py +++ b/steps_generator.py @@ -37,9 +37,11 @@ def generate_linear_steps(problem): elif b.is_positive: steps.append(algebraic_steps.subtract_both_sides(current, b)) current = steps[-1]["after"] - ## Second Step - if a != 1: + ## Second Step + if a != 1 and a != -1: steps.append(algebraic_steps.divide_both_sides(current, a)) + elif a == -1: + steps.append(algebraic_steps.multiply_both_sides(current, a)) return steps @@ -173,6 +175,7 @@ def generate_like_terms_steps (problem): #ax + bx + c = d steps = [] + x = symbols('x') current = problem["problem"] ## First Step @@ -183,6 +186,23 @@ def generate_like_terms_steps (problem): left, right = current.split("=") left_expr = parse_expr(left) b = left_expr.subs(x, 0) + if b.is_negative: + steps.append(algebraic_steps.add_both_sides(current, -b)) + elif b.is_positive: + steps.append(algebraic_steps.subtract_both_sides(current, b)) + current = steps[-1]["after"] + left, right = current.split("=") + left_expr = parse_expr(left, transformations=transformations) + right_expr = parse_expr(right) + steps[-1]["after"] = f"{sstr(left_expr)} = {sstr(right_expr)}" + current = steps[-1]["after"] + + ## Third Step + div = left_expr.coeff(x) + if div != 1 and div != -1: + steps.append(algebraic_steps.divide_both_sides(current, div)) + elif div == -1: + steps.append(algebraic_steps.multiply_both_sides(current, div)) return steps @@ -191,6 +211,38 @@ def generate_quadratic_steps (problem): #ax² + bx + c = 0 steps = [] + x = symbols('x') + current = problem["problem"] + left, right = current.split("=") + left_expr = parse_expr(left, transformations=transformations) + right_expr = parse_expr(right) + a = left_expr.coeff(x**2) + b = left_expr.coeff(x) + c = left_expr.subs(x, 0) + div = gcd(a, b, c) + if a.is_negative: + div = -div + + ## First Step + if div != 1 and c.is_zero == False: + steps.append(algebraic_steps.factor_out(current, div)) + current = steps[-1]["after"] + elif c.is_zero: + div = gcd(a, b) + steps.append(algebraic_steps.factor_out(current, div*x)) + current = steps[-1]["after"] + + if c.is_zero == False: + ## Second Steps + left, right = current.split("=") + left_expr = parse_expr(left, transformations=transformations) + inner = left_expr / div + steps.extend(algebraic_steps.trinomial_by_grouping(current,inner)) + current = steps[-1]["after"] + + ##Solve the Roots + steps.extend(algebraic_steps.solve_roots(current)) + return steps @register_steps_generator("difference_squares") @@ -198,12 +250,37 @@ def generate_difference_squares_steps (problem): #x² - a² = 0 steps = [] + x = symbols('x') + current = problem["problem"] + left, right = current.split("=") + left_expr = parse_expr(left, transformations=transformations) + b = left_expr.subs(x, 0) + + ## Step 1 + if b.is_negative: + steps.append(algebraic_steps.add_both_sides(current, -b)) + elif b.is_positive: + steps.append(algebraic_steps.subtract_both_sides(current, b)) + current = steps[-1]["after"] + left, right = current.split("=") + left_expr = parse_expr(left, transformations=transformations) + right_expr = parse_expr(right) + steps[-1]["after"] = f"{sstr(left_expr)} = {sstr(right_expr)}" + current = steps[-1]["after"] + + ## Step 2 + steps.append(algebraic_steps.square_root_both_sides(current)) + + return steps @register_steps_generator("zero_product") def generate_zero_product_steps (problem): #(x + a)(x + b) = 0 steps = [] + current = problem["problem"] + + steps.extend(algebraic_steps.solve_roots(current)) return steps @@ -211,6 +288,28 @@ def generate_zero_product_steps (problem): def generate_radical_steps (problem): #√(x + a) = b steps = [] + x = symbols('x') + current = problem["problem"] + + + ## First Step + steps.append(algebraic_steps.square_both_sides(current)) + + ## Second Step + current = steps[-1]["after"] + left, right = current.split("=") + left_expr = parse_expr(left, transformations=transformations) + b = left_expr.subs(x, 0) + if b.is_zero != False: + if b.is_negative: + steps.append(algebraic_steps.add_both_sides(current, -b)) + elif b.is_positive: + steps.append(algebraic_steps.subtract_both_sides(current, b)) + current = steps[-1]["after"] + left, right = current.split("=") + left_expr = parse_expr(left, transformations=transformations) + right_expr = parse_expr(right) + steps[-1]["after"] = f"{sstr(left_expr)} = {sstr(right_expr)}" return steps @@ -218,6 +317,31 @@ def generate_radical_steps (problem): def generate_fraction_steps (problem): #(x/a) + b = c steps = [] + x = symbols('x') + current = problem["problem"] + + ## First Step + left, right = current.split("=") + left_expr = parse_expr(left, transformations=transformations) + b = left_expr.subs(x, 0) + if b.is_zero == False: + if b.is_negative: + steps.append(algebraic_steps.add_both_sides(current, -b)) + elif b.is_positive: + steps.append(algebraic_steps.subtract_both_sides(current, b)) + current = steps[-1]["after"] + left, right = current.split("=") + left_expr = parse_expr(left, transformations=transformations) + right_expr = parse_expr(right) + steps[-1]["after"] = f"{sstr(left_expr)} = {sstr(right_expr)}" + current = steps[-1]["after"] + + ## Second step + num, den = fraction(left_expr) + if left_expr.subs(x,1).is_negative: + steps.append(algebraic_steps.multiply_both_sides(current, -den)) + else: + steps.append(algebraic_steps.multiply_both_sides(current, den)) return steps @@ -225,6 +349,17 @@ def generate_fraction_steps (problem): def generate_binomial_steps (problem): #a(x + b) + c(x + d) = e steps = [] + current = problem["problem"] + + last_len = -1 + while last_len != len(steps): + last_len = len(steps) + steps.extend(algebraic_steps.distribute_step(current)) + if len(steps): + current = steps[-1]["after"] + + steps.append(algebraic_steps.combine_like_terms(current)) + current = steps[-1]["after"] return steps