• Aucun résultat trouvé

Générateur séquentiel de tableaux de hauteurs

6.4.1 RÉVISION DU SUFFIXE D’UN TABLEAU DE HAUTEURS

Ce générateur séquentiel, nommé HEIGHT, est constitué de deux fonctions C first_-

heightet next_height. C’est une instanciation du patron de génération par révision du suffixe présenté dans le chapitre 3.

Selon l’ordre lexicographique, le plus petit tableau de hauteurs est (1 0)n, correspondant

au mot de Dyck (a¯a)n. Il est généré par la fonction first_height donnée dans le lis-

ting 6.1.

1 void first_height(int h[], int n) {

2 for (int i = 0; i < 2*n; i++) h[i] = 1-i%2; 3 }

Listing 6.1 – Fonction first_height.

Le processus de révision du suffixe d’un tableau de hauteurs est implémenté par la fonc- tion next_height présentée dans le listing 6.2. Il est constitué de plusieurs étapes. Afin de faciliter la vérification automatique de cette fonction, nous l’avons scindée en plu- sieurs sous-fonctions qui correspondent chacune à une étape élémentaire de la révision du suffixe.

1 int next_height(int h[], int n) { 2 int rev,gnd; 3 rev = revision_point(h,n); 4 if (rev == 0) return 0; 5 h[rev] = h[rev]+2; 6 gnd = descent(h,n,rev); 7 zigzag(h,n,gnd); 8 return 1; 9 }

Listing 6.2 – Fonction next_height.

i= 0 1 2 3 4 5 6 7 8 9 1011 h=1 2 3 2 3 4

w= a a a ¯a a ¯a

(a) Fonction revision_point (étape 1). i= 0 1 2 3 4 5 6 7 8 9 1011 h=1 2 3 2 3 4 w= a a a ¯a a a (b) Etape 3. i= 0 1 2 3 4 5 6 7 8 9 1011 h=1 2 3 2 3 4 3 2 1 0 w= a a a ¯a a a ¯a ¯a ¯a ¯a

(c) Fonction descent (étape 4).

i= 0 1 2 3 4 5 6 7 8 9 1011 h=1 2 3 2 3 4 3 2 1 0 1 0 w= a a a ¯a a a ¯a ¯a ¯a ¯a a ¯a

(d) Fonction zigzag (étape 5).

FIGURE 6.4 – Révision du suffixe du tableau de hauteurs présenté dans la figure 6.3,

avec son chemin de Dyck associé.

Cette fonction détermine d’abord le point de révision du tableau h grâce à un appel de la sous-fonction revision_point. Dans le cas où la recherche du point de révision

échoue, le dernier tableau de hauteurs est atteint et cette fonction retourne 0 (ligne 4). Dans le cas contraire, elle augmente la hauteur correspondante de 2 (ligne 5), révise le suffixe du tableau en appelant successivement les sous-fonctions descent (ligne 6) et zigzag(ligne 7), puis retourne 1.

Détaillons ces différentes étapes à l’aide des chemins de Dyck associés. Ces étapes sont illustrées sur un exemple dans la figure 6.4, à partir du tableau des hauteurs du mot de Dyck w présenté dans la figure 6.3.

1. La recherche du point de révision est implémentée par la sous-fonction

revision_point, présentée dans le listing 6.3.

1 int revision_point(int h[], int n) { 2 int rev = 2*n-2;

3 while (rev ≥ 1 ∧ !(h[rev] == h[rev-1]-1 ∧ h[rev]+2 ≤ 2*n-1-rev)) rev--; 4 return rev;

5 }

Listing 6.3 – Fonction revision_point.

Cette fonction parcourt le tableau de droite à gauche en cherchant l’abscisse rev de la première étape SE rencontrée (condition h[rev] == h[rev-1]-1) qui peut être remplacée par une étape NE (condition h[rev]+2 <= 2*n-1-rev). Cette dernière condition s’assure qu’on garde la possibilité d’atteindre la hauteur 0 avant la fin du chemin. La fonction revision_point retourne ensuite le point de révision rev. Par exemple, le point de révision du tableau des hauteurs du mot de Dyck w présenté dans la figure 6.3 est 5. La figure 6.4(a) donne l’état du chemin de Dyck et du tableau de hauteurs de w à l’issue de l’appel de la fonction revision_point.

2. Si aucun point de révision n’est trouvé, le dernier tableau de hauteurs est atteint et

le processus prend fin (ligne 4 du listing 6.2).

3. Dans le cas contraire, remplacer l’étape SE par une étape NE (ligne 5 du listing

6.2). Cela revient à remplacer une lettre ¯a par une lettre a dans le mot de Dyck associé. La figure 6.4(b) donne l’état du chemin de Dyck et du tableau de hauteurs de w à l’issue de cette action.

4. Placer ensuite autant d’étapes SE que nécessaire pour atteindre la hauteur 0. Cette

étape est implémentée par la sous-fonction descent présentée dans le listing 6.4. Elle inscrit dans le tableau h une suite décroissante de hauteurs jusqu’à 0 à partir du successeur de l’indice de révision rev. Elle retourne ensuite l’indice de "retour au sol" rev+h[rev], pour lequel la hauteur est 0.

1 int descent(int h[], int n, int rev) {

2 for (int k = rev+1; k ≤ rev+h[rev]; k++) h[k] = rev+h[rev]-k; 3 return rev+h[rev];

4 }

Listing 6.4 – Fonction descent.

Cela revient à placer tous les matchings ¯a (s’ils n’étaient pas déjà placés) des lettres a situées dans le préfixe du mot de Dyck associé. La figure 6.4(c) donne l’état du chemin de Dyck et du tableau de hauteurs de w à l’issue de l’appel de la fonction descent.

5. Compléter le chemin par des paires (NE,SE) jusqu’à la fin du chemin. Cette étape

est implémentée par la sous-fonction zigzag, présentée dans le listing 6.5, qui inscrit alternativement 1 puis 0 jusqu’à la fin du tableau h à partir du successeur de l’indice de "retour au sol" gnd.

6.4. GÉNÉRATEUR SÉQUENTIEL DE TABLEAUX DE HAUTEURS 93

1 void zigzag(int h[], int n, int gnd) {

2 for (int i = gnd+1; i < 2*n; i++) h[i] = 1-i%2; 3 }

Listing 6.5 – Fonction zigzag.

Cela revient à placer des paires a¯a jusqu’à la fin du mot de Dyck associé. La figure 6.4(d) donne l’état du chemin de Dyck et du tableau de hauteurs de w à l’issue de l’appel de la fonction zigzag.

6.4.2 SPÉCIFICATION ACSL ET PREUVE

Les fonctions de la partie 6.4.1 peuvent être munies d’une spécification ACSL permettant la preuve automatique des propriétés de correction et de progression du générateur sé- quentiel constitué des deux fonctions first_height et next_height. Les contrats de ces fonctions utilisent les prédicats ACSL présentés dans le listing 6.6.

1 /*@ predicate is_non_neg(int *a, Z b) = ∀ Z i; 0 ≤ i < b ⇒ a[i] ≥ 0;

2 @ predicate is_descent(int *a, Z b, Z c) = ∀ Z i; b+1 ≤ i < c ⇒ a[i] == b+a[b]-i; 3 @ predicate is_zigzag(int *a, Z b, Z c) = ∀ Z i; b ≤ i < c ⇒ a[i] == 1-i%2; 4 @ predicate is_diff_1(int *a, Z b) =

5 @ ∀ Z i; 0 ≤ i < b ⇒ (a[i+1] == a[i]+1 ∨ a[i+1] == a[i]-1); 6 @ predicate is_height(int *a, Z n) =

7 @ a[0] == 1∧ a[n-1] == 0 ∧ is_non_neg(a,n) ∧ is_diff_1(a,n-1); */

Listing 6.6 – Prédicats ACSL pour les tableaux de hauteurs.

La caractérisation d’un tableau de hauteurs donnée dans le théorème 2 est implémentée par le prédicat is_height présenté lignes 6-7. Ce prédicat utilise le prédicat is_non_- neg, déjà utilisé dans le listing 3.1 du chapitre 3 (ligne 1), ainsi que le prédicat is_- diff_1 (lignes 4-5) stipulant que la différence entre deux hauteurs consécutives est toujours 1. Le prédicat is_descent (ligne 2) paraphrase le code de la fonction descent, présentée dans le listing 6.4. De même, le prédicat is_zigzag (ligne 3) paraphrase le code des fonctions zigzag et first_height, présentées respectivement dans les listings 6.5 et 6.1. Ces deux prédicats sont utilisés dans des invariants de boucle de ces fonctions afin de prouver certaines de leurs postconditions.

Le premier tableau de hauteurs est construit par la fonction first_height présentée avec son contrat ACSL dans le listing 6.7.

1 /*@ requires n > 0∧ \valid(h+(0..2*n-1)); 2 @ assigns h[0..2*n-1];

3 @ ensures is_height(h,2*n); */ 4 void first_height(int h[], int n) { 5 /*@ loop invariant 0 ≤ i ≤ 2*n; 6 @ loop invariant is_zigzag(h,0,i); 7 @ loop assigns i, h[0..2*n-1]; 8 @ loop variant 2*n-i; */

9 for (int i = 0; i < 2*n; i++) h[i] = 1-i%2; 10 }

Listing 6.7 – Fonction first_height avec son contrat.

Le contrat ACSL de la fonction next_height est présenté dans le listing 6.8. La précon- dition de ce contrat stipule que le tableau a est de taille positive, est alloué en mémoire, et représente un tableau de hauteurs. La postcondition ligne 3 assure que le tableau a représente un tableau de hauteurs en sortie de fonction. La postcondition ligne 4 assure que si la fonction a modifié le tableau a, celui-ci est supérieur au tableau a en entrée de fonction, selon l’ordre total adopté.

1 /*@ requires n > 0∧ \valid(h+(0..2*n-1))∧ is_height(h,2*n); 2 @ assigns h[0..2*n-1];

3 @ ensures is_height(h,2*n);

4 @ ensures \result == 1⇒ lt_lex{Pre,Post}(h,2*n); */

Listing 6.8 – Contrat de la fonction next_height.

Ce contrat est prouvé grâce aux différents contrats des sous-fonctions constituant la fonction next_height, en suivant le principe de la vérification modulaire, comme pour la vérification par preuve de la fonction next_effi présentée dans le chapitre 5. Les postconditions établies dans le contrat d’une sous-fonction sont alors transmises à la sous-fonction suivante en tant que préconditions, permettant ainsi l’établissement de la postcondition de la fonction next_height par cascade.

La fonction revision_point est munie d’un contrat et d’annotations présentés dans le listing 6.9. Les deux postconditions lignes 4-5 assurent qu’en sortie de fonction le point de révision rev vérifie les conditions de sortie de boucle (ligne 11), s’il est différent de 0. La postcondition ligne 4 assure que nous sommes à une étape NE du chemin de Dyck, et la postcondition ligne 5 assure que le chemin de Dyck garde la possibilité de regagner la hauteur 0 avant la fin du chemin.

1 /*@ requires n > 0∧ \valid(h+(0..2*n-1))∧ is_height(h,2*n); 2 @ assigns \nothing;

3 @ ensures h[0] == 1∧ 0 ≤ \result ≤ 2*n-2 ∧ (\result+h[\result])%2 == 1; 4 @ ensures \result , 0⇒ h[\result] == h[\result-1]-1;

5 @ ensures \result , 0⇒ h[\result]+2 ≤ 2*n-1-\result; */ 6 int revision_point(int h[], int n) {

7 int rev = 2*n-2;

8 /*@ loop invariant 0 ≤ rev ≤ 2*n-2 ∧ (rev+h[rev])%2 == 1; 9 @ loop assigns rev;

10 @ loop variant rev; */

11 while (rev ≥ 1 ∧ !(h[rev] == h[rev-1]-1 ∧ h[rev]+2 ≤ 2*n-1-rev)) rev--; 12 return rev;

13 }

Listing 6.9 – Fonction revision_point avec contrat et annotations ACSL. L’un des points clés de la preuve de correction est d’établir que le point de “retour au sol”, décrit dans la partie 6.3.3, est un nombre impair. La troisième partie de la postcondition (ligne 3), qui assure que la somme du point de révision et de la hauteur du tableau à ce point est impaire, permettra de l’établir. Cette partie de la postcondition est prouvée grâce à l’invariant de boucle ligne 8.

1 /*@ requires n > 0∧ h[0] == 1 ∧ \valid(h+(0..2*n-1))∧ 1 ≤ rev ≤ 2*n-2; 2 @ requires h[rev] == h[rev-1]+1∧ 0 ≤ h[rev] ≤ 2*n-1-rev;

3 @ requires (rev+h[rev])%2 == 1∧ is_diff_1(h,rev) ∧ is_non_neg(h,rev); 4 @ assigns h[rev+1..rev+h[rev]];

5 @ ensures h[0] == 1∧ \result%2 == 1∧ 1 ≤ \result ≤ 2*n-1 ∧ h[\result] == 0; 6 @ ensures is_non_neg(h,\result)∧ is_diff_1(h,\result)∧ \result > rev; */ 7 int descent(int h[], int n, int rev) {

8 /*@ loop invariant rev+1 ≤ k ≤ rev+h[rev]+1 ∧ is_descent(h,rev,k); 9 @ loop assigns k, h[rev+1..rev+h[rev]];

10 @ loop variant rev+h[rev]-k; */

11 for (int k = rev+1; k ≤ rev+h[rev]; k++) h[k] = rev+h[rev]-k; 12 return(rev+h[rev]);

13 }

Listing 6.10 – Fonction descent avec contrat et annotations ACSL.

La fonction descent est présentée dans le listing 6.10 avec son contrat et ses an- notations ACSL. Son contrat permet d’établir que l’indice de "retour au sol" est impair (deuxième partie de la postcondition ligne 5) et qu’à ce point la hauteur est 0 (quatrième