'''
Created on 12-Jul-2021

@author: bthakur
'''
import random
import inspect
import traceback   #        {inspect.stack()[0][3]}

import re
from sympy import (Eq, symbols, Symbol,
                   sqrt, latex,
                   Rational, solve, log, nsimplify, E, exp,  
                   sin, cos, I, series, pi)
from sympy.abc import theta
import sympy


from xv.km.util import latex_fraction
from ....util import LatexEquation


from xv.util import UtilClass

class ModellingPercentageHelper(UtilClass):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        pass

    def get_random_simple_model(self, **kwargs):
        self._log(f"Starting {self.__class__.__name__}.{inspect.stack()[0][3]} ...")
        methods = [self.get_model_type_gain_loss_percent, 
                   self.get_model_type_purchase_cost, 
                   self.get_model_type_units_bought_sold,
                   self.get_model_percent_based_equations,
                   ]
        
        method = random.choice(methods)
        
        return method(**kwargs)

    def _get_units_bought_sold_data(self, **kwargs):
        self._log(f"Starting {self.__class__.__name__}.{inspect.stack()[0][3]} ...")

        multiplier = random.choice([5, 10])
        bought_units = random.randint(5, 10) * multiplier
        delta_units = random.randint(1, 3) * random.choice([1, -1])
        sold_units = bought_units + delta_units
        
        unit_cost = random.randint(1, 10) * random.choice([1, 5, 10])
        cost_of_sales = sold_units * unit_cost
        sale_price_of_sales = bought_units * unit_cost
        gain_loss = sale_price_of_sales - cost_of_sales
        gain_loss_percent = round(gain_loss * 100 / cost_of_sales, 2)

        if gain_loss < 0:
            gain_loss_text = "loss"
            gain_loss_percent_op = "-"
        else:
            gain_loss_text = "gain"
            gain_loss_percent_op = "+"
            

        money_unit = Symbol(r'\$ ')

        data_dict = {
            'money_unit': money_unit,
            'bought_units': bought_units,
            'delta_units': delta_units,
            'sold_units': sold_units,
            'unit_cost': unit_cost,
            'cost_of_sales': cost_of_sales,
            'sale_price_of_sales': sale_price_of_sales,
            'gain_loss': gain_loss,
            'gain_loss_text': gain_loss_text,
            'gain_loss_percent_op': gain_loss_percent_op,
            'gain_loss_percent': gain_loss_percent,
            }
        
        return data_dict
        pass

    def get_model_type_units_bought_sold(self, **kwargs):
        self._log(f"Starting {self.__class__.__name__}.{inspect.stack()[0][3]} ...")
        
        data_dict = self._get_units_bought_sold_data()
        
        bought_units = data_dict.get('bought_units')
        sold_units = data_dict.get('sold_units')
        
        gain_loss_text = data_dict.get('gain_loss_text')
        gain_loss_percent = data_dict.get('gain_loss_percent')
        gain_loss_percent_op = data_dict.get('gain_loss_percent_op')
        
                
        sold_units_symb = Symbol('x')
        unit_cost_symb = Symbol(f'\\text {{ cost per unit }}')

             
        prob_s = f"""
        The cost price of ${latex(bought_units)}$ articles is the same as the selling price of 
        ${latex(sold_units_symb)}$ articles. 
        If the {gain_loss_text} is ${latex(gain_loss_percent)} \%$, 
        then what is the value of ${latex(sold_units_symb)}$?
        """
        prob_s = prob_s.replace("\n", " ")

        sales_value_symb = Symbol(f"\\text {{ sales value of units sold }}")
        cost_value_symb = Symbol(f"\\text {{ cost of units sold }}")
                
        soln_stmts = []  
        
        
        
        soln_stmt = f"""
        As the {gain_loss_text} is ${latex(abs(gain_loss_percent))} \%$,
        
        $\\displaystyle
        \\left( {latex(sales_value_symb)} \\right)
        = \\left( 100 \% {gain_loss_percent_op} {latex(abs(gain_loss_percent))} \% \\right)
        \\text {{ of }}
        \\left( {latex(cost_value_symb)} \\right)
        $
        
        $\\displaystyle 
        \\implies
        \\left( {latex(sales_value_symb)} \\right)
        = {latex(100 + gain_loss_percent)} \%
        \\times
        \\left( {latex(cost_value_symb)} \\right)
        \\qquad \\cdots \\text{{ (i) }}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        
        soln_stmt = f"""
        The cost price of $
        {latex(bought_units)}
        $ articles is the same as the selling price of $
        {latex(sold_units_symb)}$ articles. Therefore,
        
        $\\displaystyle 
        {latex(sales_value_symb)}
         = {latex(bought_units)} * {latex(unit_cost_symb)} 
        \\\\ ~ \\\\
        {latex(cost_value_symb)}
        = {latex(sold_units_symb)} \\cdot {latex(unit_cost_symb)}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        
        soln_stmt = f"""
        $\\displaystyle 
        \\begin{{aligned}}
        
        &\\implies
        {latex(bought_units)} * {latex(unit_cost_symb)}
        = {latex(100 + gain_loss_percent)} \%
        \\cdot
        {latex(sold_units_symb)} \\cdot {latex(unit_cost_symb)}
        \\qquad \\cdots \\text{{ from (i) }}
        \\\\ \\\\
        
        &
        \\implies
        {latex(bought_units)}
        = {latex(100 + gain_loss_percent)} \%
        \\cdot
        {latex(sold_units_symb)}
        \\\\ \\\\
        
        &
        \\implies
        {latex(100 + gain_loss_percent)} \%
        \\cdot
        {latex(sold_units_symb)}
        = {latex(bought_units)}
        \\\\ \\\\
        
        &
        \\implies
        {latex(sold_units_symb)}
        
        =
        \\frac {{ {latex(bought_units)} }} {{ {latex(100 + gain_loss_percent)} \% }}
        \\\\ \\\\
        
        &
        \\implies
        {latex(sold_units_symb)}
        =
        \\frac {{ {latex(bought_units)} }} {{ {latex(100 + gain_loss_percent)} \% }}
        \\times \\frac {{ 100 \% }} {{ 1 }} 
        \\qquad \\text {{ as 100 % = 1 }}
        \\\\ \\\\
        
        &
        \\implies
        {latex(sold_units_symb)}
        =
        \\frac {{ {latex(bought_units)} \\times 100 }} {{ {latex(100 + gain_loss_percent)} }}
        \\\\ \\\\
        
        &
        \\implies
        {latex(sold_units_symb)}
        = {latex(round(bought_units * 100 / (100 + gain_loss_percent), 2))}
        \\\\ \\\\
        
        &
        \\implies
        {latex(sold_units_symb)}
        = {latex(sold_units)}
        \\qquad \\cdots \\text{{ round off to an integer }}
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        
        
        
        answer_s = f"""
        $\\displaystyle
        {latex(sold_units)} 
        $
        """
        
        soln_s = "\n\n".join(soln_stmts)
        
        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        pass

    def get_model_type_gain_loss_percent(self, **kwargs):
        self._log(f"Starting {self.__class__.__name__}.{inspect.stack()[0][3]} ...")
        
        data_dict = self._get_cost_repair_sell_data()
        money_unit = data_dict.get('money_unit')
        cost = data_dict.get('cost')
        addl_cost = data_dict.get('addl_cost')
        sell_price = data_dict.get('sell_price')
        tot_cost = data_dict.get('tot_cost')
        gain_loss = data_dict.get('gain_loss')
        abs_gain_loss = data_dict.get('abs_gain_loss')
        abs_gain_loss_percent = data_dict.get('abs_gain_loss_percent')
        gain_loss_text = data_dict.get('gain_loss_text')
        
            
        prob_s = f"""
        A person buys an item for ${latex(money_unit)} {latex(cost)}$ 
        and spends ${latex(money_unit)} {latex(addl_cost)}$ on its repairs. 
        If he sells that item for ${latex(money_unit)} {latex(sell_price)}$ , 
        what is his gain or loss percent?
        """
        prob_s = prob_s.replace("\n", " ")
                
        soln_s = f"""
        The person buys an item for $
        {latex(money_unit)} {latex(cost)}$ and spends $
        {latex(money_unit)} {latex(addl_cost)}$ on its repairs.
        
        $\\displaystyle
        \\implies \\text {{ Total cost }} 
        = 
        {latex(money_unit)} \\left( {latex(cost)} + {latex(addl_cost)} \\right)
        = 
        {latex(money_unit)} {latex(tot_cost)}
        $
        
        He sells that item for ${latex(money_unit)} {latex(sell_price)}$
        
        $\\displaystyle 
        \\begin{{aligned}}

        \\text {{Therefore, gain or loss }}
        &
        = \\text {{ (sell price) - (total cost) }}  
        \\\\ \\\\
        
        &
        = {latex(money_unit)} {latex(sell_price)} 
        - {latex(money_unit)} {latex(tot_cost)} 
        \\\\ \\\\
        
        &
        = {latex(money_unit)} {latex(gain_loss)} 
        
        \\end{{aligned}}
        $

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

        \\text {{Therefore, the person makes a {gain_loss_text} }}
        &
        = \\text {{ {latex(money_unit)} {latex(abs_gain_loss)} on total cost }}  
        \\\\ \\\\
        
        &
        = \\text {{ {latex(money_unit)} {latex(abs_gain_loss)} on {latex(money_unit)} {latex(tot_cost)} }}  
        \\\\ \\\\
        
        &
        = \\frac {{ {latex(abs_gain_loss)} }} {{ {latex(tot_cost)} }}
        \\\\ \\\\
        
        &
        = \\frac {{ {latex(abs_gain_loss)} }} {{ {latex(tot_cost)} }} 
        \\times \\frac {{ 100 \% }} {{ 1 }} 
        \\qquad \\text {{ as 100 % = 1 }}
        
        \\\\ \\\\
        
        &
        = {latex(abs_gain_loss_percent)} \% 
        
        \\end{{aligned}}
        $
        """
        
        answer_s = f"""
        $\\displaystyle 
        {latex(abs_gain_loss_percent)} \% 
        $
        """
        
        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        pass

    def get_model_type_purchase_cost(self, **kwargs):
        self._log(f"Starting {self.__class__.__name__}.{inspect.stack()[0][3]} ...")
        
        data_dict = self._get_cost_repair_sell_data()
        money_unit = data_dict.get('money_unit')
        cost = data_dict.get('cost')
        addl_cost = data_dict.get('addl_cost')
        sell_price = data_dict.get('sell_price')
        tot_cost = data_dict.get('tot_cost')
        gain_loss = data_dict.get('gain_loss')
        abs_gain_loss = data_dict.get('abs_gain_loss')
        gain_loss_percent = data_dict.get('gain_loss_percent')
        abs_gain_loss_percent = data_dict.get('abs_gain_loss_percent')
        gain_loss_text = data_dict.get('gain_loss_text')
        
        cost_symbol = Symbol('x')
        tot_cost_expr = cost_symbol + addl_cost
        gain_loss_expr = sell_price - tot_cost_expr
        
        purchase_cost_symb = Symbol(f"\\text {{ purchase cost }}")
        
        if gain_loss_percent < 0:
            gain_loss_percent_op = "-"
        else:
            gain_loss_percent_op = "+"
            
        
        prob_s = f"""
        A person buys an item for some amount 
        and spends ${latex(money_unit)} {latex(addl_cost)}$ on its repairs. 
        If he sells that item for ${latex(money_unit)} {latex(sell_price)}$ , 
        his {gain_loss_text} percent is ${latex(abs_gain_loss_percent)}$. 
        Find his cost of purchase.
        """
        prob_s = prob_s.replace("\n", " ")
                
        soln_s = f"""
        His {gain_loss_text} percent is ${latex(abs_gain_loss_percent)}$.

        $\\displaystyle 
        \\begin{{aligned}}
        
        
        \\text {{Therefore, his sell price }} 
        &
        = \\left( 100 \% {gain_loss_percent_op} {latex(abs_gain_loss_percent)} \% \\right)
        \\text {{ of total cost }} 
        \\\\ \\\\

        &
        = {latex(100 + gain_loss_percent)} \%
        \\text {{ of total cost }} 
        
        \\end{{aligned}}
        $

        He sells that item for ${latex(money_unit)} {latex(sell_price)}$.
        
        $\\displaystyle 
        \\begin{{aligned}}
        
        &
        \\implies
        {latex(100 + gain_loss_percent)} \%
        \\text {{ of total cost }} 
        = {latex(money_unit)} {latex(sell_price)}
        \\\\ \\\\
        
        &
        \\implies
        {latex(100 + gain_loss_percent)} \%
        \\text {{ of total cost }} 
        \\times \\frac {{ 1 }} {{ 100 \% }} 
        = {latex(money_unit)} {latex(sell_price)}
        \\qquad \\text {{ as 100 % = 1 }}
        \\\\ \\\\
        
        &
        \\implies
        \\text {{ Total cost }} 
        = 
        {latex(money_unit)} {latex(sell_price)}
        \\times \\frac {{ 100 }} {{ {latex(100 + gain_loss_percent)} }} 
        \\\\ \\\\
        
        &
        \\implies
        \\text {{ Total cost }} 
        = 
        {latex(money_unit)} {latex(tot_cost)}
        
        \\end{{aligned}}
        $
        
        He spends ${latex(money_unit)} {latex(addl_cost)}$ on its repairs.
        
        $\\displaystyle 
        \\begin{{aligned}}

        &\\implies
        {latex(purchase_cost_symb)} + \\text {{ repair cost }}  
        = \\text {{total cost}}
        \\\\ \\\\
        
        &\\implies
        {latex(purchase_cost_symb)}  + {latex(money_unit)} {latex(addl_cost)} 
        = {latex(money_unit)} {latex(tot_cost)}
        \\\\ \\\\
        
        &\\implies
        {latex(purchase_cost_symb)}
        = {latex(money_unit)} {latex(tot_cost)} - {latex(money_unit)} {latex(addl_cost)} 
        \\\\ \\\\
        
        &\\implies
        {latex(purchase_cost_symb)}
        = {latex(money_unit)} {latex(cost)}  
        
        \\end{{aligned}}
        $
        """
        
        answer_s = f"""
        $\\displaystyle 
        {latex(money_unit)} {latex(cost)} 
        $
        """
        
        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        pass


    def get_model_sales_price_profit_percent_relation(self, **kwargs):
        self._log(f"Starting {self.__class__.__name__}.{inspect.stack()[0][3]} ...")
        
        cost_price_symb = Symbol('c')
        profit_symb = Symbol('p')
        
        sp_mul = random.randint(2, 3)
        p_mul = sp_mul + random.randint(1, 3)
        
        profit_percent = (sp_mul - 1) * 100 / (p_mul - sp_mul)
        profit_percent = round(profit_percent, 2)

        
        prob_s = f"""
        If selling price is increases by ${sp_mul}$-times, the profit becomes ${p_mul}$-times. 
        Find the profit percent.
        """
        prob_s = prob_s.replace("\n", " ")
              
        soln_stmts = []
                
        soln_stmt = f"""
        Let: 
        cost $ = {latex(cost_price_symb)} $
        profit $ = {latex(profit_symb)} $
        selling price $ = {latex(cost_price_symb + profit_symb)} $
        
        """
        soln_stmts.append(soln_stmt.strip())

                
        soln_stmt = f"""
        If selling price is increases by ${sp_mul}$-times, the profit becomes ${p_mul}$-times. 
        
        $\\displaystyle 
        \\begin{{aligned}}
        
        \\text{{ Cost }}
        & = \\text{{ (new selling price) }} - \\text{{ (new profit) }}
        \\\\ \\\\
        
        & = {latex(sp_mul)} \\times \\left( {latex(cost_price_symb + profit_symb)} \\right)
        - {latex(p_mul)} \\times {latex(profit_symb)}
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        soln_stmt = f"""
        $\\displaystyle 
        \\begin{{aligned}}
        
        & \\implies {latex(cost_price_symb)}
        = {latex(sp_mul)} \\times \\left( {latex(cost_price_symb + profit_symb)} \\right)
        - {latex(p_mul)} \\times {latex(profit_symb)}
        \\\\ \\\\
        
        & \\implies {latex(cost_price_symb)}
        = {latex(sp_mul * cost_price_symb)} + {latex(sp_mul *  profit_symb)} 
        - {latex(p_mul * profit_symb)}
        \\\\ \\\\
        
        & \\implies {latex(cost_price_symb - sp_mul * cost_price_symb)}
        = {latex(sp_mul *  profit_symb - p_mul * profit_symb)} 
        \\\\ \\\\
        
        & \\implies {latex(p_mul * profit_symb - sp_mul *  profit_symb)} 
        = {latex(sp_mul * cost_price_symb - cost_price_symb)}
        \\\\ \\\\
        
        & \\implies 
        \\frac {{ {latex(profit_symb)} }} {{ {latex(cost_price_symb)} }}
        = \\frac {{ {latex(sp_mul - 1)} }} {{ {latex(p_mul - sp_mul)} }}
        \\\\ \\\\
        
        & \\implies 
        \\frac {{ {latex(profit_symb)} }} {{ {latex(cost_price_symb)} }}
        = \\frac {{ {latex(sp_mul - 1)} }} {{ {latex(p_mul - sp_mul)} }}
        \\times \\frac {{ 100 \% }} {{ 1 }}
        \\qquad \\text {{ as 100 % = 1 }}
        \\\\ \\\\
        
        & \\implies 
        \\frac {{ {latex(profit_symb)} }} {{ {latex(cost_price_symb)} }}
        = {latex( profit_percent  )}  \%
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        soln_stmt = f"""
        $\\displaystyle \\text {{Therefore, profit on cost }} = {latex( profit_percent  )}  \% $
        """
        soln_stmts.append(soln_stmt.strip())

        soln_s = "\n\n".join(soln_stmts)
        
        answer_s = f"""
        $\\displaystyle {latex( profit_percent  )}  \% $
        """
        
        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        pass

    def _get_cost_repair_sell_data(self, **kwargs):
        self._log(f"Starting {self.__class__.__name__}.{inspect.stack()[0][3]} ...")

        cost = random.choice([
            random.randint(1, 9),
            random.randint(10, 99),
            random.randint(100, 999),
            ]) * random.choice([10, 100])
            
            
        addl_cost = cost * random.choice([5, 10, 20, 25, 50, 100, 125, 150]) / 100
        addl_cost = int(round(addl_cost, 0))
        
        tot_cost = cost + addl_cost
            
        gain_loss_percent = random.choice([
            random.randint(1, 4) * 5,
            random.randint(1, 20)
            ]) * random.choice([-1, 1])
        
        gain_loss = tot_cost * gain_loss_percent / 100
        gain_loss = round(gain_loss, 2)
        abs_gain_loss = abs(gain_loss)
        
        gain_loss_percent = round(100 * gain_loss / tot_cost, 2)
        abs_gain_loss_percent = round(100 * abs_gain_loss / tot_cost, 2)
        
        sell_price = tot_cost + gain_loss

        if gain_loss < 0:
            gain_loss_text = "loss"
        else:
            gain_loss_text = "gain"
        
        money_unit = Symbol(r'\$ ')

        data_dict = {
            'money_unit': money_unit,
            'cost': cost,
            'addl_cost': addl_cost,
            'sell_price': sell_price,
            'tot_cost': tot_cost,
            'gain_loss': gain_loss,
            'abs_gain_loss': abs_gain_loss,
            'gain_loss_percent': gain_loss_percent,
            'abs_gain_loss_percent': abs_gain_loss_percent,
            'gain_loss_text': gain_loss_text,
            }
        
        return data_dict
        pass

    def get_random_model(self, **kwargs):
        self._log(f"Starting {self.__class__.__name__}.{inspect.stack()[0][3]} ...")

        methods = [self.model_price_dicount_1,
                   self.model_price_dicount_2,
                   self.model_price_dicount_3,
                   self.model_price_dicount_tax,
                   ]
        
        method = random.choice(methods)
        random_model = method()
    
        var_names = random_model['var_names']
        var_vals = random_model['var_vals']
        
        
        solutions = random_model['solutions']
        
        var_names_str = [f"${latex(k)}: {latex(v)}$" for k,v in var_names.items()]
        var_names_str = "\n".join(var_names_str)
        
        solutions_str = [f"${latex(k)}: {latex(v)}$" for k,v in solutions.items()]
        solutions_str = "\n".join(solutions_str)
        
        prob_s = f"""
        {random_model['statement_header']}
        {random_model['statement']}
        {random_model['statement_footer']}
        """
                
        soln_s = f"""
        <b>Let:</b>
        {var_names_str}
        
        <b>Equations:</b>
        {random_model['eqs_str']}
        
        <b>Solving the equations:</b>
        {solutions_str}
        """
        
        answer_s = f"""
        {solutions_str}
        """
        
        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        pass

    def model_price_dicount_1(self, **kwargs):
        self._log(f"Starting {self.__class__.__name__}.{inspect.stack()[0][3]} ...")

        p, dr, d, dp = symbols('p, r_d, d, p_d')
        
        var_names = {p: 'price',
                     dr: 'discount rate (%)',
                     d: 'discount',
                     dp: 'discounted price',
                     }
        
        p_val = 100 * random.randint(1, 20)
        dr_val = random.randint(1, 20)
        d_val = p_val * dr_val / 100
        dp_val = p_val - d_val

        vals_dict = {p: p_val,
                    dr: dr_val,
                    d: d_val,
                    dp: dp_val,
                   }
        
        var_vals = self._get_filtered_dict(vals_dict, [p, dr])
        solutions = self._get_filtered_dict(vals_dict, [d, dp])
        
        statement_header = ''
        statement = f"""
        The price of an item is ${latex(p_val)}$ and the discount on offer is ${latex(dr_val)}$%. 
        """
        statement_footer = "Find the discount and the discounted price."

        eq1 = Eq(dp, p - d )
        latex_eq1 = LatexEquation(f"{dp}",
                                  f"{p} - {d}",
                                  eq1)
        
        eq2 = Eq(d, p * dr / 100 )
        latex_eq2 = LatexEquation(f"{d}",
                                  f"{p} * {latex_fraction(dr, 100)}",
                                  eq2)

        latex_eqs = [latex_eq1, latex_eq2]
        eqs_str = self._get_eqs_str(latex_eqs)
        

        random_model = {
            'statement_header': statement_header,
            'statement': statement,
            'statement_footer': statement_footer,
            'var_names': var_names,
            'latex_eqs': latex_eqs,
            'eqs_str': eqs_str,
            'var_names': var_names,
            'var_vals': var_vals,
            'solutions': solutions,
            }
        
        return random_model
        pass

    def model_price_dicount_2(self, **kwargs):
        self._log(f"Starting {self.__class__.__name__}.{inspect.stack()[0][3]} ...")

        p, dr, d, dp = symbols('p, r_d, d, p_d')
        
        var_names = {p: 'price',
                     dr: 'discount rate (%)',
                     d: 'discount',
                     dp: 'discounted price',
                     }
        
        p_val = 100 * random.randint(1, 20)
        dr_val = random.randint(1, 20)
        d_val = p_val * dr_val / 100
        dp_val = p_val - d_val

        vals_dict = {p: p_val,
                    dr: dr_val,
                    d: d_val,
                    dp: dp_val,
                   }
        
        var_vals = self._get_filtered_dict(vals_dict, [p, dp])
        solutions = self._get_filtered_dict(vals_dict, [dr, d])
        
        statement_header = ''
        statement = f"""
        The price of an item is ${latex(p_val)}$ and the discounted price is ${latex(dp_val)}$. 
        """
        statement_footer = "Find the discount rate and the discount."

        eq1 = Eq(dp, p - d )
        latex_eq1 = LatexEquation(f"{dp}",
                                  f"{p} - {d}",
                                  eq1)
        
        eq2 = Eq(d, p * dr / 100 )
        latex_eq2 = LatexEquation(f"{d}",
                                  f"{p} * {latex_fraction(dr, 100)}",
                                  eq2)

        latex_eqs = [latex_eq1, latex_eq2]
        eqs_str = self._get_eqs_str(latex_eqs)
        

        random_model = {
            'statement_header': statement_header,
            'statement': statement,
            'statement_footer': statement_footer,
            'var_names': var_names,
            'latex_eqs': latex_eqs,
            'eqs_str': eqs_str,
            'var_names': var_names,
            'var_vals': var_vals,
            'solutions': solutions,
            }
        
        return random_model
        pass

    def model_price_dicount_3(self, **kwargs):
        self._log(f"Starting {self.__class__.__name__}.{inspect.stack()[0][3]} ...")

        p, dr, d, dp = symbols('p, r_d, d, p_d')
        
        var_names = {p: 'price',
                     dr: 'discount rate (%)',
                     d: 'discount',
                     dp: 'discounted price',
                     }
        
        p_val = 100 * random.randint(1, 20)
        dr_val = random.randint(1, 20)
        d_val = p_val * dr_val / 100
        dp_val = p_val - d_val

        vals_dict = {p: p_val,
                    dr: dr_val,
                    d: d_val,
                    dp: dp_val,
                   }
        
        var_vals = self._get_filtered_dict(vals_dict, [d, dp])
        solutions = self._get_filtered_dict(vals_dict, [dr, p])
        
        statement_header = ''
        statement = f"""
        The discount and the discounted price are  ${latex(d_val)}$ and ${latex(dp_val)}$ respectively. 
        """
        statement_footer = "Find the discount rate and the original price."

        eq1 = Eq(dp, p - d )
        latex_eq1 = LatexEquation(f"{dp}",
                                  f"{p} - {d}",
                                  eq1)
        
        eq2 = Eq(d, p * dr / 100 )
        latex_eq2 = LatexEquation(f"{d}",
                                  f"{p} * {latex_fraction(dr, 100)}",
                                  eq2)

        latex_eqs = [latex_eq1, latex_eq2]
        eqs_str = self._get_eqs_str(latex_eqs)
        

        random_model = {
            'statement_header': statement_header,
            'statement': statement,
            'statement_footer': statement_footer,
            'var_names': var_names,
            'latex_eqs': latex_eqs,
            'eqs_str': eqs_str,
            'var_names': var_names,
            'var_vals': var_vals,
            'solutions': solutions,
            }
        
        return random_model
        pass

    def model_price_dicount_tax(self, **kwargs):
        self._log(f"Starting {self.__class__.__name__}.{inspect.stack()[0][3]} ...")

        p, dr, d, dp, tr, t, pat  = symbols('p, r_d, d, p_d, r_t, t, p_t')
        
        var_names = {p: 'price',
                     dr: 'discount rate (%)',
                     d: 'discount',
                     dp: 'discounted price',
                     tr: 'tax rate (%)',
                     t: 'tax',
                     pat: 'price after tax',
                     }
        
        p_val = 100 * random.randint(1, 20)
        dr_val = random.randint(1, 20)
        d_val = p_val * dr_val /100
        dp_val = p_val - d_val
        
        tr_val = random.randint(1, 20)
        t_val = dp_val * tr_val / 100
        pat_val = dp_val + t_val
        
        vals_dict = {p: p_val,
                    dr: dr_val,
                    tr: tr_val,
                    d: d_val,
                    dp: dp_val,
                    t: t_val,
                    pat: pat_val,
                   }
        
        var_vals = self._get_filtered_dict(vals_dict, [p, dr, tr])
        solutions = self._get_filtered_dict(vals_dict, [d, dp, t, pat])
        
        statement_header = ''

        statement = f"""
        The price of an item is ${latex(p_val)}$ and the discount offer is ${latex(dr_val)}$%. 
        The rate of tax on the discounted price is ${latex(tr_val)}$%.
        """
        
        statement_footer = "Find the price after tax."


        eq1 = Eq(dp, p - d )
        latex_eq1 = LatexEquation(f"{dp}",
                                  f"{p} - {d}",
                                  eq1)
        
        eq2 = Eq(d, p * dr / 100 )
        latex_eq2 = LatexEquation(f"{d}",
                                  f"{p} * {latex_fraction(dr, 100)}",
                                  eq2)

        eq3 = Eq(pat, dp + t )
        latex_eq3 = LatexEquation(f"{pat}",
                                  f"{dp} - {t}",
                                  eq3)
        
        eq4 = Eq(t, dp * tr / 100 )
        latex_eq4 = LatexEquation(f"{t}",
                                  f"{dp} * {latex_fraction(tr, 100)}",
                                  eq4)

        latex_eqs = [latex_eq1, latex_eq2, latex_eq3, latex_eq4]
        eqs_str = self._get_eqs_str(latex_eqs)
        

        random_model = {
            'statement_header': statement_header,
            'statement': statement,
            'statement_footer': statement_footer,
            'var_names': var_names,
            'latex_eqs': latex_eqs,
            'eqs_str': eqs_str,
            'var_names': var_names,
            'var_vals': var_vals,
            'solutions': solutions,
            }
        
        return random_model
        pass
    
    def _get_eqs_str(self, latex_eqs):
        eqs_str = [f'$\\displaystyle {latex_eq}$' for latex_eq in latex_eqs]
        eqs_str = "\n\n".join(eqs_str)
        return eqs_str
        pass
    
    def _get_filtered_dict(self, dct, keys):
        return {k:dct[k] for k in keys}
        pass
    
    def get_model_percent_based_equations(self, **kwargs):
        self._log(f"Starting {self.__class__.__name__}.{inspect.stack()[0][3]} ...")

        methods = [self._get_model_percent_based_equations_type_a,
                   
                   ]
        method = random.choice(methods)
        return method()
             
    def _get_model_percent_based_equations_type_a(self, **kwargs):
        self._log(f"Starting {self.__class__.__name__}.{inspect.stack()[0][3]} ...")
        
        money_unit = Symbol(r'\$ ')
        num_w_bags = random.randint(1, 10) * random.choice([10, 100])
        num_g_bags = random.randint(1, 10) * random.choice([10, 100])
        num_bags = num_w_bags + num_g_bags

        sell_price_symb = Symbol('x')
        
        profit_per_w_bag = random.randint(1, 10) * random.choice([1, 10])
        profit_per_g_bag = - random.randint(1, 10) * random.choice([1, 10])
        
        gain_loss_w_bags = num_w_bags * profit_per_w_bag 
        gain_loss_g_bags = num_g_bags * profit_per_g_bag 
        
        gain_loss = gain_loss_w_bags + gain_loss_g_bags

        if gain_loss < 0:
            gain_loss_text = "loss"
            gain_loss_percent_op = "-"
        else:
            gain_loss_text = "gain"
            gain_loss_percent_op = "+"
            
             
        prob_s = f"""
        A cement company earns a profit of 
        ${latex(money_unit)} {latex(profit_per_w_bag)}$ per bag of white cement sold
        and a loss of 
        ${latex(money_unit)} {latex(abs(profit_per_g_bag))}$ per bag of grey cement sold. 
        They both are sold at same price. 
        The company sells ${latex(num_w_bags)}$ bags of white cement 
        and ${latex(num_g_bags)}$ bags of grey cement in a month. 
        What is the value of total profit or loss in a month?
        """
        prob_s = prob_s.replace("\n", " ").strip()

                
        soln_stmts = []  
        
        soln_stmt = f"""
        $\\displaystyle 
        \\begin{{aligned}}

        \\text{{Net profit }} 
        & = {latex(num_w_bags)} \\cdot {latex(money_unit)} {latex(profit_per_w_bag)}  
        - {latex(num_g_bags)} \\cdot {latex(money_unit)} {latex(abs(profit_per_g_bag))}  
        \\\\ \\\\
        
        & = {latex(money_unit)} {latex(gain_loss_w_bags)}   
        - {latex(money_unit)} {latex(abs(gain_loss_g_bags))}
        \\\\ \\\\
        
        & = {gain_loss_percent_op} {latex(money_unit)} {latex(abs(gain_loss))}   
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        
        soln_stmt = f"""
        Therefore, the net {gain_loss_text} per month is $\\displaystyle
        {latex(money_unit)} {latex(abs(gain_loss))}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        
        soln_stmt = f"""
        The net {gain_loss_text} per month is $\\displaystyle
        {latex(money_unit)} {latex(abs(gain_loss))}
        $
        """
        soln_stmts.append(soln_stmt.strip())

        
        answer_s = f"""
        The net {gain_loss_text} per month is $\\displaystyle
        {latex(money_unit)} {latex(abs(gain_loss))}
        $
        """
        
        soln_s = "\n\n".join(soln_stmts)
        
        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        
        return result
        pass

    
    pass
#class ModellingPercentageHelper:
