'''
Created on 01-Apr-2021
@author: bthakur

'''

from xv.util import counting_words

import inspect
import traceback

import random
from xv.km.util import latex_fraction

from xv.util import toIntOrFloat

from sympy import (symbols, Symbol, Function,
                   latex, Eq, Matrix, latex, sympify,
                   oo)
import sympy
import numpy as np
import random


from ...helper import BaseHelper
from xv.km.managers import BaseManager
from xv.km.kelements import KProblemTemplate, KPrintElement
from sympy.core.expr import UnevaluatedExpr
from xv.util.functions._util_functions import list_to_str
        
class AlgebricExpressionManager(BaseManager):
        
    '''
    Arithmetic operation of Algebric expressions 


    Minimum Grade: 3
    Maximum Grade: 7


    Examples
    --------
    ke = AlgebricExpressionManager()
    ke 
    
    ke.printProblemTypes()
    ke.helpProblemType(0)
    
    ke.getRandomProblem()
    ke.getRandomProblem(problem_type = 0)


    ke.printProblem()
    ke.printAnswer()
    ke.printSolution()


    doc_style: xv_doc
    ''' 

    
    def __new__(cls, *args, **kwargs):
        instance = super(AlgebricExpressionManager, cls).__new__(cls, *args, **kwargs)
        for base_class in AlgebricExpressionManager.__bases__:
            base_class.__init__(instance, *args, **kwargs)
        return instance
    
    def __init__(self, *args, **kwargs):
        self.base_helper = BaseHelper(**kwargs)
        pass


    def _init_problem_types(self, *args, **kwargs):
        self._append_problem_types(
            [
                ['_problem_add',
                 'Add two simple agberic expressions'],
                '_problem_add_advanced',
                '_problem_subtract',
                '_problem_subtract_advanced',
                '_problem_multiple_subtracts',
                '_problem_multiply',
                '_problem_multiply_advanced',
                '_problem_divide',
                '_problem_divide_advanced_1',
                '_problem_divide_advanced_2',
                '_problem_divide_advanced_3',
                '_problem_division_with_zero',
                '_problem_power_with_zero',
                '_problem_abs_values',
                
            ]
        )
        pass
    
    def _problem_add(self, *args, **kwargs):
        """
        Add two algebric expressions 


        Minimum Grade: 3
        Maximum Grade: 10

    
        Examples
        --------
        from xv.math.algebra import AlgebricExpressionManager
        ke = AlgebricExpressionManager()


        ke.getRandomProblem(problem_type = '_problem_add')


        doc_style: xv_doc
        """
        aeh = self.base_helper.get_algebra_helper().get_algebric_expression_helper()
        
        A, X, B, eqs, expression = aeh.get_random_matrix_data(dimY = 1)
        prob_expr = aeh.get_latex_str(expression)
        
        prob_expr_str = list_to_str(prob_expr, sep = f" , \\quad ")
        prob_expr_str = f"$\\displaystyle {prob_expr_str}$"
        
        expr_str = list_to_str(prob_expr, sep = f" \\; + \\; ")
        expr_str = f"$\\displaystyle {expr_str}$"

        A_arr = A.T.tolist()[0]
        X_var = X.T.tolist()[0][0]
        
        A_expr_str = [item for item in A_arr if item != 0]
        A_expr_str = list_to_str(A_expr_str, sep = f" \\; + \\; ")
        A_expr_str = f"$\\displaystyle {A_expr_str}$"


        
        A_col_result = Matrix(np.array(A).sum(axis=0)).T

        soln_expr = aeh.getExpressions(A_col_result, X)
        
        answer = aeh.get_latex_str(soln_expr)
        answer_str = "\n".join(answer)

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        statement = f"""
            Add the following expressions:
            {prob_expr_str}
            """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        soln_s = f"""
        {expr_str}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        if 0 in A_arr:
            soln_s = f"""
            As zeros does not change the result of a sum, we will ignore them.
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        soln_s = f"""
        <br/>As all the expression are multiples of ${latex(X_var)}$,
        we can take it as common and add the numbers.
        = ({A_expr_str}) ${latex(X_var)}$
        
        = ${answer_str}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        problemTemplate.add_answer(f"${answer_str}$", 'text/html')
        return problemTemplate
        pass
   
    def _problem_add_advanced(self, *args, **kwargs):
        """
        Add multiple algebric expressions 


        Minimum Grade: 3
        Maximum Grade: 10

    
        Examples
        --------
        from xv.math.algebra import AlgebricExpressionManager
        ke = AlgebricExpressionManager()


        ke.getRandomProblem(problem_type = '_problem_add_advanced')


        doc_style: xv_doc
        """
        aeh = self.base_helper.get_algebra_helper().get_algebric_expression_helper()
        

        A, X, B, eqs, expression = aeh.get_random_matrix_data()
        prob_expr = aeh.get_latex_str(expression)
        prob_expr_str = "\n".join([
            f"${item}$" for item in prob_expr 
            ])

        A_with_X = A.row_insert(0, X.T)

        A_col_result = Matrix(np.array(A).sum(axis=0)).T
        A_col_result_with_X = A_col_result.row_insert(0, X.T)

        soln_expr = aeh.getExpressions(A_col_result, X)
        
        answer = aeh.get_latex_str(soln_expr)
        answer_str = "\n".join(answer)

        implies = symbols(u'\U000021D2')

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        statement = f"""
            Add the following expressions:
            {prob_expr_str}
            """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        var_str = ", ".join([f"${latex(v)}$" for v in  X.T.tolist()[0]])
        
        soln_s = f"""
        We have to add:
        {prob_expr_str}
        
        The coefficients of variables {var_str}  are:
        ${latex(A_with_X)}$
        
        Add the columns:
        ${latex(A_col_result_with_X)}$
        
        {implies} The sum of expressions:
        = ${answer_str}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        
        problemTemplate.add_answer(f"${answer_str}$", 'text/html')
        return problemTemplate
        pass

    def _problem_subtract(self, *args, **kwargs):
        """
        Subtract one algebric expression from another


        Minimum Grade: 3
        Maximum Grade: 10

    
        Examples
        --------
        from xv.math.algebra import AlgebricExpressionManager
        ke = AlgebricExpressionManager()


        ke.getRandomProblem(problem_type = '_problem_subtract')


        doc_style: xv_doc
        """
        aeh = self.base_helper.get_algebra_helper().get_algebric_expression_helper()
        

        A, X, B, eqs, expression = aeh.get_random_matrix_data(dimX = 2, dimY = 1)
        
        expr_order = [0, 1]
        random.shuffle(expr_order)
        expr_order = [(item, counting_words(item))
            for item in expr_order
            ]

        prob_expr = aeh.get_latex_str(expression)
        prob_expr_str = ", ".join([
            f"${item}$" for item in prob_expr 
            ])

        A_with_X = A.row_insert(0, X.T)
        #change sign of first 
        A_modified = A.copy()
        A_modified.row_op(expr_order[0][0], lambda v, j:  v * -1)
        A_modified_with_X = A_modified.row_insert(0, X.T)
        
        

        A_col_result = Matrix(np.array(A_modified).sum(axis=0)).T
        A_col_result_with_X = A_col_result.row_insert(0, X.T)

        soln_expr = aeh.getExpressions(A_col_result, X)
        
        answer = aeh.get_latex_str(soln_expr)
        answer_str = "\n".join(answer)

        implies = symbols(u'\U000021D2')

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        
        statement = f"""
            Subtract {expr_order[0][1]} expression from the {expr_order[1][1]}:
            {prob_expr_str}
            """
        problemTemplate.add_problem(statement.strip(), "text/html")

        A_arr = A.T.tolist()[0]
        X_var = X.T.tolist()[0][0]

        if expr_order[0][0] == 0:
            expr_str =f"""
            $- {prob_expr[0]}$ $+ {prob_expr[1]}$
            """
            A_expr_str =f"""
            $- {A_arr[0]}$ $+ {A_arr[1]}$
            """
        else:
            expr_str =f"""
            ${prob_expr[0]}$ $- {prob_expr[1]}$
            """
            A_expr_str =f"""
            ${A_arr[0]}$ $- {A_arr[1]}$
            """
        
        
        soln_stmts = [] 
        soln_stmt = f"""
        We have 
        <br/>
        {prob_expr_str}
        <br/>
        <br/>
        As we have to subtract {expr_order[0][1]} expression, 
        put a negative $(-)$ sign before it and add all expressions.
        <br/>
        {expr_str}
        <br/>
        <br/>
        As all the expression are multiples of ${latex(X_var)}$,
        we can take it as common and add the numbers.
        <br/>
        = ({A_expr_str.strip()}) ${latex(X_var)}$
        <br/>
        = ${answer_str}$
        """
        soln_stmts.append(soln_stmt.replace("\n", " ").strip())
        
        
        soln_s = "\n\n".join(soln_stmts)
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        problemTemplate.add_answer(f"${answer_str}$", 'text/html')
        return problemTemplate
        pass
   
   
    def _problem_subtract_advanced(self, *args, **kwargs):
        """
        Subtract one a set of algebric expression from another set


        Minimum Grade: 3
        Maximum Grade: 10

    
        Examples
        --------
        from xv.math.algebra import AlgebricExpressionManager
        ke = AlgebricExpressionManager()


        ke.getRandomProblem(problem_type = '_problem_subtract_advanced')


        doc_style: xv_doc
        """
        aeh = self.base_helper.get_algebra_helper().get_algebric_expression_helper()
        

        A, X, B, eqs, expression = aeh.get_random_matrix_data(dimX = 2)
        
        expr_order = [0, 1]
        random.shuffle(expr_order)
        expr_order = [(item, counting_words(item))
            for item in expr_order
            ]

        prob_expr = aeh.get_latex_str(expression)
        prob_expr_str = "\n".join([
            f"${item}$" for item in prob_expr 
            ])
        

        A_with_X = A.row_insert(0, X.T)

        #change sign of first 
        A_modified = A.copy()
        A_modified_ueval = A.copy()

        A_modified_ueval.row_op(expr_order[0][0], lambda v, j: Symbol(f"-{v}"))
        A_modified_ueval_with_X = A_modified_ueval.row_insert(0, X.T)
        

        A_modified.row_op(expr_order[0][0], lambda v, j:  v * -1)
        A_modified_with_X = A_modified.row_insert(0, X.T)
        

        A_col_result = Matrix(np.array(A_modified).sum(axis=0)).T
        A_col_result_with_X = A_col_result.row_insert(0, X.T)

        soln_expr = aeh.getExpressions(A_col_result, X)
        
        answer = aeh.get_latex_str(soln_expr)
        answer_str = "\n".join(answer)

        implies = symbols(u'\U000021D2')

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        
        statement = f"""
            Subtract {expr_order[0][1]} expression from the {expr_order[1][1]}:
            {prob_expr_str}
            """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        var_str = ", ".join([f"${latex(v)}$" for v in  X.T.tolist()[0]])
        
        soln_s = f"""
        We have to subtract {expr_order[0][1]} expression from the {expr_order[1][1]}:
        {prob_expr_str}
        
        The coefficients of variables {var_str}  are:
        ${latex(A_with_X)}$

        As We have to subtract {expr_order[0][1]} expression from the {expr_order[1][1]},
        we will put a negative $(-)$ sign before each coefficient in the {expr_order[0][1]} expression
        ${latex(A_modified_ueval_with_X)}$
        
        ${latex(A_modified_with_X)}$
        
        Add the columns:
        ${latex(A_col_result_with_X)}$
        
        {implies} The sum of expressions:
        = ${answer_str}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        problemTemplate.add_answer(f"${answer_str}$", 'text/html')
        return problemTemplate
        pass
   
    def _problem_multiple_subtracts(self, *args, **kwargs):
        """
        Subtract one a set of algebric expression from another set


        Minimum Grade: 3
        Maximum Grade: 10

    
        Examples
        --------
        from xv.math.algebra import AlgebricExpressionManager
        ke = AlgebricExpressionManager()


        ke.getRandomProblem(problem_type = '_problem_subtract_advanced')


        doc_style: xv_doc
        """

        aeh = self.base_helper.get_algebra_helper().get_algebric_expression_helper()
        
        A, X, B, eqs, expression = aeh.get_random_matrix_data()
        
        expr_order = list(range(A.rows))
        deduct_count = random.randint(1, A.rows - 1)
            
            
        deduct_order = random.sample(expr_order, deduct_count)
        add_order = list(set(expr_order) - set(deduct_order))
        
        #random.shuffle(expr_order)
        add_order = sorted(add_order)
        deduct_order = sorted(deduct_order)
        
        add_order_expr = ", ".join([counting_words(item)
                                    for item in add_order])
        
        deduct_order_expr = ", ".join([counting_words(item)
                                    for item in deduct_order])
        
        add_order_expr = " and".join(add_order_expr.rsplit(",", 1))
        deduct_order_expr = " and".join(deduct_order_expr.rsplit(",", 1))
        
        prob_expr = aeh.get_latex_str(expression)
        prob_expr_str = "\n".join([
            f"${item}$" for item in prob_expr 
            ])
        
        A_with_X = A.row_insert(0, X.T)

        #change sign of first 
        A_modified = A.copy()
        A_modified_ueval = A.copy()
        
        for i in deduct_order:
            A_modified_ueval.row_op(i, lambda v, j:  Symbol(f"- {v}"))
            A_modified.row_op(i, lambda v, j:  v * -1)
            
        A_modified_with_X_ueval = A_modified_ueval.row_insert(0, X.T)
        
        A_modified_with_X = A_modified.row_insert(0, X.T)
        

        A_col_result = Matrix(np.array(A_modified).sum(axis=0)).T
        A_col_result_with_X = A_col_result.row_insert(0, X.T)

        soln_expr = aeh.getExpressions(A_col_result, X)
        
        answer = aeh.get_latex_str(soln_expr)
        answer_str = "\n".join(answer)

        implies = symbols(u'\U000021D2')

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        
        statement = f"""
            Add {add_order_expr} and subtract {deduct_order_expr}:
            {prob_expr_str}
            """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        var_str = ", ".join([f"${latex(v)}$" for v in  X.T.tolist()[0]])
        
        soln_s = f"""
        We have to subtract {deduct_order_expr} expression from {add_order_expr}:
        {prob_expr_str}
        
        The coefficients of variables {var_str}  are:
        ${latex(A_with_X)}$

        As We have to subtract {deduct_order_expr} expression from the {add_order_expr},
        we will put a negative $(-)$ sign before each coefficient in the {deduct_order_expr} expression
        
        ${latex(A_modified_with_X_ueval)}$
        
        ${latex(A_modified_with_X)}$
        
        Add the columns:
        ${latex(A_col_result_with_X)}$
        
        {implies} The sum of expressions:
        = ${answer_str}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        problemTemplate.add_answer(f"${answer_str}$", 'text/html')
        return problemTemplate
        pass
    
    def _problem_multiply(self, *args, **kwargs):
        """
        Multiply one algebric expression from another


        Minimum Grade: 3
        Maximum Grade: 10

    
        Examples
        --------
        from xv.math.algebra import AlgebricExpressionManager
        ke = AlgebricExpressionManager()


        ke.getRandomProblem(problem_type = '_problem_multiply')


        doc_style: xv_doc
        """
        aeh = self.base_helper.get_algebra_helper().get_algebric_expression_helper()
        

        implies = symbols(u'\U000021D2')
        X = aeh.getRandomVars(2)
        A = aeh.getRandomMatrix( (X.rows, 1) )
        
        X_arr = X.T.tolist()[0]
        A_arr = A.T.tolist()[0]

        exprs = [
            A_arr[0] * X_arr[0],
            A_arr[1] * X_arr[1]
            ] 
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        
        statement = f"""
        Multiply $\\displaystyle {latex(exprs[0])}$ and  $\\displaystyle {latex(exprs[1])}$.
        
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        
        soln_s = f"""
        $\\displaystyle ({latex(exprs[0])}) \\cdot ({latex(exprs[1])})$
        
        $\\displaystyle = ({latex(A_arr[0])} \\cdot {latex(A_arr[1])}) 
          \\cdot ({latex(X_arr[0])} \\cdot {latex(X_arr[1])})$
        
        $\\displaystyle = {latex(exprs[0] * exprs[1])}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle{latex(exprs[0] * exprs[1])}$
        """
        
        problemTemplate.add_answer(answer_s, 'text/html')
        return problemTemplate
        pass
   
   
    def _problem_multiply_advanced(self, *args, **kwargs):
        """
        Multiply one a set of algebric expression from another set


        Minimum Grade: 3
        Maximum Grade: 10

    
        Examples
        --------
        from xv.math.algebra import AlgebricExpressionManager
        ke = AlgebricExpressionManager()


        ke.getRandomProblem(problem_type = '_problem_multiply_advanced')


        doc_style: xv_doc
        """

        aeh = self.base_helper.get_algebra_helper().get_algebric_expression_helper()
        
        implies = symbols(u'\U000021D2')
        X = aeh.getRandomVars(random.randint(1, 4))
        A = aeh.getRandomMatrix( (X.rows, 1) )
        
        X_arr = X.T.tolist()[0]
        A_arr = A.T.tolist()[0]

        (A1, X1, A1_arr, X1_arr, A1_X1_expr, A1_X1_expr_str) = aeh.get_multiply_expr()
        (A2, X2, A2_arr, X2_arr, A2_X2_expr, A2_X2_expr_str) = aeh.get_multiply_expr()

        
        soln_step1_html = []
        soln_step2_html = []
        html_row0 = [f"<b>$\\displaystyle \\color{{blue}}{{\\Large{{{latex(item)}}}}} $</b>" 
                     for item in A2_X2_expr]
        html_row0.insert(0, "$\\;$")
        soln_step1_html.append(html_row0)
        soln_step2_html.append(html_row0)
        
        soln_step1 = []
        for item1 in A1_X1_expr:
            step1_row = [f"<b>$\\displaystyle \\color{{red}}{{\\Large{{{latex(item1)}}}}} $</b>"]
            step2_row = step1_row.copy()
            
            soln_step1.append(f"$\\displaystyle {latex(item1)} \\cdot \\; $({A2_X2_expr_str})")
            
            for idx, item2 in enumerate(A2_X2_expr):
                step1_row.append(f"""
                $\\displaystyle 
                \\color{{red}}{{{latex(item1)}}} \\cdot \\color{{blue}}{{{latex(item2)}}} 
                $
                """)
                step2_row.append(f"$\\displaystyle {latex(item1 * item2)}$")
                pass
            
            soln_step1_html.append(step1_row)
            soln_step2_html.append(step2_row)
            pass
        
        from xv.util import html_table
        soln_step1_html = html_table(soln_step1_html)
        soln_step2_html = html_table(soln_step2_html)
        
        soln_step1_str = "\t " + "\n\t +\t".join(soln_step1)
        
        
        soln_step2 = []
        for item1 in A1_X1_expr:
            mult1 = []
            for item2 in A2_X2_expr:
                mult1.append(f"$\\displaystyle {latex(item1)} \\cdot {latex(item2)}$")
                pass
            
            mult1_str = " + ".join(mult1)
            soln_step2.append(mult1_str)
            pass
        
        soln_step2_str = "\t" + "\n\t +\t".join(soln_step2)
        
        soln_step3 = []
        for item1 in A1_X1_expr:
            mult1 = []
            for item2 in A2_X2_expr:
                mult1.append(f"$\\displaystyle {latex(item1 * item2)}$")
                pass
            
            mult1_str = " + ".join(mult1)
            soln_step3.append(mult1_str)
            pass
        
        soln_step3_str = "\t" + "\n\t +\t".join(soln_step3)
        

        soln_step4 = 0
        for item1 in A1_X1_expr:
            mult1 = []
            for item2 in A2_X2_expr:
                soln_step4 = soln_step4 + item1 * item2
                pass
            pass
        
        soln_step4_str = f"$\\displaystyle {latex(soln_step4)}$"
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        
        statement = f"""
        Multiply 
        \t{A1_X1_expr_str} \tand 
        \t{A2_X2_expr_str}
        
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        
        soln_s = f"""
        \t({A1_X1_expr_str}) $\\cdot$ ({A2_X2_expr_str})
        
        Write in matrix form and multiply:
        
        {soln_step1_html}
        
        Compute the multiplication:
        
        {soln_step2_html}
        
        Now write all the terms and add them:
        
        =\t{soln_step3_str}
        
        =\t{soln_step4_str}
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        problemTemplate.add_answer(soln_step4_str, 'text/html')
        return problemTemplate
        pass

    def _problem_divide(self, *args, **kwargs):
        """
        Divide one algebric expression by another set


        Minimum Grade: 3
        Maximum Grade: 10

    
        Examples
        --------
        from xv.math.algebra import AlgebricExpressionManager
        ke = AlgebricExpressionManager()


        ke.getRandomProblem(problem_type = '_problem_divide')


        doc_style: xv_doc
        """

        aeh = self.base_helper.get_algebra_helper().get_algebric_expression_helper()
        
        implies = symbols(u'\U000021D2')
        X = aeh.getRandomVars(2)
        A = aeh.getRandomMatrix( (X.rows, 1) )
        
        X_arr = X.T.tolist()[0]
        A_arr = A.T.tolist()[0]

        exprs = [
            A_arr[0] * X_arr[0],
            A_arr[1] * X_arr[1]
            ] 
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        
        statement = f"""
        Divide $\\displaystyle {latex(exprs[0])}$ by  ${latex(exprs[1])}$.
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        frac1 = f"""\\frac {{ {latex(A_arr[0])} }} {{ {latex(A_arr[1])} }}"""
        
        frac2 = f"""\\frac {{ {latex(X_arr[0])} }} {{ {latex(X_arr[1])} }}"""
        
        
        soln_s = f"""
        $\\displaystyle 
        \\frac {{ {latex(exprs[0])} }} {{ {latex(exprs[1])} }}
        $
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        
        if exprs[0] == 0 and exprs[1] != 0:
            answer_s = 0
            soln_s = f"""
            Zero divided by any number other than zero and infinity is zero.
            Hence, the answer is zero.
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
            pass
        elif exprs[1] == 0:
            answer_s = "Undefined"
            soln_s = f"""
            It is undefined as zero cannot be a divisor.
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
            pass
        else:
            answer_s = f"""$\\displaystyle 
            \\frac {{ {latex(exprs[0])} }} {{ {latex(exprs[1])} }}
            $"""
            
            soln_s = f"""
            = $\\displaystyle 
            \\frac {{ {latex(A_arr[0])} }} {{ {latex(A_arr[1])} }}
            \\cdot
            \\frac {{ {latex(X_arr[0])} }} {{ {latex(X_arr[1])} }}
            
            = {frac1} \\cdot {frac2}
            $
            
            = {answer_s}
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
            pass
        
        problemTemplate.add_answer(f"{answer_s}", 'text/html')
        return problemTemplate
        pass
   
   
   
    def _problem_divide_advanced_1(self, *args, **kwargs):
        """
        Divide one a set of algebric expression by another expression


        Minimum Grade: 3
        Maximum Grade: 10

    
        Examples
        --------
        from xv.math.algebra import AlgebricExpressionManager
        ke = AlgebricExpressionManager()


        ke.getRandomProblem(problem_type = '_problem_divide_advanced_1')


        doc_style: xv_doc
        """

        aeh = self.base_helper.get_algebra_helper().get_algebric_expression_helper()
        
        implies = symbols(u'\U000021D2')
        (A1, X1, A1_arr, X1_arr, A1_X1_expr, A1_X1_expr_str) = aeh.get_multiply_expr()
        
        divisor = random.choice(X1.T.tolist()[0])
        divisor_str = f"${latex(divisor)}$"
        division_steps, division_steps_str =  aeh.get_division_steps(divisor, A1_X1_expr)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        
        statement = f"""
        Divide {A1_X1_expr_str}\t by \t{divisor_str}
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        soln_s = aeh.get_division_steps_str(A1_X1_expr_str, 
                                              divisor_str, 
                                              division_steps_str)
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        problemTemplate.add_answer(division_steps_str[2], 'text/html')
        return problemTemplate
        pass
   
    def _problem_divide_advanced_2(self, *args, **kwargs):
        """
        Divide one a set of algebric expression by another expression


        Minimum Grade: 3
        Maximum Grade: 10

    
        Examples
        --------
        from xv.math.algebra import AlgebricExpressionManager
        ke = AlgebricExpressionManager()


        ke.getRandomProblem(problem_type = '_problem_divide_advanced_2')


        doc_style: xv_doc
        """

        aeh = self.base_helper.get_algebra_helper().get_algebric_expression_helper()
        
        implies = symbols(u'\U000021D2')

        (A1, X1, A1_arr, X1_arr, A1_X1_expr, A1_X1_expr_str) = aeh.get_multiply_expr()
        (A2, X2, A2_arr, X2_arr, A2_X2_expr, A2_X2_expr_str) = aeh.get_multiply_expr()
        
        
        A_X_expr = []
        A_X_expr.extend(A1_X1_expr)
        A_X_expr.extend(A2_X2_expr)

        A_X_expr_str = " + ".join([
            f"${latex(item)}$"
            for item in A_X_expr
            ])
        
        divisor = random.choice(X1.T.tolist()[0])
        divisor_str = f"${latex(divisor)}$"

        division_steps, division_steps_str =  aeh.get_division_steps(divisor, A_X_expr)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        
        statement = f"""
        Divide {A_X_expr_str}\t by \t{divisor_str}
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        soln_s = aeh.get_division_steps_str(A_X_expr_str, 
                                              divisor_str, 
                                              division_steps_str)
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        problemTemplate.add_answer(division_steps_str[2], 'text/html')
        return problemTemplate
        pass
   
    def _problem_divide_advanced_3(self, *args, **kwargs):
        """
        Divide one a set of algebric expression by another expression


        Minimum Grade: 3
        Maximum Grade: 10

    
        Examples
        --------
        from xv.math.algebra import AlgebricExpressionManager
        ke = AlgebricExpressionManager()


        ke.getRandomProblem(problem_type = '_problem_divide_advanced_3')


        doc_style: xv_doc
        """

        aeh = self.base_helper.get_algebra_helper().get_algebric_expression_helper()
        
        implies = symbols(u'\U000021D2')
        (A1, X1, A1_arr, X1_arr, A1_X1_expr, A1_X1_expr_str) = aeh.get_multiply_expr()
        (A2, X2, A2_arr, X2_arr, A2_X2_expr, A2_X2_expr_str) = aeh.get_multiply_expr()
        
        divisor1 = random.choice(X1.T.tolist()[0])
        divisor2 = random.choice(X2.T.tolist()[0])
        divisors = [divisor1, divisor2, 
                    divisor1/divisor2, divisor2/divisor1,
                    divisor1*divisor2
                    ]
        
        random.shuffle(divisors)
        divisor1, divisor2 = random.sample(divisors, 2)

        divisor1_str = f"${latex(divisor1)}$".replace("\\frac", "\\dfrac")
        division1_steps, division1_steps_str =  aeh.get_division_steps(
                                                    divisor1, A1_X1_expr)
        
        divisor2_str = f"${latex(divisor2)}$".replace("\\frac", "\\dfrac")
        division2_steps, division2_steps_str =  aeh.get_division_steps(
                                                    divisor2, A2_X2_expr)
        
        expr_part1_str = division1_steps_str[-1].strip("$")
        expr_part2_str = division2_steps_str[-1].strip("$")
        
        random_third_op = random.choice([0, 1, 2])
        
        if random_third_op ==  0:
            third_op_stmt = "Add the results of the above."
            expr_part = division1_steps[-1] + division2_steps[-1]
            
            third_op_expr = f""" 
            \\left( {expr_part1_str} \\right) + 
            \\left( {expr_part2_str} \\right) """
            
        elif random_third_op ==  1:
            third_op_stmt = "Deduct the first result from the second."
            expr_part = - division1_steps[-1] + division2_steps[-1]
            
            third_op_expr = f"""
            - \\left( {expr_part1_str} \\right) + 
            \\left( {expr_part2_str} \\right) """

        else:
            third_op_stmt = "Deduct the second result from the first."
            expr_part = division1_steps[-1] - division2_steps[-1]
            
            third_op_expr = f"""
            \\left( {expr_part1_str} \\right) -
            \\left( {expr_part2_str} \\right) """

        
        self.expr_part = expr_part
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        
        statement = f"""
        Do the followings:
        
        1. Divide {A1_X1_expr_str}\t by \t{divisor1_str}
        
        2. Divide {A2_X2_expr_str}\t by \t{divisor2_str}
        
        3. {third_op_stmt}
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        
        soln_s1 = aeh.get_division_steps_str(A1_X1_expr_str, 
                                              divisor1_str, 
                                              division1_steps_str)
        soln_s = f"""
        <h2>Part 1:</h2>{soln_s1}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        soln_s2 = aeh.get_division_steps_str(A2_X2_expr_str, 
                                              divisor2_str, 
                                              division2_steps_str)
        soln_s = f"""
        <h2>Part 2:</h2>{soln_s2}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        soln_s = f"""
        <h2>Part 3:</h2>
        {third_op_stmt}
        
        $\\displaystyle = {third_op_expr}$
        
        $\\displaystyle = {latex(expr_part)}$
        
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {latex(expr_part)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        return problemTemplate
        pass
   
    def _problem_division_with_zero(self, *args, **kwargs):
        """
        Division expressions with zero in it

        doc_style: xv_doc
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        #x = Symbol(random.choice(list('abcdefghuvwxyz')))
        x = Symbol(random.choice(list('axy')))
        
        random_val = random.randint(1, 9)  * random.choice([1, -1])
        
        rule1 = "You divide zero any number of times, it will remain zero."
        rule2 = "You cannot divide anything with zero."
        rule3 = f'${latex(oo)}$ is undefined and any function of it is also undefined.'
        
        rule4 = "You multiply zero with itself number of times, it will remain zero."
        rule5 = f"Any negative power will make it a divisor and {rule3.lower()}"

        rule6 = f"Zero cannot have a power of zero."
        rule7 = f"A non-zero, non-infinity number raised to power zero is ones."
        
        data_set = []
        
        data_item_expr = latex_fraction(0, latex(x))
        
        indeterminate = Symbol(f"\\text{{indeterminate}}")
        undefined = Symbol(f"\\text{{undefined}}")
        
        data_item_details = [
                [f"{latex(x)} \\ne 0, {latex(oo)}", 0, rule1], 
                [f"{latex(x)} = 0", f"{latex(indeterminate)}", rule2], 
                [f"{latex(x)} = {latex(oo)}", f"{latex(undefined)}", rule3], 
            ]
        data_set.append([data_item_expr] + random.choice(data_item_details))
        
        data_item_expr = latex_fraction(latex(x), 0)
        data_item_details = [
                [f"{latex(x)} \\ne 0, {latex(oo)}", f"{latex(indeterminate)}", rule2], 
                [f"{latex(x)} = 0", f"{latex(indeterminate)}", rule2], 
                [f"{latex(x)} = {latex(oo)}", f"{latex(undefined)}", rule3], 
            ]
        data_set.append([data_item_expr] + random.choice(data_item_details))
        
        
        data = random.choice(data_set)
        
        statement = f"""
        Find the value of $\\displaystyle {data[0]} \\; \\text{{if}} \\; {data[1]}$
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        
        soln_s = f"""
        $\\displaystyle {data[0]} = {data[2]} 
        
        \\qquad \\text{{if}} \\; {data[1]}$
        
        {data[3]}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        
        answer_s = f"""
        $\\displaystyle {data[2]} $
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')

        return problemTemplate
        pass
   
    def _problem_power_with_zero(self, *args, **kwargs):
        """
        Power expressions with zero in it

        doc_style: xv_doc
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        #x = Symbol(random.choice(list('abcdefghuvwxyz')))
        x = Symbol(random.choice(list('axy')))
        
        random_val = random.randint(1, 9)  * random.choice([1, -1])
        
        rule1 = "You divide zero any number of times, it will remain zero."
        rule2 = "You cannot divide anything with zero."
        rule3 = f'${latex(oo)}$ is undefined and any function of it is also undefined.'
        
        rule4 = "You multiply zero with itself number of times, it will remain zero."
        rule5 = f"Any negative power will make it a divisor and {rule3.lower()}"

        rule6 = f"Zero cannot have a power of zero."
        rule7 = f"A non-zero, non-infinity number raised to power zero is ones."
        
        rules_str = [rule1, rule2, rule3, rule4, rule5, rule6, rule7]
        rules_str = [f"{idx + 1}. {x}" for idx, x in enumerate(rules_str) ]
        rules_str = "<br/>".join(rules_str)
        
        data_set = []
        
        indeterminate = Symbol(f"\\text{{indeterminate}}")
        undefined = Symbol(f"\\text{{undefined}}")
        
        
        data_item_expr = f"{{0}}^{{{latex(x)}}}"
        data_item_details = [
                [f"{latex(x)} > 0, and \\; {latex(x)} \\ne {latex(oo)}", 0, rule4], 
                [f"{latex(x)} = 0", f"{latex(undefined)}", rule6], 
                [f"{latex(x)} < 0", f"{latex(indeterminate)}", rule5], 
                [f"{latex(x)} = {latex(oo)}", f"{latex(undefined)}", rule3], 
            ]
        data_set.append([data_item_expr] + random.choice(data_item_details))
        
        data_item_expr = f"{{{latex(x)}}}^{{0}}"
        data_item_details = [
                [f"{latex(x)} > 0, and \\; {latex(x)} \\ne {latex(oo)}", 1, rule7], 
                [f"{latex(x)} = 0", f"{latex(undefined)}", rule6], 
                [f"{latex(x)} < 0", 1, rule7], 
                [f"{latex(x)} = {latex(oo)}", f"{latex(undefined)}", rule3], 
            ]
        data_set.append([data_item_expr] + random.choice(data_item_details))
        
        data = random.choice(data_set)
        
        statement = f"""
        Find the value of $\\displaystyle {data[0]} \\; \\text{{if}} \\; {data[1]}$
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        
        soln_s = f"""
        $\\displaystyle {data[0]} = {data[2]} 
        
        \\qquad \\text{{if}} \\; {data[1]}$
        
        {data[3]}
        
        Please note the following rules:
        {rules_str} 
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        
        answer_s = f"""
        $\\displaystyle {data[2]} $
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')

        return problemTemplate
        pass
   

    def _problem_abs_values(self, *args, **kwargs):
        """
        Absolute values

        doc_style: xv_doc
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        x, y, z = symbols('x y z')


        
        x_val = random.randint(-10, 10)
        y_val = random.randint(-10, 10)

        if random.choice([True, False]):        
            expr = abs(x - y)
            expr_latex = f"{latex(x)} - {latex(y)}"
            expr_latex_1 = f"\\left({latex(x_val)}\\right) - \\left({latex(y_val)}\\right)"
            expr_gross_val = x_val - y_val
        else:
            expr = abs(x + y)
            expr_latex = f"{latex(x)} + {latex(y)}"
            expr_latex_1 = f"\\left({latex(x_val)}\\right) + \\left({latex(y_val)}\\right)"
            expr_gross_val = x_val + y_val
            
        expr_latex_2 = f"{latex(expr_gross_val)}"
        
        expr_val = expr.subs({x: x_val, y: y_val})

        conds = []

        if x_val < 0 and y_val < 0:
            conds.append(f"{latex(x)} < 0, \\; {latex(y)} < 0")
            if x_val >= y_val:
                conds.append(f"\\; \\text{{and}} \\; {latex(x)} >= {latex(y)}")
            else:
                conds.append(f"\\; \\text{{and}} \\; {latex(x)} < {latex(y)}")

        if x_val >= 0 and y_val >= 0:
            conds.append(f"{latex(x)} >= 0, \\; {latex(y)} >= 0")
            if x_val >= y_val:
                conds.append(f"\\; \\text{{and}} \\; {latex(x)} >= {latex(y)}")
            else:
                conds.append(f"\\; \\text{{and}} \\; {latex(x)} < {latex(y)}")

        if x_val >= 0 and y_val < 0:
            conds.append(f"{latex(x)} >= 0, \\; {latex(y)} < 0")

        if x_val < 0 and y_val >= 0:
            conds.append(f"{latex(x)} < 0, \\; {latex(y)} >= 0")

        if x_val == y_val:
            conds.append(f"{latex(x)} = {latex(y)}")

        conds = "\n".join(conds)

        
        if expr_val == 0:
            answer_s = f"0"
        elif expr_val == x_val + y_val:
            answer_s = f"{latex(x)} + {latex(y)}"
        
        elif expr_val == x_val - y_val:
            answer_s = f"{latex(x)} - {latex(y)}"
        
        elif expr_val == y_val - x_val:
            answer_s = f"{latex(y)} - {latex(x)}"
        
        elif expr_val == -(x_val + y_val):
            answer_s = f"- \\left({latex(x)} + {latex(y)} \\right)"
        else:
            answer_s = None
        
        
        statement = f"""
        Find the absolute value of $\\displaystyle 
        {expr_latex} $
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        if conds:
            statement = f"""
            if $\\displaystyle 
            {conds} $ 
            """
            problemTemplate.add_problem(statement.strip(), "text/html")
        
        
        soln_s = f"""
        $\\displaystyle 
        \\text{{abs}} \\left( {expr_latex} \\right)
        $
        
        $\\displaystyle 
        = \\left| {expr_latex} \\right|
        $

        Let $\\displaystyle 
        {latex(x)} = {latex(x_val)}
        $, and $\\displaystyle 
        {latex(y)} = {latex(y_val)}
        $ 
        
        $\\displaystyle 
        = \\left| {expr_latex_1} \\right|
        $
        
        $\\displaystyle 
        = \\left| {expr_latex_2} \\right|
        $
        
        $\\displaystyle 
        = {latex(expr_val)}
        $

        Hence,
        $\\displaystyle 
        \\left| {expr_latex} \\right|= {answer_s}
        $ \t\t See note below.
        
        <b>Note: </b>
        x + y = {x_val + y_val}
        x - y = {x_val - y_val}
        y - x = {y_val - x_val}
        -(x + y) = {-(x_val + y_val)}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        
        answer_s = f"""
        $\\displaystyle 
        {answer_s}
        $
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')

        return problemTemplate
        pass
   
