'''
Created on 12-Jul-2021

@author: bthakur
'''
import random
import inspect
import traceback
import re
from sympy import (Eq, symbols, sqrt, latex,
                   Rational, solve, log, nsimplify )

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

class SingleVariableEquationHelper:
    def __init__(self, **kwargs):
        self.verbose = kwargs.get('verbose', False)
        self.x = symbols('x')
        self.eqs = self._init_eqs()
        self.implies = symbols(u'\U000021D2')
        pass

    def _init_eqs(self):
        x = self.x
        eqs = {'eq_8': Eq(random.randint(1, 9)**x, random.randint(1, 9)),
             'eq_10': Eq((-random.randint(1, 9))**x, 9),
             'eq_12': Eq((1/random.randint(1, 9))**x, random.randint(1, 9)),
             'eq_14': Eq((-1/random.randint(1, 9))**x, 5),
             'eq_16': Eq(Log(random.randint(1, 9), x), random.randint(1, 9)),
             'eq_17': Eq(Log(x, random.randint(1, 9)), 7),
             'eq_18': Eq(Log(-random.randint(1, 9), x), 7),
             'eq_19': Eq(Log(x, -random.randint(1, 9)), 7),
             'eq_20': Eq(Log(1/random.randint(1, 9), x), random.randint(1, 9)),
             'eq_21': Eq(Log(x, 1/random.randint(1, 9)), random.randint(1, 9)),
             'eq_22': Eq(Log(-1/random.randint(1, 9), x), random.randint(1, 9)),
             'eq_23': Eq(Log(x, -1/random.randint(1, 9)), random.randint(1, 9))
             }
         
        return eqs   
        pass
    
    def get_equation(self, eq_type = None):
        if eq_type is None:
            eq_type = random.choice(list(self.eqs.keys()))

        if isinstance(eq_type, int):
            eq_type = f"eq_{eq_type}"
        
        method_name = f"_get_{eq_type}"
        if hasattr(self, method_name):
            method = getattr(self, method_name)  
            return method(eq_type)
            
        return {'eq_type': eq_type, 
                'eq': self.eqs.get(eq_type, None)
            }
        pass

    def _get_eq_0(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(1, 10), 2)
        x_sign = random.choice([1, -1]) 
        eq =  Eq(x_sign * x + n1, n2)
        
        eq_str = f"${x_sign * x} + {n1}$ = ${n2}$"
        steps = [eq_str]
        steps.append(f"\nAs {n1} is added to ${x_sign * x}$, subtract {n1} from both sides")
        steps.append(f"{implies} ${x_sign * x} + {n1} - {n1}$ = ${n2} - {n1}$")
        steps.append(f"\n{implies} ${x_sign * x}$ = ${n2 - n1}$")
        if x_sign == -1:
            steps.append(f"\nAs {x} has negative sign, multiply both sides by $-1$")
            steps.append(f"\n{implies} $- {x_sign * x}$ = $- {n2 - n1}$")
            steps.append(f"\n{implies} ${x}$ = ${- (n2 - n1)}$")
            
        
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x)
        answer_s = ", ".join([f"${latex(v)}$" for v in x_val_sympy])
        
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    def _get_eq_1(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(1, 10), 2)
        x_sign = random.choice([1, -1]) 
        eq =  Eq(x_sign * x - n1, n2)
        
        eq_str = f"${x_sign * x} - {n1}$ = ${n2}$"
        steps = [eq_str]
        steps.append(f"\nAs {n1} is subtracted from ${x_sign * x}$, add {n1} to both sides")
        steps.append(f"{implies} ${x_sign * x} - {n1} + {n1}$ = ${n2} + {n1}$")
        steps.append(f"\n{implies} ${x_sign * x}$ = ${n2 + n1}$")
        if x_sign == -1:
            steps.append(f"\nAs {x} has negative sign, multiply both sides by $-1$")
            steps.append(f"\n{implies} $- {x_sign * x}$ = $- {n2 + n1}$")
            steps.append(f"\n{implies} ${x}$ = ${- (n2 + n1)}$")
            
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x)
        answer_s = ", ".join(['$' +  latex(v).replace('\\frac', '\\dfrac') + '$'
                              for v in x_val_sympy])
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass

    def _get_eq_2(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(1, 10), 2)
        n1_inv = Rational(1, n1)
        n1_inv_latex = latex_fraction(1, n1)

        x_sign = random.choice([1, -1]) 

        eq =  Eq(x_sign * x + n1_inv, n2)
        eq_str = f"${x_sign * x} + {n1_inv_latex}$ = ${n2}$"

        steps = [eq_str]
        steps.append(f"\nAs ${n1_inv_latex}$ is added from ${x_sign * x}$, subtract ${n1_inv_latex}$ from both sides")
        steps.append(f"\n{implies} ${x_sign * x} + {n1_inv_latex} - {n1_inv_latex}$ = ${n2} - {n1_inv_latex}$")

        x_val = latex(n2 - n1_inv).replace('\\frac', '\\dfrac')
        answer_s = f"${x_sign * x}$ = ${x_val}$"
        steps.append(f"\n{implies} {answer_s}")
        
        if x_sign == -1:
            steps.append(f"\nAs {x} has negative sign, multiply both sides by $-1$")
            steps.append(f"\n{implies} $- {x_sign * x}$ = $- {x_val}$")
        
            x_val = latex(- n2 + n1_inv).replace('\\frac', '\\dfrac')
            answer_s = f"${x}$ = ${(x_val)}$"
            steps.append(f"\n{implies} {answer_s}")
        
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)
        
        x_val_sympy = solve(eq, x)
        answer_s = ", ".join(['$' +  latex(v).replace('\\frac', '\\dfrac') + '$'
                              for v in x_val_sympy])
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    def _get_eq_3(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(1, 10), 2)
        n1_inv = Rational(1, n1)
        n1_inv_latex = latex_fraction(1, n1)

        x_sign = random.choice([1, -1]) 

        eq =  Eq(x_sign * x - n1_inv, n2)
        eq_str = f"${x_sign * x} - {n1_inv_latex}$ = ${n2}$"

        steps = [eq_str]
        steps.append(f"\nAs ${n1_inv_latex}$ is subtracted from {x_sign * x}, add ${n1_inv_latex}$ to both sides")
        steps.append(f"\n{implies} ${x_sign * x} - {n1_inv_latex} + {n1_inv_latex}$ = ${n2} + {n1_inv_latex}$")

        x_val = latex(n2 + n1_inv).replace('\\frac', '\\dfrac')
        answer_s = f"${x_sign * x}$ = ${x_val}$"
        steps.append(f"\n{implies} {answer_s}")
        
        if x_sign == -1:
            steps.append(f"\nAs {x} has negative sign, multiply both sides by $-1$")
            steps.append(f"\n{implies} $- {x_sign * x}$ = $- {x_val}$")
        
            x_val = latex(- n2 - n1_inv).replace('\\frac', '\\dfrac')
            answer_s = f"${x}$ = ${(x_val)}$"
            steps.append(f"\n{implies} {answer_s}")
        
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)
        
        x_val_sympy = solve(eq, x)
        answer_s = ", ".join(['$' +  latex(v).replace('\\frac', '\\dfrac') + '$'
                              for v in x_val_sympy])
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    def _get_eq_4(self, eq_type):
        #'eq_4': Eq(random.randint(1, 9)*x, random.randint(1, 9)),
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(1, 10), 2)
        n1 = n1 * random.choice([1, -1])

        eq =  Eq(x * n1, n2)
        
        eq_str = f"${n1}{x}$ = ${n2}$"
        frac11 = latex_fraction(f"{n1}{x}", n1)
        frac12 = latex_fraction(n2, n1)
        frac22 = latex(Rational(n2, n1)).replace("\\frac", "\\dfrac")
        
        steps = [eq_str]
        steps.append(f"\nAs {n1} is multiplied to {x}, divide both sides by {n1}")
        steps.append(f"{implies} ${frac11}$ = ${frac12}$")
        steps.append(f"\n{implies} ${x}$ = ${frac22}$")
        
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x)
        answer_s = ", ".join(['$' +  latex(v).replace('\\frac', '\\dfrac') + '$'
                              for v in x_val_sympy])
        
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    def _get_eq_5(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(1, 10), 2)
        n1 = n1 * random.choice([1, -1])
        x_sign = random.choice([1, -1]) 

        eq =  Eq(x / n1, n2)
        
        eq_lhs = latex_fraction(x, n1)
        eq_str = f"${eq_lhs}$ = ${n2}$"
        
        steps = [eq_str]
        steps.append(f"\nAs {x} is divided by {n1}, multiply both sides by {n1}")
        steps.append(f"{implies} ${eq_lhs} * {n1}$ = ${n2} * {n1}$")
        steps.append(f"\n{implies} ${x}$ = ${n2 * n1}$")
        
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x)
        answer_s = ", ".join([f"${latex(v)}$" for v in x_val_sympy])
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    def _get_eq_6(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(1, 10), 2)
        x_sign = random.choice([1, -1]) 
        eq =  Eq(x_sign * x / n1, n2)
        
        eq_lhs = latex_fraction(x_sign * x, n1)
        eq_str = f"${eq_lhs}$ = ${n2}$"
        
        steps = [eq_str]
        steps.append(f"\nAs {x} is divided by {n1}, multiply both sides by {n1}")
        steps.append(f"{implies} ${eq_lhs} * {n1}$ = ${n2} * {n1}$")
        steps.append(f"\n{implies} ${x_sign * x}$ = ${n2 * n1}$")
        if x_sign == -1:
            steps.append(f"\nAs {x} has negative sign, multiply both sides by $-1$")
            steps.append(f"\n{implies} $- {x_sign * x}$ = $- {n2 * n1}$")
            steps.append(f"\n{implies} ${x}$ = ${- n2 * n1}$")
            
        
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x)
        answer_s = ", ".join([f"${latex(v)}$" for v in x_val_sympy])
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass

    def _get_eq_7(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(1, 10), 2)
        x_sign = random.choice([1, -1]) 
        eq =  Eq(1 / (x_sign * x + n1), n2)
        
        lhs_frac = latex_fraction(1, f"{x_sign * x } + {n1}")
        eq_str = f"${lhs_frac}$ = ${n2}$"

        rhs_inv_frac = latex_fraction(1, n2)
        n2_inv = Rational(1, n2)

        steps = [eq_str]
        steps.append(f"\nAs {x} is in the denominator, inverse both sides")
        steps.append(f"\n{implies} ${x_sign * x} + {n1}$ = ${rhs_inv_frac}$")
        
        steps.append(f"\nAs {n1} is added to ${x_sign * x}$, subtract {n1} from both sides")
        steps.append(f"{implies} ${x_sign * x} + {n1} - {n1}$ = ${rhs_inv_frac} - {n1}$")
        
        x_val = latex(n2_inv - n1).replace("\\frac", "\\dfrac")
        steps.append(f"\n{implies} ${x_sign * x}$ = ${x_val}$")
        if x_sign == -1:
            steps.append(f"\nAs {x} has negative sign, multiply both sides by $-1$")
            steps.append(f"\n{implies} $- {x_sign * x}$ = $- {x_val}$")

            x_val = latex(- (n2_inv - n1)).replace("\\frac", "\\dfrac")
            steps.append(f"\n{implies} ${x}$ = ${x_val}$")
            
        
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x)
        answer_s = ", ".join(['$' +  latex(v).replace('\\frac', '\\dfrac') + '$'
                              for v in x_val_sympy])
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    def _get_eq_8(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(1, 10), 2)
        x_sign = random.choice([1, -1]) 
        eq =  Eq(1 / (x_sign * x - n1), n2)
        
        lhs_frac = latex_fraction(1, f"{x_sign * x } - {n1}")
        eq_str = f"${lhs_frac}$ = ${n2}$"
        steps = [eq_str]

        rhs_inv_frac = latex_fraction(1, n2)
        n2_inv = Rational(1, n2)

        steps.append(f"\nAs {x} is in the denominator, inverse both sides")
        steps.append(f"\n{implies} ${x_sign * x} - {n1}$ = ${rhs_inv_frac}$")
        
        steps.append(f"\nAs {n1} is subtracted from ${x_sign * x}$, add {n1} to both sides")
        steps.append(f"{implies} ${x_sign * x} - {n1} + {n1}$ = ${rhs_inv_frac} + {n1}$")
        
        x_val = latex(n2_inv + n1).replace("\\frac", "\\dfrac")
        steps.append(f"\n{implies} ${x_sign * x}$ = ${x_val}$")
        if x_sign == -1:
            steps.append(f"\nAs {x} has negative sign, multiply both sides by $-1$")
            steps.append(f"\n{implies} $- {x_sign * x}$ = $- {x_val}$")

            x_val = latex(- (n2_inv + n1)).replace("\\frac", "\\dfrac")
            steps.append(f"\n{implies} ${x}$ = ${x_val}$")
            
        
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x)
        answer_s = ", ".join(['$' +  latex(v).replace('\\frac', '\\dfrac') + '$'
                              for v in x_val_sympy])
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    def _get_eq_9(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(2, 6), 2)
        x_sign = random.choice([1, -1]) 
        eq =  Eq((x_sign * x) ** n1, n2)
        
        n1_inv = Rational(1, n1)
        n1_inv_frac = latex(n1_inv)
        
        lhs_exponent = latex_exponent(x_sign * x, n1)
        eq_str = f"${lhs_exponent} = {n2}$"
        steps = [eq_str]
        
        lhs1_exponent = latex_exponent(x, n1, p_bracket = True)
        if x_sign ** n1 >= 0:
            x_sign_str = f""
        else:
            x_sign_str = f"- "
            
        if x_sign == -1:
            steps.append(f"""\n${implies}
             {x_sign_str}{lhs1_exponent}
              = {n2}
              $""")
        
        if x_sign ** n1 == -1:
            steps.append(f"\nAs left hand side has negative sign, multiply both sides by $-1$")

            steps.append(f"""\n${implies}
             - {x_sign_str}{lhs1_exponent}
              = - {n2}
              $""")
            
            steps.append(f"""\n${implies}
             {lhs1_exponent}
              = {x_sign_str} {n2}
              $""")
            pass
        
        x_exponent = latex_exponent(x, n1)
        lhs2_exponent = latex_exponent(x_exponent, 
                                       n1_inv_frac, p_bracket = True)
        rhs2_exponent = latex_exponent(x_sign ** n1 * n2, n1_inv_frac, p_bracket = True)

        steps.append(f"\nAs ${x}$ has a power of {n1}, raise both sides to the power of ${n1_inv_frac}$")
        
        steps.append(f"""\n${implies}
         {lhs2_exponent}
          = {rhs2_exponent}
          $""")
        
        steps.append(f"""\n${implies}
         {x}
          = {rhs2_exponent}
          $""")
        
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x)
        answer_s = ", ".join(['$' +  latex(v) + '$'
                              for v in x_val_sympy])
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    def _get_eq_10(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(2, 6), 2)
        x_sign = random.choice([1, -1]) 
        n2_sign = random.choice([1, -1]) 
        
        
        n1_inv = Rational(1, n1)
        n1_inv_frac = latex(n1_inv)
        eq =  Eq((x_sign * x) ** n1_inv, n2_sign * n2)
        
        lhs_exponent = latex_exponent(x_sign * x, n1_inv_frac)
        eq_str = f"${lhs_exponent} = {n2_sign * n2}$"
        steps = [eq_str]

        lhs1_exponent = latex_exponent(lhs_exponent, n1, p_bracket = True)
        rhs1_exponent = latex_exponent(n2_sign * n2, n1, p_bracket = True)

        steps.append(f"""\n${implies} {lhs1_exponent} = {rhs1_exponent} $""")
        steps.append(f"""\n${implies} {x_sign * x} = {(n2_sign * n2) ** n1} $""")
        
        if x_sign == -1:
            steps.append(f"\nAs ${x}$ has negative sign, multiply both sides by $-1$")
            steps.append(f"""\n${implies} - {x_sign * x} = - {(n2_sign * n2) ** n1} $""")
            
            rhs2_exponent = - (n2_sign ** n1) * (n2 ** n1)
            steps.append(f"""\n${implies} {x} = {rhs2_exponent} $""")
            pass
        
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x)
        answer_s = ", ".join(['$' +  latex(v) + '$'
                              for v in x_val_sympy])
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass

    def _get_eq_11(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(2, 6), 2)
        n1 = -n1
        x_sign = random.choice([1, -1]) 
        eq =  Eq((x_sign * x) ** n1, n2)
        
        n1_inv = Rational(1, n1)
        n1_inv_frac = latex(n1_inv)
        
        lhs_exponent = latex_exponent(x_sign * x, n1)
        
        lhs1_exponent = latex_exponent(x, n1, p_bracket = True)
        if x_sign ** n1 >= 0:
            x_sign_str = f""
        else:
            x_sign_str = f"- "
            
        x_exponent = latex_exponent(x, n1)
        lhs2_exponent = latex_exponent(x_exponent, 
                                       n1_inv_frac, p_bracket = True)
        rhs2_exponent = latex_exponent(x_sign ** n1 * n2, n1_inv_frac, p_bracket = True)
            
            
        eq_str = f"${lhs_exponent} = {n2}$"

        steps = [eq_str]

        if x_sign == -1:
            steps.append(f"""\n${implies}
             {x_sign_str}{lhs1_exponent}
              = {n2}
              $""")
        
        if x_sign ** n1 == -1:
            steps.append(f"\nAs left hand side has negative sign, multiply both sides by $-1$")

            steps.append(f"""\n${implies}
             - {x_sign_str}{lhs1_exponent}
              = - {n2}
              $""")
            
            steps.append(f"""\n${implies}
             {lhs1_exponent}
              = {x_sign_str} {n2}
              $""")
            pass
        
        steps.append(f"\nAs ${x}$ has a power of {n1}, raise both sides to the power of ${n1_inv_frac}$")
        
        steps.append(f"""\n${implies}
         {lhs2_exponent}
          = {rhs2_exponent}
          $""")
        
        steps.append(f"""\n${implies}
         {x_sign_str}{x}
          = {rhs2_exponent}
          $""")
        
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x)
        answer_s = ", ".join(['$' +  latex(v) + '$'
                              for v in x_val_sympy])
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass

    def _get_eq_12(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(2, 10), 2)
        
        n2_sign = random.choice([1, -1]) 
        if n2_sign == -1:
            n2_latex = f"(- {n2})"
        else:
            n2_latex = f"{n2}"
        n2_pm = n2_sign * n2
        
        eq =  Eq(log(x)/log(n1), n2_sign * n2)
        
        eq_str = f"$log_{{{n1}}}{x} = {n2_pm} $"
        steps = [eq_str]
        
        steps.append(f"""\n {implies} $log_{{{n1}}}{x} 
        = {{{n2_latex}}} * {{ log_{{{n1}}}{n1} }}$ \t\t As $
        {{ log_{{{n1}}}{n1} }} = 1$
        """)
        
        steps.append(f"""{implies} $
        log_{{{n1}}}{x} = {{ log_{{{n1}}}{n1} }}^{{{n2_pm}}}$ \t\t Move  $
        {n2_pm}$ to exponent
        """)
        
        steps.append(f"{implies} ${x} = {n1}^{{{n2_pm}}}$ \t\t Cancel log on both sides")

        if n2_sign == -1:
            n2_frac = latex_fraction(1, f"{n1}^{{{n2}}}")
            steps.append(f"\n {implies} ${x} = {n2_frac}$")
            xval = Rational(1, n1 ** n2)
        else:
            xval = n1 ** n2
            
        steps.append(f"\n {implies} ${x} = {latex(xval)}$")

        problem_s = f"""
        Solve: {eq_str}
        
        Note: The same can be written as \t ${latex(eq)}$
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x, rational = True)
        answer_s = ", ".join([f"${latex(v)}$" for v in x_val_sympy])
        
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    def _get_eq_13(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(2, 10), 2)
        
        n2_inv = Rational(1, n2)
        n2_sign = random.choice([1, -1]) 
        
        n2_inv_latex = f"{latex(n2_inv)}"
        
        if n2_sign == -1:
            n2_latex = f"(- {n2})"
            n2_inv_pm_latex = f"- {latex(n2_inv)}"
        else:
            n2_latex = f"{n2}"
            n2_inv_pm_latex = f"{latex(n2_inv)}"

        n2_pm = n2_sign * n2
        n2_inv_pm = n2_sign * n2_inv
        
        eq =  Eq(log(x)/log(n1), n2_inv_pm)
        
        eq_str = f"$log_{{{n1}}}{x} = {n2_inv_pm_latex} $"
        steps = [eq_str]
        
        steps.append(f"""\n {implies} $log_{{{n1}}}{x} 
        = {{{n2_inv_pm_latex}}} * {{ log_{{{n1}}}{n1} }}$ \t\t As $
        {{ log_{{{n1}}}{n1} }} = 1$
        """)
        
        steps.append(f"""{implies} $
        log_{{{n1}}}{x} = {{ log_{{{n1}}}{n1} }}^{{{n2_inv_pm_latex}}}$ \t\t Move  $
        {n2_pm}$ to exponent
        """)
        
        steps.append(f"{implies} ${x} = {n1}^{{{n2_inv_pm_latex}}}$ \t\t Cancel log on both sides")

        if n2_sign == -1:
            n2_frac = latex_fraction(1, f"{n1}^{{{n2_inv_latex}}}")
            steps.append(f"\n {implies} ${x} = {n2_frac}$")
            xval = nsimplify(1/ n1 ** n2_inv)
        else:
            xval = n1 ** n2_inv
            
        steps.append(f"\n {implies} ${x} = {latex(xval)}$")

        problem_s = f"""
        Solve: {eq_str}
        
        Note: The same can be written as \t ${latex(eq)}$
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x, rational = True)
        answer_s = ", ".join([f"${latex(v)}$" for v in x_val_sympy])
        
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    def _get_eq_14(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(2, 10), 2)
        
        x_sign = random.choice([1, -1]) 
        n2_sign = random.choice([1, -1]) 
        x_pm = x_sign * x
        
        x_latex = f"{x}"
        if x_sign == -1:
            x_pm_latex = f"(- {x_latex})"
        else:
            x_pm_latex = x_latex
        
        if n2_sign == -1:
            n2_latex = f"(- {n2})"
        else:
            n2_latex = f"{n2}"
            
            
        n2_pm = n2_sign * n2
        
        eq =  Eq(log(x_pm)/log(n1), n2_sign * n2)
        
        eq_str = f"$log_{{{n1}}}{x_pm_latex} = {n2_pm} $"
        steps = [eq_str]
        
        steps.append(f"""\n {implies} $log_{{{n1}}}{x_pm_latex} 
        = {{{n2_latex}}} * {{ log_{{{n1}}}{n1} }}$ \t\t As $
        {{ log_{{{n1}}}{n1} }} = 1$
        """)
        
        steps.append(f"""{implies} $
        log_{{{n1}}}{x_pm_latex} = {{ log_{{{n1}}}{n1} }}^{{{n2_pm}}}$ \t\t Move  $
        {n2_pm}$ to exponent
        """)
        
        steps.append(f"{implies} ${x_pm_latex} = {n1}^{{{n2_pm}}}$ \t\t Cancel log on both sides")

        if n2_sign == -1:
            n2_frac = latex_fraction(1, f"{n1}^{{{n2}}}")
            steps.append(f"\n {implies} ${x_pm_latex} = {n2_frac}$")
            xval = Rational(1, n1 ** n2)
        else:
            xval = n1 ** n2
            
        steps.append(f"\n {implies} ${x_pm_latex} = {latex(xval)}$")

        if x_sign == -1:
            steps.append(f"\n {implies} $- {x_pm_latex} = - {latex(xval)}$ \t\t Multiply both sides with {x_sign}")
            xval = - xval
            steps.append(f"\n {implies} ${x} = {latex(xval)}$")
            pass
        
        problem_s = f"""
        Solve: {eq_str}
        
        Note: The same can be written as \t ${latex(eq)}$
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x, rational = True)
        answer_s = ", ".join([f"${latex(v)}$" for v in x_val_sympy])
        
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    def _get_eq_15(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(2, 10), 2)
        
        x_sign = random.choice([1, -1]) 
        n2_sign = random.choice([1, -1]) 
        
        x_pm = x_sign * x
        x_inv_pm = x_sign / x
        
        x_latex = f"{x}"
        x_inv_latex = latex_fraction(1, x)
        
        if x_sign == -1:
            x_pm_latex = f"\\left(- {x_latex}\\right)"
            x_inv_pm_latex = f"\\left(- {x_inv_latex}\\right)"
        else:
            x_pm_latex = x_latex
            x_inv_pm_latex = x_inv_latex
        
        if n2_sign == -1:
            n2_latex = f"(- {n2})"
        else:
            n2_latex = f"{n2}"
            
            
        n2_pm = n2_sign * n2
        
        eq =  Eq(log(x_inv_pm)/log(n1), n2_sign * n2)
        
        eq_str = f"$log_{{{n1}}}{x_inv_pm_latex} = {n2_pm} $"
        steps = [eq_str]
        
        steps.append(f"""\n {implies} $log_{{{n1}}}{x_inv_pm_latex} 
        = {{{n2_latex}}} * {{ log_{{{n1}}}{n1} }}$ \t\t As $
        {{ log_{{{n1}}}{n1} }} = 1$
        """)
        
        steps.append(f"""{implies} $
        log_{{{n1}}}{x_inv_pm_latex} = {{ log_{{{n1}}}{n1} }}^{{{n2_pm}}}$ \t\t Move  $
        {n2_pm}$ to exponent
        """)
        
        steps.append(f"{implies} ${x_inv_pm_latex} = {n1}^{{{n2_pm}}}$ \t\t Cancel log on both sides")

        if n2_sign == -1:
            n2_frac = latex_fraction(1, f"{n1}^{{{n2}}}")
            steps.append(f"\n {implies} ${x_inv_pm_latex} = {n2_frac}$")
            xval = Rational(1, n1 ** n2)
        else:
            xval = n1 ** n2
            
        steps.append(f"\n {implies} ${x_inv_pm_latex} = {latex(xval)}$")

        if x_sign == -1:
            steps.append(f"\n {implies} $- {x_inv_pm_latex} = - {latex(xval)}$ \t\t Multiply both sides with {x_sign}")
            xval = - xval
            steps.append(f"\n {implies} ${x_inv_latex} = {latex(xval)}$")
            pass
        
        xval = nsimplify(1 / xval)
        steps.append(f"\n {implies} ${x} = {latex(xval)}$ \t\t Inverse both sides")
        
        problem_s = f"""
        Solve: {eq_str}
        
        Note: The same can be written as \t ${latex(eq)}$
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x, rational = True)
        answer_s = ", ".join([f"${latex(v)}$" for v in x_val_sympy])
        
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    def _get_eq_16(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(2, 10), 2)
        eq =  Eq(n1 ** x, n2)
        
        eq_str = f"${n1}^{{{x}}}  = {n2}$"
        
        steps = [eq_str]
        steps.append(f"\n {implies} ${n1}^{{{x}}}  = {n2}$")
        steps.append(f"\n {implies} $log_{{{n1}}}{n1}^{{{x}}}  = log_{{{n1}}}{n2}$")
        steps.append(f"\n {implies} ${x}  = log_{{{n1}}}{n2}$")

        steps.append(f"\n It can also be written as")
        rhs_frac_latex = latex_fraction(f"{latex(log(n2))}",f"{latex(log(n1))}")
        steps.append(f"\n ${x}  = {rhs_frac_latex}$")
        
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x)
        answer_s = ", ".join([f"${latex(v)}$" for v in x_val_sympy])
        
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    def _get_eq_17(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(2, 10), 2)

        x_expr = x
        x_expr_latex = f"{x}"

        x_sign = random.choice([1, -1]) 
        is_x_inverse = random.choice([True, False]) 
        
        if is_x_inverse:
            x_expr = 1/x_expr
            #x_expr_latex = latex_fraction(1, x_expr_latex)
            x_expr_latex = latex(x_expr)
            
        if x_sign == -1:
            x_expr = - x_expr
            x_expr_latex = f"- {x_expr_latex}"
        
        
        n1_expr = n1
        n1_expr_latex = f"{n1}"
        
        is_n1_inverse = random.choice([True, False]) 
        
        if is_n1_inverse:
            n1_expr = 1/n1_expr
            n1_expr_latex = latex_fraction(1, n1_expr_latex)
            n1_expr_latex = f"\\left({n1_expr_latex}\\right)"

        n2_expr = n2
        n2_expr_latex = f"{n2}"
        
        n2_sign = random.choice([1, -1]) 
        is_n2_inverse = random.choice([True, False]) 
        
        if is_n2_inverse:
            n2_expr = 1/n2_expr
            n2_expr_latex = latex_fraction(1, n2_expr_latex)
            
        if n2_sign == -1:
            n2_expr = - n2_expr
            n2_expr_latex = f"- {n2_expr_latex}"

        if is_n2_inverse or n2_sign == -1:
            n2_expr_latex = f"\\left({n2_expr_latex}\\right)"

        eq =  Eq((n1_expr) ** (x_expr), n2_expr)
        
        eq_str = f"${n1_expr_latex}^{{{x_expr_latex}}}  = {n2_expr_latex}$"
        
        steps = [eq_str]
        steps.append(f"\n {implies} $ log_{{{n1_expr_latex}}}{{ {n1_expr_latex}^{{{x_expr_latex}}} }} =  log_{{{n1_expr_latex}}}{{ {n2_expr_latex} }}$")
        steps.append(f"\n {implies} ${x_expr_latex}  = log_{{{n1_expr_latex}}}{{ {n2_expr_latex} }}$")

        steps.append(f"\n It can also be written as")
        rhs_frac_latex = latex_fraction(f"log{{{n2_expr_latex}}}",f"log{{{n1_expr_latex}}}")
        steps.append(f"\n {implies} ${x_expr_latex}  = {rhs_frac_latex}$")

        if x_sign == -1:
            steps.append(f"\n Multipy $-1$ to both sides")
            steps.append(f"\n {implies} $- {x_expr_latex}  = - {rhs_frac_latex}$")
            
            if is_x_inverse:
                lhs_frac_latex =  latex_fraction(1, x)
            else:
                lhs_frac_latex =  x
            steps.append(f"\n {implies} ${lhs_frac_latex}  = - {rhs_frac_latex}$")
                
        if is_x_inverse:
            steps.append(f"\n Inverse both sides")
            if x_sign == -1:
                rhs_frac_latex = " - " + latex_fraction(f"log{{{n1_expr_latex}}}",f"log{{{n2_expr_latex}}}")
            else:
                rhs_frac_latex = latex_fraction(f"log{{{n1_expr_latex}}}",f"log{{{n2_expr_latex}}}")
                
            steps.append(f"\n {implies} ${x}  = {rhs_frac_latex}$")
            
        
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x)
        answer_s = ", ".join([f"${latex(v)}$" for v in x_val_sympy])
        
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    
    def _get_eq_SAMPLE(self, eq_type):
        x = self.x
        implies = self.implies
        n1, n2 = random.sample(range(1, 10), 2)
        eq =  Eq(x + n1, n2)
        
        eq_str = f""
        steps = [eq_str]
        steps.append(f"\n {implies} $$")
        
        problem_s = f"""
        Solve: {eq_str}
        """
        
        soln_s = f"\n".join(steps)

        x_val_sympy = solve(eq, x)
        answer_s = ", ".join([f"${latex(v)}$" for v in x_val_sympy])
        
        
        return {'eq_type': eq_type, 
                'eq': eq,
                'problem_s': problem_s,
                'soln_s': soln_s,
                'answer_s': answer_s
            }
        pass
    
    
    pass
#class SingleVariableEquationHelper:
