ProTEX for C

Stephen A. Fulling Department of Mathematics

Texas A&M University

version of August 21, 1995

Preliminary extended version, for private distribution only

ProTEX is the creation of Eitan M. Gurari. The definitive reference on it constitutes the last third of his book, TEX and LaTEX: Drawing and Literate Programming, McGraw–Hill, New York, 1994.

The purpose of this document is to provide a brief, practical introduction to the use of ProTEX in writing “literate” C programs embodying mathematical algorithms (rather than, for example, systems programming or data processing applications). The same principles apply to other programming languages and other types of applications. For full discussion of the program’s features and options, see the book.

Literate programming

The goal of literate programming is to make computer programs as readable by human beings (including a program’s own author, at a later date!) as they are by machines. This goal is achieved in two main ways:

1. The full power of TEX is unleashed upon the content and appearance of the written explanation of the code. In particular, mathematical formulas are typeset in standard mathematical notation. The resulting documentation is far superior to anything that even the most diligent programmer can provide in the form of comments in an ascii file.

2. The fragments of code that make up a program are presented in the order that is easiest to understand and think about, not the order in which they must be presented to the compiler. Technical, low-level pieces of the program are represented in the listing of higher-level parts by descriptive, summarizing phrases (“pseudocode”). The literate programming system is responsible for assembling the fragments in the right order before the code is sent to the compiler.

Any system that performs the second function must preprocess the source file to produce a valid .c file. (This is called “tangling”.) Most literate programming systems also, as a separate operation, preprocess the source file into a .tex file, which is then used to fulfill the first function. (This is called “weaving”.)

ProTEX is one of the simplest literate programming systems, in that it avoids weaving entirely and does not need separate software to perform its tangling. A ProTEX source file is already a valid .tex file. All processing is done by TEX itself, with the aid of two rather short TEX macro files. When the source file is run through TEX, the latter produces both a .dvi file containing the typeset documentation, and a .c file ready to be compiled.

This simplicity gives ProTEX a tremendous advantage of portability. The source code is fully usable (compilable, listable, and modifiable) by anyone possessing TEX and a C compiler; it is not necessary to have installed any special software (except the macro files ProTex.sty and AlProTex.sty). Furthermore, the additional syntax (beyond those of C and basic TEX) that must be learned in order to write ProTEX programs is quite small. ProTEX therefore promises to overcome the major problem of the literate programming movement — the tendency of each system to be used only by a narrow circle of enthusiasts, who must abandon it when working on collaborative projects with coworkers who have not converted to the faith.

A simple example

Here is a short file that demonstrates the important features of ProTEX as applied to C. Of course, for illustrative purposes the code has been cut up into ridiculously small fragments and decorated with some rather pointless commentary. (In a real literate program, a typical fragment consists of about 10 lines of code.) The input file and its outputs are intended to be studied in conjunction with the explanation provided in the next section (“Analysis of the example”).

% PTSAMPLE.TEX with BACKSLASH as escape character
\input ProTex.sty
\AlProTex{c,<<<>>>,list,title,_^}
\font\headingfont=cmbf10 scaled \magstep1
\font\smc=cmcsc10
\outer\def\heading{\bigskip\penalty-8000
 \bgroup\let\\=\cr\tabskip\centering\halign to \hsize
 \bgroup\smc\hfill\ignorespaces##\unskip\hfill\cr}
\def\endheading{\cr\egroup\egroup\nobreak\medskip}
\heading \headingfont ptsample \endheading
\bigskip
\heading The main program \endheading
This is a totally useless program to demonstrate Pro\TeX.
The first fragment of code is just the skeleton of a simple C program.
\<ptsample\><<<
\<Compiler preliminaries\>
main()
{  \<Do something here.\>
   exit(0);
}
>>>
\heading Some code segments \endheading
Let's make the canonical statement.
\<Do something here.\><<<
   printf("\\nHello, world!");
>>>
Now that we've finished the main idea of the program, we must remember to
put in any technical stuff that the C compiler needs to start work on our
program.
\<Compiler preliminaries\><<<
#include <stdio.h>  \% We need this in order to print\bf anything.  \%
>>>
\heading Appending a fragment to a code segment \endheading
As an afterthought, let's savor our accomplishment.
\<Do something here.\><<<
   printf("\\n\\n  We really did something here!");
>>>
\OutputCode\<ptsample\>
\bye

The command tex ptsample causes the creation of two files. One is the computer program, ptsample.c:

#include <stdio.h>
main()
{     printf("\nHello, world!");
   printf("\n\n  We really did something here!");
   exit(0);
}

The other product is ptsample.dvi, which, when processed by a standard TEX device driver, yields this typeset output:

ptsample

The main program

This is a totally useless program to demonstrate ProTEX. The first fragment of code is just the skeleton of a simple C program.

                                  ▷▷ ptsample ◁◁
      ◁◁ Compiler preliminaries ▷▷
      main()
      { ◁◁ Do something here. ▷▷
         exit(0);
      }

⋄ ⋄ ⋄

Some code segments

Let’s make the canonical statement.

                             ▷▷ Do something here. ◁◁
         printf("\nHello, world!");

⋄ ⋄ ⋄

Now that we’ve finished the main idea of the program, we must remember to put in any technical stuff that the C compiler needs to start work on our program.

                           ▷▷ Compiler preliminaries ◁◁

#include <stdio.h> We need this in order to print anything. ⋄ ⋄ ⋄

Appending a fragment to a code segment

As an afterthought, let’s savor our accomplishment.

                           ++▷▷ Do something here. ◁◁++
         printf("\n\n  We really did something here!");

⋄ ⋄ ⋄

Analysis of the example

It is easy to write ProTEX programs following the file ptsample.tex as a template. The following commentary should assist you in such endeavors.

The opening lines

    \input ProTex.sty
    \AlProTex{c,<<<>>>,list,title,_^}

load the ProTEX macros. \AlProTex{...} is a macro defined in ProTeX.sty, which causes the file Al-ProTex.sty to be input with certain options. (Some of the hieroglyphics in the \AlProTex line will be explained in the next section.) Incidentally, ProTEX is compatible with LaTEX; in that case, the line \input

ProTex.sty should come after the \documentstyle command and before \begin{document}.

The next block of 6 lines of code has nothing to do with literate programming per se. Since ProTEX provides no direct support for titles and section headings, I have inserted a simple macro to facilitate their construction. If you use a macro package, such as LaTEX, that provides such high-level formatting, then you can omit this block.

In the example program we next see a title and section heading constructed with the \heading command, followed by some introductory text stating the purpose of the program and identifying the first fragment of the ProTEX listing as the top-level (outer) framework of the eventual .c file.

Each fragment of code is introduced by a line of the form

    \<title\><<<

and is ended by a complementary line

     >>>

The title of the top-level fragment is the same as the title of the program. In the typeset listing, the title appears as a centered heading inside inward-pointing triangles.

The lines in a code fragment may be either ordinary lines of C code, or pseudocode references to other code segments.

The C statements are typed as usual (with indentation at the typist’s discretion), with one important exception: Because the use of the backslash character in TEX conflicts with that in C, a single backslash that is to appear in the ultimate .c file must be typed as a double backslash in the ProTEX source file. The most common occurrence of backslashes is in quoted newline characters, as in “printf("\n")”. We shall see in the next section that there is a way to eliminate this nuisance, but it carries its own price.

A pseudocode line has the form

\<title\>

It calls for the code segment with the given title to be inserted into the .c file at this point. In the typeset listing, the title of the cited segment appears in the citing segment inside outward-pointing triangles.

A “segment” consists of one or more “fragments”. When a fragment is introduced with a title that has already been used as the title of another fragment, it is recognized as the continuation of a segment. The printed title of such a fragment is labeled as a continuation by the + symbol. In the tangling process, the fragments constituting a segment are assembled in order and inserted into the .c file in the place (or places) where the segment is cited. This process can be seen clearly in the example ptsample: There the main program segment cites two other segments. Those segments are introduced by the fragments in the second section of the source listing. One of them is extended by another fragment, presented in the third section.

Normally each fragment will be preceded (or followed) by a paragraph of text that explains it. Also, short comments can be attached to individual code lines, as in traditional programming. When such comments are typed in the form

\%comment\%

ProTEX prints them in the listing (with italics as the default font) and does not pass them through to the .c file. You might experiment with comments of the forms

/*comment*/

and

    \%/*comment*/\%

Finally, the program ends with the lines

    \OutputCode\<ptsample\>
    \bye

which cause the .c code to be written to disk and terminate the TEX session. (Of course, ptsample should be replaced by the title of your own program.)

Options

The first argument of \AlProTex{c,...} is the file extension to be assigned to the output code (c for C, p for Pascal, etc.). The other arguments can be listed in any order, and they are simply omitted when the default behavior is desired.

Among these, you will probably never want to do without the options <<<>>>, list, and title. The option “_^” is necessary to prevent conflicts between the uses of those two characters in C and in TEX.

Adding [] (a pair of brackets) to the argument list of \AlProTex changes the format of the printed output: The titles of code segments appear inside rectangular boxes instead of between little triangles.

You can make other changes in format if you are willing and able to revise the style files. For example, the standard indication of a continuation fragment is a single plus sign following the heading. I prefer a less subtle and more symmetrical form, so I changed my copy of ProTex.sty to print two plus signs at the beginning and two at the end of the heading. I did so by changing the line

     \def\:FrontTitle##1{\PortTitle{##1}\:AppendSign}%

to

      \def\:FrontTitle##1{\:AppendSign\:AppendSign\PortTitle{##1}%
      \:AppendSign\:AppendSign}%

If you do this sort of thing, be sure to insert a comment at the top of the file indicating that it has been modified, and sign it! If feasible, comment each modified line also.

Adding @ to the argument list changes the escape character of TEX (the one that begins a control sequence) from \ to @ within the code parts of the source file. Consider the following variant of our sample program:

% PTSAMPLE.TEX with ATSIGN as escape character
\input ProTex.sty
\AlProTex{c,<<<>>>,list,title,@,_^}
\catcode`\@=0
\font\headingfont=cmbf10 scaled \magstep1
\font\smc=cmcsc10
\outer\def\heading{\bigskip\penalty-8000
 \bgroup\let\\=\cr\tabskip\centering\halign to \hsize
 \bgroup\smc\hfill\ignorespaces##\unskip\hfill\cr}
\def\endheading{\cr\egroup\egroup\nobreak\medskip}
\heading \headingfont ptsample \endheading
\bigskip
\heading The main program \endheading
This is a totally useless program to demonstrate Pro\TeX.
The first fragment of code is just the skeleton of a simple C program.
@<ptsample@><<<
@<Compiler preliminaries@>
main()
{  @<Do something here.@>
   exit(0);
}
>>>
\heading Some code segments \endheading
Let's make the canonical statement.
@<Do something here.@><<<
   printf("\nHello, world!");
>>>
Now that we've finished the main idea of the program, we must remember to
put in any technical stuff that the C compiler needs to start work on our
program.
@<Compiler preliminaries@><<<
#include <stdio.h>  @% We need this in order to print@bf anything.  @%
>>>
\heading Appending a fragment to a code segment \endheading
As an afterthought, let's savor our accomplishment.
@<Do something here.@><<<
   printf("\n\n  We really did something here!");
>>>
\OutputCode@<ptsample@>
\bye

The two versions of ptsample.tex produce identical .c files and functionally identical .dvi files. The only differences are in how the information is typed in.

First, the newline character \n is now recognized as a normal part of C code, not a TEX command. So it is no longer necessary to type “\\n” instead.

Second, the code titles now have the form

@<title@>

instead of

\<title\>

This style is very similar to that employed in most other literate programming systems for C, such as CWEB.

For these two reasons, the @ option is convenient if code previously written in ordinary C, or in another literate programming language, is to be converted to ProTEX, or vice versa. Furthermore, there are sound philosophical and practical reasons for wanting the code in the source file to be as close as possible to genuine C code.

On the other hand, we must type “@” instead of “\” also in the comment delimiter @% and in any TEX control sequence that appears inside a comment (but not in those that appear in the text outside the code fragments). (Note, in particular, the comment attached to the #include statement.) This can be disconcerting.

Furthermore, one might want to use ProTEX with some other programming language, such as Mathematica, in which the @ character plays a prominent role. In that case the @ option would be highly inappropriate. Forever switching the style of source coding within a project involving both C and Mathematica also would be disconcerting.

A final useful option is ShowIndex. It turns on ProTEX’s indexing feature. A command \ShowIndex at the end of the file then produces an index of the code segments, listing by page number all their appearances in the printed output (both the fragments defining them and the pseudocode references to them). One can also create index entries for other words or phrases by inserting commands of the form

    \TextIndex{term}

into the TEX source at the appropriate places. These features are demonstrated in the longer sample program in the Appendix.

Limitations of ProTEX; Comparison with other systems

ProTEX must ask TEX to do some things for which it was not specifically designed. Consequently, TEX sometimes rebels in unexpected ways, which are likely to frustrate the inexperienced ProTEX user.

You may have considered the typing style in ptsample.tex to be rather cramped. The reason is that ProTEX is very fussy about unnecessary spaces. When in doubt, leave them out! Otherwise, you may get TEX error messages, and whole parts of your program may be mysteriously missing from the output code.

A particularly vicious kind of superfluous space is one that is invisible on the monitor screen because it comes at the end of a line. ProTEX requires the <<< that starts a fragment to be followed immediately by a carriage return. If, instead, there is a space character after the <<<, then some versions of TEX will halt with the error message

    !  Use of \:ScanAppend doesn't match its definition.

(In my case, a program that had functioned perfectly on a unix server crashed in this way on my dos machine, and it took me a couple of hours to figure out what was wrong.)

The @ option of AlProTex is incompatible with some standard macro packages, such as vanilla.sty, that redefine the @ character for their own purposes. Debugging such problems requires an understanding of TEX’s category codes.

Two other things that will cause grief are trying to use TEX control sequences (such as font changes) inside segment titles, and trying to use \TextIndex inside code fragments (including the comments).

ProTEX’s indexing capabilities are rather primitive. The index is not alphabetized. Unlike CWEB, for instance, ProTEX does not automatically provide an index of all the identifiers in the program. The processing of the index by TEX is rather slow. And the dreaded message, TeX capacity exceeded, is lurking in the background. For this reason, ProTEX is probably not the best choice for a large program (that is, one of size comparable to TEX itself, for which the source code is of book length).

There is, in fact, a hierarchy of types of literate programming systems, represented in order of increasing sophistication by

    CNOWEB

ProTEX

    NOWEB
    CWEB

(This list is far from exhaustive, particularly in the last two categories.)

CNOWEB is a simple TEX macro file that “pretty-prints” C source code and commentary. It cleverly allows the same source file to serve as both a valid .c file and a valid .tex file. Thus it is one step simpler even than ProTEX. CNOWEB does not “tangle”; the lines of the program are compiled in the order in which they’re typed. For this reason it must be considered only “half-literate”. Nevertheless, it is perfectly adequate for very short programs (one or two pages), such as most undergraduate classroom exercises.

ProTEX, as previously explained, constructs a file of C code (or whatever language is in use) from a source file that is also a finished .tex file. It is therefore literate in both senses, although it does not involve an explicit “weaving” step. It can be recommended for programs of medium length (with listings of, say, 30 pages), for which extensive indexing is not necessary.

More sophisticated systems such as NOWEB and CWEB deal with source files that are neither .c nor .tex, and process them into both .c (or other code) and .tex files through two respective pieces of special software. They provide elaborate indexing and cross-referencing of code fragments and identifiers. (This is regarded as a third necessary component of “literacy” by the most demanding practitioners of the art.)

CWEB and some other systems go to great lengths to format and typeset code in a variety of fonts and in a form close to standard mathematical notation, rather than in the pure ascii traditionally associated with computer programs. (The desirability of such a transformation is highly controversial among literate programmers.) Such systems must “understand” the programming language they are processing, so they tend to be developed separately for each language.

Other systems, such as NOWEB, are less ambitious with respect to formatting of code — simply passing it through to TEX as ProTEX does, in a single ascii font, with the programmer responsible for indentation, spacing, etc. The computer programs comprising them tend to be smaller. Like ProTEX, they can handle any programming language.

APPENDIX: A serious example program

Here we present a real program written in ProTEX and rewritten in CWEB for comparison. (More accurately: the program was written first in straight C, translated later into CWEB, and still later into ProTEX.) The source file and the typeset listing are shown for both systems.

ProTEX source file

% WKBFLIST.TEX
\input ProTex.sty
\AlProTex{c,<<<>>>,list,title,_^,ShowIndex}
\font\headingfont=cmbf10 scaled \magstep1
\font\smc=cmcsc10
\outer\def\heading{\bigskip\penalty-8000
 \bgroup\let\\=\cr\tabskip\centering\halign to \hsize
 \bgroup\smc\hfill\ignorespaces##\unskip\hfill\cr}
\def\endheading{\cr\egroup\egroup\nobreak\medskip}
\def\bN{{\bf N}}
\heading \headingfont wkbflist
\quad\rm(formerly \tt grind) \endheading
\bigskip
\heading Introduction \endheading
 This is the first of two programs described in \TextIndex{References}
\smallskip{\narrower\noindent
      S. A. Fulling, The local geometric asymptotics of continuum
      eigenfunction expansions.    II.\ One-dimen\-sional systems,
      {\sl SIAM J. Math.\ Anal.\ \bf14}, 780--795 (1983)
\par}\smallskip\noindent
(henceforth cited as ``the paper'') and in
\smallskip{\narrower\noindent
      S. A. Fulling,  The analytic approach to recursion
      relations, {\sl J. Symbolic Comput.  \bf 9},  73--85 (1990).
\par}\smallskip\noindent
 For one-dimensional vectorial Schr\"odinger equations,
 $$  -\, {d^2\psi\over dx^2} - E(x)\psi(x) = p^2 \psi(x)$$
(where the potential function $E$ is matrix-valued), the programs
calculate the coefficients in the WKB--Fr\"oman or phase-integral
approximation to the eigenfunctions and in the related spectral
measures.
 Please consult the paper for details of
notation\TextIndex{Notation}.
 The key object is $N(x)$, the ``logarithmic-derivative matrix'' of
an eigenfunction, defined by the condition
 $$\psi' =ipN\psi$$
and expanded as
 $$N \sim 1 + \sum_{s=2}^\infty p^{-s} \,{i^{s-2}
   \over 2^{s-1}}\, \bN_s \,,$$

where $\bN_s$ contains terms of total ``order'' $s$;
for example,
 $$\bN_6 = E'''' + 3EE'' +3E''E + 5(E')^2 + 2 E^3.$$
 (Each potential is counted like two derivatives.)
 From this series one obtains asymptotic expansions of the spectral
densities in the eigenfunction expansion, and hence
 \TextIndex{Applications}
expansions of the associated heat kernel, resolvent kernel, etc.
\smallskip
{\bf This is a ProTeX version of the program called {\tt GRIND.C}
in the papers.}
\smallskip
 This program prints out a list of all possible terms of a given
order, in the code described on p.~791 of the paper.
 This output is to be piped into the program {\tt WKBFCOEF}, which
calculates the coefficient of each term in the WKBF series or in
the expansion of one of the spectral measures.
 Note that the repetition of a term with reversed order of the
factors is omitted as redundant;  \TextIndex{Redundant terms}
 see the captions of Tables 2 and 3 of the paper for the proper
interpretation in each case.  \smallskip
 {\bf Command-line syntax:} \quad  {\tt wkbflist} $<$order$>$
   $<$label parameter$>$
\smallskip
 \noindent   The order should be at least 2; if it is too large
(larger than about 80), the parameter {\tt NMAX} will need to be
increased, along with {\tt MAXLEN} and {\tt MAXCARD} in {\tt WKBFCOEF}.
 The label parameter should be 0 for producing input to {\tt
WKBFCOEF},
  nonzero for producing printed output.
 (When it's nonzero, the
order and a count of the terms are printed out.)
\smallskip
 {\tt NMAX} is the maximum number of factors allowed in a term.
 The program's structure is
\<wkbflist\><<<
#define NMAX 40
\<Compiler preliminaries\>
\<Main program\>
\<Central algorithm\>
\<Incidental functions\>
>>>
We need two global variables, one to count the terms and one to
help us recover the starting point after the recursive algorithm.
\<Compiler preliminaries\><<<
#include <stdio.h>
int ctr, d1;
>>>

\heading The central, recursive algorithm \endheading
The function {\tt grind(item, degree, deriv_order)} grinds out a
list of all terms consisting of {\tt degree} factors carrying {\tt
deriv_order} derivatives.
 The term is built up in the array {\tt item} and then printed.
(The $i$th element of this integer array indicates the number of
differentiations acting on the potential matrix to create the
$i$th factor.)    \TextIndex{Notation}
\TextIndex{Notation, in data file}
\<Central algorithm\><<<
grind(item, degree, deriv_order)
   int item[];
   int degree, deriv_order;
{  \<Declarations for grind\>
   if(degree>1)
   {\% then the first element may be any number up to\tt deriv\_order\rm.  \%
      for(a=0; a<=deriv_order; a++)
      {  item[0] = a;
         grind(item+1, degree-1, deriv_order-a);
      }
   }else
   {\% all the differentiations must be used up at once.  \%
      item[0] = deriv_order;
      pitem = item - d1 + 1; \% Find the beginning again.  \%
      if(revtest(pitem))   \% then the item is not redundant.  \%
         \<Print the item and advance the counter.\>
   }
   return(0);
}
>>>
 \<Declarations for grind\><<<
   extern int d1, ctr;
   int a;
   int *pitem;
>>>
 We reject the term as redundant \TextIndex{Redundant terms}  if it
is the same as one previously generated, except for being in
reverse order.
 Since the terms of a fixed length are generated in lexicographic
order, we need merely test whether the term precedes its reversal
in that ordering.
\<Incidental functions\><<<
revtest(item)
   int item[];
{  extern int d1;
   int i, a, b;

   for(i=1; i<=d1/2; i++)
   {  a = item[i-1];
      b = item[d1-i];
      if(a>b)
         return(0);  \% repetition \%
      if(a<b)
        return(1);   \% new pair \%
   }
   return(1);        \% symmetrical \%
}
>>>
 The numbers in the array are printed, separated by hyphens.
\TextIndex{Notation, in data file}
 A harmless hyphen is left at the end, since suppressing it is too
much trouble.
 \<Print the item and advance the counter.\><<<
      {  for(j=0; j<d1; j++)
            printf("%d-", *(pitem+j));
         printf("\\n");
         ctr++;
      }
>>>
 Although it's overkill, let's demonstrate {\tt ProTeX}'s ability
to add another utility variable as an afterthought.
\<Declarations for grind\><<<
   int j;
>>>
\heading The main program \endheading
We will generate all terms of a given order.
(This ``total'' order equals the total number of differentiations
plus twice the number of factors.)  \TextIndex{Notation}
\<Main program\><<<
main(argc, argv)
   int argc;
   char *argv[];
{  \<Declarations for main\>
   \<Decode the command-line arguments.\>
   ctr=0;  \% Initialize the count of terms.  \%
   if(label)
      printf("\\norder %d:\\n", total_order);
   for(degree=1; degree<=total_order/2; degree++)
      \<Generate all terms with given total order and degree.\>
   if(label)
     printf("\\n%d terms\\n", ctr);
   exit(0);
}
>>>

\<Generate all terms with given total order and degree.\><<<
{  d1 = degree;  \% Record length of journey so we can get home again.  \%
   deriv_order = total_order - 2*degree;
   grind(item, degree, deriv_order);
}
>>>
\<Declarations for main\><<<
extern int d1, ctr;
int total_order, degree, deriv_order, label;
int item[NMAX];
>>>
\<Decode the command-line arguments.\><<<
if(argc!=3)
   abort();
total_order = atoi(argv[1]);
if(total_order<2)
   abort();
label = atoi(argv[2]);
>>>
\<Incidental functions\><<<
abort()
{  printf("Improper arguments.");
printf("  Please read comments in WKBFLIST source code listing.");
printf("\\n  <order> <1 to label, 0 for WKBFCOEF input>\\n");
exit(1);
}
>>>
\OutputCode\<wkbflist\>
\ShowIndex
\bye

ProTEX output

wkbflist (formerly grind)

Introduction

This is the first of two programs described in

S. A. Fulling, The local geometric asymptotics of continuum eigenfunction expansions. II. Onedimensional systems, SIAM J. Math. Anal. 14, 780–795 (1983)

(henceforth cited as “the paper”) and in

S. A. Fulling, The analytic approach to recursion relations, J. Symbolic Comput. 9, 73–85 (1990).

For one-dimensional vectorial Schrödinger equations,

− \frac{d^{2}ψ}{dx^{2}} − E(x)ψ(x) = p^{2}ψ(x)

(where the potential function E is matrix-valued), the programs calculate the coefficients in the WKB– Fröman or phase-integral approximation to the eigenfunctions and in the related spectral measures. Please consult the paper for details of notation. The key object is N(x), the “logarithmic-derivative matrix” of an eigenfunction, defined by the condition

ψ′ = ipNψ

and expanded as

N ∼ 1 + ∑_{s=2}^{∞} p^{−s} \frac{i^{s−2}}{2^{s−1}} N_{s} ,

where N_{s} contains terms of total “order” s; for example,

N_{6} = E′′′′ + 3EE′′ + 3E′′E + 5(E′)^{2} + 2E^{3}.

(Each potential is counted like two derivatives.) From this series one obtains asymptotic expansions of the spectral densities in the eigenfunction expansion, and hence expansions of the associated heat kernel, resolvent kernel, etc.

This is a ProTeX version of the program called GRIND.C in the papers.

This program prints out a list of all possible terms of a given order, in the code described on p. 791 of the paper. This output is to be piped into the program WKBFCOEF, which calculates the coefficient of each term in the WKBF series or in the expansion of one of the spectral measures. Note that the repetition of a term with reversed order of the factors is omitted as redundant; see the captions of Tables 2 and 3 of the paper for the proper interpretation in each case.

Command-line syntax: wkbflist <order> <label parameter>

The order should be at least 2; if it is too large (larger than about 80), the parameter NMAX will need to be increased, along with MAXLEN and MAXCARD in WKBFCOEF. The label parameter should be 0 for producing input to WKBFCOEF, nonzero for producing printed output. (When it’s nonzero, the order and a count of the terms are printed out.)

NMAX is the maximum number of factors allowed in a term.

The program’s structure is

                             ▷▷ wkbflist ◁◁
#define NMAX 40
◁◁ Compiler preliminaries ▷▷
◁◁ Main program ▷▷
◁◁ Central algorithm ▷▷
◁◁ Incidental functions ▷▷

⋄ ⋄ ⋄

We need two global variables, one to count the terms and one to help us recover the starting point after the recursive algorithm.

                      ▷▷ Compiler preliminaries ◁◁
#include <stdio.h>
int ctr, d1;

⋄ ⋄ ⋄

The central, recursive algorithm

The function grind(item, degree, deriv_order) grinds out a list of all terms consisting of degree factors carrying deriv_order derivatives. The term is built up in the array item and then printed. (The ith element of this integer array indicates the number of differentiations acting on the potential matrix to create the ith factor.)

                        ▷▷ Central algorithm ◁◁
grind(item, degree, deriv_order)
int item[];
int degree, deriv_order;
{ ◁◁ Declarations for grind ▷▷
if(degree>1)

{ then the first element may be any number up to deriv order.

   for(a=0; a<=deriv_order; a++)
   {  item[0] = a;
      grind(item+1, degree-1, deriv_order-a);
   }
}else

{ all the differentiations must be used up at once.

   item[0] = deriv_order;

pitem = item - d1 + 1; Find the beginning again.

if(revtest(pitem)) then the item is not redundant.

      ◁◁ Print the item and advance the counter. ▷▷
}
return(0);
}

⋄ ⋄ ⋄

                      ▷▷ Declarations for grind ◁◁
extern int d1, ctr;
int a;
int *pitem;

⋄ ⋄ ⋄

We reject the term as redundant if it is the same as one previously generated, except for being in reverse order. Since the terms of a fixed length are generated in lexicographic order, we need merely test whether the term precedes its reversal in that ordering.

                       ▷▷ Incidental functions ◁◁
revtest(item)
int item[];
{  extern int d1;
int i, a, b;
for(i=1; i<=d1/2; i++)
{  a = item[i-1];
   b = item[d1-i];
   if(a>b)
      return(0); repetition
   if(a<b)
     return(1); new pair
}

return(1); symmetrical

}

⋄ ⋄ ⋄

The numbers in the array are printed, separated by hyphens. A harmless hyphen is left at the end, since suppressing it is too much trouble.

             ▷▷ Print the item and advance the counter. ◁◁
   {  for(j=0; j<d1; j++)
         printf("%d-", *(pitem+j));
      printf("\n");
      ctr++;
   }

⋄ ⋄ ⋄

Although it’s overkill, let’s demonstrate ProTeX’s ability to add another utility variable as an afterthought.

                    ++▷▷ Declarations for grind ◁◁++
int j;

⋄ ⋄ ⋄

The main program

We will generate all terms of a given order. (This “total” order equals the total number of differentiations plus twice the number of factors.)

                           ▷▷ Main program ◁◁
main(argc, argv)
int argc;
char *argv[];
{ ◁◁ Declarations for main ▷▷
◁◁ Decode the command-line arguments. ▷▷

ctr=0; Initialize the count of terms.

if(label)
   printf("\norder %d:\n", total_order);
for(degree=1; degree<=total_order/2; degree++)
   ◁◁ Generate all terms with given total order and degree. ▷▷
if(label)
  printf("\n%d terms\n", ctr);
exit(0);
}

⋄ ⋄ ⋄

      ▷▷ Generate all terms with given total order and degree. ◁◁

{ d1 = degree; Record length of journey so we can get home again.

   deriv_order = total_order - 2*degree;
   grind(item, degree, deriv_order);
}

⋄ ⋄ ⋄

                      ▷▷ Declarations for main ◁◁
extern int d1, ctr;
int total_order, degree, deriv_order, label;
int item[NMAX];

⋄ ⋄ ⋄

                ▷▷ Decode the command-line arguments. ◁◁
if(argc!=3)
   abort();
total_order = atoi(argv[1]);
if(total_order<2)
   abort();
label = atoi(argv[2]);

⋄ ⋄ ⋄

                     ++▷▷ Incidental functions ◁◁++
abort()
{  printf("Improper arguments.");
printf("  Please read comments in WKBFLIST source code listing.");
printf("\n  <order> <1 to label, 0 for WKBFCOEF input>\n");
exit(1);
}

⋄ ⋄ ⋄

References                                                                             15
Notation                                                                         15 16 18
Applications                                                                           15
Redundant terms                                                                     15 17
◁◁ wkbflist ▷▷  16
◁◁ Compiler preliminaries ▷▷  \underline{16} 16
◁◁ Main program ▷▷  \underline{16} 18
◁◁ Central algorithm ▷▷  \underline{16} 16
◁◁ Incidental functions ▷▷  \underline{16} 17 18
Notation, in data file                                                              16 17
◁◁ Declarations for grind ▷▷  \underline{16} 16 17
◁◁ Print the item and advance the counter. ▷▷  \underline{16} 17
◁◁ Declarations for main ▷▷  \underline{18} 18
◁◁ Decode the command-line arguments. ▷▷  \underline{18} 18
◁◁ Generate all terms with given total order and degree. ▷▷  \underline{18} 18

CWEB source file

Observe that the syntax of CWEB is more complicated than that of ProTEX, including numerous commands for fine-tuning the typesetting.

% WKBFLIST.W
\def\oldtitle{GRIND}
\def\topofcontents{\centerline{\titlefont\title\quad(formerly \oldtitle)}
\vfill}
\def\bN{{\bf N}}
@* Introduction.
   This is the first of two programs described in  @^References@>
\smallskip{\narrower\noindent
      S. A. Fulling, The local geometric asymptotics of continuum
      eigenfunction expansions.    II.\ One-dimen\-sional systems,
      {\sl SIAM J. Math.\ Anal.\ \bf14}, 780--795 (1983)
\par}\smallskip\noindent
(henceforth cited as ``the paper'') and in
\smallskip{\narrower\noindent
      S. A. Fulling,  The analytic approach to recursion
      relations, {\sl J. Symbolic Comput.  \bf 9},  73--85 (1990).
\par}\smallskip\noindent
 For one-dimensional vectorial Schr\"odinger equations,
 $$  -\, {d^2\psi\over dx^2} - E(x)\psi(x) = p^2 \psi(x)$$
   (where the potential function $E$ is matrix-valued),
 the programs calculate the coefficients in the WKB--Fr\"oman or
phase-integral approximation to the eigenfunctions and in the
related spectral measures.
 Please consult the paper for details of notation.  @^Notation@>
  The key object is $N(x)$, the ``logarithmic-derivative matrix''
of an eigenfunction, defined by the condition
   $$\psi' =ipN\psi$$
and expanded as
   $$N \sim 1 + \sum_{s=2}^\infty p^{-s} \,{i^{s-2}
   \over 2^{s-1}}\, \bN_s \,,$$
 where $\bN_s$ contains terms of total ``order'' $s$;
 for example,
   $$\bN_6 = E'''' + 3EE'' +3E''E + 5(E')^2 + 2 E^3.$$
 (Each potential is counted like two derivatives.)
 From this series one obtains asymptotic expansions of the spectral
densities in the eigenfunction expansion, and hence
@^Applications@> expansions of the associated heat kernel,
resolvent kernel, etc.
 \smallskip
{\bf This is a \.{CWEB} version of the program called \.{GRIND.C} in the
papers.}
\smallskip

 This program prints out a list of all possible terms of a given
order, in the code described on p.~791 of the paper.
 This output is to be piped into the program \.{WKBFCOEF}, which
calculates the coefficient of each term in the WKBF series or in
the expansion of one of the spectral measures.
 Note that the repetition of a term with reversed order of the
factors is omitted as redundant;  @^Redundant terms@>
 see the captions of Tables 2 and 3 of the paper for the proper
interpretation in each case.
 \smallskip
 {\bf Command-line syntax:} \quad  \.{wkbflist} $<$order$>$
   $<$label parameter$>$
\smallskip
  \noindent   The order should be at least 2; if it is too large
(larger than about   80), the parameter \.{NMAX} will need to be
increased, along with \.{MAXLEN} and \.{MAXCARD} in \.{WKBFCOEF}.
 The label parameter should be 0 for producing input to \.{WKBFCOEF},
nonzero for producing printed output.
 (When it's nonzero, the order and a count of the terms are printed
out.)
 \smallskip
  \.{NMAX} is the maximum number of factors allowed in a term.
@d NMAX 40
@ The program's structure is
@c
 @< Compiler preliminaries @>@/
 @< Main program @>@/
 @< Central algorithm @>@/
 @< Incidental functions @>
@ We need two global variables, one to count the terms and one to help us
recover the starting point after the recursive algorithm.
@s exit return
@< Compiler preliminaries @> =
#include <stdio.h>@#
int ctr, d1;
@* The central, recursive algorithm.
The function |grind(item, degree, deriv_order)| grinds out a list
of all terms consisting of |degree| factors carrying |deriv_order|
derivatives.    The term is built up in the array |item| and then
printed.    (The $i$th element of this integer array indicates the
number of differentiations acting on the potential matrix to create
the $i$th factor.)    @^Notation@>
   @^Notation, in data file@>

@< Central algorithm @> =
grind(item, degree, deriv_order)
int item[];
int degree, deriv_order;
{  @< Declarations for |grind| @>@;@#
if(degree>1)
{/* then the first element may be any number up to |deriv_order|.  */
   for(a=0; a<=deriv_order; a++)
   {  item[0] = a;
      grind(item+1, degree-1, deriv_order-a);
   }
}else
{/* all the differentiations must be used up at once.  */
   item[0] = deriv_order;
   pitem = item - d1 + 1; /* Find the beginning again.  */
   if(revtest(pitem))   /* then the item is not redundant.  */
         @^Redundant terms@> @/
      @< Print the item and advance the counter.  @>@;
}@;
return(0);
}
@ @< Declarations for |grind| @> =
extern int d1, ctr;
int a;
int *pitem;
@ We reject the term as redundant  @^Redundant terms@>
if it is the same as one
previously generated, except for being in reverse order.
Since the terms of a fixed length are generated in lexicographic
order, we need merely test whether the term
precedes its reversal in that ordering.
@< Incidental functions @> =
revtest(item)
int item[];
{  extern int d1;
int i, a, b;
for(i=1; i<=d1/2; i++)
{  a = item[i-1];
   b = item[d1-i];
   if(a>b) @/
      return(0);   /* repetition */
   if(a<b) @/
     return(1);   /* new pair */
}
return(1);         /* symmetrical */
}

@ The numbers in the array are printed, separated by hyphens.
   @^Notation, in data file@>
A harmless hyphen is left at the end, since suppressing it is too
much trouble.
@< Print ...  @> =
      {  for(j=0; j<d1; j++) @/
            printf("%d-", *(pitem+j));
         printf("\n");
         ctr++;
      }
@ Although it's overkill, let's demonstrate \.{CWEB}'s ability to add
another utility variable as an afterthought.
@< Declarations for |grind| @> =
   int j;
@* The main program.
We will generate all terms of a given order.
(This ``total'' order equals the total number of differentiations
plus twice the number of factors.)    @^Notation@>
@< Main program @> =
main(argc, argv)
   int argc;
   char *argv[];
{  @< Declarations for |main| @>@;@#
   @< Decode the command-line arguments.  @>@/
   ctr=0;  /* Initialize the count of terms.  */
   if(label) @/
      printf("\norder %d:\n", total_order);
   for(degree=1; degree<=total_order/2; degree++) @/
      @< Generate all terms with given |total_order| and |degree|.  @>@;
   if(label) @/
     printf("\n%d terms\n", ctr);
   exit(0);
}
@ @< Generate all terms with given |total_order| and |degree|.  @> =
   {  d1 = degree;
         /* Record length of journey so we can get home again.  */
      deriv_order = total_order - 2*degree;
      grind(item, degree, deriv_order);
   }
@ @< Declarations for |main| @> =
   extern int d1, ctr;
   int total_order, degree, deriv_order, label;
   int item[NMAX];

@ @< Decode the command-line arguments.  @> =
if(argc!=3) @/
   abort();
total_order = atoi(argv[1]);
if(total_order<2) @/
   abort();
label = atoi(argv[2]);
@ @< Incidental functions @> =
abort()
{  printf("Improper arguments.");
printf("  Please read comments in WKBFLIST source code listing.");
printf("\n  <order> <1 to label, 0 for WKBFCOEF input>\n");
exit(1);
}
@* Index.

WKBFLIST OUTPUT 25

CWEB output

WKBFLIST (formerly GRIND)

Section Page

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 26 The central, recursive algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 27 The main program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 29 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 30

S. A. Fulling

version of January 3, 1993

26  INTRODUCTION  WKBFLIST §1

1. Introduction. This is the first of two programs described in S. A. Fulling, The local geometric asymptotics of continuum eigenfunction expansions. II. One-dimen-sional systems, SIAM J. Math. Anal. 14, 780–795 (1983)

(henceforth cited as “the paper”) and in

S. A. Fulling, The analytic approach to recursion relations, J. Symbolic Comput. 9, 73–85 (1990). For one-dimensional vectorial Schrödinger equations,

− \frac{d^{2}ψ}{dx^{2}} − E(x)ψ(x) = p^{2}ψ(x)

(where the potential function E is matrix-valued), the programs calculate the coefficients in the WKB– Fröman or phase-integral approximation to the eigenfunctions and in the related spectral measures. Please consult the paper for details of notation. The key object is N(x), the “logarithmic-derivative matrix” of an eigenfunction, defined by the condition

ψ′ = ipNψ

and expanded as

N ∼ 1 + ∑_{s=2}^{∞} p^{−s} \frac{i^{s−2}}{2^{s−1}} N_{s} ,

where N_{s} contains terms of total “order” s; for example,

N_{6} = E′′′′ + 3EE′′ + 3E′′E + 5(E′)^{2} + 2E^{3}.

(Each potential is counted like two derivatives.) From this series one obtains asymptotic expansions of the spectral densities in the eigenfunction expansion, and hence expansions of the associated heat kernel, resolvent kernel, etc.

This is a version of the program called in the papers.

This program prints out a list of all possible terms of a given order, in the code described on p. 791 of the paper. This output is to be piped into the program , which calculates the coefficient of each term in the WKBF series or in the expansion of one of the spectral measures. Note that the repetition of a term with reversed order of the factors is omitted as redundant; see the captions of Tables 2 and 3 of the paper for the proper interpretation in each case.

Command-line syntax: <order> <label parameter> The order should be at least 2; if it is too large (larger than about 80), the parameter will need to be increased, along with and in . The label parameter should be 0 for producing input to , nonzero for producing printed output. (When it’s nonzero, the order and a count of the terms are printed out.)

is the maximum number of factors allowed in a term.

#define  40

2. The program’s structure is ⟨ Compiler preliminaries 3 ⟩ ⟨ Main program 9 ⟩ ⟨ Central algorithm 4 ⟩ ⟨ Incidental functions 6 ⟩

3. We need two global variables, one to count the terms and one to help us recover the starting point after the recursive algorithm. ⟨ Compiler preliminaries 3 ⟩ ≡ #include

int ctr, d1;

This code is used in section 2.

§4  WKBFLIST  THE CENTRAL, RECURSIVE ALGORITHM 27

4. The central, recursive algorithm. The function grind(item, degree, deriv order) grinds out a list of all terms consisting of degree factors carrying deriv order derivatives. The term is built up in the array item and then printed. (The ith element of this integer array indicates the number of differentiations acting on the potential matrix to create the ith factor.) ⟨ Central algorithm 4 ⟩ ≡

grind(item, degree, deriv order) int item[ ]; int degree, deriv order;

{ ⟨ Declarations for grind 5 ⟩ if (degree > 1) { /∗ then the first element may be any number up to deriv order. ∗/

for (a = 0; a ≤ deriv order; a^{++}) { item[0] = a; grind(item + 1, degree − 1, deriv order − a);

} } else { /∗ all the differentiations must be used up at once. ∗/

item[0] = deriv order; pitem = item − d1 + 1; /∗ Find the beginning again. ∗/ if (revtest(pitem)) /∗ then the item is not redundant. ∗/

⟨ Print the item and advance the counter. 7 ⟩ } return (0);

}

This code is used in section 2.

5. ⟨ Declarations for grind 5 ⟩ ≡ extern int d1, ctr; int a; int ∗pitem;

See also section 8. This code is used in section 4.

28  THE CENTRAL, RECURSIVE ALGORITHM  WKBFLIST §6

6. We reject the term as redundant if it is the same as one previously generated, except for being in reverse order. Since the terms of a fixed length are generated in lexicographic order, we need merely test whether the term precedes its reversal in that ordering. ⟨ Incidental functions 6 ⟩ ≡

revtest(item)

int item[ ]; {

extern int d1; int i, a, b; for (i = 1; i ≤ d1/2; i^{++}) {

a = item[i − 1]; b = item[d1 − i]; if (a > b)

return (0); /∗ repetition ∗/ if (a < b)

return (1); /∗ new pair ∗/ } return (1); /∗ symmetrical ∗/

}

See also section 13. This code is used in section 2.

7. The numbers in the array are printed, separated by hyphens. A harmless hyphen is left at the end, since suppressing it is too much trouble. ⟨ Print the item and advance the counter. 7 ⟩ ≡

{

for (j = 0; j < d1; j^{++})

printf ( , ∗(pitem + j)); printf ( ); ctr^{++};

}

This code is used in section 4.

8. Although it’s overkill, let’s demonstrate ’s ability to add another utility variable as an afterthought. ⟨ Declarations for grind 5 ⟩ +≡

int j;

§9  WKBFLIST  THE MAIN PROGRAM 29

9. The main program. We will generate all terms of a given order. (This “total” order equals the total number of differentiations plus twice the number of factors.) ⟨ Main program 9 ⟩ ≡

main(argc, argv )

int argc;

char ∗argv [ ]; {

⟨ Declarations for main 11 ⟩ ⟨ Decode the command-line arguments. 12 ⟩ ctr = 0; /∗ Initialize the count of terms. ∗/ if (label)

printf ( ␣ , total order); for (degree = 1; degree ≤ total order/2; degree^{++})

⟨ Generate all terms with given total order and degree. 10 ⟩ if (label)

printf ( ␣ , ctr); exit (0);

}

This code is used in section 2.

10. ⟨ Generate all terms with given total order and degree. 10 ⟩ ≡

{ d1 = degree; /∗ Record length of journey so we can get home again. ∗/ deriv order = total order − 2 ∗ degree; grind(item, degree, deriv order);

}

This code is used in section 9.

11. ⟨ Declarations for main 11 ⟩ ≡ extern int d1, ctr; int total order, degree, deriv order, label; int item[ ];

This code is used in section 9.

12. ⟨ Decode the command-line arguments. 12 ⟩ ≡

if (argc ≠ 3)

abort( ); total order = atoi(argv [1]); if (total order < 2)

abort( ); label = atoi(argv [2]);

This code is used in section 9.

13. ⟨ Incidental functions 6 ⟩ +≡ abort( ) {

printf (  ␣  );

printf ( ␣␣      ␣    ␣        ␣  ␣        ␣       ␣    ␣  );
printf ( ␣␣       ␣  ␣  ␣       ␣ ␣   ␣         ␣  );

exit (1); }

30  INDEX  WKBFLIST §14

14. Index.

a: \underline{5}, \underline{6}. abort: 12, \underline{13}. Applications: 1. argc: \underline{9}, 12. argv : \underline{9}, 12. atoi: 12. b: \underline{6}. ctr: \underline{3}, \underline{5}, 7, 9, \underline{11}. degree: \underline{4}, 9, 10, \underline{11}. deriv order: \underline{4}, 10, \underline{11}. d1: \underline{3}, 4, \underline{5}, \underline{6}, 7, 10, \underline{11}. grind: \underline{4}, 10. i: \underline{6}. item: \underline{4}, \underline{6}, 10, \underline{11}. j: \underline{8}. label: 9, \underline{11}, 12. main: \underline{9}. : \underline{1}, 11. Notation: 1, 4, 9. Notation, in data file: 4, 7. pitem: 4, \underline{5}, 7. printf : 7, 9, 13. Redundant terms: 1, 4, 6. References: 1. revtest: 4, \underline{6}. total order: 9, 10, \underline{11}, 12.

WKBFLIST NAMES OF THE SECTIONS 31

⟨ Central algorithm 4 ⟩ Used in section 2. ⟨ Compiler preliminaries 3 ⟩ Used in section 2.

⟨ Declarations for grind 5, 8 ⟩ Used in section 4.

⟨ Declarations for main 11 ⟩ Used in section 9. ⟨ Decode the command-line arguments. 12 ⟩ Used in section 9. ⟨ Generate all terms with given total order and degree. 10 ⟩ Used in section 9. ⟨ Incidental functions 6, 13 ⟩ Used in section 2.

⟨ Main program 9 ⟩ Used in section 2.

⟨ Print the item and advance the counter. 7 ⟩ Used in section 4.