'''
Created on 01-Apr-2021
@author: bthakur
'''
import random

from sympy import symbols, latex, simplify

from xv.km.util import latex_fraction

        
from ..helper import BaseHelper
from xv.km.managers import BaseManager
from xv.km.kelements import KProblemTemplate

        
class GeometricSeriesManager(BaseManager):
    '''
    GeometricSeriesManager
    Example:
        ke = GeometricSeriesManager()
        ke.getRandomProblem()
        ke.getRandomProblem(problem_type = 0)
        ...
        
        ke.printProblem()
        ke.printAnswer()
        ke.printSolution()
        
        ke.printProblemTypes()

        Optional parameters with most problem types:
        first_term = None  #sympy or numeric expression
        common_ratio = None #sympy or numeric expression
        
    '''    
    _OPR_TYPE_COUNT = 10
    
    def __new__(cls, *args, **kwargs):
        instance = super(GeometricSeriesManager, cls).__new__(cls, *args, **kwargs)
        for base_class in GeometricSeriesManager.__bases__:
            base_class.__init__(instance, *args, **kwargs)
        return instance
    
    def __init__(self, *args, **kwargs):
        self.base_helper = BaseHelper(**kwargs)
        
        pass
    

    def _init_problem_types(self, *args, **kwargs):
        self._append_problem_types([
            '_problem_write_expression_for_as',
            '_problem_write_expression_for_gs_simple',
            '_problem_write_expression_for_gs',
            '_problem_term_in_a_gs',
            '_problem_write_expression_for_gs_sum_simple',
            '_problem_write_expression_for_gs_sum',
            '_problem_future_value_simple',
            '_problem_future_value',
            '_problem_present_value',
            '_problem_future_annuity_value_simple',
            '_problem_future_annuity_value',
            '_problem_present_annuity_value_simple',
            '_problem_present_annuity_value',
            
            ])
        pass

    def _problem_write_expression_for_as(self, *args, **kwargs):
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        t0 = random.choice([0, symbols('a')])
        t = symbols('t')
        d = symbols('d')
        n = symbols('n')
        
        t0 = random.choice([t0, random.randint(1, 9)])
        t = random.choice([t, random.randint(1, 9)])
        d = random.choice([d, random.randint(1, 9)])
        n = random.choice([n, random.randint(1, 9)])
        
        terms = [t0]
        t_latexes = [f"${t}_{0} = {latex(t0)}$"]
        
        for i in [1, 2, 3, n]:
            prevTerm = terms[-1]
            if i == n:
                prevTerm = t0 + d * (n-1)
            t_latex = f"""
            ${t}_{{{i}}} = {latex(prevTerm)} + {d} = {latex(t0 + d * i)} $
            """
            terms.append(simplify(prevTerm + d))
            t_latexes.append(t_latex.strip())
            pass
        
        t_latexes.insert(-1, r"$\cdots$")
        if t0 == 0:
            terms_str = [f"${latex(term)}$" for term in terms ]
        else:
            terms_str = [f"$({latex(term)})$" for term in terms ]
            
        terms_str.insert(-1, r"$\cdots$")
        
        t_latexes_str = "\n".join(t_latexes)
        series_str = " + ".join(terms_str)
        
        problem_s = f"""
        Write arithmetic series of ${n + 1}$ terms, 
        with first term as ${t0}$ and the common difference as ${d}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)
        
        soln_s = f"""
        Add {d} to the previous terms with to get next term
        {t_latexes_str}
        
        So, the series is
        {series_str}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {series_str}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
    
    def _problem_write_expression_for_gs_simple(self, *args, **kwargs):
        gs_helper = self.base_helper.get_algebra_helper().get_geometric_series_helper()

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        gs = gs_helper.get_standard_series(a = random.choice([1, symbols('a')]))
        terms = gs.get_terms(to_latex =  True)
        series = gs.get_series(terms, to_str = False)
        series_str = gs.get_series(terms, to_str = True)
        summtion_latex = gs.get_summtion_latex()
        
        problem_s = f"""
        Write geometric series of ${latex(gs.n)}$ terms, 
        with first term as ${latex(gs.a)}$ and the common ratio as ${latex(gs.r)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)
        
        r_latex = gs.get_var_latex(gs.r)
        n_latex = gs.get_var_latex(gs.n)
        
        soln_s = f"""
        {gs_helper.get_term_formula_statement()}
        
        ${{t}}_{{0}} = {latex(gs.a)} = {gs.get_term(0, to_latex = True)} $ 
        
        ${{t}}_{{1}} 
        = {{t}}_{{0}} * {r_latex}
        = {gs.get_term(0, to_latex = True)} * {r_latex}
        = {gs.get_term(1, to_latex = True)} $ 
        
        ${{t}}_{{2}} 
        = {{t}}_{{1}} * {r_latex}
        = {gs.get_term(1, to_latex = True)} * {r_latex}
        = {gs.get_term(2, to_latex = True)} $ 
        
        $\\cdots$
        
        ${{t}}_{{{latex(gs.n - 1)}}} 
        = {{t}}_{{{latex(gs.n - 2)}}} * {r_latex}
        = {gs.get_term(gs.n - 2, to_latex = True)} * {r_latex}
        = {gs.get_term(gs.n - 1, to_latex = True)} $ 
        
        ${{t}}_{{{latex(gs.n)}}} 
        = {{t}}_{{{latex(gs.n - 1)}}} * {r_latex}
        = {gs.get_term(gs.n - 1, to_latex = True)} * {r_latex}
        = {gs.get_term(gs.n, to_latex = True)} $ 
        
        Therefore, the series is
        ${series_str}$
        
        It can also be written as ${summtion_latex}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        ${series_str}$

        It can also be written as ${summtion_latex}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
    
    def _problem_write_expression_for_gs(self, *args, **kwargs):
        '''
        Optional Parameters:
        a = None  #first term; sympy or numeric expression
        r = None #common ration; sympy or numeric expression
        n = None #number of terms; sympy or numeric expression
        '''
        
        gs_helper = self.base_helper.get_algebra_helper().get_geometric_series_helper()

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        a = kwargs.get('a', None)
        r = kwargs.get('r', None)
        n = kwargs.get('n', None)

        gs = gs_helper.get_random_series(a = a, r = r, n = n)
        terms = gs.get_terms(to_latex =  True)
        series = gs.get_series(terms, to_str = False)
        series_str = gs.get_series(terms, to_str = True)
        summtion_latex = gs.get_summtion_latex()

        r_latex = gs.get_var_latex(gs.r)
        n_latex = gs.get_var_latex(gs.n)
        
        problem_s = f"""
        Write geometric series of ${latex(gs.n)}$ terms, 
        with first term as ${latex(gs.a)}$ and the common ratio as ${latex(gs.r)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)
        
        soln_s = f"""
        {gs_helper.get_term_formula_statement()}
        
        ${{t}}_{{0}} = {latex(gs.a)} = {gs.get_term(0, to_latex = True)} $ 
        
        ${{t}}_{{1}} 
        = {{t}}_{{0}} * {r_latex}
        = {gs.get_term(0, to_latex = True)} * {r_latex}
        = {gs.get_term(1, to_latex = True)} $ 
        
        ${{t}}_{{2}} 
        = {{t}}_{{1}} * {r_latex}
        = {gs.get_term(1, to_latex = True)} * {r_latex}
        = {gs.get_term(2, to_latex = True)} $ 
        
        $\\cdots$
        
        ${{t}}_{{{latex(gs.n - 1)}}} 
        = {{t}}_{{{latex(gs.n - 2)}}} * {r_latex}
        = {gs.get_term(gs.n - 2, to_latex = True)} * {r_latex}
        = {gs.get_term(gs.n - 1, to_latex = True)} $ 
        
        ${{t}}_{{{latex(gs.n)}}} 
        = {{t}}_{{{latex(gs.n - 1)}}} * {r_latex}
        = {gs.get_term(gs.n - 1, to_latex = True)} * {r_latex}
        = {gs.get_term(gs.n, to_latex = True)} $ 
        
        Therefore, the series is
        ${series_str}$
        
        It can also be written as ${summtion_latex}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        ${series_str}$
        
        It can also be written as ${summtion_latex}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
    
    
    def _problem_term_in_a_gs(self, *args, **kwargs):
        '''
        Optional Parameters:
        a = None  #first term; sympy or numeric expression
        r = None #common ration; sympy or numeric expression
        n = None #index of term; sympy or numeric expression
        '''
        gs_helper = self.base_helper.get_algebra_helper().get_geometric_series_helper()

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        a = kwargs.get('a', None)
        r = kwargs.get('r', None)
        n = kwargs.get('n', None)

        gs = gs_helper.get_random_series(a = a, r = r, n = n)
        
        term_latex = gs.get_term(gs.n, to_latex =  True)
        
        problem_s = f"""
        Write $t_{{{latex(gs.n)}}}$ or $\\left({latex(gs.n + 1)}\\right)th$ term of the geometric series 
        whose first term is ${latex(gs.a)}$ and the common ratio as ${latex(gs.r)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)
        
        soln_s = f"""
        $\\left({latex(gs.n + 1)}\\right)th$ term
        
        $= {{t}}_{{{latex(gs.n)}}}$ 
        
        $= {term_latex}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        ${term_latex}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    
    def _problem_write_expression_for_gs_sum_simple(self, *args, **kwargs):
        gs_helper = self.base_helper.get_algebra_helper().get_geometric_series_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        gs = gs_helper.get_standard_series(a = random.choice([1, symbols('a')]))
        terms = gs.get_terms(to_latex =  True)
        series = gs.get_series(terms, to_str = False)
        series_str = gs.get_series(terms, to_str = True)
        
        sum_str = gs.get_sum(to_latex = True)
        
        gs_r = gs_helper.get_standard_series(a = gs.a * gs.r)
        terms_r = gs_r.get_terms(to_latex =  True)
        series_r = gs_r.get_series(terms_r, to_str = False)
        series_r_str = gs_r.get_series(terms_r, to_str = True)
        
        summtion_latex = gs.get_summtion_latex()
        
        problem_s1 = f"""
        Write the sum of geometric series of ${latex(gs.n)}$ terms, 
        with first term as ${latex(gs.a)}$ and the common ratio as ${latex(gs.r)}$
        """
        problem_s2 = f"""
        Solve: ${summtion_latex}$
        """
        problem_s = random.choice([problem_s1, problem_s2])
        
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)
        
        soln_s = f"""
        $S = {series_str}$
        
        $S*{gs.r} = {series_r_str}$
        
        $\\implies S - S*{gs.r} = {list(terms.values())[0]} - {list(terms_r.values())[-1]}$
        
        $\\implies S * (1 - {gs.r}) = {list(terms.values())[0]} - {list(terms_r.values())[-1]}$
        
        $\\implies S = {sum_str}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        ${sum_str}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_write_expression_for_gs_sum(self, *args, **kwargs):
        gs_helper = self.base_helper.get_algebra_helper().get_geometric_series_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')


        a = kwargs.get('a', None)
        r = kwargs.get('r', None)
        n = kwargs.get('n', None)

        standard_gs = gs_helper.get_standard_series(a = symbols('a'))
        standard_sum_str = standard_gs.get_sum(to_latex = True)
        
        gs = gs_helper.get_random_series(a = a, r = r, n = n)
        sum_str = gs.get_sum(to_latex = True)
        
        summtion_latex = gs.get_summtion_latex()
        
        problem_s1 = f"""
        Write sum of geometric series of ${latex(gs.n)}$ terms, 
        with first term as ${latex(gs.a)}$ and the common ratio as ${latex(gs.r)}$
        """
        problem_s2 = f"""
        Solve: ${summtion_latex}$
        """
        problem_s = random.choice([problem_s1, problem_s2])
        
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)
        
        soln_s = f"""
        $S = {standard_sum_str}$
        Where:
        \t\t ${latex(standard_gs.a)} = {latex(gs.a)}$
        
        \t\t ${latex(standard_gs.r)} = {latex(gs.r)}$
        
        \t\t ${latex(standard_gs.n)} = {latex(gs.n)}$
        
        $\\implies S = {sum_str}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        ${sum_str}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_future_value_simple(self, *args, **kwargs):
        gs_helper = self.base_helper.get_algebra_helper().get_geometric_series_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        fv = symbols('F')

        standard_gs = gs_helper.get_standard_series(a = symbols('a'))
        standard_principal = standard_gs.a
        time_unit = symbols('year')
        
        problem_s = f"""
        Write future value of ${latex(standard_principal)}$ 
        at the end of ${latex(standard_gs.n)}$ ${latex(time_unit)}$
        compounded @ rate of interest ${latex(standard_gs.r)}$ per ${latex(time_unit)}$.
        
        <br/><br/>
        Please note that the future value at the end of 
        ${latex(standard_gs.n)}$ ${latex(time_unit)}$
        is the the future value at the beginning of 
        ${latex(standard_gs.n + 1)}$ ${latex(time_unit)}$, 
        also written as $F_{{{latex(standard_gs.n)}}}$
        as we start counting ${latex(time_unit)}$ from 0, not 1.
        """
        
        answer_s = f"{standard_gs.get_future_value(standard_gs.n, to_latex = True)}"
        
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)
        
        soln_s = f"""
        $(next \\space period \\space value) = (previous \\space period \\space value) + interest $
        $\\implies (next \\space period \\space value) 
            = (previous \\space period \\space value) 
            + (previous \\space period \\space value) * (rate \\space of \\space interest) $
        $\\implies (next \\space period \\space value) 
            = (previous \\space period \\space value) 
            * (1  + rate \\space of \\space interest)$
            
        ${fv}_{{{0}}} = {standard_gs.get_future_value(0, to_latex = True)} $

        Value after $1$ ${latex(time_unit)}$ $= {fv}_{{{1}}} 
        = {fv}_{{{0}}} * \\left( 1 + {latex(standard_gs.r)} \\right) 
        = {standard_gs.get_future_value(1, to_latex = True)} $

        Value after $2$ ${latex(time_unit)}$ $= {fv}_{{{2}}} 
        = {fv}_{{{1}}} * \\left( 1 + {latex(standard_gs.r)} \\right) 
        = {standard_gs.get_future_value(2, to_latex = True)} $

        Value after $3$ ${latex(time_unit)}$ $= {fv}_{{{3}}} 
        = {fv}_{{{2}}} * \\left( 1 + {latex(standard_gs.r)} \\right) 
        = {standard_gs.get_future_value(3, to_latex = True)} $
        $\\cdots$
        Value after ${latex(standard_gs.n-1)}$ ${latex(time_unit)}$ $= {fv}_{{{latex(standard_gs.n-1)}}} 
        = {fv}_{{{latex(standard_gs.n - 2)}}} * \\left( 1 + {latex(standard_gs.r)} \\right) 
        = {standard_gs.get_future_value(standard_gs.n - 1, to_latex = True)} $

        Value after ${latex(standard_gs.n)}$ ${fv}_{{{latex(standard_gs.n)}}} 
        = {fv}_{{{latex(standard_gs.n - 1)}}} * \\left( 1 + {latex(standard_gs.r)} \\right) 
        = {standard_gs.get_future_value(standard_gs.n, to_latex = True)} $


        Therefore, the future value at the end of ${latex(standard_gs.n)}$ ${latex(time_unit)}$
        $= F_{{{latex(standard_gs.n)}}}$ 
        $=  {answer_s} $
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        ${answer_s}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_future_value(self, *args, **kwargs):
        gs_helper = self.base_helper.get_algebra_helper().get_geometric_series_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        standard_gs = gs_helper.get_standard_series(a = symbols('a'))
        time_unit = symbols('year')
        fv = symbols('F')
        
        a = kwargs.get('a', None)
        r = kwargs.get('r', None)
        n = kwargs.get('n', None)
        gs = gs_helper.get_random_series(a = a, r = r, n = n)
        principal = gs.a

        answer_s = f"{gs.get_future_value(gs.n, to_latex = True)}"
        
        problem_s = f"""
        Write future value of ${latex(principal)}$ 
        at the end of ${latex(gs.n)}$ ${latex(time_unit)}$
        compounded @ rate of interest ${latex(gs.r)}$ per ${latex(time_unit)}$.
        """
        
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)
        
        soln_s = f"""
        Future value at the end of {latex(standard_gs.n)} ${latex(time_unit)}$, or
        at the beginning of {latex(standard_gs.n + 1)} ${latex(time_unit)}$
        $= {fv}_{{{latex(standard_gs.n)}}} 
        = {standard_gs.get_future_value(standard_gs.n, to_latex = True)} $
        
        We have:
        \t\t ${latex(standard_gs.a)} =$ Present Value $= {latex(gs.a)}$
        
        \t\t ${latex(standard_gs.r)} =$ Rate of Interest $= {latex(gs.r)}$
        
        \t\t ${latex(standard_gs.n)} =$ Time Periods $= {latex(gs.n)}$
        
        $\\implies {fv}_{{{latex(gs.n + 1)}}} 
        = {answer_s} $

        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        ${answer_s}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_present_value(self, *args, **kwargs):
        gs_helper = self.base_helper.get_algebra_helper().get_geometric_series_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        pv = symbols('P')

        standard_gs = gs_helper.get_standard_series(a = pv)
        fv_s = f'{pv}_{{{standard_gs.n}}}'
        fv = symbols(fv_s)
        
        time_unit = symbols('year')
        
        a = kwargs.get('a', None)
        r = kwargs.get('r', None)
        n = kwargs.get('n', None)
        gs = gs_helper.get_random_series(a = a, r = r, n = n)
        principal = gs.a

        answer_s = f"{gs.get_present_value(gs.a, gs.n, to_latex = True)}"
        
        problem_s = f"""
        Write present value of a value that will be ${latex(principal)}$ 
        at the end of ${latex(gs.n)}$ ${latex(time_unit)}$
        discounted @ rate of interest ${latex(gs.r)}$ per ${latex(time_unit)}$.
        
        <br/><br/>
        Please note that the future value at the end of 
        ${latex(gs.n)}$ ${latex(time_unit)}$
        is the the future value at the beginning of 
        ${latex(gs.n + 1)}$ ${latex(time_unit)}$, 
        also written as $F_{{{latex(gs.n)}}}$
        as we start counting ${latex(time_unit)}$ from 0, not 1.
        """
        
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)
        
        soln_s = f"""
        Future value at the end of {latex(standard_gs.n)} ${latex(time_unit)}$, or
        at the beginning of {latex(standard_gs.n + 1)} ${latex(time_unit)}$
        
        $= {fv} = {standard_gs.get_future_value(standard_gs.n, to_latex = True)} $
        
        $\\implies$ Present Value $ {pv} 
        = {standard_gs.get_present_value(fv, standard_gs.n, to_latex = True)} $
        
        Where:
        \t\t ${latex(standard_gs.a)} =$ Present Value 
        
        \t\t ${fv} =$ Future Value $= {latex(gs.a)}$
        
        \t\t ${latex(standard_gs.r)} =$ discounted rate of interest $= {latex(gs.r)}$
        
        \t\t ${latex(standard_gs.n)} =$ future periods $= {latex(gs.n)}$
        
        The present value of a value that will be ${latex(principal)}$ at the end of ${latex(gs.n)}$ ${latex(time_unit)}$
        = The present value of a value that will be ${latex(principal)}$ at the beginning of ${latex(gs.n + 1)}$ ${latex(time_unit)}$
        
        $= {pv}
        = {answer_s} $

        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        answer_s = f"""
        ${answer_s}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_future_annuity_value_simple(self, *args, **kwargs):
        gs_helper = self.base_helper.get_algebra_helper().get_geometric_series_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        fv = symbols('F')

        standard_gs = gs_helper.get_standard_series(a = symbols('a'))
        equal_installment = standard_gs.a
        time_unit = symbols('year')

        sum_gs = gs_helper.get_random_series(a = standard_gs.get_future_value(1), 
                                            r = 1 + standard_gs.r, 
                                            n = standard_gs.n
                                            )

        sum_str = sum_gs.get_sum(to_latex = True)
        standard_sum_str = standard_gs.get_sum(to_latex = True)

        sum_fv_latex = standard_gs.get_future_annuity_value( 
                                 equal_installment = standard_gs.a, 
                                 r = standard_gs.r, 
                                 n = standard_gs.n,
                                 as_alpha = False,
                                 to_latex = True)
        
        sum_fv_alpha_latex = standard_gs.get_future_annuity_value( 
                                 equal_installment = standard_gs.a, 
                                 r = standard_gs.r, 
                                 n = standard_gs.n,
                                 as_alpha = True,
                                 to_latex = True)
        

        
        problem_s = f"""
        Write future value at the end of ${latex(standard_gs.n)}$ ${latex(time_unit)}$,
        of equal installments ${latex(equal_installment)}$ 
        deposited at the beginning of every ${latex(time_unit)}$
        compounded @ rate of interest ${latex(standard_gs.r)}$ per ${latex(time_unit)}$.
        
        <br/><br/>
        Please note that the future value at the end of 
        ${latex(standard_gs.n)}$ ${latex(time_unit)}$
        is the the future value at the beginning of 
        ${latex(standard_gs.n + 1)}$ ${latex(time_unit)}$, also written as $F_{{{latex(standard_gs.n + 1)}}}$.
        """
        
        
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)
        
        soln_s = f"""
        The sum deposited at the beginning of $0$  ${latex(time_unit)}$ will earn compounded interest for next ${latex(standard_gs.n)}$ ${latex(time_unit)}$. So, its future value will be 
        ${fv}_{{0}} = {standard_gs.get_future_value(standard_gs.n - 0, to_latex = True)}$

        The next installment will earn interest for $1$ ${latex(time_unit)}$ less. So, its future value will be 
        ${fv}_{{1}} = {standard_gs.get_future_value(standard_gs.n - 1, to_latex = True)}$
        
        The next installment will earn interest for $2$ ${latex(time_unit)}$ less. So, its future value will be 
        ${fv}_{{2}} = {standard_gs.get_future_value(standard_gs.n - 2, to_latex = True)}$
        $\\cdots$
        The last installment will earn interest for just $1$ ${latex(time_unit)}$
        as it will be deposited in the beginning of the ${latex(time_unit)}$ and will mature the end of ${latex(time_unit)}$. So, its future value will be 
        ${fv}_{{{standard_gs.n}}} = {standard_gs.get_future_value(1, to_latex = True)}$
        
        The future value of all equal installments will be the sum of all future values above.
        
        ${fv} = {fv}_{{0}} + {fv}_{{1}} + {fv}_{{2}} + \\cdots + {fv}_{{{standard_gs.n}}}$
        
        $= {standard_gs.get_future_value(standard_gs.n, to_latex = True)} 
        + {standard_gs.get_future_value(standard_gs.n - 1, to_latex = True)}
        + {standard_gs.get_future_value(standard_gs.n - 2, to_latex = True)}
        + \\cdots
        + {standard_gs.get_future_value(1, to_latex = True)} $
 
        If we write the terms in reverse order, we get a geometric series with 
        \t\t first term = ${standard_gs.get_future_value(1, to_latex = True)}$
        \t\t common ratio = $1 + {standard_gs.get_var_latex(standard_gs.r)}$
        \t\t number of terms = ${latex(standard_gs.n)}$ as there are ${latex(standard_gs.n)}$ installments
        
        The future value, i.e., the sum of  this series will be
        
        ${fv} = {sum_str}$ \t\t applying the formula $\\left[S = {standard_sum_str}\\right. $
        
        $= {sum_fv_latex}$ \t\t ,or
        
        $= {sum_fv_alpha_latex}$ \t\t Where ${latex(standard_gs.alpha)} 
        = {standard_gs.get_alpha_value(standard_gs.r, to_latex = True)} $  
        
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html',
                                     add_html_line_break = True)
        
        answer_s = f"""
        $= {sum_fv_latex}$ \t\t ,or
        
        $= {sum_fv_alpha_latex}$ \t\t Where ${latex(standard_gs.alpha)} 
        = {standard_gs.get_alpha_value(standard_gs.r, to_latex = True)} $  
        
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_future_annuity_value(self, *args, **kwargs):
        gs_helper = self.base_helper.get_algebra_helper().get_geometric_series_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        fv = symbols('F')

        standard_gs = gs_helper.get_standard_series(a = symbols('a'))
        equal_installment = standard_gs.a
        
        time_unit = symbols('year')
        
        a = kwargs.get('a', None)
        r = kwargs.get('r', None)
        n = kwargs.get('n', None)
        gs = gs_helper.get_random_series(a = a, r = r, n = n)

        standard_gs_sum_fv_latex = standard_gs.get_future_annuity_value( 
                                 equal_installment = standard_gs.a, 
                                 r = standard_gs.r, 
                                 n = standard_gs.n,
                                 as_alpha = True,
                                 to_latex = True)

        gs_sum_fv_latex = standard_gs.get_future_annuity_value( 
                                 equal_installment = gs.a, 
                                 r = gs.r, 
                                 n = gs.n,
                                 as_alpha = True,
                                 to_latex = True)

        
        problem_s = f"""
        Write future value at the end of ${latex(gs.n)}$ ${latex(time_unit)}$,
        of equal installments ${latex(gs.a)}$ 
        deposited at the beginning of every ${latex(time_unit)}$
        compounded @ rate of interest ${latex(gs.r)}$ per ${latex(time_unit)}$.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)
        
        soln_s = f"""
        Present Value $ {fv} $
        
        $= {standard_gs_sum_fv_latex}$ \t\t Where ${latex(standard_gs.alpha)} 
        = {standard_gs.get_alpha_value(standard_gs.r, to_latex = True)} $
        
        Where:
        \t\t ${latex(standard_gs.a)} =$ Each Installment $= {latex(gs.a)}$
        
        \t\t ${latex(standard_gs.r)} =$ discounted rate of interest $= {latex(gs.r)}$
        
        \t\t ${latex(standard_gs.n)} =$ future periods $= {latex(gs.n)}$
        
        $\\implies 
        {fv} = {gs_sum_fv_latex} $ \t\t Where ${latex(gs.alpha)} 
        = {standard_gs.get_alpha_value(gs.r, to_latex = True)} $

        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        answer_s = f"""
        ${fv} = {gs_sum_fv_latex} $ \t\t Where ${latex(gs.alpha)} 
        = {standard_gs.get_alpha_value(gs.r, to_latex = True)} $
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_present_annuity_value_simple(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        fv = symbols('F')
        pv = symbols('P')

        standard_gs = gs_helper.get_standard_series(a = symbols('a'))
        equal_installment = standard_gs.a
        time_unit = symbols('year')
        r_latex = standard_gs.get_var_latex(standard_gs.r)

        sum_gs_r = 1 / (1 + standard_gs.r)
        
        sum_gs_r_latex = latex_fraction(1, f"1 + {r_latex}")

        sum_gs = gs_helper.get_random_series(
                    a = standard_gs.get_present_value(standard_gs.a, 0), 
                    r = sum_gs_r, 
                    n = standard_gs.n
                    )

        sum_str = sum_gs.get_sum(to_latex = True)
        standard_sum_str = standard_gs.get_sum(to_latex = True)

        sum_pv_latex = standard_gs.get_present_annuity_value( 
                                 equal_installment = standard_gs.a, 
                                 r = standard_gs.r, 
                                 n = standard_gs.n,
                                 as_beta = True,
                                 to_latex = True)
        
        problem_s = f"""
        Write present value of of ${latex(standard_gs.n)}$ equal installments ${latex(equal_installment)}$ 
        deposited at the beginning of every ${latex(time_unit)}$
        discounted @ rate of interest ${latex(standard_gs.r)}$ per ${latex(time_unit)}$.
        """
        
        
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)
        
        soln_s = f"""
        The present value of installment deposited at the beginning of $0$ ${latex(time_unit)}$ will be same
        ${pv}_{{0}} = {standard_gs.get_present_value(standard_gs.a, 0, to_latex = True)}$

        The present value of installment deposited at the beginning of $1$ ${latex(time_unit)}$ will be discounted for $1$ ${latex(time_unit)}$
        ${pv}_{{1}} = {standard_gs.get_present_value(standard_gs.a, 1, to_latex = True)}$
        
        The present value of installment deposited at the beginning of $2$ ${latex(time_unit)}$ will be discounted for $2$ ${latex(time_unit)}$
        ${pv}_{{2}} = {standard_gs.get_present_value(standard_gs.a, 2, to_latex = True)}$
        $\\cdots$

        The present value of installment deposited at the beginning of ${latex(standard_gs.n)}$ ${latex(time_unit)}$ will be discounted for ${latex(standard_gs.n)}$ ${latex(time_unit)}$
        ${pv}_{{{latex(standard_gs.n)}}} = {standard_gs.get_present_value(standard_gs.a, standard_gs.n, to_latex = True)}$
        
        The present value of all equal installments will be the sum of all present values above.
        
        ${pv} = {pv}_{{0}} + {pv}_{{1}} + {pv}_{{2}} + \\cdots + {pv}_{{{latex(standard_gs.n)}}}$
        
        $= {standard_gs.get_present_value(standard_gs.a, 0, to_latex = True)}
        + {standard_gs.get_present_value(standard_gs.a, 1, to_latex = True)}
        + {standard_gs.get_present_value(standard_gs.a, 2, to_latex = True)}
        + \\cdots
        + {standard_gs.get_present_value(standard_gs.a, standard_gs.n, to_latex = True)} $
 
        If we write the terms in reverse order, we get a geometric series with 
        
        \t\t first term = ${latex(standard_gs.a)}$
        
        \t\t common ratio = ${sum_gs_r_latex}$
        
        \t\t number of terms = ${latex(standard_gs.n)}$ as there are ${latex(standard_gs.n)}$ installments
        
        The sum of  this series will be
        
        ${pv} = {sum_str}$ \t\t applying the formula $\\left[S = {standard_sum_str}\\right. $
        
        $= {sum_pv_latex}$ \t\t Where ${latex(standard_gs.beta)} 
        = {standard_gs.get_beta_value(standard_gs.r, to_latex = True)} $ 
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html',
                                     add_html_line_break = True)
        
        answer_s = f"""
        ${sum_str}$ \t\t or,
        
        ${sum_pv_latex}$  \t\t Where ${latex(standard_gs.beta)} 
        = {standard_gs.get_beta_value(standard_gs.r, to_latex = True)} $ 
        
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_present_annuity_value(self, *args, **kwargs):
        gs_helper = self.base_helper.get_algebra_helper().get_geometric_series_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        pv = symbols('P')

        standard_gs = gs_helper.get_standard_series(a = symbols('a'))
        equal_installment = standard_gs.a
        
        time_unit = symbols('year')
        
        a = kwargs.get('a', None)
        r = kwargs.get('r', None)
        n = kwargs.get('n', None)
        gs = gs_helper.get_random_series(a = a, r = r, n = n)

        standard_gs_sum_pv_latex = standard_gs.get_present_annuity_value( 
                                 equal_installment = standard_gs.a, 
                                 r = standard_gs.r, 
                                 n = standard_gs.n,
                                 as_beta = True,
                                 to_latex = True)

        gs_sum_pv_latex = standard_gs.get_present_annuity_value( 
                                 equal_installment = gs.a, 
                                 r = gs.r, 
                                 n = gs.n,
                                 as_beta = True,
                                 to_latex = True)

        answer_s = f"{gs.get_present_value(gs.a, gs.n + 1, to_latex = True)}"
        
        problem_s = f"""
        Write present value of of ${latex(gs.n)}$ equal installments ${latex(equal_installment)}$ 
        deposited at the beginning of every ${latex(time_unit)}$
        discounted @ rate of interest ${latex(gs.r)}$ per ${latex(time_unit)}$.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)
        
        soln_s = f"""
        Present Value $ {pv} $
        
        $= {standard_gs_sum_pv_latex}$ \t\t Where ${latex(standard_gs.beta)} 
        = {standard_gs.get_beta_value(standard_gs.r, to_latex = True)} $
        
        Where:
        \t\t ${latex(standard_gs.a)} =$ Each Installment $= {latex(gs.a)}$
        
        \t\t ${latex(standard_gs.r)} =$ discounted rate of interest $= {latex(gs.r)}$
        
        \t\t ${latex(standard_gs.n)} =$ future periods $= {latex(gs.n)}$
        
        $\\implies 
        {pv} = {gs_sum_pv_latex} $ \t\t Where ${latex(gs.beta)} 
        = {standard_gs.get_beta_value(gs.r, to_latex = True)} $

        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        answer_s = f"""
        ${pv} = {gs_sum_pv_latex} $ \t\t Where ${latex(gs.beta)} 
        = {standard_gs.get_beta_value(gs.r, to_latex = True)} $
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate


    def _sample(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

