types/Complexe.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 { chaineNombre, ConvDegRad, ConvRadDeg, divComp, uniteAngleRadian } from '../kernel/kernel'
// CCb est requis dans Complexe.prototype.expComp, pour l'être à l'exécution
// sinon on a des pbs de dépendances cycliques que webpack n'arrive pas à résoudre
// Remplacé par CCbGlob
import CCbGlob from '../kernel/CCbGlob'

export default Complexe

/**
 * Classe représentant un nombre complexe pour les calculs dans MathGraph32.
 * this.x est la partie réelle.
 * this.y est la partie imaginaire.
 * @constructor
 * @returns {Complexe}
 */
function Complexe () {
  if (arguments.length === 0) {
    this.x = 0
    this.y = 0
  } else if (arguments.length === 1) { // Construction à partir d'un autre complexe
    this.x = arguments[0].x
    this.y = arguments[0].y
  } else { // Construction par donnée de partie réelle et imaginaire
    this.x = arguments[0]
    this.y = arguments[1]
  }
}
/**
 * Fonction donnant une valeur à un nombre complee.
 * Avec un paramètre : Le paramètre est lui-même un Complexe
 * Avec deux paramètres : On donne une valeur à la partir réelle
 * et à la partie imaginaire.
 * @returns {void}
 */
Complexe.prototype.set = function () {
  if (arguments.length === 2) { // Construction par partie réelle et imaginaire
    this.x = arguments[0]
    this.y = arguments[1]
  } else { // Construction à partir d'un autre complexe
    this.x = arguments[0].x
    this.y = arguments[0].y
  }
}
/**
 * Fonction renvoyant true si la valeur réelle ou imaginaire de this n'est pas définie.
 * @returns {boolean}
 */
Complexe.prototype.isNanOrInfinite = function () {
  return !isFinite(this.x) || !isFinite(this.y)
}
/**
 * Fonction renvoyant l'argument principal du complexe s'il est non nul.
 * La réponse tient compte du paramètre uniteAngle
 * @param {KernelUniteAngle} uniteAngle 0 pour le radian, 1 pour le degré.
 * @returns {number} : L'argument principal.
 */
Complexe.prototype.argument = function (uniteAngle) {
  let resul
  if (this.x === 0) {
    if (this.y > 0) resul = Math.PI / 2
    else resul = -Math.PI / 2
  } else {
    const tangente = this.y / this.x
    if (this.x > 0) resul = Math.atan(tangente)
    else {
      if (this.y >= 0) resul = Math.atan(tangente) + Math.PI
      else resul = Math.atan(tangente) - Math.PI
    }
  }
  if (uniteAngle === uniteAngleRadian) return resul
  else return resul * ConvRadDeg
}
/**
 * Renvoie le module du nombre complexe.
 * @returns {number}
 */
Complexe.prototype.module = function () {
  return Math.sqrt(this.x * this.x + this.y * this.y)
}
/**
 * Renvoie dans le complexe res la somme de this avec z1.
 * @param {Complexe} z1
 * @param {Complexe} res
 * @returns {void}
 */
Complexe.prototype.sommeComp = function (z1, res) {
  res.x = this.x + z1.x
  res.y = this.y + z1.y
}
/**
 * Renvoie dans le complexe z1 la différence de this avec z1.
 * @param {Complexe} z1
 * @param {Complexe} res
 * @returns {void}
 */
Complexe.prototype.differenceComp = function (z1, res) {
  res.x = this.x - z1.x
  res.y = this.y - z1.y
}
/**
 * Renvoie dans le complexe res le produit de this avec z1.
 * @param {Complexe} z1
 * @param {Complexe} res
 * @returns {void}
 */
Complexe.prototype.produitComp = function (z1, res) {
  // Corrigé version 7.1 car si on réaffectait le résultat à this ne marchait pas
  // et cela provoquait des résultats faux dans les produits indicés de complexes.
  // res.x = this.x * z1.x - this.y * z1.y
  // res.y = this.x * z1.y + z1.x * this.y
  const x = this.x * z1.x - this.y * z1.y
  const y = this.x * z1.y + z1.x * this.y
  res.x = x
  res.y = y
}
/**
 * Renvoie le carré du module du complexe.
 * @returns {number}
 */
Complexe.prototype.moduleCarre = function () {
  return this.x * this.x + this.y * this.y
}
/**
 * Fonction renvoyant l'inverse de tthis  dans res
 * @param res
 */
Complexe.prototype.inverseComp = function (res) {
  const modCar = this.moduleCarre()
  res.x = this.x / modCar
  res.y = -this.y / modCar
}
Complexe.prototype.sinComp = function (uniteAngle, res) {
// On utilise la formule sinz = 1/(2i)(exp(iz)-exp(-iz))
  const c1 = new Complexe(-this.y, this.x)
  // c1 contient iz
  const c2 = new Complexe(this.y, -this.x)
  // c2 contient -iz
  const c3 = new Complexe()
  c1.expComp(uniteAngle, c3) // C3 contient exp(iz)
  const c4 = new Complexe()
  c2.expComp(uniteAngle, c4) // C4 contient exp(-iz)
  const c5 = new Complexe()
  c3.differenceComp(c4, c5)
  // On divise c5 par 2i, donc on le multiplie par -0.5*i
  res.x = 0.5 * c5.y
  res.y = -0.5 * c5.x
}
Complexe.prototype.cosComp = function (uniteAngle, res) {
// On utilise la formule sinz = 1/2(exp(iz)+exp(-iz))
  const c1 = new Complexe(-this.y, this.x)
  // c1 contient iz
  const c2 = new Complexe(this.y, -this.x)
  // c2 contient -iz
  const c3 = new Complexe()
  c1.expComp(uniteAngle, c3) // C3 contient exp(iz)
  const c4 = new Complexe()
  c2.expComp(uniteAngle, c4) // C4 contient exp(-iz)
  const c5 = new Complexe()
  c3.sommeComp(c4, c5)
  res.x = 0.5 * c5.x
  res.y = 0.5 * c5.y
}

Complexe.prototype.tanComp = function (uniteAngle, res) {
  // On utilise la formule 1/i*(exp(2iz-1)/(exp(2iz+1))
  const c1 = new Complexe(-2 * this.y, 2 * this.x) // c1 contient 2ic
  // c1 contient 2ic
  const c2 = new Complexe()
  c1.expComp(uniteAngle, c2) // c2 contient exp(2ic)
  const c3 = new Complexe(c2.x + 1, c2.y) // c3 contient exp(2c) + 1
  c2.x = c2.x - 1 // z1 contient exp(2c) - 1
  const c4 = new Complexe()
  // c2.quotientComp(c3, c4)
  divComp(c2, c3, c4)
  res.set(c4.y, -c4.x)
}
Complexe.prototype.shComp = function (uniteAngle, res) {
  const c1 = new Complexe(-this.x, -this.y)
  // c1 contient -this
  const c2 = new Complexe()
  this.expComp(uniteAngle, c2) // c2 contient expComp(this)
  const c3 = new Complexe()
  c1.expComp(uniteAngle, c3) // C3 contient  expComp(-this)
  c2.differenceComp(c3, res)
  res.x /= 2
  res.y /= 2
}
Complexe.prototype.chComp = function (uniteAngle, res) {
  const c1 = new Complexe(-this.x, -this.y)
  // c1 contient -this
  const c2 = new Complexe()
  this.expComp(uniteAngle, c2) // c2 contient expComp(this)
  const c3 = new Complexe()
  c1.expComp(uniteAngle, c3) // c3 contient expComp(-this)
  c2.sommeComp(c3, res)
  res.x /= 2
  res.y /= 2
}
Complexe.prototype.thComp = function (uniteAngle, res) {
  const c1 = new Complexe()
  this.expComp(uniteAngle, c1)
  const c2 = new Complexe(-this.x, -this.y)
  const c3 = new Complexe()
  c2.expComp(uniteAngle, c3)
  const num = new Complexe()
  c1.differenceComp(c3, num)
  const den = new Complexe()
  c1.sommeComp(c3, den)
  divComp(num, den, res)
}

/**
 * Renvoie l'exponentielle complexe de this en tenant compte de uniteAngle.
 * @param {KernelUniteAngle} uniteAngle
 * @param {Complexe} res
 * @returns {void}
 */
Complexe.prototype.expComp = function (uniteAngle, res) {
  // Corrigé version 7.1 car sin on réaffectait le résultat à this ne marchait pas
  let x, y
  if (uniteAngle === uniteAngleRadian) {
    x = Math.exp(this.x) * CCbGlob.cosinus(this.y)
    y = Math.exp(this.x) * CCbGlob.sinus(this.y)
  } else {
    x = Math.exp(this.x) * CCbGlob.cosinus(this.y * ConvDegRad)
    y = Math.exp(this.x) * CCbGlob.sinus(this.y * ConvDegRad)
  }
  res.x = x
  res.y = y
}
/**
 * Renvoie le logarithme complexe de this en tenant compte de uniteAngle.
 * @param {KernelUniteAngle} uniteAngle  0 pour radian, 1 pour degré.
 * @param {Complexe} res
 * @returns {void}
 */
Complexe.prototype.lnComp = function (uniteAngle, res) {
  // Corrigé version 7.1 car sin on réaffectait le résultat à this ne marchait pas
  const r = this.module()
  const x = Math.log(r)
  const y = this.argument(uniteAngle)
  res.x = x
  res.y = y
}
/**
 * Renvoie dans res this élevé à la puissnace b (b complexe)
 * @param {Complexe} b
 * @param {Complexe} res
 * @returns {void}
 */
Complexe.prototype.puisComp = function (b, res) {
  const c1 = new Complexe()
  this.lnComp(uniteAngleRadian, c1) // c1 contient ln(this)
  const c2 = new Complexe()
  b.produitComp(c1, c2) // c2 contient b*ln(this)
  c2.expComp(uniteAngleRadian, res)
}
/**
 * Elévation d'un complexe à une puissance entière d'exposant
 * strictement positif, inférieur ou égal à 255 et non nul
 * On utilise l'algorithme optimisé n'utilisant que des produits.
 * Le résultat est renvoyé dans res.
 * @param {number} exposant
 * @param {Complexe} res
 * @returns {void}
 */
Complexe.prototype.puisCompExpEnt = function (exposant, res) {
  res.x = 1
  res.y = 0
  const tampon = new Complexe(this.x, this.y)
  // Version n'utilisant que des décalages de bits pour optimisation
  let b = 128
  let indfin = 7
  let x1, y1
  while ((b & exposant) === 0) {
    indfin--
    b = b >> 1
  }
  for (let i = 0; i <= indfin; i++) {
    if ((exposant % 2) !== 0) {
      // On n'appele pas produitComp qui créerait un nouvel objet Complexe
      x1 = res.x * tampon.x - res.y * tampon.y
      y1 = res.x * tampon.y + res.y * tampon.x
      res.x = x1
      res.y = y1
    }
    exposant >>= 1
    // On élève tampon au carré
    x1 = tampon.x * tampon.x - tampon.y * tampon.y
    y1 = 2 * tampon.x * tampon.y
    tampon.x = x1
    tampon.y = y1
  }
}
/**
 * Renvoie une chaîne de caractères représentant le complexe this.
 * @returns {string}
 */
Complexe.prototype.toString = function () {
  const y = this.y
  if (y === 0) return this.x.toString()
  if (y < 0) return this.x.toString() + this.y.toString() + 'i'
  return this.x.toString() + '+' + this.y.toString() + 'i'
}

Complexe.prototype.chaineValeurComplexe = function (nbDecimales) {
  const ch1 = chaineNombre(this.x, nbDecimales)
  let ch2 = chaineNombre(this.y, nbDecimales)
  if (ch2 === '0') return ch1
  else {
    if (ch1 === '0') {
      if (ch2 === '-1') return '- i'
      else if (ch2 === '1') ch2 = ''
      return ch2 + ' i'
    } else {
      if (ch2 === '1') return ch1 + ' + i'
      else {
        // if (ch2 === "-1") ch2 = "-";
        if (ch2 === '- 1') return ch1 + ' - i'
        if (ch2.charAt(0) === '-') return ch1 + ' - ' + ch2.substring(1) + ' i'
        else return ch1 + ' + ' + ch2 + ' i'
      }
    }
  }
}