kernel/CalcR.js

/*
 * Created by yvesb on 12/12/2016.
 */
/*
 * MathGraph32 Javascript : Software for animating online dynamic mathematics figures
 * https://www.mathgraph32.org/
 * @version 5.1.2
 * @Author Yves Biton (yves.biton@sesamath.net)
 * @License: GNU AGPLv3 https://www.gnu.org/licenses/agpl-3.0.html
 */

import CCbGlob from './CCbGlob'
import Pointeur from '../types/Pointeur'
import Ope from '../types/Ope'
import Opef from '../types/Opef'
import Opef3v from '../types/Opef3v'
import Opef4v from '../types/Opef4v'
import Opef5v from '../types/Opef5v'
import Fonte from '../types/Fonte'
import NatCal from '../types/NatCal'
import { ConvDegRad, ConvRadDeg, uniteAngleRadian } from '../kernel/kernel'
import CAppelFonction from '../objets/CAppelFonctionBase'
import CAppelFonctionNVar from '../objets/CAppelFonctionNVar'
import CTermMat from '../objets/CTermMat'
import CConstante from '../objets/CConstanteBase'
import CFonction from '../objets/CFonction'
import CFonction2Var from '../objets/CFonction2Var'
import CFonction3Var from '../objets/CFonction3Var'
import CIntegraleDansFormule from '../objets/CIntegraleDansFormule'
import CPrimitive from '../objets/CPrimitive'
import CMoinsUnaire from '../objets/CMoinsUnaire'
import CVariableFormelle from '../objets/CVariableFormelle'
import CSommeDansFormule from '../objets/CSommeDansFormule'
import CResultatValeur from '../objets/CResultatValeur'
import COp from '../objets/COperationBase'
import CProduitDansFormule from '../objets/CProduitDansFormule'
import CPuissance from '../objets/CPuissance'
import CCbNull from '../objets/CCbNull'
import CAppelFonctionInterne from '../objets/CAppelFonctionInterne'
import CAppelFonctionInterneNVar from 'src/objets/CAppelFonctionInterneNVar'
import CFonctionMat from 'src/objets/CFonctionMat'
import { getLeft, getRight } from 'src/objets/CCb'

/**
 * Fonction statique récursive créant un arbre binaire de calcul à partir de la chaîne
 * de caractères ch représentant le calcul, la chaîne étant interprétée de pdebut à pfin.
 * nomsVariablesFormelles est un array dont les éléments représentent les noms éventuels des variables
 * formelles quand le calcul évalué représente une fonction.
 * Le calcul renvoie un résultat réel.
 * Fonction appelée creeCalculBase dans la version Java.
 * @param {string} ch  La chaîne décrivant le  calcul.
 * @param {CListeObjets} ptListe  La liste propriétaire du calcul à créer.
 * @param {number} pdebut  L'indice du début de l'interprétationde la chaîne
 * @param {number} pfin  : L'indice de la fin de l'interprétationde la chaîne
 * @param {string[]|null} nomsVariablesFormelles  Un tableau donnant les noms des variables
 * formelles dans le cas où le calcul est utilisé à l'intérieur d'une focntion de
 * une ou plusieurs variables.
 * @param {CListeObjets} listeSource  La liste source des objets utilisables (ne diffère de ptListe que pour les exercices de construction)
 * @returns {CCb}
 */
function ccb (ch, ptListe, pdebut, pfin, nomsVariablesFormelles, listeSource) {
  if (arguments.length <= 5) listeSource = ptListe
  try {
    // Version 7.0 : On autorise un calcul réel du type deter(A) ou nbRow(A) ou nbCol(A) où A est le nom d'une matrice

    // On regarde d'abord si c'est plausible avec une regex générique avant de faire le traitement (car c'est rarement utile)
    // (Attention à revoir cette regex si un nom de fonction pouvait contenir autre chose que des lettres/chiffres/underscore,
    // donc pas d'accent, c'est mis en remarque dans les fichiers de traduction)

    // on cherche :
    // ^ début de la chaîne
    // (deter|nbrow|nbcol) on capture deter ou nbrow ou nbcol (sera dans chunks[1])
    // \( suivi d'une parenthèse ouvrante (antislash car c'est pas une capture)
    // [^() */+-]+ suivi d'une capture d'un ou plusieurs caractères, tous sauf () ou opération ou espace, (sera dans chunks[2])
    // \) suivi d'une parenthèse fermante
    // $ fin de la chaine
    const chunks = /^(deter|nbrow|nbcol)\(([^() */+-]+)\)$/.exec(ch)
    if (chunks) {
      // c'est un calcul matriciel, reste à vérifier le nom de la matrice
      // cf https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec
      // on récupère chunks[1] dans nomFn et chunks[2] dans nomMat
      const [, nomFn, nomMat] = chunks
      const mat = ptListe.pointeurMat(nomMat)
      if (mat) {
        // mat est bien le nom d'une matrice
        const idOpef = nomFn === 'deter'
          ? Opef.Deter
          : nomFn === 'nbrow'
            ? Opef.Nbrow
            : Opef.Nbcol
        return new CFonctionMat(ptListe, idOpef, new CResultatValeur(ptListe, mat))
      }
      // else c'était bien une fct de matrice mais avec un nom qui n'existe pas, on laisse la suite du traitement gérer ça
    }

    // si on est toujours là, c'était pas un calcul matriciel (ou pas un nom de matrice connu)

    // Spécial JavaScript car pas d'erreur levée par charAt si pdebut > pFin
    if (pdebut > pfin) throw new Error('Bornes invalides')
    // Contiendra dans x l'indice du caractère précédent et dans y l'opérateur trouvé
    const info = { x: 0, y: 0 }
    const nomF = new Pointeur(null)
    let ptr, car, op, indicePremiereVirg, operande1, operande2, indiceParF, indiceDeuxiemeVirg
    let indiceTroisiemeVirg, nomVariableSommation, nomVariableFormelle2
    const ptVa = new Pointeur(null)
    const parametreTrouve = new Pointeur(0)
    const nombreVar = new Pointeur(0)
    // On élimine les blancs de début
    car = ch.charAt(pdebut)
    while ((car === ' ') && (pdebut < pfin)) {
      pdebut++
      car = ch.charAt(pdebut)
    }
    // Puis de fin
    car = ch.charAt(pfin)
    while ((car === ' ') && (pdebut < pfin)) {
      pfin--
      car = ch.charAt(pfin)
    }
    // On recherche d'abord les opérateurs de ou logique
    ptr = CCbGlob.premierOperateurLogique('|', ch, pdebut, pfin, info)
    if (ptr !== -1) {
      return new COp(
        ptListe,
        CalcR.ccb(ch, ptListe, pdebut, info.x, nomsVariablesFormelles, listeSource),
        CalcR.ccb(ch, ptListe, ptr + 1, pfin, nomsVariablesFormelles, listeSource),
        info.y
      )
    }
    // On recherche ensuite les opérateurs de et logique
    ptr = CCbGlob.premierOperateurLogique('&', ch, pdebut, pfin, info)
    if (ptr !== -1) {
      return new COp(
        ptListe,
        CalcR.ccb(ch, ptListe, pdebut, info.x, nomsVariablesFormelles, listeSource),
        CalcR.ccb(ch, ptListe, ptr + 1, pfin, nomsVariablesFormelles, listeSource),
        info.y
      )
    }

    // On recherche d'abord les opérateurs d'inégalités
    ptr = CCbGlob.premiereInegalite(ch, pdebut, pfin, info)
    if (ptr !== -1) {
      return new COp(
        ptListe,
        CalcR.ccb(ch, ptListe, pdebut, info.x, nomsVariablesFormelles, listeSource),
        CalcR.ccb(ch, ptListe, ptr + 1, pfin, nomsVariablesFormelles, listeSource),
        info.y
      )
    }

    // On recherche ensuite les operateurs d'addition ou soustraction
    ptr = CCbGlob.premiereSomme(ch, pdebut, pfin)
    if (ptr !== -1) {
      // On regarde d'abord si la chaine commence par un signe - ou
      // un signe +
      car = ch.charAt(ptr)
      if (car === '+') { op = Ope.Plus } else { op = Ope.Moin }
      if (ptr === pdebut) {
        car = ch.charAt(ptr)
        if (car === '+') {
          return CalcR.ccb(ch, ptListe, pdebut + 1, pfin, nomsVariablesFormelles, listeSource)
        }
        return new CMoinsUnaire(ptListe, CalcR.ccb(ch, ptListe, pdebut + 1, pfin, nomsVariablesFormelles, listeSource))
      }
      return new COp(
        ptListe,
        CalcR.ccb(ch, ptListe, pdebut, ptr - 1, nomsVariablesFormelles, listeSource),
        CalcR.ccb(ch, ptListe, ptr + 1, pfin, nomsVariablesFormelles, listeSource),
        op
      )
    }

    // if (ptr !== NULL)
    ptr = CCbGlob.premierProduit(ch, pdebut, pfin)
    if (ptr !== -1) {
      car = ch.charAt(ptr)
      if (car === '*') { op = Ope.Mult } else { op = Ope.Divi }
      return new COp(
        ptListe,
        CalcR.ccb(ch, ptListe, pdebut, ptr - 1, nomsVariablesFormelles, listeSource),
        CalcR.ccb(ch, ptListe, ptr + 1, pfin, nomsVariablesFormelles, listeSource),
        op
      )
    }

    // Le calcul ne contient ni addition, ni différence, ni produit
    // ni quotient}
    // Attention : par rapport à la version C++ 1.8, autorisation
    // du symbole ² d'élévation au carré.
    ptr = CCbGlob.premierePuissance(ch, pdebut, pfin)
    if (ptr !== -1) {
      if (ch.charAt(ptr) === '²') {
        return new CPuissance(
          ptListe,
          CalcR.ccb(ch, ptListe, pdebut, ptr - 1, nomsVariablesFormelles, listeSource),
          new CConstante(ptListe, 2)
        )
      }
      return new CPuissance(
        ptListe,
        CalcR.ccb(ch, ptListe, pdebut, ptr - 1, nomsVariablesFormelles, listeSource),
        CalcR.ccb(ch, ptListe, ptr + 1, pfin, nomsVariablesFormelles, listeSource)
      )
    }

    // On regarde si la chaine commence par le nom d'une fonction
    // ou d'une valeur déjà définie
    const longNom = listeSource.commenceParNomFonctionOuValeurOuParametre(ch.substring(pdebut), nomsVariablesFormelles, ptVa, nomF, parametreTrouve, nombreVar)
    if (longNom > 0) {
      if (parametreTrouve.getValue() !== -1) {
        return new CVariableFormelle(ptListe, parametreTrouve.getValue())
      }
      if (ptVa.getValue() === null) {
        // Modification version 3.0. Il faut regarder si la fonction
        // est une fonction de plusieurs paramètres ou non
        // Ajouté version 4.7.4.1
        if (CCbGlob.parentheseFermante(ch, pdebut + longNom) === -1) throw new Error()

        if (nombreVar.getValue() === 1) {
          return new CFonction(
            ptListe,
            nomF.getValue(),
            CalcR.ccb(ch, ptListe, pdebut + longNom, pfin, nomsVariablesFormelles, listeSource)
          )
        }

        if (nombreVar.getValue() === 2) {
          indicePremiereVirg = CCbGlob.premiereVirgule(ch, pdebut + longNom + 1)
          operande1 = CalcR.ccb(ch, ptListe, pdebut + longNom + 1, indicePremiereVirg - 1, nomsVariablesFormelles, listeSource)
          indiceParF = CCbGlob.parentheseFermanteApresVirgule(ch, indicePremiereVirg + 1)
          // A revoir plus tard si on crée des fonctions de trois
          // variables ou plus
          operande2 = CalcR.ccb(ch, ptListe, indicePremiereVirg + 1, indiceParF - 1, nomsVariablesFormelles, listeSource)
          return new CFonction2Var(ptListe, nomF.getValue(), operande1, operande2)
        }
        if (nombreVar.getValue() === 3) { // 3 variables
          indicePremiereVirg = CCbGlob.premiereVirgule(ch, pdebut + longNom + 1)
          operande1 = CalcR.ccb(ch, ptListe, pdebut + longNom + 1, indicePremiereVirg - 1, nomsVariablesFormelles, listeSource)
          indiceDeuxiemeVirg = CCbGlob.premiereVirgule(ch, indicePremiereVirg + 1)
          operande2 = CalcR.ccb(ch, ptListe, indicePremiereVirg + 1, indiceDeuxiemeVirg - 1, nomsVariablesFormelles, listeSource)
          indiceParF = CCbGlob.parentheseFermanteApresVirgule(ch, indiceDeuxiemeVirg + 1)
          // A revoir plus tard si on crée des fonctions de trois
          // variables ou plus
          const operande3 = CalcR.ccb(
            ch,
            ptListe,
            indiceDeuxiemeVirg + 1,
            indiceParF - 1,
            nomsVariablesFormelles,
            listeSource
          )
          return new CFonction3Var(ptListe, nomF.getValue(), operande1, operande2, operande3)
        }
        if (nombreVar.getValue() === 4) {
          // Seules fonctions à 4 variables : Cas d'une intégrale ou calcul de primitive prise entre deux bornes
          indicePremiereVirg = CCbGlob.premiereVirgule(ch, pdebut + longNom + 1)
          indiceDeuxiemeVirg = CCbGlob.premiereVirgule(ch, indicePremiereVirg + 1)
          indiceTroisiemeVirg = CCbGlob.premiereVirgule(ch, indiceDeuxiemeVirg + 1)
          indiceParF = CCbGlob.parentheseFermanteApresVirgule(ch, indiceTroisiemeVirg + 1)
          nomVariableSommation = ch.substring(indicePremiereVirg + 1, indiceDeuxiemeVirg)
          nomVariableSommation = nomVariableSommation.trim()
          const n = nomsVariablesFormelles?.length ?? 0
          // String[] nomVariableFormelle2 = new String[n + 1];
          nomVariableFormelle2 = new Array(n + 1)
          // Code nettoyé version mtgApp
          for (let j = 0; j < n; j++) nomVariableFormelle2[j] = nomsVariablesFormelles[j]
          // nomVariableFormelle2[n+1] = new String(nomVariableSommation);
          nomVariableFormelle2[n] = nomVariableSommation
          const Classef4var = (nomF.getValue() === Opef4v.integrale) ? CIntegraleDansFormule : CPrimitive
          return new Classef4var(
            ptListe,
            CalcR.ccb(ch, ptListe, pdebut + longNom + 1, indicePremiereVirg - 1, nomVariableFormelle2, listeSource),
            CalcR.ccb(ch, ptListe, indiceDeuxiemeVirg + 1, indiceTroisiemeVirg - 1, nomsVariablesFormelles, listeSource),
            CalcR.ccb(ch, ptListe, indiceTroisiemeVirg + 1, indiceParF - 1, nomsVariablesFormelles, listeSource),
            nomVariableSommation,
            false
          )
        }

        // fonctions à 5 variables : la somme indicée et
        // produit indicé
        indicePremiereVirg = CCbGlob.premiereVirgule(ch, pdebut + longNom + 1)
        indiceDeuxiemeVirg = CCbGlob.premiereVirgule(ch, indicePremiereVirg + 1)
        indiceTroisiemeVirg = CCbGlob.premiereVirgule(ch, indiceDeuxiemeVirg + 1)
        const indiceQuatriemeVirg = CCbGlob.premiereVirgule(ch, indiceTroisiemeVirg + 1)
        indiceParF = CCbGlob.parentheseFermanteApresVirgule(ch, indiceQuatriemeVirg + 1)
        nomVariableSommation = ch.substring(indicePremiereVirg + 1, indiceDeuxiemeVirg)
        nomVariableSommation = nomVariableSommation.trim()
        const n = nomsVariablesFormelles?.length ?? 0
        // String[] nomVariableFormelle2 = new String[n + 1];
        nomVariableFormelle2 = new Array(n + 1)
        for (let j = 0; j < n; j++) {
          nomVariableFormelle2[j] = nomsVariablesFormelles[j]
        }
        nomVariableFormelle2[n] = nomVariableSommation
        if (nomF.getValue() === Opef5v.somme) {
          return new CSommeDansFormule(
            ptListe,
            CalcR.ccb(ch, ptListe, pdebut + longNom + 1, indicePremiereVirg - 1, nomVariableFormelle2, listeSource),
            CalcR.ccb(ch, ptListe, indiceDeuxiemeVirg + 1, indiceTroisiemeVirg - 1, nomVariableFormelle2, listeSource),
            CalcR.ccb(ch, ptListe, indiceTroisiemeVirg + 1, indiceQuatriemeVirg - 1, nomVariableFormelle2, listeSource),
            CalcR.ccb(ch, ptListe, indiceQuatriemeVirg + 1, indiceParF - 1, nomVariableFormelle2, listeSource),
            nomVariableSommation
          )
        }
        return new CProduitDansFormule(
          ptListe,
          CalcR.ccb(ch, ptListe, pdebut + longNom + 1, indicePremiereVirg - 1, nomVariableFormelle2, listeSource),
          CalcR.ccb(ch, ptListe, indiceDeuxiemeVirg + 1, indiceTroisiemeVirg - 1, nomVariableFormelle2, listeSource),
          CalcR.ccb(ch, ptListe, indiceTroisiemeVirg + 1, indiceQuatriemeVirg - 1, nomVariableFormelle2, listeSource),
          CalcR.ccb(ch, ptListe, indiceQuatriemeVirg + 1, indiceParF - 1, nomVariableFormelle2, listeSource),
          nomVariableSommation
        )
      }
      if (ptVa.getValue().estFonctionOuSuite()) {
        // Ajouté version 4.7.4.1
        if (CCbGlob.parentheseFermante(ch, pdebut + longNom) === -1) throw new Error()
        const calc = ptVa.getValue()
        const nbvar = calc.nombreVariables()
        if (nbvar === 1) {
          return new CAppelFonction(
            ptListe,
            ptVa.getValue(),
            CalcR.ccb(ch, ptListe, pdebut + longNom, pfin, nomsVariablesFormelles, listeSource)
          )
        }
        // Ccb[] tabcal = new Ccb[nbvar];
        const tabcal = new Array(nbvar)
        let deb = pdebut + longNom
        let fin = 0
        for (let i = 0; i < nbvar - 1; i++) {
          fin = CCbGlob.premiereVirgule(ch, deb + 1)
          tabcal[i] = CalcR.ccb(ch, ptListe, deb + 1, fin - 1, nomsVariablesFormelles, listeSource)
          deb = fin
        }
        deb++
        fin = CCbGlob.parentheseFermanteApresVirgule(ch, deb)
        tabcal[nbvar - 1] = CalcR.ccb(ch, ptListe, deb,
          fin - 1, nomsVariablesFormelles, listeSource)
        return new CAppelFonctionNVar(ptListe, nbvar, ptVa.getValue(), tabcal)
      }
      if (ptVa.getValue().estMatrice() && ch.charAt(pdebut + longNom) === '(') {
        const tabcal = new Array(2)
        let deb = pdebut + longNom
        let fin = CCbGlob.premiereVirgule(ch, deb + 1)
        tabcal[0] = CalcR.ccb(ch, ptListe, deb + 1, fin - 1, nomsVariablesFormelles, listeSource)
        deb = fin + 1
        fin = CCbGlob.parentheseFermanteApresVirgule(ch, deb)
        tabcal[1] = CalcR.ccb(ch, ptListe, deb, fin - 1, nomsVariablesFormelles, listeSource)
        return new CTermMat(ptListe, tabcal[0], tabcal[1], ptVa.getValue())
      }
      return new CResultatValeur(ptListe, ptVa.getValue())
    }

    // Dans le cas où l'expression commence par une parenthèse
    // ouvrante, elle finit par une fermante et il suffit
    // d'évaluer ce que la parenthèse contient
    car = ch.charAt(pdebut)
    // Si le premier caractère est une parenthèse ouvrante
    // il faut faire attention car le dernier peut être
    // un espace.
    // Cela a donné un bug dans la version 16 bits
    if (car === '(') {
      if (CCbGlob.parentheseFermante(ch, pdebut) === -1) throw new Error()
      return CalcR.ccb(ch, ptListe, pdebut + 1, CCbGlob.parentheseFermante(ch, pdebut) - 1, nomsVariablesFormelles, listeSource)
    }

    // Le résultat ne peut être que numérique}
    const chaineNombre = ch.substring(pdebut, pfin + 1)
    if (chaineNombre === '') throw new Error() // Spécial JavaScript
    if (Fonte.estDecimalCorrect(chaineNombre)) {
      const f = parseFloat(chaineNombre)
      if (isNaN(f)) throw new Error()
      return new CConstante(ptListe, f)
    }

    return new CCbNull(chaineNombre)
  } catch (e) {
    // on n'affiche pas cette erreur à chaque fois, y'a plein de cas où c'est "normal" (racine ou log d'un négatif par ex)
    // Amélioration version 4.7.4.1 : Même si l'expression est incorrecte, on remplace les * de multiplication par des \times
    // Supprimé version 4.9.3
    const ch1 = ch.substring(pdebut, pfin + 1)
    // Amélioration version 4.9.3
    if ((ch1.charAt(0) === '(') && (ch1.length >= 1)) { // Cas d'une parenthèse ouvrante non fermée
      return CalcR.ccb(ch, ptListe, pdebut + 1, pfin, nomsVariablesFormelles, listeSource)
    }
    return new CCbNull(ch1)
    // return new CCbNull(stb.toString());
  }
}

/**
 * Fonction récursive vérifiant la syntaxe de l'expression réelle contenue dans ch en
 * tenant compte du fait qu'elle ne doit pas utiliser de valeur dont l'indice
 * dans ptListe est strictement supérieur à indiceMaxiDansListe.
 * Le calcul doit être réel.
 * @param {CListeObjets} ptListe  La liste propriétaire du calcul analysé
 * @param {string} ch  La chaîne dans laquelle se fait la recherche syntaxique.
 * @param {Pointeur} indiceErreur  getValeur renverra l'indice de l'erreur
 * de syntaxe dans la chapine s'il y en a une.
 * @param {number} indiceMaxiDansListe  L'indice maxi d'interprétation dans la chaîne ch.
 * @param {string[]|null} parametresFormels  Un tableau représentant les noms de la ou des variables formelles
 * d'une fonction de une ou plusieurs variables.
 * @returns {boolean} : true si la syntaxe est correcte, false sinon.
 */
function verifieSyntaxe (ptListe, ch,
  indiceErreur, indiceMaxiDansListe, parametresFormels) {
  let i
  let indiceDansListe
  let pdebut
  let car, carSuivant
  let erreur
  let itemPrecedent
  let itemSuivant = 0
  let pointDecimalRencontre
  let sommeParentheses
  let longNom
  const nomF = new Pointeur()
  const ptVa = new Pointeur()
  let queDesBlancs
  const parametreTrouve = new Pointeur(0)
  const nombreVariables = new Pointeur(0)
  let nivPar = 0 // Niveau de parenthèses
  const appelFonction = new Array(CCbGlob.nombreMaxParentheses + 1) // Tableau de booléens 64 niveaux de parenthèses imbriqués maxi
  for (let j = 0; j <= CCbGlob.nombreMaxParentheses; j++) appelFonction[j] = false
  erreur = false
  const longueurCh = ch.length
  // On vérifie d'abord qu'il n'y a pas d'erreur de parenthèse
  if (longueurCh === 0) {
    indiceErreur.setValue(0)
    return false
  }
  i = 0
  sommeParentheses = 0
  while ((i < longueurCh) && (sommeParentheses >= 0)) {
    car = ch.charAt(i)
    switch (car) {
      case '(':
        sommeParentheses++
        break
      case ')':
        sommeParentheses--
    } // switch
    i++
  }
  if (sommeParentheses !== 0) {
    indiceErreur.setValue(i)
    return false
  }
  if (ch === '.') {
    indiceErreur.setValue(0)
    return false
  }
  // Version 7.0 : On autorise un calcul réel du type detr(A) ou nbRow(A) ou nbCol(A)
  // où A est le nom d'une matrice
  const nomsFoncMat = ['deter', 'nbrow', 'nbcol']
  let res = false
  nomsFoncMat.some(function (st) {
    if (ch.startsWith(st) && ch.endsWith(')')) {
      const arg = ch.substring(st.length + 1, ch.length - 1)
      // On regarde si arg est le nom d'une matrice
      if (ptListe.pointeurMat(arg)) {
        res = true
        return true
      } else return false
    } else return false
  })
  if (res) return true
  i = 0
  // Tout se passe au début comme si l'expression était précédée
  // d'une parenthèse ouvrante
  itemPrecedent = CCbGlob.ParOuvrante
  while ((i < longueurCh) && !erreur) {
    car = ch.charAt(i)
    pointDecimalRencontre = false
    if (Fonte.chiffre(car) || (car === '.')) {
      while ((Fonte.chiffre(car) || (car === '.')) && (i < longueurCh) &&
      !erreur) {
        if (car === '.') {
          if (pointDecimalRencontre) {
            erreur = true
            indiceErreur.setValue(i - 1)
            // IndiceMessageErreur = IDE_SYNTAXE1;
          } else { pointDecimalRencontre = true }
        }
        i++
        if (i < longueurCh) { car = ch.charAt(i) }
      } // while
      if (!erreur) { itemSuivant = CCbGlob.Nombre }
    } else {
      // cas où le caractère en cours n'est pas un nombre
      // le caractère en cours n'est pas un chiffre ou un point décimal}
      pdebut = i
      // On regarde si cela commence par le nom d'une fonction ou
      // une valeur déjà définie
      longNom = ptListe.commenceParNomFonctionOuValeurOuParametre(
        ch.substring(pdebut), parametresFormels, ptVa, nomF, parametreTrouve, nombreVariables)
      if (longNom > 0) {
        if (parametreTrouve.getValue() !== -1) {
          itemSuivant = CCbGlob.Valeur
          i = i + longNom
        } else {
          appelFonction[nivPar + 1] = true
          if (ptVa.getValue() === null) {
            if (((i + longNom) < ch.length) &&
              ch.charAt(i + longNom) !== '(') {
              erreur = true
              indiceErreur.setValue(i + longNom)
            } else {
              itemSuivant = CCbGlob.Fonction
              // Ajout version 4.5.2 pour corriger un bug
              if (!CCbGlob.autorisationSyntaxe(itemPrecedent, itemSuivant)) {
                erreur = true
                indiceErreur.setValue(i)
                break
              }
              // Fin ajout
              // Il faut regarder si la fonction est de plusieurs variables
              const nvirg = CCbGlob.NombreVirgules(ch, i + longNom + 1)
              if (nvirg !== nombreVariables.getValue() - 1) {
                if (nvirg <= nombreVariables.getValue() - 1) {
                  indiceErreur.setValue(CCbGlob.indiceCaractereVirgule(ch, i +
                    longNom + 1, nvirg))
                } else {
                  indiceErreur.setValue(CCbGlob.indiceCaractereVirgule(ch, i +
                    longNom + 1, nombreVariables.getValue() - 1) - 1)
                }
                erreur = true
              } else {
                // Si le nombre de variables est supérieur ou égal à 4, il
                // s'agit soit d'un calcul d'intégrale
                // soit d'une somme indicée pour laquelle la syntaxe est la
                // même
                if (nombreVariables.getValue() >= 4) {
                  nivPar++
                  const indicePremVirg = CCbGlob.premiereVirgule(ch, i + longNom + 1)
                  const chformuleASommer = ch.substring(i + longNom + 1, indicePremVirg)
                  const indiceDeuxiemeVirg = CCbGlob.premiereVirgule(ch, indicePremVirg + 1)
                  let nomVariableSommation = ch.substring(indicePremVirg + 1, indiceDeuxiemeVirg)
                  // Il faut retirer les espaces de début et de fin
                  // On vérifie si le nom de variable formelle pour la somme est valable
                  nomVariableSommation = nomVariableSommation.trim()
                  if (!ptListe
                  // Ci-dessous paramètre bAccepNamei à true on accepte i comme variable de sommation
                    .validationNomCalculSansMessage(nomVariableSommation, true) ||
                    ptListe.egalNomValeurOuFonctionOuMesure(nomVariableSommation, -1)) {
                    erreur = true
                    indiceErreur.setValue(indiceDeuxiemeVirg)
                  } else {
                    // On vérifie si la syntaxe du calcul à sommer est
                    // correct.
                    // Pour cela on appelle à nouveau vérifieSyntaxe
                    const indiceErreur2 = new Pointeur(0)
                    const n = (parametresFormels !== null) ? parametresFormels.length : 0
                    const parametreFormel2 = new Array(n + 1)
                    for (let j = 0; j < n; j++) parametreFormel2[j] = parametresFormels[j]
                    parametreFormel2[n] = nomVariableSommation
                    if (!CalcR.verifieSyntaxe(ptListe, chformuleASommer,
                      indiceErreur2, indiceMaxiDansListe, parametreFormel2)) {
                      erreur = true
                      indiceErreur.setValue(i + longNom + 1 + indiceErreur2.getValue())
                    } else {
                      i = indiceDeuxiemeVirg + 1
                      itemPrecedent = CCbGlob.Valeur
                      itemSuivant = CCbGlob.Virgule
                    }
                  }
                } else { i += longNom }
              }
            }
          } else {
            const vd = ptVa.getValue()
            indiceDansListe = ptListe.indexOf(vd)
            if (indiceDansListe > indiceMaxiDansListe) {
              erreur = true
              indiceErreur.setValue(i)
            } else {
              i += longNom
              if (vd.estFonctionOuSuite()) {
                if ((i < ch.length) && ch.charAt(i) !== '(') {
                  erreur = true
                  indiceErreur.setValue(i + longNom)
                } else {
                  itemSuivant = CCbGlob.Fonction
                  const nbvar = vd.nombreVariables()
                  if (nbvar >= 2) {
                    const nvirg = CCbGlob.NombreVirgules(ch, i + 1)
                    if (nvirg !== nombreVariables.getValue() - 1) {
                      if (nvirg <= nombreVariables.getValue() - 1) {
                        indiceErreur.setValue(CCbGlob.indiceCaractereVirgule(ch,
                          i + 1, nvirg))
                      } else {
                        indiceErreur.setValue(CCbGlob.indiceCaractereVirgule(ch,
                          i + 1, nombreVariables.getValue() - 1) - 1)
                      }
                      erreur = true
                    }
                  } else {
                    // Bug corrigé version 4.6 : Il ne faut pas qu'un appel de fonction d'une variable comprenne des virgules
                    if (CCbGlob.NombreVirgules(ch, i + 1) > 0) {
                      indiceErreur.setValue(ch.indexOf(',', i + 1))
                      erreur = true
                    }
                  }
                }
              } else {
                if (vd.estMatrice()) {
                  if ((i < ch.length) && ch.charAt(i) === '(') {
                    // Un nom de matrice suivi de deux paramètres fait référence à un terme de la matrice
                    itemSuivant = CCbGlob.Fonction
                    const nvirg = CCbGlob.NombreVirgules(ch, i + 1)
                    if (nvirg !== 1) {
                      if (nvirg <= 1) {
                        indiceErreur.setValue(CCbGlob.indiceCaractereVirgule(ch,
                          i + 1, nvirg))
                      } else {
                        indiceErreur.setValue(CCbGlob.indiceCaractereVirgule(ch,
                          i + 1, 1) - 1)
                      }
                      erreur = true
                    }
                  } else {
                    erreur = true
                    indiceErreur.setValue(i)
                  }
                } else {
                  itemSuivant = CCbGlob.Valeur
                }
              }
            }
          }
        }
      } else {
        switch (car) {
          case '+':
          case '-':
            itemSuivant = CCbGlob.Addition
            i++
            break
          case '*':
          case '/':
            itemSuivant = CCbGlob.Multiplication
            i++
            break
          case '(':
            if (nivPar >= CCbGlob.nombreMaxParentheses - 1) {
              erreur = true
              indiceErreur.setValue(i)
            } else {
              itemSuivant = CCbGlob.ParOuvrante
              i++
              nivPar++
            }
            break
          case ')':
            appelFonction[nivPar] = false
            nivPar--
            itemSuivant = CCbGlob.ParFermante
            i++
            break
          case '^':
            itemSuivant = CCbGlob.Puissance
            i++
            break
          // Ajout par rapport à la version C++ 1.8, autorisation du carré ²
          case '²':
            itemSuivant = CCbGlob.Carre
            i++
            break
          // ni nombre ni opérateur}
          case '<':
            itemSuivant = CCbGlob.Inegalite
            i++
            if (i <= longueurCh - 2) {
              carSuivant = ch.charAt(i)
              if ((carSuivant === '=') || (carSuivant === '>')) { i++ }
            }
            break
          case '>':
            itemSuivant = CCbGlob.Inegalite
            i++
            if (i <= longueurCh - 2) {
              carSuivant = ch.charAt(i)
              if (carSuivant === '=') { i++ }
            }
            break
          // Ajout version 4.9.5
          case '&':
          case '|':
          case '=':
            itemSuivant = CCbGlob.Inegalite
            i++
            break
          case ' ':
            itemSuivant = CCbGlob.Blanc
            i++
            break
          case ',':
            if (!appelFonction[nivPar]) {
              erreur = true
              indiceErreur.setValue(i)
            } else {
              itemSuivant = CCbGlob.Virgule
              i++
            }
            break
          default: // switch Car
            erreur = true
            indiceErreur.setValue(i - 1)
        } // switch Car
      }// else du if LongNom > 0
    } // else du if Chiffre(Car) || (Car === '.'))
    if (!erreur) {
      if (CCbGlob.autorisationSyntaxe(itemPrecedent, itemSuivant)) {
        if (itemSuivant !== CCbGlob.Blanc) { itemPrecedent = itemSuivant }
      } else {
        erreur = true
        indiceErreur.setValue(i - 1)
      }
    }
  } // while
  // Il faut aussi regarder si la fin de l'expression est correcte
  itemSuivant = CCbGlob.ParFermante
  if (!erreur) {
    if (!CCbGlob.autorisationSyntaxe(itemPrecedent, itemSuivant)) {
      erreur = true
      indiceErreur.setValue(longueurCh)
    }
  }
  if (indiceErreur.getValue() < 0) indiceErreur.setValue(0)
  else if (indiceErreur.getValue() > longueurCh) indiceErreur.setValue(longueurCh)
  // On regarde si la chaîne ne contient que des blancs
  i = 0
  queDesBlancs = true
  while ((i <= longueurCh - 1) && queDesBlancs) {
    car = ch.charAt(i)
    if (car !== ' ') { queDesBlancs = false } else { i++ }
  }
  if (queDesBlancs) {
    erreur = true
    indiceErreur.setValue(longueurCh)
  }
  return !erreur
}

/**
 * Procédure récursive créant l'objet Ccb représentant l'arbre de la
 * dérivée d'une fonction.
 * Sera utilisé au chargement d'un objet du type CDerivee ou CEdriveePartielle.
 * @param {CCb} calc  Le calcul dont on veut calculer la dérivée ou dérivée partielle.
 * @param {number} indiceVar  L'indice de la variable par rapport à laquelle on dérive.
 * @returns {CCb} : L'arbre de calcul binaire représentant la dérivée.
 */
function creeDerivee (calc, indiceVar) {
  let op1dep, op2dep, uder, vder, uCopie, vCopie, fcalcder, appelfder, f, fcalcderu
  let fcalcderv, appelfderu, appelfderv, prod1, prod2, wder, wCopie, fcalcderw
  let appelfderw, sum1
  const li = calc.listeProprietaire
  const natcalc = calc.nature()
  if (natcalc === CCbGlob.natConstante) return new CConstante(li, 0)
  else if (natcalc === CCbGlob.natVariableFormelle) {
    // Si l'indice de la variable formelle est le bon on renvoie 1 sinon 0
    if (calc.indvar === indiceVar) { return new CConstante(li, 1) } else { return new CConstante(li, 0) }
  } else if (natcalc === CCbGlob.natResultatValeur) return new CConstante(li, 0)
  else if (natcalc === CCbGlob.natAppelFonction) {
    // Ici il y a un problème car l'appel de fonction peut être un appel
    // à une suite récurrente. Cet appel est licite s'il ne fait pas appel
    // à la variable formelle.
    // Il faudra le vérifier avant d'autoriser le calcul d'une dérivée.
    // Ici on suppose que c'est la cas
    // Modifié version 4.6
    //  CAppelFonction appfon = (CAppelFonction) calc;
    // if (appfon.fonctionAssociee.getClass() === CSuiteRec.class) {
    if (calc.fonctionAssociee.estDeNatureCalcul(NatCal.NSuiteRecurrenteReelle)) {
      // L'appel de suite sans paramètre est considéré comme une constante
      return new CConstante(li, 0)
    } else {
      // Cas d'une vraie fonction
      f = calc.fonctionAssociee
      // On regarde si l'appel de fonction est seulement de la forme
      // f(paramètre)
      if (calc.operande.nature() === CCbGlob.natVariableFormelle) {
        const va = calc.operande
        if (va.indvar === indiceVar) { return CalcR.creeDerivee(f.calcul, indiceVar) } else {
          uder = CalcR.creeDerivee(calc.operande, indiceVar)
          uCopie = calc.operande.getCopie()
          fcalcder = CalcR.creeDerivee(f.calcul, indiceVar)
          appelfder = new CAppelFonctionInterne(fcalcder, uCopie)
          return new COp(li, uder, appelfder, Ope.Mult)
        }
      } else { // Dans ce cas il faut dériver comme une composée du type fou
        uder = CalcR.creeDerivee(calc.operande, indiceVar)
        uCopie = calc.operande.getCopie()
        fcalcder = CalcR.creeDerivee(f.calcul, indiceVar)
        appelfder = new CAppelFonctionInterne(fcalcder, uCopie)
        return new COp(li, uder, appelfder, Ope.Mult)
      }
    }
  } else if (natcalc === CCbGlob.natOperation) {
    // var op = calc;
    switch (calc.ope) {
      case Ope.Plus:
        op1dep = calc.operande1.dependDeVariable(indiceVar)
        op2dep = calc.operande2.dependDeVariable(indiceVar)
        if (!op1dep) {
          if (!op2dep) return new CConstante(li, 0)
          else return CalcR.creeDerivee(calc.operande2, indiceVar)
        }
        if (!op2dep) return CalcR.creeDerivee(calc.operande1, indiceVar)
        else {
          return new COp(li, CalcR.creeDerivee(calc.operande1, indiceVar),
            CalcR.creeDerivee(calc.operande2, indiceVar), Ope.Plus)
        }
      case Ope.Moin:
        op1dep = calc.operande1.dependDeVariable(indiceVar)
        op2dep = calc.operande2.dependDeVariable(indiceVar)
        if (!op1dep) {
          if (!op2dep) return new CConstante(li, 0)
          else return new CMoinsUnaire(li, CalcR.creeDerivee(calc.operande2, indiceVar))
        }
        if (!op2dep) return CalcR.creeDerivee(calc.operande1, indiceVar)
        return new COp(li, CalcR.creeDerivee(calc.operande1, indiceVar),
          CalcR.creeDerivee(calc.operande2, indiceVar), Ope.Moin)
      case Ope.Mult: {
        // On regarde d'abord si les facteurs du produit dépendent de la
        // variable
        op1dep = calc.operande1.dependDeVariable(indiceVar)
        op2dep = calc.operande2.dependDeVariable(indiceVar)
        if (!op1dep) {
          if (!op2dep) return new CConstante(li, 0)
          else {
            return new COp(li, calc.operande1.getCopie(),
              CalcR.creeDerivee(calc.operande2, indiceVar), Ope.Mult)
          }
        }
        if (!op2dep) {
          return new COp(li, CalcR.creeDerivee(calc.operande1, indiceVar),
            calc.operande2.getCopie(), Ope.Mult)
        } else {
          // Formule u'v + uv'
          return new COp(li, new COp(li, CalcR.creeDerivee(calc.operande1, indiceVar),
            calc.operande2.getCopie(), Ope.Mult), new COp(li,
            CalcR.creeDerivee(calc.operande2, indiceVar),
            calc.operande1.getCopie(), Ope.Mult), Ope.Plus)
        }
      }
      case Ope.Divi: // Formule (u'v-uv')/v²
        // On regarde d'abord si le dénominateur dépend de la variable
        if (!calc.operande2.dependDeVariable(indiceVar)) {
          return new COp(li, CalcR.creeDerivee(calc.operande1, indiceVar),
            calc.operande2.getCopie(), Ope.Divi)
        } else {
          return new COp(li, new COp(li, new COp(li,
            CalcR.creeDerivee(calc.operande1, indiceVar), calc.operande2.getCopie(),
            Ope.Mult), new COp(li, CalcR.creeDerivee(calc.operande2,
            indiceVar), calc.operande1.getCopie(), Ope.Mult),
          Ope.Moin), new CPuissance(li, calc.operande2.getCopie(),
            new CConstante(li, 2)), Ope.Divi)
        }
      case Ope.Inf:
      case Ope.Sup:
      case Ope.InfOuEgal:
      case Ope.SupOuEgal:
      case Ope.Egalite:
      case Ope.Diff:
      case Ope.And:
      case Ope.Or:
        return new CConstante(li, 0)
    }
  } else if (natcalc === CCbGlob.natFonction) {
    // var f = calc;
    switch (calc.opef) {
      case Opef.Abso:
        // La fonction valeur absolue n'étant pas dérivable en 0
        // On va renvoyer comme formule
        // 1/((operande>0)-(Operande<0))*(Operande)'
        return new COp(li, new COp(li, new CConstante(li, 1),
          new COp(li, new COp(li, calc.operande.getCopie(),
            new CConstante(li, 0), Ope.Sup), new COp(li,
            calc.operande.getCopie(), new CConstante(li, 0), Ope.Inf),
          Ope.Moin), Ope.Divi), CalcR.creeDerivee(calc.operande,
          indiceVar), Ope.Mult)
      case Opef.Enti: // la dérivée existe et est nulle sauf en
        // tout entier
        // On renvoie donc comme formule 0/(operande<>int(operande))
        return new COp(li, new CConstante(li, 0), new COp(li,
          calc.operande.getCopie(), new CFonction(li, Opef.Enti,
            calc.operande.getCopie()), Ope.Diff), Ope.Divi)
      case Opef.Raci:
      case Opef.Raci2: // Formule u'/(2Racine(u))
        return new COp(
          li,
          CalcR.creeDerivee(calc.operande, indiceVar),
          new COp(li, new CConstante(li, 2), new CFonction(li,
            Opef.Raci, calc.operande.getCopie()), Ope.Mult),
          Ope.Divi)
      case Opef.Sinu: // La dérivée ne sera correcte que si on est
        // en mode radian
        // On applique la formule u'*cos(u)
        if (li.uniteAngle === uniteAngleRadian) {
          return new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
            new CFonction(li, Opef.Cosi, calc.operande.getCopie()),
            Ope.Mult)
        } else {
          return new COp(li, new CConstante(li, ConvDegRad),
            new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
              new CFonction(li, Opef.Cosi,
                calc.operande.getCopie()), Ope.Mult), Ope.Mult)
        }

      case Opef.Cosi:
        // On applique la formule -u'*sin(u)
        if (li.uniteAngle === uniteAngleRadian) {
          return new COp(li, new COp(li, CalcR.creeDerivee(calc.operande,
            indiceVar), new CFonction(li, Opef.Sinu,
            calc.operande.getCopie()), Ope.Mult), new CConstante(li, -1),
          Ope.Mult)
        } else {
          return new COp(li, new CConstante(li, ConvDegRad),
            new COp(li, new COp(li, CalcR.creeDerivee(calc.operande,
              indiceVar), new CFonction(li, Opef.Sinu,
              calc.operande.getCopie()), Ope.Mult), new CConstante(li,
              -1), Ope.Mult), Ope.Mult)
        }

      case Opef.Tang: // On applique la formule u'/cos²(u)
        if (li.uniteAngle === uniteAngleRadian) {
          return new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
            new CPuissance(li, new CFonction(li, Opef.Cosi,
              calc.operande.getCopie()), new CConstante(li, 2)),
            Ope.Divi)
        } else {
          return new COp(li, new CConstante(li, ConvDegRad),
            new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
              new CPuissance(li, new CFonction(li, Opef.Cosi,
                calc.operande.getCopie()), new CConstante(li, 2)),
              Ope.Divi), Ope.Mult)
        }
      case Opef.Logn: // Formule u'/(u*(u>0))
        return new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
          new COp(li, calc.operande.getCopie(), new COp(li,
            calc.operande.getCopie(), new CConstante(li, 0), Ope.Sup),
          Ope.Mult), Ope.Divi)
      case Opef.Expo: // Formule u'*exp(u)
        return new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
          new CFonction(li, Opef.Expo, calc.operande.getCopie()),
          Ope.Mult)
      case Opef.ATan: // Formule u'/(1+u²)
        if (li.uniteAngle === uniteAngleRadian) {
          return new COp(
            li,
            CalcR.creeDerivee(calc.operande, indiceVar),
            new COp(li, new CConstante(li, 1), new CPuissance(li,
              calc.operande.getCopie(), new CConstante(li, 2)), Ope.Plus),
            Ope.Divi)
        } else {
          return new COp(li, new CConstante(li, ConvRadDeg),
            new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
              new COp(li, new CConstante(li, 1), new CPuissance(li,
                calc.operande.getCopie(), new CConstante(li, 2)),
              Ope.Plus), Ope.Divi), Ope.Mult)
        }
      case Opef.ACos: // Formule -u'/(rac(1-u²))
        if (li.uniteAngle === uniteAngleRadian) {
          return new COp(li, new COp(li, CalcR.creeDerivee(calc.operande,
            indiceVar),
          new CFonction(li, Opef.Raci, new COp(li,
            new CConstante(li, 1), new CPuissance(li, calc.operande
              .getCopie(), new CConstante(li, 2)), Ope.Moin)),
          Ope.Divi), new CConstante(li, -1), Ope.Mult)
        } else {
          return new COp(li, new COp(li, CalcR.creeDerivee(calc.operande,
            indiceVar),
          new CFonction(li, Opef.Raci, new COp(li,
            new CConstante(li, 1), new CPuissance(li, calc.operande
              .getCopie(), new CConstante(li, 2)), Ope.Moin)),
          Ope.Divi), new CConstante(li, -ConvRadDeg),
          Ope.Mult)
        }
      case Opef.ASin: // Formule u'/(rac(1-u²))
        if (li.uniteAngle === uniteAngleRadian) {
          return new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
            new CFonction(li, Opef.Raci, new COp(li,
              new CConstante(li, 1), new CPuissance(li,
                calc.operande.getCopie(), new CConstante(li, 2)),
              Ope.Moin)), Ope.Divi)
        } else {
          return new COp(li, new CConstante(li, ConvRadDeg),
            new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
              new CFonction(li, Opef.Raci, new COp(li,
                new CConstante(li, 1), new CPuissance(li,
                  calc.operande.getCopie(), new CConstante(li, 2)),
                Ope.Moin)), Ope.Divi), Ope.Mult)
        }
      case Opef.Cosh: // Formule u'*Sinh(u)
        return new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
          new CFonction(li, Opef.Sinh, calc.operande.getCopie()),
          Ope.Mult)
      case Opef.Sinh: // Formule u'*Cosh(u)
        return new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
          new CFonction(li, Opef.Cosh, calc.operande.getCopie()),
          Ope.Mult)
      case Opef.Tanh: // Formule u'/Cosh²(u)
        return new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
          new CPuissance(li, new CFonction(li, Opef.Cosh,
            calc.operande.getCopie()), new CConstante(li, 2)), Ope.Divi)
      case Opef.ArgCh: // Formule u'/Rac(u²-1)
        return new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
          new CFonction(li, Opef.Raci,
            new COp(li, new CPuissance(li, calc.operande.getCopie(),
              new CConstante(li, 2)), new CConstante(li, 1),
            Ope.Moin)), Ope.Divi)
      case Opef.ArgSh: // Formule u'/(Rac(1+u²)
        return new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
          new CFonction(li, Opef.Raci,
            new COp(li, new CPuissance(li, calc.operande.getCopie(),
              new CConstante(li, 2)), new CConstante(li, 1),
            Ope.Plus)), Ope.Divi)
      case Opef.ArgTh: // Formule (Abs(u)<1)*u'/(1-u²)
        return new COp(li, new COp(li, new CFonction(li,
          Opef.Abso, calc.operande.getCopie()), new CConstante(li,
          1), Ope.Inf), new COp(li, CalcR.creeDerivee(calc.operande,
          indiceVar), new COp(li, new CConstante(li, 1),
          new CPuissance(li, calc.operande.getCopie(), new CConstante(li, 2)),
          Ope.Moin), Ope.Divi), Ope.Mult)
      case Opef.Rand:
        return new CConstante(li, 0)
      // Rajouté version 4.7.2
      case Opef.Left:
        // return CalcR.creeDerivee(calc.operande.membreGauche(), indiceVar)
        return CalcR.creeDerivee(getLeft(calc.operande), indiceVar)
      case Opef.Right:
        // return CalcR.creeDerivee(calc.operande.membreDroit(), indiceVar)
        return CalcR.creeDerivee(getRight(calc.operande), indiceVar)
      case Opef.Core:
        return CalcR.creeDerivee(calc.operande, indiceVar)
    }
  } else if (natcalc === CCbGlob.natPuissance) {
    // CPuissance pui = (CPuissance) calc;
    // Si l'exposant ne dépend pas de la variable formelle on emploie la
    // formule
    // (u^n)' = nu'u^(n-1)
    if (!calc.exposant.dependDeVariable(indiceVar)) {
      if (!calc.operande.dependDeVariable(indiceVar)) { return new CConstante(li, 0) } else {
        if (calc.exposant.nature() === CCbGlob.natConstante) {
          const co = calc.exposant
          if (co.valeur === 0) return new CConstante(li, 0)
          else if (co.valeur === 1) return CalcR.creeDerivee(calc.operande, indiceVar)
          else {
            return new COp(li,
              new COp(li, calc.exposant.getCopie(), CalcR.creeDerivee(
                calc.operande, indiceVar), Ope.Mult),
              new CPuissance(li, calc.operande.getCopie(), new CConstante(li, co.valeur - 1)),
              Ope.Mult)
          }
        } else {
          return new COp(li,
            new COp(li, calc.exposant.getCopie(), CalcR.creeDerivee(
              calc.operande, indiceVar), Ope.Mult),
            new CPuissance(li, calc.operande.getCopie(), new COp(li,
              calc.exposant.getCopie(), new CConstante(li, 1), Ope.Moin)),
            Ope.Mult)
        }
      }
    } else {
      // Sinon on considère u^v comme exp(v*lnu) de dérivée
      // (v'*lnu+v*u'/u)*u^v
      return new COp(
        li,
        new COp(li, new COp(li, CalcR.creeDerivee(calc.exposant,
          indiceVar), new CFonction(li, Opef.Logn,
          calc.operande.getCopie()), Ope.Mult), new COp(li,
          new COp(li, CalcR.creeDerivee(calc.operande, indiceVar),
            calc.operande.getCopie(), Ope.Divi), calc.exposant
            .getCopie(), Ope.Mult), Ope.Plus),
        new CPuissance(li, calc.operande.getCopie(), calc.exposant.getCopie()),
        Ope.Mult)
    }
  } else if (natcalc === CCbGlob.natMoinsUnaire) {
    // Ajout version 4.6 : Nouvel opérateur moins unaire
    // CMoinsUnaire mun = (CMoinsUnaire) calc;
    return new CMoinsUnaire(li, CalcR.creeDerivee(calc.operande, indiceVar))
  } else if (natcalc === CCbGlob.natAppelFonctionNVar) {
    // Ajout version 3.0 pour le cas d'un appel à une fonction utilisateur de
    // plusieurs variables
    // Pour une fonction du type f(t) = g[u(t),v(t)] on utilise la formule
    // f'(t) = u'(t) g'u(u(t),v(t)) + v'(t) g'v(u(t),v(t))

    // Version 3.0 : seulement des fonctions jutilisateur de 2 à 4 variables
    // peuvent être créées.
    // CAppelFonctionNVar appfon = (CAppelFonctionNVar) calc;
    f = calc.fonctionAssociee
    const nvar = f.nbVar
    uder = CalcR.creeDerivee(calc.operandes[0], indiceVar) // Calcul
    // de
    // u'(t)
    vder = CalcR.creeDerivee(calc.operandes[1], indiceVar) // Calcul
    // de
    // v'(t)
    uCopie = calc.operandes[0].getCopie()
    vCopie = calc.operandes[1].getCopie()
    const tabcal = new Array(nvar)
    tabcal[0] = uCopie
    tabcal[1] = vCopie
    fcalcderu = CalcR.creeDerivee(f.calcul, 0) // Dérivée partielle
    // par rapport à la
    // première variable u
    fcalcderv = CalcR.creeDerivee(f.calcul, 1) // Dérivée partielle
    // par rapport à la
    // deuxième variable v
    appelfderu = new CAppelFonctionInterneNVar(fcalcderu, tabcal)
    appelfderv = new CAppelFonctionInterneNVar(fcalcderv, tabcal)
    /*
    var prod1 = new COp(li, uder, appelfderu, Ope.Mult);
    var prod2 = new COp(li, vder, appelfderv, Ope.Mult);
    var sum1 = new COp(li, prod1, prod2, Ope.Plus);
    */
    if ((uder.nature() === CCbGlob.natConstante) && (uder.valeur === 0)) {
      prod1 = new CConstante(li, 0)
    } else prod1 = new COp(li, uder, appelfderu, Ope.Mult)
    if ((vder.nature() === CCbGlob.natConstante) && (vder.valeur === 0)) {
      prod2 = new CConstante(li, 0)
      // else prod1 = new COp(li, uder, appelfderu, Ope.Mult) // Ligne corrigée version 6
    } else prod2 = new COp(li, vder, appelfderv, Ope.Mult)
    sum1 = new COp(li, prod1, prod2, Ope.Plus)
    if (nvar === 2) return sum1
    // Cas d'une fonction de 3 variables ou plus
    for (let i = 2; i < nvar; i++) {
      wder = CalcR.creeDerivee(calc.operandes[i], indiceVar) // Calcul
      wCopie = calc.operandes[i].getCopie()
      tabcal[i] = wCopie
      fcalcderw = CalcR.creeDerivee(f.calcul, i) // Dérivée partielle
      appelfderw = new CAppelFonctionInterneNVar(fcalcderw, tabcal)
      if (wder.nature() === CCbGlob.natConstante && (wder.valeur === 0)) {
        prod2 = new CConstante(li, 0)
      } else prod2 = new COp(li, wder, appelfderw, Ope.Mult)
      sum1 = new COp(li, sum1, prod2, Ope.Plus)
    }
    return sum1
  } else if (natcalc === CCbGlob.natAppelFonctionInterneNVar) {
    // Version 3.0 : seulement des fonctions jutilisateur de 2 à 4 variables
    // peuvent être créées.
    // var appfon = calc;
    f = calc.fonctionInterne
    const nvar = calc.operandes.length
    uder = CalcR.creeDerivee(calc.operandes[0], indiceVar) // Calcul
    // de
    // u'(t)
    vder = CalcR.creeDerivee(calc.operandes[1], indiceVar) // Calcul
    // de
    // v'(t)
    uCopie = calc.operandes[0].getCopie()
    vCopie = calc.operandes[1].getCopie()
    const tabcal = new Array(nvar)
    tabcal[0] = uCopie
    tabcal[1] = vCopie
    fcalcderu = CalcR.creeDerivee(f, 0) // Dérivée partielle par
    // rapport à la première
    // variable u
    fcalcderv = CalcR.creeDerivee(f, 1) // Dérivée partielle par
    // rapport à la deuxième
    // variable v
    appelfderu = new CAppelFonctionInterneNVar(fcalcderu, tabcal)
    appelfderv = new CAppelFonctionInterneNVar(fcalcderv, tabcal)
    // Test suivany rajouté version 6.8 pour optimisation
    if ((uder.nature() === CCbGlob.natConstante) && (uder.valeur === 0)) {
      prod1 = new CConstante(li, 0)
    } else prod1 = new COp(li, uder, appelfderu, Ope.Mult)
    prod1 = new COp(li, uder, appelfderu, Ope.Mult)
    if ((vder.nature() === CCbGlob.natConstante) && (vder.valeur === 0)) {
      prod2 = new CConstante(li, 0)
    } else prod2 = new COp(li, vder, appelfderv, Ope.Mult)
    sum1 = new COp(li, prod1, prod2, Ope.Plus)
    if (nvar === 2) return sum1
    for (let i = 2; i < nvar; i++) {
      wder = CalcR.creeDerivee(calc.operandes[i], indiceVar) // Calcul
      wCopie = calc.operandes[i].getCopie()
      tabcal[i] = wCopie
      fcalcderw = CalcR.creeDerivee(f, i) // Dérivée partielle par rapport à la i ième variable w
      appelfderw = new CAppelFonctionInterneNVar(fcalcderw, tabcal)
      if (wder.nature() === CCbGlob.natConstante && (wder.valeur === 0)) {
        prod2 = new CConstante(li, 0)
      } else prod2 = new COp(li, wder, appelfderw, Ope.Mult)
      sum1 = new COp(li, sum1, prod2, Ope.Plus)
    }
    return sum1
  } else if (natcalc === CCbGlob.natFonction3Var) {
    // CFonction3Var f = (CFonction3Var) calc;
    switch (calc.opef) {
      // La fonction valeur absolue n'étant pas dérivable en 0
      // On va renvoyer comme formule (operande1=1)*(Operande2)' +
      // (operande1!==1)*(Operande3)'
      case Opef3v.si:
        return new COp(li, new COp(li, new COp(li,
          calc.operande1.getCopie(), new CConstante(li, 1), Ope.Egalite),
        CalcR.creeDerivee(calc.operande2, indiceVar), Ope.Mult),
        new COp(li, new COp(li, calc.operande1.getCopie(),
          new CConstante(li, 1), Ope.Diff), CalcR.creeDerivee(
          calc.operande3, indiceVar), Ope.Mult), Ope.Plus)
      default:
        return new CConstante(li, 0)
    }
  } else if (natcalc === CCbGlob.natSommeDansFormule) {
    // CSommeDansFormule s = (CSommeDansFormule) calc;
    return new CSommeDansFormule(li, CalcR.creeDerivee(calc.calculASommer, indiceVar),
      calc.indiceDebut.getCopie(), calc.indiceFin.getCopie(), calc.pas.getCopie(),
      calc.nomVariable)
  } else
  // Si on a autorisé un produit indicé c'est qu'il ne dépendait pas de la
  // variable de dérivation
  // donc la dérivée est null
    if (natcalc === CCbGlob.natProduitDansFormule) {
      return new CConstante(li, 0)
    }
  // Pour les autres types, on considère le calcul constant (fonction
  // prédifinie de deux variables comme max() par exemple
  return new CConstante(li, 0) // Pour satisfaire le compilateur
}

const CalcR = {
  ccb,
  creeDerivee,
  verifieSyntaxe
}

export default CalcR