'''
Created on 02-Mar-2021

@author: bthakur
'''
import numpy as np
import random
import inspect
import traceback

from sympy.simplify.simplify import nsimplify
from xv.util import toIntOrFloat



from xv.util import list_to_str

from sympy import (simplify, sympify, log, latex, Symbol, symbols, 
                   Rational, sqrt,
                   UnevaluatedExpr as ueval)

from xv.km.util import latex_fraction
from xv.km.kelements import KPrintElement
        
from ..helper import BaseHelper
from xv.km.managers import BaseManager
from xv.km.kelements import KProblemTemplate

# _grade8_manager
# Grade6Manager
        
class Grade6Manager(BaseManager):
    '''
     Grade 6 (class 6) math
    Example:
        ke = Grade6Manager()
        ke.getRandomProblem()
        ke.getRandomProblem(problem_type = 0)
        ke.getRandomProblem(problem_type = 1)
        ...
        ke.printProblem()
        ke.printAnswer()
        ke.printSolution()

        ke.printProblemTypes()
    '''    
    def __new__(cls, *args, **kwargs):
        instance = super(Grade6Manager, cls).__new__(cls, *args, **kwargs)
        for base_class in Grade6Manager.__bases__:
            base_class.__init__(instance, *args, **kwargs)
        return instance
    
    def __init__(self, *args, **kwargs):
        self.base_helper = BaseHelper(**kwargs)
        pass

    def _TEMPLATE_CUSTOM_QUESTIONS(self, keywords, **kwargs):
        
        from ..storeddata import Grade6StoredQuestions
        
        stored_questions = Grade6StoredQuestions(verbose = self.verbose)
        result = stored_questions.get_random_question(keywords)

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Failed to get questions
            keywords = {keywords}
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        
        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
        pass


    def _init_problem_types(self, *args, **kwargs):
        '''
        '_problem_custom_questions_algebric_equation',
        '_problem_custom_questions_surface_area', 
        '_problem_custom_questions_median', 
        '_problem_custom_questions_statistics', 
        '_problem_custom_questions_ratio',
        '_problem_custom_questions_percentage',
        '_problem_custom_questions_equations',
        '_problem_custom_questions_find_the_value',
        '_problem_custom_questions_complete_the_table',
        '_problem_custom_questions_equivalent_ratios',
        '_problem_custom_questions_arithmetic_operation',
        '_problem_custom_questions_inequality',
        '_problem_custom_questions_frequency_table',
        '_problem_custom_questions_plotting',
        '_problem_custom_questions_inequality_2',
        '_problem_custom_questions_distributive_property_over_addition',
        '_problem_custom_questions_power',
        '_problem_custom_questions_lcm',
        '_problem_custom_questions_describe_the_inequality',
        '_problem_custom_questions_differentiation',
        '_problem_custom_questions_plot_the_point',
        '_problem_custom_questions_identify_the_shape',
        '_problem_custom_questions_mean_absolute_deviation',
        '_problem_custom_questions_clusters',
        '_problem_custom_questions_division',
        '_problem_custom_questions_coordinate',
        '_problem_custom_questions_histogram',

        '''
        
        self._append_problem_types([
            
            
            '_problem_equivalent_ratios',
            '_problem_measurement_of_a_quantity',
            '_problem_percent',
            '_problem_quantity',
            '_problem_rate',
            '_problem_ratio',
            '_problem_ratio_relationship',
            '_problem_type_of_unit_systems',
            '_problem_unit_rate',
            '_problem_greatest_common_factor',
            '_problem_least_common_multiple',
            '_problem_multiplicative_inverse',
            '_problem_absolute_value',
            '_problem_integer',
            '_problem_magnitude',
            '_problem_negative_number',
            '_problem_opposite',
            '_problem_positive_number',
            '_problem_quadrant',
            '_problem_rational_number',
            '_problem_equation',
            '_problem_equivalent_expressions',
            '_problem_exponential_notation_for_whole_number_exponents',
            '_problem_expression',
            '_problem_linear_expression',
            '_problem_number_sentence',
            '_problem_truth_values_of_a_number_sentence',
            '_problem_variable',
            '_problem_area_of_basic_shapes',
            '_problem_cube',
            '_problem_regular_polygons',
            '_problem_geometrical_2d_shapes',
            '_problem_geometrical_3d_shapes',
            '_problem_line_perpendicular_to_a_plane',
            '_problem_net',
            '_problem_parallel_planes',
            '_problem_pentagon',
            '_problem_right_rectangular_prism',
            '_problem_surface_of_a_prism',
            '_problem_triangular_region',
            '_problem_absolute_deviation',
            '_problem_box_plot',
            '_problem_dot_plot',
            '_problem_frequency',
            '_problem_frequency_table',
            '_problem_histogram',
            '_problem_interquartile_range_iqr',
            '_problem_mean',
            '_problem_mean_absolute_deviation_mad',
            '_problem_median',
            '_problem_relative_frequency',
            '_problem_relative_frequency_histogram',
            '_problem_relative_frequency_table',
            '_problem_statistical_question',
            '_problem_variability',


         ])
        pass

    def _problem_custom_questions_algebric_equation(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'algebric_equation')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)
    
    def _problem_custom_questions_surface_area(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'surface_area')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)
    
    def _problem_custom_questions_median(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'median')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)

    def _problem_custom_questions_statistics(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'statistics')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)

    def _problem_custom_questions_ratio(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'ratio')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)

    def _problem_custom_questions_percentage(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'percentage')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)

    def _problem_custom_questions_equations(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'equations')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)

    def _problem_custom_questions_find_the_value(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'algebric_expression')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)
    
    def _problem_custom_questions_complete_the_table(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'complete_the_table')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)

    def _problem_custom_questions_equivalent_ratios(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'equivalent_ratios')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     

    def _problem_custom_questions_arithmetic_operation(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'arithmetic_operation')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     

    def _problem_custom_questions_inequality(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'inequality')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     
    
    def _problem_custom_questions_frequency_table(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'frequency_table')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     
    
    def _problem_custom_questions_plotting(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'plotting')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     

    def _problem_custom_questions_inequality_2(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'inequality_2')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     

    def _problem_custom_questions_distributive_property_over_addition(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'distributive_property_over_addition')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     

    def _problem_custom_questions_power(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'power')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)  
       
    def _problem_custom_questions_lcm(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'lcm')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     

    def _problem_custom_questions_describe_the_inequality(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'describe_the_inequality')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     

    def _problem_custom_questions_differentiation(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'differentiation')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     

    def _problem_custom_questions_plot_the_point(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'plot_the_point')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     

    def _problem_custom_questions_identify_the_shape(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'identify_the_shape')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     

    def _problem_custom_questions_mean_absolute_deviation(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'mean_absolute_deviation')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     

    def _problem_custom_questions_clusters(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'clusters')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     

    def _problem_custom_questions_division(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'division')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs) 
        
    def _problem_custom_questions_coordinate(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'coordinate')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     

    def _problem_custom_questions_histogram(self, *args, **kwargs):
        keywords = kwargs.get('keywords', 'histogram')
        return self._TEMPLATE_CUSTOM_QUESTIONS(keywords, **kwargs)     
    







    def _problem_equivalent_ratios(self, *args, **kwargs):
        """
        equivalent ratios
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        while True:
            num = Rational(random.randint(1, 9), random.randint(1, 9))
            if num.q != 1:
                break
        
        er_expanded_latexes = []
        er_latexes = []
        
        for i in range(2, 7):
            er_expanded_latex = latex_fraction(f"{num.p} \\cdot {i}", f"{num.q} \\cdot {i}")
            
            er_expanded_latexes.append(er_expanded_latex)
            
            er_latex = latex_fraction(num.p * i, num.q * i)
            er_latexes.append(er_latex)
            pass
        
        er_latexes = list_to_str(er_latexes, sep = ", \\quad")
        er_expanded_latexes = list_to_str(er_expanded_latexes, sep = ", \\quad")
        
        
        prob_s = f"""
        Write an equivalence ratio of $\\displaystyle {latex(num)}$.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        Two ratios A:B and C:D are equivalent ratios if there is a nonzero number n such that
        C = nA and D = nB. 
        Two ratios are equivalent if their resultant values are equal.
        
        We can multiply numerator and denominator both with a number and get equivalent ratios
        
        $\\displaystyle {latex(num)}$
        
        $\\displaystyle = {er_expanded_latexes}$
        
        $\\displaystyle = {er_latexes}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

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




    def _problem_measurement_of_a_quantity(self, *args, **kwargs):
        """
        Units of measurement
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        nu_helper = self.base_helper.get_common_helper().get_number_unit_helper()
        conversion_category = nu_helper.get_random_conversion_category()
        
        units = nu_helper.get_units(conversion_category)
        units_str = list_to_str(sorted(units))
 
        conversion_set = nu_helper.get_conversion_set(conversion_category)
        conversion_set_str = nu_helper.get_conversion_set(conversion_category, output_type = 'str')
        
        
        prob_s = f"""
        Give examples of measurement units of {conversion_category.lower()}.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        {conversion_category.capitalize()} can be measured as multiple of any of the following units of measurement. 
        
        {units_str}
        
        The related conversion is table is:
        
        {conversion_set_str} 
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        answer_s = f"""

        {units_str} 
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass




    def _problem_percent(self, *args, **kwargs):
        """
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        one_hundredth = Rational(1, 100)
        
        num = random.randint(1, 10) * 10 ** random.randint(1, 5)
        
        percent_num = random.randint(1, 100)
        
        hundred_percent_frac = latex_fraction(1, f"100\\%")
        
        from sympy import nsimplify
        
        calc_num = nsimplify(num * percent_num / 100 , rational=True)
        
        prob_s = f"""
        What is $\\displaystyle {percent_num}\\% \\text{{ of }} {num}$?
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        100% (read as one hundred percent) is equal to 1. 
        So, 1% is equal to $\\displaystyle {one_hundredth}$.
        It is used to express as a unit of measurement of something in percent multiples.
        
        $\\displaystyle {percent_num}\\% \\text{{ of }} {num}$
        
        'of' is basically multiplier symbol in this case.
        
        $\\displaystyle = {percent_num}\\% \\cdot {num}$
        
        As 100% = 1, dividing by 100% won't change the value of the expression.
        
        $\\displaystyle = {percent_num}\\% \\cdot {num} \\cdot {hundred_percent_frac}$
        
        $\\displaystyle = {percent_num} \\cdot {num} \\cdot \\frac{{1}}{{100}}$
        
        $\\displaystyle = {latex(calc_num)}$
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

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




    def _problem_quantity(self, *args, **kwargs):
        """
        Examples of a quantity include a length, an area, a volume, a mass, a weight, a length of time, or a speed. Each example is an instance of a type of quantity. All quantities of the same type have the properties that (1) two quantities can be compared, (2) two quantities can be combined to get a new quantity of that same type, and (3) there always exists a quantity that is a multiple of any given quantity. These properties help define ways to measure quantities using a standard quantity called a unit of measurement.
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        nu_helper = self.base_helper.get_common_helper().get_number_unit_helper()
        conversion_units = nu_helper.get_conversion_units()
        categories = sorted(list(conversion_units.keys()))
        
        categories_str = list_to_str(categories, sep = "\n")
        
        prob_s = f"""
        Give example of a quantity that we can measure.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        Some of examples of quantities that we can measure are:
        {categories_str}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        answer_s = f"""
        Some of examples of quantities that we can measure are:
        {categories_str}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass




    def _problem_rate(self, *args, **kwargs):
        """
         A rate is a quantity that describes a ratio relationship between two types of quantities. 
         
         For example, is a rate that describes a ratio relationship between hours and miles: 
         If an object is traveling at a constant , 
         then after 1 hour it has gone 1.25 miles, 
         after 2 hours it has gone 2.50 miles, 
         after 3 hours it has gone 3.75 miles, and so on. 
         
         Rates differ from ratios in how they describe ratio relationships-rates are quantities 
         and have the properties of quantities. 
         
         For example, rates of the same type can be added together to get a new rate, as in , 
         whereas ratios cannot be added together.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        

        from ..algebra import RationalNumberManager
        ke_manager = RationalNumberManager(verbose = self.verbose)
        
        problem_types = [                
            '_problem_int_qty',
            '_problem_fraction_qty_unit_price_100',
            '_problem_fraction_qty',
            ]
        
        problem_type = random.choice(problem_types)
        problemTemplate = ke_manager.initProblemTemplate(problem_type = problem_type) 
        
        return problemTemplate
        pass




    def _problem_ratio(self, *args, **kwargs):
        """
         A ratio is an ordered pair of numbers that are not both zero. 
         A ratio is denoted A:B to indicate the order of the numbers-the number A is first 
         and the number B is second.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        while True:
            num = Rational(random.randint(1, 9), random.randint(1, 9))
            if num.q != 1:
                break
        
        er_expanded_latexes = []
        er_latexes = []
        
        i = random.randint(2, 7)
        
        p_comp = num.p * i
        q_comp = num.q * i
        
        num_comp_expanded_latex = latex_fraction(f"{num.p} \\cdot {i}", f"{num.q} \\cdot {i}")
        num_comp_latex = latex_fraction(num.p * i, num.q * i)
        
        
        prob_s = f"""
        You have {p_comp} chocolates and your friend has {q_comp} chocolates.
        What is the ratio of the your and your friend's chocolates?
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        
        Ratio of the your and your friend's chocolates
        
        = $\\displaystyle {p_comp} : {q_comp}$
        
        = $\\displaystyle {num_comp_latex}$
        
        = $\\displaystyle {num_comp_expanded_latex}$
        
        After cancellation:
        
        $\\displaystyle = {latex(num)}$
        
        $\\displaystyle = {num.p} : {num.q}$
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        answer_s = f"""
        $\\displaystyle {num.p} : {num.q}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass




    def _problem_ratio_relationship(self, *args, **kwargs):
        """
        A ratio relationship is the set of all ratios that are equivalent ratios. A ratio such as 5:4 can be used to describe the ratio relationship . Ratio language such as "5 miles for every 4 hours" can also be used to describe a ratio relationship. Ratio relationships are often represented by ratio tables, double number line diagrams, and equations and their graphs.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        while True:
            num = Rational(random.randint(1, 9), random.randint(1, 9))
            if num.q != 1:
                break
        
        er_expanded_latexes = []
        er_latexes = []
        
        common_ratios = list(range(2, 7))
        random.shuffle(common_ratios)
        
        for i in common_ratios:
            er_expanded_latex = latex_fraction(f"{num.p} \\cdot {i}", f"{num.q} \\cdot {i}")
            
            er_expanded_latexes.append(er_expanded_latex)
            
            er_latex = latex_fraction(num.p * i, num.q * i)
            er_latexes.append(er_latex)
            pass
        
        er_latexes = list_to_str(er_latexes, sep = ", \\quad")
        er_expanded_latexes = list_to_str(er_expanded_latexes, sep = ", \\quad")
        
        
        prob_s = f"""
        Find simplified ratio of the followings:
        
        $\\displaystyle {er_latexes}$
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        $\\displaystyle {er_latexes}$

        $\\displaystyle = {er_expanded_latexes}$
        
        After cancellation, each of them is equal to $\\displaystyle {latex(num)}$.
        
        Therefore, the simplified ratio of each of them is:
        
        $\\displaystyle {latex(num)}$
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

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




    def _problem_type_of_unit_systems(self, *args, **kwargs):
        """
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        from xv.km.util import PintUnitSystem
        ps = PintUnitSystem(verbose = self.verbose)
        
        unit_system_names = ps.get_unit_system_names()
        unit_system_names_str = list_to_str(unit_system_names, sep = "\n") 
        
        prob_s = f"""
        List some types of unit systems.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

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

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




    def _problem_unit_rate(self, *args, **kwargs):
        """
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        item_names = ["item"]
        item_name = random.choice(item_names)
        
        qty = Rational(random.randint(1, 9), random.randint(1, 9))
        
        cost = nsimplify(qty.p * random.randint(1, 9) * 10 ** random.randint(1, 5))
        
        rate_latex = latex_fraction(latex(cost), latex(qty))
        
        rate = nsimplify(cost / qty)
        
        prob_s = f"""
        Find rate of an {item_name} if $\\displaystyle 
        {qty} $ {item_name} costs $\\displaystyle {cost} $.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        
        Rate of an {item_name}
        
        $\\displaystyle = {qty} \\; {item_name} = {cost} $
        
        Divide both sides with $\\displaystyle {qty}$
        
        $\\displaystyle \\implies 1 \\; {item_name} = {rate_latex} $
                
        $\\displaystyle \\implies 1 \\; {item_name} = {latex(rate)} $
                
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

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


    def _problem_greatest_common_factor(self, *args, **kwargs):
        """
        The greatest common factor (GCF) of two whole numbers (not both zero) is the greatest whole number that is a factor of each number. For example, the GCF of 24 and 36 is 12 because when all of the whole number factors of 24 and 36 are listed, the largest factor they share is 12.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        

        from ..basicmaths import LcmManager
        ke_manager = LcmManager(verbose = self.verbose)
        
        problem_types = [                
            '_problem_lcm_by_integer_multiples',
            '_problem_lcm_by_prime_factors',
            '_problem_lcm_by_common_divisors',
            '_problem_lcm_hcf_pair_relations',
            ]
        
        problem_type = random.choice(problem_types)
        problemTemplate = ke_manager.initProblemTemplate(problem_type = problem_type) 
        
        return problemTemplate
        pass




    def _problem_least_common_multiple(self, *args, **kwargs):
        """
        The least common multiple (LCM) of two whole numbers is the smallest whole number greater than zero that is a multiple of each number. For example, the LCM of 4 and 6 is 12 because when the multiples of 4 and 6 are listed, the smallest or first multiple they share is 12.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        

        from ..basicmaths import LcmManager
        ke_manager = LcmManager(verbose = self.verbose)
        
        problem_types = [                
            '_problem_hcf_by_prime_factors',
            '_problem_hcf_by_common_divisors',
            '_problem_lcm_hcf_pair_relations',
            ]
        
        problem_type = random.choice(problem_types)
        problemTemplate = ke_manager.initProblemTemplate(problem_type = problem_type) 
        
        return problemTemplate
        pass




    def _problem_multiplicative_inverse(self, *args, **kwargs):
        """
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        num = Rational(random.randint(1, 9), random.randint(1, 9))
        
        num_inverse = Rational(num.q, num.p)
        
        prob_s = f"""
        Find multiplicative inverse of $\\displaystyle {latex(num)}$.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        As $\\displaystyle {latex(num)} \\cdot {latex(num_inverse)} = 1 $.
        
        Therefore, multiplicative inverse of $\\displaystyle {latex(num)} = {latex(num_inverse)}$.
        
        Note:
        A multiplicative inverse of a number is a number such that the product of both numbers is 1. 
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

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




    def _problem_absolute_value(self, *args, **kwargs):
        """
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        num = Rational(random.randint(1, 9), random.randint(1, 9)) * random.choice([1, -1])
        
        num_abs = abs(num)
        
        prob_s = f"""
        Find absolute value of $\\displaystyle {latex(num)}$.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        Absolute value is always positive.
        
        Therefore, absolute value of $\\displaystyle {latex(num)} = {latex(num_abs)}$.
        
        Note:
        The absolute value of a number is the distance between the number and zero on the number line.
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

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





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

        common_ratio = random.randint(1, 9)
        q = random.randint(1, 9) * common_ratio
        p = random.choice([q, random.randint(1, 9)]) * common_ratio
        
        num_latex = latex_fraction(p, q)
        
        num = Rational(p, q) 
        num = num * random.choice([1, -1])
        
        prob_s = f"""
        Is $\\displaystyle {num_latex}$ an integer?
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        The set of integers is the infinite list of numbers: 
        $ \\cdots, -3, -2, -1, 0, 1, 2, 3,  \\cdots $
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        if common_ratio != 1:
            soln_s = f"""
            Simplify the number:
            $\\displaystyle {num_latex} = {latex(num)}$
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')


        if num == int(num):
            answer_s = f"""
            $\\displaystyle {num_latex}$ is an integer.
            """
        else:
            answer_s = f"""
            $\\displaystyle {num_latex}$ is not an integer.
            """
            
        soln_s = f"""
        Therefore, {answer_s.strip()}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass




    def _problem_magnitude(self, *args, **kwargs):
        """
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        num = Rational(random.randint(1, 9), random.randint(1, 9)) * random.choice([1, -1])
        
        num_abs = abs(num)
        
        unit_names = ['meter', 'kg', 'gram', 'second']
        unit_name = random.choice(unit_names)
        
        prob_s = f"""
        Find magnitude of $\\displaystyle {latex(num)}$ {unit_name}.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        
        Magnitude of $\\displaystyle {latex(num)}$ {unit_name}
        
        = Absolute value of $\\displaystyle {latex(num)}$ 
        
        = $\\displaystyle {latex(num_abs)}$.
        
        Note:
        The magnitude of a measurement is the absolute value of the measure of the measurement. 
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

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





    def _problem_negative_number(self, *args, **kwargs):
        """
        A negative number is a number less than zero.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')


        common_ratio1 = random.randint(1, 9)
        q1 = random.randint(1, 9) * common_ratio1
        p1 = random.choice([q1, random.randint(1, 9)]) * common_ratio1
        num1_latex = latex_fraction(p1, q1)
        num1 = Rational(p1, q1)
        
        common_ratio1 = p1 // num1.p
        num1_expanded_latex = latex_fraction(f"{num1.p} \\cdot {common_ratio1}", 
                                             f"{num1.q} \\cdot {common_ratio1}")
        
        common_ratio2 = random.randint(2, 9)
        q2 = random.randint(2, 9) * common_ratio2
        p2 = random.choice([q2, random.randint(2, 9)]) * common_ratio2
        num2_latex = latex_fraction(p2, q2)
        num2 = Rational(p2, q2)

        common_ratio2 = p2 // num2.p
        num2_expanded_latex = latex_fraction(f"{num2.p} \\cdot {common_ratio2}", 
                                             f"{num2.q} \\cdot {common_ratio2}")
        
        prob_s = f"""
        Is $\\displaystyle 
        {num1_latex} - {num2_latex}
        $ a negative number?
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        $\\displaystyle {num1_latex} - {num2_latex} $ 
        
        $\\displaystyle = {num1_expanded_latex} - {num2_expanded_latex} $ 
        
        After canceling,
        
        $\\displaystyle = {latex(num1)} - {latex(num2)} $ 
        
        $\\displaystyle = {latex(num1 - num2)} $ 
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        if num1 - num2 < 0:
            answer_s = f"""
            $\\displaystyle {num1_latex} - {num2_latex}$ is a negative number.
            """
        else:
            answer_s = f"""
            $\\displaystyle {num1_latex} - {num2_latex}$ is not a negative number.
            """
            
        soln_s = f"""
        Therefore, {answer_s.strip()}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass




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


        common_ratio = random.randint(1, 9)
        q = random.randint(1, 9) * common_ratio
        p = random.choice([q, random.randint(0, 9)]) * common_ratio * random.choice([1, -1])
        
        num_latex = latex_fraction(p, q)
        opp_num_latex = latex_fraction(-p, q)
        
        prob_s = f"""
        Find the opposite number of $\\displaystyle {num_latex}$.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        To find the opposite number of $\\displaystyle 
        {num_latex}$, change its sign.
        
        Opposite number of $\\displaystyle {num_latex}$
        
        $\\displaystyle = - {num_latex}$
        
        $\\displaystyle = {opp_num_latex}$

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

            
        answer_s = f"""
        $\\displaystyle {opp_num_latex}$
        """

        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass




    def _problem_positive_number(self, *args, **kwargs):
        """
        A positive number is a number greater than zero.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        


        common_ratio1 = random.randint(1, 9)
        q1 = random.randint(1, 9) * common_ratio1
        p1 = random.choice([q1, random.randint(1, 9)]) * common_ratio1
        num1_latex = latex_fraction(p1, q1)
        num1 = Rational(p1, q1)
        
        common_ratio1 = p1 // num1.p
        num1_expanded_latex = latex_fraction(f"{num1.p} \\cdot {common_ratio1}", 
                                             f"{num1.q} \\cdot {common_ratio1}")
        
        common_ratio2 = random.randint(2, 9)
        q2 = random.randint(2, 9) * common_ratio2
        p2 = random.choice([q2, random.randint(2, 9)]) * common_ratio2
        num2_latex = latex_fraction(p2, q2)
        num2 = Rational(p2, q2)

        common_ratio2 = p2 // num2.p
        num2_expanded_latex = latex_fraction(f"{num2.p} \\cdot {common_ratio2}", 
                                             f"{num2.q} \\cdot {common_ratio2}")
        
        prob_s = f"""
        Is $\\displaystyle 
        {num1_latex} - {num2_latex}
        $ a positive number?
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        $\\displaystyle {num1_latex} - {num2_latex} $ 
        
        $\\displaystyle = {num1_expanded_latex} - {num2_expanded_latex} $ 
        
        After canceling,
        
        $\\displaystyle = {latex(num1)} - {latex(num2)} $ 
        
        $\\displaystyle = {latex(num1 - num2)} $ 
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        if num1 - num2 > 0:
            answer_s = f"""
            $\\displaystyle {num1_latex} - {num2_latex}$ is a positive number.
            """
        else:
            answer_s = f"""
            $\\displaystyle {num1_latex} - {num2_latex}$ is not a positive number.
            """
            
        soln_s = f"""
        Therefore, {answer_s.strip()}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate


    def _problem_quadrant(self, *args, **kwargs):
        """
        In the Cartesian plane, the two axes separate the plane into four regions called quadrants. 
        The first quadrant consists of all the points whose x- and y-coordinates are both positive.
        The first, second, third, and fourth quadrants are identified counterclockwise around 
        the origin in order starting with the first quadrant.
        """
        
        plotting_helper = self.base_helper.get_plotting_helper()
        
        point = (random.randint(-9, 9), random.randint(-9, 9))
        
        if point[0] > 0 and point[1] > 0:
            quadrant = "1"
        elif point[0] < 0 and point[1] > 0:
            quadrant = "2"
        elif point[0] < 0 and point[1] < 0:
            quadrant = "3"
        elif point[0] > 0 and point[1] < 0:
            quadrant = "4"
        else:
            quadrant = "None"
        
        svg_str = plotting_helper.plot_quadrants(point)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        Plot the point {point} and find the quadrant it is located in.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        Quadrant {quadrant}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')

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




    def _problem_rational_number(self, *args, **kwargs):
        """
        
        """
        
        data = {}
        irr_nums = [sqrt(ueval(2)),
                    sqrt(ueval(5)),
                    sqrt(ueval(7)),
                    sqrt(ueval(11)),
                    ]
        data[latex(random.choice(irr_nums))] = False
        
        neg_num = ueval(random.randint(-9, -1))
        data[latex(sqrt(neg_num))] = False

        import sympy 
        
        irr_entities = [latex(sympy.pi) * random.choice([1, -1]),
                       ]
        data[random.choice(irr_entities)] = False
        data[latex_fraction(random.randint(-5, 5) , 0)] = False
        
        rat_num = random.randint(1, 100) * 10 ** random.randint(-5, 5) * random.choice([1, -1])
        rat_num = toIntOrFloat(rat_num)
        data[latex(rat_num)] = True
        
        rat_num = random.randint(1, 100) * 10 ** random.randint(-4, -3)
        rec_num = random.randint(1, 100) 
        rec_num = f"{latex(rat_num)}\\overline{{{latex(rec_num)}}}"
        data[rec_num] = False
        
        common_ratio1 = random.randint(1, 9)
        q1 = random.randint(1, 9) * common_ratio1
        p1 = random.choice([q1, random.randint(1, 9)]) * common_ratio1 * random.choice([1, -1])
        num1_latex = latex_fraction(p1, q1)
        data[num1_latex] = True
        
        
        data_keys = list(data.keys())
        random.shuffle(data_keys)
        data_keys_str = list_to_str(data_keys, sep = ", \\quad")
        
        rational_keys = [k for k, v in data.items() if v]
        
        rational_keys = [k for k in data_keys if k in rational_keys]
        
        rational_keys_str = list_to_str(rational_keys, sep = ", \\quad")
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        Which of these are rational numbers?
        
        $\\displaystyle {data_keys_str}$
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        $\\displaystyle {rational_keys_str}$
        
        Note:
        A rational number is a number that can be represented as a fraction or 
        the opposite of a fraction. In other words, they can be expressed in 
        p/q form where p and q are integers. Rational numbers can be negative or positive.
        
        The following are not rational numbers:
        $\\displaystyle {latex(sympy.pi)}$, 
        square roots of prime integers,
        square roots of negative integers, 
        recurring numbers
        any number divided by zero
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

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




    def _problem_equation(self, *args, **kwargs):
        """
         An equation is a statement of equality between two expressions.
        
        """
        
        from xv.math.algebra import SingleVariableManager
        ke = SingleVariableManager(verbose = self.verbose)
        problem_types = [
                '_problem_add',
                '_problem_subtract',
                '_problem_multiply',
                '_problem_divide',
                '_problem_solve_equations_easy_level_1',
            ]
        
        problem_type = random.choice(problem_types)
        kwargs['problem_type'] = problem_type
        problemTemplate = ke.getProblemTemplate(*args, **kwargs)
        
        return problemTemplate



    def _problem_equivalent_expressions(self, *args, **kwargs):
        """
        Two expressions are equivalent if both expressions evaluate to the same number 
        for every substitution of numbers into all the variables in both expressions.
        
        """
        x = symbols('x')
        n1 = random.randint(-9, -1)
        n2 = random.randint(1, 9)
        n3 = random.randint(1, 5)
        
        data = []
        expr1 = f"{latex(n1)}({latex(n2)}{latex(x)} - {latex(n3)})"
        expr2 = f"{latex(n2 * n1)}{latex(x)} - {latex(n3 * n1)}"
        data.append([expr1, expr2])
        
        expr1 = f"{latex(x)}({latex(n2)} \cdot {latex(n1)} - {latex(n3)})"
        expr2 = f"{latex(n2 * n1)}{latex(x)} - {latex(n3 * x)}"
        data.append([expr1, expr2])
        
        expr1 = f"{latex(x)}({latex(n1)} + {latex(n2)} - {latex(n3)})"
        expr2 = f"{latex(n2 - n3)}{latex(x)} - {latex(n2 * x)}"
        data.append([expr1, expr2])
        
        
        idx = random.randint(0, len(data) - 1)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        Which expression is equal to $\\displaystyle {data[idx][0]}$?
        
        $\\displaystyle {data[0][1]}$
        
        $\\displaystyle {data[1][1]}$

        $\\displaystyle {data[2][1]}$
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        $\\displaystyle {data[idx][1]}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        answer_s = f"""
        $\\displaystyle {data[idx][1]}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass



    def _problem_exponential_notation_for_whole_number_exponents(self, *args, **kwargs):
        """
        Let m be a nonzero whole number. 
        For any number a, the expression am is the product of m factors of a (i.e., ). 
        The number a is called the base, and m is called the exponent or power of a.
        """
        
        from xv.math.basicmaths import PowerManager
        ke = PowerManager(verbose = self.verbose)
        problem_types = [
                '_problem_concept_of_power',
                '_problem_express_as_power',
                '_problem_express_as_power_of_pairs',
                '_problem_express_as_power_as_large_series',
                '_problem_express_as_exp_of_base',
                '_problem_express_pair_as_exp_of_base',
                '_problem_exponent_to_root_form',
                '_problem_root_to_exponent_form',
            ]
        
        problem_type = random.choice(problem_types)
        kwargs['problem_type'] = problem_type
        problemTemplate = ke.getProblemTemplate(*args, **kwargs)
        
        return problemTemplate
        pass


    def _problem_expression(self, *args, **kwargs):
        """
         An expression is a numerical expression, 
         or it is the result of replacing some (or all) of the numbers 
         in a numerical expression with variables.
         
         
        """
        
        from xv.math.basicmaths import ArithmeticOperationManager
        ke = ArithmeticOperationManager(verbose = self.verbose)
        problem_types = [
            '_problem_learn_add',
            '_problem_learn_multiply',
            '_problem_learn_multiply_2',
            '_problem_learn_add_and_multiply',
            '_problem_learn_add_and_multiply_2',
            '_problem_express_add',
            '_problem_express_add_negative',
            '_problem_express_multiply',
            '_problem_add',
            '_problem_subtract',
            '_problem_multiply',
            '_problem_divide',
            '_problem_multiply_divide',
            '_problem_pow_zero',
            '_problem_pow_pos_base',
            '_problem_abs',
            '_problem_abs_advanced',

            ]
        
        problem_type = random.choice(problem_types)
        kwargs['problem_type'] = problem_type
        problemTemplate = ke.getProblemTemplate(*args, **kwargs)
        
        return problemTemplate
        pass




    def _problem_linear_expression(self, *args, **kwargs):
        """
        A linear expression is an expression that is equivalent to the sum or difference 
        of one or more expressions where each expression is either a number, a variable, 
        or a product of a number and a variable.
        """
        
        x = symbols('x')
        n1 = random.randint(-9, -1)
        n2 = random.randint(1, 9)
        n3 = random.randint(1, 5)
        
        data = []
        expr2 = f"{latex(n2 * n1)}{latex(x)} - {latex(n3 * n1)}"
        data.append([expr2, True])
        
        expr2 = f"{latex(n2 * n1)}{latex(x)} - {latex(n3 * x)}"
        data.append([expr2, True])
        
        expr2 = f"{latex(n2 - n3)}{latex(x)} - {latex(n2 * x)}"
        data.append([expr2, True])
        
        expr2 = f"{latex(n2 - n3)}{latex(x**2)} - {latex(n2 * x)}"
        data.append([expr2, False])
        
        expr2 = f"{latex(x**2)} + {latex(n2 - n3)}"
        data.append([expr2, False])
        
        expr2 = f"{latex(n2 - n3)} - {latex(1 / x**2)}"
        data.append([expr2, False])
        
        expr2 = f"{latex(n2 - n3)} + {latex(1 / x)}"
        data.append([expr2, False])
        
        random.shuffle(data)

        exprs = [item[0] for item in  data]
        exprs_str = list_to_str(exprs, sep = ", \\quad ")
        
        linear_exprs = [item[0] for item in  data if item[1]]
        linear_exprs_str = list_to_str(linear_exprs, sep = ", \\quad ")
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        Which of these are linear expressions?
        
        $\\displaystyle {exprs_str}$
        
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        $\\displaystyle {linear_exprs_str}$
        
        Note:
        In a linear expression, the power of variable must be either zero or one in each term.
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

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

    def _problem_number_sentence(self, *args, **kwargs):
        """
        A number sentence is a statement of equality between two numerical expressions.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass



    def _problem_truth_values_of_a_number_sentence(self, *args, **kwargs):
        """
        A number sentence is said to be true if both numerical expressions evaluate to the same number; it is said to be false otherwise. True and false are called truth values.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass


    def _problem_variable(self, *args, **kwargs):
        """
        
        """
        
        x, y, c, k = symbols('x, y, c, k')
        
        data = []
        
        expr = random.choice([x, y, c, k])
        data.append([-expr, False])
        data.append([1/expr, False])
        data.append([random.randint(2, 9) + expr, False])
        data.append([random.randint(2, 9) - expr, False])
        data.append([expr - random.randint(2, 9), False])
        data.append([expr * random.randint(2, 9), False])
        
        expr = random.randint(-10, 10)
        data.append([expr, False])
        
        expr = Rational(random.randint(-10, 10), random.randint(2, 9))
        data.append([expr, False])
        
        a, b = random.sample([x, y, c, k], 2)
        expr = random.choice([a+b, a-b, a/b, a*b])
        data.append([expr, False])



        data = random.sample(data, 3)
        
        expr = random.choice([x, y, c, k])
        data.append([expr, True])
        
        random.shuffle(data)


        exprs = [item[0] for item in  data]
        exprs_str = list_to_str(exprs, sep = ", \\quad ")
        
        var_exprs = [item[0] for item in  data if item[1]]
        var_exprs_str = list_to_str(var_exprs, sep = ", \\quad ")
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        
        prob_s = f"""
        Which of these are variables?
        
        $\\displaystyle {exprs_str}$
        
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        soln_s = f"""
        $\\displaystyle {var_exprs_str}$
        
        Note:
         A variable is a symbol (such as a letter) that is a placeholder for a number.
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

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




    def _problem_area_of_basic_shapes(self, *args, **kwargs):
        """
        
        """
        
        from xv.math.geometry import AreaManager
        ke = AreaManager(verbose = self.verbose)
        problem_types = [
            '_problem_area_rectangle',
            '_problem_area_square',
            '_problem_area_parallelogram',
            '_problem_area_triangle',
            '_problem_area_rhombus',
            '_problem_area_trapezoid',
            '_problem_area_sector_of_circle',
            '_problem_area_circle',
            '_problem_area_ellipse',
            '_problem_area_sphere',
            '_problem_area_cylinder',
            '_problem_area_cone',
            '_problem_area_prism',
            '_problem_area_cuboid',
            '_problem_area_cube',
            ]
        
        problem_type = random.choice(problem_types)
        kwargs['problem_type'] = problem_type
        problemTemplate = ke.getProblemTemplate(*args, **kwargs)
        
        return problemTemplate
        pass




    def _problem_cube(self, *args, **kwargs):
        """
         A cube is a right rectangular prism, all of whose edges are of equal length.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_regular_polygons(self, *args, **kwargs):
        """
        
        """
        from xv.math.kids import GeometricalShapeManager
        ke = GeometricalShapeManager(verbose = self.verbose)
        problem_types = [
            '_problem_plot_regular_polygon',
            ]
        
        problem_type = random.choice(problem_types)
        kwargs['problem_type'] = problem_type
        problemTemplate = ke.getProblemTemplate(*args, **kwargs)
        
        return problemTemplate
        pass




    def _problem_geometrical_2d_shapes(self, *args, **kwargs):
        """
        
        """
        from xv.plotter.managers import Plotting2DManager
        ke = Plotting2DManager(verbose = self.verbose)
        problem_types = [
            '_problem_plot_points',
            '_problem_plot_line_segment',
            '_problem_plot_circle',
            '_problem_plot_regular_polygon',
            '_problem_plot_polygon',
            '_problem_plot_triangle',
            '_problem_plot_quadrilateral',
            '_problem_plot_circular_line',
            '_problem_plot_pie_chart',
            ]
        
        problem_type = random.choice(problem_types)
        kwargs['problem_type'] = problem_type
        problemTemplate = ke.getProblemTemplate(*args, **kwargs)
        
        return problemTemplate
        pass


    def _problem_geometrical_3d_shapes(self, *args, **kwargs):
        """
        
        """
        from xv.plotter.managers import Plotting3DManager
        ke = Plotting3DManager(verbose = self.verbose)
        problem_types = [
            '_problem_plot_points',
            '_problem_plot_line_segment',
            '_problem_plot_ring',
            '_problem_plot_disk',
            '_problem_plot_cylinder',
            '_problem_plot_cone',
            '_problem_plot_sphere',
            '_problem_plot_rotated_sphere',
            '_problem_plot_sliced_sphere',
            '_problem_plot_cuboid',
            ]
        
        problem_type = random.choice(problem_types)
        kwargs['problem_type'] = problem_type
        problemTemplate = ke.getProblemTemplate(*args, **kwargs)
        
        return problemTemplate
        pass



    def _problem_line_perpendicular_to_a_plane(self, *args, **kwargs):
        """
        A line L in space that intersects a plane E at a point P is said to be perpendicular to the plane E if L is perpendicular to every line that (1) lies in E and (2) passes through the point P. A segment is said to be perpendicular to a plane if the line that contains the segment is perpendicular to the plane. In Grade 6, a line perpendicular to a plane can be described using a picture.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_net(self, *args, **kwargs):
        """
         If the surface of a 3-dimensional solid can be cut along sufficiently many edges so that the faces can be placed in one plane to form a connected figure, then the resulting system of faces is called a net of the solid.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_parallel_planes(self, *args, **kwargs):
        """
        Two planes in space are parallel if they do not intersect. In Euclidean geometry, a useful test for checking whether two planes are parallel is if the planes are different and if there is a line that is perpendicular to both planes.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_pentagon(self, *args, **kwargs):
        """
         Given 5 different points A, B, C, D, E in the plane, a 5-sided polygon, or pentagon, is the union of 5 segments , , , , such that (1) the segments intersect only at their endpoints, and (2) no two adjacent segments are collinear.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_right_rectangular_prism(self, *args, **kwargs):
        """
        Let E and E? be two parallel planes. Let B be a rectangular region1 in the plane E. At each point P of B, consider the segment PP? perpendicular to E, joining P to a point P? of the plane E?. The union of all these segments is called a right rectangular prism.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_surface_of_a_prism(self, *args, **kwargs):
        """
        The surface of a prism is the union of all of its faces-the base faces and lateral faces.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_triangular_region(self, *args, **kwargs):
        """
        A triangular region is the union of the triangle and its interior.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_absolute_deviation(self, *args, **kwargs):
        """
        deviation is the distance of a data value from the mean of the data set.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_box_plot(self, *args, **kwargs):
        """
        A box plot is a graphical representation of five numerical summary measures: the minimum, lower quartile, median, upper quartile, and maximum. It conveys information about center and variability in a data set.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_dot_plot(self, *args, **kwargs):
        """
        A dot plot is a plot of numerical data along a number line. Data values are represented by a dot placed in a column directly above where the data value is located on the number line.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_frequency(self, *args, **kwargs):
        """
         A frequency associated with an interval used to construct a summary table or a histogram is the number of data values that are included in the interval.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_frequency_table(self, *args, **kwargs):
        """
        A frequency table summarizes a data distribution. The table includes the data values (sometimes grouped into intervals) and the associated frequencies.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_histogram(self, *args, **kwargs):
        """
         A histogram is a graphical representation of a numerical data set that has been grouped into intervals. Each interval is represented by a bar drawn above that interval that has a height corresponding to the number of observations in that interval or the relative frequency corresponding to that interval.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_interquartile_range_iqr(self, *args, **kwargs):
        """
        s a measure of variability appropriate for data distributions that are skewed. It is the difference between the upper quartile and the lower quartile of a data set and describes how spread out the middle 50?% of the data are.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_mean(self, *args, **kwargs):
        """
         The mean is a measure of center appropriate for data distributions that are approximately symmetric. It is the average of the values in the data set. Two common interpretations of the mean are as a "fair share" and as the balance point of the data distribution.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_mean_absolute_deviation_mad(self, *args, **kwargs):
        """
        The mean absolute deviation of a numerical data set is the mean of all the distances from the mean for that data set.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_median(self, *args, **kwargs):
        """
         The median is a measure of center appropriate for skewed data distributions. It is the middle value when the data are ordered from smallest to largest if the number of observations is odd and halfway between the middle two observations if the number of observations is even.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_relative_frequency(self, *args, **kwargs):
        """
        A relative frequency associated with an interval used to construct a summary table or a histogram is equal to the number of data values that are included in the interval divided by the total number of values in the data set. It is the proportion of the data values that are included in the interval.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_relative_frequency_histogram(self, *args, **kwargs):
        """
        A relative frequency histogram is a histogram that is constructed using relative frequencies rather than frequencies to determine the heights of the bars.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_relative_frequency_table(self, *args, **kwargs):
        """
        A relative frequency table is a frequency table that displays relative frequency in addition to frequency for each data value or interval.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_statistical_question(self, *args, **kwargs):
        """
        A statistical question is a question that can be answered by collecting data and that anticipates variability in the data collected.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass




    def _problem_variability(self, *args, **kwargs):
        """
         Variability of a data set is the extent to which data values differ from each other. Variability occurs when the observations in the data set are not all the same.
        
        """
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        TODO
        """
        problemTemplate.add_problem(prob_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
        pass


