[ADD] engine tests: reference fingerprints of today's plans and ranking
The coming configurable order must leave every default plan and ranking unchanged at equal seed. Fingerprints of the plans of the four shipped demos, of the acceptance switch, and of a twelve-proposal ranking where each criterion is the first to split some neighbouring pair are written once on the previous revision and verified by every run; rewriting them needs GTT_EMPREINTES=ecrire, any other value is refused by name. Checked: 20 node tests, 3 long; named mutations of the order caught. --- FR --- [ADD] épreuves du moteur : empreintes des plans et du classement actuels L'ordre réglable à venir doit laisser chaque plan et chaque classement par défaut inchangés à graine égale. Les empreintes des plans des quatre démonstrations, de la bascule d'acceptation et d'un classement de douze propositions où chaque critère départage une paire voisine sont écrites une fois sur la révision précédente et vérifiées à chaque exécution ; les récrire exige GTT_EMPREINTES=ecrire, toute autre valeur est refusée. Vérifié : 20 épreuves node, 3 longues ; mutations nommées de l'ordre prises. Assisted-by: Claude Opus 5.5
This commit is contained in:
parent
a5cf1b2630
commit
c445df3dc8
5 changed files with 1469 additions and 0 deletions
137
src/application/empreintes.test.js
Normal file
137
src/application/empreintes.test.js
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||||
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
// Classement de référence (§ 5.7, § 14.10) : une charge de propositions
|
||||
// engendrées par rechercher, enregistrées par la commande, s'évalue par
|
||||
// evaluer sous les quatre cases cochées, plafond a priori calculé puis
|
||||
// inconnu. L'ordre, les critères appliqués, le critère sauté et les grandeurs
|
||||
// nommées de chaque proposition se comparent au fichier versionné
|
||||
// test/fixtures/moteur/empreintes_defaut.json, que seule
|
||||
// src/moteur/empreintes.test.js récrit, sous GTT_EMPREINTES=ecrire : cette
|
||||
// épreuve-ci ne fait que relire, et refuse de se jouer sous ce mode. Les
|
||||
// grandeurs de chaque proposition sont celles qu'une épreuve des seize
|
||||
// combinaisons relit pour en déduire l'ordre attendu sous chacune.
|
||||
//
|
||||
// Un ordre figé ne garde un critère que s'il y décide quelque chose : la
|
||||
// référence est tenue de faire de chaque critère appliqué, dans un de ses
|
||||
// classements au moins, le premier qui départage deux propositions voisines,
|
||||
// et un critère dont le retrait change l'ordre. Sans quoi un comparateur qui
|
||||
// inverse ou oublie ce critère rend le même ordre que la référence.
|
||||
import assert from 'node:assert/strict';
|
||||
import { describe, test } from '../../test/lanceur.js';
|
||||
import {
|
||||
CASES,
|
||||
CLASSEMENT,
|
||||
chargeDeClassement,
|
||||
classementLu,
|
||||
empreintesDuClassement,
|
||||
exigerVerification,
|
||||
lireEmpreintes,
|
||||
} from '../../test/empreintes_moteur.js';
|
||||
import { evaluer } from './evaluation.js';
|
||||
|
||||
exigerVerification(process.env);
|
||||
const REFERENCE = lireEmpreintes();
|
||||
|
||||
// Les deux classements de chaque charge, sous le nom de leur plafond a priori.
|
||||
const PLAFONDS = Object.freeze(['connu', 'inconnu']);
|
||||
|
||||
// Pour chaque critère du classement, le nom de la grandeur nommée qu'il lit
|
||||
// dans le fichier.
|
||||
const GRANDEUR_DU_CRITERE = new Map([
|
||||
['ecartAuPlafondAPrioriMax', 'ecartAuPlafondAPrioriMax'],
|
||||
['excedentCollisions', 'excedentCollisions'],
|
||||
['collisionsCumulees', 'collisionsCumulees'],
|
||||
['rencontresRepetees', 'rencontresRepeteesChoisies'],
|
||||
['redondance', 'redondance'],
|
||||
]);
|
||||
|
||||
// La valeur d'un critère pour la proposition id, lue dans les grandeurs de
|
||||
// la référence. Lève pour un critère sans grandeur, en le nommant.
|
||||
function valeur(grandeurs, id, critere) {
|
||||
const nom = GRANDEUR_DU_CRITERE.get(critere);
|
||||
if (nom === undefined) throw new Error(`critère sans grandeur nommée : ${critere}`);
|
||||
return grandeurs[String(id)][nom];
|
||||
}
|
||||
|
||||
// Le premier critère de criteres sur lequel deux propositions diffèrent ;
|
||||
// null quand elles sont égales sur tous.
|
||||
function premierQuiDepartage(grandeurs, criteres, a, b) {
|
||||
return criteres.find((critere) => valeur(grandeurs, a, critere) !== valeur(grandeurs, b, critere)) ?? null;
|
||||
}
|
||||
|
||||
// Les identifiants ids rangés critère par critère dans l'ordre de criteres,
|
||||
// chacun croissant, puis par identifiant croissant : le classement du § 5.7,
|
||||
// refait sur les grandeurs du fichier.
|
||||
function ordonner(grandeurs, criteres, ids) {
|
||||
return [...ids].sort((a, b) => {
|
||||
const critere = premierQuiDepartage(grandeurs, criteres, a, b);
|
||||
return critere === null ? a - b : valeur(grandeurs, a, critere) - valeur(grandeurs, b, critere);
|
||||
});
|
||||
}
|
||||
|
||||
// Chaque classement de la référence, nommé par sa charge et son plafond a
|
||||
// priori, avec les grandeurs de ses propositions.
|
||||
function classementsDeReference() {
|
||||
return CLASSEMENT.cles.flatMap((cle) =>
|
||||
PLAFONDS.map((plafond) => ({
|
||||
nom: `${cle}, plafond a priori ${plafond}`,
|
||||
...REFERENCE.classements[cle][plafond],
|
||||
grandeurs: REFERENCE.classements[cle].propositions,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
describe('classement de référence : evaluer sous les quatre cases cochées (§ 5.7, § 14.10)', () => {
|
||||
test(`la référence porte ${CLASSEMENT.cles.join(' et ')}, ${CLASSEMENT.nombre} propositions chacune`, () => {
|
||||
assert.deepEqual(Object.keys(REFERENCE.classements), CLASSEMENT.cles);
|
||||
for (const cle of CLASSEMENT.cles) {
|
||||
const { propositions } = REFERENCE.classements[cle];
|
||||
assert.equal(Object.keys(propositions).length, CLASSEMENT.nombre, cle);
|
||||
}
|
||||
});
|
||||
|
||||
for (const cle of CLASSEMENT.cles) {
|
||||
test(`${cle} : l’ordre et les critères appliqués, plafond a priori calculé puis inconnu, sont ceux de la référence`, () => {
|
||||
const charge = chargeDeClassement(cle);
|
||||
assert.deepEqual(CASES.map((nom) => charge.reglages[nom]), [true, true, true, true], `${cle} : les quatre cases cochées`);
|
||||
const attendu = REFERENCE.classements[cle];
|
||||
assert.deepEqual(classementLu(evaluer(charge).classement), attendu.connu, `${cle}, plafond a priori calculé`);
|
||||
assert.deepEqual(
|
||||
classementLu(evaluer(charge, { plafondsAPriori: null }).classement),
|
||||
attendu.inconnu,
|
||||
`${cle}, plafond a priori inconnu`,
|
||||
);
|
||||
});
|
||||
|
||||
test(`${cle} : les grandeurs nommées de chaque proposition sont celles de la référence`, () => {
|
||||
assert.deepEqual(empreintesDuClassement(cle).propositions, REFERENCE.classements[cle].propositions, cle);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('classement de référence : chaque critère appliqué y décide (§ 5.7, § 14.10)', () => {
|
||||
test('les grandeurs de la référence, rangées sous ses critères appliqués, redonnent son ordre', () => {
|
||||
for (const { nom, ordre, criteresAppliques, grandeurs } of classementsDeReference()) {
|
||||
assert.deepEqual(ordonner(grandeurs, criteresAppliques, ordre), ordre, nom);
|
||||
}
|
||||
});
|
||||
|
||||
test('chaque critère appliqué départage le premier deux propositions voisines, et son retrait change l’ordre', () => {
|
||||
const classements = classementsDeReference();
|
||||
const criteres = [...new Set(classements.flatMap(({ criteresAppliques }) => criteresAppliques))];
|
||||
const sansEffet = [];
|
||||
for (const critere of criteres) {
|
||||
const departage = classements.some(({ ordre, criteresAppliques, grandeurs }) =>
|
||||
ordre.slice(1).some((id, rang) => premierQuiDepartage(grandeurs, criteresAppliques, ordre[rang], id) === critere),
|
||||
);
|
||||
const compte = classements.some(({ ordre, criteresAppliques, grandeurs }) => {
|
||||
const sans = criteresAppliques.filter((autre) => autre !== critere);
|
||||
return criteresAppliques.includes(critere) && ordonner(grandeurs, sans, ordre).join() !== ordre.join();
|
||||
});
|
||||
if (!departage) sansEffet.push(`${critere} : ne départage le premier aucune paire voisine`);
|
||||
if (!compte) sansEffet.push(`${critere} : son retrait ne change aucun ordre`);
|
||||
}
|
||||
assert.deepEqual(sansEffet, []);
|
||||
});
|
||||
});
|
||||
39
src/moteur/empreintes.long.test.js
Normal file
39
src/moteur/empreintes.long.test.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||||
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
// Empreintes de référence du moteur, série node-long (§ 5.7, § 14.10,
|
||||
// § 15.5) : la grande démonstration et sa variante sans exception, sous les
|
||||
// trois graines et le compte réduit de test/empreintes_moteur.js. Chaque plan
|
||||
// rendu par rechercher se compare, empreinte et grandeurs nommées, au fichier
|
||||
// versionné test/fixtures/moteur/empreintes_defaut.json, que seule
|
||||
// src/moteur/empreintes.test.js récrit, sous GTT_EMPREINTES=ecrire : cette
|
||||
// épreuve-ci ne fait que relire, et refuse de se jouer sous ce mode, où elle
|
||||
// pourrait lire un fichier en cours d'écriture.
|
||||
import assert from 'node:assert/strict';
|
||||
import { describe, test } from '../../test/lanceur.js';
|
||||
import {
|
||||
ARRET,
|
||||
GRAINES,
|
||||
differences,
|
||||
empreintesDesPlans,
|
||||
exigerVerification,
|
||||
lireEmpreintes,
|
||||
} from '../../test/empreintes_moteur.js';
|
||||
import { HISTORIQUE_PAR_DEFAUT } from './recherche.js';
|
||||
|
||||
exigerVerification(process.env);
|
||||
const REFERENCE = lireEmpreintes();
|
||||
|
||||
describe('empreintes de référence : la grande démonstration et sa variante sans exception (§ 14.10)', () => {
|
||||
test('la référence a été relevée à l’historique par défaut de la recherche', () => {
|
||||
assert.equal(REFERENCE.reglages.historique, HISTORIQUE_PAR_DEFAUT);
|
||||
});
|
||||
|
||||
for (const cle of ['grande', 'grande-sans-exception']) {
|
||||
test(`${cle} : les plans des graines ${GRAINES.join(', ')}, à ${ARRET} mouvements, sont ceux de la référence`, () => {
|
||||
const calcule = empreintesDesPlans(cle);
|
||||
assert.deepEqual(differences(cle, calcule, REFERENCE.plans[cle]), []);
|
||||
assert.deepEqual(calcule, REFERENCE.plans[cle], cle);
|
||||
});
|
||||
}
|
||||
});
|
||||
180
src/moteur/empreintes.test.js
Normal file
180
src/moteur/empreintes.test.js
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||||
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
// Empreintes de référence du moteur, série node (§ 5.7, § 14.10, § 15.5) : la
|
||||
// petite démonstration et sa variante au conflit inévitable, sous les trois
|
||||
// graines et le compte réduit de test/empreintes_moteur.js, et la petite sous
|
||||
// chacune des seize combinaisons des quatre cases ; les deux encore sous le
|
||||
// compte plus long de la section bascule, où l'ordre d'acceptation qui suit
|
||||
// l'annulation de l'écart laisse sa trace dans le plan. Chaque plan rendu par
|
||||
// rechercher se compare, empreinte et grandeurs nommées, au fichier versionné
|
||||
// test/fixtures/moteur/empreintes_defaut.json : une empreinte qui change est
|
||||
// un défaut, jamais une régénération. Le fichier ne se récrit que sous
|
||||
// GTT_EMPREINTES=ecrire, par cette seule épreuve, qui recalcule alors toutes
|
||||
// les sections — la grande et le classement compris — avant de les relire.
|
||||
// La grande et sa variante sans exception se vérifient dans la série
|
||||
// node-long (empreintes.long.test.js), le classement par evaluer dans
|
||||
// src/application/empreintes.test.js.
|
||||
import assert from 'node:assert/strict';
|
||||
import { describe, test } from '../../test/lanceur.js';
|
||||
import {
|
||||
ARRET,
|
||||
BASCULE,
|
||||
CASES,
|
||||
CLASSEMENT,
|
||||
GRAINES,
|
||||
GRANDEURS,
|
||||
combinaisons,
|
||||
differences,
|
||||
ecrireEmpreintes,
|
||||
empreinteDe,
|
||||
empreinteDuPlan,
|
||||
empreintesDesCombinaisons,
|
||||
empreintesDesPlans,
|
||||
lireEmpreintes,
|
||||
modeEmpreintes,
|
||||
} from '../../test/empreintes_moteur.js';
|
||||
import { CATALOGUE } from '../demo/catalogue.js';
|
||||
import { indexerPlan, normaliser } from './configuration.js';
|
||||
import { HISTORIQUE_PAR_DEFAUT, rechercher } from './recherche.js';
|
||||
|
||||
if (modeEmpreintes(process.env) === 'ecrire') ecrireEmpreintes();
|
||||
const REFERENCE = lireEmpreintes();
|
||||
|
||||
// Les configurations de cette série, dans l'ordre du catalogue.
|
||||
const PETITES = ['petite', 'petite-conflit'];
|
||||
|
||||
describe('empreintes de référence : la petite démonstration et sa variante (§ 14.10)', () => {
|
||||
for (const cle of PETITES) {
|
||||
test(`${cle} : les plans des graines ${GRAINES.join(', ')}, à ${ARRET} mouvements, sont ceux de la référence`, () => {
|
||||
const calcule = empreintesDesPlans(cle);
|
||||
assert.deepEqual(differences(cle, calcule, REFERENCE.plans[cle]), []);
|
||||
assert.deepEqual(calcule, REFERENCE.plans[cle], cle);
|
||||
});
|
||||
}
|
||||
|
||||
test('petite : sous chacune des seize combinaisons des quatre cases, graine 1, le plan est celui de la référence', () => {
|
||||
const calcule = empreintesDesCombinaisons();
|
||||
assert.equal(Object.keys(REFERENCE.combinaisons).length, 16);
|
||||
assert.deepEqual(differences('petite', calcule, REFERENCE.combinaisons), []);
|
||||
assert.deepEqual(calcule, REFERENCE.combinaisons);
|
||||
});
|
||||
|
||||
for (const cle of BASCULE.cles) {
|
||||
test(`${cle} : à ${BASCULE.arret} mouvements, où la meilleure affectation s’améliore après la bascule, les plans sont ceux de la référence`, () => {
|
||||
const calcule = empreintesDesPlans(cle, BASCULE.arret);
|
||||
assert.deepEqual(differences(`${cle} (bascule)`, calcule, REFERENCE.bascule[cle]), []);
|
||||
assert.deepEqual(calcule, REFERENCE.bascule[cle], cle);
|
||||
});
|
||||
}
|
||||
|
||||
test('la référence a été relevée sous les réglages de test/empreintes_moteur.js', () => {
|
||||
assert.deepEqual(REFERENCE.reglages, {
|
||||
graines: [...GRAINES],
|
||||
arret: ARRET,
|
||||
historique: HISTORIQUE_PAR_DEFAUT,
|
||||
cases: [...CASES],
|
||||
classement: { ...CLASSEMENT, cles: [...CLASSEMENT.cles] },
|
||||
bascule: { ...BASCULE, cles: [...BASCULE.cles] },
|
||||
});
|
||||
});
|
||||
|
||||
test('la référence couvre les quatre configurations du catalogue, chacune sous chaque graine', () => {
|
||||
assert.deepEqual(
|
||||
Object.keys(REFERENCE.plans),
|
||||
CATALOGUE.map(({ cle }) => cle),
|
||||
);
|
||||
for (const { cle } of CATALOGUE) {
|
||||
assert.deepEqual(Object.keys(REFERENCE.plans[cle]), GRAINES.map(String), cle);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('empreintes de référence : ce que porte une empreinte', () => {
|
||||
// Un plan de la petite variante et son instance, la configuration du
|
||||
// catalogue sous la graine 1.
|
||||
function unPlan() {
|
||||
const configuration = CATALOGUE.find(({ cle }) => cle === 'petite-conflit').construire();
|
||||
const [{ plan }] = rechercher(configuration, { graine: 1, arret: 2_000, nombre: 1 });
|
||||
return { instance: normaliser(configuration), plan };
|
||||
}
|
||||
|
||||
test('les grandeurs nommées sont les six du § 14.10, dans cet ordre, et aucune composante du n-uplet', () => {
|
||||
assert.deepEqual(GRANDEURS, [
|
||||
'ecartAuPlafondAPrioriMax',
|
||||
'excedentCollisions',
|
||||
'collisionsCumulees',
|
||||
'rencontresRepeteesChoisies',
|
||||
'retoursChoisis',
|
||||
'redondance',
|
||||
]);
|
||||
const { instance, plan } = unPlan();
|
||||
assert.deepEqual(Object.keys(empreinteDuPlan(instance, plan).grandeurs), GRANDEURS);
|
||||
});
|
||||
|
||||
test('deux plans qui ne diffèrent que par la table d’une personne à un tour ont deux empreintes', () => {
|
||||
const { instance, plan } = unPlan();
|
||||
const avant = empreinteDuPlan(instance, plan);
|
||||
// La première personne des deux premières tables du tour 1 échange sa
|
||||
// place : les grandeurs peuvent rester égales, le plan ne l'est pas.
|
||||
const tours = plan.tours.map((tour) => tour.map((liste) => [...liste]));
|
||||
const [a] = tours[0][0];
|
||||
const [b] = tours[0][1];
|
||||
tours[0][0][0] = b;
|
||||
tours[0][1][0] = a;
|
||||
const apres = empreinteDuPlan(instance, { ...plan, tours });
|
||||
assert.notEqual(apres.empreinte, avant.empreinte);
|
||||
assert.notDeepEqual(indexerPlan(instance, { ...plan, tours }), indexerPlan(instance, plan));
|
||||
});
|
||||
|
||||
test('l’empreinte lit chaque grandeur sous son nom : une grandeur changée change l’empreinte', () => {
|
||||
const { instance, plan } = unPlan();
|
||||
const { empreinte, grandeurs } = empreinteDuPlan(instance, plan);
|
||||
const tableDe = indexerPlan(instance, plan);
|
||||
for (const nom of GRANDEURS) {
|
||||
const autre = { ...grandeurs, [nom]: grandeurs[nom] + 1 };
|
||||
assert.notEqual(empreinteDe(tableDe, autre), empreinte, nom);
|
||||
}
|
||||
assert.equal(empreinteDe(tableDe, grandeurs), empreinte);
|
||||
});
|
||||
|
||||
test('les seize combinaisons sont distinctes, chacune nommée par ses cases cochées', () => {
|
||||
const toutes = combinaisons();
|
||||
assert.equal(toutes.length, 16);
|
||||
assert.equal(new Set(toutes.map(({ cle }) => cle)).size, 16);
|
||||
assert.deepEqual(toutes[0], { cle: CASES.join('+'), contraintes: Object.fromEntries(CASES.map((c) => [c, true])) });
|
||||
assert.deepEqual(toutes.at(-1), { cle: 'aucune', contraintes: Object.fromEntries(CASES.map((c) => [c, false])) });
|
||||
for (const { cle, contraintes } of toutes) {
|
||||
assert.deepEqual(Object.keys(contraintes), CASES, cle);
|
||||
const cochees = CASES.filter((c) => contraintes[c]);
|
||||
assert.equal(cle, cochees.length === 0 ? 'aucune' : cochees.join('+'));
|
||||
}
|
||||
});
|
||||
|
||||
test('differences nomme la configuration, la graine absente de la référence et la grandeur changée', () => {
|
||||
const attendu = { 1: { empreinte: 'a', grandeurs: { excedentCollisions: 2, redondance: 5 } } };
|
||||
assert.deepEqual(differences('petite', attendu, attendu), []);
|
||||
assert.deepEqual(
|
||||
differences('petite', { 4: attendu[1] }, attendu),
|
||||
['petite, 4 : absente de la référence', 'petite, 1 : référence non recalculée'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
differences('petite', { 1: { empreinte: 'b', grandeurs: { excedentCollisions: 3, redondance: 5 } } }, attendu),
|
||||
['petite, 1 : excedentCollisions 2 → 3'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
differences('petite', { 1: { empreinte: 'b', grandeurs: attendu[1].grandeurs } }, attendu),
|
||||
['petite, 1 : plan changé, grandeurs égales'],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GTT_EMPREINTES : verifier par défaut, ecrire sur demande', () => {
|
||||
test('absente ou vide : verifier ; verifier et ecrire se lisent tels quels ; une autre valeur lève en la nommant', () => {
|
||||
assert.equal(modeEmpreintes({}), 'verifier');
|
||||
assert.equal(modeEmpreintes({ GTT_EMPREINTES: '' }), 'verifier');
|
||||
assert.equal(modeEmpreintes({ GTT_EMPREINTES: 'verifier' }), 'verifier');
|
||||
assert.equal(modeEmpreintes({ GTT_EMPREINTES: 'ecrire' }), 'ecrire');
|
||||
assert.throws(() => modeEmpreintes({ GTT_EMPREINTES: 'ecrir' }), /GTT_EMPREINTES.*« ecrir »/);
|
||||
});
|
||||
});
|
||||
392
test/empreintes_moteur.js
Normal file
392
test/empreintes_moteur.js
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||||
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
// Empreintes de référence du moteur (§ 5.7, § 14.10, § 15.5) : ce qui se
|
||||
// calcule, et le fichier versionné qui en garde la valeur,
|
||||
// test/fixtures/moteur/empreintes_defaut.json.
|
||||
//
|
||||
// Empreinte d'un plan. Le plan indexé tableDe (indexerPlan : une case par
|
||||
// personne et par tour, dans l'ordre canonique de l'instance, l'index de la
|
||||
// table ou RESERVE) s'écrit en entiers de 32 bits petit-boutistes, suivis du
|
||||
// texte JSON des grandeurs nommées, chacune sous son nom, dans l'ordre de
|
||||
// GRANDEURS ; l'empreinte est le SHA-256 de ces octets, en hexadécimal. Les
|
||||
// grandeurs sont celles que le classement lit — le plus grand écart au
|
||||
// plafond a priori, l'excédent de collisions, les collisions cumulées, les
|
||||
// rencontres répétées choisies, les retours choisis, la redondance —, lues
|
||||
// dans mesurer et dans manque.js, jamais dans le n-uplet de la recherche :
|
||||
// une composante toujours nulle qu'on retire ou qu'on ajoute ne change aucune
|
||||
// empreinte. Le fichier garde aussi les grandeurs en clair, pour que l'échec
|
||||
// nomme celle qui a changé.
|
||||
//
|
||||
// Ce que le fichier porte, chaque section dans cet ordre :
|
||||
// - reglages : les constantes de ce module sous lesquelles il a été relevé ;
|
||||
// - plans : pour chaque configuration du catalogue, dans son ordre, et chaque
|
||||
// graine de GRAINES, l'empreinte du plan que rend rechercher à ARRET
|
||||
// mouvements, une proposition, historique par défaut, quatre cases cochées ;
|
||||
// - combinaisons : la petite démonstration sous chacune des seize
|
||||
// combinaisons des quatre cases, graine 1, mêmes réglages ;
|
||||
// - bascule : les configurations de BASCULE sous chaque graine de GRAINES, à
|
||||
// BASCULE.arret mouvements, assez pour que la meilleure affectation
|
||||
// s'améliore encore après la bascule vers l'ordre des contraintes : à
|
||||
// ARRET, sur ces configurations, elle ne change plus une fois l'écart
|
||||
// annulé, et l'ordre des composantes après la bascule ne laisse aucune
|
||||
// trace dans les plans ;
|
||||
// - classements : pour chaque charge de CLASSEMENT.cles, le classement
|
||||
// qu'evaluer rend sous les quatre cases cochées, plafond a priori calculé
|
||||
// (connu) puis inconnu, et les grandeurs nommées de chaque proposition.
|
||||
//
|
||||
// GTT_EMPREINTES choisit le mode, sur le modèle de GTT_CAPTURES : verifier
|
||||
// par défaut ; ecrire récrit le fichier, par la seule épreuve
|
||||
// src/moteur/empreintes.test.js, et seulement sur l'état du moteur que le
|
||||
// fichier doit garder. Une empreinte qui change est un défaut, jamais une
|
||||
// régénération : écrire est un geste conscient (§ 15.5, point 5).
|
||||
import { createHash } from 'node:crypto';
|
||||
import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
||||
import { dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { appliquerCommande } from '../src/application/commandes.js';
|
||||
import { evaluer } from '../src/application/evaluation.js';
|
||||
import { CATALOGUE } from '../src/demo/catalogue.js';
|
||||
import { texteLivre } from '../src/demo/livrees.js';
|
||||
import { indexerPlan, normaliser } from '../src/moteur/configuration.js';
|
||||
import { mesurer } from '../src/moteur/indicateurs.js';
|
||||
import { ecartsAuPlafondAPriori } from '../src/moteur/manque.js';
|
||||
import { plafondsAPriori } from '../src/moteur/plafond.js';
|
||||
import { HISTORIQUE_PAR_DEFAUT, rechercher } from '../src/moteur/recherche.js';
|
||||
import { analyser, configurationDepuisCharge } from '../src/stockage/document.js';
|
||||
import { VERSION } from '../src/version.genere.js';
|
||||
|
||||
/** Le fichier des empreintes de référence. */
|
||||
export const CHEMIN_EMPREINTES = fileURLToPath(new URL('./fixtures/moteur/empreintes_defaut.json', import.meta.url));
|
||||
|
||||
/** Les graines des plans de référence, une proposition chacune. */
|
||||
export const GRAINES = Object.freeze([1, 2, 3]);
|
||||
|
||||
/** Le compte d'arrêt réduit des plans de référence. */
|
||||
export const ARRET = 20_000;
|
||||
|
||||
/**
|
||||
* Les plans de la section bascule (en-tête du module) : les configurations
|
||||
* et le compte d'arrêt, les graines étant celles de GRAINES.
|
||||
*/
|
||||
export const BASCULE = Object.freeze({
|
||||
cles: Object.freeze(['petite', 'petite-conflit']),
|
||||
arret: 50_000,
|
||||
});
|
||||
|
||||
/** Les quatre cases du § 5.3, dans l'ordre où une combinaison les nomme. */
|
||||
export const CASES = Object.freeze([
|
||||
'separerAppartenances',
|
||||
'nouveauxVoisins',
|
||||
'nouvelleTable',
|
||||
'varierAppartenances',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Les charges du classement de référence, des démonstrations livrées, et la
|
||||
* génération qui leur donne leurs propositions : nombre propositions de
|
||||
* graine et arret. Le compte court laisse les propositions inégales sur
|
||||
* plusieurs critères, ce dont les seize combinaisons ont besoin pour
|
||||
* départager ; le nombre donne à des propositions voisines des égalités
|
||||
* jusqu'aux derniers critères. Sous ces grandeurs, chaque critère appliqué
|
||||
* est, dans un classement au moins, le premier qui départage deux
|
||||
* propositions voisines, et son retrait change l'ordre :
|
||||
* src/application/empreintes.test.js le vérifie sur le fichier.
|
||||
*/
|
||||
export const CLASSEMENT = Object.freeze({
|
||||
cles: Object.freeze(['petite-conflit', 'grande']),
|
||||
graine: 7,
|
||||
arret: 1_000,
|
||||
nombre: 12,
|
||||
});
|
||||
|
||||
const MODES = Object.freeze(['verifier', 'ecrire']);
|
||||
|
||||
// Les grandeurs nommées, chacune avec sa lecture dans les mesures d'un plan
|
||||
// et le plus grand de ses écarts au plafond a priori, null quand ce plafond
|
||||
// est inconnu.
|
||||
const LECTURES = Object.freeze([
|
||||
['ecartAuPlafondAPrioriMax', (_mesures, ecartMax) => ecartMax],
|
||||
['excedentCollisions', (mesures) => mesures.excedentCollisions],
|
||||
['collisionsCumulees', (mesures) => mesures.collisionsCumulees],
|
||||
['rencontresRepeteesChoisies', (mesures) => mesures.rencontresRepetees.choisies],
|
||||
['retoursChoisis', (mesures) => mesures.totalRetoursChoisis],
|
||||
['redondance', (mesures) => mesures.totalRedondance],
|
||||
]);
|
||||
|
||||
/** Les noms des grandeurs qu'une empreinte porte, dans leur ordre. */
|
||||
export const GRANDEURS = Object.freeze(LECTURES.map(([nom]) => nom));
|
||||
|
||||
/**
|
||||
* Le mode que GTT_EMPREINTES demande : 'verifier' quand la variable est
|
||||
* absente ou vide. Seule lecture de cette variable.
|
||||
*
|
||||
* @param {Record<string, string|undefined>} env
|
||||
* @returns {'verifier'|'ecrire'}
|
||||
* @throws {Error} une autre valeur, nommée
|
||||
*/
|
||||
export function modeEmpreintes(env) {
|
||||
const valeur = env.GTT_EMPREINTES ?? '';
|
||||
if (valeur === '') return 'verifier';
|
||||
if (MODES.includes(valeur)) return valeur;
|
||||
throw new Error(`GTT_EMPREINTES : « ${valeur} » n’est ni ${MODES.map((m) => `« ${m} »`).join(' ni ')}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refuse le mode ecrire : une épreuve qui ne fait que relire le fichier
|
||||
* pourrait le lire pendant que src/moteur/empreintes.test.js le récrit.
|
||||
*
|
||||
* @param {Record<string, string|undefined>} env
|
||||
* @throws {Error} sous GTT_EMPREINTES=ecrire, ou une valeur inconnue
|
||||
*/
|
||||
export function exigerVerification(env) {
|
||||
if (modeEmpreintes(env) === 'ecrire') {
|
||||
throw new Error('GTT_EMPREINTES=ecrire se joue sur src/moteur/empreintes.test.js seule ; cette épreuve ne fait que relire');
|
||||
}
|
||||
}
|
||||
|
||||
// Le plus grand élément d'une liste ; null pour une liste vide ou absente.
|
||||
function plusGrand(valeurs) {
|
||||
if (valeurs === null || valeurs.length === 0) return null;
|
||||
let plus = valeurs[0];
|
||||
for (const valeur of valeurs) if (valeur > plus) plus = valeur;
|
||||
return plus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Les grandeurs nommées d'un plan mesuré, dans l'ordre de GRANDEURS.
|
||||
*
|
||||
* @param {import('../src/moteur/indicateurs.js').Mesures} mesures
|
||||
* @param {number|null} ecartMax le plus grand écart au plafond a priori
|
||||
* @returns {Record<string, number|null>}
|
||||
*/
|
||||
export function grandeursDe(mesures, ecartMax) {
|
||||
return Object.fromEntries(LECTURES.map(([nom, lire]) => [nom, lire(mesures, ecartMax)]));
|
||||
}
|
||||
|
||||
/**
|
||||
* L'empreinte d'un plan indexé et de ses grandeurs nommées (en-tête du
|
||||
* module). Ne lit des grandeurs que celles de GRANDEURS, chacune sous son
|
||||
* nom.
|
||||
*
|
||||
* @param {ArrayLike<number>} tableDe
|
||||
* @param {Record<string, number|null>} grandeurs
|
||||
* @returns {string} 64 chiffres hexadécimaux
|
||||
*/
|
||||
export function empreinteDe(tableDe, grandeurs) {
|
||||
const octets = Buffer.alloc(tableDe.length * 4);
|
||||
for (let i = 0; i < tableDe.length; i += 1) octets.writeInt32LE(tableDe[i], i * 4);
|
||||
const texte = JSON.stringify(GRANDEURS.map((nom) => [nom, grandeurs[nom]]));
|
||||
return createHash('sha256').update(octets).update(texte, 'utf8').digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* L'empreinte d'un plan et ses grandeurs nommées. Le plafond a priori se
|
||||
* calcule quand l'appelant ne le donne pas.
|
||||
*
|
||||
* @param {import('../src/moteur/types.js').Instance} instance
|
||||
* @param {import('../src/moteur/types.js').Plan} plan
|
||||
* @param {ArrayLike<number>} [aPriori]
|
||||
* @returns {{empreinte: string, grandeurs: Record<string, number|null>}}
|
||||
*/
|
||||
export function empreinteDuPlan(instance, plan, aPriori = plafondsAPriori(instance)) {
|
||||
const tableDe = indexerPlan(instance, plan);
|
||||
const mesures = mesurer(instance, tableDe);
|
||||
const grandeurs = grandeursDe(mesures, plusGrand(ecartsAuPlafondAPriori(mesures, aPriori)));
|
||||
return { empreinte: empreinteDe(tableDe, grandeurs), grandeurs };
|
||||
}
|
||||
|
||||
// La configuration de la démonstration du catalogue de clé cle.
|
||||
function configurationDuCatalogue(cle) {
|
||||
const entree = CATALOGUE.find((candidate) => candidate.cle === cle);
|
||||
if (entree === undefined) throw new RangeError(`configuration inconnue du catalogue : ${cle}`);
|
||||
return entree.construire();
|
||||
}
|
||||
|
||||
// L'empreinte du plan que rechercher rend pour une configuration sous une
|
||||
// graine, une proposition, à arret mouvements, historique par défaut.
|
||||
function empreinteRecherchee(configuration, graine, arret = ARRET) {
|
||||
const instance = normaliser(configuration);
|
||||
const [{ plan }] = rechercher(configuration, { graine, arret, nombre: 1 });
|
||||
return empreinteDuPlan(instance, plan);
|
||||
}
|
||||
|
||||
/**
|
||||
* Les empreintes des plans d'une configuration du catalogue, une par graine
|
||||
* de GRAINES, sous la clé de la graine, à arret mouvements.
|
||||
*
|
||||
* @param {string} cle
|
||||
* @param {number} [arret=ARRET]
|
||||
* @returns {Record<string, {empreinte: string, grandeurs: object}>}
|
||||
*/
|
||||
export function empreintesDesPlans(cle, arret = ARRET) {
|
||||
const configuration = configurationDuCatalogue(cle);
|
||||
return Object.fromEntries(
|
||||
GRAINES.map((graine) => [String(graine), empreinteRecherchee(configuration, graine, arret)]),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Les seize combinaisons des quatre cases, la première toutes cochées, la
|
||||
* dernière aucune, chacune nommée par ses cases cochées jointes par « + »,
|
||||
* ou « aucune ».
|
||||
*
|
||||
* @returns {{cle: string, contraintes: Record<string, boolean>}[]}
|
||||
*/
|
||||
export function combinaisons() {
|
||||
const toutes = [];
|
||||
for (let masque = 2 ** CASES.length - 1; masque >= 0; masque -= 1) {
|
||||
const contraintes = Object.fromEntries(
|
||||
CASES.map((nom, rang) => [nom, ((masque >> (CASES.length - 1 - rang)) & 1) === 1]),
|
||||
);
|
||||
const cochees = CASES.filter((nom) => contraintes[nom]);
|
||||
toutes.push({ cle: cochees.length === 0 ? 'aucune' : cochees.join('+'), contraintes });
|
||||
}
|
||||
return toutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Les empreintes des plans de la petite démonstration sous chacune des seize
|
||||
* combinaisons, graine 1, sous la clé de la combinaison.
|
||||
*
|
||||
* @returns {Record<string, {empreinte: string, grandeurs: object}>}
|
||||
*/
|
||||
export function empreintesDesCombinaisons() {
|
||||
return Object.fromEntries(
|
||||
combinaisons().map(({ cle, contraintes }) => [
|
||||
cle,
|
||||
empreinteRecherchee({ ...configurationDuCatalogue('petite'), contraintes }, GRAINES[0]),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* La charge du classement de référence : la démonstration livrée de clé
|
||||
* cle, et la génération de CLASSEMENT enregistrée par la commande.
|
||||
*
|
||||
* @param {string} cle
|
||||
* @returns {import('../src/stockage/types.js').Charge}
|
||||
*/
|
||||
export function chargeDeClassement(cle) {
|
||||
const charge = analyser(texteLivre(cle)).charge;
|
||||
const configuration = configurationDepuisCharge(charge);
|
||||
const { graine, arret, nombre } = CLASSEMENT;
|
||||
const propositions = rechercher(configuration, { graine, arret, nombre });
|
||||
return appliquerCommande('enregistrerGeneration', charge, {
|
||||
propositions,
|
||||
configuration,
|
||||
produitVersion: VERSION.affichee,
|
||||
}).charge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ce que l'empreinte garde d'un classement : l'ordre, les noms des critères
|
||||
* appliqués dans leur ordre, le critère sauté.
|
||||
*
|
||||
* @param {{ordre: number[], criteresAppliques: string[], critereSaute: string|null}} classement
|
||||
* @returns {{ordre: number[], criteresAppliques: string[], critereSaute: string|null}}
|
||||
*/
|
||||
export function classementLu({ ordre, criteresAppliques, critereSaute }) {
|
||||
return { ordre: [...ordre], criteresAppliques: [...criteresAppliques], critereSaute };
|
||||
}
|
||||
|
||||
/**
|
||||
* Le classement de référence d'une charge de CLASSEMENT.cles : connu et
|
||||
* inconnu, ses classements par evaluer, plafond a priori calculé puis
|
||||
* inconnu ; propositions, les grandeurs nommées de chacune, plafond a priori
|
||||
* calculé, par identifiant croissant.
|
||||
*
|
||||
* @param {string} cle
|
||||
* @returns {{connu: object, inconnu: object, propositions: Record<string, Record<string, number|null>>}}
|
||||
* @throws {Error} une proposition qui ne se compare pas : elle n'a pas de
|
||||
* grandeurs
|
||||
*/
|
||||
export function empreintesDuClassement(cle) {
|
||||
const charge = chargeDeClassement(cle);
|
||||
const connu = evaluer(charge);
|
||||
const inconnu = evaluer(charge, { plafondsAPriori: null });
|
||||
const ligneDe = new Map(connu.propositions.map((ligne) => [ligne.id, ligne]));
|
||||
const propositions = [...charge.propositions]
|
||||
.sort((a, b) => a.id - b.id)
|
||||
.map(({ id }) => {
|
||||
const ligne = ligneDe.get(id);
|
||||
if (ligne.statut !== 'comparable') throw new Error(`${cle}, proposition ${id} : ${ligne.statut}`);
|
||||
return [String(id), grandeursDe(ligne.mesures, ligne.ecartAuPlafondAPrioriMax)];
|
||||
});
|
||||
return {
|
||||
connu: classementLu(connu.classement),
|
||||
inconnu: classementLu(inconnu.classement),
|
||||
propositions: Object.fromEntries(propositions),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Tout le contenu du fichier des empreintes, recalculé (en-tête du module).
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
export function calculerEmpreintes() {
|
||||
return {
|
||||
reglages: {
|
||||
graines: [...GRAINES],
|
||||
arret: ARRET,
|
||||
historique: HISTORIQUE_PAR_DEFAUT,
|
||||
cases: [...CASES],
|
||||
classement: { ...CLASSEMENT, cles: [...CLASSEMENT.cles] },
|
||||
bascule: { ...BASCULE, cles: [...BASCULE.cles] },
|
||||
},
|
||||
plans: Object.fromEntries(CATALOGUE.map(({ cle }) => [cle, empreintesDesPlans(cle)])),
|
||||
combinaisons: empreintesDesCombinaisons(),
|
||||
bascule: Object.fromEntries(BASCULE.cles.map((cle) => [cle, empreintesDesPlans(cle, BASCULE.arret)])),
|
||||
classements: Object.fromEntries(CLASSEMENT.cles.map((cle) => [cle, empreintesDuClassement(cle)])),
|
||||
};
|
||||
}
|
||||
|
||||
/** Le fichier des empreintes, tel qu'il est versionné. */
|
||||
export function lireEmpreintes() {
|
||||
return JSON.parse(readFileSync(CHEMIN_EMPREINTES, 'utf8'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Récrit le fichier des empreintes à partir du moteur courant : un fichier
|
||||
* voisin, puis un renommage, pour qu'aucune lecture ne trouve un fichier à
|
||||
* demi écrit.
|
||||
*/
|
||||
export function ecrireEmpreintes() {
|
||||
const texte = `${JSON.stringify(calculerEmpreintes(), null, 2)}\n`;
|
||||
mkdirSync(dirname(CHEMIN_EMPREINTES), { recursive: true });
|
||||
const provisoire = `${CHEMIN_EMPREINTES}.ecriture`;
|
||||
writeFileSync(provisoire, texte);
|
||||
renameSync(provisoire, CHEMIN_EMPREINTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ce qui sépare des empreintes recalculées de celles de la référence, une
|
||||
* ligne par écart, chacune ouverte par la configuration et la clé : une clé
|
||||
* absente de la référence, une clé de la référence non recalculée, les
|
||||
* grandeurs changées, ou un plan changé sous des grandeurs égales. [] quand
|
||||
* rien ne les sépare.
|
||||
*
|
||||
* @param {string} cle
|
||||
* @param {Record<string, {empreinte: string, grandeurs: object}>} calcule
|
||||
* @param {Record<string, {empreinte: string, grandeurs: object}>} [attendu]
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export function differences(cle, calcule, attendu = {}) {
|
||||
const nomsCalcules = Object.keys(calcule);
|
||||
const nomsAttendus = Object.keys(attendu);
|
||||
const lignes = [
|
||||
...nomsCalcules.filter((nom) => !nomsAttendus.includes(nom)).map((nom) => `${cle}, ${nom} : absente de la référence`),
|
||||
...nomsAttendus.filter((nom) => !nomsCalcules.includes(nom)).map((nom) => `${cle}, ${nom} : référence non recalculée`),
|
||||
];
|
||||
for (const nom of nomsCalcules.filter((n) => nomsAttendus.includes(n))) {
|
||||
const avant = attendu[nom];
|
||||
const apres = calcule[nom];
|
||||
const changees = GRANDEURS.filter((g) => apres.grandeurs[g] !== avant.grandeurs[g]).map(
|
||||
(g) => `${g} ${avant.grandeurs[g]} → ${apres.grandeurs[g]}`,
|
||||
);
|
||||
if (changees.length > 0) lignes.push(`${cle}, ${nom} : ${changees.join(', ')}`);
|
||||
else if (apres.empreinte !== avant.empreinte) lignes.push(`${cle}, ${nom} : plan changé, grandeurs égales`);
|
||||
}
|
||||
return lignes;
|
||||
}
|
||||
721
test/fixtures/moteur/empreintes_defaut.json
vendored
Normal file
721
test/fixtures/moteur/empreintes_defaut.json
vendored
Normal file
|
|
@ -0,0 +1,721 @@
|
|||
{
|
||||
"reglages": {
|
||||
"graines": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"arret": 20000,
|
||||
"historique": 1000,
|
||||
"cases": [
|
||||
"separerAppartenances",
|
||||
"nouveauxVoisins",
|
||||
"nouvelleTable",
|
||||
"varierAppartenances"
|
||||
],
|
||||
"classement": {
|
||||
"cles": [
|
||||
"petite-conflit",
|
||||
"grande"
|
||||
],
|
||||
"graine": 7,
|
||||
"arret": 1000,
|
||||
"nombre": 12
|
||||
},
|
||||
"bascule": {
|
||||
"cles": [
|
||||
"petite",
|
||||
"petite-conflit"
|
||||
],
|
||||
"arret": 50000
|
||||
}
|
||||
},
|
||||
"plans": {
|
||||
"grande": {
|
||||
"1": {
|
||||
"empreinte": "2f637984985154ff3a8785a7d29092a634fdd4b3651c33448b877a1cb0077b16",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 3,
|
||||
"excedentCollisions": 1,
|
||||
"collisionsCumulees": 58,
|
||||
"rencontresRepeteesChoisies": 74,
|
||||
"retoursChoisis": 21,
|
||||
"redondance": 1334
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"empreinte": "111a3077e5f0a381a81df2648c1aac5bb875a4ad84a8d95840210a0dcf905dba",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 3,
|
||||
"excedentCollisions": 1,
|
||||
"collisionsCumulees": 53,
|
||||
"rencontresRepeteesChoisies": 49,
|
||||
"retoursChoisis": 18,
|
||||
"redondance": 1322
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"empreinte": "b6500109a02cc02b3a0ab121b57af6a15fd0a0e73e4460d65820aada73834ccd",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 3,
|
||||
"excedentCollisions": 1,
|
||||
"collisionsCumulees": 45,
|
||||
"rencontresRepeteesChoisies": 30,
|
||||
"retoursChoisis": 10,
|
||||
"redondance": 1311
|
||||
}
|
||||
}
|
||||
},
|
||||
"grande-sans-exception": {
|
||||
"1": {
|
||||
"empreinte": "eb59055b0f65f2f5a4bb7cecb5b1c747d2ba3fc6fe8c77bcb17659b24cbedf8f",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 3,
|
||||
"excedentCollisions": 2,
|
||||
"collisionsCumulees": 54,
|
||||
"rencontresRepeteesChoisies": 49,
|
||||
"retoursChoisis": 19,
|
||||
"redondance": 1314
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"empreinte": "c1908708b1b74643be66202beef64b689d8634aaf3db81ead0c275bc62b2bcd6",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 3,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 43,
|
||||
"rencontresRepeteesChoisies": 74,
|
||||
"retoursChoisis": 27,
|
||||
"redondance": 1246
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"empreinte": "c68933c83e2d0c53eb1798e108dd94f0bfd82e4dbf1a55309b576aad7dc585e0",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 3,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 52,
|
||||
"rencontresRepeteesChoisies": 74,
|
||||
"retoursChoisis": 21,
|
||||
"redondance": 1336
|
||||
}
|
||||
}
|
||||
},
|
||||
"petite": {
|
||||
"1": {
|
||||
"empreinte": "d13db767aaa2fc7ad59ad69973d663320931598b7d02a9212ca0f4e2949f8a21",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 13,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 10,
|
||||
"redondance": 58
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"empreinte": "7de7cee8dcb6561c13b8fdd72ce8730f8022b9d2ff00ee7d11acc8a9a2c21558",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 0,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 14,
|
||||
"rencontresRepeteesChoisies": 0,
|
||||
"retoursChoisis": 17,
|
||||
"redondance": 60
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"empreinte": "b531231af7422c46eaee40ece57222b9d15c57942dd822bda8651010c9fbec86",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 0,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 10,
|
||||
"rencontresRepeteesChoisies": 0,
|
||||
"retoursChoisis": 14,
|
||||
"redondance": 61
|
||||
}
|
||||
}
|
||||
},
|
||||
"petite-conflit": {
|
||||
"1": {
|
||||
"empreinte": "eb8b6cfd59ab3c422c126ede84cf34319674bbf1ff1260963ff87d0dbf1134ad",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 11,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 14,
|
||||
"redondance": 58
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"empreinte": "3d69f6c6701355b4945758c73721d222dadab5dff2af07f2f7b1fa8e3cee64bb",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 0,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 10,
|
||||
"rencontresRepeteesChoisies": 0,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 61
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"empreinte": "d4daa8def32d39046daad3bfa7263ac52bcafc5d0b28eddf6efaf7359ae90f33",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 9,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 59
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"combinaisons": {
|
||||
"separerAppartenances+nouveauxVoisins+nouvelleTable+varierAppartenances": {
|
||||
"empreinte": "d13db767aaa2fc7ad59ad69973d663320931598b7d02a9212ca0f4e2949f8a21",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 13,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 10,
|
||||
"redondance": 58
|
||||
}
|
||||
},
|
||||
"separerAppartenances+nouveauxVoisins+nouvelleTable": {
|
||||
"empreinte": "f76257b5ecf1260a0d7c81b948c5971ffb0c0c18ed6310979cfb7b2be6698dfb",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 11,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 58
|
||||
}
|
||||
},
|
||||
"separerAppartenances+nouveauxVoisins+varierAppartenances": {
|
||||
"empreinte": "9b22b885a65791752a5928b7fe1b285355a4c336dec9cbd9e630aaa4f084ef04",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 7,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 58
|
||||
}
|
||||
},
|
||||
"separerAppartenances+nouveauxVoisins": {
|
||||
"empreinte": "00885d606c84f32f15eef4731a9835a5f97a4754842f00e26f7f6fd73d6d3b4d",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 0,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 11,
|
||||
"rencontresRepeteesChoisies": 0,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 60
|
||||
}
|
||||
},
|
||||
"separerAppartenances+nouvelleTable+varierAppartenances": {
|
||||
"empreinte": "cb80a1470adbb6203469abeafdf4f5fa03797242b0a80ca1665d73d5ab5ccc3b",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 0,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 11,
|
||||
"rencontresRepeteesChoisies": 0,
|
||||
"retoursChoisis": 16,
|
||||
"redondance": 60
|
||||
}
|
||||
},
|
||||
"separerAppartenances+nouvelleTable": {
|
||||
"empreinte": "b97d6d1666c04024d0958b44e04667d0c68b205ef0a8a8473aafa61eaf688cbe",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 8,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 16,
|
||||
"redondance": 58
|
||||
}
|
||||
},
|
||||
"separerAppartenances+varierAppartenances": {
|
||||
"empreinte": "1765e4f6977feac2298a40c8558b76cec322b2ddd3783db78b1fbe923aa1e88e",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 0,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 12,
|
||||
"rencontresRepeteesChoisies": 0,
|
||||
"retoursChoisis": 16,
|
||||
"redondance": 60
|
||||
}
|
||||
},
|
||||
"separerAppartenances": {
|
||||
"empreinte": "caceccc8edeed9a9dcbd999308cb3a8b1bbca95475d9eddbbeb3563e4749ed40",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 10,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 58
|
||||
}
|
||||
},
|
||||
"nouveauxVoisins+nouvelleTable+varierAppartenances": {
|
||||
"empreinte": "9e498d46ae56717fe5fee4cd33f06bc0c159116cfce761bac73969e85d668983",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 10,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 13,
|
||||
"redondance": 58
|
||||
}
|
||||
},
|
||||
"nouveauxVoisins+nouvelleTable": {
|
||||
"empreinte": "ac14e9bd382bc0ac5878df97b67a3fec74b3891750b2aa3a69147882020a03cb",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 13,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 11,
|
||||
"redondance": 58
|
||||
}
|
||||
},
|
||||
"nouveauxVoisins+varierAppartenances": {
|
||||
"empreinte": "2104c85460bc6751ec96249c8a65b5a5b09cb3028f6455d57145596b2ed893ef",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 13,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 58
|
||||
}
|
||||
},
|
||||
"nouveauxVoisins": {
|
||||
"empreinte": "e19ecf733df91fdc2f6d1a25484e1b3625eed22e306b100b2f174b7147f5cc55",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 12,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 17,
|
||||
"redondance": 58
|
||||
}
|
||||
},
|
||||
"nouvelleTable+varierAppartenances": {
|
||||
"empreinte": "f680569b067ea17677aae9659dea9977e528171dd28c788a6b9536da28316732",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 12,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 16,
|
||||
"redondance": 59
|
||||
}
|
||||
},
|
||||
"nouvelleTable": {
|
||||
"empreinte": "9477b22afcc42ecf0017bc0dd5711916f6e546cd2a04d6598ac406c07ef57b27",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 13,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 14,
|
||||
"redondance": 58
|
||||
}
|
||||
},
|
||||
"varierAppartenances": {
|
||||
"empreinte": "c3df5155baa7cbbb7d0b980c9fbd071247e2934ce19732137291b6bad66fcdcc",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 0,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 10,
|
||||
"rencontresRepeteesChoisies": 0,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 60
|
||||
}
|
||||
},
|
||||
"aucune": {
|
||||
"empreinte": "9fd1147b184369d7ba5ffe04c81f3f721d6967fcecd7fc64a623006e76fbf0f9",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 13,
|
||||
"rencontresRepeteesChoisies": 1,
|
||||
"retoursChoisis": 17,
|
||||
"redondance": 58
|
||||
}
|
||||
}
|
||||
},
|
||||
"bascule": {
|
||||
"petite": {
|
||||
"1": {
|
||||
"empreinte": "a028aa693dd46b17070762d3cac2a2713eb0ee9dfc7bf021b8fc234a27bcf671",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 0,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 12,
|
||||
"rencontresRepeteesChoisies": 0,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 60
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"empreinte": "e72ba70986cbf2a15231d9defe4bcdc1490db6633552e97d4799d4221232da78",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 0,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 0,
|
||||
"rencontresRepeteesChoisies": 0,
|
||||
"retoursChoisis": 11,
|
||||
"redondance": 72
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"empreinte": "b531231af7422c46eaee40ece57222b9d15c57942dd822bda8651010c9fbec86",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 0,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 10,
|
||||
"rencontresRepeteesChoisies": 0,
|
||||
"retoursChoisis": 14,
|
||||
"redondance": 61
|
||||
}
|
||||
}
|
||||
},
|
||||
"petite-conflit": {
|
||||
"1": {
|
||||
"empreinte": "991a8d86d7ae00d3be99bb19990642efc2025eac51867b7def97102d998fc6b2",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 0,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 12,
|
||||
"rencontresRepeteesChoisies": 0,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 60
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"empreinte": "3d69f6c6701355b4945758c73721d222dadab5dff2af07f2f7b1fa8e3cee64bb",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 0,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 10,
|
||||
"rencontresRepeteesChoisies": 0,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 61
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"empreinte": "48aafbee06bc5fd1567b44b2d3734df55f91cd1e0d0a915f333f53be8bf92f90",
|
||||
"grandeurs": {
|
||||
"ecartAuPlafondAPrioriMax": 0,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 14,
|
||||
"rencontresRepeteesChoisies": 0,
|
||||
"retoursChoisis": 14,
|
||||
"redondance": 61
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"classements": {
|
||||
"petite-conflit": {
|
||||
"connu": {
|
||||
"ordre": [
|
||||
7,
|
||||
10,
|
||||
2,
|
||||
1,
|
||||
12,
|
||||
8,
|
||||
4,
|
||||
9,
|
||||
5,
|
||||
11,
|
||||
6,
|
||||
3
|
||||
],
|
||||
"criteresAppliques": [
|
||||
"ecartAuPlafondAPrioriMax",
|
||||
"excedentCollisions",
|
||||
"collisionsCumulees",
|
||||
"rencontresRepetees",
|
||||
"redondance"
|
||||
],
|
||||
"critereSaute": null
|
||||
},
|
||||
"inconnu": {
|
||||
"ordre": [
|
||||
1,
|
||||
12,
|
||||
7,
|
||||
8,
|
||||
10,
|
||||
4,
|
||||
2,
|
||||
9,
|
||||
5,
|
||||
11,
|
||||
6,
|
||||
3
|
||||
],
|
||||
"criteresAppliques": [
|
||||
"excedentCollisions",
|
||||
"collisionsCumulees",
|
||||
"rencontresRepetees",
|
||||
"redondance"
|
||||
],
|
||||
"critereSaute": "ecartAuPlafondAPrioriMax"
|
||||
},
|
||||
"propositions": {
|
||||
"1": {
|
||||
"ecartAuPlafondAPrioriMax": 2,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 11,
|
||||
"rencontresRepeteesChoisies": 4,
|
||||
"retoursChoisis": 16,
|
||||
"redondance": 53
|
||||
},
|
||||
"2": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 1,
|
||||
"collisionsCumulees": 13,
|
||||
"rencontresRepeteesChoisies": 5,
|
||||
"retoursChoisis": 12,
|
||||
"redondance": 53
|
||||
},
|
||||
"3": {
|
||||
"ecartAuPlafondAPrioriMax": 2,
|
||||
"excedentCollisions": 2,
|
||||
"collisionsCumulees": 16,
|
||||
"rencontresRepeteesChoisies": 6,
|
||||
"retoursChoisis": 14,
|
||||
"redondance": 49
|
||||
},
|
||||
"4": {
|
||||
"ecartAuPlafondAPrioriMax": 2,
|
||||
"excedentCollisions": 1,
|
||||
"collisionsCumulees": 10,
|
||||
"rencontresRepeteesChoisies": 3,
|
||||
"retoursChoisis": 16,
|
||||
"redondance": 54
|
||||
},
|
||||
"5": {
|
||||
"ecartAuPlafondAPrioriMax": 2,
|
||||
"excedentCollisions": 2,
|
||||
"collisionsCumulees": 15,
|
||||
"rencontresRepeteesChoisies": 4,
|
||||
"retoursChoisis": 17,
|
||||
"redondance": 52
|
||||
},
|
||||
"6": {
|
||||
"ecartAuPlafondAPrioriMax": 2,
|
||||
"excedentCollisions": 2,
|
||||
"collisionsCumulees": 15,
|
||||
"rencontresRepeteesChoisies": 5,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 52
|
||||
},
|
||||
"7": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 12,
|
||||
"rencontresRepeteesChoisies": 3,
|
||||
"retoursChoisis": 17,
|
||||
"redondance": 55
|
||||
},
|
||||
"8": {
|
||||
"ecartAuPlafondAPrioriMax": 2,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 13,
|
||||
"rencontresRepeteesChoisies": 5,
|
||||
"retoursChoisis": 16,
|
||||
"redondance": 50
|
||||
},
|
||||
"9": {
|
||||
"ecartAuPlafondAPrioriMax": 2,
|
||||
"excedentCollisions": 2,
|
||||
"collisionsCumulees": 14,
|
||||
"rencontresRepeteesChoisies": 6,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 49
|
||||
},
|
||||
"10": {
|
||||
"ecartAuPlafondAPrioriMax": 1,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 15,
|
||||
"rencontresRepeteesChoisies": 3,
|
||||
"retoursChoisis": 18,
|
||||
"redondance": 54
|
||||
},
|
||||
"11": {
|
||||
"ecartAuPlafondAPrioriMax": 2,
|
||||
"excedentCollisions": 2,
|
||||
"collisionsCumulees": 15,
|
||||
"rencontresRepeteesChoisies": 5,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 51
|
||||
},
|
||||
"12": {
|
||||
"ecartAuPlafondAPrioriMax": 2,
|
||||
"excedentCollisions": 0,
|
||||
"collisionsCumulees": 11,
|
||||
"rencontresRepeteesChoisies": 5,
|
||||
"retoursChoisis": 15,
|
||||
"redondance": 50
|
||||
}
|
||||
}
|
||||
},
|
||||
"grande": {
|
||||
"connu": {
|
||||
"ordre": [
|
||||
10,
|
||||
9,
|
||||
8,
|
||||
1,
|
||||
5,
|
||||
12,
|
||||
3,
|
||||
2,
|
||||
7,
|
||||
6,
|
||||
4,
|
||||
11
|
||||
],
|
||||
"criteresAppliques": [
|
||||
"ecartAuPlafondAPrioriMax",
|
||||
"excedentCollisions",
|
||||
"collisionsCumulees",
|
||||
"rencontresRepetees",
|
||||
"redondance"
|
||||
],
|
||||
"critereSaute": null
|
||||
},
|
||||
"inconnu": {
|
||||
"ordre": [
|
||||
10,
|
||||
9,
|
||||
11,
|
||||
8,
|
||||
1,
|
||||
5,
|
||||
12,
|
||||
3,
|
||||
2,
|
||||
7,
|
||||
6,
|
||||
4
|
||||
],
|
||||
"criteresAppliques": [
|
||||
"excedentCollisions",
|
||||
"collisionsCumulees",
|
||||
"rencontresRepetees",
|
||||
"redondance"
|
||||
],
|
||||
"critereSaute": "ecartAuPlafondAPrioriMax"
|
||||
},
|
||||
"propositions": {
|
||||
"1": {
|
||||
"ecartAuPlafondAPrioriMax": 4,
|
||||
"excedentCollisions": 2,
|
||||
"collisionsCumulees": 58,
|
||||
"rencontresRepeteesChoisies": 128,
|
||||
"retoursChoisis": 42,
|
||||
"redondance": 1312
|
||||
},
|
||||
"2": {
|
||||
"ecartAuPlafondAPrioriMax": 4,
|
||||
"excedentCollisions": 3,
|
||||
"collisionsCumulees": 70,
|
||||
"rencontresRepeteesChoisies": 142,
|
||||
"retoursChoisis": 32,
|
||||
"redondance": 1326
|
||||
},
|
||||
"3": {
|
||||
"ecartAuPlafondAPrioriMax": 4,
|
||||
"excedentCollisions": 2,
|
||||
"collisionsCumulees": 68,
|
||||
"rencontresRepeteesChoisies": 112,
|
||||
"retoursChoisis": 36,
|
||||
"redondance": 1308
|
||||
},
|
||||
"4": {
|
||||
"ecartAuPlafondAPrioriMax": 4,
|
||||
"excedentCollisions": 4,
|
||||
"collisionsCumulees": 75,
|
||||
"rencontresRepeteesChoisies": 129,
|
||||
"retoursChoisis": 32,
|
||||
"redondance": 1314
|
||||
},
|
||||
"5": {
|
||||
"ecartAuPlafondAPrioriMax": 4,
|
||||
"excedentCollisions": 2,
|
||||
"collisionsCumulees": 64,
|
||||
"rencontresRepeteesChoisies": 124,
|
||||
"retoursChoisis": 34,
|
||||
"redondance": 1298
|
||||
},
|
||||
"6": {
|
||||
"ecartAuPlafondAPrioriMax": 4,
|
||||
"excedentCollisions": 4,
|
||||
"collisionsCumulees": 69,
|
||||
"rencontresRepeteesChoisies": 130,
|
||||
"retoursChoisis": 38,
|
||||
"redondance": 1282
|
||||
},
|
||||
"7": {
|
||||
"ecartAuPlafondAPrioriMax": 4,
|
||||
"excedentCollisions": 3,
|
||||
"collisionsCumulees": 72,
|
||||
"rencontresRepeteesChoisies": 116,
|
||||
"retoursChoisis": 31,
|
||||
"redondance": 1347
|
||||
},
|
||||
"8": {
|
||||
"ecartAuPlafondAPrioriMax": 4,
|
||||
"excedentCollisions": 2,
|
||||
"collisionsCumulees": 57,
|
||||
"rencontresRepeteesChoisies": 120,
|
||||
"retoursChoisis": 39,
|
||||
"redondance": 1305
|
||||
},
|
||||
"9": {
|
||||
"ecartAuPlafondAPrioriMax": 4,
|
||||
"excedentCollisions": 1,
|
||||
"collisionsCumulees": 62,
|
||||
"rencontresRepeteesChoisies": 120,
|
||||
"retoursChoisis": 34,
|
||||
"redondance": 1291
|
||||
},
|
||||
"10": {
|
||||
"ecartAuPlafondAPrioriMax": 4,
|
||||
"excedentCollisions": 1,
|
||||
"collisionsCumulees": 57,
|
||||
"rencontresRepeteesChoisies": 123,
|
||||
"retoursChoisis": 38,
|
||||
"redondance": 1287
|
||||
},
|
||||
"11": {
|
||||
"ecartAuPlafondAPrioriMax": 5,
|
||||
"excedentCollisions": 1,
|
||||
"collisionsCumulees": 71,
|
||||
"rencontresRepeteesChoisies": 119,
|
||||
"retoursChoisis": 30,
|
||||
"redondance": 1339
|
||||
},
|
||||
"12": {
|
||||
"ecartAuPlafondAPrioriMax": 4,
|
||||
"excedentCollisions": 2,
|
||||
"collisionsCumulees": 67,
|
||||
"rencontresRepeteesChoisies": 117,
|
||||
"retoursChoisis": 44,
|
||||
"redondance": 1301
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue