'''
Created on 07-Feb-2021

@author: bthakur
'''
import decimal
import numpy as np
import random
import inspect
import traceback
from xv.util import toIntOrFloat

from sympy import (simplify, sympify, log, symbols, nsimplify, latex,
                   Rational)

from xv.km.util import (latex_fraction)
        
import re
        
from ..helper import BaseHelper
from xv.km.managers import BaseManager
from xv.km.kelements import KProblemTemplate
        
class DecimalOperationManager(BaseManager):

    '''
    Decimal operations


    Minimum Grade: 3
    Maximum Grade: 10


    Examples
    --------
    ke = DecimalOperationManager()
    ke 
    
    ke.printProblemTypes()
    ke.helpProblemType(0)
    
    ke.getRandomProblem()
    ke.getRandomProblem(problem_type = 0)



    doc_style: xv_doc
    ''' 


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

    def _remove_trailing_decimal_zero(self, num):
        if not isinstance(num, str):
            num = str(num)
            pass
        if not '.' in num:
            return num
        
        while num[-1] == '0':
            num = num[:-1]
            pass
        
        return num
        pass

    def _getRandomDecimal(self, max_digits = 5, **kwargs):
        p = random.randint(1, 1000) * 10 ** random.randint(0, 2)
        expnt = random.randint(1, 5)
        q = 10 ** expnt
        r = f"{p/q:.{expnt}f}"
        r = self._remove_trailing_decimal_zero(r)
        if not "." in r:
            r = r + "." 
        int_digits = r.index(".")
        decimal_digits = len(r) - int_digits - 1
        p = int(float(r.replace(".", "")))
        q = 10 ** decimal_digits

        return r, p, q, int_digits, decimal_digits
        pass



    def _get_latex_set_of_fraction(self, r, p, q):
        rTemp = r
        if not "." in rTemp:
            rTemp = rTemp + "." 
        int_digits = rTemp.index(".")
        decimal_digits = len(rTemp) - int_digits - 1

        latex_set = {}
        latex_set['p/q'] = latex_fraction(p, q)
        latex_set['float_p/q'] = latex_fraction(float(p), float(q))

        p_similar, q_similar, pq_digits = self._make_decimal_strs_similar(f"{p}", f"{q}")
        latex_set['similar_p/q'] = latex_fraction(p_similar, q_similar)
        
        latex_set['no_dec_p/q'] = latex_fraction(
                p_similar.replace(".", ""), 
                q_similar.replace(".", ""))
        
        latex_set['r/1'] = latex_fraction(r, "1")

        one_float = f"{1:.{decimal_digits}f}"
        latex_set['one_float'] = latex_fraction(r, one_float)

        r_similar, one_similar, one_digits = self._make_decimal_strs_similar(r, one_float)
        latex_set['similar'] = latex_fraction(r_similar, one_similar)
        return latex_set
        pass

    def _make_decimal_strs_similar(self, dcm1, dcm2):
        if not '.' in dcm1:
            dcm1 = dcm1 + "."
        if not '.' in dcm2:
            dcm2 = dcm2 + "."
            
        parts1 = dcm1.split('.')
        parts2 = dcm2.split('.')
        
        int_digits = max(len(parts1[0]), len(parts2[0]))
        parts1[0] = parts1[0].zfill(int_digits)
        parts2[0] = parts2[0].zfill(int_digits)
        
        len1 = len(parts1[1])
        len2 = len(parts2[1])
        if len1 >= len2:
            parts2[1] = parts2[1] + "0" * (len1 - len2)
        else:
            parts1[1] = parts1[1] + "0" * (len2 - len1)
            pass
        
        dcm1_m = '.'.join(parts1)
        dcm2_m = '.'.join(parts2)
        
        decimal_digits =  max(len1, len2)
        return dcm1_m, dcm2_m, decimal_digits
        pass
    
    def latex_fraction(self, p, q):
        
        return f"""
            {{\\frac{{{p}}}{{{q}}}}}
        """
        pass


    def _get_decimal_digit_count(self, num1, num2, n3):
        num1_str = f"{toIntOrFloat(num1, n3)}"
        num2_str = f"{toIntOrFloat(num2, n3)}"
        
        if "." in num1_str:
            num1_dec_digits = len(num1_str.split(".")[-1])
        else:
            num1_dec_digits = 0
            
        if "." in num2_str:
            num2_dec_digits = len(num2_str.split(".")[-1])
        else:
            num2_dec_digits = 0
            
        n3 = max(num1_dec_digits, num2_dec_digits)
        return n3
        

    def _get_decimal_number_details(self, num, n3):
        num_str = f"{num: .{n3}f}".strip() 
        if "." in num_str:
            num_str_parts = num_str.split(".")
        else:
            num_str_parts = [num_str, ""]
            
        pos_values = []
        non_zero_pos_values = []
        if len(num_str_parts) == 2:
            n_tmp = num_str_parts[0]
            len_n_tmp = len(n_tmp)
            for i in range(len(n_tmp)):
                pos_value = int(n_tmp[i]) * 10 ** (len_n_tmp - i - 1)
                pos_values.append(f"{pos_value}")
                if pos_value != 0 :
                    non_zero_pos_values.append(f"{pos_value}")
                    pass
                pass
            pass
        n_tmp = num_str_parts[-1]
        for i in range(len(n_tmp)):
            pos_value = Rational(int(n_tmp[i]), 10 ** (i + 1))
            pos_value_str = latex_fraction(f"{int(n_tmp[i])}", 
                                           f"{10 ** (i + 1)}")
            
            pos_values.append(pos_value_str)
            if pos_value != 0:
                non_zero_pos_values.append(pos_value_str)
                pass
            pass

        
        pos_values_str = " + ".join(pos_values)
        non_zero_pos_values_str = " + ".join(non_zero_pos_values)
        
        
        result = {
            'num_str': num_str,
            'pos_values_str': pos_values_str,
            'non_zero_pos_values_str': non_zero_pos_values_str,
            }
        return result

    
    def _init_problem_types(self, *args, **kwargs):
        self._append_problem_types([
                '_problem_positional_values',
                '_problem_concept_of_decimal',
                '_problem_concept_of_decimal_advanced',
                '_problem_compare_decimal_numbers',
                '_problem_type_int_as_float',
                '_problem_type_int_multiplied_by_ten_exp',
                '_problem_type_int_divided_by_ten_exp',
                '_problem_type_decimal_and_exp',
                '_problem_convert_decimal_to_rational',
                '_problem_convert_rational_to_decimal',
                '_problem_type_multiply_decimal_numbers',
                '_problem_convert_decimals_div_to_rational_number',
                '_problem_type_frac_add',
                '_problem_type_frac_sub',
                '_problem_type_frac_pow',
                '_problem_type_mul_fractions',
                '_problem_decimals_mixed_operations_1',
                '_problem_decimals_mixed_operations_2',
                '_problem_decimals_mixed_operations_3',
                '_problem_decimals_mixed_operations_4',
                '_problem_convert_number_systems',
                '_problem_add_subtract_number_systems',
                '_problem_operate_number_systems',
            ])

        pass


    def _problem_positional_values(self, *args, **kwargs):
        """
        doc_style: xv_doc
        """
        m1, m2 = random.sample([
            random.randint(1, 9),
            random.randint(10, 99),
            random.randint(100, 999),
            random.randint(1000, 9999),
            random.randint(10000, 99999),
            ], 2)
        n1 = m1 + m2
        n2 = 0
        n3 = 0
        num = n1

        result = self._get_decimal_number_details(num, n3)
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        problem_s = f"""
        What does {result['num_str']} mean? Please write the expanded form to explain.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        $\\displaystyle {latex(result['num_str'])}$
        
        $\\displaystyle = {result['pos_values_str']}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        if result['non_zero_pos_values_str'].strip() != result['pos_values_str'].strip():   
            soln_s = f"""
            Ignoring all items which are zeros:
            
            $\\displaystyle = {result['non_zero_pos_values_str']}$
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        
        answer_s = f"""
        $\\displaystyle {result['non_zero_pos_values_str']}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    
    def _problem_concept_of_decimal(self, *args, **kwargs):
        """
        Concept of decimal


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_concept_of_decimal')


        doc_style: xv_doc
        """
        while True:
            n1 = random.randint(1, 9)
            n2 = random.randint(1, 3)
            n3 = n2
            num = n1 / (10 ** n2)
            if num != int(num):
                break
            pass

        result = self._get_decimal_number_details(num, n3)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        problem_s = f"""
        What does {result['num_str']} mean? Please write the expanded form to explain.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        $\\displaystyle {latex(result['num_str'])}$
        
        $\\displaystyle = {result['pos_values_str']}$
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        if result['non_zero_pos_values_str'].strip() != result['pos_values_str'].strip():   
            soln_s = f"""
            Ignoring all items which are zeros:
            
            $\\displaystyle = {result['non_zero_pos_values_str']}$
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
        

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

    def _problem_concept_of_decimal_advanced(self, *args, **kwargs):
        """
        Concept of decimal - advanced


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_concept_of_decimal_advanced')


        doc_style: xv_doc
        """

        while True:
            n1 = random.choice([
                random.randint(1, 9),
                random.randint(10, 99),
                random.randint(100, 999),
                ])
            n2 = random.randint(1, 5)
            n3 = random.randint(n2, n2 + 2)
            num = n1 / (10 ** n2)
            if num != int(num):
                break
            pass

        result = self._get_decimal_number_details(num, n3)
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        problem_s = f"""
        What does {result['num_str']} mean? Please write the expanded form to explain.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        $\\displaystyle {latex(result['num_str'])}$
        
        $\\displaystyle = {result['pos_values_str']}$
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        if result['non_zero_pos_values_str'].strip() != result['pos_values_str'].strip():   
            soln_s = f"""
            Ignoring all items which are zeros:
            
            $\\displaystyle = {result['non_zero_pos_values_str']}$
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
        

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

    def _problem_compare_decimal_numbers(self, *args, **kwargs):
        """
        Compare decimal numbers


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_compare_decimal_numbers')


        doc_style: xv_doc
        """

        while True:
            n1 = random.choice([
                random.randint(1, 9),
                random.randint(10, 99),
                random.randint(100, 999),
                ])
            n21 = random.randint(1, 5)
            n22 = random.randint(1, 5)
            n3 = random.randint(max(n21, n22), max(n21, n22) + 4)
            num1 = n1 / (10 ** n21)
            num2 = n1 / (10 ** n22) 
            if num1 < num2:
                num1 = num1 + random.choice([0, 0.1, 0.01])
            else:
                num2 = num2 + random.choice([0, 0.1, 0.01])
            
            if num1 != int(num1) and num2 != int(num2):
                break
            pass

        n3 = self._get_decimal_digit_count(num1, num2, n3)
        
        result1 = self._get_decimal_number_details(num1, n3)
        result2 = self._get_decimal_number_details(num2, n3)
        
        num_str1_no_decimal = result1['num_str'].replace(".", "")
        num_str2_no_decimal = result2['num_str'].replace(".", "")
        
        num_str1_no_decimal.strip('0')

        if num1 > num2:
            rel = ">"
        elif num1 < num2:
            rel = "<"
        else:
            rel = "="
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        problem_s = f"""
        ${latex(num1)} \\;\\;?\\;\\; {latex(num2)}$ 
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        <h2>Method 1</h2>
        
        ${latex(num1)} \\;\\;?\\;\\; {latex(num2)}$ 

        Make both numbers similar,  means the digits to the left and the right of decimal must be same in both numbers, 

        $\\displaystyle \\implies 
        {latex(result1['num_str'])} \\;\\;?\\;\\; 
        {latex(result2['num_str'])}$ 

        As both numbers are similar, we can ignore decimal while comparing them.
        
        $\\displaystyle \\implies 
        {latex(num_str1_no_decimal)} \\;\\;?\\;\\; 
        {latex(num_str2_no_decimal)}
        $

        $\\displaystyle \\implies 
        {latex(num_str1_no_decimal.lstrip('0'))} \\;\\;?\\;\\; 
        {latex(num_str2_no_decimal.lstrip('0'))}
        $

        $\\displaystyle \\implies 
        {latex(num_str1_no_decimal)} {rel}
        {latex(num_str2_no_decimal)}
        $

        $\\displaystyle \\implies 
        {num1} {rel}
        {num2}
        $
        
        <h2>Method 2</h2>
        
        ${latex(num1)} \\;\\;?\\;\\; {latex(num2)}$ 

        Make both numbers similar,  means the digits to the left and the right of decimal must be same in both numbers, 

        $\\displaystyle \\implies 
        {result1['num_str']} \\;\\;?\\;\\; 
        {result2['num_str']}$ 
        
        $\\displaystyle \\implies 
        {result1['pos_values_str']} \\;\\;?\\;\\; 
        {result2['pos_values_str']}
        $
        
        Ignoring all numerators which are zeros,
        
        $\\displaystyle \\implies 
        {result1['non_zero_pos_values_str']} \\;\\;?\\;\\;
        {result2['non_zero_pos_values_str']}
        $

        $\\displaystyle \\implies 
        {result1['non_zero_pos_values_str']} {rel}
        {result2['non_zero_pos_values_str']}
        $

        $\\displaystyle \\implies 
        {num1} {rel}
        {num2}
        $
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $
        {num1} {rel}
        {num2}
        $
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass


    def _problem_type_int_as_float(self, *args, **kwargs):
        """
        Convert integer to float


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_type_int_as_float')
        ke.getRandomProblem(problem_type = '_problem_type_int_as_float', n = 123)


        doc_style: xv_doc
        """

        num1 = kwargs.get('n', 
                          random.randint(1, 1000) * 10 ** random.randint(0, 2))
        
        str_num1 = f"{float(num1)}"
        if str_num1[-1] == "0":
            str_num1 = str_num1[:-1]

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

        problem_s = f"""
        Express integer {num1} as decimal number.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        If we place a decimal at the end of an integer, its value does not change.
        Similarly, if we put trailing zeros after decimal, the value of number does not change.

        {num1} = {str_num1} = {float(num1)}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {str_num1} \t\t or
        {float(num1)}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    def _problem_type_int_multiplied_by_ten_exp(self, *args, **kwargs):
        """
        Multiply integer with multiples of 10


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_type_int_multiplied_by_ten_exp')
        ke.getRandomProblem(problem_type = '_problem_type_int_multiplied_by_ten_exp', n = 1200)


        doc_style: xv_doc
        """

        num1 = kwargs.get('n', 
                          random.randint(1, 1000) * 10 ** random.randint(0, 2))
        mExp = random.randint(1, 5)
        mFactor = 10 ** mExp
        

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

        problem_s = f"""
        Multiply {num1} with {mFactor}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        
        for i in range(mExp + 1):
            soln_s = f"{num1} * {10 ** i} = {num1 * 10 ** i}"
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {num1 * mFactor}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    def _problem_type_int_divided_by_ten_exp(self, *args, **kwargs):
        """
        Divide an integer with multiples of 10


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_type_int_divided_by_ten_exp')


        doc_style: xv_doc
        """

        r, p, q, int_digits, decimal_digits = self._getRandomDecimal()
        latex_set = self._get_latex_set_of_fraction(r, p, q)
        num_frac = latex_fraction(f"{p}.", f"{q}.")
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Divide {p} by {q}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        if q == 1:
            soln_s = f"""
            If we divide anything with 1, its value remains same.
            
            $\\displaystyle {latex_set['p/q']} = {p}$
            """
        else:
            soln_s = f"""
            $\\displaystyle {latex_set['p/q']}$
            
            $\\displaystyle = {num_frac}$
            
            If we add leading zeroes before a number, its value does not change.
            Make numerator and denominator similar by adding leading zeroes before decimal. 
            
            $\\displaystyle = {latex_set['similar_p/q']}$
            
            If we divide anything by 1, the value of number does not change.
            So, shift decimals in numerator and denominator to left by {decimal_digits}, to make denominator 1.
            
            $\\displaystyle = {latex_set['similar']}$
            
            $\\displaystyle = {latex_set['r/1']}$
            
            $\\displaystyle = {r}$
            
            """
            pass
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {r}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    def _problem_type_decimal_and_exp(self, *args, **kwargs):
        """
        Divide a decimal number with multiples of 10


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_type_decimal_and_exp')


        doc_style: xv_doc
        """

        r, p, q, int_digits, decimal_digits = self._getRandomDecimal()
        expnt = random.randint(2, 5)
        q2 = 10 ** expnt
        decimal_digits2 = decimal_digits + expnt
        r2 = float(r) / q2
        r2 = f"{r2:.{decimal_digits2}f}"
        latex_set = self._get_latex_set_of_fraction(r2, r, q2)

        num_frac = latex_fraction(f"{r}", f"{q2}.")

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

        problem_s = f"""
        Divide {r} by {q2} and express result as a decimal number
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        $\\displaystyle {latex_set['p/q']}$
        
        $\\displaystyle = {num_frac}$

        Make numerator and denominator similar. 
        The number of digits before and after the decimal in numerator and denominator must be same.
        
        $\\displaystyle = {latex_set['similar_p/q']}$
                
        If we divide anything by 1, the value of number does not change.
        Shift decimals in numerator and denominator to left by {expnt}, to make denominator 1.
        
        $\\displaystyle = {latex_set['one_float']}$
        
        $\\displaystyle = {latex_set['r/1']}$
        
        $\\displaystyle = {r2}$
                
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {r2}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    def _problem_convert_decimal_to_rational(self, *args, **kwargs):
        """
        Convert a decimal number to rational number


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_convert_decimal_to_rational')


        doc_style: xv_doc
        """

        r, p, q, int_digits, decimal_digits = self._getRandomDecimal()
        latex_set = self._get_latex_set_of_fraction(r, p, q)

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

        problem_s = f"""
        Express {r} as a rational number.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        if decimal_digits == 0:
            soln_s = f"""
            $\\displaystyle  {r} $
            
            If we divide anything by 1, the value of number does not change.
            
            $\\displaystyle = {latex_set['r/1']}$
            
            $\\displaystyle = {latex_set['p/q']}$
            
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
            
            pass
        else:
            soln_s = f"""
            $\\displaystyle  {r} $
            
            If we divide anything by 1, the value of number does not change.
            
            $\\displaystyle = {latex_set['r/1']}$
            
            Make numerator and denominator similar. 
            The number of digits before and after the decimal in numerator and denominator must be same.
            
            $\\displaystyle = {latex_set['similar_p/q']}$
            
            Remove decimal as decimal digits are same in numerator and denominator
            
            $\\displaystyle = {latex_set['no_dec_p/q']}$
            
            $\\displaystyle =  {latex_set['p/q']} $
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
            pass
        
        answer_s = f"""
        $\\displaystyle  {latex_set['p/q']} $
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass


    def _problem_convert_rational_to_decimal(self, *args, **kwargs):
        """
        Convert a rational number to decimal number


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_convert_rational_to_decimal')


        doc_style: xv_doc
        """

        r, p, q, int_digits, decimal_digits = self._getRandomDecimal()
        latex_set = self._get_latex_set_of_fraction(r, p, q)

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

        problem_s = f"""
        Express {p}/{q} as decimal number
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        $\\displaystyle {latex_set['p/q']}$
        
        Make numerator and denominator similar. 
        The number of digits before and after the decimal in numerator and denominator must be same.
        
        $\\displaystyle = {latex_set['similar_p/q']}$
        
        Shift decimals in numerator and denominator to left by {decimal_digits}, to make denominator 1. 
        
        $\\displaystyle = {latex_set['one_float']}$
        
        $\\displaystyle = {latex_set['r/1']}$
        
        If we divide anything by 1, the value of number does not change.
            
        $\\displaystyle = {r} $
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {r}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass


    def _problem_type_multiply_decimal_numbers(self, *args, **kwargs):
        """
        Multiply decimal numbers



        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_type_multiply_decimal_numbers')


        doc_style: xv_doc
        """

        r1, p1, q1, int_digits1, decimal_digits1 = self._getRandomDecimal()
        r2, p2, q2, int_digits2, decimal_digits2 = self._getRandomDecimal()
        decimal_digits = decimal_digits1 + decimal_digits2
        r = round(float(r1) * float(r2), decimal_digits)
        r = f"{r:.{decimal_digits}f}"
        

        latex_set1 = self._get_latex_set_of_fraction(r1, p1, q1)
        latex_set2 = self._get_latex_set_of_fraction(r2, p2, q2)
        latex_set3 = self._get_latex_set_of_fraction(r, p1 * p2, q1 * q2)

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

        problem_s = f"""
        Multiply {r1} * {r2}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        <h2>Method 1</h2>
        $\\displaystyle {r1} * {r2}$ 
        
        Convert fractions to rational numbers
        
        $\\displaystyle = {latex_set1['p/q']} * {latex_set2['p/q']}$
        
        $\\displaystyle = {latex_set3['p/q']}$
        
        $\\displaystyle = {r}$ 
        
        
        <h2>Method 2</h2>
        $\\displaystyle {r1} * {r2}$ 
        
        Ignore decimal points and multiply
        
        $\\displaystyle {p1} * {p2} = {p1 * p2}$
        
        Count the decimal digits of both numbers and put decimal in the above result
        
        $\\displaystyle = {r}$ 
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        
        answer_s = f"""
        $\\displaystyle {r}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    
    def _problem_convert_decimals_div_to_rational_number(self, *args, **kwargs):
        """
        Express division of decimals as a rational number


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_convert_decimals_div_to_rational_number')


        doc_style: xv_doc
        """

        r1, p1, q1, int_digits1, decimal_digits1 = self._getRandomDecimal()
        r2, p2, q2, int_digits2, decimal_digits2 = self._getRandomDecimal()
        
        r1_m, r2_m, decimal_digits = self._make_decimal_strs_similar(r1, r2)
        r1_no_dec = r1_m.replace(".", "")
        r2_no_dec = r2_m.replace(".", "")
        
        r1_int = int(r1_no_dec)
        r2_int = int(r2_no_dec)

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

        problem_s = f"""
        Express {r1} / {r2} as a fraction.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        $\\displaystyle  {latex_fraction(r1, r2)} $
        
        Make numerator and denominator similar. 
        The number of digits before and after the decimal in numerator and denominator must be same.
        
        $\\displaystyle = {latex_fraction(r1_m, r2_m)} $
        
        Remove decimal as numerator and denominator are similar fractions
        
        $\\displaystyle = {latex_fraction(r1_no_dec, r2_no_dec)} $
        
        Remove leading zeros
        
        $\\displaystyle = {latex_fraction(r1_int, r2_int)} $
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {latex_fraction(r1_int, r2_int)} $
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    
 
    def _problem_type_frac_add(self, *args, **kwargs):
        """
        Add rational numbers


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_type_frac_add')


        doc_style: xv_doc
        """

        r1, p1, q1, int_digits1, decimal_digits1 = self._getRandomDecimal()
        r2, p2, q2, int_digits2, decimal_digits2 = self._getRandomDecimal()
        
        r1_m, r2_m, decimal_digits = self._make_decimal_strs_similar(r1, r2)
        r = round(float(r1) + float(r2), decimal_digits)
        r = f"{r:.{decimal_digits}f}"

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

        problem_s = f"""
        Add {r1} and {r2}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        $\\displaystyle  {r1} + {r2} $

        Make numerator and denominator similar. 
        The number of digits before and after the decimal in numerator and denominator must be same.
        
        $\\displaystyle = {r1_m} + {r2_m} $
        
        $\\displaystyle = {r} $
                
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle  {r} $
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    
    def _problem_type_frac_sub(self, *args, **kwargs):
        """
        Subtract rational numbers


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_type_frac_sub')


        doc_style: xv_doc
        """

        r1, p1, q1, int_digits1, decimal_digits1 = self._getRandomDecimal()
        r2, p2, q2, int_digits2, decimal_digits2 = self._getRandomDecimal()
        
        r1_m, r2_m, decimal_digits = self._make_decimal_strs_similar(r1, r2)
        r = round(float(r1) - float(r2), decimal_digits)
        r = f"{r:.{decimal_digits}f}"

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

        problem_s = f"""
        Deduct {r2} from {r1}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        $\\displaystyle  {r1} - {r2} $

        Make numerator and denominator similar. 
        The number of digits before and after the decimal in numerator and denominator must be same.
        
        $\\displaystyle = {r1_m} - {r2_m} $
        
        $\\displaystyle = {r} $
                
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle  {r} $
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    
    
    def _problem_type_frac_pow(self, *args, **kwargs):
        """
        Power of a rational number


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_type_frac_pow')


        doc_style: xv_doc
        """

        r, p, q, int_digits, decimal_digits = self._getRandomDecimal()
        expnt = random.randint(2, 5)
        result = round(float(r)** expnt , decimal_digits * expnt)
        result = f"{result:.{decimal_digits * expnt}f}"

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

        problem_s = f"""
        Find the value of $\\displaystyle  {r} ^ {{ {expnt} }} $
        
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        $\\displaystyle  {r} ^ {{ {expnt} }} $

        Calculate the power without considering decimals

        $\\displaystyle = {p} ^ {{ {expnt} }} = {p ** expnt} $

        The {r} has {decimal_digits} decimal digits and it has a power of {expnt}.
        
        As {r} is repeated {expnt} times, decimal digits are also repeated {expnt} times.
        
        Hence, the number of decimal digits = {decimal_digits} * {expnt} = {decimal_digits * expnt}

        $\\displaystyle = {result} $
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle  {result} $
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    
    def _problem_type_mul_fractions(self, *args, **kwargs):
        """
        Multiply rational numbers


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_type_mul_fractions')


        doc_style: xv_doc
        """

        r1, p1, q1, int_digits1, decimal_digits1 = self._getRandomDecimal()
        r2, p2, q2, int_digits2, decimal_digits2 = self._getRandomDecimal()
        p1p2 = p1 * p2

        decimal_digits = decimal_digits1 + decimal_digits2
        result = float(r1) * float(r2)
        result = f"{result:.{decimal_digits}f}"
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Find the value of $\\displaystyle  {r1} * {r2} $
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        $\\displaystyle  {r1} * {r2} $
        
        Calculate the power without considering decimals
        
        {p1} * {p2} = {p1p2} 
        
        The decimal digits in both numbers = {decimal_digits1} + {decimal_digits2} = {decimal_digits}
        
        So, the final result will have {decimal_digits} decimal digits.
        
        $\\displaystyle = {result} $
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle  {result} $
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass


    def _convert_to_rational(self, r1, r2):
        r1r2= nsimplify(r1/r2)
        if r1r2 == int(r1r2):
            p1 = r1r2
            q1 = 1
        else:
            p1 = r1r2.p
            q1 = r1r2.q
            
        return p1, q1
        pass

    def _problem_decimals_mixed_operations_1(self, *args, **kwargs):
        """
        Mixed operations of rational numbers


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_decimals_mixed_operations_1')


        doc_style: xv_doc
        """

        
        
        r1 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        r2 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        r3 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        r4 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        
        p1, q1 = self._convert_to_rational(r1, r2)
        p2, q2 = self._convert_to_rational(r3, r4)

        r_frc0 = f"{latex(nsimplify(r1/r2))} + {latex(nsimplify(r3/r4))}"
        r_frc0 = r_frc0.replace("\\frac", "\\dfrac")

        r_frc1 = latex_fraction(f"{p1} * {q2} + {p2} * {q1}", f"{q1} * {q2}")
        r_frc2 = latex_fraction(f"{p1 * q2} + {p2 * q1}", f"{q1 * q2}")
        r_frc3 = latex_fraction(f"{p1 * q2 + p2 * q1}", f"{q1 * q2}")
        
        r = Rational(p1 * q2 + p2 * q1, q1 * q2)
        if q1 == 1 and q2 == 1:
            r_frc = latex(p1 + p2)
        else:
            r_frc = latex_fraction(f"{r.p}", f"{r.q}")

        ans1 = r
        ans2 = (r1/r2) + (r3/r4)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Simplify the followings:
        
        $\\displaystyle {latex_fraction(r1, r2)} + {latex_fraction(r3, r4)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        $\\displaystyle {latex_fraction(r1, r2)} + {latex_fraction(r3, r4)}$

        $\\displaystyle = {latex_fraction(latex(nsimplify(r1)), latex(nsimplify(r2)))} + {latex_fraction(latex(nsimplify(r3)), latex(nsimplify(r4)))}$

        $\\displaystyle = {r_frc0}$

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

        if not (q1 == 1 and q2 == 1):
            soln_s = f"""
            $\\displaystyle = {r_frc1}$
    
            $\\displaystyle = {r_frc2}$
    
            $\\displaystyle = {r_frc3}$
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        soln_s = f"""
        $\\displaystyle = {r_frc}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


        soln_s = f"""

        Alternatively:
        --------------------------------------

        $\\displaystyle {latex_fraction(r1, r2)} + {latex_fraction(r3, r4)}$

        $\\displaystyle = {r1/r2} + {r3/r4}$

        $\\displaystyle = {(r1/r2) + (r3/r4)}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        answer_s = f"""
        $\\displaystyle {r_frc}$ \t\t or
        
        $\\displaystyle {latex(ans2)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')

        ans_diff = abs(ans1 - ans2)
        if ans_diff > 10 ** (-3):
            answer_s = f"""
            
            The answers don't match.
            The diff is {ans_diff}
            """
            problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        
        return problemTemplate

    def _problem_decimals_mixed_operations_2(self, *args, **kwargs):
        """
        Mixed operations of rational numbers


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_decimals_mixed_operations_2')


        doc_style: xv_doc
        """

        
        r1 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        r2 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        r3 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        r4 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        
        t11 = latex_fraction(f"{r1} + {r2}", r3)
        t12 = latex_fraction(1, r4)
   
        t21 = latex_fraction(f"{r1 + r2}", r3)
        t22 = 1/r4
   
        t31 = (r1 + r2) / r3
        t32 = t22

        t41 = t31 + t32

        r1r2 = r1 + r2

        p01, q01 = self._convert_to_rational(r1 + r2, 1)
        p02, q02 = self._convert_to_rational(r3, 1)
 
        r1r2_frc = Rational(p01, q01)
        r3_frc = Rational(p02, q02)
        r1r2r3_frc = latex_fraction(f"{p01} * {q02}", f"{p02} * {q01}")
        
        p1 = p01 * q02 
        q1 = p02 * q01
        
        p2, q2 = self._convert_to_rational(1, r4)


        frc0 = f"{latex_fraction(r1r2, r3)} + {latex_fraction(p2, q2)}"
        
        frc1 = f"{latex_fraction(latex(r1r2_frc), latex(r3_frc))} + {latex_fraction(p2, q2)}"
        
        frc2 = f"{r1r2r3_frc} + {latex_fraction(p2, q2)}"
        
        frc3 = f"{latex_fraction(p1, q1)} + {latex_fraction(p2, q2)}"
                
        frc4 = latex_fraction(f"{p1} * {q2} + {p2} * {q1}", f"{q1} * {q2}")
        frc5 = latex_fraction(f"{p1 * q2} + {p2 * q1}", f"{q1 * q2}")
        frc6 = latex_fraction(f"{p1 * q2 + p2 * q1}", f"{q1 * q2}")
        
        
        r = Rational(p1 * q2 + p2 * q1, q1 * q2)
        if q1 == 1 and q2 == 1:
            r_frc = latex(p1 + p2)
        else:
            r_frc = latex_fraction(f"{r.p}", f"{r.q}")
        
        ans1 = r
        ans2 = t41
                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')


        problem_s = f"""
        Simplify the followings:
        
        $\\displaystyle {t11} + {t12}$
        
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')

        
        soln_s = f"""
        
        $\\displaystyle {t11} + {t12}$
        
        $\\displaystyle = {frc0}$
        
        $\\displaystyle = {frc1}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
 
        if not (q01 == 1 and q02 == 1):
            soln_s = f"""
            $\\displaystyle = {frc2}$
            
            $\\displaystyle = {frc3}$
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
 


        if not (q1 == 1 and q2 == 1):
            soln_s = f"""
            $\\displaystyle = {frc4}$
            
            $\\displaystyle = {frc5}$
            
            $\\displaystyle = {frc6}$
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
 

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

        
        soln_s = f"""
        
        Alternatively:
        ----------------------------------
        
        $\\displaystyle {t11} + {t12}$
        
        $\\displaystyle = {t21} + {t22}$
        
        $\\displaystyle = {t31} + {t32}$
        
        $\\displaystyle = {t41}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
 
        answer_s = f"""
        $\\displaystyle {r_frc}$ \t\t or
        
        $\\displaystyle {t41}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')

        ans_diff = abs(ans1 - ans2)
        if ans_diff > 10 ** (-3):
            answer_s = f"""
            
            The answers don't match.
            The diff is {ans_diff}
            """
            problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_decimals_mixed_operations_3(self, *args, **kwargs):
        """
        Mixed operations of rational numbers


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_decimals_mixed_operations_3')


        doc_style: xv_doc
        """

        
        r1 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        r2 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        r3 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        r4 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        
        t11 = latex_fraction(f"{r1} + {r2}", r3)
        t12 = latex_fraction(1, r4)
   
        t21 = latex_fraction(f"{r1 + r2}", r3)
        t22 = 1/r4
   
        t31 = (r1 + r2) / r3
        t32 = t22

        t41 = t31 * t32
        
        ##################################################################
        
        r1r2 = r1 + r2

        p01, q01 = self._convert_to_rational(r1 + r2, 1)
        p02, q02 = self._convert_to_rational(r3, 1)
 
        r1r2_frc = Rational(p01, q01)
        r3_frc = Rational(p02, q02)
        r1r2r3_frc = latex_fraction(f"{p01} * {q02}", f"{p02} * {q01}")
        
        p2, q2 = self._convert_to_rational(1, r4)

        r1r2r3r4_frc = latex_fraction(f"{p01} * {q02} * {p2}", f"{p02} * {q01} * {q2}")

        p1 = p01 * q02 
        q1 = p02 * q01

        frc0 = f"{latex_fraction(r1r2, r3)} * {latex_fraction(p2, q2)}"
        
        frc1 = f"{latex_fraction(latex(r1r2_frc), latex(r3_frc))} * {latex_fraction(p2, q2)}"
        
        frc2 = f"{r1r2r3_frc} * {latex_fraction(p2, q2)}"
        
        frc3 = f"{r1r2r3r4_frc}"
                
        frc4 = latex_fraction(f"{p1 * p2}", f"{q1 * q2}")
        
        
        r = Rational(p1 * p2, q1 * q2)
        if q1 == 1 and q2 == 1:
            r_frc = latex(p1 * p2)
        else:
            r_frc = latex_fraction(f"{r.p}", f"{r.q}")
        
        ans1 = r
        ans2 = t41
        
        ##################################################################
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')


        problem_s = f"""
        Simplify the followings:
        
        $\\displaystyle {t11} * {t12}$
        
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')

        
        soln_s = f"""
        $\\displaystyle {t11} * {t12}$
        
        $\\displaystyle = {frc0}$
        
        $\\displaystyle = {frc1}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
 
        if not (q01 == 1 and q02 == 1):
            soln_s = f"""
            $\\displaystyle = {frc2}$
            
            $\\displaystyle = {frc3}$
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
 


        if not (q1 == 1 and q2 == 1):
            soln_s = f"""
            $\\displaystyle = {frc4}$
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
 

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

        soln_s = f"""

        
        Alternatively:
        ------------------------------------
        
        $\\displaystyle {t11} * {t12}$
        
        $\\displaystyle = {t21} * {t22}$
        
        $\\displaystyle = {t31} * {t32}$
        
        $\\displaystyle = {t41}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


        
        answer_s = f"""
        $\\displaystyle {r_frc}$ \t\t or
        
        $\\displaystyle {t41}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        

        ans_diff = abs(ans1 - ans2)
        if ans_diff > 10 ** (-3):
            answer_s = f"""
            
            The answers don't match.
            The diff is {ans_diff}
            """
            problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_decimals_mixed_operations_4(self, *args, **kwargs):
        """
        Mixed operations of rational numbers


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_decimals_mixed_operations_4')


        doc_style: xv_doc
        """

        
        r1 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        r2 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        r3 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        r4 = random.randint(1, 9) / (10 ** random.randint(-3, 3))
        
        t11 = f"{r1} * {r2}"
        t12 = f"{r3} + {r4}"


        p1, q1 = self._convert_to_rational(r1 * r2, 1)
        p2, q2 = self._convert_to_rational(r3 + r4, 1)

        frc0 = latex_fraction(f"{latex(Rational(p1, q1))}", f"{latex(Rational(p2, q2))}")
        
        frc1 = latex_fraction(f"{p1} * {q2}", f"{p2} * {q1}")
        
        frc2 = latex_fraction(f"{p1 * q2}", f"{p2 * q1}")

        r = Rational(p1 * q2, p2 * q1)
        r_frc = latex_fraction(f"{r.p}", f"{r.q}")
        
        ans1 = r
        ans2 = (r1 * r2) / (r3 + r4)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Simplify the followings:
        
        $\\displaystyle {latex_fraction(t11, t12)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        $\\displaystyle {latex_fraction(t11, t12)}$

        $\\displaystyle = {latex_fraction(r1 * r2, r3 + r4)}$

        $\\displaystyle = {frc0}$

        $\\displaystyle = {frc1}$
        
        $\\displaystyle = {frc2}$
        
        $\\displaystyle = {r_frc}$

        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        
        soln_s = f"""
        
        Alternatively:
        ----------------------------------------------

        $\\displaystyle {latex_fraction(t11, t12)}$

        $\\displaystyle = {latex_fraction(r1 * r2, r3 + r4)}$

        $\\displaystyle = {latex((r1 * r2) / (r3 + r4))}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {r_frc}$ \t\t or
        
        $\\displaystyle {latex(ans2)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        

        ans_diff = abs(ans1 - ans2)
        if ans_diff > 10 ** (-3):
            answer_s = f"""
            
            The answers don't match.
            The diff is {ans_diff}
            """
            problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate



    def _problem_convert_number_systems(self, *args, **kwargs):
        """
        Convert number systems


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_convert_number_systems')


        doc_style: xv_doc
        """
        
        nsys = {
            'hundred': 2,
            'thousand': 3,
            'million': 6,
            'billion': 9,
            'trillion': 12,
            }
        
        p = random.choice([random.randint(1, 5),
                           random.randint(1, 100) ,
                           random.randint(1, 1000)])
        q = random.randint(2, 5)
        
        n = toIntOrFloat(p/q, 4)
        
        nsy = random.choice(list(nsys.keys()))
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Write {n} {nsy} as number.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        {n} {nsy} 
        
        $\\displaystyle = {n} * {10 ** nsys[nsy]:,} $ 
        
        $\\displaystyle = {toIntOrFloat(n * 10 ** nsys[nsy]):,} $ 
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {toIntOrFloat(n * 10 ** nsys[nsy]):,} $ 
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
    

    def _problem_add_subtract_number_systems(self, *args, **kwargs):
        """
        Convert number systems


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_add_subtract_number_systems')


        doc_style: xv_doc
        """
        
        nsys = {
            'hundred': 2,
            'thousand': 3,
            'million': 6,
            'billion': 9,
            'trillion': 12,
            }
        
        nums = []
        ops = ["", "+", "-"]
        
        for i in range(random.randint(2, 5)):
            p = random.choice([random.randint(1, 5),
                               random.randint(1, 100) ,
                               random.randint(1, 1000)])
            q = random.randint(2, 5)
            n = toIntOrFloat(p/q, 4)
            nsy = random.choice(list(nsys.keys()))
            
            if len(nums) == 0:
                op = random.choice(ops[:3])
                pass
            else:
                op = random.choice(ops[1:])
                pass
            
            nums.append({'n': n, 'nsy': nsy, 'op': op})
            pass
        
        num_expr = []
        num_expanded_expr = []
        num_net_expr = []
        for num in nums:
            n = num['n']
            nsy = num['nsy']
            op = num['op']
            num_expr.append(f"{op} {n} {nsy}")
            num_expanded_expr.append(f"{op} {n} * {10 ** nsys[nsy]:,}")
            num_net_expr.append(f"{op} {toIntOrFloat(n * 10 ** nsys[nsy]):,}")
            pass
        num_expr_s = " ".join(num_expr)
        num_expanded_expr_s = " ".join(num_expanded_expr)
        num_net_expr_s = " ".join(num_net_expr)
        
        num_expr2 = num_net_expr_s.replace(",", "")
        num_expr2 = re.split('([-+])',num_expr2)
        num_expr2 = [x.strip() for x in num_expr2 if x.strip()]
        
        if not num_expr2[0] in ["+", "-"]:
            num_expr2.insert(0, "+")
            
        num_expr2 = [f"{num_expr2[2 * i]}{num_expr2[2 * i + 1]}" 
                     for i in range(len(num_expr2) // 2)]
        
        num_expr2 = [sympify(x) for x in num_expr2]
        num_expr2 = [f"+ {float(term):,}" if term >= 0 
                     else f"{float(term):,}"
                     for term in num_expr2]

        num_expr2_s = " ".join(num_expr2)

        num_expr3 = num_expr2_s.replace(",", "")
        
        num_expr3 = sympify(num_expr3)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Simplify:
        
        {num_expr_s}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        {num_expr_s}
        
        = {num_expanded_expr_s}
        
        = {num_net_expr_s}
        
        = {num_expr2_s}
        
        = {num_expr3:,}
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {num_expr3:,}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
    
    def _problem_operate_number_systems(self, *args, **kwargs):
        """
        Convert number systems


        Minimum Grade: 3
        Maximum Grade: 8


        Examples
        --------
        from xv.math.algebra import DecimalOperationManager
        ke = DecimalOperationManager()


        ke.getRandomProblem(problem_type = '_problem_operate_number_systems')


        doc_style: xv_doc
        """
        
        nsys = {
            'hundred': 2,
            'thousand': 3,
            'million': 6,
            'billion': 9,
            #'trillion': 12,
            }
        
        nums = []
        ops = ["", "+", "-", "*", "/"]
        
        for i in range(random.randint(2, 5)):
            p = random.choice([random.randint(1, 5),
                               random.randint(1, 100) ,
                               random.randint(1, 1000)])
            q = random.randint(2, 5)
            n = toIntOrFloat(p/q, 4)
            nsy = random.choice(list(nsys.keys()))
            
            if len(nums) == 0:
                op = random.choice(ops[:3])
                pass
            else:
                op = random.choice(ops[1:])
                pass
            
            nums.append({'n': n, 'nsy': nsy, 'op': op})
            pass
        
        num_expr = []
        num_expanded_expr = []
        num_net_expr = []
        for num in nums:
            n = num['n']
            nsy = num['nsy']
            op = num['op']
            num_expr.append(f"{op} {n} {nsy}")
            num_expanded_expr.append(f"{op} ({n} * {10 ** nsys[nsy]:,})")
            num_net_expr.append(f"{op} {toIntOrFloat(n * 10 ** nsys[nsy]):,}")
            pass
        num_expr_s = " ".join(num_expr)
        num_expanded_expr_s = " ".join(num_expanded_expr)
        num_net_expr_s = " ".join(num_net_expr)
        
        num_expr2 = num_net_expr_s.replace(",", "")
        num_expr2 = re.split('([-+])',num_expr2)
        num_expr2 = [x.strip() for x in num_expr2 if x.strip()]
        
        if not num_expr2[0] in ["+", "-"]:
            num_expr2.insert(0, "+")
            
        num_expr2 = [f"{num_expr2[2 * i]}{num_expr2[2 * i + 1]}" 
                     for i in range(len(num_expr2) // 2)]
        
        num_expr2 = [sympify(x) for x in num_expr2]
        num_expr2 = [f"+ {float(term):,}" if term >= 0 
                     else f"{float(term):,}"
                     for term in num_expr2]

        num_expr2_s = " ".join(num_expr2)

        num_expr3 = num_expr2_s.replace(",", "")
        
        num_expr3 = sympify(num_expr3)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Simplify:
        
        {num_expr_s}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        {num_expr_s}
        
        = {num_expanded_expr_s}
        
        = {num_net_expr_s}
        
        = {num_expr2_s}
        
        = {num_expr3:,}
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {num_expr3:,}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
    
