Path: blob/master/sage/graphs/modular_decomposition/src/dm.c
4056 views
/******************************************************12Copyright 2004, 2010 Fabien de Montgolfier3[email protected]45This program is free software; you can redistribute it and/or6modify it under the terms of the GNU General Public License7as published by the Free Software Foundation; either version 28of the License, or (at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program; if not, write to the Free Software17Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.18**********************************************************/1920/********************************************************2122DECOMPOSITION MODULAIRE DE GRAPHES NON-ORIENTES2324Cet algorithme construit l'arbre de decomposition modulaire25d'un graphe donne sous forme d'une matrice d'adjacence.26Il s'effectue en temps O(m log n) temps et $O(m)$ espace.27Il est la concatenation de deux algorithmes distincts.28Le premier realise une permutation factorisante des sommets du graphe29(pour cette notion, cf these de Christian Capelle)30grace a une technique d'affinage de partitions (cf Habib, Paul & Viennot)31Le second construit l'arbre de decomposition modulaire a partir de cette32permutation, cf mon memoire de DEA33Montpellier, decembre 200034********************************************************/3536//#include "dm_english.h"37#include <stdio.h>38#include <stdlib.h>3940#define DEBUG 0 /* si 0 aucune sortie graphique!! */4142/* dm.h definit les constantes FEUILLE, MODULE, etc...43ainsi que les structures noeud et fils. Les autres44structures n'ont pas a etre connues par les programmes45exterieurs et sont donc definies ici. */464748/* un sommet du graphe49(utilise dans la premiere partie seulement, ainsi que ce qui suit)*/50typedef struct Sommet {51int place;/* numero du sommet dans la NOUVELLE numerotation */52int nom; /* numero du sommet dans l'ANCIENNE numerotation */53/* On a donc sigma(nom)=place */54struct Sadj *adj;55struct SClasse *classe;56} sommet;5758/* liste d'adjacence d'un sommet, DOUBLEMENT chainee*/59typedef struct Sadj {60struct Sommet *pointe;61struct Sadj *suiv;62struct Sadj *prec;63} sadj;6465/* classe de la partition courante,66organisees en liste chainnee selon l'ordre de la partition */67typedef struct SClasse {68int debut;69int fin;70struct Sommet *firstpivot;71int inpivot; /*indice de la classe dans le tableau pivot */72int inmodule; /* (resp module); -1 si non present */73int whereXa; /* lie le couple X/Xa: vaut740 si X n'est actuellement lie a aucun Xa75-1 si Xa est a gauche76+1 si Xa est a droite */77struct SClasse *suiv; /* forment une liste chainee */78struct SClasse *prec; /*...doublement */79} sclasse;8081/* plein de parametres statiques que algo1() donne a Raffine() */82typedef struct Info {83sclasse **pivot;84int *ipivot;85sclasse **module;86int *imodule;87int *numclasse;88int *n;89} info;9091/* clef a deux entrees utilisee pour le tri lineaire92represente l'arrete ij */93typedef struct Clef2{94int i; //sommet pointeur95int nom; // nom du sommet pointe96int place; //place du sommet pointe97} clef2;9899/*************************************************************100utilitaires101*************************************************************/102void *fabmalloc(size_t s)103/* malloc sans erreur */104{105void *p;106p=malloc(s);107if(p==NULL)108{109perror("Erreur de malloc!\n");110exit(1);111}112return p;113}114115int min(int a, int b)116{117return (a<b) ? a : b;118}119120int max(int a, int b)121{122return (a > b) ? a : b;123}124/**************************************************************125Premiere passe de l'algorithme: il s'agit de trouver une126permutation factorisante du graphe. Nous utilisons les127techniques de raffinement de partition. Tout cela est128explique dans l'article de Habib, Viennot & Paul, dont je ne129fais ici que transcrire le travail.130****************************************************************/131132void printS(sommet ** S, int n)133{134/* imprimme S selon S et selon les classes */135int i;136sclasse *s;137138for (s = S[0]->classe; s != NULL; s = s->suiv) {139printf("[ ");140for (i = s->debut; i <= s->fin; i++)141printf("%i ", 1 + S[i]->nom);142printf("] ");143}144printf("\n");145}146147sclasse *nouvclasse(sclasse * un, sclasse * deux)148{149/* cree une nouvelle classe et l'insere entre un et deux;150on suppose que si un et deux sont pas NULL alors151FORCEMENT un=deux->suiv */152153sclasse *nouv;154nouv = (sclasse *) fabmalloc(sizeof(sclasse));155nouv->whereXa = 0;156nouv->inpivot = -1;157nouv->inmodule = -1;158nouv->firstpivot = NULL;159nouv->prec = un;160if (un != NULL) /* accroche pas en tete de chaine */161un->suiv = nouv;162nouv->suiv = deux;163if (deux != NULL) /* pas en queue de chaine */164deux->prec = nouv;165166/* debut et fin ne sont PAS initialises! */167return nouv;168}169170void permute(sommet ** S, int a, int b)171{172/* transpose les sommets a et b dans S */173/* ne touche pas aux classes! */174sommet *tmp;175S[a]->place = b;176S[b]->place = a;177tmp = S[a];178S[a] = S[b];179S[b] = tmp;180}181182void Raffiner(sommet ** S, sommet * p, sommet * centre, info * I)183{184/* melange raffiner, pivotset, insertright et addpivot */185sadj *a; /* parcours l'adjacence du pivot */186sommet *x; /* sommet quiva changer de classe */187sclasse *X, *Xa; /* x in X; Xa nouv classe de x */188sclasse *Z;189sclasse **pivot;190sclasse **module;191int *ipivot, *imodule, *numclasse, n;192193if (DEBUG)194printf("Raffinage avec le pivot %i\n", 1 + p->nom);195pivot = I->pivot;196module = I->module;197ipivot = I->ipivot;198imodule = I->imodule;199numclasse = I->numclasse;200n = *(I->n);201202for (a = p->adj; a != NULL; a = a->suiv) {203x = a->pointe;204X = x->classe;205if (X == p->classe)206continue; /* on raffine pas la classe du pivot! */207208if (X->whereXa == 0) {209/* c'est la premiere fois qu'on trouve un x210appartenant a X lors de cet appel a raffiner */211212if ((centre->place < x->place && x->place < p->place)213|| (p->place < x->place && x->place < centre->place)) {214/* insere a gauche */215Xa = nouvclasse(X->prec, X);216(*numclasse)++;217permute(S, x->place, X->debut);218X->debut++;219X->whereXa = -1;220Xa->whereXa = 1; /* besoin dans le second tour */221}222else { /* insere a droite */223224Xa = nouvclasse(X, X->suiv);225(*numclasse)++;226permute(S, x->place, X->fin);227X->fin--;228X->whereXa = 1;229Xa->whereXa = -1;230}231x->classe = Xa;232Xa->debut = x->place;233Xa->fin = x->place;234}235else {236if (X->whereXa == -1) {237Xa = X->prec;238permute(S, x->place, X->debut);239X->debut++;240Xa->fin++;241}242else {243Xa = X->suiv;244permute(S, x->place, X->fin);245X->fin--;246Xa->debut--;247}248x->classe = Xa;249}250}251252for (a = p->adj; a != NULL; a = a->suiv)253/* deuxieme couche! Maintenant on va faire les addpivot,254et remettre les whereXa a 0255Noter qu'on lit les Xa et plus les X */256{257x = a->pointe;258Xa = x->classe;259if (Xa->whereXa == 0)260continue; /* deja remis a zero! */261if (Xa->whereXa == -1)262X = Xa->prec;263else264X = Xa->suiv;265266if (X->debut > X->fin) {267/*on a trop enleve! X est vide268-> on va le supprimer mechamment */269270(*numclasse)--;271if (Xa->whereXa == 1) { /*deconnecte */272Xa->suiv = X->suiv;273if (Xa->suiv != NULL)274Xa->suiv->prec = Xa;275}276else {277Xa->prec = X->prec;278if (Xa->prec != NULL)279Xa->prec->suiv = Xa;280}281Xa->inpivot = X->inpivot;282if (X->inpivot != -1) /* ecrase X dans pivot */283pivot[X->inpivot] = Xa;284Xa->inmodule = X->inmodule;285if (X->inmodule != -1) /* ecrase X dans pivot */286module[X->inmodule] = Xa;287288Xa->whereXa = 0;289continue;290}291292/* Maintenant on fait addpivot(X,Xa)293noter que X et Xa sont non vides */294295if (X->inpivot == -1) {296if ((X->inmodule != -1)297&& (X->fin - X->debut < Xa->fin - Xa->debut)) {298/* remplace X par Xa dans module */299module[X->inmodule] = Xa;300Xa->inmodule = X->inmodule;301X->inmodule = -1;302if (DEBUG)303printf("Dans module %i-%i ecrase %i-%i\n",3041 + S[Xa->debut]->nom, 1 + S[Xa->fin]->nom,3051 + S[X->debut]->nom, 1 + S[X->fin]->nom);306}307else {308if (X->inmodule == -1) {309if (X->fin - X->debut < Xa->fin - Xa->debut)310Z = Xa;311else312Z = X;313/* ajoute Z (=max(X,Xa)) a module */314module[(*imodule)] = Z;315Z->inmodule = (*imodule);316(*imodule)++;317if (DEBUG)318printf("module empile:%i-%i\n",3191 + S[Z->debut]->nom, 1 + S[Z->fin]->nom);320}321}322}323324if (X->inpivot != -1)325Z = Xa;326else if (X->fin - X->debut < Xa->fin - Xa->debut)327Z = X;328else329Z = Xa;330/* on empile Z dans pivot */331pivot[(*ipivot)] = Z;332Z->inpivot = (*ipivot);333(*ipivot)++;334if (DEBUG)335printf("pivot empile: %i-%i\n", 1 + S[Z->debut]->nom,3361 + S[Z->fin]->nom);337X->whereXa = 0;338Xa->whereXa = 0;339}340if (DEBUG) {341printS(S, n);342printf("\n");343}344}345346sommet **algo1(graphe G)347/* Entree: un graphe G348Sortie: une permutation factorisante de G,349donnee sous la forme d'un tableau de structures Sommet ordonnees selon sigma.350d'apres le travail de Habib/Paul/Viennot */351{352int n; // nombre de sommets de G353354sclasse **pivot; /*pile des pivots */355int ipivot = 0; /*indice sur la precedante */356357sclasse **module; /*idem, modules */358int imodule = 0;359360sclasse *singclasse;361/*invariant: toute classe avant singclasse dans la chaine */362/*a un seul element */363int numclasse; /* quand vaut n, on a fini!! */364365sclasse *C1; /*premiere classe, tete de la chaine */366sclasse *Y; /*classe qui raffine */367sclasse *X; /*classe raffinee */368sclasse *Xa, *Xc; /* morceaux de X */369sommet *x; /* x in X */370sommet *y; /* y in Y */371sommet *centre; /* le centre du raffinage actuel */372373sommet **S; /*la permutation factorisante ! */374375int i, j; /*divers indices */376sommet *scourant; /* pour l'init */377sadj *nextadj; /*sommet adjacent suivant */378adj *nextadj2; /* idem mais de type adj */379info Inf; /* diverses info a passer a raffiner */380381/* debut des initialisations */382n=G.n;383/*initialisation des tableaux */384module = (sclasse **) fabmalloc(n * sizeof(sclasse *));385pivot = (sclasse **) fabmalloc(n * sizeof(sclasse *));386S = (sommet **) fabmalloc(n * sizeof(sommet *));387/* on va initialiser la permutation factorisante,388ainsi que chaque structure sommet */389C1 = nouvclasse(NULL, NULL);390numclasse = 1;391singclasse = C1;392C1->debut = 0;393C1->fin = n - 1;394for (i = 0; i < n; i++) {395/* initialisation des sommets */396/* notre bebe est le sommet i dans M */397scourant = (sommet *) fabmalloc(sizeof(struct Sommet));398scourant->nom = i;399scourant->place = i; /* a ce point S=identite */400scourant->adj = NULL; /* pas encore d'adjacence */401scourant->classe = C1;402S[i] = scourant;403}404for (i = 0; i < n; i++)405{406nextadj2 = G.G[i];407while(nextadj2 != NULL)408{409j=nextadj2->s; //numero du sommet pointe410if((j<0)||(j>=n))411{412perror("Graphe invalide (numero de sommet erronne)!\n");413exit(1);414}415nextadj = (sadj *) fabmalloc(sizeof(struct Sadj));416//un nouveau sadj417nextadj->pointe = S[j];418nextadj->suiv = S[i]->adj; //tete de liste419if(nextadj->suiv!=NULL)420nextadj->suiv->prec=nextadj;421nextadj->prec=NULL;422S[i]->adj = nextadj; /*et le tour est joue */423nextadj2=nextadj2->suiv;424}425}426/* NB: module et pivot sont vides */427Inf.pivot = pivot;428Inf.ipivot = &ipivot;429Inf.module = module;430Inf.imodule = &imodule;431Inf.numclasse = &numclasse;432Inf.n = &n;433/* init terminnee */434435while (1) {436while (ipivot > 0 || imodule > 0) {437while (ipivot > 0) {438/*cette boucle raffine selon tous les sommets439de la premiere classe dans pivot */440441Y = pivot[ipivot - 1];442ipivot--;443Y->inpivot = -1;444445for (i = Y->debut; i <= Y->fin; i++)446Raffiner(S, S[i], centre, &Inf);447448/* une optimisation de la fin de l'algo */449if (numclasse == n)450return (S);451}452/*maintenant pivot est vide, mais peut-etre pas module */453if (imodule > 0) {454/* relance par un sommet (pas au pif...) */455/* de chaque module qui le represente */456Y = module[imodule - 1];457imodule--;458Y->inmodule = -1;459y = S[Y->debut]; /* le firstpivot sera toujours... */460Y->firstpivot = y; /* le premier!! */461if (DEBUG)462printf("module-pivot %i-%i: sommet %i\n",4631 + S[Y->debut]->nom, 1 + S[Y->fin]->nom,4641 + y->nom);465Raffiner(S, y, centre, &Inf);466}467}468/* a ce point, pivot et module sont vides...469pas de pb! On va faire initpartition HERE */470if (DEBUG)471printf("\nInit Partition\n");472/**** ajoute ici pour debbugger, mais moche!! */473singclasse = S[0]->classe;474while ((singclasse != NULL) &&475(singclasse->debut == singclasse->fin))476{477singclasse = singclasse->suiv;478}479/* singclasse est la premiere classe480non singlette, sauf si: */481if (singclasse == NULL)482/* on a n classes singlettes? ben c'est gagne! */483{484return (S);485}486if (singclasse == NULL && numclasse < n) {487perror("c'est pas normal! Ca termine trop vite!\n");488exit(1);489}490491X = singclasse;492x = X->firstpivot;493if (x == NULL)494x = S[X->debut];495else /* remet firstpivot a NULL!! */496X->firstpivot = NULL;497498if (DEBUG)499printf("Relance dans le module %i-%i avec le sommet %i\n",5001 + S[X->debut]->nom, 1 + S[X->fin]->nom, 1 + x->nom);501502centre = x; /*important! */503/* astuce: on place {x} en tete de X504ensuite, on raffine S selon x -> seule X est coupee505il y a alors {x} X Xa506-> on met {x} en queue de X et c'est bon!507ainsi on a bien nonvoisins-x-voisons */508Xc = nouvclasse(X->prec, X);509numclasse++;510x->classe = Xc;511permute(S, x->place, X->debut);512X->debut++;513Xc->debut = x->place;514Xc->fin = x->place;515Raffiner(S, x, x, &Inf);516/* X existe-il encore? */517if (X->debut > X->fin)518continue;519/* echange de x et {x}. Init: -{x}-X- */520Xc->suiv = X->suiv;521if (X->suiv != NULL)522X->suiv->prec = Xc;523X->prec = Xc->prec;524if (Xc->prec != NULL)525Xc->prec->suiv = X;526X->suiv = Xc;527Xc->prec = X;528permute(S, x->place, X->fin);529Xc->debut = x->place;530Xc->fin = x->place;531X->debut--;532X->fin--;533//antibug?534singclasse=X;535/* now -X-{x}- */536if (DEBUG)537printS(S, n);538}539}540541/***************************************************************542Etape intermediaire: trier toutes les listes d'adjacence543selon S. ce sont les listes de type sadj qui sont concernees544***************************************************************/545int Calculm(graphe G)546/* compte le nombre d'arretes du graphe */547{548int i,r; adj *a;549r=0;550for(i=0;i<G.n;i++)551{552a=G.G[i];553while(a!=NULL)554{555a=a->suiv;556r++;557}558}559if(r%2!=0)560{561perror("Erreur: nombre impaire d'arrete, graphe non-oriente??\n");562exit(1);563}564return r/2; // G symetrique!565}566567void TrierTous(sommet **S, int n, int m)568/* trie chaque liste d'adjacence de S*/569{570//n sommets, m arretes571int i; // numero du sommet courant572sadj *a,*atmp;// parcours sa liste d'adjacence573clef2 *c; // enregistrement a trier574int *tab1; clef2 **tab2; //tableaux du tri par seaux575tab1=(int *)fabmalloc(n*sizeof(int));576tab2=(clef2 **)fabmalloc(m * 2 * sizeof(clef2 *));577for(i=0; i<n; i++)578tab1[i]=0;579580// premiere passe: construit tab1:581// tab[i] est la frequence du ieme (selon S) sommet582for(i=0; i<n; i++)583{584a=S[i]->adj;585while(a!=NULL)586{587tab1[i]++;588a=a->suiv;589}590}591//deuxieme passe: frequences cumulees a rebours592// (car les listes d'adjacences se construisent a l'envers593//tab1[n-1]--; // a cause des indices de tableau qui commence a zero594//for(i=n-1;i>0;i--)595// tab1[i-1]+=tab1[i];596597//deuxieme passe: frequences cumulees598for(i=1;i<n;i++)599tab1[i]+=tab1[i-1];600601//troisieme passe: liste double602for(i=0; i<n; i++)603{604a=S[i]->adj;605while(a!=NULL)606{607/* cree un nouveau record */608c=(clef2 *)fabmalloc(sizeof(struct Clef2));609c->i=i;610c->nom=a->pointe->nom;611c->place=a->pointe->place;612/* le place bien dans tab2 */613tab1[c->place]--;614tab2[tab1[c->place]]=c;615/*et on continue */616a=a->suiv;617}618}619620//quatrieme passe: detruit les vielles listes d'adjacence621for(i=0; i<n; i++)622{623a=S[i]->adj;624while(a!=NULL)625{626atmp=a->suiv;627free(a);628a=atmp;629}630S[i]->adj=NULL;631}632633//derniere passe: reconstruit les listes d'adjacence634for(i=0;i<2*m;i++)635{636c=tab2[i];637a=(sadj *)fabmalloc(sizeof(struct Sadj));638a->pointe=S[c->i];639a->suiv=S[c->place]->adj; //insere en tete640if(a->suiv!=NULL)641a->suiv->prec=a;642a->prec=NULL;643S[c->place]->adj=a;644//nettoie645free(c);646}647free(tab1);648free(tab2);649}650651652/***************************************************************653Maintenant, la deuxieme partie de l'aglorithme654On va, etant donne la matrice M construite a l'etape precedante,655etablir l'arbre de decomposition modulaire.656Tous les details sont dans mon memoire de DEA657****************************************************************/658noeud *nouvnoeud(int type, noeud * pere, int sommet, int n)659{660/* cree un nouveau noeud. Noter que l'on est oblige de passer n661comme parametre car les bords et separateurs droits doivent662etre initilises avec des valeurs >n */663noeud *nn;664static int compteur = 0;665/*pour donner un ID unique aux noeuds. juste pour debug */666667nn = (noeud *) fabmalloc(sizeof(noeud));668nn->type = type;669nn->pere = pere;670/* nn->fpere ne peut etre deja mis a jour... */671nn->sommet = sommet;672nn->ps = n + 2;673nn->ds = -2;674/*ces valeurs pour distinguer "non calcule" (-2) */675/* de "abscence de separateur" (-1). De plus, on fera des min et des */676/* max sur les bords */677nn->bg = n + 2;678nn->bd = -2; /* idem */679680nn->fils = NULL;681nn->lastfils = NULL;682nn->id = compteur;683compteur++;684return nn;685}686687void ajoutfils(noeud * pere, noeud * nfils)688{689fils *nf;690/* noter que c'est un ajout en queue! */691nf = (fils *) fabmalloc(sizeof(fils));692nf->pointe = nfils;693nf->suiv = NULL;694if (pere->fils == NULL)695pere->fils = nf; /* on cree le premier fils */696else697pere->lastfils->suiv = nf; /* on ajoute nf a la chaine */698pere->lastfils = nf;699nfils->pere = pere; /* normalement: redondant,mais... */700nfils->fpere = nf;701}702703void fusionne(noeud * pere, noeud * artefact)704{705/*fusionne un artefact a son pere.706utilise le champ fpere qui permet de savoir ou se greffer707une structure fils sera detruite dans l'operation: artefact->fils */708fils *greffe;709fils *f;710/* met a jour la liste des peres */711f = artefact->fils;712while (f != NULL) {713f->pointe->pere = pere; /*avant c'etait ancien... */714/* f->pointe->fpere est inchange */715f = f->suiv;716}717/* greffe la liste */718greffe = artefact->fpere;719artefact->lastfils->suiv = greffe->suiv;720greffe->pointe = artefact->fils->pointe;721greffe->suiv = artefact->fils->suiv;722artefact->fils->pointe->fpere = greffe; /*artefact->fils a disparu */723if (pere->lastfils == greffe)724pere->lastfils = artefact->lastfils;725}726727void728extraire(noeud * ancien, noeud * nouveau, fils * premier, fils * dernier)729{730/* extrait la liste [premier...dernier] des fils de l'ancien noeud,731et en fait la liste des fils du nouveau noeud */732fils *nf; /* il faut une structure fils de plus */733fils *f; /*indice de mise a jour */734nf = (fils *) fabmalloc(sizeof(fils));735nf->pointe = premier->pointe;736nf->suiv = premier->suiv;737premier->pointe->fpere = nf;738nouveau->pere = ancien;739nouveau->fils = nf;740nouveau->lastfils = dernier;741nouveau->bg = premier->pointe->bg;742nouveau->bd = dernier->pointe->bd;743nouveau->ps = premier->pointe->bg; /* nouveau est suppose etre un */744nouveau->ds = dernier->pointe->bd; /* module, donc bords=separateurs! */745if (ancien->lastfils == dernier)746ancien->lastfils = premier;747/* ecrase l'ancier premier */748nouveau->fpere = premier;749premier->pointe = nouveau;750premier->suiv = dernier->suiv;751/* met a jour dernier */752dernier->suiv = NULL;753/* met a jour la liste des peres */754f = nf;755while (f != dernier->suiv) {756f->pointe->pere = nouveau; /*avant c'etait ancien... */757f->pointe->fpere = premier;758f = f->suiv;759}760}761762void printnoeud(noeud * N, int level)763{764/* imprime recursivement l'arbre par parcours en profondeur */765fils *ffils;766noeud *nfils;767int i;768ffils = N->fils;769770for (i = 0; i < level - 1; i++)771printf(" |");772if (N->pere == NULL)773printf(" ");774else775printf(" +-");776switch (N->type) {777case UNKN:778printf("Noeud\n");779break;780case MODULE:781printf("Module\n");782break;783case ARTEFACT:784printf("Artefact\n");785break;786case SERIE:787printf("Serie \n");788break;789case PARALLELE:790printf("Parallele \n");791break;792case PREMIER:793printf("Premier \n");794break;795}796797do {798nfils = ffils->pointe;799if (nfils->type == FEUILLE) {800for (i = 0; i < level; i++)801printf(" |");802printf(" +--");803printf("%i\n", 1 + nfils->nom);804}805else {806printnoeud(nfils, level + 1);807}808ffils = ffils->suiv;809}810while (ffils != NULL);811}812813void printarbre(noeud * N)814{815printnoeud(N, 0);816}817818noeud *algo2(graphe G, sommet **S)819{820/* algorithme de decomposition modulaire, deuxieme passe821entree: le graphe G, et sa permutation factorisante S.822sortie: un pointeur sur un arbre de decomposition modulaire823*/824/* debug: S n'est utilise que pour mettre vrainom a jour */825int n; //nombre de sommets du graphe826int *ouvrantes; /* tableau du nombre de parentheses ouvrantes */827/* ouvrante[i]=3 ssi i-1(((i */828/* ouvrante [0]=3: (((0 */829830int *fermantes; /* idem fermantes[i]=2 ssi i)))i+1831fermante [n-1]=2 ssi n))) */832int *ps; /* ps[i]=premier separateur de (i,i+1) */833int *ds;834835int i, j; /*indices de paires ou de sommets */836837sadj *a1, *a2; /* parcours de liste d'adjacence */838839noeud *racine; /*racine du pseudocoardre */840noeud *courant, *nouveau; /* noeud courant du pseudocoarbre */841noeud **pileinterne; /* pile des modules pour les passes 3,5,5 */842int indicepileinterne = 0; /*pointeur dans cette pile */843int taillepileinterne; /* taille de la pile apres la 2eme passe */844845int *adjii; /* adjii[i]=1 ssi S[i] et S[i+1] sont */846/* adjacents */847/*PROPHASE : initialisations */848n=G.n;849ouvrantes = (int *) fabmalloc(n * sizeof(int));850fermantes = (int *) fabmalloc(n * sizeof(int));851ps = (int *) fabmalloc(n * sizeof(int));852ds = (int *) fabmalloc(n * sizeof(int));853pileinterne = (noeud **) fabmalloc((2 * n + 4) * sizeof(noeud *));854adjii= (int *) fabmalloc(n*sizeof(int));855/*pas plus de 2n+4 noeuds internes dans le pseudocoarbre */856for (i = 0; i < n; i++) {857ouvrantes[i] = 0;858fermantes[i] = 0;859adjii[i]=0;860}861862/* remplit adjii qui dit si S[i] adjacent a S[i+1] */863for(i=0; i<n-1; i++)864{865a1=S[i]->adj;866while((a1!=NULL)&&(a1->pointe->place != i+1))867a1=a1->suiv;868if( a1 == NULL)869adjii[i]=0;870else // a1->pointe->place==i+1, donc i adj i+1871adjii[i]=1;872}873adjii[n-1]=0; //perfectionnisme874875/* PREMIERE PASSE876on va parentheser la permutation factorisante.877tout bonnement, on lit S et on cherche les separateurs;878apres quoi ouvrantes et fermantes sont ecrites879complexite: O(n^2) */880881ouvrantes[0] = 1;882fermantes[n - 1] = 1; /* parentheses des bords */883884for (i = 0; i < n - 1; i++) {885/*recherche de ps(i,i+1) */886a1=S[i]->adj;887a2=S[i+1]->adj;888while((a1!=NULL) && (a2!=NULL) && (a1->pointe->place<i) &&889(a2->pointe->place<i) && (a1->pointe->place == a2->pointe->place))890{891a1=a1->suiv;892a2=a2->suiv;893}894895//arbre de decision complique pour trouver le premier separateur!896if( ((a1==NULL) && (a2==NULL))897||((a1==NULL) &&(a2->pointe->place >= i))898||((a2==NULL) && (a1->pointe->place >= i))899||((a1!=NULL) && (a2!=NULL) && (a1->pointe->place >= i) && (a2->pointe->place >= i)))900//pas de separateur901ps[i]=i+1;902else903{904if((a1==NULL) || (a1->pointe->place >= i))905ps[i]=a2->pointe->place;906else if((a2==NULL) || (a2->pointe->place >= i))907ps[i]=a1->pointe->place;908else909{910if((a1->suiv!=NULL)&&(a1->suiv->pointe->place == a2->pointe->place))911ps[i]=a1->pointe->place;912else if ((a2->suiv!=NULL)&&(a2->suiv->pointe->place == a1->pointe->place))913ps[i]=a2->pointe->place;914else915ps[i]=min(a1->pointe->place , a2->pointe->place);916}917ouvrantes[ps[i]]++; /* marque la fracture gauche, if any */918fermantes[i]++;919}920if (DEBUG)921printf("ps(%i,%i)=%i\n", i , i+1, ps[i]);922923/*recherche de ds(i,i+1)924plus penible encore!*/925a1=S[i]->adj;926if(a1!=NULL) // se place en queue de liste.927while(a1->suiv!=NULL)928a1=a1->suiv;929a2=S[i+1]->adj;930if(a2!=NULL)931while(a2->suiv!=NULL)932a2=a2->suiv;933while((a1!=NULL) && (a2!=NULL) && (a1->pointe->place > i+1) &&934(a2->pointe->place > i+1) && (a1->pointe->place == a2->pointe->place))935{936a1=a1->prec;937a2=a2->prec;938}939if( ((a1==NULL) && (a2==NULL))940||((a1==NULL) && (a2->pointe->place <= i+1))941||((a2==NULL) && (a1->pointe->place <= i+1))942||((a1!=NULL) && (a2!=NULL) && (a1->pointe->place <= i+1) && (a2->pointe->place <= i+1)))943//pas de separateur944ds[i]=i+1;945else946{947if((a1==NULL) || (a1->pointe->place <= i+1))948ds[i]=a2->pointe->place;949else if((a2==NULL) || (a2->pointe->place <= i+1))950ds[i]=a1->pointe->place;951else952{953if((a1->prec!=NULL)&&(a1->prec->pointe->place == a2->pointe->place))954ds[i]=a1->pointe->place;955else if((a2->prec!=NULL)&&(a2->prec->pointe->place == a1->pointe->place))956ds[i]=a2->pointe->place;957else958ds[i]=max(a1->pointe->place , a2->pointe->place);959}960961962//ds[i] = j;963ouvrantes[i + 1]++; /* marque la fracture gauche, if any */964fermantes[ds[i]]++; /* attention aux decalages d'indices */965}966if (DEBUG)967printf("ds(%i,%i)=%i\n", i,i+1,ds[i]);968//S[i]->nom + 1, S[i + 1]->nom + 1, S[ds[i]]->nom + 1);969}970971/*DEUXIEME PASSE: construction du pseudocoarbre */972973racine = nouvnoeud(UNKN, NULL, -1, n);974courant = racine;975for (i = 0; i < n; i++) {976/*1: on lit des parentheses ouvrantes: descentes */977for (j = 0; j < ouvrantes[i]; j++) {978/*Descente vers un nouveau noeud */979nouveau = nouvnoeud(UNKN, courant, -1, n);980ajoutfils(courant, nouveau); /*on l'ajoute... */981courant = nouveau; /* et on descent */982if (DEBUG)983printf("(");984}985/* 2: on lit le sommet: feuille */986nouveau = nouvnoeud(FEUILLE, courant, i, n);987ajoutfils(courant, nouveau); /*on ajoute le bebe... */988(*nouveau).ps = i;989(*nouveau).ds = i;990(*nouveau).bg = i;991(*nouveau).bd = i;992nouveau->nom = S[i]->nom;993/* et pourquoi pas i ? Je m'embrouille... */994if (DEBUG)995printf(" %i ", S[i]->nom + 1);996997/*3: on lit des parentheses fermantes: remontees */998for (j = 0; j < fermantes[i]; j++) {999/*ASTUCE: ici on va en profiter pour supprimer les1000noeuds a un fils, afin d'economiser une passe */1001if (courant->fils == courant->lastfils) { /*just one son */1002courant->pere->lastfils->pointe = courant->fils->pointe;1003courant->fils->pointe->pere = courant->pere;1004courant->fils->pointe->fpere = courant->pere->lastfils;1005/* explication: le dernier fils de courant.pere est1006actuellement courant himself. Il est donc pointe par1007courant.pere.lastfils.pointe. Il suffit de changer ce1008pointeur pour qu'il pointe maintenant non plus sur courant,1009mais sur l'unique fils de courant: courant.fils.pointe.1010Ouf! */1011/* NB: courant est maintenant deconnecte de l'arbre.1012on pourrait faire un free() mais bon... */1013}1014else {1015/*on empile ce noeud interne.1016L'ordre est celui de la postvisite d'un DFS */1017pileinterne[indicepileinterne] = courant;1018indicepileinterne++;1019}1020/* et dans tous les cas: on remonte! */1021courant = courant->pere;1022if (DEBUG)1023printf(")");1024}1025}1026if (DEBUG)1027printf("\n");10281029/* on enleve un ptit defaut */1030racine = racine->fils->pointe;1031racine->pere = NULL;1032racine->fpere = NULL;1033if (DEBUG) {1034printf("Arbre apres la deuxieme passe:\n");1035printnoeud(racine, 0);1036}10371038/*TROISIEME PASSE */1039/* A ce stade, on a un pseudocoarbre de racine racine,1040sans noeuds a un fils, et dont les etiquettes sont1041FEUIILLE ou UNKN. Il y a une pile des noeuds UNKN, stockes1042dans l'ordre de la postvisite d'un parcours en profondeur.1043On va s'en servir pour faire remonter les bords et les1044separateurs de bas en haut */10451046taillepileinterne = indicepileinterne;1047for (indicepileinterne = 0; indicepileinterne < taillepileinterne;1048indicepileinterne++) {1049noeud *scanne;1050fils *nextfils;1051noeud *succourant;1052/* scanne est le noeud (pere) que l'on regarde;1053nextfils parcours sa liste de fils;1054courant est le fils actuellement examine et1055succourant=succ(courant) */1056noeud *debutnoeud;1057fils *debutfils;1058/*deux variables utilise pour la recherche de jumeaux:1059debut du bloc max */10601061scanne = pileinterne[indicepileinterne]; /*he oui, la pile */1062nextfils = scanne->fils; /*on commence au premier fils */1063do {1064/*la boucle chiante... cf mon memoire de DEA */1065courant = nextfils->pointe;1066/* bords */1067scanne->bg = min(scanne->bg, courant->bg);1068scanne->bd = max(scanne->bd, courant->bd);1069/*separateurs */1070scanne->ps = min(scanne->ps, courant->ps);1071if (scanne->fils->pointe != courant)1072/*ce n'est pas le premier fils */1073scanne->ps = min(scanne->ps, ps[(courant->bg) - 1]);1074scanne->ds = max(scanne->ds, courant->ds);1075if (scanne->lastfils->pointe != courant)1076/*ce n'est pas le dernier fils */1077scanne->ds = max(scanne->ds, ds[courant->bd]);10781079nextfils = nextfils->suiv;1080}1081while (nextfils != NULL);108210831084if (DEBUG)1085printf("noeud %i-%i: ps=%i ds=%i", 1 + scanne->bg,10861 + scanne->bd, 1 + scanne->ps, 1 + scanne->ds);10871088/* maintenant le test tout simple pour savoir si on a un module: */1089if (((scanne->ps) == (scanne->bg))1090&& ((scanne->ds) == (scanne->bd))) {1091/*on a un module */1092scanne->type = MODULE;1093if (DEBUG)1094printf(" Module.\n");1095}1096else {1097scanne->type = ARTEFACT;1098if (DEBUG)1099printf(" artefact.\n");1100}1101}11021103if (DEBUG) {1104printf("Arbre apres la troisieme passe:\n");1105printnoeud(racine, 0);1106}11071108/* QUATRIEME PASSE */1109/* technique:on se contente de fusionner les artefacts a leurs parents1110ca se fait de bas en haut grace a pileinterne (toujours elle!) */11111112for (indicepileinterne = 0; indicepileinterne < taillepileinterne;1113indicepileinterne++) {1114noeud *scanne;1115scanne = pileinterne[indicepileinterne]; /*he oui, la pile */1116if (scanne->type == ARTEFACT) {1117/*attention! La pile peut contenir des noeuds deconnectes */1118fusionne(scanne->pere, scanne);1119if (DEBUG)1120printf("Artefact elimine: %i-%i\n", 1 + scanne->bg,11211 + scanne->bd);1122}1123}1124if (DEBUG) {1125printf("Arbre apres la quatrieme passe:\n");1126printnoeud(racine, 0);1127}11281129/* CINQIEME ET DERNIERE PASSE */1130/* on va typer les noeuds et extraire les fusions.1131comment on fait? Ben, la pile.... */1132for (indicepileinterne = 0; indicepileinterne < taillepileinterne;1133indicepileinterne++) {1134noeud *scanne;1135fils *nextfils;1136noeud *succourant;1137/* scanne est le noeud (pere) que l'on regarde;1138nextfils parcours sa liste de fils;1139courant est le fils actuellement examine et1140succourant=succ(courant) */1141noeud *debutnoeud;1142fils *debutfils;1143/*deux variables utilise pour la recherche de jumeaux:1144debut du bloc max */11451146scanne = pileinterne[indicepileinterne];1147if (scanne->type != MODULE)1148continue; /* je traite que les modules */11491150nextfils = scanne->fils; /*on commence au premier fils */1151while (1) {1152courant = nextfils->pointe;1153succourant = nextfils->suiv->pointe;1154if (ps[courant->bd] >= courant->bg1155&& ds[courant->bd] <= succourant->bd) {1156/*Des jumeaux!! ->serie ou parallele! */1157/* on va determiner le bloc max de jumeaux consecutifs */1158debutnoeud = courant;1159debutfils = nextfils;1160while (ps[courant->bd] >= courant->bg &&1161ds[courant->bd] <= succourant->bd &&1162nextfils->suiv != NULL) {1163nextfils = nextfils->suiv;1164courant = nextfils->pointe;1165if (nextfils->suiv == NULL)1166break;1167succourant = nextfils->suiv->pointe;1168}1169/*maintenant on connait la taille du bloc: il va de1170debutnoeud a courant inclus,1171et dans la liste des fils de scanne,1172il s'etend de debutfils a nextfils inclus.1173On extrait cette liste pour en faire les fils d'un1174nouveau noeud... sauf si pas la peine! */1175if (debutfils == scanne->fils1176&& nextfils == scanne->lastfils) {1177/* le noeud scanne etait serie ou parallele */1178if ( adjii[debutnoeud->bd] !=0)1179scanne->type = SERIE;1180else1181scanne->type = PARALLELE;1182}1183else {1184if ( adjii[debutnoeud->bd]!=0)1185/*seule cette ligne distingue G de G' !! */1186{1187nouveau = nouvnoeud(SERIE, scanne, -1, n);1188if (DEBUG)1189printf("Module serie extrait: %i-%i\n",11901 + debutnoeud->bg, 1 + courant->bd);1191}1192else {1193nouveau = nouvnoeud(PARALLELE, scanne, -1, n);1194if (DEBUG)1195printf("Module parallele extrait: %i-%i\n",11961 + debutnoeud->bg, 1 + courant->bd);1197}1198extraire(scanne, nouveau, debutfils, nextfils);1199}1200}1201if (scanne->type == MODULE)1202scanne->type = PREMIER;1203if (nextfils->suiv == NULL || nextfils->suiv->suiv == NULL1204|| nextfils->suiv->suiv->suiv == NULL)1205break;1206nextfils = nextfils->suiv;1207}1208}1209if (DEBUG) {1210printf("Arbre final:\n");1211printnoeud(racine, 0);1212}1213return racine;1214}12151216void PrintG(graphe G)1217/* affiche le graphe */1218{1219int i,r; adj *a;1220r=0;1221for(i=0;i<G.n;i++)1222{1223printf("%i : ",i);1224a=G.G[i];1225while(a!=NULL)1226{1227printf("%i ", a->s);1228a=a->suiv;1229}1230printf("\n");1231}1232}1233void PrintGS(sommet **S, int n)1234/* affiche le graphe trie selon S (celui utilise par algo2)*/1235{1236int i; sadj *a;1237for(i=0;i<n;i++)1238{1239printf("%i : ",i);1240a=S[i]->adj;1241while(a!=NULL)1242{1243printf("%i ", a->pointe->place);1244a=a->suiv;1245}1246printf("\n");1247}1248}12491250void PrintS2(sommet **S, int n)1251/* affiche la permutation factorisante */1252{1253int i;1254printf( "Place (nouvelle num) ");1255for(i=0;i<n;i++)1256printf("%3i ",S[i]->place);1257printf("\nNom (ancienne num) : ");1258for(i=0;i<n;i++)1259printf("%3i ",S[i]->nom);1260printf("\n");1261}126212631264/* la fonction principale; qui fait pas grand'chose....*/1265noeud *decomposition_modulaire(graphe G)1266{12671268sommet **S; /* la permutation factorisante */1269noeud *Racine; /* le futur arbre de decomposition */12701271setbuf(stdout,NULL);12721273S = algo1(G); /* premiere partie: calcul1274de la permutation factorisante */12751276TrierTous(S,G.n,Calculm(G));/* Trie les listes d'adjacence selon S1277*/1278if(DEBUG)1279{1280PrintGS(S,G.n);1281PrintS2(S,G.n);1282}1283Racine = algo2(G, S); /* deuxieme partie: calcul de l'arbre */1284return Racine;1285}128612871288128912901291129212931294129512961297