'''
Created on 12-Jul-2021

@author: bthakur
'''
import random
import inspect
import traceback
import re
from sympy import (Eq, symbols, Symbol, sqrt, latex, sympify,
                   Rational, solve, log, nsimplify,
                   UnevaluatedExpr as ueval, simplify, expand
                    )

from ...util import MathUtil


from xv.km import CustomLogarithm as Log
from xv.km.util import latex_fraction, latex_exponent
from xv.km.util import lcm 
from xv.util import toIntOrFloat

class SingleVariableHelper:
    def __init__(self, **kwargs):
        self.verbose = kwargs.get('verbose', False)
        self.x = symbols('x')
        self.eq = None
        self.implies = symbols(u'\U000021D2')
        pass
    
    def _check_answer(self, result):
        x = self.x
        
        eq = result.get('eq')
        answer = result.get('answer')
        soln_x = solve(eq, x)[0]
        if abs(soln_x - answer) > 10 ** -4:
            print(f"""
            Error:
            eq = {eq}
            soln_x = {soln_x}
            answer = {answer}
            """)

    def _simplify_fraction(self, p, q, frc):
        if (p != frc.p or q != frc.q):
            return True
        return False
        pass

    def get_problem_0(self):
        x = self.x        
        a, b = random.sample(list(range(1, 10)), 2)
        b = b * random.choice([1, -1])
        self.a, self.b = a, b
        
        type_index = random.randint(0, 7)
        result = self.get_problem_0_TEMPLATE(type_index, x, a, b)
        
        self._check_answer(result)
        result = self._flip_sides(result, x)
        return result
        pass

    def get_problem_1(self):
        x = self.x        
        a, b = random.sample(list(range(2, 10)), 2)
        b = b * random.choice([1, -1])
        self.a, self.b = a, b
        
        type_index = random.randint(0, 7)
        result = self.get_problem_1_TEMPLATE(type_index, x, a, b)
        
        self._check_answer(result)
        result = self._flip_sides(result, x)
        return result
        pass

    def get_problem_2(self):
        x = self.x        
        a, b, c = random.sample(list(range(2, 10)), 3)
        b = b * random.choice([1, -1])
        self.a, self.b = a, b

        type_index = random.randint(0, 7)
        result = self.get_problem_2_TEMPLATE(type_index, x, a, b, c)
        
        self._check_answer(result)
        result = self._flip_sides(result, x)
        return result
        pass


    def get_problem_3(self):
        x = self.x        
        a, b, c = random.sample(list(range(2, 10)), 3)
        b = b * random.choice([1, -1])
        self.a, self.b = a, b

        type_index = random.randint(0, 7)
        result = self.get_problem_3_TEMPLATE(type_index, x, a, b, c)
        result = self._flip_sides(result, x)
        
        self._check_answer(result)
        return result
        pass
    
    def get_sign(self, v, omit_plus = False):
        if isinstance(v, str):
            v = sympify(f"{v}")
        if v < 0:
            return "-"
        if omit_plus:
            return ""
        return "+"
        pass

    def get_problem_4(self):
        x = self.x        
        a, b, c, d = random.sample(list(range(2, 10)), 4)
        a = a * random.choice([1, -1])
        b = b * random.choice([1, -1])
        c = c * random.choice([1, -1])
        d = d * random.choice([1, -1])
        self.a, self.b, self.c, self.d = a, b, c, d

        result = self.get_problem_4_TEMPLATE(x, a, b, c, d)
        result = self._inverse_sides(result, x)

        self._check_answer(result)
        return result
        pass


    def get_problem_5(self):
        x = self.x        
        a, b, c, d, e, f = random.sample(list(range(2, 10)), 6)
        a = a * random.choice([1, -1])
        b = b * random.choice([1, -1])
        c = c * random.choice([1, -1])
        d = d * random.choice([1, -1])
        e = e * random.choice([1, -1])
        f = f * random.choice([1, -1])
        self.a, self.b, self.c, self.d, self.e, self.f = a, b, c, d, e, f

        result = self.get_problem_5_TEMPLATE(x, a, b, c, d, e, f)

        self._check_answer(result)
        return result
        pass

    def get_problem_6(self):
        x = self.x        
        a, b, c, d, e, f = random.sample(list(range(2, 10)), 6)
        a = a * random.choice([1, -1])
        b = b * random.choice([1, -1])
        c = c * random.choice([1, -1])
        d = d * random.choice([1, -1])
        e = e * random.choice([1, -1])
        f = f * random.choice([1, -1])
        self.a, self.b, self.c, self.d, self.e, self.f = a, b, c, d, e, f

        result = self.get_problem_6_TEMPLATE(x, a, b, c, d, e, f)
        self._check_answer(result)
        return result
        pass

    def get_problem_7(self):
        x = self.x        
        a, b, c, d, e, f, g, h = random.sample(list(range(2, 20)), 8)
        a = a * random.choice([1, -1])
        b = b * random.choice([1, -1])
        c = c * random.choice([1, -1])
        d = d * random.choice([1, -1])
        e = e * random.choice([1, -1])
        f = f * random.choice([1, -1])
        g = g * random.choice([1, -1])
        h = h * random.choice([1, -1])
        (self.a, self.b, self.c, self.d, 
         self.e, self.f, self.g, self.h) = a, b, c, d, e, f, g, h

        result = self.get_problem_7_TEMPLATE(x, a, b, c, d, e, f, g, h)
        self._check_answer(result)
        return result
        pass

    def get_problem_8(self):
        x = self.x        
        a, b, c, d, e, f, g, h = random.sample(list(range(2, 20)), 8)
        a = a * random.choice([1, -1])
        b = b * random.choice([1, -1])
        c = c * random.choice([1, -1])
        d = d * random.choice([1, -1])
        e = e * random.choice([1, -1])
        f = f * random.choice([1, -1])
        g = g * random.choice([1, -1])
        h = h * random.choice([1, -1])
        (self.a, self.b, self.c, self.d, 
         self.e, self.f, self.g, self.h) = a, b, c, d, e, f, g, h

        result = self.get_problem_8_TEMPLATE(x, a, b, c, d, e, f, g, h)
        self._check_answer(result)
        return result
        pass

    def get_problem_9(self):
        x = self.x        
        a, b, c, d, e, f, g, h = random.sample(list(range(2, 20)), 8)
        a = a * random.choice([1, -1])
        b = b * random.choice([1, -1])
        c = c * random.choice([1, -1])
        d = d * random.choice([1, -1])
        e = e * random.choice([1, -1])
        f = f * random.choice([1, -1])
        g = g * random.choice([1, -1])
        h = h * random.choice([1, -1])
        (self.a, self.b, self.c, self.d, 
         self.e, self.f, self.g, self.h) = a, b, c, d, e, f, g, h

        result = self.get_problem_9_TEMPLATE(x, a, b, c, d, e, f, g, h)
        self._check_answer(result)
        return result
        pass

    def get_problem_0_TEMPLATE(self, type_index, x, a, b):
        prob_method = getattr(self, f"get_problem_0_type_{type_index}")
        
        result = prob_method(x, a, b)
        return result
        pass

    def get_problem_1_TEMPLATE(self, type_index, x, a, b):
        prob_method = getattr(self, f"get_problem_1_type_{type_index}")
        
        result = prob_method(x, a, b)
        return result
        pass

    def get_problem_2_TEMPLATE(self, type_index, x, a, b, c):
        result = self.get_problem_0_TEMPLATE(type_index, c * x, a, b)
        c_inv_latex = latex_fraction(1, c)
    
        answer = result.get('answer')
        answer_c_latex = latex_fraction(answer, c)
        
        soln_s = result.get('soln_s')
        
        soln_s = f"""{soln_s}
        $\\displaystyle \\implies {c} {latex(x)} * {c_inv_latex} = {latex(answer)} * {c_inv_latex}$ \t\t divide both sides by $\\displaystyle {c}$
        
        $\\displaystyle \\implies {latex(x)} = {answer_c_latex}$
        """

        answer = nsimplify(answer / c)
        
        if self._simplify_fraction(b, a, answer):
            soln_s = f"""{soln_s}
            
            $\\displaystyle \\implies {latex(x)} = {latex(answer)}$
            """
        
        result['soln_s'] = soln_s
        result['answer'] = answer
        return result
        pass

    def get_problem_3_TEMPLATE(self, type_index, x, a, b, c):
        varX = list(x.free_symbols)[0]

        b_inv = Rational(1, b)
        result = self.get_problem_0_TEMPLATE(type_index, c * x, a, b_inv)
        c_inv_latex = latex(Rational(1, c)) #latex_fraction(1, c)

        eq = result.get('eq')
        eq = Eq( 1/ eq.lhs, 1/ eq.rhs)
        result['eq'] = eq

        eq_lhs_latex = latex_fraction(1, result.get('eq_lhs_latex'))
        eq_rhs_latex = f"{latex(b)}"
        
        answer = result.get('answer')
        answer_c_latex = latex(nsimplify(answer / c))

        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {eq_lhs_latex} = {eq_rhs_latex}$
        """
        result['problem_s'] = problem_s

        soln_s = result.get('soln_s')
        result['soln_s'] = f"""
        $\\displaystyle {eq_lhs_latex} = {eq_rhs_latex}$
        
        Inverse both sides and we get:
        {soln_s}
        $\\displaystyle \\implies {c} {latex(x)} * {c_inv_latex} = {latex(answer)} * {c_inv_latex}$ \t\t divide both sides by $\\displaystyle {c}$
        
        $\\displaystyle \\implies {latex(x)} = {answer_c_latex}$
        
        """
        answer = nsimplify(answer / c)
        result['eq_lhs_latex'] = eq_lhs_latex
        result['eq_rhs_latex'] = eq_rhs_latex 
        result['answer'] = answer
        return result
        pass

    def get_problem_4_TEMPLATE(self, x, a, b, c, d):
        varX = list(x.free_symbols)[0]

        eq = Eq(a + c * x, b + d * x)

        if random.choice([True, False]):
            lhs_terms = [f"{latex(a)}", f"{self.get_sign(c)} {latex(abs(c))}{x}"]
        else:
            lhs_terms = [f"{latex(c)}{x}", f"{self.get_sign(a)}{latex(abs(a))}"]
        
        if random.choice([True, False]):
            rhs_terms = [f"{latex(b)}", f"{self.get_sign(d)} {latex(abs(d))}{x}"]
        else:
            rhs_terms = [f"{latex(d)}{x}", f"{self.get_sign(b)}{latex(abs(b))}"]
        
        eq_lhs_latex = f"{lhs_terms[0]} {lhs_terms[1]}"
        eq_rhs_latex = f"{rhs_terms[0]} {rhs_terms[1]}"
        
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"

        op1 = f"To remove $\\displaystyle {latex(d * x)}$ from right side, "

        if d < 0:
            op1 = f"{op1}, add $\\displaystyle {latex(abs(d) * x)}$ to both sides."
        else:
            op1 = f"{op1}, deduct $\\displaystyle {latex(abs(d) * x)}$ from both sides."
        
        op2 = f"To remove $\\displaystyle {latex(a)}$ from left side, "
        if a < 0:
            op2 = f"{op2}, add $\\displaystyle {latex(abs(a))}$ to both sides."
        else:
            op2 = f"{op2}, deduct $\\displaystyle {latex(abs(a))}$ from both sides."
        
        answer = nsimplify((b-a)/(c-d))
        answer_fraction = latex_fraction(f"{latex(b-a)}", f"{latex(c-d)}")
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        {op1}
        Also {op2}

        $\\displaystyle \\implies {latex( (c-d)*x )} = {latex( (b-a) )}$
        """
        if c-d != 1:
            soln_s =  f"""{soln_s}
            $\\displaystyle \\implies {latex(x)} = {answer_fraction}$ \t\t divide both sides with $\\displaystyle {c-d}$
            """
        
        if self._simplify_fraction(b-a, c-d, answer):
            soln_s =  f"""{soln_s}
            $\\displaystyle \\implies {latex(x)} = {latex(answer)}$
            """
        
        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result
        pass

    def get_problem_5_TEMPLATE(self, x, a, b, c, d, e, f):
        varX = list(x.free_symbols)[0]

        #eq = Eq(a + c * x, b + d * x)
        ef_lcm = lcm(e, f) 
        a1 = a * (ef_lcm // e)
        c1 = c * (ef_lcm // e)
        b1 = b * (ef_lcm // f)
        d1 = d * (ef_lcm // f)
        
        result0 = self.get_problem_4_TEMPLATE(x, a, b, c, d)
        result = self.get_problem_4_TEMPLATE(x, a1, b1, c1, d1)

        eq0 = result0.get('eq')
        eq = Eq( eq0.lhs / e, eq0.rhs / f)
        result['eq'] = eq

        eq_lhs_latex0 = result0.get('eq_lhs_latex')
        eq_rhs_latex0 = result0.get('eq_rhs_latex')

        eq_lhs_latex = latex_fraction(eq_lhs_latex0, e)
        eq_rhs_latex = latex_fraction(eq_rhs_latex0, f)
        result['eq_lhs_latex'] = eq_lhs_latex
        result['eq_rhs_latex'] = eq_rhs_latex
       
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"

        problem_expr_latex2 = f"{eq_lhs_latex} * {ef_lcm} = {eq_rhs_latex} * {ef_lcm}"
        
        problem_expr_latex3 = f"({eq_lhs_latex0} ) * {latex(ef_lcm // e)} = ({eq_rhs_latex0}) * {latex(ef_lcm // f)}"

        result['problem_s'] =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        
        $\\displaystyle {problem_expr_latex}$
        
        """
        
        soln_s = result.get('soln_s').strip()
        soln_s = soln_s[soln_s.index('$') + 1:]

        result['soln_s'] =  f"""
        $\\displaystyle {problem_expr_latex}$ 

        We have to remove both sides denominators, $\\displaystyle {e}$ and $\\displaystyle {f}$.
        To remove them, multiply both sides with their lcm, ignoring signs.
        The lcm of $\\displaystyle {abs(e)}$ and $\\displaystyle {abs(f)}$ is $\\displaystyle {ef_lcm}$. Hence,
        
        $\\displaystyle \\implies {problem_expr_latex2}$
        
        $\\displaystyle \\implies {problem_expr_latex3}$
        
        $\\displaystyle \\implies {soln_s}
        """
        return result

    def get_problem_6_TEMPLATE(self, x, a, b, c, d, e, f):
        varX = list(x.free_symbols)[0]

        result0 = self.get_problem_4_TEMPLATE(x, a, b, c, d)
        result = self.get_problem_5_TEMPLATE(x, a, b, c, d, e, f)
        
        eq0 = result0.get('eq')
        result['eq'] = Eq(e / eq0.lhs, f/ eq0.rhs)
        
        eq_lhs_latex = latex_fraction(e, result0.get('eq_lhs_latex'))
        eq_rhs_latex = latex_fraction(f, result0.get('eq_rhs_latex'))
        result['eq_lhs_latex'] = eq_lhs_latex
        result['eq_rhs_latex'] = eq_rhs_latex
       
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        result['problem_s'] =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s = result.get('soln_s')
        result['soln_s'] =  f"""
        $\\displaystyle {problem_expr_latex}$ 
        
        Inverse sides to bring x to numerator:
        {soln_s}
        """
        
        return result
        pass

    def get_problem_7_TEMPLATE(self, x, a, b, c, d, e, f, g, h):
        varX = list(x.free_symbols)[0]

        result0 = self.get_problem_5_TEMPLATE(x, a, b, c, d, e, f)
        result = result0.copy()

        eq0 = result0.get('eq')
        
        eq = Eq(sympify(f"({eq0.lhs}) + {g}", evaluate = False), 
                sympify(f"({eq0.rhs}) + {h}", evaluate = False))

        h_minus_g = h - g        
        ef_lcm = lcm(e, f)
        a1 = a * (ef_lcm // e)
        c1 = c * (ef_lcm // e)
        b1 = b * (ef_lcm // f)
        d1 = d * (ef_lcm // f)
        h1 = h_minus_g * ef_lcm

        result1 = self.get_problem_4_TEMPLATE(x, a1, b1, c1, d1)
        result2 = self.get_problem_4_TEMPLATE(x, a, b, c, d)
        b3 = b1 + h_minus_g * ef_lcm
        result3 = self.get_problem_4_TEMPLATE(x, a1, b3, c1, d1)
                
        eq_lhs_latex0 = result0.get('eq_lhs_latex')
        eq_rhs_latex0 = result0.get('eq_rhs_latex')
        
        eq_lhs_latex1 = result1.get('eq_lhs_latex')
        eq_rhs_latex1 = result1.get('eq_rhs_latex')
        
        eq_lhs_latex2 = result2.get('eq_lhs_latex')
        eq_rhs_latex2 = result2.get('eq_rhs_latex')
        
        eq_lhs_latex = f"{eq_lhs_latex0} {self.get_sign(g)} {latex(abs(g))}"
        eq_rhs_latex = f"{eq_rhs_latex0} {self.get_sign(h)} {latex(abs(h))}"
       
       
        step1_op1 = f"To remove $\\displaystyle {latex(g)}$ from left side, "
        if g < 0:
            step1_op1 = f"{step1_op1}, add $\\displaystyle {latex(abs(g))}$ to both sides."
        else:
            step1_op1 = f"{step1_op1}, deduct $\\displaystyle {latex(abs(g))}$ from both sides."

        
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s = result3.get('soln_s').strip()
        soln_s = soln_s[soln_s.index('$') + 1:]
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$ 
        
        {step1_op1}

        $\\displaystyle \\implies \\left({eq_lhs_latex}\\right)
         {self.get_sign(-g)} {latex(abs(-g))}
        = \\left({eq_rhs_latex}\\right)
         {self.get_sign(-g)} {latex(abs(-g))}
        $
        
        $\\displaystyle \\implies {eq_lhs_latex0}
        = {eq_rhs_latex0}
         {self.get_sign(h_minus_g)} {latex(abs(h_minus_g))}
        $

        We have to remove both sides denominators, $\\displaystyle {e}$ and $\\displaystyle {f}$.
        To remove them, multiply both sides with their lcm, ignoring signs.
        The lcm of $\\displaystyle {abs(e)}$ and $\\displaystyle {abs(f)}$ is $\\displaystyle {ef_lcm}$. Hence,
        
        $\\displaystyle \\implies \\left( {eq_lhs_latex0} \\right) * {latex(ef_lcm)}
        = \\left( {eq_rhs_latex0}
         {self.get_sign(h_minus_g)} {latex(abs(h_minus_g))} \\right) * {latex(ef_lcm)}
        $
        
        $\\displaystyle \\implies \\left( {eq_lhs_latex0} \\right) * {latex(ef_lcm)}
        = \\left( {eq_rhs_latex0}\\right) * {latex(ef_lcm)}
         {self.get_sign(h_minus_g)} {latex(abs(h_minus_g))} * {latex(ef_lcm)}
        $
        
        $\\displaystyle \\implies \\left( {eq_lhs_latex2} \\right) * {latex(ef_lcm//e)}
        = \\left( {eq_rhs_latex2}\\right) * {latex(ef_lcm//f)}
         {self.get_sign(h_minus_g)} {latex(abs(h_minus_g))} * {latex(ef_lcm)}
        $
        
        $\\displaystyle \\implies {eq_lhs_latex1}
        = {eq_rhs_latex1} {self.get_sign(h1)} {latex(abs(h1))}
        $
        
        $\\displaystyle \\implies {soln_s}
        """
        
        result['eq'] = eq
        result['eq_lhs_latex'] = eq_lhs_latex
        result['eq_rhs_latex'] = eq_rhs_latex

        result['problem_s'] = problem_s
        result['soln_s'] = soln_s
        result['answer'] = result3.get('answer')
        
        return result
        pass

    def get_problem_8_TEMPLATE(self, x, a, b, c, d, e, f, g, h):
        varX = list(x.free_symbols)[0]

        result0 = self.get_problem_5_TEMPLATE(x, a, b, c, d, e, f)
        result = result0.copy()

        eq0 = result0.get('eq')
        
        eq = Eq(sympify(f"({eq0.lhs}) + ({eq0.rhs}) ", evaluate = False), 
                sympify(f" {g + h}", evaluate = False))

        h_minus_g = h - g        
        ef_lcm = lcm(e, f)
        a1 = a * (ef_lcm // e)
        c1 = c * (ef_lcm // e)
        b1 = b * (ef_lcm // f)
        d1 = d * (ef_lcm // f)
        h1 = h_minus_g * ef_lcm

        result1 = self.get_problem_4_TEMPLATE(x, a1, b1, c1, d1)
        result2 = self.get_problem_4_TEMPLATE(x, a, b, c, d)

        #eq = Eq(a + c * x, b + d * x
        a3 = a * (ef_lcm//e) 
        c3 = c * (ef_lcm//e) 
        b3 = b * (ef_lcm//f) 
        d3 = d * (ef_lcm//f) 
        gh3 = (g + h) * ef_lcm
        
        type_index = 4
        result3 = self.get_problem_2_TEMPLATE(type_index, x, a3 + b3, gh3, c3 + d3)
                
        eq_lhs_latex0 = result0.get('eq_lhs_latex')
        eq_rhs_latex0 = result0.get('eq_rhs_latex')

        eq_lhs_latex = f"{eq_lhs_latex0} + {eq_rhs_latex0}"
        eq_rhs_latex = f"{latex(g + h)}"

        
        eq_lhs_latex1 = result1.get('eq_lhs_latex')
        eq_rhs_latex1 = result1.get('eq_rhs_latex')
        
        eq_lhs_latex2 = result2.get('eq_lhs_latex')
        eq_rhs_latex2 = result2.get('eq_rhs_latex')
        
        
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s = result3.get('soln_s').strip()
        soln_s = soln_s[soln_s.index('$') + 1:]
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$ 
        
        We have to remove both sides denominators, $\\displaystyle {e}$ and $\\displaystyle {f}$.
        To remove them, multiply both sides with their lcm, ignoring signs.
        The lcm of $\\displaystyle {abs(e)}$ and $\\displaystyle {abs(f)}$ is $\\displaystyle {ef_lcm}$. Hence,
        
        $\\displaystyle \\implies \\left( {eq_lhs_latex} \\right) * {latex(ef_lcm)}
        = {eq_rhs_latex} * {latex(ef_lcm)}
        $
        
        $\\displaystyle \\implies 
          \\left( {eq_lhs_latex0} \\right) * {latex(ef_lcm)}
        + \\left( {eq_rhs_latex0} \\right) * {latex(ef_lcm)}
        = {latex((g + h) * ef_lcm)}
        $
        
        $\\displaystyle \\implies 
          \\left( {eq_lhs_latex2} \\right) * {latex(ef_lcm//e)}
        + \\left( {eq_rhs_latex2} \\right) * {latex(ef_lcm//f)}
        = {latex((g + h) * ef_lcm)}
        $
        
        $\\displaystyle \\implies 
          \\left( {eq_lhs_latex1} \\right)  
        + \\left( {eq_rhs_latex1} \\right) 
        = {latex((g + h) * ef_lcm)}
        $

        $\\displaystyle \\implies {soln_s}
        """
        
        result['eq'] = eq
        result['eq_lhs_latex'] = eq_lhs_latex
        result['eq_rhs_latex'] = eq_rhs_latex

        result['problem_s'] = problem_s
        result['soln_s'] = soln_s
        result['answer'] = result3.get('answer')
        
        return result
        pass

    def get_problem_9_TEMPLATE(self, x, a, b, c, d, e, f, g, h):
        varX = list(x.free_symbols)[0]

        result0 = self.get_problem_5_TEMPLATE(x, a, b, c, d, e, f)
        result = result0.copy()

        eq0 = result0.get('eq')
        
        eq = Eq(sympify(f"({eq0.lhs}) - ({eq0.rhs}) ", evaluate = False), 
                sympify(f" {g - h}", evaluate = False))

        h_minus_g = h - g        
        ef_lcm = lcm(e, f)
        a1 = a * (ef_lcm // e)
        c1 = c * (ef_lcm // e)
        b1 = b * (ef_lcm // f)
        d1 = d * (ef_lcm // f)
        h1 = h_minus_g * ef_lcm

        result1 = self.get_problem_4_TEMPLATE(x, a1, b1, c1, d1)
        result2 = self.get_problem_4_TEMPLATE(x, a, b, c, d)

        #eq = Eq(a + c * x, b + d * x
        a3 = a * (ef_lcm//e) 
        c3 = c * (ef_lcm//e) 
        b3 = b * (ef_lcm//f) 
        d3 = d * (ef_lcm//f) 
        gh3 = (g - h) * ef_lcm
        
        type_index = 4
        result3 = self.get_problem_2_TEMPLATE(type_index, x, a3 - b3, gh3, c3 - d3)
                
        eq_lhs_latex0 = result0.get('eq_lhs_latex')
        eq_rhs_latex0 = result0.get('eq_rhs_latex')

        eq_lhs_latex = f"{eq_lhs_latex0} - {eq_rhs_latex0}"
        eq_rhs_latex = f"{latex(g - h)}"

        
        eq_lhs_latex1 = result1.get('eq_lhs_latex')
        eq_rhs_latex1 = result1.get('eq_rhs_latex')
        
        eq_lhs_latex2 = result2.get('eq_lhs_latex')
        eq_rhs_latex2 = result2.get('eq_rhs_latex')
        
        
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s = result3.get('soln_s').strip()
        soln_s = soln_s[soln_s.index('$') + 1:]
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$ 
        
        We have to remove both sides denominators, ${e}$\\displaystyle  and ${f}$\\displaystyle .
        To remove them, multiply both sides with their lcm, ignoring signs.
        The lcm of $\\displaystyle {abs(e)}$ and $\\displaystyle {abs(f)}$ is $\\displaystyle {ef_lcm}$. Hence,
        
        $\\displaystyle \\implies \\left( {eq_lhs_latex} \\right) * {latex(ef_lcm)}
        = {eq_rhs_latex} * {latex(ef_lcm)}
        $
        
        $\\displaystyle \\implies 
          \\left( {eq_lhs_latex0} \\right) * {latex(ef_lcm)}
        - \\left( {eq_rhs_latex0} \\right) * {latex(ef_lcm)}
        = {latex((g - h) * ef_lcm)}
        $
        
        $\\displaystyle \\implies 
          \\left( {eq_lhs_latex2} \\right) * {latex(ef_lcm//e)}
        - \\left( {eq_rhs_latex2} \\right) * {latex(ef_lcm//f)}
        = {latex((g - h) * ef_lcm)}
        $
        
        $\\displaystyle \\implies 
          \\left( {eq_lhs_latex1} \\right)  
        - \\left( {eq_rhs_latex1} \\right) 
        = {latex((g - h) * ef_lcm)}
        $

        $\\displaystyle \\implies {soln_s}
        """
        
        result['eq'] = eq
        result['eq_lhs_latex'] = eq_lhs_latex
        result['eq_rhs_latex'] = eq_rhs_latex

        result['problem_s'] = problem_s
        result['soln_s'] = soln_s
        result['answer'] = result3.get('answer')
        
        return result
        pass
    
    

    def get_problem_0_type_0(self, x, a, b):
        varX = list(x.free_symbols)[0]

        eq = Eq(a + x, b)
        
        eq_lhs_latex = f"{latex(a)} + {latex(x)}"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies {latex(a)} + {latex(x)} - {latex(a)} = {latex(b)} - {latex(a)}$ \t\t deduct $\\displaystyle {latex(a)}$ from both sides
        
        $\\displaystyle \\implies {latex(x)} = {latex(b - a)}$
        """
        answer = b - a
        
        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def get_problem_0_type_1(self, x, a, b):
        varX = list(x.free_symbols)[0]

        eq = Eq(- a + x, b)
        eq_lhs_latex = f"- {latex(a)} + {latex(x)}"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies - {latex(a)} + {latex(x)} + {latex(a)} = {latex(b)} + {latex(a)}$ \t\t add $\\displaystyle {latex(a)}$ to both sides
        
        $\\displaystyle \\implies {latex(x)} = {latex(b + a)}$
        """
        answer = b + a
        
        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def get_problem_0_type_2(self, x, a, b):
        varX = list(x.free_symbols)[0]

        eq = Eq(a - x, b)
        eq_lhs_latex = f"{latex(a)} - {latex(x)}"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies {latex(a)} - {latex(x)} - {latex(a)} = {latex(b)} - {latex(a)}$ \t\t deduct ${latex(a)}$ from both sides
        
        $\\displaystyle \\implies - {latex(x)} = {latex(b - a)}$
        
        $\\displaystyle \\implies {latex(x)} = {latex(- b + a)}$ \t\t multiply $-1$ to both sides
        """
        answer = - b + a
        
        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def get_problem_0_type_3(self, x, a, b):
        varX = list(x.free_symbols)[0]

        eq = Eq(- a - x, b)
        eq_lhs_latex = f"- {latex(a)} - {latex(x)}"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies - {latex(a)} - {latex(x)} + {latex(a)} = {latex(b)} + {latex(a)}$ \t\t add ${latex(a)}$ to both sides
        
        $\\displaystyle \\implies - {latex(x)} = {latex(b + a)}$
        
        $\\displaystyle \\implies {latex(x)} = {latex(- b - a)}$ \t\t multiply $-1$ to both sides
        """
        answer = - b - a
        
        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def get_problem_0_type_4(self, x, a, b):
        varX = list(x.free_symbols)[0]

        eq = Eq(x + a, b)
        
        latex_a = f"{self.get_sign(a)} {latex(abs(a))}"
        
        eq_lhs_latex = f"{latex(x)} {latex_a}"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        """
        if a > 0:
            soln_s =  f"""{soln_s}
            $\\displaystyle \\implies {latex(x)} {latex_a} - {latex(a)} = {latex(b)} - {latex(a)}$ \t\t deduct ${latex(a)}$ from both sides
            """
        else:
            soln_s =  f"""{soln_s}
            $\\displaystyle \\implies {latex(x)} {latex_a} + {latex(-a)} = {latex(b)} + {latex(-a)}$ \t\t add ${latex(-a)}$ to both sides
            """
        
        soln_s =  f"""{soln_s}
        $\\displaystyle \\implies {latex(x)} = {latex(b - a)}$
        """
        answer = b - a
        
        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def get_problem_0_type_5(self, x, a, b):
        varX = list(x.free_symbols)[0]

        eq = Eq(x - a, b)
        
        eq_lhs_latex = f"{latex(x)} - {latex(a)}"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies {latex(x)} - {latex(a)} + {latex(a)} = {latex(b)} + {latex(a)}$ \t\t add ${latex(a)}$ to both sides
        
        $\\displaystyle \\implies {latex(x)} = {latex(b + a)}$
        """
        answer = b + a
        
        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def get_problem_0_type_6(self, x, a, b):
        varX = list(x.free_symbols)[0]

        eq = Eq(- x + a, b)
        eq_lhs_latex = f"- {latex(x)} + {latex(a)}"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies - {latex(x)} + {latex(a)} - {latex(a)} = {latex(b)} - {latex(a)}$ \t\t deduct ${latex(a)}$ from both sides
        
        $\\displaystyle \\implies - {latex(x)} = {latex(b - a)}$
        
        $\\displaystyle \\implies {latex(x)} = {latex(- b + a)}$ \t\t multiply $-1$ to both sides
        """
        answer = - b + a
        
        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def get_problem_0_type_7(self, x, a, b):
        varX = list(x.free_symbols)[0]

        eq = Eq(- x - a, b)
        eq_lhs_latex = f"- {latex(x)} - {latex(a)}"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies - {latex(x)} - {latex(a)} + {latex(a)} = {latex(b)} + {latex(a)}$ \t\t add ${latex(a)}$ to both sides
        
        $\\displaystyle \\implies - {latex(x)} = {latex(b + a)}$
        
        $\\displaystyle \\implies {latex(x)} = {latex(- b - a)}$ \t\t multiply $-1$ to both sides
        """
        answer = - b - a
        
        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result
    
    def get_problem_1_type_0(self, x, a, b):
        varX = list(x.free_symbols)[0]

        eq = Eq(a * x, b)
        a_inv_latex = latex_fraction(1, a)
        b_a_latex = latex_fraction(b, a)
        
        eq_lhs_latex = f"{latex(a)} * {latex(x)}"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies {latex(a)} * {latex(x)} * {a_inv_latex} = {latex(b)} * {a_inv_latex}$ \t\t divide both sides by ${latex(a)}$
        
        $\\displaystyle \\implies {latex(x)} = {b_a_latex}$
        """

        answer = nsimplify(b / a)
        
        if self._simplify_fraction(b, a, answer):
            soln_s =  f"""{soln_s}
            
            $\\displaystyle \\implies {latex(x)} = {latex(answer)}$
            """
        
        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def get_problem_1_type_1(self, x, a, b):
        varX = list(x.free_symbols)[0]

        eq = Eq(- a * x, b)
        a_inv_latex = latex_fraction(1, a)
        b_a_latex = latex_fraction(b, a)
        b_a_minus_latex = latex_fraction(-b, a)
        
        eq_lhs_latex = f"- {latex(a)} * {latex(x)}"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies - {latex(a)} * {latex(x)} * {a_inv_latex} = {latex(b)} * {a_inv_latex}$ \t\t divide both sides by ${latex(a)}$
        
        $\\displaystyle \\implies -{latex(x)} = {b_a_latex}$
        
        $\\displaystyle \\implies {latex(x)} = {b_a_minus_latex}$ \t\t multiply $-1$ to both sides
        """
        answer = Rational(-b , a)

        if self._simplify_fraction(-b, a, answer):
            soln_s =  f"""{soln_s}
            
            $\\displaystyle \\implies {latex(x)} = {latex(answer)}$
            """
        
        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def get_problem_1_type_2(self, x, a, b):
        varX = list(x.free_symbols)[0]

        result = self.get_problem_1_type_1(x, a, b)
        eq = Eq(a * (-x), b)
        answer = result.get('answer')
        soln_s = result.get('soln_s').strip()
        soln_s = soln_s[soln_s.index('$') + 1:]
        


        eq_lhs_latex = f"{latex(a)} * (- {latex(x)})"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies {soln_s}
        """
        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def get_problem_1_type_3(self, x, a, b):
        varX = list(x.free_symbols)[0]

        result = self.get_problem_1_type_0(x, a, b)
        eq = Eq(- a *(- x), b)
        answer = result.get('answer')
        soln_s = result.get('soln_s').strip()
        soln_s = soln_s[soln_s.index('$') + 1:]

        eq_lhs_latex = f"- {latex(a)} * (- {latex(x)})"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies {soln_s}
        """

        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def get_problem_1_type_4(self, x, a, b):
        varX = list(x.free_symbols)[0]

        result = self.get_problem_1_type_0(x, a, b)
        eq = Eq(x * a, b)
        answer = result.get('answer')
        soln_s = result.get('soln_s').strip()
        soln_s = soln_s[soln_s.index('$') + 1:]


        eq_lhs_latex = f"{latex(x)} * {latex(a)}"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies {soln_s}
        """

        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def get_problem_1_type_5(self, x, a, b):
        varX = list(x.free_symbols)[0]

        result = self.get_problem_1_type_1(x, a, b)
        eq = Eq(x * (- a), b)
        answer = result.get('answer')
        soln_s = result.get('soln_s').strip()
        soln_s = soln_s[soln_s.index('$') + 1:]


        eq_lhs_latex = f"{latex(x)} * (- {latex(a)})"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies {soln_s}
        """

        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def get_problem_1_type_6(self, x, a, b):
        varX = list(x.free_symbols)[0]

        result = self.get_problem_1_type_1(x, a, b)
        eq = Eq(- x * a, b)
        answer = result.get('answer')
        soln_s = result.get('soln_s').strip()
        soln_s = soln_s[soln_s.index('$') + 1:]


        eq_lhs_latex = f"- {latex(x)} * {latex(a)}"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies {soln_s}
        """

        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def get_problem_1_type_7(self, x, a, b):
        varX = list(x.free_symbols)[0]

        result = self.get_problem_1_type_0(x, a, b)
        eq = Eq(- x * (- a), b)
        answer = result.get('answer')
        soln_s = result.get('soln_s').strip()
        soln_s = soln_s[soln_s.index('$') + 1:]

        
        eq_lhs_latex = f"- {latex(x)} * (- {latex(a)})"
        eq_rhs_latex = f"{latex(b)}"
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        
        problem_s =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s =  f"""
        $\\displaystyle {problem_expr_latex}$
        
        $\\displaystyle \\implies {soln_s}
        """

        result = {
            'eq': eq,
            'eq_lhs_latex': eq_lhs_latex,
            'eq_rhs_latex': eq_rhs_latex,
            'problem_s': problem_s,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result
    
    def _inverse_sides(self, result, x):
        varX = list(x.free_symbols)[0]

        if not random.choice([True, False]):
            return result

        eq = result.get('eq')
        result['eq'] = Eq(1 / eq.lhs, 1/ eq.rhs)
        
        eq_lhs_latex = latex_fraction(1, result.get('eq_lhs_latex'))
        eq_rhs_latex = latex_fraction(1, result.get('eq_rhs_latex'))
        result['eq_lhs_latex'] = eq_lhs_latex
        result['eq_rhs_latex'] = eq_rhs_latex
       
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        result['problem_s'] =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s = result.get('soln_s')
        result['soln_s'] =  f"""
        $\\displaystyle {problem_expr_latex}$ 
        
        Inverse sides to bring x to numerator:
        {soln_s}
        """
        
        return result
        pass


    def _flip_sides(self, result, x):
        varX = list(x.free_symbols)[0]

        if not random.choice([True, False]):
            return result

        eq = result.get('eq')
        result['eq'] = Eq(eq.rhs, eq.lhs)
        
        eq_lhs_latex = result.get('eq_rhs_latex')
        eq_rhs_latex = result.get('eq_lhs_latex')
        result['eq_lhs_latex'] = eq_lhs_latex
        result['eq_rhs_latex'] = eq_rhs_latex
       
        problem_expr_latex = f"{eq_lhs_latex} = {eq_rhs_latex}"
        result['problem_s'] =  f"""
        Find value of $\\displaystyle {latex(varX)}$ when
        $\\displaystyle {problem_expr_latex}$
        """
        
        soln_s = result.get('soln_s')
        result['soln_s'] =  f"""
        $\\displaystyle {problem_expr_latex}$ 
        
        Flip sides to bring x to the left hand side:
        {soln_s}
        """
        
        return result
        pass


    def _update_multi_level_soln(self, 
                                 soln_dict_list, 
                                 prob_eq = None, 
                                 last_eq = None,
                                 soln_item = None, 
                                 is_start = False):
        idx = len(soln_dict_list)
        if prob_eq is None and soln_dict_list:
            prob_eq = soln_dict_list[idx - 1]['last_eq']
        
        if not soln_item is None:
            soln_item = soln_item.strip()
        
        soln_dict_list_item = {
            'prob_eq': prob_eq,
            'last_eq': last_eq,
            'soln_item': soln_item,
            'is_start': is_start,
            }
        soln_dict_list.append(soln_dict_list_item) 
        return True
        pass
        
    def has_negative_sign(self, expr):
        try:
            if str(expr).strip().startswith("-"):
                return True
            return False
        except:
            return False
        pass
    
    


    def get_multi_level_equation_type_2_solution(self, has_num_values = None):
        
        x = symbols('x', real = True)
        
        a1, a2, a3, a4 = symbols('a1, a2, a3, a4')
        b1, b2, b3, b4 = symbols('b1, b2, b3, b4')

        if has_num_values is None:
            has_num_values = random.choice([True, False])
            
        if has_num_values:
            a1 = random.randint(2, 9) * random.choice([1, -1])
            a2 = random.randint(2, 9) * random.choice([1, -1])
            a3 = random.randint(2, 9) * random.choice([1, -1])
            a4 = random.randint(2, 9) * random.choice([1, -1])
            
            b1 = random.randint(2, 9) * random.choice([1, -1])
            b2 = random.randint(2, 9) * random.choice([1, -1])
            
            b3_1 = random.randint(2, 9) * random.choice([1, -1]) 
            b3_2 = a3 * random.choice([1, -1]) 
            b3 = random.choice([b3_1, b3_2])
            
            b4 = random.randint(2, 9) * random.choice([1, -1])
            pass
        
        
        print(f"""
        THAKUR
        a1 = {a1}
        a2 = {a2}
        a3 = {a3}
        a4 = {a4}
        b1 = {b1}
        b2 = {b2}
        b3_1 = {b3_1}
        b3_2 = {b3_2}
        b4 = {b4}
        """)
            
            

        lhs_a = a1 * x + a2  
        lhs_b = b1 * x + b2  
        
        lhs_ab = lhs_a / lhs_b
        lhs_ab_symb = latex_fraction(f"{latex(lhs_a)}", f"{latex(lhs_b)}")
        lhs_ab_symb = Symbol(lhs_ab_symb)
          
        eq = Eq(lhs_ab_symb + a4, b4)

        #soln_items = []
        soln_dict_list = []

        ## update soln_dict_list
        self._update_multi_level_soln(soln_dict_list, 
                                      prob_eq = None, 
                                      last_eq = eq, 
                                      soln_item = None, 
                                      is_start = False)


        if self.has_negative_sign(a4):
            lhs_0_0 = Symbol(f"{latex(eq.lhs)} + {latex(-a4)}")
            rhs_0_0 = Symbol(f"{latex(eq.rhs)} + {latex(-a4)}")
            message_0_0 = f"add it {-a4} both sides"
        else:
            lhs_0_0 = Symbol(f"{latex(eq.lhs)} - {latex(a4)}")
            rhs_0_0 = Symbol(f"{latex(eq.rhs)} - {latex(a4)}")
            message_0_0 = f"subtracted {a4} from both sides"
            
        eq_soln_step_0_0 = Eq(lhs_0_0, rhs_0_0)

        ab4 = b4 - a4
        
        lhs_0_1 = lhs_ab_symb
        rhs_0_1 = ab4
        eq_soln_step_0_1 = Eq(lhs_0_1, rhs_0_1)

        if a4 != 0:
            soln_item = f"""
            $\\displaystyle 
            \\implies {latex(eq_soln_step_0_0)}
            \\qquad \\text{{To remove }} {latex(a4)} 
            \\text{{ from left side, {message_0_0}.}} 
            $
    
            $\\displaystyle 
            \\implies {latex(eq_soln_step_0_1)}
            $
    
            """
            #soln_items.append(soln_item.strip())
    
            ## update soln_dict_list
            self._update_multi_level_soln(soln_dict_list, 
                                          prob_eq = None, 
                                          last_eq = eq_soln_step_0_1, 
                                          soln_item = soln_item, 
                                          is_start = True)
    
            pass
        

        lhs_1_0 = Symbol(f"{latex(lhs_ab_symb)} \\cdot \\left( {latex(lhs_b)} \\right)")
        if has_num_values:
            rhs_1_0 = Symbol(f"{latex(ab4)} \\cdot \\left( {latex(lhs_b)} \\right)")
        else:
            rhs_1_0 = Symbol(f"\\left( {latex(ab4)} \\right) \\cdot \\left( {latex(lhs_b)} \\right)")
            pass
        
        
        eq_soln_step_1_0 = Eq(lhs_1_0, rhs_1_0)

        lhs_1_1 = lhs_a
        rhs_1_1 = ab4 * lhs_b
        eq_soln_step_1_1 = Eq(lhs_1_1, rhs_1_1)

        soln_item = f"""
        $\\displaystyle 
        \\implies {latex(eq_soln_step_1_0)}
        \\qquad \\text{{To remove denominator from lhs, multiplied it by }} 
        \\left( {latex(lhs_b)} \\right)  
        $

        $\\displaystyle 
        \\implies {latex(eq_soln_step_1_1)}
        $

        """
        #soln_items.append(soln_item.strip())

        ## update soln_dict_list
        self._update_multi_level_soln(soln_dict_list, 
                                      prob_eq = None, 
                                      last_eq = eq_soln_step_1_1, 
                                      soln_item = soln_item, 
                                      is_start = True)



        if self.has_negative_sign(a2):
            lhs_2_0 = Symbol(f"{latex(lhs_a)} + {latex(-a2)}")
            rhs_2_0 = Symbol(f"{latex(ab4 * lhs_b)} + {latex(-a2)}")
            message_2_0 = f"added it {-a2} both sides"
        else:
            lhs_2_0 = Symbol(f"{latex(lhs_a)} - {latex(a2)}")
            rhs_2_0 = Symbol(f"{latex(ab4 * lhs_b)} - {latex(a2)}")
            message_2_0 = f"subtracted {a2} from both sides"
            
        eq_soln_step_2_0 = Eq(lhs_2_0, rhs_2_0)

        
        lhs_2_1 = a1 * x
        
        rhs_2_1 = ab4 * lhs_b - a2
        if not has_num_values:
            #rhs_2_1 = simplify(expand(rhs_2_1))
            pass
        
        eq_soln_step_2_1 = Eq(lhs_2_1, rhs_2_1)

        if a2 != 0:
            soln_item = f"""
            $\\displaystyle 
            \\implies {latex(eq_soln_step_2_0)}
            \\qquad \\text{{To remove }} {latex(a4)} 
            \\text{{ from left side, {message_2_0}.}} 
            $
    
            $\\displaystyle 
            \\implies {latex(eq_soln_step_2_1)}
            $
    
            """
            #soln_items.append(soln_item.strip())
    
            ## update soln_dict_list
            self._update_multi_level_soln(soln_dict_list, 
                                          prob_eq = None, 
                                          last_eq = eq_soln_step_0_1, 
                                          soln_item = soln_item, 
                                          is_start = True)

            pass
        
        
        rhs_x_coeff = ab4 * b1

        if self.has_negative_sign(rhs_x_coeff):
            lhs_3_0 = Symbol(f"{latex(a1 * x)} + {latex(-rhs_x_coeff * x)}")
            rhs_3_0 = Symbol(f"{latex(rhs_2_1)} + {latex(-rhs_x_coeff * x)}")
            message_3_0 = f"added it ${latex(-rhs_x_coeff * x)}$ both sides"
        else:
            lhs_3_0 = Symbol(f"{latex(a1 * x)} - {latex(rhs_x_coeff * x)}")
            rhs_3_0 = Symbol(f"{latex(rhs_2_1)} - {latex(rhs_x_coeff * x)}")
            message_3_0 = f"subtracted ${latex(rhs_x_coeff * x)}$ from both sides"
            
        eq_soln_step_3_0 = Eq(lhs_3_0, rhs_3_0)

        
        lhs_3_1 = a1 * x - rhs_x_coeff * x

        rhs_3_1 = rhs_2_1 - rhs_x_coeff * x
        if not has_num_values:
            lhs_3_1 = simplify(expand(lhs_3_1))
            rhs_3_1 = simplify(expand(rhs_3_1))
            pass
        
        eq_soln_step_3_1 = Eq(lhs_3_1, rhs_3_1)

        if rhs_x_coeff != 0:
            soln_item = f"""
            $\\displaystyle 
            \\implies {latex(eq_soln_step_3_0)}
            \\qquad \\text{{1. rhs_x_coeff != 0 HELLO To move }} {latex(x)} 
            \\text{{ terms to the left side, {message_3_0}.}} 
            $
    
            $\\displaystyle 
            \\implies {latex(eq_soln_step_3_1)}
            $
    
            """
            #soln_items.append(soln_item.strip())
    
            ## update soln_dict_list
            self._update_multi_level_soln(soln_dict_list, 
                                          prob_eq = None, 
                                          last_eq = eq_soln_step_3_1, 
                                          soln_item = soln_item, 
                                          is_start = True)

            pass
    
        
        lhs_x_coeff = a1 - b1 * ab4
        
        lhs_4_0 = latex_fraction(f"{latex(lhs_x_coeff * x)}",  
                                 f"{latex(lhs_x_coeff)}")
        lhs_4_0 = Symbol(lhs_4_0)
        
        rhs_4_0 = latex_fraction(f"{latex(rhs_3_1)}",
                                 f"{latex(lhs_x_coeff)}")
        rhs_4_0 = Symbol(rhs_4_0)

        eq_soln_step_4_0 = Eq(lhs_4_0, rhs_4_0)

        lhs_4_1 = x
        rhs_4_1 = rhs_3_1 / lhs_x_coeff
        eq_soln_step_4_1 = Eq(lhs_4_1, rhs_4_1)

        if lhs_x_coeff != 1:
            soln_item = f"""
            $\\displaystyle 
            \\implies {latex(eq_soln_step_4_0)}
            \\qquad \\text{{Divided both sides with }} {latex(lhs_x_coeff)}
            $
    
            $\\displaystyle 
            \\implies {latex(eq_soln_step_4_1)}
            $
    
            """
            #soln_items.append(soln_item.strip())

            ## update soln_dict_list
            self._update_multi_level_soln(soln_dict_list, 
                                          prob_eq = None, 
                                          last_eq = eq_soln_step_4_1, 
                                          soln_item = soln_item, 
                                          is_start = True)
    

       
        ##############LAST STEPS #######################
        ###########################################
        eq = Eq(lhs_ab_symb + a4, b4)
        
        x_solns = solve(eq, x)
        if not x_solns:
            x_solns = solve(eq_soln_step_1_1, x)
        
        if not x_solns:
            x_soln = eq_soln_step_4_1.rhs
        else:
            x_soln = x_solns[0]
        
        eq_soln = Eq(x, x_soln)
  
        if str(x_soln).strip() != str(eq_soln_step_4_1.rhs).strip():
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq_soln)}
            $
            """
            #soln_items.append(soln_item.strip())
    
            ## update soln_dict_list
            self._update_multi_level_soln(soln_dict_list, 
                                          prob_eq = None, 
                                          last_eq = eq_soln, 
                                          soln_item = soln_item, 
                                          is_start = False)
        
        if has_num_values:
            x_soln = toIntOrFloat(x_soln, 2)
 
        if has_num_values:
            sol1 = round(float(eq_soln_step_4_1.rhs), 2)
            sol2 = round(float(eq_soln.rhs), 2)
            if sol1 != sol2:
                soln_item = f"""
                <h3>Error: Solution is wrong!</h3>
                {sol1} != {sol2}
                """
                #soln_items.append(soln_item.strip())
    
                ## update soln_dict_list
                self._update_multi_level_soln(soln_dict_list, 
                                              prob_eq = None, 
                                              last_eq = eq_soln, 
                                              soln_item = soln_item, 
                                              is_start = False)
    
       
        
        answer_s = f"""
        $\\displaystyle {latex(x_soln)}$
        """

        result = {
            'soln_dict_list': soln_dict_list,
            'answer_s': answer_s
            }
        
        return result
        pass

    def get_multi_level_equation_solution(self, has_num_values = None):
        
        x = symbols('x', real = True)
        
        a1, a2, a3, a4 = symbols('a1, a2, a3, a4')
        b1, b2, b3, b4 = symbols('b1, b2, b3, b4')

        if has_num_values is None:
            has_num_values = random.choice([True, False])
            
        if has_num_values:
            a1 = random.randint(2, 9) * random.choice([1, -1])
            a2 = random.randint(2, 9) * random.choice([1, -1])
            a3 = random.randint(2, 9) * random.choice([1, -1])
            a4 = random.randint(2, 9) * random.choice([1, -1])
            
            b1 = random.randint(2, 9) * random.choice([1, -1])
            b2 = random.randint(2, 9) * random.choice([1, -1])
            
            b3_1 = random.randint(2, 9) * random.choice([1, -1]) 
            b3_2 = a3 * random.choice([1, -1]) 
            b3 = random.choice([b3_1, b3_2])
            
            b4 = random.randint(2, 9) * random.choice([1, -1])

        lhs_a = ueval(a1 * x + a2) / a3     
        lhs = lhs_a + a4

        rhs_b = ueval(b1 * x + b2) / b3     
        rhs = rhs_b + b4
        

        eq = Eq(lhs, rhs)

        #soln_items = []
        soln_dict_list = []

        ## update soln_dict_list
        self._update_multi_level_soln(soln_dict_list, 
                                      prob_eq = None, 
                                      last_eq = eq, 
                                      soln_item = None, 
                                      is_start = False)

        if self.has_negative_sign(a4):
            lhs_0_0 = Symbol(f"{latex(eq.lhs)} + {latex(-a4)}")
            rhs_0_0 = Symbol(f"{latex(eq.rhs)} + {latex(-a4)}")
            message_0_0 = f"added it {-a4} both sides"
        else:
            lhs_0_0 = Symbol(f"{latex(eq.lhs)} - {latex(a4)}")
            rhs_0_0 = Symbol(f"{latex(eq.rhs)} - {latex(a4)}")
            message_0_0 = f"subtracted {a4} from both sides"
            
        eq_soln_step_0_0 = Eq(lhs_0_0, rhs_0_0)

        ab4 = b4 - a4
        
        lhs_0_1 = lhs_a
        rhs_0_1 = rhs_b + ab4
        eq_soln_step_0_1 = Eq(lhs_0_1, rhs_0_1)

        soln_item = f"""
        $\\displaystyle 
        \\implies {latex(eq_soln_step_0_0)}
        \\qquad \\text{{To remove }} {latex(a4)} 
        \\text{{ from left side, {message_0_0}.}} 
        $

        $\\displaystyle 
        \\implies {latex(eq_soln_step_0_1)}
        $

        """
        #soln_items.append(soln_item.strip())

        ## update soln_dict_list
        self._update_multi_level_soln(soln_dict_list, 
                                      prob_eq = None, 
                                      last_eq = eq_soln_step_0_1, 
                                      soln_item = soln_item, 
                                      is_start = True)

        if self.has_negative_sign(rhs_b):
            lhs_1_0 = Symbol(f"{latex(lhs_a)} + {latex(-rhs_b)}")
            rhs_1_0 = Symbol(f"{latex(rhs_b + ab4)} + {latex(-rhs_b)}")

            lhs_1_1 = Symbol(f"{latex(lhs_a)} + {latex(-rhs_b)}")
        
        else:
            lhs_1_0 = Symbol(f"{latex(lhs_a)} - {latex(rhs_b)}")
            rhs_1_0 = Symbol(f"{latex(rhs_b + ab4)} - {latex(rhs_b)}")

            lhs_1_1 = Symbol(f"{latex(lhs_a)} - {latex(rhs_b)}")
            pass
        
        eq_soln_step_1_0 = Eq(lhs_1_0, rhs_1_0)

        rhs_1_1 = ab4
        eq_soln_step_1_1 = Eq(lhs_1_1, rhs_1_1)

        soln_item = f"""
        $\\displaystyle 
        \\implies {latex(eq_soln_step_1_0)}
        \\qquad \\text{{Moved }} {latex(x)} \\text{{ terms to the lhs.}} 
        $

        $\\displaystyle 
        \\implies {latex(eq_soln_step_1_1)}
        $

        """
        #soln_items.append(soln_item.strip())

        ## update soln_dict_list
        self._update_multi_level_soln(soln_dict_list, 
                                      prob_eq = None, 
                                      last_eq = eq_soln_step_1_1, 
                                      soln_item = soln_item, 
                                      is_start = True)


        if has_num_values:
            lcm_a3_b3 = MathUtil.lcm(abs(a3), abs(b3))
            fctr_a = lcm_a3_b3 // a3
            fctr_b = lcm_a3_b3 // b3
        else:
            lcm_a3_b3 = a3 * b3
            fctr_a = lcm_a3_b3 / a3
            fctr_b = lcm_a3_b3 / b3

        fctr_a_latex = latex_fraction(latex(fctr_a), latex(fctr_a))

        fctr_b_latex = latex_fraction(latex(fctr_b), latex(fctr_b))

        
        lhs_a_2_0 = Symbol(f"{latex(lhs_a)} \\cdot {fctr_a_latex}")  

        if self.has_negative_sign(rhs_b):
            lhs_b_2_0 = Symbol(f"{latex(-rhs_b)} \\cdot {fctr_b_latex}")       
            lhs_2_0 = Symbol(f"{latex(lhs_a_2_0)} + {latex(lhs_b_2_0)}")
            pass
        else:
            lhs_b_2_0 = Symbol(f"{latex(rhs_b)} \\cdot {fctr_b_latex}")       
            lhs_2_0 = Symbol(f"{latex(lhs_a_2_0)} - {latex(lhs_b_2_0)}")
            pass
        

        rhs_2_0 = ab4
        eq_soln_step_2_0 = Eq(lhs_2_0, rhs_2_0)


        if str(abs(a3)) != str(abs(b3)):
            soln_item = f"""
            $\\displaystyle 
            \\implies {latex(eq_soln_step_2_0)}
            \\qquad \\text{{Made denominator of both terms on left equal. }} 
            \\text{{ The LCM of }} {latex(abs(a3))}, {latex(abs(b3))} = {latex(lcm_a3_b3)}
            $
    
            """
            #soln_items.append(soln_item.strip())

            ## update soln_dict_list
            self._update_multi_level_soln(soln_dict_list, 
                                          prob_eq = None, 
                                          last_eq = eq_soln_step_2_0, 
                                          soln_item = soln_item, 
                                          is_start = True)




        a1b3 = a1 * fctr_a
        a2b3 = a2 * fctr_a
        
        b1a3 = b1 * fctr_b
        b2a3 = b2 * fctr_b
        
        lhs_a_3_0 = a1b3 * x + a2b3
        lhs_b_3_0 = b1a3 * x + b2a3   

        lhs_a_3_0_ltx = f"\\left({latex(lhs_a_3_0)}\\right)"
 
        if self.has_negative_sign(rhs_b):
            lhs_b_3_0_ltx = f"\\left({latex(-lhs_b_3_0)}\\right)"
            lhs_a_3_0_numr = f"{lhs_a_3_0_ltx} + {lhs_b_3_0_ltx}"      
            pass
        else:
            lhs_b_3_0_ltx = f"\\left({latex(lhs_b_3_0)}\\right)"
            lhs_a_3_0_numr = f"{lhs_a_3_0_ltx} - {lhs_b_3_0_ltx}"      
            pass
        

        lhs_3_0 = latex_fraction(f"{lhs_a_3_0_numr}",
                                 f"{latex(lcm_a3_b3)}")
        
        lhs_3_0 = Symbol(lhs_3_0)
        
        rhs_3_0 = ab4
        eq_soln_step_3_0 = Eq(lhs_3_0, rhs_3_0)

        soln_item = f"""
        $\\displaystyle 
        \\implies  {latex(eq_soln_step_3_0)}
        $

        """
        #soln_items.append(soln_item.strip())

        ## update soln_dict_list
        self._update_multi_level_soln(soln_dict_list, 
                                      prob_eq = None, 
                                      last_eq = eq_soln_step_3_0, 
                                      soln_item = soln_item, 
                                      is_start = True)



        a1b3_b1a3 = a1b3 - b1a3
        a2b3_b2a3 = a2b3 - b2a3

        lhs_4_0_numr = a1b3_b1a3 * x + a2b3_b2a3

        lhs_4_0 = latex_fraction(latex(lhs_4_0_numr), latex(lcm_a3_b3))
        lhs_4_0 = Symbol(lhs_4_0)
        
        rhs_4_0 = ab4
        eq_soln_step_4_0 = Eq(lhs_4_0, rhs_4_0)

        soln_item = f"""
        $\\displaystyle 
        \\implies {latex(eq_soln_step_4_0)}
        $

        """
        #soln_items.append(soln_item.strip())

        ## update soln_dict_list
        self._update_multi_level_soln(soln_dict_list, 
                                      prob_eq = None, 
                                      last_eq = eq_soln_step_4_0, 
                                      soln_item = soln_item, 
                                      is_start = True)



        lhs_5_0_numr = a1b3_b1a3 * x + a2b3_b2a3

        lhs_5_0 = latex_fraction(latex(lhs_5_0_numr), latex(lcm_a3_b3))
        lhs_5_0 = Symbol(f"{lhs_5_0} \\cdot {latex(lcm_a3_b3)}")
        
        rhs_5_0 = Symbol(f"{latex(ab4)} \\cdot {latex(lcm_a3_b3)}")
        eq_soln_step_5_0 = Eq(lhs_5_0, rhs_5_0)

        lhs_5_1 = Symbol(f"{latex(lhs_5_0_numr)}")
        rhs_5_1 = Symbol(f"{latex(ab4 * lcm_a3_b3)}")
        eq_soln_step_5_1 = Eq(lhs_5_1, rhs_5_1)

        if lcm_a3_b3 != 1:
            soln_item = f"""
            $\\displaystyle 
            \\implies {latex(eq_soln_step_5_0)}
            \\qquad \\text{{Multiplied both sides with }} {latex(lcm_a3_b3)}
            $
    
            $\\displaystyle 
            \\implies {latex(eq_soln_step_5_1)}
            $
    
            """
            #soln_items.append(soln_item.strip())

            ## update soln_dict_list
            self._update_multi_level_soln(soln_dict_list, 
                                          prob_eq = None, 
                                          last_eq = eq_soln_step_5_1, 
                                          soln_item = soln_item, 
                                          is_start = True)
    
    
            
        
        if self.has_negative_sign(a2b3_b2a3):
            if has_num_values:
                latex_a2b3_b2a3 = f"{latex(-a2b3_b2a3)}"
            else:
                latex_a2b3_b2a3 = f"\\left({latex(-a2b3_b2a3)}\\right)"
                pass
            lhs_6_0 = Symbol(f"{latex(a1b3_b1a3 * x + a2b3_b2a3)} + {latex_a2b3_b2a3}")
            rhs_6_0 = Symbol(f"{latex(ab4 * lcm_a3_b3)} + {latex_a2b3_b2a3}")
            message_6_0 = f"To both sides, added ${latex(-a2b3_b2a3)}$"
            pass
        else:
            if has_num_values:
                latex_a2b3_b2a3 = f"{latex(a2b3_b2a3)}"
            else:
                latex_a2b3_b2a3 = f"\\left({latex(a2b3_b2a3)}\\right)"
                pass
            lhs_6_0 = Symbol(f"{latex(a1b3_b1a3 * x + a2b3_b2a3)} - {latex_a2b3_b2a3}")
            rhs_6_0 = Symbol(f"{latex(ab4 * lcm_a3_b3)} - {latex_a2b3_b2a3}")
            message_6_0 = f"From both sides, subtracted ${latex(a2b3_b2a3)}$"
            pass

        eq_soln_step_6_0 = Eq(lhs_6_0, rhs_6_0)

        lhs_6_1 = a1b3_b1a3 * x
        rhs_6_1 = ab4 * lcm_a3_b3 - a2b3_b2a3
        eq_soln_step_6_1 = Eq(lhs_6_1, rhs_6_1)

        if a2b3_b2a3 != 0:
            soln_item = f"""
            $\\displaystyle 
            \\implies {latex(eq_soln_step_6_0)}
            \\qquad \\text{{{message_6_0} }} 
            $
    
            $\\displaystyle 
            \\implies {latex(eq_soln_step_6_1)}
            $
    
            """
            #soln_items.append(soln_item.strip())

            ## update soln_dict_list
            self._update_multi_level_soln(soln_dict_list, 
                                          prob_eq = None, 
                                          last_eq = eq_soln_step_6_1, 
                                          soln_item = soln_item, 
                                          is_start = True)
    


        lhs_7_0 = latex_fraction(f"{latex(a1b3_b1a3 * x)}",  
                                 f"{latex(a1b3_b1a3)}")
        lhs_7_0 = Symbol(lhs_7_0)
        
        rhs_7_0 = latex_fraction(f"{latex(ab4 * lcm_a3_b3 - a2b3_b2a3)}",
                                 f"{latex(a1b3_b1a3)}")
        rhs_7_0 = Symbol(rhs_7_0)

        eq_soln_step_7_0 = Eq(lhs_7_0, rhs_7_0)

        lhs_7_1 = x
        rhs_7_1 = (ab4 * lcm_a3_b3 - a2b3_b2a3) / a1b3_b1a3
        eq_soln_step_7_1 = Eq(lhs_7_1, rhs_7_1)

        if a1b3_b1a3 != 1:
            soln_item = f"""
            $\\displaystyle 
            \\implies {latex(eq_soln_step_7_0)}
            \\qquad \\text{{Divided both sides with }} {latex(a1b3_b1a3)}
            $
    
            $\\displaystyle 
            \\implies {latex(eq_soln_step_7_1)}
            $
    
            """
            #soln_items.append(soln_item.strip())

            ## update soln_dict_list
            self._update_multi_level_soln(soln_dict_list, 
                                          prob_eq = None, 
                                          last_eq = eq_soln_step_7_1, 
                                          soln_item = soln_item, 
                                          is_start = True)
    


        lhs_a = (a1 * x + a2) / a3     
        lhs = lhs_a + a4

        rhs_b = (b1 * x + b2) / b3     
        rhs = rhs_b + b4

        eq = Eq(lhs, rhs)
        x_soln = solve(eq, x)[0]
        if has_num_values:
            x_soln = toIntOrFloat(x_soln, 2)
        
        eq_soln = Eq(x, x_soln)

        
        if str(x_soln).strip() != str(eq_soln_step_7_1.rhs).strip():
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq_soln)}
            $
            """
            #soln_items.append(soln_item.strip())
    
            ## update soln_dict_list
            self._update_multi_level_soln(soln_dict_list, 
                                          prob_eq = None, 
                                          last_eq = eq_soln, 
                                          soln_item = soln_item, 
                                          is_start = False)



        if has_num_values:
            sol1 = round(float(eq_soln_step_7_1.rhs), 2)
            sol2 = round(float(eq_soln.rhs), 2)
            if sol1 != sol2:
                soln_item = f"""
                <h3>Error: Solution is wrong!</h3>
                {sol1} != {sol2}
                """
                #soln_items.append(soln_item.strip())
    
                ## update soln_dict_list
                self._update_multi_level_soln(soln_dict_list, 
                                              prob_eq = None, 
                                              last_eq = eq_soln, 
                                              soln_item = soln_item, 
                                              is_start = False)
    
        
        answer_s = f"""
        $\\displaystyle {latex(eq_soln.rhs)}$
        """

        result = {
            'soln_dict_list': soln_dict_list,
            'answer_s': answer_s
            }
        
        return result
        pass
        

    def add_expression(self, expressions, opr_type = None, n = None):
        from sympy import UnevaluatedExpr as ueval
        if n is None:
            n = random.randint(1, 100) * random.choice([1, -1])
        
        if (isinstance(n, int) or isinstance(n, Symbol)):
            n_expr = f"{n}"
        else:
            n_expr = f"\\left({{{latex(n)}}}\\right)"
            n_expr = n_expr.replace("\\frac", "\\dfrac")
        
        prev_expr = expressions[-1][1]
        prev_latex_expr = f"\\left({{{latex(prev_expr)}}}\\right)"
        #prev_latex_expr = f"\\left({{{expressions[-1][2]}}}\\right)"

        if opr_type == 1:
            statement = f"Add $\\displaystyle {n_expr}$ to the expression"
            latex_expr = f"{prev_latex_expr} + {n_expr} "
            #expr = Add(prev_expr, n, evaluate = False)
            expr =  ueval(prev_expr) + ueval(n) 

        elif opr_type == 2:
            statement = f"Add expression to $\\displaystyle {n_expr}$"
            latex_expr = f"{n_expr} + {prev_latex_expr}"
            #expr = Add(n, prev_expr, evaluate = False)
            expr =  ueval(n) + ueval(prev_expr) 
                
        #subtract
        elif opr_type == 3:
            statement = f"Subtract $\\displaystyle {n_expr}$ from the expression"
            latex_expr = f"{prev_latex_expr} - {n_expr} "
            #expr = Add(prev_expr, -n, evaluate = False)
            expr =  ueval(prev_expr) - ueval(n) 
                
        elif opr_type == 4:
            statement = f"Subtract the expression from $\\displaystyle {n_expr}$"
            latex_expr = f"{n_expr} - {prev_latex_expr}"
            #expr = Add(n, -prev_expr, evaluate = False)
            expr =  ueval(n) - ueval(prev_expr) 
    
            #Multiply        
        elif opr_type == 5:
            statement = f"Multiply $\\displaystyle {n_expr}$ with the expression"
            latex_expr = f"{n_expr} * {prev_latex_expr}"
            #expr = Mul(n, prev_expr, evaluate = False)
            expr =  ueval(n) * ueval(prev_expr) 
                
        elif opr_type == 6:
            statement = f"Multiply the expression with $\\displaystyle {n_expr}$"
            latex_expr = f"{prev_latex_expr} * {n_expr} "
            #expr = Mul(n, prev_expr, evaluate = False)
            expr =  ueval(prev_expr)  * ueval(n)

            #Divide        
        elif opr_type == 7:
            statement = f"Divide the expression by $\\displaystyle {n_expr}$"
            latex_expr = latex_fraction(f"{latex(prev_expr)}", n_expr)
            try:
                #expr = Mul(prev_expr, Rational(1, n), evaluate = False)
                expr = prev_expr * ueval(Rational(1, n)) 
            except:
                #expr = Mul(prev_expr, 1/n, evaluate = False)
                expr = prev_expr / ueval(n) 
                
        elif opr_type == 8:
            statement = f"Divide $\\displaystyle {n_expr}$ by the expression"
            latex_expr = latex_fraction(n_expr, f"{latex(prev_expr)}")
            #expr = Mul(n, 1 / (prev_expr), evaluate = False)
            expr = ueval(n) / prev_expr
            

        elif opr_type == 9:
            statement = f"Raise $\\displaystyle {n_expr}$ to the power of expression"
            latex_expr = f"{{{n_expr}}}^{{{prev_latex_expr}}}"
            #expr = Pow(n, prev_expr, evaluate = False)
            expr = n ** ueval(prev_expr)
                
        elif opr_type == 10:
            statement = f"Raise expression to the power of $\\displaystyle {n_expr}$"
            latex_expr = f"{{{prev_latex_expr}}}^{{{n_expr}}}"
            #expr = Pow(prev_expr, n, evaluate = False)
            expr = prev_expr ** ueval(n)
        else:
            raise Exception(f"Invalid opr_type = {opr_type}")
        
        expressions.append([statement, expr, latex_expr])
        pass
    
    def get_prob_soln_str(self, expressions):
        problem_arr = []
        solution_arr = []
        for idx, expression in enumerate(expressions):
            latex_s = latex(expression[1]) #.replace("\\frac", "\\dfrac")
            latex_expr = expression[2] #.replace("\\frac", "\\dfrac")
            if idx == 0:
                soln_s = f"""
                    <strong>The given expression is:</strong>
                    $\\displaystyle {latex_s}$ 
                    """
                p_str = f"""
                    The given expression is $\\displaystyle {latex_s}$. 
                    Do the followings:
                """
                problem_arr.append(p_str.strip())
            else:
                soln_s = f"""
                    <strong>{expression[0]}:</strong>
                    
                    $\\displaystyle {latex_expr}$
                    
                    = $\\displaystyle {latex_s}$ 
                    """
                problem_arr.append(expression[0])
                
            solution_arr.append(soln_s.strip())
  

            pass
        answer_str = latex(expressions[-1][1]) #.replace("\\frac", "\\dfrac")
        answer_str = f"$\\displaystyle {answer_str}$"
        
        #problem_arr_str = "<ul><li>" + "</li><li>".join(problem_arr) + "</li></ul>"
        problem_arr_str = "</li><li>".join(problem_arr[1:])
        problem_arr_str = f"""
        <strong>{problem_arr[0]}</strong>
        <ol><li>{problem_arr_str}</li></ol>
        """
        solution_arr_str = f"\n\n".join(solution_arr)
        
        return problem_arr_str, solution_arr_str, answer_str
        pass
    

   
    pass
#class SingleVariableHelper:
