\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{pythontex-tools}[2022/06/24]

%%% MAJ du 2022/06/23 : amélioration de la fonction calculant l'intégrale 
%%% MAJ du 2022/06/24 : ajout des séparateurs de milliers dans tous les affichages + remplacement des points par des ";" partout

\@ifpackageloaded{pythontex}{}{\RequirePackage{pythontex}}
\@ifpackageloaded{amsmath}{}{\RequirePackage{amsmath}}
\@ifpackageloaded{amssymb}{}{\RequirePackage{amssymb}}
\@ifpackageloaded{xargs}{}{\RequirePackage{xargs}}
\@ifpackageloaded{longtable}{}{\RequirePackage{longtable}}
\@ifpackageloaded{numprint}{}{\RequirePackage{numprint}}
\RequirePackage[table]{xcolor}

%%%%%%%% Décomposition en produit de facteurs premiers
%%%%%%%% Syntaxe : \ifactors[<True|False>]{nombre}

\newcommandx\ifactors[2][1=False]{\py{decompose(#2,#1)}}

\begin{pycode}
def decompose (n , table = False):
    factors_list = []
    i = 2
    val = n
    while n>1:
        exposant = 0
        while n%i == 0:
            exposant = exposant + 1
            n = n/i
        if exposant != 0:
            factors_list.append([i,exposant])
        i = i+1

    if table == False:
        decomp = str(factors_list[0][0]) + '^' + str(factors_list[0][1])
        for i in range(1,len(factors_list)):
            if factors_list[i][1] != 0:
                decomp = decomp + '\\times' + str(factors_list[i][0])+'^'+str(factors_list[i][1])
        return '\\ensuremath{' + decomp + '}'
    else:
        result = '\\begin{tabular}{r|l}'
        n = val
        for prime in factors_list:
            for f in range(prime[1]):
                result = result+str(n)+'&'+str(prime[0])+'\\\\'
                n = n//prime[0]
        return result + '1&\\\\\\end{tabular}'
\end{pycode}


%%%%%%%% Simplification de racines carrées
%%%%%%%% Syntaxe : \simpsqrt{nombre}

\newcommand\simpsqrt[1]{\begingroup\edef\x{\endgroup\noexpand\py{simpsqrt(#1)}}\x}

\begin{pycode}
def simpsqrt(n):
    if n**0.5 == int( n**0.5 ):
        return int( n**0.5 )
    else:
        a , b = 1 , n
        for k in range(2,int(n**0.5)):
            if b % (k*k) == 0: # si k² divise n
                a = k
                b = b // (k*k)
            
        if a == 1 and  b != 1:
            return '\\ensuremath{\\sqrt{' + str(b) + '}}'
        else:
            return '\\ensuremath{' + str(a) + '\\sqrt{' + str(b) + '}}'
\end{pycode}

%%%%%%%% PGCD
%%%%%%%% Syntaxe : \gcd{nombre}

\renewcommand\gcd[1]{\begingroup\edef\x{\endgroup\noexpand\py{gcd(#1)}}\x}

\begin{pycode}
from math import *
\end{pycode}

%%%%%%%% Simplification de fractions
%%%%%%%% Syntaxe : \simpfrac{a,b}

\newcommand\simpfrac[1]{\begingroup\edef\x{\endgroup\noexpand\py{simpfrac(#1)}}\x}
\newcommand\simpdfrac[1]{\begingroup\edef\x{\endgroup\noexpand\py{simpfrac(#1,True)}}\x}

\begin{pycode}
def simpfrac(a,b,displaystyle=False):
    d = gcd(a,b)
    num , denom = a // d , b // d
    if denom !=1:
        if displaystyle == True: 
            display = 'd'
        else:
            display = ''
        return '\\ensuremath{\\' + display + 'frac{' + str(num) + '}{' + str(denom) + '}}'
    else:
        return num
\end{pycode}

%%%%%%%% Calcul d'intégrales
%%%%%%%% Syntaxe : \integ{fct(x),a,b}

\newcommand\integ[1]{\begingroup\edef\x{\endgroup\noexpand\py{integ(#1)}}\x}

\begin{pycode}
from scipy.integrate import quad
def integ(f,a,b,marge=False,dec=20,decm=10):
    r , m = quad(f , a , b) # r = résultat ,  m = marge d'erreur
    # on vérifie si la partie décimal du résultat comporte trop de zéros
    p = str(r).find('.')
    partie_decimale = str(r)[p+1:]
    longueur = len(partie_decimale)
    n = partie_decimale.count('0')
    if n > longueur/4:
        r = float( int(r) )
    # suite
    if marge == False:
        if str(round(r,dec))[-2:] == '.0':
            return '\\ensuremath{\\numprint{' + str(int(round(r,dec))) + '}}'
        else:
            return '\\ensuremath{\\numprint{' + str(round(r,dec)).replace('.',',') + '}}'
    else:
        e = str(m)[-3:]
        nb = str(m)[:decm+2].replace('.',',')
        return '\\ensuremath{\\numprint{' + str(round(r,dec)).replace('.',',') + '}\\text{ (avec une marge d\'erreur de }\\numprint{' + nb + '}\\times10^{' + e + '})}'
\end{pycode}

%%%%%%%% Calcul de :
%%%%%%%% 			P(X = k) où X suit la loi B(n,p) 		---> \binomial{n,p,k,dec}
%%%%%%%%			n! 										---> \factorial{n}
%%%%%%%% 			coefficient binomial 					---> \coefbinom{n,k}
%%%%%%%%			P(X <= k)								---> \binomialcdf{n,p,k,dec}
%%%%%%%%			Table des P(X <= k) 					---> \binomialcdftable{n,p,k,dec,seuil,opacity}
%%%%%%%%			Bornes de l'intervalle de fluctuation 	---> \intfluct{n,p}

\newcommand\binomial[1]{\begingroup\edef\x{\endgroup\noexpand\py{binomial(#1)}}\x}
\newcommand\factorial[1]{\begingroup\edef\x{\endgroup\noexpand\py{factorial(#1)}}\x}
\newcommand\coefbinom[1]{\begingroup\edef\x{\endgroup\noexpand\py{binom(#1)}}\x}
\newcommand\binomialcdf[1]{\begingroup\edef\x{\endgroup\noexpand\py{binomialcdf(#1)}}\x}
\newcommand\binomialcdftable[1]{\begingroup\edef\x{\endgroup\noexpand\py{binomialcdftable(#1)}}\x}
\def\colorbinomialcdftable{gray}
\def\colorhead{black}
\newcommand\intfluct[1]{\begingroup\edef\x{\endgroup\noexpand\py{intfluct(#1)}}\x}

\begin{pycode}
def factorial(n,text=True):
    if n == 0 or n == 1:
        return 1
    else:
        f = 1
        for i in range(2,n+1):
            f *= i         
    if text:
        return '\\numprint{' + str(f) + '}'
    else:
        return f

def binom(n,k,text=True):
    if text:
        return '\\numprint{' + str( factorial(n,False) // (factorial(k,False) * factorial(n-k,False)) ) + '}'
    else:
        return factorial(n,False) // (factorial(k,False) * factorial(n-k,False))

def binomial(n,p,k,dec=3,text=True):
    if text:
        return '\\numprint{'+str(round(binom(n,k,False) * (p ** k) * (1-p)**(n-k) , dec)).replace('.',',')+'}'
    else:
        return round(binom(n,k,False) * (p ** k) * (1-p)**(n-k) , dec)
	
def binomialcdf(n,p,k,dec=3,text=True):
    s = 0
    for i in range(k+1):
        s += binomial(n,p,i,dec,text=False)
    if text:
        return '\\numprint{' + str(round(s,dec)).replace('.',',') + '}'
    else:
        return round(s,dec)
	
def binomialcdftable(n,p,dec=5,seuil=0,opacity=20):
	t = '\\begin{longtable}{|c|c|}\\hline\\rowcolor{\\colorbinomialcdftable}\\textcolor{\\colorhead}{$\\pmb{k}$} & \\textcolor{\\colorhead}{$\\pmb{P(X \leqslant k)}$}\\\\\\hline'
	for k in range(n+1):
		colorline = ''
		if seuil != 0:
			delta = (1 - seuil)/2
			if k > 0 and binomialcdf(n,p,k-1,dec,text=False) < delta and binomialcdf(n,p,k,dec,text=False) > delta:
				colorline = '\\rowcolor{\\colorbinomialcdftable!'+str(opacity)+'}'
			if binomialcdf(n,p,k-1,dec,text=False) < 1-delta and binomialcdf(n,p,k,dec,text=False) > 1-delta:
				colorline = '\\rowcolor{\\colorbinomialcdftable!'+str(opacity)+'}'
		t += colorline + str(k) + ' & \\numprint{' + str( round(binomialcdf(n,p,k,dec,text=False),dec) ).replace('.',',') + '}\\\\\\hline'
	t += '\\end{longtable}'
	return t
	
def intfluct(n,p,seuil=0.95,freq = False,dec=4):
	a , b = 0 , n
	for k in range(n+1):
		delta = (1 - seuil)/2
		if k > 0 and binomialcdf(n,p,k-1,text=False) < delta and binomialcdf(n,p,k,text=False) > delta:
			a = k
		if binomialcdf(n,p,k-1,text=False) < 1-delta and binomialcdf(n,p,k,text=False) > 1-delta:
			b = k
	if freq:
		return '[\\numprint{' + str(round(a/n,dec)).replace('.',',') + '}\;;\;\\numprint{' + str(round(b/n,dec)).replace('.',',') + '}]'
	else:
		return '[\\numprint{' + str(a) + '}\;;\;\\numprint{' + str(b) + '}]'
\end{pycode}


