'''
Created on 12-Jul-2021

@author: rishi_priyansh

'''
import inspect
import traceback

import numpy as np
import random

from sympy import (Symbol, symbols, 
                   Derivative, Integral,
                   UnevaluatedExpr,
                   latex, Rational, simplify,
                   )


from xv.km.ui import SvgHelper
from xv.util.data import CacheUtil
from xv.util import strip_source_code_tabs, get_function_source_code
from xv.km.util import latex_fraction

from xv.plotter.helper import DiscreteXyGraphHelper, ContinuousXyGraphHelper

from ._base_helper import BaseHelper

class NewtonianSpaceTimeHelper(BaseHelper):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        
        self.discrete_xy_graph_helper = DiscreteXyGraphHelper(**kwargs)
        self.continuous_xy_graph_helper = ContinuousXyGraphHelper(**kwargs)
        pass

    def get_statement(self):
        m = Symbol('m')
        s = Symbol('s')
        s0 = symbols('s_0')

        t = Symbol('t', positive = True)
        t0 = symbols('t_0')
        
        v = Symbol('v')
        v0 = symbols('v_0')
        
        a = Symbol('a')
        a0 = symbols('a_0')
        
        p = Symbol('p')
        p0 = symbols('p_0')
        
        F = Symbol('F')
        W = Symbol('W')
        k = Symbol('k')

        label_dict = {
            m: 'mass',
            s: 'displacement',
            t: 'time',
            v: 'velocity',
            a: 'acceleration',
            p: 'momentum',
            F: 'force',
            W: 'work',
            k: 'constant',
        }


        label_dict_str = [f"\t{k} = {v}" for k,v in label_dict.items()]
        label_dict_str = "\n".join(label_dict_str)
        
        
        expr1 = UnevaluatedExpr(v0) + UnevaluatedExpr(Integral(a, (t, t0, t))) 

        advanced_form = f"""
        $\\displaystyle 
        {latex(v)} 
        = {latex(Derivative(s, t))}
        
        \\implies 
        {latex(Integral(1, (s, s0, s)))}
        = {latex(Integral(v, (t, t0, t)))}
        
        \\implies 
        {latex(s - s0)}
        = {latex(Integral(v, (t, t0, t)))}
        $
        
        $\\displaystyle 
        {latex(a)} 
        = {latex(Derivative(v, t))} 
        
        \\implies 
        {latex(Integral(1, (v, v0, v)))}
        = {latex(Integral(a, (t, t0, t)))}
        
        \\implies 
        {latex(v - v0)}
        = {latex(Integral(a, (t, t0, t)))}
        $
        
        $\\displaystyle 
        \\implies 
        {latex(Derivative(s, t))}
        = {latex(expr1)}
        
        \\implies 
        {latex(Integral(1, (s, s0, s)))}
        = {latex(Integral(expr1, (t, t0, t)))} 
        $
        

        $\\displaystyle 
        {latex(a)} 
        = {latex(Derivative(v, t))} 

        \\implies 
        {latex(a)} 
        = {latex(Derivative(v, s) * Derivative(s, t))}
        
        \\implies 
        {latex(a)} 
        = {latex(Derivative(v, s) * v)}
        
        \\implies 
        {latex(Integral(a, (s, s0, s)))}
        = {latex(Integral(v, (v, v0, v)))} 
        $
        

        $\\displaystyle 
        {latex(p)} 
        = {latex(m * v)}
        = {latex(m * Derivative(s, t))}
        $
        
        $\\displaystyle 
        {latex(F)} 
        = {latex(m * UnevaluatedExpr(a))}
        = {latex(m * Derivative(v, t))}
        = {latex(m * Derivative(Derivative(s, t), t))} 
        $

        $\\displaystyle 
        {latex(W)} 
        = {latex(F * s)}
        $
        
        """
        
        result = {
            'label_dict_str': label_dict_str,
            'advanced_form': advanced_form,
            }

        return result
        pass
 
    def get_discrete_time_position_graph(self):
        discrete_graph_helper = self.discrete_xy_graph_helper 

        x_label = 'time'
        y_label = 'position'
        slope_label = 'velocity'
        area_label = None
        y_delta_label = 'displacement'
        
        m = Symbol('m')
        s = Symbol('s')
        s0 = symbols('s_0')
        delta_s = Symbol(r'\Delta s')

        t = Symbol('t', positive = True)
        t0 = symbols('t_0')
        delta_t = Symbol(r'\Delta t')
        
        v = Symbol('v')
        v0 = symbols('v_0')
        
        a = Symbol('a')
        a0 = symbols('a_0')
        
        p = Symbol('p')
        p0 = symbols('p_0')
        
        F = Symbol('F')
        W = Symbol('W')
        k = Symbol('k')

        label_dict = {
            m: 'mass',
            s: 'displacement',
            t: 'time',
            v: 'velocity',
            a: 'acceleration',
            p: 'momentum',
            F: 'force',
            W: 'work',
            k: 'constant',
        }

        label_dict_str = [f"\t{k} = {v}" for k,v in label_dict.items()]
        label_dict_str = "\n".join(label_dict_str)

        x_values, y_values = discrete_graph_helper.get_random_xy_values()
        
        xy_labels = list('ABCDEFGHIJKLMNOPQRSTUVWZYZ')[:len(x_values)]
        
        
        
        plot_code, svg_str = discrete_graph_helper.plot_xy_graph(
                                                         x_label, 
                                                         y_label,
                                                         x_values, 
                                                         y_values, 
                                                         xy_labels,
                                                         use_cache = False)
        
        start_label = f"{xy_labels[0]}({x_values[0]}, {y_values[0]})"
        end_label = f"{xy_labels[-1]}({x_values[-1]}, {y_values[-1]})"
        
        
        
        delta_x_stmt = f"""
        The {x_label} between {start_label} and {end_label}
        = $\\Delta {latex(t)}$
        = {x_values[-1]} - {x_values[0]}
        = {x_values[-1] - x_values[0]}
        """.strip()

        delta_y_stmt = f"""
        The {y_delta_label} between {start_label} and {end_label}
        = $\\Delta {latex(s)}$
        = {y_values[-1]} - {y_values[0]}
        = {y_values[-1] - y_values[0]}
        """.strip()
        
          
        result_average_slope = discrete_graph_helper.get_slope_statement(
                        slope_label = slope_label,
                        slope_symbol = Symbol(f"\\overline{{ {latex(v)} }}"),
                        start_idx = 0, 
                        end_idx = len(x_values) - 1,
                        x_values = x_values,
                        y_values = y_values,
                        xy_labels = xy_labels,
                        x = t,
                        y = s,
                        slope_prefix = 'average')
        
        
        rndm_idxs = sorted(random.sample(list(range(len(x_values))), 
                                         2))
        
        result_rndm_slope = discrete_graph_helper.get_slope_statement(
                        slope_label = slope_label,
                        slope_symbol = v,
                        start_idx = rndm_idxs[0], 
                        end_idx = rndm_idxs[1],
                        x_values = x_values,
                        y_values = y_values,
                        xy_labels = xy_labels,
                        x = t,
                        y = s,
                        slope_prefix = '')
        
        result = {
            'x': t,
            'y': s,
            'x_label': x_label,
            'y_label': y_label,
            
            'slope_label': slope_label,
            
            'area_label': area_label,

            'start_label': start_label,
            'end_label': end_label,
            'label_dict_str': label_dict_str,
            'plot_code': plot_code,
            'svg_str': svg_str,
            
            
            'delta_x_stmt': delta_x_stmt,
            'delta_y_stmt': delta_y_stmt,

            'result_average_slope': result_average_slope,
            'result_rndm_slope': result_rndm_slope,
            }

        return result
        pass

    def get_discrete_time_velocity_graph(self):
        discrete_graph_helper = self.discrete_xy_graph_helper 
        
        x_label = 'time'
        y_label = 'velocity'
        slope_label = 'acceleration'
        area_label = 'displacement'
        
        
        m = Symbol('m')
        s = Symbol('s')
        s0 = symbols('s_0')

        t = Symbol('t', positive = True)
        t0 = symbols('t_0')
        delta_t = Symbol(r'\Delta t')
        
        v = Symbol('v')
        v0 = symbols('v_0')
        delta_v = Symbol(r'\Delta v')
        
        a = Symbol('a')
        a0 = symbols('a_0')
        
        p = Symbol('p')
        p0 = symbols('p_0')
        
        F = Symbol('F')
        W = Symbol('W')
        k = Symbol('k')

        label_dict = {
            m: 'mass',
            s: 'displacement',
            t: 'time',
            v: 'velocity',
            a: 'acceleration',
            p: 'momentum',
            F: 'force',
            W: 'work',
            k: 'constant',
        }

        label_dict_str = [f"\t{k} = {v}" for k,v in label_dict.items()]
        label_dict_str = "\n".join(label_dict_str)

        
        x_values, y_values = discrete_graph_helper.get_random_xy_values()
        
        xy_labels = list('ABCDEFGHIJKLMNOPQRSTUVWZYZ')[:len(x_values)]
        
        plot_code, svg_str = discrete_graph_helper.plot_xy_graph(
                                                 x_label, 
                                                 y_label,
                                                 x_values, 
                                                 y_values, 
                                                 xy_labels,
                                                 use_cache = False)
        
        start_label = f"{xy_labels[0]}({x_values[0]}, {y_values[0]})"
        end_label = f"{xy_labels[-1]}({x_values[-1]}, {y_values[-1]})"
        
        delta_x_stmt = f"""
        The {x_label} between {start_label} and {end_label}
        = $\\Delta {latex(t)}$
        = {x_values[-1]} - {x_values[0]}
        = {x_values[-1] - x_values[0]}
        """  

        delta_y_stmt = f"""
        The {y_label} between {start_label} and {end_label}
        = $\\Delta {latex(v)}$
        = {y_values[-1]} - {y_values[0]}
        = {y_values[-1] - y_values[0]}
        """  

        rndm_start_idx = random.choice(list(range(len(x_values)))[:-1])
        rndm_end_idx = rndm_start_idx + 1
        rndm_idxs = [rndm_start_idx, rndm_end_idx]
                
        result_rndm_area = discrete_graph_helper.get_area_statement(
                        area_label = area_label,
                        area_symbol = s,
                        start_idx = rndm_idxs[0], 
                        end_idx = rndm_idxs[1],
                        x_values = x_values,
                        y_values = y_values,
                        xy_labels = xy_labels,
                        x = t, 
                        y = v,
                        area_prefix = ''
                        )
        
        
        result_total_area = discrete_graph_helper.get_area_statement(
                        area_label = area_label,
                        area_symbol = s,
                        start_idx = 0, 
                        end_idx = len(x_values) - 1,
                        x_values = x_values,
                        y_values = y_values,
                        xy_labels = xy_labels,
                        x = t, 
                        y = v,
                        area_prefix = 'total'
                        )
        
        
        result_average_slope = discrete_graph_helper.get_slope_statement(
                        slope_label = slope_label,
                        slope_symbol = Symbol(f"\\overline{{ {latex(a)} }}"),
                        start_idx = 0, 
                        end_idx = len(x_values) - 1,
                        x_values = x_values,
                        y_values = y_values,
                        xy_labels = xy_labels,
                        x = t,
                        y = v,
                        slope_prefix = 'average')
        
        result_rndm_slope = discrete_graph_helper.get_slope_statement(
                        slope_label = slope_label,
                        slope_symbol = a,
                        start_idx = rndm_idxs[0], 
                        end_idx = rndm_idxs[1],
                        x_values = x_values,
                        y_values = y_values,
                        xy_labels = xy_labels,
                        x = t,
                        y = v,
                        slope_prefix = '')
        
        result_slope_at_a_point = discrete_graph_helper.get_slope_statement(
                        slope_label = slope_label,
                        slope_symbol = a,
                        start_idx = rndm_idxs[0], 
                        end_idx = rndm_idxs[0],
                        x_values = x_values,
                        y_values = y_values,
                        xy_labels = xy_labels,
                        x = t,
                        y = v,
                        slope_prefix = '')
        
        
        result = {
            'x': t,
            'y': v,
            'x_label': x_label,
            'y_label': y_label,
            
            'slope_label': slope_label,
            'result_average_slope': result_average_slope,
            
            'area_label': area_label,
            'result_total_area': result_total_area,

            'start_label': start_label,
            'end_label': end_label,
            'label_dict_str': label_dict_str,
            'plot_code': plot_code,
            'svg_str': svg_str,
            
            'delta_x_stmt': delta_x_stmt,
            'delta_y_stmt': delta_y_stmt,
            
            'result_rndm_area': result_rndm_area,
            'result_rndm_slope': result_rndm_slope,
            'result_slope_at_a_point': result_slope_at_a_point,
            }
        
        return result
        pass
    
    def get_problem_continuous_time_position_graph(self, **kwargs):
        '''
        '''
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: 
        kwargs = {kwargs}
        """
        self._log(sInfo.strip())
        

        #build data
        x_label = 'time'
        y_label = 'position'
        slope_label = 'velocity'
        area_label = None
        
        m = Symbol('m')
        s = Symbol('s')
        s0 = symbols('s_0')

        t = Symbol('t', positive = True)
        t0 = symbols('t_0')
        delta_t = Symbol(r'\Delta t')
        
        v = Symbol('v')
        v0 = symbols('v_0')
        delta_v = Symbol(r'\Delta v')
        
        a = Symbol('a')
        a0 = symbols('a_0')
        
        p = Symbol('p')
        p0 = symbols('p_0')
        
        F = Symbol('F')
        W = Symbol('W')
        k = Symbol('k')
        ke = Symbol('ke')

        label_dict = {
            m: 'mass',
            s: 'displacement',
            t: 'time',
            v: 'velocity',
            a: 'acceleration',
            p: 'momentum',
            F: 'force',
            W: 'work',
            k: 'constant',
            ke: 'kinetic energy',
        }
        

        label_dict_str = [f"\t{k} = {v}" for k,v in label_dict.items()]
        label_dict_str = "\n".join(label_dict_str)

        x_range = [0, 10]
        position_function = random.randint(-9, 9) + (
                            + random.randint(-1, 1) * t ** 1
                            + random.randint(-3, 3) * t ** 2
                            + random.randint(-1, 1) * t ** 3
                            + random.randint(-1, 1) * t ** 4
                            )
        
        xy_labels = list('ABCDEFGHIJKLMNOPQRSTUVWZYZ')[:2]
        
        xy_graph_data = {
                     'x': t,
                     'y': s,
                     'x_range': x_range,
                     'y_function': position_function,
                     'area_symbol': None,
                     'slope_symbol': v,
                     'x_label': x_label,
                     'y_label': y_label,
                     'area_label': area_label,
                     'slope_label': slope_label,
                     'xy_labels': xy_labels,
                     }
        
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: 
        xy_graph_data = {xy_graph_data}
        """
        self._log(sInfo.strip())

        continuous_graph_helper = self.continuous_xy_graph_helper
        
        y_function = xy_graph_data.get('y_function')
        x = xy_graph_data.get('x')
        y = xy_graph_data.get('y')

        prob_graph = continuous_graph_helper.get_xy_graph( use_cache = False,
                                                             **xy_graph_data,  
                                                             )

        prob_s = f"""
        Write a commentary on the following {x_label}({x}) 
        vs {y_label}({y}) graph.
        <br />
        <br />
        $\\displaystyle 
        {latex(y)} = {latex(y_function)}
        $
        {prob_graph['svg_str']}
        """
        prob_s = prob_s.replace("\n", " ").strip()
        
        
        soln_stmts = []
        
        x0 = random.randint(list(range(*x_range)))
        
        slope_result = continuous_graph_helper.get_slope_statement(**xy_graph_data,
                                                                    x0 = x0,
                                                                    use_cache = False,
                                                                    )
        
        if slope_result is None:
            sError = f"""Error {self.__class__.__name__}.{inspect.stack()[0][3]}: 
            Call to get_slope_statement failed.
            """
            self._log(sError.strip())
            return None
        
        
        
        soln_stmt = f"""
        <b>Let</b>
        {label_dict_str}

        <b>The graph is shown for (x = {x_label}, y = {y_label}).</b>
        {slope_result['tangent_svg_str']}

        {slope_result['slope_at_x0_stmt']}
        
        
        """
        soln_stmts.append(soln_stmt.strip())

        #{slope_result['tangent_stmt']}
        
        #{slope_result['ke_stmt']}
        
        #{slope_result['ke_stmt_at_a_point']}
        
        #{slope_result['plot_code']}
        
        soln_stmt = f"""
        <h2>PLot Code:</h2>
        <pre></pre>
        """
        soln_stmts.append(soln_stmt.strip())

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

        
        
        
        
        start_label = f"{xy_labels[0]}({t} = {x_range[0]})"
        end_label = f"{xy_labels[-1]}({t} = {x_range[-1]})"
        
        t0 = random.randint(*x_range)

        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: 
        slope_result.keys() = {slope_result.keys()}
        """
        self._log(sInfo.strip())
        
        
        velocity_function = self.get_velocity_from_position_function(position_function, t)
        ke = self.get_ke(m, velocity_function)
        
        ke_stmt = self.get_ke_statement_from_position_function(
                                m, position_function, t, s, v = v)
        
        ke_stmt_at_a_point = self.get_ke_statement_at_a_point(ke, t, t0)
        
        result = xy_graph_data 
        
        result.update({
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            'start_label': start_label,
            'end_label': end_label,
            'label_dict_str': label_dict_str,
            'plot_code': prob_graph['plot_code'],
            'svg_str': prob_graph['svg_str'],
            
            'slope_at_x0_stmt': slope_result['slope_at_x0_stmt'],
            
            'tangent_stmt': slope_result['tangent_stmt'],
            'tangent_svg_str': slope_result['tangent_svg_str'],
            
            'ke': ke,
            'ke_stmt': ke_stmt,
            'ke_stmt_at_a_point': ke_stmt_at_a_point,
            })
        
        
        return result
        
        pass
    
    def get_continuous_time_position_graph(self):
        continuous_graph_helper = self.continuous_xy_graph_helper
        
        x_label = 'time'
        y_label = 'position'
        slope_label = 'velocity'
        area_label = None
        
        m = Symbol('m')
        s = Symbol('s')
        s0 = symbols('s_0')

        t = Symbol('t', positive = True)
        t0 = symbols('t_0')
        delta_t = Symbol(r'\Delta t')
        
        v = Symbol('v')
        v0 = symbols('v_0')
        delta_v = Symbol(r'\Delta v')
        
        a = Symbol('a')
        a0 = symbols('a_0')
        
        p = Symbol('p')
        p0 = symbols('p_0')
        
        F = Symbol('F')
        W = Symbol('W')
        k = Symbol('k')
        ke = Symbol('ke')

        label_dict = {
            m: 'mass',
            s: 'displacement',
            t: 'time',
            v: 'velocity',
            a: 'acceleration',
            p: 'momentum',
            F: 'force',
            W: 'work',
            k: 'constant',
            ke: 'kinetic energy',
        }
        

        label_dict_str = [f"\t{k} = {v}" for k,v in label_dict.items()]
        label_dict_str = "\n".join(label_dict_str)

        x_range = [0, 10]
        position_function = random.randint(-9, 9) + (
                            + random.randint(-1, 1) * t ** 1
                            + random.randint(-3, 3) * t ** 2
                            + random.randint(-1, 1) * t ** 3
                            + random.randint(-1, 1) * t ** 4
                            )
        
        xy_labels = list('ABCDEFGHIJKLMNOPQRSTUVWZYZ')[:2]
        
        xy_graph_data = {
                     'x': t,
                     'y': s,
                     'x_range': x_range,
                     'y_function': position_function,
                     'area_symbol': None,
                     'slope_symbol': v,
                     'x_label': x_label,
                     'y_label': y_label,
                     'area_label': area_label,
                     'slope_label': slope_label,
                     'xy_labels': xy_labels,
                     }
        
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: 
        xy_graph_data = {xy_graph_data}
        """
        self._log(sInfo.strip())
        
        
        result_graph = continuous_graph_helper.get_xy_graph( use_cache = False,
                                                             **xy_graph_data,  
                                                             )
        
        start_label = f"{xy_labels[0]}({t} = {x_range[0]})"
        end_label = f"{xy_labels[-1]}({t} = {x_range[-1]})"
        
        t0 = random.randint(*x_range)

        slope_result = continuous_graph_helper.get_slope_statement(**xy_graph_data,
                                                                    x0 = t0,
                                                                    use_cache = False,
                                                                    )
        
        if slope_result is None:
            sError = f"""Error {self.__class__.__name__}.{inspect.stack()[0][3]}: 
            Call to get_slope_statement failed.
            """
            self._log(sError.strip())
            return None
        
        
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: 
        slope_result.keys() = {slope_result.keys()}
        """
        self._log(sInfo.strip())
        
        
        velocity_function = self.get_velocity_from_position_function(position_function, t)
        ke = self.get_ke(m, velocity_function)
        
        ke_stmt = self.get_ke_statement_from_position_function(
                                m, position_function, t, s, v = v)
        
        ke_stmt_at_a_point = self.get_ke_statement_at_a_point(ke, t, t0)
        
        
        result = xy_graph_data 
        
        
        result.update({
            'start_label': start_label,
            'end_label': end_label,
            'label_dict_str': label_dict_str,
            'plot_code': result_graph['plot_code'],
            'svg_str': result_graph['svg_str'],
            
            'slope_at_x0_stmt': slope_result['slope_at_x0_stmt'],
            
            'tangent_stmt': slope_result['tangent_stmt'],
            'tangent_svg_str': slope_result['tangent_svg_str'],
            
            'ke': ke,
            'ke_stmt': ke_stmt,
            'ke_stmt_at_a_point': ke_stmt_at_a_point,
            })
        
        
        return result
        pass

    
    def get_continuous_time_velocity_graph(self):
        continuous_graph_helper = self.continuous_xy_graph_helper
        
        x_label = 'time'
        y_label = 'velocity'
        slope_label = 'acceleration'
        area_label = 'displacement'
        
        m = Symbol('m')
        s = Symbol('s')
        s0 = symbols('s_0')

        t = Symbol('t', positive = True)
        t0 = symbols('t_0')
        delta_t = Symbol(r'\Delta t')
        
        v = Symbol('v')
        v0 = symbols('v_0')
        delta_v = Symbol(r'\Delta v')
        
        a = Symbol('a')
        a0 = symbols('a_0')
        
        p = Symbol('p')
        p0 = symbols('p_0')
        
        F = Symbol('F')
        W = Symbol('W')
        k = Symbol('k')

        label_dict = {
            m: 'mass',
            s: 'displacement',
            t: 'time',
            v: 'velocity',
            a: 'acceleration',
            p: 'momentum',
            F: 'force',
            W: 'work',
            k: 'constant',
        }

        

        label_dict_str = [f"\t{k} = {v}" for k,v in label_dict.items()]
        label_dict_str = "\n".join(label_dict_str)

        x_range = [0, 10]
        velocity_function = 5 + 4 * t
        
        xy_labels = list('ABCDEFGHIJKLMNOPQRSTUVWZYZ')[:2]
        
        plot_code, svg_str = continuous_graph_helper.plot_xy_graph(
                                                 x_label, 
                                                 y_label,
                                                 t,
                                                 x_range, 
                                                 velocity_function, 
                                                 xy_labels,
                                                 use_cache = False)
        
        start_label = f"{xy_labels[0]}({t} = {x_range[0]})"
        end_label = f"{xy_labels[-1]}({t} = {x_range[-1]})"
        
        slope, slope_stmt = continuous_graph_helper.get_slope_statement('acceleration',
                                                              v, 
                                                              velocity_function, 
                                                              t)
        
        
        area, area_stmt = continuous_graph_helper.get_area_statement('displacement',
                                                              v, 
                                                              velocity_function, 
                                                              t, t0, t)
        
        total_area, total_area_stmt = continuous_graph_helper.get_area_statement('displacement',
                                                              v, 
                                                              velocity_function, 
                                                              t, *x_range,
                                                              name_prefix = "total")
        
        
        result = {
            'x': t,
            'y': v,
            'y_function': velocity_function,
            'x_label': x_label,
            'y_label': y_label,
            'slope_label': slope_label,
            'slope_stmt': slope_stmt,
            
            'area_label': area_label,
            'area_stmt': area_stmt,
            'total_area_stmt': total_area_stmt,

            'start_label': start_label,
            'end_label': end_label,
            'label_dict_str': label_dict_str,
            'plot_code': plot_code,
            'svg_str': svg_str,
            }

        return result
        pass
    
    def get_ke_statement_at_a_point(self, 
                                     ke_function,
                                     t,
                                     t0 = None):
        
        ke_stmt = f"""
        
        kinetic energy at $\\displaystyle {latex(t)} = {latex(t0)}$
        
        $\\displaystyle = \\left. {latex(ke_function)} \\right|_{{{latex(t)} = {latex(t0)}}}$
        
        $\\displaystyle = {latex(ke_function.subs(t, t0))}$
        
        """.strip()
    
        return ke_stmt
        pass
    
    
    def get_ke_statement_from_position_function(self, 
                                     m,
                                     position_function,
                                     t,
                                     s,
                                     v = None):
        if v is None:
            v = Symbol('v')
        
        ke = Rational(1, 2) * m * (Derivative(position_function, t).doit())**2
        
        ke_latex = f"""
        {latex(Rational(1, 2))} * {latex(m)} * 
        {latex(Derivative(s, t) ** 2)}
        """.strip()
        
        ke_latex_2 = f"""
        {latex(Rational(1, 2))} * {latex(m)} * 
        {latex(Derivative(position_function, t) ** 2)}
        """.strip()
        
        ke_latex_3 = f"""
        {latex(Rational(1, 2))} * {latex(m)} * 
        {latex(Derivative(position_function, t).doit() ** 2)}
        """.strip()
        
        ke_stmt = f"""
        <b>kinetic energy</b>
        
        $\\displaystyle = {latex(Rational(1, 2))} * {latex(m)} * {latex(v**2)} $
        
        $\\displaystyle = {ke_latex}$
        
        $\\displaystyle = {ke_latex_2}$
        
        $\\displaystyle = {ke_latex_3}$
        
        $\\displaystyle = {latex(ke)}$
        """.strip()
        return ke_stmt
        pass
    
    
    def get_ke(self, m, v):
        ke = Rational(1, 2) * m * v**2
        return ke
        pass
    
    def get_velocity_from_position_function(self, position_function, t):
        v = Derivative(position_function, t).doit()
        return v
        pass
    
    pass
#class NewtonianSpaceTimeHelper:
    