/**
* 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 mathjs from '../kernel/mathjs'
import Complexe from '../types/Complexe'
import Opef2v from '../types/Opef2v'
import { erreurCalculException, getStr, ncr, npr, pgcd, ppcm } from '../kernel/kernel'
import CCb from './CCb'
import CCbGlob from '../kernel/CCbGlob'
const { matrix } = mathjs
export default CFonction2Var
/**
* Classe représentant dans un arbre binaire de calcul un appel de fonction
* prédéfinie à deux variables : maxi, mini, gcd, lcm, ncr, npr ou mod.
* @constructor
* @extends CCb
* @param {CListeObjets} listeProprietaire La liste propriétaire.
* @param {number} opef Entier donnant l'opérateur (voir OperateurFonction Opef)
* @param {CCb} operande1 Le premier opérande.
* @param {CCb} operande2 Le deuxième opérande.
* @returns {CFonction2Var}
*/
function CFonction2Var (listeProprietaire, opef, operande1, operande2) {
CCb.call(this, listeProprietaire)
if (arguments.length !== 1) {
this.opef = opef
this.operande1 = operande1
this.operande2 = operande2
}
this.z1loc = new Complexe()
this.z2loc = new Complexe()
}
CFonction2Var.prototype = new CCb()
CFonction2Var.prototype.constructor = CFonction2Var
CFonction2Var.prototype.superClass = 'CCb'
CFonction2Var.prototype.className = 'CFonction2Var'
CFonction2Var.prototype.nombreVariables = function () {
return 2
}
CFonction2Var.prototype.nature = function () {
return CCbGlob.natFonction2Var
}
CFonction2Var.prototype.getClone = function (listeSource, listeCible) {
const cloneOperande1 = this.operande1.getClone(listeSource, listeCible)
const cloneOperande2 = this.operande2.getClone(listeSource, listeCible)
return new CFonction2Var(listeCible, this.opef, cloneOperande1, cloneOperande2)
}
CFonction2Var.prototype.initialisePourDependance = function () {
CCb.prototype.initialisePourDependance.call(this)
this.operande1.initialisePourDependance()
this.operande2.initialisePourDependance()
}
CFonction2Var.prototype.depDe = function (p) {
if (this.elementTestePourDependDe === p) return this.dependDeElementTeste
return this.memDep(CCb.prototype.depDe.call(this, p) ||
this.operande1.depDe(p) || this.operande2.depDe(p))
}
CFonction2Var.prototype.dependDePourBoucle = function (p) {
return this.operande1.dependDePourBoucle(p) || this.operande2.dependDePourBoucle(p)
}
CFonction2Var.prototype.existe = function () {
return this.operande1.existe() && this.operande2.existe()
}
function divmax (n, k) {
const N = Math.abs(n)
const nimpair = n !== 2 * Math.round(n / 2)
const pstart = nimpair ? 3 : 2
const step = nimpair ? 2 : 1
// Si Math.exp(Math.log(N) / k) est un entier la valeur rendue peut être approchée et inférieure à la valeur exacte
// Il faut donc arrondir à l'entier le plus proche au risque de faire un test inutile pour la dernière valeur
const max = Math.round(Math.exp(Math.log(N) / k))
let res = 1
for (let p = pstart; p <= max; p += step) {
if (N === p * Math.round(N / p)) { // On regarde d'abord si N est divisible par p pour optimiser
const puis = CCbGlob.puisExpEnt(p, k)
if (N === puis * Math.round(N / puis)) res = p
}
}
return res
}
/**
* Renvoie le résultat de la fonction réelle prédéfinie appliquée
* aux opérandes x et y.
* @param {boolean} infoRandom true si les calculs aléatoires par rand
* soient réactualisés.
* @param {number} x Le premier opérande
* @param {number} y Le second opérande
* @returns {number}
*/
CFonction2Var.prototype.resultatBase = function (infoRandom, x, y) {
switch (this.opef) {
case Opef2v.maxi:
if (x > y) return x; else return y
case Opef2v.mini:
if (x < y) return x; else return y
case Opef2v.gcd:
if ((x !== Math.floor(x)) || (y !== Math.floor(y)) || ((x === 0) && (y === 0)) ||
(x < 0) || (y < 0)) { throw new Error(erreurCalculException) } else return pgcd(x, y)
case Opef2v.lcm:
if ((x !== Math.floor(x)) || (y !== Math.floor(y)) || (x < 0) || (y < 0)) { throw new Error(erreurCalculException) } else return ppcm(x, y)
case Opef2v.ncr:
if ((x !== Math.floor(x)) || (y !== Math.floor(y)) || (x < 0) || (y < 0) || (y > x)) { throw new Error(erreurCalculException) } else return ncr(x, y)
case Opef2v.npr:
if ((x !== Math.floor(x)) || (y !== Math.floor(y)) || (x < 0) || (y < 0) || (y > x)) { throw new Error(erreurCalculException) } else return npr(x, y)
case Opef2v.mod:
// Version 9.7.1 on accepte les nombres non entiers pour x et on utilise %
if ((y !== Math.floor(y)) || (y <= 0)) {
throw new Error(erreurCalculException)
} else {
if (x % y === 0) return 0
if (x >= 0) return x % y
return x % y + y
}
case Opef2v.divmaxp:
if ((x !== Math.floor(x)) || (y !== Math.floor(y)) || y < 2 || y >= 256 || x === 0 || x > 1000000) {
throw new Error(erreurCalculException)
} else return divmax(x, y)
default : throw new Error(erreurCalculException)
}
}
CFonction2Var.prototype.resultat = function (infoRandom) {
const x1 = this.operande1.resultat(infoRandom)
const y1 = this.operande2.resultat(infoRandom)
return this.resultatBase(infoRandom, x1, y1)
}
CFonction2Var.prototype.resultatFonction = function (infoRandom, valeurParametre) {
const x1 = this.operande1.resultatFonction(infoRandom, valeurParametre)
const y1 = this.operande2.resultatFonction(infoRandom, valeurParametre)
return this.resultatBase(infoRandom, x1, y1)
}
/**
* Renvoie le résultat de la fonction réelle prédéfinie appliquée
* aux opérandes complexes z1 et z2.
* @param {boolean} infoRandom true si les calculs aléatoires par rand doivent
* être réactualisés.
* @param {Complexe} z1 Le premier opérande.
* @param {Complexe} z2 Le second opérande.
* @param {Complexe} zRes Contient au retour le résultat.
* @returns {void}
*/
CFonction2Var.prototype.resultatComplexeBase = function (infoRandom, z1, z2, zRes) {
zRes.y = 0
if ((z1.y !== 0) || (z2.y !== 0)) throw new Error(erreurCalculException)
switch (this.opef) {
case Opef2v.maxiC:
if (z1.x > z2.x) zRes.x = z1.x; else zRes.x = z2.x
break
case Opef2v.miniC:
if (z1.x < z2.x) zRes.x = z1.x; else zRes.x = z2.x
break
case Opef2v.gcdC:
if ((z1.x !== Math.floor(z1.x)) || (z2.x !== Math.floor(z2.x)) || ((z1.x === 0) && (z2.x === 0)) ||
(z1.x < 0) || (z1.x < 0)) throw new Error(erreurCalculException)
else zRes.x = pgcd(z1.x, z2.x)
break
case Opef2v.lcmC:
if ((z1.x !== Math.floor(z1.x)) || (z2.x !== Math.floor(z2.x)) || (z1.x < 0) || (z2.x < 0)) { throw new Error(erreurCalculException) } else zRes.x = ppcm(z1.x, z2.x)
break
case Opef2v.ncrC:
if ((z1.x !== Math.floor(z1.x)) || (z2.x !== Math.floor(z2.x)) || (z1.x < 0) || (z2.x < 0) || (z2.x > z1.x)) { throw new Error(erreurCalculException) } else zRes.x = ncr(z1.x, z2.x)
break
case Opef2v.nprC:
if ((z1.x !== Math.floor(z1.x)) || (z2.x !== Math.floor(z2.x)) || (z1.x < 0) || (z2.x < 0) || (z2.x > z1.x)) { throw new Error(erreurCalculException) } else zRes.x = npr(z1.x, z2.x)
break
case Opef2v.modC:
// Version 7.9.1 on accepte un dividende négatif
if ((z2.x !== Math.floor(z2.x)) || (z2.x <= 0)) throw new Error(erreurCalculException)
else {
if (z1.x % z2.x === 0) zRes.x = 0
else {
if (z1.x >= 0) zRes.x = z1.x % z2.x
else zRes.x = z1.x % z2.x + z2.x
}
}
break
case Opef2v.divmaxp:
if ((z1.x !== Math.floor(z1.x)) || (z2.x !== Math.floor(z2.x)) || z2.x < 2 || z2.x >= 256 || z1.x === 0 || z1.x > 1000000) {
throw new Error(erreurCalculException)
} else zRes.x = divmax(z1.x, z2.x)
break
default : throw new Error(erreurCalculException)
}
}
CFonction2Var.prototype.resultatComplexe = function (infoRandom, zRes) {
this.operande1.resultatComplexe(infoRandom, this.z1loc)
this.operande2.resultatComplexe(infoRandom, this.z2loc)
this.resultatComplexeBase(infoRandom, this.z1loc, this.z2loc, zRes)
}
CFonction2Var.prototype.resultatFonctionComplexe = function (infoRandom, valeurParametre, zRes) {
this.operande1.resultatFonctionComplexe(infoRandom, valeurParametre, this.z1loc)
this.operande2.resultatFonctionComplexe(infoRandom, valeurParametre, this.z2loc)
this.resultatComplexeBase(infoRandom, this.z1loc, this.z2loc, zRes)
}
CFonction2Var.prototype.resultatMatBase = function (op1, op2, infoRandom) {
if (this.opef === Opef2v.dotmult) {
if (typeof op1 === 'number' && typeof op2 === 'number') return op1 * op2
else {
if (typeof op1 === 'number' || typeof op2 === 'number') throw new Error(erreurCalculException)
// Cas du produit de deux matrices terme à terme
const size1 = op1.size()
const n1 = size1[0]
const p1 = size1[1]
const size2 = op1.size()
const n2 = size2[0]
const p2 = size2[1]
if ((n1 !== n2) || (p1 !== p2)) throw new Error(erreurCalculException)
// return matrix(op1.mul(op2))
let res = []
for (let i = 0; i < n1; i++) {
const lig = []
res.push(lig)
for (let j = 0; j < p1; j++) {
lig.push(op1.get([i, j]) * op2.get([i, j]))
}
}
res = matrix(res)
const size = res.size()
if (size[0] === 1 && size[1] === 1) return res.get([0, 0])
return res
}
} else {
if (this.opef === Opef2v.sortbyrow) {
if (typeof op2 !== 'number') throw new Error(erreurCalculException)
if (typeof op1 === 'number') return op1
const size = op1.size()
const n = size[0]
const p = size[1]
if (op2 !== Math.floor(op2) || op2 <= 0 || op2 > n) throw new Error(erreurCalculException)
// Pour pouvoir trier la matrice on la transpose
const transp = []
for (let j = 0; j < p; j++) {
const lig = []
transp.push(lig)
for (let i = 0; i < n; i++) {
lig.push(op1.get([i, j]))
}
}
// On trie la matrice transposée suivant l'élement d'indice row - 1 de chaque ligne
transp.sort((a, b) => a[op2 - 1] - b[op2 - 1])
// On la retranspose pour revenir au format initial
const res = []
for (let i = 0; i < n; i++) {
const lig = []
res.push(lig)
for (let j = 0; j < p; j++) {
lig.push(transp[j][i])
}
}
return matrix(res)
} else {
if (this.opef === Opef2v.sortbycol) {
// Pour un tri par colonne on n'a pas besoin de créer une trasposée de la matrice mais juste de la cloner
if (typeof op2 !== 'number') throw new Error(erreurCalculException)
if (typeof op1 === 'number') return op1
const size = op1.size()
const n = size[0]
const p = size[1]
if (op2 !== Math.floor(op2) || op2 <= 0 || op2 > p) throw new Error(erreurCalculException)
const res = []
for (let i = 0; i < n; i++) {
const lig = []
res.push(lig)
for (let j = 0; j < p; j++) {
lig.push(op1.get([i, j]))
}
}
res.sort((a, b) => a[op2 - 1] - b[op2 - 1])
return matrix(res)
}
}
}
if (typeof op1 !== 'number' || typeof op2 !== 'number') throw new Error(erreurCalculException)
return this.resultatBase(infoRandom, op1, op2)
}
// Ajout version 6.7
CFonction2Var.prototype.resultatMat = function (infoRandom) {
const op1 = this.operande1.resultatMat(infoRandom)
const op2 = this.operande2.resultatMat(infoRandom)
return this.resultatMatBase(op1, op2, infoRandom)
}
CFonction2Var.prototype.resultatMatFonction = function (infoRandom, valeurParametre) {
const op1 = this.operande1.resultatMatFonction(infoRandom, valeurParametre)
const op2 = this.operande2.resultatMatFonction(infoRandom, valeurParametre)
return this.resultatMatBase(op1, op2, infoRandom)
}
CFonction2Var.prototype.dependDeVariable = function (ind) {
return this.operande1.dependDeVariable(ind) || this.operande2.dependDeVariable(ind)
}
CFonction2Var.prototype.getCopie = function () {
return new CFonction2Var(this.listeProprietaire, this.opef, this.operande1.getCopie(), this.operande2.getCopie())
}
CFonction2Var.prototype.chaineCalcul = function (varFor) {
const parouv = '('
const parfer = ')'
const cha1 = this.operande1.chaineCalculSansPar(varFor)
const cha2 = this.operande2.chaineCalculSansPar(varFor)
const ch = parouv + cha1 + ',' + cha2 + parfer
switch (this.opef) {
case Opef2v.maxi :
case Opef2v.maxiC :
return getStr('maxi') + ch
case Opef2v.mini :
case Opef2v.miniC :
return getStr('mini') + ch
case Opef2v.gcd :
case Opef2v.gcdC :
return getStr('gcd') + ch
case Opef2v.lcm :
case Opef2v.lcmC :
return getStr('lcm') + ch
case Opef2v.ncr :
case Opef2v.ncrC :
return getStr('ncr') + ch
case Opef2v.npr :
case Opef2v.nprC :
return getStr('npr') + ch
case Opef2v.mod :
case Opef2v.modC :
return getStr('mod') + ch
case Opef2v.dotmult :
return getStr('dotmult') + ch
case Opef2v.divmaxp:
return getStr('divmaxp') + ch
case Opef2v.sortbyrow:
return getStr('sortbyrow') + ch
case Opef2v.sortbycol:
return getStr('sortbycol') + ch
default : return ''
}
}
CFonction2Var.prototype.chaineLatex = function (varFor, fracSimple = false) {
const parouvLatex = '\\left('
const parferLatex = '\\right)'
const cha1 = this.operande1.chaineLatexSansPar(varFor, fracSimple)
const cha2 = this.operande2.chaineLatexSansPar(varFor, fracSimple)
const ch = parouvLatex + cha1 + ',' + cha2 + parferLatex
switch (this.opef) {
case Opef2v.maxi :
case Opef2v.maxiC :
return getStr('maxi') + ch
case Opef2v.mini :
case Opef2v.miniC :
return getStr('mini') + ch
case Opef2v.gcd :
case Opef2v.gcdC :
return getStr('gcd') + ch
case Opef2v.lcm :
case Opef2v.lcmC :
return getStr('lcm') + ch
case Opef2v.ncr :
case Opef2v.ncrC :
return getStr('ncr') + ch
case Opef2v.npr :
case Opef2v.nprC :
return getStr('npr') + ch
case Opef2v.mod :
case Opef2v.modC :
return getStr('mod') + ch
case Opef2v.dotmult :
return getStr('dotmult') + ch
case Opef2v.divmaxp:
return getStr('divmaxp') + ch
case Opef2v.sortbyrow:
return getStr('sortbyrow') + ch
case Opef2v.sortbycol:
return getStr('sortbycol') + ch
default : return ''
}
}
CFonction2Var.prototype.read = function (inps, list) {
CCb.prototype.read.call(this, inps, list)
this.opef = inps.readByte()
this.operande1 = inps.readObject(list)
this.operande2 = inps.readObject(list)
}
CFonction2Var.prototype.write = function (oups, list) {
CCb.prototype.write.call(this, oups, list)
oups.writeByte(this.opef)
oups.writeObject(this.operande1)
oups.writeObject(this.operande2)
}
CFonction2Var.prototype.estConstant = function () {
return (this.operande1.estConstant() && this.operande2.estConstant())
}
CFonction2Var.prototype.deriveePossible = function (indiceVariable) {
return this.operande1.deriveePossible(indiceVariable) && this.operande2.deriveePossible(indiceVariable)
}
CFonction2Var.prototype.calculAvecValeursRemplacees = function (bfrac) {
return new CFonction2Var(this.listeProprietaire, this.opef,
this.operande1.calculAvecValeursRemplacees(bfrac), this.operande2.calculAvecValeursRemplacees(bfrac))
}
// Déplacé ici version 6.4.7
CFonction2Var.prototype.calculNormalise = function (rempval, sousdivnorm, rempDecParFrac, eliminMult1 = true) {
const op1 = this.operande1.calculNormalise(rempval, sousdivnorm, rempDecParFrac, eliminMult1)
const op2 = this.operande2.calculNormalise(rempval, sousdivnorm, rempDecParFrac, eliminMult1)
return new CFonction2Var(this.listeProprietaire, this.opef, op1, op2)
}