In this page we describe a non-trivial example of the
\loop command.
We also show how to analyse the transcript file produced by *Tralics*.

Consider a document that contains the following code:

% this is file testloop.tex $Revision: 1.4 $ %\tracingall \let\olda\a %% \newcount defines \a globally... \countdef\td 4 \td=0%% no need to allocate a counter \newif\ifprime \newif\ifunknown \newcount\n \newcount\p \newcount\d \newcount\a \newcount\N \def\primes#1{2,~3% assume that #1 is at least 3 \n=#1 \advance\n by-2 % n more to go \p=5 % odd primes starting with p \loop\ifnum\n>0 \printifprime\advance\p by2 \repeat} \def\printp{,% we will invoke \printp if p is prime \N=\n \divide\N by 8 \multiply \N by -8 \advance\N by \n \ifnum\N=0 { }\else{ }\fi \ifnum\n=1 and~\fi % this precedes the last value \number\p \advance\n by -1 } \def\printifprime{\testprimality \ifprime\printp\fi} \def\testprimality{{\d=3 \global\primetrue \loop\trialdivision \ifunknown\advance\d by2 \repeat}} \def\trialdivision{\a=\p \divide\a by\d \global\advance\td by 1 %% count calls to trialdivision \ifnum\a>\d \unknowntrue\else\unknownfalse\fi \multiply\a by\d \ifnum\a=\p \global\primefalse\unknownfalse\fi} The first thirty prime numbers are \primes{30}. trial division macro was expanded \the\td\ times. %% Knuth says cpu time=4 sec, I see 0.02 \let\a\olda %% restore

The XML translation by *Tralics* is:

<?xml version='1.0' encoding='iso-8859-1'?> <!DOCTYPE unknown SYSTEM 'unknown.dtd'> <!-- translated from latex by tralics 2.2 (pl3)--> <unknown> <p>The first thirty prime numbers are 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, and 113. trial division macro was expanded 132 times.</p> </unknown>

The example above is in the
TeXbook, at the end of Chapter 20. We just
added the code that computes the number of calls of
`\trialdivision`, and the test of `\n` mod 8, that
avoids long lines in the XML output.
You may wish to look at the description of:
\newcount,
\newif,
\ifnum.

We show here the content of the transcript file for version 2.15.4
rather than 2.2 (pl3) or 2.7 (pl5). The XML file is the same,
modulo (a) the encoding, which is now UTF-8, (b) the comment line with
the version also indicates teh compilation time,
(c) ` ` has been replaced aby a character.
The transcript file starts like this

Transcript file of tralics 2.15.4 for file testloop1.tex Copyright INRIA/MIAOU/APICS/MARELLE 2002-2015, Jos\'e Grimm Tralics is licensed under the CeCILL Free Software Licensing Agreement Start compilation: 2015/12/02 16:44:19 OS: Apple, machine maclaurin Starting translation of file testloop1.tex. Output encoding: UTF8 (idem transcript). Left quote is ` right quote is ' \notrivialmath=1 ++ Input encoding is 1 (iso-8859-1) for the main file ++ file .tralics_rc does not exist. ++ file ../confdir/.tralics_rc exists. Configuration file identification: standard $ Revision: 2.24 $ Read configuration file ../confdir/.tralics_rc. Configuration file has type \documentclass No \documentclass in source file Using some default type dtd is unknown from unknown.dtd (standard mode) OK with the configuration file, dealing with the TeX file... There are 30 lines

These lines are printed before translation starts. What you can see is that
some configuration file was used, that did not define a document type
for *Tralics* compatible with the current `\documentclass`
(there is no document class specified in the source). As a consequence,
the main XML element will be called `<unknown>`.

Let's remove the comment on the line with `\tracingall`.
This will have as effect to add much more lines to the transcript file.
(We could also use the --verbose flag, this would enter verbose mode
from the start, and add some additional lines to the transcript file).

As a general rule, all lines of the form [number] text
indicate source lines, read by *Tralics*. Other lines start with a plus
sign, an open brace, or a command.
A line of the form
{\let} indicates that the main command interpreter of *Tralics*
has executed the command `\let`. In some cases, a second
line is printed by the C++ code associated to the command. Here you
can see the arguments read by the command, and the effects: new and
old value of `\a`.

[3] \let\olda\a %% \newcount defines \a globally... {\let} {\let \olda \a} {changing \olda=undefined} {into \olda=\a}

Here, you can see that `\newif` reads a command, and
calls twice the inter code of `\let`.

[5] \newif\ifprime \newif\ifunknown {\newif} {\newif \ifprime} {changing \primetrue=undefined} {into \primetrue=macro:->\let \ifprime \iftrue } {changing \primefalse=undefined} {into \primefalse=macro:->\let \ifprime \iffalse }

This is now the trace for `\global\primetrue`.
The `\global` command is a prefix that applies to the assignment
that follows, that can be of the form `\def cmd` or
`\count nbr=...`; the next token is fully expanded.
In this case, `\primetrue` is a user defined command,
the transcript file shows the body and the arguments (there are none here).
The third line shows the argument of `\global` (the first token
in the assignment sequence). What happens is the following; if the first
non-expandable token is not an assignment operator, you will get an error
of the form *You cannot use the prefix \global before \par*. Otherwise,
the assignment will be marked global. You can have more than one
prefix (for instance, before `\def`); so you can
see *You cannot use the prefix \global\long before \count*.

{\global} \primetrue ->\let \ifprime \iftrue {\global\let} {\let \ifprime \iftrue} {globally changing \ifprime=\iffalse} {into \ifprime=\iftrue}

This behaviour is different from TeX: if you say
`\global \let \ifprime \iftrue`,
TeX will print only one line in its transcript file. And for the code above,
you will see:

{\global} \primetrue ->\let \ifprime \iftrue

Chapter 24 of the TeXbook starts
with explanations of what
a <number> or a <dimen>
is. These rules are complicated, this is why *Tralics* prints
in the transcript file lines starting
with +scanint or +scandimen
whenever such a thing is scanned.
In general the result is used by a command (here `\countdef`)
or put in a register (here register number 4, via its symbolic name
`\td`).
If you ask: what is `\td`, the answer will be
`\count4` (previous versions of *Tralics* printed something
like `\countref1540`, for a command that accesses to position 1540
in the table of integers). If the value of a counter is changed, you
see the old and new value. If these two values are equal, you
see reassigning instead of changing.

```
[4] \countdef\td 4 \td=0
{\countdef}
+scanint for \countdef->4
{\countdef \td=\count4}
{\count4}
+scanint for \td->0
{reassigning \count4=0}
...
{\divide}
+scanint for \N->8 %this divides N by 8
{changing \count69=28 into \count69=3}
```

In previous versions of *Tralics*, the third line
was of the form `{\countdef \n=1553}`, where 1553 is to be
interpreted as above (since 1540 was the index for counter 4, an easy
computation shows that 1553 is the index of counter 17; the current version
improves a bit the interpretation of the transcript file; even in
silent mode, the transcript contains a line for each use of
command of the type `\countdef`.

[6] \newcount\n \newcount\p \newcount\d \newcount\a \newcount\N {\newcount} {\countdef \n=\count65} ... {\countdef \N=\count69}

You may see lines like these when a group starts. Here a begin-group
character, i.e., a character of
`\catcode` 1, an open brace, caused
a new group to be pushed on the stack. The current level is increased to 2
(bottom level is number 1). You can also see that the current line is
27 (because all the stack alterations are the result of
the `\primes` command.)

{begin-group character} +stack: level + 2 for brace entered on line 27

You may see lines like these when a group ends. Here an end-group
character, i.e., a character of
`\catcode` 2, a closing brace, caused the
stack to be unwound.
The current execution level decreases (from 2 down to 1).
The commands `\ifunknown` and `\iterate` are
restored (we truncated the value). Two integers are restored (`\d`
and `\a`).

{end-group character} +stack: restoring \ifunknown=\iffalse. +stack: restoring \count68=0. +stack: restoring \iterate=macro:->\ifnum ... +stack: restoring \count67=0. +stack: level - 2 for brace from line 27

If in TeX, you say
`\par The first thirty prime numbers are`,
a line is added to the transcript for each group of letters
(a line is also added for each non-letter, for instance a space).
As can been seen, a paragraph is started, and horizontal mode is entered
because of the first letter.

{the letter T} {horizontal mode: the letter T} {blank space } {the letter f} {blank space } {the letter t} {blank space } {the letter p} {blank space } {the letter n} {blank space } {the letter a} {blank space }

This is the *Tralics* transcript file

Character sequence: The first thirty prime numbers are .

You can see that there is no difference between spaces and other characters. The character sequence starts with the character that follows the space after colon, and ends with the characters that precedes the period (in the example, the first character is a T, the last is a space). Currently, mode changes are not logged.

This is what the transcript file contains, starting with the line that follows the
definitions. Lines preceding line 25 contain definitions and global
assignments. Current mode is outer vertical, so that newline characters
between definitions are ignored. Empty lines and `\par` commands
are ignored as well, but appear in the transcript.

[25] {\par} [26] {\par}

A new paragraph is started when the letter T is seen. The following
things happen: current mode becomes horizontal; a new XML element is created;
a new frame is pushed on the stack. What you see in the transcript file is the
name of the frame, in some cases, as here, the frame and the element have the
same name. At the end of the run, the XML tree contains three sons: the
`<p>` element created here, and two newline
characters, one before and one after. This element contains
one son: a piece of text.

For every character translated, something is printed in the transcript
file. The transcript file is flushed whenever a non-character is seen
(here the `\primes` command). The characters are put in another
buffer, flushed when needed.

[27] The first thirty prime numbers are \primes{30}. trial division macro was {Push p 1} Character sequence: The first thirty prime numbers are .

You can see the body and arguments of the `\primes` command
(the transcript file contains a single line, contrarily to TeX that inserts
newline characters whenever the line is full).
You can also see that the expansion of `~` is
`\nobreakspace`. The lines
shown here add some characters to the XML buffer, namely 2, 3, a comma,
and whatever `\nobreakspace` translates to.

\primes #1->2,~3\n =#1 \advance \n by-2 \p =5 \loop \ifnum \n >0 \printifprime \advance \p by2 \repeat #1<-30 Character sequence: 2,. ~ ->\nobreakspace {\nobreakspace} Character sequence: 3.

This is the transcript for `\n =#1 \advance \n by-2 \p =5`.

{\count65} +scanint for \n->30 {changing \count65=0 into \count65=30} {\advance} +scanint for \n->-2 {changing \count65=30 into \count65=28} {\count66} +scanint for \p->5 {changing \count66=0 into \count66=5}

This is the outer loop: we have to compute `\n` prime numbers
(since we have already printed 2 and 3, the current value of `\n`
is 28), and the candidate `\p` is 5. After this is found to be prime
or not-prime, we increment it by two (because even numbers greater than two
cannot be prime) and continue the loop.

{\loop} {changing \iterate=undefined} {into \iterate=macro:->\ifnum \n >0 ... } \iterate ->\ifnum \n >0 \printifprime \advance \p by2 \relax \expandafter \iterate \fi

Here you can see the expansion of `\ifnum \n >0`. Two numbers
are compared, the result is true. *Tralics* indicates the values, it
also indicates for each conditional its serial number.

+\ifnum1 +scanint for \ifnum->28 +scanint for \ifnum->0 +iftest1 true

We do not indicate here what is done with the number 5. We skip some code and go to 13.

\printifprime ->\testprimality \ifprime \printp \fi ...

The idea is the following: we open a group, and set `\d` to 3.
We make the a priori assumption that our number is prime (the `\global`
is needed, because we have to know, outside the group, whether the number is
prime or not). The `\trialdivision` macro sets
`\ifunknown` to some value: if set to false, the status is known,
indicated by `\ifprime`. The number `\d` is incremented
by 2.
Loop invariant: we assume that `\p` is not a multiple of any
prime number less that `\d`).

\printifprime ->\testprimality \ifprime \printp \fi \testprimality ->{\d =3 \global \primetrue \loop \trialdivision \ifunknown \advance \d by2 \repeat } {begin-group character} +stack: level + 2 for brace entered on line 27 {\count67} +scanint for \d->3 {changing \count67=0 into \count67=3} {\global} \primetrue ->\let \ifprime \iftrue {\global\let} {\let \ifprime \iftrue} {globally changing \ifprime=\iftrue} {into \ifprime=\iftrue}

We are now in the inner loop. (note that `\iterate` is
locally redefined for each loop)

{\loop} {changing \iterate=macro:->... } {into \iterate=macro:-> ... } \iterate->\trialdivision \ifunknown \advance \d by2 \relax \expandafter \iterate \fi

We divide 13 by 3, quotient in `\a`, and increment the number
of divisions. We set `\ifunknown` to false, because 3 is less than
4. Said otherwise our divider `\d` is less that the square root
of `\p`, not big enough to conclude that `\p` is prime.

\trialdivision ->\a =\p \divide \a by\d \global \advance \td by 1 \ifnum \a >\d \unknowntrue \else \unknownfalse \fi \multiply \a by\d \ifnum \a =\p \global \primefalse \unknownfalse \fi {\count68} +scanint for \a->13 {changing \count68=0 into \count68=13} {\divide} +scanint for \a->3 {changing \count68=13 into \count68=4} {\global} {\global\advance} +scanint for \td->1 {globally changing \count4=4 into \count4=5} +\ifnum28 +scanint for \ifnum->4 +scanint for \ifnum->3 +iftest28 true \unknowntrue ->\let \ifunknown \iftrue {\let} {\let \ifunknown \iftrue} {changing \ifunknown=\iffalse} {into \ifunknown=\iftrue} +\else28 +\fi28

We multiply 4 by 3, in `\a`, and compared to
`\p`. Comparison is false, `\d` does not divide
`\p`.
We cannot conclude that `\p` is not prime.

{\multiply} +scanint for \a->3 {changing \count68=4 into \count68=12} +\ifnum29 +scanint for \ifnum->12 +scanint for \ifnum->13 +iftest29 false +\fi29

Since we do not know that our number is prime, we must continue checking.
We first increment out divider. We find it convenient to
exchange the order of the `\iterate` and `\fi` tokens
(in this case, the only effect of the `\fi` is to pop the condition
stack). Purpose of the `\relax`: the scanint procedure reads an
integer, and could evaluate the `\expandafter` without this token
(said otherwise, the space after the digit 2 is not necessary).

+\ifunknown30 +iftest30 true {\advance} +scanint for \d->2 {changing \count67=3 into \count67=5} {\relax} {\expandafter \iterate \fi} +\fi30

We have to execute the loop a second time.

\iterate ->\trialdivision \ifunknown \advance \d by2 \relax \expandafter \iterate \fi \trialdivision ->\a =\p \divide \a by\d \global \advance \td by 1 \ifnum \a >\d \unknowntrue \else \unknownfalse \fi \multiply \a by\d \ifnum \a =\p \global \primefalse \unknownfalse \fi

We see here that 5 does not divide 13. However
13 is less than `5\times(5+1)`, hence is prime.
Said otherwise, conditional number 31 shows that primality of 13 depends
only on the result of test 32.

{\count68} +scanint for \a->13 {changing \count68=12 into \count68=13} {\divide} +scanint for \a->5 {changing \count68=13 into \count68=2} {\global} {\global\advance} +scanint for \td->1 {globally changing \count4=5 into \count4=6} +\ifnum31 +scanint for \ifnum->2 +scanint for \ifnum->5 +iftest31 false +\else31 \unknownfalse ->\let \ifunknown \iffalse {\let} {\let \ifunknown \iffalse} {changing \ifunknown=\iftrue} {into \ifunknown=\iffalse} +\fi31 {\multiply} +scanint for \a->5 {changing \count68=2 into \count68=10} +\ifnum32 +scanint for \ifnum->10 +scanint for \ifnum->13 +iftest32 false +\fi32

Since we know whether our number is prime or not, it is time to conclude
the inner loop. The assignment
`\let\iterate\relax` is not really useful, as will be seen in a
minute. Its purpose is to release memory.

+\ifunknown33 +iftest33 false +\fi33 {\let} {\let \iterate \relax} {changing \iterate=macro:->... } {into \iterate=\relax}

The last action of `\testprimality` is to close the current
group. What you can see is that two integers are restored (`\a`
and `\d`).
The value of `\iterate` is restored; this is the code of the outer loop.

{end-group character} +stack: restoring \ifunknown=\iffalse. +stack: restoring \count68=0. +stack: restoring \iterate=macro:->\ifnum \n >0 ... \fi . +stack: restoring \count67=0. +stack: level - 2 for brace from line 27

Our number is prime! We print it.

+\ifprime34 +iftest34 true \printp ->,\N =\n \divide \N by 8 ... Character sequence: ,.

We print a space or a newline character, depending on whether
`\n` is zero modulo 8. This piece of code is not in the original
program. The test could also have been:
`\ifnum\N=0 \char13 \else \char32 \fi`

{\count69} +scanint for \N->25 {changing \count69=2 into \count69=25} {\divide} +scanint for \N->8 {changing \count69=25 into \count69=3} {\multiply} +scanint for \N->-8 {changing \count69=3 into \count69=-24} {\advance} +scanint for \N->25 {changing \count69=-24 into \count69=1} +\ifnum35 +scanint for \ifnum->1 +scanint for \ifnum->0 +iftest35 false +\else35 {begin-group character} +stack: level + 2 for brace entered on line 27 Character sequence: . {end-group character} +stack: level - 2 for brace from line 27 +\fi35

It is not the last number, so that only comma-space is printed.

+\ifnum36 +scanint for \ifnum->25 +scanint for \ifnum->1 +iftest36 false +\fi36

The transcript shows a `\fi34` corresponding to the `\ifprime`
above and `\fi27` that corresponds to nothing. In fact,
we skipped a big part of the trace. Testing whether n is prime
for n=5, 7, 9, 11, 13, and 15 uses the conditional 5, 12, 19, 24,
34, and 41: you can see that one more test is needed for 13 than for
other numbers; remember that if a number is prime, a further test has to be
done: is it the last one?

+scanint for \number->13 Character sequence: 13. {\advance} +scanint for \n->-1 {changing \count65=25 into \count65=24} +\fi34 {\advance} +scanint for \p->2 {changing \count66=13 into \count66=15} {\relax} {\expandafter \iterate \fi} +\fi27

This considers the number 15.

\iterate ->\ifnum \n >0 \printifprime \advance \p by2 \relax ... +\ifnum37 +scanint for \ifnum->24 +scanint for \ifnum->0 +iftest37 true \printifprime ->\testprimality \ifprime \printp \fi \testprimality ->{\d =3 \global \primetrue \loop \trialdivision ... {begin-group character} +stack: level + 2 for brace entered on line 27 +scanint for \d->3 {changing \count67=0 into \count67=3} {\global} \primetrue ->\let \ifprime \iftrue {\global\let} {\let \ifprime \iftrue} {globally changing \ifprime=\iftrue} {into \ifprime=\iftrue}

Dividing 15 by 3 is not enough to prove that 15 is a prime number.

{\loop} {changing \iterate=macro:->\ifnum ... \fi } {into \iterate=macro:->\trialdivision \ifunknown ... \fi } \iterate ->\trialdivision \ifunknown \advance \d by2 \relax ... \trialdivision ->\a =\p \divide \a by\d \global \advance \td by 1 ... {\count68} +scanint for \a->15 {changing \count68=0 into \count68=15} {\divide} +scanint for \a->3 {changing \count68=15 into \count68=5} {\global} {\global\advance} +scanint for \td->1 {globally changing \count4=6 into \count4=7} +\ifnum38 +scanint for \ifnum->5 +scanint for \ifnum->3 +iftest38 true \unknowntrue ->\let \ifunknown \iftrue {\let} {\let \ifunknown \iftrue} {changing \ifunknown=\iffalse} {into \ifunknown=\iftrue} +\else38 +\fi38

But dividing 15 by 3 *is* enough to prove that 15 is a not prime number.

{\multiply} +scanint for \a->3 {changing \count68=5 into \count68=15} +\ifnum39 +scanint for \ifnum->15 +scanint for \ifnum->15 +iftest39 true {\global} \primefalse ->\let \ifprime \iffalse {\global\let} {\let \ifprime \iffalse} {globally changing \ifprime=\iftrue} {into \ifprime=\iffalse} \unknownfalse ->\let \ifunknown \iffalse {\let} {\let \ifunknown \iffalse} {changing \ifunknown=\iftrue} {into \ifunknown=\iffalse} +\fi39

Skipping 6000 lines.

+iftest563 false +\fi563 {\let} {\let \iterate \relax} {changing \iterate=macro:->\ifnum ... \fi } {into \iterate=\relax} Character sequence: . trial division macro was . [28] expanded \the\td\ times. % Knuth says cpu time=4 sec, I see 0.02 Character sequence: expanded . {\the} {\the \td} \the->132. Character sequence: 132. {\ } Character sequence: times. .

Line 29 of the input file is a hack: the `\a` command is
not defined by plainTeX, so that Knuth can use, but it is defined in
LaTeX, and we have to restore it.

[29] \let\a\olda %% restore {\let} {\let \a \olda} {changing \a=\count68} {into \a=\a}

The last line of the file is empty, this is equivalent `\par`,
and has as effect to finish the current element.

[30] {\par} {Text:The first thirty prime numbers are 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, and 113. trial division macro was expanded 132 times. } {Pop 1: document_v p_v} ++ Input stack empty at end of file

Note the last line of the file, the empty one, is not really needed.
Without it, the last line of the XML result would have been
`expanded 132 times. </p></unknown>`.
In fact, when *Tralics* executes a `\par` command, it removes
the last space (that corresponds in general to the end-of-line
on the line before the empty line), and adds a newline character in the XML
tree after the `<p>` element.
In this example, there is a space before
the comment line 28, the EOL characters on lines 28 and 29 are commented out,
and this one is visible if the paragraph is ended without a
`\par` command.

back to home page © INRIA 2003, 2006 Last modified $Date: 2015/12/15 09:13:31 $