'''
Created on 19-Apr-2024

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

from sympy import (symbols, Symbol, limit, latex, Function,
                   Limit, oo, Derivative, simplify,
                   Eq,
                   Derivative, Integral,
                   exp, log, expand_log,
                   Mul, Add, Pow, UnevaluatedExpr,
                   Sum, binomial, factorial, Rational,
                   UnevaluatedExpr as ueval
                   )

from sympy import (sin, cos, tan, cot, sec, csc,
                   asin, acos, atan, acot, asec, acsc,
                   sinh, cosh, tanh, coth, sech, csch,
                   asinh, acosh, atanh, acoth, asech, acsch,
                   )

import sympy as sp

from xv.km.util import latex_fraction
from xv.util import UtilClass

# _std_integration_by_subs_helper
# StdIntegrationBySubsHelper

from ._std_integration_base_helper import StdIntegrationBaseHelper

class StdIntegrationBySubsHelper(StdIntegrationBaseHelper):
    '''
    integration of standard functions by reversing the result of differntiation
    '''

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        pass


    def get_standrad_integral_by_substitution(self, index = None):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        if index is None:
            random_choices = list(range(1, 13))

            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._get_sqrt_x_minus_1_x_sqr()

        if random_choice == 2:
            return self._get_ax_plus_b_by_cx_plus_d()

        if random_choice == 3:
            return self._get_1_by_ax_plus_b_times_cx_plus_d()

        if random_choice == 4:
            return self._get_px_plus_q_by_x_plus_a_times_x_plus_b()

        if random_choice == 5:
            return self._get_px_plus_q_by_x_plus_a_sqr()

        if random_choice == 6:
            return self._get_px_sqr_plus_qx_plus_r_by_x_plus_a_times_x_plus_b_times_x_plus_c()

        if random_choice == 7:
            return self._get_px_sqr_plus_qx_plus_r_by_x_plus_a_sqr_times_x_plus_b()

        if random_choice == 8:
            return self._get_1_by_px_sqr_plus_qx_plus_r()

        if random_choice == 9:
            return self._get_1_by_sqrt_over_px_sqr_plus_qx_plus_r()

        if random_choice == 10:
            return self._get_ax_plus_b_by_sqrt_over_px_sqr_plus_qx_plus_r()

        if random_choice == 11:
            return self._get_px_sqr_plus_qx_plus_r_by_x_plus_a_times_x_sqr_plus_bx_plus_c()

        if random_choice == 12:
            return self._get_x_times_sqrt_1_plus_a_x_sqr()

        if random_choice == 13:
            return self._get_exp_x_mul_sqrt_1_plus_exp_2x()

        pass


    def _get_sqrt_x_minus_1_x_sqr(self, x = None):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        if x is None:
            x = self.x

        try:
            I = Symbol('I')
            z = Symbol('z')

            dzdx = Derivative(z, x)

            z_val =  x - 1


            dz_val_dx = Derivative(z_val, x)
            dz_val_dx_doit = dz_val_dx.doit()


            C = self.C
            f = x ** 2 * sp.sqrt(x -  1)

            Intgl_f = Integral(f, x)
            Intgl_f_doit = Intgl_f.doit()

            g_ueval = Symbol(f""" {latex((z + 1) ** 2)} \\cdot {latex(sp.sqrt(z))} """)
            g = ((z + 1) ** 2 * sp.sqrt(z)).expand()


            g_jacob_ueval = Symbol(f"""
            \\left( {latex(g, root_notation = False)} \\right)
            \\cdot \\left(
            \\frac {{ 1 }}  {{ {latex(dzdx)} }}
            \\right)
            """)

            g_jacob = g / dz_val_dx_doit

            Intgl_g_jacob_ueval = Integral(g_jacob_ueval, z)

            Intgl_g_jacob = Integral(g_jacob, z)
            Intgl_g_jacob_doit = Intgl_g_jacob.doit()

            Intgl_g_jacob_doit_subs = Intgl_g_jacob_doit.subs(z, z_val)

            prob_s = f"""
            Find integral of $\\displaystyle {latex(f)}
            $ with respect to $\\displaystyle {latex(x)}$.
            """


            soln_stmts = []

            soln_stmt = f"""
            Let ${latex(z)} = {latex(z_val)} $

            $\\displaystyle
            \\implies
            \\left \\{{

            \\begin{{aligned}}

            \\implies {latex(dzdx)}
            & = {latex(dz_val_dx)}
            = {latex(dz_val_dx_doit)}
            \\qquad \\cdots \\text {{ (i) }}
            \\\\ \\\\

            {latex(f)}
            & = {latex(g_ueval)}
            \\\\ \\\\

            & = {latex(g, root_notation = False)}
            \\qquad \\cdots \\text {{ (ii) }}

            \\end{{aligned}}

            \\right .
            $

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



            soln_stmt = f"""
            Now, we can calculate the integration.

            $\\displaystyle
            \\begin{{aligned}}

            {latex(I)}
            & = {latex(Intgl_f)}
            \\\\ \\\\

            & = {latex(Intgl_g_jacob_ueval)}
            \\qquad \\text {{ substitute ${latex(z_val)} = {latex(z)}$ }}
            \\\\ \\\\

            & = {latex(Intgl_g_jacob, root_notation = False)}
            \\qquad \\text {{ substitute ${latex(dzdx)} = {latex(dz_val_dx_doit)}$ }}
            \\\\ \\\\

            & = {latex(Intgl_g_jacob_doit)}
            \\\\ \\\\

            & = {latex(Intgl_g_jacob_doit_subs)}
            \\qquad \\text {{ substitute ${latex(z)} = {latex(z_val)}$ }}
            \\\\ \\\\

            & = {latex(Intgl_g_jacob_doit_subs)} + {latex(C)}
            \\qquad \\text{{ added integration constant }} {latex(C)}

            \\end{{aligned}}
            $

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

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


            soln_s = "\n\n".join(soln_stmts)



            answer_s = f"""
            $\\displaystyle {latex(Intgl_g_jacob_doit_subs)} + {latex(C)} $
            """

            result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }

            return result

        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 _get_ax_plus_b_by_cx_plus_d(self, x = None):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        if x is None:
            x = self.x

        try:
            I = Symbol('I')
            z = Symbol('z')

            dzdx = Derivative(z, x)

            a, b, c, d = sp.symbols("m, n, p, q", real = True)

            f_numr = a * x + b
            f_denm = c * x + d
            x_val = sp.solve(sp.Eq(z, f_denm), x)[0]

            f = f_numr / f_denm

            z_val = f_numr

            dz_val_dx = Derivative(z_val, x)
            dz_val_dx_doit = dz_val_dx.doit()


            C = self.C

            Intgl_f = Integral(f, x)
            Intgl_f_doit = Intgl_f.doit()

            g_ueval = Symbol(f""" {latex(f.subs(x, x_val))} """)
            g = f.subs(x, x_val).simplify().expand()

            prob_s = f"""
            Find integral of $\\displaystyle {latex(f)}
            $ with respect to $\\displaystyle {latex(x)}$.
            """


            soln_stmts = []

            soln_stmt = f"""
            Let ${latex(z)} = {latex(z_val)} $

            $\\displaystyle
            \\implies
            \\left \\{{

            \\begin{{aligned}}

            {latex(dzdx)}
            & = {latex(dz_val_dx)}
            = {latex(dz_val_dx_doit)}
            \\qquad \\cdots \\text {{ (i) }}
            \\\\ \\\\

            {latex(x)}
            & = {latex(x_val)}
            \\qquad \\cdots \\text {{ (ii) }}
            \\\\ \\\\

            {latex(f)}
            & = {latex(g_ueval)}
            \\\\ \\\\

            & = {latex(g, root_notation = False)}
            \\qquad \\cdots \\text {{ (iii) }}

            \\end{{aligned}}

            \\right .
            $

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

            g_jacob_ueval = Symbol(f"""
            \\left( {latex(g, root_notation = False)} \\right)
            \\cdot \\left(
            \\frac {{ 1 }}  {{ {latex(dzdx)} }}
            \\right)
            """)

            g_jacob = g / dz_val_dx_doit
            g_jacob_simp = g_jacob.simplify().expand()

            Intgl_g_jacob_ueval = Integral(g_jacob_ueval, z)
            Intgl_g_jacob = Integral(g_jacob, z)

            Intgl_g_jacob_simp = Integral(g_jacob_simp, z)

            Intgl_g_jacob_doit = Intgl_g_jacob.doit().simplify().expand()

            Intgl_g_jacob_doit_subs = Intgl_g_jacob_doit.subs(z, z_val)

            '''

            '''

            soln_stmt = f"""
            Now, we can calculate the integration.

            $\\displaystyle
            \\begin{{aligned}}

            {latex(I)}
            & = {latex(Intgl_f)}
            \\\\ \\\\

            & = {latex(Intgl_g_jacob_ueval)}
            \\qquad \\text {{ substitute ${latex(z_val)} = {latex(z)}$ }}
            \\\\ \\\\

            & = {latex(Intgl_g_jacob, root_notation = False)}
            \\qquad \\text {{ substitute ${latex(dzdx)} = {latex(dz_val_dx_doit)}$ }}
            \\\\ \\\\

            & = {latex(Intgl_g_jacob_simp, root_notation = False)}
            \\\\ \\\\

            & = {latex(Intgl_g_jacob_doit)}
            \\\\ \\\\

            & = {latex(Intgl_g_jacob_doit_subs)}
            \\qquad \\text {{ substitute ${latex(z)} = {latex(z_val)}$ }}
            \\\\ \\\\

            & = {latex(Intgl_g_jacob_doit_subs)} + {latex(C)}
            \\qquad \\text{{ added integration constant }} {latex(C)}

            \\end{{aligned}}
            $

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

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


            soln_s = "\n\n".join(soln_stmts)



            answer_s = f"""
            $\\displaystyle {latex(Intgl_g_jacob_doit_subs)} + {latex(C)} $
            """

            result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }

            return result

        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 _get_1_by_ax_plus_b_times_cx_plus_d(self, x = None):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        if x is None:
            x = self.x

        try:
            I = Symbol('I')
            y = Symbol('y')
            z = Symbol('z')
            lmbda1 = Symbol(f"\\lambda_1")
            lmbda2 = Symbol(f"\\lambda_2")

            dzdx = Derivative(z, x)

            a, b, c, d = sp.symbols("m, n, p, q", real = True)

            f_denm1 = a * x + b
            f_denm2 = c * x + d

            f_denm1_symb = Symbol(f""" \\left( {latex(f_denm1)} \\right) """)
            f_denm2_symb = Symbol(f""" \\left( {latex(f_denm2)} \\right) """)

            f = 1 / (f_denm1 * f_denm2)

            numr = (lmbda1 * f_denm2 + lmbda2 * f_denm1).expand().simplify()


            prob_s = f"""
            Find integral of $\\displaystyle {latex(f)}
            $ with respect to $\\displaystyle {latex(x)}$.
            """


            soln_stmts = []

            soln_stmt = f"""
            Let us transform our expression as follows so that we can integrate each terms easily.

            $\\displaystyle

            \\begin{{aligned}}

            {latex(f)}
            & = {latex(lmbda1 / f_denm1)} + {latex(lmbda2 / f_denm2)}
            \\qquad \\cdots \\text {{ (i) }}
            \\\\ \\\\

            \\implies
            {latex(f)}
            & = \\frac
            {{ {latex(lmbda1 * f_denm2_symb)} + {latex(lmbda2 * f_denm1_symb)} }}
            {{ {latex(f_denm1 * f_denm2)} }}
            \\\\ \\\\


            \\implies
            {latex(f)}
            & = \\frac
            {{ {latex(numr)} }}
            {{ {latex(f_denm1 * f_denm2)} }}

            \\end{{aligned}}

            $

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

            lmbda_eq1 = Eq(lmbda1 * c + lmbda2 * a, 0)
            lmbda_eq2 = Eq(lmbda1 * d + lmbda2 * b, 1)

            lmbda_solns = sp.solve((lmbda_eq1, lmbda_eq2), (lmbda1, lmbda2))

            print(lmbda_solns)

            lmbda1_val = lmbda_solns[lmbda1]
            lmbda2_val = lmbda_solns[lmbda2]

            soln_stmt = f"""
            Comparing numerator of both sides.

            $\\displaystyle

            \\begin{{aligned}}


            & {latex(lmbda1 * c)} + {latex(lmbda2 * a)} = 0
            \\qquad \\text {{ as coefficient of ${latex(x)}$ is zero. }}
            \\\\ \\\\

            & {latex(lmbda1 * d)} + {latex(lmbda2 * b)} = 1
            \\\\ \\\\

            \\implies
            & {latex(lmbda1)} = {latex(lmbda1_val)}
            \\\\ \\\\

            & {latex(lmbda2)} = {latex(lmbda2_val)}

            \\end{{aligned}}

            $


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

            I1 = Integral(lmbda1 / f_denm1, x).doit()
            I2 = Integral(lmbda2 / f_denm2, x).doit()

            I1_subs = I1.subs(lmbda1, lmbda1_val)
            I2_subs = I2.subs(lmbda2, lmbda2_val)

            I_val = I1_subs + I2_subs

            soln_stmt = f"""
            Integrating from (i):

            $\\displaystyle

            \\begin{{aligned}}

            {latex(I)}
            & = {latex(Integral(f, x))}
            \\\\ \\\\

            & = {latex(Integral(lmbda1 / f_denm1, x))}
              + {latex(Integral(lmbda2 / f_denm2, x))}
            \\\\ \\\\

            & = {latex(I1)}
              + {latex(I2)}
            \\\\ \\\\

            & \\text {{Replace the values of ${latex(lmbda1)}$ and ${latex(lmbda2)}$ }}
            \\\\ \\\\

            {latex(I)}
            & = {latex(I1_subs)}
              + {latex(I2_subs)}
            \\\\ \\\\

            \\end{{aligned}}

            $

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


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


            soln_s = "\n\n".join(soln_stmts)



            answer_s = f"""
            $\\displaystyle {latex(I_val)}$
            """

            result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }

            return result

        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 _get_px_plus_q_by_x_plus_a_times_x_plus_b(self, x = None):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        if x is None:
            x = self.x

        try:
            C = self.C
            I = Symbol('I')

            #a, b, p, q = sp.symbols("a, b, p, q", real = True)
            a, b, p, q = self._get_int_values(4)

            f_numr = p * x + q

            denm1 = (x + a)
            denm2 = (x + b)

            f_denm = denm1  * denm2

            f = f_numr / f_denm

            prob_s = f"""
            Find integral of $\\displaystyle {latex(f)}
            $ with respect to $\\displaystyle {latex(x)}$.
            """

            soln_stmts = []

            A, B = sp.symbols("A, B", real = True)

            numr_calc = (A * denm2 + B * denm1).simplify().expand()

            soln_stmt = f"""
            Let us express the expression as

            $\\displaystyle
            \\begin{{aligned}}

            {latex(f)}
            & = {latex(A / denm1)} + {latex(B / denm2)}
            \\\\ \\\\

            & = \\frac{{ {latex(A * denm2)} + {latex(B * denm1)} }} {{ {latex(denm1 * denm2)} }}
            \\\\ \\\\

            & = \\frac{{ {latex(numr_calc)} }} {{ {latex(denm1 * denm2)} }}
            \\qquad \\cdots \\text{{ (i) }}


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


            numr_poly = sp.Poly(numr_calc, x)

            eq1 = Eq(numr_poly.coeff_monomial(x), p)
            eq2 = Eq(numr_poly.coeff_monomial(1), q)


            AB_sols = sp.solve((eq1, eq2), (A, B))

            print(AB_sols)

            A_val = AB_sols[A]
            B_val = AB_sols[B]

            soln_stmt = f"""
            Comparing the numerator on both sides:

            $\\displaystyle
            \\begin{{aligned}}

            \\implies
            &
                \\left \\{{
                \\begin{{array}} {{ll}}
                & {latex(eq1)} \\qquad \\text{{coeff of }} {latex(x ** 1)} \\\\
                & {latex(eq2)} \\qquad \\text{{coeff of }} {latex(x ** 0)}
                \\end{{array}}
                \\right .
            \\\\ \\\\

            \\implies
            &
                \\left \\{{
                \\begin{{array}} {{ll}}
                & {latex(A)} = {latex(A_val)} \\\\
                & {latex(B)} = {latex(B_val)}
                \\end{{array}}
                \\right .

            \\\\ \\\\


            \\implies
            {latex(f)}
            & = {latex(A_val / denm1)} + {latex(B_val / denm2)}
            \\qquad \\cdots \\text{{ (ii) }}


            \\end{{aligned}}
            $

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

            I = Symbol('I')
            f1 = A_val / denm1
            f2 = B_val / denm2

            I_val = Integral(f1, x).doit() + Integral(f2, x).doit()
            I_val = I_val.simplify()

            soln_stmt = f"""
            Let us compute the integration

            $\\displaystyle
            \\begin{{aligned}}

            {latex(I)}
            & = {latex(Integral(f, x))}
            \\\\ \\\\

            & = {latex(Integral(f1, x))} + {latex(Integral(f2, x))}
            \\qquad \\text{{ using (ii) }}
            \\\\ \\\\

            & = {latex(Integral(f1, x).doit())} + {latex(Integral(f2, x).doit())}
            \\\\ \\\\

            & = {latex(I_val)}

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

            soln_s = "\n\n".join(soln_stmts)


            answer_s = f"""
            $\\displaystyle
            {latex(I_val)}
            $
            """

            result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }

            return result

        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 _get_px_plus_q_by_x_plus_a_sqr(self, x = None):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        if x is None:
            x = self.x

        try:
            C = self.C
            I = Symbol('I')

            #a, b, p, q = sp.symbols("a, b, p, q", real = True)
            a, b, p, q = self._get_int_values(4)

            f_numr = p * x + q
            denm = (x + a)

            f_denm = denm ** 2

            f = f_numr / f_denm

            prob_s = f"""
            Find integral of $\\displaystyle {latex(f)}
            $ with respect to $\\displaystyle {latex(x)}$.
            """

            soln_stmts = []

            A, B = sp.symbols("A, B", real = True)

            numr_calc = (A * denm + B).simplify().expand()

            soln_stmt = f"""
            Let us express the expression as

            $\\displaystyle
            \\begin{{aligned}}

            {latex(f)}
            & = {latex(A / denm)} + {latex(B / denm ** 2)}
            \\\\ \\\\

            & = \\frac{{ {latex(A * denm)} + {latex(B)} }} {{ {latex(denm ** 2)} }}
            \\\\ \\\\

            & = \\frac{{ {latex(numr_calc)} }} {{ {latex(denm ** 2)} }}
            \\qquad \\cdots \\text{{ (i) }}


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


            numr_poly = sp.Poly(numr_calc, x)

            eq1 = Eq(numr_poly.coeff_monomial(x), p)
            eq2 = Eq(numr_poly.coeff_monomial(1), q)

            #eq1 = Eq(A, p)
            #eq2 = Eq(A * a + B, q)

            AB_sols = sp.solve((eq1, eq2), (A, B))

            print(AB_sols)

            A_val = AB_sols[A]
            B_val = AB_sols[B]

            soln_stmt = f"""
            Comparing the numerator on both sides:

            $\\displaystyle
            \\begin{{aligned}}

            \\implies
            &
                \\left \\{{
                \\begin{{array}} {{ll}}
                & {latex(eq1)} \\qquad \\text{{coeff of }} {latex(x ** 1)} \\\\
                & {latex(eq2)} \\qquad \\text{{coeff of }} {latex(x ** 0)}
                \\end{{array}}
                \\right .
            \\\\ \\\\

            \\implies
            &
                \\left \\{{
                \\begin{{array}} {{ll}}
                & {latex(A)} = {latex(A_val)} \\\\
                & {latex(B)} = {latex(B_val)}
                \\end{{array}}
                \\right .
                \\qquad \\cdots \\text{{ (ii) }}

            \\\\ \\\\


            \\implies
            {latex(f)}
            & = {latex(A_val / denm)} + {latex(B_val / denm ** 2)}
            \\\\ \\\\


            \\end{{aligned}}
            $

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

            I = Symbol('I')
            f1 = A_val / denm
            f2 = B_val / denm ** 2

            I_val = Integral(f1, x).doit() + Integral(f2, x).doit()
            I_val = I_val.simplify()

            soln_stmt = f"""
            Let us compute the integration

            $\\displaystyle
            \\begin{{aligned}}

            {latex(I)}
            & = {latex(Integral(f, x))}
            \\\\ \\\\

            & = {latex(Integral(f1 + f2, x))}
            \\\\ \\\\

            & = {latex(Integral(f1, x))} + {latex(Integral(f2, x))}
            \\qquad \\text{{ using (i) and (ii) }}
            \\\\ \\\\

            & = {latex(Integral(f1, x).doit())} + {latex(Integral(f2, x).doit())}
            \\\\ \\\\

            & = {latex(I_val)}

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

            soln_s = "\n\n".join(soln_stmts)


            answer_s = f"""
            $\\displaystyle
            {latex(I_val)}
            $
            """

            result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }

            return result

        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 _get_px_sqr_plus_qx_plus_r_by_x_plus_a_times_x_plus_b_times_x_plus_c(self, x = None):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        if x is None:
            x = self.x

        try:
            C = self.C
            I = Symbol('I')

            #a, b, c, p, q, r = sp.symbols("a, b, c, p, q, r", real = True)
            a, b, c, p, q, r = self._get_int_values(6)

            f_numr = p * x**2 + q * x + r

            denm1 = x + a
            denm2 = x + b
            denm3 = x + c

            f_denm = denm1  * denm2 * denm3

            f = f_numr / f_denm

            prob_s = f"""
            Find integral of $\\displaystyle {latex(f)}
            $ with respect to $\\displaystyle {latex(x)}$.
            """

            soln_stmts = []

            A, B, C = sp.symbols("A, B, C", real = True)

            numr_calc = (A * denm2 * denm3
                         + B * denm3 * denm1
                         + C * denm1 * denm2).simplify().expand()


            soln_stmt = f"""
            Let us express the expression as


            $\\displaystyle
            \\begin{{aligned}}

            {latex(f)}
            & = {latex(A / denm1)} + {latex(B / denm2)} + {latex(C / denm3)}
            \\\\ \\\\

            & = \\frac{{
                          {latex(A)} \\left( {latex(denm2)} \\right) \\left( {latex(denm3)} \\right)
                        + {latex(B)} \\left( {latex(denm3)} \\right) \\left( {latex(denm1)} \\right)
                        + {latex(C)} \\left( {latex(denm1)} \\right) \\left( {latex(denm2)} \\right)

                      }}
                      {{ \\left( {latex(denm1)} \\right)
                         \\left( {latex(denm2)} \\right)
                         \\left( {latex(denm3)} \\right)
                      }}
            \\\\ \\\\

            & = \\frac{{ {latex(numr_calc)} }}
                      {{ \\left( {latex(denm1)} \\right)
                         \\left( {latex(denm2)} \\right)
                         \\left( {latex(denm3)} \\right)
                      }}
            \\qquad \\cdots \\text{{ (i) }}


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


            '''
            eq1 = Eq(A + B + C, p)
            eq2 = Eq(A * (b + c) + B * (c + a) + C * (a + b), q)
            eq3 = Eq(A * b * c + B * c * a + C * a * b, r)
            '''

            numr_poly = sp.Poly(numr_calc, x)

            eq1 = Eq(numr_poly.coeff_monomial(x **2), p)
            eq2 = Eq(numr_poly.coeff_monomial(x), q)
            eq3 = Eq(numr_poly.coeff_monomial(1), r)


            ABC_sols = sp.solve((eq1, eq2, eq3), (A, B, C))

            print(ABC_sols)

            A_val = ABC_sols[A]
            B_val = ABC_sols[B]
            C_val = ABC_sols[C]

            soln_stmt = f"""
            Comparing the numerator on both sides:

            $\\displaystyle
            \\begin{{aligned}}

            \\implies
            &
                \\left \\{{
                \\begin{{array}} {{ll}}
                & {latex(eq1)} \\qquad \\text{{coeff of }} {latex(x ** 2)} \\\\
                & {latex(eq2)} \\qquad \\text{{coeff of }} {latex(x ** 1)} \\\\
                & {latex(eq3)} \\qquad \\text{{coeff of }} {latex(x ** 0)}
                \\end{{array}}
                \\right .
            \\\\ \\\\

            \\implies
            &
                \\left \\{{
                \\begin{{array}} {{ll}}
                & {latex(A)} = {latex(A_val)} \\\\
                & {latex(B)} = {latex(B_val)} \\\\
                & {latex(C)} = {latex(C_val)}
                \\end{{array}}
                \\right .

            \\\\ \\\\


            \\implies
            {latex(f)}
            & = {latex(A_val / denm1)} + {latex(B_val / denm2)} + {latex(C_val / denm3)}
            \\qquad \\cdots \\text{{ (ii) }}

            \\end{{aligned}}
            $

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

            I = Symbol('I')
            f1 = A_val / denm1
            f2 = B_val / denm2
            f3 = C_val / denm3

            I_val = Integral(f1, x).doit() + Integral(f2, x).doit() + Integral(f3, x).doit()
            I_val = I_val.simplify()

            soln_stmt = f"""
            Let us compute the integration

            $\\displaystyle
            \\begin{{aligned}}

            {latex(I)}
            & = {latex(Integral(f, x))}
            \\\\ \\\\

            & = {latex(Integral(f1, x))} + {latex(Integral(f2, x))} + {latex(Integral(f3, x))}
            \\qquad \\text{{ using (ii) }}
            \\\\ \\\\

            & = {latex(Integral(f1, x).doit())}
              + {latex(Integral(f2, x).doit())}
              + {latex(Integral(f3, x).doit())}
            \\\\ \\\\

            & = {latex(I_val)}

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

            soln_s = "\n\n".join(soln_stmts)


            answer_s = f"""
            $\\displaystyle
            {latex(I_val)}
            $
            """


            result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }

            return result

        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 _get_px_sqr_plus_qx_plus_r_by_x_plus_a_sqr_times_x_plus_b(self, x = None):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        if x is None:
            x = self.x

        try:
            C = self.C
            I = Symbol('I')

            #a, b, c, p, q, r = sp.symbols("a, b, c, p, q, r", real = True)
            a, b, c, p, q, r = self._get_int_values(6)

            f_numr = p * x**2 + q * x + r


            denm1 = x + a
            denm2 = x + b

            denm1_sqr_symb = Symbol(f"\\left({latex(denm1)}\\right)^2")

            f_denm = denm1**2  * denm2

            f = f_numr / f_denm

            prob_s = f"""
            Find integral of $\\displaystyle {latex(f)}
            $ with respect to $\\displaystyle {latex(x)}$.
            """

            soln_stmts = []

            A, B, C = sp.symbols("A, B, C", real = True)

            numr_calc = (A * denm1 * denm2
                         + B * denm2
                         + C * denm1**2 ).simplify().expand()


            soln_stmt = f"""
            Let us express the expression as


            $\\displaystyle
            \\begin{{aligned}}

            {latex(f)}
            & = {latex(A / denm1)} + {latex(B / denm1_sqr_symb)} + {latex(C / denm2)}
            \\\\ \\\\

            & = \\frac{{
                          {latex(A)} \\left( {latex(denm1)} \\right) \\left( {latex(denm2)} \\right)
                        + {latex(B)} \\left( {latex(denm2)} \\right)
                        + {latex(C)} {latex(denm1_sqr_symb)}

                      }}
                      {{ {latex(denm1_sqr_symb)}
                         \\left( {latex(denm2)} \\right)
                      }}
            \\\\ \\\\

            & = \\frac{{ {latex(numr_calc)} }}
                      {{ {latex(denm1_sqr_symb)}
                         \\left( {latex(denm2)} \\right)
                      }}
            \\qquad \\cdots \\text{{ (i) }}

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

            numr_poly = sp.Poly(numr_calc, x)

            eq1 = Eq(numr_poly.coeff_monomial(x **2), p)
            eq2 = Eq(numr_poly.coeff_monomial(x), q)
            eq3 = Eq(numr_poly.coeff_monomial(1), r)

            ABC_sols = sp.solve((eq1, eq2, eq3), (A, B, C))

            print(ABC_sols)

            A_val = ABC_sols[A]
            B_val = ABC_sols[B]
            C_val = ABC_sols[C]

            soln_stmt = f"""
            Comparing the numerator on both sides:

            $\\displaystyle
            \\begin{{aligned}}

            \\implies
            &
                \\left \\{{
                \\begin{{array}} {{ll}}
                & {latex(eq1)} \\qquad \\text{{coeff of }} {latex(x ** 2)} \\\\
                & {latex(eq2)} \\qquad \\text{{coeff of }} {latex(x ** 1)} \\\\
                & {latex(eq3)} \\qquad \\text{{coeff of }} {latex(x ** 0)}
                \\end{{array}}
                \\right .
            \\\\ \\\\

            \\implies
            &
                \\left \\{{
                \\begin{{array}} {{ll}}
                & {latex(A)} = {latex(A_val)} \\\\
                & {latex(B)} = {latex(B_val)} \\\\
                & {latex(C)} = {latex(C_val)}
                \\end{{array}}
                \\right .

            \\\\ \\\\


            \\implies
            {latex(f)}
            & = {latex(A_val / denm1)} + {latex(B_val / denm1_sqr_symb)} + {latex(C_val / denm2)}
            \\qquad \\cdots \\text{{ (ii) }}

            \\end{{aligned}}
            $

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

            I = Symbol('I')
            f1 = A_val / denm1
            f2 = B_val / denm1 ** 2
            f3 = C_val / denm2

            I_val = Integral(f1, x).doit() + Integral(f2, x).doit() + Integral(f3, x).doit()
            I_val = I_val.simplify()

            soln_stmt = f"""
            Let us compute the integration

            $\\displaystyle
            \\begin{{aligned}}

            {latex(I)}
            & = {latex(Integral(f, x))}
            \\\\ \\\\

            & = {latex(Integral(f1, x))} + {latex(Integral(f2, x))} + {latex(Integral(f3, x))}
            \\qquad \\text{{ using (ii) }}
            \\\\ \\\\

            & = {latex(Integral(f1, x).doit())}
              + {latex(Integral(f2, x).doit())}
              + {latex(Integral(f3, x).doit())}
            \\\\ \\\\

            & = {latex(I_val)}

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

            soln_s = "\n\n".join(soln_stmts)


            answer_s = f"""
            $\\displaystyle
            {latex(I_val)}
            $
            """


            result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }

            return result

        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 _get_1_by_px_sqr_plus_qx_plus_r(self, x = None):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        if x is None:
            x = self.x

        try:
            C = self.C
            I = Symbol('I')

            k, m, n, p, q, r = self._get_int_values(6)

            f_numr = p * x + q
            f_denm = k * x**2 + m * x + n

            eq_denm = Eq(f_denm, 0)

            f_denm_roots = sp.solve(eq_denm, x)
            a = - f_denm_roots[0]
            b = - f_denm_roots[-1]

            denm1 = x + a
            denm2 = x + b

            f = f_numr / f_denm

            prob_s = f"""
            Find integral of $\\displaystyle {latex(f)}
            $ with respect to $\\displaystyle {latex(x)}$.
            """

            soln_stmts = []

            A, B = sp.symbols("A, B", real = True)

            numr_calc = (A * denm2 + B * denm1).simplify().expand()

            soln_stmt = f"""
            Let us find roots of the denominator of the expression

            $\\displaystyle
            \\begin{{aligned}}

            & {latex(eq_denm)}
            \\\\ \\\\

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

            \\implies
            & {latex(f_denm)} = \\left( {latex(denm1)} \\right) \\left( {latex(denm2)} \\right)
            \\qquad \\cdots \\text{{ (i) }}

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


            soln_stmt = f"""
            Let us express the expression as

            $\\displaystyle
            \\begin{{aligned}}

            {latex(f)}
            & = {latex(A / denm1)} + {latex(B / denm2)}
            \\\\ \\\\

            & = \\frac{{ {latex(A * denm2)} + {latex(B * denm1)} }} {{ {latex(denm1 * denm2)} }}
            \\\\ \\\\

            & = \\frac{{ {latex(numr_calc)} }} {{ {latex(denm1 * denm2)} }}
            \\qquad \\cdots \\text{{ (ii) }}


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


            numr_poly = sp.Poly(numr_calc, x)

            eq1 = Eq(numr_poly.coeff_monomial(x), p)
            eq2 = Eq(numr_poly.coeff_monomial(1), q)


            AB_sols = sp.solve((eq1, eq2), (A, B))


            A_val = AB_sols[A]
            B_val = AB_sols[B]

            soln_stmt = f"""
            Comparing the numerator on both sides:

            $\\displaystyle
            \\begin{{aligned}}

            \\implies
            &
                \\left \\{{
                \\begin{{array}} {{ll}}
                & {latex(eq1)} \\qquad \\text{{coeff of }} {latex(x ** 1)} \\\\
                & {latex(eq2)} \\qquad \\text{{coeff of }} {latex(x ** 0)}
                \\end{{array}}
                \\right .
            \\\\ \\\\

            \\implies
            &
                \\left \\{{
                \\begin{{array}} {{ll}}
                & {latex(A)} = {latex(A_val)} \\\\
                & {latex(B)} = {latex(B_val)}
                \\end{{array}}
                \\right .

            \\\\ \\\\


            \\implies
            {latex(f)}
            & = {latex(A_val / denm1)} + {latex(B_val / denm2)}
            \\qquad \\cdots \\text{{ (iii) }}


            \\end{{aligned}}
            $

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

            I = Symbol('I')
            f1 = A_val / denm1
            f2 = B_val / denm2

            I_val = Integral(f1, x).doit() + Integral(f2, x).doit()
            I_val = I_val.simplify()

            soln_stmt = f"""
            Let us compute the integration

            $\\displaystyle
            \\begin{{aligned}}

            {latex(I)}
            & = {latex(Integral(f, x))}
            \\\\ \\\\

            & = {latex(Integral(f1, x))} + {latex(Integral(f2, x))}
            \\qquad \\text{{ using (iii) }}
            \\\\ \\\\

            & = {latex(Integral(f1, x).doit())} + {latex(Integral(f2, x).doit())}
            \\\\ \\\\

            & = {latex(I_val)}

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

            soln_s = "\n\n".join(soln_stmts)


            answer_s = f"""
            $\\displaystyle
            {latex(I_val)}
            $
            """

            result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }

            return result

        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 _get_1_by_sqrt_over_px_sqr_plus_qx_plus_r(self, x = None, int_values = None):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        if x is None:
            x = self.x

        try:
            C = self.C
            I = Symbol('I')

            if not int_values is None:
                k, m, n = int_values
            else:
                k, m, n = self._get_int_values(3)


            p = 0
            q = 1

            f_numr = p * x + q
            f_denm_sqr = k * x**2 + m * x + n

            f_denm = sp.sqrt(f_denm_sqr)
            f = f_numr / f_denm

            prob_s = f"""
            Find integral of $\\displaystyle {latex(f)}
            $ with respect to $\\displaystyle {latex(x)}$.
            """

            soln_stmts = []

            term1_sqrt = x + Rational(m, 2 * k)
            #term2_sqrt = sp.sqrt(Rational(m, 2 * k)**2 - Rational(n, k))

            term2 = - Rational(m, 2 * k)** 2 + Rational(n, k)
            term2_sqrt = sp.sqrt(term2)

            k_by_term2 = k / term2
            term1_sqrt_mod = term1_sqrt / sp.sqrt(term2)
            term2_mod = 1

            z = Symbol('z')

            soln_stmt = f"""
            Let us rewrite the quadratic expression in denominator to make it in form of $
            \\displaystyle {latex(1 + z**2)}
            $

            $\\displaystyle
            \\begin{{aligned}}

            {latex(f_denm_sqr)}
            & = {latex(k)} \\left( {latex(x**2 + Rational(m, k) * x + Rational(n, k))} \\right)
            \\\\ \\\\

            & = {latex(k)}
            \\left(
                {latex(x**2)} + 2 \\cdot {latex(Rational(m, 2 * k))} \\cdot {latex(x)}
                + \\left( {latex(Rational(m, 2 * k))} \\right)^2
                - \\left( {latex(Rational(m, 2 * k))} \\right)^2
                + {latex(Rational(n, k))}
            \\right)
            \\\\ \\\\


            & = {latex(k)}
            \\left(
                  \\left( {latex(x + Rational(m, 2 * k))} \\right)^2
                + \\left( {latex(term2)} \\right)
            \\right)
            \\\\ \\\\

            & = {latex(k_by_term2)}
            \\left(
                  \\left( {latex(term1_sqrt_mod)} \\right)^2
                + {latex(term2_mod)}
            \\right)
            \\qquad \\text{{Took common out}}  {latex(1 / term2)}
            \\qquad \\cdots \\text{{ (1A) }}

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

            z_val = term1_sqrt_mod
            z_val_symb = Symbol(f"\\left({latex(z_val)}\\right)")

            dz_dx_symb = Derivative(z, x)
            dz_dx = Derivative(z_val, x).doit()

            f_denm_sqr_symb = Symbol(f""" {latex(z ** 2)} + {latex(term2_mod)} """)


            soln_stmt = f"""
            Let $\\displaystyle
            {latex(z)} = {latex(z_val_symb)}
            \\qquad \\cdots \\text{{ (2A) }}
            $

            $\\displaystyle
            \\begin{{aligned}}

            \\implies
            & {latex(f_denm_sqr)}
            = {latex(k_by_term2)} \\left( {latex(f_denm_sqr_symb)} \\right)
            \\qquad \\cdots \\text{{ (2B) }}
            \\\\ \\\\

            \\text {{Also, }}
            & {latex(dz_dx_symb)}
            = {latex(dz_dx)}
            \\\\ \\\\

            \\implies
            & d{latex(x)}
            = {latex(1 / dz_dx)} \\; d{latex(z)}
            \\qquad \\cdots \\text{{ (2C) }}

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

            dz_dx_inv = 1 / dz_dx

            f_mod1 = Symbol(f"""
            \\frac {{ 1 }} {{ \\sqrt{{ {latex(f_denm_sqr_symb)} }} }}
            """)

            f_mod1_term1_calc = dz_dx_inv / k_by_term2
            f_mod1_term2_calc = Integral(f_mod1, z)

            I1 = Symbol("I_1")

            soln_stmt = f"""
            Let us compute the integration

            $\\displaystyle
            \\begin{{aligned}}

            {latex(I)}
            & = {latex(Integral(f, x))}
            \\\\ \\\\

            & = \\frac {{ {latex(dz_dx_inv)} }} {{ \\sqrt{{ {latex(k_by_term2)}  }} }}
            {latex(Integral(f_mod1, z))}
            \\qquad \\text{{ using (2B) }}
            \\\\ \\\\

            & = {latex(f_mod1_term1_calc)} \\left( {latex(f_mod1_term2_calc)} \\right)
            \\\\ \\\\

            & = {latex(f_mod1_term1_calc)} {latex(I1)}
            \\qquad \\cdots \\text {{ (3A) }}
            \\end{{aligned}}
            $
            """
            soln_stmts.append(soln_stmt.strip())


            theta = Symbol(f"\\theta")

            soln_stmt = f"""

            $\\displaystyle
            \\begin{{aligned}}

            \\text{{Let }}
            {latex(z)}
            & = {latex(tan(theta))}
            \\qquad \\cdots \\text {{ (4A) }}
            \\\\ \\\\

            \\implies
            {latex(Derivative(z, theta))}
            & = {latex(Derivative(tan(theta), theta))}
             = {latex(sp.sec(theta)**2)}
            \\qquad \\cdots \\text {{ (4B) }}

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


            I1_expr1 = Symbol(f"""
            \\frac {{ 1 }}  {{ {latex(sp.sec(theta))} }}
            {latex(sp.sec(theta)**2)}
            """)

            I1_expr2 = Symbol(f"""
            {latex(sp.sec(theta))}
            """)

            w = Symbol('w')
            w_val = sp.sec(theta) + sp.tan(theta)

            I1_expr3 = Symbol(f"""
            {latex(sp.sec(theta))}
            \\frac {{ {latex(w_val)} }}
            {{ {latex(w_val)} }}
            """)


            soln_stmt = f"""

            $\\displaystyle
            \\begin{{aligned}}

            {latex(I1)}
            & = {latex(f_mod1_term2_calc)}
            \\\\ \\\\

            & = {latex(Integral(I1_expr1, theta))}
            \\\\ \\\\

            & = {latex(Integral(I1_expr2, theta))}
            \\\\ \\\\


            & = {latex(Integral(I1_expr3, theta))}
            \\qquad \\cdots \\text {{ (5A) }}

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

            dw_dtheta = Derivative(w_val, theta)
            dw_dtheta_doit = sp.sec(theta) **2 + sp.sec(theta) * tan(theta)
            dw_dtheta_doit_symb = Symbol(f"""
            {latex(sp.sec(theta))}
            \\left(
            {latex(tan(theta))} + {latex(sp.sec(theta))}
            \\right)
            """)

            soln_stmt = f"""

            $\\displaystyle
            \\begin{{aligned}}

            \\text{{Let }}
            {latex(w)}
            & = {latex(w_val)}
            \\qquad \\cdots \\text {{ (6A) }}
            \\\\ \\\\

            \\implies
            {latex(Derivative(w, theta))}
            & = {latex(Derivative(w_val, theta))}
            \\\\ \\\\

            & = {latex(dw_dtheta_doit)}
            \\\\ \\\\

            & = {latex(dw_dtheta_doit_symb)}
            \\qquad \\cdots \\text {{ (6B) }}

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


            I1_val = Integral(1 / w, w).doit()

            I1_val_of_theta = I1_val.subs(w, w_val)

            I1_val_of_z = I1_val_of_theta.subs({tan(theta): z,
                                                sec(theta): sp.sqrt(1 + z**2) })

            soln_stmt = f"""

            $\\displaystyle
            \\begin{{aligned}}

            \\implies
            {latex(I1)}
            & ={latex(Integral(1 / w, w))}
            \\qquad \\text {{ from (5A), (6A) and (6B) }}
            \\\\ \\\\


            & = {latex(I1_val)}
            \\\\ \\\\

            & = {latex(I1_val_of_theta)}
            \\qquad \\text {{ substituted from (6A), }} \\qquad {latex(w)} = {latex(w_val)}
            \\\\ \\\\

            & = {latex(I1_val_of_z)}
            \\qquad \\text {{ substituted from (4A), }} \\qquad {latex(z)} = {latex(tan(theta))}
            \\\\ \\\\

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


            I1_val_of_x = I1_val_of_z.subs(z, z_val)

            I_val_calc = f_mod1_term1_calc * I1_val_of_x

            I_val_calc_expanded = I_val_calc.simplify().expand()

            soln_stmt = f"""

            $\\displaystyle
            \\begin{{aligned}}

            \\implies
            {latex(I)}
            & = {latex(f_mod1_term1_calc)} {latex(I1_val_of_z)}
            \\qquad \\text {{ from (3A) }}
            \\\\ \\\\

            & = {latex(f_mod1_term1_calc)}  {latex(I1_val_of_x)}
            \\qquad \\text {{ substituted from (6A), }} \\qquad {latex(z)} = {latex(z_val)}
            \\\\ \\\\

            & = {latex(I_val_calc)}
            \\\\ \\\\

            & = {latex(I_val_calc_expanded)}
            \\\\ \\\\

            & = {latex(Integral(f, x).doit())}

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



            soln_s = "\n\n".join(soln_stmts)


            answer_s = f"""
            $\\displaystyle
            $
            """

            result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }

            return result

        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 _get_ax_plus_b_by_sqrt_over_px_sqr_plus_qx_plus_r(self, x = None):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        if x is None:
            x = self.x

        try:
            C = self.C
            I = Symbol('I')

            k, m, n, p, q = self._get_int_values(5)

            f_numr = p * x + q

            f_denm_sqr = k * x**2 + m * x + n
            f_denm = sp.sqrt(f_denm_sqr)

            f = f_numr / f_denm

            prob_s = f"""
            Find integral of $\\displaystyle {latex(f)}
            $ with respect to $\\displaystyle {latex(x)}$.
            """

            soln_stmts = []

            A, B = sp.symbols("A, B", real = True)

            df_denm_sqr_dx = Derivative(f_denm_sqr, x)
            df_denm_sqr_dx_doit = df_denm_sqr_dx.doit()

            numr_calc = (A * df_denm_sqr_dx_doit + B).simplify().expand()

            soln_stmt = f"""
            Let us rewrite the numerator.

            $\\displaystyle
            \\begin{{aligned}}

            {latex(f_numr)}
            & = {latex(A)} \\left( {latex(df_denm_sqr_dx)} \\right)
             + {latex(B)}
            \\\\ \\\\

            & = {latex(A)} \\left( {latex(df_denm_sqr_dx_doit)} \\right)
             + {latex(B)}
            \\\\ \\\\

            & = {latex(numr_calc)}
            \\qquad \\cdots \\text{{ (i) }}

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

            numr_poly = sp.Poly(numr_calc, x)

            eq1 = Eq(numr_poly.coeff_monomial(x), p)
            eq2 = Eq(numr_poly.coeff_monomial(1), q)

            AB_sols = sp.solve((eq1, eq2), (A, B))


            A_val = AB_sols[A]
            B_val = AB_sols[B]

            soln_stmt = f"""
            Comparing the both sides:

            $\\displaystyle
            \\begin{{aligned}}

            \\implies
            &
                \\left \\{{
                \\begin{{array}} {{ll}}
                & {latex(eq1)} \\qquad \\text{{coeff of }} {latex(x ** 1)} \\\\
                & {latex(eq2)} \\qquad \\text{{coeff of }} {latex(x ** 0)}
                \\end{{array}}
                \\right .
            \\\\ \\\\

            \\implies
            &
                \\left \\{{
                \\begin{{array}} {{ll}}
                & {latex(A)} = {latex(A_val)} \\\\
                & {latex(B)} = {latex(B_val)}
                \\end{{array}}
                \\right .

            \\\\ \\\\


            \\implies
            {latex(f_numr)}
            & = {latex(A_val)} \\left( {latex(df_denm_sqr_dx_doit)} \\right)
             + {latex(B_val)}
            \\\\ \\\\

            \\implies
            {latex(f_numr)}
            & = \\left( {latex(A_val * df_denm_sqr_dx_doit)} \\right)
             + {latex(B_val)}
            \\qquad \\cdots \\text{{ (ii) }}


            \\end{{aligned}}
            $

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

            I = Symbol('I')
            I1 = Symbol('I1')
            I2 = Symbol('I2')

            f1 = A_val * df_denm_sqr_dx_doit / f_denm


            I2_expr = 1 / f_denm
            f2 = B_val * I2_expr

            I_val = Integral(f1, x).doit() + Integral(f2, x).doit()
            I_val = I_val.simplify()

            soln_stmt = f"""
            Let us compute the integration

            $\\displaystyle
            \\begin{{aligned}}

            {latex(I)}
            & = {latex(Integral(f, x))}
            \\\\ \\\\

            & = {latex(Integral(f1, x))} + {latex(B_val)} \\cdot {latex(Integral(I2_expr, x))}
            \\qquad \\text{{ using (ii) }}
            \\\\ \\\\

            & = {latex(I1)} + {latex(B_val)} \\cdot {latex(I2)}
            \\qquad \\text{{ assumed. For detailed solution see other parts. }}
            \\\\ \\\\

            & = {latex(Integral(f1, x).doit())} + {latex(Integral(f2, x).doit())}
            \\\\ \\\\

            & = {latex(I_val)}

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


            f_denm_sqr_symb = Symbol(f"\\left({latex(f_denm)}\\right)")
            z = Symbol('z')

            f1_mod = A_val / sp.sqrt(z)

            I1_val = Integral(f1_mod, z).doit()
            I1_val_subs = I1_val.subs(z, f_denm_sqr)


            soln_stmt = f"""
            <h3>Part 1: Computation of ${latex(I1)} = {latex(Integral(f1, x))}$</h3>
            $\\displaystyle
            \\begin{{aligned}}

            \\text{{Let }}
            & {latex(f_denm_sqr)}
            = {latex(z)}
            \\\\ \\\\

            \\implies
            & \\left( {latex(Derivative(f_denm_sqr, x).doit())} \\right) d{latex(x)}
            = d{latex(z)}
            \\\\ \\\\

            \\implies
            & \left( {latex(A_val * Derivative(f_denm_sqr, x).doit())} \\right) d{latex(x)}
            = \\left( {latex(A_val)} \\right) d{latex(z)}
            \\qquad \\text{{ multiplied value of ${latex(A)} = {latex(A_val)}$ to both sides. }}
            \\\\ \\\\

            \\end{{aligned}}
            $

            $\\displaystyle
            \\begin{{aligned}}

            {latex(I1)}
            & = {latex(Integral(f1, x))}
            \\\\ \\\\

            & = {latex(Integral(f1_mod, z))}
            \\\\ \\\\

            & = {latex(I1_val)}
            \\\\ \\\\

            & = {latex(I1_val_subs)}


            \\end{{aligned}}
            $

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



            part2_result = self._get_1_by_sqrt_over_px_sqr_plus_qx_plus_r(
                                    x, int_values = (k, m, n))


            soln_stmt = f"""
            <h3>Part 2: Computation of ${latex(I2)} = {latex(Integral(I2_expr, x))}$</h3>

            {part2_result['soln_s']}
            """
            soln_stmts.append(soln_stmt.strip())



            soln_s = "\n\n".join(soln_stmts)


            answer_s = f"""
            $\\displaystyle
            {latex(I_val)}
            $
            """



            result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }

            return result

        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 _get_px_sqr_plus_qx_plus_r_by_x_plus_a_times_x_sqr_plus_bx_plus_c(self, x = None):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        if x is None:
            x = self.x

        try:
            C = self.C
            I = Symbol('I')

            #a, b, c, p, q, r = sp.symbols("a, b, c, p, q, r", real = True)
            a, b, c, p, q, r = self._get_int_values(6)

            f_numr = p * x**2 + q * x + r


            denm1 = x + a
            denm2 = x ** 2 + b * x + c

            denm1_sqr_symb = Symbol(f"\\left({latex(denm1)}\\right)^2")

            f_denm = denm1 * denm2

            f = f_numr / f_denm

            prob_s = f"""
            Find integral of $\\displaystyle {latex(f)}
            $ with respect to $\\displaystyle {latex(x)}$.
            """

            soln_stmts = []

            A, B, C = sp.symbols("A, B, C", real = True)

            numr_calc = (A * denm2
                         + (B * x + C) * denm1 ).simplify().expand()


            soln_stmt = f"""
            Let us express the expression as

            $\\displaystyle
            \\begin{{aligned}}

            {latex(f)}
            & = {latex(A / denm1)} + {latex((B * x + C) / denm2)}
            \\\\ \\\\

            & = \\frac{{
                          {latex(A)} \\left( {latex(denm2)} \\right)
                        + \\left( {latex(B * x + C)} \\right) \\left( {latex(denm1)} \\right)
                      }}
                      {{ \\left( {latex(denm1)} \\right)
                         \\left( {latex(denm2)} \\right)
                      }}
            \\\\ \\\\

            & = \\frac{{ {latex(numr_calc)} }}
                      {{ \\left( {latex(denm1)} \\right)
                         \\left( {latex(denm2)} \\right)
                      }}
            \\qquad \\cdots \\text{{ (i) }}

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

            numr_poly = sp.Poly(numr_calc, x)

            eq1 = Eq(numr_poly.coeff_monomial(x **2), p)
            eq2 = Eq(numr_poly.coeff_monomial(x), q)
            eq3 = Eq(numr_poly.coeff_monomial(1), r)

            ABC_sols = sp.solve((eq1, eq2, eq3), (A, B, C))

            print(ABC_sols)

            A_val = ABC_sols[A]
            B_val = ABC_sols[B]
            C_val = ABC_sols[C]

            soln_stmt = f"""
            Comparing the numerator on both sides:

            $\\displaystyle
            \\begin{{aligned}}

            \\implies
            &
                \\left \\{{
                \\begin{{array}} {{ll}}
                & {latex(eq1)} \\qquad \\text{{coeff of }} {latex(x ** 2)} \\\\
                & {latex(eq2)} \\qquad \\text{{coeff of }} {latex(x ** 1)} \\\\
                & {latex(eq3)} \\qquad \\text{{coeff of }} {latex(x ** 0)}
                \\end{{array}}
                \\right .
            \\\\ \\\\

            \\implies
            &
                \\left \\{{
                \\begin{{array}} {{ll}}
                & {latex(A)} = {latex(A_val)} \\\\
                & {latex(B)} = {latex(B_val)} \\\\
                & {latex(C)} = {latex(C_val)}
                \\end{{array}}
                \\right .

            \\\\ \\\\


            \\implies
            {latex(f)}
            & = {latex(A_val / denm1)} + {latex((B_val * x + C_val) / denm2)}
            \\qquad \\cdots \\text{{ (ii) }}

            \\end{{aligned}}
            $

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

            I = Symbol('I')
            f1 = A_val / denm1
            f2 = (B_val * x + C_val) / denm2

            I_val = Integral(f1, x).doit() + Integral(f2, x).doit()
            I_val = I_val.simplify()

            soln_stmt = f"""
            Let us compute the integration

            $\\displaystyle
            \\begin{{aligned}}

            {latex(I)}
            & = {latex(Integral(f, x))}
            \\\\ \\\\

            & = {latex(Integral(f1, x))} + {latex(Integral(f2, x))}
            \\qquad \\text{{ using (ii) }}
            \\\\ \\\\

            & = {latex(Integral(f1, x).doit())}
              + {latex(Integral(f2, x).doit())}
            \\\\ \\\\

            & = {latex(I_val)}

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

            soln_s = "\n\n".join(soln_stmts)


            answer_s = f"""
            $\\displaystyle
            {latex(I_val)}
            $
            """


            result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }

            return result

        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 _get_x_times_sqrt_1_plus_a_x_sqr(self, x = None):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        if x is None:
            x = self.x

        try:
            C = self.C
            I = Symbol('I')

            a = Symbol('a', real = True)
            z = Symbol('z')

            z_val = 1 + a * x**2

            f = x * sp.sqrt(z_val)

            prob_s = f"""
            Find integral of $\\displaystyle {latex(f)}
            $ with respect to $\\displaystyle {latex(x)}$.
            """

            soln_stmts = []

            soln_stmt = f"""

            $\\displaystyle
            \\begin{{aligned}}

            \\text{{Let }} {latex(z)}
            & = {latex(z_val)}
            \\\\ \\\\

            {latex(Derivative(z, x))}
            & = {latex(Derivative(z_val, x))}
            \\\\ \\\\

            & = {latex(Derivative(z_val, x).doit())}
            \\\\ \\\\

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

            dz_val_dx_doit = Derivative(z_val, x).doit()

            f_of_z = f.subs(z_val, z) / dz_val_dx_doit


            I = Symbol('I')

            I_val_of_z = Integral(f_of_z, z).doit()
            I_val_of_z = I_val_of_z.simplify()

            I_val = I_val_of_z.subs(z, z_val)

            soln_stmt = f"""
            Let us compute the integration

            $\\displaystyle
            \\begin{{aligned}}

            {latex(I)}
            & = {latex(Integral(f, x))}
            \\\\ \\\\

            & = {latex(Integral(f_of_z, z))}
            \\\\ \\\\

            & = {latex(I_val_of_z)}
            \\\\ \\\\

            & = {latex(I_val)}

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

            soln_s = "\n\n".join(soln_stmts)


            answer_s = f"""
            $\\displaystyle
            {latex(I_val)}
            $
            """


            result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            'I_val': I_val,
            }

            return result

        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 _get_exp_x_mul_sqrt_1_plus_exp_2x(self, x = None):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        if x is None:
            x = self.x

        try:
            C = self.C
            I = Symbol('I')

            a = Symbol('a', real = True)
            theta = Symbol('theta')

            f = sp.exp(x) * sp.sqrt(1 + sp.exp(2 * x))

            f_symb = Symbol(f"""
                        {latex(sp.exp(x))} \\cdot {latex(sp.sqrt(1 + sp.exp(2 * x)))}
                        """)


            prob_s = f"""
            Find integral of $\\displaystyle {latex(f_symb)}
            $ with respect to $\\displaystyle {latex(x)}$.
            """

            soln_stmts = []


            eq = Eq(sp.exp(x), sp.tan(theta))
            deq_dtheta1 = Eq(Derivative(eq.lhs, theta) ,
                            Derivative(eq.rhs, theta) )

            deq_dtheta2 = Eq(Derivative(eq.lhs, x).doit() * Derivative(x, theta) ,
                            sp.sec(theta) ** 2 )


            soln_stmt = f"""

            $\\displaystyle
            \\begin{{aligned}}

            \\text{{Let }}
            & {latex(eq)}
            \\\\ \\\\

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

            \\implies
            & {latex(deq_dtheta2)}

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


            I = Symbol('I')

            g = sp.sec(theta) ** 3

            g_symb = Symbol(f""" {latex( sp.sqrt( 1 + sp.tan(theta) ** 2 ) )} \\cdot
                                 {latex( sp.sec(theta) ** 2 )}
                            """)

            I_term1 = sp.sec(theta) * sp.tan(theta)
            I_term2 = sp.sec(theta) + sp.tan(theta)

            Ig_val_of_theta = Rational(1, 2) * (I_term1 + log(I_term2))

            I_val = Ig_val_of_theta.subs({sp.tan(theta): sp.exp(x),
                                          sp.sec(theta): sp.sqrt(1 + sp.exp(x) ** 2),
                                          })

            soln_stmt = f"""
            Let us compute the integration

            $\\displaystyle
            \\begin{{aligned}}

            {latex(I)}
            & = {latex(Integral(f_symb, x))}
            \\\\ \\\\

            & = {latex(Integral(g_symb, theta))}
            \\\\ \\\\

            & = {latex(Integral(g, theta))}
            \\\\ \\\\

            & = {latex(Ig_val_of_theta)}
            \\\\ \\\\

            & = {latex(I_val)}

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

            soln_s = "\n\n".join(soln_stmts)


            answer_s = f"""
            $\\displaystyle
            {latex(I_val)}
            $
            """


            result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            'I_val': I_val,
            }

            return result

        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 _get_int_values(self, size):


        int_values = random.sample(list(range(1, 10)), size)
        int_values = [x * random.choice([1, -1]) for x in int_values]
        return int_values
        pass
    pass
#class StdIntegrationBySubsHelper:
