
'''
Created on 26-Jan-2022
@author: bthakur

#implement more form 
https://assets.stevens.edu/mviowpldu823/4dKxOHcMRcEQR5JusfxKFe/47285bf451e37f10a8b6870223ef377c/Math_Olympiad_Sample_Problems_2016-2019.pdf
        
        
'''
import random
import inspect
import traceback
from sympy import (latex, Rational, Add, Mul)
from xv.km.util import latex_fraction, wrap_latex_statement

import numpy as np

from ...helper import BaseHelper
from xv.km.managers import BaseManager
from xv.km.kelements import KProblemTemplate
        
# _rational_number_manager       
# RationalNumberManager
        
class  RationalNumberManager(BaseManager):
    '''
    Example:
        ke = RationalNumberManager()
        ke
        
        ke.printProblemTypes()
        
        ke.getRandomProblem()
        ke.getRandomProblem(problem_type = 0)
        ke.getRandomProblem(problem_type = 1)
        ...
        
        ke.printProblem()
        ke.printAnswer()
        ke.printSolution()
        
    '''    
    def __new__(cls, *args, **kwargs):
        instance = super(RationalNumberManager, cls).__new__(cls, *args, **kwargs)
        for base_class in RationalNumberManager.__bases__:
            base_class.__init__(instance, *args, **kwargs)
        return instance
    
    def __init__(self, *args, **kwargs):
        self.base_helper = BaseHelper(**kwargs)
        pass

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

        
        
        statement = f"""
        If cost of one {data['name']} is $
        {data['unit_price_with_currency']}
        $. How many {data['name']}s you can buy in $
        {data['amount_with_currency']}
        $?
        
        ${data['currency_latex']} = $ {data['currency_name']}
        
        """
        problemTemplate.add_problem(statement.strip(), "text/html")

        soln_s = f"""
        {data['computation_latex']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        answer_s = f"""{data['qty_stmt']}"""
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
        
    def _init_problem_types(self, *args, **kwargs):
        self._append_problem_types([
                '_problem_plot_unit_circle_divided_into_n_sides',
            
                ['_problem_int_qty', 'This is desc of int qty'],
                '_problem_fraction_qty_unit_price_100',
                '_problem_fraction_qty',
                '_problem_convert_denominator',
                
                '_problem_simplify_rational_numbers',
                '_problem_simplify_divisions_of_rational_numbers',
                '_problem_convert_to_similar_rational_numbers',
                
                '_problem_convert_to_common_denoms',
                '_problem_add_rational_numbers',
                '_problem_average_of_rational_numbers',
                '_problem_sort_rational_numbers',
                '_problem_median_of_rational_numbers',
                '_problem_plot_rational_numbers',
                '_problem_convert_to_mixed_numeral_system',
                '_problem_simplify_mixed_numeral_system',
                '_problem_convert_int_frac_to_mixed_numeral_system',
                '_problem_mixed_numeral_sum',
                '_problem_mixed_numeral_prod',
                '_problem_mixed_numeral_division',
                
                '_problem_fractions_from_competitions',
                
                '_problem_rational_number_questions_easy',
                
             ])
        pass
    
    def _problem_plot_unit_circle_divided_into_n_sides(self, *args, **kwargs):
        """

        doc_style: xv_doc
        """

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_unit_circle_divided_into_n_sides
        
        point_count = random.randint(2, 10)
        svg_str_prob  = plot_method(point_count = point_count, show_point_lables = False)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if svg_str_prob is None:
            prob_s = f"""
            Error: Failed to plot a unit circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        result = circle_helper.get_plot_code_and_svg_str(plot_method,
                                                         point_count = point_count)

        if result is None:
            prob_s = f"""
            Error: Failed to plot a unit circle with angle of the points on the circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            

        # set show_point_lables = True in plot_code      
        plot_code = result['plot_code']
        repl_text = """kwargs.get('show_point_lables', True)"""
        plot_code = plot_code.replace(repl_text, 'True')
        
        repl_text = """point_count = kwargs.get('point_count')"""
        new_text = f"""point_count = {point_count}"""
        plot_code = plot_code.replace(repl_text, new_text)
        
        prob_s = f"""
        Divide the circle into {point_count} parts and mark the points.
        {svg_str_prob}
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{plot_code}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    
    

    
    def _problem_int_qty(self, *args, **kwargs):
        helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
        
        data = helper.get_random_price_qty_data(int_qty = True,
                                                   unit_price_100 = False)
        return self._TEMPLATE_QTY_PROBLEM(data, *args, **kwargs)
        pass


    def _problem_fraction_qty_unit_price_100(self, *args, **kwargs):
        helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
        
        data = helper.get_random_price_qty_data(simple_fraction_qty = True,
                                                   unit_price_100 = True,
                                                   decimal_answer = False)
        return self._TEMPLATE_QTY_PROBLEM(data, *args, **kwargs)
        pass

                
    def _problem_fraction_qty(self, *args, **kwargs):
        helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
        
        data = helper.get_random_price_qty_data(unit_price_100 = False)
        return self._TEMPLATE_QTY_PROBLEM(data, *args, **kwargs)
        pass


    def _problem_convert_denominator(self, *args, **kwargs):
        """
        Convert to similar rational numbers


        Minimum Grade: 3
        Maximum Grade: 6

        doc_style: xv_doc
        """

        helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
        result = helper.get_rational_denominator_transform()
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Write a number equal to $\\displaystyle
        {result['n_latex']}$ but with denominator $\\displaystyle
        {latex(result['q'] * result['ratio'])}$ 
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        We have to change denominator from $\\displaystyle 
        {latex(result['q'])}$ to $\\displaystyle {latex(result['q'] * result['ratio'])}$. 
        
        We know, $\\displaystyle {result['factor_latex']} = {latex(result['ratio'])}$
        
        So, we will multiply both numerator and denominator by $\\displaystyle 
        {latex(result['ratio'])}$:
        
        $\\displaystyle {result['n_latex']}
        
        = {result['n_similar_expanded_latex']}
        
        = {result['n_similar_latex']}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {result['n_similar_latex']}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate


    def _problem_simplify_rational_numbers(self, *args, **kwargs):
        """
        Simplify rational numbers to a p/q coprime form


        Minimum Grade: 3
        Maximum Grade: 6
        doc_style: xv_doc
        """

        helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
        result = helper.get_problem_simplify_rational_numbers()

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

        prob_s = f"""
        {result['prob_s']}
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        {result['soln_s']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['answer_s']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_simplify_divisions_of_rational_numbers(self, *args, **kwargs):
        """
        Simplify rational numbers to a p/q coprime form


        Minimum Grade: 3
        Maximum Grade: 6
        doc_style: xv_doc
        """

        helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
        result = helper.get_problem_simplify_divisions_of_rational_numbers()

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

        prob_s = f"""
        {result['prob_s']}
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        {result['soln_s']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['answer_s']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_convert_to_similar_rational_numbers(self, *args, **kwargs):
        """
        Convert to similar rational numbers


        Minimum Grade: 3
        Maximum Grade: 6
        doc_style: xv_doc
        """

        helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
        result = helper.get_similar_rational_pairs()

        q1 = result['q1']
        q2 = result['q2']

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

        problem_s = f"""
        Make the numbers $\\displaystyle 
        {result['n1_latex']}$ and $\\displaystyle 
        {result['n2_latex']}$ similar.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        
        The lowest multiples of denominators ${latex(result['q1'])}$ and ${latex(result['q2'])}$ is $
        {latex(result['lcm'])}$. Check the tables of ${latex(result['q1'])}$ and ${latex(result['q2'])}$ to verify it.
        
        So, we will make both denominators ${latex(result['lcm'])}$.
        
        Therefore, multiply numerator and denominator of the first number with  ${latex(result['n1_multiplier'])}$
        
        $\\displaystyle {result['n1_latex']} = {result['n1_sim_expanded']} = {result['n1_sim']}$ 
        
        Similarly, multiply numerator and denominator of the second number with ${latex(result['n2_multiplier'])}$
        
        $\\displaystyle {result['n2_latex']} = {result['n2_sim_expanded']} = {result['n2_sim']}$ 
    
        Thus, the similar form of numbers are 
        
        $\\displaystyle {result['n1_sim']} , {result['n2_sim']}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {result['n1_sim']} , {result['n2_sim']}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate



    def _problem_convert_to_common_denoms(self, *args, **kwargs):
        """
        Transform rational numbers into numbers with common denominators


        doc_style: xv_doc
        """

        helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
        size = random.randint(3, 5)
        rational_numbers = helper.get_random_rational_numbers(size = size)
        
        

        rational_numbers_str = helper.rational_numbers_to_str(rational_numbers, 
                                    operator= ",",
                                    prepend = "")
        
        soln_statement, answer_s = helper.solution_common_denom(rational_numbers)


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

        problem_s = f"""
        Convert the numbers to  numbers with common denominator:
        
        {rational_numbers_str}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        Numbers:
        
        {rational_numbers_str}
        
        
        {soln_statement}
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {answer_s}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    def _problem_add_rational_numbers(self, *args, **kwargs):
        """
        Compute sum of rational numbers

        doc_style: xv_doc
        """

        helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
        size = random.randint(3, 5)
        rational_numbers = helper.get_random_rational_numbers(size = size)
        
        

        rational_numbers_str = helper.rational_numbers_to_str(rational_numbers, 
                                    operator= " + ",
                                    prepend = "")
        
        soln_statement, answer_s = helper.solution_sum(rational_numbers)

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

        problem_s = f"""
        Find sum of the following:
        
        {rational_numbers_str}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        Sum of numbers
        
        {rational_numbers_str}
        
        {soln_statement}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {answer_s}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    

    def _problem_average_of_rational_numbers(self, *args, **kwargs):
        """
        Compute average of rational numbers

        doc_style: xv_doc
        """

        helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
        size = random.randint(3, 5)
        rational_numbers = helper.get_random_rational_numbers(size = size)
        
        

        rational_numbers_str = helper.rational_numbers_to_str(rational_numbers, 
                                    operator= ",",
                                    prepend = "")
        
        rational_numbers_plus_str = helper.rational_numbers_to_str(rational_numbers, 
                                    operator= " + ",
                                    prepend = "")
        
        soln_statement, answer_s = helper.solution_average(rational_numbers)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Find average of the following:
        
        {rational_numbers_str}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        Sum of numbers
        
        {rational_numbers_plus_str}
        
        {soln_statement}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {answer_s}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
        
    def _problem_sort_rational_numbers(self, *args, **kwargs):
        """
        Sort rational numbers in ascending order

        Minimum Grade: 3
        Maximum Grade: 6

        doc_style: xv_doc
        """
        
        helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
        size = random.randint(3, 5)
        rational_numbers = helper.get_random_rational_numbers(size = size)
        
        rational_numbers_str = helper.rational_numbers_to_str(rational_numbers, 
                                    operator= ",",
                                    prepend = "")
        
        soln_statement, answer_s = helper.solution_sort(rational_numbers)

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

        problem_s = f"""
        Sort the following numbers in ascending order:
        
        {rational_numbers_str}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        Fractions in given order:
        
        {rational_numbers_str}
        
        {soln_statement}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {answer_s}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass


    def _problem_median_of_rational_numbers(self, *args, **kwargs):
        """
        Find median of rational numbers

        doc_style: xv_doc
        """
        
        helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
        size = random.randint(3, 6)
        rational_numbers = helper.get_random_rational_numbers(size = size)
        
        rational_numbers_str = helper.rational_numbers_to_str(rational_numbers, 
                                    operator= ",",
                                    prepend = "")
        
        soln_statement, answer_s = helper.solution_median(rational_numbers)

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

        problem_s = f"""
        Find the median of the numbers:
        
        {rational_numbers_str}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        Fractions in given order:
        
        {rational_numbers_str}
        
        {soln_statement}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {answer_s}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass


    def _problem_plot_rational_numbers(self, *args, **kwargs):
        """
        Plot rational numbers

        doc_style: xv_doc
        """
        
        helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
        size = random.randint(3, 6)
        rational_numbers = helper.get_random_rational_numbers(size = size)
        
        rational_numbers_str = helper.rational_numbers_to_str(rational_numbers, 
                                    operator= ",",
                                    prepend = "")
        
        result = helper.solution_plot(rational_numbers)
        svg_str = result['svg_str']
        

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

        problem_s = f"""
        Plot of the numbers:
        
        {rational_numbers_str}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        <h2>Numbers:</h2>
        {rational_numbers_str}
        
        <h2>Common Denominators:</h2>
        {result['soln_common_denom']}

        <h2>Sum:</h2>
        {result['soln_sum']}

        <h2>Average:</h2>
        {result['soln_average']}

        <h2>Sorted Numbers:</h2>
        {result['sorted_composite_rational_numbers_str']}
        
        = {result['sorted_rational_numbers_str']}
        
        <h2>Median:</h2>
        {result['soln_median']}

        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')
        
        answer_s = f"""
        <h2>Sum:</h2>
        {result['sum_value']}

        <h2>Average:</h2>
        $\\displaystyle {latex(result['average'])}$

        <h2>Sorted Numbers:</h2>
        {result['sorted_rational_numbers_str']}

        <h2>Median:</h2>
        $\\displaystyle {latex(result['median'])}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        problemTemplate.add_answer(svg_str, 'text/svg')
        
        return problemTemplate
        pass

    def _problem_convert_to_mixed_numeral_system(self, *args, **kwargs):
        rn_helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        size = kwargs.get('size', 4)
        nums = rn_helper.get_random_mixed_numbers(size = size)
        nums_mixed_form = rn_helper.convert_to_mixed_form_latex(nums)
        
        latex_str = [f"{latex(x)}" for x in nums]
        latex_str = f" ,\\; ".join(latex_str)
        
        nums_mixed_form_str = f" ,\\; ".join(nums_mixed_form)
        
        statement = f"""
        Convert the numbers in mixed form:
        
        $\\displaystyle {latex_str}$
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        soln_s = f"""
        $\\displaystyle {latex_str}$

        $\\displaystyle = {nums_mixed_form_str}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


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

    def _problem_simplify_mixed_numeral_system(self, *args, **kwargs):
        rn_helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        size = kwargs.get('size', 4)
        nums = rn_helper.get_random_mixed_numbers(size = size, mixed_form = True)
        nums_mixed_form = rn_helper.convert_to_mixed_form_latex(nums)
        

        nums_mixed_calc = rn_helper.convert_to_mixed_calc_latex(nums)
        
        
        latex_str = [f"{latex(x)}" for x in nums]
        latex_str = f" ,\\; ".join(latex_str)
        
        nums_mixed_form_str = f" ,\\; ".join(nums_mixed_form)
        nums_mixed_calc_str = f" ,\\; ".join(nums_mixed_calc)
        
        
        
        statement = f"""
        Simplify the mixed form:
        
        $\\displaystyle {nums_mixed_form_str}$
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        soln_s = f"""
        $\\displaystyle {nums_mixed_form_str}$
        
        $\\displaystyle = {nums_mixed_calc_str}$
        
        $\\displaystyle = {latex_str}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


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


    def _problem_convert_int_frac_to_mixed_numeral_system(self, *args, **kwargs):
        rn_helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        size = 2
        
        nums = []
        nums.append(random.randint(1, 20)  * random.choice([1, -1]))
                    
        while len(nums) < size:
            p = random.randint(1, 9) * random.choice([1, -1])
            q = random.randint(1, 9)
            num = Rational(p, q)
            if not num.has_mixed_form():
                if num != int(num):
                    nums.append(num)
            pass
        
        #nums_sum_list = rn_helper.convert_to_mixed_form_latex_sum_list(nums)
        nums_sum_list = rn_helper.convert_to_sum_latex(nums)
        nums_sum_list_str = f"  ".join(nums_sum_list)
        nums_sum = Rational(Add(*nums))
        
        statement = f"""
        Convert the expression to a mixed form:
        
        $\\displaystyle {nums_sum_list_str}$
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        soln_s = f"""
        $\\displaystyle {nums_sum_list_str}$

        $\\displaystyle = {latex(nums_sum)}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


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


    def _problem_mixed_numeral_sum(self, *args, **kwargs):
        rn_helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        size = kwargs.get('size', 4)
        
        nums = rn_helper.get_random_mixed_numbers(size = size)
        nums_sum = Rational(Add(*nums))

        nums_mixed_form = rn_helper.convert_to_mixed_form_latex(nums)
        
        latex_str = [f"{latex(x)}" for x in nums]
        latex_str = f" ,\\; ".join(latex_str)
        nums_mixed_form_str = f" ,\\; ".join(nums_mixed_form)
        
        nums_list = rn_helper.convert_to_mixed_form_rational_list(nums)
        nums_mixed_form_sum_list = rn_helper.convert_to_sum_latex(nums, mixed_form = True)
        
        nums_mixed_form_sum_str = f" ".join(nums_mixed_form_sum_list)
        
        nums_mixed_expanded = rn_helper.convert_to_mixed_expanded_latex(nums)

        ints_list = [x for x in nums_list if x == int(x)]
        fracs_list = [x for x in nums_list if x != int(x)]
        
        nums_mixed_expanded_str = f" ,\\; ".join(nums_mixed_expanded)
        
        nums_sum_list = rn_helper.convert_to_sum_latex(nums_list)
        nums_sum_list_str = f"  ".join(nums_sum_list)

        ints_sum_list = rn_helper.convert_to_sum_latex(ints_list)
        ints_sum_list_str = f"  ".join(ints_sum_list)

        ints_sum = Rational(Add(*ints_list))

        fracs_sum_list = rn_helper.convert_to_sum_latex(fracs_list)
        fracs_sum_list_str = f"  ".join(fracs_sum_list)
        fracs_sum = Rational(Add(*fracs_list))
        

        fracs_list_soln_statement, fracs_list_answer_s = rn_helper.solution_sum(fracs_list)

        
        fracs_sum_latex = [f"{latex(x)}" for x in fracs_sum.expanded_list()]
        if fracs_sum >= 0:
            fracs_sum_latex = " + ".join(fracs_sum_latex)
        else:
            fracs_sum_latex = " ".join(fracs_sum_latex)

        mod_nums = [ints_sum, fracs_sum]

        ints_fracs_sum_latex = rn_helper.convert_to_sum_latex(mod_nums)
        ints_fracs_sum_latex = f"  ".join(ints_fracs_sum_latex)
        
        
        statement = f"""
        Compute sum of the following numbers:

        $\\displaystyle {nums_mixed_form_str}$
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        

        soln_s_sum = []
        soln_s_sum_item = f"""
        $\\displaystyle {nums_mixed_form_sum_str}$
        
        $\\displaystyle = {nums_sum_list_str}$

        $\\displaystyle = \\left( {ints_sum_list_str} \\right) + \\left( {fracs_sum_list_str} \\right) $
        
        $\\displaystyle = {ints_fracs_sum_latex}
        \\qquad \\cdots \\text{{For details of sum of fractions, see the end of this solution.}}
        $

        
        """
        soln_s_sum.append(soln_s_sum_item.strip())


        if abs(fracs_sum) > 1:
            mod_nums_list = rn_helper.convert_to_mixed_form_rational_list(mod_nums)
            mod_nums_sum_list = rn_helper.convert_to_sum_latex(mod_nums_list)
            mod_nums_sum_list_str = f"  ".join(mod_nums_sum_list)

            ints_list = [x for x in mod_nums_list if x == int(x)]
            fracs_list = [x for x in mod_nums_list if x != int(x)]

            ints_sum = Rational(Add(*ints_list))
            fracs_sum = Rational(Add(*fracs_list))
            
            mod_nums = [ints_sum, fracs_sum]
            re_mod_nums_sum_list = rn_helper.convert_to_sum_latex(mod_nums)
            re_mod_nums_sum_list_str = f"  ".join(re_mod_nums_sum_list)
            
            soln_s_sum_item = f"""
            $\\displaystyle = {mod_nums_sum_list_str}$

            $\\displaystyle = {re_mod_nums_sum_list_str}$

            """
            soln_s_sum.append(soln_s_sum_item.strip())
            pass

        if (mod_nums[0] > 1 and mod_nums[1] < 0 or 
            mod_nums[0] < -1 and mod_nums[1] > 0):
            
            if mod_nums[0] > 0 and mod_nums[1] < 0:
                mod_nums_temp = [mod_nums[0] - 1, 1, mod_nums[1]] 
                mod_nums = [mod_nums[0] - 1, mod_nums[1] + 1] 
            else:
                mod_nums_temp = [mod_nums[0] + 1, -1, mod_nums[1]] 
                mod_nums = [mod_nums[0] + 1, mod_nums[1] - 1] 
                pass

            mod_nums_temp_sum_list = rn_helper.convert_to_sum_latex(mod_nums_temp)
            mod_nums_temp_sum_list_str = f"  ".join(mod_nums_temp_sum_list)
            
            mod_nums_sum_list = rn_helper.convert_to_sum_latex(mod_nums)
            mod_nums_sum_list_str = f"  ".join(mod_nums_sum_list)
            
            soln_s_sum_item = f"""
            $\\displaystyle = {mod_nums_temp_sum_list_str}$

            $\\displaystyle = {mod_nums_sum_list_str}$
            """
            soln_s_sum.append(soln_s_sum_item.strip())
            pass
        
        soln_s_sum_item = f"""
        $\\displaystyle = {nums_sum.mixed_form_latex()}$
        """
        soln_s_sum.append(soln_s_sum_item.strip())
        
        if latex(nums_sum) != nums_sum.mixed_form_latex():
            soln_s_sum_item = f"""
            $\\displaystyle = {latex(nums_sum)}$
            """
            soln_s_sum.append(soln_s_sum_item.strip())


        soln_s_sum_item = f"""
        <h3>Sum of fractions:</h3>
        
        $\\displaystyle {fracs_sum_list_str}$
        
        {fracs_list_soln_statement}
        """
        soln_s_sum.append(soln_s_sum_item.strip())
        
        soln_s_sum = "\n\n".join(soln_s_sum)
        problemTemplate.add_solution(soln_s_sum.strip(), 'text/html')




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

    def _problem_mixed_numeral_prod(self, *args, **kwargs):
        rn_helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        size = kwargs.get('size', 4)
        
        nums = rn_helper.get_random_mixed_numbers(size = size)
        nums_prod = Rational(Mul(*nums))

        nums_mixed_form = rn_helper.convert_to_mixed_form_latex(nums)
        
        nums_mixed_form_str = f" ,\\; ".join(nums_mixed_form)
        
        prod_mixed_form_str = f" \\cdot ".join(nums_mixed_form)
        prod_str = f" \\cdot ".join([latex(x) for x in nums])
        
        
        statement = f"""
        Compute product of the following numbers:

        $\\displaystyle {nums_mixed_form_str}$
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        

        soln_s_prod = []
        soln_s_prod_item = f"""
        
        $\\displaystyle {prod_mixed_form_str}$

        $\\displaystyle = {prod_str}$
        
        $\\displaystyle = {latex(nums_prod)}$
        """
        soln_s_prod.append(soln_s_prod_item.strip())

        
        if latex(nums_prod) != nums_prod.mixed_form_latex():
            soln_s_prod_item = f"""
            $\\displaystyle = {nums_prod.mixed_form_latex()}$
            """
            soln_s_prod.append(soln_s_prod_item.strip())
        
        soln_s_prod = "\n\n".join(soln_s_prod)
        problemTemplate.add_solution(soln_s_prod.strip(), 'text/html')

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

    def _problem_mixed_numeral_division(self, *args, **kwargs):
        rn_helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
                
                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        size = 2
        
        nums = rn_helper.get_random_mixed_numbers(size = size, mixed_form = True)
        nums_mixed_form = rn_helper.convert_to_mixed_form_latex(nums)
        nums_mixed_form_str = f" ,\\; ".join(nums_mixed_form)

        nums_mixed_form_div_str = latex_fraction(nums[0].mixed_form_latex(),
                                             nums[1].mixed_form_latex()
                                             )
        
        mixed_nums_calc_str = latex_fraction(nums[0].mixed_calc_latex(),
                                             nums[1].mixed_calc_latex()
                                             )
        
        nums_div_str = latex_fraction(latex(nums[0]), latex(nums[1]))
        
        nums_simp_div_str = latex_fraction(f"{nums[0].p} \\cdot {nums[1].q}", 
                                           f"{nums[0].q} \\cdot {nums[1].p}")
        
        nums_division = Rational(nums[0] / nums[1])
        
        statement = f"""
        Divide the first number by the second.

        $\\displaystyle {nums_mixed_form_str}$
        """
        problemTemplate.add_problem(statement.strip(), "text/html")

        if latex(nums_division) != nums_division.mixed_form_latex():
            last_step_latex = f"""
            \\\\ \\\\
    
            & = {nums_division.mixed_form_latex()}
            """
        else:
            last_step_latex = ""
            


        soln_s_prod = []
        soln_s_prod_item = f"""
        
        $\\displaystyle 
        \\begin{{aligned}}

        \\large{{ {nums_mixed_form_div_str} }}
        
        &  = \\Large {{ {mixed_nums_calc_str}  }}
        \\\\ \\\\
        
        & = \\large{{ {nums_div_str} }}
        \\\\ \\\\
        
        & = {nums_simp_div_str}
        \\\\ \\\\
        
        & = {latex(nums_division)}
        
        {last_step_latex}
        
        \\end{{aligned}}
        $
        """
        soln_s_prod.append(soln_s_prod_item.strip())

        
        soln_s_prod = "\n\n".join(soln_s_prod)
        problemTemplate.add_solution(soln_s_prod.strip(), 'text/html')


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



    def _problem_fractions_from_competitions(self, *args, **kwargs):
        '''
        '''
        
        
        rn_helper = self.base_helper.get_arithmetic_helper().get_rational_number_helper()
        
        result = rn_helper.get_problem_fractions_from_competitions(**kwargs)

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        {result['prob_s']}
        """
        problemTemplate.add_problem(prob_s.strip(), "text/html")

        
        soln_s = f"""
        {result['soln_s']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


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



    def _problem_rational_number_questions_easy(self, *args, **kwargs):
        """
        
        """
        
        folder_path = "question_bank/math/arithmetic"
        search_file_paths = [
                "rational_number_questions_easy.txt"
            ]
        
        file_paths = self.get_q_bank_factory().search_question_bank_files(folder_path, search_file_paths)
        return self.get_q_bank_factory().PROBLEM_TEMPLATE_Q_BANK_RANDOM_QUESTION(file_paths, *args, **kwargs)



        