Consignes

Le TP est à rendre, par email, avant le mercredi 21 avril à 20h...

Pour ce TP comme pour les suivants, la pièce la plus importante sera une archive tp1-nom-prenom.tar contenant votre programme. J'insiste sur les points suivants :

  1. l'archive est au format tar (éventuellement au format tar.gz ou tar.bz2),
  2. l'archive contient un répertoire tp1-nom-prenom contenant votre code (elle ne crée rien d'autre dans le répertoire courant),
  3. l'archive ne contient pas de fichier binaire ou exécutable (seulement des fichiers sources),
  4. l'archive contient un fichier Makefile pour compiler votre programme, avec au minimum les cibles all et clean.

Le non-respect des ces consignes risque de me mettre de mauvaise humeur... (Remarque : un fichier Makefile basique mais modulaire est disponible ici.)

Attention, le seul fait que votre programme fonctionne ne suffit pas pour avoir une bonne note. Les points suivants seront pris en compte :

  1. l'architecture de votre programme (code découpé en fonctions etc.),
  2. la lisibilité de votre programme (choix pertinent pour les noms de variables etc.),
  3. la présence de commentaires aux endroits appropriés,
  4. la présence de documentation pour vos fonctions.

Certaines questions appellent à une réponse que vous pouvez mettre en commentaire dans votre fichier principal. Si vous le souhaitez vraiment, vous pouvez aussi m'envoyer un rapport de TP. Le format de ce rapport sera au choix, mais par ordre de préférence :

Attention : les points suivants ne rapportent rien, mais ne pas les respecter pourra retrancher jusqu'à 10 points sur la note finale :

Liens utiles


Préliminaires : la bibliothèque GMP

Les types de données

La bibliothèque GMP est une bibliothèque pour manipuler des nombres (entiers, rationnels ou flottants) avec une précision arbitraire. Les initiales signifient « GNU Multi Precision ». Pour compiler du code utilisant cette bibliothèque avec gcc, il faut ajouter le drapeau de compilation -lgmp. (Il faut bien entendu que la bibliothèque soit installée sur votre machine. Pour Debian ou Ubuntu, il s'agit du paquet libgmp3-dev.)

Nous allons essentiellement nous intéresser aux nombres entiers. Pour GMP, un entier est simplement un tableau de « chiffres ». Les types suivants sont définis par GMP :

En fait, le type mp_limb_t est plus général que juste des « digit », mais cette intuition est suffisante pour les entiers.

Les autres types importants sont :

Les opérations

Je vous renvoie à la documentation, et plus particulièrement à la partie sur les entiers et celle sur les fonctions de formatage.

J'attire votre attention sur le fait que toutes les opérations sur les grands entiers sont en passage par adresse. Ceci vient du fait que les grands entiers sont stockés dans des tableaux, et que l'on veut pouvoir contrôler l'utilisation de la mémoire.

1. Structure d'un grand entier

En regardant le fichier /usr/include/gmp.h (ou /usr/include/gmp-ARCH.h), cherchez les définitions des types

Qu'en pensez-vous ?

Écrivez à la main une fonction de prototype

void initialise_grand_entier (mp_size_t t, mpz_t grand_entier) ;

qui alloue l'espace mémoire demandé pour un grand entier et initialise le grand entier à 0.

Écrivez une petite fonction qui affiche le contenu d'un grand entier, digit par digit. Affichez également les autres informations contenues dans l'entier :

void affiche_grand_entier(mpz_t n) ;

Pensez à tester votre fonction sur des entiers petits, grands, positifs, négatifs, etc. Expliquez ce que vous observez...

Pour faciliter l'affichage des digits, vous pouvez utiliser la fonction gmp_printf qui permet d'afficher des entiers, des digits et autres, tout en suivant les conventions de la fonction printf habituelle. (Voir ici pour les nouveautés par rapport à printf.)

2. Arithmétique

(falcultative, bonus) Programmez une fonction d'addition des grands entiers :

int addition (mpz_t resultat, mpz_t a, mpz_t b);

Cette fonction renverra 0 ou 1 suivant qu'il y a eu un dépassement de capacité, càd si l'on a du réalloué de la mémoire pour stocker le résultat.

Programmez votre propre version de la fonction mpz_powm qui permet de calculer x^n (mod m) de deux manières différentes :

void puissance_naive (mpz_t resultat, mpz_t x, mpz_t n, mpz_t m) ;
void puissance_chinoise (mpz_t resultat, mpz_t x, mpz_t n, mpz_t m) ;

Vous n'avez bien entendu pas le droit d'utiliser la fonction mpz_powm, mais vous pouvez utiliser les fonctions mpz_add, mpz_mul ou mpz_cmp.

Comparez l'efficacité de vos deux fonctions, et comparez la seconde avec la fonction de GMP. Expliquez votre démarche et donnez vos résultats.

Reprogrammez votre propre version de la fonction gcdext qui calcule le pgcd de deux nombres ainsi que les nombres de Bezout associés :

void pgcd_bezout (mpz_t g, mpz_t x, mpz_t y, const mpz_t a, const mpz_t b) ;

N'oubliez pas de tester votre fonction.

3. Interface de calcul modulaire

(Bonus) Programmez une petite interface de calcul modulaire multiprecision que vous appellerez dans votre fonction principale.

Votre interface pourra par exemple vous permettre :