'''
Created on 01-Apr-2021
@author: bthakur
'''
import math
import os 
#import decimal
from functools import reduce
from xv.km.ui import SvgHelper 

import numpy as np
import random
import inspect
import traceback
from sympy import (simplify, sympify, log, symbols, Symbol,
                    Eq, Matrix, latex, 
                    UnevaluatedExpr as ueval,
                   solve, Rational, diag, 
                   Add, Mul, Pow, Matrix)

import sympy

from xv.km.util import latex_fraction, wrap_latex_statement
from xv.km._sympy_extension import CustomLogarithm as Log 
        
from xv.util import toIntOrFloat
from mpmath import rand


#from xv.km import ExtendedEq as CustomEq 
#from xv.km._sympy_extension import CustomLogarithm as Log 


from ...util import MathUtil
from ...helper import BaseHelper

from xv.km.kelements import KProblemTemplate, KPrintElement
from xv.km.managers import BaseManager
        
class SingleVariableManager(BaseManager):
    '''
    SingleVariableManager
    Example:
        ke = SingleVariableManager()
        ke.getRandomProblem()
        ke.getRandomProblem(problem_type = 0)
        ...
        
        ke.printProblem()
        ke.printAnswer()
        ke.printSolution()
        
        ke.printProblemTypes()
        
    '''    
    _OPR_TYPE_COUNT = 10
    
    def __new__(cls, *args, **kwargs):
        instance = super(SingleVariableManager, cls).__new__(cls, *args, **kwargs)
        for base_class in SingleVariableManager.__bases__:
            base_class.__init__(instance, *args, **kwargs)
        return instance
    
    def __init__(self, *args, **kwargs):
        self.base_helper = BaseHelper(**kwargs)
        self._equation = None
        self.x = symbols('x')
        
        self.svh = self.base_helper.get_algebra_helper().get_single_variable_helper()
        pass
    

    def _init_problem_types(self, *args, **kwargs):
        self._append_problem_types([
            '_problem_write_expression',
            '_problem_write_expression_advanced',
            '_problem_write_expression_advanced_2',

            '_problem_add',
            '_problem_subtract',
            '_problem_multiply',
            '_problem_divide',
            '_problem_pow',
            '_problem_reverse_pow',
            '_problem_log',
            '_problem_reverse_log',
            
            '_problem_solve_equations_algebaic',

            '_problem_solve_equations_easy_level_1',
            '_problem_solve_equations_easy_level_2',
            '_problem_solve_equations_easy_level_3',
            '_problem_solve_equations_easy_level_4',
            '_problem_solve_equations_easy_level_5',
            '_problem_solve_equations_easy_level_6',
            '_problem_solve_equations_easy_level_7',
            
            ])
        pass

    def _problem_write_expression(self, *args, **kwargs):
        x = self.x
        expressions = [[f"The expression is", x, latex(x)]]

        opr_types = list(range(1, SingleVariableManager._OPR_TYPE_COUNT + 1))
        opr_types = random.sample(opr_types, random.randint(1, len(opr_types)))
        for opr_type in opr_types:
            self.svh.add_expression(expressions, opr_type)
            
        (problem_arr_str, 
         solution_arr_str,
         answer_str) = self.svh.get_prob_soln_str(expressions)
                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        statement = problem_arr_str.strip()
        problemTemplate.add_problem(statement, "text/html")
        
        soln_s = f"""
        {solution_arr_str}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


        problemTemplate.add_answer(answer_str, 'text/html')
        
        return problemTemplate

    def _problem_write_expression_advanced(self, *args, **kwargs):
        x = self.x
        expressions = [[f"The expression is", x, latex(x)]]

        opr_types = list(range(1, SingleVariableManager._OPR_TYPE_COUNT + 1))
        random.shuffle(opr_types)
        for opr_type in opr_types:
            self.svh.add_expression(expressions, opr_type)
            
        startIndex = random.randint(0, len(expressions) - 3)
        expressions = expressions[startIndex:]
        
        if len(expressions) > 3:
            endIndex = random.randint(3, len(expressions))
            expressions = expressions[:endIndex]
            
            
        (problem_arr_str, 
         solution_arr_str,
         answer_str) = self.svh.get_prob_soln_str(expressions)
                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        statement = problem_arr_str.strip()
        problemTemplate.add_problem(statement, "text/html")
        
        soln_s = f"""
        {solution_arr_str}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


        problemTemplate.add_answer(answer_str, 'text/html')
        
        return problemTemplate

    def _problem_write_expression_advanced_2(self, *args, **kwargs):
        x = self.x
        expressions = [[f"The expression is", x, latex(x)]]

        opr_types = list(range(1, SingleVariableManager._OPR_TYPE_COUNT + 1))
        opr_types = random.sample(opr_types, 5)
        for opr_type in opr_types:
            self.svh.add_expression(expressions, opr_type)

        temp_expressions = random.sample(expressions, 1)

        n = random.choice(expressions)
        
        self.svh.add_expression(temp_expressions, 
                             opr_type =  random.choice(opr_types),
                             n = n[1]
                             )

        if random.choice([True, False]):
            n = random.choice(expressions)
            self.svh.add_expression(temp_expressions, 
                                 opr_type =  random.choice(opr_types),
                                 n = n[1]
                                 )

        
        (problem_arr_str, 
         solution_arr_str,
         answer_str) = self.svh.get_prob_soln_str(temp_expressions)
                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        statement = problem_arr_str.strip()
        problemTemplate.add_problem(statement, "text/html")
        
        soln_s = f"""
        {solution_arr_str}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        problemTemplate.add_answer(answer_str, 'text/html')
        
        return problemTemplate



    def _problem_add(self, *args, **kwargs):
        x = symbols('x', real = True)
        a = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
        b = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
        
        b = b * random.choice([-1, 1])
 
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        eq = Eq(x + a, b)
        eq_answer = solve(eq, x)[0]

        is_side_switched = random.choice([True, False])
        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)

        is_reversed = random.choice([True, False])
        if is_reversed:
            eq = Eq(1 / eq.lhs, 1 / eq.rhs)

        soln_items = []

        problem_s = f"""
        Solve the following:
        $\\displaystyle {latex(eq)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_item = f"""
        $\\displaystyle {latex(eq)}$
        """
        soln_items.append(soln_item.strip())

        if is_reversed:
            eq = Eq(1 / eq.lhs, 1 / eq.rhs)

            soln_item = f"""
            $\\displaystyle \\implies {latex(eq)}
            \\qquad \\text{{Inverse both sides to move }} {latex(x)} \\text{{ to numerator.}}
            $
            """
            soln_items.append(soln_item.strip())
            pass

        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq)}
            \\qquad \\text{{Switch sides to move }} {latex(x)} \\text{{ to the left.}}
            $
            """
            soln_items.append(soln_item.strip())
            pass

        if eq.lhs == x:
            eq_soln = eq
        else:
            eq_soln_step_0 = Eq(Symbol(f"{latex(x + a)} - {latex(a)}"), 
                                Symbol(f"{latex(b)} - {latex(a)}"))
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq_soln_step_0)}
            \\qquad 
            {latex(a)} \\text{{ is added to }} {latex(x)} 
            \\text{{, to remove it, subtract }} {latex(a)} 
            \\text{{ to both sides}} 
            $
            
            """
            soln_items.append(soln_item.strip())
    
    
            eq_soln = Eq(x, Rational(b - a))
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq_soln)}$
            """
            soln_items.append(soln_item.strip())
            pass
        
        if eq_answer != eq_soln.rhs:
            soln_item = f"""
            The answer is wrong. It should be $\\displaystyle {latex(eq_answer)}$
            """
            soln_items.append(soln_item.strip())
            pass
        
        soln_s = "\n\n".join(soln_items)
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {latex(eq_soln.rhs)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

   
    
    def _problem_subtract(self, *args, **kwargs):
        x = symbols('x', real = True)
        a = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
        b = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
        
        b = b * random.choice([-1, 1])
 
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        eq = Eq(x - a, b)
        eq_answer = solve(eq, x)[0]

        is_side_switched = random.choice([True, False])
        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)

        is_reversed = random.choice([True, False])
        if is_reversed:
            eq = Eq(1 / eq.lhs, 1 / eq.rhs)

        soln_items = []

        problem_s = f"""
        Solve the following:
        $\\displaystyle {latex(eq)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_item = f"""
        $\\displaystyle {latex(eq)}$
        """
        soln_items.append(soln_item.strip())

        if is_reversed:
            eq = Eq(1 / eq.lhs, 1 / eq.rhs)

            soln_item = f"""
            $\\displaystyle \\implies {latex(eq)}
            \\qquad \\text{{Inverse both sides to move }} {latex(x)} \\text{{ to numerator.}}
            $
            """
            soln_items.append(soln_item.strip())
            pass

        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq)}
            \\qquad \\text{{Switch sides to move }} {latex(x)} \\text{{ to the left.}}
            $
            """
            soln_items.append(soln_item.strip())
            pass

        if eq.lhs == x:
            eq_soln = eq
        else:
            eq_soln_step_0 = Eq(Symbol(f"{latex(x - a)} + {latex(a)}"), 
                                Symbol(f"{latex(b)} + {latex(a)}"))
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq_soln_step_0)}
            \\qquad 
            {latex(a)} \\text{{ is subtracted from }} {latex(x)} 
            \\text{{, to remove it, add }} {latex(a)} 
            \\text{{ to both sides}} 
            $
            
            """
            soln_items.append(soln_item.strip())
    
    
            eq_soln = Eq(x, Rational(b + a))
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq_soln)}$
            """
            soln_items.append(soln_item.strip())
            pass
        
        if eq_answer != eq_soln.rhs:
            soln_item = f"""
            The answer is wrong. It should be $\\displaystyle {latex(eq_answer)}$
            """
            soln_items.append(soln_item.strip())
            pass
        
        soln_s = "\n\n".join(soln_items)
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {latex(eq_soln.rhs)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
   
   
    def _problem_multiply(self, *args, **kwargs):
        x = symbols('x', real = True)
        a = random.randint(2, 9)
        b = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
        
        a = a * random.choice([-1, 1])
        b = b * random.choice([-1, 1])
 
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        eq = Eq(x * a, b)
        eq_answer = solve(eq, x)[0]

        is_side_switched = random.choice([True, False])
        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)

        is_reversed = random.choice([True, False])
        if is_reversed:
            eq = Eq(1 / eq.lhs, 1 / eq.rhs)

        soln_items = []

        problem_s = f"""
        Solve the following:
        $\\displaystyle {latex(eq)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_item = f"""
        $\\displaystyle {latex(eq)}$
        """
        soln_items.append(soln_item.strip())

        if is_reversed:
            eq = Eq(1 / eq.lhs, 1 / eq.rhs)

            soln_item = f"""
            $\\displaystyle \\implies {latex(eq)}
            \\qquad \\text{{Inverse both sides to move }} {latex(x)} \\text{{ to numerator.}}
            $
            """
            soln_items.append(soln_item.strip())
            pass

        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq)}
            \\qquad \\text{{Switch sides to move }} {latex(x)} \\text{{ to the left.}}
            $
            """
            soln_items.append(soln_item.strip())
            pass

        if eq.lhs == x:
            eq_soln = eq
        else:
            inv_a = Rational(1, a)
            eq_soln_step_0 = Eq(Symbol(f"{latex(x * a)} \\cdot {latex(inv_a)}"), 
                                Symbol(f"{latex(b)} \\cdot {latex(inv_a)}"))
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq_soln_step_0)}
            \\qquad 
            {latex(x)} \\text{{ is multiplied by }} {latex(a)} 
            \\text{{, to remove it, multiply both sides with its inverse }} {latex(inv_a)} 
            $
            
            """
            soln_items.append(soln_item.strip())
    
    
            eq_soln = Eq(x, Rational(b / a))
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq_soln)}$
            """
            soln_items.append(soln_item.strip())
            pass
        
        if eq_answer != eq_soln.rhs:
            soln_item = f"""
            The answer is wrong. It should be $\\displaystyle {latex(eq_answer)}$
            """
            soln_items.append(soln_item.strip())
            pass
        
        soln_s = "\n\n".join(soln_items)
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {latex(eq_soln.rhs)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
   
   
    def _problem_divide(self, *args, **kwargs):
        x = symbols('x', real = True)
        a = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
        b = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
        
        a = a * random.choice([-1, 1])
        b = b * random.choice([-1, 1])
 
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        eq = Eq(x/a, b)
        eq_answer = solve(eq, x)[0]

        is_side_switched = random.choice([True, False])
        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)

        is_reversed = random.choice([True, False])
        if is_reversed:
            eq = Eq(1 / eq.lhs, 1 / eq.rhs)

        soln_items = []

        problem_s = f"""
        Solve the following:
        $\\displaystyle {latex(eq)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_item = f"""
        $\\displaystyle {latex(eq)}$
        """
        soln_items.append(soln_item.strip())

        if is_reversed:
            eq = Eq(1 / eq.lhs, 1 / eq.rhs)

            soln_item = f"""
            $\\displaystyle \\implies {latex(eq)}
            \\qquad \\text{{Inverse both sides to move }} {latex(x)} \\text{{ to numerator.}}
            $
            """
            soln_items.append(soln_item.strip())
            pass

        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq)}
            \\qquad \\text{{Switch sides to move }} {latex(x)} \\text{{ to the left.}}
            $
            """
            soln_items.append(soln_item.strip())
            pass


        if eq.lhs == x:
            eq_soln = eq
        else:
            eq_soln_step_0 = Eq(Symbol(f"{latex(x/a)} \\cdot {latex(a)}"), 
                                Symbol(f"{latex(b)} \\cdot {latex(a)}"))
            
            if a == int(a):
                soln_item = f"""
                $\\displaystyle \\implies {latex(eq_soln_step_0)}
                \\qquad 
                {latex(x)} \\text{{ is divided by }} {latex(a)} 
                \\text{{, to remove it, multiply both sides with }} {latex(a)} 
                $
                """
                soln_items.append(soln_item.strip())
            else:
                inv_a = Rational(1, a)
    
                soln_item = f"""
                $\\displaystyle \\implies {latex(eq_soln_step_0)}
                \\qquad 
                {latex(x)} \\text{{ is multiplied by }} {latex(inv_a)} 
                \\text{{, to remove it, multiply both sides with it inverse }} {latex(a)} 
                $
                """
                soln_items.append(soln_item.strip())
                pass
    
    
            eq_soln = Eq(x, Rational(b * a))
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq_soln)}$
            """
            soln_items.append(soln_item.strip())
            pass
        
        if eq_answer != eq_soln.rhs:
            soln_item = f"""
            The answer is wrong. It should be $\\displaystyle {latex(eq_answer)}$
            """
            soln_items.append(soln_item.strip())
            pass
        
        soln_s = "\n\n".join(soln_items)
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {latex(eq_soln.rhs)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    def _problem_pow(self, *args, **kwargs):
        x = symbols('x', real = True)
        a = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
        b = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
 
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        eq = Eq(x**a, b)
        eq_answer = solve(eq, x)[0]

        is_side_switched = random.choice([True, False])
        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)

        soln_items = []

        problem_s = f"""
        Solve the following:
        $\\displaystyle {latex(eq)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_item = f"""
        $\\displaystyle {latex(eq)}$
        """
        soln_items.append(soln_item.strip())

        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq)}
            \\qquad \\text{{Switch sides to move }} {latex(x)} \\text{{ to the left.}}
            $
            """
            soln_items.append(soln_item.strip())
            pass

        eq_soln_step_0 = Eq((ueval(x**a))**ueval(1/a), 
                            b ** ueval(1/a))
        
        eq_soln_step_1 = Eq(x ** (ueval(a) * ueval(1/a)), 
                            b ** ueval(1/a))
        
        
        
        soln_item = f"""
        $\\displaystyle \\implies {latex(eq_soln_step_0)}
        \\qquad \\text{{Raise both sides with power of }} {latex(1/a)} 
        \\text{{ to cancel power of the lhs.}}
        $
        
        $\\displaystyle \\implies {latex(eq_soln_step_1)}
        \\qquad \\text{{Power to power is multiplied together.}}
        $
        
        """
        soln_items.append(soln_item.strip())


        eq_soln = Eq(x, ueval(b) ** ueval(1/a))
        
        soln_item = f"""
        $\\displaystyle \\implies {latex(eq_soln)}$
        """
        soln_items.append(soln_item.strip())

        
        if eq_answer != eq_soln.rhs:
            soln_item = f"""
            The answer is wrong. It should be $\\displaystyle {latex(eq_answer)}$
            """
            #soln_items.append(soln_item.strip())
            pass
        
        soln_s = "\n\n".join(soln_items)
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {latex(eq_soln.rhs)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
   

   
    def _problem_reverse_pow(self, *args, **kwargs):
        x = symbols('x', real = True)
        a = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
        b = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
 
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        eq = Eq(a**x, b)
        eq_answer = solve(eq, x)[0]

        is_side_switched = random.choice([True, False])
        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)

        soln_items = []

        problem_s = f"""
        Solve the following:
        $\\displaystyle {latex(eq)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_item = f"""
        $\\displaystyle {latex(eq)}$

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

        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq)}
            \\qquad \\text{{Switch sides to move }} {latex(x)} \\text{{ to the left.}}
            $
            """
            soln_items.append(soln_item.strip())
            pass

        eq_soln_step_0 = Eq(Symbol(f"{Log(eq.lhs, a)}"), 
                            Symbol(f"{Log(eq.rhs, a)}")
                            )

        eq_soln_step_1 = Eq(Symbol(f"{latex(x)} \\cdot {Log(a, a)}"), 
                            Symbol(f"{Log(eq.rhs, a)}")
                            )

        
        soln_item = f"""
        $\\displaystyle \\implies {latex(eq_soln_step_0)}
        \\qquad \\text{{Add log on both sides with base }} {latex(a)}
        $

        $\\displaystyle \\implies {latex(eq_soln_step_1)}
        \\qquad \\text{{Power of the log number can be switched to the multiple of the log expression.}}
        $
        """
        soln_items.append(soln_item.strip())


        eq_soln = Eq(x, Symbol(f"{Log(eq.rhs, a)}"))
        
        soln_item = f"""
        $\\displaystyle \\implies {latex(eq_soln)}$
        """
        soln_items.append(soln_item.strip())

        
        if eq_answer != eq_soln.rhs:
            soln_item = f"""
            The answer is wrong. It should be $\\displaystyle {latex(eq_answer)}$
            """
            #soln_items.append(soln_item.strip())
            pass
        
        soln_s = "\n\n".join(soln_items)
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {latex(eq_soln.rhs)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
   
    def _problem_log(self, *args, **kwargs):
        x = symbols('x', real = True)
        a = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
        b = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
 
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        eq = Eq(Symbol(f"{Log(x, a)}"), b)

        is_side_switched = random.choice([True, False])
        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)

        soln_items = []

        problem_s = f"""
        Solve the following:
        $\\displaystyle {latex(eq)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_item = f"""
        $\\displaystyle {latex(eq)}$

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

        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq)}
            \\qquad \\text{{Switch sides to move }} {latex(x)} \\text{{ to the left.}}
            $
            """
            soln_items.append(soln_item.strip())
            pass

        eq_soln_step_0 = Eq(Symbol(f"{Log(x, a)}"), 
                            Symbol(f"{latex(b)} \\cdot {Log(a, a)}"))

        eq_soln_step_1 = Eq(Symbol(f"{Log(x, a)}"), 
                            Symbol(f"{Log(a ** ueval(b), a)}") )


        
        soln_item = f"""
        $\\displaystyle \\implies {latex(eq_soln_step_0)}
        \\qquad \\text{{If base and number of a log expression are same, its value is 1}}
        $

        $\\displaystyle \\implies {latex(eq_soln_step_1)}
        \\qquad \\text{{Multiple of a log expression can be switched to the power of the log number.}}
        $
        """
        soln_items.append(soln_item.strip())


        eq_soln = Eq(x, a ** ueval(b))

        
        soln_item = f"""
        $\\displaystyle \\implies {latex(eq_soln)}
        \\qquad \\text{{Remove the log from both sides.}}
        $
        """
        soln_items.append(soln_item.strip())

        
        soln_s = "\n\n".join(soln_items)
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {latex(eq_soln.rhs)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
   
    def _problem_reverse_log(self, *args, **kwargs):
        x = symbols('x', real = True)
        a = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
        b = Rational(random.randint(2, 9), random.choice([1, random.randint(2, 9)]))
 
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        eq = Eq(Symbol(f"{Log(a, x)}"), b)

        is_side_switched = random.choice([True, False])
        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)

        soln_items = []

        problem_s = f"""
        Solve the following:
        $\\displaystyle {latex(eq)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_item = f"""
        $\\displaystyle {latex(eq)}$

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

        if is_side_switched:
            eq = Eq(eq.rhs, eq.lhs)
            
            soln_item = f"""
            $\\displaystyle \\implies {latex(eq)}
            \\qquad \\text{{Switch sides to move }} {latex(x)} \\text{{ to the left.}}
            $
            """
            soln_items.append(soln_item.strip())
            pass

        b = Rational(1/b)
        eq_soln_step_0 = Eq(Symbol(f"{Log(x, a)}"), b)

        eq_soln_step_1 = Eq(Symbol(f"{Log(x, a)}"), 
                            Symbol(f"{latex(b)} \\cdot {Log(a, a)}"))

        eq_soln_step_2 = Eq(Symbol(f"{Log(x, a)}"), 
                            Symbol(f"{Log(a ** ueval(b), a)}") )


        
        soln_item = f"""
        $\\displaystyle 
        \\implies {latex(eq_soln_step_0)}
        \\qquad \\text{{If we reverse the base and number of a log expression, the value is also reversed.}}
        $

        $\\displaystyle \\implies {latex(eq_soln_step_1)}
        \\qquad \\text{{If base and number of a log expression are same, its value is 1}}
        $

        $\\displaystyle \\implies {latex(eq_soln_step_2)}
        \\qquad \\text{{Multiple of a log expression can be switched to the power of the log number.}}
        $
        """
        soln_items.append(soln_item.strip())


        eq_soln = Eq(x, a ** ueval(b))

        
        soln_item = f"""
        $\\displaystyle \\implies {latex(eq_soln)}
        \\qquad \\text{{Remove the log from both sides.}}
        $
        """
        soln_items.append(soln_item.strip())

        
        soln_s = "\n\n".join(soln_items)
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {latex(eq_soln.rhs)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    def _problem_solve_equations_algebaic(self, *args, **kwargs):
        sv_helper = self.base_helper.get_algebra_helper().get_single_variable_helper()

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        has_num_values = False
        if random.choice([True, False]):
            result = sv_helper.get_multi_level_equation_solution(
                        has_num_values = has_num_values)
        else:
            result = sv_helper.get_multi_level_equation_type_2_solution(
                        has_num_values = has_num_values)
            pass

        soln_dict_list = result['soln_dict_list']
        answer_s = result['answer_s']
        soln_dict_list = soln_dict_list[1:]

        problem_s = f"""
        Solve the following:
        $\\displaystyle {latex(soln_dict_list[0]['prob_eq'])}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_items = [item['soln_item'] for item in soln_dict_list
                      if item['soln_item']]
        
        soln_items.insert(0, f"$\\displaystyle {latex(soln_dict_list[0]['prob_eq'])}$")
        
        soln_s = "\n\n".join(soln_items)
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
   

    def _TEMP_SOLVE_EQUATIONS_EASY_LEVELS(self, *args, **kwargs):
        '''
        '''
        sv_helper = self.base_helper.get_algebra_helper().get_single_variable_helper()
        level = kwargs.get('level', 1)
        is_type2_prob = kwargs.get('is_type2_prob', False)

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        has_num_values = True
        
        if not is_type2_prob:
            result = sv_helper.get_multi_level_equation_solution(
                has_num_values = has_num_values)
        else:
            # this code has issue
            result = sv_helper.get_multi_level_equation_type_2_solution(
                has_num_values = has_num_values)
            pass
        
        soln_dict_list = result['soln_dict_list']
        answer_s = result['answer_s']
        soln_dict_list = soln_dict_list[level: ]
        
        self.eq = soln_dict_list[0]['prob_eq']

        problem_s = f"""
        <h6>Easy level: {level}</h6>
        Solve the following:
        $\\displaystyle {latex(soln_dict_list[0]['prob_eq'])}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_items = [item['soln_item'] for item in soln_dict_list
                      if item['soln_item']]
        
        soln_items.insert(0, f"$\\displaystyle {latex(soln_dict_list[0]['prob_eq'])}$")
        
        soln_s = "\n\n".join(soln_items)
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    def _problem_solve_equations_easy_level_1(self, *args, **kwargs):
        kwargs['level'] = 1
        return self._TEMP_SOLVE_EQUATIONS_EASY_LEVELS(*args, **kwargs)
        pass
        
    def _problem_solve_equations_easy_level_2(self, *args, **kwargs):
        kwargs['level'] = 2
        return self._TEMP_SOLVE_EQUATIONS_EASY_LEVELS(*args, **kwargs)
        pass
        
    def _problem_solve_equations_easy_level_3(self, *args, **kwargs):
        kwargs['level'] = 3
        return self._TEMP_SOLVE_EQUATIONS_EASY_LEVELS(*args, **kwargs)
        pass
        
    def _problem_solve_equations_easy_level_4(self, *args, **kwargs):
        is_type2_prob = random.choice([True, False])
        kwargs['is_type2_prob'] = is_type2_prob
 
        if is_type2_prob:
            kwargs['level'] = 1
        else:
            kwargs['level'] = 4
            pass
        
        return self._TEMP_SOLVE_EQUATIONS_EASY_LEVELS(*args, **kwargs)
        pass
        
    def _problem_solve_equations_easy_level_5(self, *args, **kwargs):
        is_type2_prob = random.choice([True, False])
        kwargs['is_type2_prob'] = is_type2_prob
 
        if is_type2_prob:
            kwargs['level'] = 2
        else:
            kwargs['level'] = 5
            pass
        
        return self._TEMP_SOLVE_EQUATIONS_EASY_LEVELS(*args, **kwargs)
        pass
        
    def _problem_solve_equations_easy_level_6(self, *args, **kwargs):
        is_type2_prob = random.choice([True, False])
        kwargs['is_type2_prob'] = is_type2_prob
 
        if is_type2_prob:
            kwargs['level'] = 3
        else:
            kwargs['level'] = 6
            pass
        
        return self._TEMP_SOLVE_EQUATIONS_EASY_LEVELS(*args, **kwargs)
        pass
        
    def _problem_solve_equations_easy_level_7(self, *args, **kwargs):
        is_type2_prob = random.choice([True, False])
        kwargs['is_type2_prob'] = is_type2_prob
 
        if is_type2_prob:
            kwargs['level'] = 4
        else:
            kwargs['level'] = 7
            pass
        
        return self._TEMP_SOLVE_EQUATIONS_EASY_LEVELS(*args, **kwargs)
        pass
        
