'''
Created on Mar-07-2024

@author: bthakur

pip install roman

'''

import random

import inspect
import traceback

from sympy import (latex,
                   Matrix,
                   nsimplify, Rational,
                   Symbol, symbols,
                   Add, Mul, Eq, 
                   UnevaluatedExpr as ueval,
                   cos, sin,
                   )
import sympy as sp
import numpy as np

from xv.util import (get_arrow_symbols, get_arrow_symbol,
                     )

from sympy.vector import (CoordSys3D)

from ._geometry_3d_base_helper import Geometry3dBaseHelper

# _geometry_3d_line_helper
# Geometry3dLineHelper
        
class Geometry3dLineHelper(Geometry3dBaseHelper):
    
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.N = CoordSys3D('N')
        pass
    

    def get_problem_equation_line_using_vector_form(self, v1 = None, v2 = None, N = None, **kwargs):
        '''

        '''
        ref_idx = kwargs.get('ref_idx', 0)
        sub_problem_idx = kwargs.get('sub_problem_idx', None)
        if sub_problem_idx is None:
            sub_problem_idx = ""
        else:
            sub_problem_idx = f"{sub_problem_idx}."


        N = self._parse_N(v1, v2, N = N)
        
        if v1 is None:
            v1 = self.get_random_vector(N = N)
        
        if v2 is None:
            v2 = self.get_random_vector(N = N)
        
        x, y, z = symbols("x, y, z")
        vP = self.get_vector(x, y, z, N)
        
        
        prob_stmts = []
        soln_stmts = []
        answer_stmts = []
        
        prob_stmt = f"""
        """
        prob_stmts.append(prob_stmt.strip())

        prob_stmt = f"""
        Find the equation of line passing through points with position vectors $\\displaystyle 
        {v1.get_latex()}\\text {{ and }} {v2.get_latex()} 
        $. 
        """
        prob_stmts.append(prob_stmt.strip())

        A, B, P, AB = get_arrow_symbols("A B P AB".split(" "))

        lamb = Symbol(f"\\lambda", real = True) 

        vArb = (1 - lamb) * v1 + lamb * v2

        soln_stmt = f"""
        If $
        {latex(A)}$ and ${latex(B)}
        $ are the given point. Any point ${latex(P)}
        $ on the line can be expressed as

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

        {latex(P)} 
        & = {latex(A)} + {latex(lamb)} \\; {latex(AB)}
        \\text {{ where }} {latex(lamb)} \\text {{ is any arbitrary real number }}
        \\\\ \\\\
        
        & = {latex(A)} + {latex(lamb)} \\left( {latex(B)} - {latex(A)} \\right)
        \\\\ \\\\
        
        & = \\left( 1 - {latex(lamb)} \\right) {latex(A)} + {latex(lamb)} \\; {latex(B)}
        \\\\ \\\\
        
        & = \\left( 1 - {latex(lamb)} \\right) \\left( {v1.get_latex()} \\right) 
        + {latex(lamb)} \\left( {v2.get_latex()} \\right)
        \\\\ \\\\
        
        & = {vArb.get_latex()}
        \\\\ \\\\
        
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        soln_stmt = f"""
        Therefore, the parametric equation of the line passing through $
        {latex(A)}$ and ${latex(B)}$ can be written as 

        $\\displaystyle 
        {latex(P)} = {vArb.get_latex()}
        \\qquad \\cdots \\text{{ ({sub_problem_idx}{ref_idx + 1}) }}
        $

        It can be expanded and written as

        $\\displaystyle 
        {vP.get_latex()} = {vArb.get_latex()}
        $
        """
        soln_stmts.append(soln_stmt.strip())


        P_coords = vP.get_coords()
        vArb_coords = vArb.get_coords()


        soln_stmt = f"""
        $\\displaystyle \\implies
        \\left \\{{
        
        \\begin{{aligned}}
        
        & {latex(P_coords[0])} = {latex(vArb_coords[0])} \\\\ \\\\
        
        & {latex(P_coords[1])} = {latex(vArb_coords[1])} \\\\ \\\\
        
        & {latex(P_coords[2])} = {latex(vArb_coords[2])}
        
        \\end{{aligned}}
        
        \\right .
        $
        """
        soln_stmts.append(soln_stmt.strip())

        A_coords = v1.get_coords()
        B_coords = v2.get_coords()

        line_eq = [(P_coords[idx] - A_coords[idx], 
                    B_coords[idx] - A_coords[idx]) 
                   for idx in range(3)]
        
        soln_stmt = f"""
        
        $\\displaystyle \\implies
        {latex(lamb)} 
        = {self.get_line_eq_latex(line_eq)}
        $
        
        Dropping ${latex(lamb)}$, we get another form of the equation of the line.
        
        $\\displaystyle 
        {self.get_line_eq_latex(line_eq)}
        \\qquad \\cdots \\text{{ ({sub_problem_idx}{ref_idx + 2}) }} 
        $
        
        """
        soln_stmts.append(soln_stmt.strip())

        soln_stmt = f"""
        Finally, we have a parametric equation having a single arbitrary point.
        This is the equation of the plane.
        """
        soln_stmts.append(soln_stmt.replace("\n", " ").strip())

        
        answer_stmt = f"""
        $\\displaystyle 
        {self.get_line_eq_latex(line_eq)}
        $
        """
        answer_stmts.append(answer_stmt.strip())


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


    def get_problem_equation_line_using_cosines(self, v1 = None, v2 = None, N = None, **kwargs):
        '''
        line_eq returned by this function is in form of a list of terms.
        Each term is a tuple of two sub-terms, numerator and denominator(direction vector)
        '''

        ref_idx = kwargs.get('ref_idx', 0)
        sub_problem_idx = kwargs.get('sub_problem_idx', None)
        if sub_problem_idx is None:
            sub_problem_idx = ""
        else:
            sub_problem_idx = f"{sub_problem_idx}."



        N = self._parse_N(v1, v2, N = N)
        
        if v1 is None:
            v1 = self.get_random_vector(N = N)
        
        if v2 is None:
            v2 = self.get_random_vector(N = N)
        
        x, y, z = symbols("x, y, z")
        vP = self.get_vector(x, y, z, N)
        
        x0, y0, z0 = symbols("x_0, y_0, z_0")
        vP0 = self.get_vector(x0, y0, z0, N)
        
        
        prob_stmts = []
        soln_stmts = []
        answer_stmts = []
        
        prob_stmt = f"""
        """
        prob_stmts.append(prob_stmt.strip())

        prob_stmt = f"""
        Find the equation of line passing through points with position vectors $\\displaystyle 
        {v1.get_latex()}\\text {{ and }} {v2.get_latex()} 
        $. 
        """
        prob_stmts.append(prob_stmt.strip())

        A, B, P, P0, P0P, AB, AP = get_arrow_symbols("A B P P_0 PP_0 AB AP".split(" "))

        alpha, beta, gamma = symbols(f"\\alpha, \\beta, \\gamma")

        soln_stmt = f"""
        Let ${latex(P0)}$ and ${latex(P)}$ be two points on the line and
        
        $\\displaystyle 
        \\begin{{aligned}}
        
        & {latex(P0)} = {vP0.get_latex()} \\\\ \\\\
        
        & {latex(P)} = {vP.get_latex()} 
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        
        soln_stmt = f"""
        Let $\\displaystyle 
        {latex(alpha)}, {latex(beta)} \\text {{ and }} {latex(gamma)}
        $ be the angles ${latex(P0P)}
        $ forms with the axes. Then, we can write:
        TODO: add image
        
        $\\displaystyle 
        \\left|{latex(P0P)}\\right| {latex(cos(alpha))} = {latex(P0P)}_{{{latex(x)}}} 
        \\\\ ~ \\\\
        
        \\left|{latex(P0P)}\\right| {latex(cos(beta))} = {latex(P0P)}_{{{latex(y)}}} 
        \\\\ ~ \\\\
        
        \\left|{latex(P0P)}\\right| {latex(cos(gamma))} = {latex(P0P)}_{{{latex(z)}}} 
        $
        
        $\\displaystyle \\implies
        \\frac {{ {latex(P0P)}_{{{latex(x)}}} }} {{ {latex(cos(alpha))} }} 
        
        = \\frac {{ {latex(P0P)}_{{{latex(y)}}} }} {{ {latex(cos(beta))} }} 
        
        = \\frac {{ {latex(P0P)}_{{{latex(z)}}} }} {{ {latex(cos(gamma))} }} 
        
        = \\left|{latex(P0P)}\\right| 
        \\qquad \\cdots \\text{{ ({sub_problem_idx}{ref_idx + 1}) }}
        $
        
        Equation ({sub_problem_idx}{ref_idx + 1}) is the parametric equation of the line passing through $
        {latex(P0)}$ and ${latex(P)}$.
        """
        soln_stmts.append(soln_stmt.strip())


        soln_stmt = f"""
        Let us compute the values required in ({sub_problem_idx}{ref_idx + 1}).
        
        $\\displaystyle 
        \\begin{{aligned}}
        
        \\implies {latex(P0P)} 
        & = {latex(P)} - {latex(P0)} \\\\ \\\\
        
        & = \\left( {vP.get_latex()} \\right) - \\left( {vP0.get_latex()} \\right) \\\\ \\\\
        
        & = {(vP - vP0).get_latex()}
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        P0P_coords = (vP - vP0).get_coords()
        
        soln_stmt = f"""
        Substitute the values in ({sub_problem_idx}{ref_idx + 1}).
        
        $\\displaystyle \\implies
        \\frac {{ {latex(P0P_coords[0])} }} {{ {latex(cos(alpha))} }} 
        
        = \\frac {{ {latex(P0P_coords[1])} }} {{ {latex(cos(beta))} }} 
        
        = \\frac {{ {latex(P0P_coords[2])} }} {{ {latex(cos(gamma))} }} 
        
        = \\left|{latex(P0P)}\\right| 
        \\qquad \\cdots \\text{{ ({sub_problem_idx}{ref_idx + 2}) }}
        $
        
        Equation ({sub_problem_idx}{ref_idx + 2}) is another form of parametric equation of the line passing through $
        {latex(P0)}$ and ${latex(P)}
        $. But we need to find the values of $\\displaystyle 
        {latex(cos(alpha))}, {latex(cos(beta))} \\text {{ and }} {latex(cos(gamma))}
        $. After that we can ignore $\\left|{latex(P0P)}\\right|$.
        """
        soln_stmts.append(soln_stmt.strip())



        soln_stmt = f"""
        We can replace $
        {latex(P0)}$ and ${latex(P)}
        $ with other points on the line. Let us take $
        {latex(A)}$ and ${latex(B)}
        $. Let us calculate ${latex(AB)}$.
        """
        soln_stmts.append(soln_stmt.strip())

        soln_stmt = f"""
        $\\displaystyle 
        \\begin{{aligned}}
        
        {latex(AB)} 
        & = {latex(B)} - {latex(A)} \\\\ \\\\
        
        & = \\left( {v2.get_latex()} \\right) - \\left( {v1.get_latex()} \\right) \\\\ \\\\
        
        & = {(v2 - v1).get_latex()}
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())


        
        r_magnitude = (v2 - v1).magnitude()

        soln_stmt = f"""
        $\\displaystyle 
        \\begin{{aligned}}
        
        \\implies \\left| {latex(AB)} \\right|
        
        & = {self.get_magnitude_calc_latex(v2 - v1)} \\\\ \\\\
        
        & = {latex(r_magnitude)}
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())


        AB_coords = (v2 - v1).get_coords()
        cos_alpha = AB_coords[0] / r_magnitude
        cos_beta = AB_coords[1] / r_magnitude
        cos_gamma = AB_coords[2] / r_magnitude

        soln_stmt = f"""
        Substitute the values in ({sub_problem_idx}{ref_idx + 2})
        
        $\\displaystyle \\implies
        \\frac {{ {latex(AB_coords[0])} }} {{ {latex(cos(alpha))} }} 
        
        = \\frac {{ {latex(AB_coords[1])} }} {{ {latex(cos(beta))} }} 
        
        = \\frac {{ {latex(AB_coords[2])} }} {{ {latex(cos(gamma))} }} 
        
        = {latex(r_magnitude)}
        \\qquad \\text{{ using coefficients and magnitude of }} {latex(AB)}
        $
        """
        soln_stmts.append(soln_stmt.strip())


        soln_stmt = f"""
        $
        \\implies \\left\\{{ 

        \\begin{{aligned}}
        
        & {latex(cos(alpha))} = {latex(cos_alpha)} 
        \\\\ \\\\
        
        
        &  {latex(cos(beta))} = {latex(cos_beta)} 
        \\\\ \\\\

        &  {latex(cos(gamma))} = {latex(cos_gamma)} 
        
        \\end{{aligned}}
        
        \\right .
        $

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


        soln_stmt = f"""
        Now, we can write ({sub_problem_idx}{ref_idx + 2}) with these values and drop the last part.
        
        $\\displaystyle \\implies
        \\frac {{ {latex(P0P_coords[0])} }} {{ {latex(cos_alpha)} }} 
        
        = \\frac {{ {latex(P0P_coords[1])} }} {{ {latex(cos_beta)} }} 
        
        = \\frac {{ {latex(P0P_coords[2])} }} {{ {latex(cos_gamma)} }} 
        \\qquad \\cdots \\text{{ ({sub_problem_idx}{ref_idx + 3}) }} 
        $
        """
        soln_stmts.append(soln_stmt.strip())

        
        AP_coords = (vP - v1).get_coords()
        
        line_eq_latex_ueval = f"""
        \\frac {{ {latex(AP_coords[0])} }} {{ {latex(cos_alpha)} }} 
        
        = \\frac {{ {latex(AP_coords[1])} }} {{ {latex(cos_beta)} }} 
        
        = \\frac {{ {latex(AP_coords[2])} }} {{ {latex(cos_gamma)} }} 
        """

        soln_stmt = f"""
        Finally, we can replace values of ${latex(P0)}$ with values of ${latex(A)}$.
        
        $\\displaystyle \\implies
        {line_eq_latex_ueval}
        \\qquad \\cdots \\text{{ ({sub_problem_idx}{ref_idx + 4}) }} 
        $
        """
        soln_stmts.append(soln_stmt.strip())

        AB_magnitude = (v2 - v1).magnitude()
        
        direction_tuple = tuple([item * AB_magnitude
                                 for item in (cos_alpha, cos_beta, cos_gamma)])

        line_eq = [(AP_coords[idx], direction_tuple[idx]) for idx in range(3)] 
        
        soln_stmt = f"""
        In a more simplified form (divide by the magnitude of $
        {latex(AB)} = {latex(AB_magnitude)} $):
        
        $\\displaystyle \\implies
        {self.get_line_eq_latex(line_eq)}
        \\qquad \\cdots \\text{{ ({sub_problem_idx}{ref_idx + 5}) }} 
        $
        """
        soln_stmts.append(soln_stmt.strip())

        soln_stmt = f"""
        Finally, we have a parametric equation having a single arbitrary point.
        This is the equation of the plane.
        """
        soln_stmts.append(soln_stmt.replace("\n", " ").strip())

        
        answer_stmt = f"""
        $\\displaystyle 
        {self.get_line_eq_latex(line_eq)}
        $
        """
        answer_stmts.append(answer_stmt.strip())


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



    def get_problem_direction_unit_vector_of_line(self, line_eq, 
                                             N, **kwargs):
        '''
        '''
        
        ref_idx = kwargs.get('ref_idx', 0)
        sub_problem_idx = kwargs.get('sub_problem_idx', None)
        if sub_problem_idx is None:
            sub_problem_idx = ""
        else:
            sub_problem_idx = f"{sub_problem_idx}."



        direction_tuple = tuple([ item[1] for item in line_eq])
        
        prob_stmts = []
        soln_stmts = []
        answer_stmts = []
        
        prob_stmt = f"""
        """
        prob_stmts.append(prob_stmt.strip())


        prob_stmt = f"""
        Find the direction unit vector of the line $\\displaystyle 
        {self.get_line_eq_latex(line_eq)} $. 
        """
        prob_stmts.append(prob_stmt.strip())


        P, P0, P0P, r = get_arrow_symbols("P P_0 PP_0 r".split(" "))
        
        r_unit = Symbol(f"\\hat{{r}}")

        alpha, beta, gamma = symbols(f"\\alpha, \\beta, \\gamma")

        x, y, z = symbols("x, y, z")
        vP = self.get_vector(x, y, z, N)
        
        x0, y0, z0 = symbols("x_0, y_0, z_0")
        vP0 = self.get_vector(x0, y0, z0, N)

        P0P_coords = (vP - vP0).get_coords()
        
        v_direction_vector = self.get_vector(*direction_tuple, N)

        soln_stmt = f"""
        The standard parametric equation of a line is
        
        $\\displaystyle \\implies
        \\frac {{ {latex(P0P_coords[0])} }} {{ {latex(cos(alpha))} }} 
        
        = \\frac {{ {latex(P0P_coords[1])} }} {{ {latex(cos(beta))} }} 
        
        = \\frac {{ {latex(P0P_coords[2])} }} {{ {latex(cos(gamma))} }} 
        $ 
        
        where $\\displaystyle \\left(
        {latex(cos(alpha))}, {latex(cos(beta))}, {latex(cos(gamma))}
        \\right)
        $ is called direction vector of the line.
        """
        soln_stmts.append(soln_stmt.strip())

        

        soln_stmt = f"""
        Compare it with the given equation of the line:
        
        $\\displaystyle {self.get_line_eq_latex(line_eq)} $
        
        The direction vector of the line is $\\displaystyle {latex(direction_tuple)} $
        
        In vector form, it is
        
        $\\displaystyle {latex(r)} = {v_direction_vector.get_latex()} $
        """
        soln_stmts.append(soln_stmt.strip())
        
        r_magnitude = v_direction_vector.magnitude()

        soln_stmt = f"""
        The magnitude of ${latex(r)}$
        
        $\\displaystyle 
        \\begin{{aligned}}
        
        \\left| {latex(r)} \\right|
        
        & = {self.get_magnitude_calc_latex(v_direction_vector)} \\\\ \\\\
        
        & = {latex(r_magnitude)}
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        direction_unit_tuple = [item / r_magnitude for item in direction_tuple]
        
        v_direction_unit_vector = self.get_vector(*direction_unit_tuple, N)

        soln_stmt = f"""
        Thus the direction unit vector can be written as

        $\\displaystyle 
        \\begin{{aligned}}
        
        {latex(r_unit)} 
        
        & = \\frac {{ 1 }} {{ {latex(r_magnitude)} }} 
        \\left( {v_direction_vector.get_latex()}  \\right)
        \\\\ \\\\
        
        & = {v_direction_unit_vector.get_latex()} 
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())


        answer_stmt = f"""
        $\\displaystyle {v_direction_unit_vector.get_latex()} $
        """
        answer_stmts.append(answer_stmt.strip())


        prob_s = "\n\n".join(prob_stmts)
        soln_s = "\n\n".join(soln_stmts)
        answer_s = "\n\n".join(answer_stmts)
        
        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            'v_direction_vector': v_direction_vector,
            'v_direction_unit_vector': v_direction_unit_vector,
            }
        return result
        pass

    
    def get_problem_distance_of_point_from_line(self, v3, line_eq, 
                                                N, **kwargs):
        '''
        '''
        
        ref_idx = kwargs.get('ref_idx', 0)
        sub_problem_idx = kwargs.get('sub_problem_idx', None)
        if sub_problem_idx is None:
            sub_problem_idx = ""
        else:
            sub_problem_idx = f"{sub_problem_idx}."



        direction_tuple = tuple([ item[1] for item in line_eq])
        
        prob_stmts = []
        soln_stmts = []
        answer_stmts = []
        
        prob_stmt = f"""
        """
        prob_stmts.append(prob_stmt.strip())

        prob_stmt = f"""
        Find the distance of the point ${v3.get_latex()}$ from the line
        
        $\\displaystyle {self.get_line_eq_latex(line_eq)} $
        """
        prob_stmts.append(prob_stmt.strip())


        C, P, D, DC, r = get_arrow_symbols("C P D DC r".split(" "))
        
        r_unit = Symbol(f"\\hat{{r}}")

        alpha, beta, gamma = symbols(f"\\alpha, \\beta, \\gamma")

        x, y, z = symbols("x, y, z")
        vP = self.get_vector(x, y, z, N)
        
        x0, y0, z0 = symbols("x_0, y_0, z_0")
        vP0 = self.get_vector(x0, y0, z0, N)

        P0P_coords = (vP - vP0).get_coords()
        P_coords = vP.get_coords()
        
        soln_stmt = f"""
        We have equation of a line and a point (may not be on the line). 
        We will find the direction vector and a point on the line. 
        Then we will use the property of the area of parallelogram formed by two vectors.
        """
        soln_stmts.append(soln_stmt.replace("\n", " ").strip())


        soln_stmt = f"""
        The standard parametric equation of a line is
        
        $\\displaystyle \\implies
        \\frac {{ {latex(P0P_coords[0])} }} {{ {latex(cos(alpha))} }} 
        
        = \\frac {{ {latex(P0P_coords[1])} }} {{ {latex(cos(beta))} }} 
        
        = \\frac {{ {latex(P0P_coords[2])} }} {{ {latex(cos(gamma))} }} 
        $ 
        
        where $\\displaystyle \\left(
        {latex(cos(alpha))}, {latex(cos(beta))}, {latex(cos(gamma))}
        \\right)
        $ is called direction vector of the line.
        """
        soln_stmts.append(soln_stmt.strip())

        
        v_direction_vector = self.get_vector(*direction_tuple, N)

        soln_stmt = f"""
        Compare it with the given equation of the line:
        
        $\\displaystyle {self.get_line_eq_latex(line_eq)} $
        
        The direction vector of the line is $\\displaystyle {latex(direction_tuple)} $
        
        In vector form, it is
        
        $\\displaystyle {latex(r)} = {v_direction_vector.get_latex()} 
        \\qquad \\cdots \\text {{ ({sub_problem_idx}{ref_idx + 1}) }}
        $
        """
        soln_stmts.append(soln_stmt.strip())


        result_point = self.get_problem_find_point_on_the_line(
                                        line_eq, N, r_val = 0, 
                                        vector_subscript = D,
                                        ref_idx = 1,
                                        **kwargs)
        v4 = result_point.get('v')
        
        soln_stmt = f"""
        {result_point.get('soln_s')}
        """
        soln_stmts.append(soln_stmt.strip())


        soln_stmt = f"""
        We already have another point (may not be on the line), as given in the problem.
        
        $\\displaystyle
        {latex(C)} = {v3.get_latex()} 
        \\qquad \\cdots \\text {{ ({sub_problem_idx}{ref_idx + 3}) }}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        distance = Symbol('d')


        soln_stmt = f"""
        We have ${latex(r)}$ a line segment on the line. ${latex(DC)}
        $ forms a parallelogram with ${latex(D)}$ as one of vertices on the line.
        
        The area of this parallelogram can be derived in two ways:
        TODO: Add image.
        
        $\\displaystyle
        {latex(r)} \\times {latex(DC)} = 
        {latex(r)} \\times {latex(distance)}
        \\qquad \\text{{ where }} {latex(distance)} \\text{{ 
        is the perpendicular distance between the line and point }} {latex(C)}
        $
        
        $\\displaystyle \\implies
        \\left | {latex(r)} \\times {latex(DC)} \\right | = 
        \\left | {latex(r)}  \\right | \\times {latex(distance)}
        $
        
        $\\displaystyle \\implies
        {latex(distance)} = 
        \\frac {{ \\left | {latex(r)} \\times {latex(DC)} \\right | }} 
               {{ \\left | {latex(r)}  \\right |  }}
        \\qquad \\cdots \\text {{ ({sub_problem_idx}{ref_idx + 4}) }}
        $
        """
        soln_stmts.append(soln_stmt.strip())


        soln_stmt = f"""
        Now, we can calculate the values required compute ${latex(distance)}
        $ from ({sub_problem_idx}{ref_idx + 4}).
        """
        soln_stmts.append(soln_stmt.strip())


        v_rDC = v_direction_vector.cross(v3 - v4)
        base_vectors  = get_arrow_symbols(['i', 'j', 'k'])
        matrix = self.convert_to_matrix(base_vectors, v_direction_vector, v3 - v4)

        soln_stmt = f"""
        $\\displaystyle 
        \\begin{{aligned}}
        
        {latex(r)} \\times {latex(DC)}
        
        & = {latex(r)} \\times \\left[ {latex(C)} - {latex(D)} \\right]
        \\\\ \\\\
        
        & = \\left( {v_direction_vector.get_latex()} \\right)
        \\; \\times \\;
        \\left[ 
            \\left( {v3.get_latex()} \\right) - \\left( {v4.get_latex()} \\right)
        \\right]
        \\\\ \\\\
        
        & = \\left( {v_direction_vector.get_latex()} \\right)
        \\; \\times \\;
        \\left( {(v3 - v4).get_latex()} \\right)
        \\\\ \\\\
        
        & = {latex(matrix)}
        \\\\ \\\\
        
        & = {v_rDC.get_latex()}
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())



        v_rDC_coords = v_rDC.get_coords()

        
        v_rDC_magnitude = v_rDC.magnitude()

        soln_stmt = f"""
        The magnitude of ${latex(r)} \\times {latex(DC)}$
        
        $\\displaystyle 
        \\begin{{aligned}}
        
        \\left| {latex(r)} \\times {latex(DC)} \\right|
        
        & = {self.get_magnitude_calc_latex(v_rDC)} 
        \\\\ \\\\
        
        & = {latex(v_rDC_magnitude)}
        \\qquad \\cdots \\text {{ ({sub_problem_idx}{ref_idx + 5}) }}
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())




        r_coords = v_direction_vector.get_coords()

        r_magnitude = v_direction_vector.magnitude()

        soln_stmt = f"""
        The magnitude of ${latex(r)}$
        
        $\\displaystyle 
        \\begin{{aligned}}
        
        \\left| {latex(r)} \\right|
        
        & = {self.get_magnitude_calc_latex(v_direction_vector)} 
        \\qquad \\cdots \\text {{ using ({sub_problem_idx}{ref_idx + 1}) }}
        \\\\ \\\\
        
        & = {latex(r_magnitude)}
        \\qquad \\cdots \\text {{ ({sub_problem_idx}{ref_idx + 6}) }}
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        d_val = v_rDC_magnitude / r_magnitude

        soln_stmt = f"""
        Substituting the values from ({sub_problem_idx}{ref_idx + 5}) and ({sub_problem_idx}{ref_idx + 6}) in ({sub_problem_idx}{ref_idx + 4}):
        
        $\\displaystyle 
        \\begin{{aligned}}
        
        {latex(distance)} = 

        & = \\frac {{ \\left | {latex(r)} \\times {latex(DC)} \\right | }} 
                   {{ \\left | {latex(r)}  \\right |  }}
        \\\\ \\\\
        
        & = \\frac {{ {latex(v_rDC_magnitude)} }} {{ {latex(r_magnitude)}  }}
        \\\\ \\\\
        
        & = {latex(d_val)}
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())




        answer_stmt = f"""
        $\\displaystyle {latex(d_val)} $
        """
        answer_stmts.append(answer_stmt.strip())


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



    def get_problem_find_point_on_the_line(self, line_eq, N, r_val = None, **kwargs):
        '''
        @vector_subscript: it is a symbol
        '''

        explanation = kwargs.get('explanation', True)
        ref_idx = kwargs.get('ref_idx', 0)
        sub_problem_idx = kwargs.get('sub_problem_idx', None)
        if sub_problem_idx is None:
            sub_problem_idx = ""
        else:
            sub_problem_idx = f"{sub_problem_idx}."

        
        vector_subscript = kwargs.get('vector_subscript', None)
        
        if not vector_subscript is None:
            D = vector_subscript
        else:
            D = Symbol('D')
            
        
        x, y, z = symbols("x, y, z")
        vP = self.get_vector(x, y, z, N)
        P_coords = vP.get_coords()
        
        prob_stmts = []
        soln_stmts = []
        answer_stmts = []
        
        prob_stmt = f"""
        """
        prob_stmts.append(prob_stmt.strip())

        prob_stmt = f"""
        Find a point equation on the line 
        
        $\\displaystyle {self.get_line_eq_latex(line_eq)}$
        """
        prob_stmts.append(prob_stmt.strip())
        
        
        if r_val is None:
            r_val = 0

        D_coords = []
        for idx, line_eq_item in enumerate(line_eq):
            item_vals = sp.solve(Eq(line_eq_item[0], line_eq_item[1] * r_val), 
                                 P_coords[idx])
            D_coords.append(item_vals[0])
            pass

        D_coords = tuple(D_coords)
        v4 = self.get_vector(*D_coords, N)

        if explanation:
            soln_stmt = f"""
            To find a point on the line, we can equate its equation to any real value. 
            """
            soln_stmts.append(soln_stmt.strip())


        soln_stmt = f"""
        $\\displaystyle {self.get_line_eq_latex(line_eq)} = {latex(r_val)} $
        
        $\\displaystyle \\implies
        {latex(D)} {latex(tuple(P_coords))} =  = {latex(D_coords)} 
        $
        
        $\\displaystyle \\implies
        {latex(D)} = {v4.get_latex()} 
        \\qquad \\cdots \\text {{ ({sub_problem_idx}{ref_idx + 1}) }}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        answer_stmt = f"""
        $\\displaystyle {v4.get_latex()} $
        """
        answer_stmts.append(answer_stmt.strip())


        prob_s = "\n\n".join(prob_stmts)
        soln_s = "\n\n".join(soln_stmts)
        answer_s = "\n\n".join(answer_stmts)
        
        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            'v': v4,
            }
        return result
        pass

    
    pass
#class Geometry3dLineHelper:
