%+-----------------------------------------------------------------------------+
%|                                                                             |
%|                   Entiers de longueur arbitraire                            |
%|                                                                             |
%+-----------------------------------------------------------------------------+

% M. Quercia, 24/08/2001

\documentclass[a4paper]{report}
\input accents.tex
%\usepackage[T1]{fontenc}
\usepackage[francais]{babel}
\usepackage{amssymb}
\def\og{\leavevmode\lower0.2ex\hbox{\lasy\char40\kern-0.5ex\char40}}
\def\fg{\leavevmode\lower0.2ex\hbox{\lasy\char41\kern-0.5ex\char41}}

\def\Numerix{{\tt Numerix}}
\def\Clong{{\tt Clong}}
\def\Dlong{{\tt Dlong}}
\def\Slong{{\tt Slong}}
\def\GMP{{\tt GMP}}
\def\Big{{\tt Big\_int}}

\def\ra{\rightarrow}
\def\la{\leftarrow}

\def\sv{\vskip4mm}

\font\sltt = cmsltt10
{\catcode`(=\active\catcode`/=\active\catcode`*=\active
\gdef\startpar{%
  \def\à{\accent 18 a}
  \def\é{\accent 19 e}
  \def\è{\accent 18 e}
  \def\ra{$\rightarrow$}
  \def\la{$\leftarrow$}
  \def\eq{$=$}
  \def\neq{$\ne$}
  \def\lt{$<$}
  \def\le{$\leqslant$}
  \def\gt{$>$}
  \def\ge{$\geqslant$}
  \catcode`(=\active\catcode`*=\active
  \def({\futurelet\next\suitepar}
  \def*{\futurelet\next\suiteast}
  \def\suitepar{\char40\relax\ifx\next*\bgroup\sltt\fi}
  \def\suiteast{\char42\relax\ifx\next)\egroup\fi}
}%
\gdef\startslash{%
  \def\à{\accent 18 a}
  \def\é{\accent 19 e}
  \def\è{\accent 18 e}
  \def\ra{$\rightarrow$}
  \def\la{$\leftarrow$}
  \def\eq{$=$}
  \def\neq{$\ne$}
  \def\lt{$<$}
  \def\le{$\leqslant$}
  \def\gt{$>$}
  \def\ge{$\geqslant$}
  \catcode`/=\active\catcode`*=\active
  \def/{\futurelet\next\suitepar}
  \def*{\futurelet\next\suiteast}
  \def\suitepar{\char47\relax\ifx\next*\bgroup\sltt\fi}
  \def\suiteast{\char42\relax\ifx\next/\egroup\fi}
}}

\newenvironment{camllisting}
{\bgroup\startpar\catcode`§=0\advance\hsize by 5cm}
{\egroup}
\newenvironment{clisting}
{\bgroup\startslash\catcode`§=0\advance\hsize by 5cm}
{\egroup}
\newenvironment{shelllisting}
{\bgroup\catcode`§=0\advance\hsize by 5cm}
{\egroup}

\title{\Huge\bf Numerix\vspace{1cm}\\Bibliothèque de calcul en grands entiers}
\author{Michel Quercia\\
\tt michel.quercia\char 64 prepas.org\vspace{5mm}\\
}
\date{version 0.19 du 24 août 2001}

\begin{document}
\maketitle
\setcounter{tocdepth}1
\tableofcontents

                         %+----------------+
                         %|  Présentation  |
                         %+----------------+

\chapter{Présentation}

\Numerix\ est une bibliothèque implémentant les nombres entiers
relatifs de longueur arbitraire et les principales opérations
arithmétiques sur ces nombres. Conçue pour être utilisée avec le
langage Objective-Caml, elle est aussi disponible avec des fonctionnalités
réduites pour les langages Camllight, C et Pascal sur des machines de type
Unix 32 ou 64 bits. Elle est fournie sous trois versions~:

\begin{description}
\item\Clong~:\par
écrite en C standard. L'entité de base est le \og\ chiffre \fg\
dont la longueur en bits est la moitié de celle d'un mot machine.
De la sorte les opérations élémentaires produisant des résultats sur
deux chiffres sont implémentées par des opérations ordinaires du C
sur des variables de type {\tt long}, et cette bibliothèque est
à priori portable sur toute architecture binaire pour laquelle la
taille d'un mot est paire et d'au moins 32 bits.

\item\Dlong~:\par
écrite en C aussi, mais un chiffre occupe un
mot machine en entier. Les opérations entre chiffres sont réalisées
avec le type {\tt long long} de {\tt gcc} ce qui limite à l'heure
actuelle cette bibliothèque aux machines 32 bits (sur les machines
64 bits de l'Inria, un {\tt long long} a la même taille qu'un
{\tt long} et on ne dispose pas d'opérations double précision).

\item\Slong~:\par
écrite pour partie en assembleur pour processeurs
Intel x86 et pour partie en C, un chiffre est un mot de 32 bits.
\end{description}

En termes de performances, \Numerix\ se compare
favorablement aux autres bi\-blio\-thè\-ques de calcul multiprécision
couramment disponibles en particulier \Big\ (adaptation pour Camllight/Ocaml
de {\tt BigNum}) et \GMP.
A titre d'exem\-ple voici les temps de calcul des $n$ premières décimales
de~$\pi$ sur un PC-Linux équipé d'un processeur Athlon-550Mhz avec
128Mo de mémoire vive~:
\sv
\begin{center}
\begin{tabular}{|r|r|r|r|r|r|}
\hline
$n$      &\Slong    &\Dlong     &\Clong     &\tt GMP-3 &\Big \\
\hline
$10^4$   &  0.06s   &    0.24s  &    0.33s  &    0.08s &   0.45s\vphantom{$2^{2^{2}}$}\\
$10^5$   &  1.49s   &    6.49s  &    9.05s  &    4.95s &  57.22s\\
$10^6$   & 36.27s   &  148.71s  &  208.61s  &  505.58s &  15867s \\
\hline
\end{tabular}
\end{center}
\sv
Dans tous les cas le même algorithme est utilisé, déduit d'un développement
en série de Ramanujan, seule l'implémentation des grands entiers change.
Les bibliothèques \Slong, \Clong, \Dlong\ et \GMP\ ont été utilisées avec
un programme principal en C, \Big\ avec un programme principal en
Ocaml. L'incidence du programme principal sur le temps d'exécution est
toutefois négligeable pour ce type de programmes où la majeure partie
du temps est passée dans les calculs sur des nombres de plusieurs millions de
bits~; on observe des temps similaires lorsque les cinq bibliothèques
sont pilotées par un programme principal en Ocaml.
\sv
Voici les temps de calcul
sur une station Dec avec un processeur alpha-500 et 256Mo de mémoire vive~:
\sv
\begin{center}
\begin{tabular}{|r|r|r|}
\hline
$n$      &\Clong  &\Big \\
\hline
$10^4$   & 0.15s  & 0.55s\vphantom{$2^{2^{2}}$}\\
$10^5$   & 4.68s  &   60s\\
$10^6$   &  112s  & 8310s\\
\hline
\end{tabular}
\end{center}
\sv

La clé de cette grande vitesse de calcul réside dans l'implémentation
d'algorithmes rapides pour toutes les
opérations, notamment la multiplication (algorithmes de Karatsuba,
et Schönage (FFT)) la division (algorithme de Burnikel-Ziegler),
la racine carrée (algorithme de Zimmermann),
le pgcd (algorithme de Lehmer récursif)
et la conversion en décimal. {\tt GMP-3.1} implémente
des algorithmes similaires pour la multiplication et la division
ce qui lui confère pour ces opérations une vitesse meilleure que celle de
\Numerix\ avec des
opérandes de taille moyenne, moindre avec des opérandes dépassant le
million de bits. Mais les autres opérations sont implémentées par des algorithmes
de complexité quadratique, ce qui explique un temps total d'exécution
plus élevé en général avec \GMP\ qu'avec \Numerix. De plus l'interfaçage
de \GMP\ avec Camllight ou avec Ocaml conduit à une gestion de la mémoire
inefficace pouvant dans certains cas provoquer un ralentissement supplémentaire
voire l'arrêt du programme pour manque de mémoire.
Quant à \Big, toutes les opérations à partir de la multiplication
ont une complexité au moins quadratique, donc elle n'est pas adaptée
aux calculs sur des nombres de grande taille.
\sv
De l'autre côté de
l'échelle, pour des calculs sur des \og\ petits \fg\ nombres ne dépassant pas
300 bits, \Numerix\ affiche également de bonnes performances face à \Big\
pour des raisons de moindre charge imposée au gestionnaire mémoire et
de prise en compte au niveau C ou assembleur des signes et longueurs des
entiers manipulés au lieu de confier cette gestion au code Caml/Ocaml.
\GMP, bien que disposant d'algorithmes optimisés pour des opérandes de petite taille,
se révèle généralement moins rapide que \Numerix\ dans son utilisation par
un programme en Caml ou en Ocaml du fait d'une mauvaise adéquation
entre les gestionnaires mémoire intégrés à \GMP\ d'une part et à Caml/Ocaml
d'autre part.
Le problème de Hamming (trouver le $n$-ème entier naturel n'ayant que 2,3
et 5 comme diviseurs premiers) conduit à effectuer de l'ordre de
$n^{1/3}\ln(n)$
opérations sur des entiers de petite taille, entre 100 et 300 bits.
Voici les temps de calcul avec chacune des bibliothèques interfacées à
Ocaml (sur PC-Linux Athlon-550, 128Mo de RAM)~:
\sv
\begin{center}
\begin{tabular}{|r|r|r|r|r|r|}
\multispan6 \hfil avec Ocaml 2.04\hfil\\
\noalign{\smallskip}
\hline
$n$      & \Slong   & \Dlong    & \Clong    & \tt GMP-3 & \Big \\
\hline
$10^{12}$&   0.17s   &    0.28s &   0.26s    &    2.13s &   0.69s\vphantom{$2^{2^{2}}$}\\
$10^{15}$&   1.51s   &    2.52s &   2.44s    &   20.23s &   6.80s \\
$10^{18}$&  17.06s   &   29.07s &  28.99s    &  233.62s &  79.59s \\
$10^{21}$& 214.33s   &  355.56s & 370.77s    & 2815.57s & 891.14s \\
\hline
\noalign{\medskip}
\multispan6 \hfil avec Ocaml 3.00\hfil\\
\noalign{\smallskip}
\hline
$n$      & \Slong   & \Dlong    & \Clong    & \tt GMP-3 & \Big \\
\hline
$10^{12}$&   0.17s   &    0.27s &   0.26s    &    2.54s &   0.70s\vphantom{$2^{2^{2}}$}\\
$10^{15}$&   1.47s   &    2.61s &   2.57s    &       -- &   6.58s \\
$10^{18}$&  17.05s   &   30.10s &  30.77s    &       -- &  75.52s \\
$10^{21}$& 213.15s   &  368.21s & 386.83s    &       -- & 933.48s \\
\hline
\end{tabular}
\end{center}
\sv
\GMP\ ne passe pas ce test pour $n\geqslant 10^{15}$ avec Ocaml-3.00
et provoque un débordement mémoire.
\sv

Bien que conçue pour être interfacée avec les langages fonctionnels Camllight et Ocaml,
\Numerix\ offre la possibilité d'effectuer les opérations
\og\ sur place \fg, c'est à dire en utilisant la mémoire attribuée à une
variable pour stocker le résultat d'une opération au lieu d'allouer
un nouveau bloc mémoire pour ce résultat. Si le bloc mémoire attribué
à une variable se révèle insuffisant pour contenir le résultat à stocker,
alors un nouveau bloc de taille convenable est alloué à cette variable  et
l'ancien est abandonné aux bons soins du gestionnaire mémoire de
Camllight/Ocaml pour récupération éventuelle. L'accroissement de taille
se fait selon une politique de \og\ doublement des besoins \fg\ (on alloue
un bloc deux fois plus gros que nécessaire) de façon à éviter des
réallocations continuelles. Le calcul sur place avec cette politique
de doublement présente l'avantage de réduire pratiquement à zéro la
charge du gestionnaire mémoire et son influence sur le temps total
d'exécution d'un programme, au prix d'une programmation moins intuitive
et d'une consommation mémoire accrue. Voici les temps de calcul pour
le problème de Hamming en version surplace~:
\sv
\begin{center}
\begin{tabular}{|r|r|r|r|r|r|}
\multispan6 \hfil avec Ocaml 2.04\hfil\\
\noalign{\smallskip}
\hline
$n$      & \Slong   & \Dlong    & \Clong    &\tt GMP-3 & \Big \\
\hline
$10^{12}$&   0.15s  &  0.27s    &   0.26s   &   0.85s   &   0.69s\vphantom{$2^{2^{2}}$}\\
$10^{15}$&   1.33s  &  2.39s    &   2.26s   &   3.24s   &   6.74s \\
$10^{18}$&  15.20s  & 27.35s    &  26.31s   &  23.10s   &  78.77s \\
$10^{21}$& 187.06s  &334.55s    & 330.44s   & 285.82s   & 978.26s \\
\hline
\noalign{\medskip}
\multispan6 \hfil avec Ocaml 3.00\hfil\\
\noalign{\smallskip}
\hline
$n$      & \Slong   & \Dlong    & \Clong    &\tt GMP-3 & \Big \\
\hline
$10^{12}$&   0.17s  &  0.28s    &   0.27s   &   0.94s   &   0.76s\vphantom{$2^{2^{2}}$}\\
$10^{15}$&   1.38s  &  2.58s    &   2.36s   &   3.49s   &   7.51s \\
$10^{18}$&  15.46s  & 29.56s    &  27.30s   &  22.96s   &  87.15s \\
$10^{21}$& 189.25s  &360.63s    & 343.15s   & 284.75s   &1074.41s \\
\hline
\end{tabular}
\end{center}
\sv

La bibliothèque \Big\ est ici désavantagée par ce test car les opérations
sur place n'y sont pas disponibles et elles ont été simulées par
l'usage de références ce qui augmente le temps d'exécution par rapport
à la version fonctionnelle. Par contre, les problèmes de gestion mémoire
entre \GMP\ et Ocaml sont éliminés par cette programmation sur place.



                    %+--------------------------+
                    %|  Utilisation avec Ocaml  |
                    %+--------------------------+

\chapter{Utilisation avec Ocaml}

\section{Interface}

\Numerix\ a été développé et testé avec Ocaml-2.02 puis avec Ocaml-3.00.
Quelques essais avec d'autres versions de Ocaml n'ont pas posé de
problème particulier à l'exception de Ocaml-1.05 pour lequel
l'activation automatique des pretty-printers dans {\tt ocamlnumx} n'a pas
pu être implémentée (mais l'activation manuelle par {\tt install\_printer}
fonctionne). Il est donc probable que \Numerix\ fonctionne correctement
pour toute version de Ocaml comprise au sens large entre 1.05 et 3.02.
\sv

Les cinq bibliothèques \Clong, \Dlong, \Slong, \GMP\ et \Big\ sont déclarées
comme des sous-modules du module \Numerix\ avec la même signature de type
{\tt Int\_type} dans laquelle les types de données grand entier et grand
entier mutable (pour les calculs sur place) sont abstraits. Ceci permet
d'écrire un code utilisateur indépendant d'une implémentation particulière
des grands entiers et de tester ce code avec les diverses implémentations
disponibles sans avoir à recompiler le programme, grâce au système de
modules de Ocaml et à la possibilité de définir des modules paramétrés
(foncteurs). Voici l'interface publique extraite du fichier {\tt numerix.mli}~:
\sv

\halign{\hskip\leftskip\tt val #\hfil&\tt: #\hfil&\it(* # \hfil\it*)\cr
\multispan3\hskip\leftskip\tt module type Int\_type = sig\hfil\cr
\multispan3\hskip\leftskip\tt type t\phantom{ref} \it(* entier abstrait \phantom{mutable} *)\hfil\cr
\multispan3\hskip\leftskip\tt type tref \it(* entier mutable abstrait *)\hfil\cr
\noalign{\sv}
%
\multispan3\hskip\leftskip\it (* a,b,c,... désignent les arguments des fonctions dans cet ordre *)\hfil\cr
\multispan3\hskip\leftskip\it(* opérations unaires et binaires *)\hfil\cr
add          &t $\ra$ t $\ra$ t           &$a + b                $\cr
sub          &t $\ra$ t $\ra$ t           &$a - b                $\cr
mul          &t $\ra$ t $\ra$ t           &$a * b                $\cr
quo          &t $\ra$ t $\ra$ t           &$a$ div $b            $\cr
modulo       &t $\ra$ t $\ra$ t           &$a$ mod $b            $\cr
quomod       &t $\ra$ t $\ra$ t*t         &$(a$ div $b, a$ mod $b)$\cr
abs          &t $\ra$         t           &$|a|                  $\cr
neg          &t $\ra$         t           &$-a                   $\cr
sqr          &t $\ra$         t           &$a^2                  $\cr
sqrt         &t $\ra$         t           &$\lfloor\sqrt a\rfloor$\cr
powmod       &t $\ra$ t $\ra$ t $\ra$ t   &$a^b$ mod $c$, $b\geqslant0$\cr
gcd          &t $\ra$ t $\ra$ t           &pgcd$(a,b)            $\cr
gcd\_ex      &t $\ra$ t $\ra$ t*t*t       &coefficients de Bézout et pgcd\cr
cfrac        &t $\ra$ t $\ra$ t*t*t*t*t   &\omit\it(* entiers réduits, coef. de Bézout et pgcd *)\hidewidth\cr
eq           &t $\ra$ t $\ra$ bool        &$a =  b$ ?             \cr
neq          &t $\ra$ t $\ra$ bool        &$a \ne b$ ?            \cr
inf          &t $\ra$ t $\ra$ bool        &$a <  b$ ?             \cr
infeq        &t $\ra$ t $\ra$ bool        &$a \leqslant b$ ?      \cr
sup          &t $\ra$ t $\ra$ bool        &$a >  b$ ?             \cr
supeq        &t $\ra$ t $\ra$ bool        &$a \geqslant b$ ?      \cr
sgn          &t $\ra$         int         &signe$\rlap{$(a)$}\phantom{(a-b)}$ $(1,-1,0)$\cr
cmp          &t $\ra$ t $\ra$ int         &signe$(a-b)$ $(1,-1,0)$\cr
max          &t $\ra$ t $\ra$ t           &max$(a,b)             $\cr
min          &t $\ra$ t $\ra$ t           &min$(a,b)             $\cr
\noalign{\sv}
%
\multispan3\hskip\leftskip\it(* opérations mixtes *)\hfil\cr
add\_1       &t $\ra$ int $\ra$ t         &$a + b                 $\cr
sub\_1       &t $\ra$ int $\ra$ t         &$a - b                 $\cr
mul\_1       &t $\ra$ int $\ra$ t         &$a * b                 $\cr
quo\_1       &t $\ra$ int $\ra$ t         &$a$ div $b             $\cr
mod\_1       &t $\ra$ int $\ra$ int       &$a$ mod $b             $\cr
quomod\_1    &t $\ra$ int $\ra$ t*int     &$(a$ div $b, a$ mod $b)$\cr
eq\_1        &t $\ra$ int $\ra$ bool      &$a =  b$ ?              \cr
neq\_1       &t $\ra$ int $\ra$ bool      &$a \ne b$ ?             \cr
inf\_1       &t $\ra$ int $\ra$ bool      &$a <  b$ ?              \cr
infeq\_1     &t $\ra$ int $\ra$ bool      &$a \leqslant b$ ?       \cr
sup\_1       &t $\ra$ int $\ra$ bool      &$a >  b$ ?              \cr
supeq\_1     &t $\ra$ int $\ra$ bool      &$a \geqslant b$ ?       \cr
cmp\_1       &t $\ra$ int $\ra$ int       &signe$(a-b)$ $(1,-1,0)$ \cr
pow          &t $\ra$ int $\ra$ t         &$a ^ b                 $\cr
root         &t $\ra$ int $\ra$ t         &$\lfloor\root b \of a\rfloor$\cr
fact         &int $\ra$ t                 &$a!                    $\cr
random\_bits &int $\ra$ t                 &nombre pseudo-aléatoire \cr
\noalign{\sv}
%
\multispan3\hskip\leftskip\it(* accès à la représentation binaire *)\hfil\cr
nbits        &t $\ra$ int                 &taille en bits de $|a|$ \cr
lowbits      &t $\ra$ int                 &bits $0..30$ de $|a|$   \cr
highbits     &t $\ra$ int                 &bits $n-31..n-1$, $n=$nbits$(a)$\cr
nth\_bit     &t $\ra$ int $\ra$ bool      &bit $b$ de $|a|$        \cr
nth\_word    &t $\ra$ int $\ra$ int       &bits $16b..16b+15$ de $|a|$\cr
shr          &t $\ra$ int $\ra$ t         &sgn$(a)*(|a|$ div $2^b)$\cr
shl          &t $\ra$ int $\ra$ t         &$a * 2^b$               \cr
split        &t $\ra$ int $\ra$ t*t       &sgn$(a)*(|a|$ div $2^b, |a|$ mod $2^b)$\cr
join         &t $\ra$ t $\ra$ int $\ra$ t &$a + b*2^c$             \cr
\noalign{\sv}
%
\multispan3\hskip\leftskip\it(* fonctions de conversion *)\hfil\cr
of\_int      &int    $\ra$ t       \cr
of\_string   &string $\ra$ t       \cr
int\_of      &t      $\ra$ int     \cr
string\_of   &t      $\ra$ string  \cr
\noalign{\sv}
%
\multispan3\hskip\leftskip\it(* opérations sur place *)\hfil\cr
add\_in      &tref $\ra$ t $\ra$ t $\ra$ unit    &$a \la b + c                 $\cr
sub\_in      &tref $\ra$ t $\ra$ t $\ra$ unit    &$a \la b - c                 $\cr
mul\_in      &tref $\ra$ t $\ra$ t $\ra$ unit    &$a \la b * c                 $\cr
quo\_in      &tref $\ra$ t $\ra$ t $\ra$ unit    &$a \la b$ div $c             $\cr
quomod\_in   &tref $\ra$ tref $\ra$ t $\ra$ t $\ra$ unit &division et reste     \cr
abs\_in      &tref $\ra$ t $\ra$ unit            &$a \la |b|                   $\cr
neg\_in      &tref $\ra$ t $\ra$ unit            &$a \la -b                    $\cr
sqr\_in      &tref $\ra$ t $\ra$ unit            &$a \la b^2                   $\cr
sqrt\_in     &tref $\ra$ t $\ra$ unit            &$a \la \lfloor\sqrt b\rfloor $\cr
powmod\_in   &tref $\ra$ t $\ra$ t $\ra$ t $\ra$ unit &$a \la b^c \bmod d$, $c\geqslant0$\cr
add\_1\_in   &tref $\ra$ t $\ra$ int $\ra$ unit  &$a \la b + c                 $\cr
sub\_1\_in   &tref $\ra$ t $\ra$ int $\ra$ unit  &$a \la b - c                 $\cr
mul\_1\_in   &tref $\ra$ t $\ra$ int $\ra$ unit  &$a \la b * c                 $\cr
quomod\_1\_in&tref $\ra$ t $\ra$ int $\ra$ int   &$a \la b$ div $c$, retourne $b$ mod $c$\cr
pow\_in      &tref $\ra$ t $\ra$ int $\ra$ unit  &$a \la b ^ c                 $\cr
root\_in     &tref $\ra$ t $\ra$ int $\ra$ unit  &$a \la \lfloor\root c \of b\rfloor$\cr
fact\_in     &tref $\ra$ int $\ra$ unit          &$a \la b!                    $\cr
shl\_in      &tref $\ra$ t $\ra$ int $\ra$ unit  &$a \la b$ shl $c             $\cr
shr\_in      &tref $\ra$ t $\ra$ int $\ra$ unit  &$a \la b$ shr $c             $\cr
split\_in    &tref$\ra$tref$\ra$t$\ra$int$\ra$unit &$(a,b) \la c$ split $d$     \cr
join\_in     &tref $\ra$ t $\ra$ t $\ra$ int $\ra$ unit&$a \la b + c*2^d$       \cr
gcd\_in      &tref $\ra$ t $\ra$ t $\ra$ unit    &$a \la$ pgcd$(b,c)$           \cr
gcd\_ex\_in  &tref$\ra$tref$\ra$tref$\ra$t$\ra$t$\ra$unit &coef. de Bézout et pgcd\cr
cfrac\_in    &tref $\ra$ tref $\ra$ tref $\ra$ tref $\ra$ tref $\ra$ t $\ra$ t $\ra$ unit\hidewidth\cr
\multispan3\hskip\leftskip\hfil\it(* entiers réduits, coef. de Bézout et pgcd *)\cr
\noalign{\sv}
%
\multispan3\hskip\leftskip\it(* gestion des références *)\hfil\cr
make\_ref    &t    $\ra$ tref                    &ref(copy $a$)\cr
copy\_in     &tref $\ra$ t $\ra$ unit            &$a \la b$    \cr
copy\_out    &tref $\ra$ t                       &copy($!a$)   \cr
look         &tref $\ra$ t                       &$!a$         \cr
\noalign{\sv}
%
\multispan3\hskip\leftskip\it(* divers *)\hfil\cr
name            &unit -> string   &nom du module\cr
\multispan2\hskip\leftskip\tt val toplevel\_print : t $\ra$ unit  &affichage avec {\tt Format}\cr
\noalign{\sv}
\multispan3\hskip\leftskip\tt end \it(* module type Int\_type *)\hfil\cr
\noalign{\sv}
%
%
\multispan3\hskip\leftskip\it\hfil(* notation infixe *)\hfil\cr
\noalign{\sv}
\multispan3\hskip\leftskip\tt module Infixes(E : Int\_type) : sig\hfil\cr
\omit\hskip\leftskip\tt  open E\cr
\noalign{\sv}
(  +  )  &   t $\ra$ t   $\ra$ t      & add      \cr
(  -  )  &   t $\ra$ t   $\ra$ t      & sub      \cr
(  *  )  &   t $\ra$ t   $\ra$ t      & mul      \cr
(  /  )  &   t $\ra$ t   $\ra$ t      & quo      \cr
(  \% )  &   t $\ra$ t   $\ra$ t      & modulo   \cr
( /\% )  &   t $\ra$ t   $\ra$ t*t    & quomod   \cr
( <{}< ) &   t $\ra$ int $\ra$ t      & shl      \cr
( >{}> ) &   t $\ra$ int $\ra$ t      & shr      \cr
(  ** )  &   t $\ra$ int $\ra$ t      & pow      \cr
\noalign{\sv}
(  +. )  &   t $\ra$ int $\ra$ t      & add\_1   \cr
(  -. )  &   t $\ra$ int $\ra$ t      & sub\_1   \cr
(  *. )  &   t $\ra$ int $\ra$ t      & mul\_1   \cr
(  /. )  &   t $\ra$ int $\ra$ t      & quo\_1   \cr
(  \%.)  &   t $\ra$ int $\ra$ int    & mod\_1   \cr
( /\%.)  &   t $\ra$ int $\ra$ t*int  & quomod\_1\cr
\noalign{\sv}
(  +! )  & int $\ra$ int $\ra$ int    & add      \cr
(  -! )  & int $\ra$ int $\ra$ int    & sub      \cr
(  *! )  & int $\ra$ int $\ra$ int    & mul      \cr
(  /! )  & int $\ra$ int $\ra$ int    & quo      \cr
(  \%!)  & int $\ra$ int $\ra$ int    & mod      \cr
\noalign{\sv}
(  =  )  &   t $\ra$ t   $\ra$ bool   & eq    (Ocaml $<$ 3.00)   \cr
( <>  )  &   t $\ra$ t   $\ra$ bool   & neq   (Ocaml $<$ 3.00)   \cr
(  <  )  &   t $\ra$ t   $\ra$ bool   & inf   (Ocaml $<$ 3.00)   \cr
( <=  )  &   t $\ra$ t   $\ra$ bool   & infeq (Ocaml $<$ 3.00)   \cr
(  >  )  &   t $\ra$ t   $\ra$ bool   & sup   (Ocaml $<$ 3.00)   \cr
( >=  )  &   t $\ra$ t   $\ra$ bool   & supeq (Ocaml $<$ 3.00)   \cr
\noalign{\sv}
(  =. )  &   t $\ra$ int $\ra$ bool   & eq\_1    \cr
( <>. )  &   t $\ra$ int $\ra$ bool   & neq\_1   \cr
(  <. )  &   t $\ra$ int $\ra$ bool   & inf\_1   \cr
( <=. )  &   t $\ra$ int $\ra$ bool   & infeq\_1 \cr
(  >. )  &   t $\ra$ int $\ra$ bool   & sup\_1   \cr
( >=. )  &   t $\ra$ int $\ra$ bool   & supeq\_1 \cr
\noalign{\sv}
(  =! )  & 'a  $\ra$ 'a  $\ra$ bool   & eq       \cr
( <>! )  & 'a  $\ra$ 'a  $\ra$ bool   & neq      \cr
(  <! )  & 'a  $\ra$ 'a  $\ra$ bool   & inf      \cr
( <=! )  & 'a  $\ra$ 'a  $\ra$ bool   & infeq    \cr
(  >! )  & 'a  $\ra$ 'a  $\ra$ bool   & sup      \cr
( >=! )  & 'a  $\ra$ 'a  $\ra$ bool   & supeq    \cr
\noalign{\sv}
( \~\ )    & tref $\ra$ t             & look (Ocaml $<$ 3.00)    \cr
( \~{}\~\ )& tref $\ra$ t             & look     \cr
\noalign{\sv}
\multispan3\hskip\leftskip\tt end \it(* foncteur Infixes *)\hfil\cr
%
%
\multispan3\hskip\leftskip\it\hfil(* modules disponibles *)\hfil\cr
\noalign{\sv}
\omit\hskip\leftskip\tt module Big   &Int\_type        &bigints de Ocaml\cr
\omit\hskip\leftskip\tt module Slong &Int\_type        &longints assembleur\cr
\omit\hskip\leftskip\tt module Clong &Int\_type        &longints C\cr
\omit\hskip\leftskip\tt module Dlong &Int\_type        &longints C double précision\cr
\omit\hskip\leftskip\tt module Gmp   &Int\_type        &bigints de GMP\cr
%
\noalign{\sv}
\multispan3\hskip\leftskip\it\hfil(* chronométrage et sélection d'un module à l'exécution *)\hfil\cr
\noalign{\sv}
\multispan3\hskip\leftskip\tt   external chrono : string $\ra$ unit = "chrono"\hfil\cr
\noalign{\sv}
\multispan3\hskip\leftskip\tt   module type Main\_type = sig\hfil\cr
\multispan3\hskip\leftskip\tt   \quad  val main : string list $\ra$ unit\hfil\cr
\multispan3\hskip\leftskip\tt   end\hfil\cr
\multispan3\hskip\leftskip\tt   module Start(Main : functor(E : Int\_type) $\ra$ Main\_type) : sig\hfil\cr
\multispan3\hskip\leftskip\tt   \quad  val start : unit $\ra$ unit\hfil\cr
\multispan3\hskip\leftskip\tt   end\hfil\cr
}
\sv

                         %+-----------------+
                         %|  Détails Ocaml  |
                         %+-----------------+

\section{Précisions}

Les modules {\tt Big}, {\tt Slong}, {\tt Clong}, {\tt Dlong} ou {\tt Gmp}
ne sont disponibles que sur les machines ayant une architecture matérielle
permettant leur compilation et dans le cas de {\tt Big} et {\tt Gmp} si les bibliothèques
{\tt BigNum} et {\GMP} sont installées.
En règle générale les différents modules de type {\tt Int\_type} sont
interchangeables c'est à dire qu'une opération donnée avec des
arguments valides retourne un
résultat sémantiquement indépendant du module utilisé et de l'architecture
matérielle. Seules les opérations {\tt gcd\_ex}, {\tt gcd\_ex\_in},
{\tt cfrac}, {\tt cfrac\_in}, {\tt look} et les fonctions de
hachage, sérialisation et désérialisation échappent à cette règle (voir ci-après).
Par contre les exceptions déclenchées en cas d'argument incorrect
peuvent différer d'un module à l'autre.
\sv

%-------------------------------------------------------- Division
\subsubsection{Division}
Les opérations de division {\tt quo}, {\tt quo\_1}, {\tt modulo}, {\tt mod\_1},
{\tt quomod} et {\tt quomod\_1}
et leurs équivalents sur place se conforment à la convention suivante~:

\begin{description}

\item{\tt quomod} $a$ $b$ et {\tt quomod\_1} $a$ $b$
retournent le couple $(q,r)$ tel que
$a = bq+r$ avec $0\leqslant |r| < |b|$, $r$ ayant même signe que $b$.
$b$ ne doit pas être nul.

\item{\tt quo} $a$ $b$ et {\tt quo\_1} $a$ $b$ retournent
seulement $q$.

\item{\tt modulo} $a$ $b$ et {\tt mod\_1} $a$ $b$ retournent seulement $r$.
\end{description}
\sv

%------------------------------------------------------------ PGCD
\subsubsection{Pgcd}
Les opérations relatives au calcul du plus petit diviseur commun sont
{\tt gcd}, {\tt gcd\_ex}, {\tt cfrac} et leurs équivalents sur place.
\begin{description}

\item{\tt cfrac} $a$ $b$ retourne un quintuplet $(p,q,u,v,d)$ tel que~:
\begin{enumerate}
\item$d$ est le pgcd positif de $a$ et $b$~;
\item$ua-vb = d$~;
\item$pu - qv = 1$~;
\item$pa = qb$ est le ppcm de $a$ et $b$ ayant même signe que $ab$.
\end{enumerate}
Ces conditions assurent l'unicité de $p,q,d$ lorsque $a$ ou $b$ n'est pas
nul, mais les coefficients $u$ et $v$ ne sont pas uniques et peuvent différer
d'un module à l'autre. Assurer leur unicité aurait été possible
connaissant $p$ et $q$ mais cela aurait conduit à effectuer des divisions
supplémentaires potentiellement coûteuses en temps de calcul.

\item{\tt gcd\_ex} $a$ $b$ retourne le triplet $(u,v,d)$.
\item{\tt gcd} $a$ $b$ retourne $d$.

\end{description}
\sv

%--------------------------------------------------------------- cmp,sgn
\subsubsection{Comparaisons}
Les fonctions {\tt sgn}, {\tt cmp} et {\tt cmp\_1} retournent exclusivement
l'un des entiers $0$, $1$ ou $-1$ donc il est possible d'effectuer
un filtrage sur le résultat de l'une de ces fonctions.
\sv

Les symboles de comparaison infixe
({\tt <}, {\tt <=}, {\tt =}, {\tt <>}, {\tt >=} et {\tt >})
ont un statut différent selon la version de Ocaml utilisée et selon
que l'on a ouvert un module construit par le foncteur {\tt Infixes} ou non.
\sv
\begin{itemize}
\item
Pour les versions de Ocaml antérieures à 3.00, si l'on n'a pas ouvert
de module {\tt Infixes(E)}, les symboles {\tt <},\dots,{\tt >}
désignent les opérateurs de comparaison polymorphes traditionnels de Ocaml,
de type \verb?'a->'a->bool?. Ces opérateurs déclenchent une exception~:
\sv
\verb?Invalid_argument("equal: abstract value")?
\sv\noindent s'ils sont appliqués à
des grands entiers.

\sv\item
Pour les versions de Ocaml antérieures à 3.00, si l'on a ouvert
un module {\tt Infixes(E)}, les symboles {\tt <},\dots,{\tt >}
désignent les opérateurs de comparaison entre grands entiers,
de type \verb?E.t->E.t?. Les opérateurs de comparaison polymorphes
restent disponibles via les symboles {\tt <!},\dots,{\tt >!}.

\sv\item
A partir de Ocaml 3.00, les symboles {\tt <},\dots,{\tt >} désignent
les opérateurs de comparaison polymorphes traditionnels de Ocaml,
que l'on ait ouvert un module {\tt Infixes(E)} ou non, ils ne sont plus
redéfinis par le foncteur {\tt Infixes}. Les modules {\tt Clong}, {\tt Dlong},
{\tt Slong} et {\tt Gmp} comportent une interface avec la fonction
de comparaison générique de Ocaml, et les opérateurs de comparaison
polymorphes de Ocaml peuvent être utilisés avec les grands entiers de
ces modules sans déclencher d'exception. De plus, les grands entiers de
ces modules peuvent être stockés dans des structures ordonnées
telles que les ensembles du module {\tt Set} ou les tables d'association
du module {\tt Map} en utilisant la fonction de comparaison générique
{\tt compare}.

\sv
Par contre la bibliothèque {\tt Bignum} ne comporte pas d'interface
avec la fonction de comparaison générique, et de ce fait les fonctions
de comparaison polymorphes de Ocaml ne peuvent pas être utilisées
avec des données de type {\tt Big.t}. Si l'on souhaite pouvoir comparer
de telles données avec les symboles infixes {\tt <},\dots,{\tt >},
il faut redéfinir ces symboles soi-même, ou modifier la bibliothèque
{\tt Bignum} pour y inclure une interface à la fonction de comparaison
générique de Ocaml. Une telle modification est décrite au chapitre
{\bf Installation}, section {\bf Modification de la
bibliothèque Bignum}, page~\pageref{modif-bignum}.
\sv
Enfin, les symboles {\tt <!},\dots,{\tt >!} restent liés aux opérateurs
de comparaison polymorphes par le foncteur {\tt Infixes}, pour préserver la
compatibilité avec les versions antérieures de \Numerix.

\end{itemize}
\sv

%-------------------------------------------------------------- repr. binaire
\subsubsection{Représentation binaire}
Les fonctions d'accès à la représentation binaire des grands entiers
se confor\-ment aux conventions suivantes~:

\begin{description}

\item{\tt nbits} $a$ retourne le nombre de bits significatifs de $|a|$~;
{\tt nbits} $0 = 0$.

\item{\tt lowbits} $a$ et {\tt highbits} $a$ retournent les $31$ bits de
poids faible ou fort de $|a|$ si $a$ est non nul et retournent $0$ si
$a=0$. Pour {\tt highbits}, lorsque $0 < |a| < 2^{30}$, des bits nuls
sont ajoutés à droite pour atteindre un compte de $31$
bits. Mathématiquement, pour $a$ non nul et $n$ entier tel que
$2^{n-1}\leqslant |a| < 2^n$ on~a~:
\sv
\indent{\tt highbits}$(a) = (|a|*2^{\max(0,31-n)}) \hbox{ div } 2^{\max(n-31,0)}$.
\sv

\item{\tt nth\_bit} $a$ $n$ et {\tt nth\_word} $a$ $n$ retournent le $n$-ème
bit ou le $n$-ème mot de $16$ bits de $|a|$, le bit ou mot de poids faible
ayant l'indice zéro. Si $n$ est négatif ou supérieur à la longueur de~$|a|$
ces fonctions retournent $0$.

\item{\tt shl} $a$ $n$ retourne $a*2^n$ si $n\geqslant 0$ et
{\tt sgn}$(a)*(|a|$ div $2^{-n})$
si $n < 0$. {\tt shr} $a$ $n$ est équivalent à {\tt shl} $a$ $(-n)$.

\item{\tt split} $a$ $n$ découpe $|a|$ en deux parties et attache le signe de $a$
à chacune de ces parties. $n$ doit être positif ou nul.

\item{\tt join} $a$ $b$ $n$ retourne le nombre $a + b*2^n$ y compris lorsque
$a$ et $b$ ont des signes différents ou se recouvrent.
$n$ doit être positif ou nul.
\end{description}
\sv

%-------------------------------------------------------------------- nb aléatoires
\subsubsection{Entiers pseudo-aléatoires}
{\tt random\_bits} $n$ retourne un entier pseudo-aléatoire
compris entre $0$ et ${2^n-1}$ inclus, $n$ doit être positif ou nul.
Le générateur pseudo-aléatoire utilisé est celui de Ocaml, donc les valeurs
retournées par {\tt random\_bits} sont identiques d'une exécution à l'autre
d'un même programme ne modifiant pas la graine du générateur, et identiques
pour tous les modules de grands entiers disponibles dans \Numerix.

%-------------------------------------------------------------------- références
\subsubsection{Entiers mutables}
Les valeurs de type {\tt tref} se comportent sémantiquement comme des
ré\-fé\-ren\-ces à des valeurs de type {\tt t}, mais leur implémentation
peut éventuellement être plus efficace et reste abstraite.

\begin{description}
\item {\it op\tt\_in $r$ \it args} calcule {\it op args} et range
le résultat dans la mémoire référencée par~$r$. Si ce résultat est plus grand
que la capacité de cette mémoire un nouveau bloc mémoire est alloué pour
contenir le résultat et $r$ est mis à jour pour référencer ce nouveau bloc.
Pour les opérations retournant des $n$-uplets~: {\tt quomod}, {\tt gcd\_ex},
{\tt cfrac} et {\tt split} l'équivalent sur place range les composantes
du $n$-uplet dans les mémoires dont les références sont transmises en
argument. Ces références doivent désigner des emplacements mémoire
distincts.

\item{\tt make\_ref} $v$ effectue une copie physique de $v$ et retourne
une référence à cette copie.

\item{\tt copy\_in} $r$ $v$ effectue une copie physique de $v$ dans
la mémoire référencée par~$r$.

\item{\tt copy\_out} $r$ retourne une copie physique de la mémoire
référencée par~$r$.

\item{\tt look} $r$ retourne la valeur référencée par $r$. Le résultat
est volatile, c'est-à-dire que la valeur retournée n'est garantie valide
que jusqu'à la prochaine opération sur place impliquant $r$.
Les opérations de type lecture-modification-écriture, par exemple
{\it op\tt\_in} $r$ $({\tt look}\ r)$ $x$, sont traitées correctement.
Le foncteur {\tt Infixes} donne deux abréviations pour l'opération
{\tt look} : {\tt\~{}} valide pour toutes les versions de Ocaml inférieures
ou égales à~2.99 et {\tt\~{}\~{}} valide pour toutes les versions de Ocaml inférieures
ou égales à~3.00 ({\tt \~{}} est devenu un identificateur réservé dans
Ocaml 3.00).
\end{description}
\sv

%--------------------------------------------------------------- Chrono
\subsubsection{Chronométrage}
{\tt chrono \it msg} affiche sur le canal d'erreur
standard le temps CPU en secondes depuis le début du processus, la
différence avec le temps précédent et la chaîne {\it msg}.
L'insertion de quelques appels à {\tt chrono} dans un programme
permet de connaître approximativement les temps d'exécution des
différentes phases de ce programme.
\sv

%--------------------------------------------------------------- Start
\subsubsection{Sélection d'un module à l'exécution}
\label{selmodule}
Le foncteur {\tt Start} permet de sélectionner à l'exécution une
implémentation particulière des grands entiers et d'exécuter le
programme avec cette im\-plé\-men\-ta\-tion. L'argument de {\tt Start} est
un foncteur {\tt Main} prenant en argument une implémentation des
grands entiers et fournissant une implémentation de la fonction
{\tt main : string list $\ra$ unit} qui constitue le point d'entrée
du programme.
L'argument de {\tt main} est la liste des paramètres figurant
sur la ligne de commande à l'exception de ceux désignant l'implémentation
des grands entiers à utiliser. Le choix d'une implémentation
particulière est effectué par la fonction {\tt start} qui analyse
la ligne de commande à la recherche d'une option {\tt -e \it module}
où {\it module} est le nom en minuscules
d'un module grands entiers disponible,
Si plusieurs options {\tt -e \it module} figurent sur la
ligne de commande seule la dernière est retenue~; si aucune option
{\tt -e \it module} n'est présente alors le premier module disponible est
sélectionné. Si une option {\tt -e} n'est pas suivie d'un nom de module reconnu
alors un message d'erreur est affiché rappelant la liste des modules
disponibles et le programme est arrêté.
\sv

%--------------------------------------------------------------- hash, serialisation
\subsubsection{Hachage, sérialisation et désérialisation}
A partir de la version 3.00 de Ocaml les modules
{\tt Clong}, {\tt Dlong}, {\tt Slong} et {\tt Gmp} comportent
des interfaces avec la fonction de hachage générique et les fonctions
de sérialisation et de désérialisation de Ocaml, donc les grands entiers de ces modules
peuvent être inclus dans des tables de hachage utilisant la fonction
{\tt Hashtbl.hash}, et peuvent être exportés ou importés
via les fonctions {\tt output\_value} et {\tt input\_value}.
La bibliothèque {\tt Bignum} comporte des interfaces avec les
fonctions de sérialisation et de désérialisation de Ocaml, mais pas d'interface
avec la fonction de hachage générique, donc les données de type
{\tt Big.t} peuvent être exportées ou importées avec les fonctions
{\tt output\_value} et {\tt input\_value}, mais elles ne peuvent pas être
incluses dans des tables de hachage utilisant la fonction de hachage
générique {\tt Hashtbl.hash}. Il est possible de modifier la bibliothèque
{\tt Bignum} de façon à y incorporer une interface avec la fonction de hachage
générique, voir le chapitre {\bf Installation}, section {\bf Modification de la
bibliothèque Bignum}, page~\pageref{modif-bignum} à ce sujet.
\sv
En ce qui concerne la fonction de hachage générique, {\tt Hashtbl.hash},
les clés de hachage retournées par cette fonction peuvent différer d'un module à l'autre
pour des grands entiers sémantiquement égaux,
du fait de représentations binaires différentes.
\sv
En ce qui concerne l'exportation et l'importation de grands entiers
(fonctions {\tt output\_value} et {\tt input\_value}) et l'encodage
des grands entiers par des suites d'octets (fonctions du module {\tt Marshal}),
le typage doit être préservé entre l'exportation ou l'encodage et
l'importation ou le décodage, c'est-à-dire qu'il n'est pas possible de transformer
un grand entier d'un module en grand entier d'un autre module avec ces
fonctions.


                           %+--------------------------+
                           %|  Modules supplémentaires |
                           %+--------------------------+

\section{Modules supplémentaires}

\Numerix\ comporte deux modules permettant de créer de nouveaux modules
de type {\tt Int\_type} à partir de modules existant~: {\tt Numcmp} et {\tt Numcount}.

%------------------------------------------------------------ Cmp
\subsection{{\tt Numcmp} : comparaison entre deux modules}
Le module {\tt Numcmp} contient deux foncteurs~:

\begin{verbatim}
module M(A:Int_type)(B:Int_type) : Int_type

module Start(Main : functor(E:Int_type) -> Main_type) : sig
  val start : unit -> unit
end
\end{verbatim}

{\tt Numcmp.M} prend en argument deux modules $A$ et $B$ de type
{\tt Int\_type} et retourne un module $C$ de même type dans lequel
chaque opération {\it op\/} est réalisée par appels à $A$.{\it op\/}
et  $B$.{\it op\/} puis comparaison sémantique des résultats obtenus.
Lorsqu'une comparaison échoue, c'est à dire lorsque $A$.{\it op\/}
et $B$.{\it op\/} retournent des résultats sémantiquement différents
pour des arguments supposés sémantiquement identiques, une exception
est générée et les arguments et résultats dans chaque module sont affichés
sur le canal d'erreur standard. Ce foncteur a été utilisé pour déboguer les
modules en cours de développement par comparaison avec un module fiable,
son usage en dehors de cette situation est déconseillé car la duplication
des calculs et la comparaison des résultats consomment un temps important.
Pour les opérations {\tt gcd\_ex}, {\tt gcd\_ex\_in}, {\tt cfrac} et
{\tt cfrac\_in} les coef\-ficients de Bézout ne sont pas comparés, ceux
produits par $B$ sont convertis en valeurs de type $A.{\tt t}$ pour
former des résultats de type  $C.{\tt t}$.
\sv

{\tt Numcmp.Start} est un foncteur analogue à {\tt Numerix.Start}
permettant de sélectionner à l'exécution par une option
{\tt -e \it module1\_module2} quels sont les modules de grands entiers
à comparer. Les couples de modules disponibles sont les suivants~:
\sv
{\tt big\_clong}, {\tt big\_dlong}, {\tt big\_slong}, {\tt big\_gmp}

{\tt gmp\_clong}, {\tt gmp\_dlong}, {\tt gmp\_slong}.
\sv
Il n'y a pas de module par défaut. En cas d'option {\tt -e} incorrecte
le programme est arrêté et la liste des noms reconnus est affichée sur
le canal d'erreur.

%--------------------------------------------------------------- Count
\subsection{{\tt Numcount} : comptage des opérations}
Le module {\tt Numcount} contient deux foncteurs~:

\begin{camllisting}
\begin{verbatim}
module M(A:Int_type) : sig

  type statelt = {
    mutable n:float;  (* nombre d'appels   *)
    mutable s:float;  (* somme des tailles *)
    mutable m:int     (* taille maximale   *)
  }

  val cadd  : statelt (* add      sub                              *)
  val cmul  : statelt (* mul      sqr                              *)
  val cquo  : statelt (* quo      modulo       quomod              *)
  val cpow  : statelt (* pow      powmod       fact                *)
  val croot : statelt (* sqrt     root                             *)
  val cgcd  : statelt (* gcd      gcd_ex       cfrac               *)
  val cbin  : statelt (* shr      shl          split       join    *)
                      (* nbits    lowbits      highbits    nth_bit *)
                      (* nth_word random_bits                      *)
  val cmisc : statelt (* abs      neg          make_ref    copy_in *)
                      (* copy_out comparaisons conversions         *)

  val clear_stats : unit -> unit (* remise §à z§éro                       *)
  val print_stats : unit -> unit (* affiche les statistiques sur stderr *)

  module Int : Int_type (* module de grands entiers *)
end

module Start(Main : functor(E:Int_type) -> Main_type) : sig
  val start : unit -> unit
end
\end{verbatim}
\end{camllisting}

Le foncteur {\tt Numcount.M} prend en argument un module $A$ de type
{\tt Int\_type} et retourne un module $B${\tt.Int} de même type
dans lequel chaque opération {\it op\/} est réalisée par appel
à $A$.{\it op\/} et mise à jour de compteurs dépendant
de l'opération effectuée. Le but de ce foncteur est de fournir des statistiques
sur le nombre d'opérations entre grands entiers effectuées dans
un programme. Ces opérations sont regroupées en huit catégories
chacune associée à un compteur différent~:

\sv
\par $B${\tt.cadd } compte les additions et soustractions~;
\par $B${\tt.cmul } compte les multiplications et les élévations au carré~;
\par $B${\tt.cquo } compte les divisions~;
\par $B${\tt.cpow } compte les exponentiations et les factorielles~;
\par $B${\tt.croot} compte les racines carrées et les racines $p$-èmes~;
\par $B${\tt.cgcd } compte les pgcd et les opérations associées~;
\par $B${\tt.cbin } compte les opérations sur les représentations binaires~;
\par $B${\tt.cmisc} compte toutes les autres opérations.

\sv\noindent
Chaque compteur $B${\tt.cxxx} comporte trois champs~:

\begin{description}
\item{$B${\tt.cxxx.n}}~: nombres d'appels à l'une des fonctions associées à {\tt cxxx}~;
\item{$B${\tt.cxxx.s}}~: somme des tailles en bits des arguments~;
\item{$B${\tt.cxxx.m}}~: maximum des tailles en bits des arguments.
\end{description}

Pour chaque appel à une fonction de $B${\tt.Int} le champ {\tt n} du
compteur associé est incrémenté d'une unité, le champ {\tt s} est incrémenté
de la moyenne des longueurs en bits des opérandes de type grand entier
passés en paramètres et le champ {\tt m} est mis à jour de façon à conserver
le maximum des tailles en bits des opérandes des fonctions associées à ce
compteur. Les opérandes de type {\tt tref}, {\tt int}, {\tt string} ne sont pas pris
en compte dans ces calculs de taille.
Noter que les calculs effectués par $B${\tt.Int} sont intrinsèquement plus lents
que les calculs correspondants effectués par $A$ du fait de la gestion des
compteurs et des appels indirects.
\sv

Il est possible de consulter et de modifier à tout moment chacun de
ces compteurs afin de déterminer combien d'opérations dans chaque catégorie
ont été effectuées depuis la dernière remise à zéro.
La fonction $B${\tt.clear\_stats} remet à zéro tous les compteurs et la
fonction $B${\tt.print\_stats} affiche sur le canal d'erreur les statistiques
associées à chaque compteur (nombre d'opérations, taille moyenne et taille maximum
d'un opérande).
\sv

{\tt Numcount.Start} est un foncteur analogue à {\tt Numerix.Start}
permettant de sélectionner à l'exécution par une option
{\tt -e \it module} quel est le module $E$ de grands entiers
à utiliser avec les mêmes conventions que {\tt Numerix.Start}. Si l'option
{\tt -count} figure sur la ligne de commande alors la fonction
\medskip

\centerline{\tt Main(Numcount.M$(E)$\tt.Int).main}

\medskip\noindent
est appelée avec la liste des
autres paramètres figurant sur la ligne de commande et la fonction
{\tt Numcount.M}$(E)${\tt.print\_stats} est appelée à la fin du programme.
S'il n'y a pas d'option {\tt -count} sur la ligne de commande alors
la fonction {\tt Main}$(E)${\tt.main} est appelée comme avec
{\tt Numerix.Start}, dans ce cas il n'y a pas de statistiques affichées
ni de ralentissement par rapport à l'utilisation de {\tt Numerix.Start}.

\sv
Voici par exemple l'utilisation avec et sans l'option {\tt -count}
du programme de calcul de $\pi$ décrit dans la section {\ref{pi}}~:
\sv
\begin{verbatim}
exemples/pi/ocaml/pi-opt -e slong -count 1000000 -d -skip
    0.01     0.01 Count(Slong), surplace
    0.36     0.35 puiss 5
    3.79     3.43 sqrt
   26.48    22.69 série lb=6875847 ls=6875855
   31.93     5.45 quotient
   39.11     7.18 conversion
   op      count   avg.size   max.size
  add     424091        240    6875855
  mul     636133        323    6875847
  quo          3    2845643   10197784
  pow          3         13         19
 root          1    6643863    6643863
  gcd          0          -          -
  bin          1    4643867    4643867
 misc    1767125        353    6875855
3.
14159 26535  89793 23846  26433 83279  50288 41971  69399 37510
... (19998 lignes omises)
56787 96130  33116 46283  99634 64604  22090 10610  57794 58151

exemples/pi/ocaml/pi-opt -e slong 1000000 -d -skip
    0.00     0.00 Slong, surplace
    0.36     0.36 puiss 5
    3.83     3.47 sqrt
   23.96    20.13 série lb=6875847 ls=6875855
   29.41     5.45 quotient
   36.60     7.19 conversion
3.
14159 26535  89793 23846  26433 83279  50288 41971  69399 37510
... (19998 lignes omises)
56787 96130  33116 46283  99634 64604  22090 10610  57794 58151
\end{verbatim}
\sv

Le calcul du premier million de décimales de~$\pi$ a donc nécessité
$424091$ additions/soustractions sur des opérandes de $240$ bits en moyenne,
$636133$ multiplications/carrés sur des opérandes de $323$ bits en moyenne,
$3$ divisions sur des opérandes de $2845643$ en moyenne, etc.
Le coût en temps dû à la tenue de ces statistiques est d'environ $2.5$ secondes.

                          %+---------------+
                          %|  Compilation  |
                          %+---------------+

\section{Compilation}
Les programmes Ocaml utilisant \Numerix\ doivent être compilés avec les commandes
suivantes~:
\sv
\begin{shelllisting}
\begin{verbatim}
ocamlc -custom§it options§tt nums.cma numerix.cma§it fichiers source§tt \
       -cclib -lmlnumx -cclib -lnums -cclib -lgmp

ocamlopt§it options§tt nums.cmxa numerix.cmxa§it fichiers source§tt \
       -cclib -lmlnumx -cclib -lnums -cclib -lgmp
\end{verbatim}
\end{shelllisting}
\sv

Les fichiers {\tt nums.cma}, {\tt nums.cmxa}, {\tt numerix.cma} et
{\tt numerix.cmxa} contien\-nent
sous forme compilée les parties Ocaml des bibliothèques {\tt BigNum}
(bi\-blio\-thè\-que fournie avec Ocaml) et \Numerix. Il peut être
nécessaire d'indiquer aux compilateurs où se trouvent les fichiers
{\tt numerix.cma} et {\tt numerix.cmxa} au moyen d'une option
{\tt -I} {\it chemin}.
\sv

Les options {\tt -cclib -lmlnumx},  {\tt -cclib -lnums}
et {\tt -cclib -lgmp} demandent à l'éditeur de liens de rechercher
dans les fichiers {\tt libmlnumx.a}, {\tt libnums.a} et {\tt libgmp.a}
les primitives C dont il aurait besoin.
{\tt libnums.a} contient sous forme compilée la partie C de {\tt BigNum},
{\tt libmlnumx.a} la partie C de \Numerix\ et {\tt libgmp.a} la
partie C de \GMP. Il peut être nécessaire d'indiquer à l'éditeur
de liens dans quels répertoires chercher ces fichiers {\tt libxxx.a}
au moyen d'options {\tt -ccopt -L\it chemin}.
Si {\tt GMP} n'est pas installé ou si son interface avec Ocaml n'est pas
incluse dans votre version de \Numerix, alors l'option {\tt -cclib -lgmp}
doit être omise. De même, les paramètres {\tt nums.cma},
{\tt nums.cmxa} et {\tt -cclib -lnums}
doivent être omis si {\tt BigNum} n'est pas installé.
\sv

A partir de la version 3.00 de Ocaml, les options {\tt -custom},
{\tt -cclib -lxxx}
et {\tt -ccopt xxx} sont enregistrées dans les fichiers {\tt numerix.cma}
et {\tt numerix.cmxa} au moment de la compilation de \Numerix\, donc n'ont
pas à être redonnées pour compiler un programme utilisant \Numerix.


                            %+-----------+
                            %|  Exemple  |
                            %+-----------+

\section{Exemple}

\begin{camllisting}
\begin{verbatim}
(* +--------------------------------------------------------------------------+
   |  Fichier simple.ml: D§émonstration de la biblioth§èque Numerix avec Ocaml  |
   +--------------------------------------------------------------------------+ *)

(* calcul de (sqrt(3) + sqrt(2))/(sqrt(3)-sqrt(2)) avec n d§écimales *)

open Numerix

module Main(E:Int_type) = struct
  module I = Infixes(E)
  open E
  open I

  (* version fonctionnelle *)
  let calc_func n =
    let dix_n  = (of_int(5) ** n) << n in   (* 10^n  *)
    let dix_2n = sqr(dix_n)            in   (* 10^2n *)
    let sqrt_2 = sqrt(dix_2n *. 200)   in   (* sqrt(2) avec n+1 d§écimales *)
    let sqrt_3 = sqrt(dix_2n *. 300)   in   (* sqrt(3) avec n+1 d§écimales *)
    (dix_n * (sqrt_3 + sqrt_2)) / (sqrt_3 - sqrt_2)

  (* version sur place *)
  let calc_in n =
    let dix_n  = make_ref(of_int 0) in
    let dix_2n = make_ref(of_int 0) in
    let sqrt_2 = make_ref(of_int 0) in
    let sqrt_3 = make_ref(of_int 0) in
    let x      = make_ref(of_int 0) in
    let y      = make_ref(of_int 0) in

    pow_in   dix_n    (of_int 5)  n;        (* dix_n <- 5^n ...      *)
    shl_in   dix_n    ~~dix_n     n;        (*        ... * 2^n      *)
    sqr_in   dix_2n   ~~dix_n;              (* dix_2n <- 10^(2n)     *)
    mul_1_in sqrt_2   ~~dix_2n    200;      (* sqrt_2 <- ...         *)
    sqrt_in  sqrt_2   ~~sqrt_2;             (* .. sqrt(2*10^(2n+2))  *)
    mul_1_in sqrt_3   ~~dix_2n    300;      (* sqrt_3 <- ...         *)
    sqrt_in  sqrt_3   ~~sqrt_3;             (* .. sqrt(3*10^(2n+2))  *)
    add_in   x        ~~sqrt_3    ~~sqrt_2; (* x <- sqrt_2 + sqrt_2  *)
    sub_in   y        ~~sqrt_3    ~~sqrt_2; (* y <- sqrt_3 - sqrt_2  *)
    mul_in   x        ~~dix_n     ~~x;      (* x <- trunc(10^n*x ... *)
    quo_in   x        ~~x         ~~y;      (*               ... /y) *)
    copy_out x                              (* r§ésultat              *)

  (* fonction principale *)
  let main arglist =

    let rec parse (n,f) = function
      | []          -> (n,  f)
      | "-test"::s  -> parse (30, calc_func) s
      | "-func"::s  -> parse (n,  calc_func) s
      | "-in"  ::s  -> parse (n,  calc_in)   s
      | "-n"::x::s  -> parse (int_of_string x, f) s
      | _ -> prerr_endline "syntaxe : simple [-e entiers] [-test] [-func|-in] [-n <n>]";
             exit(1)
    in
    let (n,f) = parse (30,calc_func) arglist in
    print_endline(string_of(f n))

end (* Main *)

module S = Numcount.Start(Main)
let _ = S.start()
\end{verbatim}
\end{camllisting}
\sv\noindent
Compilation et exécution (avec Ocaml-3.00)~:
\sv
\begin{verbatim}
> ocamlc -o simple-byte nums.cma numerix.cma simple.ml
> ./simple-byte -e slong
9898979485566356196394568149410
>
> ocamlopt -o simple-opt nums.cmxa numerix.cmxa simple.ml
> ./simple-opt -e gmp -n 50 -count
   op      count   avg.size   max.size
  add          2        170        171
  mul          4        250        333
  quo          1        253        338
  pow          1          3          3
 root          2        340        341
  gcd          0          -          -
  bin          1        117        117
 misc          2         85        170
989897948556635619639456814941178278393189496131335
\end{verbatim}

                      %+----------------------+
                      %|  Système interactif  |
                      %+----------------------+

\section{Système interactif}

{\tt ocamlnumx} est une version spécialisée du système interactif {\tt ocaml}
lié avec le fichier objet {\tt numerix.cma} et les bibliothèques
compilées {\tt libnums.a}, {\tt libgmp.a} et {\tt libmlnumx.a}. Il permet d'utiliser tous
les modules de \Numerix, le choix d'une implémentation des grands entiers
se faisant par une directive {\tt open} appropriée.
\sv

\begin{verbatim}
> ocamlnumx
ocamlnumx : Objective Caml with big integer libraries
Numerix submodules : Big Clong Dlong Slong Gmp
Numerix version    : 0.19 (24 Aout 2001)
 
        Objective Caml version 3.02

# open Numerix;;
# open Big;;
# fact(30);;
- : Numerix.Big.t = 265252859812191058636308480000000
# module I = Infixes(Big) open I;;
module I :
  sig
    val ( + ) : Numerix.Big.t -> Numerix.Big.t -> Numerix.Big.t
    val ( - ) : Numerix.Big.t -> Numerix.Big.t -> Numerix.Big.t
    val ( * ) : Numerix.Big.t -> Numerix.Big.t -> Numerix.Big.t
    ...
    val ( ~~ ) : Numerix.Big.tref -> Numerix.Big.t
  end
# (of_int 1) << 100;;
- : Numerix.Big.t = 1267650600228229401496703205376
# #quit;;
\end{verbatim}

                  %+------------------------------+
                  %|  Utilisation avec Camllight  |
                  %+------------------------------+

\chapter{Utilisation avec Camllight}

L'interface pour Camllight de \Numerix\ est dérivée de celle
pour Ocaml en retirant ou en adaptant les fonctionnalités spécifiques
au langage Ocaml. On se reportera au chapitre précédent pour la liste
des fonctions disponibles, seules les différences avec la version Ocaml
sont présentées ici. Cette interface a été testée avec succès avec les versions
0.73, 0.74 et 0.75 de Camllight.

                            %+-----------+
                            %|  Modules  |
                            %+-----------+

\section{Modules}

Camllight dispose d'un système limité de modules et en particulier ne
supporte pas les notions de sous-module ni de module paramétré. Il reste
toutefois possible d'écrire du code indépendant d'une implémentation
particulière des grands entiers en utilisant les noms courts des fonctions,
les noms longs étant déduits par le compilateur en fonction de directives
{\tt\#open} figurant dans le fichier source. Il suffit de modifier ces
directives (éventuellement de façon automatique au moyen d'un préprocesseur)
et de recompiler le code source pour changer l'im\-plé\-men\-ta\-tion des
grands entiers utilisée.
\sv

Les modules disponibles portent les mêmes noms qu'en Ocaml sans majuscule~:
{\tt big}, {\tt clong}, {\tt dlong}, {\tt slong} et {\tt gmp}.
Il n'y a pas d'équivalents aux modules construits en Ocaml avec les foncteurs
{\tt Numcmp.M} et {\tt Numcount.M}. Les notations infixes sont accessibles en ouvrant le module
{\tt infxxx} où {\tt xxx} est le nom du module implémentant les grands entiers.


                           %+-------------+
                           %|  Fonctions  |
                           %+-------------+

\section{Fonctions}

Les fonctions décrites dans la signature {\tt Int\_type} en Ocaml
ont été conser\-vées en Camllight avec seulement deux différences~:

\begin{description}
\item La division sans reste est notée {\tt quo} en Ocaml et {\tt div}
en Camllight. La raison de cette différence est que l'identificateur
{\tt quo} a un statut infixe en Camllight. Les autres noms dérivés
de {\tt quo}~: {\tt quomod}, {\tt quo\_1}, {\tt quomod\_1},
{\tt quo\_in}, {\tt quomod\_in} et {\tt quomod\_1\_in}
ont été conservés.

\item La consultation d'une référence est notée {\tt look} ou {\tt \~{}}
en Ocaml, mais {\tt look} ou {\tt ?} en Camllight. Il y a deux raisons
à cette différence~: l'identificateur {\tt ?} est réservé et donc indisponible
en Ocaml et l'identificateur {\tt\~{}} a un statut préfixe en Ocaml
mais infixe en Camllight.
\end{description}

                          %+---------------+
                          %|  Compilation  |
                          %+---------------+

\section{Compilation}
Les programmes Caml utilisant \Numerix\ doivent être compilés avec la commande
suivante~:
\sv
\begin{shelllisting}
\begin{verbatim}
camlc -custom§it options§tt nums.zo numerix.zo§it fichiers source§tt -lclnumx -lnums -lgmp
\end{verbatim}
\end{shelllisting}
\sv
Les fichiers {\tt nums.zo} et {\tt numerix.zo} contiennent
sous forme compilée les parties Caml des bibliothèques {\tt BigNum}
(bibliothèque fournie avec Camllight) et \Numerix. Il peut être
nécessaire d'indiquer au compilateur où se trouve le fichier
{\tt numerix.zo} au moyen d'une option
{\tt -I} {\it chemin}.
\sv

Les options {\tt -lclnumx},  {\tt -lnums}
et {\tt -lgmp} demandent à l'éditeur de liens de rechercher
dans les fichiers {\tt libclnumx.a},  {\tt libnums.a} et {\tt libgmp.a}
les primitives C dont il aurait besoin.
{\tt libnums.a} contient sous forme compilée la partie C de {\tt BigNum},
{\tt libclnumx.a} la partie C de \Numerix\ et {\tt libgmp.a} la
partie C de \GMP. Il peut être nécessaire d'indiquer à l'éditeur
de liens dans quels répertoires chercher ces fichiers {\tt libxxx.a}
au moyen d'options {\tt -ccopt -L\it chemin}.
Si {\tt GMP} n'est pas installé ou si son interface avec Camllight n'est pas
incluse dans votre version de \Numerix, alors l'option {\tt -lgmp}
doit être omise.
De même, les paramètres {\tt nums.zo} et {\tt -cclib -lnums}
doivent être omis si {\tt BigNum} n'est pas installé.

                            %+-----------+
                            %|  Exemple  |
                            %+-----------+

\section{Exemple}

\begin{camllisting}
\begin{verbatim}
(* +------------------------------------------------------------------------------+
   |  Fichier simple.ml: D§émonstration de la biblioth§èque Numerix avec Camllight  |
   +------------------------------------------------------------------------------+ *)

(* calcul de (sqrt(3) + sqrt(2))/(sqrt(3)-sqrt(2)) avec n d§écimales *)

#open "slong";;
#open "infslong";;

  (* version fonctionnelle *)
  let calc_func n =
    let dix_n  = (of_int(5) ** n) << n in   (* 10^n  *)
    let dix_2n = sqr(dix_n)            in   (* 10^2n *)
    let sqrt_2 = sqrt(dix_2n *. 200)   in   (* sqrt(2) avec n+1 d§écimales *)
    let sqrt_3 = sqrt(dix_2n *. 300)   in   (* sqrt(3) avec n+1 d§écimales *)
    (dix_n * (sqrt_3 + sqrt_2)) / (sqrt_3 - sqrt_2);;

  (* version sur place *)
  let calc_in n =
    let dix_n  = make_ref(of_int 0) in
    let dix_2n = make_ref(of_int 0) in
    let sqrt_2 = make_ref(of_int 0) in
    let sqrt_3 = make_ref(of_int 0) in
    let x      = make_ref(of_int 0) in
    let y      = make_ref(of_int 0) in

    pow_in   dix_n    (of_int 5)  n;        (* dix_n <- 5^n ...      *)
    shl_in   dix_n    ?dix_n      n;        (*        ... * 2^n      *)
    sqr_in   dix_2n   ?dix_n;               (* dix_2n <- 10^(2n)     *)
    mul_1_in sqrt_2   ?dix_2n     200;      (* sqrt_2 <- ...         *)
    sqrt_in  sqrt_2   ?sqrt_2;              (* .. sqrt(2*10^(2n+2))  *)
    mul_1_in sqrt_3   ?dix_2n     300;      (* sqrt_3 <- ...         *)
    sqrt_in  sqrt_3   ?sqrt_3;              (* .. sqrt(3*10^(2n+2))  *)
    add_in   x        ?sqrt_3     ?sqrt_2;  (* x <- sqrt_3 + sqrt_2  *)
    sub_in   y        ?sqrt_3     ?sqrt_2;  (* y <- sqrt_3 - sqrt_2  *)
    mul_in   x        ?dix_n      ?x;       (* x <- trunc(10^n*x ... *)
    quo_in   x        ?x          ?y;       (*               ... /y) *)
    copy_out x;;                            (* r§ésultat              *)

  (* fonction principale *)
  let main arglist =

    let rec parse (n,f) = function
      | []          -> (n,  f)
      | "-test"::s  -> parse (30, calc_func) s
      | "-func"::s  -> parse (n,  calc_func) s
      | "-in"  ::s  -> parse (n,  calc_in)   s
      | "-n"::x::s  -> parse (int_of_string x, f) s
      | _ -> prerr_endline "syntaxe : simple [-test] [-func|-in] [-n <n>]";
             exit(1)
    in
    let (n,f) = parse (30,calc_func) arglist in
    print_endline(string_of(f n));
    flush stdout;;

main (tl(list_of_vect sys__command_line));;
\end{verbatim}
\end{camllisting}
\sv\noindent
Compilation et exécution~:
\sv
\begin{verbatim}
> camlc -custom -o simple nums.zo numerix.zo simple.ml \
        -lclnumx -lnums -lgmp
> ./simple
9898979485566356196394568149410
\end{verbatim}
\sv

Noter que les trois bibliothèques {\tt libclnumx.a}, {\tt libnums.a}
et {\tt libgmp.a} doi\-vent être fournies à l'éditeur de liens même si
seul le module {\tt slong} est utilisé, car les autres modules sont
présents dans {\tt numerix.zo} et font référence à des fonctions de
ces trois bibliothèques.

                      %+----------------------+
                      %|  Système interactif  |
                      %+----------------------+

\section{Système interactif}

De même que pour la version Ocaml, un système interactif spécialisé
est disponible~:
\sv

\begin{verbatim}
> camllight ~/lib/camlnumx
>       Caml Light version 0.74

camlnumx : Caml toplevel with big integer libraries
Numerix submodules : big clong dlong slong gmp
Numerix version    : 0.19 (24 Aout 2001)

##open "big";;
##open "infbig";;
#fact(30);;
- : t = 265252859812191058636308480000000
#(of_int 1) << 100;;
- : t = 1267650600228229401496703205376
#quit();;
\end{verbatim}

                       %+--------------------+
                       %|  Utilisation en C  |
                       %+--------------------+

\chapter{Utilisation en C}

L'interface C de \Numerix\ a été dérivée de l'interface Ocaml en ajoutant un
gestionnaire mémoire simplifié destiné à palier l'absence de celui de
Ocaml et en se limitant aux opérations sur place. La raison d'être de cette
interface est de permettre une comparaison non biaisée entre \Numerix\
et \GMP\ dont l'environnement naturel d'utilisation est le langage C,
et de pouvoir compiler et exécuter des programmes de test sur une machine ne
disposant pas de Ocaml.
\sv

                           %+-------------+
                           %|  Interface  |
                           %+-------------+

\section{Interface}
Les trois modules {\tt clong}, {\tt dlong} et {\tt slong} sont
disponibles, pour autant que le compilateur C utilisé et l'architecture
matérielle de la machine cible le permettent. Le choix du module à utiliser
et la taille des mots machine doivent être indiqués à la compilation
au moyen des directives {\tt \#define} suivantes~:

\begin{description}
\item{\tt \#define bits\_32} et {\tt \#define bits\_64} indiquent la
taille du mot machine. Une et une seule de ces directives doit
être spécifiée.
\item{\tt \#define use\_clong}, {\tt \#define use\_dlong} et
{\tt \#define use\_slong} sé\-lec\-tion\-nent le module implémentant les
grands entiers. Au plus une de ces directives doit être spécifiée, le
module sélectionné en l'absence de spécification est \Clong.
\end{description}

Ces directives peuvent être incluses en tête de chaque fichier source
ou transmises au préprocesseur à l'aide d'options {\tt -Dxxx} sur la ligne
de commande de compilation.
\sv

Le fichier {\tt c-long\_int.h} définit le type de données {\tt xint} représentant
un grand entier et donne les prototypes des fonctions opérant sur les grands
entiers. Les noms des fonctions sont préfixés par une chaîne de trois caractères
identifiant le module \Clong, \Dlong\ ou \Slong\ auquel elles appartiennent
et le niveau d'abstraction auquel elles opèrent~:

\begin{description}
\item{\tt cn\_nnn}~: fonction {\tt nnn} du module \Clong\ opérant sur les entiers naturels
(tableaux de chiffres)~;
\item{\tt cz\_nnn}~: fonction {\tt nnn} du module \Clong\  opérant sur les entiers relatifs
(tableaux de chiffres avec longueur et signe accolés)~;
\item{\tt cx\_nnn}~: fonction {\tt nnn} du module \Clong\  opérant sur les grands entiers
(entiers relatifs extensibles et relogeables).
\item{\tt dn\_nnn}, {\tt dz\_nnn}, {\tt dx\_nnn}, idem pour le module \Dlong.
\item{\tt sn\_nnn}, {\tt sz\_nnn}, {\tt sx\_nnn}, idem pour le module \Slong.
\end{description}
\sv

Normalement le programmeur ne devrait avoir affaire qu'aux grands entiers,
de type {\tt xint}, et n'utiliser que les fonctions {\tt cx\_nnn} ou
{\tt dx\_nnn} ou {\tt sx\_nnn} qui opèrent sur ces grands entiers. Les
fonctions de niveau {\tt n} ou {\tt z} sont à usage interne uniquement et
ne sont pas documentées ici. De façon à permettre l'écriture d'un code
indépendant de l'implémentation des grands entiers le fichier
{\tt c-long\_int.h} définit une macro {\tt xx} dont le code d'expansion
est {\tt cx\_} ou {\tt dx\_} or {\tt sx\_} selon que l'un des symboles
{\tt use\_clong}, {\tt use\_dlong} ou {\tt use\_slong} est défini. On
écrira donc~:
\sv
\indent{\tt xx(add)(\&x,a,b);}
\sv\noindent
pour additionner {\tt a} et {\tt b} dans {\tt x}, ce code étant transformé
par le préprocesseur en~:
\sv
\indent{\tt cx\_add(\&x,a,b);} ou {\tt dx\_add(\&x,a,b);} ou {\tt sx\_add(\&x,a,b);}
\sv
Il conseillé d'utiliser systématiquement la macro {\tt xx} et de ne
pas coder directement les identificateurs expansés, cela permet de
recompiler son programme avec une autre implémentation des grands entiers
par simple modification de la directive {\tt \#define use\_xxx}. En tout
état de cause les fonctions d'un module ne peuvent opérer sur les données
d'un autre module et il n'y a pas de mécanisme permettant de différencier
le type de données {\tt xint} selon le module.
\sv

Voici un extrait de {\tt c-long\_int.h} donnant les prototypes
des fonctions publiques~:

\begin{clisting}
\begin{verbatim}
/* pr§éfixe des fonctions publiques  */
#if defined(use_slong)
#define xx(nom) sx_##nom
#elif defined(use_dlong)
#define xx(nom) dx_##nom
#else
#define xx(nom) cx_##nom
#endif

/* entier extensible  */
typedef struct {...} * xint;

/* allocation/d§ésallocation */
xint xx(new)(void);
void xx(free)(xint *x);

/* initialisation §à partir d'un long, d'un entier ou d'une chaine */
xint xx(of_int)     (long a);
xint xx(of_xint)    (xint a);
xint xx(of_string)  (char *s);

/* modification d'un entier d§éj§à initialis§é */
void xx(copy)       (xint *x, xint a);              /* x §la a         */
void xx(copy_int)   (xint *x, long a);              /* x §la a         */
void xx(copy_string)(xint *x, char *s);             /* x §la val(s)    */

/* op§érations usuelles */
void xx(add)   (xint *x, xint a, xint b);           /* x §la a+b       */
void xx(sub)   (xint *x, xint a, xint b);           /* x §la a-b       */
void xx(mul)   (xint *x, xint a, xint b);           /* x §la ab        */
void xx(quo)   (xint *x, xint a, xint b);           /* x §la a div b   */
void xx(quomod)(xint *x, xint *y,xint a, xint b);   /* x §la a div b, y §la a mod b */
void xx(gcd)   (xint *z, xint a, xint b);           /* x §la pgcd(a,b) */
void xx(powmod)(xint *x, xint a, xint b, xint c);   /* x §la a^b mod c */

/* pgcd §étendu : z §la pgcd(a,b), xa-by = z, xr-ys = 1 */
void xx(gcd_ex)(xint *x, xint *y, xint *z, xint a, xint b);
void xx(cfrac) (xint *r, xint *s, xint *x, xint *y, xint *z, xint a, xint b);

/* op§érations unaires */
void xx(abs)   (xint *x, xint a);                   /* x §la |a|       */
void xx(neg)   (xint *x, xint a);                   /* x §la -a        */
void xx(sqr)   (xint *x, xint a);                   /* x §la a^2       */
void xx(sqrt)  (xint *x, xint a);                   /* x §la trunc(a^1/2) */

/* op§érations mixtes */
void xx(add_1) (xint *x, xint a, long b);           /* x §la a+b       */
void xx(sub_1) (xint *x, xint a, long b);           /* x §la a-b       */
void xx(mul_1) (xint *x, xint a, long b);           /* x §la ab        */
long xx(quomod_1) (xint *x, xint a, long b);        /* x §la a div b, retourne a mod b */
void xx(shl)   (xint *x, xint a, long b);           /* x §la a*2^b     */
void xx(shr)   (xint *x, xint a, long b);           /* x §la a div 2^b */
void xx(split) (xint *x, xint *y,xint a, long b);   /* x §la a mod 2^b, y §la a div 2^b */
void xx(join)  (xint *x, xint a, xint b, long c);   /* x §la a + b*2^c */
void xx(pow)   (xint *x, xint a, long b);           /* x §la a^b       */
void xx(root)  (xint *x, xint a, long b);           /* x §la trunc(a^1/b) */
void xx(fact)  (xint *x, long a);                   /* x §la a!        */

/* comparaison */
int xx(eq)     (xint a, xint b);                    /* a §eq b ?        */
int xx(neq)    (xint a, xint b);                    /* a §neq b ?        */
int xx(inf)    (xint a, xint b);                    /* a §lt b ?        */
int xx(infeq)  (xint a, xint b);                    /* a §le b ?        */
int xx(sup)    (xint a, xint b);                    /* a §gt b ?        */
int xx(supeq)  (xint a, xint b);                    /* a §ge b ?        */
int xx(cmp)    (xint a, xint b);                    /* signe(a-b)      */
int xx(sgn)    (xint a);                            /* signe(a)        */

int xx(eq_1)   (xint a, long b);                    /* a §eq b ?        */
int xx(neq_1)  (xint a, long b);                    /* a §neq b ?        */
int xx(inf_1)  (xint a, long b);                    /* a §lt b ?        */
int xx(infeq_1)(xint a, long b);                    /* a §le b ?        */
int xx(sup_1)  (xint a, long b);                    /* a §gt b ?        */
int xx(supeq_1)(xint a, long b);                    /* a §ge b ?        */
int xx(cmp_1)(xint a, long b);                      /* sign(a-b)       */

/* acc§ès §à la structure binaire, lowbits, highbits et int_of retournent
  des mots de 31 bits seulement pour compatibilit§é avec Ocaml */
long xx(nbits)   (xint a);                          /* taille de a     */
long xx(lowbits) (xint a);                          /* voir Ocaml      */
long xx(highbits)(xint a);                          /* voir Ocaml      */
long xx(nth_word)(xint a, long n);                  /* bits 16b..16b+15*/
long xx(int_of)  (xint a);                          /* bits 0..30      */

/* conversion entier -> chaine, d§ésallouer la chaine par free_string */
char *xx(string_of)(xint a);
void xx(free_string)(char *s);

/* chronom§étrage */
void chrono(char *msg);
\end{verbatim}
\end{clisting}

                          %+--------------+
                          %|  Précisions  |
                          %+--------------+

\section{Précisions}

Une variable de type {\tt xint} est un pointeur sur une structure
de données gérée par le gestionnaire mémoire intégré à la version C
de \Numerix. L'initialisation d'une variable {\tt x} de type {\tt xint}
s'effectue normalement en deux étapes~:
\begin{enumerate}
\item initialisation du pointeur {\tt x} par appel à {\tt xx(new)()}~;
\item affectation d'une valeur en indiquant l'adresse de {\tt x}, {\tt \&x},
en paramètre résultat d'un calcul, par exemple {\tt xx(add)(\&x,a,b)}.
\end{enumerate}

Les fonctions
{\tt xx(of\_int)}, {\tt xx(of\_xint)} et {\tt xx(of\_string)} réalisent
à la fois l'initialisation du pointeur et l'affectation d'une valeur
à partir d'un entier ordinaire, d'un autre grand entier ou d'une chaîne
de caractères.
L'instruction~:
\sv
\indent{\tt xint x = xx(of\_int)($i$);}
\sv\noindent
est formellement équivalente aux instructions~:
\sv
\indent{\tt xint x = xx(new)(); xx(copy\_int)(\&x,$i$);}
\sv\noindent
et de même pour {\tt xx(of\_xint)} et {\tt xx(of\_string)}.
L'exécution d'une opération par un appel tel que~:
\sv
\indent{\tt xx(quo)(\&x,a,b);}
\sv\noindent
est valide si et seulement si les conditions suivantes sont remplies~:
\begin{enumerate}
\item les variables {\tt a} et {\tt b} sont initialisées et désignent
des valeurs valides pour l'opération en question ({\tt a} désigne une
valeur quelconque et {\tt b} une valeur quelconque non nulle)~;
\item la variable {\tt x} contient un pointeur initialisé.
\end{enumerate}
\sv

Il n'est pas nécessaire que {\tt x} ait reçu une valeur préalablement à
cette opé\-ra\-tion. Si c'est le cas alors le bloc mémoire contenant cette valeur
est surchargé avec le résultat de l'opération s'il est suffisamment grand,
sinon un nouveau bloc mémoire est alloué pour recevoir le résultat, le pointeur
{\tt x} est modifié pour désigner ce nouveau bloc et l'ancien bloc est
désalloué.
\sv

Les opérations de type lecture-modification-écriture où une même variable
figure à la fois en positions opérande et résultat sont correctement
traitées. Par contre pour les opérations produisant plusieurs résultats
({\tt quomod}, {\tt gcd\_ex}, {\tt cfrac} et {\tt split}) une même variable
ne peut figurer plusieurs fois en position résultat.
Par exemple l'instruction suivante est illégale~:
\sv
\indent{\tt xx(quomod)(\&x,\&x,a,b); /* \sltt ill{\accent 19 e}gal \tt */}
\sv

La fonction {\tt xx(free)} permet de forcer la libération du bloc mémoire
occupé par une valeur qui n'est plus utile. L'instruction~:
\sv
\indent{\tt xx(free)(\&x);}
\sv\noindent
a pour effet de désallouer la valeur désignée par {\tt x} s'il y en a une
et de réinitialiser le pointeur {\tt x}. La variable {\tt x} reste utilisable
après cette instruction pour recevoir une nouvelle valeur.
\sv

La conversion d'un grand entier en chaîne de caractères est réalisée par
la fonction {\tt xx(string\_of)} qui alloue une chaîne de caractères de taille
convenable et y copie la représentation décimale de son argument. Cette
chaîne devra être libérée après usage par appel à la fonction
{\tt xx(free\_string)}.
\sv

Enfin, la fonction {\tt chrono} affiche sur le canal d'erreur standard le
temps CPU en secondes écoulé depuis le début du processus, la différence
avec le temps précédent et le message passé en  argument.

                          %+---------------+
                          %|  Compilation  |
                          %+---------------+

\section{Compilation}
Les programmes C utilisant \Numerix\ doivent être compilés avec la commande
suivante~:
\sv
\begin{shelllisting}
\begin{verbatim}
gcc§it options§tt -Dbits_xx -Duse_yyy §it fichiers source§tt -lcnumx
\end{verbatim}
\end{shelllisting}
\sv

{\tt -Dbits\_xx} indique la taille d'un mot machine, ${\tt xx} = 32$ ou $64$.
{\tt -Duse\_yyy} indique le module à utiliser, ${\tt yyy} = {\tt clong}$
ou {\tt dlong} ou {\tt slong}.
{\tt -lcnumx} indique à l'éditeur de liens de rechercher dans le
fichier {\tt libcnumx.a} les fonctions compilées dont il aurait besoin.
Il peut être nécessaire d'indiquer à l'éditeur de liens dans quel répertoire
chercher ce fichier {\tt libcnumx.a} au moyen d'une option {\tt -L\it chemin}.
De même il peut être nécessaire d'indiquer au préprocesseur
au moyen d'une option
{\tt -I\it chemin}
où trouver le fichier d'en-tête {\tt c-long\_int.h}.




                            %+-----------+
                            %|  Exemple  |
                            %+-----------+

\section{Exemple}

\begin{clisting}
\begin{verbatim}
 /* +---------------------------------------------------------------------+
    |  Fichier simple.c: D§émonstration de la biblioth§èque Numerix avec C  |
    +---------------------------------------------------------------------+ */

/* calcul de (sqrt(3) + sqrt(2))/(sqrt(3)-sqrt(2)) avec n d§écimales */

#include <stdlib.h>
#include <stdio.h>
#include "c-long_int.h"

void calcul(xint *res, long n) {
   xint dix_n  = xx(new)();
   xint dix_2n = xx(new)();
   xint sqrt_2 = xx(new)();
   xint sqrt_3 = xx(new)();
   xint x      = xx(new)();
   xint y      = xx(new)();

   xx(copy_int)(&dix_n,5);
   xx(pow)  (&dix_n,  dix_n,  n);            /* dix_n <- 5^n ...      */
   xx(shl)  (&dix_n,  dix_n,  n);            /*        ... * 2^n      */
   xx(sqr)  (&dix_2n, dix_n);                /* dix_2n <- 10^(2n)     */
   xx(mul_1)(&sqrt_2, dix_2n, 200);          /* sqrt_2 <- ...         */
   xx(sqrt) (&sqrt_2, sqrt_2);               /* .. sqrt(2*10^(2n+2))  */
   xx(mul_1)(&sqrt_3, dix_2n, 300);          /* sqrt_3 <- ...         */
   xx(sqrt) (&sqrt_3, sqrt_3);               /* .. sqrt(3*10^(2n+2))  */
   xx(add)  (&x,      sqrt_3, sqrt_2);       /* x <- sqrt_3 + sqrt_2  */
   xx(sub)  (&y,      sqrt_3, sqrt_2);       /* y <- sqrt_3 - sqrt_2  */
   xx(mul)  (&x,      dix_n,  x);            /* res <- trunc(10^n*x...*/
   xx(quo)  (res,     x,      y);            /*               ... /y) */

   xx(free)(&dix_n);
   xx(free)(&dix_2n);
   xx(free)(&sqrt_2);
   xx(free)(&sqrt_3);
   xx(free)(&x);
   xx(free)(&y);
}


int main(int argc, char **argv) {
  long n = 30;
  xint x = xx(new)();
  char *s;

  for(argc--, argv++; (argc); argc--, argv++) {
    if      (strcmp(*argv,"-test") == 0) {n = 30;}
    else if ((strcmp(*argv,"-n") == 0) && (argc > 0)) {
      argc--, argv++;
      n = atol(*argv);
    } else {
      fprintf(stderr,"syntaxe : simple [-test] [-n <n>]\n");
      fflush(stderr);
      exit(1);
    }
  }

  calcul(&x,n);
  s = xx(string_of)(x);
  fprintf(stdout,"%s\n",s);
  fflush(stdout);

  xx(free)(&x);
  xx(free_string)(s);
  return(0);
}
\end{verbatim}
\end{clisting}
\sv\noindent
Compilation et exécution~:
\sv
\begin{verbatim}
> gcc -O2 -Wall -Dbits_32 -Duse_slong -o simple simple.c -lcnumx
> ./simple
9898979485566356196394568149410
\end{verbatim}

                     %+-------------------------+
                     %|  Utilisation en Pascal  |
                     %+-------------------------+

\chapter{Utilisation en Pascal}

L'interface Pascal de \Numerix\ est construite à partir de l'interface C
et fournit les mêmes fonctionnalités que cette dernière. Elle a été développée
sur un PC-Linux (processeur Athlon) avec le compilateur Free Pascal version 1.0.2.
\sv

                           %+-------------+
                           %|  Interface  |
                           %+-------------+

\section{Interface}
Trois unités sont définies : {\tt clong}, {\tt dlong} et {\tt slong}.
Chaque unité contient la dé\-cla\-ra\-tion du type grand entier correspondant et
les déclarations des pro\-cé\-du\-res et fonctions associées. Les identificateurs
exportés sont les mêmes d'une unité à l'autre, ce qui permet d'écrire
des programmes utilisateurs indépendants de l'unité choisie, seule la
clause {\tt uses} devant être modifiée pour changer l'im\-plé\-men\-ta\-tion
des grands entiers.
\sv

\begin{camllisting}
\begin{verbatim}
unit clong; (* ou dlong ou slong *)
interface

(* entier extensible  *)
type xint = ...;

(* allocation/d§ésallocation *)
function  xnew : xint;
procedure xfree(var x : xint);

(* initialisation §à partir d'un entier, d'un long ou d'une chaine *)
function  of_int     (a : longint) : xint;
function  of_xint    (a : xint   ) : xint;
function  of_string  (s : pchar  ) : xint;

(* modification d'un entier d§éj§à initialis§é *)
procedure copy       (var x : xint; a : xint);                 (* x §la a         *)
procedure copy_int   (var x : xint; a : longint);              (* x §la a         *)
procedure copy_string(var x : xint; s : pchar);                (* x §la val(s)    *)

(* op§érations usuelles *)
procedure add     (var x   : xint; a,b   : xint);              (* x §la a+b       *)
procedure sub     (var x   : xint; a,b   : xint);              (* x §la a-b       *)
procedure mul     (var x   : xint; a,b   : xint);              (* x §la ab        *)
procedure quo     (var x   : xint; a,b   : xint);              (* x §la a div b   *)
procedure quomod  (var x,y : xint; a,b   : xint);              (* x §la a div b,  *)
                                                               (* y §la a mod b   *)
procedure gcd     (var z   : xint; a,b   : xint);              (* x §la pgcd(a,b) *)
procedure powmod  (var x   : xint; a,b,c : xint);              (* x §la a^b mod c *)

(* pgcd §étendu : z §la pgcd(a,b), xa-by = z, xr-ys = 1 *)
procedure gcd_ex  (var x,y,z     : xint; a,b : xint);
procedure cfrac   (var r,s,x,y,z : xint; a,b : xint);

(* op§érations unaires *)
procedure abs     (var x   : xint; a     : xint);              (* x §la |a|       *)
procedure neg     (var x   : xint; a     : xint);              (* x §la -a        *)
procedure sqr     (var x   : xint; a     : xint);              (* x §la a^2       *)
procedure sqrt    (var x   : xint; a     : xint);              (* x §la trunc(a^1/2) *)

(* op§érations mixtes *)
procedure add_1   (var x   : xint; a     : xint; b : longint); (* x §la a+b       *)
procedure sub_1   (var x   : xint; a     : xint; b : longint); (* x §la a-b       *)
procedure mul_1   (var x   : xint; a     : xint; b : longint); (* x §la ab        *)
function  quomod_1(var x   : xint; a     : xint; b : longint) : longint;
                                                               (* x §la a div b,  *)
                                                               (* retourne a mod b *)
procedure shiftl  (var x   : xint; a     : xint; b : longint); (* x §la a*2^b     *)
procedure shiftr  (var x   : xint; a     : xint; b : longint); (* x §la a div 2^b *)
procedure split   (var x,y : xint; a     : xint; b : longint); (* x §la a mod 2^b,*)
                                                               (* y §la a div 2^b *)
procedure join    (var x   : xint; a,b   : xint; c : longint); (* x §la a + b*2^c *)
procedure pow     (var x   : xint; a     : xint; b : longint); (* x §la a^b       *)
procedure root    (var x   : xint; a     : xint; b : longint); (* x §la trunc(a^1/b) *)
procedure fact    (var x   : xint; a     : xint);              (* x §la a!        *)

(* comparaison *)
function eq   (a,b : xint) : boolean;                          (* a §eq b ?        *)
function neq  (a,b : xint) : boolean;                          (* a §neq b ?        *)
function inf  (a,b : xint) : boolean;                          (* a §lt b ?        *)
function infeq(a,b : xint) : boolean;                          (* a §le b ?        *)
function sup  (a,b : xint) : boolean;                          (* a §gt b ?        *)
function supeq(a,b : xint) : boolean;                          (* a §ge b ?        *)
function cmp  (a,b : xint) : longint;                          (* signe(a-b)      *)
function sgn  (a   : xint) : longint;                          (* signe(a)        *)

function eq_1   (a : xint; b : longint) : boolean;             (* a §eq b ?        *)
function neq_1  (a : xint; b : longint) : boolean;             (* a §neq b ?        *)
function inf_1  (a : xint; b : longint) : boolean;             (* a §lt b ?        *)
function infeq_1(a : xint; b : longint) : boolean;             (* a §le b ?        *)
function sup_1  (a : xint; b : longint) : boolean;             (* a §gt b ?        *)
function supeq_1(a : xint; b : longint) : boolean;             (* a §ge b ?        *)
function cmp_1  (a : xint; b : longint) : longint;             (* signe(a-b)      *)

(* accès à la structure binaire, lowbits, highbits et int_of retournent
  des mots de 31 bits seulement pour compatibilité avec Ocaml *)
function nbits   (a : xint)              : longint;            (* taille de a     *)
function lowbits (a : xint)              : longint;            (* voir Ocaml      *)
function highbits(a : xint)              : longint;            (* voir Ocaml      *)
function nth_word(a : xint; n : longint) : longint;            (* bits 16b..16b+15*)
function int_of  (a : xint)              : longint;            (* bits 0..30      *)

(* conversion en chaine *)
function string_of(x : xint) : ansistring;

(* chronom§étrage *)
procedure chrono(msg : pchar);

(* erreurs *)
procedure failwith(msg : pchar);
\end{verbatim}
\end{camllisting}

                          %+--------------+
                          %|  Précisions  |
                          %+--------------+

\section{Précisions}

Une variable de type {\tt xint} est un pointeur sur une structure
de données gérée par le gestionnaire mémoire intégré à la version Pascal
de \Numerix. L'initialisation d'une variable {\tt x} de type {\tt xint}
s'effectue normalement en deux étapes~:
\begin{enumerate}
\item initialisation du pointeur {\tt x} par appel à {\tt xnew}~;
\item affectation d'une valeur en résultat d'un calcul, par exemple {\tt add(x,a,b)}.
\end{enumerate}

Les fonctions
{\tt of\_int}, {\tt of\_xint} et {\tt of\_string} réalisent
à la fois l'initialisation du pointeur et l'affectation d'une valeur
à partir d'un entier ordinaire, d'un autre grand entier ou d'une chaîne
de caractères.
L'instruction~:
\sv
\indent{\tt x := of\_int($i$);}
\sv\noindent
est formellement équivalente aux instructions~:
\sv
\indent{\tt x := xnew; copy\_int(x,$i$);}
\sv\noindent
et de même pour {\tt of\_xint} et {\tt of\_string}.
L'exécution d'une opération par un appel tel que~:
\sv
\indent{\tt quo(x,a,b);}
\sv\noindent
est valide si et seulement si les conditions suivantes sont remplies~:
\begin{enumerate}
\item les variables {\tt a} et {\tt b} sont initialisées et désignent
des valeurs valides pour l'opération en question ({\tt a} désigne une
valeur quelconque et {\tt b} une valeur quelconque non nulle)~;
\item la variable {\tt x} contient un pointeur initialisé.
\end{enumerate}
\sv

Il n'est pas nécessaire que {\tt x} ait reçu une valeur préalablement à
cette opé\-ra\-tion. Si c'est le cas alors le bloc mémoire contenant cette valeur
est surchargé avec le résultat de l'opération s'il est suffisamment grand,
sinon un nouveau bloc mémoire est alloué pour recevoir le résultat, le pointeur
{\tt x} est modifié pour désigner ce nouveau bloc et l'ancien bloc est
désalloué.
\sv

Les opérations de type lecture-modification-écriture où une même variable
figure à la fois en positions opérande et résultat sont correctement
traitées. Par contre pour les opérations produisant plusieurs résultats
({\tt quomod}, {\tt gcd\_ex}, {\tt cfrac} et {\tt split}) une même variable
ne peut figurer plusieurs fois en position résultat.
Par exemple l'instruction suivante est illégale~:
\sv
\indent{\tt quomod(x,x,a,b); (* \sltt ill{\accent 19 e}gal \tt *)}
\sv

La procédure {\tt xfree} permet de forcer la libération du bloc mémoire
occupé par une valeur qui n'est plus utile. L'instruction~:
\sv
\indent{\tt xfree(x);}
\sv\noindent
a pour effet de désallouer la valeur désignée par {\tt x} s'il y en a une
et de réinitialiser le pointeur {\tt x}. La variable {\tt x} reste utilisable
après cette instruction pour recevoir une nouvelle valeur.
\sv

La conversion d'un grand entier en chaîne de caractères est réalisée par
la fonction {\tt string\_of} qui alloue une chaîne de caractères de taille
convenable et y copie la représentation décimale de son argument. Cette
chaîne sera automatiquement libérée par le gestionnaire mémoire de Free Pascal lorsqu'elle
ne sera plus référencée.
\sv

La procédure {\tt chrono} affiche sur le canal d'erreur standard le
temps CPU en secondes écoulé depuis le début du processus, la différence
avec le temps précédent et le message passé en  argument.
\sv

Enfin, la procédure {\tt failwith} affiche le message passé en argument
sur le canal d'erreur et arrête le programme.


                          %+---------------+
                          %|  Compilation  |
                          %+---------------+

\section{Compilation}
Les programmes Pascal utilisant \Numerix\ doivent être compilés avec la commande
suivante~:
\sv
{\tt ppc386 \it options \tt -Fu\it ppu\_path \tt -Fl\it lib\_path fichiers source}
\sv
{\tt -Fu\it ppu\_path} désigne le répertoire
contenant les fichiers compilés {\tt clong.ppu}, {\tt clong.o},
{\tt dlong.ppu}, {\tt dlong.o}, {\tt slong.ppu} et {\tt slong.o}.
{\tt -Fl\it lib\_path} désigne le répertoire contenant
la bibliothèque {\tt libcnumx.a}. Ces directives peuvent être omises si ces fichiers sont
stockés dans les répertoires normalement examinés par le compilateur Pascal.


                            %+-----------+
                            %|  Exemple  |
                            %+-----------+

\section{Exemple}

\begin{camllisting}
\begin{verbatim}
program simple;

 (* +--------------------------------------------------------------------------+
    |  Fichier simple.p: D§émonstration de la biblioth§èque Numerix avec Pascal  |
    +--------------------------------------------------------------------------+ *)

(* calcul de (sqrt(3) + sqrt(2))/(sqrt(3)-sqrt(2)) avec n d§écimales *)

uses clong; (* ou dlong ou slong *)

procedure calcul(var res : xint; n : longint);
var dix_n, dix_2n, sqrt_2, sqrt_3, x, y : xint;
begin
   dix_n  := xnew;
   dix_2n := xnew;
   sqrt_2 := xnew;
   sqrt_3 := xnew;
   x      := xnew;
   y      := xnew;

   copy_int(dix_n,5);
   pow     (dix_n,  dix_n,  n);       (* dix_n <- 5^n ...      *)
   shiftl  (dix_n,  dix_n,  n);       (*        ... * 2^n      *)
   sqr     (dix_2n, dix_n);           (* dix_2n <- 10^(2n)     *)
   mul_1   (sqrt_2, dix_2n, 200);     (* sqrt_2 <- ...         *)
   sqrt    (sqrt_2, sqrt_2);          (* .. sqrt(2*10^(2n+2))  *)
   mul_1   (sqrt_3, dix_2n, 300);     (* sqrt_3 <- ...         *)
   sqrt    (sqrt_3, sqrt_3);          (* .. sqrt(3*10^(2n+2))  *)
   add     (x,      sqrt_3, sqrt_2);  (* x <- sqrt_3 + sqrt_2  *)
   sub     (y,      sqrt_3, sqrt_2);  (* y <- sqrt_3 - sqrt_2  *)
   mul     (x,      dix_n,  x);       (* res <- trunc(10^n*x...*)
   quo     (res,    x,      y);       (*               ... /y) *)
   
   xfree(dix_n);
   xfree(dix_2n);
   xfree(sqrt_2);
   xfree(sqrt_3);
   xfree(x);
   xfree(y);

end;


(* programme principal *)
var n,i : longint;
    c   : word;
    x   : xint;
begin

   n := 30;
   x := xnew;
   i := 1;
   while i <= paramcount do begin
      if  paramstr(i) = '-test' then n := 30
      else if (paramstr(i) = '-n') and (i < paramcount) then begin
         i := i+1;
         val(paramstr(i),n,c);
      end
      else failwith('syntaxe : simple [-test] [-n <n>]');
      i := i+1;
   end;

   calcul(x,n);
   writeln(string_of(x));

   xfree(x);
   
end.
\end{verbatim}
\end{camllisting}
\sv\noindent
Compilation et exécution~:
\sv
\begin{verbatim}
> ppc386 -Fu/home/quercia/lib -Fl/home/quercia/lib simple.p
Free Pascal Compiler version 1.0.2 [2001/03/12] for i386
Copyright (c) 1993-2000 by Florian Klaempfl
Target OS: Linux for i386
Compiling simple.p
Assembling simple
Linking simple
69 Lines compiled, 0.1 sec
> ./simple
9898979485566356196394568149410
\end{verbatim}


                         %+----------------+
                         %|  Installation  |
                         %+----------------+

\chapter{Installation}

                        %+------------------+
                        %|  Téléchargement  |
                        %+------------------+

\section{Téléchargement}

\Numerix\ est disponible à l'URL suivant~:
\sv
\verb?http://pauillac.inria.fr/~quercia/cdrom/bibs/numerix.tar.gz?
\sv\noindent
Vous devez disposer du compilateur C {\tt gcc}
pour compiler les parties C et assembleur de la bibliothèque. A priori toute
version récente de {\tt gcc} devrait convenir, la bibliothèque a été
compilée avec succès sur un PC-Linux avec {\tt egcs-2.91.66}
ainsi que sur une station Dec
avec {\tt gcc 2.7.2.3} pour le module \Clong\ uniquement.
\sv
Pour Ocaml vous devez disposer d'une version supérieure ou
égale à~{\tt 1.05} et pour Camllight d'une version
supérieure ou égale à~{\tt 0.73}. Ocaml et Camllight sont disponibles
à l'URL~:
\sv
\verb?http://pauillac.inria.fr/caml/index-fra.html?
\sv\noindent
Si vous voulez inclure dans les interfaces Ocaml et Camllight le
module {\tt Gmp} vous devez disposer de \GMP\ qui est téléchargeable à
l'URL~:
\sv
\verb?http://www.swox.com/gmp/?
\sv
L'interface Pascal ne peut être compilée que sur un PC-Linux avec
le compilateur Free Pascal téléchargeable à l'URL~:
\sv
\verb?http://www.freepascal.org/?
\sv
Notez que l'interface Pascal utilise la version compilée de l'interface
C~: {\tt libcnumx.a}. Par conséquent l'interface Pascal ne peut être compilée
que si l'interface C l'est aussi.
\sv


                         %+-----------------+
                         %|  Configuration  |
                         %+-----------------+

\section{Configuration automatique}
Extraire l'archive {\tt numerix.tar.gz} dans un répertoire temporaire
et lancer le script de configuration qui se trouve à la racine.
\sv
\noindent
Sur un PC-Linux avec le shell {\tt bash} lancez~:
\sv
\indent\verb?./configure   2>&1 | tee conflog?\par
\sv
\noindent
Sur une station Dec avec le shell {\tt csh} lancez~:
\sv
\indent\verb?./configure       |& tee conflog?\par
\sv
Ce script détermine quelles  parties de \Numerix\ peuvent être compilées
sur votre  machine et crée  un fichier {\tt Makefile} et un fichier {\tt numconf}
correspondant  à votre
configuration. Le script {\tt configure} reconnaît les options suivantes~:

\begin{description}
\item{\tt- -prefix=\it dir}

Fixe la racine commune  aux répertoires d'installation~:

\indent\verb?INSTALL_LIB     = ?{\it dir}\verb?/lib?,\par
\indent\verb?INSTALL_BIN     = ?{\it dir}\verb?/bin?,\par
\indent\verb?INSTALL_INCLUDE = ?{\it dir}\verb?/include?.\par
Le préfixe par défaut est \verb?$HOME?.%$

\item{\tt-{}-libdir=\it dir, \tt-{}-bindir=\it dir, \tt-{}-includedir=\it dir}

Fixe l'un  des répertoire \verb?INSTALL_LIB?, \verb?INSTALL_BIN?
et \verb?INSTALL_INCLUDE? indépendamment des autres.

\item{\tt-{}-enable-c\rm, \tt-{}-disable-c}

Sélectionne  ou  désélectionne  l'interface  C. Par  défaut  elle  est
sélectionnée.

\item{\tt-{}-enable-caml\rm, \tt-{}-disable-caml}

Sélectionne  ou désélectionne l'interface  Camllight. Par  défaut elle
est  sé\-lec\-tion\-née  si Camllight  a  été  trouvé  sur la  machine.

\item{\tt-{}-enable-ocaml\rm, \tt-{}-disable-ocaml}

Sélectionne ou  désélectionne l'interface  Ocaml. Par défaut  elle est
sé\-lec\-tion\-née si  Ocaml a  été trouvé sur  la machine.

\item{\tt-{}-enable-pascal\rm, \tt-{}-disable-pascal}

Sélectionne  ou désélectionne l'interface  Pascal. Par  défaut elle
est sé\-lec\-tion\-née si {\tt ppc386}  a  été  trouvé  sur la  machine.

\item{\tt-{}-enable-clong\rm, \tt-{}-disable-clong}

Sélectionne  ou  désélectionne le  module  \Clong.  Par  défaut il  est
sélectionné.

\item{\tt-{}-enable-dlong\rm, \tt-{}-disable-dlong}

Sélectionne  ou  désélectionne le  module  \Dlong.  Par  défaut il  est
sélectionné  si  le compilateur  C  reconnaît  le  type entier  double
précision  ({\tt long long})  et si  ce type  est effectivement  de longueur
double de celle  d'un {\tt long}.

\item{\tt-{}-enable-slong\rm, \tt-{}-disable-slong}

Sélectionne  ou  désélectionne le  module  \Slong.  Par  défaut il  est
sélectionné si  le processeur est  de type Intel  x86 et le  type {\tt long
long} est  correctement implémenté par  le compilateur C.

\item{\tt-{}-enable-gmp\rm, \tt-{}-disable-gmp}

Sélectionne  ou  désélectionne  le  module  {\tt Gmp}.  Par  défaut  il  est
sélectionné si la bibliothèque \GMP\  a été trouvée sur la machine.

\item{\tt-{}-enable-caml\_bignum\rm, \tt-{}-disable-caml\_bignum}\par\vskip-5pt\vskip0pt
\item{\tt-{}-enable-ocaml\_bignum\rm, \tt-{}-disable-ocaml\_bignum}

Sélectionne ou  désélectionne le module {\tt Big}  séparément pour Camllight
et pour Ocaml. Par défaut ce module est sélectionné si la bibliothèque
{\tt libnums} associée  a été  trouvée.

\item{\tt-{}-disable-lang}

Désélectionne  tous les  langages non  explicitement  sélectionnés par
une option {\tt-{}-enable-xxx}.

\item{\tt-{}-disable-modules}

Désélectionne  tous  les modules  non  explicitement sélectionnés  par
une option {\tt-{}-enable-xxx}.

\item{\tt-{}-disable-all}

Désélectionne  toutes  les  combinaisons  de langage  et  modules  non
explicitement sélectionnées par une option {\tt-{}-enable-xxx}.

\end{description}



                  %+-------------------------------+
                  %|  Edition du fichier numconf  |
                  %+-------------------------------+

\section{\'Edition du fichier {\tt numconf}}

Normalement le script {\tt configure} décrit à la section précédente
devrait créer un fichier {\tt numconf} convenable. En cas de problème
vous pouvez éditer ce fichier et corriger les valeurs indiquées
par {\tt configure} si elles sont incorrectes.
Si vous modifiez {\tt numconf}, il faut recréer un fichier {\tt Makefile}
pour prendre en compte les modifications effectuées. Pour ce faire
lancez la commande~:
\sv
\indent\verb?make -f numconf makerules?\par
\sv
La plupart de ces paramètres renseignés dans le fichier {\tt numconf}
doivent recevoir une valeur booléenne avec la convention
$0 =$ faux et $1 =$ vrai. A titre d'exemple, les fichiers {\tt logs/pentium/numconf}
et {\tt logs/alpha/numconf} contiennent les réglages ayant servi pour les tests
sur processeurs Pentium et Digital alpha.

\begin{verbatim}
BITS = 32
\end{verbatim}
Indiquez la taille d'un mot machine, les seules valeurs reconnues sont
$32$ et $64$.
\sv

\begin{verbatim}
USE_CLONG        = 1
USE_DLONG        = 1
USE_SLONG        = 1
USE_GMP          = 1
USE_CAML_BIGNUM  = 1
USE_OCAML_BIGNUM = 1
\end{verbatim}
Indiquez les modules que vous voulez compiler, plusieurs modules
peuvent être sélectionnés. Les modules \Dlong\ et \Slong\ ne peuvent
être compilés que pour les machines équipées d'un processeur Intel x86.
Les modules {\tt Gmp} et {\tt Big} ne peuvent être compilés
que si vous disposez de \GMP\ et {\tt BigNum}.
\sv

\begin{verbatim}
MAKE_C_LIB      = 1
MAKE_OCAML_LIB  = 1
MAKE_CAML_LIB   = 1
MAKE_PASCAL_LIB = 1
\end{verbatim}
Indiquez quelles interfaces vous voulez compiler.
\sv

\begin{verbatim}
GCC = gcc -O2 -Wall
AR  = ar -rc
RANLIB = ranlib
\end{verbatim}
Indiquez les commandes de compilation C et de création d'archives.
Vous pouvez au besoin ajouter des directives {\tt -Ixxx} et
{\tt -Lxxx} si le compilateur ou l'éditeur de liens ne trouvent
pas par eux-mêmes certains fichiers d'en-tête ou certaines bibliothèques

\begin{verbatim}
OCAML_VERSION = 204
OCAML_LIBDIR  = /usr/local/lib/ocaml
CAML_VERSION  = 074
CAML_LIBDIR   = /usr/local/lib/caml-light
\end{verbatim}
Indiquez sans point décimal les versions de Ocaml et Camllight dont
vous disposez et les répertoires où sont installées ces versions.
Ces variables ne sont consultées que si les interfaces
Ocaml et Camllight doivent être compilées.
\sv

\begin{verbatim}
CAML       = camlc
CAMLLIBR   = camllibr
CAMLMKTOP  = camlmktop

OCAML      = ocamlc
OCAMLOPT   = ocamlopt
OCAMLMKTOP = ocamlmktop
\end{verbatim}
Indiquez les commandes que {\tt make} doit lancer pour invoquer
respectivement le compilateur Camllight, l'archiveur
Camllight, le compilateur de toplevel Camllight, le compilateur Ocaml,
le compilateur optimiseur Ocaml et le compilateur de toplevel Ocaml.
Normalement les chaînes ci-dessus doivent convenir, éventuellement
avec une indication du chemin d'accès.
\sv

\begin{verbatim}
PPC = ppc386
\end{verbatim}
Indiquez la commande que {\tt make} doit lancer pour invoquer
le compilateur Pascal.
Normalement la chaîne ci-dessus doit convenir, éventuellement
avec des indications de chemins d'accès.
\sv

\begin{verbatim}
INSTALL_LIB     = $(HOME)/lib
INSTALL_INCLUDE = $(HOME)/include
INSTALL_BIN     = $(HOME)/bin
\end{verbatim} %$
Indiquez dans quels répertoires doivent être installés les bibliothèques
compilées, les fichiers d'en-tête (pour l'interface C uniquement) et
les programmes exécutables.
La variable {\tt INSTALL\_LIB} est recopiée en dur dans les systèmes
interactifs {\tt ocamlnumx} pour Ocaml $\geqslant 1.07$ et {\tt camlnumx}
pour Camllight $\geqslant 0.74$ de façon que ces systèmes puissent trouver
par eux-mêmes les interfaces compilées {\tt numerix.cmi} et {\tt numerix.zi}.
Les versions antérieures de Ocaml et Camllight ne disposent pas de cette
facilité et vous devrez indiquer par une directive {\tt directory} l'emplacement
de ces interfaces compilées à chaque lancement du système interactif.
De même l'activation des pretty-printers n'est faite automatiquement
qu'à partir de Ocaml-1.07 et Camllight-0.74~; pour les versions an\-té\-rieu\-res
vous devrez l'effectuer manuellement avec la directive {\tt install\_printer}.

            %+------------------------------------------+
            %|  Modification de la bibliothèque Bignum  |
            %+------------------------------------------+

\section{Modification de la bibliothèque Bignum}
\label{modif-bignum}
A partir de la version Ocaml-3.00 les données externes à Ocaml peuvent
être comparées, hachées, exportées et importées via les fonctions génériques
de Ocaml, sous réserve de fournir des interfaces avec ces fonctions génériques.
Les modules {\tt Clong}, {\tt Dlong}, {\tt Slong} et {\tt Gmp}
comportent de telles interfaces, mais le module {\tt Big\_int}, livré en
standard avec Ocaml, ne comporte
que les interfaces avec les fonctions d'exportation et d'importation, il
manque les interfaces avec les fonctions de comparaison et de hachage.
\sv
Le répertoire {\tt lib/ocaml} de \Numerix\ contient un fichier correctif
fournissant ces interfaces manquantes, {\tt bignum.patch}, il peut être
utilisé pour modifier la bibliothèque {\tt Bignum} afin de disposer
des comparaisons et du hachage générique avec le module {\tt Big}.
\sv
{\it Note~: les modifications décrites ci-dessous sont facultatives.
Si vous ne comptez pas utiliser le module {\sltt Big}, ou si vous pouvez
l'utiliser sans les comparaisons et le hachage génériques, il est
déconseillé de procéder à ces modifications~; vous pouvez passer
dans ce cas à la section suivante~: {\bf Compilation}.
}
\sv
Vous devez disposer des sources Ocaml. Décompactez les dans un répertoire
temporaire, placez vous à la racine de l'arborescence créée
({\tt ocaml-}{\it x.yy}) et lancez la commande~:
\sv
\noindent\verb?patch -b -p0 <.../numerix-0.19/lib/ocaml/bignum.patch?\par
\sv\noindent
où {\tt ...} désigne le répertoire contenant les sources de \Numerix.
Le programme {\tt patch} doit rendre compte de la correction avec un message tel que~:
\sv
Sur un PC Linux\sv
\noindent{\sltt patching file otherlibs/num/nat\_stubs.c}\sv
Sur une Station Dec\sv
\begin{clisting}\begin{verbatim}§sltt
Hmm...  Looks like a new-style context diff to me...
The text leading up to this was:
--------------------------
|*** otherlibs/num/nat_stubs.c.orig     Fri Aug 24 15:08:51 2001
|--- otherlibs/num/nat_stubs.c  Fri Aug 24 15:46:58 2001
--------------------------
Patching file otherlibs/num/nat_stubs.c using Plan A...
Hunk #1 succeeded at 11.
Hunk #2 succeeded at 30.
Hunk #3 succeeded at 335.
done
\end{verbatim}\end{clisting}
\sv\noindent
S'il n'y a pas d'erreur, vous pouvez recompiler et installer le
système Ocaml modifié.

                          %+---------------+
                          %|  Compilation  |
                          %+---------------+

\section{Compilation}

Après configuration et adaptation éventuelle du fichier {\tt numconf} vous pouvez
lancer la compilation. Les cibles sont~:
\begin{description}
\item{\tt lib}~:\par
compile les bibliothèques et les fichiers d'interface~;

\item{\tt exemples}~:\par
compile les exemples~;

\item{\tt test}~:\par
exécute chaque programme exemple avec l'option {\tt -test}~;

\item{\tt install}~:\par
copie les bibliothèques, les fichiers d'en-tête et
les exécutables dans les répertoires désignés par les variables
{\tt INSTALL\_xxx}~;

\item{\tt clean}~:\par
supprime tous les fichiers compilés.
\end{description}
\sv
\noindent
Sur un PC-Linux avec le shell {\tt bash} lancez dans cet ordre~:
\sv
\indent\verb?make lib      2>&1 | tee liblog?\par
\indent\verb?make exemples 2>&1 | tee exlog?\par
\indent\verb?make test     2>&1 | tee testlog?\par
\sv
\noindent
Sur une station Dec avec le shell {\tt csh} lancez dans cet ordre~:
\sv
\indent\verb?make lib      |& tee liblog?\par
\indent\verb?make exemples |& tee exlog?\par
\indent\verb?make test     |& tee testlog?\par
\sv

Il ne doit y avoir aucune erreur de compilation ni aucun message
d'avertissement. S'il y en a et si vous n'arrivez pas à débloquer
la situation envoyez par courrier électronique les fichiers de
trace {\tt liblog}, {\tt exlog} et {\tt testlog}
à \verb?michel.quercia@prepas.org? pour analyse. Si vous avez rencontré des
pro\-blè\-mes que vous avez pu résoudre par vous même, merci de me le
faire savoir afin que je puisse corriger les fichiers fautifs.
Les fichiers {\tt logs/pentium/xxxlog} et {\tt logs/alpha/xxxlog}
inclus dans la distribution contiennent les traces de compilation
sur un PC-Linux et sur une station Dec, vous pouvez vous y référer
en cas de problème.
\sv

Si la compilation et les tests se sont déroulés correctement vous pouvez
installer la bibliothèque \Numerix\ avec l'une des commandes~:
\sv
\indent\verb?make install  2>&1 | tee inslog?\par
\indent\verb?make install  |& tee inslog?\par
\sv\noindent
Ceci a pour effet de copier les fichiers~:
\sv
\begin{verbatim}
big.zi       dlong.o      infgmp.zi    numcount.cmi slong.ppu    
camlnumx     dlong.zi     infslong.zi  numerix.a    slong.o      
clong.ppu    gmp.zi       libclnumx.a  numerix.cma  slong.zi     
clong.o      infbig.zi    libcnumx.a   numerix.cmi  
clong.zi     infclong.zi  libmlnumx.a  numerix.cmxa 
dlong.ppu    infdlong.zi  numcmp.cmi   numerix.zo    
\end{verbatim}
dans le répertoire \verb?$(INSTALL_LIB)?, %$
\sv
\begin{verbatim}
c-long_int.h  long_int.h
\end{verbatim}
dans le répertoire \verb?$(INSTALL_INCLUDE)? et %$
\sv
\begin{verbatim}
ocamlnumx
\end{verbatim}
dans le répertoire \verb?$(INSTALL_BIN)? %$
ou seulement certains de ces fichiers selon les modules et interfaces que vous
avez sélectionné.
\sv
L'installation est terminée, vous pouvez à présent vous adonner aux joies du
calcul multiprécision. Le guide d'utilisation que vous lisez présentement
est disponible dans le sous-répertoire {\tt doc} au format Postscript
({\tt numerix.ps}) et au format \LaTeX\ ({\tt numerix.tex}) si vous désirez
l'imprimer.

                   %+----------------------------+
                   %|  Description des exemples  |
                   %+----------------------------+

\section{Description des exemples}

Le sous-répertoire {\tt exemples} contient divers programmes utilisant
\Numerix\ en C, Camllight, Ocaml ou Pascal. Chaque exemple est placé dans
un répertoire distinct avec des sous-répertoires {\tt c}, {\tt caml},
{\tt ocaml} et {\tt pascal} contenant les fichiers source correspondant.
\sv

Les programmes
écrits en C sont fournis en deux versions~: une utilisant la bibliothèque
\Numerix\ (fichier {\tt xxx-long.c}) et une utilisant la bibliothèque
\GMP\ (fichier {\tt xxx-gmp.c}). Les exécutables produits ont un nom
de la forme~:
{\tt xxx-c}, {\tt xxx-d}, {\tt xxx-s} et {\tt xxx-g}, la dernière lettre
indiquant quel module est utilisé.
\sv

Les programmes écrits en Camllight comportent un fichier source
{\tt xxx.mlp} qui après compilation donne les exécutables
{\tt xxx-b}, {\tt xxx-c}, {\tt xxx-d}, {\tt xxx-s} et {\tt xxx-g},
la dernière lettre indiquant quel module est utilisé.
\sv

Les programmes écrits en Ocaml comportent un seul fichier source
{\tt xxx.ml} qui après compilation donne les exécutables
{\tt xxx-byte} (produit par le compilateur {\tt ocamlc})
et {\tt xxx-opt}  (produit par le compilateur {\tt ocamlopt}).
Chacun de ces exécutables contient tous les modules grands entiers
compilés et un module particulier est sélectionné à l'exécution
par une option {\tt -e \it module} comme décrit à la
section~\ref{selmodule}.
\sv

Les programmes écrits en Pascal comportent un fichier source
{\tt xxx.p} qui après compilation donne les exécutables
{\tt xxx-c}, {\tt xxx-d} et {\tt xxx-s},
la dernière lettre indiquant quelle unité est utilisée.
\sv


\subsection{chrono}
Mesure de vitesse des différentes bibliothèques (source en C uniquement).
Ce programme tire au hasard des grands entiers de $n$ et $2n$ bits et
chronomètre le temps de diverses opérations entre ces entiers~:
\begin{description}
\item\verb?mul   ?~: multiplication $n$ bits par $n$ bits~;
\item\verb?sqr   ?~: carré d'un nombre de $n$ bits~;
\item\verb?quomod?~: division avec reste $2n$ bits par $n$ bits~;
\item\verb?quo   ?~: division sans reste $2n$ bits par $n$ bits~;
\item\verb?sqrt  ?~: racine carrée d'un entier de $2n$ bits~;
\item\verb?gcd   ?~: pgcd de deux entiers de $n$ bits~;
\item\verb?gcd_ex?~: pgcd et coefficients de Bézout de deux entiers de $n$ bits~;
\item\verb?all   ?~: toutes les opérations ci-dessus.
\end{description}
\sv\noindent
Indiquez sur la ligne de commande la valeur de $n$ et quelles opérations
effectuer parmi {\tt mul}, {\tt sqr}, {\tt quomod}, {\tt quo}, {\tt sqrt}, {\tt gcd} et
{\tt gcd\_ex}. Vous pouvez indiquer un facteur de répétition par l'option
{\tt -r \it r}, dans ce cas chaque opération est répétée $r$ fois.
Par exemple~:
\begin{verbatim}
> exemples/chrono/c/chrono-g -all 1000000
    0.11     0.11 début
    1.15     1.04 mul
    1.84     0.69 sqr
    6.02     4.18 quomod
   10.23     4.21 quo
   40.09    29.86 sqrt
  242.48   202.39 gcd
  698.63   456.15 gcd_ex
> exemples/chrono/c/chrono-s -all 1000000
    0.23     0.23 début
    1.11     0.88 mul
    1.65     0.54 sqr
    6.50     4.85 quomod
   10.62     4.12 quo
   13.93     3.31 sqrt
   45.59    31.66 gcd
   86.28    40.69 gcd_ex
\end{verbatim}
Donc sur la machine de test (Pentium-233Mhz) avec le module {\tt Gmp-3}
une multiplication entre
deux nombres d'un million de bits prend $1.04$ secondes, le carré
d'un nombre d'un million de bits prend $0.69$ secondes, etc.
Le second test présente les temps correspondants pour le module \Slong.

\subsection{hamming}
Calcul du $k$-ème entier de Hamming ou du rang d'un entier de Hamming
(source en Ocaml uniquement).
Le problème de Hamming et divers algorithmes le résolvant sont
présentés dans les documents~:
\sv
\indent\verb?http://pauillac.inria.fr/~quercia/papers/hamming.ps.gz?

\indent\verb?http://pauillac.inria.fr/~quercia/papers/hamming-new.ps.gz?
\sv
Le programme donné en exemple avec \Numerix\ implémente un
algorithme non publié de complexité $O(k^{1/3}\ln(k))$
({\tt hamming-new} implémente un algorithme plus rapide en $O(\ln(k)^3)$).
La syntaxe de la ligne de commande est~:
\sv
\indent{\tt hamming-xxx ($k$ \verb?|? $x$ $y$ $z$) [-d] [-v {\it volume}] [-e {\it entiers}]}
\sv\noindent
où $k$ désigne le rang de l'entier cherché et $x,y,z$ les exposants
de $2,3,5$ d'un entier de Hamming dont on cherche le rang.
{\tt -d} demande d'afficher les différentes phases du programme avec
chronométrage, {\tt -v \it volume} désigne la méthode de calcul des volumes
de tétraèdres ({\tt surplace} ou {\tt func}) et {\tt -e \it entiers}
le module des grands entiers à utiliser. Par exemple~:
\sv
\begin{verbatim}
> exemples/hamming/ocaml/hamming-opt -e slong -d -v func 1T
    0.03     0.03 entiers    Slong
    0.03     0.00 volume     func
    0.03     0.00 Paramètres 12081378335  14699735488
    0.03     0.00            19148531618  23298529519
    0.03     0.00            28052091781  34131728817
    0.03     0.00 Approx     338940570505262 -> 1000267456452
    0.03     0.00 Approx     338910358662417 -> 1000000023838
    0.03     0.00 Approx     338910355969313 -> 1000000000001
    0.37     0.34 Exact      338910355969313 -> 1000000000001
    0.40     0.03 n2         338910355969346
    0.42     0.02 n0         338910355969286
    0.50     0.08 n1         338910355969190
    0.51     0.01 Dichotomie 8295 2426 6853
x = 8295  y = 2426  z = 6853
\end{verbatim}
\sv

\subsection{nextpp}
Recherche d'entiers pseudo-premiers avec le test de Rabin-Miller (source en
Caml, Ocaml, C et Pascal).
Ce programme prend deux entiers $n$ et $a$ dans cet ordre sur la ligne de commande
et recherche le plus petit entier à~$n+1$ chiffres pseudo-premier en
base~$a$. Par exemple~:
\sv
\begin{verbatim}
> time exemples/nextpp/c/nextpp-s 1000 2
453

real    0m32.111s
user    0m31.980s
sys     0m0.000s
> time exemples/nextpp/c/nextpp-g 1000 2
453

real    0m30.184s
user    0m30.050s
sys     0m0.010s
\end{verbatim}
ce qui signifie que $10^{1000}+453$ est le plus petit entier à~$1001$ chiffres
pseudo-premier en base~$2$. Ce programme est le seul parmi tous les exemples
où {\tt Gmp-3} se révèle plus rapide que \Slong, du fait d'une plus grande vitesse
de calcul pour l'exponentiation modulaire.
\sv

\subsection{pi}\label{pi}
Calcul des $n$ premières décimales de $\pi$ (sources en C, Camllight, Ocaml et Pascal).
Ce programme implémente le calcul approché de $\pi$ décrit dans le
manuel de référence de la bibliothèque {\tt BigNum}
({\it The Caml Numbers Reference Manual}, Inria, RT-0141) avec une technique
de sommation dichotomique pour accélérer le calcul de la série
(les termes sont additionnés deux par deux, puis $4$ par $4$, puis $8$ par $8$
etc). Par exemple~:
\sv
\begin{verbatim}
> exemples/pi/ocaml/pi-opt -e slong -d 1000000 -skip
    0.03     0.03 Slong, surplace
    1.67     1.64 puiss 5
   19.75    18.08 sqrt
  122.44   102.69 série lb=6875847 ls=6875855
  150.43    27.99 quotient
  186.94    36.51 conversion
3.
14159 26535  89793 23846  26433 83279  50288 41971  69399 37510
... (19998 lignes omises)
56787 96130  33116 46283  99634 64604  22090 10610  57794 58151
\end{verbatim}
\sv

\subsection{simple}
Programme de démonstration élémentaire (sources en C, Camllight, Ocaml et Pascal).
Ce programme sert de support à la présentation des interfaces
de \Numerix. Il calcule les $n$ premières décimales du
nombre $(\sqrt3 + \sqrt2)/(\sqrt3 - \sqrt2)$. Par exemple~:
\sv
\begin{verbatim}
> exemples/simple/ocaml/simple-opt -e slong -n 50
989897948556635619639456814941178278393189496131335
\end{verbatim}

                    %+---------------------------+
                    %|  Informations techniques  |
                    %+---------------------------+

\chapter{Informations techniques}

%--------------------------------------------------------- Algorithmes
\section{Algorithmes}

\subsection{Multiplication}
\Numerix\ implémente quatre algorithmes de multiplication chacun
adapté à une certaine plage d'opérandes.
\sv
\begin{description}
\item{\tt mul\_2}~: multiplication d'un entier long par un entier
simple ou double précision. Cette multiplication est effectuée par
une simple boucle avec une complexité $O(n)$, $n$ étant la
taille de l'entier long.

\item{\tt mul\_n2}~: multiplication de deux entiers longs,
le plus court ayant au plus {\tt klim} chiffres. Cette multiplication
est effectuée par une double boucle de complexité $O(np)$ où $n$
et $p$ sont les tailles des opérandes.

\item{\tt karamul}~: multiplication de deux entiers longs
ayant entre {\tt klim} et {\tt flim} chiffres par l'algorithme
\og\ diviser pour régner \fg\ de Karatsuba. Soient $n\leqslant p$
les tailles des opérandes~: si $n \leqslant p/2$ on divise l'opérande
de taille $p$ en deux blocs de tailles $n$ et $p-n$ et on calcule
récursivement les deux produits correspondants ($n\times n$ et
$n\times(p-n)$). Lorsque $n=p$ la complexité de cette multiplication
est $O(n^{\ln(3)/\ln(2)})$.

\item{\tt fftmul}~: multiplication de deux entiers longs
de tailles $n$, $p$ avec {\tt klim} $< n \leqslant p$ et
$p \geqslant$ {\tt flim} par l'algorithme de transformée de Fourier
rapide modulaire de Schönage. Les opérandes et le produit sont considérés
comme des polynômes à coefficients dans $\mathbb Z/(2^{2^\ell}+1)\mathbb Z$,
le polynôme produit étant calculé à partir de ses valeurs aux
points $2^k$, $0\leqslant k < 2^{\ell+1}$ qui forment un sous-groupe
multiplicatif cyclique de $(\mathbb Z/(2^{2^\ell}+1)\mathbb Z)^*$. Le paramètre
$\ell$ est déterminé en fonction de la taille du {\it nombre\/} produit
de façon à minimiser une estimation approximative du temps de calcul.
Typiquement $5 \leqslant \ell \leqslant 15$.
La complexité théorique de l'algorithme de Schönage est $O(n\ln(n))$
pour un produit de taille~$n$ si $\ell$ peut être stocké sur
un mot machine, $O(n\ln(n)\ln(\ln n))$ sinon (cas inenvisageable
avec les machines actuelles). Cependant la méthode de choix
de~$\ell$ ne permet pas d'estimer facilement la complexité de
l'implémentation, expérimentalement le temps de calcul est presque
linéaire jusqu'à une dizaine de millions de bits, au delà apparaissent
des problèmes dûs à la quantité importante de mémoire auxiliaire consommée.
\end{description}
\sv
Les valeurs de {\tt klim} et {\tt flim} ont été déterminées par
tâtonnements. Pour une architecture 32 bits avec des chiffres de
32 bits, on a {\tt klim} $= 32$ chiffres, {\tt flim} $=937$ chiffres c'est-à-dire
que l'on bascule de la multiplication naïve en $O(n^2)$ à celle
de Karatsuba à partir de $1024$ bits et de la multiplication de
Karatsuba à celle de Schönage à partir de $30000$ bits.
\sv

Les algorithmes de multiplication naïve et de Karatsuba sont décrits
dans de de nombreux ouvrages, notamment
{\it Knuth, The art of computer programming, vol. 2}.
La multiplication par FFT modulaire de Schönage est décrite
dans un article de Paul Zimmermann~:
\sv
\verb? http://www.loria.fr/~zimmerma/bignum/schon.ps.gz?
\sv
Ces algorithmes sont également utilisés pour l'élévation au carré, en tenant
compte des simplifications dues au fait que les deux opérandes à
multiplier sont égaux.

\subsection{Division}
\Numerix\ implémente trois algorithmes de division chacun
adapté à une certaine plage d'opérandes.
\sv
\begin{description}
\item{\tt quo\_2}~: division d'un entier long par un entier
simple ou double précision, par une simple boucle de complexité
$O(n)$ où $n$ est la taille du dividende.

\item{\tt quo\_n2}~: division de deux entiers longs par l'algorithme
\og\ scolaire \fg\ lorsque la taille du diviseur ou celle du quotient
est inférieure à {\tt klim}. Cette division a pour complexité
$O(np)$ où $n$ est la taille du diviseur et $p$ celle du quotient.

\item{\tt bzdiv}~: division de deux entiers longs par l'algorithme
de Burnikel et Ziegler, qui est une version \og\ diviser pour régner \fg\
de l'algorithme scolaire. Avec cet algorithme le temps nécessaire
pour diviser un nombre de $2n$ bits par un nombre de $n$ bits
est de l'ordre de deux multiplications $n$ bits par $n$ bits dans la
plage de la multiplication de Karatsuba, et de l'ordre de $C\ln(n)$
multiplications $n$ bits par $n$ bits dans la plage de la multiplication
par FFT. En pratique, jusqu'à une dizaine de millions de bits, le
temps d'une division $2n$ par $n$ bits est au plus égal à celui de cinq
multiplications $n$ bits par $n$ bits.
\end{description}
\sv

Le calcul de la racine carrée entière d'un entier long est réalisé
par des algorithmes similaires~: pour calculer $b=\lfloor\sqrt a\rfloor$
on divise $a$ par $b$, le quotient est à deux unités près
le nombre $b$ cherché. Pour effectuer cette division, il suffit
de connaître le chiffre de tête de $b$, les chiffres suivants sont déterminés
en cours de calcul, c'est-à-dire que l'on découvre le diviseur au fur et
à mesure que la division se déroule. Le chiffre de tête de $b$ est déterminé
indépendamment par itération de Newton. Le temps nécessaire au calcul
de la racine carrée d'un nombre de $2n$ bits selon cette méthode
est théoriquement
de l'ordre de la moitié du temps d'une division $2n$ bits par $n$ bits, soit
équivalent à une multiplication $n$ bits par $n$ bits dans la
plage de la multiplication de Karatsuba (rapport effectivement observé), et $C\ln(n)/2$ dans la plage de la
multiplication par FFT (temps constaté pour $10^7$ bits: $3$ multiplications).
\sv

La conversion d'un entier long en décimal peut être effectuée par divisions
successives par~$10$, mais cela conduit à un algorithme de complexité
$O(n^2)$ pour convertir un nombre de~$n$ bits. La méthode utilisée par
\Numerix\ consiste à calculer, par élévations au carré successives,
une puissance de~$10$~: $10^{2^k}$ dont la longueur est comprise
entre $n/2$ et $n$ bits puis à diviser le nombre à convertir par $10^{2^k}$
et à convertir récursivement le quotient et le reste à l'aide des
puissances de $10$ déjà calculées. De la sorte
le temps de conversion est de l'ordre de deux multiplications $n$ bits
par $n$ bits dans la plage de la multiplication de Karatsuba et
$O(\ln(n))$ multiplications $n$ bits par $n$ bits dans la plage de la multiplication
par FFT.
\sv

\noindent
{\bf Références}
\begin{description}
\item{Division scolaire}~:
{\it Knuth, The art of computer programming, vol. 2}

\item{Division de Burnikel et Ziegler}~:
{\it Fast recursive division,
Rapport de recherche MPI-I-98-1-022, Max Planck Institut Für Informatik, octobre 1998}

\item{Racine carrée récursive (Zimmermann)}~:
{\it Karatsuba square root,
Rapport de recherche INRIA \no\ 3805, \tt http://www.inria.fr/RRRT/RR3805.html}

\item{Versions itératives des algorithmes de Burnikel-Ziegler et Zimmermann}~:\par
{\it Division et racine carrée rapides,\par
\verb?http://pauillac.inria.fr/~quercia/papers/divrap.ps.gz?}
\end{description}


\subsection{Pgcd}
Le calcul du pgcd de deux entiers a fait l'objet de moins d'efforts
d'optimisation que les algorithmes précédents, un seul algorithme est
implémenté~: une version \og\ diviser pour régner \fg\ de l'algorithme
de Lehmer.
\sv

L'algorithme de Lehmer, décrit dans le livre de Knuth, pour calculer le pgcd et
les coefficients de Bézout de deux entiers longs $a$ et $b$  consiste à
développer en fraction continue le nombre $a/b$, les quotients
obtenus étant ceux de l'algorithme d'Euclide appliqué à $a$ et $b$.
Les quotients sont déterminés à partir des chiffres de tête $\alpha$
et $\beta$ de~$a$ et~$b$ en développant en parallèle en fraction
continue $\alpha/(\beta+1)$ et $(\alpha+1)/\beta$ qui encadrent
$a/b$ tant que les quotients sont identiques pour les deux fractions
encadrantes. Lorsque l'on constate une différence, on applique la transformation
homographique obtenue à $a$ et $b$ puis on itère la méthode. Cet algorithme
a la même complexité asymptotique que l'algorithme d'Euclide, soit
$O(n^2)$ pour des opérandes de $n$ bits, mais la plupart des opérations sont
effectuées en simple précision et il se révèle en pratique environ $20$ fois plus
rapide que l'algorithme d'Euclide sur des grands nombres.
\sv

\Numerix\ implémente une version \og\ diviser pour régner \fg\ de la méthode
de Lehmer en prenant pour $\alpha$ et $\beta$ les moitiés hautes de
$a$ et $b$ ce qui donne une complexité théorique de $18$ multiplications
$n$ bits par $n$ bits dans la plage de la multiplication de Karatsuba
(confirmée expérimentalement)
et $O(\ln(n))$ multiplications $n$ bits par $n$ bits dans la plage de la
multiplication par FFT (pour $10^6$ bits, le rapport est de l'ordre de~$45$).
Cette version récursive de l'algorithme de Lehmer n'est décrite dans aucun des
documents que j'ai consultés, par contre Schönage a donné un
algorithme apparenté, appelé {\it demi-pgcd\/} ou {\it descente controlée},
décrit dans~:
\sv
{\it Equation Solving in Terms of Computational Complexity,\par
Proceedings of the International Congress of Mathematicians, 1987}

%------------------------------------------------- Différences avec Numerix-0.13
\section{\hbox{Différences entre \Numerix-0.13 et \Numerix-0.17\hskip-15pt}}

\Numerix-0.13 était la première version publique, diffusée en février 2000.
Les versions suivantes 0.14, 0.15 et 0.16 ont réglé au coup par coup
quelques problèmes de compilation signalés par certains utilisateurs, et
cette version, \Numerix-0.17, est la deuxième véritable version publique.
\sv
\noindent{\bf Noyau}~:
\begin{description}
\item{}Réécriture complète du noyau C et assembleur de façon à permettre
une évolution ultérieure plus facile.

\item{}Correction d'un bogue dans le code de la division rapide
(dépassement de capacité dans une soustraction, signalé par
YAMAGATA yoriyuki).

\item{}Implémentation de la multiplication de Schönage.
\end{description}
\sv

\noindent{\bf Interface Caml/Ocaml}~:
\begin{description}
\item{}Mise à jour pour Ocaml-3.00.

\item{}Ajout de la racine $p$-ème entière (algorithme de Newton récursif).

\item{}Ajout de l'exponentiation modulaire.

\item{}Le module {\tt Cmp} a été séparé de \Numerix\ pour devenir un module
indépendant~: {\tt Numcmp}.

\item{}Ajout du module {\tt Numcount}.

\item{}Regroupement du code compilé de {\tt Numerix}, {\tt Numcmp}
et {\tt Numcount} dans les archives {\tt numerix.cma}, {\tt numerix.cmxa}
au lieu de  {\tt numerix.cmo}, \hbox{\tt numerix.cmx.\hskip-6pt}

\end{description}

%------------------------------------------------- Différences avec Numerix-0.17
\section{\hbox{Différences entre \Numerix-0.17 et \Numerix-0.18\hskip-15pt}}
\sv
\noindent{\bf Noyau}~:
\begin{description}

\item{}Correction d'un bogue dans la division par un nombre de deux chiffres.

\item{}Codage de l'exponentiation modulaire en C, elle reste moins rapide que celle
de \GMP\ dans le cas où la base est un grand nombre.

\item{}Adaptation des règles de compilation pour OpenBSD-2.8.

\end{description}
\sv

\noindent{\bf Interface C}~:
\begin{description}
\item{}Ajout de la fonction {\tt root}.
\end{description}
\sv

\goodbreak
\noindent{\bf Interface Pascal}~:
\begin{description}
\item{}Première publication de l'interface Pascal.
\end{description}


%------------------------------------------------- Différences avec Numerix-0.18
\section{\hbox{Différences entre \Numerix-0.18 et \Numerix-0.19\hskip-15pt}}
\sv
\noindent{\bf Interface Ocaml}~:
\begin{description}
\item{}Suppression des opérateurs de comparaison entre grands entiers
dans le foncteur {\tt Infixes},
les opérateurs de comparaison polymorphes de Ocaml deviennent utilisables
avec les grands entiers.

\item{}Implémentation des fonctions de hachage, de sérialisation et de désérialisation.
\end{description}

\vfil\centerline{\vrule height0.4pt width 3cm}
\end{document}

