'''
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,
                   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_differentiation_helper
# StdDifferentiationHelper



class StdDifferentiationHelper(UtilClass):
    '''
    differentiation of standard functions 
    '''    

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        
        self.a = Symbol('a')

        self.x = Symbol('x')
        self.y = Symbol('y')
        
        x = self.x
        self.deltax = Symbol(f"\\Delta {latex(x)}")
        self.x_plus_deltax = Symbol(f"{latex(x)} + \\Delta {latex(x)}")
        self.x_plus_deltax_minus_x = Symbol(f"\\left({latex(x)} + \\Delta {latex(x)}\\right) - {latex(x)}")
        self.x_plus_deltax_plus_x = Symbol(f"\\left({latex(x)} + \\Delta {latex(x)}\\right) + {latex(x)}")
        pass
    
    
    def get_standrad_derivative(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        
        random_choices = list(range(1, 4))

        identifier = f"{self.__class__.__name__}{id(self)}.{inspect.stack()[0][3]}.random_choices"
        random_choice = self.get_no_repeat_random_choice(identifier, random_choices)
        
        if random_choice == 1:
            return self.get_standrad_basic_derivative()
            
        if random_choice == 2:
            return self.get_standrad_trig_derivative()
            
        if random_choice == 3:
            return self.get_standrad_trig_hyperbolic_derivative()
            
        pass

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

        
        random_choices = list(range(1, 4))

        identifier = f"{self.__class__.__name__}{id(self)}.{inspect.stack()[0][3]}.random_choices"
        random_choice = self.get_no_repeat_random_choice(identifier, random_choices)
        
        if random_choice == 1:
            return self._get_e()
        
        if random_choice == 2:
            return self._get_log()
        
        if random_choice == 3:
            return self._get_a_power_x()
        
        pass
    
    
    def _get_e(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        deltax = self.deltax
        x_plus_deltax = self.x_plus_deltax
        x_plus_deltax_minus_x = self.x_plus_deltax_minus_x
        x_plus_deltax_plus_x = self.x_plus_deltax_plus_x
        
        
        f = sp.exp(x)
        f1 = sp.exp(x + deltax)
        
        Df = Derivative(f, x)
        dfdx = Df.doit()

        x0 = 0
        
        f_limit = Symbol(f"""
        \\frac{{ {latex(f1)} - {latex(f)} }}  {{ {latex(deltax)} }}
        """)
        
        dfdx_Lim = Limit(f_limit, deltax, x0)
 
        sin_expr = sin(x_plus_deltax_minus_x / 2)
        cos_expr = cos(x_plus_deltax_plus_x / 2)
        
        f_limit_step1 = Symbol(f"""
        {latex(f)}
        \\frac
        {{ {latex(f)} - 1 }}  
        {{ {latex(deltax)} }}
        """)
        dfdx_Lim_step1 = Limit(f_limit_step1, deltax, x0)
        
        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []

        soln_stmt = f"""
        $\\displaystyle
        \\begin{{aligned}}

        {latex(Df)}
        
        & = {latex(dfdx_Lim)}
        \\\\ \\\\
        
        & = {latex(dfdx_Lim_step1)}
        \\qquad \\cdots \\text {{ (i) }} 
        
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        t = Symbol('t')
        deltat = Symbol(f"\\Delta {latex(t)}")
        t0 = x0
        
        t_eq = Eq(t, Symbol(f"{latex(sp.exp(deltax))} - 1"))
        
        deltax_eq = Eq(deltax, log(t + 1) )

        soln_stmt = f"""
        $\\displaystyle
        \\begin{{aligned}}

        \\text {{Let }}
        & {latex(t_eq)}
        \\\\ \\\\
        
        & \\implies 
        {latex(deltax_eq)}
        \\\\ \\\\
        
        & \\implies 
        {latex(t)} \\rightarrow 0 \\text{{ when }} {latex(deltax)} \\rightarrow 0 
        \\\\ \\\\
        
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())


        t_limit_step2 = Symbol(f"""
        \\frac
        {{ {latex(t)}}}  
        {{ {latex(log(t + 1))} }}
        """)
        dfdx_Lim_step2 = Limit(t_limit_step2, deltat, t0)

        t_limit_step3 = Symbol(f"""
        \\frac
        {{ 1 }}  
        {{ \\frac {{ 1 }}{{ {latex(t)} }}   {latex(log(t + 1))} }}
        """)
        dfdx_Lim_step3 = Limit(t_limit_step3, deltat, t0)

        
        t_limit_step4 = Symbol(f"""
        \\left(
        \\frac {{ 1 }}{{ {latex(t)} }}   {latex(log(t + 1))} 
        \\right)
        """)
        dfdx_Lim_step4 = Limit(t_limit_step4, deltat, t0)

        t_limit_step5 = Symbol(f"""
        {latex(log(t + 1))} ^ {{ \\frac {{ 1 }}{{ {latex(t)} }}  }}
        """)
        dfdx_Lim_step5 = Limit(t_limit_step5, deltat, t0)


        t_limit_step6 = Symbol(f"""
        \\left({latex(t + 1)}\\right) ^ {{ \\frac {{ 1 }}{{ {latex(t)} }}  }}
        """)
        dfdx_Lim_step6 = Limit(t_limit_step6, deltat, t0)

        
        eq_def_e = Eq(sp.exp(1), dfdx_Lim_step6)
        
        soln_stmt = f"""
        $\\displaystyle
        \\begin{{aligned}}

        {latex(Df)}

        & = {latex(f)} \\cdot {latex(dfdx_Lim_step2)}
        \\qquad \\text {{ rewriting limit in (i) in terms of ${latex(t)}$ }} 
        \\\\ \\\\
        
        & = {latex(f)} \\cdot {latex(dfdx_Lim_step3)}
        \\qquad \\text {{ dividing numerator and denominator by ${latex(t)}$ }} 
        \\\\ \\\\
        
        & = {latex(f)} \\cdot 
        \\frac {{ 1 }} {{ {latex(dfdx_Lim_step4)} }}
        \\\\ \\\\
        
        & = {latex(f)} \\cdot 
        \\frac {{ 1 }} {{ {latex(dfdx_Lim_step5)} }}
        \\\\ \\\\
        
        & = {latex(f)} \\cdot 
        \\frac {{ 1 }} {{ {latex(log(ueval(sp.exp(1))))} }}
        \\qquad \\text {{ by definition }} {latex(eq_def_e)}
        \\\\ \\\\
        
        & = {latex(f)} \\cdot 
        \\frac {{ 1 }} {{ {latex(log(sp.exp(1)))} }}
        \\\\ \\\\
        
        & = {latex(dfdx)}
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

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

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

        x = self.x
        y = self.y

        f = log(x)
        fy = exp(y)
        
        Df = Derivative(f, x)
        dfdx = Df.doit()
        
        eq = Eq(y, f)

        eq_step1 = Eq(fy, x)

        eq_step2 = Eq(Derivative(fy, x), Derivative(x, x))
        
        eq_step3 = Eq(Derivative(fy, y).doit() * Derivative(y, x), 1)
        
        eq_step4 = Eq(Derivative(y, x), 1 / Derivative(fy, y).doit())
        
        eq_step5 = Eq(Df, dfdx)
        
        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []
        

        soln_stmt = f"""
        $\\displaystyle
        \\begin{{aligned}}
        
        \\text{{Let }} & {latex(eq)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step1)}
        \\qquad \\text{{ by definition }} \\quad \\cdots \\text {{ (i) }}
        \\\\ \\\\
        
        \\implies & {latex(eq_step2)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step3)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step4)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step5)}
        \\qquad \\text {{ deriving from (i)}}
        
        \\end{{aligned}}
        $
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
    
    def _get_a_power_x(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        a = self.a
        x = self.x
        y = self.y
        f = a ** x
        
        Df = Derivative(f, x)
        dfdx = Df.doit()
        
        Dy = Derivative(y, x)
        
        
        eq = Eq(y, a ** x)

        eq_step1 = Eq(log(y), log(a ** x))

        eq_step2 = Eq(log(y), x * log(a))

        eq_step3 = Eq(Symbol(f""" {latex( (1 / y))} \\cdot {latex(Dy)} """), log(a))

        eq_step4 = Eq(Dy, log(a) * y)

        eq_step5 = Eq(Df, dfdx)

        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []
        

        soln_stmt = f"""
        $\\displaystyle
        \\begin{{aligned}}
        
        \\text{{Let }} & {latex(eq)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step1)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step2)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step3)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step4)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step5)}
        
        \\end{{aligned}}
        $
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
    
    def get_standrad_trig_derivative(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        
        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)
        
        if random_choice == 1:
            return self._get_sin()
        
        if random_choice == 2:
            return self._get_cos()
        
        if random_choice == 3:
            return self._get_tan()
        
        if random_choice == 4:
            return self._get_cot()
        
        if random_choice == 5:
            return self._get_sec()
        
        if random_choice == 6:
            return self._get_csc()
        
        if random_choice == 7:
            return self._get_asin()
        
        if random_choice == 8:
            return self._get_acos()
        
        if random_choice == 9:
            return self._get_atan()
        
        if random_choice == 10:
            return self._get_acot()
        
        if random_choice == 11:
            return self._get_asec()
        
        if random_choice == 12:
            return self._get_acsc()

        pass

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

        x = self.x
        deltax = self.deltax
        x_plus_deltax = self.x_plus_deltax
        x_plus_deltax_minus_x = self.x_plus_deltax_minus_x
        x_plus_deltax_plus_x = self.x_plus_deltax_plus_x
        
        
        f = sin(x)
        f1 = sin(x + deltax)
        
        Df = Derivative(f, x)
        dfdx = Df.doit()

        x0 = 0
        
        f_limit = Symbol(f"""
        \\frac{{ {latex(f1)} - {latex(f)} }}  {{ {latex(deltax)} }}
        """)
        
        dfdx_Lim = Limit(f_limit, deltax, x0)
 
        sin_expr = sin(x_plus_deltax_minus_x / 2)
        cos_expr = cos(x_plus_deltax_plus_x / 2)
        
        f_limit_step1 = Symbol(f"""
        \\frac
        {{ 2 \\cdot {latex(sin_expr)} \\cdot {latex(cos_expr)} }}  
        {{ {latex(deltax)} }}
        """)
        dfdx_Lim_step1 = Limit(f_limit_step1, deltax, x0)
        
        
        sin_expr = sin(deltax / 2)
        cos_expr = cos((2 * x + deltax) / 2)
        
        f_limit_step2 = Symbol(f"""
        \\frac
        {{ {latex(sin_expr)} \\cdot {latex(cos_expr)} }}  
        {{ {latex(deltax)} / 2 }}
        """)
        dfdx_Lim_step2 = Limit(f_limit_step2, deltax, x0)
        
        
        sin_limit_expr = Symbol(f"""
        \\frac
        {{ {latex(sin_expr)} }}  
        {{ {latex(deltax)} / 2 }}
        """)
        dfdx_Lim_step3_part1 = Limit(sin_limit_expr, deltax / 2, x0)
        dfdx_Lim_step3_part2 = Limit(cos_expr, deltax, x0)
        
        theta = Symbol('theta')
        sin_zero_Lim = Limit(sin(theta) / theta, theta, 0)
        sin_zero_Lim_eq = Eq(sin_zero_Lim, 1)
        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []

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

        {latex(Df)}
        
        & = {latex(dfdx_Lim)}
        \\\\ \\\\
        
        & = {latex(dfdx_Lim_step1)}
        \\\\ \\\\
        
        & = {latex(dfdx_Lim_step2)}
        \\qquad \\text {{ divide numerator and denominator by 2}}
        \\\\ \\\\
        
        & = {latex(dfdx_Lim_step3_part1)} \\cdot {latex(dfdx_Lim_step3_part2)}
        \\qquad \\text {{ split limits}}
        \\\\ \\\\
        
        & = {latex(dfdx_Lim_step3_part2)}
        \\qquad \\text {{ the first part becomes 1 as }} {latex(sin_zero_Lim_eq)}
        \\\\ \\\\
        
        & = {latex(dfdx)}
        \\qquad \\text {{ by substituting the value of }} {latex(deltax)}
        
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        
    def _get_cos(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        deltax = self.deltax
        x_plus_deltax = self.x_plus_deltax
        x_plus_deltax_minus_x = self.x_plus_deltax_minus_x
        x_plus_deltax_plus_x = self.x_plus_deltax_plus_x
        
        
        f = cos(x)
        f1 = cos(x + deltax)
        
        Df = Derivative(f, x)
        dfdx = Df.doit()

        x0 = 0
        
        f_limit = Symbol(f"""
        \\frac{{ {latex(f1)} - {latex(f)} }}  {{ {latex(deltax)} }}
        """)
        
        dfdx_Lim = Limit(f_limit, deltax, x0)
 
        sin1_expr = sin(x_plus_deltax_minus_x / 2)
        sin2_expr = sin(x_plus_deltax_plus_x / 2)
        
        f_limit_step1 = Symbol(f"""
        \\frac
        {{ -2 \\cdot {latex(sin1_expr)} \\cdot {latex(sin2_expr)} }}  
        {{ {latex(deltax)} }}
        """)
        dfdx_Lim_step1 = Limit(f_limit_step1, deltax, x0)
        
        
        sin1_expr = sin(deltax / 2)
        sin2_expr = sin((2 * x + deltax) / 2)
        
        f_limit_step2 = Symbol(f"""
        \\frac
        {{ - {latex(sin1_expr)} \\cdot {latex(sin2_expr)} }}  
        {{ {latex(deltax)} / 2 }}
        """)
        dfdx_Lim_step2 = Limit(f_limit_step2, deltax, x0)
        
        
        sin_limit_expr = Symbol(f"""
        \\frac
        {{ {latex(sin1_expr)} }}  
        {{ {latex(deltax)} / 2 }}
        """)
        dfdx_Lim_step3_part1 = Limit(sin_limit_expr, deltax / 2, x0)
        dfdx_Lim_step3_part2 = Limit(- sin2_expr, deltax, x0)
        
        theta = Symbol('theta')
        sin_zero_Lim = Limit(sin(theta) / theta, theta, 0)
        sin_zero_Lim_eq = Eq(sin_zero_Lim, 1)
        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []

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

        {latex(Df)}
        
        & = {latex(dfdx_Lim)}
        \\\\ \\\\
        
        & = {latex(dfdx_Lim_step1)}
        \\\\ \\\\
        
        & = {latex(dfdx_Lim_step2)}
        \\qquad \\text {{ divide numerator and denominator by 2}}
        \\\\ \\\\
        
        & = {latex(dfdx_Lim_step3_part1)} \\cdot {latex(dfdx_Lim_step3_part2)}
        \\qquad \\text {{ split limits}}
        \\\\ \\\\
        
        & = {latex(dfdx_Lim_step3_part2)}
        \\qquad \\text {{ the first part becomes 1 as }} {latex(sin_zero_Lim_eq)}
        \\\\ \\\\
        
        & = {latex(dfdx)}
        \\qquad \\text {{ by substituting the value of }} {latex(deltax)}
        
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
    
    def _get_tan(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        deltax = self.deltax
        
        f = tan(x)
        
        Df = Derivative(f, x)
        dfdx = sec(x) ** 2
        
        sinx_by_cosx = Symbol(f"\\frac {{ {latex(sin(x))} }} {{ {latex(cos(x))} }}")

        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []

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

        {latex(Df)}
        & = {latex(Derivative(sinx_by_cosx, x))}
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(Derivative(sin(x), x))} \\cdot {latex(cos(x))}
           - {latex(sin(x))} \\cdot {latex(Derivative(cos(x), x))} 
          }} 
        {{ {latex( (cos(x)) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(cos(x))} \\cdot {latex(cos(x))}
           - {latex(sin(x))} \\cdot  {latex(-sin(x))}
          }} 
        {{ {latex( (cos(x)) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(cos(x) ** 2)} 
           + {latex(sin(x) ** 2)} 
          }} 
        {{ {latex( (cos(x)) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac {{ 1 }} {{ {latex( (cos(x)) ** 2 )} }} 
        \\qquad \\text {{ numerator is equal to 1 }} 
        \\\\ \\\\
        
        & = {latex(dfdx)}
        
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
    
    def _get_cot(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        deltax = self.deltax
        
        f = cot(x)
        
        Df = Derivative(f, x)
        dfdx = - csc(x) ** 2
        
        cosx_by_sinx = Symbol(f"\\frac {{ {latex(cos(x))} }} {{ {latex(sin(x))} }}")

        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []

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

        {latex(Df)}
        & = {latex(Derivative(cosx_by_sinx, x))}
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(Derivative(cos(x), x))} \\cdot {latex(sin(x))}
           - {latex(cos(x))} \\cdot {latex(Derivative(sin(x), x))} 
          }} 
        {{ {latex( (sin(x)) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(-sin(x))} \\cdot {latex(sin(x))}
           - {latex(cos(x))} \\cdot  {latex(cos(x))}
          }} 
        {{ {latex( (sin(x)) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac 
        {{ - {latex(sin(x) ** 2)} 
           - {latex(cos(x) ** 2)} 
          }} 
        {{ {latex( (sin(x)) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac {{ - 1 }} {{ {latex( (sin(x)) ** 2 )} }} 
        \\qquad \\text {{ numerator is equal to 1 }} 
        \\\\ \\\\
        
        & = {latex(dfdx)}
        
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
    
    def _get_sec(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        deltax = self.deltax
        
        f = sec(x)
        
        Df = Derivative(f, x)
        dfdx = Df.doit()
        
        one_by_cosx = Symbol(f"\\frac {{ 1 }} {{ {latex(cos(x))} }}")

        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []

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

        {latex(Df)}
        & = {latex(Derivative(one_by_cosx, x))}
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(Derivative(1, x))} \\cdot {latex(cos(x))}
           - 1 \\cdot {latex(Derivative(cos(x), x))} 
          }} 
        {{ {latex( (cos(x)) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac 
        {{ - 1 \\cdot {latex(-sin(x))}  }} 
        {{ {latex( (cos(x)) ** 2 )} }} 
        \\qquad \\text{{ as }} {latex(Derivative(1, x))} = 0
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(sin(x))} }} 
        {{ {latex( cos(x) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac {{ {latex(sin(x))} }}  {{ {latex( cos(x))} }}  \\cdot
            \\frac {{ 1 }}  {{ {latex( cos(x))} }} 
        \\\\ \\\\
        
        & = {latex(dfdx)}
        
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
    
    def _get_csc(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        deltax = self.deltax
        
        f = csc(x)
        
        Df = Derivative(f, x)
        dfdx = Df.doit()
        
        one_by_sinx = Symbol(f"\\frac {{ 1 }} {{ {latex(sin(x))} }}")

        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []

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

        {latex(Df)}
        & = {latex(Derivative(one_by_sinx, x))}
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(Derivative(1, x))} \\cdot {latex(sin(x))}
           - 1 \\cdot {latex(Derivative(sin(x), x))} 
          }} 
        {{ {latex( (sin(x)) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac 
        {{ - 1 \\cdot {latex(cos(x))}  }} 
        {{ {latex( (sin(x)) ** 2 )} }} 
        \\qquad \\text{{ as }} {latex(Derivative(1, x))} = 0
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(- cos(x))} }} 
        {{ {latex( sin(x) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac {{ {latex( - cos(x))} }}  {{ {latex( sin(x))} }}  \\cdot
            \\frac {{ 1 }}  {{ {latex( sin(x))} }} 
        \\\\ \\\\
        
        & = {latex(dfdx)}
        
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
    
    def _get_asin(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        y = self.y
        
        f = asin(x)
        fy = sin(y)

        return self._get_arc_trig_func(x, y, f, fy)
    
    def _get_acos(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        y = self.y
        
        f = acos(x)
        fy = cos(y)

        return self._get_arc_trig_func(x, y, f, fy)
    
    def _get_atan(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        y = self.y

        f = atan(x)
        fy = tan(y)
        
        return self._get_arc_trig_func(x, y, f, fy)

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

        x = self.x
        y = self.y

        f = acot(x)
        fy = cot(y)
        
        return self._get_arc_trig_func(x, y, f, fy)

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

        x = self.x
        y = self.y

        f = asec(x)
        fy = sec(y)
        
        return self._get_arc_trig_func(x, y, f, fy)

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

        x = self.x
        y = self.y

        f = acsc(x)
        fy = csc(y)
        
        return self._get_arc_trig_func(x, y, f, fy)
    
    def _get_arc_trig_func(self, x, y, f, fy):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        
        Df = Derivative(f, x)
        dfdx = Df.doit()
        
        eq = Eq(y, f)

        eq_step1 = Eq(fy, x)

        eq_step2 = Eq(Derivative(fy, x), Derivative(x, x))
        
        eq_step3 = Eq(Derivative(fy, y).doit() * Derivative(y, x), 1)
        
        eq_step4 = Eq(Derivative(y, x), 1 / Derivative(fy, y).doit())
        
        eq_step5 = Eq(Df, dfdx)
        
        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []
        
        soln_stmt = f"""
     
        Entities:
        
        $
        \\left .
     
        \\begin{{array}} {{ll}}
        
        & {latex(cos(x) ** 2)} + {latex(sin(x) ** 2)} = 1 \\\\
        & {latex(sec(x) ** 2)} - {latex(tan(x) ** 2)} = 1 \\\\
        & {latex(csc(x) ** 2)} - {latex(cot(x) ** 2)} = 1 

        \\end{{array}}
        
        \\right \\}}
        \\quad \\cdots \\text {{ (i) }}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        


        soln_stmt = f"""
        $\\displaystyle
        \\begin{{aligned}}
        
        \\text{{Let }} & {latex(eq)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step1)}
        \\qquad \\cdots \\text {{ (ii) }}
        \\\\ \\\\
        
        \\implies & {latex(eq_step2)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step3)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step4)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step5)}
        \\qquad \\text {{ deriving from (i) and (ii) }}
        
        \\end{{aligned}}
        $
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
    
    
    def get_standrad_trig_hyperbolic_derivative(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        
        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)
        
        if random_choice == 1:
            return self._get_sinh()
        
        if random_choice == 2:
            return self._get_cosh()
        
        if random_choice == 3:
            return self._get_tanh()
        
        if random_choice == 4:
            return self._get_coth()
        
        if random_choice == 5:
            return self._get_sech()
        
        if random_choice == 6:
            return self._get_csch()
        
        if random_choice == 7:
            return self._get_asinh()
        
        if random_choice == 8:
            return self._get_acosh()
        
        if random_choice == 9:
            return self._get_atanh()
        
        if random_choice == 10:
            return self._get_acoth()
        
        if random_choice == 11:
            return self._get_asech()
        
        if random_choice == 12:
            return self._get_acsch()

        pass

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

        x = self.x
        y = self.y

        f = sinh(x)
        fexp = f.rewrite(exp)
        
        
        
        Df = Derivative(f, x)
        dfdx = Df.doit()
        
        Dfexp = Derivative(fexp, x)
        dfexpdx = Dfexp.doit()
        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []

        soln_stmt = f"""
     
        By definition:
        
        $
        \\left .
     
        \\begin{{array}} {{ll}}
        
        & {latex(sinh(x))} = {latex(sinh(x).rewrite(exp))} \\\\ \\\\
        & {latex(cosh(x))} = {latex(cosh(x).rewrite(exp))} 

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

        soln_stmt = f"""
        $\\displaystyle
        \\begin{{aligned}}
        
        {latex(Df)}
        & = {latex(Dfexp)}
        \\\\ \\\\
        
        & = {latex(dfexpdx)}
        \\\\ \\\\
        
        & = {latex(dfdx)}
        
        \\end{{aligned}}
        $
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
    
    def _get_cosh(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        y = self.y

        f = cosh(x)
        fexp = f.rewrite(exp)
        
        Df = Derivative(f, x)
        dfdx = Df.doit()
        
        Dfexp = Derivative(fexp, x)
        dfexpdx = Dfexp.doit()
        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []


        soln_stmt = f"""
     
        By definition:
        
        $
        \\left .
     
        \\begin{{array}} {{ll}}
        
        & {latex(sinh(x))} = {latex(sinh(x).rewrite(exp))} \\\\ \\\\
        & {latex(cosh(x))} = {latex(cosh(x).rewrite(exp))} 

        \\end{{array}}
        
        \\right \\}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
        $\\displaystyle
        \\begin{{aligned}}
        
        {latex(Df)}
        & = {latex(Dfexp)}
        \\\\ \\\\
        
        & = {latex(dfexpdx)}
        \\\\ \\\\
        
        & = {latex(dfdx)}
        
        \\end{{aligned}}
        $
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
    
    def _get_tanh(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        deltax = self.deltax
        
        f = tanh(x)
        
        Df = Derivative(f, x)
        dfdx = sech(x) ** 2
        
        sinhx_by_coshx = Symbol(f"\\frac {{ {latex(sinh(x))} }} {{ {latex(cosh(x))} }}")

        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []

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

        {latex(Df)}
        & = {latex(Derivative(sinhx_by_coshx, x))}
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(Derivative(sinh(x), x))} \\cdot {latex(cosh(x))}
           - {latex(sinh(x))} \\cdot {latex(Derivative(cosh(x), x))} 
          }} 
        {{ {latex( (cosh(x)) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(Derivative(sinh(x), x).doit())} \\cdot {latex(cosh(x))}
           - {latex(sinh(x))} \\cdot {latex(Derivative(cosh(x), x).doit())} 
          }} 
        {{ {latex( (cosh(x)) ** 2 )} }} 
        \\\\ \\\\
        
        
        & = \\frac 
        {{ {latex(cosh(x) ** 2)} 
           - {latex(sinh(x) ** 2)} 
          }} 
        {{ {latex( (cosh(x)) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac {{ 1 }} {{ {latex( (cosh(x)) ** 2 )} }} 
        \\qquad \\text {{ as }} {self._get_hyperbolic_identity_latex(x)}
        \\\\ \\\\
        
        & = {latex(dfdx)}
        
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
    
    def _get_coth(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        deltax = self.deltax
        
        f = coth(x)
        
        Df = Derivative(f, x)
        dfdx = - csch(x) ** 2
        
        coshx_by_sinhx = Symbol(f"\\frac {{ {latex(cosh(x))} }} {{ {latex(sinh(x))} }}")

        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []

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

        {latex(Df)}
        & = {latex(Derivative(coshx_by_sinhx, x))}
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(Derivative(cosh(x), x))} \\cdot {latex(sinh(x))}
           - {latex(cosh(x))} \\cdot {latex(Derivative(sinh(x), x))} 
          }} 
        {{ {latex( (sinh(x)) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(Derivative(cosh(x), x).doit())} \\cdot {latex(sinh(x))}
           - {latex(cosh(x))} \\cdot {latex(Derivative(sinh(x), x).doit())} 
          }} 
        {{ {latex( (sinh(x)) ** 2 )} }} 
        \\\\ \\\\
        
        
        & = \\frac 
        {{ {latex(sinh(x) ** 2)} 
           - {latex(cosh(x) ** 2)} 
          }} 
        {{ {latex( (sinh(x)) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac {{ - 1 }} {{ {latex( (sinh(x)) ** 2 )} }} 
        \\qquad \\text {{ as }} {self._get_hyperbolic_identity_latex(x)}
        \\\\ \\\\
        
        & = {latex(dfdx)}
        
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
    
    def _get_sech(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        deltax = self.deltax
        
        f = sech(x)
        
        Df = Derivative(f, x)
        dfdx = Df.doit()
        
        one_by_coshx = Symbol(f"\\frac {{ 1 }} {{ {latex(cosh(x))} }}")

        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []

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

        {latex(Df)}
        & = {latex(Derivative(one_by_coshx, x))}
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(Derivative(1, x))} \\cdot {latex(cosh(x))}
           - 1 \\cdot {latex(Derivative(cosh(x), x))} 
          }} 
        {{ {latex( (cosh(x)) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(Derivative(1, x).doit())} \\cdot {latex(cosh(x))}
           - 1 \\cdot {latex(Derivative(cosh(x), x).doit())} 
          }} 
        {{ {latex( (cosh(x)) ** 2 )} }} 
        \\\\ \\\\
        
        
        & = \\frac 
        {{ {latex(sinh(x))} }} 
        {{ {latex( cosh(x) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac {{ {latex(sinh(x))} }}  {{ {latex( cosh(x))} }}  \\cdot
            \\frac {{ 1 }}  {{ {latex( cosh(x))} }} 
        \\\\ \\\\
        
        & = {latex(dfdx)}
        
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
    
    def _get_csch(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        deltax = self.deltax
        
        f = csch(x)
        
        Df = Derivative(f, x)
        dfdx = Df.doit()
        
        one_by_sinhx = Symbol(f"\\frac {{ 1 }} {{ {latex(sinh(x))} }}")

        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []

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

        {latex(Df)}
        & = {latex(Derivative(one_by_sinhx, x))}
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(Derivative(1, x))} \\cdot {latex(sinh(x))}
           - 1 \\cdot {latex(Derivative(sinh(x), x))} 
          }} 
        {{ {latex( (sinh(x)) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac 
        {{ {latex(Derivative(1, x).doit())} \\cdot {latex(sinh(x))}
           - 1 \\cdot {latex(Derivative(sinh(x), x).doit())} 
          }} 
        {{ {latex( (sinh(x)) ** 2 )} }} 
        \\\\ \\\\
        
        
        & = \\frac 
        {{ - {latex(cosh(x))} }} 
        {{ {latex( sinh(x) ** 2 )} }} 
        \\\\ \\\\
        
        & = \\frac {{ {latex( - cosh(x))} }}  {{ {latex( sinh(x))} }}  \\cdot
            \\frac {{ 1 }}  {{ {latex( sinh(x))} }} 
        \\\\ \\\\
        
        & = {latex(dfdx)}
        
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
    
    def _get_asinh(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        y = self.y
        
        f = asinh(x)
        fy = sinh(y)

        return self._get_arc_trig_hyperbolic_func(x, y, f, fy)
    
    def _get_acosh(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        y = self.y
        
        f = acosh(x)
        fy = cosh(y)

        return self._get_arc_trig_hyperbolic_func(x, y, f, fy)
    
    def _get_atanh(self):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        x = self.x
        y = self.y

        f = atanh(x)
        fy = tanh(y)
        
        return self._get_arc_trig_hyperbolic_func(x, y, f, fy)

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

        x = self.x
        y = self.y

        f = acoth(x)
        fy = coth(y)
        
        return self._get_arc_trig_hyperbolic_func(x, y, f, fy)

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

        x = self.x
        y = self.y

        f = asech(x)
        fy = sech(y)
        
        return self._get_arc_trig_hyperbolic_func(x, y, f, fy)

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

        x = self.x
        y = self.y

        f = acsch(x)
        fy = csch(y)
        
        return self._get_arc_trig_hyperbolic_func(x, y, f, fy)
    
    
    def _get_arc_trig_hyperbolic_func(self, x, y, f, fy):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())
        

        
        Df = Derivative(f, x)
        dfdx = Df.doit()
        
        eq = Eq(y, f)

        eq_step1 = Eq(fy, x)

        eq_step2 = Eq(Derivative(fy, x), Derivative(x, x))
        
        eq_step3 = Eq(Derivative(fy, y).doit() * Derivative(y, x), 1)
        
        eq_step4 = Eq(Derivative(y, x), 1 / Derivative(fy, y).doit())
        
        eq_step5 = Eq(Df, dfdx)
        
        
        prob_s = f"""
        Find derivative of $\\displaystyle {latex(f)}
        $ with respect to $\\displaystyle {latex(x)}$.
        """
        
        soln_stmts = []

        soln_stmt = f"""
     
        Hyperbolic entities:
        
        $
        \\left .
     
        \\begin{{array}} {{ll}}
        
        & {latex(cosh(x) ** 2)} - {latex(sinh(x) ** 2)} = 1 \\\\
        & {latex(tanh(x) ** 2)} + {latex(sech(x) ** 2)} = 1 \\\\
        & {latex(coth(x) ** 2)} - {latex(csch(x) ** 2)} = 1 

        \\end{{array}}
        
        \\right \\}}
        \\quad \\cdots \\text {{ (i) }}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        soln_stmt = f"""
        $\\displaystyle
        \\begin{{aligned}}
        
        \\text{{Let }} & {latex(eq)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step1)}
        \\qquad \\cdots \\text {{ (ii) }}
        \\\\ \\\\
        
        
        
        \\implies & {latex(eq_step2)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step3)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step4)}
        \\\\ \\\\
        
        \\implies & {latex(eq_step5)}
        \\qquad \\text {{ deriving from (i) and (ii) }}
        
        \\end{{aligned}}
        $
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        $\\displaystyle {latex(dfdx)}$
        """

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

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

    def _get_hyperbolic_identity_latex(self, x):
        return f""" {latex(cosh(x) ** 2)}  - {latex(sinh(x) ** 2)} = 1 """

        
    pass
#class StdDifferentiationHelper:
