'''
Created on 12-Jul-2021

@author: bthakur
'''
import random
import inspect
import traceback

import numpy as np
import sympy as sp

from sympy import (symbols, Symbol, pi, latex, sympify,
                   Rational, Derivative, simplify,
                   UnevaluatedExpr as ueval
                   )
from xv.km.util import latex_fraction, wrap_in_latex
from xv.util import number_to_nth, list_to_str


from  xv.util import UtilClass

# _putnam_level_01_helper
# PutnamLevel01Helper


class PutnamLevel01Helper(UtilClass):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        pass
    
    def get_random_problem(self, **kwargs):
        '''
        '''
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        index = kwargs.get('index', None)
        
        try:
            
            random_methods = [
                self._problem_2001_a1,
                self._problem_2001_a2,
                self._problem_dec_2004_a1,
                self._problem_dec_2004_a2,
                ]
            
            if index is None:
                random_choices = [x for x in  range(len(random_methods))]
        
                identifier = f"{self.__class__.__name__}{id(self)}.{inspect.stack()[0][3]}.random_choices"
                random_choice = self.get_no_repeat_random_choice(identifier, random_choices)
            else:
                random_choice = index
            
            random_method = random_methods[random_choice]
            
            return random_method(**kwargs)
        
        except Exception as e:
            sError = f"""Error {self.__class__.__name__}.{inspect.stack()[0][3]}: {e}
            """
            self._log(sError)
            self._log(traceback.format_exc())
            pass
        
        return None
        pass

    def _problem_2001_a1(self, **kwargs):
        
        prob_s = f"""
        Consider a set $S$ and a binary operation $*
        $, that is, for each $a, b \\in S, a * b \\in S$. 
        
        Assume $(a * b) * a=b$ for all $a, b \\in S$. 
        
        Prove that $a *(b * a)=b$ for all $a, b \\in S$.
        
        """
        prob_s = prob_s.strip()
        
        soln_stmts = []
        
        soln_stmt = f"""
        for all $a, b \\in S$: 
        
        $\\displaystyle 
        \\begin{{aligned}}
        
        & (a * b) * a = b
        \\qquad \\cdots \\text{{ (i) }} 
        \\\\ \\\\
        
        
        \\implies &  ((b * a) * b) * (b * a) = b
        \\qquad \\text{{ replaced }} a \\text{{ with }} b * a \\; \\text {{ as }} \\;  b * a \\in S
        \\\\ \\\\
        
        \\implies &  a * (b * a) = b
        \\qquad  \\text{{ using (i), }} \\;  (b * a) * b = a
        \\\\ \\\\
        
        \\end{{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        

        answer_s = f"""
        See solution.
        """
        
        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 _problem_2001_a2(self, **kwargs):
        
        prob_s = f"""
        You have coins $C_1, C_2, \\ldots, C_n$. For each $k, C_k
        $ is biased so that, when tossed, it has probability 
        $\\displaystyle \\frac{{1}}{{2 k+1}}$ of falling heads. 
        
        If the $n$ coins are tossed, what is the probability that the number of heads is odd? 
        Express the answer as a rational function of $n$.
        """
        prob_s = prob_s.strip()
        
        soln_stmts = []
        
        soln_stmt = f"""
        Let $P_n$ denote the desired probability. 
        
        Then $\\displaystyle 
        P_1 
        = \\frac{{1}}{{2 \\cdot 1+1}}
        = \\frac{{1}}{{3}}
        $ 
        
        For $j > 1
        $ there are two ways to obtain an odd number of heads when flipping the first $j$ coins:
        1.  either there were an odd number of heads in the first $j-1
        $ coins, and the $j$ th coin is a head, or 
        2. there were an even number of heads in the first $j-1
        $ coins, and the $j$ th coin is a tail. Thus for $j > 1$.
        
        $\\displaystyle 
        \\begin{{aligned}}
        P_j 
        & = \\left(\\frac{{2 j}}{{2 j+1}}\\right) P_{{j-1}}
        + \\left(\\frac{{1}}{{2 j+1}}\\right)\\left(1-P_{{j-1}}\\right) 
        \\\\ \\\\
        
        & =\\left(\\frac{{2 j-1}}{{2 j+1}}\\right) P_{{j-1}}+\\frac{{1}}{{2 j+1}}
        
        \\end{{aligned}}
        $
        
        
        The recurrence yields $P_2=2 / 5, P_3=3 / 7$, and simple induction shows that $P_j=$ $j /(2 j+1)$ for general $j$.
        
        """
        soln_stmts.append(soln_stmt.strip())
        

        answer_s = f"""
        $\\displaystyle \\frac {{n}} {{(2 n+1)}}$
        """
        
        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 _problem_dec_2004_a1(self, **kwargs):

        m, N = symbols('m, N')
        S = sp.Function('S')(N)
        percent = 80
        percent_frac = Rational(80, 100)
        
        prob_s = f"""
        
        Basketball star Shanille O'Keal's team statistician keeps track of the number, $
        {latex(S)}$, of successful free throws she has made in her first ${latex(N)}
        $ attempts of the season. Early in the season, ${latex(S)}
        $ was less than ${latex(percent)} \\%$ of ${latex(N)}$, but by the end of the season, ${latex(S)}
        $ was more than ${latex(percent)} \\%$ of ${latex(N)}
        $. Was there necessarily a moment in between when ${latex(S)}
        $ was exactly ${latex(percent)} \\%$ of ${latex(N)}$ ?
        
        """
        prob_s = prob_s.strip()
        
        soln_stmts = []
        
        
        soln_stmt = f"""
        Suppose there was not any moment in between when ${latex(S)}
        $ was exactly ${latex(percent)} \\%$ of ${latex(N)}$.
        
        Let she makes ${latex(m)}$ of her first ${latex(N)}
        $ when her percentage is under ${latex(percent)} \\%$.
        
        $\\displaystyle 
        {latex(m / N)} < {latex(percent_frac)}
        
        \\implies 5 {latex(m)} < 4 {latex(N)} 
        \\qquad \\cdots \\text {{ (i) }}
        $
        
        In next throw, her percentage is above ${latex(percent)} \\%$.
        
        $\\displaystyle 
        {latex((m + 1) / (N + 1))} > {latex(percent_frac)}
        
        \\implies 5 ({latex(m + 1)}) > 4 ({latex(N + 1)})
        
        \\implies 5 {latex(m + 1)} > 4 {latex(N)} 

        \\implies 4 {latex(N)} < 5 {latex(m + 1)} 
        \\qquad \\cdots \\text {{ (ii) }}
        $
        
        from (i) and (ii): 
        
        $\\displaystyle \\implies 5 {latex(m)} < 4 {latex(N)} < 5 {latex(m + 1)} $ which is impossible.
        
        So, our assumption is wrong. Hence, there must have been a moment in between when ${latex(S)}
        $ was exactly ${latex(percent)} \\%$ of ${latex(N)}$.
        """
        soln_stmts.append(soln_stmt.strip())
        

        answer_s = f"""
        Yes
        """
        
        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 _problem_dec_2004_a2(self, **kwargs):

        a1, b1, c1 = sp.symbols("a1, b1, c1")
        a2, b2, c2 = sp.symbols("a2, b2, c2")

        A1, T1 = sp.symbols("A1, T1")
        A2, T2 = sp.symbols("A2, T2")

        prob_s = f"""
        
        For $i=1,2$ let $T_i$ be a triangle with side lengths $a_i, b_i, c_i
        $, and area $A_i$. 
        
        Suppose that $
        {latex(a1)} \\leq {latex(a2)}, {latex(b1)} \\leq {latex(b2)}, {latex(c1)} \\leq {latex(c2)}
        $, and that ${latex(T2)}
        $ is an acute triangle. Does it follow that $
        {latex(A1)} \\leq {latex(A2)}$ ?
        
        """
        prob_s = prob_s.strip()
        
        soln_stmts = []
        
        
        soln_stmt = f"""

    
        For $i=1,2$, let $P_i, Q_i, R_i$ be the vertices of $T_i
        $ opposite the sides of length $a_i, b_i, c_i
        $, respectively. 
        
        Since the angle measures in any triangle add up to $\\pi
        $, some angle of ${latex(T1)}
        $ must have measure less than or equal to its counterpart in $
        {latex(T2)}$. Without loss of generality assume that $
        \\angle P_1 \\leq \\angle P_2
        $. Since the latter is acute (because ${latex(T2)}
        $ is acute), it follows that $\\sin \\angle P_1 \\leq \\sin \\angle P_2
        $. Then

        $\\displaystyle 
        {latex(A1)}
        = \\frac{{1}}{{2}} {latex(b1)} {latex(c1)} \\sin \\angle P_1 
        \\leq \\frac{{1}}{{2}} {latex(b2)} {latex(c2)} \\sin \\angle P_2
        = {latex(A2)}
        $


        """
        soln_stmts.append(soln_stmt.strip())
        

        answer_s = f"""
        Yes
        """
        
        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 PutnamLevel01Helper:
