legacy/outils/espace/fctsEspace.js

/** @module legacy/outils/espace/fctsEspace */

import { j3pRandomTab, j3pVirgule } from 'src/legacy/core/functions'

import { pgcdMulti } from 'sesajs/number'

/**
 * Recherche d’occurence d’une valeur : nombre et positions
 * @param {string|number} [occurenceCherchee] valeur recherchée dans le tableau
 * @param {Array} [monTableau]
 * @return {Object} {nombre, emplacements} nombre d’apparition de la valeur cherchée dans le tableau et tableau des index où a été trouvée cette valeur
 */
export function occurences (occurenceCherchee, monTableau) {
  const stock = {}
  const tabDesOcc = []
  let count = 0
  let pos = monTableau.indexOf(occurenceCherchee)

  while (pos !== -1) {
    count++
    tabDesOcc.push(pos)
    pos = monTableau.indexOf(occurenceCherchee, pos + 1)
  }
  stock.nombre = count
  stock.emplacements = tabDesOcc
  return stock
}
/**
 * Crée un tableau contenant plusieurs fois un même élément
 * @param {string|number} [elementIdentique] valeur à dupliquer
 * @param {Number} [taille] nombre de fois où on la souhiate
 * @return {Array} tableau contenant taille fois la valeur elementIdentique
 */
export function creerTableauDe (elementIdentique, taille) {
  // Cette fonction crée un tableau de dimmension taille en le remplissant d’élement identiques
  const stock = []
  for (let k = 0; k < taille; k++) {
    stock.push(elementIdentique)
  }
  return stock
}

/**
 * Déterminant de deux vecteurs du plan
 * @param {Array} [tabU] tableau de deux éléments (vecteur du plan)
 * @param {Array} [tabV] tableau de deux éléments (vecteur du plan)
 * @return {Object} {valeur, ecriture} valeur du déterminant et écriture du calcul (x1*y2-y2*x1)
 */
export function determinant (tabU, tabV) {
  const stock = {}
  const valeur = tabU[0] * tabV[1] - tabU[1] * tabV[0]
  let ecriture = tabU[0] + '\\times '
  if (j3pVirgule(tabV[1]).substring(0, 1) === '-') {
    ecriture = ecriture + '(' + tabV[1] + ')-'
  } else {
    ecriture = ecriture + tabV[1] + '-'
  }
  if (j3pVirgule(tabU[1]).substring(0, 1) === '-') {
    ecriture = ecriture + '(' + tabU[1] + ')\\times '
  } else {
    ecriture = ecriture + tabU[1] + '\\times '
  }
  if (j3pVirgule(tabV[0]).substring(0, 1) === '-') {
    ecriture = ecriture + '(' + tabV[0] + ')'
  } else {
    ecriture = ecriture + tabV[0]
  }
  stock.valeur = valeur
  stock.ecriture = ecriture
  return stock
}

/**
 * Déterminant de trois vecteurs de l’espace
 * @param {Array} [U] tableau de trois éléments (vecteur de l’espace)
 * @param {Array} [V] tableau de trois éléments (vecteur de l’espace)
 * @param {Array} [W] tableau de trois éléments (vecteur de l’espace)
 * @return {Number} déterminant de la matrice
 */
export function determinant3D (U, V, W) {
  return U[0] * V[1] * W[2] + U[2] * V[0] * W[1] + U[1] * V[2] * W[0] - (U[2] * V[1] * W[0] + U[0] * V[2] * W[1] + U[1] * V[0] * W[2])
}

/**
 * Colinéarité de deux vecteurs de l’espace
 * @param {Array} [tabU] tableau de trois éléments (vecteur de l’espace)
 * @param {Array} [tabV] tableau de trois éléments (vecteur de l’espace)
 * @return {Boolean} renvoie true si les vecteurs sont colinéarires
 */
export function UcolV (tabU, tabV) {
  return (Math.abs(tabU[0] * tabV[1] - tabU[1] * tabV[0]) < 1e-12) && (Math.abs(tabU[1] * tabV[2] - tabU[2] * tabV[1]) < 1e-12) && (Math.abs(tabU[0] * tabV[2] - tabU[2] * tabV[0]) < 1e-12)
}

/**
 * Egalité de tableaux de nombres
 * @param {Array} [tab1] tableau de number
 * @param {Array} [tab2] tableau de number
 * @return {Boolean} renvoie true si les tableaux sont de la même taille et leurs valeurs tableaux sont deux à deux égales
 */
export function compareTabNum (tab1, tab2) {
  if (tab1.length < tab2.length) return false
  for (let k = 0; k < tab2.length; k++) {
    if (Math.abs(tab1[k] - tab2[k]) > 1e-12) {
      return false
    }
  }
  return true
}

/**
 * Cette fonction renvoie true si on retrouve tous les éléments du tableau tabJeLesVeux dans monTableau
 * Sauf si l’un des éléments du tableau tabJeLeVeux est de proba nulle
 * @param {Array} [tabJeLesVeux] tableau
 * @param {Array} [probaJeLesVeux] tableau de number entre 0 et 1 (proba)
 * @param {Array} [monTableau] tableau
 * @return {Boolean}
 */
export function jeLesVeuxDansMonTab (tabJeLesVeux, probaJeLesVeux, monTableau) {
  for (let k = 0; k < tabJeLesVeux.length; k++) {
    if (!monTableau.includes(tabJeLesVeux[k]) && probaJeLesVeux[k] !== 0) return false
  }
  return true
}

/**
 * Cette fonction crée un tableau de taille dimension, en y mettant des éléments pris dans tabDesChoix, et en respectant la tabDesProbas imposée.
 * de telle sorte que tous les éléments de tabDesChoix apparaissent dans la mesure où dimension>=longueur de tabDesChoix
 * @param {Array} [tabDesChoix] tableau
 * @param {Array} [tabDesProbas] tableau de number entre 0 et 1 (proba)
 * @param {Number} [dimension] taille du tableau souhaité
 * @return {Array}
 */
export function creeTableauAleatoire (tabDesChoix, tabDesProbas, dimension) {
  let stock = []
  if (dimension < tabDesChoix.length) {
    for (let k = 0; k < dimension; k++) {
      stock.push(j3pRandomTab(tabDesChoix, tabDesProbas))
    }
  } else {
    do {
      stock = []
      for (let k = 0; k < dimension; k++) {
        stock.push(j3pRandomTab(tabDesChoix, tabDesProbas))
      }
    } while (!jeLesVeuxDansMonTab(tabDesChoix, tabDesProbas, stock))
  }
  return stock
}

/**
 * Cette fonction écrit correctement la somme des ak*bk où ak et bk sont les coordonnées respectives des tableau tabAk, tabBk
 * @param {Array} [tabAk] tableau de number
 * @param {Array} [tabBk] tableau de number
 * @return {Object} {valeur, ecriture} valeur du produit scalaire des deux vecteurs et écriture de ce produit
 */
export function sommeAkBk (tabAk, tabBk) {
  // le rôle de cette fonction est d’écrire correctement ie avec des parenthèses en cas de signe "-" devant l’un des ak ou bk la somme des ak*bk
  // tabAk et tabBk sont des tableaux numériques
  // renvoie l’objet stock : .valeur: le résultat de cette somme et .ecriture l’écriture mathquillcorrecte
  let valeur = tabAk[0] * tabBk[0]
  let ecriture = tabAk[0] + '\\times '
  if (j3pVirgule(tabBk[0]).substring(0, 1) === '-') {
    ecriture = ecriture + '(' + tabBk[0] + ')+'
  } else {
    ecriture = ecriture + tabBk[0] + '+'
  }
  for (let k = 1; k < tabAk.length; k++) {
    valeur = valeur + tabAk[k] * tabBk[k]
    if (j3pVirgule(tabAk[k]).substring(0, 1) === '-') {
      ecriture = ecriture + '(' + tabAk[k] + ')\\times '
    } else {
      ecriture = ecriture + tabAk[k] + '\\times '
    }
    if (j3pVirgule(tabBk[k]).substring(0, 1) === '-') {
      ecriture = ecriture + '(' + tabBk[k] + ')+'
    } else {
      ecriture = ecriture + tabBk[k] + '+'
    }
  }
  ecriture = ecriture.substring(0, ecriture.length - 1)
  return { valeur, ecriture }
}

/**
 * Cette fonction renvoie un nouveau tableau obtenu par application de ma_fonction à tous les éléments du tableau
 * @param {Function} [maFonction] fonction à appliquer à chaque valeur du tableau
 * @param {Array} [monTableau] tableau
 * @return {Array}
 */
export function fonctionTab (maFonction, monTableau) {
  return monTableau.map(maFonction)
}

/**
 * Cette fonction renvoie vrai si le tableau n’est composé que de true
 * @param {Array} [monTableau] tableau de boolean
 * @return {Boolean}
 */
export function tableauVrai (monTableau) {
  return monTableau.every((elt) => elt)
}

/**
 * Cette fonction renvoie un objet correspondant à l’écriture d’une fraction
 * @param {number} [nbre] nombre réel
 * @return {Object} {ch, mathq, num, den, valeur} ch="a/b" avec pgcd(a,b)=1 et a/b=nbre, mathq est l’écriture de cette fraction en latex, valeur vaut nbre (qui est du type number nécessairement)
 */
export function fraction (nbre) {
  // Cette fonction retourne un objet
  // .ch :"a/b" avec a/b mise en fraction irréductible du nbre
  // .mathq : fraction éditée dans mathquill
  // .num : numérateur
  // .denom:dénominateur
  // .valeur (c’est le nombre saisi)
  const stock = {}
  if (typeof (nbre) !== 'number') return console.error(nbre, 'doit être du type nuber')
  stock.valeur = nbre
  let q = Math.abs(nbre)
  const x = q
  let b = 1
  let d = 1
  let s = 0
  let n = Math.floor(q)
  const signeNbre = (nbre < 0) ? -1 : 1
  if (x <= 1e-12) {
    stock.ch = '0'
    stock.mathq = '0'
    stock.num = 0
    stock.denom = 1
  } else {
    while (Math.abs(x * d - n) > 1e-8) {
      s = q - b * Math.floor(q / b)
      n = Math.floor(x / b)
      d = Math.floor(1 / b)
      q = b
      b = s
    }
    if (d === 1) {
      n = signeNbre * n
      stock.ch = n
      stock.num = n
      stock.denom = 1
      stock.mathq = (n < 0) ? '-' + Math.abs(n) : n
      // stock.mathq="$"+n+"$";
    } else {
      stock.mathq = (nbre < 0) ? '-\\frac{' + n + '}{' + d + '}' : '\\frac{' + n + '}{' + d + '}'

      stock.num = signeNbre * n
      stock.denom = d
      stock.ch = signeNbre * n + '/' + d
    }
  }
  return stock
}

function egal (a, b) {
  // Renvoie true si la différence entre a et b est inférieure ou égale à 10^-9
  return Math.abs(a - b) < 1e-9
}

/**
 * Cette fonction vérifie si monNombre est entier ou non
 * @param {number} [monNombre] nombre réel
 * @return {Boolean}
 */
export function estEntier (monNombre) {
  return egal(monNombre, Math.floor(monNombre))
}

/**
 * Cette fonction écrit correctement le monome sous la forme coeff*variable
 * @param {number} [valeurCoeff] nombre réel qui, s’il est décimal, sera écrit sous forme fractionnaire
 * @param {string} [devantCoeff] '+' ou '-'
 * @param {string} [nomVariable] nomVariable peut être une variable quelconque ou une chaine mathquill comme celle d’un vecteur
 * @return {String}
 */
export function monome (valeurCoeff, devantCoeff, nomVariable) {
  let stock = ''
  if (!egal(valeurCoeff, 0)) {
    if (egal(valeurCoeff, 1)) {
      if (nomVariable === '') {
        stock = devantCoeff + '1'
      } else {
        stock = devantCoeff + nomVariable
      }
    }
    if (egal(valeurCoeff, -1)) {
      stock = (nomVariable === '') ? '-1' : '-' + nomVariable
    }
    if (!egal(valeurCoeff, 1) && !egal(valeurCoeff, -1)) {
      if (valeurCoeff < 0) {
        stock = fraction(valeurCoeff).mathq + nomVariable
      } else {
        stock = devantCoeff + fraction(valeurCoeff).mathq + nomVariable
      }
    }
  }
  return stock
}

/**
 * Cette fonction écrit correctement une somme de monome (pour un polynôme ou autre, la variable pouvant être différente d’un monome à l’autre)
 * @param {Array} [tabCoeff] nombre réel qui, s’il est décimal, sera écrit sous forme fractionnaire
 * @param {Array} [tabVariables]
 * @return {String}
 */
export function sommeMonomes (tabCoeff, tabVariables) {
  let stock = ''
  let entreLesChaines = ''
  for (let i = 0; i < tabCoeff.length; i++) {
    if (tabCoeff[i] !== 0 && i > 0) entreLesChaines = '+'
    stock = stock + monome(tabCoeff[i], entreLesChaines, tabVariables[i])
  }
  if (stock.charAt(0) === '+') stock = stock.substring(1, stock.length)
  if (stock === '') stock = '0'
  return stock
}

/**
 * Cette fonction renvoie le vecteur AB version mathquil en écrivant vLatex("AB")
 * @param {string} [strAB] chaine représentant un vecteur
 * @return {string}
 */
export function vLatex (strAB) {
  return '\\vecteur{' + strAB + '}'
}

/**
 * Cette fonction renvoie un entier aléatoire entre entierMini inclus, entierMaxi inclus sans tomber sur les éléments du tableau tabSauf
 * @param {number} [entierMini] entier minimal
 * @param {number} [entierMaxi] entier maximal
 * @param {Array} [tabSauf] liste des entiers à éviter
 * @return {Number}
 */
export function entierAlea (entierMini, entierMaxi, tabSauf) {
  let stock
  do {
    stock = entierMini + Math.floor((entierMaxi - entierMini + 1) * Math.random())
  } while (tabSauf.includes(stock))
  return stock
}
/**
 * Cette fonction renvoie le vecteur égal au produit vectoriel de deux vecteurs de l’espace
 * @param {Array} [U] tableau de 3 nombres
 * @param {Array} [V] tableau de 3 nombres
 * @return {Array}
 */
export function produitVectoriel (U, V) {
  return [U[1] * V[2] - U[2] * V[1],
    U[2] * V[0] - U[0] * V[2],
    U[0] * V[1] - U[1] * V[0]]
}

/**
 * Calcul le pgcd du tableau de nombres d’une manière bizarre
 * - si y’en a pas ça retourne 0
 * - si y’en a qu’un ça retourne sa valeur absolue
 * - si y’en a plusieurs, on prend leur valeur absolue, puis on regarde le pgcd par paire en retournant le plus grand des deux si l’un est nul…
 * @param nombres
 * @private
 * @return {number}
 */
export function pgcdZarb (nombres) {
  // ça les calculait par deux, en prenant le plus grand lorsque l’un était nul, ça revient à supprimer les 0 du tableau
  nombres = nombres.filter(nb => nb !== 0).map(Math.abs)
  if (nombres.length === 0) return 0
  if (nombres.length === 1) return nombres[0]
  return pgcdMulti(nombres)
}