#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Mar 17 19:11:52 2022
@author: jlebovits
"""
from __future__ import division
import sys
import random
from copy import deepcopy
import src.scripts.Mes_fctions.Mes_fctions_deterministes
from src.scripts.Mes_fctions.Mes_fctions_deterministes import *
import src.scripts.Mes_fctions.Mes_fctions_generalistes
from src.scripts.Mes_fctions.Mes_fctions_generalistes import *
import src.scripts.Mes_fctions.Mes_fctions_probabilistes
from src.scripts.Mes_fctions.Mes_fctions_probabilistes import *
import src.scripts.Mes_fctions.Mes_fctions_d_ecriture_Latex
from src.scripts.Mes_fctions.Mes_fctions_d_ecriture_Latex import *
import src.scripts.Mes_fctions.Mes_fctions_d_alg_lineaire_bis
from src.scripts.Mes_fctions.Mes_fctions_d_alg_lineaire_bis import *
import src.scripts.Mes_fctions.Mes_fctions_generalistes_bis
from src.scripts.Mes_fctions.Mes_fctions_generalistes_bis import *
from sympy import *
from src.scripts.pxs_runtime import get_pxs_lang, myst
[docs]
def pxs_config():
pxs_lang = get_pxs_lang()
if pxs_lang == 'fr':
return {"ln_notation": True, "mul_symbol": "", "order": "lex", "decimal_separator": "comma"}
else:
return {"ln_notation": True, "mul_symbol": "", "order": "lex", "decimal_separator": "dot"}
[docs]
def pxsl_par_minus(expr):
"""
Wraps a LaTeX expression in parentheses if it starts with a minus sign.
Parameters
----------
expr : sympy expression or numeric
The expression to be displayed.
Returns
-------
myst : LaTeX-formatted object (via the myst function)
The LaTeX string, wrapped in parentheses if negative.
"""
config_standard = pxs_config()
# Vérifie si l'expression, convertie en chaîne, commence par un signe '-'
# Cela permet d'identifier les expressions négatives (par exemple -x, -3, -sin(x)...)
if str(expr).startswith('-'):
# Si l'expression est négative, on ajoute des parenthèses autour de son affichage LaTeX
# Exemple : -x → \left(-x\right)
# myst() est ici utilisée pour insérer la version LaTeX dynamique de l'expression
return myst(
r"""\left(\py{latex(expr, **config_standard)}\right) """,
globals(), locals()
)
else:
# Si l'expression n’est pas négative, on la renvoie simplement en LaTeX sans parenthèses
return myst(
r"""\py{latex(expr, **config_standard)} """,
globals(), locals()
)
[docs]
def pxsl_par(expr):
"""
Wraps a LaTeX expression in parentheses if it starts with a minus sign or if ti is an Add.
Parameters
----------
expr : sympy expression or numeric
The expression to be displayed.
Returns
-------
myst : LaTeX-formatted object (via the myst function)
The LaTeX string, wrapped in parentheses if negative or Add.
"""
config_standard = pxs_config()
# Vérifie si l'expression, convertie en chaîne, commence par un signe '-'
# Cela permet d'identifier les expressions négatives (par exemple -x, -3, -sin(x)...)
if str(expr).startswith('-') or isinstance(expr, Add):
# Si l'expression est négative, on ajoute des parenthèses autour de son affichage LaTeX
# Exemple : -x → \left(-x\right)
# myst() est ici utilisée pour insérer la version LaTeX dynamique de l'expression
return myst(
r"""\left(\py{latex(expr, **config_standard)}\right) """,
globals(), locals()
)
else:
# Si l'expression n’est pas négative, on la renvoie simplement en LaTeX sans parenthèses
return myst(
r"""\py{latex(expr, **config_standard)} """,
globals(), locals()
)
[docs]
def pxsl_par_add(expr):
"""
Wraps a LaTeX expression in parentheses if it is an Add object.
Parameters
----------
expr : sympy expression or numeric
The expression to be displayed.
Returns
-------
myst : LaTeX-formatted object (via the myst function)
The LaTeX string, wrapped in parentheses if Add.
"""
config_standard = pxs_config()
# Vérifie si l'expression, convertie en chaîne, commence par un signe '-'
# Cela permet d'identifier les expressions négatives (par exemple -x, -3, -sin(x)...)
if isinstance(expr, Add):
# Si l'expression est négative, on ajoute des parenthèses autour de son affichage LaTeX
# Exemple : -x → \left(-x\right)
# myst() est ici utilisée pour insérer la version LaTeX dynamique de l'expression
return myst(
r"""\left(\py{latex(expr, **config_standard)}\right) """,
globals(), locals()
)
else:
# Si l'expression n’est pas négative, on la renvoie simplement en LaTeX sans parenthèses
return myst(
r"""\py{latex(expr, **config_standard)} """,
globals(), locals()
)
def _pxs_resolve_pairs(var = Symbol('x'), f1 = None, f2 = None, type_int = None):
"""
Resolves the function pairs provided by the user for integration by parts.
Allowed cases: (u, dv) or (du, v).
Returns consistent (u, du, v, dv) tuples.
Parameters
----------
var : sympy Symbol
The variable of integration.
f1, f2 : sympy expressions
The two functions or derivatives provided by the user.
type_int : str
Type of pairing expected, either "udv" for (u, dv) or "vdu" for (du, v).
Returns
-------
tuple
A tuple (u, du, v, dv) with coherent expressions for integration by parts.
"""
# Si le type correspond à (u, dv)
# On dérive f1 pour obtenir du et on intègre f2 pour obtenir v
if type_int == "udv":
du = diff(f1, var)
v = integrate(f2, var)
return f1, du, v, f2
# Si le type correspond à (du, v)
# On intègre f2 pour obtenir u et on dérive f1 pour obtenir dv
if type_int == "vdu":
u = integrate(f2, var)
dv = diff(f1, var)
return u, f2, f1, dv
def _pxsl_choose_udv(u, du, v, dv, var = Symbol('x')):
"""
Generates bilingual LaTeX text (English/French) explaining the choice of
functions u and v' in an integration by parts setup.
Parameters
----------
u : sympy expression
Function chosen as u(x).
du : sympy expression
Derivative of u(x).
v : sympy expression
Function chosen as v(x).
dv : sympy expression
Derivative of v(x), i.e., v'(x).
Returns
-------
str
A LaTeX-formatted bilingual text describing u, v', and their derivatives.
"""
config_standard = pxs_config()
text = ""
# Introduction : phrase bilingue annonçant le choix de u et v'
text = myst(r"""
\en{with}\fr{On choisit $u$ et $v'$ tels que :}
""", globals(), locals())
# Bloc d'équations affichant les expressions de u(x) et v'(x)
text += myst(r"""
\en{\begin{equation*}
&u = \py{latex(u, **config_standard)}&
&\textrm{and}&
&v' = \py{latex(dv, **config_standard)}&
\end{equation*}}
\fr{\begin{equation*}
&u(\py{var}) = \py{latex(u, **config_standard)}&
&\textrm{et}&
&v'(\py{var}) = \py{latex(dv, **config_standard)}.&
\end{equation*}}
""", globals(), locals())
# Transition bilingue vers les dérivées et primitives correspondantes
text += myst(r"""
\en{gives}\fr{On en déduit donc}
""")
# Bloc d'équations affichant u'(x) et v(x)
text += myst(r"""
\en{\begin{equation*}
&u' = \py{latex(du, **config_standard)}&
&\textrm{and}&
&v = \py{latex(v, **config_standard)}.&
\end{equation*}}
\fr{\begin{equation*}
&u'(\py{var}) = \py{latex(du, **config_standard)}&
&\textrm{et}&
&v(\py{var}) = \py{latex(v, **config_standard)}.&
\end{equation*}}
""", globals(), locals())
# Retourne le texte LaTeX complet (mélange de phrases et équations)
return text
def _pxsl_choose_vdu(u, du, v, dv, var = Symbol('x')):
"""
Generates bilingual LaTeX text (English/French) explaining the choice of
functions u' and v in an integration by parts setup.
Parameters
----------
u : sympy expression
Function corresponding to u(x).
du : sympy expression
Derivative of u(x), i.e., u'(x).
v : sympy expression
Function chosen as v(x).
dv : sympy expression
Derivative of v(x), i.e., v'(x).
Returns
-------
str
A LaTeX-formatted bilingual text describing u', v, and their corresponding
functions and derivatives.
"""
config_standard = pxs_config()
text = ""
# Introduction : phrase bilingue annonçant le choix de u' et v
text = myst(r"""
\en{with}\fr{On choisit $u'$ et $v$ tels que :}
""", globals(), locals())
# Bloc d'équations affichant u'(x) et v(x)
text += myst(r"""
\en{\begin{equation*}
&u' = \py{latex(du, **config_standard)}&
&\textrm{and}&
&v = \py{latex(v, **config_standard)}&
\end{equation*}}
\fr{\begin{equation*}
&u'(\py{var}) = \py{latex(du, **config_standard)}&
&\textrm{et}&
&v(\py{var}) = \py{latex(v, **config_standard)}.&
\end{equation*}}
""", globals(), locals())
# Transition bilingue vers les primitives et dérivées correspondantes
text += myst(r"""
\en{gives}\fr{On en déduit donc}
""")
# Bloc d’équations affichant u(x) et v'(x)
text += myst(r"""
\en{\begin{equation*}
&u = \py{latex(u, **config_standard)}&
&\textrm{and}&
&v' = \py{latex(dv, **config_standard)}.&
\end{equation*} }
\fr{\begin{equation*}
&u(\py{var}) = \py{latex(u, **config_standard)}&
&\textrm{et}&
&v'(\py{var}) = \py{latex(dv, **config_standard)}.&
\end{equation*} }
""", globals(), locals())
# Retourne le texte LaTeX complet (phrases bilingues et équations)
return text
def _pxsl_resolution_prim(u, du, v, dv, type_int, var = Symbol('x')):
"""
Generates the bilingual LaTeX formula showing the integration by parts
resolution in the case of an *indefinite integral* (primitive).
Depending on the type of pair chosen ("udv" or "vdu"),
the function builds the appropriate LaTeX equation for:
∫ u·v' dx = u·v − ∫ u'·v dx
or
∫ u'·v dx = u·v − ∫ u·v' dx
Parameters
----------
u : sympy expression
Function u(x).
du : sympy expression
Derivative of u(x).
v : sympy expression
Function v(x).
dv : sympy expression
Derivative of v(x), i.e., v'(x).
type_int : str
Type of integration by parts ("udv" or "vdu").
Returns
-------
str
A LaTeX-formatted bilingual text representing the integration by parts formula.
"""
# Cas où l'utilisateur a fourni (u, dv)
# Construction de la formule : ∫u·v' dx = u·v − ∫u'·v dx
if type_int == "udv":
text = myst(r"""
\begin{equation*}
\label{eq}
\int \py{pxsl_par_add(u)}\cdot \py{pxsl_par_add(dv)} \ d\py{var}
&= \py{pxsl_par_add(u)}\cdot \py{pxsl_par(v)} - \int \py{pxsl_par_add(du)} \cdot \py{pxsl_par(v)} \ d\py{var}\\
""", globals(), locals())
# Cas où l'utilisateur a fourni (du, v)
# Construction de la formule : ∫u'·v dx = u·v − ∫u·v' dx
if type_int == "vdu":
text = myst(r"""
\begin{equation*}
\label{eq}
\int \py{pxsl_par_add(du)}\cdot \py{pxsl_par_add(v)} \ d\py{var}
&= \py{pxsl_par_add(u)}\cdot \py{pxsl_par(v)} - \int \py{pxsl_par_add(u)} \cdot \py{pxsl_par(dv)} \ d\py{var}\\
""", globals(), locals())
# Retourne le texte LaTeX de la résolution par parties
return text
def _pxsl_resolution_int(u, du, v, dv, type_int, a, b, var = Symbol('x')):
"""
Generates the bilingual LaTeX formula showing the integration by parts
resolution in the case of a *definite integral* with bounds a and b.
Depending on the type of pair chosen ("udv" or "vdu"),
the function builds the appropriate LaTeX equation for:
∫[a,b] u·v' dx = [u·v]_a^b − ∫[a,b] u'·v dx
or
∫[a,b] u'·v dx = [u·v]_a^b − ∫[a,b] u·v' dx
Parameters
----------
u : sympy expression
Function u(x).
du : sympy expression
Derivative of u(x).
v : sympy expression
Function v(x).
dv : sympy expression
Derivative of v(x), i.e., v'(x).
type_int : str
Type of integration by parts ("udv" or "vdu").
a, b : numeric or symbolic
Lower and upper bounds of integration.
Returns
-------
str
A LaTeX-formatted bilingual text representing the definite integral
integration by parts formula.
"""
# Définition des crochets ou barres selon la langue (français/anglais)
# bl = left bracket pour la version française
# br = right bracket (ou borne d’évaluation) avec indices a et b
config_standard = pxs_config()
bl = myst(r"""
\en{}\fr{\left[}
""", globals(), locals())
br = myst(r"""
\en{\bigg|_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}}}\fr{\right]_{\py{latex(a, **config_satndard)}}^{\py{latex(b, **config_standard)}}}
""", globals(), locals())
# Cas (u, dv) : ∫[a,b] u·v' dx = [u·v]_a^b − ∫[a,b] u'·v dx
if type_int == "udv":
text = myst(r"""
\begin{equation*}
\label{eq}
\int_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}} \py{pxsl_par_add(u)}\cdot \py{pxsl_par(dv)} \ d\py{var}
&= \py{bl}\py{pxsl_par_add(u)}\cdot \py{pxsl_par(v)}\py{br} - \int_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}} \py{pxsl_par_add(du)} \cdot \py{pxsl_par(v)} \ d\py{var}\\
""", globals(), locals())
# Cas (du, v) : ∫[a,b] u'·v dx = [u·v]_a^b − ∫[a,b] u·v' dx
if type_int == "vdu":
text = myst(r"""
\begin{equation*}
\label{eq}
\int_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}} \py{pxsl_par_add(du)}\cdot \py{pxsl_par(v)} \ d\py{var}
&= \py{bl}\py{pxsl_par_add(u)}\cdot \py{pxsl_par(v)}\py{br} - \int_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}} \py{pxsl_par_add(u)} \cdot \py{pxsl_par(dv)} \ d\py{var}\\
""", globals(), locals())
# Retourne le texte LaTeX complet de la résolution avec bornes
return text
def _pxsl_minus(uv, expr, nb_IBP, a = None, b = None, var = Symbol('x')):
"""
Builds LaTeX fragments for the integration by parts process when handling
sign changes and coefficients, with or without integration bounds.
Parameters
----------
uv : sympy expression
The product term u·v from integration by parts.
expr : sympy expression
The remaining integral expression after applying integration by parts.
nb_IBP : int
The number of successive integrations by parts already applied (used to adjust display).
a, b : numeric or symbolic, optional
Lower and upper bounds of integration. If None, the integral is indefinite.
Returns
-------
tuple
(text, text2) : Two LaTeX strings representing intermediate and simplified steps
of the integration by parts process, with correct sign and coefficient handling.
"""
config_standard = pxs_config()
# Si les bornes a et b ne sont pas données → intégrale indéfinie
if a is None and b is None:
bl = myst(r""" """)
br = myst(r""" """)
intf = myst(r""" \int """)
else:
# Si les bornes sont données → intégrale définie
bl = myst(r"""\en{}\fr{\left[} """, globals(), locals())
br = myst(r"""\en{\bigg|_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}}}\fr{\right]_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}}} """, globals(), locals())
intf = myst(r""" \int_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}} """, globals(), locals())
# Extraction du coefficient numérique de expr (partie indépendante de x)
# puis gestion du signe et de la valeur absolue
if expr.as_independent(var)[0] < 0 and expr.as_independent(var)[0] != -1:
coeff = myst(r"""\py{latex(-expr.as_independent(var)[0], **config_standard)} """, globals(), locals())
elif expr.as_independent(var)[0] > 0 and expr.as_independent(var)[0] != 1:
coeff = myst(r"""\py{latex(expr.as_independent(var)[0], **config_standard)} """, globals(), locals())
else:
coeff = myst(r""" """, globals(), locals())
# Si l’expression commence par un signe négatif
if str(expr).startswith('-'):
# Cas "plus" après simplification du signe
text = myst(r"""
&=\py{bl}\py{latex(uv, **config_standard)}\py{br} + \py{coeff} \py{intf} \py{latex(simplify(expr.as_independent(var)[1]), **config_standard)} \ d\py{var}
""", globals(), locals())
# Si c’est une deuxième IBP avec bornes données
if nb_IBP == 2 and a is not None and b is not None:
text += myst(r"""
\\&=\py{latex(uv.subs(var,b), **config_standard)} - \py{pxsl_par(uv.subs(var,a))} + \py{coeff} \py{intf} \py{latex(expr.as_independent(var)[1], **config_standard)} \ d\py{var}
""", globals(), locals())
# Vérifie si la différence symbolique a une forme simplifiée
if Add(uv.subs(var,b), - uv.subs(var,a), evaluate = False) != uv.subs(var,b) - uv.subs(var,a):
text += myst(r"""
\\&=\py{latex(uv.subs(var,b) - uv.subs(var,a), **config_standard)} + \py{coeff} \py{intf} \py{latex(expr.as_independent(var)[1], **config_standard)} \ dx
""", globals(), locals())
# Deuxième bloc textuel, version simplifiée
text2 = myst(r"""
&=\py{bl}\py{latex(uv, **config_standard)}\py{br} + \py{coeff}
""", globals(), locals())
else:
# Cas standard (pas de signe négatif initial)
text = myst(r"""
&=\py{bl}\py{latex(uv, **config_standard)}\py{br} - \py{coeff} \py{intf} \py{latex(simplify(expr.as_independent(var)[1]), **config_standard)} \ d\py{var}
""", globals(), locals())
# Gestion spéciale pour la deuxième IBP avec bornes
if nb_IBP == 2 and a is not None and b is not None:
text += myst(r"""
\\&=\py{latex(uv.subs(var,b), **config_standard)} - \py{pxsl_par(uv.subs(var,a))} - \py{coeff} \py{intf} \py{latex(expr.as_independent(var)[1], **config_standard)} \ d\py{var}
""", globals(), locals())
# Vérifie si la forme développée diffère de la soustraction directe
if Add(uv.subs(var,b), - uv.subs(var,a), evaluate = False) != uv.subs(var,b) - uv.subs(var,a):
text += myst(r"""
\\&=\py{latex(uv.subs(var,b)-uv.subs(var,a), **config_standard)} - \py{coeff} \py{intf} \py{latex(expr.as_independent(var)[1], **config_standard)} \ d\py{var}
""", globals(), locals())
# Deuxième bloc textuel, version simplifiée
text2 = myst(r"""
&=\py{bl}\py{latex(uv, **config_standard)}\py{br} - \py{coeff}
""", globals(), locals())
# Retourne les deux chaînes LaTeX construites
return text, text2
def _pxsl_explain(u, du, v, dv, type_int, nb_IBP, a = None, b = None, var = Symbol('x')):
"""
Builds a bilingual LaTeX explanation for the integration by parts process,
using either the primitive form or the definite integral form.
The function combines:
- the core integration by parts equation (via _pxsl_resolution_prim or _pxsl_resolution_int)
- the corresponding minus/plus term adjustments (via _pxsl_minus)
depending on the integration type ("udv" or "vdu") and the presence of bounds.
Parameters
----------
u : sympy expression
Function u(x).
du : sympy expression
Derivative of u(x).
v : sympy expression
Function v(x).
dv : sympy expression
Derivative of v(x), i.e., v'(x).
type_int : str
Integration by parts type: "udv" for (u, dv) or "vdu" for (du, v).
nb_IBP : int
Number of integration by parts steps performed (for formatting purposes).
a, b : numeric or symbolic, optional
Integration bounds. If None, the integral is indefinite.
Returns
-------
tuple
(text, text2) : Two LaTeX strings representing the detailed explanation
and simplified version of the integration by parts process.
"""
# Si les bornes ne sont pas fournies → cas d’intégrale indéfinie
if a is None and b is None:
# Étape 1 : construire la résolution pour une primitive
text = _pxsl_resolution_prim(u, du, v, dv, type_int, var)
# Étape 2 : ajouter les termes avec le bon signe selon le type d’intégration
if type_int == "udv":
text += _pxsl_minus(u*v, v*du, nb_IBP, var = var)[0]
text2 = _pxsl_minus(u*v, v*du, nb_IBP, var = var)[1]
else:
text += _pxsl_minus(u*v, u*dv, nb_IBP, var = var)[0]
text2 = _pxsl_minus(u*v, u*dv, nb_IBP, var = var)[1]
# Retourne le texte complet et sa version simplifiée
return text, text2
# Sinon → cas d’intégrale définie
else:
# Étape 1 : construire la résolution avec bornes
text = _pxsl_resolution_int(u, du, v, dv, type_int, a, b, var)
# Étape 2 : ajouter les termes corrigés en fonction du type d’intégration
if type_int == "udv":
text += _pxsl_minus(u*v, v*du, nb_IBP, a, b, var)[0]
text2 = _pxsl_minus(u*v, v*du, nb_IBP, a, b, var)[1]
else:
text += _pxsl_minus(u*v, u*dv, nb_IBP, a, b, var)[0]
text2 = _pxsl_minus(u*v, u*dv, nb_IBP, a, b, var)[1]
# Retourne le texte complet et la version simplifiée avec bornes
return text, text2
def _pxsl_conclude(uv, expr, origin_int, var = Symbol('x')):
"""
Generates the final bilingual LaTeX step that concludes the integration by parts
process, displaying the complete evaluated primitive with constant C.
Parameters
----------
uv : sympy expression
The product term u·v obtained during integration by parts.
expr : sympy expression
The remaining part of the expression (integral to be evaluated or simplified).
origin_int : sympy expression
The original integral expression (used for final verification and display).
Returns
-------
str
A LaTeX-formatted bilingual text showing the final result of the integration,
including the constant of integration.
"""
config_standard = pxs_config()
# Extraction du coefficient numérique de l’expression (partie indépendante de x)
# Gestion du signe et des cas particuliers (valeurs 1 ou -1 ignorées)
if expr.as_independent(var)[0] < 0 and expr.as_independent(var)[0] != -1:
coeff = myst(r"""\py{latex(-expr.as_independent(var)[0], **config_standard)} """, globals(), locals())
dot = myst(r"""\cdot """)
elif expr.as_independent(var)[0] > 0 and expr.as_independent(var)[0] != 1:
coeff = myst(r"""\py{latex(expr.as_independent(var)[0], **config_standard)} """, globals(), locals())
dot = myst(r"""\cdot """)
else:
coeff = myst(r""" """, globals(), locals())
# Si l’expression commence par un signe négatif
if str(expr).startswith('-'):
# Cas où le signe devient "plus" après simplification
text = myst(r""" \\&=
\py{latex(uv, **config_standard)} + \py{coeff}\py{dot} \py{pxsl_par(integrate(expr.as_independent(var)[1], x))} + C
""", globals(), locals())
else:
# Cas standard : on garde le signe négatif devant le terme intégré
text = myst(r""" \\&=
\py{latex(uv, **config_standard)} - \py{coeff}\py{dot}\py{pxsl_par(integrate(expr.as_independent(var)[1], var))} + C
""", globals(), locals())
# Ajout de la ligne finale donnant la primitive complète de l’intégrale d’origine
text += myst(r"""\\
& = \py{latex(integrate(origin_int, var), **config_standard)} + C, \textrm{ \en{with}\fr{où} }C\textrm{ \en{a real constant}\fr{est une constante réelle}}.
\end{equation*}
""", globals(), locals())
# Retourne le texte LaTeX complet de conclusion
return text
def _pxsl_conclude_int(uv, expr, origin_int, a, b, var = Symbol('x')):
"""
Generates the final bilingual LaTeX step concluding the integration by parts
process for a *definite integral* between bounds a and b.
This function evaluates and formats the expression:
[u·v]_a^b ± coefficient × [∫f(x)dx]_a^b
and shows the fully evaluated result of the original definite integral.
Parameters
----------
uv : sympy expression
The product term u·v obtained during integration by parts.
expr : sympy expression
The remaining part of the expression (integral to be evaluated or simplified).
origin_int : sympy expression
The original integral expression (used for final comparison and display).
a, b : numeric or symbolic
Lower and upper bounds of the definite integral.
Returns
-------
str
A LaTeX-formatted bilingual text showing the final step of the definite
integration by parts, with all evaluations and signs displayed correctly.
"""
config_standard = pxs_config()
# Définit les crochets et notations bilingues pour les bornes d’intégration
bl = myst(r"""\en{}\fr{\left[} """, globals(), locals())
br = myst(r"""\en{\bigg|_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}}}\fr{\right]_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}}} """, globals(), locals())
# Extraction du coefficient numérique et gestion du signe
# On ajoute aussi les symboles nécessaires selon la présence d’un coefficient
if expr.as_independent(var)[0] < 0 and expr.as_independent(var)[0] != -1:
coeff = myst(r"""\py{latex(-expr.as_independent(var)[0], **config_standard)} """, globals(), locals())
dot = myst(r"""\cdot """)
l_par = myst(r"""\left( """)
r_par = myst(r"""\right) """)
elif expr.as_independent(var)[0] > 0 and expr.as_independent(var)[0] != 1:
coeff = myst(r"""\py{latex(expr.as_independent(var)[0], **config_standard)} """, globals(), locals())
dot = myst(r"""\cdot """)
l_par = myst(r"""\left( """)
r_par = myst(r"""\right) """)
else:
coeff = myst(r""" """, globals(), locals())
dot = myst(r""" """)
l_par = myst(r""" """)
r_par = myst(r""" """)
# Si l’expression commence par un signe négatif
if expr.could_extract_minus_sign():
# Cas où le signe devient "plus" après simplification
text = myst(r""" \\&=
\py{latex(uv.subs(var,b), **config_standard)} - \py{pxsl_par(uv.subs(var,a))} + \py{coeff} \py{dot}\py{bl}\py{pxsl_par_minus(integrate(expr.as_independent(var)[1], var))}\py{br}""", globals(), locals())
if isinstance(uv.subs(var,a), Add):
text += myst(r"""
\\&=\py{latex(uv.subs(var,b), **config_standard)}""", globals(), locals())
for term in uv.subs(var,a).as_ordered_terms():
if term.could_extract_minus_sign():
text += myst(r""" + \py{latex(-term, **config_standard)}""", globals(), locals())
else:
text += myst(r""" - \py{latex(abs(term), **config_standard)}""", globals(), locals())
text += myst(r""" + \py{coeff} \py{dot}\py{l_par}\py{latex(integrate(expr.as_independent(var)[1], var).subs(var,b), **config_standard)} - \py{pxsl_par(integrate(expr.as_independent(var)[1], var).subs(var,a))}\py{r_par}
""", globals(), locals())
elif uv.subs(var,a).could_extract_minus_sign():
text += myst(r"""
\\&=\py{latex(uv.subs(var,b), **config_standard)} + \py{latex(-uv.subs(var,a), **config_standard)} + \py{coeff} \py{dot}\py{l_par}\py{latex(integrate(expr.as_independent(var)[1], var).subs(var,b), **config_standard)} - \py{pxsl_par(integrate(expr.as_independent(var)[1], var).subs(var,a))}\py{r_par}
""", globals(), locals())
else:
text += myst(r"""
\\&=\py{latex(uv.subs(var,b), **config_standard)} - \py{latex(uv.subs(var,a), **config_standard)} + \py{coeff} \py{dot}\py{l_par}\py{latex(integrate(expr.as_independent(var)[1], var).subs(var,b), **config_standard)} - \py{pxsl_par(integrate(expr.as_independent(var)[1], var).subs(var,a))}\py{r_par}
""", globals(), locals())
else:
# Cas standard : le signe reste négatif devant la deuxième intégrale
text = myst(r""" \\&=
\py{latex(uv.subs(var,b), **config_standard)} - \py{pxsl_par(uv.subs(var,a))} - \py{coeff}\py{dot}\py{bl}\py{pxsl_par_minus(integrate(expr.as_independent(var)[1], var))}\py{br}""", globals(), locals())
if isinstance(uv.subs(var,a), Add):
text += myst(r"""
\\&=\py{latex(uv.subs(var,b), **config_standard)}""", globals(), locals())
for term in uv.subs(var,a).as_ordered_terms():
if term.could_extract_minus_sign():
text += myst(r""" + \py{latex(-term, **config_standard)}""", globals(), locals())
else:
text += myst(r""" - \py{latex(term, **config_standard)}""", globals(), locals())
text += myst(r""" - \py{coeff} \py{dot}\left(\py{latex(integrate(expr.as_independent(var)[1], var).subs(var,b), **config_standard)} - \py{pxsl_par(integrate(expr.as_independent(var)[1], var).subs(var,a))}\right)
""", globals(), locals())
elif uv.subs(var,a).could_extract_minus_sign():
text += myst(r"""
\\&=\py{latex(uv.subs(var,b), **config_standard)} + \py{latex(-uv.subs(var,a), **config_standard)} - \py{coeff} \py{dot}\left(\py{latex(integrate(expr.as_independent(var)[1], var).subs(var,b), **config_standard)} - \py{pxsl_par(integrate(expr.as_independent(var)[1], var).subs(var,a))}\right)
""", globals(), locals())
else:
text += myst(r"""
\\&=\py{latex(uv.subs(var,b), **config_standard)} - \py{latex(uv.subs(var,a), **config_standard)} - \py{coeff} \py{dot}\left(\py{latex(integrate(expr.as_independent(var)[1], var).subs(var,b), **config_standard)} - \py{pxsl_par(integrate(expr.as_independent(var)[1], var).subs(var,a))}\right)
""", globals(), locals())
# Ligne finale : égalité avec l’intégrale définie complète d’origine
text += myst(r"""\\
& = \py{latex(integrate(origin_int, (var, a, b)), **config_standard)}.
\end{equation*}
""", globals(), locals())
# Retourne le texte LaTeX complet de conclusion (forme avec bornes)
return text
def _pxs_explain_IBP(var = Symbol('x'), f1 = None, f2 = None, type_int = None, nb_IBP = 1, a = None, b = None):
"""
Builds a complete bilingual LaTeX correction text for the Integration by Parts (IBP) process,
handling both single and double applications, and supporting definite or indefinite integrals.
Depending on the number of integrations by parts (`nb_IBP`) and the type (`type_int`),
this function calls all the appropriate sub-functions:
- _pxs_resolve_pairs → identifies (u, du, v, dv)
- _pxsl_choose_udv/vdu → generates the introduction LaTeX text
- _pxsl_explain → generates the main formula
- _pxsl_conclude/_int → final step (primitive or definite)
- recursively calls itself if nb_IBP = 2
Parameters
----------
x : sympy Symbol
The integration variable.
f1, f2 : sympy expressions
The two parts of the integrand provided by the user.
type_int : str
Integration by parts type: "udv" for (u, dv) or "vdu" for (du, v).
nb_IBP : int, optional
Number of integration by parts steps (1 or 2). Default is 1.
a, b : numeric or symbolic, optional
Lower and upper bounds of integration (if given, produces a definite integral).
Returns
-------
str
A LaTeX-formatted bilingual text that fully explains the integration by parts
procedure, with all symbolic steps, coefficients, and final results.
"""
config_standard = pxs_config()
text = ""
# --- Définition des éléments de mise en forme selon la présence de bornes ---
if a is None and b is None:
bl = myst(r""" """)
br = myst(r""" """)
intf = myst(r""" \int """)
else:
bl = myst(r"""\en{}\fr{\left[} """, globals(), locals())
br = myst(r"""\en{\bigg|_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}}}\fr{\right]_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}}} """, globals(), locals())
intf = myst(r""" \int_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}} """, globals(), locals())
# === Première intégration par parties ===
u, du, v, dv = _pxs_resolve_pairs(var, f1, f2, type_int)
# Introduction selon le type choisi (u,dv) ou (du,v)
if type_int == "udv":
text += _pxsl_choose_udv(u, du, v, dv, var)
else:
text += _pxsl_choose_vdu(u, du, v, dv, var)
# Phrase bilingue avant l’application de la formule
text += myst(r"""
\en{So integration by parts gives us:}\fr{En appliquant la formule, on obtient : }""")
# Construction de la formule selon la présence ou non de bornes
if a is None and b is None:
text += _pxsl_explain(u, du, v, dv, type_int, nb_IBP, var = var)[0]
text2 = _pxsl_explain(u, du, v, dv, type_int, nb_IBP, var = var)[1]
else:
text += _pxsl_explain(u, du, v, dv, type_int, nb_IBP, a, b, var = var)[0]
text2 = _pxsl_explain(u, du, v, dv, type_int, nb_IBP, a, b, var = var)[1]
# === Conclusion si une seule intégration par parties ===
if nb_IBP == 1 and a is None and b is None:
if type_int == "udv":
text += _pxsl_conclude(u*v, v*du, u*dv, var = var)
if type_int == "vdu":
text += _pxsl_conclude(u*v, u*dv, v*du, var = var)
elif nb_IBP == 1:
if type_int == "udv":
text += _pxsl_conclude_int(u*v, v*du, u*dv, a, b, var = var)
if type_int == "vdu":
text += _pxsl_conclude_int(u*v, u*dv, v*du, a, b, var = var)
# === Cas où une deuxième intégration par parties est demandée (primitive) ===
if nb_IBP == 2 and a is None and b is None:
text += myst(r"""
\end{equation*}
""")
if type_int == "udv":
text += _pxs_explain_IBP(var, du.as_independent(var)[1], v.as_independent(var)[1], "udv")
text += myst(r"""
\en{Thus}\fr{On a donc :}
\begin{equation*}
\int \py{latex(u*dv, **config_standard)} d\py{var} \py{text2}\py{latex(integrate(du.as_independent(var)[1]*v.as_independent(var)[1],var), **config_standard)}=\py{latex(integrate(u*dv,var), **config_standard)}
\end{equation*}
""", globals(), locals())
if type_int == "vdu":
text += _pxs_explain_IBP(var, dv.as_independent(var)[1], u.as_independent(var)[1], "vdu")
# === Cas où une deuxième intégration par parties est demandée (avec bornes) ===
elif nb_IBP == 2:
text += myst(r"""
\end{equation*}
""")
if type_int == "udv":
text += _pxs_explain_IBP(var, du.as_independent(var)[1], v.as_independent(var)[1], "udv", a = a, b = b)
# Gestion du signe et du coefficient pour la 2e IBP
if (v*du).as_independent(var)[0] < 0:
coeff = latex((v*du).as_independent(var)[0], **config_standard) if (v*du).as_independent(var)[0] != -1 else myst(r""" """)
dot = myst("""\cdot """) if (v*du).as_independent(var)[0] != -1 else myst(r""" """)
if a == None and b == None:
text += myst(r"""
\en{Thus}\fr{On a donc :}
\begin{equation*}
\int \py{latex(u*dv, **config_standard)} d\py{var} =latex(u*v, **config_standard) + \py{coeff}\py{dot}\py{pxsl_par(integrate((v*du).as_independent(var)[1],var), **config_standard)}=\py{latex(integrate(u*dv,var), **config_standard)}
\end{equation*}
""", globals(), locals())
else:
text += myst(r"""
\en{Thus}\fr{On a donc :}
\begin{equation*}
\int_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}} \py{latex(u*dv, **config_standard)} d\py{var} =\py{latex((u*v).subs(var,b)-(u*v).subs(var,a), **config_standard)} + \py{coeff}\py{dot}\py{pxsl_par(integrate((v*du).as_independent(var)[1],(var,a,b)), **config_standard)}=\py{latex(integrate(u*dv,(var,a,b)), **config_standard)}
\end{equation*}
""", globals(), locals())
else:
coeff = latex((v*du).as_independent(var)[0], **config_standard) if (v*du).as_independent(var)[0] != 1 else myst(r""" """)
dot = myst("""\cdot """) if (v*du).as_independent(var)[0] != 1 else myst(r""" """)
if a == None and b == None:
text += myst(r"""
\en{Thus}\fr{On a donc :}
\begin{equation*}
\int \py{latex(u*dv, **config_standard)} d\py{var} =latex(u*v, **config_standard) - \py{coeff}\py{dot}\left(\py{pxsl_par(integrate((v*du).as_independent(var)[1],var), **config_standard)}=\py{latex(integrate(u*dv,var), **config_standard)}\right)
\end{equation*}
""", globals(), locals())
else:
text += myst(r"""
\en{Thus}\fr{On a donc :}
\begin{equation*}
\int_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}} \py{latex(u*dv, **config_standard)} d\py{var} =\py{latex, **config_standard)} - \py{coeff}\py{dot}\left(\py{pxsl_par(integrate((v*du).as_independent(var)[1],(var,a,b)), **config_standard)}\right)=\py{latex(integrate(u*dv,(var,a,b)), **config_standard)}
\end{equation*}
""", globals(), locals())
if type_int == "vdu":
text += _pxs_explain_IBP(var, dv.as_independent(var)[1], u.as_independent(var)[1], "vdu", a = a, b = b)
if (u*dv).as_independent(var)[0] < 0:
coeff = latex((u*dv).as_independent(var)[0], **config_standard) if (u*dv).as_independent(var)[0] != -1 else myst(r""" """)
dot = myst("""\cdot """) if (v*du).as_independent(var)[0] != -1 else myst(r""" """)
text += myst(r"""
\en{Thus}\fr{On a donc :}
\begin{equation*}
\int \py{latex(v*du, **config_standard)} d\py{var} =\py{latex((u*v).subs(var,b)-(u*v).subs(var,a), **config_standard)} + \py{coeff}\py{dot}\py{pxsl_par(integrate((u*dv).as_independent(var)[1],(var,a,b)), **config_standard)}=\py{latex(integrate(v*du,(var,a,b)), **config_standard)}
\end{equation*}
""", globals(), locals())
else:
coeff = latex((u*dv).as_independent(var)[0], **config_standard) if (u*dv).as_independent(var)[0] != 1 else myst(r""" """)
dot = myst("""\cdot """) if (v*du).as_independent(var)[0] != 1 else myst(r""" """)
text += myst(r"""
\en{Thus}\fr{On a donc :}
\begin{equation*}
\int \py{latex(v*du, **config_standard)} d\py{var} = \py{latex((u*v).subs(var,b)-(u*v).subs(var,a), **config_standard)} - \py{coeff}\py{dot}\py{pxsl_par(integrate((u*dv).as_independent(var)[1],(var,a,b)), **config_standard)}=\py{latex(integrate(v*du,(var,a,b)), **config_standard)}
\end{equation*}
""", globals(), locals())
# Retourne le texte LaTeX complet expliquant les étapes de l’IBP
return text
[docs]
def pxs_explain_IBP(var = Symbol('x'), f1 = None, f2 = None, type_int = "udv", a = None, b = None, nb_IBP = 1, link = "https://app.pyxiscience.com/teacher/modules/7e0b271d-92f9-11f0-a777-0e37881c19a9/chapter/258e5825-9de5-11f0-a5a8-0e37881c19a9#quotient-fini#IBP2"):
"""
Creates a complete bilingual LaTeX explanation for computing an integral
using the Integration by Parts (IBP) method.
This is the main public-facing function that introduces the IBP concept,
calls the internal recursive explanation generator `_pxs_explain_IBP`, and
concludes by showing the final boxed result of the integral (definite or
indefinite).
Parameters
----------
var : sympy Symbol
The integration variable.
f1, f2 : sympy expressions
The two parts of the integrand (used as u and dv or du and v).
type_int : str, optional
Type of integration by parts ("udv" or "vdu"). Default is "udv".
a, b : numeric or symbolic, optional
Lower and upper bounds of the integral. If None, it’s treated as an
indefinite integral.
nb_IBP : int, optional
Number of integrations by parts to perform (1 or 2). Default is 1.
Returns
-------
str
A bilingual LaTeX-formatted text containing the full reasoning,
step-by-step explanation, and final boxed conclusion of the IBP process.
"""
config_standard = pxs_config()
# Introduction bilingue avec lien interactif vers le chapitre concerné
text = myst(r"""
```{fr}
On calcule cette intégrale en réalisant une [{color:blue}`intégration par parties`]({{link}}) :
```
```{en}
Using [{color:blue}`integration by parts`]({{link}})
```
""", globals(), locals())
# Détermination des paires u, du, v, dv à partir des deux fonctions données
u, du, v, dv = _pxs_resolve_pairs(var, f1, f2, type_int)
# Appel de la fonction principale interne qui rédige les étapes détaillées
text += _pxs_explain_IBP(var, f1, f2, type_int, nb_IBP, a, b)
# === Cas (u, dv) sans bornes ===
if type_int == "udv" and a is None and b is None:
text += myst(r"""
\en{Thus,}\fr{On a donc montré :}
\begin{equation*}
\fbox{$\displaystyle{\int \py{latex(u*dv, **config_standard)} \;d\py{var} = \py{latex(integrate(u*dv, var), **config_standard)}} + C$, \en{with}\fr{où} $C$ \en{a real constant}\fr{est une constante réelle}.}
\end{equation*}""", globals(), locals())
# === Cas (u, dv) avec bornes ===
elif type_int == "udv":
text += myst(r"""
\en{Thus,}\fr{On a donc montré :}
\begin{equation*}
\fbox{$\displaystyle{\int_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}} \py{latex(u*dv, **config_standard)} \;d\py{var} = \py{latex(integrate(u*dv, (var, a, b)), **config_standard)}}$}
\end{equation*}""", globals(), locals())
# === Cas (du, v) sans bornes ===
if type_int == "vdu" and a is None and b is None:
text += myst(r"""
\en{Thus,}\fr{On a donc montré :}
\begin{equation*}
\fbox{$\displaystyle{\int \py{latex(v*du, **config_standard)} \;d\py{var} = \py{latex(integrate(v*du, var), **config_standard)}} + C$, \en{with}\fr{où} $C$ \en{a real constant}\fr{est une constante réelle}.}
\end{equation*}""", globals(), locals())
# === Cas (du, v) avec bornes ===
elif type_int == "vdu":
text += myst(r"""
\en{Thus,}\fr{On a donc montré :}
\begin{equation*}
\fbox{$\displaystyle{\int_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}} \py{latex(v*du, **config_standard)}\;d\py{var} = \py{latex(integrate(v*du, (var, a, b)), **config_standard)}}$}
\end{equation*}""", globals(), locals())
# Retourne le texte complet, incluant l’introduction, le déroulé et la conclusion
return text