Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/modular_decomposition/src/dm.c
4056 views
1
/******************************************************
2
3
Copyright 2004, 2010 Fabien de Montgolfier
4
[email protected]
5
6
This program is free software; you can redistribute it and/or
7
modify it under the terms of the GNU General Public License
8
as published by the Free Software Foundation; either version 2
9
of the License, or (at your option) any later version.
10
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
15
16
You should have received a copy of the GNU General Public License
17
along with this program; if not, write to the Free Software
18
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
**********************************************************/
20
21
/********************************************************
22
23
DECOMPOSITION MODULAIRE DE GRAPHES NON-ORIENTES
24
25
Cet algorithme construit l'arbre de decomposition modulaire
26
d'un graphe donne sous forme d'une matrice d'adjacence.
27
Il s'effectue en temps O(m log n) temps et $O(m)$ espace.
28
Il est la concatenation de deux algorithmes distincts.
29
Le premier realise une permutation factorisante des sommets du graphe
30
(pour cette notion, cf these de Christian Capelle)
31
grace a une technique d'affinage de partitions (cf Habib, Paul & Viennot)
32
Le second construit l'arbre de decomposition modulaire a partir de cette
33
permutation, cf mon memoire de DEA
34
Montpellier, decembre 2000
35
********************************************************/
36
37
//#include "dm_english.h"
38
#include <stdio.h>
39
#include <stdlib.h>
40
41
#define DEBUG 0 /* si 0 aucune sortie graphique!! */
42
43
/* dm.h definit les constantes FEUILLE, MODULE, etc...
44
ainsi que les structures noeud et fils. Les autres
45
structures n'ont pas a etre connues par les programmes
46
exterieurs et sont donc definies ici. */
47
48
49
/* un sommet du graphe
50
(utilise dans la premiere partie seulement, ainsi que ce qui suit)*/
51
typedef struct Sommet {
52
int place;/* numero du sommet dans la NOUVELLE numerotation */
53
int nom; /* numero du sommet dans l'ANCIENNE numerotation */
54
/* On a donc sigma(nom)=place */
55
struct Sadj *adj;
56
struct SClasse *classe;
57
} sommet;
58
59
/* liste d'adjacence d'un sommet, DOUBLEMENT chainee*/
60
typedef struct Sadj {
61
struct Sommet *pointe;
62
struct Sadj *suiv;
63
struct Sadj *prec;
64
} sadj;
65
66
/* classe de la partition courante,
67
organisees en liste chainnee selon l'ordre de la partition */
68
typedef struct SClasse {
69
int debut;
70
int fin;
71
struct Sommet *firstpivot;
72
int inpivot; /*indice de la classe dans le tableau pivot */
73
int inmodule; /* (resp module); -1 si non present */
74
int whereXa; /* lie le couple X/Xa: vaut
75
0 si X n'est actuellement lie a aucun Xa
76
-1 si Xa est a gauche
77
+1 si Xa est a droite */
78
struct SClasse *suiv; /* forment une liste chainee */
79
struct SClasse *prec; /*...doublement */
80
} sclasse;
81
82
/* plein de parametres statiques que algo1() donne a Raffine() */
83
typedef struct Info {
84
sclasse **pivot;
85
int *ipivot;
86
sclasse **module;
87
int *imodule;
88
int *numclasse;
89
int *n;
90
} info;
91
92
/* clef a deux entrees utilisee pour le tri lineaire
93
represente l'arrete ij */
94
typedef struct Clef2{
95
int i; //sommet pointeur
96
int nom; // nom du sommet pointe
97
int place; //place du sommet pointe
98
} clef2;
99
100
/*************************************************************
101
utilitaires
102
*************************************************************/
103
void *fabmalloc(size_t s)
104
/* malloc sans erreur */
105
{
106
void *p;
107
p=malloc(s);
108
if(p==NULL)
109
{
110
perror("Erreur de malloc!\n");
111
exit(1);
112
}
113
return p;
114
}
115
116
int min(int a, int b)
117
{
118
return (a<b) ? a : b;
119
}
120
121
int max(int a, int b)
122
{
123
return (a > b) ? a : b;
124
}
125
/**************************************************************
126
Premiere passe de l'algorithme: il s'agit de trouver une
127
permutation factorisante du graphe. Nous utilisons les
128
techniques de raffinement de partition. Tout cela est
129
explique dans l'article de Habib, Viennot & Paul, dont je ne
130
fais ici que transcrire le travail.
131
****************************************************************/
132
133
void printS(sommet ** S, int n)
134
{
135
/* imprimme S selon S et selon les classes */
136
int i;
137
sclasse *s;
138
139
for (s = S[0]->classe; s != NULL; s = s->suiv) {
140
printf("[ ");
141
for (i = s->debut; i <= s->fin; i++)
142
printf("%i ", 1 + S[i]->nom);
143
printf("] ");
144
}
145
printf("\n");
146
}
147
148
sclasse *nouvclasse(sclasse * un, sclasse * deux)
149
{
150
/* cree une nouvelle classe et l'insere entre un et deux;
151
on suppose que si un et deux sont pas NULL alors
152
FORCEMENT un=deux->suiv */
153
154
sclasse *nouv;
155
nouv = (sclasse *) fabmalloc(sizeof(sclasse));
156
nouv->whereXa = 0;
157
nouv->inpivot = -1;
158
nouv->inmodule = -1;
159
nouv->firstpivot = NULL;
160
nouv->prec = un;
161
if (un != NULL) /* accroche pas en tete de chaine */
162
un->suiv = nouv;
163
nouv->suiv = deux;
164
if (deux != NULL) /* pas en queue de chaine */
165
deux->prec = nouv;
166
167
/* debut et fin ne sont PAS initialises! */
168
return nouv;
169
}
170
171
void permute(sommet ** S, int a, int b)
172
{
173
/* transpose les sommets a et b dans S */
174
/* ne touche pas aux classes! */
175
sommet *tmp;
176
S[a]->place = b;
177
S[b]->place = a;
178
tmp = S[a];
179
S[a] = S[b];
180
S[b] = tmp;
181
}
182
183
void Raffiner(sommet ** S, sommet * p, sommet * centre, info * I)
184
{
185
/* melange raffiner, pivotset, insertright et addpivot */
186
sadj *a; /* parcours l'adjacence du pivot */
187
sommet *x; /* sommet quiva changer de classe */
188
sclasse *X, *Xa; /* x in X; Xa nouv classe de x */
189
sclasse *Z;
190
sclasse **pivot;
191
sclasse **module;
192
int *ipivot, *imodule, *numclasse, n;
193
194
if (DEBUG)
195
printf("Raffinage avec le pivot %i\n", 1 + p->nom);
196
pivot = I->pivot;
197
module = I->module;
198
ipivot = I->ipivot;
199
imodule = I->imodule;
200
numclasse = I->numclasse;
201
n = *(I->n);
202
203
for (a = p->adj; a != NULL; a = a->suiv) {
204
x = a->pointe;
205
X = x->classe;
206
if (X == p->classe)
207
continue; /* on raffine pas la classe du pivot! */
208
209
if (X->whereXa == 0) {
210
/* c'est la premiere fois qu'on trouve un x
211
appartenant a X lors de cet appel a raffiner */
212
213
if ((centre->place < x->place && x->place < p->place)
214
|| (p->place < x->place && x->place < centre->place)) {
215
/* insere a gauche */
216
Xa = nouvclasse(X->prec, X);
217
(*numclasse)++;
218
permute(S, x->place, X->debut);
219
X->debut++;
220
X->whereXa = -1;
221
Xa->whereXa = 1; /* besoin dans le second tour */
222
}
223
else { /* insere a droite */
224
225
Xa = nouvclasse(X, X->suiv);
226
(*numclasse)++;
227
permute(S, x->place, X->fin);
228
X->fin--;
229
X->whereXa = 1;
230
Xa->whereXa = -1;
231
}
232
x->classe = Xa;
233
Xa->debut = x->place;
234
Xa->fin = x->place;
235
}
236
else {
237
if (X->whereXa == -1) {
238
Xa = X->prec;
239
permute(S, x->place, X->debut);
240
X->debut++;
241
Xa->fin++;
242
}
243
else {
244
Xa = X->suiv;
245
permute(S, x->place, X->fin);
246
X->fin--;
247
Xa->debut--;
248
}
249
x->classe = Xa;
250
}
251
}
252
253
for (a = p->adj; a != NULL; a = a->suiv)
254
/* deuxieme couche! Maintenant on va faire les addpivot,
255
et remettre les whereXa a 0
256
Noter qu'on lit les Xa et plus les X */
257
{
258
x = a->pointe;
259
Xa = x->classe;
260
if (Xa->whereXa == 0)
261
continue; /* deja remis a zero! */
262
if (Xa->whereXa == -1)
263
X = Xa->prec;
264
else
265
X = Xa->suiv;
266
267
if (X->debut > X->fin) {
268
/*on a trop enleve! X est vide
269
-> on va le supprimer mechamment */
270
271
(*numclasse)--;
272
if (Xa->whereXa == 1) { /*deconnecte */
273
Xa->suiv = X->suiv;
274
if (Xa->suiv != NULL)
275
Xa->suiv->prec = Xa;
276
}
277
else {
278
Xa->prec = X->prec;
279
if (Xa->prec != NULL)
280
Xa->prec->suiv = Xa;
281
}
282
Xa->inpivot = X->inpivot;
283
if (X->inpivot != -1) /* ecrase X dans pivot */
284
pivot[X->inpivot] = Xa;
285
Xa->inmodule = X->inmodule;
286
if (X->inmodule != -1) /* ecrase X dans pivot */
287
module[X->inmodule] = Xa;
288
289
Xa->whereXa = 0;
290
continue;
291
}
292
293
/* Maintenant on fait addpivot(X,Xa)
294
noter que X et Xa sont non vides */
295
296
if (X->inpivot == -1) {
297
if ((X->inmodule != -1)
298
&& (X->fin - X->debut < Xa->fin - Xa->debut)) {
299
/* remplace X par Xa dans module */
300
module[X->inmodule] = Xa;
301
Xa->inmodule = X->inmodule;
302
X->inmodule = -1;
303
if (DEBUG)
304
printf("Dans module %i-%i ecrase %i-%i\n",
305
1 + S[Xa->debut]->nom, 1 + S[Xa->fin]->nom,
306
1 + S[X->debut]->nom, 1 + S[X->fin]->nom);
307
}
308
else {
309
if (X->inmodule == -1) {
310
if (X->fin - X->debut < Xa->fin - Xa->debut)
311
Z = Xa;
312
else
313
Z = X;
314
/* ajoute Z (=max(X,Xa)) a module */
315
module[(*imodule)] = Z;
316
Z->inmodule = (*imodule);
317
(*imodule)++;
318
if (DEBUG)
319
printf("module empile:%i-%i\n",
320
1 + S[Z->debut]->nom, 1 + S[Z->fin]->nom);
321
}
322
}
323
}
324
325
if (X->inpivot != -1)
326
Z = Xa;
327
else if (X->fin - X->debut < Xa->fin - Xa->debut)
328
Z = X;
329
else
330
Z = Xa;
331
/* on empile Z dans pivot */
332
pivot[(*ipivot)] = Z;
333
Z->inpivot = (*ipivot);
334
(*ipivot)++;
335
if (DEBUG)
336
printf("pivot empile: %i-%i\n", 1 + S[Z->debut]->nom,
337
1 + S[Z->fin]->nom);
338
X->whereXa = 0;
339
Xa->whereXa = 0;
340
}
341
if (DEBUG) {
342
printS(S, n);
343
printf("\n");
344
}
345
}
346
347
sommet **algo1(graphe G)
348
/* Entree: un graphe G
349
Sortie: une permutation factorisante de G,
350
donnee sous la forme d'un tableau de structures Sommet ordonnees selon sigma.
351
d'apres le travail de Habib/Paul/Viennot */
352
{
353
int n; // nombre de sommets de G
354
355
sclasse **pivot; /*pile des pivots */
356
int ipivot = 0; /*indice sur la precedante */
357
358
sclasse **module; /*idem, modules */
359
int imodule = 0;
360
361
sclasse *singclasse;
362
/*invariant: toute classe avant singclasse dans la chaine */
363
/*a un seul element */
364
int numclasse; /* quand vaut n, on a fini!! */
365
366
sclasse *C1; /*premiere classe, tete de la chaine */
367
sclasse *Y; /*classe qui raffine */
368
sclasse *X; /*classe raffinee */
369
sclasse *Xa, *Xc; /* morceaux de X */
370
sommet *x; /* x in X */
371
sommet *y; /* y in Y */
372
sommet *centre; /* le centre du raffinage actuel */
373
374
sommet **S; /*la permutation factorisante ! */
375
376
int i, j; /*divers indices */
377
sommet *scourant; /* pour l'init */
378
sadj *nextadj; /*sommet adjacent suivant */
379
adj *nextadj2; /* idem mais de type adj */
380
info Inf; /* diverses info a passer a raffiner */
381
382
/* debut des initialisations */
383
n=G.n;
384
/*initialisation des tableaux */
385
module = (sclasse **) fabmalloc(n * sizeof(sclasse *));
386
pivot = (sclasse **) fabmalloc(n * sizeof(sclasse *));
387
S = (sommet **) fabmalloc(n * sizeof(sommet *));
388
/* on va initialiser la permutation factorisante,
389
ainsi que chaque structure sommet */
390
C1 = nouvclasse(NULL, NULL);
391
numclasse = 1;
392
singclasse = C1;
393
C1->debut = 0;
394
C1->fin = n - 1;
395
for (i = 0; i < n; i++) {
396
/* initialisation des sommets */
397
/* notre bebe est le sommet i dans M */
398
scourant = (sommet *) fabmalloc(sizeof(struct Sommet));
399
scourant->nom = i;
400
scourant->place = i; /* a ce point S=identite */
401
scourant->adj = NULL; /* pas encore d'adjacence */
402
scourant->classe = C1;
403
S[i] = scourant;
404
}
405
for (i = 0; i < n; i++)
406
{
407
nextadj2 = G.G[i];
408
while(nextadj2 != NULL)
409
{
410
j=nextadj2->s; //numero du sommet pointe
411
if((j<0)||(j>=n))
412
{
413
perror("Graphe invalide (numero de sommet erronne)!\n");
414
exit(1);
415
}
416
nextadj = (sadj *) fabmalloc(sizeof(struct Sadj));
417
//un nouveau sadj
418
nextadj->pointe = S[j];
419
nextadj->suiv = S[i]->adj; //tete de liste
420
if(nextadj->suiv!=NULL)
421
nextadj->suiv->prec=nextadj;
422
nextadj->prec=NULL;
423
S[i]->adj = nextadj; /*et le tour est joue */
424
nextadj2=nextadj2->suiv;
425
}
426
}
427
/* NB: module et pivot sont vides */
428
Inf.pivot = pivot;
429
Inf.ipivot = &ipivot;
430
Inf.module = module;
431
Inf.imodule = &imodule;
432
Inf.numclasse = &numclasse;
433
Inf.n = &n;
434
/* init terminnee */
435
436
while (1) {
437
while (ipivot > 0 || imodule > 0) {
438
while (ipivot > 0) {
439
/*cette boucle raffine selon tous les sommets
440
de la premiere classe dans pivot */
441
442
Y = pivot[ipivot - 1];
443
ipivot--;
444
Y->inpivot = -1;
445
446
for (i = Y->debut; i <= Y->fin; i++)
447
Raffiner(S, S[i], centre, &Inf);
448
449
/* une optimisation de la fin de l'algo */
450
if (numclasse == n)
451
return (S);
452
}
453
/*maintenant pivot est vide, mais peut-etre pas module */
454
if (imodule > 0) {
455
/* relance par un sommet (pas au pif...) */
456
/* de chaque module qui le represente */
457
Y = module[imodule - 1];
458
imodule--;
459
Y->inmodule = -1;
460
y = S[Y->debut]; /* le firstpivot sera toujours... */
461
Y->firstpivot = y; /* le premier!! */
462
if (DEBUG)
463
printf("module-pivot %i-%i: sommet %i\n",
464
1 + S[Y->debut]->nom, 1 + S[Y->fin]->nom,
465
1 + y->nom);
466
Raffiner(S, y, centre, &Inf);
467
}
468
}
469
/* a ce point, pivot et module sont vides...
470
pas de pb! On va faire initpartition HERE */
471
if (DEBUG)
472
printf("\nInit Partition\n");
473
/**** ajoute ici pour debbugger, mais moche!! */
474
singclasse = S[0]->classe;
475
while ((singclasse != NULL) &&
476
(singclasse->debut == singclasse->fin))
477
{
478
singclasse = singclasse->suiv;
479
}
480
/* singclasse est la premiere classe
481
non singlette, sauf si: */
482
if (singclasse == NULL)
483
/* on a n classes singlettes? ben c'est gagne! */
484
{
485
return (S);
486
}
487
if (singclasse == NULL && numclasse < n) {
488
perror("c'est pas normal! Ca termine trop vite!\n");
489
exit(1);
490
}
491
492
X = singclasse;
493
x = X->firstpivot;
494
if (x == NULL)
495
x = S[X->debut];
496
else /* remet firstpivot a NULL!! */
497
X->firstpivot = NULL;
498
499
if (DEBUG)
500
printf("Relance dans le module %i-%i avec le sommet %i\n",
501
1 + S[X->debut]->nom, 1 + S[X->fin]->nom, 1 + x->nom);
502
503
centre = x; /*important! */
504
/* astuce: on place {x} en tete de X
505
ensuite, on raffine S selon x -> seule X est coupee
506
il y a alors {x} X Xa
507
-> on met {x} en queue de X et c'est bon!
508
ainsi on a bien nonvoisins-x-voisons */
509
Xc = nouvclasse(X->prec, X);
510
numclasse++;
511
x->classe = Xc;
512
permute(S, x->place, X->debut);
513
X->debut++;
514
Xc->debut = x->place;
515
Xc->fin = x->place;
516
Raffiner(S, x, x, &Inf);
517
/* X existe-il encore? */
518
if (X->debut > X->fin)
519
continue;
520
/* echange de x et {x}. Init: -{x}-X- */
521
Xc->suiv = X->suiv;
522
if (X->suiv != NULL)
523
X->suiv->prec = Xc;
524
X->prec = Xc->prec;
525
if (Xc->prec != NULL)
526
Xc->prec->suiv = X;
527
X->suiv = Xc;
528
Xc->prec = X;
529
permute(S, x->place, X->fin);
530
Xc->debut = x->place;
531
Xc->fin = x->place;
532
X->debut--;
533
X->fin--;
534
//antibug?
535
singclasse=X;
536
/* now -X-{x}- */
537
if (DEBUG)
538
printS(S, n);
539
}
540
}
541
542
/***************************************************************
543
Etape intermediaire: trier toutes les listes d'adjacence
544
selon S. ce sont les listes de type sadj qui sont concernees
545
***************************************************************/
546
int Calculm(graphe G)
547
/* compte le nombre d'arretes du graphe */
548
{
549
int i,r; adj *a;
550
r=0;
551
for(i=0;i<G.n;i++)
552
{
553
a=G.G[i];
554
while(a!=NULL)
555
{
556
a=a->suiv;
557
r++;
558
}
559
}
560
if(r%2!=0)
561
{
562
perror("Erreur: nombre impaire d'arrete, graphe non-oriente??\n");
563
exit(1);
564
}
565
return r/2; // G symetrique!
566
}
567
568
void TrierTous(sommet **S, int n, int m)
569
/* trie chaque liste d'adjacence de S*/
570
{
571
//n sommets, m arretes
572
int i; // numero du sommet courant
573
sadj *a,*atmp;// parcours sa liste d'adjacence
574
clef2 *c; // enregistrement a trier
575
int *tab1; clef2 **tab2; //tableaux du tri par seaux
576
tab1=(int *)fabmalloc(n*sizeof(int));
577
tab2=(clef2 **)fabmalloc(m * 2 * sizeof(clef2 *));
578
for(i=0; i<n; i++)
579
tab1[i]=0;
580
581
// premiere passe: construit tab1:
582
// tab[i] est la frequence du ieme (selon S) sommet
583
for(i=0; i<n; i++)
584
{
585
a=S[i]->adj;
586
while(a!=NULL)
587
{
588
tab1[i]++;
589
a=a->suiv;
590
}
591
}
592
//deuxieme passe: frequences cumulees a rebours
593
// (car les listes d'adjacences se construisent a l'envers
594
//tab1[n-1]--; // a cause des indices de tableau qui commence a zero
595
//for(i=n-1;i>0;i--)
596
// tab1[i-1]+=tab1[i];
597
598
//deuxieme passe: frequences cumulees
599
for(i=1;i<n;i++)
600
tab1[i]+=tab1[i-1];
601
602
//troisieme passe: liste double
603
for(i=0; i<n; i++)
604
{
605
a=S[i]->adj;
606
while(a!=NULL)
607
{
608
/* cree un nouveau record */
609
c=(clef2 *)fabmalloc(sizeof(struct Clef2));
610
c->i=i;
611
c->nom=a->pointe->nom;
612
c->place=a->pointe->place;
613
/* le place bien dans tab2 */
614
tab1[c->place]--;
615
tab2[tab1[c->place]]=c;
616
/*et on continue */
617
a=a->suiv;
618
}
619
}
620
621
//quatrieme passe: detruit les vielles listes d'adjacence
622
for(i=0; i<n; i++)
623
{
624
a=S[i]->adj;
625
while(a!=NULL)
626
{
627
atmp=a->suiv;
628
free(a);
629
a=atmp;
630
}
631
S[i]->adj=NULL;
632
}
633
634
//derniere passe: reconstruit les listes d'adjacence
635
for(i=0;i<2*m;i++)
636
{
637
c=tab2[i];
638
a=(sadj *)fabmalloc(sizeof(struct Sadj));
639
a->pointe=S[c->i];
640
a->suiv=S[c->place]->adj; //insere en tete
641
if(a->suiv!=NULL)
642
a->suiv->prec=a;
643
a->prec=NULL;
644
S[c->place]->adj=a;
645
//nettoie
646
free(c);
647
}
648
free(tab1);
649
free(tab2);
650
}
651
652
653
/***************************************************************
654
Maintenant, la deuxieme partie de l'aglorithme
655
On va, etant donne la matrice M construite a l'etape precedante,
656
etablir l'arbre de decomposition modulaire.
657
Tous les details sont dans mon memoire de DEA
658
****************************************************************/
659
noeud *nouvnoeud(int type, noeud * pere, int sommet, int n)
660
{
661
/* cree un nouveau noeud. Noter que l'on est oblige de passer n
662
comme parametre car les bords et separateurs droits doivent
663
etre initilises avec des valeurs >n */
664
noeud *nn;
665
static int compteur = 0;
666
/*pour donner un ID unique aux noeuds. juste pour debug */
667
668
nn = (noeud *) fabmalloc(sizeof(noeud));
669
nn->type = type;
670
nn->pere = pere;
671
/* nn->fpere ne peut etre deja mis a jour... */
672
nn->sommet = sommet;
673
nn->ps = n + 2;
674
nn->ds = -2;
675
/*ces valeurs pour distinguer "non calcule" (-2) */
676
/* de "abscence de separateur" (-1). De plus, on fera des min et des */
677
/* max sur les bords */
678
nn->bg = n + 2;
679
nn->bd = -2; /* idem */
680
681
nn->fils = NULL;
682
nn->lastfils = NULL;
683
nn->id = compteur;
684
compteur++;
685
return nn;
686
}
687
688
void ajoutfils(noeud * pere, noeud * nfils)
689
{
690
fils *nf;
691
/* noter que c'est un ajout en queue! */
692
nf = (fils *) fabmalloc(sizeof(fils));
693
nf->pointe = nfils;
694
nf->suiv = NULL;
695
if (pere->fils == NULL)
696
pere->fils = nf; /* on cree le premier fils */
697
else
698
pere->lastfils->suiv = nf; /* on ajoute nf a la chaine */
699
pere->lastfils = nf;
700
nfils->pere = pere; /* normalement: redondant,mais... */
701
nfils->fpere = nf;
702
}
703
704
void fusionne(noeud * pere, noeud * artefact)
705
{
706
/*fusionne un artefact a son pere.
707
utilise le champ fpere qui permet de savoir ou se greffer
708
une structure fils sera detruite dans l'operation: artefact->fils */
709
fils *greffe;
710
fils *f;
711
/* met a jour la liste des peres */
712
f = artefact->fils;
713
while (f != NULL) {
714
f->pointe->pere = pere; /*avant c'etait ancien... */
715
/* f->pointe->fpere est inchange */
716
f = f->suiv;
717
}
718
/* greffe la liste */
719
greffe = artefact->fpere;
720
artefact->lastfils->suiv = greffe->suiv;
721
greffe->pointe = artefact->fils->pointe;
722
greffe->suiv = artefact->fils->suiv;
723
artefact->fils->pointe->fpere = greffe; /*artefact->fils a disparu */
724
if (pere->lastfils == greffe)
725
pere->lastfils = artefact->lastfils;
726
}
727
728
void
729
extraire(noeud * ancien, noeud * nouveau, fils * premier, fils * dernier)
730
{
731
/* extrait la liste [premier...dernier] des fils de l'ancien noeud,
732
et en fait la liste des fils du nouveau noeud */
733
fils *nf; /* il faut une structure fils de plus */
734
fils *f; /*indice de mise a jour */
735
nf = (fils *) fabmalloc(sizeof(fils));
736
nf->pointe = premier->pointe;
737
nf->suiv = premier->suiv;
738
premier->pointe->fpere = nf;
739
nouveau->pere = ancien;
740
nouveau->fils = nf;
741
nouveau->lastfils = dernier;
742
nouveau->bg = premier->pointe->bg;
743
nouveau->bd = dernier->pointe->bd;
744
nouveau->ps = premier->pointe->bg; /* nouveau est suppose etre un */
745
nouveau->ds = dernier->pointe->bd; /* module, donc bords=separateurs! */
746
if (ancien->lastfils == dernier)
747
ancien->lastfils = premier;
748
/* ecrase l'ancier premier */
749
nouveau->fpere = premier;
750
premier->pointe = nouveau;
751
premier->suiv = dernier->suiv;
752
/* met a jour dernier */
753
dernier->suiv = NULL;
754
/* met a jour la liste des peres */
755
f = nf;
756
while (f != dernier->suiv) {
757
f->pointe->pere = nouveau; /*avant c'etait ancien... */
758
f->pointe->fpere = premier;
759
f = f->suiv;
760
}
761
}
762
763
void printnoeud(noeud * N, int level)
764
{
765
/* imprime recursivement l'arbre par parcours en profondeur */
766
fils *ffils;
767
noeud *nfils;
768
int i;
769
ffils = N->fils;
770
771
for (i = 0; i < level - 1; i++)
772
printf(" |");
773
if (N->pere == NULL)
774
printf(" ");
775
else
776
printf(" +-");
777
switch (N->type) {
778
case UNKN:
779
printf("Noeud\n");
780
break;
781
case MODULE:
782
printf("Module\n");
783
break;
784
case ARTEFACT:
785
printf("Artefact\n");
786
break;
787
case SERIE:
788
printf("Serie \n");
789
break;
790
case PARALLELE:
791
printf("Parallele \n");
792
break;
793
case PREMIER:
794
printf("Premier \n");
795
break;
796
}
797
798
do {
799
nfils = ffils->pointe;
800
if (nfils->type == FEUILLE) {
801
for (i = 0; i < level; i++)
802
printf(" |");
803
printf(" +--");
804
printf("%i\n", 1 + nfils->nom);
805
}
806
else {
807
printnoeud(nfils, level + 1);
808
}
809
ffils = ffils->suiv;
810
}
811
while (ffils != NULL);
812
}
813
814
void printarbre(noeud * N)
815
{
816
printnoeud(N, 0);
817
}
818
819
noeud *algo2(graphe G, sommet **S)
820
{
821
/* algorithme de decomposition modulaire, deuxieme passe
822
entree: le graphe G, et sa permutation factorisante S.
823
sortie: un pointeur sur un arbre de decomposition modulaire
824
*/
825
/* debug: S n'est utilise que pour mettre vrainom a jour */
826
int n; //nombre de sommets du graphe
827
int *ouvrantes; /* tableau du nombre de parentheses ouvrantes */
828
/* ouvrante[i]=3 ssi i-1(((i */
829
/* ouvrante [0]=3: (((0 */
830
831
int *fermantes; /* idem fermantes[i]=2 ssi i)))i+1
832
fermante [n-1]=2 ssi n))) */
833
int *ps; /* ps[i]=premier separateur de (i,i+1) */
834
int *ds;
835
836
int i, j; /*indices de paires ou de sommets */
837
838
sadj *a1, *a2; /* parcours de liste d'adjacence */
839
840
noeud *racine; /*racine du pseudocoardre */
841
noeud *courant, *nouveau; /* noeud courant du pseudocoarbre */
842
noeud **pileinterne; /* pile des modules pour les passes 3,5,5 */
843
int indicepileinterne = 0; /*pointeur dans cette pile */
844
int taillepileinterne; /* taille de la pile apres la 2eme passe */
845
846
int *adjii; /* adjii[i]=1 ssi S[i] et S[i+1] sont */
847
/* adjacents */
848
/*PROPHASE : initialisations */
849
n=G.n;
850
ouvrantes = (int *) fabmalloc(n * sizeof(int));
851
fermantes = (int *) fabmalloc(n * sizeof(int));
852
ps = (int *) fabmalloc(n * sizeof(int));
853
ds = (int *) fabmalloc(n * sizeof(int));
854
pileinterne = (noeud **) fabmalloc((2 * n + 4) * sizeof(noeud *));
855
adjii= (int *) fabmalloc(n*sizeof(int));
856
/*pas plus de 2n+4 noeuds internes dans le pseudocoarbre */
857
for (i = 0; i < n; i++) {
858
ouvrantes[i] = 0;
859
fermantes[i] = 0;
860
adjii[i]=0;
861
}
862
863
/* remplit adjii qui dit si S[i] adjacent a S[i+1] */
864
for(i=0; i<n-1; i++)
865
{
866
a1=S[i]->adj;
867
while((a1!=NULL)&&(a1->pointe->place != i+1))
868
a1=a1->suiv;
869
if( a1 == NULL)
870
adjii[i]=0;
871
else // a1->pointe->place==i+1, donc i adj i+1
872
adjii[i]=1;
873
}
874
adjii[n-1]=0; //perfectionnisme
875
876
/* PREMIERE PASSE
877
on va parentheser la permutation factorisante.
878
tout bonnement, on lit S et on cherche les separateurs;
879
apres quoi ouvrantes et fermantes sont ecrites
880
complexite: O(n^2) */
881
882
ouvrantes[0] = 1;
883
fermantes[n - 1] = 1; /* parentheses des bords */
884
885
for (i = 0; i < n - 1; i++) {
886
/*recherche de ps(i,i+1) */
887
a1=S[i]->adj;
888
a2=S[i+1]->adj;
889
while((a1!=NULL) && (a2!=NULL) && (a1->pointe->place<i) &&
890
(a2->pointe->place<i) && (a1->pointe->place == a2->pointe->place))
891
{
892
a1=a1->suiv;
893
a2=a2->suiv;
894
}
895
896
//arbre de decision complique pour trouver le premier separateur!
897
if( ((a1==NULL) && (a2==NULL))
898
||((a1==NULL) &&(a2->pointe->place >= i))
899
||((a2==NULL) && (a1->pointe->place >= i))
900
||((a1!=NULL) && (a2!=NULL) && (a1->pointe->place >= i) && (a2->pointe->place >= i)))
901
//pas de separateur
902
ps[i]=i+1;
903
else
904
{
905
if((a1==NULL) || (a1->pointe->place >= i))
906
ps[i]=a2->pointe->place;
907
else if((a2==NULL) || (a2->pointe->place >= i))
908
ps[i]=a1->pointe->place;
909
else
910
{
911
if((a1->suiv!=NULL)&&(a1->suiv->pointe->place == a2->pointe->place))
912
ps[i]=a1->pointe->place;
913
else if ((a2->suiv!=NULL)&&(a2->suiv->pointe->place == a1->pointe->place))
914
ps[i]=a2->pointe->place;
915
else
916
ps[i]=min(a1->pointe->place , a2->pointe->place);
917
}
918
ouvrantes[ps[i]]++; /* marque la fracture gauche, if any */
919
fermantes[i]++;
920
}
921
if (DEBUG)
922
printf("ps(%i,%i)=%i\n", i , i+1, ps[i]);
923
924
/*recherche de ds(i,i+1)
925
plus penible encore!*/
926
a1=S[i]->adj;
927
if(a1!=NULL) // se place en queue de liste.
928
while(a1->suiv!=NULL)
929
a1=a1->suiv;
930
a2=S[i+1]->adj;
931
if(a2!=NULL)
932
while(a2->suiv!=NULL)
933
a2=a2->suiv;
934
while((a1!=NULL) && (a2!=NULL) && (a1->pointe->place > i+1) &&
935
(a2->pointe->place > i+1) && (a1->pointe->place == a2->pointe->place))
936
{
937
a1=a1->prec;
938
a2=a2->prec;
939
}
940
if( ((a1==NULL) && (a2==NULL))
941
||((a1==NULL) && (a2->pointe->place <= i+1))
942
||((a2==NULL) && (a1->pointe->place <= i+1))
943
||((a1!=NULL) && (a2!=NULL) && (a1->pointe->place <= i+1) && (a2->pointe->place <= i+1)))
944
//pas de separateur
945
ds[i]=i+1;
946
else
947
{
948
if((a1==NULL) || (a1->pointe->place <= i+1))
949
ds[i]=a2->pointe->place;
950
else if((a2==NULL) || (a2->pointe->place <= i+1))
951
ds[i]=a1->pointe->place;
952
else
953
{
954
if((a1->prec!=NULL)&&(a1->prec->pointe->place == a2->pointe->place))
955
ds[i]=a1->pointe->place;
956
else if((a2->prec!=NULL)&&(a2->prec->pointe->place == a1->pointe->place))
957
ds[i]=a2->pointe->place;
958
else
959
ds[i]=max(a1->pointe->place , a2->pointe->place);
960
}
961
962
963
//ds[i] = j;
964
ouvrantes[i + 1]++; /* marque la fracture gauche, if any */
965
fermantes[ds[i]]++; /* attention aux decalages d'indices */
966
}
967
if (DEBUG)
968
printf("ds(%i,%i)=%i\n", i,i+1,ds[i]);
969
//S[i]->nom + 1, S[i + 1]->nom + 1, S[ds[i]]->nom + 1);
970
}
971
972
/*DEUXIEME PASSE: construction du pseudocoarbre */
973
974
racine = nouvnoeud(UNKN, NULL, -1, n);
975
courant = racine;
976
for (i = 0; i < n; i++) {
977
/*1: on lit des parentheses ouvrantes: descentes */
978
for (j = 0; j < ouvrantes[i]; j++) {
979
/*Descente vers un nouveau noeud */
980
nouveau = nouvnoeud(UNKN, courant, -1, n);
981
ajoutfils(courant, nouveau); /*on l'ajoute... */
982
courant = nouveau; /* et on descent */
983
if (DEBUG)
984
printf("(");
985
}
986
/* 2: on lit le sommet: feuille */
987
nouveau = nouvnoeud(FEUILLE, courant, i, n);
988
ajoutfils(courant, nouveau); /*on ajoute le bebe... */
989
(*nouveau).ps = i;
990
(*nouveau).ds = i;
991
(*nouveau).bg = i;
992
(*nouveau).bd = i;
993
nouveau->nom = S[i]->nom;
994
/* et pourquoi pas i ? Je m'embrouille... */
995
if (DEBUG)
996
printf(" %i ", S[i]->nom + 1);
997
998
/*3: on lit des parentheses fermantes: remontees */
999
for (j = 0; j < fermantes[i]; j++) {
1000
/*ASTUCE: ici on va en profiter pour supprimer les
1001
noeuds a un fils, afin d'economiser une passe */
1002
if (courant->fils == courant->lastfils) { /*just one son */
1003
courant->pere->lastfils->pointe = courant->fils->pointe;
1004
courant->fils->pointe->pere = courant->pere;
1005
courant->fils->pointe->fpere = courant->pere->lastfils;
1006
/* explication: le dernier fils de courant.pere est
1007
actuellement courant himself. Il est donc pointe par
1008
courant.pere.lastfils.pointe. Il suffit de changer ce
1009
pointeur pour qu'il pointe maintenant non plus sur courant,
1010
mais sur l'unique fils de courant: courant.fils.pointe.
1011
Ouf! */
1012
/* NB: courant est maintenant deconnecte de l'arbre.
1013
on pourrait faire un free() mais bon... */
1014
}
1015
else {
1016
/*on empile ce noeud interne.
1017
L'ordre est celui de la postvisite d'un DFS */
1018
pileinterne[indicepileinterne] = courant;
1019
indicepileinterne++;
1020
}
1021
/* et dans tous les cas: on remonte! */
1022
courant = courant->pere;
1023
if (DEBUG)
1024
printf(")");
1025
}
1026
}
1027
if (DEBUG)
1028
printf("\n");
1029
1030
/* on enleve un ptit defaut */
1031
racine = racine->fils->pointe;
1032
racine->pere = NULL;
1033
racine->fpere = NULL;
1034
if (DEBUG) {
1035
printf("Arbre apres la deuxieme passe:\n");
1036
printnoeud(racine, 0);
1037
}
1038
1039
/*TROISIEME PASSE */
1040
/* A ce stade, on a un pseudocoarbre de racine racine,
1041
sans noeuds a un fils, et dont les etiquettes sont
1042
FEUIILLE ou UNKN. Il y a une pile des noeuds UNKN, stockes
1043
dans l'ordre de la postvisite d'un parcours en profondeur.
1044
On va s'en servir pour faire remonter les bords et les
1045
separateurs de bas en haut */
1046
1047
taillepileinterne = indicepileinterne;
1048
for (indicepileinterne = 0; indicepileinterne < taillepileinterne;
1049
indicepileinterne++) {
1050
noeud *scanne;
1051
fils *nextfils;
1052
noeud *succourant;
1053
/* scanne est le noeud (pere) que l'on regarde;
1054
nextfils parcours sa liste de fils;
1055
courant est le fils actuellement examine et
1056
succourant=succ(courant) */
1057
noeud *debutnoeud;
1058
fils *debutfils;
1059
/*deux variables utilise pour la recherche de jumeaux:
1060
debut du bloc max */
1061
1062
scanne = pileinterne[indicepileinterne]; /*he oui, la pile */
1063
nextfils = scanne->fils; /*on commence au premier fils */
1064
do {
1065
/*la boucle chiante... cf mon memoire de DEA */
1066
courant = nextfils->pointe;
1067
/* bords */
1068
scanne->bg = min(scanne->bg, courant->bg);
1069
scanne->bd = max(scanne->bd, courant->bd);
1070
/*separateurs */
1071
scanne->ps = min(scanne->ps, courant->ps);
1072
if (scanne->fils->pointe != courant)
1073
/*ce n'est pas le premier fils */
1074
scanne->ps = min(scanne->ps, ps[(courant->bg) - 1]);
1075
scanne->ds = max(scanne->ds, courant->ds);
1076
if (scanne->lastfils->pointe != courant)
1077
/*ce n'est pas le dernier fils */
1078
scanne->ds = max(scanne->ds, ds[courant->bd]);
1079
1080
nextfils = nextfils->suiv;
1081
}
1082
while (nextfils != NULL);
1083
1084
1085
if (DEBUG)
1086
printf("noeud %i-%i: ps=%i ds=%i", 1 + scanne->bg,
1087
1 + scanne->bd, 1 + scanne->ps, 1 + scanne->ds);
1088
1089
/* maintenant le test tout simple pour savoir si on a un module: */
1090
if (((scanne->ps) == (scanne->bg))
1091
&& ((scanne->ds) == (scanne->bd))) {
1092
/*on a un module */
1093
scanne->type = MODULE;
1094
if (DEBUG)
1095
printf(" Module.\n");
1096
}
1097
else {
1098
scanne->type = ARTEFACT;
1099
if (DEBUG)
1100
printf(" artefact.\n");
1101
}
1102
}
1103
1104
if (DEBUG) {
1105
printf("Arbre apres la troisieme passe:\n");
1106
printnoeud(racine, 0);
1107
}
1108
1109
/* QUATRIEME PASSE */
1110
/* technique:on se contente de fusionner les artefacts a leurs parents
1111
ca se fait de bas en haut grace a pileinterne (toujours elle!) */
1112
1113
for (indicepileinterne = 0; indicepileinterne < taillepileinterne;
1114
indicepileinterne++) {
1115
noeud *scanne;
1116
scanne = pileinterne[indicepileinterne]; /*he oui, la pile */
1117
if (scanne->type == ARTEFACT) {
1118
/*attention! La pile peut contenir des noeuds deconnectes */
1119
fusionne(scanne->pere, scanne);
1120
if (DEBUG)
1121
printf("Artefact elimine: %i-%i\n", 1 + scanne->bg,
1122
1 + scanne->bd);
1123
}
1124
}
1125
if (DEBUG) {
1126
printf("Arbre apres la quatrieme passe:\n");
1127
printnoeud(racine, 0);
1128
}
1129
1130
/* CINQIEME ET DERNIERE PASSE */
1131
/* on va typer les noeuds et extraire les fusions.
1132
comment on fait? Ben, la pile.... */
1133
for (indicepileinterne = 0; indicepileinterne < taillepileinterne;
1134
indicepileinterne++) {
1135
noeud *scanne;
1136
fils *nextfils;
1137
noeud *succourant;
1138
/* scanne est le noeud (pere) que l'on regarde;
1139
nextfils parcours sa liste de fils;
1140
courant est le fils actuellement examine et
1141
succourant=succ(courant) */
1142
noeud *debutnoeud;
1143
fils *debutfils;
1144
/*deux variables utilise pour la recherche de jumeaux:
1145
debut du bloc max */
1146
1147
scanne = pileinterne[indicepileinterne];
1148
if (scanne->type != MODULE)
1149
continue; /* je traite que les modules */
1150
1151
nextfils = scanne->fils; /*on commence au premier fils */
1152
while (1) {
1153
courant = nextfils->pointe;
1154
succourant = nextfils->suiv->pointe;
1155
if (ps[courant->bd] >= courant->bg
1156
&& ds[courant->bd] <= succourant->bd) {
1157
/*Des jumeaux!! ->serie ou parallele! */
1158
/* on va determiner le bloc max de jumeaux consecutifs */
1159
debutnoeud = courant;
1160
debutfils = nextfils;
1161
while (ps[courant->bd] >= courant->bg &&
1162
ds[courant->bd] <= succourant->bd &&
1163
nextfils->suiv != NULL) {
1164
nextfils = nextfils->suiv;
1165
courant = nextfils->pointe;
1166
if (nextfils->suiv == NULL)
1167
break;
1168
succourant = nextfils->suiv->pointe;
1169
}
1170
/*maintenant on connait la taille du bloc: il va de
1171
debutnoeud a courant inclus,
1172
et dans la liste des fils de scanne,
1173
il s'etend de debutfils a nextfils inclus.
1174
On extrait cette liste pour en faire les fils d'un
1175
nouveau noeud... sauf si pas la peine! */
1176
if (debutfils == scanne->fils
1177
&& nextfils == scanne->lastfils) {
1178
/* le noeud scanne etait serie ou parallele */
1179
if ( adjii[debutnoeud->bd] !=0)
1180
scanne->type = SERIE;
1181
else
1182
scanne->type = PARALLELE;
1183
}
1184
else {
1185
if ( adjii[debutnoeud->bd]!=0)
1186
/*seule cette ligne distingue G de G' !! */
1187
{
1188
nouveau = nouvnoeud(SERIE, scanne, -1, n);
1189
if (DEBUG)
1190
printf("Module serie extrait: %i-%i\n",
1191
1 + debutnoeud->bg, 1 + courant->bd);
1192
}
1193
else {
1194
nouveau = nouvnoeud(PARALLELE, scanne, -1, n);
1195
if (DEBUG)
1196
printf("Module parallele extrait: %i-%i\n",
1197
1 + debutnoeud->bg, 1 + courant->bd);
1198
}
1199
extraire(scanne, nouveau, debutfils, nextfils);
1200
}
1201
}
1202
if (scanne->type == MODULE)
1203
scanne->type = PREMIER;
1204
if (nextfils->suiv == NULL || nextfils->suiv->suiv == NULL
1205
|| nextfils->suiv->suiv->suiv == NULL)
1206
break;
1207
nextfils = nextfils->suiv;
1208
}
1209
}
1210
if (DEBUG) {
1211
printf("Arbre final:\n");
1212
printnoeud(racine, 0);
1213
}
1214
return racine;
1215
}
1216
1217
void PrintG(graphe G)
1218
/* affiche le graphe */
1219
{
1220
int i,r; adj *a;
1221
r=0;
1222
for(i=0;i<G.n;i++)
1223
{
1224
printf("%i : ",i);
1225
a=G.G[i];
1226
while(a!=NULL)
1227
{
1228
printf("%i ", a->s);
1229
a=a->suiv;
1230
}
1231
printf("\n");
1232
}
1233
}
1234
void PrintGS(sommet **S, int n)
1235
/* affiche le graphe trie selon S (celui utilise par algo2)*/
1236
{
1237
int i; sadj *a;
1238
for(i=0;i<n;i++)
1239
{
1240
printf("%i : ",i);
1241
a=S[i]->adj;
1242
while(a!=NULL)
1243
{
1244
printf("%i ", a->pointe->place);
1245
a=a->suiv;
1246
}
1247
printf("\n");
1248
}
1249
}
1250
1251
void PrintS2(sommet **S, int n)
1252
/* affiche la permutation factorisante */
1253
{
1254
int i;
1255
printf( "Place (nouvelle num) ");
1256
for(i=0;i<n;i++)
1257
printf("%3i ",S[i]->place);
1258
printf("\nNom (ancienne num) : ");
1259
for(i=0;i<n;i++)
1260
printf("%3i ",S[i]->nom);
1261
printf("\n");
1262
}
1263
1264
1265
/* la fonction principale; qui fait pas grand'chose....*/
1266
noeud *decomposition_modulaire(graphe G)
1267
{
1268
1269
sommet **S; /* la permutation factorisante */
1270
noeud *Racine; /* le futur arbre de decomposition */
1271
1272
setbuf(stdout,NULL);
1273
1274
S = algo1(G); /* premiere partie: calcul
1275
de la permutation factorisante */
1276
1277
TrierTous(S,G.n,Calculm(G));/* Trie les listes d'adjacence selon S
1278
*/
1279
if(DEBUG)
1280
{
1281
PrintGS(S,G.n);
1282
PrintS2(S,G.n);
1283
}
1284
Racine = algo2(G, S); /* deuxieme partie: calcul de l'arbre */
1285
return Racine;
1286
}
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297