'''
Created on 12-Jul-2021

@author: bthakur
'''
import random
import inspect
import traceback

import numpy as np
import sympy as sp

from sympy import (symbols, Symbol, pi, latex, sympify,
                   Rational, Derivative, simplify,
                   UnevaluatedExpr as ueval
                   )
from xv.km.util import latex_fraction, wrap_in_latex
from xv.util import number_to_nth, list_to_str


from  xv.util import UtilClass

# _advanced_power_helper
# AdvancedPowerHelper


class AdvancedPowerHelper(UtilClass):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        pass
    
    def _init_data(self):
        b1 = random.randint(2, 9)
        e1, e2, e3, e4, e5 = random.sample(list(range(2, 10)), 5)
        
        e1_large = random.randint(20, 100)
        b1_expr = " + ".join([str(b1) for x in range(e1)])
        b2_expr = " + ".join([str(b1) for x in range(e2)])
        b3_expr = " + ".join([str(b1) for x in range(e3)])
        b1_b2_expr = " + ".join([f"({b1_expr})" for x in range(e2)])
        
        return b1, e1, e2, e3, e4, e5, e1_large, b1_expr, b2_expr, b3_expr, b1_b2_expr
    
    def _init_prime_data(self):
        primes = [2, 3, 5, 7, 11, 13, 17]
        p1 = random.choice(primes)
        p2 = random.choice(primes)
        p3 = random.choice(primes)
        e1 = random.randint(2, 5) 
        f1 = random.randint(2, 5) 
        f2 = random.randint(2, 5) 
        if f1%2 == 0:
            s1 = 1
        else:
            s1 = random.choice([1, -1])
            pass
        
        p1f1s1 = (p1* s1) ** f1 
        
        return p1, p2, p3, e1, f1, f2, s1, p1f1s1
    
    
    def simplify_powers(self, power_tuple):
        simplified_expr = {}
        is_simplified = False
        for base, power in power_tuple:
            v = simplified_expr.get(base, None)
            if not v is None:
                v.append(power)
                simplified_expr[base] = v
                is_simplified = True
                continue

            simplified_expr[base] = [power] 
            pass
        
        if not is_simplified:
            return None
        
        simplified_expr_str1 =[]
        simplified_expr_str2 =[]
        for base, powers in simplified_expr.items():
            powers_str = " + ".join([f"{p}" for p in powers])
            simplified_expr_str1.append(f"{base}^{{{powers_str}}}")
            simplified_expr_str2.append(f"{base}^{{{sum(powers)}}}")
            pass
        
        simplified_expr_str1 = " * ".join(simplified_expr_str1)
        simplified_expr_str2 = " * ".join(simplified_expr_str2)

        return [simplified_expr_str1, simplified_expr_str2]
        pass


    def get_problem_simplify_as_expr_of_exponents(self, **kwargs):
        '''
        '''
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        index = kwargs.get('index', None)
        
        try:
            
            if index is None:
                random_choices = list(range(1, 6))
        
                identifier = f"{self.__class__.__name__}{id(self)}.{inspect.stack()[0][3]}.random_choices"
                random_choice = self.get_no_repeat_random_choice(identifier, random_choices)
            else:
                random_choice = index
            
            if random_choice == 1:
                return self._problem_exp_of_exp_pair_1()
            
            if random_choice == 2:
                return self._problem_exp_of_exp_pair_2()
            
            if random_choice == 3:
                return self._problem_exp_of_exp_pair_3()
            
            if random_choice == 4:
                return self._problem_exp_of_exp_pair_4()
            
            if random_choice == 5:
                return self._problem_exp_of_exp_pair_5()
            
    
            return self._problem_exp_of_exp_pair_1()
        
        except Exception as e:
            sError = f"""Error {self.__class__.__name__}.{inspect.stack()[0][3]}: {e}
            """
            self._log(sError)
            self._log(traceback.format_exc())
            pass
        
        return None
        pass

    def _problem_exp_of_exp_pair_1(self, **kwargs):

        (b1, e1, e2, e3, e4, e5, e1_large, b1_expr, 
         b2_expr, b3_expr, b1_b2_expr) = self._init_data()
        
        prob_s = f"""
        Simplify as power of {b1}
        
        $\\displaystyle ({b1_expr})^{{{e3}}} * ({b2_expr})^{{{e4}}}$
        """
        
        soln_stmts = []
        
        soln_stmt = f"""
        $\\displaystyle ({b1_expr})^{{{e3}}} * ({b2_expr})^{{{e4}}}$

        $\\displaystyle = {{ \\left({{{b1}}}*{{{e1}}}\\right) }}^{{{e3}}} * {{ \\left({{{b1}}}*{{{e2}}}\\right) }}^{{{e4}}} $

        $\\displaystyle = {b1}^{{{e3}}} * {e1}^{{{e3}}} * {b1}^{{{e4}}} * {e2}^{{{e4}}}$ 
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        power_tuple = [(b1, e3), (e1, e3), (b1, e4), (e2, e4)]
        simplified_exprs = self.simplify_powers(power_tuple)

        soln_stmt = f"""
        $\\displaystyle = {simplified_exprs[0]}$
        
        $\\displaystyle = {simplified_exprs[1]}$
         """
        soln_stmts.append(soln_stmt.strip())

        answer_s = f"""
        $\\displaystyle {simplified_exprs[1]}$
        """
        
        soln_s = "\n\n".join(soln_stmts)
        
                
        result = {
            'prob_s': prob_s.replace("\n", " ").strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        pass


    def _problem_exp_of_exp_pair_2(self, **kwargs):

        (b1, e1, e2, e3, e4, e5, e1_large, b1_expr, 
         b2_expr, b3_expr, b1_b2_expr) = self._init_data()

        latex_fr_prob = latex_fraction(f"({b1_expr})^{{{e3}}}", 
                                       f"({b2_expr})^{{{e4}}}")
        

        latex_fr_soln1 = latex_fraction(f"{{ \\left({{{b1}}}*{{{e1}}}\\right) }}^{{{e3}}}", 
                                        f"{{ \\left({{{b1}}}*{{{e2}}}\\right) }}^{{{e4}}}")
        
        latex_fr_soln2 = latex_fraction(f"{b1}^{{{e3}}} * {e1}^{{{e3}}}", 
                                        f"{b1}^{{{e4}}} * {e2}^{{{e4}}}")
        
        latex_fr_soln3 = f"""
        {b1}^{{{e3} - {e4}}} * {e1}^{{{e3}}} * {e2}^{{- {e4}}}
        """
        
        latex_fr_soln4 = f"""
        {b1}^{{{e3 - e4}}} * {e1}^{{{e3}}} * {e2}^{{- {e4}}}
        """
        

        
        prob_s = f"""
        Simplify as power of {b1}
        
        $\\displaystyle {latex_fr_prob}$
        """
        
        soln_stmts = []
        
        soln_stmt = f"""
        $\\displaystyle {latex_fr_prob}$

        $\\displaystyle = {latex_fr_soln1}$

        $\\displaystyle = {latex_fr_soln2}$

        """
        soln_stmts.append(soln_stmt.strip())

        power_tuple = [(b1, e3), (e1, e3), (b1, -e4), (e2, -e4)]
        simplified_exprs = self.simplify_powers(power_tuple)
        
        soln_stmt = f"""
        $\\displaystyle = {simplified_exprs[0]}$
        
        $\\displaystyle = {simplified_exprs[1]}$
         """
        soln_stmts.append(soln_stmt.strip())

        answer_s = f"""
        $\\displaystyle {simplified_exprs[1]}$
        """
        
        soln_s = "\n\n".join(soln_stmts)
        
                
        result = {
            'prob_s': prob_s.replace("\n", " ").strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        pass


    def _problem_exp_of_exp_pair_3(self, **kwargs):
        (b1, e1, e2, e3, e4, e5, e1_large, b1_expr, 
         b2_expr, b3_expr, b1_b2_expr) = self._init_data()
        
        prob_s = f"""
        Simplify as power of {b1}
        
        $\\displaystyle \\left({b1}^{{{e1}}} * {b1}^{{{e2}}}\\right)^{{{e3}}}$
        """
        
        soln_stmts = []
        
        soln_stmt = f"""
        $\\left({b1}^{{{e1}}} * {b1}^{{{e2}}}\\right)^{{{e3}}}$

        $\\displaystyle = \\left({b1}^{{{e1} + {e2}}}\\right)^{{{e3}}}$
        
        $\\displaystyle = \\left({b1}^{{{e1 + e2}}}\\right)^{{{e3}}}$
        
        $\\displaystyle = {b1}^{{{e1 + e2} * {e3}}}$
        
        $\\displaystyle = {b1}^{{{(e1 + e2) * e3}}}$
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
         """
        soln_stmts.append(soln_stmt.strip())

        answer_s = f"""
        $\\displaystyle {b1}^{{{(e1 + e2) * e3}}}$
        """
        
        soln_s = "\n\n".join(soln_stmts)
        
                
        result = {
            'prob_s': prob_s.replace("\n", " ").strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        pass





    def _problem_exp_of_exp_pair_4(self, **kwargs):
        (b1, e1, e2, e3, e4, e5, e1_large, b1_expr, 
         b2_expr, b3_expr, b1_b2_expr) = self._init_data()

        
        prob_s = f"""
        Simplify as power of {b1}
        
        $\\displaystyle \\left({{ \\left({b1_expr}\\right)^{{{e3}}} * \\left({b2_expr}\\right)^{{{e4}}}  }}\\right) ^{{{e5}}}$
        """
        
        soln_stmts = []
        
        soln_stmt = f"""
        $\\displaystyle \\left({{ \\left({b1_expr}\\right)^{{{e3}}} * \\left({b2_expr}\\right)^{{{e4}}}  }}\\right) ^{{{e5}}}$
        
        $\\displaystyle = \\left({{ \\left({b1}*{{{e1}}}\\right)^{{{e3}}} * \\left({b1}*{{{e2}}}\\right)^{{{e4}}}  }}\\right) ^{{{e5}}}$
        
        $\\displaystyle = \\left({b1}^{{{e3}}} * {e1}^{{{e3}}}  * {b1}^{{{e4}}} * {e2}^{{{e4}}}\\right) ^{{{e5}}}$
        
        $\\displaystyle = {b1}^{{{e3} * {e5}}} * {e1}^{{{e3} * {e5}}}  * {b1}^{{{e4} * {e5}}} * {e2}^{{{e4} * {e5}}}$
        
        $\\displaystyle = {b1}^{{{e3 * e5}}} * {e1}^{{{e3 * e5}}}  * {b1}^{{{e4 * e5}}} * {e2}^{{{e4 * e5}}}$
        """
        soln_stmts.append(soln_stmt.strip())

        power_tuple = [(b1, e3 * e5), (e1, e3 * e5), (b1, e4 * e5), (e2, e4 * e5)]
        simplified_exprs = self.simplify_powers(power_tuple)

        
        soln_stmt = f"""
        $\\displaystyle = {simplified_exprs[0]}$
        
        $\\displaystyle = {simplified_exprs[1]}$
         """
        soln_stmts.append(soln_stmt.strip())

        answer_s = f"""
        $\\displaystyle {simplified_exprs[1]}$
        """
        
        soln_s = "\n\n".join(soln_stmts)
        
                
        result = {
            'prob_s': prob_s.replace("\n", " ").strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        pass


    def _problem_exp_of_exp_pair_5(self, **kwargs):
        b1, e1, e2, e3, e4, e5, e1_large, b1_expr, b2_expr, b3_expr, b1_b2_expr = self._init_data()

        latex_fr_prob = latex_fraction(f"({b1_expr})^{{{e3}}}", 
                                       f"({b2_expr})^{{{e4}}}")
        

        latex_fr_soln1 = latex_fraction(f"\\left({b1}*{{{e1}}}\\right)^{{{e3}}} ", 
                                        f"\\left({b1}*{{{e2}}}\\right)^{{{e4}}}")
        
        latex_fr_soln2 = latex_fraction(f"{b1}^{{{e3}}} * {e1}^{{{e3}}}", 
                                        f"{b1}^{{{e4}}} * {e2}^{{{e4}}}")
        
        latex_fr_soln3 = latex_fraction(f"{b1}^{{{e3}*{e5}}} * {e1}^{{{e3}*{e5}}}", 
                                        f"{b1}^{{{e4}*{e5}}} * {e2}^{{{e4}*{e5}}}")

        
        latex_fr_soln4 = latex_fraction(f"{b1}^{{{e3*e5}}} * {e1}^{{{e3*e5}}}", 
                                        f"{b1}^{{{e4*e5}}} * {e2}^{{{e4*e5}}}")
        
        
        prob_s = f"""
        Simplify as power of {b1}
        
        $\\displaystyle \\left({{ {latex_fr_prob}  }}\\right) ^{{{e5}}}$
        """
        
        soln_stmts = []
        
        soln_stmt = f"""
        
        $\\displaystyle \\left({{ {latex_fr_prob}  }}\\right) ^{{{e5}}}$
        
        $\\displaystyle = \\left({{ {latex_fr_soln1}  }}\\right) ^{{{e5}}}$
        
        $\\displaystyle = \\left({{ {latex_fr_soln2}  }}\\right) ^{{{e5}}}$
        
        $\\displaystyle = {{ {latex_fr_soln3}  }}$
        
        $\\displaystyle = {{ {latex_fr_soln4}  }}$
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        power_tuple = [(b1, e3 * e5), (e1, e3 * e5), (b1, -e4 * e5), (e2, -e4 * e5)]
        simplified_exprs = self.simplify_powers(power_tuple)

        soln_stmt = f"""
        $\\displaystyle = {simplified_exprs[0]}$
        
        $\\displaystyle = {simplified_exprs[1]}$
         """
        soln_stmts.append(soln_stmt.strip())

        answer_s = f"""
        $\\displaystyle {simplified_exprs[1]}$
        """
        
        soln_s = "\n\n".join(soln_stmts)
        
                
        result = {
            'prob_s': prob_s.replace("\n", " ").strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        pass




    def get_problem_difficult_problems(self, **kwargs):
        '''
        '''
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        index = kwargs.get('index', None)
        
        try:
            
            if index is None:
                random_choices = list(range(1, 3))
        
                identifier = f"{self.__class__.__name__}{id(self)}.{inspect.stack()[0][3]}.random_choices"
                random_choice = self.get_no_repeat_random_choice(identifier, random_choices)
            else:
                random_choice = index
            
            if random_choice == 1:
                return self._problem_simplify_expr_type_1()
            
            if random_choice == 2:
                return self._problem_simplify_expr_type_2()
            
            return self._problem_simplify_expr_type_1()
        
        except Exception as e:
            sError = f"""Error {self.__class__.__name__}.{inspect.stack()[0][3]}: {e}
            """
            self._log(sError)
            self._log(traceback.format_exc())
            pass
        
        return None
        pass



    def _problem_simplify_expr_type_1(self, **kwargs):
        
        num1 = random.choice([2, 3, 6, 5, 7, 8])
        num2 = random.randint(1, 9)
        num_exp = random.choice([2, 3, 4, 6, 8])
        
        
        
        a,b = symbols("a, b")
        
        a_by_b_pow_n = Symbol(f""" 
            \\left(\\frac{{{latex(a)}}}{{{latex(b)}}}\\right)^{{ {latex(num_exp)} }} 
            """)
        
        b_by_a_pow_n = Symbol(f""" 
            \\left(\\frac{{{latex(b)}}}{{{latex(a)}}}\\right)^{{ {latex(num_exp)} }}
            """)
        
        a_val = sp.sqrt(num1) + num2
        b_val = sp.sqrt(num1) - num2
        
        a_symb = Symbol(f""" \\sqrt{{{latex(num1)}}} + {latex(num2)} """)
        b_symb = Symbol(f""" \\sqrt{{{latex(num1)}}} - {latex(num2)} """)
        
        prob_s = f"""'
        If 
        $\\displaystyle {latex(a)} = {latex(a_symb)} $ and 
        $\\displaystyle {latex(b)} = {latex(b_symb)} $, 
        find the value of 
        $\\displaystyle 
        {latex(a_by_b_pow_n)} + {latex(b_by_a_pow_n)}
        $
        """
        prob_s = prob_s.replace("\n", " ").strip()
        
        soln_stmts = []
        
        numr_step1 = Symbol(f"""
            {latex( a ** (num_exp * 2) )} + {latex( b ** (num_exp * 2) )}
            """)
        
        denm_step0 = a**num_exp * b**num_exp 
        denm_step1 = ueval(a * b)**num_exp 
        
        soln_stmt = f"""
        $\\displaystyle 
        \\begin{{aligned}}
        
        & {latex(a_by_b_pow_n)} + {latex(b_by_a_pow_n)}
        \\\\ \\\\

        = & {latex( (a / b ) ** num_exp )} + {latex( (b / a ) ** num_exp )}
        \\\\ \\\\
        
        = & {latex(numr_step1 / denm_step0)}
        \\\\ \\\\
        
        = & \\frac{{ {latex(numr_step1)} }} {{ {latex(denm_step1)} }} 
        \\qquad \\cdots \\text{{ (i) }}

        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())


        from ._prime_number_helper import PrimeNumberHelper
        pn_helper = PrimeNumberHelper(verbose = self.verbose)
        prime_factors = pn_helper.getPrimeFactors(num_exp * 2)

        soln_stmt = f"""
        We know:
        """
        soln_stmts.append(soln_stmt.strip())


        ##########################

        curr_exp = 1
        eqs = []
        for temp_exp,v in prime_factors.items():
            for _ in range(1, v + 1):
                
                mod_a = a ** curr_exp
                mod_b = b ** curr_exp
                mod_ab = ueval(a * b) ** curr_exp
                
                soln_stmt = f"""
                $\\displaystyle 
                \\begin{{aligned}}
                """
                soln_stmts.append(soln_stmt.strip())
                
                if temp_exp == 2:
                    soln_stmt = f"""
                    {latex(mod_a ** 2)}  + {latex(mod_b ** 2)} 
                    & = \\left( {latex(mod_a)}  + {latex(mod_b)} \\right)^2
                      - 2 \\; {latex(mod_a)} \\; {latex(mod_b)}
                      \\\\ \\\\
                    & = \\left( {latex(mod_a)}  + {latex(mod_b)} \\right)^2
                      - 2 \\; {latex(mod_ab)}
                    """
                    soln_stmts.append(soln_stmt.strip())
                    curr_exp = curr_exp * temp_exp
                elif temp_exp == 3:
                    soln_stmt = f"""
                    {latex(mod_a ** 3)}  + {latex(mod_b ** 3)} 
                    & = \\left( {latex(mod_a)}  + {latex(mod_b)} \\right)^3
                      - 3 \\; {latex(mod_a)} \\; {latex(mod_b)}
                        \\; \\left( {latex(mod_a)}  + {latex(mod_b)} \\right)
                      \\\\ \\\\
                    & = \\left( {latex(mod_a)}  + {latex(mod_b)} \\right)^3
                      - 3 \\; {latex(mod_ab)}
                        \\; \\left( {latex(mod_a)}  + {latex(mod_b)} \\right)
                    """
                    soln_stmts.append(soln_stmt.strip())
                    curr_exp = curr_exp * temp_exp
                    pass
                else:
                    soln_stmt = f"""
                    & \\text{{Invalid temp_exp }} {temp_exp}
                    """
                    soln_stmts.append(soln_stmt.strip())
                    curr_exp = curr_exp * temp_exp
                    pass
                    
                
                soln_stmt = f"""
                \\end{{aligned}}
                $
                """
                soln_stmts.append(soln_stmt.strip())
                pass
            pass
        ######################################
        subs_dict = {}

        a_plus_b_val = a_val + b_val

        subs_dict[a + b] = a_plus_b_val 
        
        soln_stmt = f"""
        Let us calculate,
        
        $\\displaystyle 
        \\begin{{aligned}}
        
        {latex(a)} + {latex(b)}
        & = \\left( {latex(a_val)} \\right) + \\left( {latex(b_val)} \\right) 
        \\\\ \\\\
        & = {latex(a_plus_b_val)}
        \\qquad \\cdots \\text{{ (ii) }}

        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())


        a_mul_b_val = sp.simplify(a_val * b_val)

        subs_dict[a * b] = a_mul_b_val 
        
        soln_stmt = f"""
        $\\displaystyle 
        \\begin{{aligned}}
        {latex(a)}  {latex(b)}
        
        & = \\left( {latex(a_val)} \\right)  \\left( {latex(b_val)} \\right) 
        \\\\ \\\\
        & = \\left( {latex(sp.sqrt(num1))} \\right)^2 - \\left( {latex(num2)} \\right)^2
        \\\\ \\\\
        & =  {latex(num1)} - {latex(num2 ** 2)} 
        \\\\ \\\\
        & = {latex(a_mul_b_val)}
        \\qquad \\cdots \\text{{ (iii) }}

        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())


        ##########################

        curr_exp = 1
        eqs = []
        for temp_exp,v in prime_factors.items():
            for _ in range(1, v + 1):
                
                mod_a = a ** curr_exp
                mod_b = b ** curr_exp
                mod_ab = ueval(a * b) ** curr_exp
                mod_ab_val = Symbol(f""" \\left( {latex(a_mul_b_val)} \\right)^{{ {latex(curr_exp)} }} """) 

                temp_a_plus_b_val = subs_dict.get(mod_a + mod_b, None)
                temp_a_mul_b_val = subs_dict.get(mod_a * mod_b, None)

                soln_stmt = f"""
                $\\displaystyle 
                \\begin{{aligned}}
                """
                soln_stmts.append(soln_stmt.strip())
                
                if temp_exp == 2:
                    temp_val = temp_a_plus_b_val ** 2 - 2 * temp_a_mul_b_val
                    
                    soln_stmt = f"""
                    {latex(mod_a ** 2)}  + {latex(mod_b ** 2)} 
                    & = \\left( {latex(mod_a)}  + {latex(mod_b)} \\right)^2
                      - 2 \\cdot {latex(mod_ab)}
                    \\\\ \\\\
                    
                    & = \\left( {latex(temp_a_plus_b_val)} \\right)^2
                      - 2 \\cdot {latex(mod_ab_val)}
                    \\\\ \\\\
                      
                    & = {latex(temp_a_plus_b_val ** 2)} 
                      - {latex(2 * temp_a_mul_b_val)}
                    \\\\ \\\\
                    
                    & = {latex(temp_val)}
                      
                    """
                    soln_stmts.append(soln_stmt.strip())
                    
                    subs_dict[mod_a ** 2 + mod_b ** 2] = temp_val 
                    
                    temp_mul = mod_a ** 2 * mod_b ** 2
                    subs_dict[temp_mul] = temp_mul.subs(subs_dict)

                    curr_exp = curr_exp * temp_exp
                elif temp_exp == 3:
                    temp_val = temp_a_plus_b_val ** 3 - 3 * temp_a_mul_b_val * temp_a_plus_b_val
                    
                    soln_stmt = f"""
                    {latex(mod_a ** 3)}  + {latex(mod_b ** 3)} 
                    & = \\left( {latex(mod_a)}  + {latex(mod_b)} \\right)^3
                      - 3 \\cdot {latex(mod_ab)}
                        \\cdot \\left( {latex(mod_a)}  + {latex(mod_b)} \\right)
                    \\\\ \\\\
                    
                    & = \\left( {latex(temp_a_plus_b_val)} \\right)^3
                      - 3 \\cdot {latex(mod_ab_val)}
                        \\cdot \\left( {latex(temp_a_plus_b_val)} \\right)
                    \\\\ \\\\
                    
                    & = {latex(temp_a_plus_b_val ** 3)} 
                      - {latex(3 * temp_a_mul_b_val * temp_a_plus_b_val)}
                    \\\\ \\\\
                    
                    & = {latex(temp_val)}
                    
                    """
                    soln_stmts.append(soln_stmt.strip())
                    
                    subs_dict[mod_a ** 3 + mod_b ** 3] = temp_val 

                    temp_mul = mod_a ** 3 * mod_b ** 3
                    subs_dict[temp_mul] = temp_mul.subs(subs_dict)

                    curr_exp = curr_exp * temp_exp
                    pass
                else:
                    soln_stmt = f"""
                    & \\text{{Invalid temp_exp }} {temp_exp}
                    """
                    soln_stmts.append(soln_stmt.strip())
                    curr_exp = curr_exp * temp_exp
                    pass
                    
                
                soln_stmt = f"""
                \\end{{aligned}}
                $
                """
                soln_stmts.append(soln_stmt.strip())
                pass
            pass
        ######################################


        numr_key = a ** (num_exp * 2) + b ** (num_exp * 2)
        denm_base_key = a * b

        numr_val = subs_dict.get(numr_key, None)
        denm_base_val = subs_dict.get(denm_base_key, None) 

        expr_val = Rational(numr_val, denm_base_val ** num_exp)
    
        soln_stmt = f"""
        Finally, we can calculate the expression from (i).
        
        $\\displaystyle 
        \\begin{{aligned}}
        
        & {latex(a_by_b_pow_n)} + {latex(b_by_a_pow_n)}
        \\\\ \\\\

        = & \\frac{{ {latex(numr_step1)} }} {{ {latex(denm_step1)} }} 
        \\\\ \\\\

        = & \\frac {{ {latex(numr_val)} }} 
                   {{ \\left( {latex(denm_base_val)} \\right)^{{ {latex(num_exp)} }} }}
        \\\\ \\\\

        = & \\frac {{ {latex(numr_val)} }} 
                   {{ {latex(denm_base_val ** num_exp)}  }}
        \\\\ \\\\

        = & {latex(expr_val)}

        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())



        soln_stmt = f"""
        
        """
        soln_stmts.append(soln_stmt.strip())





        answer_s = f"""
        $\\displaystyle {latex(expr_val)} $
        """
        
        soln_s = "\n\n".join(soln_stmts)
        
                
        result = {
            'prob_s': prob_s.replace("\n", " ").strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        pass

    def _problem_simplify_expr_type_2(self, **kwargs):
        
        a = random.randint(1, 9)
        b = random.randint(1, 9)
        c = random.choice([2, 3, 5, 6, 7, 8])
        
        x, y = symbols("x, y")
        
        
        
        expr = sp.sqrt(a + sp.sqrt(b * sp.sqrt(c)))
        sqrt_c_symb = Symbol(f"""\\sqrt {{ {latex(c)} }}""") 
        b_root_c_symb = Symbol(f""" {latex(b)} {latex(sqrt_c_symb)} """)
        
        b_root_c_sqr = (b * sp.sqrt(c)) ** 2
        
        expr_sqr_symb = Symbol(f"""{latex(a)} + {latex(b_root_c_symb)}""") 
        expr_symb = Symbol(f""" \\sqrt {{ {latex(expr_sqr_symb)} }}""") 
        
        xy_val = b_root_c_sqr / 4
        
        quad_eq = sp.Eq(x**2 - a * x + xy_val, 0)
        
        
        x_vals = sp.solve(quad_eq, x)
        y_vals = [xy_val / x_val for x_val in x_vals]
        
        prob_s = f"""
        If $\\displaystyle 
        {latex(expr_symb)} 
        = {latex(sp.sqrt(x))} + {latex(sp.sqrt(y))} 
        $, find the values of {latex(x)} and {latex(y)}.
        """
        
        soln_stmts = []
        
        soln_stmt = f"""
        Let
        $\\displaystyle 
        \\begin {{aligned}}
        
        & {latex(expr_symb)} 
        = {latex(sp.sqrt(x))} + {latex(sp.sqrt(y))} 
        \\qquad \\cdots \\text {{ (i) }} 
        \\\\ \\\\

        \\implies
        & {latex(expr_sqr_symb)} 
        = {latex(x)} + {latex(y)} + 2 {latex(sp.sqrt(x * y))} 
        \\\\ \\\\

        \\implies
        & {latex(x)} + {latex(y)} = {latex(a)}
        \\\\
        & 2 {latex(sp.sqrt(x * y))} = {latex(b_root_c_symb)}
        \\\\ \\\\

        \\implies
        & {latex(x)} + {latex(y)} = {latex(a)}
        \\\\
        & 4 {latex(x * y)} = {latex(b_root_c_sqr)}
        \\\\ \\\\

        \\implies
        & {latex(x * x)} + {latex(y * x)} = {latex(a * x)}
        \\qquad \\text {{ multiplied both side with }} {latex(x)}
        \\\\
        & {latex(x * y)} = {latex(xy_val)}
        \\qquad \\text {{ divided both side with }} {latex(4)}
        \\qquad \\cdots \\text {{ (ii) }} 
        \\\\ \\\\

        \\implies
        & {latex(x * x)} + {latex(xy_val)} = {latex(a * x)}
        \\qquad \\text {{ replaced the value from the second into the first }} 
        \\\\ \\\\

        \\implies
        & {latex(quad_eq)}
        \\\\ \\\\

        \\implies
        & {latex(x)} = {latex(x_vals)}
        \\\\ \\\\

        \\implies
        & {latex(y)} = {latex(y_vals)}
        \\qquad \\cdots \\text {{ using (ii) }}, {latex(y)} = {latex(xy_val / x)}
        \\\\ \\\\

        \\end {{aligned}}
        $
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
         """
        soln_stmts.append(soln_stmt.strip())

        soln_stmt = f"""
         """
        soln_stmts.append(soln_stmt.strip())

        soln_stmt = f"""
         """
        soln_stmts.append(soln_stmt.strip())

        soln_stmt = f"""
         """
        soln_stmts.append(soln_stmt.strip())

        answer_s = f"""
        """
        
        soln_s = "\n\n".join(soln_stmts)
        
                
        result = {
            'prob_s': prob_s.replace("\n", " ").strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        pass



    def _SAMPLE(self, **kwargs):
        
        prob_s = f"""
        """
        
        soln_stmts = []
        
        soln_stmt = f"""
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
         """
        soln_stmts.append(soln_stmt.strip())

        answer_s = f"""
        """
        
        soln_s = "\n\n".join(soln_stmts)
        
                
        result = {
            'prob_s': prob_s.replace("\n", " ").strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        pass


    pass
#class AdvancedPowerHelper:
