'''
Created on 12-Jul-2021

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

from sympy import (latex, Symbol, symbols, UnevaluatedExpr as ueval
                   )
import sympy as sp
from xv.km.util import SympyUnitSystem

from ._base_helper import BaseHelper

from xv.util import list_to_str

# _optics_helper
# OpticsHelper


class OpticsHelper(BaseHelper):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        pass
    
    def get_problem_refractive_index(self, **kwargs):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: 
        """
        self._log(sInfo.strip())

        methods = [
            self._get_speed_of_light_in_a_medium,
            self._get_change_in_speed_of_light,
            self._get_angle_of_refraction_from_medium_1_to_medium_2,
            self._get_angle_of_refraction_passing_through_multiple_mediums,
            ]

        index = kwargs.get('index', None)
        try:
            if index is None:
                indexes = list(range(len(methods)))
                identifier = f"{self.__class__.__name__}{id(self)}.{inspect.stack()[0][3]}.random_choices"
                index = self.get_no_repeat_random_choice(identifier, indexes)
            
            method = methods[index]
            return method(**kwargs)
        
        except Exception as e:
            sError = f"""Error {self.__class__.__name__}.{inspect.stack()[0][3]}: {e}
            methods = {methods}
            index = {index}
            """
            self._log(sError)
            self._log(traceback.format_exc())
            return None
        
        pass
    
    def _get_change_in_speed_of_light(self, **kwargs):

        su_system = self.get_sympy_unit_system()
        speed_of_light = su_system.to_unit('speed_of_light')
        meter, second =  su_system.to_units(['meter', 'second'])
        speed_of_light = su_system.convert_to(speed_of_light, [meter, second])

        refractive_index_1 = random.randint(10, 42)/10 
        refractive_index_2 = random.randint(10, 42)/10 
        mu1, mu2, c, v1, v2 = symbols('mu_1, mu_2, c, v_1, v_2')
        c_val = speed_of_light
        v1_val = (c_val/refractive_index_1).n(2)
        v2_val = (c_val/refractive_index_2).n(2)
        change_in_speed = v2_val - v1_val
        percent_change_in_speed = (change_in_speed/v1_val).n(2)

        prob_s = f"""
        If light moves from a medium($\\mu_1 = {refractive_index_1}$) to medium($\\mu_2 = {refractive_index_2}$), What will percent change in the speed of light?
        """
        prob_s = prob_s.replace("\n", " ").strip()

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

        & \\displaystyle 
        \\text{{Refractive index of a medium 1 }}
        = \\frac
        {{ \\text{{speed of light in vacuum}} }}
        {{ \\text{{speed of light in the medium 1}} }} 
        \\\\ \\\\

        & \\displaystyle 
        {latex(mu1)}
        = \\frac
        {{ {latex(c)} }}
        {{ {latex(v1)} }}  
        \\\\ \\\\

        & \\displaystyle 
        {latex(v1)}
        = \\frac
        {{ {latex(c)} }}
        {{ {latex(mu1)} }} 
        \\\\ \\\\

        & \\displaystyle 
        {latex(v1)}
        = \\frac
        {{ {latex(c_val)} }}
        {{ {latex(refractive_index_1)} }} 
        \\\\ \\\\

        & \\displaystyle 
        {latex(v1)}
        = {latex(v1_val)}

        \\end{{aligned}}
        $



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

        & \\displaystyle 
        \\text{{Refractive index of a medium 2 }}
        = \\frac
        {{ \\text{{speed of light in vacuum}} }}
        {{ \\text{{speed of light in the medium 2}} }} 
        \\\\ \\\\

        & \\displaystyle 
        {latex(mu2)}
        = \\frac
        {{ {latex(c)} }}
        {{ {latex(v2)} }}  
        \\\\ \\\\
    
        & \\displaystyle 
        {latex(v2)}
        = \\frac
        {{ {latex(c)} }}
        {{ {latex(mu2)} }} 
        \\\\ \\\\

        & \\displaystyle 
        {latex(v2)}
        = \\frac
        {{ {latex(c_val)} }}
        {{ {latex(refractive_index_2)} }} 
        \\\\ \\\\

        & \\displaystyle 
        {latex(v2)}
        = {latex(v2_val)}

        \\end{{aligned}}
        $

        

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

        \\text{{Percent change in speed}}
        & \\displaystyle 
        = \\frac
        {{ \\text{{Change in speed}} }}
        {{ \\text{{speed of light in the medium 1}} }} 
        \\\\ \\\\

        & \\displaystyle 
        = \\frac
        {{ {latex(v2_val)} - {latex(v1_val)} }}
        {{ {latex(v1_val)} }}  
        \\\\ \\\\
    
        & \\displaystyle 
        = \\frac
        {{ {latex(change_in_speed)} }}
        {{ {latex(v1_val)} }} 
        \\\\ \\\\

        & \\displaystyle 
        = {latex(percent_change_in_speed)}
        \\\\ \\\\

        & \\displaystyle 
        = {latex(percent_change_in_speed*100)} %

        \\end{{aligned}}
        $
        """
        soln_s = soln_s.strip()
        
        answer_s = f"""
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result
    
    def _get_angle_of_refraction_from_medium_1_to_medium_2(self, **kwargs):

        refractive_index_1 = random.randint(10, 42)/10 
        refractive_index_2 = random.randint(10, 42)/10 
        ratio_of_refractive_index = refractive_index_2/refractive_index_1
        mu1, mu2, theta, theta_r = symbols('mu_1, mu_2, theta, theta_r')
        theta_val = random.randint(10, 80)
        import math
        sin_theta_val = sp.sin(math.radians(theta_val))
        

        prob_s = f"""
        If light moves from medium 1 ($\\mu_1 = {refractive_index_1}$) to medium 2 ($\\mu_2 = {refractive_index_2}$), find the angle of refraction if angle of incidence is ${latex(theta)} = {theta_val}^\\circ$

        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        Using Snell's Law
        $\\displaystyle
        \\begin{{aligned}}

        & \\displaystyle 
        \\frac
        {{ {latex(mu2)} }}
        {{ {latex(mu1)} }} 
        = \\frac
        {{ \\sin{latex(theta)} }}
        {{ \\sin{latex(theta_r)} }} 
        \\\\ \\\\

        & \\displaystyle 
        \\frac
        {{ {latex(refractive_index_2)} }}
        {{ {latex(refractive_index_1)} }} 
        = \\frac
        {{ \\sin{latex(theta_val)}^\\circ }}
        {{ \\sin{latex(theta_r)} }} 
        \\\\ \\\\

        & \\displaystyle 
        {latex(ratio_of_refractive_index)} 
        = \\frac
        {{ {latex(sin_theta_val)} }}
        {{ \\sin{latex(theta_r)} }} 
        \\\\ \\\\

        & \\displaystyle 
        \\sin{latex(theta_r)} 
        = \\frac
        {{ {latex(sin_theta_val)} }}
        {{ {latex(ratio_of_refractive_index)} }} 
        \\\\ \\\\

        & \\displaystyle 
        \\sin{latex(theta_r)} 
        = {latex(sin_theta_val/ratio_of_refractive_index)}
        \\\\ \\\\

        & \\displaystyle 
        {latex(theta_r)} 
        = {latex(sp.asin(sin_theta_val/ratio_of_refractive_index))}
        \\\\ \\\\

        & \\displaystyle 
        {latex(theta_r)} 
        = {latex(math.degrees(sp.asin(sin_theta_val/ratio_of_refractive_index)))}^\\circ
        
        \\end{{aligned}}
        $
        
        """
        soln_s = soln_s.strip()
        
        answer_s = f"""
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result
    
    def _get_angle_of_refraction_passing_through_multiple_mediums(self, **kwargs):

        init_refractive_index = 1
        refractive_index_1 = random.randint(10, 42)/10 
        refractive_index_2 = random.randint(10, 42)/10 
        refractive_index_3 = random.randint(10, 42)/10 
        final_refractive_index = refractive_index_3
        ratio_of_refractive_index = init_refractive_index/final_refractive_index
        mu, mu1, mu2, mu3, theta, theta_r = symbols('mu, mu_1, mu_2, mu_3, theta, theta_r')
        theta_val = random.randint(10, 80)
        import math
        sin_theta_val = sp.sin(math.radians(theta_val))
        

        prob_s = f"""
        If the ray passes through media with refractive indexes $\\mu_1 = {refractive_index_1}$, $\\mu_2 = {refractive_index_2}$, $\\mu_3 = {refractive_index_3}$, find the angle of refraction if angle of incidence is${latex(theta)} = {theta_val}^\\circ$. Assuming initial refractive index of the medium is $\\mu = 1$

        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        Using Snell's Law
        $\\displaystyle
        \\begin{{aligned}}

        & \\displaystyle 
        \\frac
        {{ {latex(mu1)} }}
        {{ {latex(mu)} }} 
        \\cdot 
         \\frac
        {{ {latex(mu2)} }}
        {{ {latex(mu1)} }} 
        \\cdot 
         \\frac
        {{ {latex(mu3)} }}
        {{ {latex(mu2)} }} 

        = \\frac
        {{ \\sin{latex(theta)} }}
        {{ \\sin{latex(theta_r)} }} 
        \\\\ \\\\

        & \\displaystyle 
        \\frac
        {{ {latex(mu3)} }}
        {{ {latex(mu)} }} 
        
        = \\frac
        {{ \\sin{latex(theta)} }}
        {{ \\sin{latex(theta_r)} }} 
        \\\\ \\\\

        & \\displaystyle 
        \\frac
        {{ {latex(final_refractive_index)} }}
        {{ {latex(init_refractive_index)} }} 
        = \\frac
        {{ \\sin{latex(theta_val)}^\\circ }}
        {{ \\sin{latex(theta_r)} }} 
        \\\\ \\\\

        & \\displaystyle 
        {latex(ratio_of_refractive_index)} 
        = \\frac
        {{ {latex(sin_theta_val)} }}
        {{ \\sin{latex(theta_r)} }} 
        \\\\ \\\\

        & \\displaystyle 
        \\sin{latex(theta_r)} 
        = \\frac
        {{ {latex(sin_theta_val)} }}
        {{ {latex(ratio_of_refractive_index)} }} 
        \\\\ \\\\

        & \\displaystyle 
        \\sin{latex(theta_r)} 
        = {latex(sin_theta_val/ratio_of_refractive_index)}
        \\\\ \\\\

        & \\displaystyle 
        {latex(theta_r)} 
        = {latex(sp.asin(sin_theta_val/ratio_of_refractive_index))}
        \\\\ \\\\

        
        \\end{{aligned}}
        $
        
        """
        soln_s = soln_s.strip()
        
        answer_s = f"""
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result
    
    def _TEMP(self, **kwargs):

        
        prob_s = f"""

        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        
        """
        soln_s = soln_s.strip()
        
        answer_s = f"""
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result
    
    def _get_speed_of_light_in_a_medium(self, **kwargs):

        su_system = self.get_sympy_unit_system()
        speed_of_light = su_system.to_unit('speed_of_light')
        meter, second =  su_system.to_units(['meter', 'second'])
        speed_of_light = su_system.convert_to(speed_of_light, [meter, second])

        refractive_index = random.randint(10, 42)/10 
        mu, c, v = symbols('mu, c, v')
        c_val = speed_of_light
        v_val = (c_val/refractive_index).n(2)

        prob_s = f"""
        If refractive index of a medium is ${refractive_index}$, find the speed of in the medium.
        """
        prob_s = prob_s.replace("\n", " ").strip()

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

        & \\displaystyle 
        \\text{{Refractive index of a medium }}
        = \\frac
        {{ \\text{{speed of light in vacuum}} }}
        {{ \\text{{speed of light in the medium}} }} 
        \\\\ \\\\

        & \\displaystyle 
        {latex(mu)}
        = \\frac
        {{ {latex(c)} }}
        {{ {latex(v)} }}  
        \\\\ \\\\

        & \\displaystyle 
        {latex(v)}
        = \\frac
        {{ {latex(c)} }}
        {{ {latex(mu)} }} 
        \\\\ \\\\

        & \\displaystyle 
        {latex(v)}
        = \\frac
        {{ {latex(c_val)} }}
        {{ {latex(refractive_index)} }} 
        \\\\ \\\\

        & \\displaystyle 
        {latex(v)}
        = {latex(v_val)}

        \\end{{aligned}}
        $
        """
        soln_s = soln_s.replace("\n", " ").strip()
        
        answer_s = f"""
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

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

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

    def _get_night_vision(self, **kwargs):

        wl_helper = self.get_web_link_helper()
        
        search_str = 'Night Vision'
        web_link_result = wl_helper.search(search_str)
        web_link_result = wl_helper.convert_links_to_strs(web_link_result)

        prob_s = f"""
        How Do Night Vision Goggles Work?
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        Watch {web_link_result['anchors_str']}
        """
        soln_s = soln_s.replace("\n", " ").strip()
        
        answer_s = f"""
        Watch {web_link_result['anchors_str']}
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def _types_of_night_vision(self, **kwargs):
        """

        
        """
        wl_helper = self.get_web_link_helper()
        
        search_str = 'Night Vision'
        web_link_result = wl_helper.search(search_str)
        web_link_result = wl_helper.convert_links_to_strs(web_link_result)

        prob_s = f"""
        What are main types of technologies used for Vision Goggles?
        Which one has least time lag?
        Which one is the best in no light and far vision?
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        Watch {web_link_result['anchors_str']}
        """
        soln_s = soln_s.replace("\n", " ").strip()
        
        answer_s = f"""
        The types of technologies used for Vision Goggles:
        
        1. Active illumination
        2. Image intensification
        3. Thermal imaging
        
        Image intensification has the least time lag.
        Thermal imaging is the best in no light and far vision?
        """
        answer_s = answer_s.strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def _get_monochromatic_light(self, **kwargs):
        """
        
        doc_style: xv_doc
        """
        wl_helper = self.get_web_link_helper()
        
        search_str = ['Perceiving Color', 'monochromatic']
        web_link_result = wl_helper.search(search_str)
        web_link_result = wl_helper.convert_links_to_strs(web_link_result)

        prob_s = f"""
        <ol>
        <li>
        What is monochromatic light?
        </li>
        <li>
        How is monochromatic light created?
        </li>
        <li>
        What does monochromatic light look like?
        </li>
        </ol>
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        <ol>
        <li>
        What is monochromatic light?
        <br/>
        In theory, monochromatic light consists of a single wavelength. However, in practice, light with a narrow range of wavelengths is referred to as monochromatic.
        </li>
        <li>
        How is monochromatic light created?
        <br/>
        Monochromatic light can be extracted from polychromatic light using a monochromator.
        </li>
        <li>
        What does monochromatic light look like?
        <br/>
        Monochromatic light within the visible wavelength range is seen as a spectral color.
        </li>
        </ol>

        Also refer to:
        {web_link_result['anchors_str']}
        """
        soln_s = soln_s.replace("\n", " ").strip()
        
        answer_s = f"""
        <ol>
        <li>
        In theory, monochromatic light consists of a single wavelength. However, in practice, light with a narrow range of wavelengths is referred to as monochromatic.
        </li>
        <li>
        Monochromatic light can be extracted from polychromatic light using a monochromator.
        </li>
        <li>
        Monochromatic light within the visible wavelength range is seen as a spectral color.
        </li>
        </ol>
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def _get_color_seen(self, **kwargs):
        """
        
        doc_style: xv_doc
        """
        red = 'red'
        green = 'green'
        blue = 'blue'
        yellow = 'yellow'
        magenta = 'magenta'
        white = 'white'
        cyan = 'cyan'
        
        light_sources = {
            red: set([red]),
            green: set([green]),
            blue: set([blue]),
            cyan: set([blue, green]),
            yellow: set([red, green]),
            magenta: set([red, blue]),
            white: set([red, green, blue]),
            }
        
        object_colors = {
            red: set([red]),
            green: set([green]),
            blue: set([blue]),
            cyan: set([blue, green]),
            yellow: set([red, green]),
            magenta: set([red, blue]),
            white: set([red, green, blue]),
            }
        
            
        
        light_key = random.choice(list(light_sources.keys()))
        selected_light_source = light_sources[light_key]
        
        object_key = random.choice(list(object_colors.keys()))
        selected_object_color = object_colors[object_key]
        apparant_colors = set(selected_light_source).intersection(set(selected_object_color))
        
        selected_light_source_str = list_to_str(selected_light_source, last_sep = " and ")
        selected_object_color_str = list_to_str(selected_object_color, last_sep = " and ")
        apparant_colors_str = list_to_str(apparant_colors, last_sep = " and ")


        
        apparant_color_decoded = apparant_colors
        apparant_color_note = ""
        if apparant_color_decoded:
            for k, v in object_colors.items(): 
                if apparant_colors == v:
                    apparant_color_decoded = k
                    pass
        else:
            apparant_color_decoded = 'black'
            apparant_color_note = "as black means no color"
        

        prob_s = f"""
        If a naturally {object_key} object is seen in {light_key} light, 
        what color will the object appear to be?
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_stmts = []
        soln_stmt = f"""
        Let us assume the colors mentioned are either monochromatic or combination of them.
        """
        soln_stmts.append(soln_stmt.replace("\n", " ").strip())

        if len(selected_light_source) > 1:
            soln_stmt = f"""
            The {light_key} light is made of a combination of
            monochromatic {selected_light_source_str} colors.
            So, the object will receive {selected_light_source_str} colors.
            """
            soln_stmts.append(soln_stmt.replace("\n", " ").strip())
        else:
            soln_stmt = f"""
            So, the object will receive {selected_light_source_str} color
            from the light source.
            """
            soln_stmts.append(soln_stmt.replace("\n", " ").strip())
            pass
            

        if len(selected_object_color) > 1:
            soln_stmt = f"""
            The object color {object_key} is made of a combination of
            monochromatic {selected_object_color_str} colors.
            Therefore, the {object_key} object absorbs all other colors
            except monochromatic {selected_object_color_str}.
            """
            soln_stmts.append(soln_stmt.replace("\n", " ").strip())
        else:
            soln_stmt = f"""
            The object color {object_key} absorbs all other colors
            except monochromatic {selected_object_color_str}.
            """
            soln_stmts.append(soln_stmt.replace("\n", " ").strip())
            
        if apparant_colors:
            soln_stmt = f"""
            The object will reflect {apparant_colors_str} light, 
            and the viewer's eyes will receive {apparant_colors_str} color lights.
            """
            soln_stmts.append(soln_stmt.replace("\n", " ").strip())
            if len(apparant_colors) > 1:
                soln_stmt = f"""
                As the combination of {apparant_colors_str} colors
                gives {apparant_color_decoded} color, 
                the object will appear {apparant_color_decoded}.
                """
                soln_stmts.append(soln_stmt.replace("\n", " ").strip())
        else:
            soln_stmt = f"""
            The object will absorb monochromatic {selected_light_source_str} light, 
            and the viewer's eyes will receive no light
            and hence, will appear black.
            """
            soln_stmts.append(soln_stmt.replace("\n", " ").strip())
            
            
        soln_stmt = f"""
        The result can be summarized as follows:
        <br/>
        <br/>
        
        $\\displaystyle 
        \\begin{{aligned}}
        
        \\text{{Apparant color }}
        & = \\text{{ light source }} \\cap \\text{{ object color }} 
        \\\\ \\\\
        
        & = \\text{{ {light_key} }} \\cap \\text{{ {object_key} }} 
        \\\\ \\\\
        
        & = {latex(selected_light_source)}
        \\cap {latex(selected_object_color)}
        \\\\ \\\\
        
        & = {latex(apparant_colors)}
        \\\\ \\\\
        
        & = \\text{{ {apparant_color_decoded} }}
        \\qquad \\text{{ {apparant_color_note} }}
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.replace("\n", " ").strip())

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

    def get_problem_mirror_radius_focal_length(self, **kwargs):
        """
        
        doc_style: xv_doc
        """
        
        soln_stmts = []

        prob_s = f"""
        Prove the relationship between the radius of curvature $\\displaystyle  R 
        $ and the focal length $\\displaystyle  f 
        $ for a spherical mirror.
        
        """
        prob_s = prob_s.strip()


        soln_stmt = f"""
        In the diagram, 
        $\\displaystyle  C $ is the center of curvature
        $\\displaystyle  F $ is the focal point
        $\\displaystyle  P $ is the pole of the mirror
        $\\displaystyle  Q $ is the point of incidence
        $\\displaystyle  R = CP $ is the radius of curvature
        
        The focal length is defined as $\\displaystyle  f = FP $.

        see image::\\images\\xv\\physics\\optics_mirror_radius_focal_length_0001.png  
        """
        soln_stmts.append(soln_stmt.strip())
        


        soln_stmt = f"""
        According to the law of reflection at $\\displaystyle  Q 
        $, the angle of incidence $\\displaystyle  i 
        $ equals the angle of reflection $\\displaystyle  r 
        $. 

        $\\displaystyle
        i = r
        \\qquad \\cdots \\text{{ (i) }}
        $
        
        The above relation implies that the angles $C$ and $Q
        $ in the triangle $\\displaystyle  \\triangle CFQ 
        $ are equal. Thus, it is an isosceles triangle and therefore,
        
        $\\displaystyle
        FQ = CF = R - f
        \\qquad \\cdots \\text{{ (ii) }}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        


        soln_stmt = f"""
        As $\\displaystyle AQ \\; || \\; CP $,
         
        $\\displaystyle 
        \\angle QFP = i + r = i + i = 2 i
        $
        """
        soln_stmts.append(soln_stmt.strip())
        


        soln_stmt = f"""
        For large aperture of mirror, $i$ will be very small. Hence,
        
        $\\displaystyle 
        \\begin{{aligned}}
       
        i \\rightarrow 0 
        & \\implies
        2 i \\rightarrow 0
        \\\\ \\\\
        
        & \\implies
        P \\rightarrow Q  
        \\qquad \\text{{ see }} \\triangle QFP 
        \\\\ \\\\
        
        & \\implies
        FP \\rightarrow FQ
        \\qquad \\text{{ see }} \\triangle QFP 
        \\\\ \\\\
        
        & \\implies
        f \\rightarrow R -f 
        \\\\ \\\\
        
        & \\implies
        f \\approx R -f 
        \\text{{ for large aperture of mirror }} 
        \\\\ \\\\
        
        & \\implies
        f = \\frac{{R}}{{2}}
        
        \\end{{aligned}}
        $
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_s = "\n\n".join(soln_stmts)
        
        answer_s = f"""
        The relationship between the radius of curvature $\\displaystyle  R 
        $ and the focal length $\\displaystyle  f $ is:  
        $\\displaystyle 
        f = \\frac{{R}}{{2}}
        $  
        """
        answer_s = answer_s.strip()
        
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result
    
    def get_problem_lens_radius_focal_length(self, **kwargs):
        """
        
        doc_style: xv_doc
        """
        
        soln_stmts = []

        prob_s = f"""
        Find the relationship between the radii of curvature of a lens and its focal length.
        
        """
        prob_s = prob_s.strip()


        soln_stmt = f"""
        see image::\\images\\xv\\physics\\optics_lens_radius_focal_length_0001.png  
        """
        soln_stmts.append(soln_stmt.strip())
        


        soln_stmt = f"""
        From the figure:
        
        $\\displaystyle 
        \\begin{{aligned}}
       
        i    
        &
        = \\alpha + \\gamma
        \\\\ \\\\
        
        & 
        = tan \\alpha + tan \\gamma
        \\qquad \\text{{ if }} \\alpha \\text{{ and }} \\gamma \\text{{ are very small }}
        \\\\ \\\\
        
        & 
        = \\frac{{ NP }}{{ - u }} + \\frac{{ NP }}{{ R }}
        \\qquad \\cdots \\text{{ (i) }} 
        
        \\end{{aligned}}
        $
        
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        Similarly:
        
        $\\displaystyle 
        \\begin{{aligned}}
       
        r    
        &
        = \\gamma - \\beta
        \\\\ \\\\
        
        & 
        = tan \\gamma - tan \\beta
        \\qquad \\text{{ if }} \\gamma \\text{{ and }} \\beta \\text{{ are very small }}
        \\\\ \\\\
        
        & 
        = \\frac{{ NP }}{{ R }} - \\frac{{ NP }}{{ v }}
        \\qquad \\cdots \\text{{ (ii) }} 
        
        \\end{{aligned}}
        $
        
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        From Snell's law:
        

        $\\displaystyle 
        \\begin{{aligned}}
       
        &
        n_1 \sin i = n_2 \sin r
        \\\\ \\\\
        
        & \\implies
        n_1 \\; i = n_2 \\; r
        \\qquad \\text{{ if }} i \\text{{ and }} r \\text{{ are very small }}
        \\\\ \\\\
        
        & \\implies
        n_1 \\left( \\frac{{ NP }}{{ - u }} + \\frac{{ NP }}{{ R }} \\right) 
        = 
        n_2 \\left( \\frac{{ NP }}{{ R }} - \\frac{{ NP }}{{ v }} \\right)
        \\qquad \\text{{ from (i) and (ii) }}
        \\\\ \\\\
        
        & \\implies
        n_1 \\left( \\frac{{ 1 }}{{ - u }} + \\frac{{ 1 }}{{ R }} \\right) 
        = 
        n_2 \\left( \\frac{{ 1 }}{{ R }} - \\frac{{ 1 }}{{ v }} \\right)
        \\qquad \\text{{ divided both sides by }} NP
        \\\\ \\\\
        
        & \\implies
        \\frac{{ n_2 }}{{ R }} - \\frac{{ n_1 }}{{ R }}
        =
        \\frac{{ n_2 }}{{ v }} - \\frac{{ n_1 }}{{ u }} 
        
        \\\\ \\\\
        
        & \\implies
        \\frac{{ n_2 - n_1 }}{{ R }} 
        =
        \\frac{{ n_2 }}{{ v }} - \\frac{{ n_1 }}{{ u }}  
        \\qquad \\cdots \\text{{ (iii) }} 
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        Let us change the names of variables for the first refractive surface of the lens. 
        
        $\\displaystyle 
        R = R_1 \\qquad \\text{{ and }}
        v = v_1
        $
        
        Therefore, the equation (iii) becomes
        
        $\\displaystyle 
        \\frac{{ n_2 - n_1 }}{{ R_1 }} 
        =
        \\frac{{ n_2 }}{{ v_1 }} - \\frac{{ n_1 }}{{ u }}  
        \\qquad \\cdots \\text{{ (iv) }} 
        $
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        Similarly, for the second refractive surface of the lens, 
        we can calculate the relation similar to the equation (iv) above.
        <br/>
        <br/>
        In this case, the ray goes from medium 2 $(n_2)$ to medium 1 $(n_1)$. 
        <br/>
        <br/>
        The image formed the first refractive surface becomes object for 
        the second refractive surface. Therefore, $u_2 = v_1$.
        <br/>
        <br/>
        The position of the images formed by the second refractive surface 
        is also the final position of the image formed by lens. Therefore, 
        $v_2 = v$.
        """
        soln_stmts.append(soln_stmt.replace("\n", " ").strip())
        

        soln_stmt = f"""
        Therefore, for the second refractive surface of the lens
        
        $\\displaystyle 
        \\frac{{ n_1 - n_2 }}{{ R_2 }} 
        =
        \\frac{{ n_1 }}{{ v }} - \\frac{{ n_2 }}{{ v_1 }}  
        \\qquad \\cdots \\text{{ (v) }} 
        $
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        Combining (iv) and (v):
        
        $\\displaystyle 
        \\begin{{aligned}}
       
        &
        \\frac{{ n_2 - n_1 }}{{ R_1 }} 
        +
        \\frac{{ n_1 - n_2 }}{{ R_2 }} 
        =
        \\frac{{ n_2 }}{{ v_1 }} - \\frac{{ n_1 }}{{ u }}  
        +
        \\frac{{ n_1 }}{{ v }} - \\frac{{ n_2 }}{{ v_1 }}  
        \\\\ \\\\
        
        & \\implies
        \\left( n_2 - n_1 \\right)
        
        \\left(
        \\frac{{ 1 }}{{ R_1 }} 
        -
        \\frac{{ 1 }}{{ R_2 }} 
        \\right)
        =
        \\frac{{ n_1 }}{{ v }} - \\frac{{ n_1 }}{{ u }}  
        \\\\ \\\\
        
        & \\implies
        \\left( n_2 - n_1 \\right)
        
        \\left(
        \\frac{{ 1 }}{{ R_1 }} 
        -
        \\frac{{ 1 }}{{ R_2 }} 
        \\right)
        = n_1 
        \\left(
        \\frac{{ 1 }}{{ v }} - \\frac{{ 1 }}{{ u }}  
        \\right)
        \\\\ \\\\
        
        & \\implies
        \\left( n_2 - n_1 \\right)
        
        \\left(
        \\frac{{ 1 }}{{ R_1 }} 
        -
        \\frac{{ 1 }}{{ R_2 }} 
        \\right)
        = n_1 \\; \\frac{{ 1 }}{{ f }}
        \\qquad \\text{{ as }} 
        \\frac{{ 1 }}{{ v }} - \\frac{{ 1 }}{{ u }} = \\frac{{ 1 }}{{ f }} 
        \\\\ \\\\
        
        & \\implies
        n_1 \\; 
        \\frac{{ 1 }}{{ f }} 
        =
        \\left( n_2 - n_1 \\right)
        
        \\left(
        \\frac{{ 1 }}{{ R_1 }} 
        -
        \\frac{{ 1 }}{{ R_2 }} 
        \\right)
        \\qquad \\text{{ flipped sides }} 
        \\\\ \\\\
        
        & \\implies
        \\frac{{ 1 }}{{ f }} 
        =
        \\left( \\frac{{ n_2 }}{{ n_1 }}  - 1 \\right)
        
        \\left(
        \\frac{{ 1 }}{{ R_1 }} 
        -
        \\frac{{ 1 }}{{ R_2 }} 
        \\right)
        
        \\qquad \\text{{ divided both sides by }} n_1
        \\\\ \\\\
        
        & \\implies
        \\frac{{ 1 }}{{ f }} 
        =
        \\left( {{}}^1{{\\mu}}_{{2}}  - 1 \\right)
        
        \\left(
        \\frac{{ 1 }}{{ R_1 }} 
        -
        \\frac{{ 1 }}{{ R_2 }} 
        \\right)
        \\qquad \\cdots \\text{{ (vi) }} 
        
        \\\\ \\\\
        
        & \\text{{ where }} {{}}^1{{\\mu}}_{{2}} 
        \\text{{ is relative refractive index of medium 2 with respect to medium 1 }}
        
        \\end{{aligned}}
        $
        

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

        
        soln_s = "\n\n".join(soln_stmts)
        
        answer_s = f"""
        $\\displaystyle 
        \\frac{{ 1 }}{{ f }} 
        =
        \\left( {{}}^1{{\\mu}}_{{2}}  - 1 \\right)
        
        \\left(
        \\frac{{ 1 }}{{ R_1 }} 
        -
        \\frac{{ 1 }}{{ R_2 }} 
        \\right)
        $
        """
        answer_s = answer_s.strip()
        
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result


    def get_problem_magnification_by_mirror(self, **kwargs):
        """
        
        doc_style: xv_doc
        """
        
        soln_stmts = []

        prob_s = f"""
        Find expression for magnification of image by a mirror.
        
        """
        prob_s = prob_s.strip()


        soln_stmt = f"""
        The object is at $\\displaystyle  A 
        $, the image is at $\\displaystyle  A' 
        $, the mirror has its center of curvature at $\\displaystyle  C 
        $, and the principal focus is $\\displaystyle  F $.  
        
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        Let us note:
           &emsp; $\\bullet $ The ray $\\displaystyle  AB 
           $ parallel to the principal axis reflects through the focus $\\displaystyle  F $.  
           &emsp; $\\bullet $ The ray $\\displaystyle  AC 
           $ passing through the center of curvature reflects back along the same path.
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        see image::\\images\\xv\\physics\\optics_magnification_by_mirror_0001.png  

        height of the object is $\\displaystyle  h $
        height of the image is $\\displaystyle  h' $
        object distance = $\\displaystyle  u $  
        image distance = $\\displaystyle  v $  
        focal length = $\\displaystyle  f $  
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        Following the convention, the positions from the pole $\\displaystyle  P 
        $ on the image side are negative and the other side, positive.
        As all positions are left of $\\displaystyle  P 
        $, the signs are ignored.
        """
        soln_stmts.append(soln_stmt.strip())
        


        soln_stmt = f"""
        From the similar triangles $\\displaystyle  \\triangle ABP 
        $ and $\\displaystyle  \\triangle A'B'P $, we get:
        
        $\\displaystyle 
        \\begin{{aligned}}

        &
        \\frac{{h'}}{{h}} = \\frac{{v}}{{u}}
        \\\\ \\\\
        
        & \\implies
        \\frac
        {{ \\text{{Size of the image }} }}
        {{ \\text{{Size of the object }} }}
        = \\frac{{v}}{{u}}
        \\\\ \\\\
        
        & \\implies
        m = \\frac{{v}}{{u}}
        
        \\end{{aligned}}
        $
        
        $m$ is called magnification coefficient of the mirror.
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_s = "\n\n".join(soln_stmts)
        
        answer_s = f"""
        $\\displaystyle  
        m = \\frac{{v}}{{u}}
        $
        """
        answer_s = answer_s.strip()
        
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result


    def get_problem_magnification_by_a_series_of_mirrors(self, **kwargs):
        """
        
        doc_style: xv_doc
        """
        
        soln_stmts = []

        prob_s = f"""
        Find the expression for the magnification of an image produced by a series of mirrors.
        
        """
        prob_s = prob_s.strip()


        soln_stmt = f"""
        The magnification $\\displaystyle  m $ for a single mirror is given by:
        
        $\\displaystyle 
        m = -\\frac{{v}}{{u}}
        $
        
        where:
        &emsp; $\\displaystyle  v $: Image distance,  
        &emsp; $\\displaystyle  u $: Object distance.  

        The negative sign accounts for the inversion of the image.
        
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        Consider a system of $\\displaystyle  n $ mirrors in sequence. 
        The object for the first mirror produces an image 
        that acts as the object for the second mirror, and so on.
        
        """
        soln_stmts.append(soln_stmt.replace("\n", " ").strip())
        

        soln_stmt = f"""
        Let:
        &emsp; $\\displaystyle  m_1, m_2, \\dots, m_n 
        $ be the magnifications produced by the individual mirrors.  
        
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        <b>For the $\\displaystyle  i^{{\\text{{th}}}} $ mirror:</b>
        
        $\\displaystyle 
        m_i = -\\frac{{v_i}}{{u_i}}
        $
        
        where $\\displaystyle  v_i $ and $\\displaystyle  u_i 
        $ are the image and object distances for the $\\displaystyle  
        i^{{\\text{{th}}}} $ mirror.
        
        
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        <b>For two mirrors:</b>
        
        Magnification of the first mirror:
        
          $\\displaystyle 
          m_1 = -\\frac{{v_1}}{{u_1}}
          $
        
        Magnification of the second mirror:
          
          $\\displaystyle 
          m_2 = -\\frac{{v_2}}{{u_2}}
          $
        
        Total magnification:

        $\\displaystyle 
        M 
        = m_1 \\cdot m_2 
        = \\left(-\\frac{{v_1}}{{u_1}}\\right) \\cdot \\left(-\\frac{{v_2}}{{u_2}}\\right)
        $
        
        The signs and distances depend on the arrangement of the system.
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        <b>Generalization for $n$ mirrors:</b>

        The total magnification $\\displaystyle  M 
        $ is the product of all the individual magnifications:
        
        $\\displaystyle 
        M = m_1 \\cdot m_2 \\cdot \\dots \\cdot m_n
        $
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        $\\displaystyle 
        M = \\prod_{{i=1}}^n m_i = \\prod_{{i=1}}^n \\left(-\\frac{{v_i}}{{u_i}}\\right)
        $
        
        This gives the general expression for the magnification of an image produced by a series of mirrors.
        """
        soln_stmts.append(soln_stmt.strip())
        

        
        soln_s = "\n\n".join(soln_stmts)
        
        answer_s = f"""
        $\\displaystyle 
        M 
        = m_1 \\cdot m_2 \\cdot \\dots \\cdot m_n 
        = \\prod_{{i=1}}^n m_i 
        = \\prod_{{i=1}}^n \\left(-\\frac{{v_i}}{{u_i}}\\right)
        $
        """
        answer_s = answer_s.strip()
        
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result


    def get_problem_magnification_by_lens(self, **kwargs):
        """
        
        doc_style: xv_doc
        """
        
        soln_stmts = []

        prob_s = f"""
        Find expression for magnification of image by a lens.
        
        """
        prob_s = prob_s.strip()


        soln_stmt = f"""
        Using the given diagram, the object is at $\\displaystyle  A 
        $, the image is at $\\displaystyle  A' 
        $, and the lens is centered at $\\displaystyle  P 
        $. The focal points $\\displaystyle  F $ and $\\displaystyle  F' 
        $ represent the focal points of the lens.
        see image::\\images\\xv\\physics\\optics_lens_formula_0001.png
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
        The ray passing through the optical center $\\displaystyle  P 
        $ does not bend. Hence, the triangles $\\displaystyle  \\triangle ABP 
        $ and $\\displaystyle  \\triangle A'B'P $ are similar. 
        
        Following the convention, the positions from $\\displaystyle  P 
        $ on the image side are negative and the other side, positive.
        
        """
        soln_stmts.append(soln_stmt.strip())
        


        soln_stmt = f"""
        This gives the relation:

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

        &
        \\frac{{h'}}{{h}} = \\frac{{v}}{{-u}}
        \\\\ \\\\
        
        & \\implies
        \\frac
        {{ \\text{{Size of the image }} }}
        {{ \\text{{Size of the object }} }}
        = \\frac{{v}}{{-u}}
        \\\\ \\\\
        
        & \\implies
        m = - \\frac{{v}}{{u}}
        
        \\end{{aligned}}
        $
        
        $m$ is called magnification coefficient of the lens.
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_s = "\n\n".join(soln_stmts)
        
        answer_s = f"""
        $\\displaystyle  
        m = - \\frac{{v}}{{u}}
        $
        """
        answer_s = answer_s.strip()
        
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result


    def get_problem_magnification_by_a_series_of_lenses(self, **kwargs):
        """
        
        doc_style: xv_doc
        """
        
        soln_stmts = []

        prob_s = f"""
        Find the expression for the magnification of an image produced by a series of lenses.
        
        """
        prob_s = prob_s.strip()

        soln_stmt = f"""
        The magnification $\\displaystyle  m $ for a single lens is given by:
        
        $\\displaystyle 
        m = \\frac{{v}}{{u}}
        $
        
        where:  
        $\\displaystyle  v $: Image distance,  
        $\\displaystyle  u $: Object distance.
        
        The positive sign indicates that the nature of the image (erect or inverted) depends on the lens configuration.
        
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        Consider a system of $\\displaystyle  n $ lenses in sequence.  
        The object for the first lens produces an image that acts as the object for the second lens, and so on.
        
        Let:  
        $\\displaystyle  m_1, m_2, \\ldots, m_n $ be the magnifications produced by the individual lenses.
        
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        <b>For the $\\displaystyle  i^{{\\text {{th }}}}$ lens:</b>
        
        $\\displaystyle 
        m_i = \\frac{{v_i}}{{u_i}}
        $
        
        where $\\displaystyle  v_i $ and $\\displaystyle  u_i $ are the image and object distances for the $\\displaystyle  i^{{\\text {{th }}}}$ lens.
        
        """
        soln_stmts.append(soln_stmt.strip())
        


        soln_stmt = f"""
        <b>For two lenses:  </b>
        
        Magnification of the first lens:
        $\\displaystyle 
        m_1 = \\frac{{v_1}}{{u_1}}
        $
        
        Magnification of the second lens:
        $\\displaystyle 
        m_2 = \\frac{{v_2}}{{u_2}}
        $
        
        Total magnification:  
        $\\displaystyle 
        M = m_1 \\cdot m_2 = \\left(\\frac{{v_1}}{{u_1}}\\right) \\cdot \\left(\\frac{{v_2}}{{u_2}}\\right)
        $
        
        The nature of the image depends on the lens configuration, whether converging or diverging.
        
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        <b>Generalization for $\\displaystyle  n $ lenses:  </b>
        
        The total magnification $\\displaystyle  M 
        $ is the product of all the individual magnifications:
        
        $\\displaystyle 
        \\begin{{aligned}}
        
        M 
        & = m_1 \\cdot m_2 \\cdot \\cdots \\cdot m_n 
        \\\\ \\\\
        & = \\prod_{{i=1}}^n m_i = \\prod_{{i=1}}^n \\frac{{v_i}}{{u_i}}
        
        \\end{{aligned}}
        $
        
        This gives the general expression for the magnification of an image produced by a series of lenses.
        """
        soln_stmts.append(soln_stmt.strip())
        

        
        soln_s = "\n\n".join(soln_stmts)
        
        answer_s = f"""
        $\\displaystyle 
        M 
        = m_1 \\cdot m_2 \\cdot \\dots \\cdot m_n 
        = \\prod_{{i=1}}^n m_i 
        = \\prod_{{i=1}}^n \\left(-\\frac{{v_i}}{{u_i}}\\right)
        $
        """
        answer_s = answer_s.strip()
        
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result



    def get_problem_relative_refractive_index(self, **kwargs):
        """
        
        doc_style: xv_doc
        """
        
        soln_stmts = []

        prob_s = f"""
        Find expression for relative refractive index of a medium with respect to another.
        
        """
        prob_s = prob_s.strip()


        soln_stmt = f"""
        The refractive index of medium 1 with respect to a vacuum:
        
        $\\displaystyle 
        {{}}^0 \\mu_1 = \\frac{{c}}{{v_1}}
        \\qquad \\cdots \\text{{ (i) }}
        $
        
        The refractive index of medium 2 with respect to a vacuum:
        
        $\\displaystyle 
        {{}}^0 \\mu_2 = \\frac{{c}}{{v_2}}
        \\qquad \\cdots \\text{{ (ii) }}
        $
        
        where:
        &emsp; $\\displaystyle  c $ is the speed of light in a vacuum,  
        &emsp; $\\displaystyle  v_1 $ and $\\displaystyle  v_2 $ are the speeds of light in medium 1 and medium 2, respectively.
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
        The refractive index of medium 2 with respect to medium 1:
        
        $\\displaystyle 
        \\begin{{aligned}}
       
        {{}}^1 \\mu_2 
        & = \\frac{{\\text{{Speed of light in medium 1}}}}{{\\text{{Speed of light in medium 2}}}} 
        \\\\ \\\\
        
        & =
        \\frac{{v_1}}{{v_2}}
        \\\\ \\\\
        
        & =
        \\displaystyle \\frac{{\\frac{{c}}{{{{}}^0 \\mu_1}}}}{{\\frac{{c}}{{{{}}^0 \\mu_2}}}}
        \\end{{aligned}}
        $
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
        
        $\\displaystyle 
        \\implies {{}}^1 \\mu_2 = \\frac{{{{}}^0 \\mu_2}}{{{{}}^0 \\mu_1}}
        $
        
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        <h3>Geometric interpretation:</h3>
        Let there are two optical media with refractive indices $\\mu_1$ and $\\mu_2
        $ as shown in the figure below.
        
        see image::\\images\\xv\\physics\\optics_relative_refractive_index_0001.png  
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        Using Snell's law:
        
        $\\displaystyle 
        \\begin{{aligned}}
        
        & 
        {{ }}^0 \\mu_1
        = \\frac{{\\sin \\theta_1}}{{\\sin \\theta_0}} 
        \\qquad \\text{{ at point }} A
        \\\\ \\\\
        
        & 
        {{ }}^1 \\mu_2
        = \\frac{{\\sin \\theta_2}}{{\\sin \\theta_1}} 
        \\qquad \\text{{ at point }} B
        \\\\ \\\\
        
        & 
        {{ }}^0 \\mu_2
        = \\frac{{\\sin \\theta_2}}{{\\sin \\theta_0}} 
        \\qquad \\text{{ at point }} C
        \\\\ \\\\
        
        & \\implies 
        {{ }}^0 \\mu_2
        = \\frac{{\\sin \\theta_2}}{{\\sin \\theta_1}} 
        \\cdot \\frac{{\\sin \\theta_1}}{{\\sin \\theta_0}} 
        \\\\ \\\\
        
        & \\implies 
        {{ }}^0 \\mu_2 = {{ }}^1 \\mu_2 \\cdot {{ }}^0 \\mu_1  
        \\\\ \\\\

        & \\implies 
        {{ }}^1 \\mu_2
        = \\frac{{ {{ }}^0 \\mu_2 }}{{  {{ }}^0 \\mu_1 }}

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

        
        soln_s = "\n\n".join(soln_stmts)
        
        answer_s = f"""
        $\\displaystyle 
        {{}}^1 \\mu_2 = \\frac{{{{}}^0 \\mu_2}}{{{{}}^0 \\mu_1}}
        $
        """
        answer_s = answer_s.strip()
        
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result


    def get_problem_mirror_formula(self, **kwargs):
        """
        
        doc_style: xv_doc
        """
        
        soln_stmts = []

        prob_s = f"""
        Prove the mirror formula:
        
        $\\displaystyle 
        \\frac{{1}}{{f}} = \\frac{{1}}{{v}} + \\frac{{1}}{{u}}
        $
        
        """
        prob_s = prob_s.strip()


        soln_stmt = f"""
        The object is at $\\displaystyle  A 
        $, the image is at $\\displaystyle  A' 
        $, the mirror has its center of curvature at $\\displaystyle  C 
        $, and the principal focus is $\\displaystyle  F $.  
        
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        Let us note:
           &emsp; $\\bullet $ The ray $\\displaystyle  AB 
           $ parallel to the principal axis reflects through the focus $\\displaystyle  F $.  
           &emsp; $\\bullet $ The ray $\\displaystyle  AC 
           $ passing through the center of curvature reflects back along the same path.
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        see image::\\images\\xv\\physics\\optics_mirror_formula_0001.png  

        height of the object is $\\displaystyle  h $
        height of the image is $\\displaystyle  h' $
        object distance = $\\displaystyle  u $  
        image distance = $\\displaystyle  v $  
        focal length = $\\displaystyle  f $  
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        Following the convention, the positions from the pole $\\displaystyle  P 
        $ on the image side are negative and the other side, positive.
        As all positions are left of $\\displaystyle  P 
        $, the signs are ignored.
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        From the similar triangles $\\displaystyle  \\triangle ABC 
        $ and $\\displaystyle  \\triangle A'B'C $, we get:

        $\\displaystyle 
        \\frac{{h}}{{h'}} = \\frac{{u - 2 f}}{{2 f - v}}
        \\qquad \\cdots \\text{{ (i) }} 
        $
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        From the similar triangles $\\displaystyle  \\triangle ABP 
        $ and $\\displaystyle  \\triangle A'B'P $, we get:
        
        $\\displaystyle 
        \\frac{{h}}{{h'}} = \\frac{{u}}{{v}}
        \\qquad \\cdots \\text{{ (ii) }} 
        $
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        From (i) and (ii):
        
        $\\displaystyle 
        \\begin{{aligned}}
       
        &
           \\frac{{u - 2 f}}{{2 f - v}} = \\frac{{u}}{{v}}
        \\\\ \\\\
        
        & \\implies
           (u - 2 f) v = u (2 f - v)
        \\\\ \\\\
        
        & \\implies
           uv - 2vf = 2uf - uv
        \\\\ \\\\
        
        & \\implies
           2uv = 2uf + 2vf
        \\\\ \\\\
        
        & \\implies
           \\frac{{1}}{{f}} = \\frac{{1}}{{v}} + \\frac{{1}}{{u}}
            \\qquad \\text{{ divided both sides by }} uvf 
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        

        soln_stmt = f"""
        This proves the mirror formula.  
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_s = "\n\n".join(soln_stmts)
        
        answer_s = f"""
        The mirror formula relates the object distance ($\\displaystyle  u 
        $), image distance ($\\displaystyle  v 
        $), and focal length ($\\displaystyle  f $) for a spherical mirror.
        """
        answer_s = answer_s.strip()
        
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result


    def get_problem_lens_formula(self, **kwargs):
        """
        
        doc_style: xv_doc
        """
        
        soln_stmts = []

        prob_s = f"""
        Prove the lens formula:
        
        $\\displaystyle 
        \\frac{{1}}{{f}} = \\frac{{1}}{{v}} - \\frac{{1}}{{u}}
        $
        
        """
        prob_s = prob_s.strip()


        soln_stmt = f"""
        Using the given diagram, the object is at $\\displaystyle  A 
        $, the image is at $\\displaystyle  A' 
        $, and the lens is centered at $\\displaystyle  P 
        $. The focal points $\\displaystyle  F $ and $\\displaystyle  F' 
        $ represent the focal points of the lens.
        see image::\\images\\xv\\physics\\optics_lens_formula_0001.png
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
        The ray passing through the optical center $\\displaystyle  P 
        $ does not bend. Hence, the triangles $\\displaystyle  \\triangle ABP 
        $ and $\\displaystyle  \\triangle A'B'P $ are similar. 
        
        Following the convention, the positions from $\\displaystyle  P 
        $ on the image side are negative and the other side, positive.
        
        This gives the relation:

        $\\displaystyle 
        \\frac{{h'}}{{h}} = \\frac{{v}}{{-u}}
        \\qquad \\cdots \\text{{ (i) }}
        $
        
        where $\\displaystyle  h $ is the height of the object and $\\displaystyle  h' 
        $ is the height of the image.
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
        The triangles $\\displaystyle  \\triangle PQF 
        $ and $\\displaystyle  \\triangle A'B'F $ are similar. This gives the relation:

        $\\displaystyle 
        \\begin{{aligned}}
       
        &
        \\frac{{h'}}{{h}} = \\frac{{v - f}}{{f}}
        \\\\ \\\\
        
        & \\implies
        \\frac{{v}}{{-u}} = \\frac{{v - f}}{{f}}
        \\qquad \\text{{ from (i) }} 
        \\\\ \\\\
        
        & \\implies
        vf = -uv + uf
        \\\\ \\\\
        
        & \\implies
        \\frac{{1}}{{u}} = - \\frac{{1}}{{f}} + \\frac{{1}}{{v}}
        \\qquad \\text{{ divide both sides by }} u v f
        \\\\ \\\\
        
        & \\implies
        \\frac{{1}}{{f}} = \\frac{{1}}{{v}} - \\frac{{1}}{{u}}
        
        \\end{{aligned}}
        $
        
        This proves the lens formula.
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_s = "\n\n".join(soln_stmts)
        
        answer_s = f"""
        The lens formula relates the object distance ($\\displaystyle  u 
        $), image distance ($\\displaystyle  v $), and focal length ($\\displaystyle  f 
        $) for a thin lens.  
        """
        answer_s = answer_s.strip()
        
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result



    def get_problem_snells_law(self, **kwargs):
        """
        
        doc_style: xv_doc
        """
        
        soln_stmts = []

        prob_s = f"""
        Prove Snell's Law for refraction.
        """
        prob_s = prob_s.strip()


        soln_stmt = f"""
        Snell's Law can be derived using Fermat's principle, which states that 
        light travels between two points along the path that requires the least time. 
        Consider two media with refractive indices 
        $\\displaystyle  n_1 $ and $\\displaystyle  n_2 $, 
        separated by a flat boundary. 
        Light travels from point $\\displaystyle  A $ in medium $\\displaystyle  1 $ 
        to point $\\displaystyle  B $ in medium $\\displaystyle  2 $, 
        crossing the boundary at point $\\displaystyle  P(x, y) $.  
        """
        soln_stmts.append(soln_stmt.replace("\n", " ").strip())
        
        soln_stmt = f"""
        The total travel time $\\displaystyle  t $ for light to go from $\\displaystyle  A $ to $\\displaystyle  B $ is:
        
        $\\displaystyle 
        t = \\frac{{\\text{{distance in medium 1}}}}{{\\text{{speed in medium 1}}}} + \\frac{{\\text{{distance in medium 2}}}}{{\\text{{speed in medium 2}}}}
        $
        
        The distances traveled in the two media are:
        
        $\\displaystyle 
        s_1 = \\sqrt{{x^2 + y_1^2}} \\quad \\text{{ in medium 1, and }}
        \\\\ ~ \\\\
         
        s_2 = \\sqrt{{(d-x)^2 + y_2^2}} \\quad \\text{{ in medium 2 }}
        $
        
        see image::/images/xv/physics/optics_snells_law_0001.png
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
        Therefore:
        
        $\\displaystyle 
        t = \\frac{{\\sqrt{{x^2 + y_1^2}}}}{{v_1}} + \\frac{{\\sqrt{{(d-x)^2 + y_2^2}}}}{{v_2}}
        $
        
        where $\\displaystyle  v_1 $ and $\\displaystyle  v_2 
        $ are the speeds of light in the respective media.
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
        From Fermat's principle, $\\displaystyle  t 
        $ must be minimized. To find the minimum, take the derivative of $\\displaystyle  t 
        $ with respect to $\\displaystyle  x $:

        $\\displaystyle 
        \\frac{{dt}}{{dx}} 
        = \\frac{{x}}{{v_1 \\sqrt{{x^2 + y_1^2}}}} 
        - \\frac{{d-x}}{{v_2 \\sqrt{{(d-x)^2 + y_2^2}}}}
        $
        
        Setting $\\displaystyle  \\frac{{dt}}{{dx}} = 0 $ for minimum $t$, we get:
        
        $\\displaystyle 
        \\implies \\frac{{x}}{{v_1 \\sqrt{{x^2 + y_1^2}}}} 
        = \\frac{{d-x}}{{v_2 \\sqrt{{(d-x)^2 + y_2^2}}}}
        $
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
        The sines of the angles of incidence ($\\displaystyle  \\theta_1 
        $) and refraction ($\\displaystyle  \\theta_2 $) are:
        
        $\\displaystyle 
        \\sin \\theta_1 
        = \\frac{{x}}{{\\sqrt{{x^2 + y_1^2}}}} 
        \\\\ ~ \\\\
        
        \\sin \\theta_2 
        = \\frac{{d-x}}{{\\sqrt{{(d-x)^2 + y_2^2}}}}
        $
        
        Substituting these values into the equation:
        
        $\\displaystyle 
        \\frac{{\\sin \\theta_1}}{{v_1}} = \\frac{{\\sin \\theta_2}}{{v_2}}
        $
        
        Multiply both sides with speed of light $c$:
        
        $\\displaystyle 
        \\frac{{c \\sin \\theta_1}}{{v_1}} = \\frac{{c \\sin \\theta_2}}{{v_2}}
        $
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
        The refractive index $\\displaystyle  n 
        $ is related to the speed of light $\\displaystyle  v $ as:
        
        $\\displaystyle 
        n = \\frac{{c}}{{v}}
        $ where $\\displaystyle  c $ is the speed of light in a vacuum. 
        
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
        Therefore, $\\displaystyle 
        n_1 \\sin \\theta_1 = n_2 \\sin \\theta_2
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_s = "\n\n".join(soln_stmts)
        
        answer_s = f"""
        $\\displaystyle 
        n_1 \\sin \\theta_1 = n_2 \\sin \\theta_2
        $
        """
        answer_s = answer_s.strip()
        
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result



    def get_problem_TEMP(self, **kwargs):
        """
        
        doc_style: xv_doc
        """
        
        soln_stmts = []

        prob_s = f"""
        """
        prob_s = prob_s.replace("\n", " ").strip()


        soln_stmt = f"""
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_stmt = f"""
        """
        soln_stmts.append(soln_stmt.strip())
        
        soln_s = "\n\n".join(soln_stmts)

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


    pass
#class OpticsHelper:

    