kernel/CCbGlob.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 Opef from '../types/Opef'
import Opef2v from '../types/Opef2v'
import Opef3v from '../types/Opef3v'
import Opef4v from '../types/Opef4v'
import Opef5v from '../types/Opef5v'
import Ope from '../types/Ope'

/**
 * Le set des noms de fonctions, affecté au 1er appel de tabNomsFoncPred
 * @type {null|Set<string>}
 */
let nomsFoncPred = null

/**
 * Fonction renvoyant un set formé de tous les noms de fonctions réelles ou complexes prédéfinies
 * Utilisé dans les CEditeurFonction pour ajouter les opérations implicites et dans CListeObjets
 * @returns {Set<string>}
 */
function tabNomsFoncPred () {
  if (nomsFoncPred) return nomsFoncPred
  const res = new Set()
  let i
  // Version 7.0 : Il faut rechercher à la fois dans les noms de fonctions réelles et complexes
  // car certains différent comme dotmult
  // De toute façon add ne rajoute pas ce qui est déjà dans le Set
  for (i = 0; i < Opef.nomsFoncs.length; i++) {
    // Les toLowerCase sont rajoutés par précaution car normalement les noms de fonctions
    // traduits dans les fichiers de traduction doivent être en minuscules
    res.add(Opef.nomsFonctions(i).toLowerCase())
  }
  for (i = 0; i < Opef.nomsFoncsComplexes.length; i++) {
    res.add(Opef.nomsFonctionsComplexes(i).toLowerCase())
  }
  for (i = 0; i < Opef2v.nomsFoncs2Var.length; i++) {
    res.add(Opef2v.nomsFonctions2Var(i).toLowerCase())
  }
  for (i = 0; i < Opef2v.nomsFoncsComplexes2Var.length; i++) {
    res.add(Opef2v.nomsFonctionsComplexes2Var(i).toLowerCase())
  }
  for (i = 0; i < Opef3v.nomsFoncs3Var.length; i++) {
    res.add(Opef3v.nomsFonctions3Var(i).toLowerCase())
  }
  for (i = 0; i < Opef3v.nomsFoncsComplexes3Var.length; i++) {
    res.add(Opef3v.nomsFonctionsComplexes3Var(i).toLowerCase())
  }
  for (i = 0; i < Opef4v.nomsFoncs4Var.length; i++) {
    res.add(Opef4v.nomsFonctions4Var(i).toLowerCase())
  }
  for (i = 0; i < Opef4v.nomsFoncsComplexes4Var.length; i++) {
    res.add(Opef4v.nomsFonctionsComplexes4Var(i).toLowerCase())
  }
  for (i = 0; i < Opef5v.nomsFoncs5Var.length; i++) {
    res.add(Opef5v.nomsFonctions5Var(i).toLowerCase())
  }
  for (i = 0; i < Opef5v.nomsFoncsComplexes5Var.length; i++) {
    res.add(Opef5v.nomsFonctionsComplexes5Var(i))
  }
  if (res.has('')) {
    console.error(Error('Un des noms de fonction n’a pas été trouvé (textes non chargés ?)'))
    res.delete('')
  }
  if (res.size) nomsFoncPred = res
  else console.error(Error('Aucun nom de fonction chargé'))
  return res
}

/**
 * Fonction appelée dans le cas ou pRecherche est l'indice dans chaine d'une
 * parenthèse fermante et renvoyant l'indice de la parenthèse
 * ouvrante correspondante. La recherche se fait donc de la fin de la chapine vers le début.
 * Elle ne doit être appelée que si pdebut<=pRecherche et si pRecherche pointe sur une
 * parenthèse fermante et si la syntaxe est correcte au point de vue parenthèses.
 * @param {string} chaine  La chaîne dans laquelle se fait la recherche
 * @param {number} pdebut  L'indice d'arrêt de la recherhce dans la chaîne..
 * @param {number} pRecherche  L'indice de début de recherche dans la chaîne qui doit
 * être l'indice d'une parenthèse fermante.
 * @returns {number}
 */
function parentheseOuvrante (chaine, pdebut, pRecherche) {
  let p
  let ch
  let somme

  somme = 0
  p = pRecherche
  do {
    ch = chaine.charAt(p)
    if (ch === ')') { somme-- } else {
      if (ch === '(') { somme++ }
    }
    p = p - 1
  } while ((somme !== 0) && (p >= pdebut))
  return p + 1
}
/**
 * Fonction appelée dans le cas ou pDebut est l'indice dans chaine d'une
 * parenthèse ouvrante et renvoyant l'indice de la parenthèse
 * fermante correspondante.
 * @param {string} chaine  La chaîne dans laquelle se fait la recherche
 * @param {number} pdebut  l'indice de début de la racherche dans la chaîne.
 * @returns {number}
 */
function parentheseFermante (chaine, pdebut) {
  let p
  let ch
  let somme

  somme = 1
  p = pdebut + 1
  while (p < chaine.length) {
    ch = chaine.charAt(p)
    if (ch === '(') { somme++ } else {
      if (ch === ')') { somme-- }
    }
    if (somme === 0) break
    p++
  }
  if (somme === 0) return p
  else return -1 // On renvoie -1 si pas trouvé
}
/**
 * Fonction renvoyant l'indice de la parenthèse fermante correspondant à une
 * virgule. Avant appel pDebut pointe sur le caractère suivant la virgule.
 * La chaîne doit être correcte sur le plan syntaxique acant appel
 * @param {string} chaine  La chaîne dans laquelle se fait la recherche
 * @param {number} pDebut  Indice du début de la recherche.
 * @returns {number}
 */
function parentheseFermanteApresVirgule (chaine, pDebut) {
  let car
  let somme = 1
  let i
  for (i = pDebut; i < chaine.length; i++) {
    car = chaine.charAt(i)
    if (car === '(') somme++
    else if (car === ')') {
      somme--
      if (somme === 0) { return i }
    }
  }
  return i // Pour satisfaire le compilateur mais on ne doit pas arriver ici
}
/**
 * Fonction renvoyant l'indice dans la chaîne ch du caractère suivant la virgule n° i
 * @param {string} ch  La chaîne dans laquelle se fait la recherche
 * @param {number} inddeb  L'indice dans la chaîne du début de la recherche
 * @param {number} virg  L'indice de la virguel recherchée.
 * @returns {number}
 */
function indiceCaractereVirgule (ch, inddeb, virg) {
  let i = inddeb
  let nbvirg = 0
  if ((virg === 0) || (i > ch.length)) return ch.length - 1
  let parentheses = 1
  while ((i < ch.length) && (parentheses >= 1) && (nbvirg <= virg)) {
    const car = ch.charAt(i)
    if (car === '(') { parentheses++ }
    if (car === ')') { parentheses-- }
    if ((car === ',') && (parentheses === 1)) { nbvirg++ }
    i++
  }
  return i
}
/**
 * Fonction renvoyant l'indice du premier caractère dans ch étant un opérateur logique & ou |
 * Renvoie dans retour.x l'indice du caractère précédent l'opérateur logique
 * et dans retour.y l'opérateur associé à cette inégalité (Ope.Or ou Ope.And).
 * La recherche se fait de la fin de la chaine vers le début en sautant les parenthèses.
 *  @param {string} chop  La chaîne représentant l'opérateur ("|# ou "&").
 * @param {string} ch  La chaîne dans laquelle se fait la recherche.
 * @param {number} pdeb  Indice du début de la recherche.
 * @param {number} pfin  Indice de la fin de la recherche.
 * @param {Object} retour Voir ci-dessus.
 * @returns {number} : -1 si n'a pas trouvé d'opérateur logique sinon son indice dans ch.
 */
function premierOperateurLogique (chop, ch, pdeb, pfin, retour) {
  let ptch
  let car
  let resultat

  ptch = pfin
  do {
    car = ch.charAt(ptch)
    resultat = ptch
    if (car === ')') { ptch = CCbGlob.parentheseOuvrante(ch, pdeb, ptch) }
    ptch = ptch - 1
  } while ((ptch >= pdeb) && (car !== chop))
  switch (car) {
    case '&':
      retour.x = resultat - 1
      retour.y = Ope.And
      return resultat
    case '|':
      retour.x = resultat - 1
      retour.y = Ope.Or
      return resultat
    default:
      return -1
  } // switch car
}

/**
 * Renvoie le nombre de virgules trouvées pour un appel de paramètre.
 * Fonction appelée dans VerifieSyntaxe pour un appel de fonctions à plusieurs
 * variables.
 * @param {string} ch  La chaîne dans laquelle se fait la recherche.
 * @param {number} inddeb  L'inidce du début de la recherche dans ch.
 * @returns {number} : -1 si indeb >= ch.length sinon renvoie le nombre de virgules trouvées.
 */
function NombreVirgules (ch, inddeb) {
  let i = inddeb
  let nbvirg = 0
  if (i >= ch.length) { return -1 }
  let parentheses = 1
  while ((i < ch.length) && (parentheses >= 1)) {
    const car = ch.charAt(i)
    if (car === '(') { parentheses++ }
    if (car === ')') { parentheses-- }
    if ((car === ',') && (parentheses === 1)) { nbvirg++ }
    i++
  }
  return nbvirg
}
/**
 * Fonction vérifiant si item peut être suivi de itemSuivant
 * sur le plan syntaxique.
 * @param {number} item  le type de calcul actuel
 * @param {number} itemSuivant  Le type de calcul dont on teste s'il peut suivre item
 * @returns {boolean}
 */
function autorisationSyntaxe (item, itemSuivant) {
  switch (item) {
    case CCbGlob.Nombre:
      switch (itemSuivant) {
        case CCbGlob.Addition:
        case CCbGlob.Multiplication:
        case CCbGlob.Puissance:
        case CCbGlob.Carre:
        case CCbGlob.ParFermante:
        case CCbGlob.Inegalite:
        case CCbGlob.Blanc:
        case CCbGlob.Virgule:
          return true
        default:
          return false
      }
    case CCbGlob.Addition:
    case CCbGlob.Multiplication:
      switch (itemSuivant) {
        case CCbGlob.Nombre:
        case CCbGlob.ParOuvrante:
        case CCbGlob.Valeur:
        case CCbGlob.Fonction:
        case CCbGlob.Blanc:
          return true
        default:
          return false
      }
    case CCbGlob.Puissance:
      switch (itemSuivant) {
        case CCbGlob.Nombre:
        case CCbGlob.Valeur:
        case CCbGlob.ParOuvrante:
        case CCbGlob.Fonction:
        case CCbGlob.Blanc:
          return true
        default:
          return false
      }
    case CCbGlob.Fonction:
      switch (itemSuivant) {
        case CCbGlob.ParOuvrante:
        case CCbGlob.Blanc:
          return true
        default:
          return false
      }
    case CCbGlob.Valeur:
    case CCbGlob.Carre:
      switch (itemSuivant) {
        case CCbGlob.Addition:
        case CCbGlob.Multiplication:
        case CCbGlob.Puissance:
        case CCbGlob.Carre:
        case CCbGlob.ParFermante:
        case CCbGlob.Inegalite:
        case CCbGlob.Blanc:
        case CCbGlob.Virgule:
          return true
        default:
          return false
      }
    case CCbGlob.ParOuvrante:
      switch (itemSuivant) {
        case CCbGlob.Addition:
        case CCbGlob.ParOuvrante:
        case CCbGlob.Nombre:
        case CCbGlob.Valeur:
        case CCbGlob.Fonction:
        case CCbGlob.Blanc:
          return true
        default:
          return false
      }
    case CCbGlob.ParFermante:
      switch (itemSuivant) {
        case CCbGlob.ParFermante:
        case CCbGlob.Addition:
        case CCbGlob.Multiplication:
        case CCbGlob.Puissance:
        case CCbGlob.Carre:
        case CCbGlob.Inegalite:
        case CCbGlob.Blanc:
        case CCbGlob.Virgule:
          return true
        default:
          return false
      }
    case CCbGlob.Inegalite:
      switch (itemSuivant) {
        case CCbGlob.Addition:
        case CCbGlob.Nombre:
        case CCbGlob.ParOuvrante:
        case CCbGlob.Valeur:
        case CCbGlob.Fonction:
        case CCbGlob.Blanc:
          return true
        default:
          return false
      }
    case CCbGlob.Virgule:
      switch (itemSuivant) {
        case CCbGlob.Addition:
        case CCbGlob.Nombre:
        case CCbGlob.ParOuvrante:
        case CCbGlob.Valeur:
        case CCbGlob.Fonction:
        case CCbGlob.Blanc:
          return true
        default:
          return false
      }
    default:
      return false // / normalement inutile
  } // switch Item
}
/**
 * Fonction renvoyant l'indice dans la chaine ch de la première inégalité
 * rencontrée et -1 s'il n'y en a pas.
 * La recherche se fait de la fin de la chaine vers le début en sautant les parenthèses.
 * @param {string} ch La chaîne dans laquelle se fait la recherche
 * @param {number} pdeb L'indice du début de la recherche dans ch.
 * @param {number} pfin L'indice de la fin de la recherche dans ch
 * @param {Object} retour Objet qui sera modifié
 * @param {number} retour.x indice du caractère précédent l'inégalité
 * @param {number} retour.y opérateur associé à cette inégalité (défini dans COperateur)
 * @returns {number} :-1 si n'a pas trouvé d'inégalité sinon son indice dans ch
 */
function premiereInegalite (ch, pdeb, pfin, retour) {
  let ptch
  let car, carPrecedent
  let resultat

  ptch = pfin
  do {
    car = ch.charAt(ptch)
    resultat = ptch
    if (car === ')') { ptch = CCbGlob.parentheseOuvrante(ch, pdeb, ptch) }
    ptch = ptch - 1
  } while ((ptch >= pdeb) && (car !== '=') && (car !== '>') && (car !== '<'))
  switch (car) {
    case '<':
      retour.x = resultat - 1
      retour.y = Ope.Inf
      return resultat

    case '>':
      retour.x = resultat - 1
      retour.y = Ope.Sup
      if (ptch > pdeb) {
        ptch = resultat - 1
        carPrecedent = ch.charAt(ptch)
        if (carPrecedent === '<') {
          retour.x = ptch - 1
          retour.y = Ope.Diff
        }
      }
      return resultat

    case '=':
      if (ptch >= pdeb) {
        ptch = resultat - 1
        carPrecedent = ch.charAt(ptch)
        switch (carPrecedent) {
          case '<':
            retour.x = ptch - 1
            retour.y = Ope.InfOuEgal
            return resultat
          case '>':
            retour.x = ptch - 1
            retour.y = Ope.SupOuEgal
            return resultat
          default:
            retour.x = ptch
            retour.y = Ope.Egalite
            return resultat
        } // switch CarPrecedent
      }
      return -1 // Ajout version 6.4.1 pour expliciter le retour par défaut

    default:
      return -1
  } // switch car
}
/**
 * Fonction renvoyant l'indice du premier caractère dans ch étant un + ou  un -
 * La recherche se fait de la fin de la chaine vers le début en sautant les parenthèses.
 * @param {string} ch  La chaîne dans laquelle se fait la recherche.
 * @param {number} pdeb  L'indice du début de la recherche dans ch.
 * @param {number} pfin  L'indice de la fin de la recherche dans ch.
 * @returns {number} : -1 si n'a pas trouvé de + ou - ou sinon l'indice du caractère.
 */
function premiereSomme (ch, pdeb, pfin) {
  let ptch
  let car
  let resultat

  ptch = pfin
  do {
    car = ch.charAt(ptch)
    resultat = ptch
    if (car === ')') ptch = CCbGlob.parentheseOuvrante(ch, pdeb, ptch)
    ptch--
  } while ((ptch >= pdeb) && (car !== '+') && (car !== '-'))
  if ((car === '+') || (car === '-')) { return resultat } else { return -1 }
}
/**
 * Fonction renvoyant l'indice du premier caractère qui soit un * ou un / dans la chaîne ch.
 * La recherche se fait de la fin de la chaine vers le début en sautant les parenthèses.
 * @param {string} ch  La chaîne dans laquelle se fait la recherche.
 * @param {number} pdeb  L'indice du début de recherche dans ch.
 * @param {number} pfin  L'indice de la fin de recherche dans ch.
 * @returns {number} : -1 si aucun * ou / n'a été trouvé, sinon l'indice de premier carcatère trouvé.
 */
function premierProduit (ch, pdeb, pfin) {
  let ptch
  let car
  let resultat

  ptch = pfin
  do {
    car = ch.charAt(ptch)
    resultat = ptch
    if (car === ')') { ptch = CCbGlob.parentheseOuvrante(ch, pdeb, ptch) }
    ptch--
  } while ((ptch >= pdeb) && (car !== '*') && (car !== '/'))
  if ((car === '*') || (car === '/')) { return resultat } else { return -1 }
}
/**
 * Fonction renvoyantl'indice du premier signe ^ou ² renontré dans la chaîne ch.
 * La recherche se fait de la fin de la chaine vers le début en sautant les parenthèses.
 * @param {string} ch  La chaîne dans laquelle se fait la recherche.
 * @param {number} pdeb  L'indice du début de la recherche dans ch.
 * @param {number} pfin  L'indice de la fin de la recherche dans ch.
 * @returns {number} : -1 si n'a pas trouvé de ^ou  de ², sinon l'indice du caractère.
 */
function premierePuissance (ch, pdeb, pfin) {
  let ptch
  let car
  let resultat

  ptch = pfin
  do {
    car = ch.charAt(ptch)
    resultat = ptch
    if (car === ')') ptch = CCbGlob.parentheseOuvrante(ch, pdeb, ptch)
    ptch--
  } while ((ptch >= pdeb) && (car !== '^') && (car !== '²'))
  if ((car === '^') || (car === '²')) { return resultat } else { return -1 }
}
/**
 * Fonction renvoyant l'indice de la première virgule suivant une parenthèse
 * ouvrante dan la chaîne chaine.
 * Avant appel pDebut pointe sur le caractère suivant la parenthèse ouvrante.
 * @param {string} chaine  La chaîne dans laquelle se fait la recherche.
 * @param {number} pDebut  L'indice de début de la recherche.
 * @returns {number} : chaine.length si pas de virgule trouvée, sinon l'indice de la première virgule.
 */
function premiereVirgule (chaine, pDebut) {
  let car
  let somme = 1
  let i
  for (i = pDebut; i < chaine.length; i++) {
    car = chaine.charAt(i)
    if (car === '(') { somme++ } else if (car === ')') { somme-- } else if ((car === ',') && (somme === 1)) { return i }
  }
  return i // Pour satisfaire le compilateur mais on ne doit pas arriver ici
}
/**
 * Fonction élevant un nombre à la puissance n sans utiiser d'exponentielle
 * mais avec un algorithme optimisé n'utilisant que des multiplications.
 * @param {number} c1  L'opérande
 * @param {number} exposant  l'exposant (compris entre 1 et 255)
 * @returns {number} : Le résultat.
 */
function puisExpEnt (c1, exposant) {
  let tampon = c1
  let res = 1
  // Version n'utilisant que des décalages de bits pour optimisation
  let b = 128
  let indfin = 7
  while ((b & exposant) === 0) {
    indfin--
    b = b >> 1
  }
  for (let i = 0; i <= indfin; i++) {
    if ((exposant % 2) !== 0) {
      res = res * tampon
    }
    exposant >>= 1
    tampon = tampon * tampon
  }
  return res
}
/**
 * Fonction calculant le sinus de x en se ramenant à un opérande compris entre 0 et 2pi
 * pour une meilleure précision.
 * @param {number} x
 * @returns {number} : Le résultat.
 */
function sinus (x) {
  return Math.sin(x - Math.floor(x / (2 * Math.PI)) * 2 * Math.PI)
}
/**
 * Fonction calculant le cosinus de x en se ramenant à un opérande compris entre 0 et 2pi
 * pour une meilleure précision.
 * @param {number} x
 * @returns {number} : Le résultat.
 */
function cosinus (x) {
  return Math.cos(x - Math.floor(x / (2 * Math.PI)) * 2 * Math.PI)
}
/**
 * Fonction calculant la tangente de x en se ramenant à un opérande compris entre 0 et 2pi
 * pour une meilleure précision.
 * @param {number} x
 * @returns {number} : Le résultat.
 */
function tangente (x) {
  return Math.tan(x - Math.floor(x / (2 * Math.PI)) * 2 * Math.PI)
}
/**
 * Calcul d'une intégrale par la méthode de Simpson.
 * @param {CCb} f  un Ccb représentant une fonction de n variables, la
 * dernière étant la variable d'intégration.
 * @param {string[]} va  un tableau représentant les paramètres passés à la fonction
 * (le dernier sera en fait affecté dans la fonction).
 * @param {number} a  la borne inférieure d'intégration.
 * @param {number} b  la borne supérieure d'intégration.
 * @param {boolean} infoRandom  true si les caclusl aléatoires avec rand
 * doivent être réactualisés.
 * @returns {number} : Le résultat arrpoché de l'intégrale.
 */
function integrale (f, va, a, b, infoRandom) {
  let s1, s2
  let t1, t2
  const n = va.length
  let k
  if (a === b) return 0
  const nb = CCbGlob.nombreSubdivisionsPourSimpson // Le nombre de subdivisions
  const pas = (b - a) / nb
  // s = f.resultatFonction(infoRandom, a) + f.resultatFonction(infoRandom, b)
  va[n - 1] = a
  const ima = f.resultatFonction(infoRandom, va)
  va[n - 1] = b
  const s = ima + f.resultatFonction(infoRandom, va)
  t1 = a
  t2 = a - pas / 2
  s1 = 0
  s2 = 0
  for (k = 1; k < nb; k++) {
    t1 = t1 + pas
    va[n - 1] = t1
    s1 = s1 + f.resultatFonction(infoRandom, va)
  }
  for (k = 1; k <= nb; k++) {
    t2 = t2 + pas
    va[n - 1] = t2
    s2 = s2 + f.resultatFonction(infoRandom, va)
  }
  return (b - a) * (s + 2 * s1 + 4 * s2) / (6 * nb)
}
/**
 * Fonction recherchant si la chaîne ch commence par le nom d'une fonction réelle
 * prédéfinie.
 * Si oui, renvoie la longeur du nom de la fonction,  résultat.getValeur() renvoie l'indice
 * du nom de la fonction dans le tableau définissant les noms de fonctions et
 * combreVariables.getValeur() renvoie le nombre de variabels de la fonction.
 * @param {string} ch  La chaîne à analyser.
 * @param {Pointeur} resultat getValue() L'indice du nom de la fonction si un nom a été trouvé
 * sinon 0.
 * @param {Pointeur} nombreVariables  Le nombre de variables de la fonction si une fonction a été trouvée.
 * @returns {number} : La longueur du nom de la fonction si une fonction a été trouvée.
 */
function commenceParNomFonctionReellePredefinie (ch, resultat, nombreVariables) {
  // Fonction optimisée version 7.0 en utilisant une regexp
  // On regarde d'abord si ch commence bien par des caractères alphanumériques suivis d'une parenthèse ouvrante
  const chunks = /(^[a-z]+)\(/.exec(ch.toLowerCase())
  if (!chunks) return 0
  const deb = chunks[1] // Contient ce qui a été capturé par l'expression régulière (les caractères alpha sans la parenthèse)
  let i
  for (i = 0; i < Opef.nomsFoncs.length; i++) {
    const st = Opef.nomsFonctions(i).toLowerCase()
    if (deb === st) {
      nombreVariables.setValue(1) // Nombre de variables de la fonction
      resultat.setValue(i) // Indice de la fonction
      return st.length
    }
  }
  for (i = 0; i < Opef2v.nomsFoncs2Var.length; i++) {
    const st = Opef2v.nomsFonctions2Var(i).toLowerCase()
    if (deb === st) {
      nombreVariables.setValue(2)
      resultat.setValue(i)
      return st.length
    }
  }
  for (i = 0; i < Opef3v.nomsFoncs3Var.length; i++) {
    const st = Opef3v.nomsFonctions3Var(i).toLowerCase()
    if (deb === st) {
      nombreVariables.setValue(3)
      resultat.setValue(i)
      return st.length
    }
  }
  for (i = 0; i < Opef4v.nomsFoncs4Var.length; i++) {
    const st = Opef4v.nomsFonctions4Var(i).toLowerCase()
    if (deb === st) {
      nombreVariables.setValue(4)
      resultat.setValue(i)
      return st.length
    }
  }
  for (i = 0; i < Opef5v.nomsFoncs5Var.length; i++) {
    const st = Opef5v.nomsFonctions5Var(i).toLowerCase()
    if (deb === st) {
      nombreVariables.setValue(5)
      resultat.setValue(i)
      return st.length
    }
  }
  return 0
}
/**
 * Fonction recherchant si la chaîne ch commence par le nom d'une fonction complexe
 * prédéfinie.
 * Si oui, renvoie la longeur du nom de la fonction,  résultat.getValeur() renvoie l'indice
 * du nom de la fonction dans le tableau définissant les noms de fonctions et
 * combreVariables.getValeur() renvoie le nombre de variabels de la fonction.
 * @param {string} ch  La chaîne à analyser.
 * @param {Pointeur} resultat  La longueur du nom de la fonction si un nom a été trouvé
 * sinon 0.
 * @param {Pointeur} nombreVariables  Le nombre de vraiables de la fonction si une fonction a été trouvée.
 * @returns {number} : La longueur du nom de la fonction si une fonction a été trouvée.
 */
function commenceParNomFonctionComplexePredefinie (ch, resultat, nombreVariables) {
  // Fonction optimisée version 7.0 en utilisant une regexp
  // On regarde d'abord si ch commence bien par des caractères alphanumériques suivis d'une parenthèse ouvrante
  const chunks = /(^[a-z]+)\(/.exec(ch.toLowerCase())
  if (!chunks) return 0
  const deb = chunks[1] // Contient ce qui a été capturé par l'expression régulière (les caractères alpha sans la parenthèse)
  let i
  for (i = 0; i < Opef.nomsFoncsComplexes.length; i++) {
    const st = Opef.nomsFonctionsComplexes(i).toLowerCase()
    if (deb === st) {
      nombreVariables.setValue(1)
      resultat.setValue(i + Opef.indPremiereFoncComplexe)
      return st.length
    }
  }
  for (i = 0; i < Opef2v.nomsFoncsComplexes2Var.length; i++) {
    const st = Opef2v.nomsFonctionsComplexes2Var(i).toLowerCase()
    if (deb === st) {
      nombreVariables.setValue(2)
      resultat.setValue(i + Opef2v.indicePremiereFonctionComplexe)
      return st.length
    }
  }
  for (i = 0; i < Opef3v.nomsFoncsComplexes3Var.length; i++) {
    const st = Opef3v.nomsFonctionsComplexes3Var(i).toLowerCase()
    if (deb === st) {
      nombreVariables.setValue(3)
      resultat.setValue(i + Opef3v.indicePremiereFonctionComplexe)
      return st.length
    }
  }
  for (i = 0; i < Opef4v.nomsFoncsComplexes4Var.length; i++) {
    const st = Opef4v.nomsFonctionsComplexes4Var(i).toLowerCase()
    if (deb === st) {
      nombreVariables.setValue(4)
      resultat.setValue(i + Opef4v.indicePremiereFonctionComplexe)
      return st.length
    }
  }
  for (i = 0; i < Opef5v.nomsFoncsComplexes5Var.length; i++) {
    const st = Opef5v.nomsFonctionsComplexes5Var(i).toLowerCase()
    if (deb === st) {
      nombreVariables.setValue(5)
      resultat.setValue(i + Opef5v.indicePremiereFonctionComplexe)
      return st.length
    }
  }
  return 0
}
/**
 * Fonction renvoyant true si chaine est le nom d'une fonction réelle ou
 * complexe prédéfinie
 * @param {string} chaine
 * @returns {boolean}
 */
function egalNomFonctionReelleOuComplexePredefinie (chaine) {
  const set = tabNomsFoncPred()
  return set.has(chaine)
}
/**
 * Fonction renvoyant true si la chaine ch commence par le nom d'une fonction
 * prédéfinie (réelle ou complexe) suivi d'une parenthèse.
 * Si oui, longNom.getValue() renvoie la longueur du nom.
 * La recherche commence à l'indice indstart
 * @param {string} pChaine  La chaîne dans laquelle se fait la recherche.
 * @param {number} indstart
 * @param {Pointeur} longNom
 * @returns {boolean}
 */
function commenceParNomFonctionPredefinieSuivieParOuvr (pChaine, indstart, longNom) {
  const set = tabNomsFoncPred()
  const ch = pChaine.substring(indstart).toLowerCase()
  // On regarde d'abord si ch commence bien par des caractères alphanumériques suivis d'une parenthèse ouvrante
  const chunks = /(^[a-z]+)\(/.exec(ch)
  if (!chunks) return false
  const deb = chunks[1] // Contient ce qui a été capturé par l'expression régulière (les caractères alpha sans la parenthèse)
  if (!set.has(deb)) return false
  longNom.setValue(deb.length)
  return true
}

const CCbGlob = {
  nombreMaxiIterations: 200000, // Nombre maxi d'itérations total dans les boucles
  nombreMaxObj: 250000, // Nombre maxi d'objets qu'on peut créer pour les impémentations de constructions récursives et itératives
  nombreSubdivisionsPourSimpson: 200, // Pour calcul intégrale par Simpson
  nombreMaxParentheses: 64, // Nombre maxi de parenthèses imbriquées
  // Constantes définissant les opérations de base
  Nombre: 0,
  Addition: 1,
  Multiplication: 2,
  Puissance: 3,
  Fonction: 4,
  Valeur: 5,
  ParOuvrante: 6,
  ParFermante: 7,
  // CCb.Inferieur = 8; // Inutile
  Inegalite: 9,
  Blanc: 10,
  Carre: 11,
  Virgule: 12,
  // Constantes ajoutées version 4.6 pour éviter des appels à getClass
  // Ces constantes définissent mes natures des objets de type CCb
  natAppelFonction: 1,
  natAppelFonctionInterne: 1 << 1,
  natAppelFonctionInterneNVar: 1 << 2,
  natAppelFonctionNVar: 1 << 3,
  natConstante: 1 << 4,
  natConstantei: 1 << 5,
  natFonction: 1 << 6,
  natFonction2Var: 1 << 7,
  natFonction3Var: 1 << 8,
  natIntegraleDansFormule: 1 << 9,
  natMoinsUnaire: 1 << 10,
  natOperation: 1 << 11,
  // natPointeurFonction  :  1 << 12, Supprimé version mtgApp. Inutile
  natPuissance: 1 << 13,
  natResultatValeur: 1 << 14,
  natResultatValeurComplexe: 1 << 15,
  natSommeDansFormule: 1 << 16,
  natProduitDansFormule: 1 << 17,
  natVariableFormelle: 1 << 18,
  natPrimitive: 1 << 19,
  natMat: 1 << 19, // Ajout version 4.7 : matrice réelle
  natTermMat: 1 << 21, // Ajout version 4.7 : Terme d'une matrice réelle
  parentheseOuvrante,
  parentheseFermante,
  parentheseFermanteApresVirgule,
  indiceCaractereVirgule,
  premierOperateurLogique,
  NombreVirgules,
  autorisationSyntaxe,
  premiereInegalite,
  premiereSomme,
  premierProduit,
  premierePuissance,
  premiereVirgule,
  puisExpEnt,
  sinus,
  cosinus,
  tangente,
  integrale,
  commenceParNomFonctionReellePredefinie,
  commenceParNomFonctionComplexePredefinie,
  egalNomFonctionReelleOuComplexePredefinie,
  commenceParNomFonctionPredefinieSuivieParOuvr
}

export default CCbGlob