objets/CCb.js

/*
 * MathGraph32 Javascript : Software for animating online dynamic mathematics figures
 * https://www.mathgraph32.org/
 * @Author Yves Biton (yves.biton@sesamath.net)
 * @License: GNU AGPLv3 https://www.gnu.org/licenses/agpl-3.0.html
 */
import COb from './COb'
import Ope from '../types/Ope'
import Opef from '../types/Opef'
import CCbGlob from '../kernel/CCbGlob'
import NatCal from 'src/types/NatCal'

// parmi toutes ces dépendances, on ne peut pas charger ici tous les objetTruc qui ont du
// objetTruc.prototype = new CCb()
// on commente ici et redéclare dans les méthodes qui les utilisent
// et qui pourraient peut-être passer dans un module séparé

// import CAppelFonction from './CAppelFonction'
// import CAppelFonctionComplexe from './CAppelFonctionComplexe'
// import CAppelFonctionNVar from './CAppelFonctionNVar'
// import CAppelFonctionComplexeNVar from './CAppelFonctionComplexeNVar'
// import CAppelFonctionInterne from './CAppelFonctionInterne'
// import CAppelFonctionInterneNVar from './CAppelFonctionInterneNVar'
// import CConstante from './CConstante'
// import CConstantei from './CConstantei'
// import CFonction from './CFonction'
// import CFonction2Var from './CFonction2Var'
// import CFonction3Var from './CFonction3Var'
// import CIntegraleDansFormule from './CIntegraleDansFormule'
// import CMoinsUnaire from './CMoinsUnaire'
// import COp from './COperation'
// import CProduitDansFormule from './CProduitDansFormule'
// import CPuissance from './CPuissance'
// import CResultatValeur from './CResultatValeur'
// import CResultatValeurComplexe from './CResultatValeurComplexe'
// import CVariableFormelle from './CVariableFormelle'
// import CSommeDansFormule from './CSommeDansFormule'
// import CCbNull from './CCbNull'

export default CCb

/**
 * Classe ancêtre de tous les objets de type calcul pouvant être utilisés dans un arbre binaire de calcul.
 * @constructor
 * @extends COb
 * @param {CListeObjets} listeProprietaire  La liste propriétaire de l'objet
 */
function CCb (listeProprietaire) {
  if (arguments.length === 0) {
    COb.call(this)
  } else {
    COb.call(this, listeProprietaire)
    // Ajout version 4.8 pour optimiser la fonction depDe
    this.elementTestePourDependDe = null
    this.dependDeElementTeste = false
  }
}

CCb.prototype = new COb()
CCb.prototype.constructor = CCb
CCb.prototype.superClass = 'COb'
CCb.prototype.className = 'CCb'

/**
 * Fonction donnant la nature du calcul à redéfinir pout tous les descendants sauf CCbNull
 * @returns {number}
 */
CCb.prototype.nature = function () {
  return 0
}
// Ajout version 4.8
/**
 * Fonction mémorisant la dépendance de l'objet par rapport à une objet déjà testé
 * @param {boolean} resultat
 * @returns {boolean}
 */
CCb.prototype.memDep = function (resultat) {
  this.dependDeElementTeste = resultat
  return resultat
}

/**
 * Fonction rendant une chaîne de caractères représentant le calcul représenté
 * par this qui est un arbre binaire de calcul.
 * A redéfinir pour les desdendants.
 * Utiliser plutôt chaineCalculSansPar car le résultat peut contenir des parenthèses.
 * @param {string[]|null} variablesFormelles  Un tableau représentant les variables formelles
 * pour une fonction de plusieurs variables.
 * @returns {string}
 */
CCb.prototype.chaineCalcul = function (variablesFormelles = null) {
  return ''
}
/**
 * Fonction rendant une chaîne de caractères sans parenthèses externes
 * représentant le calcul représenté  * par this qui est un arbre binaire de calcul.
 * A redéfinir pour les desdendants.
 * @param {string[]} variablesFormelles  Un tableau représentant les variables formelles
 * pour une fonction de plusieurs variables.
 * @returns {string}
 */
CCb.prototype.chaineCalculSansPar = function (variablesFormelles) {
  return this.chaineCalcul(variablesFormelles)
}
/**
 * Sert à reconstituer une formule LaTeX à partir de son arbre binaire
 * conjointement à chaineLatexSansPar A redéfinir pour les descendants
 */
/**
 * Fonction rendant une chaîne de caractères représentant en laTeX le calcul représenté
 * par this qui est un arbre binaire de calcul.
 * A redéfinir pour les desdendants.
 * Utiliser plutôt chaineLatexSansPar car le résultat peut contenir des parenthèses.
 * @param {string[]} variablesFormelles  Un tableau représentant les variables formelles
 * pour une fonction de plusieurs variables.
 * @param {boolean} fracSimple true si les fractions doivent être représentées avec un \frac
 * sinon elles sont représentées par un \dfrac
 * Par exemple pour un exposant, on passe fracsimple à true pour que les exposants ne soient pas trop gros
 * @returns {string}
 */
CCb.prototype.chaineLatex = function (variablesFormelles, fracSimple = false) {
  return this.chaineCalcul(variablesFormelles, fracSimple)
}
/**
 * Sert à reconstituer une formule à partir de son arbre binaire conjointement
 * à chaineCalcul A redéfinir pour les descendants
 */
/**
 * Fonction rendant une chaîne de caractères sans parenthèses externes
 * représentant en laTeX le calcul représenté
 * par this qui est un arbre binaire de calcul.
 * A redéfinir pour les desdendants.
 * @param {string[]} variablesFormelles  Un tableau représentant les variables formelles
 * pour une fonction de plusieurs variables.
 * @param {boolean} fracSimple true si les fractions doivent être représentées avec un \frac
 * sinon elles sont représentées par un \dfrac
 * Par exemple pour un exposant, on passe fracsimple à true pour que les exposants ne soient pas trop gros
 * @returns {string}
 */
CCb.prototype.chaineLatexSansPar = function (variablesFormelles, fracSimple = false) {
  return this.chaineLatex(variablesFormelles, fracSimple)
}
/**
 * Renvoie true si le calcul est constant (c'est-à-dire nn'utilise pas de valeurs
 * dynamiques) sans faire appel à la fonction Rand().
 * @returns {boolean}
 */
CCb.prototype.estConstant = function () {
  return false
}
/**
 * Fonction renvoyant true si this est un calcul constant renvoyant la valeur 0.
 * Redéfinit dans CConstante.
 * @returns {boolean}
 */
CCb.prototype.estConstante0 = function () {
  return false
}
/**
 * Fonction renvoyant true si le calcul existe.
 * A redéfinir pour les descendants.
 * existe prend un paramètre boolean infoRandom
 * C'est dû à l'optimisation de la fonction de 3 variables si(test,
 * valeurSiVrai, valeurSiFaux) qui nécessite de connaître la valeur
 * du premier argument test pour savoir si le résultat existe ou non. Si test
 * renvoie 1, existe renvoie valeurSiVrai.existe(inforRandom)
 * et sinon ValeurSiFaux.existe(infoRandom)
 * @returns {boolean}
 */
CCb.prototype.existe = function () {
  return true
}
/**
 * Fonction à redéfinir pour tous les descendants.
 * Renvoie un clone de l'objet, listeSource étant la liste propriétaire de l'objet
 * et listeCible la liste propriétaire du clone à créer.
 * @param {CListeObjets} listeSource  La liste propriétaire de l'objet
 * @param {CListeObjets} listeCible  La liste propriétaire de l'objet clone à céer.
 * @returns {CCb}
 */
CCb.prototype.getClone = function (listeSource, listeCible) {
  return null
}
/**
  * Fonction à redéfinir pour tous les descendants.
  * Renvoie une copie conforme de l'objet dans sa liste propriétaire.
  * Utilisé dans les calculs de dérivées.
 * @returns {CCb}
 */
CCb.prototype.getCopie = function () {
  return null
}
// Revu par rapport à la version Java : Paramètre infoRandom supprimé car inutilisé
/**
 * Fonction utilisée pour savoir si une fonction utilisateur existe.
 * Est différent de existe à cause de la fonction de 3 variables si(test, valeurSiVrai, valeurSiFaux)
 * @returns {boolean}
 */
CCb.prototype.existePourFonc = function () {
  return this.existe()
}

/**
 * Fonction servant pour les fonctions à savoir quel est le nombre de variables.
 * @returns {number}
 */
CCb.prototype.nombreVariables = function () {
  return 0
}
/**
 * Fonction renvoyant le résultat du calcul.
 * A redéfinir pour les descendants.
 * @param {Boolean } infoRandom  true si les calculas aléatoires avec rand
 * @returns {number}
 */
CCb.prototype.resultat = function (infoRandom) {
  throw new Error('Appel de Ccb.resultat')
}
// Ajout version 6.7
/**
 * Renvoie un résultat qui est soit un nombre soit soir une matrice réelle sous forme de tableau
 * @param infoRandom
 * @returns {number}
 */
CCb.prototype.resultatMat = function (infoRandom) {
  return this.resultat(infoRandom)
}
// Ajout pour la version 2.0 calculs sur les complexes.
// Le résultat est renvoyé dans z
/**
 * Fonction renvoyant dans zRes le résultat d'un calcul complexe.
 * A redéfinir pour les descendnats.
 * @param {boolean} infoRandom  true si les calculs aléatoires avec rand
 * @param {Complexe} zRes  Le résultat complexe du calcul
 * @returns {void}
 */
CCb.prototype.resultatComplexe = function (infoRandom, zRes) {
  zRes.x = this.resultat(infoRandom)// Par défaut on suppose le calcul réel
  zRes.y = 0
}
/**
 * Appelé lors des appels de fonctions pour fournir le résultat du calcul.
 * A redéfinir pour les descendants.
 * @param {boolean} infoRandom  true si les calculas aléatoires avec rand
 * @param {number[]|string[]} valeurparametre  un tableau donnant les valeurs des paramètres pour une
 * fonction de une ou plusieurs variables.
 * @returns {number}
 */
CCb.prototype.resultatFonction = function (infoRandom, valeurparametre) {
  return this.resultat(infoRandom)
}
/**
 * Appelé lors des appels de fonctions pour fournir le résultat complexe du calcul.
 * A redéfinir pour les descendants.
* @param {boolean} infoRandom  true si les calculas aléatoires avec rand
 * @param {number[]|string[]} valeurParametre  un tableau donnant les valeurs des paramètres pour une
 * @param {Complexe} zRes  Le complexe contenant le résultat.
 * @returns {void}
 */
CCb.prototype.resultatFonctionComplexe = function (infoRandom, valeurParametre,
  zRes) {
  this.resultatComplexe(infoRandom, zRes)
}

CCb.prototype.initialisePourDependance = function () {
  this.elementTestePourDependDe = null
  this.elementTestePourDependDePourRec = null
}

/**
 * Fonction renvoyant true si le calcul dépend de p.
 * A redéfinir pour les descendants.
 * @param {CElementBase} p
 * @returns {boolean}
 */
CCb.prototype.depDe = function (p) {
  this.elementTestePourDependDe = p
  return false
}
/**
 * La fonction suivante renverra le même résultat que DepDe sauf dans le
 * cas où le calcul fait réferrence à la fonction Rand de génération d'un
 * nombre aléatoire
 * @param {CElementBase} p
 * @returns {boolean}
 */
CCb.prototype.dependDePourBoucle = function (p) {
  return false
}

// La possibilité que ind soit égalà à -1 a été rajoutée version 6.4.8.
// Sert à tester la dépendance par rapport à toute variable formelle
/**
 * Fonction renvoyant true si l'arbre pointé par this contient un appel de variable
 * formelle, ind étant l'indice de la variable pour une fonction de plusieurs variables ou -1 si
 * on teste la dépendance par rapport à toute variable.
 * A redéfinir pour tous les descendants.
 * @param {number} ind  Indice de la variable pour les fonctions à plusieurs variables.
 * @returns {boolean}
 */
CCb.prototype.dependDeVariable = function (ind) {
  return false // Redéfini pour les descendants
}

/**
 * Fonction qui renvoie le calcul équivalent à this dans lequel les apple de gauche(), left(), si()
 * et les appels de fonctions internes sont remplacés pas leur formule effective
 * Utilisé dans les exercices de calcul pour évaluer la réponse d'un élève
 * @returns {CCb}
 */
CCb.prototype.getCore = function () {
  return this
}

// Ajout version 6.3.4 pour traiter de la même façon la commutativité des opérateurs +,* & et |
/**
 * Ajoute à liste toutes les opérandes d'opérateur de nature natop
 * Est utilisé pour les opérateurs commutatifs: addition, multiplication, ou et et logique
 * @param liste
 * @param natop
 */
CCb.prototype.ajouteOperateursParNat = function (liste, natop) {
  const nat = this.nature()
  if (nat === CCbGlob.natOperation) {
    if (this.ope === natop) {
      this.operande1.ajouteOperateursParNat(liste, natop)
      this.operande2.ajouteOperateursParNat(liste, natop)
    } else { liste.push(this) }
  } else { liste.push(this) }
}

/**
 * Fonction fournissant un calcul dans lequel tous les CResultatValeur
 * et CResultatValeurComplexes sont remplacés par une constante dont la valeur
 * est le résultat du CResultatValeur ou CResultatValeurComplexes.
 * Versionn 6.7 : On rajoute un paramètre booléen bfrac. Si celui-ci est faux, les valeurs non
 * entières seront remplacées par un quotient correspondant à la fraction continue équivalente
 * à 10^(-12) près.
 * @param {boolean} bfrac
 * @returns {CCb}
 */
CCb.prototype.calculAvecValeursRemplacees = function (bfrac) {
  return this
}
/**
 * Renvoie un calcul normalisé équivalent au calcul this.
 * Si rempval est true les appels à des CResultatValeur ou CResultatValeurComplexe
 * sont remplacés par une constante.
 * @param {boolean} rempval si true les multiplications et divisions par 1 sont supprimées (pour les multiplications
 * elles ne sont supprimées que si eliminMult1 est à true
 * @param {boolean} sousdivnorm  Si true, les soustractions sont remplacées par des additions d'opposés et les divisions pas des multiplications par l'inverse.
 * @param {boolean} rempDecParFrac  Si, true, les nombres décimaux (avec au plus 9 décimales) et la fraction irréductible
 * qui les représentent sont considérés comme équivalents.
 * @param {boolean} eliminMult1 si false, quand rempval est true, les multiplications par 1 ne sont pas éliminées
 * @returns {CCb}
 */
CCb.prototype.calculNormalise = function (rempval, sousdivnorm, rempDecParFrac, eliminMult1 = true) {
  // Dans les autres cas on renvoie le même calcul pour les objets ne redéfinissant pas cette fonction (version 6.4.7)
  return this
}

/**
 * Fonction renvayant true si this est équivalent à calc
 * Deux calculs sont équivalents s'ils contiennent les mêmes opérateurs
 * sans tenier compte de l'odre des additions algébriques ou des multiplications.
 * Avant appel, this et calc doivent avoir été normalisés de façon
 * que this et calc ne contiennent que des additions et divisions.
 * @param {CCb} calc  le calcul dont on teste l'équivalence avec this.
 * @returns {boolean}
 */
CCb.prototype.estEquivalentA = function (calc) {
  const nat1 = this.nature()
  const nat2 = calc.nature()
  if (nat1 !== nat2) { return false }
  if (nat1 === CCbGlob.natConstante) {
    return this.valeur === calc.valeur
  }
  if (nat1 === CCbGlob.natMoinsUnaire) {
    return this.operande.estEquivalentA(calc.operande)
  }
  if (nat1 === CCbGlob.natFonction) {
    return (Opef.estEquivalent(this.opef, calc.opef)) && this.operande.estEquivalentA(calc.operande)
  }
  if (nat1 === CCbGlob.natAppelFonction) {
    // Correction version 6.4.8
    // Si on compare deux CintegraleDansFormule, ce sont les formules des fonctions à inétgerer qu'il faut comparer
    // car la fonction interne a été dupliquée.
    // return (this.fonctionAssociee === calc.fonctionAssociee) && this.operande.estEquivalentA(calc.operande)
    // Version 7.4.4 : Attention un appel de fonction est aussi utilisé pour les suites récurrentes
    // et les suites récurrentes ne sont pas définies par un calcul
    if ((this.fonctionAssociee === calc.fonctionAssociee) && this.operande.estEquivalentA(calc.operande)) return true
    else {
      if (this.fonctionAssociee.estDeNatureCalcul(NatCal.NTteSuiteRec)) return false
      return (this.fonctionAssociee.calcul.estEquivalentA(calc.fonctionAssociee.calcul)) && this.operande.estEquivalentA(calc.operande)
    }
  }
  if (nat1 === CCbGlob.natFonction2Var) {
    return (this.opef === calc.opef) && this.operande1.estEquivalentA(calc.operande1) &&
        this.operande2.estEquivalentA(calc.operande2)
  }
  if (nat1 === CCbGlob.natFonction3Var) {
    return (this.opef === calc.opef) && this.operande1.estEquivalentA(calc.operande1) &&
        this.operande2.estEquivalentA(calc.operande2) &&
        this.operande3.estEquivalentA(calc.operande3)
  }
  if (nat1 === CCbGlob.natAppelFonctionNVar) {
    if (this.fonctionAssociee !== calc.fonctionAssociee) return false
    // Correction version 6.3.3
    /*
    var op1 = new Array(this.nbVar);
    for (var i = 0; i < this.nbVar; i++)
      op1[i] = this.operandes[i];
    var op2 = new CCb[calc.nbVar];
    for (i = 0; i < calc.nbVar; i++)
      op2[i] = calc.operandes[i];
    var res = true;
    for (i = 0; i < this.nbVar; i++)
      res = res && op1[i].estEquivalentA(op2[i]);
    */
    let res = true
    // Ligne suivante optimisée version 6.4.1
    // for (var i = 0; i < this.nbVar; i++) { res = res && this.operandes[i].estEquivalentA(calc.operandes[i]) }
    for (let i = 0; (i < this.nbVar) && res; i++) { res = res && this.operandes[i].estEquivalentA(calc.operandes[i]) }
    return res
  }
  if (nat1 === CCbGlob.natVariableFormelle) {
    // CVariableFormelle for1 = (CVariableFormelle) this;
    // CVariableFormelle for2 = (CVariableFormelle) calc;
    return this.indvar === calc.indvar
  }
  if (nat1 === CCbGlob.natOperation) {
    // COperation op1 = (COperation) this;
    // COperation op2 = (COperation) calc;
    const natop1 = this.ope
    const natop2 = calc.ope
    if (natop1 !== natop2) {
      if (natop1 === Ope.InfOuEgal) {
        // a <= b équivalent à b>= a)
        if (natop2 === Ope.SupOuEgal) { return (this.operande1.estEquivalentA(calc.operande2) && this.operande2.estEquivalentA(calc.operande1)) } else return false // Rajouté version 4.7.2
      } else {
        if (natop1 === Ope.Inf) {
        // a < b équivalent à b > a)
          if (natop2 === Ope.Sup) { return (this.operande1.estEquivalentA(calc.operande2) && this.operande2.estEquivalentA(calc.operande1)) } else return false // Rajouté version 4.7.2
        } else {
          if (natop1 === Ope.SupOuEgal) {
            // a >= b équivalent à b <= a)
            if (natop2 === Ope.InfOuEgal) { return (this.operande1.estEquivalentA(calc.operande2) && this.operande2.estEquivalentA(calc.operande1)) } else return false // Rajouté version 4.7.2
          } else {
            if (natop1 === Ope.Sup) {
              // a > b équivalent à b < a)
              if (natop2 === Ope.Inf) { return (this.operande1.estEquivalentA(calc.operande2) && this.operande2.estEquivalentA(calc.operande1)) } else return false // Rajouté version 4.7.2
            } else return false
          }
        }
      }
    }
    // Les deux opérateurs sont les mêmes
    // Modification version 6.3.4 pour traiter de la même façon la commutativité des opérateurs +,* & et |
    if (natop1 === Ope.Plus || natop1 === Ope.Mult || natop1 === Ope.Or || natop1 === Ope.And) {
      // Si l'opérateur est une addition, multiplication, ou ou et logique, il faut vérifier que tous les opérandes sous-jascents sont
      // équivalents pour tenir compte de la commutativité de ces opérateurs
      const list1 = []
      const list2 = []
      this.ajouteOperateursParNat(list1, natop1)
      calc.ajouteOperateursParNat(list2, natop1)
      if (list1.length !== list2.length) return false
      for (let i = 0; i < list1.length; i++) {
        const c1 = list1[i]
        let trouve = false
        for (let j = 0; j < list2.length; j++) {
          const c2 = list2[j]
          if (c1.estEquivalentA(c2)) {
            trouve = true
            list2.splice(j, 1)
            break
          }
        }
        if (!trouve) { return false } else {
          list1.splice(i, 1)
          i-- // Car le terme d'indice i a été retiré
        }
      }
      return true
    }
    // a = b équivalent à b = a
    if ((natop1 === Ope.Egalite) || (natop1 === Ope.And) || (natop1 === Ope.Or)) {
      return (this.operande1.estEquivalentA(calc.operande1) && this.operande2.estEquivalentA(calc.operande2)) ||
          (this.operande1.estEquivalentA(calc.operande2) && this.operande2.estEquivalentA(calc.operande1))
    }
    // Dans les autres cas (division comprise, pour être équivalent les
    // opérandes respectifs
    // doivent être équivalents
    return (this.operande1.estEquivalentA(calc.operande1)) &&
        (this.operande2.estEquivalentA(calc.operande2))
  }
  if (nat1 === CCbGlob.natPuissance) {
    return (this.operande.estEquivalentA(calc.operande) && this.exposant.estEquivalentA(calc.exposant))
  }
  if (nat1 === CCbGlob.natConstantei) return true
  if (nat1 === CCbGlob.natResultatValeur) {
    return this.valeurAssociee === calc.valeurAssociee // Attention test d'identité en JavaScript
  }
  if (nat1 === CCbGlob.natResultatValeurComplexe) {
    return this.valeurAssociee === calc.valeurAssociee
  }
  if (nat1 === CCbGlob.natIntegraleDansFormule) {
    return this.calculASommer.estEquivalentA(calc.calculASommer) && this.bornea.estEquivalentA(calc.bornea) &&
      this.borneb.estEquivalentA(calc.borneb)
  }
  return false
}
/**
 * Renvoie true si this est un produit factorisé par calc ou simplement équivalent à calc
 * ou à son opposé.
 * Avant appel, this et calc doivent être rendus normalisés pour pouvoir reconnaître
 * les réductions au même dénominateur.
 * @param {CCb} calc  Le calcul dont on etste si c'est un facteur commun à this.
 * @returns {boolean}
 */
CCb.prototype.estFactorisePar = function (calc) {
  let list1, list2, i, j, c1, c2, trouve
  const nat1 = this.nature()
  if (nat1 === CCbGlob.natMoinsUnaire) {
    return this.operande.estFactorisePar(calc)
  } else {
    if (nat1 === CCbGlob.natOperation) {
      // COperation op1 = (COperation) this;
      const natop1 = this.ope
      if ((natop1 === Ope.InfOuEgal) || (natop1 === Ope.Inf) || (natop1 === Ope.SupOuEgal) || (natop1 === Ope.Sup) ||
          (natop1 === Ope.Egalite) || (natop1 === Ope.Diff)) {
        return ((this.operande1.estFactorisePar(calc) && this.operande2.estConstante0()) || (this.operande2.estFactorisePar(calc) && this.operande1.estConstante0()))
      }
      if (natop1 === Ope.Mult) {
        // Il faut vérifier si un des facteurs du produit et des facteurs sous-jascents est équivalent à calc
        list1 = []
        list2 = []
        this.ajouteOperateursParNat(list1, Ope.Mult)
        const nat2 = calc.nature()
        if (nat2 !== CCbGlob.natOperation) list2.push(calc)
        else {
          const natop2 = calc.ope
          if (natop2 === Ope.Mult) calc.ajouteOperateursParNat(list2, natop2)
          else list2.push(calc)
        }
        if (list1.length < list2.length) return false
        for (i = 0; i < list2.length; i++) {
          c2 = list2[i]
          trouve = false
          for (j = 0; j < list1.length; j++) {
            c1 = list1[j]
            if (c1.estEquivalentA(c2)) {
              trouve = true
              list1.splice(j, 1)
              break
            }
          }
          if (!trouve) return false
          else {
            list2.splice(i, 1)
            i-- // Car le terme d'indice i a été retiré
          }
        }
        return true
      }
    }
    return this.estEquivalentA(calc)
  }
}
/**
 * Fonction renvoyant true si le calcul est dérivable.
 * A redéfinir pour les descendants pour lesquels ce n'est pas dérivabel.
 * @returns {boolean}
 */
CCb.prototype.deriveePossible = function () {
  return true
}

/**
 * Fontion à redéfinir pout les descendants qui renvoie true si le calcul est bien un calcul acceptable
 * pour un calcul sur les vecteurs
 * @param {string[]} tabNames Un tableau de string contenant les noms de calculs complexes assimilés à des points ou des vecteurs
 * @returns {boolean}
 */
CCb.prototype.isCalcVect = function (tabNames) {
  return false
}

/**
 * Fonction à redéfinir pout les descendants qui renvoie true si le calcul peut être correct pour un calcul vectoriel
 * @param {string[]} tabNames Un tableau de string contenant les noms de calculs complexes assimilés à des points ou des vecteurs
 * @returns {boolean}
 */
CCb.prototype.isCalcOK4Vect = function (tabNames) {
  return true
}

/**
 * Fonction renvoyant true si this représente un résultat d'un calcul complexe nommé vect0
 * Sert pour les exercices de calcul sur les vecteurs ou de calcul de produit scalaire
 * Sera redéfini seulement pour CResultatValeurComplexe
 * @returns {boolean}
 */
CCb.prototype.isVectNul = function () {
  return false
}

/**
 * Fonction servant uniquement dans les exercices sur le produit scalaire pour rendre un calcul
 * adapté à un calcul de produit scalaire.
 * La seule différence avec un arbre de calcul normal est de remplacer un vecteur élevé au carré par le carré de sa norme
 * @param {string[]} tabNames : Tableau contenant les noms des calculs complexes considérés comme des vecteurs
 * @returns {CCb}
 */
CCb.prototype.getCalc4Prosca = function (tabNames) {
  return this
}

/**
 * Fonction appelée pour avoir le résultat d'un appel de fonction d'un calcul matriciel.
 * Ne pourra arriver que pour une somme indicée ou un produit indicé de calcul matriciel
 * @param {boolean} infoRandom  true si les calculas aléatoires avec rand
 * @param {number[]|string[]} valeurParametre  un tableau donnant les valeurs des paramètres pour une fonction matricielle
 * @returns {number}
 */
CCb.prototype.resultatMatFonction = function (infoRandom, valeurParametre) {
  return this.resultatMat(infoRandom, valeurParametre)
}

/**
 * La fonction suivante sert à pouvoir enchaîner les appels de focntion gauche et droit
 * @param {CCb} arg
 * @returns {CCb}
 */
export function getLeft (arg) {
  const nat = arg.nature()
  if (nat === CCbGlob.natOperation) {
    return arg.membreGauche()
  }
  if (nat === CCbGlob.natMoinsUnaire) {
    return arg.membreGauche()
  }
  if (nat === CCbGlob.natFonction) {
    switch (arg.opef) {
      case Opef.Left :
      case Opef.LeftC :
        return getLeft(getLeft(arg.operande))
      case Opef.Right :
      case Opef.RightC :
        return getLeft(getRight(arg.operande))
    }
  }
  if (nat === CCbGlob.natAppelFonction || nat === CCbGlob.natAppelFonctionNVar) {
    return getLeft(arg.fonctionAssociee.calcul)
  }
  if (nat === CCbGlob.natAppelFonctionInterne || nat === CCbGlob.natAppelFonctionInterneNVar) {
    return getLeft(arg.fonctionInterne)
  }
  // Tout appel de getLeft avec comme argument un CResultatValeur dont l'opérande est
  // autre chose qu'un calcul réel ou complexe donnera un résultat qui n'existe pas
  if (nat === CCbGlob.natResultatValeur || nat === CCbGlob.natResultatValeurComplexe) {
    return arg.membreGauche()
  }
  if (nat === CCbGlob.natFonction3Var) { // Fonction si conditionnel qui est la seule fonction de trois variables
    const x1 = arg.operande1.resultat(false)
    if (x1 === 1) return getLeft(arg.operande2)
    else return getLeft(arg.operande3)
  }
  return arg
}

/**
 * La fonction suivante sert à pouvoir enchaîner les appels de focntion gauche et droit
 * @param {CCb} arg
 * @returns {CCb}
 */
export function getRight (arg) {
  const nat = arg.nature()
  if (nat === CCbGlob.natOperation) {
    return arg.membreDroit()
  }
  if (nat === CCbGlob.natMoinsUnaire) {
    return arg.membreDroit()
  }
  if (nat === CCbGlob.natFonction) {
    switch (arg.opef) {
      case Opef.Left :
      case Opef.LeftC :
        return getRight(getLeft(arg.operande))
      case Opef.Right :
      case Opef.RightC :
        return getRight(getRight(arg.operande))
    }
  }
  if (nat === CCbGlob.natAppelFonction || nat === CCbGlob.natAppelFonctionNVar) {
    return getRight(arg.fonctionAssociee.calcul)
  }
  if (nat === CCbGlob.natAppelFonctionInterne || nat === CCbGlob.natAppelFonctionInterneNVar) {
    return getRight(arg.fonctionInterne)
  }
  // Tout appel de getRight avec comme argument un CResultatValeur dont l'opérande est
  // autre chose qu'un calcul réel ou complexe donnera un résultat qui n'existe pas
  if (nat === CCbGlob.natResultatValeur || nat === CCbGlob.natResultatValeurComplexe) {
    return arg.membreDroit()
  }
  if (nat === CCbGlob.natFonction3Var) { // Fonction si conditionnel qui est la seule fonction de trois variables
    const x1 = arg.operande1.resultat(false)
    if (x1 === 1) return getRight(arg.operande2)
    else return getRight(arg.operande3)
  }
  return arg
}

/**
 * Fonction qui renvoie un calcul obtenu en remplaçant les enchainements de Left et Right par leur résultat effectif
 * @param {CCb} arg
 * @returns {CCb}
 */
export function getRealCalc (arg) {
  const nat = arg.nature()
  if (nat === CCbGlob.natFonction) {
    switch (arg.opef) {
      case Opef.Left :
      case Opef.LeftC :
        return getLeft(getRealCalc(arg.operande))
      case Opef.Right :
      case Opef.RightC :
        return getRight(getRealCalc(arg.operande))
      case Opef.Core :
      case Opef.CoreC :
        return getRealCalc(arg.operande)
    }
  }
  return arg
}