To provide for weaving in the standard maths and science typesetting software, TeX.

§1. Creation.

void TeX::create(void) {
    weave_format *wf = Formats::create_weave_format(I"TeX", I".tex");
    METHOD_ADD(wf, RENDER_FOR_MTID, TeX::render_TeX);
    METHOD_ADD(wf, PREFORM_DOCUMENT_FOR_MTID, TeX::preform_document);

§2. Rendering. At present, this renderer only makes the dialect of TeX needed for pdftex, which involves various extension commands: the curse of modern TeX is the combination of an outdated original, and a proliferation of non-canonical extensions, but pdftex is pretty good. All the same, we should perhaps consider adding LaTeX, or XeTeX.

enum PDFTEX_TEX_FORM from 1
void TeX::render_TeX(weave_format *self, text_stream *OUT, heterogeneous_tree *tree) {
    TeX::render_inner(OUT, tree, PDFTEX_TEX_FORM);

§3. From here on, then, the renderer, which should generate TeX which is as generic as possible, but with special features depending on trs->TeX_form.

typedef struct TeX_render_state {
    struct text_stream *OUT;
    struct weave_order *wv;
    int TeX_form;
} TeX_render_state;

void TeX::render_inner(text_stream *OUT, heterogeneous_tree *tree, int form) {
    weave_document_node *C = RETRIEVE_POINTER_weave_document_node(tree->root->content);
    TeX_render_state trs;
    trs.OUT = OUT;
    trs.wv = C->wv;
    trs.TeX_form = form;
    Trees::traverse_from(tree->root, &TeX::render_visit, (void *) &trs, 0);

§4. For the reason why footnotes are omitted, see below: they aren't really.

int TeX::render_visit(tree_node *N, void *state, int L) {
    TeX_render_state *trs = (TeX_render_state *) state;
    text_stream *OUT = trs->OUT;
    if ((N->type == weave_document_node_type) ||
        (N->type == weave_body_node_type) ||
        (N->type == weave_chapter_title_page_node_type) ||
        (N->type == weave_chapter_footer_node_type) ||
        (N->type == weave_section_footer_node_type) ||
        (N->type == weave_audio_node_type) ||
        (N->type == weave_video_node_type) ||
        (N->type == weave_download_node_type) ||
        (N->type == weave_chapter_node_type) ||
        (N->type == weave_carousel_slide_node_type) ||
        (N->type == weave_begin_footnote_text_node_type)) Render nothing4.15

    else if (N->type == weave_head_node_type) Render head4.1
    else if (N->type == weave_tail_node_type) Render tail4.2
    else if (N->type == weave_verbatim_node_type) Render verbatim4.14
    else if (N->type == weave_chapter_header_node_type) Render chapter header4.3
    else if (N->type == weave_section_header_node_type) Render header4.4
    else if (N->type == weave_section_purpose_node_type) Render purpose4.5
    else if (N->type == weave_subheading_node_type) Render subheading4.6
    else if (N->type == weave_bar_node_type) Render bar4.7
    else if (N->type == weave_pagebreak_node_type) Render pagebreak4.8
    else if (N->type == weave_linebreak_node_type) Render linebreak4.9
    else if (N->type == weave_paragraph_heading_node_type) Render paragraph heading4.10
    else if (N->type == weave_endnote_node_type) Render endnote4.11
    else if (N->type == weave_figure_node_type) Render figure4.12
    else if (N->type == weave_material_node_type) Render material4.13
    else if (N->type == weave_embed_node_type) Render embed4.16
    else if (N->type == weave_pmac_node_type) Render pmac4.17
    else if (N->type == weave_vskip_node_type) Render vskip4.18
    else if (N->type == weave_section_node_type) Render section4.19
    else if (N->type == weave_code_line_node_type) Render code line4.20
    else if (N->type == weave_function_usage_node_type) Render function usage4.21
    else if (N->type == weave_commentary_node_type) Render commentary4.22
    else if (N->type == weave_toc_node_type) Render toc4.23
    else if (N->type == weave_toc_line_node_type) Render toc line4.24
    else if (N->type == weave_defn_node_type) Render defn4.25
    else if (N->type == weave_source_code_node_type) Render source code4.26
    else if (N->type == weave_url_node_type) Render URL4.27
    else if (N->type == weave_footnote_cue_node_type) Render footnote cue4.28
    else if (N->type == weave_display_line_node_type) Render display line4.29
    else if (N->type == weave_function_defn_node_type) Render function defn4.30
    else if (N->type == weave_item_node_type) Render item4.31
    else if (N->type == weave_grammar_index_node_type) Render grammar index4.32
    else if (N->type == weave_inline_node_type) Render inline4.33
    else if (N->type == weave_locale_node_type) Render locale4.34
    else if (N->type == weave_maths_node_type) Render maths4.35

    else internal_error("unable to render unknown node");
    return TRUE;

§4.1. Render head4.1 =

    weave_head_node *C = RETRIEVE_POINTER_weave_head_node(N->content);
    WRITE("%% %S\n", C->banner);

§4.2. Render tail4.2 =

    weave_tail_node *C = RETRIEVE_POINTER_weave_tail_node(N->content);
    WRITE("%% %S\n", C->rennab);

§4.3. Render chapter header4.3 =

    weave_chapter_header_node *C = RETRIEVE_POINTER_weave_chapter_header_node(N->content);
    if (Str::ne(C->chap->md->ch_range, I"S")) {
        TeX::general_heading(OUT, trs->wv,
            FIRST_IN_LINKED_LIST(section, C->chap->sections), NULL, C->chap->md->ch_title,
            3, FALSE);
        WRITE("%S\\medskip\n", C->chap->md->rubric);
        section *S;
        LOOP_OVER_LINKED_LIST(S, section, C->chap->sections) {
            WRITE("\\smallskip\\noindent ");
            if (trs->wv->pattern->number_sections) WRITE("%d. ", S->printed_number);
            WRITE("{\\it %S}\\qquad\n%S", S->md->sect_title, S->sect_purpose);

§4.4. Render header4.4 =

    weave_section_header_node *C = RETRIEVE_POINTER_weave_section_header_node(N->content);
    TeX::general_heading(OUT, trs->wv, C->sect, NULL,
        C->sect->md->sect_title, 2, FALSE);

§4.5. Render purpose4.5 =

    weave_section_purpose_node *C = RETRIEVE_POINTER_weave_section_purpose_node(N->content);
    WRITE("\\smallskip\\par\\noindent{\\it %S}\\smallskip\\noindent\n", C->purpose);

§4.6. Render subheading4.6 =

    weave_subheading_node *C = RETRIEVE_POINTER_weave_subheading_node(N->content);
    WRITE("\\par\\noindent{\\bf %S}\\mark{%S}\\medskip\n", C->text, NULL);

§4.7. Render bar4.7 =


§4.8. Render pagebreak4.8 =


§4.9. Render linebreak4.9 =


§4.10. Render paragraph heading4.10 =

    weave_paragraph_heading_node *C =
    TeX::general_heading(OUT, trs->wv, C->para->under_section,
        C->para, I"", 0, FALSE);

§4.11. Render endnote4.11 =

    WRITE("{\\usagefont ");
    Recurse tne renderer through children nodes4.11.1;
    return FALSE;

§4.12. TeX itself has an almost defiant lack of support for anything pictorial, which is one reason it didn't live up to its hope of being the definitive basis for typography; even today the loose confederation of TeX-like programs and extensions lack standard approaches. Here we're going to use pdftex features, having nothing better. All we're trying for is to insert a picture, scaled to a given width, into the text at the current position.

Render figure4.12 =

    weave_figure_node *C = RETRIEVE_POINTER_weave_figure_node(N->content);
    filename *F = Filenames::in(
        Pathnames::down(trs->wv->weave_web->md->path_to_web, I"Figures"),
    if (C->w >= 0) WRITE(" width %d cm{%f}\n", C->w/POINTS_PER_CM, F);
    else if (C->h >= 0) WRITE(" height %d cm{%f}\n", C->h/POINTS_PER_CM, F);
    else WRITE("{%f}\n", F);
        "\\hbox to\\hsize{\\hfill\\pdfrefximage \\pdflastximage\\hfill}"

§4.13. Render material4.13 =

    weave_material_node *C = RETRIEVE_POINTER_weave_material_node(N->content);
    paragraph *first_in_para = NULL;
    if ((N == N->parent->child) &&
        (N->parent->type == weave_paragraph_heading_node_type)) {
        weave_paragraph_heading_node *PC =
        first_in_para = PC->para;
    if (C->material_type == COMMENTARY_MATERIAL)
        Deal with a commentary material node4.13.1
    else if (C->material_type == CODE_MATERIAL)
        Deal with a code material node4.13.2
    else if (C->material_type == FOOTNOTES_MATERIAL)
        Deal with a footnotes material node4.13.3
    else if (C->material_type == ENDNOTES_MATERIAL)
        Deal with a endnotes material node4.13.4
    else if (C->material_type == MACRO_MATERIAL)
        Deal with a macro material node4.13.5
    else if (C->material_type == DEFINITION_MATERIAL)
        Deal with a definition material node4.13.6;
    return FALSE;

§4.13.1. Deal with a commentary material node4.13.1 =

    Recurse tne renderer through children nodes4.11.1;

§4.13.2. Deal with a code material node4.13.2 =

    Recurse tne renderer through children nodes4.11.1;

§4.13.3. Deal with a footnotes material node4.13.3 =

    return FALSE;

§4.13.4. Deal with a endnotes material node4.13.4 =

    Recurse tne renderer through children nodes4.11.1;

§4.13.5. Deal with a macro material node4.13.5 =

    Recurse tne renderer through children nodes4.11.1;

§4.13.6. Deal with a definition material node4.13.6 =

    Recurse tne renderer through children nodes4.11.1;

§4.14. Render verbatim4.14 =

    weave_verbatim_node *C = RETRIEVE_POINTER_weave_verbatim_node(N->content);
    WRITE("%S", C->content);

§4.15. Render nothing4.15 =


§4.16. Render embed4.16 =

    weave_embed_node *C = RETRIEVE_POINTER_weave_embed_node(N->content);
    LOG("It was %d\n", C->allocation_id);

§4.17. Render pmac4.17 =

    weave_pmac_node *C = RETRIEVE_POINTER_weave_pmac_node(N->content);
    TeX::para_macro(OUT, trs->wv, C->pmac, C->defn);

§4.18. Render vskip4.18 =

    weave_vskip_node *C = RETRIEVE_POINTER_weave_vskip_node(N->content);
    if (C->in_comment) WRITE("\\smallskip\\par\\noindent%%\n");
    else WRITE("\\smallskip\n");

§4.19. Render section4.19 =

    weave_section_node *C = RETRIEVE_POINTER_weave_section_node(N->content);
    LOG("It was %d\n", C->allocation_id);

§4.20. Render code line4.20 =

    WRITE("\\smallskip\\par\\noindent ");
    Recurse tne renderer through children nodes4.11.1;
    return FALSE;

§4.21. Render function usage4.21 =

    weave_function_usage_node *C =
    WRITE("%S", C->fn->function_name);
    return FALSE;

§4.22. Render commentary4.22 =

    weave_commentary_node *C =
    if (C->in_code) WRITE(" |\\hfill{\\ttninepoint\\it ");
    TeX::commentary_text(OUT, trs->wv, C->text);
    if (C->in_code) WRITE("}|");

§4.23. Render toc4.23 =

    WRITE("\\medskip\\hrule\\smallskip\\par\\noindent{\\usagefont ");
    for (tree_node *M = N->child; M; M = M->next) {
        Trees::traverse_from(M, &TeX::render_visit, (void *) trs, L+1);
        if (M->next) WRITE("; ");
    return FALSE;

§4.24. Render toc line4.24 =

    weave_toc_line_node *C = RETRIEVE_POINTER_weave_toc_line_node(N->content);
    WRITE("%S~%S", C->text1, C->text2);

§4.25. Render defn4.25 =

    weave_defn_node *C = RETRIEVE_POINTER_weave_defn_node(N->content);
    WRITE("|{\\ninebf %S} |", C->keyword);

§4.26. Render source code4.26 =

    weave_source_code_node *C =
    int starts = FALSE;
    if (N == N->parent->child) starts = TRUE;
    TeX::source_code(OUT, trs->wv,
        C->matter, C->colouring, starts);

§4.27. Render URL4.27 =

    weave_url_node *C = RETRIEVE_POINTER_weave_url_node(N->content);
    WRITE("%S", C->url);

§4.28. The TeX macro for footnotes means that the text has to accompany the cue, which is tricky for us now because the footnote text is somewhere else in the weave tree — so, we go for a little walk:

Render footnote cue4.28 =

    weave_footnote_cue_node *C = RETRIEVE_POINTER_weave_footnote_cue_node(N->content);
    WRITE("\\footnote{${}^{%S}$}{", C->cue_text);
    tree_node *M = N;
    while ((M) && (M->type != weave_paragraph_heading_node_type)) M = M->parent;
    if (M == NULL) internal_error("tree without section nodes");
    M = M->child;
    int found = FALSE;
    while (M) {
        if (M->type == weave_material_node_type) {
            weave_material_node *MC = RETRIEVE_POINTER_weave_material_node(M->content);
            if (MC->material_type == FOOTNOTES_MATERIAL) {
                tree_node *F = M->child;
                while (F) {
                    if (F->type == weave_begin_footnote_text_node_type) {
                        weave_begin_footnote_text_node *FC =
                        if (Str::eq(FC->cue_text, C->cue_text))
                            Found the right footnote text at last4.28.1;
                    F = F->next;
        M = M->next;
    if (found == FALSE) internal_error("cue without text");

§4.28.1. And so here's the text. Note that we render only its second and subsequent child nodes: that's because the first child is a copy of the footnote cue, and TeX renders that automatically.

(The TeX renderer otherwise ignores footnote texts, so if these nodes are not rendered here, they never will be.)

Found the right footnote text at last4.28.1 =

    for (tree_node *X = F->child->next; X; X = X->next)
        Trees::traverse_from(X, &TeX::render_visit, (void *) trs, L+1);
    found = TRUE;

§4.29. Render display line4.29 =

    weave_display_line_node *C =
    WRITE("\\quotesource{%S}\n", C->text);

§4.30. Render function defn4.30 =

    weave_function_defn_node *C =
    TeX::change_colour_PDF(OUT, FUNCTION_COLOUR, TRUE);
    WRITE("%S", C->fn->function_name);
    TeX::change_colour_PDF(OUT, PLAIN_COLOUR, TRUE);
    return FALSE;

§4.31. Render item4.31 =

    weave_item_node *C = RETRIEVE_POINTER_weave_item_node(N->content);
    if (Str::len(C->label) > 0) {
        if (C->depth == 1) WRITE("\\item{(%S)}", C->label);
        else WRITE("\\itemitem{(%S)}", C->label);
    } else {
        if (C->depth == 1) WRITE("\\item{}");
        else WRITE("\\itemitem{}");

§4.32. Render grammar index4.32 =


§4.33. Render inline4.33 =

    Recurse tne renderer through children nodes4.11.1;
    return FALSE;

§4.34. Render locale4.34 =

    weave_locale_node *C = RETRIEVE_POINTER_weave_locale_node(N->content);
    WRITE("$\\%S$%S", C->par1->ornament, C->par1->paragraph_number);
    if (C->par2) WRITE("-%S", C->par2->paragraph_number);

§4.35. Render maths4.35 =

    weave_maths_node *C = RETRIEVE_POINTER_weave_maths_node(N->content);
    if (C->displayed) WRITE("$$"); else WRITE("$");
    WRITE("%S", C->content);
    if (C->displayed) WRITE("$$"); else WRITE("$");

§4.11.1. Recurse tne renderer through children nodes4.11.1 =

    for (tree_node *M = N->child; M; M = M->next)
        Trees::traverse_from(M, &TeX::render_visit, (void *) trs, L+1);


text_stream *P_literal = NULL;
void TeX::general_heading(text_stream *OUT, weave_order *wv,
    section *S, paragraph *P, text_stream *heading_text, int weight, int no_skip) {
    text_stream *TeX_macro = NULL;
    Choose which TeX macro to use in order to typeset the new paragraph heading5.1;

    if (P_literal == NULL) P_literal = Str::new_from_wide_string(L"P");
    text_stream *orn = (P)?(P->ornament):P_literal;
    text_stream *N = (P)?(P->paragraph_number):NULL;
    Work out the next mark to place into the TeX vertical list5.2;
    Str::copy(modified, heading_text);
    match_results mr = Regexp::create_mr();
    if (Regexp::match(&mr, modified, L"(%c*?): (%c*)")) {
        WRITE_TO(modified, "{\\sinchhigh %S}\\quad %S", mr.exp[0], mr.exp[1]);
    if (weight == 2)
            TeX_macro, N, modified, mark, orn, NULL);
            TeX_macro, N, modified, mark, orn, S->md->sect_range);

§5.1. We want to have different heading styles for different weights, and TeX is horrible at using macro parameters as function arguments, so we don't want to pass the weight that way. Instead we use


where the weight is the number of terminal ss, 0 to 3. (TeX macros, lamentably, are not allowed digits in their name.) In the cases 0 and 1, we also have variants \nsweavesection and \nsweavesections which are the same, but with the initial vertical spacing removed; these allow us to prevent unsightly excess white space in certain configurations of a section.

Choose which TeX macro to use in order to typeset the new paragraph heading5.1 =

    switch (weight) {
        case 0: TeX_macro = I"weavesection"; break;
        case 1: TeX_macro = I"weavesections"; break;
        case 2: TeX_macro = I"weavesectionss"; break;
        default: TeX_macro = I"weavesectionsss"; break;
    if (wv->theme_match) {
        switch (weight) {
            case 0: TeX_macro = I"tweavesection"; break;
            case 1: TeX_macro = I"tweavesections"; break;
            case 2: TeX_macro = I"tweavesectionss"; break;
            default: TeX_macro = I"tweavesectionsss"; break;
    if (no_skip) {
        switch (weight) {
            case 0: TeX_macro = I"nsweavesection"; break;
            case 1: TeX_macro = I"nsweavesections"; break;

§5.2. "Marks" are the contrivance by which TeX produces running heads on pages which follow the material on those pages: so that the running head for a page can show the paragraph range for the material which tops it, for instance.

The ornament has to be set in math mode, even in the mark. \S and \P, making a section sign and a pilcrow respectively, only work in math mode because they abbreviate characters found in math fonts but not regular ones, in TeX's deeply peculiar font encoding system.

Work out the next mark to place into the TeX vertical list5.2 =

    text_stream *chaptermark = Str::new();
    text_stream *sectionmark = Str::new();
    if (weight == 3) {
        Str::copy(chaptermark, S->owning_chapter->md->ch_title);
    if (weight == 2) {
        Str::copy(sectionmark, S->md->sect_title);
        if (Str::len(chaptermark) > 0) {
            WRITE_TO(sectionmark, " - %S", S->md->sect_title);
    WRITE_TO(mark, "%S%S\\quad$\\%S$%S", chaptermark, sectionmark, orn, N);

§6. Code is typeset by TeX within vertical strokes; these switch a sort of typewriter-type verbatim mode on and off. To get an actual stroke, we must escape from code mode, escape it using a backslash, then re-enter code mode once again:

void TeX::source_code(text_stream *OUT, weave_order *wv,
    text_stream *matter, text_stream *colouring, int starts) {
    int current_colour = PLAIN_COLOUR, colour_wanted = PLAIN_COLOUR;
    for (int i=0; i < Str::len(matter); i++) {
        colour_wanted = Str::get_at(colouring, i);
        Adjust code colour as necessary6.1;
        if (Str::get_at(matter, i) == '|') WRITE("|\\||");
        else WRITE("%c", Str::get_at(matter, i));
    colour_wanted = PLAIN_COLOUR; Adjust code colour as necessary6.1;

§6.1. Adjust code colour as necessary6.1 =

    if (colour_wanted != current_colour) {
        TeX::change_colour_PDF(OUT, colour_wanted, TRUE);
        current_colour = colour_wanted;


void TeX::change_colour_PDF(text_stream *OUT, int col, int in_code) {
    char *inout = "";
    if (in_code) inout = "|";
    switch (col) {
            WRITE("%s\\pdfliteral direct{1 1 0 0 k}%s", inout, inout); break;
        case FUNCTION_COLOUR:
            WRITE("%s\\pdfliteral direct{0 1 1 0 k}%s", inout, inout); break;
        case PLAIN_COLOUR:
            WRITE("%s\\special{PDF:0 g}%s", inout, inout); break;
        case EXTRACT_COLOUR:
            WRITE("%s\\special{PDF:0 g}%s", inout, inout); break;

§8. Any usage of angle-macros is highlighted in several cute ways: first, we make use of colour and we drop in the paragraph number of the definition of the macro in small type; and second, we use cross-reference links.

In the PDF format, these three are all called, in sequence below; in TeX or DVI, only the middle one is.

void TeX::para_macro(text_stream *OUT, weave_order *wv, para_macro *pmac, int defn) {
    if (defn)
        WRITE("|\\pdfdest num %d fit ",
            pmac->allocation_id + 100);
        WRITE("|\\pdfstartlink attr{/C [0.9 0 0] /Border [0 0 0]} goto num %d ",
            pmac->allocation_id + 100);
    TeX::change_colour_PDF(OUT, DEFINITION_COLOUR, FALSE);
    WRITE("%S ", pmac->macro_name);
    WRITE("{\\sevenss %S}}", pmac->defining_paragraph->paragraph_number);
    TeX::change_colour_PDF(OUT, PLAIN_COLOUR, FALSE);
    WRITE("$\\rangle$ ");
    if (defn)


void TeX::commentary_text(text_stream *OUT, weave_order *wv, text_stream *id) {
    int math_mode = FALSE;
    for (int i=0; i < Str::len(id); i++) {
        switch (Str::get_at(id, i)) {
            case '$': math_mode = (math_mode)?FALSE:TRUE;
                WRITE("%c", Str::get_at(id, i)); break;
            case '_': if (math_mode) WRITE("_"); else WRITE("\\_"); break;
            case '"':
                if ((Str::get_at(id, i) == '"') &&
                    ((i==0) || (Str::get_at(id, i-1) == ' ') ||
                        (Str::get_at(id, i-1) == '(')))
            default: WRITE("%c", Str::get_at(id, i));

§10. The following is called only when the language is InC, and the weave is of the special Preform grammar document.

int TeX::preform_document(weave_format *self, text_stream *OUT, web *W,
    weave_order *wv, chapter *C, section *S, source_line *L, text_stream *matter,
    text_stream *concluding_comment) {
    if (L->preform_nonterminal_defined) {
        preform_production_count = 0;
        Weave the opening line of the nonterminal definition10.1;
        return TRUE;
    } else {
        if (L->category == PREFORM_GRAMMAR_LCAT) {
            Weave a line from the body of the nonterminal definition10.2;
            return TRUE;
    return FALSE;

§10.1. Weave the opening line of the nonterminal definition10.1 =

    WRITE("\\nonterminal{%S} |::=|",
    if (L->preform_nonterminal_defined->as_function) {
        WRITE("\\quad{\\it internal definition");
        if (L->preform_nonterminal_defined->voracious)
            WRITE(" (voracious)");
        else if (L->preform_nonterminal_defined->min_word_count ==
            WRITE(" (%d word%s)",
                (L->preform_nonterminal_defined->min_word_count != 1)?"s":"");

§10.2. Weave a line from the body of the nonterminal definition10.2 =

    match_results mr = Regexp::create_mr();
    if (Regexp::match(&mr, matter, L"Issue (%c*?) problem"))
        Str::copy(problem, mr.exp[0]);
    else if (Regexp::match(&mr, matter, L"FAIL_NONTERMINAL %+"))
        WRITE_TO(problem, "fail and skip");
    else if (Regexp::match(&mr, matter, L"FAIL_NONTERMINAL"))
        WRITE_TO(problem, "fail");
    WRITE_TO(matter, "|%S|", L->text_operand);
    while (Regexp::match(&mr, matter, L"(%c+?)|(%c+)")) {
        WRITE_TO(matter, "%S___stroke___%S", mr.exp[0], mr.exp[1]);
    while (Regexp::match(&mr, matter, L"(%c*?)___stroke___(%c*)")) {
        WRITE_TO(matter, "%S|\\||%S", mr.exp[0], mr.exp[1]);
    while (Regexp::match(&mr, matter, L"(%c*)<(%c*?)>(%c*)")) {
        WRITE_TO(matter, "%S|\\nonterminal{%S}|%S",
            mr.exp[0], mr.exp[1], mr.exp[2]);
    int N = preform_production_count;
    int L = ((N-1)%26) + 1;
    if (N <= 26) WRITE_TO(label, "%c", 'a'+L-1);
    else if (N <= 52) WRITE_TO(label, "%c%c", 'a'+L-1, 'a'+L-1);
    else if (N <= 78) WRITE_TO(label, "%c%c%c", 'a'+L-1, 'a'+L-1, 'a'+L-1);
    else {
        int n = (N-1)/26;
        WRITE_TO(label, "%c${}^{%d}$", 'a'+L-1, n);
    WRITE("\\qquad {\\hbox to 0.4in{\\it %S\\hfil}}%S", label, matter);
    if (Str::len(problem) > 0)
        WRITE("\\hfill$\\longrightarrow$ {\\ttninepoint\\it %S}", problem);
    else if (Str::len(concluding_comment) > 0) {
        WRITE(" \\hfill{\\ttninepoint\\it ");
        if (Str::len(concluding_comment) > 0)
            TeX::commentary_text(OUT, wv, concluding_comment);