import $ from 'jquery'
import { j3pAddElt, j3pElement, j3pEmpty, j3pFocus, j3pGetRandomInt, j3pMathquillXcas, j3pMonome, j3pNombre, j3pPaletteMathquill, j3pPGCD, j3pRandomTab, j3pStyle, j3pVirgule } from 'src/legacy/core/functions'
import { j3pExtraireNumDen, j3pGetLatexProduit, j3pGetLatexQuotient, j3pGetLatexSomme, j3pSimplificationRacineTrinome } from 'src/legacy/core/functionsLatex'
import { j3pCreeRectangle, j3pCreeSegment } from 'src/legacy/core/functionsSvg'
import { j3pCalculValeur } from 'src/legacy/core/functionsTarbre'
import Tarbre from 'src/legacy/outils/calculatrice/Tarbre'
import { ecritBienBorne, ecritBienFraction } from 'src/legacy/sections/lycee/fonctionsetude/sectionEtudeFonction_variations'
import { j3pAffiche, mqRestriction } from 'src/lib/mathquill/functions'
const epsilon = 1e-12
/**
*
* @private
* @param typeOp
* @param nb1Latex
* @param nb2Latex
* @param puis
* @returns {string|*}
*/
function operationAvecRadical ({ typeOp, nb1Latex, nb2Latex, puis }) {
/* obj est un objet contenant jusqu'à 3 propriétés
- typeOp vaut "+", "-", "*", "/", "inv" (pour l’inverse), "^"
- nb1Latex et nb2Latex qui sont des nombres écrits au format latex et correspondant à une expression de la forme (a+b*racine(c))/d
- tabCoef1 et tabCoef2 qui sont des tableau contenant dans l’ordre a,b,c,d (c’est-à-dire les coefs des expressions des nombres)
- puis est utile pour élever nb1 à une certaine puissance (il correspond à un entier naturel)
tabCoef1 et tabCoef2 n’ont d’intérêt que si nb1Latex et nb2Latex n’ont pas été définis. Cela évite de perdre du temps dans la recherche des coefs quand on les a
Cette fonction renvoie le résultat du calcul effectué entre nb1 et nb2 avec "l’opération" précisée
*/
// à ce stade les nombres ne peuvent être qu’entiers, fractionnaires ou de la forme a+b\\sqrt{c} et \\frac{a+b\\sqrt{c}}{d}, a, b, c, d entiers
// Attention également dans l’utilisation de cette fonction : le nombre sous le radical, s’il existe doit être le même pour les 2 nombres
function extraireCoef (nbLatex) {
// nbLatex est une chaîne latex représentant un nombre sous la forme (a+b*racine(c))/d
// cette fonction me permet de récupérer les coefs pour faire les calculs ensuite
if (!nbLatex.includes('sqrt')) {
// il n’y a pas de raciFne carrée, cela va être simple car c’est un décimal ou un nb en écriture fractionnaires
const nb = j3pGetLatexProduit(nbLatex, '1')// c’est pour mettre sous forme fractionnaire les nombres décimaux
const tabFrac = j3pExtraireNumDen(nb)
if (tabFrac[0]) {
// on a un nombre en écriture fractionnaire
return [tabFrac[1], 0, 0, tabFrac[2]]
}
// On a un nombre entier
return [nb, 0, 0, 1]
}
// tout le boulot est là !
let a, b, c, d, bInit
let avantPlus, apresPlus
const bracinec = /-?\d*\\sqrt{\d+}/g // pour trouver b\sqrt{c}
const racinec = /\\sqrt{\d+}/g // pour trouver \sqrt{c}
const abRacineC = /-?\d+[+-]\d*\\sqrt{\d+}/g // pour trouver a+b\sqrt{c}
const bRacineCplusA = /-?\d*\\sqrt{\d+}[+-]\d+/g // pour trouver b\sqrt{c}+a
const bRacineCsurD = /\\frac{-?\d*\\sqrt{\d+}}{\d+}/g // pour trouver \frac{b\sqrt{c}}{d}
const bSurDracineC = /\\frac{-?\d+}{\d+}\\sqrt{\d+}/g // pour trouver \frac{b}{d}\sqrt{c}
const abRacineCsurD = /\\frac{-?\d+[+-]\d*\\sqrt{\d+}}{\d+}/g // pour trouver \frac{a+b\sqrt{c}}{d}
const bRacineCaSurD = /\\frac{-?\d*\\sqrt{\d+}[+-]\d+}{\d+}/g // pour trouver \frac{b\sqrt{c}+b}{d}
if (abRacineCsurD.test(nbLatex) || bRacineCaSurD.test(nbLatex)) {
// le nombre est de la forme \frac{a+b\sqrt{c}}{d} ou \frac{b\sqrt{c}+b}{d}
// je récupère b\sqrt{c}
const tabMatch = nbLatex.match(bracinec)// c’est un tableau de longueur 1
c = tabMatch[0].substring(tabMatch[0].lastIndexOf('{') + 1, tabMatch[0].length - 1)
bInit = tabMatch[0].replace('\\sqrt{' + c + '}', '')
if (nbLatex[0] === '-') {
// j’ai un facteur -1 devant la barre de fraction
if (bInit[0] === '-') {
bInit = bInit.substring(1)
} else {
bInit = '-' + bInit
}
}
let aSurD = nbLatex.replace(tabMatch[0], '')// je vire b\\sqrt{c} de mon expression. Il me reste alors a/d
// Je peux cependant avoir \\frac{a+}{d} ou \\frac{+a}{d}. Virons ce signe + en trop
const posAccOuv = aSurD.indexOf('{')
const posAccFer = aSurD.indexOf('}')
if (aSurD[posAccOuv + 1] === '+') { // je dois virer ce + car j’ai \\frac{+a}{d}
avantPlus = aSurD.substring(0, posAccOuv + 1)
apresPlus = aSurD.substring(posAccOuv + 2)
aSurD = avantPlus + apresPlus
} else if (aSurD[posAccFer - 1] === '+') { // je dois virer ce + car j’ai \\frac{a+}{d}
avantPlus = aSurD.substring(0, posAccFer - 1)
apresPlus = aSurD.substring(posAccFer)
aSurD = avantPlus + apresPlus
}
const tabFrac = j3pExtraireNumDen(aSurD)
a = tabFrac[1]
d = tabFrac[2]
} else if (bSurDracineC.test(nbLatex) || bRacineCsurD.test(nbLatex)) {
// le nombre est de la forme \\frac{b}{d}\\sqrt{c} ou \\frac{b\\sqrt{c}}{d}
const racine = nbLatex.match(racinec)[0]
const fraction = nbLatex.replace(racine, '')
c = racine.substring(racine.indexOf('{') + 1, racine.length - 1)
a = 0
const tabFract = j3pExtraireNumDen(fraction)
bInit = tabFract[1]
d = tabFract[2]
} else if (abRacineC.test(nbLatex) || bRacineCplusA.test(nbLatex)) {
// le nombre est de la forme a+b\\sqrt{c} ou b\\sqrt{c}+a
const racine = nbLatex.match(bracinec)[0]
const coefA = nbLatex.replace(racine, '')
if (coefA[0] === '+') {
a = coefA.substring(1)
} else if (coefA[coefA.length - 1] === '+') {
a = coefA.substring(0, coefA.length - 1)
} else {
a = coefA
}
d = 1
c = racine.substring(racine.indexOf('{') + 1, racine.length - 1)
bInit = racine.replace('\\sqrt{' + c + '}', '')
} else if (bracinec.test(nbLatex)) {
// le nombre est de la forme b\\sqrt{c}
a = 0
d = 1
c = nbLatex.substring(nbLatex.indexOf('{') + 1, nbLatex.length - 1)
bInit = nbLatex.replace('\\sqrt{' + c + '}', '')
}
if (!bInit) {
b = 1
} else if (bInit === '-') {
b = -1
} else {
b = bInit
}
return [a, b, c, d]
}
function sommeTabCoef (tab1, tab2) {
// tab1 et tab2 sont des tableau contenant les 4 coefs de la forme (a+b*racine(c))/d
// cette fonction renvoie un tableau sous la forme a2,b2,c2,d2 où (a2+b2*racine(c2)/d est le résultat de la somme des deux nombres représentés avec tab1 et tab2
const a2 = j3pGetLatexSomme(j3pGetLatexProduit(tab1[0], tab2[3]), j3pGetLatexProduit(tab1[3], tab2[0]))
const b2 = j3pGetLatexSomme(j3pGetLatexProduit(tab1[1], tab2[3]), j3pGetLatexProduit(tab1[3], tab2[1]))
let c2 = tab1[2]
if (Math.abs(Number(c2)) < epsilon) {
c2 = tab2[2]
}
const d2 = j3pGetLatexProduit(tab1[3], tab2[3])
return [a2, b2, c2, d2]
}
function produitTabCoef (tab1, tab2) {
// tab1 et tab2 sont des tableau contenant les 4 coefs de la forme (a+b*racine(c))/d
// cette fonction renvoie un tableau sous la forme a2,b2,c2,d2 où (a2+b2*racine(c2)/d est le résultat du produit des deux nombres représentés avec tab1 et tab2
let c2 = tab1[2]
if (Math.abs(Number(c2)) < epsilon) {
c2 = tab2[2]
}
const a2 = j3pGetLatexSomme(j3pGetLatexProduit(tab1[0], tab2[0]), j3pGetLatexProduit(j3pGetLatexProduit(tab1[1], tab2[1]), c2))
const b2 = j3pGetLatexSomme(j3pGetLatexProduit(tab1[1], tab2[0]), j3pGetLatexProduit(tab1[0], tab2[1]))
const d2 = j3pGetLatexProduit(tab1[3], tab2[3])
return [a2, b2, c2, d2]
}
function inverseTabCoef (tab1) {
// tab1 et tab2 sont des tableau contenant les 4 coefs de la forme (a+b*racine(c))/d
// cette fonction renvoie un tableau sous la forme a2,b2,c2,d2 où (a2+b2*racine(c2)/d est le résultat de l’inverse du nombre représenté avec tab1
const a2 = j3pGetLatexProduit(tab1[0], tab1[3])
const b2 = j3pGetLatexProduit('-1', j3pGetLatexProduit(tab1[1], tab1[3]))
const c2 = tab1[2]
const d2 = j3pGetLatexSomme(j3pGetLatexProduit(tab1[0], tab1[0]), j3pGetLatexProduit('-1', j3pGetLatexProduit(j3pGetLatexProduit(tab1[1], tab1[1]), tab1[2])))
return [a2, b2, c2, d2]
}
function divisionTabCoef (tab1, tab2) {
// tab1 et tab2 sont des tableau contenant les 4 coefs de la forme (a+b*racine(c))/d
// cette fonction renvoie un tableau sous la forme a2,b2,c2,d2 où (-a2+b2*racine(c2)/d est le résultat du quotient des deux nombres représentés avec tab1 et tab2
// j’ai choisi -a2 et non a2, de même que d/2 et non d, pour utiliser ensuite la fonction j3pSimplificationRacineTrinome(b,plusmoins,delta,a)
return produitTabCoef(tab1, inverseTabCoef(tab2))
}
function puissanceTabCoef (tab, puis) {
// tab est un tableau contenant les 4 coefs de la forme (a+b*racine(c))/d, puis est un entier naturel
// cette fonction renvoie un tableau sous la forme a2,b2,c2,d2 où (-a2+b2*racine(c2)/d est le résultat de tab^puis
// j’ai choisi -a2 et non a2, de même que d/2 et non d, pour utiliser ensuite la fonction j3pSimplificationRacineTrinome(b,plusmoins,delta,a)
if (isAlmostZero(puis)) {
return [1, 0, 0, 1]
} else {
return produitTabCoef(tab, puissanceTabCoef(tab, puis - 1))
}
}
function ecrireNbAvecCoef (tab) {
// tab est un tableau de 4 nombres [a,b,c,d] correspondant à (a+b*racine(c))/d
// cette fonction renvoie l’écriture du nombre au format latex
const b2val = j3pCalculValeur(tab[1])
if (isAlmostZero(b2val)) {
// b est nul, on se retrouve alors avec un nombre de la forme a/d
return j3pGetLatexQuotient(tab[0], tab[3])
} else {
const sousRadical = j3pGetLatexProduit(tab[2], j3pGetLatexProduit(tab[1], tab[1]))
// pour utiliser j3pSimplificationRacineTrinome, je ne dois pas entrer a mais -a, de même je dois entre d/2 au lieu de d
const a = j3pGetLatexProduit('-1', tab[0])
const d = j3pGetLatexQuotient(tab[3], 2)
if (b2val < 0) {
return j3pSimplificationRacineTrinome(a, '-', sousRadical, d)
} else {
return j3pSimplificationRacineTrinome(a, '+', sousRadical, d)
}
}
}
// La première chose à faire est d’extraire les coefs a,b,c,d de l’écriture (a+b*racine(c))/d sous la forme d’un tableau
const tabCoef1 = extraireCoef(nb1Latex)
let tabCoef2
if (!typeOp.includes('inv') && typeOp !== '^') {
// on ne demande ni l’inverse ni une puissance, donc on va chercher le deuxième nombre
// pour l’inverse, il n’y a pas de deuxième nombre
// pour la puissance, le deuxième nombre sera un entier naturel et ce sera nb2Latex
tabCoef2 = extraireCoef(nb2Latex)
} else {
tabCoef2 = []
}
if (typeOp === '-') {
// on a une soustraction. Cela revient à ajouter l’oppose
return ecrireNbAvecCoef(sommeTabCoef(tabCoef1, produitTabCoef([-1, 0, 0, 1], tabCoef2)))
}
if (typeOp === '+') {
// on a une somme
return ecrireNbAvecCoef(sommeTabCoef(tabCoef1, tabCoef2))
}
if (typeOp === '*') {
// on a un produit
return ecrireNbAvecCoef(produitTabCoef(tabCoef1, tabCoef2))
}
if (typeOp === '/') {
// on a un produit
return ecrireNbAvecCoef(divisionTabCoef(tabCoef1, tabCoef2))
}
if (typeOp.includes('inv')) {
// petite tolérance, car on peut écrire "inverse" et non seulement "inv"
// on a l’inverse
return ecrireNbAvecCoef(inverseTabCoef(tabCoef1))
}
if (typeOp === '^') {
// on a nb1^nb2. Il faut absolument que nb2 soit un entier
if (typeof puis === 'string') puis = Number(puis)
return ecrireNbAvecCoef(puissanceTabCoef(tabCoef1, puis))
}
console.error(Error('Paramètres invalides'))
} // operationAvecRadical
// Fonctions communes aux sections sur les études de fonctions
/**
*
* @param maLimite
* @returns {*|string}
*/
export function fctsEtudeEcritLimite (maLimite) {
if (maLimite === '-infini') {
return '-\\infty'
}
if (maLimite === '+infini') {
return '+\\infty'
}
return fctsEtudeEcrireNbLatex(maLimite)
}
/**
* À partir de domaine de définition détermine quelles sont les limites à calculer (en fonction des intervalles fermés ou ouverts)
* on a un cas particulier avec les valeurs interdites et limites à droite et à gauche à gérer
* @param objConteneur
* @param debugs
* @return {*}
*/
export function fctsEtudeDetermineTabLimites (objConteneur, debugs = false) {
const tabLimites = []
if (debugs) console.debug('objConteneur.domaineDef=', objConteneur.domaineDef)
// j’ai au moins deux bornes par domaine donc avec les deux crochets, 4 éléments
for (let index = 0; index < objConteneur.domaineDef.length; index = index + 4) {
// console.log("index=",index," et objConteneur.val_interdites=",objConteneur.val_interdites);
if (objConteneur.domaineDef[index + 2] === ']') { // ma borne inf correspond à un calcul de limite car le crochet est ouvert
// si c’est une valeur interdite c’est une limite à droite
if (objConteneur.val_interdites.includes(objConteneur.domaineDef[index]) || objConteneur.val_interdites.includes(ecritBienFraction(objConteneur.domaineDef[index]))) {
tabLimites.push(fctsEtudeEcritLimite(ecritBienFraction(objConteneur.domaineDef[index])) + '^+')
} else {
tabLimites.push(fctsEtudeEcritLimite(ecritBienFraction(objConteneur.domaineDef[index])))
}
}
if (objConteneur.domaineDef[index + 3] === '[') { // ma borne sup correspond à un calcul de limite car le crochet est ouvert
// si c’est une valeur interdite c’est une limite à gauche
if (objConteneur.val_interdites.includes(objConteneur.domaineDef[index + 1]) || objConteneur.val_interdites.includes(ecritBienFraction(objConteneur.domaineDef[index + 1]))) {
tabLimites.push(fctsEtudeEcritLimite(ecritBienFraction(objConteneur.domaineDef[index + 1])) + '^-')
} else {
tabLimites.push(fctsEtudeEcritLimite(ecritBienFraction(objConteneur.domaineDef[index + 1])))
}
}
}
return tabLimites
}
/**
* Dans le cas où le domaine de definition est surchargé, on doit chercher la bonne réponse parmi toutes les limites possibles du modèle
* @param valeur
* @param tab
*/
export function fctsEtudeDetermineLimiteSurcharge (valeur, tab) {
// console.log("valeur=",valeur," et tab=",tab);
// puisqu’on demande une limite d’un modèle cette valeur figure forcément sur une des bornes du modèle (sinon c pas une limite, c’est une image...)
for (let i = 0; i < tab.length; i++) {
if (ecritBienBorne(valeur) === ecritBienBorne(fctsEtudeEcritLimite(tab[i][0]))) {
return tab[i][1]
}
}
throw Error('Aucune limite trouvée correspondant à la valeur demandée :' + valeur)
}
/**
* Calculer l’image de n’importe quel nombre suivant la fonction
* @param objDerivee
* @param modele
* @param valeur
* @return {*}
*/
export function fctsEtudeDetermineImage (objDerivee, modele, valeur) {
// var tab_retour=[];
// var coef,dans_exp;
let a, b, c, d, a1, b1, c1, delta, deltaVal, monome1, monome2, monome3, terme1, terme2, num, den
let e, axplusb, dxpluse
let image, expo
switch (modele) {
case 8: // ax²+bx+c
a = objDerivee.variables[2]
b = objDerivee.variables[1]
c = objDerivee.variables[0]
monome1 = fctsEtudeProduitNbs(a, fctsEtudeProduitNbs(valeur, valeur))
monome2 = fctsEtudeProduitNbs(b, valeur)
image = fctsEtudeSommeNbs(monome1, fctsEtudeSommeNbs(monome2, c))
break
case 7:// ax+b+cln(dx+e)
a = objDerivee.variables[0]
b = objDerivee.variables[1]
c = objDerivee.variables[2]
d = objDerivee.variables[3]
e = objDerivee.variables[4]
axplusb = fctsEtudeSommeNbs(fctsEtudeProduitNbs(a, valeur), b)
dxpluse = fctsEtudeSommeNbs(fctsEtudeProduitNbs(d, valeur), e)
if (Math.abs(fctsEtudeCalculValeur(dxpluse) - 1) < epsilon) {
// on a ln 1
image = axplusb
} else {
image = (axplusb === '0')
? fctsEtudeEcrireMonome(1, 1, c, '\\ln ' + dxpluse)
: fctsEtudeEcrireMonome(1, 0, axplusb) + fctsEtudeEcrireMonome(2, 1, c, '\\ln ' + dxpluse)
}
break
case 6:// (a+bln(x))/x^c
a = objDerivee.variables[0]
b = objDerivee.variables[1]
c = objDerivee.variables[2]
if (String(valeur).includes('mathrm{e}')) {
// valeur est une exponentielle
const nbExp = valeur.substring(String(valeur).indexOf('mathrm{e}^{') + 11, valeur.length - 1)
const sansExpo = valeur.replace('\\mathrm{e}^{' + nbExp + '}', '')
if (sansExpo === '') {
// la valeur est juste de la forme exp(...) et pas a*exp(...)
num = fctsEtudeSommeNbs(a, fctsEtudeProduitNbs(b, nbExp))
const puisExp = fctsEtudeProduitNbs(nbExp, c)
// console.log("puisExp:"+puisExp)
if (isAlmostEqual(puisExp, 1)) {
den = '\\mathrm{e}'
} else if (isAlmostZero(puisExp + 1)) {
num = fctsEtudeEcrireMonome(1, 1, num, '\\mathrm{e}')
den = ''
} else if (fctsEtudeCalculValeur(puisExp) < 0) {
num = fctsEtudeEcrireMonome(1, 1, num, '\\mathrm{e}^{' + fctsEtudeProduitNbs(puisExp, -1) + '}')
den = ''
} else {
den = '\\mathrm{e}^{' + puisExp + '}'
}
if (String(num).includes('frac')) {
// il y a une fraction au numérateur
const tabNumDen = fctsEtudeExtraireNumDen(num)
num = tabNumDen[1]
den = tabNumDen[2] + den
}
if (den === '') {
image = num
} else {
image = '\\frac{' + num + '}{' + den + '}'
}
}
} else {
// ce ne peut être que 1 autrement
image = a
}
// console.log("image:"+image+" valeur:"+valeur)
break
case 5:// (ax+b)exp(cx+d)+e
a = objDerivee.variables[0]
b = objDerivee.variables[1]
c = objDerivee.variables[2]
d = objDerivee.variables[3]
e = (objDerivee.variables.length > 4) ? objDerivee.variables[4] : 0
terme1 = fctsEtudeSommeNbs(fctsEtudeProduitNbs(a, valeur), b)
terme2 = fctsEtudeSommeNbs(fctsEtudeProduitNbs(c, valeur), d)
{
const terme1Aff = (String(terme1) === '1') ? '' : (String(terme1) === '-1') ? '-' : terme1
image = terme1Aff + '\\mathrm{e}^{' + terme2 + '}' + fctsEtudeEcrireMonome(2, 0, e)
}
break
case 4:// (ax+b)/cx^2+d)
a = objDerivee.variables[0]
b = objDerivee.variables[1]
c = objDerivee.variables[2]
d = objDerivee.variables[3]
// la valeur décimale du discriminant
deltaVal = objDerivee.variables[8]
if ((deltaVal <= 0) || ((deltaVal > 0) && (!valeur.includes('sqrt')))) {
// soit le trinôme n’admet aucune ou une seule racine
// soit il a 2 racines mais elles sont simples (sans radical)
num = fctsEtudeSommeNbs(fctsEtudeProduitNbs(a, valeur), b)
den = fctsEtudeSommeNbs(fctsEtudeProduitNbs(c, fctsEtudeProduitNbs(valeur, valeur)), d)
image = fctsEtudeDivisionNbs(num, den)
} else {
let nb1Latex = operationAvecRadical({ typeOp: '*', nb1Latex: a, nb2Latex: valeur })
num = operationAvecRadical({ typeOp: '+', nb1Latex, nb2Latex: b })
const nb2Latex = operationAvecRadical({ typeOp: '^', nb1Latex: valeur, puis: 2 })
nb1Latex = operationAvecRadical({ typeOp: '*', nb1Latex: c, nb2Latex })
den = operationAvecRadical({ typeOp: '+', nb1Latex, nb2Latex: d })
image = operationAvecRadical({ typeOp: '/', nb1Latex: num, nb2Latex: den })
}
// console.log("num:"+num+" den:"+den+" image:"+image)
break
case 3:// (ax+b)/(cx+d)
a = objDerivee.variables[0]
b = objDerivee.variables[1]
c = objDerivee.variables[2]
d = objDerivee.variables[3]
num = fctsEtudeSommeNbs(fctsEtudeProduitNbs(a, valeur), b)
den = fctsEtudeSommeNbs(fctsEtudeProduitNbs(c, valeur), d)
if (den !== 0) {
image = fctsEtudeDivisionNbs(num, den)
} else { // pare feu
image = 'valInterdite'
}
break
case 2 :// ax^3+bx^2+cx+d
a = objDerivee.variables[3]
b = objDerivee.variables[2]
c = objDerivee.variables[1]
d = objDerivee.variables[0]
// la dérivée est a1x^2+b1x+c1
a1 = objDerivee.variables[4]
b1 = objDerivee.variables[5]
c1 = objDerivee.variables[6]
// le discriminant en mode txt
delta = objDerivee.variables[7]
// et sa valeur décimale
deltaVal = objDerivee.variables[8]
if ((deltaVal <= 0) || ((deltaVal > 0) && (!valeur.includes('sqrt')))) {
// soit le trinôme n’admet aucune ou une seule racine
// soit il a 2 racines mais elles sont simples (sans radical)
monome1 = fctsEtudeProduitNbs(a, fctsEtudeProduitNbs(fctsEtudeProduitNbs(valeur, valeur), valeur))
monome2 = fctsEtudeProduitNbs(b, fctsEtudeProduitNbs(valeur, valeur))
monome3 = fctsEtudeProduitNbs(c, valeur)
terme1 = fctsEtudeSommeNbs(monome1, monome2)
terme2 = fctsEtudeSommeNbs(monome3, d)
image = fctsEtudeSommeNbs(terme1, terme2)
} else {
// le trinôme admet 2 racines
const racine1Val = (-fctsEtudeCalculValeur(b1) - Math.sqrt(deltaVal)) / (2 * fctsEtudeCalculValeur(a1))// valeur décimale de (-b-sqrt{delta})/(2a)
const valeurVal = fctsEtudeCalculValeur(valeur)// valeur au format décimal
if (isAlmostEqual(racine1Val, valeurVal)) {
// on cherche l’image par le pol de degré 3 de la première racine du trinôme
image = imagePolDegre3(a, b, c, d, a1, b1, c1, delta)[0]
} else {
image = imagePolDegre3(a, b, c, d, a1, b1, c1, delta)[1]
}
}
break
default:
a = objDerivee.variables[0]
b = objDerivee.variables[1]
expo = '\\mathrm{e}^{' + valeur + '}'
if (String(valeur) === '0') {
expo = ''
}
if (String(valeur) === '1') {
expo = '\\mathrm{e}'
}
{
const fact1 = fctsEtudeSommeNbs(fctsEtudeProduitNbs(a, valeur), b)
const fact1Aff = (String(fact1) === '1') ? '' : (String(fact1) === '-1') ? '-' : String(fact1)
image = fact1Aff + expo
}
break
}
return image
}
export function fctsEtudeSigneFois (nombre) {
if (nombre > 0) {
if (nombre === 1) {
return '+'
} else {
return '+' + nombre
}
} else {
if (nombre === -1) {
return '-'
} else {
return '-' + Math.abs(nombre)
}
}
}
/**
* identique aux 3 sections exemples, car chacune peut générer l’aléatoire, a priori sur les mêmes modèles
* @param numModele
* @param {boolean} [isDebug=false]
* @return {{}}
*/
export function fctsEtudeGenereAleatoire (numModele, isDebug = false) {
// cette fonction génère les coefficients aléatoires utiles pour chaque modèle
// elle renvoie un obj avec l’expression de la fonction au format mathématique (pas latex)
// numModele est le numéro du modèle
// isDebug permet d’afficher des infos en console
const obj = {}
let a, b, c, d, e, aprime, bprime, cprime, x1, x2, pgcdab
if (numModele === 0) {
numModele = j3pGetRandomInt(1, 7)
}
switch (Number(numModele)) {
case 8:// modèle 8 : ax^2+bx+c
do {
a = (2 * j3pGetRandomInt(0, 1) - 1) * j3pGetRandomInt(2, 8)
b = (2 * j3pGetRandomInt(0, 1) - 1) * j3pGetRandomInt(1, 8)
c = (2 * j3pGetRandomInt(0, 1) - 1) * j3pGetRandomInt(1, 8)
} while (Math.abs(Math.pow(b, 2) - 4 * a * c) < Math.pow(10, -10))
obj.fonction = fctsEtudeEcrireMonome(1, 2, a) + fctsEtudeEcrireMonome(2, 1, b) + fctsEtudeEcrireMonome(3, 0, c)
break
case 7:// ax+b+cln(dx+e)
a = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 4)// a est non nul
b = j3pGetRandomInt(-4, 4)
c = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 4)// c est non nul
do {
d = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 4)// d est non nul
} while (isAlmostEqual(c, d))
e = j3pGetRandomInt(-4, 4)
{
const expressionLn = 'ln(' + fctsEtudeEcrireMonome(1, 1, d) + fctsEtudeEcrireMonome(2, 0, e) + ')'
obj.fonction = fctsEtudeEcrireMonome(1, 1, a) + fctsEtudeEcrireMonome(2, 0, b) + fctsEtudeEcrireMonome(3, 1, c, expressionLn)
}
break
case 6:// (a+bln(x))/x^c
{
const k = j3pGetRandomInt(-4, 4)
do {
b = j3pGetRandomInt(-7, 7)
c = j3pGetRandomInt(1, 3)// par défaut et pour rester un peu plus dans l’esprit du programme, on fixe c à 1
c = 1
} while ((Math.abs(b / c - Math.round(b / c)) > epsilon) || (isAlmostZero(b)))
a = Math.round(b / c) - k * b
let nume = fctsEtudeEcrireMonome(1, 0, a) + fctsEtudeEcrireMonome(2, 1, b, 'ln x')
if (isAlmostZero(a)) {
nume = fctsEtudeEcrireMonome(1, 1, b, 'ln x')
}
if (isAlmostEqual(c, 1)) {
obj.fonction = '(' + nume + ')/x'
} else {
obj.fonction = '(' + nume + ')/x^' + c + ''
}
}
break
case 5:// (ax+b)exp(cx+d)+e
a = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 3)
b = j3pGetRandomInt(-5, 5)
c = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 3)
d = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 5)
e = 0 // Par défaut, il vaudra toujours 0 mais je l’ai mis pour qu’on puisse utiliser ce cas de figure dans le cadre d’une équa diff y'=ay+b
if (isAlmostZero(b)) {
obj.fonction = fctsEtudeEcrireMonome(1, 1, a) + 'exp(' + fctsEtudeEcrireMonome(1, 1, c) + fctsEtudeEcrireMonome(2, 0, d) + ')'
} else {
obj.fonction = '(' + fctsEtudeEcrireMonome(1, 1, a) + fctsEtudeEcrireMonome(2, 0, b) + ')exp(' + fctsEtudeEcrireMonome(1, 1, c) + fctsEtudeEcrireMonome(2, 0, d) + ')'
}
break
case 4:// modèle (ax+b)/(cx^2+d), a, b, c, d entiers
// c et d seront strictement positifs pour que le domaine soit \R
// la dérivée est trinôme/(cx^2+d)^2 où le trinôme a nécessairement deux racines
// donc je commence par générer les racines x1 et x2 pour qu’elles soient simples
a = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 4)
do {
x1 = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 4)
x2 = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 4)
b = -a * (x1 + x2) / 2
} while (Math.abs(Math.round(b) - b > epsilon) || (x1 * x2 > 0)) // comme ça b sera entier et des racines de signes contraires font que d sera positif
c = j3pGetRandomInt(1, 3)
d = -c * x1 * x2
if (isAlmostZero(b)) {
// b est nul
pgcdab = Math.abs(a)
} else {
pgcdab = j3pPGCD(Math.abs(a), Math.abs(b))
}
{
const pgcdcd = j3pPGCD(Math.abs(c), Math.abs(d))
const pgcdGlobal = j3pPGCD(pgcdab, pgcdcd)
if (pgcdGlobal > 1) {
a = a / pgcdGlobal
b = b / pgcdGlobal
c = c / pgcdGlobal
d = d / pgcdGlobal
}
}
obj.fonction = '(' + fctsEtudeEcrireMonome(1, 1, a) + fctsEtudeEcrireMonome(2, 0, b) + ')/(' + fctsEtudeEcrireMonome(1, 2, c) + fctsEtudeEcrireMonome(2, 0, d) + ')'
break
case 3:// modèle 3 : (ax+b)/(cx+d)
{
let OK // variable juste là pour vérifier ensuite si les coefs sont acceptables ou pas
do {
OK = true
a = j3pGetRandomInt(-6, 6)
b = j3pGetRandomInt(-6, 6)
c = j3pGetRandomInt(-6, 6)
d = j3pGetRandomInt(-6, 6)
OK = OK && (Math.abs(c) > epsilon)// bien sûr, c doit être non nul
OK = OK && (Math.abs(d) > epsilon)// on force aussi d à être non nul
OK = OK && ((Math.abs(a) > epsilon) || (Math.abs(b) > epsilon))// le numérateur ne doit pas être nul
OK = OK && (Math.abs(a * d - b * c) > epsilon)
let pgcd1, pgcd2
if (OK) {
if (isAlmostZero(a)) {
pgcd1 = Math.abs(b)
} else if (isAlmostZero(b)) {
pgcd1 = Math.abs(a)
} else {
pgcd1 = j3pPGCD(Math.abs(a), Math.abs(b))
}
if (isAlmostZero(d)) {
pgcd2 = Math.abs(c)
} else {
pgcd2 = j3pPGCD(Math.abs(c), Math.abs(d))
}
OK = ((j3pPGCD(pgcd1, pgcd2) === 1) && (pgcd2 === 1))
}
} while (!OK)
}
if (isAlmostZero(a)) {
obj.fonction = '(' + fctsEtudeEcrireMonome(1, 0, b) + ')/(' + fctsEtudeEcrireMonome(1, 1, c) + fctsEtudeEcrireMonome(2, 0, d) + ')'
} else {
obj.fonction = '(' + fctsEtudeEcrireMonome(1, 1, a) + fctsEtudeEcrireMonome(2, 0, b) + ')/(' + fctsEtudeEcrireMonome(1, 1, c) + fctsEtudeEcrireMonome(2, 0, d) + ')'
}
break
case 2:// modèle 2 : ax^3+bx^2+cx+d
// on gère les coefficients pour que les racines du trinôme égal à la dérivée (si elles existent) soient simples
// deux cas de figure. Dans 2 cas sur 3, la dérivée admettra 2 racines, dans 1 cas sur 3, la dérivée n’aura pas de racine
{
const casFigure = j3pRandomTab(['2 racines', 'pas racine'], [0.6667, 0.3333])
if (casFigure === '2 racines') {
// j'écris la dérivée sous la forme a(x-x_1)(x-x_2)
do {
x1 = j3pGetRandomInt(-8, 8)
x2 = j3pGetRandomInt(-8, 8)
} while (Math.abs(Math.abs(x1) - Math.abs(x2)) < epsilon)
do {
aprime = j3pGetRandomInt(-6, 6)
} while ((Math.abs(Math.abs(aprime) - Math.abs(x2)) < epsilon) || (Math.abs(Math.abs(x1) - Math.abs(aprime)) < epsilon) || (isAlmostZero(aprime)))
bprime = Math.round(-1 * aprime * (x1 + x2))
cprime = Math.round(aprime * (x1 * x2))
} else {
let delta
do {
do {
aprime = j3pGetRandomInt(-8, 8)
} while (isAlmostZero(aprime))
bprime = j3pGetRandomInt(-8, 8)
cprime = j3pGetRandomInt(-8, 8)
delta = Math.pow(bprime, 2) - 4 * aprime * cprime
} while (delta >= 0)
}
a = fctsEtudeDivisionNbs(String(aprime), '3')
b = fctsEtudeDivisionNbs(String(bprime), '2')
c = String(cprime)
d = String(j3pGetRandomInt(-8, 8))
obj.fonction = fctsEtudeEcrireMonome(1, 3, a) + fctsEtudeEcrireMonome(2, 2, b) + fctsEtudeEcrireMonome(3, 1, c) + fctsEtudeEcrireMonome(4, 0, d)
}
break
default :// modèle 1 : (ax+b)exp(x)
do {
a = j3pGetRandomInt(-7, 7)
b = j3pGetRandomInt(-7, 7)
} while ((b === 0) || (a === 1) || (a === 0))
// expression de la fonction en langage mathématique
obj.fonction = '(' + fctsEtudeEcrireMonome(1, 1, a) + fctsEtudeEcrireMonome(2, 0, b) + ')exp(x)'
break
}
const objGenere = fctsEtudeGenereDonnees(numModele, obj.fonction)
for (const prop in objGenere) {
obj[prop] = objGenere[prop]
}
return obj
}
/**
* génère les données utiles de chaque modèle
* @param modele
* @param laFonction
* @param {boolean} [isDebug=false]
* @return {{}}
*/
export function fctsEtudeGenereDonnees (modele, laFonction, isDebug = false) {
// pour chaque modèle, on génère le domaine s’il n’est pas imposé
// on déterminer également les limites sur le domaine considéré si ce sont bien des limites et non des images
// isDebug permet d’afficher des infos en console pour débugger
const obj = {}
obj.modele = modele
obj.val_interdites = []// ce tableau contiendra la ou les éventuelles valeurs interdites
obj.limites = []// ce tableau contiendra les limites aux bornes des intervalles du domaine
// bien entendu, il ne contiendra pas les images lorsqu’elles existent (lorsque l’intervalle est fermé ou semi-fermé)
// on a besoin de l’expression de la fonction, puis de calculer certaines images
let a, b, c, d, nume, tabCoefs, fctAffine, coefC, dxpluse, fctAffine2, tabCoef2
obj.fonction = laFonction
if ((modele === 5) && (laFonction.indexOf('(ax+b)') === 0)) {
// c’est le modèle 5 et on a imposé une fonction où la fonction affine ax+b est aléatoire alors que cx+d est réellement imposé
fctAffine2 = laFonction.substring(laFonction.indexOf('exp') + 3)
fctAffine2 = fctAffine2.substring(fctAffine2.indexOf('(') + 1, fctAffine2.indexOf(')'))
tabCoef2 = extraireCoefsFctaffine(fctAffine2)
do {
a = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 3)
} while (Math.abs(Math.abs(a) - Math.abs(tabCoef2[0])) < epsilon)
b = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 5)
let partieExp = laFonction.substring(laFonction.indexOf('exp'))
// c’est qu’on a imposé la valeur de c ou celle de d (voire les 2)
fctAffine = partieExp.substring(4, partieExp.indexOf(')'))
if (fctAffine.includes('cx')) {
// c est aléatoire
c = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 3)
if (fctAffine.includes('+cx')) {
fctAffine = fctAffine.replace('+cx', j3pMonome(2, 1, c))
} else {
fctAffine = fctAffine.replace('cx', j3pMonome(1, 1, c))
}
}
if (fctAffine.includes('d')) {
// d est aléatoire
d = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 5)
if (fctAffine.includes('+d')) {
fctAffine = fctAffine.replace('+d', j3pMonome(2, 0, d))
} else {
fctAffine = fctAffine.replace('d', j3pMonome(1, 0, d))
}
}
const e = partieExp.substring(partieExp.indexOf(')') + 1)
partieExp = 'exp(' + fctAffine + ')'
laFonction = '(' + fctsEtudeEcrireMonome(1, 1, a) + fctsEtudeEcrireMonome(2, 0, b) + ')' + partieExp + e
// Pour être en accord avec la fonction précéddente, on pourrait ajouter e mais il vaudra 0
obj.fonction = laFonction
} else if ((modele === 5) && (laFonction.indexOf('(cx+d)') === laFonction.length - 6)) {
// c’est le modèle 5 et on a imposé une fonction où la fonction affine cx+d est aléatoire alors que ax+b est réellement imposé
// var partieExp = laFonction.substring(laFonction.indexOf("exp"));
// c’est qu’on a imposé la valeur de c ou celle de d (voire les 2)
fctAffine = laFonction.substring(0, laFonction.indexOf('exp'))
if (fctAffine.includes('ax')) {
// a est aléatoire
a = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 3)
if (fctAffine.includes('+ax')) {
fctAffine = fctAffine.replace('+ax', j3pMonome(2, 1, a))
} else {
fctAffine = fctAffine.replace('ax', j3pMonome(1, 1, a))
}
}
if (fctAffine.includes('b')) {
// b est aléatoire
do {
b = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 5)
} while (isAlmostEqual(b, 1))
if (fctAffine.includes('+b')) {
fctAffine = fctAffine.replace('+b', j3pMonome(2, 0, b))
} else {
fctAffine = fctAffine.replace('b', j3pMonome(1, 0, b))
}
}
if (String(fctAffine) === '-1') {
fctAffine = '-'
} else if (String(fctAffine) === '1') {
fctAffine = ''
}
tabCoef2 = extraireCoefsFctaffine(fctAffine)
do {
c = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 3)
} while ((Math.abs(c - 1) < Math.pow(10, -10)) || (Math.abs(Math.abs(c) - Math.abs(tabCoef2[0])) < Math.pow(10, -10)))
d = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 5)
laFonction = fctAffine + 'exp(' + fctsEtudeEcrireMonome(1, 1, c) + fctsEtudeEcrireMonome(2, 0, d) + ')'
// Même remarque sur la présence du e
obj.fonction = laFonction
} else if ((modele === 6) && (laFonction.indexOf('(a+bln(x))') === 0)) {
// c’est le modèle et on a imposé la puissance du dénominateur (par contre le numérateur est aléatoire et de la forme a+bln x
// il faut que je récupère la valeur du dénominateur
const den = laFonction.substring(laFonction.indexOf('/') + 1)
const regNb = /\d+/ig
if (regNb.test(den)) { // il y a une puissance de x
c = Number(den.match(regNb)[0])
} else { // c’est donc que la puissance de x imposée est 1
c = 1
}
const k = j3pGetRandomInt(-4, 4)
do {
if (c > 7) {
b = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 2) * c
} else {
b = j3pGetRandomInt(-7, 7)
}
} while ((Math.abs(b / c - Math.round(b / c)) > epsilon) || (isAlmostZero(b)))
a = Math.round(b / c) - k * b
nume = fctsEtudeEcrireMonome(1, 0, a) + fctsEtudeEcrireMonome(2, 1, b, 'ln x')
if (isAlmostZero(a)) {
nume = fctsEtudeEcrireMonome(1, 1, b, 'ln x')
}
if (isAlmostEqual(c, 1)) {
laFonction = '(' + nume + ')/x'
} else {
laFonction = '(' + nume + ')/x^' + c + ''
}
obj.fonction = laFonction
} else if ((modele === 7) && (laFonction.indexOf('ax+b') === 0)) {
// c’est le modèle 7 de la forme ax+b+cln(dx+e) mais on a imposé d et e, voire c
// a et b sont aléatoires
a = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 4)// a est non nul
b = j3pGetRandomInt(-4, 4)
if (laFonction.includes('c')) {
// c est aussi aléatoire (en gros on n’impose que d et e dans l’expression ln(dx+e)
c = (j3pGetRandomInt(0, 1) * 2 - 1) * j3pGetRandomInt(1, 4)// c est non nul
} else {
// il faut que je trouve la valeur de c
const fctSansaxb = laFonction.replace('ax+b', '')
c = fctSansaxb.substring(0, fctSansaxb.indexOf('ln'))
if (c[0] === '+') {
c = c.substring(1)
}
if (c === '') {
c = 1
} else if (c === '-') {
c = -1
} else {
c = Number(c)
}
}
let dansln = laFonction.substring(laFonction.indexOf('ln') + 2)
if (dansln[0] === '(') {
dansln = dansln.substring(1)
}
if (dansln[0] === ' ') {
dansln = dansln.substring(1)
}
if (dansln[dansln.length - 1] === ')') {
dansln = dansln.substring(0, dansln.length - 1)
}
if (dansln === 'x') {
laFonction = fctsEtudeEcrireMonome(1, 1, a) + fctsEtudeEcrireMonome(2, 0, b) + fctsEtudeEcrireMonome(3, 1, c, 'ln x')
} else {
laFonction = fctsEtudeEcrireMonome(1, 1, a) + fctsEtudeEcrireMonome(2, 0, b) + fctsEtudeEcrireMonome(3, 1, c, 'ln(' + dansln + ')')
}
obj.fonction = laFonction
}
const fctPourCalc = fctsEtudeEcriturePourCalcul(laFonction)
const arbreFct = new Tarbre(fctPourCalc, ['x'])
// var lnRegexp = new RegExp('(\\-?\d{0,}\\*?ln\\sx)|(\\-?\d{0,}\\*?ln\\(x\\))', 'ig')
// @todo remplacer par la ligne suivante, mais faut le tester --> J’ai fait des tests que me semblent concluants (Rémi)
const lnRegexp = /-?\d*\*?ln(?:\sx|\(x\))/ig // signe - éventuel, suivi de chiffres éventuels, suivi d’un * éventuel, suivi de 'ln' suivi de espace+x ou (x)
switch (modele) {
case 8:// modèle ax^2+bx+c
// ensembles de définition et de dérivabilité sous la forme de tableaux
obj.domaineDef = ['-infini', '+infini', ']', '[']
obj.domaineDeriv = ['-infini', '+infini', ']', '[']
// je recherche les valeurs approchées des coefs
c = arbreFct.evalue([0])
{
const imagePlus1 = arbreFct.evalue([1])
const imageMoins1 = arbreFct.evalue([-1])
b = 0.5 * (imagePlus1 - imageMoins1)
a = 0.5 * (imagePlus1 + imageMoins1 - 2 * c)
}
if (a < 0) {
obj.limites.push(['-infini', '-\\infty'])// en -\\infty, la limite vaut -\\infty
obj.limites.push(['+infini', '-\\infty'])// en +\\infty, la limite vaut -\\infty
} else {
obj.limites.push(['-infini', '+\\infty'])// en -\\infty, la limite vaut +\\infty
obj.limites.push(['+infini', '+\\infty'])// en +\\infty, la limite vaut +\\infty
}
break
case 7:// modèle ax+b+cln(dx+e)
if (laFonction.includes('(')) {
// il y a des parenthèses, donc dx+e ne se réduit à pas à x
dxpluse = laFonction.substring(laFonction.indexOf('(') + 1, laFonction.indexOf(')'))
} else {
dxpluse = 'x'
}
{
const coefDe = extraireCoefsFctaffine(dxpluse)
d = coefDe[0]
const e = coefDe[1]
const borneDomaine = fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', e), d)
const dVal = fctsEtudeCalculValeur(d)
if (dVal > 0) {
obj.domaineDef = [borneDomaine, '+infini', ']', '[']
} else {
obj.domaineDef = ['-infini', borneDomaine, ']', '[']
}
obj.domaineDeriv = obj.domaineDef
const coefDevantlnReg = /-?\d+\*?ln/ig
const coefDevantlnReg2 = /-?ln/ig
if (coefDevantlnReg.test(laFonction)) {
coefC = laFonction.match(coefDevantlnReg)
} else {
coefC = laFonction.match(coefDevantlnReg2)
}
c = coefC[0].replace('ln', '')
if (c === '') {
c = '1'
} else if (c === '-') {
c = '-1'
} else if (c[c.length - 1] === '*') {
c = c.substring(0, c.length - 1)
}
if (c === '') {
fctAffine = laFonction.substring(0, '\\ln')
} else {
fctAffine = laFonction.substring(0, laFonction.indexOf(coefC[0]))
}
if (fctAffine[fctAffine.length - 1] === '+') {
fctAffine = fctAffine.substring(0, fctAffine.length - 1)
}
const coefsab = extraireCoefsFctaffine(fctAffine)
a = coefsab[0]
b = coefsab[1]
if (isDebug) console.debug('dans fctsEtudeGenereDonnees, a:' + a + ' b:' + b + ' c:' + c + ' d:' + d + ' e:' + e)
const valA = fctsEtudeCalculValeur(a)
const valC = fctsEtudeCalculValeur(c)
if (dVal > 0) {
if (valC > 0) {
obj.limites.push([borneDomaine + '^+', '-\\infty'])
} else {
obj.limites.push([borneDomaine + '^+', '+\\infty'])
}
if (valA > 0) {
obj.limites.push(['+infini', '+\\infty'])
} else {
obj.limites.push(['+infini', '-\\infty'])
}
} else {
if (valA > 0) {
obj.limites.push(['-infini', '-\\infty'])
} else {
obj.limites.push(['-infini', '+\\infty'])
}
if (valC > 0) {
obj.limites.push([borneDomaine + '^-', '-\\infty'])
} else {
obj.limites.push([borneDomaine + '^-', '+\\infty'])
}
}
obj.val_interdites.push(borneDomaine)
}
break
case 6:// modèle (a+bln(x))/x^c
obj.val_interdites.push('0')
obj.domaineDef = ['0', '+infini', ']', '[']
obj.domaineDeriv = obj.domaineDef
{
// je dois tout d’abord trouver la valeur de b
const fctMath = j3pMathquillXcas(laFonction)// écriture de la fonction au format mathématique
const num = fctMath.substring(0, fctMath.indexOf('/'))
// console.log("num:"+num+" test:"+lnRegexp.test(num))
const tabNum = num.match(lnRegexp)
const coefB = tabNum[0].substring(0, Math.max(tabNum[0].indexOf('\\ln'), tabNum[0].indexOf('ln')))
if (coefB[0] === '-') {
// c’est que b est négatif, la limite en 0 est donc +infini
obj.limites.push(['0^+', '+\\infty'], ['+infini', '0'])
} else {
// b est positif, la limite en 0 est -infini
obj.limites.push(['0^+', '-\\infty'], ['+infini', '0'])
}
}
// console.log("fctMath:"+fctMath+" tabNum:"+tabNum+" obj.limites:"+obj.limites)
break
case 5:// modèle (ax+b)exp(cx+d)+e
obj.limites_affine1 = []// limite de ax+b dans l’écriture (ax+b)exp(cx+d)
obj.limites_affine2 = []// limite de cx+d dans l’écriture (ax+b)exp(cx+d)
obj.domaineDef = ['-infini', '+infini', ']', '[']
obj.domaineDeriv = obj.domaineDef
{
let fctAffine1 = laFonction.substring(0, laFonction.indexOf('exp'))
fctAffine2 = laFonction.substring(laFonction.indexOf('exp') + 3, laFonction.lastIndexOf(')') + 1)
let e = 0
if (laFonction.lastIndexOf(')') !== laFonction.length - 1) e = fctsEtudeCalculValeur(laFonction.substring(laFonction.lastIndexOf(')') + 1))
if (fctAffine1.indexOf('(') === 0) {
fctAffine1 = fctAffine1.substring(fctAffine1.indexOf('(') + 1, fctAffine1.indexOf(')'))
}
if (fctAffine1.charAt(fctAffine1.length - 1) === '*') fctAffine1 = fctAffine1.substring(0, fctAffine1.length - 1)
fctAffine2 = fctAffine2.substring(fctAffine2.indexOf('(') + 1, fctAffine2.indexOf(')'))
const tabCoef1 = extraireCoefsFctaffine(fctAffine1)
tabCoef2 = extraireCoefsFctaffine(fctAffine2)
if (isDebug) console.debug(fctAffine1, tabCoef1, 'et pour cx+d:', fctAffine2, tabCoef2)
if (Number(tabCoef1[0]) === 0) {
obj.limites_affine1.push(String(tabCoef1[1]), String(tabCoef1[1]))
if (Number(tabCoef1[1]) > 0) {
if (Number(tabCoef2[0]) > 0) {
obj.limites.push(['-infini', String(e)])
obj.limites.push(['+infini', '+\\infty'])
} else {
obj.limites.push(['-infini', '+\\infty'])
obj.limites.push(['+infini', String(e)])
}
} else {
if (Number(tabCoef2[0]) > 0) {
obj.limites.push(['-infini', String(e)])
obj.limites.push(['+infini', '-\\infty'])
} else {
obj.limites.push(['-infini', '-\\infty'])
obj.limites.push(['+infini', String(e)])
}
}
} else if (Number(tabCoef1[0]) > 0) {
obj.limites_affine1.push('-\\infty', '-\\infty')
obj.limites_affine1.push('+\\infty', '+\\infty')
if (Number(tabCoef2[0]) > 0) {
obj.limites.push(['-infini', String(e)])
obj.limites.push(['+infini', '+\\infty'])
} else {
obj.limites.push(['-infini', '-\\infty'])
obj.limites.push(['+infini', String(e)])
}
} else {
obj.limites_affine1.push('-\\infty', '+\\infty')
obj.limites_affine1.push('+\\infty', '-\\infty')
if (Number(tabCoef2[0]) > 0) {
obj.limites.push(['-infini', String(e)])
obj.limites.push(['+infini', '-\\infty'])
} else {
obj.limites.push(['-infini', '+\\infty'])
obj.limites.push(['+infini', String(e)])
}
}
if (Number(tabCoef2[0] > 0)) {
obj.limites_affine2.push('-\\infty', '-\\infty')
obj.limites_affine2.push('+\\infty', '+\\infty')
} else {
obj.limites_affine2.push('-\\infty', '+\\infty')
obj.limites_affine2.push('+\\infty', '-\\infty')
}
}
break
case 4:
// modèle (ax+b)/(cx^2+d), a, b, c, d entiersz
// ensembles de définition et de dérivabilité sous la forme de tableaux
obj.domaineDef = ['-infini', '+infini', ']', '[']
obj.domaineDeriv = obj.domaineDef
obj.limites.push(['-infini', '0'], ['+infini', '0'])
obj.limites_num = []// spécifique au modèle : on y met la limite en chaque borne du domaine du numérateur
obj.limites_den = []// spécifique au modèle : on y met la limite en chaque borne du domaine du dénominateur
nume = fctPourCalc.substring(fctPourCalc.indexOf('(') + 1, fctPourCalc.indexOf(')'))// je me retrouve avec une fonction affine ax+b
tabCoefs = extraireCoefsFctaffine(nume)
if (fctsEtudeCalculValeur(tabCoefs[0]) > 0) {
obj.limites_num.push('-infini', '+infini')
} else {
obj.limites_num.push('+infini', '-infini')
}// on a la limite en -infini
obj.limites_den.push('+infini', '+infini')
break
case 3:
// modèle 3 : (ax+b)/(cx+d)
// ensembles de définition et de dérivabilité sous la forme de tableaux
// il faut que je détermine les valeurs de c et d pour m’en sortir
{
let deno = fctPourCalc.substring(fctPourCalc.indexOf('/') + 1)// dans le cas où je n’ai pas de parenthèses autour du dénominateur
if (fctPourCalc[fctPourCalc.length - 1] === ')') { // et si maintenant j’en ai :
deno = fctPourCalc.substring(fctPourCalc.lastIndexOf('(') + 1, fctPourCalc.lastIndexOf(')'))// je me retrouve avec une fonction affine cx+d
}
tabCoefs = extraireCoefsFctaffine(deno)
const valInterdite = ecritBienFraction(fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', tabCoefs[1]), tabCoefs[0]))
obj.val_interdites.push(valInterdite)
obj.domaineDef = ['-infini', valInterdite, ']', '[', valInterdite, '+infini', ']', '[']
obj.domaineDeriv = obj.domaineDef
nume = fctPourCalc.substring(fctPourCalc.indexOf('(') + 1, fctPourCalc.indexOf(')'))// je me retrouve avec une fonction affine ax+b
const tabCoefs2 = extraireCoefsFctaffine(nume)
const limiteInf = fctsEtudeDivisionNbs(tabCoefs2[0], tabCoefs[0])// limite en +infty et -infty au format latex
const valeurValInterdite = fctsEtudeCalculValeur(valInterdite)// valeur interdite au format décimal (approché)
const arbreNum = new Tarbre(j3pMathquillXcas(nume), ['x'])
const imageNume = arbreNum.evalue([valeurValInterdite])
let limiteGauche, limiteDroite
if (imageNume > 0) {
if (fctsEtudeCalculValeur(tabCoefs[0]) > 0) { // c est positif
limiteGauche = '-\\infty'
limiteDroite = '+\\infty'
} else {
limiteGauche = '+\\infty'
limiteDroite = '-\\infty'
}
} else {
if (fctsEtudeCalculValeur(tabCoefs[0]) > 0) { // c est positif
limiteGauche = '+\\infty'
limiteDroite = '-\\infty'
} else {
limiteGauche = '-\\infty'
limiteDroite = '+\\infty'
}
}
obj.limites.push(['-infini', limiteInf], [valInterdite + '^-', limiteGauche], [valInterdite + '^+', limiteDroite], ['+infini', limiteInf])
obj.limites_num = []// spécifique au modèle : on y met la limite en chaque borne du domaine du numérateur
obj.limites_den = []// spécifique au modèle : on y met la limite en chaque borne du domaine du dénominateur
if (fctsEtudeCalculValeur(tabCoefs2[0]) > 0) {
obj.limites_num.push('-infini')
} else {
obj.limites_num.push('+infini')
}// on a la limite en -infini
const imageNumValInterdite = ecritBienFraction(fctsEtudeSommeNbs(fctsEtudeProduitNbs(tabCoefs2[0], valInterdite), tabCoefs2[1]))
obj.limites_num.push(imageNumValInterdite, imageNumValInterdite)// image du numérateur par -d/c
if (fctsEtudeCalculValeur(tabCoefs2[0]) > 0) {
obj.limites_num.push('+infini')
} else {
obj.limites_num.push('-infini')
}// on a la limite en +infini
// maintenant je m’occupe du dénominateur
if (fctsEtudeCalculValeur(tabCoefs[0]) > 0) {
obj.limites_den.push('-infini')
} else {
obj.limites_den.push('+infini')
}// on a la limite en -infini
if (fctsEtudeCalculValeur(tabCoefs[0]) > 0) {
obj.limites_den.push('0^-', '0^+')
} else {
obj.limites_den.push('0^+', '0^-')
}
if (fctsEtudeCalculValeur(tabCoefs[0]) > 0) {
obj.limites_den.push('+infini')
} else {
obj.limites_den.push('-infini')
}// on a la limite en +infini
}
if (isDebug) console.debug('OBJ=', obj)
break
case 2:
// modèle 2 : ax^3+bx^2+cx+d
// ensembles de définition et de dérivabilité sous la forme de tableaux
obj.domaineDef = ['-infini', '+infini', ']', '[']
obj.domaineDeriv = ['-infini', '+infini', ']', '[']
// je recherche les valeurs approchées des coefs
d = arbreFct.evalue([0])
b = 0.5 * (arbreFct.evalue([1]) + arbreFct.evalue([-1]) - 2 * d)
a = (arbreFct.evalue([2]) - 2 * arbreFct.evalue([1]) - 2 * b + d) / 6
c = arbreFct.evalue([1]) - a - b - d
if (a < 0) {
obj.limites.push(['-infini', '+\\infty'])// en -\\infty, la limite vaut +\\infty
obj.limites.push(['+infini', '-\\infty'])// en +\\infty, la limite vaut -\\infty
} else {
obj.limites.push(['-infini', '-\\infty'])// en -\\infty, la limite vaut -\\infty
obj.limites.push(['+infini', '+\\infty'])// en +\\infty, la limite vaut +\\infty
}
break
default :// modèle 1 : (ax+b)exp(x)
obj.limites_affine = []// spécifique au modèle
// ensembles de définition et de dérivabilité sous la forme de tableaux
obj.domaineDef = ['-infini', '+infini', ']', '[']
obj.domaineDeriv = ['-infini', '+infini', ']', '[']
obj.limites.push(['-infini', '0'])// en -\\infty, la limite vaut 0
// En + infini cela va dépendra du signe de a dans l’écriture (ax+b)*exp(x)
b = arbreFct.evalue([0])
{
const arbreFct2 = new Tarbre('(' + fctPourCalc + '-(' + b + ')*exp(x))*exp(-x)', ['x'])
a = arbreFct2.evalue([1])
}
if (a < 0) {
// obj.signes_derivee=["+","-"];//commenté car dépend du domaine
obj.limites.push(['+infini', '-\\infty'])// en +\\infty, la limite vaut -infty si a<0
obj.limites_affine.push('+\\infty', '-\\infty')// les limites de ax+b sont +inf et -inf
} else {
// obj.signes_derivee=["-","+"];
obj.limites.push(['+infini', '+\\infty'])// en +\\infty, la limite vaut +infty si a>0
obj.limites_affine.push('-\\infty', '+\\infty')
}
break
}
return obj
} // fin fctsEtudeGenereDonnees
/**
* normalement pas besoin de modifier en cas d’ajout de modèle de fonction (seuls les textes et la fonction genere_textes_correction sont à modifier)
* @param divConteneur
* @param couleurCorr
* @param lesTextes
* @param lesVariables
*/
export function fctsEtudeAfficheCorrection (divConteneur, couleurCorr, lesTextes, lesVariables) {
// construction de l’affichage de la correction avec les textes et les variables
if (typeof divConteneur === 'string') divConteneur = j3pElement(divConteneur)
const tabvariables = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
for (let i = 0; i < lesTextes.length; i++) {
const txt = lesTextes[i]
const obj = {}
// on récupère les variables, par texte...
for (let j = 0; j < lesVariables[i].length; j++) {
obj[tabvariables[j]] = lesVariables[i][j]
}
obj.styletexte = {}
divConteneur.style.color = couleurCorr
j3pAffiche(divConteneur, '', txt, obj)
}
} // fin fctsEtudeAfficheCorrection
/**
* vérifie si les 2 tableaux sont bien identiques (égalité stricte de tous les éléments)
* @param tab1
* @param tab2
* @returns {boolean}
*/
export function fctsEtudeEgaliteTableaux (tab1, tab2) {
if (Array.isArray(tab1) && Array.isArray(tab2)) return tab1.length === tab2.length && tab1.every((elt, index) => elt === tab2[index])
console.error(Error('fctsEtudeEgaliteTableaux appelée avec autre chose que des tableaux'), tab1, tab2)
return tab1 === tab2
} // fin fctsEtudeEgaliteTableaux
/**
* Retourne la décomposition du nombre fractionnaire nb (sous la forme \\frac{…}{…}), s’il n
* @param {number|string} nb
* @returns {Array<boolean|number|string>} [false] si nb n’est pas fractionnaire et [true, num, den, signe] sinon
*/
export function fctsEtudeExtraireNumDen (nb) {
// nb est un nombre
// s’il est fractionnaire, il est sous la forme \\frac{..}{...}
// cette fonction renvoie un tableau decompNb
// decompNb[0] vaut true or false suivant que le nb est fractionnaire ou non
// s’il est fractionnaire, decompNb[1] est le num et decompNb[2] le den'
// decompNb[3] est le signe du nombre
const decompNb = [false]
if (String(nb).includes('frac')) {
decompNb[0] = true
// on peut très bien avoir un signe "-" devant \\frac (plus facile pour gérer les nombres opposés)
let coefMoins1 = 1
if (nb.charAt(0) === '-') {
nb = nb.substring(1)
coefMoins1 = -1
}
const tabNb = nb.split('}{')
decompNb[1] = tabNb[0].substring(tabNb[0].indexOf('frac') + 5, tabNb[0].length)
if (isAlmostZero(coefMoins1 + 1)) {
// je dois remplacer le num par son opposé
if (decompNb[1].charAt(0) === '-') {
decompNb[1] = decompNb[1].substring(1)
} else {
decompNb[1] = '-' + decompNb[1]
}
}
decompNb[2] = tabNb[1].substring(0, tabNb[1].length - 1)
decompNb[3] = (j3pNombre(String(decompNb[1])) / j3pNombre(String(decompNb[2])) < 0) ? '-' : '+'
} else {
// là aussi je peux gérer les nbs de la forme "--"...
if ((String(nb).charAt(0) === '-') && (String(nb).charAt(1) === '-')) {
nb = nb.substring(2)
}
decompNb[3] = (j3pNombre(String(nb)) < 0) ? '-' : '+'
}
return decompNb
} // fin fctsEtudeExtraireNumDen
export function fctsEtudeSommeNbs (nb1, nb2) {
// nb1 et nb2 sont des nombres (entiers, décimaux ou fractionnaires au format latex)
// Cette fonction renvoie la somme sous la forme la plus simple qui soit
// Pour un résultat fractionnaire, elle renvoie la forme latex
const tabFrac1 = fctsEtudeExtraireNumDen(nb1)
const tabFrac2 = fctsEtudeExtraireNumDen(nb2)
let maSommeNum, maSommeDen, maSomme
if (tabFrac1[0]) { // le 1er nb est une fraction
if (tabFrac2[0]) { // le 2ème nb est une fraction
maSommeNum = j3pNombre(tabFrac1[1]) * j3pNombre(tabFrac2[2]) + j3pNombre(tabFrac2[1]) * j3pNombre(tabFrac1[2])
maSommeDen = j3pNombre(tabFrac1[2]) * j3pNombre(tabFrac2[2])
} else { // le deuxième nb est un nombre non frac
maSommeNum = j3pNombre(tabFrac1[1]) + j3pNombre(String(nb2)) * j3pNombre(tabFrac1[2])
maSommeDen = j3pNombre(tabFrac1[2])
}
} else { // le premier nb est un nombre non frac
if (String(nb2).includes('frac')) { // le 2ème nb est une fraction
maSommeNum = j3pNombre(String(nb1)) * j3pNombre(tabFrac2[2]) + j3pNombre(tabFrac2[1])
maSommeDen = j3pNombre(tabFrac2[2])
}
}
if (tabFrac1[0] || tabFrac2[0]) {
// j’ai tout de même un problème si l’un des nombre au moins est un décimal'
maSommeNum = Math.round(maSommeNum * Math.pow(10, 13)) / Math.pow(10, 13)
if (String(maSommeNum).includes('.')) {
// le numérateur est un décimal
const puis10 = String(maSommeNum).length - String(maSommeNum).indexOf('.')
maSommeNum = maSommeNum * Math.pow(10, puis10)
maSommeDen = maSommeDen * Math.pow(10, puis10)
}
if (isAlmostZero(maSommeNum)) {
// au final le produit est nul
maSomme = 0
} else {
const lePgcd = j3pPGCD(Math.abs(maSommeNum), Math.abs(maSommeDen))
maSommeNum = maSommeNum / lePgcd
maSommeDen = maSommeDen / lePgcd
const leSigneSomme = (maSommeNum / maSommeDen < 0) ? '-' : ''
maSomme = (Math.abs(Math.abs(maSommeDen) - 1) < epsilon) ? (maSommeNum / maSommeDen) : '\\frac{' + leSigneSomme + j3pVirgule(Math.abs(maSommeNum)) + '}{' + j3pVirgule(Math.abs(maSommeDen)) + '}'
}
} else {
maSomme = j3pNombre(String(nb1)) + j3pNombre(String(nb2))
}
return String(maSomme)
} // fin fctsEtudeSommeNbs
export function fctsEtudeProduitNbs (nb1, nb2) {
// nb1 et nb2 sont des nombres (entiers, décimaux ou fractionnaires au format latex)
// Cette fonction renvoie le produit sous la forme la plus simple qui soit
// Pour un résultat fractionnaire, elle renvoie la forme latex
const tabFrac1 = fctsEtudeExtraireNumDen(nb1)
const tabFrac2 = fctsEtudeExtraireNumDen(nb2)
let monProduitNum, monProduitDen, monProduit
if (tabFrac1[0]) { // le 1er nb est une fraction
if (tabFrac2[0]) { // le 2ème nb est une fraction
monProduitNum = j3pNombre(tabFrac1[1]) * j3pNombre(tabFrac2[1])
monProduitDen = j3pNombre(tabFrac1[2]) * j3pNombre(tabFrac2[2])
} else { // le deuxième nb est un nombre non frac
monProduitNum = j3pNombre(tabFrac1[1]) * j3pNombre(String(nb2))
monProduitDen = j3pNombre(tabFrac1[2])
}
} else { // le premier nb est un nombre non frac
if (String(nb2).includes('frac')) { // le 2ème nb est une fraction
monProduitNum = j3pNombre(String(nb1)) * j3pNombre(tabFrac2[1])
monProduitDen = j3pNombre(tabFrac2[2])
}
}
if (tabFrac1[0] || tabFrac2[0]) {
monProduitNum = Math.round(monProduitNum * Math.pow(10, 13)) / Math.pow(10, 13)
if (String(monProduitNum).includes('.')) {
// le numérateur est un décimal
const puis10 = String(monProduitNum).length - String(monProduitNum).indexOf('.')
monProduitNum = monProduitNum * Math.pow(10, puis10)
monProduitDen = monProduitDen * Math.pow(10, puis10)
}
if (isAlmostZero(monProduitNum)) {
// au final le produit est nul
monProduit = 0
} else {
const lePgcd = j3pPGCD(Math.abs(monProduitNum), Math.abs(monProduitDen))
monProduitNum = monProduitNum / lePgcd
monProduitDen = monProduitDen / lePgcd
const leSigneProduit = (monProduitNum / monProduitDen < 0) ? '-' : ''
monProduit = (Math.abs(Math.abs(monProduitDen) - 1) < epsilon) ? (monProduitNum / monProduitDen) : '\\frac{' + leSigneProduit + j3pVirgule(Math.abs(monProduitNum)) + '}{' + j3pVirgule(Math.abs(monProduitDen)) + '}'
}
} else {
monProduit = j3pNombre(String(nb1)) * j3pNombre(String(nb2))
}
return String(monProduit)
} // fin fctsEtudeProduitNbs
/**
*
* @param nb1
* @param nb2
* @returns {*}
*/
export function fctsEtudeDivisionNbs (nb1, nb2) {
// nb1 et nb2 sont des nombres (entiers, décimaux ou fractionnaires au format latex)
// Cette fonction renvoie le quotient sous la forme la plus simple qui soit
// Pour un résultat fractionnaire, elle renvoie la forme latex
const tabFrac1 = fctsEtudeExtraireNumDen(nb1)
const tabFrac2 = fctsEtudeExtraireNumDen(nb2)
let monQuotientNum = nb1
let monQuotientDen = nb2
let monQuotient, puis10
if (tabFrac1[0]) { // le 1er nb est une fraction
if (tabFrac2[0]) { // le 2ème nb est une fraction
monQuotientNum = j3pNombre(tabFrac1[1]) * j3pNombre(tabFrac2[2])
monQuotientDen = j3pNombre(tabFrac1[2]) * j3pNombre(tabFrac2[1])
} else { // le deuxième nb est un nombre non frac
monQuotientNum = j3pNombre(tabFrac1[1])
monQuotientDen = j3pNombre(tabFrac1[2]) * j3pNombre(String(nb2))
}
if (monQuotientDen < 0) {
monQuotientDen = -monQuotientDen
monQuotientNum = -monQuotientNum
}
if (String(monQuotientDen).includes('.')) {
// le numérateur est un décimal
puis10 = String(monQuotientDen).length - String(monQuotientDen).indexOf('.')
monQuotientNum = monQuotientNum * Math.pow(10, puis10)
monQuotientDen = monQuotientDen * Math.pow(10, puis10)
}
} else { // le premier nb est un nombre non frac
if (String(nb2).includes('frac')) { // le 2ème nb est une fraction
monQuotientNum = j3pNombre(String(nb1)) * j3pNombre(tabFrac2[2])
monQuotientDen = j3pNombre(tabFrac2[1])
} else {
if (String(nb2).includes('.')) {
// le numérateur est un décimal
puis10 = String(nb2).length - String(nb2).indexOf('.')
monQuotientNum = j3pNombre(String(nb1)) * Math.pow(10, puis10)
monQuotientDen = j3pNombre(String(nb2)) * Math.pow(10, puis10)
}
}
if (monQuotientDen < 0) {
monQuotientDen = -monQuotientDen
monQuotientNum = -monQuotientNum
}
}
monQuotientNum = Math.round(monQuotientNum * Math.pow(10, 13)) / Math.pow(10, 13)
if (String(monQuotientNum).includes('.')) {
// le numérateur est un décimal
puis10 = String(monQuotientNum).length - String(monQuotientNum).indexOf('.')
monQuotientNum = monQuotientNum * Math.pow(10, puis10)
monQuotientDen = monQuotientDen * Math.pow(10, puis10)
}
if (isAlmostZero(monQuotientNum)) {
// au final le produit est nul
monQuotient = 0
} else {
const lePgcd = j3pPGCD(Math.abs(monQuotientNum), Math.abs(monQuotientDen))
monQuotientNum = monQuotientNum / lePgcd
monQuotientDen = monQuotientDen / lePgcd
const leSigneQuotient = (monQuotientNum / monQuotientDen < 0) ? '-' : ''
monQuotient = (Math.abs(Math.abs(monQuotientDen) - 1) < epsilon) ? (monQuotientNum / monQuotientDen) : '\\frac{' + leSigneQuotient + j3pVirgule(Math.abs(monQuotientNum)) + '}{' + j3pVirgule(Math.abs(monQuotientDen)) + '}'
}
return String(monQuotient)
} // fin fctsEtudeDivisionNbs
/**
*
* @param expresFct
* @param modele
* @returns {string|string}
*/
export function fctsEtudeEcritureLatexFonction (expresFct, modele) {
// cette fonction renvoie l’expression de la fonction au format latex'
// expresFct est son expression écrite sous forme mathématique
// on précise le modele de fonction pour permettre de mieux gérer la transformation'
let i, num, den, tabSeparateur
let expresLatex = expresFct
const regExpo = /exp\([^)]+\)/i
const regFrac1 = /\(\d+\)\/\(\d+\)/ig // parenthèses pour numérateur ET dénumérateur
const regFrac2 = /\(\d+\/\d+\)/ig // parenthèses autour de la fraction
const regFrac3 = /\d+\/\d+/ig // pas de parenthèses
switch (modele) {
case 7:// modèle ax+b+cln(dx+e)
if ((!expresFct.includes('\\ln')) && (expresFct.includes('ln'))) {
// ln est bien présent mais n’est pas au format latex
expresLatex = expresLatex.replace('ln', '\\ln')
}
break
case 6:// c’est le modèle 6 : (a+bln(x))/x^c
num = expresFct.substring(0, expresFct.indexOf('/'))
num = (num[num.length - 1] === ')') ? num.substring(num.indexOf('(') + 1, num.length - 1) : num.substring(num.indexOf('(') + 1)
num = num.replace('ln x', '\\ln x')
num = num.replace('ln(x)', '\\ln x')
den = expresFct.substring(expresFct.indexOf('/') + 1)
// console.log("expresFct:"+expresFct+" num:"+num+" den:"+den)
expresLatex = '\\frac{' + num + '}{' + den + '}'
break
case 4:// c’est le modèle 4
// fonction (ax+b)/(cx^2+d)
num = expresFct.substring(expresFct.indexOf('(') + 1, expresFct.indexOf(')'))
den = expresFct.substring(expresFct.lastIndexOf('(') + 1, expresFct.lastIndexOf(')'))
expresLatex = '\\frac{' + num + '}{' + den + '}'
break
case 3:// c’est le modèle 3
// fonction (ax+b)/(cx+d)
if (expresFct[0] === '(') { // j’ai des parenthèses autour du numérateur
num = expresFct.substring(expresFct.indexOf('(') + 1, expresFct.indexOf(')'))
} else { // je n’en ai pas
num = expresFct.substring(0, expresFct.indexOf('/'))
}
if (expresFct[expresFct.length - 1] === ')') { // j’ai des parenthèses autour du dénominateur
den = expresFct.substring(expresFct.lastIndexOf('(') + 1, expresFct.lastIndexOf(')'))
} else { // je n’en ai pas
den = expresFct.substring(expresFct.indexOf('/') + 1)
}
expresLatex = '\\frac{' + num + '}{' + den + '}'
break
case 2:// c’est que modele vaut 2 : fonction ax^3+bx^2+cx+d
case 8:// c’est que modele vaut 8 : fonction ax^2+bx+c
while (regFrac1.test(expresLatex)) {
// on trouve (..)/(..)
const tabFrac1 = expresLatex.match(regFrac1)
for (i = 0; i < tabFrac1.length; i++) {
tabSeparateur = tabFrac1[i].split('/')
num = tabSeparateur[0].substring(1, tabSeparateur[0].length - 1)
den = tabSeparateur[1].substring(1, tabSeparateur[1].length - 1)
expresLatex = expresLatex.replace(tabFrac1[i], '\\frac{' + num + '}{' + den + '}')
}
}
while (regFrac2.test(expresLatex)) {
// on trouve (../..)
const tabFrac2 = expresLatex.match(regFrac2)
for (i = 0; i < tabFrac2.length; i++) {
tabSeparateur = tabFrac2[i].split('/')
num = tabSeparateur[0].substring(1, tabSeparateur[0].length)
den = tabSeparateur[1].substring(0, tabSeparateur[1].length - 1)
expresLatex = expresLatex.replace(tabFrac2[i], '\\frac{' + num + '}{' + den + '}')
}
}
while (regFrac3.test(expresLatex)) {
// on trouve ../..
const tabFrac3 = expresLatex.match(regFrac3)
for (i = 0; i < tabFrac3.length; i++) {
tabSeparateur = tabFrac3[i].split('/')
num = tabSeparateur[0].substring(0, tabSeparateur[0].length)
den = tabSeparateur[1].substring(0, tabSeparateur[1].length)
expresLatex = expresLatex.replace(tabFrac3[i], '\\frac{' + num + '}{' + den + '}')
}
}
// on enlève signe * devant x
while (expresLatex.includes('*x')) {
expresLatex = expresLatex.replace('*x', 'x')
}
if (expresLatex.includes('x²')) {
expresLatex = expresLatex.replace('x²', 'x^2')
}
break
default: // c’est que modele vaut 1' ou le modèle 5
// fonction (ax+b)exp(x) ou bien (ax+b)exp(cx+d)
// on enlève signe * devant x
while (expresLatex.includes('*x')) {
expresLatex = expresLatex.replace('*x', 'x')
}
// on enlève signe * devant exp
while (expresLatex.includes('*e')) {
expresLatex = expresLatex.replace('*e', 'e')
}
expresLatex = expresLatex.replace(/-1e/, '-e') // pour éviter d’écrire -1exp(...)
{
const tabExp = expresLatex.match(regExpo)
const nbExp = tabExp[0].substring(4, tabExp[0].length - 1)
expresLatex = expresLatex.replace(tabExp[0], '\\mathrm{e}^{' + fractionLatex(nbExp) + '}')
}
// console.log("expresLatex avant fraction:"+expresLatex)
expresLatex = fractionLatex(expresLatex)
break
}
// dernière étape, je fais en sorte que les parenthèses soient grandes si besoin
let expresLatexGrandePar = ''
for (i = 0; i < expresLatex.length; i++) {
if (expresLatex[i] === '(') {
expresLatexGrandePar += '\\left'
} else if (expresLatex[i] === ')') {
expresLatexGrandePar += '\\right'
}
expresLatexGrandePar += expresLatex[i]
}
expresLatex = expresLatexGrandePar
// il ne reste plus qu'à faire en sorte qu’on ait une virgule comme séparateur des décimaux'
while (expresLatex.includes('.')) {
expresLatex = expresLatex.replace('.', ',')
}
// console.log("expresLatex:"+expresLatex)
return (expresLatex)
} // fin fctsEtudeEcritureLatexFonction
/**
*
* @param expresFct
* @param modele
* @param domaineDerivee
* @param {boolean} [isDebug=false]
* @returns {{}}
*/
export function fctsEtudeEcritureLatexDerivee (expresFct, modele, domaineDerivee, isDebug = false) {
// cette fonction renvoie un objet contenant :
// - l’expression de la dérivée au format latex
// - les facteurs présents sous la forme d’un tableau en [0], ceux du numérateur et en [1] ceux du dénominateur
// - les zéros de chaque facteur
// expresFct est l’expression de la fonction'
// on précise le modele de fonction pour permettre de mieux gérer la transformation'
// on précise aussi le domaine de dérivabilité de la fonction, c’est utile car certaons facteurs vont être positifs sur ce domaine alors qu’ils ne le sont pas si le domaine est le plus large possible
// isDebug permet d’afficher des infos de debuggage en console
const objet = {}// on y place tout ce dont on a besoin pour la dérivée (expression au format latex, facteurs au format latex, valeurs où ils s’annulent au format latex)
// objet.expres_derivee est l’expression de la dérivée au format latex
objet.tabFacteurs = []
// objet.tabFacteurs est un tableau qui contiendra 2 éléments
objet.tabFacteurs[0] = []
// tabFacteurs[0] est un tableau contenant tous les facteurs du numérateur de la dérivée
objet.tabFacteurs[1] = []
// tabFacteurs[1] est un tableau contenant tous les facteurs du dénominateur de la dérivée
objet.zeros_facteurs = []
objet.zeros_facteurs[0] = []
objet.zeros_facteurs[1] = []
// objet.zeros_facteurs est un tableau. Chaque élément sera un tableau avec les zéros du facteur associé (on prend d’abord en compte les facteurs du numérateur puis ceux du dénominateur)
// cependant, il peut parfois être utile d’avoir tous les zéros des facteurs, y compris ceux qui ne sont pas dans le domaine
objet.zeros_facteurs_init = []
objet.zeros_facteurs_init[0] = []
objet.zeros_facteurs_init[1] = []
objet.signes_facteurs = []
// objet.signes_facteurs est un tableau. Chaque élément est un tableau qui contient les signes du facteurs par rapport à ses zéros
objet.variables = []
// objet.variables est un tableau contenant les variables qu’on a pu récupérer
let i, dxpluse, coefA, coefB, coefC, fctAffine, expresFctXcas, numDerivee, tabNum, tabDen, tabFraction
let delta, deltaTxt, num, den, denDerivee, racineMin, racineMax, racine1Txt, racine2Txt, numeDerive
let coefAprime, coefBprime, coefCprime, racine1, racine2, leZero
// var lnRegexp = new RegExp('(\\-?\d{0,}\\*?ln\\sx)|(\\-?\d{0,}\\*?ln\\(x\\))', 'ig')
// @todo remplacer par
const lnRegexp = /-?\d*\*?ln(?:\sx|\(x\))/ig
// var lnRegexp = /-?\d*(?:\s*\*\s*)?ln(?:\sx|\(x\))/ig
// (?:\s*\*\s*)? signifie éventuellement un signe * entouré d’évetuels espaces, ?: pour dire que c’est non capturant
// cf https://developer.mozilla.org/fr/docs/Web/JavaScript/Guide/Regular_Expressions#special-non-capturing-parentheses
switch (modele) {
case 7:// modèle ax+b+cln(dx+e)
expresFctXcas = j3pMathquillXcas(expresFct)
if (expresFctXcas.includes('(')) {
// il y a des parenthèses, donc dx+e ne se réduit à pas à x
dxpluse = expresFctXcas.substring(expresFctXcas.indexOf('(') + 1, expresFctXcas.indexOf(')'))
} else {
dxpluse = 'x'
}
{
const coefDe = extraireCoefsFctaffine(dxpluse)
const d = coefDe[0]
const e = coefDe[1]
const coefDevantlnReg = /-?\d+\*?ln/ig // j’ai laissé + et non *
const coefDevantlnReg2 = /-?ln/ig
if (coefDevantlnReg.test(expresFctXcas)) {
coefC = expresFctXcas.match(coefDevantlnReg)
} else {
coefC = expresFctXcas.match(coefDevantlnReg2)
}
let c = coefC[0].replace('ln', '')
if (c === '') {
c = '1'
} else if (c === '-') {
c = '-1'
} else if (c[c.length - 1] === '*') {
c = c.substring(0, c.length - 1)
}
// console.log("dxpluse:"+dxpluse+" d:"+d+" e:"+e+" c:"+c+" coefC:"+coefC)
if (c === '') {
fctAffine = expresFctXcas.substring(0, '\\ln')
} else {
fctAffine = expresFctXcas.substring(0, expresFctXcas.indexOf(coefC[0]))
}
if (fctAffine[fctAffine.length - 1] === '+') {
fctAffine = fctAffine.substring(0, fctAffine.length - 1)
}
const coefsab = extraireCoefsFctaffine(fctAffine)
const a = coefsab[0]
const b = coefsab[1]
const coefAnum = fctsEtudeProduitNbs(a, d)
const coefBnum = fctsEtudeSommeNbs(fctsEtudeProduitNbs(a, e), fctsEtudeProduitNbs(c, d))
numeDerive = fctsEtudeEcrireMonome(1, 1, coefAnum) + fctsEtudeEcrireMonome(2, 0, coefBnum)
objet.expres_derivee = '\\frac{' + numeDerive + '}{' + fctsEtudeEcrireMonome(1, 1, d) + fctsEtudeEcrireMonome(2, 0, e) + '}'
objet.tabFacteurs[0][0] = numeDerive
objet.tabFacteurs[1][0] = fctsEtudeEcrireMonome(1, 1, d) + fctsEtudeEcrireMonome(2, 0, e)
objet.zeros_facteurs_init[0] = [fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', coefBnum), coefAnum)]
if (fctsEtudeEstDansDomaine(objet.zeros_facteurs_init[0][0], domaineDerivee)) {
objet.zeros_facteurs[0] = [objet.zeros_facteurs_init[0][0]]
} else {
objet.zeros_facteurs[0] = []
}
if (!objet.zeros_facteurs[0][0] || !fctsEtudeEstDansDomaine(objet.zeros_facteurs[0][0], domaineDerivee)) {
// objet.zeros_facteurs[0][0] vaut undefined si le facteur ne s’annule pas sur IR donc dans ce cas, on ne cherche pas s’il est dans le domaine --> cela génère une erreur
// c’est que le facteur affine est de signe constant
// ici le domaine ne peut pas être une réunion d’intervalles, mais juste un intervalle
objet.signes_facteurs[0] = fctsEtudeSigneFct(objet.expres_derivee, domaineDerivee, [])
} else {
if (fctsEtudeCalculValeur(coefAnum) < 0) {
objet.signes_facteurs[0] = ['+', '-']
} else {
objet.signes_facteurs[0] = ['-', '+']
}
}
objet.zeros_facteurs_init[1] = [fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', e), d)]
objet.zeros_facteurs[1] = []
objet.signes_facteurs[1] = ['+']
objet.variables.push(a, b, c, d, e, coefAnum, coefBnum, objet.zeros_facteurs_init[0][0])
}
break
case 6:// modèle (a+bln(x))/x^c
// coefs de l’écriture (a+bln(x))/x^c
// coefs de l’écriture de la dérivée (sous la forme (a'+b’lnx)/x^c')
expresFctXcas = j3pMathquillXcas(expresFct)
num = expresFctXcas.substring(0, expresFctXcas.indexOf('/'))
tabNum = num.match(lnRegexp)
if (tabNum[0].includes('\\ln')) {
coefB = tabNum[0].substring(0, tabNum[0].indexOf('\\ln'))
} else {
coefB = tabNum[0].substring(0, tabNum[0].indexOf('ln'))
}
if (coefB[coefB.length - 1] === '*') {
coefB = coefB.substring(0, coefB.length - 1)// j’ai viré le signe de multiplication qui pourrait trainer.
}
if (coefB === '-') {
coefB = '-1'
} else if (coefB === '') {
coefB = '1'
}
coefA = num.replace(tabNum[0], '')
if (coefA[0] === '(') {
coefA = coefA.substring(1)
}
if (coefA[coefA.length - 1] === ')') {
coefA = coefA.substring(0, coefA.length - 1)
}
if (coefA[0] === '+') {
coefA = coefA.substring(1)
} else if (coefA[coefA.length - 1] === '+') {
coefA = coefA.substring(0, coefA.length - 1)
}
den = expresFctXcas.substring(expresFctXcas.indexOf('/') + 1)
coefC = 1
if ((den !== 'x') && (den !== '(x)')) {
// c’est que la puissance de x n’est pas 1
coefC = den.substring(den.indexOf('^') + 1)
if ((coefC[0] === '{') || (coefC[0] === '(')) {
coefC = coefC.substring(1)
}
while ((coefC[coefC.length - 1] === '}') || (coefC[coefC.length - 1] === ')')) {
coefC = coefC.substring(0, coefC.length - 1)
}
}
// console.log("coefA:"+coefA+" coefB:"+coefB+" coefC:"+coefC)
coefAprime = fctsEtudeSommeNbs(coefB, fctsEtudeProduitNbs('-1', fctsEtudeProduitNbs(coefA, coefC)))
coefBprime = fctsEtudeProduitNbs('-1', fctsEtudeProduitNbs(coefB, coefC))
coefCprime = fctsEtudeSommeNbs(coefC, 1)
numeDerive = fctsEtudeEcrireMonome(1, 0, coefAprime) + fctsEtudeEcrireMonome(2, 1, coefBprime, '\\ln x')
if (Math.abs(j3pCalculValeur(coefAprime)) < epsilon) {
numeDerive = fctsEtudeEcrireMonome(1, 1, coefBprime, '\\ln x')
}
objet.expres_derivee = '\\frac{' + numeDerive + '}{x^{' + coefCprime + '}}'
objet.tabFacteurs[0][0] = numeDerive
{
const puisZeronum = fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', coefAprime), coefBprime)
if (Math.abs(fctsEtudeCalculValeur(puisZeronum)) < epsilon) {
// c’est zéro, donc le numérateur s’annule en e^0=1
objet.zeros_facteurs_init[0] = ['1']
} else if (Math.abs(fctsEtudeCalculValeur(puisZeronum) - 1) < epsilon) {
objet.zeros_facteurs_init[0] = ['\\mathrm{e}']
} else {
objet.zeros_facteurs_init[0] = ['\\mathrm{e}^{' + puisZeronum + '}']
}
}
if (fctsEtudeEstDansDomaine(objet.zeros_facteurs_init[0][0], domaineDerivee)) {
objet.zeros_facteurs[0] = [objet.zeros_facteurs_init[0][0]]
} else {
objet.zeros_facteurs[0] = []
}
if (!fctsEtudeEstDansDomaine(objet.zeros_facteurs[0][0], domaineDerivee)) {
// c’est que le numérateur est de signe constant
// ici le domaine ne peut pas être une réunion d’intervalles, mais juste un intervalle
objet.signes_facteurs[0] = fctsEtudeSigneFct(objet.expres_derivee, domaineDerivee, [])
} else {
if (fctsEtudeCalculValeur(coefBprime) < 0) {
objet.signes_facteurs[0] = ['+', '-']
} else {
objet.signes_facteurs[0] = ['-', '+']
}
}
objet.tabFacteurs[1][0] = 'x^{' + coefCprime + '}'
objet.signes_facteurs[1] = ['+']
objet.variables.push(coefA, coefB, coefC, coefAprime, coefBprime, coefCprime, objet.zeros_facteurs_init[0][0])
break
case 5:// modèle (ax+b)exp(cx+d)
{
expresFctXcas = j3pMathquillXcas(expresFct)
if (expresFctXcas.indexOf('-e^') === 0) {
// c’est que la fonction est de la forme -exp(cx+d)+e
fctAffine = '-1'
} else {
fctAffine = expresFctXcas.substring(0, expresFctXcas.indexOf('*e^'))
}
const puissance = expresFct.substring(expresFct.lastIndexOf('{') + 1, expresFct.lastIndexOf('}'))
if (fctAffine[0] === '(') { // je vais virer les parenthèses
fctAffine = fctAffine.substring(fctAffine.indexOf('(') + 1, fctAffine.indexOf(')'))
}
const tabCoefAffine1 = extraireCoefsFctaffine(fctAffine, 'x')
const tabCoefAffine2 = extraireCoefsFctaffine(puissance, 'x')
coefA = tabCoefAffine1[0]
coefB = tabCoefAffine1[1]
coefC = tabCoefAffine2[0]
const coefD = tabCoefAffine2[1] // coef de l’écriture (ax+b)exp(cx+d)+e'
const coefE = (expresFct.lastIndexOf('}') === expresFct.length - 1) ? 0 : fctsEtudeCalculValeur(expresFct.substring(expresFct.lastIndexOf('}') + 1))
const coefaDeriv = fctsEtudeProduitNbs(coefA, coefC)
const coefbDeriv = fctsEtudeSommeNbs(coefA, fctsEtudeProduitNbs(coefB, coefC))
if ((coefbDeriv === '0') || (coefbDeriv === 0)) {
objet.expres_derivee = fctsEtudeEcrireMonome(1, 1, coefaDeriv) + '\\mathrm{e}^{' + fctsEtudeEcrireMonome(1, 1, coefC) + fctsEtudeEcrireMonome(2, 0, coefD) + '}'
objet.tabFacteurs[0][0] = fctsEtudeEcrireMonome(1, 1, coefaDeriv)
if (fctsEtudeEstDansDomaine('0', domaineDerivee)) {
objet.zeros_facteurs[0] = ['0']
} else {
objet.zeros_facteurs[0] = []
}
objet.zeros_facteurs_init[0] = ['0']
} else if ((coefaDeriv === '0') || (coefaDeriv === 0)) {
objet.expres_derivee = (Number(coefbDeriv) === 1)
? '\\mathrm{e}^{' + fctsEtudeEcrireMonome(1, 1, coefC) + fctsEtudeEcrireMonome(2, 0, coefD) + '}'
: (Number(coefbDeriv) === -1)
? '-\\mathrm{e}^{' + fctsEtudeEcrireMonome(1, 1, coefC) + fctsEtudeEcrireMonome(2, 0, coefD) + '}'
: fctsEtudeEcrireMonome(1, 0, coefbDeriv) + '\\mathrm{e}^{' + fctsEtudeEcrireMonome(1, 1, coefC) + fctsEtudeEcrireMonome(2, 0, coefD) + '}'
if (Number(coefbDeriv) !== 1) {
objet.tabFacteurs[0][0] = fctsEtudeEcrireMonome(1, 0, coefbDeriv)
}
objet.zeros_facteurs[0] = []
objet.zeros_facteurs_init[0] = []
} else {
objet.expres_derivee = '\\left(' + fctsEtudeEcrireMonome(1, 1, coefaDeriv) + fctsEtudeEcrireMonome(2, 0, coefbDeriv) + '\\right)\\mathrm{e}^{' + fctsEtudeEcrireMonome(1, 1, coefC) + fctsEtudeEcrireMonome(2, 0, coefD) + '}'
objet.tabFacteurs[0][0] = fctsEtudeEcrireMonome(1, 1, coefaDeriv) + fctsEtudeEcrireMonome(2, 0, coefbDeriv)
objet.zeros_facteurs[0] = [fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', coefbDeriv), coefaDeriv)]
if (!fctsEtudeEstDansDomaine(fctsEtudeCalculValeur(objet.zeros_facteurs[0][0]), domaineDerivee)) {
objet.zeros_facteurs[0] = []
}
objet.zeros_facteurs_init[0] = [fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', coefbDeriv), coefaDeriv)]
}
if ((Number(coefaDeriv) !== 0) || (Number(coefbDeriv) !== 1)) {
if (!objet.zeros_facteurs[0][0] || !fctsEtudeEstDansDomaine(objet.zeros_facteurs[0][0], domaineDerivee)) {
// c’est que le facteur affine est de signe constant
// ici le domaine ne peut pas être une réunion d’intervalles, mais juste un intervalle
objet.signes_facteurs[0] = fctsEtudeSigneFct(objet.expres_derivee, domaineDerivee, [])
} else {
if (fctsEtudeCalculValeur(coefaDeriv) === 0) {
if (fctsEtudeCalculValeur(coefbDeriv) < 0) {
objet.signes_facteurs[0] = ['-']
} else {
objet.signes_facteurs[0] = ['+']
}
} else if (fctsEtudeCalculValeur(coefaDeriv) < 0) {
objet.signes_facteurs[0] = ['+', '-']
} else {
objet.signes_facteurs[0] = ['-', '+']
}
}
}
objet.tabFacteurs[0].push('\\mathrm{e}^{' + fctsEtudeEcrireMonome(1, 1, coefC) + fctsEtudeEcrireMonome(2, 0, coefD) + '}')
objet.signes_facteurs.push(['+'])
objet.variables.push(coefA, coefB, coefC, coefD, coefE)
}
break
case 4:// c’est le modèle 4
// fonction (ax+b)/(cx^2+d)
tabFraction = j3pMathquillXcas(expresFct).split('/')
num = tabFraction[0].substring(1, tabFraction[0].length - 1)
den = tabFraction[1].substring(1, tabFraction[1].length - 1)
tabNum = extraireCoefsFctaffine(num)
if (den.includes('x^2')) {
// c’est x^2 qui est présent
tabDen = extraireCoefsFctaffine(den, 'x^2')
} else if (den.includes('x^{2}')) {
// c’est plutôt x^{2}
tabDen = extraireCoefsFctaffine(den, 'x^{2}')
}
// le numérateur est un polynôme de degré 2
{
const coef2Num = fctsEtudeProduitNbs('-1', fctsEtudeProduitNbs(tabDen[0], tabNum[0]))
const coef1Num = fctsEtudeProduitNbs('-2', fctsEtudeProduitNbs(tabDen[0], tabNum[1]))
const coef0Num = fctsEtudeProduitNbs(tabDen[1], tabNum[0])
numDerivee = fctsEtudeEcrireMonome(1, 2, coef2Num) + fctsEtudeEcrireMonome(2, 1, coef1Num) + fctsEtudeEcrireMonome(3, 0, coef0Num)
denDerivee = '(' + fctsEtudeEcrireMonome(1, 2, tabDen[0]) + fctsEtudeEcrireMonome(2, 0, tabDen[1]) + ')^2'
objet.expres_derivee = '\\frac{' + numDerivee + '}{' + denDerivee + '}'
objet.tabFacteurs[0][0] = numDerivee
objet.tabFacteurs[1][0] = denDerivee
// on cherche les racines du trinôme. Calculons le discriminant :
const valCoef2Num = fctsEtudeCalculValeur(coef2Num)
const valCoef1Num = fctsEtudeCalculValeur(coef1Num)
delta = Math.pow(valCoef1Num, 2) - 4 * coef2Num * coef0Num
deltaTxt = ''
if (delta < 0) {
// pas de racine
if (valCoef2Num < 0) {
objet.signes_facteurs[0] = ['-']
} else {
objet.signes_facteurs[0] = ['+']
}
objet.zeros_facteurs[0] = []
} else if (delta === 0) {
// une racine unique
leZero = fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', coef1Num), fctsEtudeProduitNbs('2', coef2Num))
if (valCoef2Num < 0) {
if (fctsEtudeEstDansDomaine(leZero, domaineDerivee)) {
objet.signes_facteurs[0] = ['-', '-']
} else {
objet.signes_facteurs[0] = ['-']
}
} else {
if (fctsEtudeEstDansDomaine(leZero, domaineDerivee)) {
objet.signes_facteurs[0] = ['+', '+']
} else {
objet.signes_facteurs[0] = ['+']
}
}
if (fctsEtudeEstDansDomaine(leZero, domaineDerivee)) {
objet.zeros_facteurs[0] = [leZero]
} else {
objet.zeros_facteurs[0] = []
}
objet.zeros_facteurs_init[0] = [leZero]
} else {
// deux racines
racine1 = (-valCoef1Num + Math.sqrt(delta)) / (2 * valCoef2Num)
racine2 = (-valCoef1Num - Math.sqrt(delta)) / (2 * valCoef2Num)
deltaTxt = fctsEtudeSommeNbs(fctsEtudeProduitNbs(coef1Num, coef1Num), fctsEtudeProduitNbs('-1', fctsEtudeProduitNbs(4, fctsEtudeProduitNbs(coef2Num, coef0Num))))
racine1Txt = simplificationRacineTrinome(coef1Num, '+', deltaTxt, coef2Num)
racine2Txt = simplificationRacineTrinome(coef1Num, '-', deltaTxt, coef2Num)
if (isDebug) {
console.debug('racine1Txt:' + racine1Txt + ' racine2Txt:' + racine2Txt)
console.debug('racine1:' + racine1 + ' racine2:' + racine2)
}
racineMin = racine2Txt
racineMax = racine1Txt
if (racine1 < racine2) {
racineMin = racine1Txt
racineMax = racine2Txt
}
objet.signes_facteurs[0] = fctsEtudeSigneFct(objet.expres_derivee, domaineDerivee, [racineMin, racineMax])
if (fctsEtudeEstDansDomaine(racineMin, domaineDerivee)) {
if (fctsEtudeEstDansDomaine(racineMax, domaineDerivee)) {
objet.zeros_facteurs[0] = [racineMin + '|' + racineMax]
} else {
objet.zeros_facteurs[0] = [racineMin]
}
} else {
if (fctsEtudeEstDansDomaine(racineMax, domaineDerivee)) {
objet.zeros_facteurs[0] = [racineMax]
} else {
objet.zeros_facteurs[0] = []
}
}
objet.zeros_facteurs_init[0] = [String(racineMin) + '|' + String(racineMax)]
}
objet.zeros_facteurs[1] = []
objet.zeros_facteurs_init[1] = []// le dénominateur ne s’annule pas, quelque soit le domaine
if (isDebug) console.debug('expresFct:' + expresFct + ' objet.expres_derivee:' + objet.expres_derivee + ' deltaTxt:' + deltaTxt + ' objet.zeros_facteurs:' + objet.zeros_facteurs + ' objet.signes_facteurs:' + objet.signes_facteurs)
objet.variables = [tabNum[0], tabNum[1], tabDen[0], tabDen[1]]// (ax+b)/cx+d) dans l’ordre des variables
// j’y ajoute les coefs du trinôme et deltaTxt
objet.variables.push(coef2Num, coef1Num, coef0Num, deltaTxt, delta)
if (delta > 0) {
objet.variables.push(racine1Txt)
}
objet.signes_facteurs.push(['+'])
}
break
case 3:// c’est le modèle 3
// fonction (ax+b)/(cx+d)
tabFraction = j3pMathquillXcas(expresFct).split('/')
num = tabFraction[0].substring(1, tabFraction[0].length - 1)
den = tabFraction[1].substring(1, tabFraction[1].length - 1)
tabNum = extraireCoefsFctaffine(num)
tabDen = extraireCoefsFctaffine(den)
numDerivee = fctsEtudeSommeNbs(fctsEtudeProduitNbs(tabNum[0], tabDen[1]), fctsEtudeProduitNbs('-1', fctsEtudeProduitNbs(tabNum[1], tabDen[0])))
{
const valeurNum = fctsEtudeCalculValeur(numDerivee)
denDerivee = '(' + fctsEtudeEcrireMonome(1, 1, tabDen[0]) + fctsEtudeEcrireMonome(2, 0, tabDen[1]) + ')^2'
objet.expres_derivee = '\\frac{' + numDerivee + '}{' + denDerivee + '}'
objet.tabFacteurs[0][0] = numDerivee
objet.zeros_facteurs[0] = []
objet.zeros_facteurs_init[0] = []
if (valeurNum > 0) {
objet.signes_facteurs[0] = ['+']
} else {
objet.signes_facteurs[0] = ['-']
}
}
objet.tabFacteurs[1][0] = denDerivee
objet.zeros_facteurs_init[1] = [fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', tabDen[1]), tabDen[0])]
objet.zeros_facteurs[1] = fctsEtudeEstDansDomaine(fctsEtudeCalculValeur(-tabDen[1] / tabDen[0]), fctsEtudeTransformationDomaine(domaineDerivee)) ? [fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', tabDen[1]), tabDen[0])] : []
objet.signes_facteurs.push(fctsEtudeSigneFct(objet.tabFacteurs[1][0], domaineDerivee, objet.zeros_facteurs[1]))
// objet.signes_facteurs.push(["+","+"]);
objet.variables = [tabNum[0], tabNum[1], tabDen[0], tabDen[1]]
break
case 2:// c’est que modele vaut 2 fonction ax^3+bx^2+cx+d avec coefs au format latex
case 8:// c’est que modele vaut 8 fonction ax^2+bx+c avec coefs au format latex
{
const posSigne = [0]
for (i = 1; i < expresFct.length; i++) {
if ((expresFct[i] === '+') || (expresFct[i] === '-')) {
posSigne.push(i)
}
}
posSigne.push(expresFct.length)
// je découpe alors l’expression à l’aide de ces signes, cela me donnera les monômes
const tabMonome = []
for (i = 1; i < posSigne.length; i++) {
tabMonome.push(expresFct.substring(posSigne[i - 1], posSigne[i]))
}
const tabCoefPolynome = (modele === 2) ? ['0', '0', '0', '0'] : ['0', '0', '0']
// tabCoefPolynome[0] est le coef constant, tabCoefPolynome[1], celui devant x, ...
for (i = 0; i < tabMonome.length; i++) {
// tabMonome.length<=4 c’est le nombre de monomes présents
if ((tabMonome[i].includes('x^{3}')) || (tabMonome[i].includes('x^3'))) {
// c’est le monome ax^3
if (tabMonome[i].charAt(0) === '+') {
tabMonome[i] = tabMonome[i].substring(1)
}
if (tabMonome[i].includes('x^{3}')) {
tabCoefPolynome[3] = tabMonome[i].substring(0, tabMonome[i].length - 5)
} else {
tabCoefPolynome[3] = tabMonome[i].substring(0, tabMonome[i].length - 3)
}
if (tabCoefPolynome[3] === '') {
tabCoefPolynome[3] = '1'
} else if (tabCoefPolynome[3] === '-') {
tabCoefPolynome[3] = '-1'
}
} else if ((tabMonome[i].includes('x^{2}')) || (tabMonome[i].includes('x^2'))) {
// c’est le monome ax^2
if (tabMonome[i].charAt(0) === '+') {
tabMonome[i] = tabMonome[i].substring(1)
}
if (tabMonome[i].includes('x^{2}')) {
tabCoefPolynome[2] = tabMonome[i].substring(0, tabMonome[i].length - 5)
} else {
tabCoefPolynome[2] = tabMonome[i].substring(0, tabMonome[i].length - 3)
}
if (tabCoefPolynome[2] === '') {
tabCoefPolynome[2] = '1'
} else if (tabCoefPolynome[2] === '-') {
tabCoefPolynome[2] = '-1'
}
} else if (tabMonome[i].indexOf('x') > -1) {
// c’est le monome ax
if (tabMonome[i].charAt(0) === '+') {
tabMonome[i] = tabMonome[i].substring(1)
}
tabCoefPolynome[1] = tabMonome[i].substring(0, tabMonome[i].length - 1)
if (tabCoefPolynome[1] === '') {
tabCoefPolynome[1] = '1'
} else if (tabCoefPolynome[1] === '-') {
tabCoefPolynome[1] = '-1'
}
} else {
// c’est le monome a
if (tabMonome[i].charAt(0) === '+') {
tabCoefPolynome[0] = tabMonome[i].substring(1)
} else {
tabCoefPolynome[0] = tabMonome[i]
}
}
}
if (modele === 8) {
coefAprime = fctsEtudeProduitNbs(tabCoefPolynome[2], '2')
coefBprime = tabCoefPolynome[1]
objet.expres_derivee = fctsEtudeEcrireMonome(1, 1, coefAprime)
if (coefBprime !== '0') {
objet.expres_derivee = objet.expres_derivee + fctsEtudeEcrireMonome(2, 0, coefBprime)
}
if (isDebug) console.debug('coefAprime:' + coefAprime + ' coefBprime:' + coefBprime + ' objet.expres_derivee:' + objet.expres_derivee)
objet.tabFacteurs[0][0] = objet.expres_derivee
// On cherche la valeur en laquelle s’annule la fonction
leZero = fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', coefBprime), coefAprime)
const valCoefAprime = fctsEtudeCalculValeur(coefAprime)
objet.signes_facteurs[0] = (valCoefAprime > 0) ? ['-', '+'] : ['+', '-']
objet.zeros_facteurs[0] = (fctsEtudeEstDansDomaine(leZero, domaineDerivee)) ? [leZero] : []
objet.zeros_facteurs_init[0] = [leZero]
objet.variables = tabCoefPolynome
} else {
// coefs de la dérivée
coefAprime = fctsEtudeProduitNbs(tabCoefPolynome[3], '3')
coefBprime = fctsEtudeProduitNbs(tabCoefPolynome[2], '2')
coefCprime = tabCoefPolynome[1]
objet.expres_derivee = fctsEtudeEcrireMonome(1, 2, coefAprime)
if (coefBprime !== '0') {
objet.expres_derivee = objet.expres_derivee + fctsEtudeEcrireMonome(2, 1, coefBprime)
}
if (coefCprime !== '0') {
objet.expres_derivee = objet.expres_derivee + fctsEtudeEcrireMonome(3, 0, coefCprime)
}
if (isDebug) console.debug('coefAprime:' + coefAprime + ' coefBprime:' + coefBprime + ' coefCprime:' + coefCprime + ' objet.expres_derivee:' + objet.expres_derivee)
objet.tabFacteurs[0][0] = objet.expres_derivee
// on cherche les racines du trinôme. Calculons le discriminant :
const valCoefAprime = fctsEtudeCalculValeur(coefAprime)
const valCoefBprime = fctsEtudeCalculValeur(coefBprime)
const valCoefCprime = fctsEtudeCalculValeur(coefCprime)
delta = Math.pow(valCoefBprime, 2) - 4 * valCoefAprime * valCoefCprime
if (delta < 0) {
// pas de racine
if (valCoefAprime < 0) {
objet.signes_facteurs[0] = ['-']
} else {
objet.signes_facteurs[0] = ['+']
}
objet.zeros_facteurs[0] = []
} else if (delta === 0) {
// une racine unique
leZero = fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', coefBprime), fctsEtudeProduitNbs('2', coefAprime))
if (valCoefAprime < 0) {
if (fctsEtudeEstDansDomaine(leZero, domaineDerivee)) {
objet.signes_facteurs[0] = ['-', '-']
} else {
objet.signes_facteurs[0] = ['-']
}
} else {
if (fctsEtudeEstDansDomaine(leZero, domaineDerivee)) {
objet.signes_facteurs[0] = ['+', '+']
} else {
objet.signes_facteurs[0] = ['+']
}
}
if (fctsEtudeEstDansDomaine(leZero, domaineDerivee)) {
objet.zeros_facteurs[0] = [leZero]
} else {
objet.zeros_facteurs[0] = []
}
objet.zeros_facteurs_init[0] = [leZero]
} else {
// deux racines
racine1 = (-valCoefBprime + Math.sqrt(delta)) / (2 * valCoefAprime)
racine2 = (-valCoefBprime - Math.sqrt(delta)) / (2 * valCoefAprime)
deltaTxt = fctsEtudeSommeNbs(fctsEtudeProduitNbs(coefBprime, coefBprime), fctsEtudeProduitNbs('-1', fctsEtudeProduitNbs(4, fctsEtudeProduitNbs(coefAprime, coefCprime))))
racine1Txt = simplificationRacineTrinome(coefBprime, '+', deltaTxt, coefAprime)
racine2Txt = simplificationRacineTrinome(coefBprime, '-', deltaTxt, coefAprime)
if (isDebug) console.debug('racine1Txt:' + racine1Txt + ' racine2Txt:' + racine2Txt)
if (isDebug) console.debug('racine1:' + racine1 + ' racine2:' + racine2)
racineMin = racine2Txt
racineMax = racine1Txt
if (racine1 < racine2) {
racineMin = racine1Txt
racineMax = racine2Txt
}
objet.signes_facteurs[0] = fctsEtudeSigneFct(objet.expres_derivee, domaineDerivee, [racineMin, racineMax])
if (fctsEtudeEstDansDomaine(racineMin, domaineDerivee)) {
if (fctsEtudeEstDansDomaine(racineMax, domaineDerivee)) {
objet.zeros_facteurs[0] = [racineMin + '|' + racineMax]
} else {
objet.zeros_facteurs[0] = [racineMin]
}
} else {
if (fctsEtudeEstDansDomaine(racineMax, domaineDerivee)) {
objet.zeros_facteurs[0] = [racineMax]
} else {
objet.zeros_facteurs[0] = []
}
}
objet.zeros_facteurs_init[0] = [racineMin + '|' + racineMax]
}
if (isDebug) console.debug('expresFct:' + expresFct + ' tabMonome:' + tabMonome + ' tabCoefPolynome:' + tabCoefPolynome + ' objet.expres_derivee:' + objet.expres_derivee + ' deltaTxt:' + deltaTxt + ' objet.zeros_facteurs:' + objet.zeros_facteurs + ' objet.signes_facteurs:' + objet.signes_facteurs)
objet.variables = tabCoefPolynome
}
// j’y ajoute les coefs du trinôme et deltaTxt
objet.variables.push(coefAprime, coefBprime, coefCprime, deltaTxt, delta)
if (delta > 0) {
objet.variables.push(racine1Txt)
}
}
break
default:// c’est donc quand le modele est 1'
// coef de l’écriture (ax+b)exp(x)'
fctAffine = j3pMathquillXcas(expresFct).substring(0, j3pMathquillXcas(expresFct).indexOf('*e^(x)'))
if ((fctAffine[0] === '(') && (fctAffine[fctAffine.length - 1] === ')')) {
fctAffine = fctAffine.substring(1, fctAffine.length - 1)
}
{
const posDecoup = Math.max(fctAffine.lastIndexOf('+'), fctAffine.lastIndexOf('-'))// cela me donne la position du signe entre b et ax
if (posDecoup <= 0) {
// c’est que c’est de la forme ax donc b vaut 0'
coefA = fctAffine.substring(0, fctAffine.length - 1)
if (coefA === '') {
coefA = 1
} else if (coefA === '-') {
coefA = -1
}
coefB = '0'
} else {
if (fctAffine[fctAffine.length - 1] === 'x') {
// fctAffine est écrit sous la forme b+ax
coefB = fctAffine.substring(0, posDecoup)
if (fctAffine[posDecoup] === '-') {
coefA = fctAffine.substring(posDecoup, fctAffine.length - 1)
} else {
coefA = fctAffine.substring(posDecoup + 1, fctAffine.length - 1)
}
if (coefA === '') {
coefA = '1'
} else if (coefA === '-') {
coefA = '-1'
}
} else {
// fctAffine est écrit sous la forme ax+b
coefA = fctAffine.substring(0, posDecoup - 1)
if (fctAffine[posDecoup] === '-') {
coefB = fctAffine.substring(posDecoup)
} else {
coefB = fctAffine.substring(posDecoup + 1)
}
if (coefA === '') {
coefA = '1'
} else if (coefA === '-') {
coefA = '-1'
}
}
}
if (coefA.charAt(coefA.length - 1) === '*') {
coefA = coefA.substring(0, coefA.length - 1)
}
coefA = fractionLatex(coefA)
coefB = fractionLatex(coefB)
// la dérivée vaut (ax+b+a)exp(x)
const coefBPrime = fctsEtudeSommeNbs(coefA, coefB)
if ((coefBPrime === '0') || (coefBPrime === 0)) {
objet.expres_derivee = fctsEtudeEcrireMonome(1, 1, coefA) + '\\mathrm{e}^x'
objet.tabFacteurs[0][0] = fctsEtudeEcrireMonome(1, 1, coefA)
if (fctsEtudeEstDansDomaine('0', domaineDerivee)) {
objet.zeros_facteurs[0] = ['0']
} else {
objet.zeros_facteurs[0] = []
}
objet.zeros_facteurs_init[0] = ['0']
} else {
objet.expres_derivee = '\\left(' + fctsEtudeEcrireMonome(1, 1, coefA) + fctsEtudeEcrireMonome(2, 0, coefBPrime) + '\\right)\\mathrm{e}^x'
objet.tabFacteurs[0][0] = fctsEtudeEcrireMonome(1, 1, coefA) + fctsEtudeEcrireMonome(2, 0, coefBPrime)
objet.zeros_facteurs[0] = [fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', coefBPrime), coefA)]
if (!fctsEtudeEstDansDomaine(fctsEtudeCalculValeur(objet.zeros_facteurs[0][0]), domaineDerivee)) {
objet.zeros_facteurs[0] = []
}
objet.zeros_facteurs_init[0] = [fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', coefBPrime), coefA)]
}
if (!objet.zeros_facteurs[0][0] || !fctsEtudeEstDansDomaine(objet.zeros_facteurs[0][0], domaineDerivee)) {
// c’est que le facteur affine est de signe constant
// ici le domaine ne peut pas être une réunion d’intervalles, mais juste un intervalle
objet.signes_facteurs[0] = fctsEtudeSigneFct(objet.expres_derivee, domaineDerivee, [])
// objet.zeros_facteurs[0] = [];
} else {
if (fctsEtudeCalculValeur(coefA) < 0) {
objet.signes_facteurs[0] = ['+', '-']
} else {
objet.signes_facteurs[0] = ['-', '+']
}
}
objet.tabFacteurs[0][1] = '\\mathrm{e}^x'
objet.signes_facteurs[1] = ['+']
objet.variables.push(coefA, coefB)
}
break
}
if (isDebug) console.debug('expres_derivee:' + objet.expres_derivee + ' tabFacteurs:' + objet.tabFacteurs + ' zeros_facteurs:' + objet.zeros_facteurs + ' zeros_facteurs_init:' + objet.zeros_facteurs_init)
return objet
} // fin fctsEtudeEcritureLatexDerivee
export function fctsEtudeExpressionAvecFacteurs (tabFacteurs) {
// tabFacteurs est un tableau de 2 éléments
// tabFacteurs[0] est un tableau contenant les facteurs du numérateur
// tabFacteurs[1] est un tableau contenant les facteurs du dénominateur
// chaque facteur est au format latex
// cette fonction renvoie l’expression au format latex du quotient (ou du produit s’il n’y a pas de dénominateur)
// on utilisera cette fonction pour savoir de quelle expression il reste à donner le signe après avoir donné les facteurs strictement positifs
function ecrireProduit (facteurs) {
// cette fonction permet d’écrire le produit en y mettant les parenthèses nécessaires
// on ne fait appel à cette fonction que lorsque facteurs.length>1
let produit = ''
// var exp_reg = /\\mathrm\{e\}^\{.*\}/i
// @todo la regexp suivante semble mieux, pour éviter de matcher une double accolade fermante et imposer un argument e^<qqchose>
// FIXME les regexp suivantes matchent du \truc, pas \\truc, pour choper \\truc c’est /\\\\truc/ qu’il faut
const cleanRegexps = [
/\\mathrm{e}\^{[^}]+}/i,
/\\(cos|ln|sin)\([^)]+\)/i,
/\\sqrt{[^}]+}/i
]
// ces expressions régulières sont là pour gérer la présence de parenthèses autour de facteurs comme (1-exp(x+1))
facteurs.forEach(function (facteur) {
const isNegatif = /^-/.test(facteur)
let cleanFacteur = isNegatif ? String(facteur).substring(1) : String(facteur) // on vire le signe - du début de la chaîne
cleanRegexps.forEach(function (regexp) {
cleanFacteur = cleanFacteur.replace(regexp, '')
})
/* Les facteurs ne sont pas ceux donnés par l’élève mais ceux qui sont générés par la section
Si c’est le premier facteur et qu’il était de la forme -2x, alors le signe - a été viré et on ne met pas de parenthèses
Sinon, s’il existe une somme ou une différence, alors on met des parenthèses (polynôme de degré supérieur ou égal à 1 non réduit à un monome)
*/
if (/[+-]/.test(cleanFacteur)) {
// c’est qu’il y a une somme ou une différence dans ce facteur donc je lui mets des parenthèses
produit += '\\left(' + facteur + '\\right)'
} else {
// dans ce cas pas besoin de parenthèses
produit += facteur
}
})
return produit
} // ecrireProduit
let numerateur = ''
let denominateur = ''
let expression = ''
if (tabFacteurs[0].length === 0) {
// tous les facteurs du numérateur étaient positifs// donc il ne reste plus que le dénominateur
if (tabFacteurs[1].length === 0) {
// tous les facteurs du dénominateur sont également positifs
// numerateur et dénominateur sont donc vides
// au final on ne fait rien
} else {
// l’expression initiale est du signe du dénominateur donc dans l’écriture, je mets le dénominateur au numérateur
expression = fctsEtudeExpressionAvecFacteurs([tabFacteurs[1], tabFacteurs[0]])
}
} else {
if (tabFacteurs[0].length === 1) {
// donc le numérateur ne contient qu’un seul facteur
numerateur = tabFacteurs[0][0]
} else {
numerateur = ecrireProduit(tabFacteurs[0])
}
if (tabFacteurs[1].length === 0) {
expression = numerateur
} else {
if (tabFacteurs[1].length === 1) {
// donc le dénominateur ne contient qu’un seul facteur
denominateur = tabFacteurs[1][0]
} else {
denominateur = ecrireProduit(tabFacteurs[1])
}
expression = '\\frac{' + numerateur + '}{' + denominateur + '}'
}
}
return expression
} // fin fctsEtudeExpressionAvecFacteurs
/**
* renvoie sous la forme d’un texte latex le monome demandé
* @param rang position du monome dans l’écriture
* @param puis puissance de x
* @param {string} nb coef écrit sous forme latex (\\frac{...}{...} pour des nb fractionnaires)
* @param {string} [nomVar=x]
* @returns {string}
*/
export function fctsEtudeEcrireMonome (rang, puis, nb, nomVar = 'x') {
let monomeTxt
const elementNb = fctsEtudeExtraireNumDen(nb)
if (elementNb[0]) { // c’est un nb fractionnaire'
if (elementNb[3] === '-') { // nb est négatif, dans ce cas, il faut que je récupère le signe dans le numérateur de la fraction
const newNum = elementNb[1].substring(1)
const newDen = elementNb[2]
monomeTxt = '-\\frac{' + newNum + '}{' + newDen + '}'
} else {
if (rang === 1) {
monomeTxt = nb
} else {
monomeTxt = '+' + nb
}
}
// monomeTxt est initialisé, on complète
if (puis === 1) {
monomeTxt += nomVar
} else if (puis > 1) {
monomeTxt += nomVar + '^{' + puis + '}'
}
} else {
monomeTxt = j3pMonome(rang, puis, j3pNombre(String(nb)), nomVar)
}
return monomeTxt
} // fin fctsEtudeEcrireMonome
/**
*
* @param tab
* @returns {string}
*/
export function fctsEtudeEcritureDomaine (tab) {
// le domaine de définition (ou de dérivabilité) d’une fonction est donné sous la forme d’un tableau
// on le renvoie au format latex
let leDomaine = ''
if (tab.length === 4) {
if ((tab[0] === '-infini') && (tab[1] === '+infini')) {
// le domaine est \\R
leDomaine = '\\R'
} else {
let borneInf = '-\\infty'
if (tab[0] !== '-infini') {
borneInf = fctsEtudeEcrireNbLatex(tab[0])
} else {
tab[2] = ']'
}
let borneSup = '+\\infty'
if (tab[1] !== '+infini') {
borneSup = fctsEtudeEcrireNbLatex(tab[1])
} else {
tab[3] = '['
}
leDomaine = tab[2] + borneInf + ';' + borneSup + tab[3]
}
} else {
if (tab.length > 4) {
// c’est que j’ai une réunion de deux intervalles (on ne devrait pas avoir d’exemple plus compliqué que ça...)
if (tab[1] === tab[5]) {
// c’est que le domaine peut s'écrire sous la forme \\R\\setminus\\{...\\}
leDomaine = '\\R\\setminus\\left\\{' + fctsEtudeEcrireNbLatex(tab[1]) + '\\right\\}'
} else {
// on écrit le domaine sous la forme de la réunion de deux intervalles (on devrait se limiter à ce cas de figure)
let borne1 = '-\\infty'
if (tab[0] !== '-infini') {
borne1 = fctsEtudeEcrireNbLatex(tab[0])
} else {
tab[2] = ']'
}
const borne2 = fctsEtudeEcrireNbLatex(tab[1])
const borne3 = fctsEtudeEcrireNbLatex(tab[4])
let borne4 = '+\\infty'
if (tab[5] !== '+infini') {
borne4 = fctsEtudeEcrireNbLatex(tab[5])
} else {
tab[7] = '['
}
leDomaine = tab[2] + borne1 + ';' + borne2 + tab[3] + '\\cup' + tab[6] + borne3 + ';' + borne4 + tab[7]
}
}
}
return leDomaine
} // fin fctsEtudeEcritureDomaine
export function fctsEtudeEstDansDomaine (nb, domaine) {
// cette fonction vérifie si le nombre nb est dans le domaine de définition (au sens strict)
// nb peut être un nombre ou une chaine de caractère correspodnant à un nombre : "2/3", "esp(5)-1", "ln(3)", "racine(5)" pa exemple
// domaine est un tableau de deux éléments
// domaine[0] représente un nombre du même style que nb ou -infini
// domaine[1] représente un nombre du même style que nb ou +infini
let appartientDomaine = true
const nbval = fctsEtudeCalculValeur(nb)
if (domaine[0] !== '-infini') {
const borneInfval = fctsEtudeCalculValeur(domaine[0])
appartientDomaine = (nbval > borneInfval)
}
if (domaine[1] !== '+infini') {
const borneSupval = fctsEtudeCalculValeur(domaine[1])
appartientDomaine = (appartientDomaine && (nbval < borneSupval))
}
return appartientDomaine
} // fin fctsEtudeEstDansDomaine
/**
*
* @param domaineInit
* @returns {string[]}
*/
export function fctsEtudeTransformationDomaine (domaineInit) {
// domaineInit est le véritable domaine de définition de la fonction, écrit sous la forme ["-infini",a,"]","[",b,"+infini","]","["] par exemple
// Cependant pour les fonction avec valeurs interdites, ces valeurs interdites ne doivent pas être prises en compte dans le domaine par moment
// c’est le cas pour les zéros des fonctions car cette valeur interdite est bien un zéro du dénominateur
const newDomaine = [domaineInit[0]]
let pos = 1
while (pos < domaineInit.length) {
if (pos + 3 < domaineInit.length) {
// il faut que je vérifie si la valeur en pos est la même qu’en pos+3, ce serait alors juste une valeur interdite
if (domaineInit[pos] !== domaineInit[pos + 3]) {
newDomaine.push(domaineInit[pos], domaineInit[pos + 1], domaineInit[pos + 2], domaineInit[pos + 3])
}
pos = pos + 4
} else {
newDomaine.push(domaineInit[pos], domaineInit[pos + 1], domaineInit[pos + 2])
pos = pos + 4
}
}
return newDomaine
} // fin fctsEtudeTransformationDomaine
/**
* renvoie le domaine en y excluant les valeurs interdites si elles sont dedans
* @param {Array<string|number>} domaineInit domaine de définition initial (peut-être celui qui est imposé)
* @param {Array<string|number>} tabValInterdites valeurs interdites de la fonction
* @param {boolean} isDebug
* @returns Array<string|number>
*/
export function fctsEtudeReconstructionDomaine (domaineInit, tabValInterdites, isDebug) {
if (!Array.isArray(domaineInit)) {
console.error(`domaineInit devrait être un tableau et ici on a ${typeof domaineInit}`, domaineInit)
domaineInit = ['-infini', '+infini', ']', '[']
}
if (!Array.isArray(tabValInterdites)) {
console.error(`tabValInterdites devrait être un tableau et ici on a ${typeof tabValInterdites}`, tabValInterdites)
tabValInterdites = []
}
let inf, sup
let newDomaine = []
const valInterditeAAjouter = fctsEtudeOrdonneTabSansDoublon(tabValInterdites, domaineInit).map(el => ecritBienFraction(el))
let i, borneInterdite, valeurBorne, valInterditeI
if (valInterditeAAjouter.length > 0) {
let numVal = 0
let posDomaine = 1
newDomaine.push(domaineInit[0])
while (numVal < valInterditeAAjouter.length && posDomaine <= domaineInit.length) {
if (domaineInit[posDomaine - 1].includes('-infini')) {
inf = -Math.pow(10, 15)
} else {
inf = fctsEtudeCalculValeur(domaineInit[posDomaine - 1])
}
if (domaineInit[posDomaine].includes('+infini')) {
sup = Math.pow(10, 15)
} else {
sup = fctsEtudeCalculValeur(domaineInit[posDomaine])
}
const valeurinterdite = fctsEtudeCalculValeur(valInterditeAAjouter[numVal])
if (isAlmostEqual(valeurinterdite, inf)) {
numVal++
} else if (isAlmostEqual(valeurinterdite, sup)) {
borneInterdite = false
valeurBorne = sup
for (i = 0; i < valInterditeAAjouter.length; i++) {
valInterditeI = fctsEtudeCalculValeur(valInterditeAAjouter[i])
if (isAlmostEqual(valeurBorne, valInterditeI)) {
borneInterdite = true
}
}
if (borneInterdite) {
// la dernière valeur est interdite, donc le crochet associé est ouvert
newDomaine.push(ecritBienFraction(domaineInit[posDomaine]), ecritBienFraction(domaineInit[posDomaine + 1]), '[')
} else {
newDomaine.push(ecritBienFraction(domaineInit[posDomaine]), ecritBienFraction(domaineInit[posDomaine + 1]), ecritBienFraction(domaineInit[posDomaine + 2]))
}
posDomaine += 4
numVal++
} else if ((valeurinterdite > inf) && (valeurinterdite < sup)) {
borneInterdite = false
valeurBorne = fctsEtudeCalculValeur(newDomaine[newDomaine.length - 1])
for (i = 0; i < valInterditeAAjouter.length; i++) {
valInterditeI = fctsEtudeCalculValeur(valInterditeAAjouter[i])
if (isAlmostEqual(valeurBorne, valInterditeI)) {
borneInterdite = true
}
}
if (borneInterdite) {
// la dernière valeur est interdite, donc le crochet associé est ouvert
newDomaine.push(valInterditeAAjouter[numVal], ']', '[', valInterditeAAjouter[numVal])
} else {
newDomaine.push(valInterditeAAjouter[numVal], domaineInit[posDomaine + 1], '[', valInterditeAAjouter[numVal])
}
numVal++
} else {
// on n’ajoute pas de valeur interdite ici
borneInterdite = false
valeurBorne = fctsEtudeCalculValeur(newDomaine[newDomaine.length - 1])
for (i = 0; i < valInterditeAAjouter.length; i++) {
valInterditeI = fctsEtudeCalculValeur(valInterditeAAjouter[i])
if (isAlmostEqual(valeurBorne, valInterditeI)) {
borneInterdite = true
}
}
if (borneInterdite) {
// la dernière valeur est interdite, donc le crochet associé est ouvert
newDomaine.push(domaineInit[posDomaine], ']', domaineInit[posDomaine + 2], domaineInit[posDomaine + 3])
} else {
newDomaine.push(domaineInit[posDomaine], domaineInit[posDomaine + 1], domaineInit[posDomaine + 2], domaineInit[posDomaine + 3])
}
posDomaine += 4
}
}
if (domaineInit[posDomaine]) { // On peut se retrouver avec un undefined ce qui explique cette rustine
newDomaine.push(domaineInit[posDomaine])
if (valInterditeAAjouter.join().includes(newDomaine[newDomaine.length - 2])) {
newDomaine.push(']')
} else {
newDomaine.push(domaineInit[posDomaine + 1])
}
if (valInterditeAAjouter.join().includes(newDomaine[newDomaine.length - 1])) {
newDomaine.push('[]]')
} else {
newDomaine.push(domaineInit[posDomaine + 2])
}
} else {
// Je ne sais pas pourquoi mais parfois il faut virer une valeur de newDomaine qui vaut undefined
if (!newDomaine[newDomaine.length - 1]) newDomaine.pop()
}
} else {
newDomaine = newDomaine.concat(domaineInit)
// attention car le domaine peut être fermé en l’une des bornes qui se trouve finalement dans le tableau des valeurs interdites
if (String(domaineInit[0]).includes('-infini')) {
inf = -Math.pow(10, 15)
} else {
inf = fctsEtudeCalculValeur(domaineInit[0])
}
if (domaineInit[domaineInit.length - 3]?.includes('+infini')) {
sup = Math.pow(10, 15)
} else {
sup = fctsEtudeCalculValeur(domaineInit[domaineInit.length - 3])
}
for (i = 0; i < tabValInterdites.length; i++) {
valInterditeI = fctsEtudeCalculValeur(tabValInterdites[i])
if (isAlmostEqual(inf, valInterditeI)) {
newDomaine[2] = ']'
}
if (isAlmostEqual(sup, valInterditeI)) {
newDomaine[newDomaine.length - 1] = '['
}
}
}
if (isDebug) console.debug('newDomaine:' + newDomaine + ' tabValInterdites:' + tabValInterdites + ' valInterditeAAjouter:' + valInterditeAAjouter)
return newDomaine
} // fin fctsEtudeReconstructionDomaine
/**
*
* @param fDex
* @returns {string}
*/
export function fctsEtudeTransformeExp (fDex) {
// fDex est une chaine de caractère correspondant à l’espression d’une fonction : f(x)
// elle revoie la même chaine en remplaçant e^... par exp(...)
// on appelle cette fonction après avoir utilisé j3pMathquillXcas, donc il n’y a plus frac{..}{..} mais (..)/(..)
// Attention car e n’est pas seulement pour exp, il peut aussi l'être pour racine
let newFdeX = ''
let finFdex = fDex
while (finFdex.includes('ne')) {
newFdeX += finFdex.substring(0, finFdex.indexOf('ne') + 2)
finFdex = finFdex.substring(finFdex.indexOf('ne') + 2)
}
let posE = finFdex.indexOf('e')
while (posE > -1) {
// e est présent et ce n’est aps celui de racine
newFdeX += finFdex.substring(0, posE)
// on regarde ce qui suit e
if ((finFdex.charAt(posE + 1) === 'x') && (finFdex.charAt(posE + 2) === 'p')) {
// il est déjà écrit exp, donc on passe
newFdeX += 'exp'
finFdex = finFdex.substring(posE + 3)
} else if ((finFdex.charAt(posE + 1) === '+') || (finFdex.charAt(posE + 1) === '-') || (finFdex.charAt(posE + 1) === '*') || (finFdex.charAt(posE + 1) === ')') || (finFdex.charAt(posE + 1) === 'x')) {
// e est tout seul, je le remplace par exp(1)
newFdeX += 'exp(1)'
finFdex = finFdex.substring(posE + 1)
} else {
// ce qui suit e est ^. Il faut alors trouver les parenthèses qui encadrent ce dont on prend l’exponentielle
if (finFdex.charAt(posE + 2) === '(') {
// c’est de la forme e^(...)
let parOuverte = 1
let posParFerme = posE + 3
while (parOuverte > 0) {
if (finFdex.charAt(posParFerme) === '(') {
parOuverte++
} else if (finFdex.charAt(posParFerme) === ')') {
parOuverte--
}
posParFerme++
}
newFdeX += 'exp(' + finFdex.substring(posE + 3, posParFerme)
finFdex = finFdex.substring(posParFerme)
} else {
// c’est uste de la forme e^., cad que seul le caractère suivant est est en puissance
newFdeX += 'exp(' + finFdex.substring(posE + 2, posE + 3) + ')'
finFdex = finFdex.substring(posE + 3)
}
}
posE = finFdex.indexOf('e')
}
newFdeX += finFdex
// J’ai encore un souci car il ne reconnait pas 3e comme un produit, donc il faut le lui expliquer...
newFdeX = newFdeX.replace(/(\d)e/g, '$1*e')
return newFdeX
}
/**
* Transforme l’écriture de texteFonction pour avoir l’expression sous la forme permettant de créer un arbre et d’appliquer la fonction evalue
* @param {string} texteFonction L’expression au format latex ou tout autre format intermédiaire
* @returns {string}
*/
export function fctsEtudeEcriturePourCalcul (texteFonction) {
// je convertis pour que ce soit plus facile à utiliser
// @todo remplacer j3pMathquillXcas par mqNormalise et vérifier que ça fonctionne comme attendu (ça devrait)
// Je peux aussi avoir un souci avec ln a qui serait écrit sans parenthèses. Il faut que j’en mette
let newExpressionFct = String(texteFonction).replace(/\\ln\s?([0-9]+[,.][0-9]+)/g, 'ln($1)') // Pour les décimaux
newExpressionFct = newExpressionFct.replace(/\\ln\s?([a-z]|[0-9]+)/g, 'ln($1)') // pour les entiers où une variable
newExpressionFct = newExpressionFct.replace(/\\ln\s?\\frac\{([^}]+)}\{([^}]+)}/g, 'ln($1/$2)') // pour les fractions
newExpressionFct = j3pMathquillXcas(newExpressionFct)
// gestion de la racine carrée
// il faut que je transforme sqrt en racine
while (newExpressionFct.includes('sqrt')) {
newExpressionFct = newExpressionFct.replace('sqrt', 'racine')
}
// mais j’ai encore un problème avec l’exponentielle : je peux avoir e isolé, ou bien e^x ou bien e^{...}. Ceschoses ne sont pas comprises, il faut modifier ça et écrire sous la forme exp(...)
newExpressionFct = fctsEtudeTransformeExp(newExpressionFct)
// on peut encore avoir un soucis si l’élève écrit exp(), cad s’il ne met rien en puissance. On va le considérer comme exp(1)
while (newExpressionFct.includes('exp()')) {
newExpressionFct = newExpressionFct.replace('exp()', 'exp(1)')
}
return newExpressionFct
}
/**
* À décrire
* @param {string|number} valeur
* @returns {number} (NaN si valeur n’est ni string ni number)
*/
export function fctsEtudeCalculValeur (valeur) {
if (typeof valeur === 'number') valeur = String(valeur)
if (typeof valeur !== 'string') {
console.error(Error(`valeur incorrecte (${typeof valeur})`))
return Number.NaN
}
if (!valeur) {
console.error(Error('paramètre incorrect (chaîne vide)'))
return Number.NaN
}
// un nombre écrit sous forme d’une chaine de caractères va être calculé pour qu’on ait sa valeur décimale
// correctif pour les cas où on entre (a+racine(b))/c (modele 2 avec domaine surchargé) on remplace d’abord par des sqrt pour ne pas avoir de pb avec j3pMathquillXcas
while (valeur.includes('racine')) {
valeur = valeur.replace('racine', 'sqrt')
}
valeur = fctsEtudeEcriturePourCalcul(valeur)
const arbreChaine = new Tarbre(valeur, [])
return arbreChaine.evalue([])
}
/**
* Renvoie un tableau contenant les éléments de tab rangés dans l’ordre croissant
* Si tab contient deux éléments égaux, un seul apparaîtra dans le tableau final
* Vérifie aussi que les valeurs sont bien dans le domaine de définition
* @param {Array<number|string>}tab
* @param {Array<number|string>} [domaineDef] Les bornes, -infini géré
* @returns {string[]}
*/
export function fctsEtudeOrdonneTabSansDoublon (tab, domaineDef) {
if (!Array.isArray(tab)) throw Error('paramètre invalide')
// si y’a rien à faire inutile d’aller plus loin
if (!tab.length) return []
const domaine = (Array.isArray(domaineDef) && domaineDef.length === 2) ? [...domaineDef] : ['-infini', '+infini']
const tabOrd = []
// il ne faut pas changer le tableau initial, d’où cette pseudo copie (pas forcément de la même taille,
// avec manipulation de chaque elt par Tarbre qui peut générer des pbs d’arrondis).
// Ça ne contient que des strings, ces strings correspondant aux valeurs exactes, surtout pas les arrondis
/** @type {string[]} */
const tabStr = []
for (const nb of tab) {
const orig = String(nb)
if (orig !== '') {
if (orig.includes('|')) {
// il y a plusieurs valeurs à ajouter dans le tableau
const valeurs = orig.split('|')
for (const valeur of valeurs) {
// je ne prends pas la valeur si elle n’est pas dans le domaine de def'
if ((valeur !== '') && fctsEtudeEstDansDomaine(valeur, domaine)) {
tabStr.push(String(valeur))
}
}
} else {
// là il n’y a qu’une valeur à ajouter'
if (!orig.includes('infini')) {
if (fctsEtudeEstDansDomaine(nb, domaine)) {
// je ne prends pas la valeur si elle n’est pas dans le domaine de def'
tabStr.push(String(orig))
}
} else if ((orig === '-infini') && (domaine[0] === '-infini')) {
tabStr.push('-infini')
}
// Dans le tableau final, -infini et +infini ne seront pas conservés, voilà pourquoi je ne prends pas +infini
// -infini, je l’ai gardé, mais j’aurai pu faire autrement (22 lignes plus loin, on voit qu’on ne le prend pas)
}
}
}
// tabStr contient alors toutes les valeurs EXACTES présentes dans tab (éventuellement au format latex), toutes en string
// si on a rien trouvé on arrête là
if (tabStr.length === 0) return []
// si y’en a qu’un on le renvoie
if (tabStr.length === 1) return tabStr
// on en fait une copie pour la réduire dans la boucle (strings only)
const tabTmp = [...tabStr]
// à chaque passage dans la boucle, je récupère l’élément minimal de tabTmp et je vire toutes ses occurrences
while (tabTmp.length !== 0) {
// on prend le plus petit
const minRestant = tabTmp.includes('-infini') ? '-infini' : minTab(tabTmp).min
tabOrd.push(minRestant)
// on supprime alors dans tabTmp toutes les occurrences
// J’utilise une boucle classique car la taille de tabTmp évolue
for (let pos = 0; pos < tabTmp.length; pos++) {
const elt = tabTmp[pos]
let aVirer = false
if (elt === minRestant) {
aVirer = true
} else if (['-infini', '-Infinity'].includes(elt)) {
aVirer = true
} else if (isAlmostEqual(elt, minRestant)) {
aVirer = true
}
if (aVirer) {
tabTmp.splice(pos, 1)
// il faut rester sur cet elt, au cas où y’en aurait un 2e à virer dans tabTmp
pos--
}
}
}
// tabOrd contient alors les valeurs exactes de la liste des valeurs passées en argument (dans tab)
return tabOrd
} // fctsEtudeOrdonneTabSansDoublon
/**
*
* @param {number} nb
* @returns {*}
*/
export function fctsEtudeEcrireNbLatex (nb) {
// un nombre est écrit au format mathématique, on le convertit au format latex
const newNb = (typeof nb === 'string' && nb.includes('/')) ? fractionLatex(nb) : String(nb)
// on fait aussi attention à la présence de exp, ln ou racine
const chaineExp = /exp\([0-9x,.^+-]+\)/ig
const chaineLn = /ln\([0-9x,.^+-]+\)/ig
const chaineRacine = /racine\([0-9x,.^+-]+\)/ig
let i3
let expressTabZero
// gestion de exp et ln
let expressionZero = newNb
if (chaineExp.test(expressionZero)) {
// dan ce cas exp(...) est présent et on le transforme en e^...
expressTabZero = expressionZero.match(chaineExp)
for (i3 = 0; i3 < expressTabZero.length; i3++) {
const puissanceExp = expressTabZero[i3].substring(4, expressTabZero[i3].length - 1)
if (puissanceExp === '1') {
expressionZero = expressionZero.replace(expressTabZero[i3], '\\mathrm{e}')
} else {
expressionZero = expressionZero.replace(expressTabZero[i3], '\\mathrm{e}^{' + puissanceExp + '}')
}
}
}
// maintenant gestion d’une chaine avec ln'
if (chaineLn.test(expressionZero)) {
// dan ce cas ln(...) est présent et on le transforme en \\ln(...)
expressTabZero = expressionZero.match(chaineLn)
for (i3 = 0; i3 < expressTabZero.length; i3++) {
const expressLn = expressTabZero[i3].substring(3, expressTabZero[i3].length - 1)
expressionZero = expressionZero.replace(expressTabZero[i3], '\\ln(' + expressLn + ')')
}
}
// et enfin gestion de la racine carrée
if (chaineRacine.test(expressionZero)) {
// dan ce cas racine(...) est présent et on le transforme en \\sqrt{...}
expressTabZero = expressionZero.match(chaineRacine)
for (i3 = 0; i3 < expressTabZero.length; i3++) {
const radical = expressTabZero[i3].substring(7, expressTabZero[i3].length - 1)
expressionZero = expressionZero.replace(expressTabZero[i3], '\\sqrt{' + radical + '}')
}
}
return expressionZero
}
export function fctsEtudeNombreIntervalle (intervalle) {
// intervalle est un tableau contenant en 0 la borne inf et en 1 la borneSup
// On renvoie un objet
// objet.nombre est un nombre présent dans l’intervalle
// objet.tests_images est un tableau de valeurs présentes dans l’intervalle. Ces valeurs servent à tester des égalités d’expressions
let eltVal
let expressBorne
let tableauImages = []// ce tableau va servir pour les tests d’égalités entre facteurs
// il faut tout de même que les valeurs dont on cherche les images soient dans le domaine, d’où la nécessité de ce tableau
let valInit
if (intervalle[0] === '-infini') {
if (intervalle[1] === '+infini') {
eltVal = '0'
tableauImages = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
} else {
// intervalle ]-infty;a[
expressBorne = j3pMathquillXcas(intervalle[1])
while (expressBorne.includes('sqrt')) {
expressBorne = expressBorne.replace('sqrt', 'racine')
}
eltVal = expressBorne + '-6'
valInit = fctsEtudeCalculValeur(eltVal)// arbre_val.evalue([]);
tableauImages = [valInit, valInit - 1, valInit - 2, valInit - 3, valInit - 4, valInit - 5, valInit - 6, valInit - 7, valInit - 8, valInit - 9]
}
} else {
if (intervalle[1] === '+infini') {
// intervalle ]a;+infty[
expressBorne = j3pMathquillXcas(intervalle[0])
eltVal = expressBorne + '+6'
valInit = fctsEtudeCalculValeur(eltVal)// arbre_val.evalue([]);
tableauImages = [valInit, valInit + 1, valInit + 2, valInit + 3, valInit + 4, valInit + 5, valInit + 6, valInit + 7, valInit + 8, valInit + 9]
} else {
// intervalle ]a;b[
expressBorne = j3pMathquillXcas('(' + intervalle[0] + '+(' + intervalle[1] + '))/2')
while (expressBorne.includes('sqrt')) {
expressBorne = expressBorne.replace('sqrt', 'racine')
}
eltVal = expressBorne
const borneInf = j3pMathquillXcas(intervalle[0])
const valInit1 = fctsEtudeCalculValeur(borneInf)
const borneSup = j3pMathquillXcas(intervalle[1])
const valInit2 = fctsEtudeCalculValeur(borneSup)
let val = (valInit1 + valInit2) / 2
while ((tableauImages.length < 10) && (val > valInit1)) {
tableauImages.push(val)
val--
}
while ((tableauImages.length < 10) && (val < valInit2)) {
tableauImages.push(val)
val++
}
}
}
return { nombre: fctsEtudeCalculValeur(eltVal), tests_images: tableauImages }
}
export function fctsEtudeSigneFct (laFonction, domaine, zeros, isDebug = false) {
// la fonction est l’expression de la fonction écrite soit au format latex soit dans un format plus mathématique
// elle sera de tout façon convertie pour être utilisée comme on le souhaite à l’aide de la fonction ecriture_pour_calc
// domaine est le domaine de définition sous la forme ["-infini",a,"]","[",b,c,"]","]"] par exemple
// zeros est un tableau qui contient toutes les valeurs où s’annule la fonction. Ces valeurs sont au format latex
// isDebug permet d’afficher des infos de debuggage en console
const newDomaine = fctsEtudeTransformationDomaine(domaine)
const tabIntervalles = tableauIntervalles(zeros, newDomaine, isDebug)
if (isDebug) console.debug('tabIntervalles=', tabIntervalles, ' et newDomaine=', newDomaine)
// le tableau précédent contient toutes les valeurs intervenant pour le signe de la fonction
// je crée alors un tableau accueillant les signes successifs
const tabSignes = []
const fonctionPourCalc = fctsEtudeEcriturePourCalcul(laFonction)
if (isDebug) console.debug('laFonction:', laFonction, fonctionPourCalc)
const arbreEvalueDerivee = new Tarbre(fonctionPourCalc, ['x'])
for (let i = 0; i < tabIntervalles.length - 1; i++) {
const valX = fctsEtudeNombreIntervalle([tabIntervalles[i], tabIntervalles[i + 1]]).nombre// je prend un nombre de l’intervalle considéré. Son image sera celle de la fonction sur tout l’intervalle
const image = arbreEvalueDerivee.evalue([valX])
if (!isNaN(image)) {
// je regarde le signe de l’image de valX par la fonction dérivée lorsque cette image existe bien
if (image < 0) {
tabSignes.push('-')
} else {
tabSignes.push('+')
}
} else {
tabSignes.push('')// cela doit pouvoir se produire si par exemple on étudie f(x)=\sqrt{4-x^2} sur ]-\\infty;-2]\\cup [2;+\\infty[ sur [-2;2], je ne dois pas avoir de signe
}
}
if (isDebug) console.debug('tabSignes=', tabSignes)
return tabSignes
} // fin fctsEtudeSigneFct
/**
* Construction du tableau de signes avec fonctions utiles
* @param obj
* @param tabDimension
* @param zeroTab
* @param valZeroTab
* @param signeTabCorr
* @param objStyleCouleur
* @param paletteComplete
* @param {boolean} [isDebug=false]
*/
export function fctsEtudeTableauSignesFct (obj, tabDimension, zeroTab, valZeroTab, signeTabCorr, objStyleCouleur, paletteComplete = false, isDebug = false) {
// on crée un tableau de signes dans obj
// ici il ne s’agit que du tableau du signe d’une fonction, pas d’un produit ou quotient
// tabDimension est un tableau : [0] est la largeur et [1] la hauteur
// tabDimension[2] est le nombre de valeurs à ajouter sur l’axe des abscisses (en plus des bornes)
// tabDimension[3] est un tableau de deux éléments : les bornes les plus extrêmes du domaine de def de la fonction (écrire infini pour \\infty)'
// tabDimension[4] est un tableau optionnel. S’il n’est pas undefined, c’est le domaine réel de définition sous la forme par exemple ["-infini",a,"]","[",a,"+infini","]","["] pour une valeur interdite
// zeroTab[0] est un booléen. Il vaut true lorsque les valeurs en lesquelles s’annule l’expression n’est pas à remplir par l’élève
// zeroTab[1] est un booléen. Il vaut true lorsque les signes sont complétés
// valZeroTab est un tableau de valeurs en lesquelles s’annule la fonction
// signeTabCorr est un tableau qui contient tous les signes attendus (dans l’ordre)
// objStyleCouleur peut contenir 4 éléments (les 2 derniers étant obligatoires) : un style de texte, couleur si on ne définit par le style, texte qui sera "signe de ", nom_f
// paletteComplete est un argument optionnel qui vaut false par défaut. On le passe à true lorsque l’exponentielle ou le logarithme sont affichés dans la palette
// isDebug vaut false par défaut. Lorsqu’il est vrai, des infos apparaissent en console
let i, j, borneInfTxt, borneSupTxt
const zoneInput = { ligneX: [], signeFacts: [] }
const listeInput = []
const nomdiv = obj
const macouleurTxt = (!objStyleCouleur.couleur) ? objStyleCouleur.style.color : objStyleCouleur.couleur
const macouleur = '#000000'
const txt2Txt = objStyleCouleur.texte + ' $' + objStyleCouleur.nom_f + '\\quad \'(x)$'
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
const L = tabDimension[0]
const h = tabDimension[1]
svg.setAttribute('width', L)
svg.setAttribute('height', h)
nomdiv.appendChild(svg)
j3pCreeRectangle(svg, { x: 1, y: 1, width: L - 1, height: h - 1, couleur: '#000000', epaisseur: 1 })
j3pCreeSegment(svg, { x1: 1, y1: 0, x2: 0, y2: h, couleur: '#000000', epaisseur: 1 })
j3pCreeSegment(svg, { x1: 1, y1: h / 2, x2: L, y2: h / 2, couleur: '#000000', epaisseur: 1 })
// placement de la ligne "signe de f'(x)"
const signeDe = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(signeDe, { position: 'absolute' })
j3pAffiche(signeDe, '', txt2Txt)
// pour placer le segment vertical, je cherche la largeur de la phrase "signe de ..."
// placement du "x"
const leX = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(leX, { position: 'absolute' })
j3pAffiche(leX, '', '$x$')
const largeurSignede = signeDe.getBoundingClientRect().width + 4
j3pCreeSegment(svg, {
x1: largeurSignede,
y1: 0,
x2: largeurSignede,
y2: h,
couleur: '#000000',
epaisseur: 1
})
const posTxt = h / 2 + h / 4 - signeDe.getBoundingClientRect().height / 2
signeDe.style.top = posTxt + 'px'
signeDe.style.left = '2px'
const posTxt2 = h / 4 - leX.getBoundingClientRect().height / 2
leX.style.top = posTxt2 + 'px'
const posTxt3 = largeurSignede / 2 - leX.getBoundingClientRect().width / 2
leX.style.left = posTxt3 + 'px'
if (tabDimension[3] === undefined) {
borneInfTxt = '$-\\infty$'
borneSupTxt = '$+\\infty$'
} else {
if ((tabDimension[3][0] === undefined) || (tabDimension[3][1] === undefined)) {
borneInfTxt = '$-\\infty$'
borneSupTxt = '$+\\infty$'
} else {
if (tabDimension[3][0] === '-infini') {
borneInfTxt = '$-\\infty$'
} else {
borneInfTxt = '$' + fctsEtudeEcrireNbLatex(tabDimension[3][0]) + '$'
}
if (tabDimension[3][1] === '+infini') {
borneSupTxt = '$+\\infty$'
} else {
borneSupTxt = '$' + fctsEtudeEcrireNbLatex(tabDimension[3][1]) + '$'
}
}
}
if (zeroTab[0]) {
// on remplit entièrement la première ligne (sans zone de saisie)
// plus et moins l’infini'
const divMoinsInf = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(divMoinsInf, { position: 'absolute' })
const divPlusInf = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(divPlusInf, { position: 'absolute' })
// Je n’affiche les bornes que si on affiche toute la première ligne'
j3pAffiche(divMoinsInf, '', borneInfTxt)
divMoinsInf.style.left = (largeurSignede + 1) + 'px'
divMoinsInf.style.top = (h / 4 - divMoinsInf.getBoundingClientRect().height / 2) + 'px'
j3pAffiche(divPlusInf, '', borneSupTxt)
divPlusInf.style.left = (L - divPlusInf.getBoundingClientRect().width - 2 + 1) + 'px'
divPlusInf.style.top = (h / 4 - divPlusInf.getBoundingClientRect().height / 2) + 'px'
}
const tabZeroOrdonne = fctsEtudeOrdonneTabSansDoublon(valZeroTab, tabDimension[3])
let nbValx = tabDimension[2]
// pour gérer les bornes qui peuvent être des zéros ou des valeurs interdites
// on y met soit un zéro, soit une double barre
let newTab4
if (!tabDimension[4]) {
newTab4 = tabDimension[3]
newTab4.push(']', '[')
} else {
newTab4 = tabDimension[4]
}
// dans le cas où on a changé le domaine de def, il faut qu’on regarde si les bornes sont des zéros de la fonction
let tabValZeros = []
for (i = 0; i < valZeroTab.length; i++) {
tabValZeros = tabValZeros.concat(String(valZeroTab[i]).split('|'))
}
if (newTab4[0] !== '-infini') {
for (j = 0; j < tabValZeros.length; j++) {
if (isAlmostEqual(tabValZeros[j], newTab4[0])) {
// c’est que la borne inf du domaine est un zéro de la fonction ou une valeur interdite
if (newTab4[newTab4.length - 2] === '[') {
// si c’est un zéro :
const zeroBorneInf = j3pAddElt(nomdiv, 'div', '0', { style: objStyleCouleur.style })
j3pStyle(zeroBorneInf, { position: 'absolute' })
zeroBorneInf.style.left = (largeurSignede) + 'px'
zeroBorneInf.style.top = (3 * h / 4 - zeroBorneInf.getBoundingClientRect().height / 2) + 'px'
} else {
// et pour une valeur interdite
j3pCreeSegment(svg, {
x1: largeurSignede + 5,
y1: h / 2,
x2: largeurSignede + 5,
y2: h,
couleur: macouleurTxt,
epaisseur: 1
})
}
}
}
}
if (newTab4[newTab4.length - 3] !== '+infini') {
for (j = 0; j < tabValZeros.length; j++) {
if (isAlmostEqual(tabValZeros[j], newTab4[newTab4.length - 3])) {
// c’est que la borne inf du domaine est un zéro de la fonction ou une valeur interdite
if (newTab4[newTab4.length - 1] === ']') {
// si c’est un zéro
const zeroBorneSup = j3pAddElt(nomdiv, 'div', '0', { style: objStyleCouleur.style })
j3pStyle(zeroBorneSup, { position: 'absolute' })
zeroBorneSup.style.left = (L - zeroBorneSup.getBoundingClientRect().width - 2 + 1) + 'px'
zeroBorneSup.style.top = (3 * h / 4 - zeroBorneSup.getBoundingClientRect().height / 2) + 'px'
} else {
// et pour une valeur interdite
j3pCreeSegment(svg, {
x1: L - 5,
y1: h / 2,
x2: L - 5,
y2: h,
couleur: macouleurTxt,
epaisseur: 1
})
}
}
}
}
// Fin de la gestion du zéro aux bornes ou de la valeur interdite
if (zeroTab[0]) {
// les endroits où s’annulent la fonction sont donnés
// attention les_zeros doivent être du type string (un entier ou un décimal ou un nb fractionnaire de la forme a/b
// dans un premier temps, on ordonne les nombre du tableau valZeroTab (qui sont ls endroits où s’annulent notre produit)
// Dans le cas où sont présents la fonction exp, la fonction ln ou la fonction racine, il va falloir gérer l’affichage'
nbValx = tabZeroOrdonne.length
for (i = 0; i < nbValx; i++) {
const leZero = (['string', 'number'].includes(typeof tabZeroOrdonne[i])) ? tabZeroOrdonne[i].replace(/\./g, ',') : tabZeroOrdonne[i][0].replace(/\./g, ',')
const monZeroTxt = fctsEtudeEcrireNbLatex(leZero)
const zoneNulle = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(zoneNulle, { position: 'absolute' })
j3pAffiche(zoneNulle, '', '$' + monZeroTxt + '$')
zoneNulle.style.left = (largeurSignede + (i + 1) * (L - largeurSignede) / (nbValx + 1) - zoneNulle.getBoundingClientRect().width / 2) + 'px'
zoneNulle.style.top = (h / 4 - zoneNulle.getBoundingClientRect().height / 2) + 'px'
}
} else {
// dans le cas où les valeur en lesquelles s’annule le produit sont à donner
for (i = 0; i <= nbValx + 1; i++) {
// i==0 pour la borne inf du domaine et i==valX+1 pour la borneSup du domaine
const zoneNulle = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(zoneNulle, { position: 'absolute' })
const elt = j3pAffiche(zoneNulle, '', '&1&', { inputmq1: { texte: '' } })
mqRestriction(elt.inputmqList[0], '\\d.,/+-', { commandes: ['fraction', 'inf', 'exp', 'ln'] })
if (i === 0) {
// placement de la borne inf
zoneNulle.style.left = (largeurSignede + 2) + 'px'
} else if (i === (nbValx + 1)) {
// placement de la borne sup
zoneNulle.style.left = (L - 5 - zoneNulle.getBoundingClientRect().width) + 'px'
} else {
zoneNulle.style.left = (largeurSignede + i * (L - largeurSignede) / (nbValx + 1) - zoneNulle.getBoundingClientRect().width / 2) + 'px'
}
zoneNulle.style.top = (h / 4 - zoneNulle.getBoundingClientRect().height / 2) + 'px'
zoneInput.ligneX.push(elt.inputmqList[0])
elt.inputmqList[0].leSpan = elt.parent.parentNode
elt.inputmqList[0].num = i
}
for (i = 0; i <= nbValx + 1; i++) {
zoneInput.ligneX[i].addEventListener('input', function () {
if (this.num === 0) {
this.leSpan.style.left = (largeurSignede + 2) + 'px'
} else if (this.num === nbValx + 1) {
this.leSpan.style.left = (L - 10 - this.getBoundingClientRect().width) + 'px'
} else {
this.leSpan.style.left = (largeurSignede + this.num * (L - largeurSignede) / (nbValx + 1) - this.getBoundingClientRect().width / 2) + 'px'
}
this.leSpan.style.top = (h / 4 - this.getBoundingClientRect().height / 2) + 'px'
})
}
j3pFocus(zoneInput.ligneX[0])
}
// on crée tout d’abord les segments verticaux
const posValZero = []
for (i = 1; i <= nbValx; i++) {
posValZero[i] = largeurSignede + i * (L - largeurSignede) / (nbValx + 1)
j3pCreeSegment(svg, {
x1: posValZero[i],
y1: h / 2,
x2: posValZero[i],
y2: h,
couleur: macouleur,
epaisseur: 1
})
}
if (zeroTab[1]) {
// on donne le signe de la fonction
// les zéros
for (i = 1; i <= nbValx; i++) {
if (fctsEtudeEstDansDomaine(tabZeroOrdonne[i - 1], newTab4)) {
const zeroFct = j3pAddElt(nomdiv, 'div', '0', { style: objStyleCouleur.style })
j3pStyle(zeroFct, { position: 'absolute' })
zeroFct.style.left = (largeurSignede + i * (L - largeurSignede) / (nbValx + 1) - zeroFct.getBoundingClientRect().width / 2) + 'px'
zeroFct.style.top = (3 * h / 4 - zeroFct.getBoundingClientRect().height / 2) + 'px'
} else {
j3pCreeSegment(svg, {
x1: largeurSignede + i * (L - largeurSignede) / (nbValx + 1) + 3,
y1: h / 2,
x2: largeurSignede + i * (L - largeurSignede) / (nbValx + 1) + 3,
y2: h,
couleur: objStyleCouleur.couleur,
epaisseur: 1
})
j3pCreeSegment(svg, {
x1: largeurSignede + i * (L - largeurSignede) / (nbValx + 1),
y1: h / 2,
x2: largeurSignede + i * (L - largeurSignede) / (nbValx + 1),
y2: h,
couleur: objStyleCouleur.couleur,
epaisseur: 1
})
}
}
// les signes
for (i = 1; i <= nbValx + 1; i++) {
const leSigne = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(leSigne, { position: 'absolute' })
j3pAffiche(leSigne, '', '$' + signeTabCorr[i - 1] + '$')
leSigne.style.left = largeurSignede + (i - 1 / 2) * (L - largeurSignede) / (nbValx + 1) - leSigne.getBoundingClientRect().width / 2 + 'px'
leSigne.style.top = 3 * h / 4 - leSigne.getBoundingClientRect().height / 2 + 'px'
}
} else {
// le signe de la fonction est à compléter
// emplacement pour le premier signe
for (i = 1; i <= nbValx + 1; i++) {
const leSigne = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(leSigne, { position: 'absolute' })
const elt = j3pAffiche(leSigne, '', '&1&', { inputmq1: { texte: '' } })
mqRestriction(elt.inputmqList[0], '+-')
leSigne.style.left = (largeurSignede + (2 * i - 1) * (L - largeurSignede) / (2 * (nbValx + 1)) - leSigne.getBoundingClientRect().width / 2) + 'px'
leSigne.style.top = (3 * h / 4 - leSigne.getBoundingClientRect().height / 2) + 'px'
zoneInput.signeFacts.push(elt.inputmqList[0])
elt.inputmqList[0].num = i
elt.inputmqList[0].leSpan = elt.parent.parentNode
}
// ajustement de la position de chaque signe
for (i = 1; i <= nbValx + 1; i++) {
zoneInput.signeFacts[i - 1].addEventListener('input', function (e) {
e = e || window.e
const key = e.which ? e.which : e.keyCode
const signeInit = $(this).mathquill('latex')
if (['+', '-'].includes(String.fromCharCode(key))) {
$(this).mathquill('latex', String.fromCharCode(key).substring(1))
} else {
// on cherche le signe s’il en existe un
const leSigneTab = signeInit.match(/[+-]/)
if (leSigneTab) $(this).mathquill('latex', leSigneTab[0])
}
this.leSpan.style.left = (largeurSignede + (2 * this.num - 1) * (L - largeurSignede) / (2 * nbValx + 2) - this.getBoundingClientRect().width / 2) + 'px'
this.leSpan.style.top = (3 * h / 4 - this.getBoundingClientRect().height / 2) + 'px'
})
}
// liste déroulante
for (i = 1; i <= nbValx; i++) {
const divListe = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(divListe, { position: 'absolute' })
const tabTextListe = ['|', '||', '0']
const elt = j3pAffiche(divListe, '', '#1#', { className: 'liste1', liste1: { texte: tabTextListe } })
divListe.style.left = (largeurSignede + i * (L - largeurSignede) / (nbValx + 1) - 8) + 'px'
divListe.style.top = (3 * h / 4 - divListe.getBoundingClientRect().height / 2) + 'px'
listeInput.push(elt.selectList[0])
}
}
// affichage d’une palette pour compléter les zones de la première ligne
let listeBoutons, zonePalette
if (!zeroTab[0]) {
// ceci ne se fait que si la première ligne n’est pas complétée
listeBoutons = ['fraction', 'inf', 'racine', 'puissance']
if (paletteComplete) listeBoutons.push('exp', 'ln')
zonePalette = j3pAddElt(obj, 'div')
// palette MQ :
j3pPaletteMathquill(zonePalette, zoneInput.ligneX[0], {
liste: listeBoutons,
style: { paddingTop: '10px' }
})
for (i = 0; i <= nbValx + 1; i++) {
$(zoneInput.ligneX[i]).focusin(function () {
ecoute(this.num)
})
}
for (i = 1; i <= (nbValx + 1) * (tabZeroOrdonne.length + 1); i++) {
$(zoneInput.signeFacts[i]).focusin(function () {
j3pEmpty(zonePalette)
})
}
for (i = 1; i <= (nbValx) * (tabZeroOrdonne.length + 1); i++) {
$(listeInput[i]).focusin(function () {
j3pEmpty(zonePalette)
})
}
}
function ecoute (num) {
if (zoneInput.ligneX[num].className.includes('mq-editable-field')) {
j3pEmpty(zonePalette)
j3pPaletteMathquill(zonePalette, zoneInput.ligneX[num], {
liste: listeBoutons,
style: { paddingTop: '10px' }
})
}
}
if (zeroTab[0]) {
if (!zeroTab[1]) j3pFocus(zoneInput.signeFacts[0])
} else {
j3pFocus(zoneInput.ligneX[0])
}
return { input: zoneInput, liste: listeInput, palette: zonePalette }
} // fin fctsEtudeTableauSignesFct
/**
* Crée le tableau de signes d’un produit ou d’un quotient dans obj
* @param obj
* @param expresTab tableau contenant tous les facteurs
* @param {number[]} tabDimension [largeur, hauteur, nombre de valeurs à ajouter sur l’axe des abscisses (en plus des bornes), opt], opt peut être un tableau de deux éléments : les bornes du domaine de def de la fonction (écrire infini pour \\infty)'
* @param prodQuot ["produit"] ou ["quotient", [true, false, true, …]] (true signifie que le facteur associé de expresTab est au dénominateur)
* @param {boolean[]} zeroTab le premier vaut true lorsque "la ligne des x" est entièrement complétée, le 2e vaut true lorsque le 1er est false et que les signes du tableau ne sont pas à donner par l’élève
* @param valZeroTab tableau des valeurs en lesquelles s’annule au moins une fonction
* @param {boolean} [lesSignes] paramètres optionnel si on souhaite avoir des zones de saisies pour les signes (on peut aussi mettre false)
* @param objStyleCouleur peut contenir 4 éléments (les 2 derniers étant obligatoires) : un style de texte, couleur si on ne définit par le style, texte (qui correspondra à "signe du produit" ou "signe d’un quotient") et enfin texte2 qui sera "signe de " (pour que les textes restante dans la section)
* @returns {{input: {ligneX: string[], signeFacts: string[], signeProd: string[]}, liste: string[], palette: HTMLElement}}
*/
export function fctsEtudeTabSignesProduit (obj, expresTab, tabDimension, prodQuot, zeroTab, valZeroTab, lesSignes, objStyleCouleur) {
const signeProdTxt = objStyleCouleur.texte
const macouleurTxt = (objStyleCouleur.couleur) ? objStyleCouleur.couleur : objStyleCouleur.style.color
// liste des boutons pour la première ligne du tableau quand on a des zones de saisie :
let listeBoutons
if (!zeroTab[0]) {
// ceci ne se fait que si la première ligne n’est pas complétée
listeBoutons = ['fraction', 'inf']
for (let i = 0; i < expresTab.length; i++) {
expresTab[i] = String(expresTab[i])
if (expresTab[i].includes('^')) {
if (listeBoutons.indexOf('racine') === -1) {
listeBoutons.push('racine', 'puissance')
}
}
if (expresTab[i].includes('exp') || expresTab[i].includes('ln') || expresTab[i].includes('e^') || expresTab[i].includes('mathrm{e}')) {
if (!listeBoutons.includes('exp')) {
listeBoutons.push('exp', 'ln')
}
}
}
}
let posTxt, divMoinsInf, divPlusInf, posTxtborneInf, posTxtborneInf2, posTxtborneSup, posTxtborneSup2, numLigne,
numColonne, nbSigne, k, tabTextListe
// objets qui vont servir à stocker les zones de saisie et les listes à compléter
const zoneInput = { ligneX: [], signeFacts: [], signeProd: [] }
const listeInput = []
const macouleur = '#000000'
const signedeTabtxt = []
for (let i = 0; i < expresTab.length; i++) signedeTabtxt[i] = objStyleCouleur.texte2 + '$' + expresTab[i] + '$'
const L = tabDimension[0] // Largeur du tableau
const h = tabDimension[1] // hauteur du tableau
const nomdiv = obj
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
svg.setAttribute('width', L)
svg.setAttribute('height', h)
nomdiv.appendChild(svg)
j3pCreeRectangle(svg, { x: 1, y: 0, width: L - 2, height: h, couleur: macouleur, epaisseur: 1 })
for (let i = 1; i < expresTab.length + 2; i++) {
j3pCreeSegment(svg, {
x1: 0,
y1: i * h / (expresTab.length + 2),
x2: L,
y2: i * h / (expresTab.length + 2),
couleur: macouleur,
epaisseur: 1
})
}
// Affichage des textes "signe de ax+b"
const tabDivSigneDe = []
for (let i = 1; i <= expresTab.length; i++) {
const divSigneDe = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(divSigneDe, { position: 'absolute' })
j3pAffiche(divSigneDe, '', signedeTabtxt[i - 1], { style: { color: macouleurTxt } })
tabDivSigneDe.push(divSigneDe)
}
// texte "signe du produit" ou "signe du quotient" (ce texte étant renseigné à l’appel de la fonction
const signeDeProduit = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(signeDeProduit, { position: 'absolute' })
j3pAffiche(signeDeProduit, '', signeProdTxt, { style: { color: macouleurTxt } })
tabDivSigneDe.push(signeDeProduit)
// pour placer le segment vertical, je cherche la largeur de la phrase "signe de ..."
let nbPuiss = signedeTabtxt[0].split('^').length - 1
let largDecalage = nbPuiss * 6
let largeurSignede = tabDivSigneDe[0].getBoundingClientRect().width + 8
for (let i = 2; i <= expresTab.length; i++) {
// j’ai vu qu’avec des puissances, la largeur est mal calculée. Il faut que je décale un peu vers la droite
nbPuiss = signedeTabtxt[i - 2].split('^').length - 1
largDecalage = nbPuiss * 6
largeurSignede = Math.max(largeurSignede, tabDivSigneDe[i - 1].getBoundingClientRect().width + 8 + largDecalage)
}
largeurSignede = Math.max(largeurSignede, tabDivSigneDe[tabDivSigneDe.length - 1].getBoundingClientRect().width + 4)
j3pCreeSegment(svg, {
x1: largeurSignede,
y1: 0,
x2: largeurSignede,
y2: h,
couleur: macouleur,
epaisseur: 1
})
for (let i = 1; i <= expresTab.length; i++) {
posTxt = (i + 0.5) * h / (expresTab.length + 2) - tabDivSigneDe[i - 1].getBoundingClientRect().height / 2
tabDivSigneDe[i - 1].style.top = posTxt + 'px'
tabDivSigneDe[i - 1].style.left = '2px'
}
const hauteurSigneProduit = tabDivSigneDe[tabDivSigneDe.length - 1].getBoundingClientRect().height
posTxt = (expresTab.length + 1 + 0.5) * h / (expresTab.length + 2) - hauteurSigneProduit / 2
tabDivSigneDe[tabDivSigneDe.length - 1].style.top = posTxt + 'px'
tabDivSigneDe[tabDivSigneDe.length - 1].style.left = '2px'
// placement du "x"
const leX = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(leX, { position: 'absolute' })
j3pAffiche(leX, '', '$x$', { style: { color: macouleurTxt } })
const posTxt2 = h / (2 * expresTab.length + 4) - leX.getBoundingClientRect().height / 2
leX.style.top = posTxt2 + 'px'
const posTxt3 = largeurSignede / 2 - leX.getBoundingClientRect().width / 2
leX.style.left = posTxt3 + 'px'
// moins l’infini (ou autre chose) dans le cas où la "ligne des x" est à compléter
if (zeroTab[0]) {
// ce div n’est créé que dans ce cas, pour gérer la tabulation dans le cas d’une zone de saisie
divMoinsInf = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(divMoinsInf, { position: 'absolute' })
}
const arbreExpressFacteur = []
if (!tabDimension[3]) {
if (zeroTab[0]) j3pAffiche(divMoinsInf, '', '$-\\infty$', { style: { color: macouleurTxt } })
} else {
let textBorneInf = String(tabDimension[3][0])
if (textBorneInf.includes('infini')) {
textBorneInf = textBorneInf.replace('infini', '\\infty')
} else {
const posFrac = textBorneInf.indexOf('/')
if (posFrac > -1) {
// on a une fraction
textBorneInf = '\\frac{' + textBorneInf.substring(0, posFrac) + '}{' + textBorneInf.substring(posFrac + 1, textBorneInf.length) + '}'
}
}
if (zeroTab[0]) j3pAffiche(divMoinsInf, '', '$' + textBorneInf + '$', { style: { color: macouleurTxt } })
// on peut aussi avoir à ajouter une double barre ou un zéro au niveau de cette borne
let borneInfInterdite = false// permettra de mettre une double barre au produit/quotient au niveau de la borne inf
const newExpressionFct = []
for (let i = 0; i < expresTab.length; i++) {
newExpressionFct[i] = fctsEtudeEcriturePourCalcul(expresTab[i])
arbreExpressFacteur[i] = new Tarbre(newExpressionFct[i], ['x'])
}
if (!textBorneInf.includes('infty')) {
// la borne inf n’est pas -infini'
let imageInfProduitQuotNulle = false
for (let i = 0; i < expresTab.length; i++) {
const imageBorneInf = arbreExpressFacteur[i].evalue([fctsEtudeCalculValeur(tabDimension[3][0])])
if (isAlmostZero(imageBorneInf) && !String(expresTab[i]).includes('mathrm{e}')) {
// on met un zéro au niveau du facteur
// J’ai exclu le cas où j’ai une exponentielle qui peut donner une image quasiment nulle
const imageBorneInf = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(imageBorneInf, { position: 'absolute' })
j3pAffiche(imageBorneInf, '', '0', { style: { color: macouleurTxt } })
posTxtborneInf = (i + 1.5) * h / (expresTab.length + 2) - imageBorneInf.getBoundingClientRect().height / 2
imageBorneInf.style.top = posTxtborneInf + 'px'
posTxtborneInf2 = largeurSignede
imageBorneInf.style.left = posTxtborneInf2 + 'px'
if ((prodQuot[0] === 'quotient') && (prodQuot[1][i] === 'den')) {
// on a un quotient et ce facteur est au dénominateur
borneInfInterdite = true
// au niveau de la dernière ligne, je dois mettre une double barre (à gauche)
} else {
imageInfProduitQuotNulle = true
}
} else if ((imageBorneInf === '-Infinity') || (imageBorneInf === '+Infinity')) {
borneInfInterdite = true
// on met une double barre au niveau du facteur
j3pCreeSegment(svg, {
x1: largeurSignede + 5,
y1: (i + 1) * h / (expresTab.length + 2),
x2: largeurSignede + 5,
y2: (i + 2) * h / (expresTab.length + 2),
couleur: macouleurTxt,
epaisseur: 1
})
}
}
if (borneInfInterdite) {
j3pCreeSegment(svg, {
x1: largeurSignede + 5,
y1: (expresTab.length + 1) * h / (expresTab.length + 2),
x2: largeurSignede + 5,
y2: (expresTab.length + 2) * h / (expresTab.length + 2),
couleur: macouleurTxt,
epaisseur: 1
})
} else if (imageInfProduitQuotNulle) {
const imageBorneInfProd = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(imageBorneInfProd, { position: 'absolute' })
j3pAffiche(imageBorneInfProd, '', '0', { style: { color: macouleurTxt } })
posTxtborneInf = (expresTab.length + 1.5) * h / (expresTab.length + 2) - imageBorneInfProd.getBoundingClientRect().height / 2
imageBorneInfProd.style.top = posTxtborneInf + 'px'
posTxtborneInf2 = largeurSignede
imageBorneInfProd.style.left = posTxtborneInf2 + 'px'
}
}
}
if (zeroTab[0]) {
// ce div n’est créé que dans ce cas, pour gérer la tabulation dans le cas d’une zone de saisie
// plus l’infini (ou autre chose) dans le cas où la "ligne des x" est à compléter
divPlusInf = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(divPlusInf, { position: 'absolute' })
}
if (!tabDimension[3]) {
if (zeroTab[0]) j3pAffiche(divPlusInf, '', '$+\\infty$', { style: { color: macouleurTxt } })
} else {
let textBorneSup = String(tabDimension[3][1])
if (textBorneSup.includes('infini')) {
textBorneSup = textBorneSup.replace('infini', '\\infty')
} else {
const posFrac2 = textBorneSup.indexOf('/')
if (posFrac2 > -1) {
// on a une fraction
textBorneSup = '\\frac{' + textBorneSup.substring(0, posFrac2) + '}{' + textBorneSup.substring(posFrac2 + 1, textBorneSup.length) + '}'
}
}
if (zeroTab[0]) j3pAffiche(divPlusInf, '', '$' + textBorneSup + '$', { style: { color: macouleurTxt } })
// on peut aussi avoir à ajouter une double barre ou un zéro au niveau de cette borne
let borneSupInterdite = false// permettra de mettre une double barre au produit/quotient au niveau de la borne sup
if (!textBorneSup.includes('infty')) {
// la borne sup n’est pas +infini'
let imageSupProduitQuotNulle = false
for (let i = 0; i < expresTab.length; i++) {
const imageBorneSup = arbreExpressFacteur[i].evalue([fctsEtudeCalculValeur(tabDimension[3][1])])
if (isAlmostZero(imageBorneSup) && (!String(expresTab[i]).includes('mathrm{e}'))) {
// on met un zéro au niveau du facteur
// J’ai exclu le cas où j’ai une exponentielle qui peut donner une image quasiment nulle
const imageBorneSup = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(imageBorneSup, { position: 'absolute' })
j3pAffiche(imageBorneSup, '', '0', { style: { color: macouleurTxt } })
posTxtborneSup = (i + 1.5) * h / (expresTab.length + 2) - imageBorneSup.getBoundingClientRect().height / 2
imageBorneSup.style.top = posTxtborneSup + 'px'
posTxtborneSup2 = L - imageBorneSup.getBoundingClientRect().width
imageBorneSup.style.left = posTxtborneSup2 + 'px'
if ((prodQuot[0] === 'quotient') && (prodQuot[1][i] === 'den')) {
// on a un quotient et ce facteur est au dénominateur
borneSupInterdite = true
} else {
imageSupProduitQuotNulle = true
}
} else if ((imageBorneSup === '-Infinity') || (imageBorneSup === '+Infinity')) {
borneSupInterdite = true
// on met une double barre au niveau du facteur
j3pCreeSegment(svg, {
x1: L - 5,
y1: (i + 1) * h / (expresTab.length + 2),
x2: L - 5,
y2: (i + 2) * h / (expresTab.length + 2),
couleur: macouleurTxt,
epaisseur: 1
})
}
}
if (borneSupInterdite) {
j3pCreeSegment(svg, {
x1: L - 5,
y1: (expresTab.length + 1) * h / (expresTab.length + 2),
x2: L - 5,
y2: (expresTab.length + 2) * h / (expresTab.length + 2),
couleur: macouleurTxt,
epaisseur: 1
})
} else if (imageSupProduitQuotNulle) {
const imageBorneSupProd = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(imageBorneSupProd, { position: 'absolute' })
j3pAffiche(imageBorneSupProd, '', '0', { style: { color: macouleurTxt } })
posTxtborneSup = (expresTab.length + 1.5) * h / (expresTab.length + 2) - imageBorneSupProd.getBoundingClientRect().height / 2
imageBorneSupProd.style.top = posTxtborneSup + 'px'
posTxtborneSup2 = L - imageBorneSupProd.getBoundingClientRect().width
imageBorneSupProd.style.left = posTxtborneSup2 + 'px'
}
}
}
if (zeroTab[0]) {
divMoinsInf.style.left = (largeurSignede + 1) + 'px'
divMoinsInf.style.top = (h / (2 * expresTab.length + 4) - divMoinsInf.getBoundingClientRect().height / 2) + 'px'
divPlusInf.style.left = (L - 1 - divPlusInf.getBoundingClientRect().width - 2 + 1) + 'px'
divPlusInf.style.top = (h / (2 * expresTab.length + 4) - divPlusInf.getBoundingClientRect().height / 2) + 'px'
}
// fctsEtudeOrdonneTabSansDoublon fonction avec des array où chaque élément est un number ou une string
// Si c’est une string, cela peut être plusieurs valeurs qui seront alors séparées par |
// Or ici valZeroTab est un array d’arrays donc ça coïnce
// Ces array doivent être transformées en string
const valZeroPourOrdonne = valZeroTab.map(elt => elt.join('|'))
const tabZeroOrdonne = fctsEtudeOrdonneTabSansDoublon(valZeroPourOrdonne, tabDimension[3])
let nbValx = tabDimension[2]
if (zeroTab[0]) {
// les endroits où s’annulent la fonction sont donnés
// attention les_zeros doivent être du type string (un entier ou un décimal ou un nb fractionnaire de la forme a/b
// dans un premier temps, on ordonne les nombre du tableau valZeroTab (qui sont ls endroits où s’annulent notre produit)
// Dans le cas où sont présents la fonction exp, la fonction ln ou la fonction racine, il va falloir gérer l’affichage'
nbValx = tabZeroOrdonne.length
for (let i = 0; i < nbValx; i++) {
const leZero = (['string', 'number'].includes(typeof tabZeroOrdonne[i])) ? tabZeroOrdonne[i].replace(/\./g, ',') : tabZeroOrdonne[i][0].replace(/\./g, ',')
const monZeroTxt = fctsEtudeEcrireNbLatex(leZero)
const laZone = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(laZone, { position: 'absolute' })
j3pAffiche(laZone, '', '$' + monZeroTxt + '$', { style: { color: macouleurTxt } })
laZone.style.left = (largeurSignede + (i + 1) * (L - largeurSignede) / (nbValx + 1) - laZone.getBoundingClientRect().width / 2) + 'px'
laZone.style.top = (h / (2 * expresTab.length + 4) - laZone.getBoundingClientRect().height / 2) + 'px'
}
} else {
// dans le cas où les valeur en lesquelles s’annule le produit sont à donner
// bien sûr dans ce cas, il y a autant de valeurs que de fonctions seulement si on n’a que des fonctions affines'
for (let i = 0; i <= nbValx + 1; i++) {
// i==0 pour la borne inf du domaine et i==valX+1 pour la borneSup du domaine
const laZone = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(laZone, { position: 'absolute' })
const elt = j3pAffiche(laZone, '', '&1&', { inputmq1: { texte: '' } })
mqRestriction(elt.inputmqList[0], '\\d.,/+-', { commandes: listeBoutons })
zoneInput.ligneX.push(elt.inputmqList[0])
if (i === 0) {
// placement de la borne inf
laZone.style.left = (largeurSignede + 2) + 'px'
} else if (i === (nbValx + 1)) {
// placement de la borne sup
laZone.style.left = (L - 5 - laZone.getBoundingClientRect().width) + 'px'
} else {
laZone.style.left = (largeurSignede + i * (L - largeurSignede) / (nbValx + 1) - laZone.getBoundingClientRect().width / 2) + 'px'
}
laZone.style.top = (h / (2 * expresTab.length + 4) - laZone.getBoundingClientRect().height / 2) + 'px'
elt.inputmqList[0].num = i
elt.inputmqList[0].leSpan = laZone
}
for (let i = 0; i <= nbValx + 1; i++) {
zoneInput.ligneX[i].addEventListener('input', function () {
if (this.num === 0) {
this.leSpan.style.left = (largeurSignede + 2) + 'px'
} else if (this.num === nbValx + 1) {
this.leSpan.style.left = (L - 10 - this.getBoundingClientRect().width) + 'px'
} else {
this.leSpan.style.left = (largeurSignede + this.num * (L - largeurSignede) / (nbValx + 1) - this.getBoundingClientRect().width / 2) + 'px'
}
this.leSpan.style.top = (h / (2 * expresTab.length + 4) - this.getBoundingClientRect().height / 2) + 'px'
})
}
j3pFocus(zoneInput.ligneX[0])
}
// on crée tout d’abord les segments verticaux
const posValZero = []
for (let i = 1; i <= nbValx; i++) {
posValZero[i] = largeurSignede + i * (L - largeurSignede) / (nbValx + 1)
j3pCreeSegment(svg, {
x1: posValZero[i],
y1: h / (expresTab.length + 2),
x2: posValZero[i],
y2: h,
couleur: macouleur,
epaisseur: 1
})
}
if (!zeroTab[1]) {
// Dans ce cas, les signes et zéros sont à compléter
// création de toutes les zones pour les signes
nbSigne = (expresTab.length + 1) * (nbValx + 1)
for (let i = 1; i <= nbSigne; i++) {
const leDiv = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(leDiv, { position: 'absolute' })
const elt = j3pAffiche(leDiv, '', '&1&', { inputmq1: { texte: '' } })
mqRestriction(elt.inputmqList[0], '+-')
// positionnement initial de toutes les zones
numLigne = Math.ceil(i / (nbValx + 1))
numColonne = (i - 1) % (nbValx + 1) + 1
leDiv.style.left = (largeurSignede + (2 * numColonne - 1) * (L - largeurSignede) / (2 * nbValx + 2) - leDiv.getBoundingClientRect().width / 2) + 'px'
leDiv.style.top = (numLigne * h / (expresTab.length + 2) + h / (2 * expresTab.length + 4) - leDiv.getBoundingClientRect().height / 2) + 'px'
elt.inputmqList[0].num = i
elt.inputmqList[0].leSpan = leDiv
zoneInput.signeFacts.push(elt.inputmqList[0])
}
// ajustement de la position de chaque signe
for (let i = 1; i <= nbSigne; i++) {
zoneInput.signeFacts[i - 1].addEventListener('input', function () {
const numLigne = Math.ceil(this.num / (nbValx + 1))
const numColonne = (this.num - 1) % (nbValx + 1) + 1
this.leSpan.style.left = (largeurSignede + (2 * numColonne - 1) * (L - largeurSignede) / (2 * nbValx + 2) - this.getBoundingClientRect().width / 2) + 'px'
this.leSpan.style.top = (numLigne * h / (expresTab.length + 2) + h / (2 * expresTab.length + 4) - this.getBoundingClientRect().height / 2) + 'px'
})
}
// listes qui ne sont pas celles de la dernières ligne
const nbAutresListes = nbValx * expresTab.length
for (let i = 1; i <= nbAutresListes; i++) {
numLigne = Math.ceil(i / (nbValx))
numColonne = (i - 1) % (nbValx) + 1
const leDiv = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(leDiv, { position: 'absolute' })
const elt = j3pAffiche(leDiv, '', '#1#', { className: 'liste1', liste1: { texte: ['|', '0'] } })
leDiv.style.left = (largeurSignede + numColonne * (L - largeurSignede) / (nbValx + 1) - 8) + 'px'
leDiv.style.top = ((2 * numLigne + 1) * h / (2 * expresTab.length + 4) - leDiv.getBoundingClientRect().height / 2) + 'px'
listeInput.push(elt.selectList[0])
}
// listes de la dernière ligne
for (let i = 1; i <= nbValx; i++) {
const leDiv = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(leDiv, { position: 'absolute' })
tabTextListe = ['|', '||', '0']
const elt = j3pAffiche(leDiv, '', '#1#', { className: 'liste1', liste1: { texte: tabTextListe } })
leDiv.style.left = (largeurSignede + i * (L - largeurSignede) / (nbValx + 1) - 8) + 'px'
leDiv.style.top = (h - h / (2 * expresTab.length + 4) - leDiv.getBoundingClientRect().height / 2) + 'px'
listeInput.push(elt.selectList[0])
}
if (zeroTab[0]) {
j3pFocus(zoneInput.signeFacts[0])
} else {
j3pFocus(zoneInput.ligneX[0])
}
} else {
// zeroTab[0] vaut nécessairement false
// On est dans le cas où tout le tableau va être affiché (signes et zéros), sauf peut-être le signe du produit/quotient
// on s’occupe dans un premier temps du signe de chaque facteur (pas celui du produit)
// je place tous les zeros
const doubleBarre = []
for (let i = 1; i <= expresTab.length; i++) {
for (k = 0; k < tabZeroOrdonne.length; k++) {
// il faut s’assurer si tabZeroOrdonne[k]annule ou non le facteur'
if (Math.abs(arbreExpressFacteur[i - 1].evalue([fctsEtudeCalculValeur(tabZeroOrdonne[k])])) < Math.pow(10, -10)) {
const zeroFacteur = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(zeroFacteur, { position: 'absolute' })
j3pAffiche(zeroFacteur, '', '0', { style: { color: macouleurTxt } })
zeroFacteur.style.left = largeurSignede + (k + 1) * (L - largeurSignede) / (tabZeroOrdonne.length + 1) - zeroFacteur.getBoundingClientRect().width / 2 + 'px'
zeroFacteur.style.top = (i + 0.5) * h / (expresTab.length + 2) - zeroFacteur.getBoundingClientRect().height / 2 + 'px'
if (prodQuot[0] === 'quotient') doubleBarre[k] = (prodQuot[1][i - 1] !== 'num')
}
}
}
nbSigne = (nbValx + 1) * expresTab.length
// le tableau suivant va servir pour le signe du produit ou du quotient
const nbSignesMoinsTab = []
for (let i = 1; i <= nbValx + 1; i++) nbSignesMoinsTab[i] = 0
for (let i = 1; i <= nbSigne; i++) {
const signeFact = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(signeFact, { position: 'absolute' })
// je cherche l’endroit où s’annule mon expression (si elle s’annule)
numLigne = Math.ceil(i / (nbValx + 1))
numColonne = (i - 1) % (nbValx + 1) + 1
let aucuneRacineDansDomaine
if ((valZeroPourOrdonne[numLigne - 1]) && (valZeroPourOrdonne[numLigne - 1] !== '')) {
// deux possibilités : soit valZeroPourOrdonne[numLigne-1] ne possède qu’une valeur, soit plusieurs'
if (!String(valZeroPourOrdonne[numLigne - 1]).includes('|')) {
// une seule valeur
aucuneRacineDansDomaine = (!tabZeroOrdonne.join().includes(valZeroPourOrdonne[numLigne - 1]))
if (aucuneRacineDansDomaine) {
// cela doit engendrer une modification du tableau lesSignes[numLigne-1] qui ne devra plus contenir qu’un seul élément et non plus 2
if ((tabDimension[3] !== undefined) && (tabDimension[3][0] !== '-infini')) {
// si le zéro est inférieur à tabDimension[3][0], alors on vire le premier élément de lesSignes[numLignes-1]
if (fctsEtudeCalculValeur(valZeroPourOrdonne[numLigne - 1]) < fctsEtudeCalculValeur(tabDimension[3][0])) {
// sauf qu’il ne faut pas le virer si ce zéro est seul
if (lesSignes[numLigne - 1].length > 1) {
lesSignes[numLigne - 1].splice(0, 1)
}
}
}
if ((tabDimension[3] !== undefined) && (tabDimension[3][1] !== '+infini')) {
// si le zéro est supérieur à tabDimension[3][1], alors on vire le dernier élément de lesSignes[numLignes-1]
if (fctsEtudeCalculValeur(valZeroPourOrdonne[numLigne - 1]) > fctsEtudeCalculValeur(tabDimension[3][1])) {
// sauf qu’il ne faut pas le virer si ce zéro est seul
if (lesSignes[numLigne - 1].length > 1) {
lesSignes[numLigne - 1].splice(lesSignes[numLigne - 1].length - 1, 1)
}
}
}
}
} else {
const tabValeursZero = String(valZeroPourOrdonne[numLigne - 1]).split('|')
const tabValeursZeroOrdonne = fctsEtudeOrdonneTabSansDoublon(tabValeursZero)
// c’est le tableau précédent que je viens d’ordonner.
aucuneRacineDansDomaine = true
for (k = 0; k < tabValeursZero.length; k++) {
aucuneRacineDansDomaine = (aucuneRacineDansDomaine && (!tabZeroOrdonne.includes(tabValeursZero[k])))
}
for (k = 0; k < tabValeursZero.length; k++) {
if (!tabZeroOrdonne.join().includes(tabValeursZeroOrdonne[k])) {
// la valeur du tableau n’est finalement pas dans le tableau (et donc pas dans le domaine)
// ça m’oblige à ne pas prendre en compte un éléments de lesSignes
if ((tabDimension[3] !== undefined) && (tabDimension[3][0] !== '-infini')) {
// si le zéro est inférieur à tabDimension[3][0], alors on vire le premier élément de lesSignes[numLignes-1]
if (fctsEtudeCalculValeur(tabValeursZeroOrdonne[k]) < fctsEtudeCalculValeur(tabDimension[3][0])) {
lesSignes[numLigne - 1].splice(0, 1)
}
}
if ((tabDimension[3] !== undefined) && (tabDimension[3][1] !== '+infini')) {
// si le zéro est supérieur à tabDimension[3][1], alors on vire le dernier élément de lesSignes[numLignes-1]
if (fctsEtudeCalculValeur(tabValeursZeroOrdonne[k]) > fctsEtudeCalculValeur(tabDimension[3][1])) {
lesSignes[numLigne - 1].splice(lesSignes[numLigne - 1].length - 1, 1)
}
}
}
}
}
} else {
aucuneRacineDansDomaine = true
}
if (aucuneRacineDansDomaine) {
// on a le même signe sur toute la ligne
j3pAffiche(signeFact, '', '$' + lesSignes[numLigne - 1][0] + '$', { style: { color: macouleurTxt } })
if (lesSignes[numLigne - 1][0] === '-') nbSignesMoinsTab[numColonne] += 1
} else {
if (!tabZeroOrdonne[numColonne - 1]) {
// pour le signe sur la dernière colonne
j3pAffiche(signeFact, '', '$' + lesSignes[numLigne - 1][lesSignes[numLigne - 1].length - 1] + '$', { style: { color: macouleurTxt } })
if (lesSignes[numLigne - 1][lesSignes[numLigne - 1].length - 1] === '-') nbSignesMoinsTab[numColonne] += 1
} else {
if (!String(valZeroPourOrdonne[numLigne - 1]).includes('|')) {
// la fonction de cette ligne ne s’annule pas plus d’une fois'
if (fctsEtudeCalculValeur(valZeroPourOrdonne[numLigne - 1]) >= fctsEtudeCalculValeur(tabZeroOrdonne[numColonne - 1])) {
j3pAffiche(signeFact, '', '$' + lesSignes[numLigne - 1][0] + '$', { style: { color: macouleurTxt } })
if (lesSignes[numLigne - 1][0] === '-') nbSignesMoinsTab[numColonne] += 1
} else {
j3pAffiche(signeFact, '', '$' + lesSignes[numLigne - 1][1] + '$', { style: { color: macouleurTxt } })
if (lesSignes[numLigne - 1][1] === '-') nbSignesMoinsTab[numColonne] += 1
}
} else {
let tableauValZeroNumligne = String(valZeroPourOrdonne[numLigne - 1]).split('|')
tableauValZeroNumligne = fctsEtudeOrdonneTabSansDoublon(tableauValZeroNumligne, tabDimension[3])
k = 0
while (fctsEtudeCalculValeur(tabZeroOrdonne[numColonne - 1]) > fctsEtudeCalculValeur(tableauValZeroNumligne[k])) {
k++
}
j3pAffiche(signeFact, '', '$' + lesSignes[numLigne - 1][k] + '$', { style: { color: macouleurTxt } })
if (lesSignes[numLigne - 1][k] === '-') {
nbSignesMoinsTab[numColonne] += 1
}
}
}
}
signeFact.style.left = (largeurSignede + (2 * numColonne - 1) * (L - largeurSignede) / (2 * nbValx + 2) - signeFact.getBoundingClientRect().width / 2) + 'px'
signeFact.style.top = (numLigne * h / (expresTab.length + 2) + h / (2 * expresTab.length + 4) - signeFact.getBoundingClientRect().height / 2) + 'px'
}
if (!lesSignes[lesSignes.length - 1]) {
// on demande de compléter des zones de saisie de la dernière ligne
for (let i = 1; i <= nbValx + 1; i++) {
const leDiv = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(leDiv, { position: 'absolute' })
const elt = j3pAffiche(leDiv, '', '&1&', { inputmq1: { texte: '' } })
zoneInput.signeProd.push(elt.inputmqList[0])
mqRestriction(elt.inputmqList[0], '+-')
// positionnement initial de toutes les zones
numColonne = (i - 1) % (nbValx + 1) + 1
leDiv.style.left = (largeurSignede + (2 * numColonne - 1) * (L - largeurSignede) / (2 * nbValx + 2) - leDiv.getBoundingClientRect().width / 2) + 'px'
leDiv.style.top = ((expresTab.length + 1.5) * h / (expresTab.length + 2) - leDiv.getBoundingClientRect().height / 2) + 'px'
elt.inputmqList[0].num = i
elt.inputmqList[0].leSpan = leDiv
}
// ajustement de la position de chaque signe
for (let i = 1; i <= nbValx + 1; i++) {
zoneInput.signeProd[i - 1].addEventListener('input', function () {
const numColonne = (this.num - 1) % (expresTab.length + 1) + 1
this.leSpan.style.left = (largeurSignede + (2 * numColonne - 1) * (L - largeurSignede) / (2 * nbValx + 2) - this.getBoundingClientRect().width / 2) + 'px'
})
}
// listes de la dernière ligne
for (let i = 1; i <= nbValx; i++) {
const leDiv = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(leDiv, { position: 'absolute' })
tabTextListe = (prodQuot[0] === 'produit') ? ['|', '0'] : ['|', '||', '0']
const elt = j3pAffiche(leDiv, '', '#1#', { className: 'liste1', liste1: { texte: tabTextListe, correction: '0' } })
leDiv.style.left = (largeurSignede + i * (L - largeurSignede) / (nbValx + 1) - 8) + 'px'
leDiv.style.top = (h - h / (2 * expresTab.length + 4) - leDiv.getBoundingClientRect().height / 2) + 'px'
listeInput.push(elt.selectList[0])
}
j3pFocus(zoneInput.signeProd[0])
} else {
// on donne les signes et les zéros ou double barres(la réponse)
if (prodQuot[0] === 'produit') {
// tout d’abord les zéros'
for (let i = 1; i <= nbValx; i++) {
const zeroProd = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(zeroProd, { position: 'absolute' })
j3pAffiche(zeroProd, '', '0', { style: { color: macouleurTxt } })
zeroProd.style.left = largeurSignede + (i) * (L - largeurSignede) / (tabZeroOrdonne.length + 1) - zeroProd.getBoundingClientRect().width / 2 + 'px'
zeroProd.style.top = (expresTab.length + 1.5) * h / (expresTab.length + 2) - zeroProd.getBoundingClientRect().height / 2 + 'px'
}
} else {
// tout d’abord les zéros ou double barres'
for (let i = 1; i <= tabZeroOrdonne.length; i++) {
if (doubleBarre[i - 1]) {
// on met une double barre
j3pCreeSegment(svg, {
x1: posValZero[i] + 3,
y1: (expresTab.length + 1) * h / (expresTab.length + 2),
x2: posValZero[i] + 3,
y2: h,
couleur: macouleurTxt,
epaisseur: 1
})
j3pCreeSegment(svg, {
x1: posValZero[i],
y1: (expresTab.length + 1) * h / (expresTab.length + 2),
x2: posValZero[i],
y2: h,
couleur: macouleurTxt,
epaisseur: 1
})
} else {
const zeroQuotient = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(zeroQuotient, { position: 'absolute' })
j3pAffiche(zeroQuotient, '', '0', { style: { color: macouleurTxt } })
zeroQuotient.style.left = largeurSignede + (i) * (L - largeurSignede) / (tabZeroOrdonne.length + 1) - zeroQuotient.getBoundingClientRect().width / 2 + 'px'
zeroQuotient.style.top = (expresTab.length + 1.5) * h / (expresTab.length + 2) - zeroQuotient.getBoundingClientRect().height / 2 + 'px'
}
}
}
// maintenant on gère les signes
for (let i = 1; i <= nbValx + 1; i++) {
const signeProd = j3pAddElt(nomdiv, 'div', '', { style: objStyleCouleur.style })
j3pStyle(signeProd, { position: 'absolute' })
if (nbSignesMoinsTab[i] % 2 === 0) {
j3pAffiche(signeProd, '', '$+$', { style: { color: macouleurTxt } })
} else {
j3pAffiche(signeProd, '', '$-$', { style: { color: macouleurTxt } })
}
signeProd.style.left = (largeurSignede + (2 * i - 1) * (L - largeurSignede) / (2 * nbValx + 2) - signeProd.getBoundingClientRect().width / 2) + 'px'
signeProd.style.top = ((expresTab.length + 1.5) * h / (expresTab.length + 2) - signeProd.getBoundingClientRect().height / 2) + 'px'
}
}
}
// affichage d’une palette pour compléter les zones de la première ligne
let zonePalette
if (!zeroTab[0]) {
// ceci ne se fait que si la première ligne n’est pas complétée
zonePalette = j3pAddElt(obj, 'div')
// palette MQ :
j3pPaletteMathquill(zonePalette, zoneInput.ligneX[0], {
liste: listeBoutons,
style: { paddingTop: '10px' }
})
for (let i = 0; i <= nbValx + 1; i++) {
$(zoneInput.ligneX[i]).focusin(function () {
ecoute(this.num)
})
}
for (let i = 1; i <= (nbValx + 1) * (expresTab.length + 1); i++) {
$(zoneInput.signeFacts[i - 1]).focusin(function () {
j3pEmpty(zonePalette)
})
}
for (let i = 1; i <= (nbValx) * (expresTab.length + 1); i++) {
$(listeInput[i - 1]).focusin(function () {
j3pEmpty(zonePalette)
})
}
}
function ecoute (num) {
if (zoneInput.ligneX[num].className.includes('mq-editable-field')) {
j3pEmpty(zonePalette)
j3pPaletteMathquill(zonePalette, zoneInput.ligneX[num], {
liste: listeBoutons,
style: { paddingTop: '10px' }
})
}
}
return { input: zoneInput, liste: listeInput, palette: zonePalette }
} // fin fctsEtudeTabSignesProduit
// fonction privées, mises après car une fois importé dans sesaparcours ça doit être après les exports des fcts ci-dessus
/**
* @private
* @param {string} fctAffine
* @param {string} [variable=x]
* @returns {number[]} tableau de 2 éléments : coefA et coefB de l’écriture ax+b au format latex
*/
function extraireCoefsFctaffine (fctAffine, variable) {
const nomVar = (variable) || 'x'
let coefA, coefB
if ((fctAffine[0] === '(') && (fctAffine[fctAffine.length - 1] === ')')) {
fctAffine = fctAffine.substring(1, fctAffine.length - 1)
}
if (fctAffine === '') {
coefA = '0'
coefB = '1'
} else if (fctAffine === '-') {
coefA = '0'
coefB = '-1'
} else if ((!fctAffine.substring(1).includes('+')) && (!fctAffine.substring(1).includes('-'))) {
// c’est qu’il n’y a qu’un seul terme dans la fonction affine
if (!fctAffine.includes(nomVar)) {
// c’est une fonction constante
coefA = '0'
coefB = fctAffine
} else {
// c’est une fonction linéaire
coefA = fctAffine.substring(0, fctAffine.length - nomVar.length)
if (coefA === '') {
coefA = '1'
} else if (coefA === '-') {
coefA = '-1'
}
coefB = '0'
}
} else {
const posDecoup = Math.max(fctAffine.lastIndexOf('+'), fctAffine.lastIndexOf('-'))// cela me donne la position du signe entre b et ax
if (posDecoup <= 0) {
// c’est que c’est de la forme ax donc b vaut 0'
coefA = fctAffine.substring(0, fctAffine.length - 1)
if (coefA === '') {
coefA = '1'
} else if (coefA === '-') {
coefA = '-1'
}
coefB = '0'
} else {
if (fctAffine.substring(fctAffine.length - nomVar.length) === nomVar) {
// fctAffine est écrit sous la forme b+ax
coefB = fctAffine.substring(0, posDecoup)
if (fctAffine[posDecoup] === '-') {
coefA = fctAffine.substring(posDecoup, fctAffine.length - nomVar.length)
} else {
coefA = fctAffine.substring(posDecoup + 1, fctAffine.length - nomVar.length)
}
if (coefA === '') {
coefA = '1'
} else if (coefA === '-') {
coefA = '-1'
}
} else {
// fctAffine est écrit sous la forme ax+b
coefA = fctAffine.substring(0, posDecoup - nomVar.length)
if (fctAffine[posDecoup] === '-') {
coefB = fctAffine.substring(posDecoup)
} else {
coefB = fctAffine.substring(posDecoup + 1)
}
if (coefA === '') {
coefA = '1'
} else if (coefA === '-') {
coefA = '-1'
}
}
}
}
// si on avait a*x au lieu de ax, il faut se débarrasser du signe de multiplication
if (coefA.charAt(coefA.length - 1) === '*') {
coefA = coefA.substring(0, coefA.length - 1)
}
coefA = fractionLatex(coefA)
coefB = fractionLatex(coefB)
return [coefA, coefB]
} // fin extraireCoefsFctaffine
/**
* @private
* @param texte
* @returns {*}
*/
function fractionLatex (texte) {
// texte est une expression dans laquelle on peut retrouver une fraction sous la forme .../... ou (.../...)
// cette fonction renvoie le texte au format latex
const fractReg1 = /\([0-9+*a-z-]+\)\/\([0-9+*a-z-]+\)/ig // on cherche (...)/(...)
const fractReg2 = /\([0-9*a-z]+\/[0-9*a-z]+\)/ig// on cherche (.../...)
// @todo ces deux regex sont très laxistes, ça matche du (42+*55)/(x3-*2), et la 2e n’a pas le signe - autorisé
let texteLatex = texte
let i, num, den, tabNumDen
if (fractReg1.test(texteLatex)) {
// l’expression (...)/(...) est présente'
const tabFract1 = texteLatex.match(fractReg1)
for (i = 0; i < tabFract1.length; i++) {
tabNumDen = tabFract1[i].split('/')
if (tabNumDen[0][tabNumDen[0].length - 1] === ')') {
num = tabNumDen[0].substring(1, tabNumDen[0].length - 1)
} else {
num = tabNumDen[0].substring(1, tabNumDen[0].length)
}
if (tabNumDen[1][0] === '(') {
den = tabNumDen[1].substring(1, tabNumDen[1].length - 1)
} else {
den = tabNumDen[1].substring(0, tabNumDen[1].length - 1)
}
texteLatex = texteLatex.replace(tabFract1[i], '\\frac{' + num + '}{' + den + '}')
}
}
if (fractReg2.test(texteLatex)) {
// l’expression (...)/(...) est présente'
const tabFract2 = texteLatex.match(fractReg2)
for (i = 0; i < tabFract2.length; i++) {
tabNumDen = tabFract2[i].split('/')
num = tabNumDen[0].substring(1)
den = tabNumDen[1].substring(0, tabNumDen[1].length - 1)
texteLatex = texteLatex.replace(tabFract2[i], '\\frac{' + num + '}{' + den + '}')
}
}
if (String(texteLatex).includes('/')) {
// il reste encore une fraction à gérer à la main, sans doute du style .../...
const signeReg = /[+()*-]/ig
const tabSansSigne = texteLatex.split(signeReg)
for (i = 0; i < tabSansSigne.length; i++) {
if (tabSansSigne[i].includes('/')) {
const newTab = tabSansSigne[i].split('/')
texteLatex = texteLatex.replace(newTab[0] + '/' + newTab[1], '\\frac{' + newTab[0] + '}{' + newTab[1] + '}')
}
}
}
return texteLatex
} // fin fractionLatex
/**
* on a un polynôme de degré 3 a1x^3+b1x^2+c1x+d1
* le polynôme dérivée du premier est ax^2+bx+c. Le discriminant de ce trinôme est delta
* on cherche l’image de (-b-sqrt{delta})/(2a) puis de (-b+sqrt{delta})/(2a) par le polynôme de degré 3
* l’image de (-b-sqrt{delta})/(2a) est (-((a1b^3+3a1b*delta)/(4a^2)-(b1b^2+b1*delta)/(2a)+c1b-2d1a)+(-3b^2a1-delta*a1)/(4a^2)+(bb1)/a-c1)sqrt{delta})/(2a)
* l’image de (-b+sqrt{delta})/(2a) est (-((a1b^3+3a1b*delta)/(4a^2)-(b1b^2+b1*delta)/(2a)+c1b-2d1a)+(3b^2a1+delta*a1)/(4a^2)-(bb1)/a+c1)sqrt{delta})/(2a)
* j'écrirai l’image sous la forme (-bSol+/-sqrt{delta_sol})/(2a) à l’aide de la fonction simplificationRacineTrinome
* @private
* @param a1
* @param b1
* @param c1
* @param d1
* @param a
* @param b
* @param c
* @param delta
* @returns {string[]}
*/
function imagePolDegre3 (a1, b1, c1, d1, a, b, c, delta) {
const quatreACarre = fctsEtudeProduitNbs('4', fctsEtudeProduitNbs(a, a))
const a1bCubePlus3a1bdelta = fctsEtudeSommeNbs(fctsEtudeProduitNbs(a1, fctsEtudeProduitNbs(b, fctsEtudeProduitNbs(b, b))), fctsEtudeProduitNbs('3', fctsEtudeProduitNbs(a1, fctsEtudeProduitNbs(b, delta))))
const b1bCarrePlusb1Delta = fctsEtudeSommeNbs(fctsEtudeProduitNbs(b1, fctsEtudeProduitNbs(b, b)), fctsEtudeProduitNbs(b1, delta))
const c1bmoins2d1a = fctsEtudeSommeNbs(fctsEtudeProduitNbs(c1, b), fctsEtudeProduitNbs('-1', fctsEtudeProduitNbs('2', fctsEtudeProduitNbs(d1, a))))
const bSol = fctsEtudeSommeNbs(fctsEtudeDivisionNbs(a1bCubePlus3a1bdelta, quatreACarre), fctsEtudeSommeNbs(fctsEtudeProduitNbs('-1', fctsEtudeDivisionNbs(b1bCarrePlusb1Delta, fctsEtudeProduitNbs('2', a))), c1bmoins2d1a))
const troisbcarrea1plusdeltaa1 = fctsEtudeSommeNbs(fctsEtudeProduitNbs('3', fctsEtudeProduitNbs(b, fctsEtudeProduitNbs(b, a1))), fctsEtudeProduitNbs(delta, a1))
const bb1moinsac1 = fctsEtudeSommeNbs(fctsEtudeProduitNbs(b, b1), fctsEtudeProduitNbs('-1', fctsEtudeProduitNbs(a, c1)))
const coefDevantdelta = fctsEtudeSommeNbs(fctsEtudeDivisionNbs(troisbcarrea1plusdeltaa1, quatreACarre), fctsEtudeProduitNbs('-1', fctsEtudeDivisionNbs(bb1moinsac1, a)))
const image1 = simplificationRacineTrinome(bSol, '-', delta, a, coefDevantdelta)
const image2 = simplificationRacineTrinome(bSol, '+', delta, a, coefDevantdelta)
return [image1, image2]
} // imagePolDegre3
/**
* Retourne la valeur minimale de tab
* @private
* @param {Array<string|number>} tab tableau de valeurs (au format nombre ou latex)
* @returns {{min: string|number, indice: number}}
*/
function minTab (tab) {
let min, indice
for (const [i, valeur] of tab.entries()) {
if (i === 0) {
indice = i
min = valeur
} else if (fctsEtudeCalculValeur(valeur) < fctsEtudeCalculValeur(min)) {
indice = i
min = valeur
}
}
return { min, indice }
} // minTab
export function fctsEtudeSigne (nombre) {
// pour gérer le cas ou c’est une fraction
const tab = fctsEtudeExtraireNumDen(nombre)
if (tab[0]) { // c une fraction
if (tab[3] === '+') {
return '+' + nombre
} else {
// on extrait la valeur absolue de num et den pour construire -a/b
const num = Math.abs(tab[1])
const den = Math.abs(tab[2])
return '-\\frac{' + num + '}{' + den + '}'
}
} else {
if (nombre === 0) {
return 0
}
if (nombre > 0) {
return '+' + nombre
} else {
return '-' + Math.abs(nombre)
}
}
} // fin fctsEtudeSigne
/**
* @private
* @param b
* @param plusmoins
* @param delta
* @param a
* @param coefDelta
* @returns {string}
*/
function simplificationRacineTrinome (b, plusmoins, delta, a, coefDelta) {
// b, delta et a sont les coef présents dans le calcul (-b-sqrt{delta})/(2*a) ou (-b+sqrt{delta})/(2*a)
/// /plusmoins vaut "+" ou "-"
// cette fonction renvoie le résultat au format latex
// coefDelta est un argument optionnel. Il n’est pas utile dans le cas des racines d’un trinôme mais pour certains calculs de la forme (-b-coefDelta*sqrt{delta})/(2*a), c’est bien utile
const deno = fctsEtudeProduitNbs('2', a)
let nume
let quotient// réponse renvoyée
const sqrtDelta = simplifieRadical(delta)
if (!sqrtDelta.includes('sqrt')) {
if (plusmoins === '+') {
nume = fctsEtudeSommeNbs(fctsEtudeProduitNbs('-1', b), sqrtDelta)
} else {
nume = fctsEtudeSommeNbs(fctsEtudeProduitNbs('-1', b), fctsEtudeProduitNbs('-1', sqrtDelta))
}
quotient = fctsEtudeDivisionNbs(nume, fctsEtudeProduitNbs(a, '2'))
} else {
let coefDevantSqrt
if (sqrtDelta.indexOf('\\sqrt') === 0) {
coefDevantSqrt = '1'
} else if ((sqrtDelta.indexOf('\\sqrt') === 1) && (sqrtDelta.charAt(0) === '-')) {
coefDevantSqrt = '-1'
} else {
coefDevantSqrt = sqrtDelta.substring(0, sqrtDelta.indexOf('\\sqrt'))
}
if (coefDelta !== undefined) {
coefDevantSqrt = fctsEtudeProduitNbs(coefDevantSqrt, coefDelta)
}
const fracB = fctsEtudeDivisionNbs(fctsEtudeProduitNbs('-1', b), deno)// c’est -b/(2a) simplifié
const fracSqrt = fctsEtudeDivisionNbs(coefDevantSqrt, deno)// dans sqrt{Delta}/(2a), c/(2a) où c est tel que sqrt{delta}=c*sqrt{b}
let denFracB = 1
let numFracB
if (String(fracB).includes('\\frac')) {
denFracB = Number(fracB.substring(fracB.lastIndexOf('{') + 1, fracB.length - 1))
numFracB = Number(fracB.substring(fracB.indexOf('{') + 1, fracB.indexOf('}')))
} else {
numFracB = Number(fracB)
}
let denFracSqrt = 1
let numFracSqrt
if (String(fracSqrt).includes('\\frac')) {
denFracSqrt = Number(fracSqrt.substring(fracSqrt.lastIndexOf('{') + 1, fracSqrt.length - 1))
numFracSqrt = Number(fracSqrt.substring(fracSqrt.indexOf('{') + 1, fracSqrt.indexOf('}')))
} else {
numFracSqrt = Number(fracSqrt)
}
const pgcdDen = j3pPGCD(denFracB, denFracSqrt)
const denCommun = denFracB * denFracSqrt / pgcdDen
const newNumFracB = numFracB * denCommun / denFracB
const newNumFracSqrt = numFracSqrt * denCommun / denFracSqrt
if (plusmoins === '+') {
if (String(newNumFracSqrt).charAt(0) === '-') {
if (String(newNumFracSqrt) === '-1') {
quotient = String(newNumFracB) + '-' + sqrtDelta.substring(sqrtDelta.indexOf('\\sqrt'))
} else {
quotient = String(newNumFracB) + String(newNumFracSqrt) + sqrtDelta.substring(sqrtDelta.indexOf('\\sqrt'))
}
} else {
if (String(newNumFracSqrt) === '1') {
quotient = String(newNumFracB) + '+' + sqrtDelta.substring(sqrtDelta.indexOf('\\sqrt'))
} else {
quotient = String(newNumFracB) + '+' + String(newNumFracSqrt) + sqrtDelta.substring(sqrtDelta.indexOf('\\sqrt'))
}
}
} else {
if (String(newNumFracSqrt).charAt(0) === '-') {
if (String(newNumFracSqrt) === '-1') {
quotient = String(newNumFracB) + '+' + sqrtDelta.substring(sqrtDelta.indexOf('\\sqrt'))
} else {
quotient = String(newNumFracB) + '+' + String(newNumFracSqrt).substring(1) + sqrtDelta.substring(sqrtDelta.indexOf('\\sqrt'))
}
} else {
if (String(newNumFracSqrt) === '1') {
quotient = String(newNumFracB) + '-' + sqrtDelta.substring(sqrtDelta.indexOf('\\sqrt'))
} else {
quotient = String(newNumFracB) + '-' + String(newNumFracSqrt) + sqrtDelta.substring(sqrtDelta.indexOf('\\sqrt'))
}
}
}
if (denCommun > 1) {
// la réponse simplifiée s'écrit sous la forme (a+b\sqrt{c})/d
// quotient déterminé précédemment correspond juste à a+b\sqrt{c}
quotient = '\\frac{' + quotient + '}{' + String(denCommun) + '}'
}
}
return quotient
} // fin simplificationRacineTrinome
/**
* @private
* @param nb
* @returns {string}
*/
function simplifieRadical (nb) {
// nb est un nombre au format string
// il est être entier ou sous la forme \\{frac{...}{...}, mais il faut que la fraction soit simplifiée
// la fonction renvoie le nombre sqrt{nb} sous forme simplifiée
function simplifieEntier (entier) {
// la fonction simplifie le nb sqrt{entier} où entier est bien sûr strictement positif
let sqrtEntierSimplifie
if (Math.abs(Math.sqrt(Number(entier)) - Math.round(Math.sqrt(Number(entier)))) < Math.pow(10, -10)) {
// sqrt{entier} est un nombre entier
sqrtEntierSimplifie = String(Math.round(Math.sqrt(Number(entier))))
} else {
let a = Math.floor(Math.sqrt(Number(entier)))
let radical = Number(entier)
let nbAvantRadical = 1
while (a > 1) {
if (radical % Math.pow(a, 2) === 0) {
radical = Math.round(radical / Math.pow(a, 2))
nbAvantRadical = Math.round(nbAvantRadical * a)
}
a--
}
sqrtEntierSimplifie = String(nbAvantRadical) + '\\sqrt{' + String(radical) + '}'
}
return sqrtEntierSimplifie
}
let nbSimplifie
const tabNb = fctsEtudeExtraireNumDen(nb)// ce tableau me donne les infos sur nb (s’il est fractionnaire, son num et den, son signe)
let sqrtNum, sqrtDen
if (tabNb[0]) {
// c’est un nb fractionnaire \\frac{tabNb[1]}{tabNb[2]}
// On fait alors en sorte d’avoir un résultat sans radical au dénominateur
if (simplifieEntier(String(tabNb[2])).includes('sqrt')) {
// c’est que la racine carrée du dénominateur n’est pas un entier.
sqrtNum = simplifieEntier(String(Number(tabNb[1]) * Number(tabNb[2])))
sqrtDen = tabNb[2]
let nbDevantRadicalNum = '1'
if (sqrtNum.indexOf('\\sqrt') > 0) {
// on a un nombre devant le radical
nbDevantRadicalNum = sqrtNum.substring(0, sqrtNum.indexOf('\\sqrt'))
}
// on a un nombre de la forme a*sqrt{b}/d
// il faut encore voir si on peut simplifier a/d
const fracSimplifiee = fctsEtudeExtraireNumDen(fctsEtudeDivisionNbs(nbDevantRadicalNum, sqrtDen))
nbSimplifie = '\\frac{' + fracSimplifiee[1] + '}{' + fracSimplifiee[2] + '}' + sqrtNum.substring(sqrtNum.indexOf('\\sqrt'))
} else {
sqrtNum = simplifieEntier(String(tabNb[1]))
sqrtDen = simplifieEntier(String(tabNb[2]))
nbSimplifie = '\\frac{' + sqrtNum + '}{' + sqrtDen + '}'
}
} else {
// c’est un entier (positif)
nbSimplifie = simplifieEntier(String(nb))
}
return nbSimplifie
} // fin simplifieRadical
/**
* @private
* @param tabZeros
* @param domaine
* @param isDebug
* @returns {string[]}
*/
function tableauIntervalles (tabZeros, domaine, isDebug) {
// cette fonction renvoie sous forme d’un tableau les intervalles sur lesquels on donnera le signe de la fonction
// tabZeros est le tableau contenant les zéros de la fonction
// domaine est un tableau. Le domaine est sous la forme ["-infini",a,b,...,"+infini"]
// isDebug permet d’afficher des infos de debuggage en console
const newTabZeros = fctsEtudeOrdonneTabSansDoublon(tabZeros, domaine)
if (isDebug) console.debug('LA avec tabZeros=', tabZeros, ' et domaine=', domaine, ' et newTabZeros=', newTabZeros)
let i
let tabIntervalles = []
tabIntervalles[0] = domaine[0]
if (domaine[1] === '+infini') {
for (i = 0; i < newTabZeros.length; i++) {
tabIntervalles.push(newTabZeros[i])
}
tabIntervalles.push('+infini')
} else {
// nombre d’intervalles présents dans la domaine de définition (sachant que pour un intervalle, j’ai besoin de 4 éléments dans le tableau domaine
const nbIntervalleDomaine = Math.floor((domaine.length - 1) / 4)
tabIntervalles.push(domaine[1])
for (i = 1; i <= nbIntervalleDomaine; i++) {
tabIntervalles.push(domaine[4 * i])
if (domaine[4 * i + 1] !== '+infini') {
tabIntervalles.push(domaine[4 * i + 1])
}
}
tabIntervalles = tabIntervalles.concat(newTabZeros)
if (isDebug) console.debug('tabIntervalles1=', tabIntervalles)
tabIntervalles.push(domaine[nbIntervalleDomaine * 4 + 1])
if (isDebug) console.debug('tabIntervalles2=', tabIntervalles)
tabIntervalles = fctsEtudeOrdonneTabSansDoublon(tabIntervalles)
if (isDebug) console.debug('tabIntervalles3=', tabIntervalles)
}
return tabIntervalles
} // fin tableauIntervalles
/**
* Retourne true si la différence entre les deux est < epsilon (comparaison avec fctsEtudeCalculValeur pour les string, (+|-)(infini|Infinity) est géré)
* @private
* @param {number|string} nb1
* @param nb2
* @returns {boolean}
*/
function isAlmostEqual (nb1, nb2) {
const number1 = Number(nb1)
const number2 = Number(nb2)
if (Number.isFinite(number1) && Number.isFinite(number2)) return Math.abs(number1 - number2) < epsilon
if (/(infini|Infinity)/.test(nb1)) {
// y’a un infini, faut que l’autre soit le même
const signe = nb1.charAt(0)
const reIdem = new RegExp(`\\${signe}(infini|Infinity)`)
return reIdem.test(nb2)
}
if (/(infini|Infinity)/.test(nb2)) return false // nb1 l’était pas
if (typeof nb1 !== 'string' || typeof nb2 !== 'string') return nb1 === nb2
// deux strings on passe par fctsEtudeCalculValeur
return Math.abs(fctsEtudeCalculValeur(nb1) - fctsEtudeCalculValeur(nb2)) < epsilon
}
/**
* Retourne true si distance(nb, 0) < epsilon
* @private
* @param {number|string} nb
* @returns {boolean}
*/
function isAlmostZero (nb) {
if (typeof nb === 'string') nb = Number(nb)
if (!Number.isFinite(nb)) return false
return Math.abs(nb) < epsilon
}