%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% Macros définies dans le livre %% %% « Apprendre à programmer en TeX » %% %% et nécessaires à sa compilation %% %% %% %% Encodage ISO 8859-1 %% %% _____ %% %% %% %% © 2014 Christian Tellechea %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% Ce fichier contient celles des macros définies dans le livre qui sont %% %% nécessaires à sa compilation. %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Les codes et les macros donnés dans ce fichier sont diffusés sous licence % libre « Creative Commons "BY-SA" ». % % https://creativecommons.org/licenses/by-sa/4.0/ % https://creativecommons.org/licenses/by-sa/4.0/legalcode % % Vous êtes autorisé à % % * partager : copier, distribuer et communiquer le matériel par tous moyens % et sous tous formats ; % * adapter : remixer, transformer et créer à partir du matériel pour toute % utilisation, y compris commerciale % % selon les conditions suivantes % % * paternité : vous devez citer le nom de l'auteur de façon appropriée, % fournir un lien vers la licence, et indiquer si des modifications ont % été apportées. Vous pouvez le faire de manière raisonnable, mais pas % d'une façon qui suggère le donneur de licence vous approuve ou approuve % l'utilisation que vous en faites ; % * partage dans les mêmes conditions : dans le cas où vous effectuez un % remix, que vous transformez, ou créez à partir du matériel composant % l'Oeuvre originale, vous devez diffuser l'Oeuvre modifiée dans les mêmes % conditions, c'est-à-dire avec la même licence avec laquelle l'Oeuvre % originale a été diffusée. % % % Les commentaires dont sont assorties les macros dans le livre ne sont pas % sous licence libre et ne sont pas reproduits dans ce fichier. \edef\restoreatcoatcode{\catcode`\noexpand\@=\the\catcode`\@} \catcode`\@11 {\def\:{\let\sptoken= }\expandafter}\: % \long\def\gobone#1{} \long\def\identity#1{#1} \long\def\firstoftwo#1#2{#1} \long\def\secondoftwo#1#2{#2} \def\gobtwo#1#2{}% \def\defactive#1{% \catcode`#1=13 \begingroup \lccode`~=`#1 \lowercase{\endgroup\def~}} \def\letactive#1{% \catcode`#1=13 \begingroup \lccode`~=`#1 \lowercase{\endgroup\let~}} \def\litterate#1{% #1=lit le token frontière choisi \begingroup% ouvre un groupe pour y faire les modifications \def\do##1{\catcode`##1=12 }% \dospecials% rend inoffensifs tous les tokens spéciaux \defactive\^^M{\leavevmode\par}% \defactive\ {\space}% \defactive<{\string<{}}\defactive>{\string>{}}% \defactive-{\string-{}}\defactive`{\string`{}}% \defactive,{\string,{}}\defactive'{\string'{}}% \defactive#1{\endgroup}% #1 sera un \endgroup \tt% passe en police à chasse fixe } \long\def\firstto@nil#1#2\@nil{#1} \long\def\remainto@nil#1#2\@nil{#2} \def\rightofsc#1#2{% \exparg\ifin{#1}{#2}% {% si #1 contient le #2 \def\right@of##1#2##2\@nil{\def#1{##2}}% définit la macro auxiliaire \expandafter\right@of#1\@nil% appelle la macro auxiliaire }% {\let#1=\empty}% sinon, ne rien faire } \def\defname#1{\expandafter\def\csname#1\endcsname} \def\letname#1{\expandafter\let\csname#1\endcsname} \long\def\addtomacro#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}} \long\def\eaddtomacro#1#2{\expandafter\addtomacro\expandafter#1\expandafter{#2}} \long\def\addtotoks#1#2{#1\expandafter{\the#1#2}} \long\def\eaddtotoks#1#2{\expandafter\addtotoks\expandafter#1\expandafter{#2}} \long\def\swaparg#1#2{#2{#1}} \long\def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}} \let\exparg\expsecond \def\exptwoargs#1#2{\expsecond{\expsecond{#1}{#2}}} \def\sgn#1{\ifnum#1<0 -\fi} \def\abs#1{\sgn{#1}#1 } \def\truncdiv#1#2{% \exptwoargs\truncdiv@i{\number\numexpr#1\relax}{\number\numexpr#2\relax}% } \def\truncdiv@i#1#2{% \numexpr(2*\sgn{#1}#1-\sgn{#2}#2+1)/(\sgn{#1}2*#2)\relax } \def\ifinside#1[#2,#3]{% \ifnum\sgn{\dimexpr#1pt-#2pt\relax}\sgn{\dimexpr#1pt-#3pt}1=1 \expandafter\secondoftwo \else \expandafter\firstoftwo \fi } \def\absval#1{\expandafter\absval@i\expandafter{\number\numexpr#1\relax}} \def\absval@i#1{\number\ifnum#1<\z@-\fi#1 } \edef\saved@catcode{\number\catcode`\:}% sauvegarde du catcode de ":" \catcode`\:12 % ":" est désormais un caractère non actif normal \def\hdif{% \begingroup% ouvrir un groupe semi-simple \catcode`\:12 % modifier le catcode de ":" \hdif@i% aller lire les deux arguments } \def\hdif@i#1#2{% lit les 2 arguments \hdif@ii#1-#2\@nil% et les transmet à la macro à argument délimités } \def\hdif@ii#1:#2:#3-#4:#5:#6\@nil{% %%%%%% calcul des nombres à afficher %%%%%% \edef\nb@hrs{\number\numexpr#1-#4\relax}% différence des heures \edef\nb@min{\number\numexpr#2-#5\relax}% différence des minutes \edef\nb@sec{\number\numexpr#3-#6\relax}% différence des secondes \ifnum\nb@sec<\z@ % si la différence des secondes est <0 \edef\nb@sec{\number\numexpr\nb@sec+60\relax}% ajouter 60 sec \edef\nb@min{\number\numexpr\nb@min-1\relax}% enlever 1 aux minutes \fi \ifnum\nb@min<\z@ % si les minutes sont <0 \edef\nb@min{\number\numexpr\nb@min+60\relax}% ajouter 60 min \edef\nb@hrs{\number\numexpr\nb@hrs-1\relax}% enlever 1 aux heures \fi %%%%%% affichage du résultat %%%%%% \let\inter@space\empty% pas d'espace avant un nombre pour l'instant \ifnum\nb@hrs>\z@ % si les heures sont >0 \nb@hrs h% afficher les heures et "h" \let\inter@space\space% espace pour plus tard \fi \ifnum\nb@min>\z@ % si les minutes sont >0 \inter@space% afficher une espace éventuelle \nb@min min% afficher les minutes et "min" \let\inter@space\space \fi \ifnum\nb@sec>\z@ % si les secondes sont >0 \inter@space% afficher une espace éventuelle \nb@sec s% afficher les secondes et "s" \fi \endgroup% fermer le groupe ouvert au début } \catcode`\:=\saved@catcode\relax% restaure le code de catégorie de ":" \long\def\afterfi#1#2\fi{#2\fi#1} \long\def\antefi#1\fi{\fi#1} \def\for#1=#2to#3\do#4#{% \edef\temp@increment{\number\numexpr0#4}% lit et normalise l'argument optionnel \ifnum\temp@increment=\z@% s'il est nul, \edef\temp@increment{% le redéfinir à -1 (si #3<#2) et 1 sinon \ifnum\numexpr#3-#2\relax<\z@ -1\else 1\fi }% \temp@increment vaut donc 1 ou -1 \fi \ifnum\numexpr\temp@increment*(#3-#2)\relax<\z@ \expandafter\gobone% si argument optionnel incompatible, manger le {} \else \edef#1{\number\numexpr#2}% initialise la variable \edef\macro@args{% définit et développe les arguments de la macro %#1=nom de la macro à définir : \expandafter\noexpand\csname for@ii@\string#1\endcsname \ifnum\temp@increment<\z@ <\else >\fi% #2=signe de comparaison {\temp@increment}% #3=incrément \noexpand#1% #4=variable {\number\numexpr#3\relax}% #5=entier max }% \antefi \expandafter\for@i\macro@args \fi } % #1=nom de la macro récursive de type "\for@ii@" % #2=signe de comparaison % #3=incrément % #4=variable % #5=entier max % #6=code à exécuter \long\def\for@i#1#2#3#4#5#6{% \def#1{% dfinit la sous macro récursive \unless\ifnum#4#2#5\relax% tant que la variable n'a pas dépassé l'entier max \afterfi{% rendre la récursivité terminale #6% exécute le code \edef#4{\number\numexpr#4+#3\relax}% incrémente la variable #1 #1% recommence }% \fi }% #1% appelle la sous-macro récursive } \def\exitfor#1{% #1=variable correspondant à la boucle de laquelle on veut sortir \expandafter\let\csname for@ii@\string#1\endcsname\empty } \def\quark{\quark} \long\def\ifempty#1{% \expandafter\ifx\expandafter\relax\detokenize{#1}\relax \expandafter\firstoftwo \else \expandafter\secondoftwo \fi} \def\reverse#1{% \ifempty{#1} {}% s'il n'y a rien à inverse, ne rien faire {\reverse@i#1\@nil\@nil}% initialisation des réservoirs et appeler \reverse@i } \def\reverse@i#1#2\@nil#3\@nil{% #1 est le 1er caractère du réservoir A \ifempty{#2}% si ce qui reste après ce 1er caractère est vide {#1#3}% renvoyer #1#3 qui est le résultat final {\reverse@i#2\@nil#1#3\@nil}% sinon, recommencer en déplaçant #1 % en 1re position du réservoir B } \long\def\ifin#1#2{% \long\def\ifin@i##1#2##2\@nil{% définit la macro auxiliaire \ifempty{##2}% si ce qu'il y a derrière le motif est vide \secondoftwo% aller à "faux" \firstoftwo% sinon à "vrai" }% \ifin@i#1#2\@nil% appel de la macro auxiliaire } \def\ifstart#1#2{% \def\if@start##1#2##2\@nil{\ifempty{##1}}% \ifempty{#2}\firstoftwo{\if@start#1\relax#2\@nil}} \def\substin#1#2#3{% \def\substin@i##1{% \ifempty{##1}% si le code est vide \relax% ne rien faire -> fin du processus {\ifin{##1}{#2}% sinon, si le code contient motif1 {\substin@ii##1\@nil}% appeler la macro à argument délimités {##1}% sinon, afficher le code }% }% \def\substin@ii##1#2##2\@nil{% ##1#3% afficher ##1 et #3 (qui est ) \substin@i{##2}% et recommencer avec ce qui reste }% \substin@i{#1}% } \def\substtocs#1#2#3#4{% \def\substtocs@i##1{% \ifempty{##1}% si le code est vide \relax% ne rien faire -> fin du processus {\ifin{##1}{#3}% sinon, si le code contient motif1 {\substtocs@ii##1\@nil}% appeler la macro à argument délimités {\addtomacro#1{##1}}% sinon, ajouter le code }% }% \def\substtocs@ii##1#3##2\@nil{% \addtomacro#1{##1#4}% ajouter ##1#4 \substtocs@i{##2}% et recommencer avec ce qui reste }% \let#1=\empty% initialise la macro à vide \substtocs@i{#2}% } \long\def\cnttimestocs#1#2#3{% #3=macro recevant le résultat \long\def\cnttimestocs@i##1\@nil##2#2##3\@nil{% % ##1=nb d'occurrences rencontrées jusqu'alors % ##2 et ##3=ce qui est avant/après le \ifin{##3}{#2}% si le est dans ce qui reste {\expandafter\cnttimestocs@i% appeler la macro récursive \number\numexpr##1+1\relax\@nil% avec une occurrence de plus ##3\@nil% et ce qui reste }% {\edef#3{\number\numexpr##1+1\relax}}% sinon, stocker 1 occurrence de plus dans #3 }% \ifin{#1}{#2}% si le est dans le {\cnttimestocs@i 0\@nil#1\@nil}% appeler la macro récursive avec 0 occurrence {\def#3{0}}% sinon, mettre 0 dans #3 } \def\ifcs#1{% \begingroup \escapechar=`\@ % prend "@" comme caractère d'échappement \if% les premiers caractères de \expandafter\firstto@nil\string#1a\@nil% "#1a" \expandafter\firstto@nil\string\relax\@nil% et "\relax" sont-ils égaux ? \endgroup\expandafter\firstoftwo% si oui, fermer le groupe et renvoyer "vrai" \else% sinon, fermer le groupe et renvoyer "faux" \endgroup\expandafter\secondoftwo \fi } \def\endif{\endif}\def\elseif{\elseif} \def\idto@endif#1\endif{#1} \def\firstto@endif#1#2\endif{#1} \def\ifxcase#1#2{% #1= #2=prochain argument \ifx\elseif#2% si #2 est \elseif \expandafter\firstoftwo\else\expandafter\secondoftwo\fi \idto@endif% aller à \idto@endif, sinon : {\ifx\endif#2% si #2 est \endif \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {}% ne rien faire, sinon : {\ifx#1#2% s'il y a égalité \expandafter\firstoftwo\else\expandafter\secondoftwo\fi \firstto@endif% aller à \firstto@endif {\ifxcase@i{#1}}% sinon aller à \ifxcase@i }% }% } \def\ifxcase@i#1#2{% #1= #2= \ifxcase{#1}% manger le et aller à \ifxcase en transmettant #1 } \newcount\cnt@nest \cnt@nest=0 % définit et initialise le compteur d'imbrication \def\end@foreach{\end@foreach} \long\def\save@macro#1#2{\expandafter\let\csname saved@var@\number\cnt@nest#1\endcsname#2} \long\def\save@macro@i#1/#2{\save@macro{a}#1\save@macro{b}#2} \long\def\restore@macro#1#2{% \expandafter\let\expandafter#2\csname saved@var@\number\cnt@nest#1\endcsname } \long\def\restore@macro@i#1/#2{\restore@macro{a}#1\restore@macro{b}#2} \def\defseplist#1{% \long\def\doforeach##1\in##2##3{% \global\advance\cnt@nest1 % entrée de boucle : incrémenter le compteur d'imbrication \ifin{##1}{/}% si ##1 contient "/" {\save@macro@i##1% sauvegarde les macros \doforeach@ii% appeler la macro récursive après avoir {##3}%\csname loop@code@\number\cnt@nest\endcsname% formé la macro contenant le code ##1% mettre les variables ##1 en 2e position ##2#1\end@foreach/\end@foreach#1% puis la liste ##2 suivie de ",\end@foreach," \restore@macro@i##1% une fois sorti de toutes les boucles, restaurer les macros }% si ##1 ne contient pas "/" {\save@macro{}##1% sauvegarde la macro \doforeach@i% appeler la macro récursive {##3}% mettre en premier le ##1% puis la variable ##1 en 2e position ##2#1\end@foreach#1% enfin la liste ##2 suivie de ",\end@foreach," \restore@macro{}##1% une fois sorti de toutes les boucles, restaurer la macro }% \global\advance\cnt@nest-1 % décrémente le compteur d'imbrications }% % ##1 = code à exécuter, ##2= variable, ##3=valeur courante \long\def\doforeach@i##1##2##3#1{% \def##2{##3}% fait l'assignation à la variable \global\let\allow@recurse\identity% permet l'appel récursif plus bas \ifx\end@foreach##2% si la fin est atteinte \expandafter\gobone\else\expandafter\identity\fi% manger sinon exécuter: {##1% le code puis \allow@recurse{\doforeach@i{##1}##2}% recommencer }% }% % ##1 = code à exécuter, ##2/##3= variables, ##4/##5=valeurs courantes \long\def\doforeach@ii##1##2/##3##4/##5#1{% \def##2{##4}\def##3{##5}% fait l'assignation des deux variables \global\let\allow@recurse\identity% permet l'appel récursif plus bas \ifx\end@foreach##2% si la fin est atteinte \expandafter\gobone\else\expandafter\identity\fi% manger sinon exécuter: {##1% le code puis \allow@recurse{\doforeach@ii{##1}##2/##3}% recommencer }% }% % macro qui, si elle remplace \allow@recurse, annule l'appel récursif \long\def\forbid@recurse##1\end@foreach#1{}% tout manger jusqu'à "\end@foreach," } % macro pour annuler le prochain appel récursif \def\doforeachexit{\global\let\allow@recurse\forbid@recurse} \defseplist{,} \begingroup% ouvrir un groupe \edef\temp{\endgroup\def\noexpand\removept##1.##2\string p\string t}% \temp{#1\ifnum#2>0 .#2\fi}% et le fermer avant de définir \removept \def\dimtodec{\expandafter\removept\the} \def\FOR#1=#2to#3\do#4#{% \ifempty{#4} {\let\FOR@increment\z@} {\edef\FOR@increment{\the\dimexpr#4pt\relax}}% lit et normalise l'argument optionnel \ifdim\FOR@increment=\z@% s'il est nul, \edef\FOR@increment{% le redéfinir à -1pt (si #3<#2) et 1pt sinon \ifdim\dimexpr#3pt-#2pt\relax<\z@ -1\else 1\fi pt }% \FOR@increment vaut donc 1 ou -1 \fi \ifdim\dimtodec\dimexpr#3pt-#2pt\relax\dimexpr\FOR@increment\relax<\z@ \expandafter\gobone% si argument optionnel incompatible, manger le {} \else \edef#1{\dimtodec\dimexpr#2pt\relax}% initialise la \ \edef\macro@args{% définit et développe les arguments à passer à \FOR@i %#1=nom de la macro récursive : \expandafter\noexpand\csname FOR@ii@\string#1\endcsname \ifdim\FOR@increment<\z@ <\else >\fi% #2=signe de comparaison {\FOR@increment}% #3=incrément \noexpand#1% #4=\ {\the\dimexpr#3pt\relax}% #5=dimension n2 }% \antefi% externalise la ligne ci-dessous de la portée du test \expandafter\FOR@i\macro@args% appelle \FOR@i avec les arguments définis ci-dessus \fi } % #1=nom de la macro récursive de type "\FOR@ii@\" % #2=signe de comparaison % #3=incrément % #4=\ % #5=dimension n2 % #6= à exécuter \long\def\FOR@i#1#2#3#4#5#6{% \def#1{% dfinit la sous macro récursive \unless\ifdim#4pt#2#5\relax% tant que la \ variable n'a pas dépassé n2 \afterfi{% rendre la récursivité terminale #6% exécute le code \edef#4{\dimtodec\dimexpr#4pt+#3\relax}% incrémente la \ #1% recommence }% \fi }% #1% appelle la sous-macro récursive }% \def\exitFOR#1{% #1=\ correspondant à la boucle de laquelle on veut sortir \defname{FOR@ii@\string#1}{}% } \def\ifexitFOR#1{% envoie vrai si on est prématurément sorti de la boucle de \ #1 % si la macro récursive est \empty \expandafter\ifx\csname FOR@ii@\string#1\endcsname\empty \expandafter\firstoftwo% c'est qu'on est sortir prématurément, renvoyer "vrai" \else \expandafter\secondoftwo% sinon, renvoyer "faux" \fi } \def\decmul#1#2{\dimtodec\dimexpr#1\dimexpr#2pt\relax\relax} \def\decdiv#1#2{% divise le décimal #1 par le décimal #2 \dimtodec\dimexpr 1pt*\dimexpr#1pt\relax/\dimexpr#2pt\relax\relax } \def\convertunit#1#2{% \dimtodec \dimexpr \numexpr \dimexpr #1 \relax * 65536 / \dimexpr 1#2 \relax \relax sp \relax } \def\vdim#1{\dimexpr\ht#1+\dp#1\relax}% hauteur totale de la boite #1 \def\cbox#1{\setbox0\vbox{#1}\lower\dimexpr(\ht0-\dp0)/2\relax\box0 } \def\clap#1{\hbox to\z@{\hss#1\hss}} \def\ifzerodimbox#1{% #1=registre de boite % revoie vrai si le registre est vide ou contient une boite vide \csname% former le nom "\firstoftwo" ou "secondoftwo" \ifvoid#1first%% si le registre est vide "first" \else% sinon \ifnum\wd#1=\z@% si la largeur \ifnum\ht#1=\z@% la hauteur \ifnum\dp#1=\z@ first% et la profondeur=0pt, "first" \else second% dans les autres cas "second" \fi \else second% \fi \else second% \fi \fi oftwo% compléter avec "oftwo" \endcsname } \def\ifvoidorempty#1{% teste si le registre de boite #1 est vide ou contient une boite vide \ifvoid#1\relax \expandafter\firstoftwo \else \begingroup% dans un groupe \setbox0=% affecter à la boite 0 \ifhbox#1\hbox\bgroup\unhcopy% un boite horizontale \else \vbox\bgroup\unvcopy% ou verticale \fi% dans laquelle on compose #1\relax% composer #1 en dimensions naturelles \expandafter\egroup% sauter la fin de la boite \expandafter% et le \endgroup \endgroup \ifnum\lastnodetype<0 % pour tester si le dernier noeud est vide \expandafter\expandafter\expandafter\firstoftwo \else \expandafter\expandafter\expandafter\secondoftwo \fi \fi } \def\showdim#1{% \vrule width0.2pt height 1ex depth 0pt \vrule width#1 height0.4pt depth 0pt \vrule width0.2pt height 1ex depth 0pt \relax} \newdimen\frboxrule \frboxrule0.4pt \newdimen\frboxsep \frboxsep5pt \def\FRbox#1{% ne pas changer le mode H ou V en cours \hbox{% mettre à la suite horizontalement \vrule width\frboxrule% 1) réglure gauche \vbox{% 2) un empilement vertical comprenant \hrule height\frboxrule% a) réglure supérieure \kern\frboxsep% b) espace verticale haute \hbox{% c) contenu + espaces en mode H \kern\frboxsep#1\kern\frboxsep }% \kern\frboxsep% d) espace verticale basse \hrule height\frboxrule% e)réglure inférieure }% \vrule width\frboxrule% 3) réglure droite }% } \def\frbox#1{% ne pas changer le mode H ou V en cours \hbox{% enferme dans une \hbox \vrule width\frboxrule% réglure gauche \vtop{% \vbox{% 1er élément de la \vtop \hrule height\frboxrule% réglure supérieure \kern\frboxsep% espace haut \hbox{% \kern\frboxsep% espace gauche #1% contenu \kern\frboxsep% espace droite }% }% puis autres éléments de la \vtop, sous la ligne de base \kern\frboxsep% espace bas \hrule height\frboxrule% réglure inférieure }% \vrule width\frboxrule% réglure droite }% } \def\souligne#1{% \setbox0=\hbox{#1}% stocke le contenu dans le registre no 0 \setbox0=\hbox{% puis, dans une \hbox, construit une réglure \vrule width\wd0 % de la longueur du contenu depth\dimexpr\dp0 + 1.4pt\relax % dp = profondeur texte + 1.4pt height\dimexpr-\dp0 - 1pt\relax % ht = -profondeux texte - 1pt }% \wd0=0pt \dp0=0pt \ht0=0pt % annule toutes les dimensions \leavevmode \box0 % affiche la réglure #1% puis le contenu } \def\Souligne#1{% \setbox0=\hbox{#1}% \setbox0=\hbox{\vrule width\wd0 depth1.4pt height-1pt }% \wd0=0pt \dp0=0pt \ht0=0pt \leavevmode \box0 #1% } \newdimen\stackwd \stackwd=3em \catcode`\@11 \def\stackbox#1{% \par% termine le paragraphe en cours \begingroup% dans un groupe semi-simple \parindent=0pt% pas d'indentation \parskip=0pt% annuler le \parskip \setbox0\hbox{Àgjp}% boite pour le strut \edef\stack@strut{\vrule width\z@ height\the\ht0 depth\the\dp0 }% définit le strut \stackbox@i#1\\\quark\\% ajoute "\\\quark\\" à la fin et appelle \stackbox@i \unkern% annule la dernière compensation verticale \par \endgroup } \def\stackbox@i#1\\{% #1=ligne courante \def\temp@{#1}% stocke la ligne courante \unless\ifx\quark\temp@% si ce n'est pas la fin \hfill % ressort infini de centrage (passe en mode horizontal) \doforeach\current@item\in{#1}% pour chaque élément dans la ligne courante... {\frbox{% ...encadrer \hbox to\stackwd{% une \hbox de largeur \stackwd contenant \hss% 1) ressort de centrage \stack@strut% 2) strut de dimension verticale \current@item%3) l'élement courant \hss}% 4)ressort de centrage }% fin de la \fbox \kern-\frboxrule% revenir en arrière pour superposer les réglures verticales }% fin de \doforeach \unkern% annuler la dernière compensation horizontale \hfill% ressort infini de centrage \null% fait prendre en compte le dernier ressort \par% termine le paragraphe \nobreak% interdit une coupure de page \nointerlineskip% sinon, ne pas ajouter le ressort interligne \kern-\frboxrule% superposer les réglures horizontales \expandafter\stackbox@i% et recommencer \fi } \def\vlap#1{\vbox to\z@{\vss#1\vss}} \newdimen\xunit \newdimen\yunit \newdimen\mainrule \newdimen\subrule \xunit=1cm \yunit=1cm \mainrule=0.8pt \subrule=0.2pt \def\grid#1#2#3#4{% #1=nb xunit #2=nb xsubunit #3=nb yunit #4=nb ysubunit \vbox{% \offinterlineskip \edef\total@wd{\the\dimexpr\xunit*#1\relax}% \edef\sub@unit{\the\dimexpr\yunit/#4\relax}% \for\iii=1to#3\do {\ifnum#4>1 \vbox to\z@{% \for\jjj=2to#4\do {\kern\sub@unit \vlap{\hrule width\total@wd height\subrule}% }% \vss }% \fi \vlap{\hrule width\total@wd height\mainrule}% \kern\yunit }% \vlap{\hrule width\total@wd height\mainrule}% \vbox to\z@{% \vss \hbox to\total@wd{% \edef\total@ht{\the\dimexpr\yunit*#3\relax}% \edef\sub@unit{\the\dimexpr\xunit/#2\relax}% \for\iii=1to#1\do {\ifnum#2>1 \rlap{% \for\jjj=2to#2\do {\kern\sub@unit \clap{\vrule width\subrule height\total@ht}% }% }% \fi \clap{\vrule width\dimexpr\mainrule height\total@ht}% \kern\xunit }% \clap{\vrule width\mainrule height\total@ht}% }% }% }% } \def\iffileexists#1#2{% #1=canal de lecture #2=nom du fichier \openin#1=#2 % \ifeof#1% le fichier n'existe pas \closein#1% \expandafter\secondoftwo% renvoyer faux \else \closein#1 \expandafter\firstoftwo% sinon renvoyer vrai \fi } \def\xread{% doit être suivie de " to \" \edef\restoreendlinechar{\endlinechar=\the\endlinechar}% \endlinechar=-1 % neutralise \endlinechar \afterassignment\restoreendlinechar% après l'assignation restaurer \endlinechar \read% attend to \ } \def\showfilecontent#1#2{% #1=canal de lecture #2=nom de fichier \begingroup \tt% sélectionner la fonte à chasse fixe \openin#1=#2\relax \ifeof#1% si la fin du fichier est déjà atteinte, il n'existe pas et Le fichier n'existe pas% afficher le message \else% le fichier existe \def\do##1{\catcode`##1=12 }% \dospecials% neutraliser tous les tokens spéciaux \obeyspaces% rendre l'espace actif \def\magicpar{\let\magicpar=\par}% \loop \xread#1 to \currline% lire une ligne \unless\ifeof#1% si la fin du fichier n'est pas atteinte \leavevmode\magicpar% former le paragraphe (sauf à la 1er itération) \currline% afficher la ligne \repeat% recommencer \fi \closein#1\relax% \endgroup } \def\exactwrite#1#2{% #1=numéro de canal #2=caractère délimiteur \begingroup \def\canal@write{#1}% sauvegarde le numéro de canal \for\xx=0 to 255\do{\catcode\xx=12 }% donne à tous les octets le catcode 12 \expandafter\exactwrite@i\expandafter{\string#2}% rend #2 de catcode 12 } \def\exactwrite@i#1{% #1=caractère frontière (de catcode 12) % la macro \exactwrite@ii lit tout jusqu'au caractère #2 et passe % ce qu'elle a lu à la macro \exactwrite@iii en mettant un \@nil à la fin \def\exactwrite@ii##1#1{\exactwrite@iii##1\@nil}% \exactwrite@ii% va lire tout ce qui se trouve jusqu'au prochain #2 } {\catcode`\^^M 12 \gdef\EOL@char{^^M}}% définit le caractère de catcode 12 \def\exactwrite@iii#1\@nil{% #1 = contenu à écrire \expsecond{\ifin{#1}}\EOL@char% si #1 contient "retour chariot" {\write@line#1\@nil}% écrire la première ligne de #1 {\immediate\write\canal@write{#1}% sinon : dernière ligne atteinte, l'écrire \endgroup% puis sortir du groupe }% } % les \expandafter provoquent le 1-développement de \EOL@char : \expandafter\def\expandafter\write@line\expandafter#\expandafter1\EOL@char#2\@nil{% \immediate\write\canal@write{#1}% écrit la ligne (ce qui se trouve avant ^^M) \exactwrite@iii#2\@nil% continue avec ce qui se trouve après ^^M } \def\quark@list{\quark@list}% quark de fin de liste \def\finditemtocs#1#2#3{% #1 = \ #2=position #3=macro à définir \def\finditemtocs@iii##1##2\quark@list,{% renvoyer #1 et manger le reste de la liste \expandafter\def\expandafter#3\expandafter{\gobone##1}% }% \let#3=\empty% \exparg\finditemtocs@i{#1}{#2}% 1-développe la \ } \def\finditemtocs@i#1#2{% #1 = liste #2=position cherchée \ifnum#2>\z@% ne faire quelque chose que si la position est >0 \antefi\finditemtocs@ii{1}{#2}\relax#1,\quark@list,% appelle la macro récursive \fi } \def\finditemtocs@ii#1#2#3,{% % #1=position courante #2=position cherché #3=\relax + élément courant \expandafter\ifx\expandafter\quark@list\gobone#3% \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {}% si fin de liste ne rien faire. Sinon, si position bonne {\ifnum#1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\finditemtocs@iii{#3}% renvoyer #3 et manger les éléments restants }% si la position n'est pas la bonne, recommencer en incrémentant #1 {\exparg\finditemtocs@ii{\number\numexpr#1+1}{#2}\relax% }% }% } \def\insitem#1#2#3{% #1 = macro #2=position cherchée #3=élément à insérer \let\item@list=\empty \ifnum#2<1 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi % si position < 1 {\addtomacro\item@list{#3,}% ajouter l'élement à insérer en premier \eaddtomacro\item@list#1% puis la liste entière } % si la position > 1 {% définir la macro récursive \def\insitem@i##1##2,{% ##1 = position courante ##2=\relax + élément courant \expandafter\ifx\expandafter\quark@list\gobone##2% \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\addtomacro\item@list{#3}}% si fin de liste, ajouter l'élément en dernier {% sinon, si la position cherchée est atteinte \ifnum##1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\addtomacro\item@list{#3,}% ajouter l'élement \add@remainlist##2,% et ##2 (en supprimant le \relax) et le reste }% si la position n'est pas atteinte {\eaddtomacro\item@list{\gobone##2,}% ajouter l'élément \exparg\insitem@i{\number\numexpr##1+1}\relax% et recommencer }% }% }% % appel de la macro récursive \expandafter\insitem@i\expandafter1\expandafter\relax#1,\quark@list,% }% \let#1=\item@list% rendre #1 égal au résultat } \def\add@remainlist#1,\quark@list,{% \eaddtomacro\item@list{\gobone#1}% ajouter #1 ainsi que les autres } \def\delitem#1#2{% #1 = macro #2=position cherchée \ifnum#2>0 % ne faire quelque chose que si la position est >0 \def\delitem@i##1##2,{% ##1 = position courante ##2=\relax + élément courant \expandafter\ifx\expandafter\quark@list\gobone##2% \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {}% si fin de liste, ne rien faire {% sinon, si la position cherchée est atteinte \ifnum##1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\add@reaminingitems% et ##2 (en supprimant le \relax) et le reste }% si la position n'est pas la bonne {\eaddtomacro\item@list{\gobone##2,}% ajouter l'élément \exparg\delitem@i{\number\numexpr##1+1}\relax% et recommencer }% }% }% \let\item@list=\empty% initialiser la macro tempporaire % appel de la macro récursive \expandafter\delitem@i\expandafter1\expandafter\relax#1,\quark@list,% \expandafter\remove@lastcomma\item@list\@nil \let#1=\item@list% rendre #1 égal au résultat \fi } \def\add@reaminingitems#1\quark@list,{% \eaddtomacro\item@list{#1}% ajouter tout jusqu'au quark } \def\remove@lastcomma#1,\@nil{\def\item@list{#1}} \def\runlist#1\with#2{% #1=liste #2=macro \def\runlist@i##1,##2\@nil{% \addtomacro\collect@run{#2{##1}}% ajouter "\{<élément>}"" \ifempty{##2}% plus d'élément ? \relax% ne rien faire (donc terminer le processus) {\runlist@i##2\@nil}% sinon, recommencer avec les éléments restants }% \begingroup% fait la collecte dans un groupe \let\collect@run=\empty% initialiser la macro \ifcs{#1}% si #1 est une séquence de contrôle {\expandafter\runlist@i#1,\@nil%% aller à \runlist@i }% sinon, former la sc {\expandafter\expandafter\expandafter\runlist@i\csname#1\endcsname,\@nil }% \expandafter\endgroup% ferme le groupe et détruit \collect@run \collect@run% après l'avoir développé ! } \def\ifspacefirst#1{% \expandafter\ifspacefirst@i\detokenize{#1W} \@nil% "W" se prémunit d'un argument vide } \def\ifspacefirst@i#1 #2\@nil{\ifempty{#1}} \expandafter\def\expandafter\gobspace\space{} \def\removefirstspaces{% \romannumeral% lance le développement maximal \removefirstspaces@i% et passe la main à la macro récursive } \def\removefirstspaces@i#1{% \ifspacefirst{#1}% si #1 commence par un espace {\expandafter\removefirstspaces@i\expandafter% recommencer {\gobspace#1}% après avoir supprimé cet espace } {\z@#1}% sinon, renvoyer l'argument où \z@ stoppe l'action de \romannumeral } \edef\catcodezero@saved{\number\catcode0 }% stocke le catcode de ^^00 \catcode0=12 % le modifie à 12 \def\removelastspaces#1{% \romannumeral% lance le développement maximal \removelastspaces@i#1^^00 ^^00\@nil% et passe la main à \removelastspaces@i } \def\removelastspaces@i#1 ^^00{% \removelastspaces@ii#1^^00% } \def\removelastspaces@ii#1^^00#2\@nil{% \ifspacefirst{#2}% si le reliquat commence par un espace {\removelastspaces@i#1^^00 ^^00\@nil}% recommencer sans passer par \removelastspaces {\z@#1}% sinon "\z@" stoppe l'action de \romannumeral } \catcode0=\catcodezero@saved\relax% restaure le catcode de ^^00 \def\removetrailspaces#1{% \romannumeral% lance le développement maximal \expandafter\expandafter\expandafter% le pont d'\expandafter \removelastspaces \expandafter\expandafter\expandafter% fait agir \removefirstspaces en premier {% \expandafter\expandafter\expandafter \z@% stoppe le développement initié par \romannumeral \removefirstspaces{#1}% }% } \newcount\test@cnt \def\ifinteger#1{% \ifstart{#1}{-}% si "-" est au début de #1 {\expandafter\ifinteger\expandafter{\gobone#1}% l'enlever et recommencer } {\ifempty{#1}% sinon, si #1 est vide, lire l'argument \secondoftwo% lire l'argument {\afterassignment\after@number% sinon, après l'assignation, aller à \after@number \test@cnt=0#1\relax% faire l'assignation (\relax termine le nombre et n'est pas mangé) }% }% } \def\after@number#1\relax{% #1 est ce qui reste après l'assignation \ifempty{#1}% teste si #1 est vide et lit l'argument ou qui suit } \newif\iftestspace \testspacefalse \def\deftok#1#2{\let#1= #2\empty} \def\ifnexttok#1#2#3{% #1=token #2=code vrai #3=code faux \let\test@tok= #1% stocke le token à tester \def\true@code{#2}\def\false@code{#3}% et les codes à exécuter \iftestspace \def\ifnexttok@i{\futurelet\nxttok\ifnexttok@ii}% \else \def\ifnexttok@i{\futurelet\nxttok\ifnexttok@iii}% \fi% après avoir défini la macro récursive selon le booléen \ifnexttok@i% l'exécuter } \def\ifnexttok@ii{% macro qui teste aussi les espaces \ifx\nxttok\test@tok \expandafter\true@code% exécuter le code vrai \else \expandafter\false@code% sinon code faux \fi } \def\ifnexttok@iii{% \ifx\nxttok\sptoken% si le prochain token est un espace \def\donext{% \afterassignment\ifnexttok@i% aller "sentir" le token d'après \let\nxttok= }% après avoir absorbé l'espace \else \let\donext\ifnexttok@ii% sinon, faire le test \fi \donext% faire l'action décidée ci-dessus } \def\ifstarred#1{\ifnexttok*{\firstoftwo{#1}}} \def\parsestop{\parsestop}% définit le quark se trouvant en fin de code \newtoks\code@toks% alloue le registre contenant le code lu \def\parseadd#1{\code@toks\expandafter{\the\code@toks#1}} \newif\ifparse@group \def\parse{% \code@toks{}% initialise le collecteur de tokens \ifstarred {\parse@grouptrue \ifnexttok{ }% si un espace suit l'étoile {\afterassignment\parse@i \let\nxttok= }% le manger et aller à \parse@i {\parse@i}% sinon, aller à \parse@i } {\parse@groupfalse \parse@i }% } \def\parse@i{\futurelet\nxttok\parse@ii}% lit le prochain token et va à \parse@ii \def\parse@ii{% \ifx\nxttok\parsestop% si la fin va être atteinte, aller à \parsestop@i \let\next@action\parsestop@i \else \ifx\nxttok\@sptoken% si un espace va être lu, aller à \read@space \let\next@action\read@space \else \ifx\nxttok\bgroup% si une accolade ouvrante va être lue \let\next@action\read@bracearg% aller à \read@bracearg \else \let\next@action\testtoken% dans les autres cas, aller à \testtoken \fi \fi \fi \next@action% faire l'action décidée ci-dessus } \def\parsestop@i{% la fin est atteinte \expandafter\the\expandafter\code@toks% afficher le registre de tokens \gobone% après avoir mangé \parsestop } \expandafter\def\expandafter\read@space\space{% \read@space mange un espace dans le code \testtoken{ }% et va à \testtoken } \def\read@bracearg{% \read@bracearg@i\relax% ajoute un \relax avant de passer la main à \read@bracearg@i } \def\read@bracearg@i#1\parsestop{% l'argument tout jusqu'à \parsestop \expsecond\ifbracefirst{\gobone#1}% retire le \relax et teste si #1 commence par "{" {\expandafter\read@bracearg@ii\gobone#1\parsestop}% lire l'argument entre accolades {\expandafter\testtoken\gobone#1\parsestop}% sinon, tester le token } \def\read@bracearg@ii#1{% l'argument entre accolades est lu \ifparse@group\expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\begingroup% ouvre un groupe pour parser l'intérieur de l'accolade \def\parsestop@i{% redéfinir localement \parsestop@i pour \expandafter% retarde \endgroup \endgroup% pour ajouter le contenu local de \code@toks entre accolades \expandafter% au contenu de ce même registre avant le groupe \parseadd \expandafter {\expandafter{\the\code@toks}}% \expandafter\parse@i% puis va lire le token suivant \gobone% après avoir mangé le \parsestop }% \parse*#1\parsestop% <- le \parsestop@i fermera le groupe semi simple } {\parseadd{{#1}}% macro non étoilée, on ajoute #1 tel quel entre accolades \parse@i% puis va lire le token suivant }% } \def\testtoken#1{% macro qui teste le token \parseadd{#1}% ici, ne rien faire à part ajouter le token \parse@i% aller lire le token suivant } \def\ifbracefirst#1{% teste si #1 commence par un token de catcode 1 \ifspacefirst{#1}% si #1 commence par un espace {\secondoftwo}% renvoyer faux {\ifnum \catcode \expandafter\expandafter\expandafter `% \expandafter \firstto@nil\detokenize{#1W}\@nil=1 \expandafter\firstoftwo \else \expandafter\secondoftwo \fi }% } \def\grab@first#1#2{% \ifx#1\empty\expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\let#2\empty% si #1 est vide, ne rien faire et assigner à #2 }% si #1 n'est pas vide {\def\arg@b{#2}% stocke la macro #2 dans \arg@b \exparg\ifbracefirst#1% si le 1er token de #1 est "{" {\expandafter\grab@arg#1\@nil#1% aller lire l'argument avec \grab@arg } {% sinon, développer #1 avant de le regarder avec \futurelet : \expandafter\futurelet\expandafter\nxttok\expandafter\test@nxttok#1\@nil#1% % puis aller à \test@nxttok }% }% } \def\test@nxttok{% si le premier token de l'arg #1 de \grab@first est \ifx\nxttok\sptoken% un espace \expandafter\grab@spc% aller le lire avec \grab@spc \else \expandafter\grab@tok% sinon, lire le token avec \grab@tok \fi } \def\grab@arg#1{% assigne l'argument de \grab@first (mis entre accolades) \expandafter\def\arg@b{{#1}}% à #2 \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first } \expandafter\def\expandafter\grab@spc\space{% \expandafter\def\arg@b{ }% assigne un espace à #2 de \grab@first \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first } \def\grab@tok#1{%% assigne le premier token de l'arg #1 de \grab@first \expandafter\def\arg@b{#1}% à la macro #2 de \grab@first \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first } % assigne tout ce qui reste à lire (moins le "\relax") à la macro % #1 de \grab@first \def\assign@tonil#1\@nil#2{\expsecond{\def#2}{\gobone#1}} \def\ifstartwith#1#2{% \def\startwith@code{#1}\def\startwith@pattern{#2}% \ifempty{#2} \firstoftwo% renvoyer vrai si #2 est vide {\ifempty{#1} \secondoftwo% renvoyer faux si #1 est vide et pas #2 \ifstartwith@i% dans les autres cas, aller à \ifstartwith@i }% } \def\ifstartwith@i{% \grab@first\startwith@code\first@code% extrait le premier "argument" de \grab@first\startwith@pattern\first@pattern% et celui de \ifx\first@code\first@pattern% s'il sont égaux \expandafter\ifempty\expandafter{\startwith@pattern} {\expandafter\firstoftwo}% et que ne contient plus rien => vrai {\expandafter\ifempty\expandafter{\startwith@code} {\expandafter\secondoftwo}% si ne contient plus rien => faux {\expandafter\ifstartwith@i}% sinon poursuivre les tests }% \else% s'ils ne sont pas égaux \expandafter\secondoftwo% renvoyer faux \fi } \newif\ifin@group \def\ifcontain{% \ifstarred {\in@groupfalse\ifcontain@i\ifcontain@star}% {\ifcontain@i\ifcontain@nostar}% } \def\ifcontain@i#1#2#3{% #1 = macro à appeler selon étoile ou pas. #2 = code. #3 = motif \def\main@arg{#2}\def\pattern@arg{#3}% \ifempty{#3}% \firstoftwo {\ifempty{#2} \secondoftwo #1% aller à \ifcontain@star ou \ifcontain@nostar }% } \def\ifcontain@nostar{% \exptwoargs\ifstartwith\main@arg\pattern@arg \firstoftwo% si motif est au début de code => vrai {\expandafter\ifempty\expandafter{\main@arg} \secondoftwo% sinon, si code est vide => faux {\grab@first\main@arg\aux@arg% autrement, manger le 1er "argument" de code \ifcontain@nostar% et recommencer }% }% } \def\ifcontain@star{% \expandafter\ifbracefirst\expandafter{\main@arg}% si code commence par "{" {\grab@first\main@arg\aux@arg% enlever {} de main@arg \begingroup% ouvrir un groupe \in@grouptrue% mettre le booléen à vrai \expandafter\assign@main@arg\aux@arg\@nil% assigner "sous code" à \main@arg \ifcontain@star% et recommencer avec ce nouveau \main@arg }% si code ne commence pas par "{" {\exptwoargs\ifstartwith\main@arg\pattern@arg% si motif est au début de code \return@true% renvoyer vrai {\expandafter\ifempty\expandafter{\main@arg}% si code est vide {\ifin@group% et que l'on est dans un groupe \endgroup \expandafter\ifcontain@star% en sortir et recommencer \else% si on n'est pas dans un groupe, le code a été parcouru \expandafter\secondoftwo% sans trouver => renvoyer faux \fi }% si code n'est pas vide {\grab@first\main@arg\arg@code% manger le 1er "argument" de code \ifcontain@star% et recommencer }% }% }% } \def\return@true{% \ifin@group% tant qu'on est dans un groupe \endgroup \expandafter\return@true% en sortir et recommencer \else \expandafter\firstoftwo% sinon, renvoyer vrai \fi } \def\forcemath#1{% compose #1 en mode math, quel que soit le mode en cours \ifmmode\expandafter\firstoftwo\else\expandafter\secondoftwo\fi {#1}{$#1$}% } \newcount\macro@cnt% numéro à mettre dans le nom des sous macros \newcount\arg@cnt% compte le nombre d'arguments \newtoks\param@text% texte de paramètre des macros sous forme #x et/ou [#x] \newtoks\arg@text% arguments sous forme {#x} et/ou [#x] \def\newmacro#1{% % stocke le nom de la macro et éventuellement "@[]" \def\macro@name##1{\expandafter\gobone\string#1\ifnum##1>0 @[\romannumeral##1]\fi}% \macro@cnt=0 \arg@cnt=0 % initialise les compteurs \param@text{}\arg@text{}% vide les registres de texte de paramètre et d'argument \newmacro@i% va voir le prochain token } \def\newmacro@i{\futurelet\nxttok\newmacro@ii}% met le prochain token dans \nxttok... % ...puis va à la macro : \def\newmacro@ii{% \ifx\nxttok[% si le prochain token est un crochet \let\donext\newmacro@optarg% aller à \newmacro@optarg \else \ifx\nxttok\bgroup% si c'est un accolade ouvrante % le texte de paramètre est fini et il faut définir la macro \def\donext{% \expandafter\def \csname\macro@name\macro@cnt\expandafter\endcsname \the\param@text}% <- code juste après non encore lu \else% sinon, c'est donc un chiffre \let\donext\newmacro@arg% aller à \newmacro@arg \fi \fi \donext% faire l'action décidée ci-dessus } \def\newmacro@optarg[#1]{% lit la valeur par défaut de l'argument optionnel % définit la macro \@ qui lit tous les arguments (optionnels ou pas) jusqu'alors définis % à l'aide de \param@text. Puis, elle testera si le prochain token est un crochet \expandafter\edef\csname\macro@name\macro@cnt\expandafter\endcsname\the\param@text{% \noexpand\ifnexttok[% % si oui : la macro \@ le lira {\expandafter\noexpand\csname\macro@name{\numexpr\macro@cnt+1}\expandafter\endcsname \the\arg@text}% % si non : transmettre à \@ l'argument optionnel par défaut lu {\expandafter\noexpand\csname\macro@name{\numexpr\macro@cnt+1}\expandafter\endcsname \the\arg@text[\unexpanded{#1}]}% }% \advance\arg@cnt 1 % incrémenter le numéro d'argument % pour ajouter "[#]" à \param@text et à \arg@text \eaddtotoks\param@text{\expandafter[\expandafter##\number\arg@cnt]}% \eaddtotoks\arg@text{\expandafter[\expandafter##\number\arg@cnt]}% \advance\macro@cnt 1 % incrémenter le numéro de nom de macro \newmacro@i% va voir le token suivant } \def\newmacro@arg#1{% #1=nb arguments obligatoires à ajouter % boucle qui ajoute "##etc" dans \param@text % et "{#}{#}etc" dans \arg@text \ifnum#1>0 % tant qu'on n'a pas ajouté le nombre de #x nécessaire \advance\arg@cnt 1 % incrémenter le numéro d'argument % pour ajouter #x à \param@text et {#x} à \arg@text \eaddtotoks\param@text{\expandafter##\number\arg@cnt}% \eaddtotoks\arg@text{\expandafter{\expandafter##\number\arg@cnt}}% \expandafter\newmacro@arg\expandafter{\number\numexpr#1-1\expandafter}% reboucler \else% après avoir rajouté e qu'il fallait à \param@text et à \arg@text \expandafter\newmacro@i% lire le token suivant \fi } % encadrement avec les réglures où l'on veut \newmacro\framebox[ULRD]1{% #1 = ULRD (Up, Down, Right, Left) % ne pas changer le mode H ou V en cours \hbox{% enferme dans une \hbox \uppercase{\ifin{#1}L}{\vrule width\frboxrule}{}% réglure gauche \vtop{% \vbox{% 1er élément de la \vtop \uppercase{\ifin{#1}U}{% si la réglure sup doit être tracée \hrule height\frboxrule% réglure supérieure \kern\frboxsep% espace haut } {}% \hbox{% \uppercase{\ifin{#1}L}{\kern\frboxsep}{}% espace gauche #2% contenu \uppercase{\ifin{#1}R}{\kern\frboxsep}{}% espace droite }% }% puis autres éléments de la \vtop, sous la ligne de base \uppercase{\ifin{#1}D}{% \kern\frboxsep% espace bas \hrule height\frboxrule% réglure inférieure }% {}% }% \uppercase{\ifin{#1}R}{\vrule width\frboxrule}{}% réglure droite }% } \def\retokenize#1{% \immediate\openout\wtest=retokenize.tex % ouvre le fichier \immediate\write\wtest{\unexpanded{#1}}% y écrit l'argument \immediate\closeout\wtest% ferme le fichier \input retokenize.tex % lit le fichier selon les catcodes en vigueur \unskip% mange l'espace précédemment ajouté qui provient de la fin du fichier } \newtoks\alter@toks% collecteur de tokens \def\alter#1#2{% #1= délimiteur #2 = macro altérée \let\alter@macro#2% sauvegarde la macro \edef\alter@restorecatcode{% restaurera le catcode de #1 \catcode`\noexpand#1=\the\catcode`#1 }% \edef\alter@tmp{\let\noexpand\alter@markertoks= \string#1}% \alter@tmp% et sauvegarder le délimiteur après avoir mis son catcode à 12 \edef\alter@tmp{\def\noexpand\alter@readlitterate@i\string#1####1\string#1}% % développe les \string#1 pour que les arguments délimités aient % des délimiteurs de catcode 12 \alter@tmp{% <- comme si on écrivait "\def\alter@readlitterate@i#1##1#1" \endgroup% après avoir lu ##1 (tokens rendus inoffensifs), fermer le groupe \addtotoks\alter@toks{{\tt##1}}% ajouter ces tokens \alter@i% et aller lire le prochain token }% \alter@toks{}% initialise le collecteur de tokens \afterassignment\alter@i% aller lire le premier token après avoir \let\alter@tmptok= % mangé l'accolade ouvrante de l'argument qui suit } \def\alter@i{% lit le prochain token et va à \alter@ii \futurelet\alter@nxttok\alter@ii} \def\alter@ii{% % teste le token qui doit être lu (car \futurelet ne l'a pas mangé) \ifx\alter@nxttok\egroup% si la fin va être atteinte \let\alter@next\alter@stop% aller à \alterstop@i \else \ifx\alter@nxttok\@sptoken% si un espace va être lu \let\alter@next\alter@readspc% aller à \alter@readspc \else \ifx\alter@nxttok\bgroup% si une accolade ouvrante av être lue \let\alter@next\alter@readarg% aller à \alter@readarg \else \ifx\alter@nxttok\alter@markertoks% si le délimiteur va être lu \let\alter@next\alter@readlitterate% aller à \alter@readlitterate \else \let\alter@next\alter@readtok% dans les autres cas, aller à \alter@readtok \fi \fi \fi \fi \alter@next% faire l'action décidée ci-dessus } \def\alter@readlitterate{% % le prochain token est le délimiteur \begingroup% ouvrir un groupe \for\alter@tmp=0to255\do{\catcode\alter@tmp=12 }% % mettre tous les catcodes à 12 \defactive{ }{\ }% sauf l'espace \doforeach\alter@tmp\in{<,>,-,`,{,},'}% pour chaque motif de ligature {\unless\if\alter@tmp\alter@markertoks% s'il est différent du délimiteur % le définir pour éviter la ligature \expandafter\alter@defligchar\alter@tmp \fi }% \alter@readlitterate@i% puis aller à \alter@readlitterate@i... % ...qui a été définie dans \alter } \def\alter@defligchar#1{% définit le caractère pour ne pas provoquer de ligature \defactive#1{\string#1{}}% } \expandafter\def\expandafter\alter@readspc\space{% \alter@readspc mange un espace dans le code \addtotoks\alter@toks{ }% ajoute l'espace \alter@i% puis lire le token suivant } \def\alter@readarg{% % le token qui suit est "{" \begingroup% ouvrir un groupe \def\alterstop@ii{% et modifier localement la macro appelée à la toute fin, % après que l'accolade fermante ait été mangée (par \alterstop@i) \expandafter\endgroup% retarder la fermeture de groupe ouvert ci-dessus \expandafter\addtotoks\expandafter\alter@toks\expandafter {\expandafter{\the\alter@toks}}% % pour ajouter hors du groupe ce qui a été collecté à l'intérieur, % le tout mis entre accolades \alter@i% puis, lire le token suivant }% \alter@toks{}% au début du groupe, initialiser le collecteur \afterassignment\alter@i% aller lire le prochain token après \let\alter@tmptok= % avoir mangé l'accolade ouvrante } \def\alter@readtok#1{% % le prochain token n'est pas un token demandant une action spéciale \addtotoks\alter@toks{#1}% l'ajouter au collecteur \alter@i% et aller lire le token suivant } \def\alter@stop{% % le token à lire est "}" \afterassignment\alterstop@ii% aller à \alterstop@ii après \let\alter@tmptok= % avoir mangé l'accolade fermante } \def\alterstop@ii{% % donner à la macro #2 de \alter tout ce qui a été récolté \expandafter\alter@macro\expandafter{\the\alter@toks}% \alter@restorecatcode% puis restaure le catcode du délimiteur } \newdimen\pixelsize \newdimen\pixelsep \pixelsize=.9pt \pixelsep=0pt \def\pixel{\vrule height\pixelsize width\pixelsize} \def\blankpixel{\vrule height\pixelsize width0pt \vrule height0pt width\pixelsize} \def\vblankpixel{\vrule height\pixelsize width0pt} \def\gap{\kern\pixelsep} \newskip\letter@skip \begingroup% dans ce groupe : \expandafter\gdef\csname impact@" "\endcsname{\hskip4\pixelsize plus0.5\pixelsize minus0.5\pixelsize\relax}% \catcode`\^^M=13\relax% le retour à la ligne est actif \edef^^M{\string,}% et se développe en une virgule (de catcode 12) \catcode`\ =12\relax% l'espace devient un "caractère autre" \global\deftok\otherspc{ }% définit un espace de catcode 12 \gdef\impact@alphabet{ a/ *** * *** * * ****, a`/ * * *** * *** * * ****, b/ * * *** * * * * * * ***, c/ *** * * * ***, d/ * * *** * * * * * * ***, e/ ** * * *** * ***, e'/ * * ** * * *** * ***, e^/ * * * ** * * *** * ***, e`/ * * ** * * *** * ***, f/ ** * *** * * * *, g/ *** * * * * * * ***_ * **, h/ * * * *** * * * * * * * *, i/ * * * * * *, j/ * * * * * *_ * **, k/ * * * * * * ** * * * *, l/ * * * * * * *, m/ ** * * * * * * * * * * * * *, n/ *** * * * * * * * *, o/ ** * * * * * * **, o^/ * * * ** * * * * * * **, p/ *** * * * * * * ***_ * *, q/ *** * * * * * * ***_ * *, r/ * ** ** * * *, s/ *** * ** * ***, t/ * * ** * * * **, u/ * * * * * * * * ***, u`/ * * * * * * * * * * ***, v/ * * * * * * * * *, w/ * * * * * * * * * * * *, x/ * * * * * * * * *, y/ * * * * * * ** *_ * **, z/ **** * * * ****, TEX/ ***** * * * * * * * * * ***** * * * * * * * * * * *** * *_ * * *****, A/ *** * * * * * * ***** * * * *, B/ *** * * * * **** * * * * ****, C/ **** * * * * * ****, D/ **** * * * * * * * * * * ****, E/ ***** * * *** * * *****, F/ ***** * * *** * * *, G/ *** * * * * ** * * * * ***, H/ * * * * * * ***** * * * * * *, I/ * * * * * * *, J/ * * * * * * * **, K/ * * * * * * ** * * * * * *, L/ * * * * * * ****, M/ * * ** ** * * * * * * * * * * * *, N/ * * ** * ** * * * * * ** * ** * *, O/ *** * * * * * * * * * * ***, P/ **** * * * * **** * * *, Q/ *** * * * * * * * * * * ** ***, R/ **** * * * * **** * * * * * *, S/ *** * * * * * * * ***, T/ ***** * * * * * *, U/ * * * * * * * * * * * * ***, V/ * * * * * * * * * * * * *, W/ * * * * * * * * * * * ** ** * *, X/ * * * * * * * * * * * * *, Y/ * * * * * * * * * *, Z/ ***** * * * * * *****, ?/ *** * * * * * *, !/ * * * * * *, '/ * * , ./ *, {,}/ *_ *, 1/ * ** * * * * * *, 2/ *** * * * * * * *****, 3/ *** * * * ** * * * ***, 4/ * ** * * * * ***** * *, 5/ ***** * **** * * * ****, 6/ *** * * **** * * * * ***, 7/ ***** * * * * * *, 8/ *** * * * * *** * * * * ***, 9/ *** * * * * **** * * ***, 0/ *** * * * * * * * * * * * ***, @/ ** * * * ** * * * * * * * ***, error/ * * * * * * * * * * * * * * * * * *}% \endgroup% \def\makecar#1#2{% #1=nom recevant le code final #2=macro contenant le dessin \let\pixabove\empty \let\pixbelow\empty \let\pix@line\empty% initialise à vide \exparg\ifin{#2}_% si le code contient _ {\expandafter\makecar@iii#2\@nil}% aller à \makecar@iii {\exparg\makecar@i{#2}}% sinon, à \makecar@i \edef#1{% définit la macro #1 comme \vtop{% une \vtop contenant : \unexpanded{\offinterlineskip\lineskip\pixelsep}% réglage d'espace inter ligne \vbox{\unexpanded\expandafter{\pixabove}}% \vbox des pixels au-dessus % de la ligne de base \unless\ifx\pixbelow\empty% s'il y a des pixels au-dessous de la baseline \unexpanded\expandafter{\pixbelow}% les ajouter dans la \vtop \fi }% }% } \def\makecar@i#1{% #1 = dessin de la lettre avec les caractères "," "*" et " " \doforeach\current@line\in{#1}% pour chaque ligne dans #1 : {\ifx\empty\current@line% si la ligne est vide \addtomacro\pixabove{\hbox{\vblankpixel}}% ajouter une fausse ligne \else% sinon \let\pix@line\empty% initialiser le code de la ligne à vide \expandafter\makecar@ii\current@line\quark% et la construire \fi }% } \def\makecar@ii#1{% #1=caractère de dessin de la ligne en cours \ifxcase#1% si le caractère est * {\addtomacro\pix@line\pixel}% \otherspc{\addtomacro\pix@line\blankpixel}% \endif \ifx#1\quark% si la fin est atteinte \addtomacro\pix@line\unkern% annuler le dernier espace interpixel \eaddtomacro\pixabove{% et encapsuler \pix@line dans une \hbox \expandafter\hbox\expandafter{\pix@line}}% \else% si la fin n'est pas atteinte, ajouter l'espace interpixel \addtomacro\pix@line\gap% \expandafter\makecar@ii% recommencer avec le caractère suivant \fi } \def\makecar@iii#1_,#2\@nil{% \makecar@i{#2}% construit la partie au-dessous de la baseline \let\pixbelow\pixabove% et affecte le code à \pixbelow \let\pixabove\empty \let\pix@line\empty% ré-initialise \makecar@i{#1}% construit la partie au-dessus de la baseline } % On parcourt le texte de remplacement de \impact@alphabet \edef\saved@crcatcode{\catcode13=\the\catcode13\relax}% \catcode`\^^M=13\relax% le retour à la ligne est actif \edef^^M{\string,}% et se développe en une virgule (de catcode 12) \expsecond{\doforeach\letter@name/\letter@code\in}\impact@alphabet% {\edef\letter@name{\letter@name}% développe la lettre (^^M devient ",") \edef\letter@code{\letter@code}% développe le code (^^M devient ",") \exparg\ifstart\letter@name,% si la lettre commence par "," {\edef\letter@name{\expandafter\gobone\letter@name}}% la retirer {}% \exparg\ifstart\letter@code,% si le code commence par "," {\edef\letter@code{\expandafter\gobone\letter@code}}% la retirer {}% \expandafter\makecar\csname impact@"\letter@name"\endcsname\letter@code% }% \saved@crcatcode% redonne le catcode de ^^M \def\end@process{\end@process}% \def\impactend{\endgroup} \newmacro\impact[0.2ex][0pt]{% \begingroup \pixelsize#1 \pixelsep#2 \letter@skip=#1 plus.1pt minus.1pt \catcode`\!12 \catcode`\?12 \impact@i } \def\impact@i{\futurelet\nxtletter\impact@ii} \def\impact@ii{% \ifx\nxtletter\impactend \let\next\unskip \else \leavevmode \ifx\nxtletter\sptoken \let\next\impact@spc \else \let\next\impact@arg \fi \fi \next } \expandafter\def\expandafter\impact@spc\space{% \csname impact@" "\endcsname \impact@i } \def\impact@arg#1{% \ifcsname impact@"#1"\endcsname \csname impact@"#1"\endcsname \else \csname impact@"error"\endcsname \fi \hskip\letter@skip \impact@i } \newdimen\maingraddim \maingraddim=4pt \newdimen\maingradwd \maingradwd=0.5pt \newdimen\subgraddim \subgraddim=2.5pt \newdimen\subgradwd \subgradwd=0.2pt \newdimen\axiswd \axiswd=0.5pt \newdimen\plotincrement \plotincrement=0.1pt \newdimen\tmpdim \def\maingradx#1{% \lower1.5ex\clap{$\scriptscriptstyle#1$}% afficher l'argument au dessous \clap{\vrule height\maingraddim width\maingradwd depth0pt }% et la réglure } \def\subgradx{\clap{\vrule height\subgraddim width\subgradwd depth0pt }} % #1= dim entre 2 grad principales #2=nb départ #3=incrément #4=nb arrivée #4=nb intervalles secondaires \newmacro\xaxis[1cm]1[1]1[4]{% \hbox{% tout mettre dans une \hbox \setbox0=\hbox{% stocke dans une \hbox les grad secondaires entre 2 unités \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions \for\xx=1 to #5-1 \do{% insérer #5-1 fois \kern\dimsubgrad% une espace secondaire \subgradx% une graduation secondaire }% }% \rlap{% en débordement à droite : \FOR\xx = #2 to #4 \do #3{% pour chaque graduation principale \maingradx{\xx}% imprimer l'abscisse \ifdim\xx pt<#4pt % et en débordement à droite, \rlap{\copy0 }% les réglures secondaires, sauf pour la dernière \fi \kern#1\relax% se déplacer vers la droite }% }% \vrule% tracer l'axe des abscisses height\axiswd% d'epaisseur \axiswd, de longueur #1*(#4-#2)/#3 width\dimexpr#1*\decdiv{\dimtodec\dimexpr#4pt-#2pt\relax}{#3}\relax depth 0pt\relax % et de profondeur nulle }% } \def\maingrady#1{% affiche... \vlap{\llap{$\scriptscriptstyle#1$\kern2pt }}% l'ordonnée... \vbox to0pt{\vss\hrule height\maingradwd width\maingraddim depth0pt }% et la réglure } % affiche une subdiv \def\subgrady{\vlap{\hrule height\subgradwd width\subgraddim depth0pt }} % #1= dim entre 2 grad principales #2=abscisse départ #3=incrément % #4=abscisse arrivée #5=nb intervalles secondaires \newmacro\yaxis[1cm]1[1]1[4]{% \vbox{% \offinterlineskip% désactiver le ressort interligne \setbox0=\vbox{% stocke dans une \hbox les grad secondaires entre 2 unités \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions \for\xx=1 to #5-1 \do{% insérer #5-1 fois \kern\dimsubgrad% une espace secondaire \subgrady% une graduation secondaire }% }% \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% distance entre 2 subdivisions \vbox to 0pt{% en débordement vers le bas \FOR\xx = #4to#2\do-#3{% \maingrady{\xx}% imprimer l'abscisse \ifdim\xx pt>#2pt % et en débordement à droite, \vbox to 0pt{\copy0 \vss}% les réglures secondaires, sauf pour la dernière \fi \kern#1\relax% se déplacer vers la droite }% \vss% assure le débordement vers le bas }% \clap{\vrule% tracer l'axe des abscisses width\axiswd% d'épaisseur \axiwd, et de hauteur (#4-#2)/#3*#1 height\decdiv{\dimtodec\dimexpr(#4pt-#2pt)\relax}{#3}\dimexpr#1\relax depth 0pt\relax % profondeur nulle } }% } \newdimen\xunit \xunit=1cm \newdimen\yunit \yunit=1cm \newmacro\graphzone1[1]1[4]1[1]1[4]{% \quitvmode \begingroup \def\graphxmin{#1}\def\graphxmax{#3}% \def\graphymin{#5}\def\graphymax{#7}% \def\xincrement{#2}\def\yincrement{#6}% \setbox0\hbox{\yaxis[\yunit]{#5}[#6]{#7}[#8]}% \setbox1\hbox{\xaxis[\xunit]{#1}[#2]{#3}[#4]}% \edef\graphboxht{\the\ht0 }% \edef\graphboxwd{\the\wd1 }% \rlap{\box1 \clap{\vrule height\dimexpr\graphboxht+\axiswd\relax width\axiswd depth0pt }}% \rlap{\box0 }% \raise\graphboxht\rlap{\kern-.5\axiswd\vrule height\axiswd width\dimexpr\graphboxwd+\axiswd}% \begingroup \catcode`\^^M=9\relax \graphzone@i } \def\graphzone@i#1{% \endgroup \xunit=\decdiv1\xincrement\xunit \yunit=\decdiv1\yincrement\yunit \setbox0\hbox{#1}% \wd0=\dimexpr\graphboxwd+\axiswd\relax \ht0=\dimexpr\graphboxht+\axiswd\relax \box0 \endgroup \ignorespaces } \def\plot(#1,#2){% \edef\x@plot{#1}\edef\y@plot{#2}% \ifinside\x@plot[\graphxmin,\graphxmax]% {\ifinside\y@plot[\graphymin,\graphymax]% {\putat{\dimexpr\x@plot\xunit-\graphxmin\xunit\relax}{\dimexpr\y@plot\yunit-\graphymin\yunit\relax}\plotstuff}% \relax } \relax } \def\showyaxis{% \ifinside0[\graphymin,\graphymax]% {\putat\z@{-\graphymin\yunit}{\vrule width\graphboxwd height\axiswd}}% \relax } \def\showxaxis{% \ifinside0[\graphxmin,\graphxmax]% {\putat{-\graphxmin\xunit}\z@{\clap{\vrule width\axiswd height\graphboxht}}}% \relax } \def\showaxis{\showxaxis\showyaxis} \def\putat#1#2#3{% \leavevmode\rlap{\kern#1\vbox to0pt{\vss\hbox{#3}\kern#2}}% } \newmacro\cross[2pt][0.2pt]{% \quitvmode \vlap{% \clap{% \vrule height#2 depth0pt width#1 \vrule height#1 depth#1 width#2 \vrule height#2 depth0pt width#1 }% }% } \protected\def\numsep{\kern0.2em }% \numsep est le séparateur mis tous les 3 chiffres \def\formatdecpart#1{% #1=série de chiffres \ifempty{#1}% si la partie décimale est vide {}% ne rien afficher {{,}\formatdecpart@i1.#1..}% sinon, afficher la virgule et mettre en forme } % #1=compteur de caractères #2= chiffre à déplacer % #3= chiffres restants #4 = chiffres déjà traités \def\formatdecpart@i#1.#2#3.#4.{% #1=compteur de caractères #2= chiffres traité #3= chiffres traités \ifempty{#3}% si #2 est le dernier chiffre à traiter {#4#2}% le mettre en dernière position et tout afficher, sinon {\ifnum#1=3 \expandafter\firstoftwo \else\expandafter\secondoftwo \fi% si 3 chiffres sont atteint, rendre #1 égal à 1 et {\formatdecpart@i 1.#3.#4#2\numsep.}% mettre #2\numsep en dernier puis recommencer % sinon, mettre #2 en dernière position et recommencer % tout en incrémentant #1 de 1 {\expandafter\formatdecpart@i\number\numexpr#1+1.#3.#4#2.}% }% } \def\formatintpart#1{% #1=série de chiffres \expandafter\formatintpart@i\expandafter1\expandafter.% \romannumeral\reverse{#1\z@}..% appelle la macro récursive } % #1=compteur de caractères #2= chiffre à déplacer % #3= chiffres restants #4 = chiffres déjà traités \def\formatintpart@i#1.#2#3.#4.{% #1=compteur de caractères #2= chiffres traité #3= chiffres traités \ifempty{#3}% si #2 est le dernier chiffre à traiter {#2#4}% le mettre en première position et tout afficher, sinon {\ifnum#1=3 \expandafter\firstoftwo \else\expandafter\secondoftwo \fi% si 3 chiffres sont atteint, rendre #1 égal à 1 et {\formatintpart@i 1.#3.\numsep#2#4.}% mettre \numsep#2 en premier puis recommencer % sinon, mettre #2 en dernière position et recommencer % tout en incrémentant #1 de 1 {\expandafter\formatintpart@i\number\numexpr#1+1.#3.#2#4.}% }% } \def\removefirstzeros#1{% \removefirstzeros@i#1\removefirstzeros@i } \def\removefirstzeros@i#1{% \ifx\removefirstzeros@i#1% \removefirstzeros@i est lu donc tout le nombre a été parcouru \expandafter0% laisser un zéro \else \ifx0#1% si le chiffre lu est un 0 \expandafter\expandafter\expandafter\removefirstzeros@i% recommencer \else% sinon remettre le chiffre et tout afficher jusqu'à \removefirstzeros@i \expandafter\expandafter\expandafter\removefirstzeros@ii\expandafter\expandafter\expandafter#1% \fi \fi } \def\removefirstzeros@ii#1\removefirstzeros@i{#1} \def\ifnodecpart#1{\ifnodecpart@i#1.\@nil} \def\ifnodecpart@i#1.#2\@nil{\ifempty{#2}} \newbox\remainbox% boite contenant le texte total \newbox\currentline% boite contenant le ligne en cours \newcount\linecnt% compteur pour numéroter les lignes \newmacro\leftline[0pt]{% définit ce qui se trouve à gauche de chaque ligne \def\wd@left{#1}% \def\stuff@left } \newmacro\rightline[0pt]{% définit ce qui se trouve à droite de chaque ligne \def\wd@right{#1}% \def\stuff@right } \let\formatline=\identity \leftline[15pt]{$\scriptscriptstyle\number\linecnt$\kern5pt }% numérotation à gauche \rightline{}% rien à droite \def\numlines{% \par\smallskip \begingroup% dans un groupe semi simple \splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante \savingvdiscards=1 % autorise la sauvagarde des éléments supprimés \linecnt=0 % initialiser le compteur de lignes \setbox\remainbox=\vbox\bgroup% compose la boite... \advance\hsize by% diminuer la \hsize -\dimexpr\wd@left+\wd@right\relax% de la largeur des contenus } \def\endnumlines{% \egroup \offinterlineskip \split@line } \def\split@line{% \ifvoid\remainbox% si la boite est vide \par% fin du processus \endgroup% fermer le groupe ouvert au début \else% sinon \advance\linecnt 1 % incrémente le compteur de lignes \edef\htbefore{\the\vdim\remainbox}% \edef\restorevfuzz{\vfuzz=\the\vfuzz\relax}% sauvegarde le \vfuzz \vfuzz=\maxdimen% annule les avertissements pour débordement \setbox\currentline=\vsplit\remainbox to 0pt % couper la boite à 0pt de hauteur \restorevfuzz \setbox\currentline=\vbox{\unvbox\currentline}% redonner à la boite sa hauteur \let\savedsplitdiscards\splitdiscards \edef\intersplitspace{% calcul de l'espace vertical perdu à la coupure \the\dimexpr\htbefore-(\vdim\remainbox+\vdim\currentline)\relax }% \hbox{% en mode vertical et dans une hbox, afficher : \hbox to\wd@left{\hss\stuff@left}% 1) ce qui est à gauche \formatline{\box\currentline}% 2) la ligne courante \hbox to\wd@right{\stuff@right\hss}% 3) ce qui est à droite }% \splitdiscards% affiche ce qui a été ignoré à la coupure \expandafter\split@line% recommencer \fi } \newskip\interletterskip \newskip\interwordskip \catcode`\@11 \newtoks\spacetxt@toks% le registre qui contient le texte final \def\spacetxt{% \let\spacetxt@endprocess\spacetxt@endnormal % définit la macro appelée en fin de processus -> à priori : fin normale \ifstarred% si la macro est étoilée {\let\spacetxt@recurse\spacetxt@star% définir la macro récursive \spacetxt@i% et aller à \spacetxt@i }% sinon {\let\spacetxt@recurse\spacetxt@nostar% définir la macro récursive \spacetxt@i% et aller à \spacetxt@i }% } \newmacro\spacetxt@i[0.3em][3\interletterskip]1{% % arg optionnel #1 et #2 = ressorts inter-lettre et inter--mot % #3 = texte à espacer \interletterskip=#1\relax \interwordskip=#2\relax \def\spacetxt@code{#3}% met le texte à espacer dans \spacetxt@code \spacetxt@toks{}% initialiser le registre contenant le texte final \spacetxt@recurse% aller à la macro récursive précédemment définie } \newif\if@indivifound% booléen qui sera vrai si un motif spécial est rencontré \def\spacetxt@nostar{% \expandafter\ifempty\expandafter{\spacetxt@code}% si texte restant est vide \spacetxt@endprocess% aller à la fin du processus {\@indivifoundfalse% sinon, à priori, les motifs non réguliers ne sont pas trouvés \expandafter\doforeach\expandafter\indivi@tmp\expandafter\in\expandafter{\indivilist} % pour chaque \indivi@tmp dans \indivilist {\exptwoargs\ifstartwith\spacetxt@code\indivi@tmp % si le code commence par le motif courant {\eaddtotoks\spacetxt@toks{\indivi@tmp\hskip\interletterskip}% % l'ajouter dans le registre ainsi que l'espace inter-lettre \expandafter\rightofsc\expandafter\spacetxt@code\expandafter{\indivi@tmp}% % et enlever le motif du texte restant à lire \@indivifoundtrue % marquer qu'un motif a été trouvé \doforeachexit % et sortir prématurément de la boucle }% \relax % si le code ne commence pas le motif courant -> ne rien faire }% \if@indivifound \else% si aucun motif n'a été trouvé \grab@first\spacetxt@code\spacetxt@temp % retirer le 1er caractère du texte % \ifcat\noexpand\spacetxt@temp\sptoken \ifx\spacetxt@temp\space % si le 1er caractère est un espace \addtotoks\spacetxt@toks{\hskip\interwordskip}% % ajouter l'espace inter-mot au registre de token \else % si le 1er caractère n'est pas un espace \eaddtotoks\spacetxt@toks{\spacetxt@temp\hskip\interletterskip}% % ajouter ce caractère et l'espace inter-lettre au registre de token \fi \fi \spacetxt@recurse% enfin, continuer le processus }% } \def\spacetxt@star{% \expandafter\ifempty\expandafter{\spacetxt@code}% si texte restant est vide \spacetxt@endprocess% aller à la fin du processus {\expandafter\ifbracefirst\expandafter{\spacetxt@code}% % sinon, si texte commence par "{" {\grab@first\spacetxt@code\spacetxt@temp % mettre {>}% liste des motifs spéciaux \def\insert@blankchar{% \ifstarred\insert@blankchar@ii\insert@blankchar@i } \def\insert@blankchar@i#1{% insère une espace de largeur #1 caractères complets \ifnum\numexpr#1\relax>0 \kern\numexpr#1\relax\dimexpr\ttchar@width+\brktt@interletter\relax \fi } \def\insert@blankchar@ii#1{% insère #1-1 caractères complets + 1 largeur de caractère \ifnum\numexpr#1\relax>0 \insert@blankchar@i{#1-1}\kern\ttchar@width \fi } \def\restart@hbox#1{% \egroup \hbox\bgroup \expsecond{\def\tt@remaintext} {\romannumeral\removefirstspaces@i{#1}}% initialiser le code à composer \let\previous@char\space% initialise le caractère précédent \line@starttrue% aucun caractère n'a encore été imprimé \brktt@cnt=0\relax% remettre le compteur à 0 } \def\print@nchar#1{% affiche #1 caractères pris dans \tt@remaintext \for\xxx= 1 to #1 \do 1{% \ifx\tt@remaintext\empty% si le code restant à composer est vide \exitfor\xxx%sortir de la boucle prématurément \else \@indivifoundfalse% sinon, à priori, les motifs de ligature ne sont pas trouvés % pour chaque \indivi@tmp dans la liste de ligatures \expsecond{\doforeach\indivi@tmp\in}{\liglist}% {% si le code commence par la \idx{ligature} courante \exptwoargs\ifstartwith\tt@remaintext\indivi@tmp% {\let\previous@char\indivi@tmp% prendre le motif pour caractère courant \expsecond{\rightofsc\tt@remaintext}{\indivi@tmp}% et l'enlever du texte restant à lire% \@indivifoundtrue% marquer qu'un motif a été trouvé \doforeachexit% et sortir prématurément de la boucle }% {}% si le code ne commence pas le motif courant -> ne rien faire }% \unless\if@indivifound% si aucun motif trouvé, \grab@first\tt@remaintext\previous@char% lire le premier caractère \fi \advance\brktt@cnt by 1 % incrémenter le compteur de caractères \hbox to\ttchar@width{\hss\previous@char\hss}% afficher le caractère lu \line@startfalse% nous ne sommes plus au début d'une ligne \ifnum\brktt@cnt<\maxchar@num\relax% si la ligne n'est pas encore remplie \kern\brktt@interletter% insérer le ressort inter-lettre \else \exitfor\xxx% sinon, sortir de la boucle prématurément \fi \fi }% } \newmacro\breakttA[0.3em][\hsize]1{% % arg optionnel #1 et #2 = ressorts inter-lettre et dimension horizontale texte % #3 = texte à espacer \begingroup% ouvrir un groupe et le fermer à la toute fin \par% commencer un nouveau paragraphe -> passage en mode vertical \parindent=0pt% empêche l'indentation \tt% passer en police à chasse fixe \setbox0 = \hbox{M}% la boite 0 contient un caractère \edef\ttchar@width{\the\wd0 }% largeur de chaque caractère en police \tt \edef\text@width{\the\dimexpr#2\relax}% largeur de composition % les 2 lignes suivantes rendent le compteur égal à E((L-l)/(l+Delta)) \brktt@cnt=\numexpr\dimexpr#2-\wd0 \relax\relax% largeur diminuée du 1er caractère \divide\brktt@cnt by \numexpr\dimexpr\wd0 + #1 \relax\relax % le nombre de caractères par ligne est égal à 1 de plus : \edef\maxchar@num{\number\numexpr\brktt@cnt+1\relax}% % calcul de la dimension inter-lettre \brktt@interletter=\dimexpr(\text@width-\ttchar@width*\maxchar@num)/\brktt@cnt\relax \expsecond{\expsecond{\def\tt@remaintext}}{\removetrailspaces{#3}}% met le texte à espacer dans \tt@remaintext \addtomacro\tt@remaintext{ \relax}% ajouter " \relax" à la fin : le code finit donc par " \relax" \def\tt@emptytext{ \relax}% sera le code lorsque tout est composé \unless\ifx\tt@remaintext\tt@emptytext% si le texte à composer n'est pas vide \hbox\bgroup% démarrer la boite horizontale contenant la première ligne \insert@blankchar\ttindent% insérer une espace d'indentation de longueur (l+Delta')*\ttindent \brktt@cnt=\ttindent\relax% tenir compte du nombre de caractères indentés \line@starttrue% il s'agit du début d'une ligne \expandafter\breakttA@i% aller à la macro récursive \fi } \def\leftofsc#1#2{% dans la sc #1, garde ce qui est à gauche de #2 \def\leftofsc@i##1#2##2\@nil{\def#1{##1}}% \expandafter\leftofsc@i#1\@nil } \def\len@tonextword{% stocke dans \next@len le nombre de caractères avant % le prochain point de coupure dans \tt@remaintext \let\next@word\tt@remaintext% copie \tt@remaintext dans la macro temporaire \next@word \leftofsc\next@word{ }% ne prend que ce qui est avant le prochain espace \exparg\ifin{\next@word}{-}% si le mot contient un tiret {\leftofsc\next@word{-}% prendre ce qui est à gauche de ce tiret \def\extra@char{1}% il y a un caractère de plus à loger après le mot } {% sinon, le caractère après le mot est un espace \def\extra@char{0}% qu'il ne faut pas compter }% \setbox0=\hbox{\next@word}% enfermer le mot dans une boite % et en calculer le nombre de caractères \edef\next@len{\number\numexpr\dimexpr\wd0 \relax/\dimexpr\ttchar@width\relax\relax}% } \newmacro\zerocompose[]2{% % #1=code à exécuter avant la composition % #2=registre de boite recevant le résultat % #3= texte à composer en largeur 0pt \setbox#2=\vbox{% #1% code a exécuter (changement de fonte par exemple) \hfuzz=\maxdimen% annule les avertissements pour débordement horizontaux \hbadness=10000 % annule les avertissements pour mauvaise boite horizontale \pretolerance=-1 % désactive la première passe (celle sans coupures) \tolerance=10000 % passe avec coupures acceptée \hyphenpenalty=-10000 % favorise fortement les copures de mots \lefthyphenmin=2 \righthyphenmin=3 % longueur mini des fragments de début et fin \clubpenalty=0 % pas de \idx{pénalité} supplémentaire après la première ligne \interlinepenalty=0 % pas de \idx{pénalité} inter-ligne \widowpenalty=0 % pas de \idx{pénalité} supplémentaire avant la dernière ligne \exhyphenpenalty=0 % ne pas pénaliser une coupure explicite \leftskip=0pt \rightskip=0pt % désactive les éventuels ressorts latéraux \everypar={}% désactive l'éventuel \everypar \parfillskip=0pt plus1fil % règle le \parfillskip par défaut \hsize=0pt % largeur de composition = 0pt \edef\restorehyphenchar{\hyphenchar\font=\number\hyphenchar\font}% \hyphenchar\font=`\- % impose "-" comme caractère de coupure \noindent % pas d'indentation + passage en mode horizontal \hskip0pt \relax% premier noeud horizontal pour permettre la coupure de la suite #3\par% compose #3 \restorehyphenchar% restaure le caractère de coupure }% } \def\hyphlengths#1#2{%#2 = macro contenant les longueurs de coupures du mot #1 \begingroup \zerocompose [\tt% passer en police à chasse fixe \setbox\z@\hbox{M}\xdef\ttwidth{\the\wd\z@}% mesurer la largeur des caractères \hyphenchar\font=`\- % choisit "-" comme caractère de coupure ]\z@{#1}% compose en 0pt dans la boite 0 \let#2 = \empty% initialise la macro #2 \def\cumul@length{0}% \vfuzz=\maxdimen% annule les avertissements pour débordement \splittopskip=\z@ % ne rajouter aucun espace au sommet de la boite restante \loop \setbox1=\vsplit\z@ to \z@% couper la boite à 0pt de hauteur {\setbox\z@=\vbox{\unvbox1 \unskip\unpenalty\global\setbox1=\lastbox}}% \setbox1=\hbox{\unhbox1 }% \unless\ifvoid\z@% si la boite 0 n'est pas encore vide \edef\cumul@length{% mettre à jour \cumul@length \number\numexpr \cumul@length +% ajouter le quotient "largeur syllabe/largeur d'1 caractère" \wd1/\dimexpr\ttwidth\relax -1% et soustraire 1 (le caractère "-") \relax }% % ajouter à #2 la virgule et le cumul actuel +1 \edef#2{% définir la macro #2 : #2% reprendre le contenu de #2 \ifx#2\empty\else,\fi% ajouter "," si #2 non vide \number\numexpr\cumul@length+1\relax% et le cumul }% \repeat% et recommencer \expsecond{% avant de fermer le groupe \endgroup \def#2}{#2}% définit #2 hors du groupe } \newif\ifnumalgo \newcount\algocnt \numalgotrue \def\algoindent{2em} \def\algorule{.4pt}% épaisseur des réglures de début et de fin \def\algohrulefill{% remplit avec une ligne horizontale \leavevmode \leaders\hrule height\algorule\hfill \kern0pt % rajoute un noeud au cas où la commande est en fin de ligne } \def\algoleftskip{10pt} \def\algorightskip{10pt} \newmacro\algorithm[\\,\#,\{,\}]2{% \medbreak \begingroup% ouvre un groupe (sera fermé à la fin de l'algorithme) \footnotesize \algocnt=1 % initialise le compteur de lignes \leftskip=\algoleftskip \rightskip=\algorightskip% initialise les ressorts latéraux \parindent=0pt % pas d'indentation %%%%%%%%%%%% affichage du titre %%%%%%% \algohrulefill% remplir avec une ligne horizontale \ifempty{#2}% si #2 est vide {}% ne rien insérer {% sinon \lower.5ex\hbox{ #2 }% insérer le titre abaissé de 0.5ex \algohrulefill% insérer une ligne horizontale }% \par% aller à la ligne \nointerlineskip% ne pas insérer le ressort interligne \kern7pt % et sauter 7pt verticalement \nobreak% empêcher une coupure de page %%%%%%%%%%%%%% fin du titre %%%%%%%%%% % %%%%%%%%%%%%%% rend les caractères actifs %%%%%%%%%%%%%% \def~##1~{\begingroup\bf##1\endgroup}% \defactive:{% rend ":" actif \futurelet\nxttok\algoassign% \nxttok = token suivant }% \def\algoassign{% suite du code de ":" \ifx=\nxttok% si ":" est suivi de "=" \ifmmode% si mode math \leftarrow% afficher "\leftarrow" \else% si mode texte ${}\leftarrow{}$% passer en mode math pour "\leftarrow" \fi \expandafter\gobone% manger le signe "=" \else% si ":" n'est pas suivi de "="' \string:% afficher ":" \fi }% \expandafter\def\expandafter\oldeverypar\expandafter% sauvegarder... {\the\everypar}% ... l'action effectuée à chaque paragraphe \ifnumalgo% si la numérotation est demandée, \everypar\expandafter{% à chaque paragraphe \the\everypar% reprendre le contenu précédent \llap{% et à droite du début de la ligne, $\scriptstyle\number\algocnt$% afficher le numéro \kern\dimexpr4pt+\leftskip-\algoleftskip\relax% en tenant compte de l'indentation }% }% \fi \def\algopar{% actions effectués par ^^M \advance\algocnt by 1 % incrémente le compteur de lignes \color{black}% repasse en couleur noire \rm% fonte droite \par% termine le paragraphe \leftskip=\algoleftskip % initialise le ressort gauche }% \doforeach\currentchar\in{#1}{\expandafter\catcode\expandafter`\currentchar=12 }% \defactive\^^I{\advance\leftskip by \algoindent\relax}% \defactive\ {\hskip1.25\fontdimen2\font\relax}% espace = 25% de + que la largeur naturelle \letactive\^^M\algopar \defactive\%{\it\color{gray}\char`\%}% passe en italique et gris puis affiche "%" \defactive_{\ifmmode_\else\string_\fi}% \defactive#3{% rend le token #3 actif (il sera rencontré à la fin de l'algorithme) \everypar\expandafter{\oldeverypar}% restaurer l'ancien \everypar \par% aller à la ligne \nointerlineskip% ne pas insérer le ressort interligne \kern7pt % et sauter 7pt verticalement \nobreak% empêcher une coupure de page \algohrulefill% tracer la ligne de fin \smallbreak% saute un petit espace vertical \endgroup% ferme le groupe ouvert au début de l'algorithme }% %%%%%%%%%%%%%%%%% fin des caractères actifs %%%%%%%%%%%%%%%% % \sanitizealgo% va manger les espaces et les ^^M au début de l'algo } \def\sanitizealgo{\futurelet\nxttok\checkfirsttok}% récupère le prochain token \def\checkfirsttok{% teste le prochaun token \def\nextaction{% à priori, on considère que la suite est " " ou "^^M" donc \afterassignment\sanitizealgo% aller à \sanitizealgo \let\nexttok= % après avoir mangé ce "^^M" ou cet espace }% \unless\ifx\nxttok\algopar% si le prochain token n'est pas un ^^M \unless\ifx\space\nxttok% et si le prochain token n'est pas un espace \let\nextaction\relax% ne rien faire ensuite \fi \fi \nextaction% faire l'action décidée ci-dessus } \def\true@sgn#1{\ifnum#11<\z@-\fi} \restoreatcoatcode\relax \endinput