@anonympins/fingerprint 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +1 -1
  3. package/composer.json +8 -1
  4. package/package.json +1 -1
  5. package/public/fingerprint-wordpress.zip +0 -0
  6. package/src/js/build-client.js +8 -10
  7. package/src/js/fingerprint.builder.js +37 -38
  8. package/src/js/fingerprint.client.js +1531 -1463
  9. package/src/js/fingerprint.client.obfuscated.js +1 -0
  10. package/src/js/fingerprint.js +152 -10
  11. package/src/js/fingerprint.utils.js +213 -213
  12. package/src/js/library.js +340 -376
  13. package/src/js/mongodb-store.js +1 -1
  14. package/src/js/optimization.worker.js +27 -27
  15. package/src/js/pow.solver.inline.js +154 -56
  16. package/src/js/pow.solver.js +180 -92
  17. package/src/js/pow.worker.js +48 -48
  18. package/src/js/redis-store.js +1 -1
  19. package/src/js/tests/fingerprint.client.init.test.js +146 -143
  20. package/src/js/tests/fingerprint.test.js +34 -7
  21. package/src/js/tests/ip-reputation.test.js +1 -0
  22. package/src/js/upow-model-task.js +14 -15
  23. package/src/php/AutoTuner.php +34 -34
  24. package/src/php/Challenge/ChallengeUtils.php +56 -10
  25. package/src/php/Config/SecurityProfiles.php +20 -0
  26. package/src/php/DirectFingerprint.php +190 -145
  27. package/src/php/FingerprintBuilder.php +29 -30
  28. package/src/php/FingerprintClient.php +147 -148
  29. package/src/php/FingerprintEngine.php +7 -5
  30. package/src/php/Ja3AnomalyDetector.php +33 -32
  31. package/src/php/Optimization/FunctionRegistry.php +7 -7
  32. package/src/php/Optimization/Optimization.php +24 -24
  33. package/src/php/Optimization/OptimizationOperators.php +26 -25
  34. package/src/php/Optimization/ProblemInitializers.php +52 -52
  35. package/src/php/ProblemManager.php +31 -34
  36. package/src/php/RequestContext.php +8 -1
  37. package/src/php/Store/IStore.php +7 -8
  38. package/src/php/Store/InMemoryStore.php +66 -66
  39. package/src/php/Store/MongoDbStore.php +5 -5
  40. package/src/php/Store/RedisStore.php +5 -5
  41. package/src/php/Store/StoreManager.php +35 -35
  42. package/src/php/Tests/ChallengeUtilsTest.php +453 -453
  43. package/src/php/Tests/DnsCircuitBreakerTest.php +105 -105
  44. package/src/php/Tests/FingerprintEngineTest.php +662 -662
  45. package/src/php/Tests/IpReputationTest.php +10 -0
  46. package/src/php/Tests/MaliciousPatternsTest.php +103 -103
  47. package/src/php/Tests/MetricsTest.php +97 -97
  48. package/src/php/Tests/ProblemManagerTest.php +376 -376
  49. package/src/php/Tests/QuicFingerprintTest.php +53 -53
  50. package/src/php/Tests/RequestUtilsTest.php +471 -471
  51. package/src/php/Tests/TLSClientHelloParserTest.php +177 -177
  52. package/src/php/Tests/config/ed25519_key.json +3 -3
  53. package/src/php/Utils/Env.php +49 -49
  54. package/src/php/Utils/MaliciousPatterns.php +74 -74
  55. package/src/php/Utils/MetricsManager.php +209 -209
  56. package/src/php/Utils/RequestUtils.php +129 -7
  57. package/src/php/Utils/TLSClientHelloParser.php +369 -369
  58. package/src/php/WordPress/WpDbStore.php +155 -0
  59. package/src/php/WordPress/fingerprint-wordpress.php +217 -0
  60. package/src/php/WordPress/package.php +123 -0
  61. package/src/php/bin/auto-tune.php +118 -118
package/src/js/library.js CHANGED
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * @file @/library.js
3
- * @description Une bibliothèque d'outils basés sur le principe fondamental de la dichotomie (division en deux).
4
- * Inclut des algorithmes pour les tableaux triés, des structures de données et des solveurs de problèmes conceptuels.
3
+ * @description A library of tools based on the fundamental principle of dichotomy (division in two).
4
+ * Includes algorithms for sorted arrays, data structures, and problem solvers.
5
5
  *
6
6
  * @template T
7
7
  * @callback Comparator
8
- * @param {T} element - L'élément du tableau.
9
- * @param {any} target - La valeur cible.
10
- * @returns {number} -1 si element < target, 0 si element == target, 1 si element > target.
8
+ * @param {T} element - Array element.
9
+ * @param {any} target - Target value.
10
+ * @returns {number} -1 if element < target, 0 if element == target, 1 if element > target.
11
11
  *
12
12
  */
13
13
 
@@ -16,7 +16,7 @@ import os from "node:os";
16
16
  import crypto from "node:crypto";
17
17
 
18
18
  /**
19
- * Génère un nombre flottant aléatoire entre 0 (inclus) et 1 (exclus).
19
+ * Generates a cryptographically secure random float between 0 (inclusive) and 1 (exclusive).
20
20
  * @returns {number}
21
21
  */
22
22
  const random = () => {
@@ -26,17 +26,15 @@ const random = () => {
26
26
  const Optimization = {
27
27
  // eslint-disable-line no-unused-vars
28
28
  /**
29
- * Trouve une bonne solution à un problème d'optimisation en utilisant le Recuit Simulé.
30
- * Cet algorithme est efficace pour trouver un optimum global dans un grand espace de recherche
31
- * avec de nombreux optima locaux (plusieurs "pics" ou "vallées").
32
- * @template TSolution - Le type de la solution (peut être un nombre, un tableau, un objet...).
33
- * @param {TSolution} initialSolution - Le point de départ de la recherche.
34
- * @param {function(TSolution): number} evaluator - Fonction qui évalue une solution. L'objectif est de minimiser ce score.
35
- * @param {function(TSolution): TSolution} neighbor - Fonction qui génère une solution "voisine" aléatoire.
36
- * @param {number} [initialTemperature=1000] - La température de départ.
37
- * @param {number} [coolingRate=0.995] - Le taux de refroidissement (proche de 1).
38
- * @param {number} [maxIterations=10000] - Le nombre total d'itérations.
39
- * @returns {{solution: TSolution, energy: number}} Le meilleur couple solution/score trouvé.
29
+ * Finds an optimized solution using Simulated Annealing.
30
+ * @template TSolution - Type of the solution (number, array, object...).
31
+ * @param {TSolution} initialSolution - Starting point for optimization.
32
+ * @param {function(TSolution): number} evaluator - Evaluates a solution (goal is to MINIMIZE score).
33
+ * @param {function(TSolution): TSolution} neighbor - Generates a random neighbor solution.
34
+ * @param {number} [initialTemperature=1000] - Initial temperature.
35
+ * @param {number} [coolingRate=0.995] - Cooling rate factor.
36
+ * @param {number} [maxIterations=10000] - Total iterations.
37
+ * @returns {{solution: TSolution, energy: number}} Best solution and corresponding energy score.
40
38
  */
41
39
  simulatedAnnealing(
42
40
  initialSolution,
@@ -58,24 +56,24 @@ const Optimization = {
58
56
  const newSolution = neighbor(currentSolution);
59
57
  const newEnergy = evaluator(newSolution);
60
58
 
61
- // Calcule la probabilité d'accepter une moins bonne solution.
59
+ // Calculate acceptance probability for a worse solution
62
60
  const acceptanceProbability = Math.exp(
63
61
  (currentEnergy - newEnergy) / temperature,
64
62
  );
65
63
 
66
- // Décide si on se déplace vers la nouvelle solution.
64
+ // Decide whether to transition to the candidate solution
67
65
  if (newEnergy < currentEnergy || random() < acceptanceProbability) {
68
66
  currentSolution = newSolution;
69
67
  currentEnergy = newEnergy;
70
68
  }
71
69
 
72
- // Met à jour la meilleure solution trouvée jusqu'à présent.
70
+ // Update global best found so far
73
71
  if (currentEnergy < bestEnergy) {
74
72
  bestSolution = currentSolution;
75
73
  bestEnergy = currentEnergy;
76
74
  }
77
75
 
78
- // Refroidit la température.
76
+ // Cool down temperature
79
77
  temperature *= coolingRate;
80
78
  }
81
79
 
@@ -83,21 +81,20 @@ const Optimization = {
83
81
  },
84
82
 
85
83
  /**
86
- * Résout un problème d'optimisation en utilisant un Algorithme Génétique.
87
- * Idéal pour les problèmes complexes où l'espace de recherche est vaste et non-linéaire.
88
- * @template TChromosome - Le type de la solution (un "chromosome").
89
- * @param {function(): TChromosome} createIndividual - Fonction pour créer un individu aléatoire.
90
- * @param {function(TChromosome): number} fitnessFunction - Évalue un individu. L'objectif est de MINIMISER ce score.
91
- * @param {function(TChromosome, TChromosome): TChromosome} crossover - Croise deux parents pour créer un enfant.
92
- * @param {function(TChromosome): TChromosome} mutate - Applique une mutation aléatoire à un individu.
93
- * @param {object} options - Options de l'algorithme.
94
- * @param {number} [options.populationSize=100] - Taille de la population.
95
- * @param {number} [options.generations=100] - Nombre de générations à simuler.
96
- * @param {number} [options.crossoverRate=0.8] - Probabilité de croisement.
97
- * @param {number} [options.mutationRate=0.1] - Probabilité de mutation.
98
- * @param {function} [options.selectionFunction] - Fonction de sélection des parents. Par défaut, un tournoi.
99
- * @param {boolean} [options.returnPopulation=false] - Si true, retourne la population finale au lieu du meilleur individu.
100
- * @returns {{solution: TChromosome, fitness: number}} Le meilleur individu trouvé.
84
+ * Solves an optimization problem using a Genetic Algorithm.
85
+ * @template TChromosome - Type of the chromosome.
86
+ * @param {function(): TChromosome} createIndividual - Creates a random individual.
87
+ * @param {function(TChromosome): number} fitnessFunction - Evaluates an individual (MINIMIZATION).
88
+ * @param {function(TChromosome, TChromosome): TChromosome} crossover - Crosses two parents.
89
+ * @param {function(TChromosome): TChromosome} mutate - Applies random mutation.
90
+ * @param {object} options - Algorithm options.
91
+ * @param {number} [options.populationSize=100] - Population size.
92
+ * @param {number} [options.generations=100] - Number of generations.
93
+ * @param {number} [options.crossoverRate=0.8] - Crossover rate.
94
+ * @param {number} [options.mutationRate=0.1] - Mutation rate.
95
+ * @param {function} [options.selectionFunction] - Parent selection function.
96
+ * @param {boolean} [options.returnPopulation=false] - Whether to return final population instead of single best.
97
+ * @returns {{solution: TChromosome, fitness: number}} Best individual found.
101
98
  */
102
99
  geneticAlgorithm(
103
100
  createIndividual,
@@ -117,33 +114,30 @@ const Optimization = {
117
114
  this.Operators.createTournamentSelection({ size: 5 });
118
115
  const returnPopulation = options.returnPopulation || false;
119
116
 
120
- // 1. Initialisation
121
- // La population est un tableau d'objets { chromosome, fitness }
122
- // La fitness est calculée une seule fois par individu.
117
+ // 1. Initialization
123
118
  let population = Array.from({ length: populationSize }, () => {
124
119
  const chromosome = createIndividual();
125
120
  return { chromosome, fitness: fitnessFunction(chromosome) };
126
121
  });
127
122
 
128
- // Trier la population initiale pour trouver le meilleur
123
+ // Sort initial population to find best
129
124
  population.sort((a, b) => a.fitness - b.fitness);
130
125
  let bestOverall = population[0];
131
126
 
132
- // 2. Boucle des générations
127
+ // 2. Generation loop
133
128
  for (let gen = 0; gen < generations; gen++) {
134
129
  const newPopulation = [];
135
130
 
136
- // Élitisme : le meilleur individu de la génération précédente est conservé.
137
- // Il est déjà à l'index 0 grâce au tri à la fin de la boucle précédente.
131
+ // Elitism: retain best individual from previous generation
138
132
  newPopulation.push(population[0]);
139
133
 
140
134
  while (newPopulation.length < populationSize) {
141
- // 3. Sélection
135
+ // 3. Selection
142
136
  const parent1 = selectionFunction(population);
143
137
  const parent2 = selectionFunction(population);
144
138
 
145
139
  let offspringChromosome;
146
- // 4. Croisement
140
+ // 4. Crossover
147
141
  if (random() < crossoverRate) {
148
142
  offspringChromosome = crossover(
149
143
  parent1.chromosome,
@@ -158,21 +152,20 @@ const Optimization = {
158
152
  offspringChromosome = mutate(offspringChromosome);
159
153
  }
160
154
 
161
- // S'assurer que les opérateurs ont bien retourné un individu
155
+ // Ensure operators returned a valid individual
162
156
  if (offspringChromosome) {
163
157
  newPopulation.push({
164
158
  chromosome: offspringChromosome,
165
159
  fitness: fitnessFunction(offspringChromosome),
166
160
  });
167
161
  } else {
168
- // Si le croisement/mutation échoue, on réinsère un parent pour garder la taille de la population
169
162
  newPopulation.push(parent1);
170
163
  }
171
164
  }
172
165
 
173
166
  population = newPopulation;
174
167
 
175
- // Trier la nouvelle population pour la prochaine génération (élitisme) et la mise à jour du meilleur
168
+ // Sort new population for elitism and update best overall
176
169
  population.sort((a, b) => a.fitness - b.fitness);
177
170
 
178
171
  if (population[0].fitness < bestOverall.fitness) {
@@ -188,13 +181,11 @@ const Optimization = {
188
181
  },
189
182
 
190
183
  /**
191
- * Exécute un solveur stochastique plusieurs fois et retourne le meilleur résultat.
192
- * C'est une méta-heuristique pour augmenter la probabilité de trouver un optimum global
193
- * en échange d'un temps de calcul plus long.
194
- * @param {function(): {solution: any, energy?: number, fitness?: number}} solverFunction - Une fonction qui, lorsqu'elle est appelée, exécute un algorithme d'optimisation et retourne un objet résultat.
195
- * @param {number} numCycles - Le nombre de fois où exécuter le solveur.
196
- * @param {boolean} [logProgress=false] - Si true, affiche le score de chaque cycle dans la console.
197
- * @returns {{bestResult: object, stats: {scores: Array<number>, average: number, stdDev: number}}} Le meilleur résultat et des statistiques sur les exécutions.
184
+ * Executes a stochastic solver multiple times and returns the best result.
185
+ * @param {function(): {solution: any, energy?: number, fitness?: number}} solverFunction - Solver function.
186
+ * @param {number} numCycles - Number of execution cycles.
187
+ * @param {boolean} [logProgress=false] - Log progress to console.
188
+ * @returns {{bestResult: object, stats: {scores: Array<number>, average: number, stdDev: number}}} Best result and run stats.
198
189
  */
199
190
  runMultiple(solverFunction, numCycles, logProgress = false) {
200
191
  let bestResult = null;
@@ -203,8 +194,7 @@ const Optimization = {
203
194
  for (let i = 0; i < numCycles; i++) {
204
195
  const currentResult = solverFunction();
205
196
 
206
- // Gère les résultats du Recuit Simulé (energy) et des Algorithmes Génétiques (fitness).
207
- // On suppose que pour les deux, un score plus bas est meilleur.
197
+ // Handle simulated annealing (energy) and genetic algorithm (fitness) results (lower is better)
208
198
  const currentScore =
209
199
  currentResult.energy !== undefined
210
200
  ? currentResult.energy
@@ -213,7 +203,7 @@ const Optimization = {
213
203
 
214
204
  if (logProgress) {
215
205
  console.log(
216
- ` -> Cycle ${i + 1}/${numCycles}: Score trouvé = ${currentScore.toFixed(2)}`,
206
+ ` -> Cycle ${i + 1}/${numCycles}: Score found = ${currentScore.toFixed(2)}`,
217
207
  );
218
208
  }
219
209
 
@@ -228,7 +218,7 @@ const Optimization = {
228
218
  }
229
219
  }
230
220
 
231
- // Calcul des statistiques
221
+ // Statistical calculations
232
222
  const sum = allScores.reduce((a, b) => a + b, 0);
233
223
  const average = sum / numCycles;
234
224
  const variance =
@@ -246,16 +236,15 @@ const Optimization = {
246
236
  },
247
237
 
248
238
  /**
249
- * Trouve un minimum local d'une fonction en utilisant l'algorithme de Descente de Gradient.
250
- * Nécessite que la fonction soit différentiable et que son gradient soit connu.
251
- * @template TSolution - Le type de la solution (nombre ou tableau de nombres).
252
- * @param {TSolution} initialSolution - Le point de départ.
253
- * @param {function(TSolution): TSolution} gradientFunction - Fonction qui calcule le gradient au point donné.
254
- * @param {object} options - Options de l'algorithme.
255
- * @param {number} [options.learningRate=0.01] - Le "pas" de la descente.
256
- * @param {number} [options.maxIterations=1000] - Nombre d'itérations.
257
- * @param {number} [options.tolerance=1e-6] - Seuil pour arrêter si la solution ne change plus beaucoup.
258
- * @returns {TSolution} La solution (minimum local) trouvée.
239
+ * Finds a local minimum of a differentiable function using Gradient Descent.
240
+ * @template TSolution - Solution type (number or array of numbers).
241
+ * @param {TSolution} initialSolution - Starting point.
242
+ * @param {function(TSolution): TSolution} gradientFunction - Computes gradient at given point.
243
+ * @param {object} options - Algorithm options.
244
+ * @param {number} [options.learningRate=0.01] - Step size.
245
+ * @param {number} [options.maxIterations=1000] - Max iterations.
246
+ * @param {number} [options.tolerance=1e-6] - Stopping threshold.
247
+ * @returns {TSolution} Local minimum found.
259
248
  */
260
249
  gradientDescent(initialSolution, gradientFunction, options = {}) {
261
250
  const {
@@ -282,7 +271,7 @@ const Optimization = {
282
271
  );
283
272
  if (change < tolerance) break;
284
273
  } else {
285
- // Cas d'une seule variable (nombre)
274
+ // Single variable scalar case
286
275
  const prevSolution = currentSolution;
287
276
  currentSolution -= learningRate * gradient;
288
277
  if (Math.abs(prevSolution - currentSolution) < tolerance) break;
@@ -292,15 +281,15 @@ const Optimization = {
292
281
  },
293
282
 
294
283
  /**
295
- * Exécute un solveur stochastique plusieurs fois en parallèle en utilisant un pool de workers pour éviter de surcharger le système.
296
- * @param {string} solverName - Le nom de la fonction solveur à appeler dans `Optimization.Operators`.
297
- * @param {Array<any>} baseSolverArgs - Les arguments de base à passer au solveur (sans les données aléatoires qui seront générées par worker).
298
- * @param {number} numCycles - Le nombre total de cycles à exécuter.
299
- * @param {boolean} [logProgress=false] - Si true, affiche la progression dans la console.
300
- * @param {object} [options={}] - Options pour la parallélisation.
301
- * @param {number} [options.concurrency] - Le nombre de workers à utiliser en parallèle. Par défaut, le nombre de cœurs CPU.
302
- * @param {function(number): Array<any>} [options.workerDataGenerator] - Une fonction qui, pour chaque cycle (index), génère les arguments spécifiques à passer au solveur. Si non fournie, `baseSolverArgs` est utilisé tel quel.
303
- * @returns {Promise<{bestResult: object, stats: {scores: Array<number>, average: number, stdDev: number}}>} Le meilleur résultat et des statistiques.
284
+ * Executes a stochastic solver multiple times in parallel using a worker pool.
285
+ * @param {string} solverName - Name of the solver function in `Optimization.Operators`.
286
+ * @param {Array<any>} baseSolverArgs - Base arguments for solver.
287
+ * @param {number} numCycles - Total cycles to run.
288
+ * @param {boolean} [logProgress=false] - Log progress.
289
+ * @param {object} [options={}] - Concurrency options.
290
+ * @param {number} [options.concurrency] - Worker concurrency count (defaults to CPU count).
291
+ * @param {function(number): Array<any>} [options.workerDataGenerator] - Dynamic argument generator per task index.
292
+ * @returns {Promise<{bestResult: object, stats: {scores: Array<number>, average: number, stdDev: number}}>} Best result and stats.
304
293
  */
305
294
  async runMultipleParallel(
306
295
  solverName,
@@ -314,7 +303,7 @@ const Optimization = {
314
303
 
315
304
  if (logProgress) {
316
305
  console.log(
317
- ` (Utilisation d'un pool de ${concurrency} workers pour ${numCycles} cycles avec le solveur ${solverName})`,
306
+ ` (Using a pool of ${concurrency} workers for ${numCycles} cycles with solver ${solverName})`,
318
307
  );
319
308
  }
320
309
 
@@ -335,15 +324,14 @@ const Optimization = {
335
324
  };
336
325
 
337
326
  const result = await new Promise(async (resolve, reject) => {
338
- // Le chemin du worker doit être absolu ou relatif au fichier appelant.
339
- // On utilise import.meta.url pour résoudre le chemin de manière fiable.
327
+ // Resolve worker script path relative to current module
340
328
  const worker = new Worker(new URL('./optimization.worker.js', import.meta.url), { workerData });
341
329
  worker.on("message", resolve);
342
330
  worker.on("error", reject);
343
331
  worker.on("exit", (code) => {
344
332
  if (code !== 0)
345
333
  reject(
346
- new Error(`Worker ${workerId} a terminé avec le code ${code}`),
334
+ new Error(`Worker ${workerId} exited with code ${code}`),
347
335
  );
348
336
  });
349
337
  });
@@ -365,7 +353,7 @@ const Optimization = {
365
353
  );
366
354
  await Promise.all(workerPromises);
367
355
 
368
- // Le reste de la logique est identique à `runMultiple`
356
+ // Remainder follows runMultiple logic
369
357
  let bestResult = null;
370
358
  const allScores = [];
371
359
  allResults.forEach((result) => {
@@ -397,30 +385,30 @@ const Optimization = {
397
385
  };
398
386
 
399
387
  /**
400
- * Détermine si la solution A domine la solution B en multi-objectifs (problème de minimisation).
388
+ * Determines whether solution A Pareto-dominates solution B (minimization problem).
401
389
  * @private
402
- * @param {number[]} objectivesA - Tableau des scores des objectifs pour la solution A.
403
- * @param {number[]} objectivesB - Tableau des scores des objectifs pour la solution B.
404
- * @returns {boolean} - True si A domine B.
390
+ * @param {number[]} objectivesA - Objective scores for solution A.
391
+ * @param {number[]} objectivesB - Objective scores for solution B.
392
+ * @returns {boolean} - True if A dominates B.
405
393
  */
406
394
  function paretoDominates(objectivesA, objectivesB) {
407
395
  let aIsBetterInOne = false;
408
396
  for (let i = 0; i < objectivesA.length; i++) {
409
397
  if (objectivesA[i] > objectivesB[i]) {
410
- return false; // A est pire sur au moins un objectif, donc ne domine pas.
398
+ return false; // A is worse on at least one objective
411
399
  }
412
400
  if (objectivesA[i] < objectivesB[i]) {
413
- aIsBetterInOne = true; // A est strictement meilleur sur au moins un objectif.
401
+ aIsBetterInOne = true; // A is strictly better on at least one objective
414
402
  }
415
403
  }
416
- return aIsBetterInOne; // A domine B si elle n'est jamais pire et au moins une fois meilleure.
404
+ return aIsBetterInOne;
417
405
  }
418
406
 
419
407
  /**
420
- * Trie une population en fronts de Pareto non-dominés (inspiré de NSGA-II).
408
+ * Sorts a population into non-dominated Pareto fronts (inspired by NSGA-II).
421
409
  * @private
422
- * @param {Array<{individual: any, objectives: number[]}>} populationWithObjectives - La population à trier.
423
- * @returns {Array<Array<{individual: any, objectives: number[]}>>} - Un tableau de fronts, où le premier est le meilleur.
410
+ * @param {Array<{individual: any, objectives: number[]}>} populationWithObjectives - Population to sort.
411
+ * @returns {Array<Array<{individual: any, objectives: number[]}>>} - Array of fronts, first being the best.
424
412
  */
425
413
  function nonDominatedSort(populationWithObjectives) {
426
414
  const fronts = [[]];
@@ -462,9 +450,9 @@ function nonDominatedSort(populationWithObjectives) {
462
450
  }
463
451
 
464
452
  /**
465
- * Calcule la distance de promiscuité (crowding distance) pour un front, afin de préserver la diversité.
453
+ * Calculates crowding distance for a front to preserve diversity.
466
454
  * @private
467
- * @param {Array<{individual: any, objectives: number[]}>} front - Le front de Pareto.
455
+ * @param {Array<{individual: any, objectives: number[]}>} front - Pareto front.
468
456
  */
469
457
  function calculateCrowdingDistance(front) {
470
458
  if (front.length === 0) return;
@@ -476,7 +464,7 @@ function calculateCrowdingDistance(front) {
476
464
  const minObj = front[0].objectives[i];
477
465
  const maxObj = front[front.length - 1].objectives[i];
478
466
 
479
- // Les solutions aux extrémités sont cruciales, on leur donne une distance infinie.
467
+ // Boundary solutions receive infinite distance to encourage spread
480
468
  front[0].crowdingDistance = Infinity;
481
469
  front[front.length - 1].crowdingDistance = Infinity;
482
470
 
@@ -491,13 +479,13 @@ function calculateCrowdingDistance(front) {
491
479
  }
492
480
 
493
481
  /**
494
- * Algorithme génétique multi-objectifs (inspiré de NSGA-II) pour trouver un front de Pareto.
495
- * @param {function(): any} createIndividual - Fonction qui crée un individu aléatoire.
496
- * @param {function(any): number[]} fitnessFunction - Fonction qui évalue un individu et retourne un tableau d'objectifs à MINIMISER.
497
- * @param {function(any, any): any} crossover - Fonction de croisement.
498
- * @param {function(any): any} mutate - Fonction de mutation.
499
- * @param {object} options - Options de l'algorithme.
500
- * @returns {Array<{solution: any, objectives: number[]}>} Le premier front de Pareto (l'ensemble des meilleures solutions de compromis).
482
+ * Multi-objective genetic algorithm (inspired by NSGA-II) to find a Pareto front.
483
+ * @param {function(): any} createIndividual - Creates a random individual.
484
+ * @param {function(any): number[]} fitnessFunction - Evaluates individual and returns array of objectives to MINIMIZE.
485
+ * @param {function(any, any): any} crossover - Crossover function.
486
+ * @param {function(any): any} mutate - Mutation function.
487
+ * @param {object} options - Algorithm options.
488
+ * @returns {Array<{solution: any, objectives: number[]}>} Non-dominated Pareto front.
501
489
  */
502
490
  Optimization.geneticAlgorithmMultiObjective = function (
503
491
  createIndividual,
@@ -507,48 +495,47 @@ Optimization.geneticAlgorithmMultiObjective = function (
507
495
  options = {},
508
496
  ) {
509
497
  const {
510
- generations = 150, // Augmenté pour une meilleure convergence
511
- populationSize = 60, // Augmenté pour plus de diversité
498
+ generations = 150,
499
+ populationSize = 60,
512
500
  mutationRate = 0.1,
513
501
  currentConfig = null,
514
502
  } = options;
515
503
 
516
504
  let population = Array.from({ length: populationSize }, () => ({
517
505
  individual: createIndividual(),
518
- })); // createIndividual doit maintenant utiliser currentConfig
506
+ }));
519
507
  population.forEach((p) => (p.objectives = fitnessFunction(p.individual)));
520
508
 
521
509
  for (let gen = 0; gen < generations; gen++) {
522
- // 1. Créer une population d'enfants
510
+ // 1. Generate offspring
523
511
  const offspring = [];
524
512
  for (let i = 0; i < populationSize; i++) {
525
- // Sélection simple pour l'exemple
526
513
  const parent1 = population[crypto.randomInt(0, population.length)];
527
514
  const parent2 = population[crypto.randomInt(0, population.length)];
528
515
  let childIndividual = crossover(parent1.individual, parent2.individual);
529
516
  if (random() < mutationRate) {
530
- childIndividual = mutate(childIndividual, currentConfig); // mutate doit maintenant utiliser currentConfig
517
+ childIndividual = mutate(childIndividual, currentConfig);
531
518
  }
532
519
  const child = { individual: childIndividual };
533
520
  child.objectives = fitnessFunction(child.individual);
534
521
  offspring.push(child);
535
522
  }
536
523
 
537
- // 2. Combiner parents et enfants
524
+ // 2. Combine parent and offspring populations
538
525
  const combinedPopulation = [...population, ...offspring];
539
526
 
540
- // 3. Trier la population combinée en fronts
527
+ // 3. Sort into Pareto fronts
541
528
  const fronts = nonDominatedSort(combinedPopulation);
542
529
 
543
- // 4. Construire la nouvelle population
530
+ // 4. Build next generation population
544
531
  const newPopulation = [];
545
532
  for (const front of fronts) {
546
533
  if (newPopulation.length + front.length <= populationSize) {
547
534
  newPopulation.push(...front);
548
535
  } else {
549
- // Si le front est trop grand, on utilise la distance de promiscuité pour choisir les individus les plus diversifiés.
536
+ // Use crowding distance truncation if front overflows population size
550
537
  calculateCrowdingDistance(front);
551
- front.sort((a, b) => b.crowdingDistance - a.crowdingDistance); // Trier par distance décroissante
538
+ front.sort((a, b) => b.crowdingDistance - a.crowdingDistance);
552
539
  const remaining = populationSize - newPopulation.length;
553
540
  newPopulation.push(...front.slice(0, remaining));
554
541
  break;
@@ -557,11 +544,11 @@ Optimization.geneticAlgorithmMultiObjective = function (
557
544
  population = newPopulation;
558
545
  }
559
546
 
560
- // Retourner le premier front de la population finale
547
+ // Return the first front of the final population
561
548
  const finalFronts = nonDominatedSort(population);
562
549
  const bestFront = finalFronts.length > 0 ? finalFronts[0] : [];
563
550
 
564
- // Filtrer le front pour ne garder que les solutions avec des objectifs uniques
551
+ // Deduplicate solutions with identical objectives
565
552
  const uniqueSolutionsMap = new Map();
566
553
  for (const p of bestFront) {
567
554
  const key = JSON.stringify(p.objectives);
@@ -576,68 +563,68 @@ Optimization.geneticAlgorithmMultiObjective = function (
576
563
  };
577
564
 
578
565
  /**
579
- * Utilitaires pour les problèmes d'optimisation.
566
+ * Optimization utilities.
580
567
  */
581
568
  Optimization.Utils = {
582
- /** Calcule la distance euclidienne entre deux points (villes). */
569
+ /** Calculates Euclidean distance between two points/cities. */
583
570
  distance: (city1, city2) => Math.sqrt(Math.pow(city1.x - city2.x, 2) + Math.pow(city1.y - city2.y, 2)),
584
571
 
585
- /** Évalue la distance totale d'un chemin TSP donné. */
572
+ /** Evaluates the total round-trip distance of a TSP path. */
586
573
  evaluatePathDistance: (cities, path) => {
587
574
  let totalDistance = 0;
588
575
  for (let i = 0; i < path.length - 1; i++) {
589
576
  totalDistance += Optimization.Utils.distance(cities[path[i]], cities[path[i + 1]]);
590
577
  }
591
- totalDistance += Optimization.Utils.distance(cities[path[path.length - 1]], cities[path[0]]); // Retour au départ
578
+ totalDistance += Optimization.Utils.distance(cities[path[path.length - 1]], cities[path[0]]); // Return to start
592
579
  return totalDistance;
593
580
  }
594
581
  };
595
582
 
596
583
  /**
597
- * Algorithme d'optimisation CMA-ES (Covariance Matrix Adaptation Evolution Strategy).
598
- * C'est un algorithme de pointe pour l'optimisation en boîte noire de fonctions non-linéaires et non-convexes.
599
- * Il est particulièrement efficace pour les problèmes avec des variables continues.
600
- * @param {function(Array<number>): number} fitnessFunction - La fonction à MINIMISER.
601
- * @param {Array<number>} initialSolution - Le point de départ de la recherche (un vecteur de nombres).
602
- * @param {number} initialStepSize - La taille de pas initiale (sigma).
603
- * @param {object} [options={}] - Options de l'algorithme.
604
- * @param {number} [options.maxGenerations=100] - Nombre maximum de générations.
605
- * @param {number} [options.populationSize] - Taille de la population (lambda). Calculée par défaut si non fournie.
606
- * @param {number} [options.tolerance=1e-6] - Seuil de tolérance pour l'arrêt précoce.
607
- * @returns {{solution: Array<number>, fitness: number}} La meilleure solution trouvée.
584
+ * CMA-ES (Covariance Matrix Adaptation Evolution Strategy) optimization algorithm.
585
+ * State-of-the-art evolutionary algorithm for black-box optimization of non-linear, non-convex functions.
586
+ * Particularly effective for problems involving continuous real-valued variables.
587
+ * @param {function(Array<number>): number} fitnessFunction - The objective function to MINIMIZE.
588
+ * @param {Array<number>} initialSolution - Starting point for the search (numeric vector).
589
+ * @param {number} initialStepSize - Initial search step size (sigma).
590
+ * @param {object} [options={}] - Algorithm options.
591
+ * @param {number} [options.maxGenerations=100] - Maximum number of generations.
592
+ * @param {number} [options.populationSize] - Population size (lambda). Automatically computed if omitted.
593
+ * @param {number} [options.tolerance=1e-6] - Tolerance threshold for early stopping.
594
+ * @returns {{solution: Array<number>, fitness: number}} Best solution found and its fitness score.
608
595
  */
609
596
  Optimization.cmaes = function(fitnessFunction, initialSolution, initialStepSize, options = {}) {
610
- const n = initialSolution.length; // Dimension du problème
597
+ const n = initialSolution.length; // Problem dimension
611
598
 
612
- // --- Paramètres de l'algorithme (stratégie) ---
599
+ // --- Algorithm strategy parameters ---
613
600
  const { maxGenerations = 100, tolerance = 1e-6 } = options;
614
601
  const populationSize = options.populationSize || (4 + Math.floor(3 * Math.log(n))); // Lambda
615
- const mu = Math.floor(populationSize / 2); // Nombre de parents pour la recombinaison
602
+ const mu = Math.floor(populationSize / 2); // Parent count for recombination
616
603
 
617
- // Poids de recombinaison
604
+ // Recombination weights
618
605
  let weights = Array.from({ length: mu }, (_, i) => Math.log(mu + 0.5) - Math.log(i + 1));
619
606
  const sumWeights = weights.reduce((s, w) => s + w, 0);
620
607
  weights = weights.map(w => w / sumWeights);
621
608
  const muEff = 1 / weights.reduce((s, w) => s + w * w, 0);
622
609
 
623
- // Paramètres d'adaptation
610
+ // Adaptation parameters
624
611
  const cc = (4 + muEff / n) / (n + 4 + 2 * muEff / n);
625
612
  const cs = (muEff + 2) / (n + muEff + 5);
626
613
  const c1 = 2 / (Math.pow(n + 1.3, 2) + muEff);
627
614
  const cmu = Math.min(1 - c1, 2 * (muEff - 2 + 1 / muEff) / (Math.pow(n + 2, 2) + muEff));
628
615
  const damps = 1 + 2 * Math.max(0, Math.sqrt((muEff - 1) / (n + 1)) - 1) + cs;
629
616
 
630
- // --- Variables d'état dynamiques ---
631
- let mean = [...initialSolution]; // Le centre de la distribution de recherche
617
+ // --- Dynamic state variables ---
618
+ let mean = [...initialSolution]; // Distribution mean / search center
632
619
  let stepSize = initialStepSize; // Sigma
633
- let C = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); // Matrice de covariance
634
- let pc = Array(n).fill(0); // Chemin d'évolution pour C
635
- let ps = Array(n).fill(0); // Chemin d'évolution pour sigma
620
+ let C = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); // Covariance matrix
621
+ let pc = Array(n).fill(0); // Evolution path for C
622
+ let ps = Array(n).fill(0); // Evolution path for sigma
636
623
 
637
624
  let bestFitness = Infinity;
638
625
  let bestSolution = null;
639
626
 
640
- // Fonction pour la décomposition de Cholesky (simplifiée, pour matrice symétrique définie positive)
627
+ // Cholesky decomposition helper (simplified for symmetric positive-definite matrices)
641
628
  function cholesky(A) {
642
629
  const L = Array.from({ length: n }, () => Array(n).fill(0));
643
630
  for (let i = 0; i < n; i++) {
@@ -648,7 +635,7 @@ Optimization.cmaes = function(fitnessFunction, initialSolution, initialStepSize,
648
635
  }
649
636
  if (i === j) {
650
637
  const val = A[i][i] - sum;
651
- if (val < 0) return null; // Non définie positive
638
+ if (val < 0) return null; // Not positive definite
652
639
  L[i][j] = Math.sqrt(val);
653
640
  } else {
654
641
  if (L[j][j] === 0) return null;
@@ -660,18 +647,18 @@ Optimization.cmaes = function(fitnessFunction, initialSolution, initialStepSize,
660
647
  }
661
648
 
662
649
  for (let gen = 0; gen < maxGenerations; gen++) {
663
- // 1. Échantillonnage de la nouvelle population
650
+ // 1. Sample new population
664
651
  const population = [];
665
- const arx = []; // Vecteurs de recherche
652
+ const arx = []; // Search vectors
666
653
  const L = cholesky(C);
667
654
  if (!L) {
668
- console.warn("[CMA-ES] La matrice de covariance n'est plus définie positive. Arrêt.");
655
+ console.warn("[CMA-ES] Covariance matrix is no longer positive definite. Stopping.");
669
656
  break;
670
657
  }
671
658
 
672
659
  for (let i = 0; i < populationSize; i++) {
673
- const z = Array.from({ length: n }, () => random() * 2 - 1); // Vecteur normal standard
674
- const y = Array(n).fill(0); // z transformé par L
660
+ const z = Array.from({ length: n }, () => random() * 2 - 1); // Standard normal approximation
661
+ const y = Array(n).fill(0); // z transformed by L
675
662
  for (let r = 0; r < n; r++) {
676
663
  for (let c = 0; c < n; c++) {
677
664
  y[r] += L[r][c] * z[c];
@@ -682,7 +669,7 @@ Optimization.cmaes = function(fitnessFunction, initialSolution, initialStepSize,
682
669
  population.push({ individual, fitness: fitnessFunction(individual) });
683
670
  }
684
671
 
685
- // 2. Trier et sélectionner les meilleurs
672
+ // 2. Sort and select elite parents
686
673
  population.sort((a, b) => a.fitness - b.fitness);
687
674
  const parents = population.slice(0, mu);
688
675
 
@@ -691,7 +678,7 @@ Optimization.cmaes = function(fitnessFunction, initialSolution, initialStepSize,
691
678
  bestSolution = parents[0].individual;
692
679
  }
693
680
 
694
- // 3. Mise à jour des variables d'état
681
+ // 3. Update distribution mean
695
682
  const oldMean = [...mean];
696
683
  const y_w = Array(n).fill(0);
697
684
  for (let j = 0; j < n; j++) {
@@ -702,15 +689,15 @@ Optimization.cmaes = function(fitnessFunction, initialSolution, initialStepSize,
702
689
  }
703
690
  mean = oldMean.map((m, i) => m + stepSize * y_w[i]);
704
691
 
705
- // 4. Adaptation des chemins d'évolution
706
- const C_inv_sqrt = cholesky(C); // Simplification, devrait être l'inverse de la racine
692
+ // 4. Adapt evolution paths
693
+ const C_inv_sqrt = cholesky(C); // Simplification, ideally inverse square root
707
694
  const C_inv_sqrt_y_w = y_w; // Approximation
708
695
  ps = ps.map((p, i) => (1 - cs) * p + Math.sqrt(cs * (2 - cs) * muEff) * C_inv_sqrt_y_w[i]);
709
696
 
710
697
  const hsig = Math.sqrt(ps.reduce((s, v) => s + v*v, 0)) / (1 - Math.pow(1 - cs, 2 * (gen + 1))) / n < 1.4 + 2 / (n + 1);
711
698
  pc = pc.map((p, i) => (1 - cc) * p + (hsig ? Math.sqrt(cc * (2 - cc) * muEff) * y_w[i] : 0));
712
699
 
713
- // 5. Adaptation de la matrice de covariance C
700
+ // 5. Adapt covariance matrix C
714
701
  let rankOneUpdate = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => c1 * pc[i] * pc[j]));
715
702
  let rankMuUpdate = Array.from({ length: n }, () => Array(n).fill(0));
716
703
  for (let k = 0; k < mu; k++) {
@@ -723,7 +710,7 @@ Optimization.cmaes = function(fitnessFunction, initialSolution, initialStepSize,
723
710
  }
724
711
  C = C.map((row, i) => row.map((val, j) => (1 - c1 - cmu) * val + rankOneUpdate[i][j] + rankMuUpdate[i][j]));
725
712
 
726
- // 6. Adaptation de la taille de pas (sigma)
713
+ // 6. Adapt step size (sigma)
727
714
  stepSize *= Math.exp((cs / damps) * (Math.sqrt(ps.reduce((s, v) => s + v*v, 0)) / Math.sqrt(n) - 1));
728
715
  }
729
716
 
@@ -732,16 +719,16 @@ Optimization.cmaes = function(fitnessFunction, initialSolution, initialStepSize,
732
719
 
733
720
  /**
734
721
  * @namespace Optimization.Operators
735
- * @description Une bibliothèque de "fabriques d'évaluateurs" pour des problèmes d'optimisation complexes,
736
- * souvent multidimensionnels, à utiliser avec les algorithmes de `Optimization` (Recuit Simulé, Algorithmes Génétiques, etc.).
722
+ * @description Evaluator factories for complex, multi-dimensional optimization problems
723
+ * designed for use with Optimization solvers (Simulated Annealing, Genetic Algorithms, etc.).
737
724
  */
738
- Optimization.Operators = {}; // Création du namespace
725
+ Optimization.Operators = {}; // Namespace creation
739
726
 
740
727
  /**
741
- * Crée une fonction de sélection par tournoi pour un algorithme génétique.
742
- * @param {object} [options] - Options pour le tournoi.
743
- * @param {number} [options.size=5] - Le nombre de participants par tournoi.
744
- * @returns {function(Array<{chromosome: any, fitness: number}>): {chromosome: any, fitness: number}} Une fonction de sélection.
728
+ * Creates a tournament selection operator for genetic algorithms.
729
+ * @param {object} [options] - Tournament options.
730
+ * @param {number} [options.size=5] - Number of candidates per tournament.
731
+ * @returns {function(Array<{chromosome: any, fitness: number}>): {chromosome: any, fitness: number}} Tournament selection operator.
745
732
  */
746
733
  Optimization.Operators.createTournamentSelection = (options = {}) => {
747
734
  const tournamentSize = options.size || 5;
@@ -756,8 +743,8 @@ Optimization.Operators.createTournamentSelection = (options = {}) => {
756
743
  best = individual;
757
744
  }
758
745
  }
759
- // Retourne le meilleur trouvé. Dans le pire des cas (tous les scores sont Infinity),
760
- // on retourne le premier candidat sélectionné au lieu de null.
746
+ // Returns the best candidate. In degenerate edge cases (all fitness scores are Infinity),
747
+ // fallback to a random candidate instead of returning null.
761
748
  if (!best) {
762
749
  return population[crypto.randomInt(0, population.length)];
763
750
  }
@@ -766,12 +753,12 @@ Optimization.Operators.createTournamentSelection = (options = {}) => {
766
753
  };
767
754
 
768
755
  /**
769
- * Crée une matrice de covariance à partir de coefficients de corrélation déclarés.
770
- * C'est une manière plus intuitive de définir les relations de risque entre les actifs.
771
- * @param {object} config - L'objet de configuration.
772
- * @param {Array<{name: string, volatility: number}>} config.assets - La liste des actifs avec leur volatilité.
773
- * @param {Array<{assets: [string, string], correlation: number}>} config.correlations - Une liste de relations de corrélation.
774
- * @returns {Array<Array<number>>} La matrice de covariance calculée.
756
+ * Creates a covariance matrix from pairwise asset correlations.
757
+ * Provides an intuitive representation for cross-asset risk modeling.
758
+ * @param {object} config - Configuration object.
759
+ * @param {Array<{name: string, volatility: number}>} config.assets - Asset definitions with volatilities.
760
+ * @param {Array<{assets: [string, string], correlation: number}>} config.correlations - Pairwise correlation entries.
761
+ * @returns {Array<Array<number>>} Resulting covariance matrix.
775
762
  */
776
763
  Optimization.Operators.createCovarianceMatrixFromCorrelations = ({
777
764
  assets,
@@ -780,24 +767,24 @@ Optimization.Operators.createCovarianceMatrixFromCorrelations = ({
780
767
  const n = assets.length;
781
768
  const matrix = Array.from({ length: n }, () => Array(n).fill(0));
782
769
 
783
- // Créer un map pour un accès rapide aux infos des actifs par leur nom.
770
+ // Fast lookup map for asset details by name
784
771
  const assetInfo = new Map();
785
772
  assets.forEach((asset, index) => {
786
773
  assetInfo.set(asset.name, { index, volatility: asset.volatility });
787
774
  });
788
775
 
789
- // 1. Remplir la diagonale avec les variances (volatilité^2)
776
+ // 1. Fill diagonal with individual variances (volatility^2)
790
777
  for (let i = 0; i < n; i++) {
791
778
  const variance = Math.pow(assets[i].volatility, 2);
792
779
  matrix[i][i] = variance;
793
780
  }
794
781
 
795
- // 2. Remplir les autres cellules avec les covariances calculées
782
+ // 2. Fill off-diagonal cells with computed covariances
796
783
  for (const corr of correlations) {
797
784
  const [nameA, nameB] = corr.assets;
798
785
  if (!assetInfo.has(nameA) || !assetInfo.has(nameB)) {
799
786
  console.warn(
800
- `Avertissement: L'un des actifs [${nameA}, ${nameB}] n'a pas été trouvé. La corrélation est ignorée.`,
787
+ `Warning: One of assets [${nameA}, ${nameB}] was not found. Correlation ignored.`,
801
788
  );
802
789
  continue;
803
790
  }
@@ -809,13 +796,13 @@ Optimization.Operators.createCovarianceMatrixFromCorrelations = ({
809
796
  const covariance = corr.correlation * infoA.volatility * infoB.volatility;
810
797
 
811
798
  matrix[infoA.index][infoB.index] = covariance;
812
- matrix[infoB.index][infoA.index] = covariance; // La matrice est symétrique
799
+ matrix[infoB.index][infoA.index] = covariance; // Symmetric matrix
813
800
  }
814
801
 
815
802
  return matrix;
816
803
  };
817
804
 
818
- // Le "déséquilibre" est la différence absolue entre l'offre et la demande. On veut le minimiser.
805
+ // Market imbalance is the absolute delta between supply and demand (goal is minimization)
819
806
  Optimization.Operators.createMarketEquilibriumEvaluator = (
820
807
  demandModel,
821
808
  supplyModel,
@@ -827,26 +814,26 @@ Optimization.Operators.createMarketEquilibriumEvaluator = (
827
814
  };
828
815
  };
829
816
 
830
- // Un "individu" est un tableau de 3 poids (ex: [0.5, 0.2, 0.3]) qui doivent sommer à 1.
831
817
  /**
832
- * Crée une fonction de fitness pour l'optimisation de portefeuille.
833
- * @param {object} config - L'objet de configuration.
834
- * @param {Array<{name: string, expectedReturn: number, volatility: number}>} config.assets - Les actifs disponibles.
835
- * @param {number} config.maxVolatility - La contrainte de volatilité maximale du portefeuille.
836
- * @param {Array<Array<number>>} [config.covarianceMatrix] - Matrice de covariance pour un calcul de risque précis.
837
- * @returns {function(Array<number>): number} Une fonction de fitness qui évalue un portefeuille (tableau de poids).
818
+ * Creates a fitness function for portfolio allocation.
819
+ * An individual is a weight array (e.g. [0.5, 0.2, 0.3]) normalized to sum to 1.
820
+ * @param {object} config - Configuration object.
821
+ * @param {Array<{name: string, expectedReturn: number, volatility: number}>} config.assets - Target assets.
822
+ * @param {number} config.maxVolatility - Maximum portfolio volatility threshold constraint.
823
+ * @param {Array<Array<number>>} [config.covarianceMatrix] - Covariance matrix for exact variance evaluation.
824
+ * @returns {function(Array<number>): number} Fitness evaluator returning negative return with risk penalty.
838
825
  */
839
826
  Optimization.Operators.createPortfolioAllocator = ({
840
827
  assets,
841
828
  maxVolatility,
842
829
  covarianceMatrix,
843
830
  }) => {
844
- // La fonction de fitness évalue un portefeuille (un tableau de poids).
845
- // L'objectif est de MINIMISER le score, donc on minimise le rendement NÉGATIF.
831
+ // Evaluates a portfolio weight vector.
832
+ // Since solvers minimize, we minimize NEGATIVE expected return.
846
833
  return function portfolioFitness(weights) {
847
- // Normaliser les poids pour qu'ils somment à 1
834
+ // Normalize weights to ensure total equals 1.0
848
835
  const totalWeight = weights.reduce((sum, w) => sum + w, 0);
849
- if (totalWeight === 0) return Infinity; // Éviter la division par zéro, score très mauvais
836
+ if (totalWeight === 0) return Infinity; // Prevent zero-division
850
837
  const normalizedWeights = weights.map((w) => w / totalWeight);
851
838
 
852
839
  let portfolioReturn = normalizedWeights.reduce(
@@ -856,8 +843,7 @@ Optimization.Operators.createPortfolioAllocator = ({
856
843
  let portfolioVolatility;
857
844
 
858
845
  if (covarianceMatrix) {
859
- // Calcul de la volatilité avec la matrice de covariance (plus précis)
860
- // Volatilité^2 = w' * C * w
846
+ // Accurate covariance volatility: Volatility^2 = w' * C * w
861
847
  let variance = 0;
862
848
  for (let i = 0; i < assets.length; i++) {
863
849
  for (let j = 0; j < assets.length; j++) {
@@ -869,7 +855,7 @@ Optimization.Operators.createPortfolioAllocator = ({
869
855
  }
870
856
  portfolioVolatility = Math.sqrt(variance);
871
857
  } else {
872
- // Calcul simplifié (moins précis) : moyenne pondérée des volatilités individuelles.
858
+ // Simplified weighted average volatility
873
859
  portfolioVolatility = normalizedWeights.reduce(
874
860
  (sum, w, i) => sum + w * assets[i].volatility,
875
861
  0,
@@ -878,39 +864,38 @@ Optimization.Operators.createPortfolioAllocator = ({
878
864
 
879
865
  for (let i = 0; i < assets.length; i++) {}
880
866
 
881
- // Forte pénalité si la contrainte de risque n'est pas respectée.
867
+ // High penalty if risk ceiling is violated
882
868
  if (portfolioVolatility > maxVolatility) {
883
- return 1000 + (portfolioVolatility - maxVolatility) * 1000; // Pénalité proportionnelle à la violation.
869
+ return 1000 + (portfolioVolatility - maxVolatility) * 1000; // Proportional penalty
884
870
  }
885
871
 
886
- // On veut maximiser le rendement, donc on minimise son opposé.
872
+ // Maximize return <=> minimize negative return
887
873
  return -portfolioReturn;
888
874
  };
889
875
  };
890
876
 
891
877
  /**
892
- * Crée un solveur complet pour le problème du voyageur de commerce (TSP) en utilisant le Recuit Simulé.
893
- * Cette fonction factorise la création de l'évaluateur de chemin et de la fonction de voisinage.
894
- * @param {Array<{x: number, y: number}>} cities - Un tableau d'objets représentant les coordonnées des villes.
895
- * @param {object} [options] - Options pour l'algorithme de recuit simulé.
896
- * @returns {{solution: Array<number>, energy: number}} Le chemin optimal (indices des villes) et sa distance.
878
+ * Solves the Traveling Salesperson Problem (TSP) using Simulated Annealing.
879
+ * @param {Array<{x: number, y: number}>} cities - City coordinates array.
880
+ * @param {object} [options] - Simulated Annealing parameters.
881
+ * @returns {{solution: Array<number>, energy: number}} Optimal tour (city indices) and total distance.
897
882
  */
898
883
  Optimization.Operators.solveTSP = (cities, options = {}) => {
899
- // Fonction interne pour calculer la distance entre deux villes.
884
+ // Euclidean distance between two cities
900
885
  const distance = (city1, city2) =>
901
886
  Math.sqrt(Math.pow(city1.x - city2.x, 2) + Math.pow(city1.y - city2.y, 2));
902
887
 
903
- // Évaluateur : calcule la longueur totale d'un chemin donné.
888
+ // Total route tour distance evaluator
904
889
  const pathEvaluator = (path) => {
905
890
  let totalDistance = 0;
906
891
  for (let i = 0; i < path.length - 1; i++) {
907
892
  totalDistance += distance(cities[path[i]], cities[path[i + 1]]);
908
893
  }
909
- totalDistance += distance(cities[path[path.length - 1]], cities[path[0]]); // Retour au départ
894
+ totalDistance += distance(cities[path[path.length - 1]], cities[path[0]]); // Return to origin
910
895
  return totalDistance;
911
896
  };
912
897
 
913
- // Voisinage : génère un chemin voisin en inversant une sous-séquence (heuristique 2-opt).
898
+ // Neighborhood: inverts a random sub-sequence (2-opt heuristic)
914
899
  const pathNeighbor = (path) => {
915
900
  const newPath = [...path];
916
901
  if (newPath.length <= 1) return newPath;
@@ -926,12 +911,12 @@ Optimization.Operators.solveTSP = (cities, options = {}) => {
926
911
  return newPath;
927
912
  };
928
913
 
929
- // Solution initiale : un chemin aléatoire.
914
+ // Initial solution: randomized route
930
915
  const initialPath = Array.from({ length: cities.length }, (_, i) => i).sort(
931
916
  () => random() - 0.5,
932
917
  );
933
918
 
934
- // Paramètres par défaut pour le TSP, pouvant être surchargés par `options`.
919
+ // Default hyperparameters for TSP
935
920
  const saOptions = {
936
921
  initialTemperature: 10000,
937
922
  coolingRate: 0.999,
@@ -950,35 +935,35 @@ Optimization.Operators.solveTSP = (cities, options = {}) => {
950
935
  };
951
936
 
952
937
  /**
953
- * Crée un solveur complet pour le problème d'optimisation de portefeuille en utilisant un Algorithme Génétique.
954
- * @param {Array<{name: string, expectedReturn: number, volatility: number}>} assets - Les actifs disponibles.
955
- * @param {number} maxVolatility - La contrainte de volatilité maximale du portefeuille.
956
- * @param {object} [options] - Options pour l'algorithme génétique.
957
- * @returns {{solution: Array<number>, fitness: number}} L'allocation de poids optimale et le score de fitness associé.
938
+ * Solves portfolio allocation using a Genetic Algorithm.
939
+ * @param {Array<{name: string, expectedReturn: number, volatility: number}>} assets - Available assets.
940
+ * @param {number} maxVolatility - Maximum allowed volatility constraint.
941
+ * @param {object} [options] - Genetic algorithm options.
942
+ * @returns {{solution: Array<number>, fitness: number}} Optimal weights and associated fitness.
958
943
  */
959
944
  Optimization.Operators.solvePortfolio = (
960
945
  assets,
961
946
  maxVolatility,
962
947
  options = {},
963
948
  ) => {
964
- // La fonction de fitness est créée par notre opérateur existant.
949
+ // Fitness function created from the allocator operator
965
950
  const fitnessFunction = Optimization.Operators.createPortfolioAllocator({
966
951
  assets,
967
952
  maxVolatility,
968
953
  covarianceMatrix: options.covarianceMatrix,
969
954
  });
970
955
 
971
- // Fonctions spécifiques au problème pour l'AG, maintenant encapsulées.
956
+ // Problem-specific genetic operators
972
957
  const createIndividual = () =>
973
958
  Array.from({ length: assets.length }, () => random());
974
959
 
975
- const crossover = (p1, p2) => p1.map((w1, i) => (w1 + p2[i]) / 2); // Moyenne des poids
960
+ const crossover = (p1, p2) => p1.map((w1, i) => (w1 + p2[i]) / 2); // Arithmetic average
976
961
 
977
962
  const mutate = (p) => {
978
963
  const newP = [...p];
979
964
  const i = crypto.randomInt(0, newP.length);
980
- newP[i] += (random() - 0.5) * 0.2; // Mutation douce
981
- newP[i] = Math.max(0, newP[i]); // Les poids ne peuvent être négatifs
965
+ newP[i] += (random() - 0.5) * 0.2; // Gentle mutation
966
+ newP[i] = Math.max(0, newP[i]); // Weights cannot be negative
982
967
  return newP;
983
968
  };
984
969
 
@@ -998,57 +983,56 @@ Optimization.Operators.solvePortfolio = (
998
983
  };
999
984
 
1000
985
  /**
1001
- * Crée un évaluateur 2D pour trouver la commission de base et le facteur de bonus qualité qui maximisent les revenus de la plateforme.
1002
- * @param {object} config - L'objet de configuration.
1003
- * @param {number} config.totalAdvertiserCredits - Le total des crédits disponibles chez les annonceurs.
1004
- * @param {Array<{qualityScore: number}>} config.websites - Un tableau d'objets représentant les sites monétisés, chacun avec un score de qualité.
1005
- * @returns {function(Array<number>): number} Un évaluateur qui prend une solution `[baseCommission, bonusFactor]` et retourne le revenu NÉGATIF (car les solveurs minimisent).
986
+ * Creates a 2D evaluator to determine base commission and quality bonus factor to maximize platform revenue.
987
+ * @param {object} config - Configuration object.
988
+ * @param {number} config.totalAdvertiserCredits - Total advertiser credits.
989
+ * @param {Array<{qualityScore: number}>} config.websites - Array of websites with quality scores.
990
+ * @returns {function(Array<number>): number} Evaluator for `[baseCommission, bonusFactor]` returning NEGATIVE revenue.
1006
991
  */
1007
992
  Optimization.Operators.createAdvancedPlatformRevenueEvaluator = ({
1008
993
  totalAdvertiserCredits,
1009
994
  websites,
1010
995
  }) => {
1011
- // Modèle de la demande (Annonceurs)
1012
- // La demande est sensible à la qualité globale de l'inventaire publicitaire.
996
+ // Advertiser demand model (increases with inventory quality)
1013
997
  const advertiserDemandModel = (averageSiteQuality) => {
1014
- // La demande de base est toujours liée aux crédits disponibles.
998
+ // Baseline demand tied to available credits
1015
999
  const baseDemand = (totalAdvertiserCredits || 100) * 10;
1016
- // La demande augmente avec la qualité moyenne des sites.
1000
+ // Demand scales with average inventory quality
1017
1001
  return baseDemand * (1 + averageSiteQuality);
1018
1002
  };
1019
1003
 
1020
- // Modèle de l'offre (Webmasters)
1021
- // L'offre de chaque site dépend de sa rémunération individuelle.
1004
+ // Publisher / webmaster supply model
1005
+ // Each site's click supply scales with its effective payout rate
1022
1006
  const webmasterSupplyModel = (baseCommission, bonusFactor) => {
1023
1007
  let totalOfferedClicks = 0;
1024
- const baseSupplyPerSite = 500; // Clics potentiels par site
1008
+ const baseSupplyPerSite = 500; // Baseline potential clicks per site
1025
1009
 
1026
1010
  for (const site of websites) {
1027
- // La commission effective est réduite pour les sites de haute qualité.
1011
+ // Effective commission is discounted for higher quality sites
1028
1012
  const effectiveCommission = Math.max(
1029
1013
  0,
1030
1014
  baseCommission - site.qualityScore * bonusFactor,
1031
1015
  );
1032
1016
  const webmasterPayoutRate = 1 - effectiveCommission;
1033
1017
 
1034
- // L'offre d'un site est proportionnelle à son taux de rémunération.
1018
+ // Supply is proportional to payout rate
1035
1019
  totalOfferedClicks += baseSupplyPerSite * webmasterPayoutRate;
1036
1020
  }
1037
1021
  return totalOfferedClicks;
1038
1022
  };
1039
1023
 
1040
- // L'évaluateur pour l'algorithme d'optimisation (Recuit Simulé, etc.)
1024
+ // Objective function for optimization algorithms
1041
1025
  return function revenueEvaluator(solution) {
1042
1026
  const [baseCommission, bonusFactor] = solution;
1043
1027
 
1044
- // Contraintes : on pénalise fortement les solutions hors des clous.
1028
+ // Constraints: heavily penalize out-of-bound solutions
1045
1029
  if (
1046
1030
  baseCommission < 0.01 ||
1047
1031
  baseCommission > 0.8 ||
1048
1032
  bonusFactor < 0 ||
1049
1033
  bonusFactor > baseCommission
1050
1034
  ) {
1051
- return Infinity; // Score très mauvais
1035
+ return Infinity;
1052
1036
  }
1053
1037
 
1054
1038
  const averageQuality =
@@ -1066,19 +1050,19 @@ Optimization.Operators.createAdvancedPlatformRevenueEvaluator = ({
1066
1050
  baseCommission - averageQuality * bonusFactor,
1067
1051
  );
1068
1052
 
1069
- // On veut MAXIMISER le revenu, donc on MINIMISE son opposé.
1053
+ // Maximize revenue <=> minimize negative revenue
1070
1054
  return -(clicks * averageCommission);
1071
1055
  };
1072
1056
  };
1073
1057
 
1074
1058
  /**
1075
- * Crée un solveur pour le problème de placement d'infrastructures (Facility Location Problem).
1076
- * @param {Array<{x: number, y: number}>} customers - Coordonnées des clients.
1077
- * @param {number} numFacilities - Le nombre d'infrastructures à placer.
1078
- * @param {{minX: number, maxX: number, minY: number, maxY: number}} bounds - Les limites de la carte où placer les infrastructures.
1079
- * @param {number} [options.fixedCostPerFacility=0] - Coût fixe pour chaque infrastructure installée.
1080
- * @param {object} [options] - Options pour le recuit simulé.
1081
- * @returns {{solution: Array<{x: number, y: number}>, energy: number}} Les coordonnées optimales des infrastructures et le coût total.
1059
+ * Solves the Facility Location Problem using Simulated Annealing.
1060
+ * @param {Array<{x: number, y: number}>} customers - Customer coordinate pairs.
1061
+ * @param {number} numFacilities - Number of facilities to place.
1062
+ * @param {{minX: number, maxX: number, minY: number, maxY: number}} bounds - Boundary box for placing facilities.
1063
+ * @param {number} [options.fixedCostPerFacility=0] - Fixed installation cost per facility.
1064
+ * @param {object} [options] - Simulated Annealing options.
1065
+ * @returns {{solution: Array<{x: number, y: number}>, energy: number}} Optimal facility locations and total cost.
1082
1066
  */
1083
1067
  Optimization.Operators.solveFacilityLocation = (
1084
1068
  customers,
@@ -1088,9 +1072,9 @@ Optimization.Operators.solveFacilityLocation = (
1088
1072
  ) => {
1089
1073
  const fixedCostPerFacility = options.fixedCostPerFacility || 0;
1090
1074
  const distanceSq = (p1, p2) =>
1091
- Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2); // On utilise la distance au carré pour l'efficacité
1075
+ Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2); // Squared Euclidean distance for efficiency
1092
1076
 
1093
- // Évaluateur : calcule la somme des distances de chaque client à son infrastructure la plus proche.
1077
+ // Evaluator: computes sum of shortest distances from each customer to nearest facility
1094
1078
  const facilityEvaluator = (facilities) => {
1095
1079
  let totalConnectionCost = 0;
1096
1080
  for (const customer of customers) {
@@ -1101,13 +1085,13 @@ Optimization.Operators.solveFacilityLocation = (
1101
1085
  minDistanceToCustomer = d;
1102
1086
  }
1103
1087
  }
1104
- totalConnectionCost += Math.sqrt(minDistanceToCustomer); // On utilise la vraie distance pour le coût
1088
+ totalConnectionCost += Math.sqrt(minDistanceToCustomer); // True Euclidean distance for cost
1105
1089
  }
1106
- // Le coût total est la somme des coûts de connexion + le coût fixe des infrastructures.
1090
+ // Total cost = connection cost + fixed facility maintenance costs
1107
1091
  return totalConnectionCost + facilities.length * fixedCostPerFacility;
1108
1092
  };
1109
1093
 
1110
- // Voisinage : déplace légèrement une infrastructure au hasard.
1094
+ // Neighborhood: randomly perturb one facility position
1111
1095
  const facilityNeighbor = (facilities) => {
1112
1096
  const newFacilities = facilities.map((f) => ({ ...f }));
1113
1097
  const i = crypto.randomInt(0, numFacilities);
@@ -1126,7 +1110,7 @@ Optimization.Operators.solveFacilityLocation = (
1126
1110
  return newFacilities;
1127
1111
  };
1128
1112
 
1129
- // Solution initiale : place les infrastructures au hasard sur la carte.
1113
+ // Initial solution: distribute facilities randomly across bounds
1130
1114
  const initialFacilities = Array.from({ length: numFacilities }, () => ({
1131
1115
  x: bounds.minX + random() * (bounds.maxX - bounds.minX),
1132
1116
  y: bounds.minY + random() * (bounds.maxY - bounds.minY),
@@ -1151,65 +1135,59 @@ Optimization.Operators.solveFacilityLocation = (
1151
1135
  return result;
1152
1136
  };
1153
1137
 
1154
- // --- SOLUTION : Définir un coût de base pour un clic ---
1155
- // 1 PRIM'S = 1 clic
1138
+ // Baseline click cost: 1 credit = 1 click
1156
1139
  const BASE_CLICK_COST = 1;
1157
1140
  /**
1158
- * Crée un évaluateur multi-objectifs pour déterminer le Coût Par Clic (CPC) optimal.
1159
- * @param {object} config - L'objet de configuration.
1160
- * @param {object} config.advertiser - L'annonceur qui paie le clic.
1161
- * @param {object} config.ad - L'annonce qui a été cliquée.
1162
- * @param {Array<object>} config.competingAds - Les autres annonces ciblant les mêmes mots-clés.
1163
- * @param {object} config.website - Le site sur lequel le clic a eu lieu.
1164
- * @param {object} config.platformParams - Les paramètres de la plateforme (taux de commission, etc.).
1165
- * @param {number} config.estimatedImpressions - Le nombre d'impressions quotidiennes estimées pour ce contexte.
1141
+ * Creates a multi-objective evaluator to determine optimal Cost Per Click (CPC).
1142
+ * @param {object} context - Configuration context.
1143
+ * @param {object} context.advertiser - Advertiser paying for the click.
1144
+ * @param {object} context.ad - Ad details.
1145
+ * @param {Array<object>} context.competingAds - Competing ads targeting the same keywords.
1146
+ * @param {object} context.website - Publisher website hosting the ad.
1147
+ * @param {object} context.platformParams - Platform commission parameters.
1148
+ * @param {number} context.estimatedImpressions - Estimated daily impressions.
1166
1149
  */
1167
1150
  Optimization.Operators.createOptimalCPCEvaluator = (context) => {
1168
1151
  const { optimalBaseCommission, optimalBonusFactor } = context.platformParams;
1169
1152
  const websiteQualityScore = (context.website?.relevanceScore || 50) / 100;
1170
1153
 
1171
- // Le taux de commission effectif pour ce site
1154
+ // Effective commission rate for this publisher
1172
1155
  const effectiveCommissionRate = Math.max(
1173
1156
  0,
1174
1157
  optimalBaseCommission - websiteQualityScore * optimalBonusFactor,
1175
1158
  );
1176
1159
 
1177
- // Modèle de la demande : combien de clics l'annonceur peut-il s'offrir ?
1160
+ // Demand model: how many clicks can the advertiser afford?
1178
1161
  const advertiserDemand = (cpc) => {
1179
1162
  if (cpc <= 0) return Infinity;
1180
1163
  return (context.advertiser.credits || 0) / cpc;
1181
1164
  };
1182
1165
 
1183
- // --- SOLUTION : Utiliser l'offre réelle et la concurrence ---
1184
- // L'offre est maintenant le nombre d'impressions estimées, un chiffre concret.
1185
- const supply = context.estimatedImpressions || 1; // Fallback à 1 pour éviter la division par zéro.
1166
+ // Available inventory supply
1167
+ const supply = context.estimatedImpressions || 1; // Fallback to 1 to prevent zero-division
1186
1168
 
1187
- // Le facteur de concurrence augmente le prix s'il y a plus de monde sur le même créneau.
1188
- // Formule simple : 1 + (0.1 * nombre de concurrents), avec un plafond.
1169
+ // Competition factor scales price based on competition density (capped at 2.5)
1189
1170
  const competitionFactor = Math.min(
1190
1171
  2.5,
1191
1172
  1 + context.competingAds.length * 0.1,
1192
1173
  );
1193
1174
 
1194
1175
  return function cpcFitness(cpcMultiplier) {
1195
- // Le CPC final est le coût de base, ajusté par le multiplicateur de l'algo et la concurrence.
1176
+ // Final CPC is baseline cost adjusted by optimizer multiplier and competition factor
1196
1177
  const adjustedCPC = BASE_CLICK_COST * cpcMultiplier * competitionFactor;
1197
- if (adjustedCPC < 0.1) return [Infinity, Infinity, Infinity]; // CPC minimum
1178
+ if (adjustedCPC < 0.1) return [Infinity, Infinity, Infinity]; // Minimum CPC constraint
1198
1179
 
1199
- // La demande de l'annonceur est calculée avec le CPC ajusté.
1200
1180
  const demand = advertiserDemand(adjustedCPC);
1201
-
1202
- // Le nombre de clics est le minimum de l'offre et de la demande.
1203
1181
  const estimatedClicks = Math.min(demand, supply);
1204
1182
 
1205
- // Objectif 1 : Maximiser le revenu de la plateforme (donc minimiser son opposé)
1183
+ // Objective 1: Maximize platform revenue (minimize negative revenue)
1206
1184
  const platformRevenue =
1207
1185
  estimatedClicks * adjustedCPC * effectiveCommissionRate;
1208
1186
 
1209
- // Objectif 2 : Maximiser la valeur pour l'annonceur (nombre de clics, donc minimiser son opposé)
1187
+ // Objective 2: Maximize advertiser value (clicks delivered, minimize negative)
1210
1188
  const advertiserValue = estimatedClicks;
1211
1189
 
1212
- // Objectif 3 : Minimiser le déséquilibre du marché (offre vs demande)
1190
+ // Objective 3: Minimize market imbalance (supply vs demand)
1213
1191
  const marketImbalance = Math.abs(demand - supply);
1214
1192
 
1215
1193
  return [-platformRevenue, -advertiserValue, marketImbalance];
@@ -1217,47 +1195,44 @@ Optimization.Operators.createOptimalCPCEvaluator = (context) => {
1217
1195
  };
1218
1196
 
1219
1197
  /**
1220
- * Applique le facteur de concurrence au CPC de base.
1198
+ * Applies competition factor to baseline CPC.
1221
1199
  * @private
1222
- * @param {number} baseCpc - Le CPC issu de l'algorithme génétique.
1223
- * @param {Array<object>} competingAds - Les annonces concurrentes.
1224
- * @returns {number} Le CPC final ajusté.
1200
+ * @param {number} baseCpc - Baseline CPC from genetic algorithm.
1201
+ * @param {Array<object>} competingAds - Competing ads.
1202
+ * @returns {number} Final adjusted CPC.
1225
1203
  */
1226
1204
  function applyCompetitionFactor(baseCpc, competingAds) {
1227
- // Formule simple : 1 + (0.1 * nombre de concurrents), avec un plafond pour éviter l'explosion des prix.
1228
1205
  const competitionFactor = Math.min(
1229
1206
  2.5,
1230
1207
  1 + (competingAds || []).length * 0.1,
1231
1208
  );
1232
1209
  const finalCpc = baseCpc * competitionFactor;
1233
- // On s'assure de ne jamais descendre sous un seuil minimal.
1234
1210
  return Math.max(0.1, finalCpc);
1235
1211
  }
1236
1212
  /**
1237
- * Résout le problème du CPC optimal en utilisant un algorithme génétique multi-objectifs.
1238
- * @param {object} context - Le contexte nécessaire pour l'évaluation (advertiser, ad, etc.).
1239
- * @param {object} [options] - Options pour l'algorithme génétique.
1240
- * @returns {Array<{solution: number, objectives: number[]}>} Le front de Pareto des solutions CPC.
1213
+ * Solves optimal CPC configuration using a multi-objective genetic algorithm.
1214
+ * @param {object} context - Context required for evaluation (advertiser, ad, etc.).
1215
+ * @param {object} [options] - Genetic algorithm options.
1216
+ * @returns {Array<{solution: number, objectives: number[]}>} Pareto front of optimal CPC solutions.
1241
1217
  */
1242
1218
  Optimization.Operators.solveOptimalCPC = (context, options = {}) => {
1243
1219
  const fitnessFunction =
1244
1220
  Optimization.Operators.createOptimalCPCEvaluator(context);
1245
1221
 
1246
- // Un "individu" est simplement une valeur de CPC.
1222
+ // An individual is a CPC multiplier scalar
1247
1223
  const createIndividual = () => {
1248
- // Le CPC peut varier, par exemple, entre 0.5 et 5 PRIM'S.
1249
1224
  return 0.5 + random() * 4.5;
1250
1225
  };
1251
1226
 
1252
- // Croisement : moyenne des CPC des parents.
1227
+ // Crossover: average of parent multipliers
1253
1228
  const crossover = (cpc1, cpc2) => {
1254
1229
  return (cpc1 + cpc2) / 2;
1255
1230
  };
1256
1231
 
1257
- // Mutation : légère variation aléatoire du CPC.
1232
+ // Mutation: slight random variation
1258
1233
  const mutate = (cpc) => {
1259
1234
  const newCpc = cpc + (random() - 0.5) * 0.5;
1260
- return Math.max(0.1, newCpc); // Assurer un CPC minimum.
1235
+ return Math.max(0.1, newCpc);
1261
1236
  };
1262
1237
 
1263
1238
  const gaOptions = {
@@ -1266,7 +1241,6 @@ Optimization.Operators.solveOptimalCPC = (context, options = {}) => {
1266
1241
  ...options,
1267
1242
  };
1268
1243
 
1269
- // On utilise l'algorithme génétique multi-objectifs pour obtenir le front de Pareto.
1270
1244
  const paretoFront = Optimization.geneticAlgorithmMultiObjective(
1271
1245
  createIndividual,
1272
1246
  fitnessFunction,
@@ -1275,60 +1249,53 @@ Optimization.Operators.solveOptimalCPC = (context, options = {}) => {
1275
1249
  gaOptions,
1276
1250
  );
1277
1251
 
1278
- // --- SOLUTION : Appliquer le facteur de concurrence sur les solutions finales ---
1252
+ // Apply competition factor to final Pareto solutions
1279
1253
  return paretoFront.map((result) => {
1280
1254
  const cpcMultiplier = result.solution;
1281
- // Le CPC final est calculé ici, en dehors de la fonction de fitness.
1282
1255
  const finalCpc = applyCompetitionFactor(
1283
1256
  BASE_CLICK_COST * cpcMultiplier,
1284
1257
  context.competingAds,
1285
1258
  );
1286
1259
  return {
1287
1260
  ...result,
1288
- solution: Math.max(0.1, finalCpc), // On s'assure de ne jamais descendre sous le plancher absolu.
1261
+ solution: Math.max(0.1, finalCpc),
1289
1262
  };
1290
1263
  });
1291
1264
  };
1292
1265
 
1293
1266
  /**
1294
- * Crée un évaluateur multi-objectifs pour trouver le TTL (Time-To-Live) optimal pour un ticket de sécurité.
1295
- * @param {object} context - L'objet de configuration.
1296
- * @param {number} context.suspicionScore - Le score de suspicion de l'utilisateur (0-100).
1297
- * @returns {function(number): number[]} Une fonction de fitness qui prend un TTL (en ms) et retourne les scores des objectifs [risque, friction].
1267
+ * Creates a multi-objective evaluator to find optimal TTL (Time-To-Live) for a security ticket.
1268
+ * @param {object} context - Configuration context.
1269
+ * @param {number} context.suspicionScore - User suspicion score (0-100).
1270
+ * @returns {function(number): number[]} Fitness function returning [risk, friction] objective scores.
1298
1271
  */
1299
1272
  Optimization.Operators.createOptimalTtlEvaluator = ({ suspicionScore }) => {
1300
- // On normalise le score pour qu'il soit plus impactant dans le calcul du risque.
1273
+ // Normalize score to ensure strong weight in risk calculation
1301
1274
  const normalizedScore = Math.max(1, suspicionScore);
1302
1275
 
1303
1276
  return function ttlFitness(ttl) {
1304
- // Contraintes : un TTL doit être dans une plage raisonnable (ex: 5min à 24h)
1277
+ // Constraints: TTL must be within realistic bounds (5m to 24h)
1305
1278
  if (ttl < 300000 || ttl > 86400000) return [Infinity, Infinity];
1306
1279
 
1307
- // Objectif 1 : Minimiser le Risque.
1308
- // Le risque est le produit du score et de la durée de la session.
1309
- // Pour un score élevé, l'algo doit choisir un TTL faible pour minimiser ce produit.
1280
+ // Objective 1: Minimize Security Risk (product of suspicion score and session duration)
1310
1281
  const risk = normalizedScore * ttl;
1311
1282
 
1312
- // Objectif 2 : Minimiser la Friction UX.
1313
- // La friction est l'inverse du TTL. On la pénalise d'autant plus que le score est FAIBLE.
1314
- // (101 - score) assure que pour un score de 1, la pénalité d'un TTL court est maximale.
1315
- // Pour un score de 100, cette pénalité est quasi nulle.
1283
+ // Objective 2: Minimize UX Friction (inverse of TTL, penalized when suspicion is low)
1316
1284
  const friction = (1 / ttl) * (101 - normalizedScore);
1317
1285
 
1318
- // On retourne 2 objectifs avec des facteurs de mise à l'échelle pour les équilibrer.
1286
+ // Return scaled objectives to balance dimensions
1319
1287
  return [risk / 1e7, friction * 1e9];
1320
1288
  };
1321
1289
  };
1322
1290
 
1323
1291
  /**
1324
- * Calcule la déviation d'une série de chiffres par rapport à la loi de Benford.
1325
- * Un score élevé indique une distribution non naturelle, potentiellement frauduleuse.
1326
- * @param {string} numberString - Une chaîne de chiffres (ex: "123456789").
1327
- * @returns {number} Un score de déviation (0 = parfait, > 0.15 = suspect).
1292
+ * Calculates Benford's Law deviation for a series of numbers.
1293
+ * Elevated scores indicate unnatural/synthetic data distributions.
1294
+ * @param {Array<number|string>} numbers - Array of numeric samples.
1295
+ * @returns {number} Deviation score (0 = perfect match, > 0.15 = suspicious).
1328
1296
  */
1329
1297
  Optimization.Operators.benfordTest = (numbers) => {
1330
1298
  if (!Array.isArray(numbers)) {
1331
- // Si l'entrée n'est pas un tableau, on ne peut pas l'analyser.
1332
1299
  return 0;
1333
1300
  }
1334
1301
  const counts = Array(10).fill(0);
@@ -1356,12 +1323,12 @@ Optimization.Operators.benfordTest = (numbers) => {
1356
1323
  }
1357
1324
 
1358
1325
  if (validCount < 10) {
1359
- return 0; // Pas assez de données pour un test fiable
1326
+ return 0; // Insufficient data points for reliable statistical testing
1360
1327
  }
1361
1328
 
1362
- // Distribution attendue selon la loi de Benford pour le premier chiffre
1329
+ // Expected Benford's Law distribution for leading digits 1 through 9
1363
1330
  const benfordDistribution = [
1364
- 0, // Index 0 non utilisé
1331
+ 0, // Index 0 unused
1365
1332
  30.1, 17.6, 12.5, 9.7, 7.9, 6.7, 5.8, 5.1, 4.6
1366
1333
  ];
1367
1334
 
@@ -1372,8 +1339,7 @@ Optimization.Operators.benfordTest = (numbers) => {
1372
1339
  totalDeviation += Math.pow(observedFrequency - expectedFrequency, 2);
1373
1340
  }
1374
1341
 
1375
- // Normalise la déviation pour obtenir un score plus interprétable.
1376
- // Cette normalisation est empirique.
1342
+ // Normalize deviation into an interpretable metric scale
1377
1343
  return Math.sqrt(totalDeviation) / 50;
1378
1344
  };
1379
1345
 
@@ -1381,17 +1347,17 @@ Optimization.Operators.benfordTest = (numbers) => {
1381
1347
 
1382
1348
 
1383
1349
  /**
1384
- * Crée un évaluateur multi-objectifs pour trouver les seuils de détection de fraude optimaux.
1385
- * @param {object} config - L'objet de configuration.
1386
- * @param {Array<object>} config.legitimateClicks - Un échantillon de clics considérés comme légitimes.
1387
- * @param {Array<object>} config.fraudulentClicks - Un échantillon de clics identifiés comme frauduleux (ex: honeypots).
1388
- * @returns {function(Array<number>): number[]} Une fonction de fitness qui prend une solution `[minTimeToClick, maxClickVariance, minMouseEntropy, minScrollEvents]` et retourne les scores des objectifs.
1350
+ * Creates a multi-objective evaluator to identify optimal fraud detection thresholds.
1351
+ * @param {object} config - Configuration object.
1352
+ * @param {Array<object>} config.legitimateClicks - Sample of legitimate click events.
1353
+ * @param {Array<object>} config.fraudulentClicks - Sample of identified fraudulent clicks (e.g. honeypots).
1354
+ * @returns {function(Array<number>): number[]} Fitness function returning [1 - TPR, FPR].
1389
1355
  */
1390
1356
  Optimization.Operators.createFraudThresholdEvaluator = ({
1391
1357
  legitimateClicks,
1392
1358
  fraudulentClicks,
1393
1359
  }) => {
1394
- // Fonction d'aide pour calculer la variance des positions de clic pour une empreinte
1360
+ // Helper to compute click coordinate variance for a given fingerprint
1395
1361
  const calculateClickVariance = (clicks) => {
1396
1362
  if (!clicks || clicks.length < 2) return 0;
1397
1363
  const meanX = clicks.reduce((sum, c) => sum + c.clickX, 0) / clicks.length;
@@ -1405,7 +1371,7 @@ Optimization.Operators.createFraudThresholdEvaluator = ({
1405
1371
  return variance;
1406
1372
  };
1407
1373
 
1408
- // Pré-calculer la variance pour chaque empreinte dans les données
1374
+ // Pre-group click events by fingerprint
1409
1375
  const getClicksByFingerprint = (clickData) => {
1410
1376
  const grouped = {};
1411
1377
  for (const click of clickData) {
@@ -1422,7 +1388,7 @@ Optimization.Operators.createFraudThresholdEvaluator = ({
1422
1388
  const [minTimeToClick, maxClickVariance, minMouseEntropy, minScrollEvents] =
1423
1389
  solution;
1424
1390
 
1425
- // Contraintes pour garder des seuils logiques
1391
+ // Logical threshold boundary constraints
1426
1392
  if (
1427
1393
  minTimeToClick < 100 ||
1428
1394
  minTimeToClick > 5000 ||
@@ -1432,18 +1398,17 @@ Optimization.Operators.createFraudThresholdEvaluator = ({
1432
1398
  minMouseEntropy > 1 ||
1433
1399
  minScrollEvents < 0
1434
1400
  ) {
1435
- return [Infinity, Infinity]; // Mauvais score si hors limites
1401
+ return [Infinity, Infinity];
1436
1402
  }
1437
1403
 
1438
- let truePositives = 0; // Bots correctement identifiés
1439
- let falsePositives = 0; // Humains incorrectement bloqués
1404
+ let truePositives = 0; // Bots accurately detected
1405
+ let falsePositives = 0; // Humans falsely flagged
1440
1406
 
1441
- // Évaluer les clics frauduleux
1407
+ // Evaluate fraudulent clicks
1442
1408
  for (const fingerprint in fraudulentGroups) {
1443
1409
  const clicks = fraudulentGroups[fingerprint];
1444
1410
  if (!clicks) continue;
1445
1411
  const variance = calculateClickVariance(clicks);
1446
- // Un groupe de clics est frauduleux si l'une des conditions est remplie
1447
1412
  const isTooFast = clicks.some((c) => c.timeToClick < minTimeToClick);
1448
1413
  const isTooUniform = variance < maxClickVariance;
1449
1414
  const hasLowEntropy = clicks.some(
@@ -1458,7 +1423,7 @@ Optimization.Operators.createFraudThresholdEvaluator = ({
1458
1423
  }
1459
1424
  }
1460
1425
 
1461
- // Évaluer les clics légitimes
1426
+ // Evaluate legitimate clicks
1462
1427
  for (const fingerprint in legitimateGroups) {
1463
1428
  const clicks = legitimateGroups[fingerprint];
1464
1429
  if (!clicks) continue;
@@ -1479,10 +1444,10 @@ Optimization.Operators.createFraudThresholdEvaluator = ({
1479
1444
  const totalFraudulent = Object.keys(fraudulentGroups).length || 1;
1480
1445
  const totalLegitimate = Object.keys(legitimateGroups).length || 1;
1481
1446
 
1482
- // Objectif 1 : Maximiser la détection de fraude (donc minimiser 1 - taux de détection)
1447
+ // Objective 1: Maximize fraud detection rate (minimize 1 - TPR)
1483
1448
  const objective1 = 1 - truePositives / totalFraudulent;
1484
1449
 
1485
- // Objectif 2 : Minimiser le taux de faux positifs
1450
+ // Objective 2: Minimize false positive rate (minimize FPR)
1486
1451
  const objective2 = falsePositives / totalLegitimate;
1487
1452
 
1488
1453
  return [objective1, objective2];
@@ -1490,27 +1455,27 @@ Optimization.Operators.createFraudThresholdEvaluator = ({
1490
1455
  };
1491
1456
 
1492
1457
  /**
1493
- * Résout le problème de la détection de fraude en trouvant un front de Pareto de seuils optimaux.
1494
- * @param {object} context - Le contexte contenant les données de clics.
1495
- * @param {Array<object>} context.legitimateClicks - Échantillon de clics légitimes.
1496
- * @param {Array<object>} context.fraudulentClicks - Échantillon de clics frauduleux.
1497
- * @param {object} [options] - Options pour l'algorithme génétique.
1498
- * @returns {Array<{solution: Array<number>, objectives: number[]}>} Le front de Pareto des solutions [minTimeToClick, maxClickVariance, minMouseEntropy, minScrollEvents].
1458
+ * Solves fraud detection optimization by finding the Pareto front of threshold values.
1459
+ * @param {object} context - Context containing click datasets.
1460
+ * @param {Array<object>} context.legitimateClicks - Sample of legitimate clicks.
1461
+ * @param {Array<object>} context.fraudulentClicks - Sample of fraudulent clicks.
1462
+ * @param {object} [options] - Genetic algorithm options.
1463
+ * @returns {Array<{solution: Array<number>, objectives: number[]}>} Pareto front of [minTimeToClick, maxClickVariance, minMouseEntropy, minScrollEvents].
1499
1464
  */
1500
1465
  Optimization.Operators.solveFraudDetection = (context, options = {}) => {
1501
1466
  const fitnessFunction =
1502
1467
  Optimization.Operators.createFraudThresholdEvaluator(context);
1503
1468
 
1504
- // Un "individu" est un tableau de 4 seuils : [minTimeToClick, maxClickVariance, minMouseEntropy, minScrollEvents]
1469
+ // An individual is a 4-threshold array: [minTimeToClick, maxClickVariance, minMouseEntropy, minScrollEvents]
1505
1470
  const createIndividual = () => {
1506
- const minTimeToClick = 100 + random() * 4900; // entre 100ms et 5s
1507
- const maxClickVariance = 1 + random() * 9999; // entre 1 et 10000
1508
- const minMouseEntropy = random() * 0.5; // entre 0 et 0.5
1509
- const minScrollEvents = crypto.randomInt(0, 10); // entre 0 et 10
1471
+ const minTimeToClick = 100 + random() * 4900; // between 100ms and 5s
1472
+ const maxClickVariance = 1 + random() * 9999; // between 1 and 10000
1473
+ const minMouseEntropy = random() * 0.5; // between 0 and 0.5
1474
+ const minScrollEvents = crypto.randomInt(0, 10); // between 0 and 10
1510
1475
  return [minTimeToClick, maxClickVariance, minMouseEntropy, minScrollEvents];
1511
1476
  };
1512
1477
 
1513
- // Croisement : moyenne des seuils des parents
1478
+ // Crossover: arithmetic mean of parent thresholds
1514
1479
  const crossover = (s1, s2) => {
1515
1480
  return [
1516
1481
  (s1[0] + s2[0]) / 2,
@@ -1520,14 +1485,14 @@ Optimization.Operators.solveFraudDetection = (context, options = {}) => {
1520
1485
  ];
1521
1486
  };
1522
1487
 
1523
- // Mutation : légère variation aléatoire d'un des seuils
1488
+ // Mutation: slight random perturbation of a single threshold
1524
1489
  const mutate = (solution) => {
1525
1490
  const newSolution = [...solution];
1526
1491
  const i = crypto.randomInt(0, 4);
1527
- // Amplitudes de mutation différentes pour chaque seuil
1492
+ // Specific mutation step scales for each dimension
1528
1493
  const mutationFactors = [500, 1000, 0.1, 2];
1529
1494
  const mutationFactor = mutationFactors[i];
1530
- newSolution[i] += (secureRandom() - 0.5) * mutationFactor;
1495
+ newSolution[i] += (random() - 0.5) * mutationFactor;
1531
1496
  return newSolution;
1532
1497
  };
1533
1498
 
@@ -1547,11 +1512,11 @@ Optimization.Operators.solveFraudDetection = (context, options = {}) => {
1547
1512
  };
1548
1513
 
1549
1514
  /**
1550
- * Crée un évaluateur multi-objectifs pour l'auto-tuning complet de la configuration de sécurité.
1551
- * Optimise à la fois les seuils, les poids de suspicion et les paramètres de détection de patterns.
1552
- * @param {object} config - L'objet de configuration.
1553
- * @param {Array<object>} config.trafficData - Données de trafic collectées.
1554
- * @returns {function(object): number[]} Une fonction de fitness qui prend une configuration complète et retourne les scores [taux de faux positifs, taux de faux négatifs].
1515
+ * Creates a multi-objective evaluator for end-to-end security configuration auto-tuning.
1516
+ * Jointly optimizes action thresholds, suspicion weights, and pattern detection parameters.
1517
+ * @param {object} context - Configuration context.
1518
+ * @param {Array<object>} context.trafficData - Collected traffic logs.
1519
+ * @returns {function(object): number[]} Fitness function returning [weighted FPR, weighted FNR].
1555
1520
  */
1556
1521
  Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1557
1522
  const trafficData = context.trafficData || [];
@@ -1561,25 +1526,25 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1561
1526
  const THREAT_PROFILES = {
1562
1527
  account_takeover: {
1563
1528
  importance: 10.0,
1564
- ux_vs_security_ratio: 0.1, // 10% FPR / 90% FNR (Priorité sécurité maximale)
1529
+ ux_vs_security_ratio: 0.1, // 10% FPR / 90% FNR (Highest security priority)
1565
1530
  target_threshold: 'block',
1566
1531
  indicators: ['requestPatternScore', 'behaviorScore', 'timeInconsistencyScore', 'clickVarianceScore']
1567
1532
  },
1568
1533
  active_exploitation: {
1569
1534
  importance: 8.0,
1570
- ux_vs_security_ratio: 0.2, // 20% FPR / 80% FNR (Sécurité prioritaire)
1535
+ ux_vs_security_ratio: 0.2, // 20% FPR / 80% FNR (Security prioritized)
1571
1536
  target_threshold: 'block',
1572
1537
  indicators: ['honeypotScore', 'headerAnomalyScore']
1573
1538
  },
1574
1539
  mass_scraping: {
1575
1540
  importance: 3.0,
1576
- ux_vs_security_ratio: 0.8, // 80% FPR / 20% FNR (UX prioritaire)
1541
+ ux_vs_security_ratio: 0.8, // 80% FPR / 20% FNR (UX prioritized)
1577
1542
  target_threshold: 'low',
1578
1543
  indicators: ['requestPatternScore', 'renderingAnomalyScore', 'clientHintsInconsistencyScore', 'virtualizationScore']
1579
1544
  },
1580
1545
  distributed_botnets: {
1581
1546
  importance: 6.0,
1582
- ux_vs_security_ratio: 0.5, // Équilibré
1547
+ ux_vs_security_ratio: 0.5, // Balanced
1583
1548
  target_threshold: 'high',
1584
1549
  indicators: ['subnetScore', 'botnetClusterScore', 'ipReputationScore', 'tlsSpoofingScore']
1585
1550
  },
@@ -1591,7 +1556,7 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1591
1556
  }
1592
1557
  };
1593
1558
 
1594
- // Ancres immuables (Baseline Anchors) pour forcer le calibrage d'échelle de suspicion
1559
+ // Immutable baseline anchors to calibrate the suspicion scale
1595
1560
  const STATIC_ANCHORS = [
1596
1561
  {
1597
1562
  type: 'request_passed',
@@ -1620,7 +1585,7 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1620
1585
  }
1621
1586
  },
1622
1587
  {
1623
- type: 'request_passed', // Profil humain sain avec du bruit (doit rester sous le seuil d'alerte, ex: < 20)
1588
+ type: 'request_passed', // Clean human profile with minor noise (must remain below alert threshold, e.g. < 20)
1624
1589
  weight: 10.0,
1625
1590
  vector: {
1626
1591
  historyScore: 15, rotationScore: 10, headerAnomalyScore: 20, requestPatternScore: 15,
@@ -1633,7 +1598,7 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1633
1598
  }
1634
1599
  },
1635
1600
  {
1636
- type: 'challenge_issued', // Profil robot furtif moyen (doit être challengé, ex: > 35)
1601
+ type: 'challenge_issued', // Stealth automated bot profile (must be challenged, e.g. > 35)
1637
1602
  weight: 10.0,
1638
1603
  vector: {
1639
1604
  historyScore: 20, rotationScore: 20, headerAnomalyScore: 20, requestPatternScore: 40,
@@ -1672,7 +1637,7 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1672
1637
  trap_triggered: 2.0,
1673
1638
  };
1674
1639
 
1675
- // 1. Évaluation sur les données de trafic réelles
1640
+ // 1. Evaluate on real traffic logs
1676
1641
  for (const log of trafficData) {
1677
1642
  const weight = log.weight || 1.0;
1678
1643
  const confidence = (confidenceWeights[log.type] || 1.0) * weight;
@@ -1713,7 +1678,7 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1713
1678
  }
1714
1679
 
1715
1680
  }
1716
- // 2. Évaluation sur les ancres immuables pour fixer l'échelle
1681
+ // 2. Evaluate against immutable baseline anchors to stabilize scale
1717
1682
  for (const anchor of STATIC_ANCHORS) {
1718
1683
  const score = calculateScore(anchor);
1719
1684
  const weight = anchor.weight;
@@ -1740,12 +1705,12 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1740
1705
  if (isBot) {
1741
1706
  threatStats[threatName].totalBots += effectiveWeight;
1742
1707
  if (score < targetThreshold) {
1743
- threatStats[threatName].fn += effectiveWeight * 10; // Pénalité punitive forte
1708
+ threatStats[threatName].fn += effectiveWeight * 10; // Strict punitive penalty
1744
1709
  }
1745
1710
  } else {
1746
1711
  threatStats[threatName].totalHumans += effectiveWeight;
1747
1712
  if (score >= targetThreshold) {
1748
- threatStats[threatName].fp += effectiveWeight * 10; // Pénalité punitive forte
1713
+ threatStats[threatName].fp += effectiveWeight * 10; // Strict punitive penalty
1749
1714
  }
1750
1715
  }
1751
1716
  }
@@ -1773,7 +1738,7 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1773
1738
  weightedFnr += fnr * importanceWeight * secRatio;
1774
1739
  }
1775
1740
 
1776
- // 3. Pénalité de dérive d'échelle L2 (Régularisation par rapport au point d'origine)
1741
+ // 3. L2 drift penalty against baseline configuration
1777
1742
  let regularizationPenalty = 0;
1778
1743
  if (currentConfig && currentConfig.weights) {
1779
1744
  for (const key in config.weights) {
@@ -1782,7 +1747,7 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1782
1747
  }
1783
1748
  }
1784
1749
 
1785
- // 4. Maximisation de la marge de séparation (SVM-like Margin Loss)
1750
+ // 4. Maximizing separation margin (SVM-like Margin Loss)
1786
1751
  const marginOverlap = Math.max(0, maxHumanScore - minBotScore);
1787
1752
  const marginPenalty = marginOverlap / 100;
1788
1753
 
@@ -1793,10 +1758,10 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1793
1758
  };
1794
1759
 
1795
1760
  /**
1796
- * Résout le problème de l'auto-tuning complet de la configuration de sécurité.
1797
- * @param {object} context - Le contexte contenant les données de trafic.
1798
- * @param {object} [options] - Options pour l'algorithme génétique.
1799
- * @returns {Array<{solution: object, objectives: number[]}>} Le front de Pareto des configurations optimales.
1761
+ * Solves the full security configuration auto-tuning problem.
1762
+ * @param {object} context - Context containing traffic event logs.
1763
+ * @param {object} [options] - Genetic algorithm options.
1764
+ * @returns {Array<{solution: object, objectives: number[]}>} Pareto front of optimal security configurations.
1800
1765
  */ // eslint-disable-line max-len
1801
1766
  Optimization.Operators.solveFullSecurityTuning = (context, options = {}) => {
1802
1767
  const fitnessFunction = Optimization.Operators.createFullSecurityConfigEvaluator(context);
@@ -1823,7 +1788,7 @@ Optimization.Operators.solveFullSecurityTuning = (context, options = {}) => {
1823
1788
  virtualizationScore: 0.8
1824
1789
  };
1825
1790
 
1826
- // Un "individu" est un objet de configuration complet
1791
+ // An "individual" is a complete security configuration object
1827
1792
  const createIndividual = () => ({
1828
1793
  thresholds: {
1829
1794
  low: 15 + random() * 20, // 15-35
@@ -1854,10 +1819,10 @@ Optimization.Operators.solveFullSecurityTuning = (context, options = {}) => {
1854
1819
  }
1855
1820
  });
1856
1821
 
1857
- // Le crossover et la mutation doivent maintenant opérer sur des objets complexes.
1822
+ // Crossover and mutation operators for complex structured configurations
1858
1823
  const crossover = (c1, c2) => {
1859
1824
  const child = JSON.parse(JSON.stringify(c1)); // Deep copy
1860
- // Croisement pour chaque groupe de paramètres
1825
+ // Crossover across each parameter section
1861
1826
  for (const key in child.thresholds) {
1862
1827
  child.thresholds[key] = (c1.thresholds[key] + c2.thresholds[key]) / 2;
1863
1828
  }
@@ -1873,9 +1838,8 @@ Optimization.Operators.solveFullSecurityTuning = (context, options = {}) => {
1873
1838
  const mutate = (c, currentConfig) => {
1874
1839
  const newConfig = JSON.parse(JSON.stringify(c));
1875
1840
 
1876
- // --- NOUVELLE LOGIQUE : Sélection de section pondérée ---
1877
- // On donne plus de poids à la mutation des 'patterns' et des 'weights',
1878
- // car ils ont un impact plus direct sur la détection que les seuils.
1841
+ // Weighted section selection: prioritize 'patterns' and 'weights'
1842
+ // as they have a more direct impact on detection accuracy than thresholds.
1879
1843
  const sections = [
1880
1844
  { name: 'patterns', weight: 0.50 },
1881
1845
  { name: 'thresholds', weight: 0.25 },
@@ -1896,7 +1860,7 @@ Optimization.Operators.solveFullSecurityTuning = (context, options = {}) => {
1896
1860
  const keyToMutate = keys[crypto.randomInt(0, keys.length)];
1897
1861
 
1898
1862
 
1899
- // S'assurer que les valeurs restent dans des limites raisonnables
1863
+ // Ensure mutated parameters stay within reasonable bounds
1900
1864
  if (sectionToMutate === 'weights') {
1901
1865
  newConfig[sectionToMutate][keyToMutate] = Math.max(0.05, Math.min(1.5, newConfig[sectionToMutate][keyToMutate] + (random() - 0.5) * 0.1));
1902
1866
  } else if (sectionToMutate === 'thresholds') {
@@ -1909,10 +1873,10 @@ Optimization.Operators.solveFullSecurityTuning = (context, options = {}) => {
1909
1873
  }
1910
1874
  }
1911
1875
 
1912
- // Contrainte de dérive maximale (±30% par rapport à la configuration actuelle)
1876
+ // Maximum drift constraint (+/- 30% relative to current reference configuration)
1913
1877
  if (currentConfig && currentConfig[sectionToMutate] && currentConfig[sectionToMutate][keyToMutate] !== undefined) {
1914
1878
  const originalValue = currentConfig[sectionToMutate][keyToMutate];
1915
- if (typeof originalValue === 'number' && originalValue !== 0) { // Éviter la division par zéro ou la contrainte sur 0
1879
+ if (typeof originalValue === 'number' && originalValue !== 0) { // Avoid division by zero or locking zeroes
1916
1880
  const minAllowed = originalValue * 0.7; // -30%
1917
1881
  const maxAllowed = originalValue * 1.3; // +30%
1918
1882
  newConfig[sectionToMutate][keyToMutate] = Math.max(minAllowed, Math.min(maxAllowed, newConfig[sectionToMutate][keyToMutate]));
@@ -1926,7 +1890,7 @@ Optimization.Operators.solveFullSecurityTuning = (context, options = {}) => {
1926
1890
  createIndividual,
1927
1891
  fitnessFunction,
1928
1892
  crossover,
1929
- (c) => mutate(c, context.currentConfig), // Passer currentConfig à la fonction de mutation
1893
+ (c) => mutate(c, context.currentConfig), // Pass currentConfig to mutation function
1930
1894
  { generations: 50, populationSize: 50, ...options }
1931
1895
  );
1932
1896
  };