texmath.c /size: 301 Kb    last modification: 2025-02-21 11:03
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7/*tex 
8
9    The code can be simplified a lot when we decide that traditional 8 bit fonts are virtualized in
10    a way that avoids the split delimiter definitions (small and large) and that the traditional 
11    way to define characters is dropped in favor of the unicode variants. So, this might happen at
12    some point. After all it makes no sense to use this engine with traditional fonts because there
13    \PDFTEX\ is a better choice. 
14
15    We might also benefit more from the fact that we have prev pointers. Occasionally I visit this 
16    file and make some variables more verbose. I'm in no hurry with that. 
17
18*/
19
20/*tex
21
22    When \TEX\ reads a formula that is enclosed between |$|'s, it constructs an \quote {mlist},
23    which is essentially a tree structure representing that formula. An mlist is a linear sequence
24    of items, but we can regard it as a tree structure because mlists can appear within mlists. For
25    example, many of the entries can be subscripted or superscripted, and such \quote {scripts} are
26    mlists in their own right.
27
28    An entire formula is parsed into such a tree before any of the actual typesetting is done,
29    because the current style of type is usually not known until the formula has been fully scanned.
30    For example, when the formula |$a+b \over c+d$| is being read, there is no way to tell that |a+b|
31    will be in script size until |\over| has appeared.
32
33    During the scanning process, each element of the mlist being built is classified as a relation,
34    a binary operator, an open parenthesis, etc., or as a construct like |\sqrt| that must be built
35    up. This classification appears in the mlist data structure.
36
37    After a formula has been fully scanned, the mlist is converted to an hlist so that it can be
38    incorporated into the surrounding text. This conversion is controlled by a recursive procedure
39    that decides all of the appropriate styles by a \quote {top-down} process starting at the
40    outermost level and working in towards the subformulas. The formula is ultimately pasted together
41    using combinations of horizontal and vertical boxes, with glue and penalty nodes inserted as
42    necessary.
43
44    An mlist is represented internally as a linked list consisting chiefly of \quote {noads}
45    (pronounced \quotation {no-adds}), to distinguish them from the somewhat similar \quote {nodes}
46    in hlists and vlists. Certain kinds of ordinary nodes are allowed to appear in mlists together
47    with the noads; \TEX\ tells the difference by means of the |type| field, since a noad's |type|
48    is always greater than that of a node. An mlist does not contain character nodes, hlist nodes,
49    vlist nodes, math nodes or unset nodes; in particular, each mlist item appears in the
50    variable-size part of |mem|, so the |type| field is always present.
51
52    Each noad is five or more words long. The first word contains the |type| and |subtype| and |link|
53    fields that are already so familiar to us; the second contains the attribute list pointer, and
54    the third, fourth an fifth words are called the noad's |nucleus|, |subscr|, and |supscr| fields.
55    (This use of a combined attribute list is temporary. Eventually, each of fields need their own
56    list)
57
58    Consider, for example, the simple formula |$x^2$|, which would be parsed into an mlist containing
59    a single element called an |ord_noad|. The |nucleus| of this noad is a representation of |x|, the
60    |subscr| is empty, and the |supscr| is a representation of |2|.
61
62    The |nucleus|, |subscr|, and |supscr| fields are further broken into subfields. If |p| points to
63    a noad, and if |q| is one of its principal fields (e.g., |q=subscr(p)|), |q=null| indicates a
64    field with no value (the corresponding attribute of noad |p| is not present). Otherwise, there are
65    several possibilities for the subfields, depending on the |type| of |q|.
66
67    \startitemize
68
69        \startitem
70            |type(q)=math_char_node| means that |math_fam(q)| refers to one of the sixteen font
71            families, and |character(q)| is the number of a character within a font of that family, as
72            in a character node.
73        \stopitem
74
75        \startitem
76            |type(q) = math_text_char_node| is similar, but the character is unsubscripted and
77            unsuperscripted and it is followed immediately by another character from the same font.
78            (This |type| setting appears only briefly during the processing; it is used to suppress
79            unwanted italic corrections.)
80        \stopitem
81
82        \startitem
83            |type(q) = sub_box_node| means that |math_list(q)| points to a box node (either an
84            |hlist_node| or a |vlist_node|) that should be used as the value of the field. The
85            |shift_amount| in the subsidiary box node is the amount by which that box will be 
86            shifted downward.
87        \stopitem
88
89        \startitem
90            |type(q) = sub_mlist_node| means that |math_list(q)| points to an mlist; the mlist must
91            be converted to an hlist in order to obtain the value of this field.
92        \stopitem
93
94        \startitem
95            In the latter case, we might have |math_list(q) = null|. This is not the same as |q =
96            null|; for example, |$P_{\}$| and |$P$| produce different results (the former will not
97            have the \quote {italic correction} added to the width of |P|, but the \quote {script
98            skip} will be added).
99        \stopitem
100
101    \startitemize
102
103    Concerning display skips, \TEX\ normally always inserts before and only after when larger than
104    zero. This can now be controlled with |\mathdisplayskipmode|:
105
106    \starttabulate
107    \NC 0 \NC normal \TEX \NC \NR
108    \NC 1 \NC always      \NC \NR
109    \NC 2 \NC non-zero    \NC \NR
110    \NC 3 \NC ignore      \NC \NR
111    \stoptabulate
112
113*/
114
115math_state_info lmt_math_state = {
116    .size       = 0,
117    .level      = 0,
118    .par_head   = NULL,
119    .fam_head   = NULL,
120    .last_left  = 0,
121    .last_right = 0,
122    .last_atom  = 0,
123    .scale      = scaling_factor,
124    .single     = 0,
125    .padding    = 0,
126};
127
128/* */
129
130typedef enum saved_math_entries {
131    saved_math_direction_entry = 0, /* value_1 */
132    saved_math_n_of_records    = 1,
133} saved_math_entries;
134
135# define saved_math_direction saved_value_1(saved_math_direction_entry)
136
137static inline void saved_math_initialize(void)
138{
139    saved_type(0) = saved_record_0;
140    saved_record(0) = math_save_type;
141}
142
143int tex_show_math_record(void)
144{
145    tex_print_str("math ");
146    switch (saved_type(0)) { 
147       case saved_record_0:
148            tex_print_format("direction %i", saved_value_1(0));
149            break;
150        default: 
151           return 0;
152    }
153    return 1;
154}
155
156/* */
157
158typedef enum saved_equation_number_entries {
159    saved_equation_number_location_entry = 0, /* value_1 */
160    saved_equation_number_n_of_records   = 1,
161} saved_equation_number_entries;
162
163# define saved_equation_number_location saved_value_1(saved_equation_number_location_entry)
164
165static inline void saved_equation_number_initialize(void)
166{
167    saved_type(0) = saved_record_0;
168    saved_record(0) = number_save_type;
169}
170
171void tex_show_math_number_group(void)
172{
173    tex_print_cmd_chr(equation_number_cmd, saved_equation_number_location);
174}
175
176int tex_show_math_number_record(void)
177{
178    tex_print_str("equation number ");
179    switch (saved_type(0)) { 
180       case saved_record_0:
181            tex_print_format("location %i", saved_value_1(0));
182            break;
183        default: 
184           return 0;
185    }
186    return 1;
187}
188
189/* */
190
191typedef enum saved_choice_entries {
192    saved_choice_count_entry  = 0, /* value_1 */
193    saved_choice_n_of_records = 1,
194} saved_choice_entries;
195
196# define saved_choice_count saved_value_1(saved_choice_count_entry)
197
198static inline void saved_choice_initialize(void)
199{
200    saved_type(0) = saved_record_0;
201    saved_record(0) = choice_save_type;
202}
203
204static inline int saved_choice_current_component(void)
205{
206    return saved_type(saved_choice_count_entry - saved_choice_n_of_records) == saved_record_0 
207        ? saved_value_1(saved_choice_count_entry - saved_choice_n_of_records) : -1 ;
208}
209
210static inline void saved_choice_update_component(void)
211{
212    saved_value_1(saved_choice_count_entry - saved_choice_n_of_records) += 1;
213}
214
215void tex_show_math_choice_group(void)
216{
217    tex_print_str_esc("mathchoice");
218    tex_aux_show_group_count(saved_choice_count);
219}
220
221int tex_show_math_choice_record(void)
222{
223    tex_print_str("choice ");
224    switch (saved_type(0)) { 
225       case saved_record_0:
226            tex_print_format("count %i", saved_value_1(0));
227            break;
228        default: 
229           return 0;
230    }
231    return 1;
232}
233
234/* */
235
236typedef enum saved_fraction_entries {
237    saved_fraction_variant_entry   = 0, /* value_1 */
238    saved_fraction_userstyle_entry = 0, /* value 2 */
239    saved_fraction_autostyle_entry = 0, /* value_3 */
240    saved_fraction_n_of_records    = 1,
241} saved_fraction_entries;
242
243# define saved_fraction_variant   saved_value_1(saved_fraction_variant_entry)
244# define saved_fraction_userstyle saved_value_2(saved_fraction_userstyle_entry)
245# define saved_fraction_autostyle saved_value_3(saved_fraction_autostyle_entry)
246
247static inline void saved_fraction_initialize(void)
248{
249    saved_type(0) = saved_record_0;
250    saved_record(0) = fraction_save_type;
251}
252
253static inline int saved_fraction_current_component(void)
254{
255    return saved_type(saved_fraction_variant_entry - saved_fraction_n_of_records) == saved_record_0 
256        ? saved_value_1(saved_fraction_variant_entry - saved_fraction_n_of_records) : -1 ;
257}
258
259static inline void saved_fraction_update_component(void)
260{
261    saved_value_1(saved_fraction_variant_entry - saved_fraction_n_of_records) += 1;
262}
263
264static inline int saved_fraction_current_userstyle(void)
265{
266    return saved_value_2(saved_fraction_userstyle_entry - saved_fraction_n_of_records);
267}
268
269static inline int saved_fraction_current_autostyle(void)
270{
271    return saved_value_3(saved_fraction_autostyle_entry - saved_fraction_n_of_records);
272}
273
274void tex_show_math_fraction_group(void)
275{
276    tex_print_str_esc("fraction");
277    tex_aux_show_group_count(saved_fraction_variant);
278}
279
280int tex_show_math_fraction_record(void)
281{
282    tex_print_str("fraction ");
283    switch (saved_type(0)) { 
284       case saved_record_0:
285            tex_print_format("variant %i, userstyle %i, autostyle %i", saved_value_1(0), saved_value_2(0), saved_value_3(0));
286            break;
287        default: 
288           return 0;
289    }
290    return 1;
291}
292
293/* */
294
295typedef enum saved_radical_entries {
296    saved_radical_degree_done_entry = 0, /* value_1 */
297    saved_radical_style_entry       = 0, /* value_2 */
298    saved_radical_n_of_records      = 1,
299} saved_radical_entries;
300
301# define saved_radical_degree_done saved_value_1(saved_radical_degree_done_entry)
302# define saved_radical_style       saved_value_2(saved_radical_style_entry)
303
304static inline void saved_radical_initialize(void)
305{
306    saved_type(0) = saved_record_0;
307    saved_record(0) = radical_save_type;
308}
309
310static inline int saved_radical_current_component(void)
311{
312    return saved_type(saved_radical_degree_done_entry - saved_radical_n_of_records) == saved_record_0 
313        ? saved_value_1(saved_radical_degree_done_entry - saved_radical_n_of_records) : -1 ;
314}
315
316static inline int saved_radical_current_style(void)
317{
318    return saved_value_2(saved_radical_style_entry - saved_radical_n_of_records);
319}
320
321static inline void saved_radical_update_component(void)
322{
323    saved_value_1(saved_radical_degree_done_entry - saved_radical_n_of_records) += 1;
324}
325
326void tex_show_math_radical_group(void)
327{
328    tex_print_str_esc("radical");
329    tex_aux_show_group_count(saved_radical_degree_done);
330}
331
332int tex_show_math_radical_record(void)
333{
334    tex_print_str("radical ");
335    switch (saved_type(0)) { 
336       case saved_record_0:
337            tex_print_format("degree %i, style %i", saved_value_1(0), saved_value_2(0));
338            break;
339        default: 
340           return 0;
341    }
342    return 1;
343}
344
345/* */
346
347typedef enum saved_operator_entries {
348    saved_operator_variant_entry = 0, /* value_1 */
349    saved_operator_n_of_records  = 1,
350} saved_operator_entries;
351
352# define saved_operator_variant saved_value_1(saved_operator_variant_entry)
353
354static inline void saved_operator_initialize(void)
355{
356    saved_type(0) = saved_record_0;
357    saved_record(0) = operator_save_type;
358}
359
360static inline int saved_operator_current_component(void)
361{
362    return saved_type(saved_operator_variant_entry - saved_operator_n_of_records) == saved_record_0 
363        ? saved_value_1(saved_operator_variant_entry - saved_operator_n_of_records) : -1 ;
364}
365
366static inline void saved_operator_update_component(void)
367{
368    saved_value_1(saved_operator_variant_entry - saved_operator_n_of_records) += 1;
369}
370
371void tex_show_math_operator_group(void)
372{
373    tex_print_str_esc("operator");
374    tex_aux_show_group_count(saved_operator_variant);
375}
376
377int tex_show_math_operator_record(void)
378{
379    tex_print_str("operator ");
380    switch (saved_type(0)) { 
381       case saved_record_0:
382            tex_print_format("variant %i", saved_value_1(0));
383            break;
384        default: 
385           return 0;
386    }
387    return 1;
388}
389
390/* */
391
392typedef enum saved_math_group_entries {
393    saved_math_group_pointer_entry   = 0, /* value_1 */
394    saved_math_group_all_class_entry = 0, /* value_2 */
395    saved_math_group_n_of_records    = 1,
396} saved_math_group_entries;
397
398# define saved_math_group_pointer   saved_value_1(saved_math_group_pointer_entry)
399# define saved_math_group_all_class saved_value_2(saved_math_group_all_class_entry)
400
401static inline void saved_math_group_initialize(void)
402{
403    saved_type(0) = saved_record_0;
404    saved_record(0) = math_group_save_type;
405}
406
407int tex_show_math_group_record(void)
408{
409    tex_print_str("math group ");
410    switch (saved_type(0)) { 
411       case saved_record_0:
412            tex_print_format("pointer %i, allclass %i", saved_value_1(0), saved_value_2(0));
413            break;
414        default: 
415           return 0;
416    }
417    return 1;
418}
419
420/* */
421
422static int      tex_aux_scan_math           (halfword p, halfword style, int usetextfont, halfword toks, halfword toks_text, int nocomponent, halfword cls, halfword all);
423static halfword tex_aux_finish_math_list    (halfword p);
424static void     tex_aux_math_math_component (halfword n, int append);
425
426# define cramped 1
427
428# define cramped_style(A) (2 * ((A) / 2) + cramped)                     /*tex cramp the style */
429# define sub_style(A)     (2 * ((A) / 4) + script_style + cramped)      /*tex smaller and cramped */
430# define sup_style(A)     (2 * ((A) / 4) + script_style + ((A) % 2))    /*tex smaller */
431# define small_style      sup_style
432# define num_style(A)     ((A) + 2 - 2 * ((A) / 6))                     /*tex smaller unless already scriptscript */
433# define smaller_style    num_style
434# define denom_style(A)   (2 * ((A) / 2) + cramped + 2 - 2 * ((A) / 6)) /*tex smaller, cramped */
435# define sup_sup_style(A) sup_style(sup_style((A)))                     /*tex smaller */
436
437static inline mathdictval tex_fake_math_dict(halfword chr) 
438{
439    mathdictval d = tex_no_dict_code();
440    if (math_dict_properties_par || math_dict_group_par) {
441        d.properties = (unsigned short) math_dict_properties_par;
442        d.group = (unsigned short) math_dict_group_par;
443        d.index = (unsigned int) chr;
444    }
445    return d;
446}
447
448void tex_math_copy_char_data(halfword target, halfword source, int wipelist)
449{
450    if (node_type(source) == math_char_node) {
451        kernel_math_family(target) = kernel_math_family(source);
452        kernel_math_character(target) = kernel_math_character(source);
453        kernel_math_options(target) = kernel_math_options(source);
454        kernel_math_properties(target) = kernel_math_properties(source);
455        kernel_math_group(target) = kernel_math_group(source);
456        kernel_math_index(target) = kernel_math_index(source);
457    } else {
458        kernel_math_list(target) = kernel_math_list(source);
459        if (wipelist) { 
460           kernel_math_list(source) = null;
461        }
462    }
463}
464
465static inline void tex_math_set_scripts_options(halfword n)
466{
467    if (math_scripts_mode_par & fixed_super_or_sub_script_code) {  
468        noad_options(n) |= noad_option_fixed_super_or_sub_script;
469    }
470    if (math_scripts_mode_par & fixed_super_and_sub_script_code) {  
471        noad_options(n) |= noad_option_fixed_super_and_sub_script;
472    }
473    if (math_scripts_mode_par & ignore_empty_super_script_code) {  
474        noad_options(n) |= noad_option_ignore_empty_super_script;
475    }
476    if (math_scripts_mode_par & ignore_empty_sub_script_code) {  
477        noad_options(n) |= noad_option_ignore_empty_sub_script;
478    }
479    if (math_scripts_mode_par & ignore_empty_prime_script_code) {  
480        noad_options(n) |= noad_option_ignore_empty_prime_script;
481    }
482}
483
484/*tex identity style */
485
486# define display_style_nibble(a)               (a << 28)
487# define cramped_display_style_nibble(a)       (a << 24)
488# define text_style_nibble(a)                  (a << 20)
489# define cramped_text_style_nibble(a)          (a << 16)
490# define script_style_nibble(a)                (a << 12)
491# define cramped_script_style_nibble(a)        (a <<  8)
492# define script_script_style_nibble(a)         (a <<  4)
493# define cramped_script_script_style_nibble(a) (a <<  0)
494
495static const unsigned int math_variant_presets[last_math_style_variant+1] = { 
496    /*tex identity style */ 
497    // math_normal_style_variant_preset
498    (unsigned int) (0
499      + display_style_nibble              (display_style              )
500      + cramped_display_style_nibble      (cramped_display_style      )
501      + text_style_nibble                 (text_style                 )
502      + cramped_text_style_nibble         (cramped_text_style         )
503      + script_style_nibble               (script_style               )
504      + cramped_script_style_nibble       (cramped_script_style       )
505      + script_script_style_nibble        (script_script_style        )
506      + cramped_script_script_style_nibble(cramped_script_script_style)
507    ),
508    /*tex cramp the style */
509    // math_cramped_style_variant_preset 
510    (unsigned int) (0
511      + display_style_nibble              (cramped_display_style      )
512      + cramped_display_style_nibble      (cramped_display_style      )
513      + text_style_nibble                 (cramped_text_style         )
514      + cramped_text_style_nibble         (cramped_text_style         )
515      + script_style_nibble               (cramped_script_style       )
516      + cramped_script_style_nibble       (cramped_script_style       )
517      + script_script_style_nibble        (cramped_script_script_style)
518      + cramped_script_script_style_nibble(cramped_script_script_style)
519    ),
520    /*tex smaller and cramped */
521    // math_subscript_style_variant_preset 
522    (unsigned int) (0
523      + display_style_nibble              (cramped_script_style       )
524      + cramped_display_style_nibble      (cramped_script_style       )
525      + text_style_nibble                 (cramped_script_style       )
526      + cramped_text_style_nibble         (cramped_script_style       )
527      + script_style_nibble               (cramped_script_script_style)
528      + cramped_script_style_nibble       (cramped_script_script_style)
529      + script_script_style_nibble        (cramped_script_script_style)
530      + cramped_script_script_style_nibble(cramped_script_script_style)
531    ),
532    /*tex smaller */  
533    // math_superscript_style_variant_preset 
534    (unsigned int) (0 
535      + display_style_nibble              (script_style               )
536      + cramped_display_style_nibble      (cramped_script_style       )
537      + text_style_nibble                 (script_style               )
538      + cramped_text_style_nibble         (cramped_script_style       )
539      + script_style_nibble               (script_script_style        )
540      + cramped_script_style_nibble       (cramped_script_script_style)
541      + script_script_style_nibble        (script_script_style        )
542      + cramped_script_script_style_nibble(cramped_script_script_style)
543    ),
544    // math_small_style_variant_preset 
545    (unsigned int) (0
546      + display_style_nibble              (script_style               ) 
547      + cramped_display_style_nibble      (cramped_script_style       )
548      + text_style_nibble                 (script_style               )
549      + cramped_text_style_nibble         (cramped_script_style       )
550      + script_style_nibble               (script_script_style        )
551      + cramped_script_style_nibble       (cramped_script_script_style)
552      + script_script_style_nibble        (script_script_style        )
553      + cramped_script_script_style_nibble(cramped_script_script_style)
554    ),
555    /*tex smaller unless already scriptscript */
556    // math_smaller_style_variant_preset 
557    (unsigned int) (0
558      + display_style_nibble              (text_style                 )
559      + cramped_display_style_nibble      (cramped_text_style         )
560      + text_style_nibble                 (script_style               )
561      + cramped_text_style_nibble         (cramped_script_style       )
562      + script_style_nibble               (script_script_style        )
563      + cramped_script_style_nibble       (cramped_script_script_style)
564      + script_script_style_nibble        (script_script_style        )
565      + cramped_script_script_style_nibble(cramped_script_script_style)
566    ),
567    // math_numerator_style_variant_preset 
568    (unsigned int) (0
569      + display_style_nibble              (text_style                 )
570      + cramped_display_style_nibble      (cramped_text_style         )
571      + text_style_nibble                 (script_style               )
572      + cramped_text_style_nibble         (cramped_script_style       )
573      + script_style_nibble               (script_script_style        )
574      + cramped_script_style_nibble       (cramped_script_script_style)
575      + script_script_style_nibble        (script_script_style        )
576      + cramped_script_script_style_nibble(cramped_script_script_style)
577    ),
578    // math_denominator_style_variant_preset 
579    (unsigned int) (0
580      + display_style_nibble              (cramped_text_style         ) 
581      + cramped_display_style_nibble      (cramped_text_style         )
582      + text_style_nibble                 (cramped_script_style       )
583      + cramped_text_style_nibble         (cramped_script_style       )
584      + script_style_nibble               (cramped_script_script_style)
585      + cramped_script_style_nibble       (cramped_script_script_style)
586      + script_script_style_nibble        (cramped_script_script_style)
587      + cramped_script_script_style_nibble(cramped_script_script_style)
588    ),
589    /*tex smaller, keep cramped */
590    // math_double_superscript_style_variant_preset 
591    (unsigned int) (0
592      + display_style_nibble              (script_script_style        )
593      + cramped_display_style_nibble      (cramped_script_script_style)
594      + text_style_nibble                 (script_script_style        )
595      + cramped_text_style_nibble         (cramped_script_script_style)
596      + script_style_nibble               (script_script_style        )
597      + cramped_script_style_nibble       (cramped_script_script_style)
598      + script_script_style_nibble        (script_script_style        )
599      + cramped_script_script_style_nibble(cramped_script_script_style)
600    ),
601};
602
603int tex_get_math_variant_preset(int i)
604{
605    return valid_math_style_variant(i) ? math_variant_presets[i] : 0;
606}
607
608/*tex
609    This is very \TEX: a variable class influences the family being used.
610*/
611
612halfword tex_size_of_style(halfword style)
613{
614    switch (style) {
615        case script_style:
616        case cramped_script_style:
617            return script_size;
618        case script_script_style:
619        case cramped_script_script_style:
620            return script_script_size;
621        default:
622            return text_size;
623    }
624}
625
626// halfword tex_math_style_variant(halfword style, halfword parameter)
627// {
628//     switch (tex_get_math_parameter(style, parameter, NULL)) {
629//         case math_normal_style_variant:
630//             return style;
631//         case math_cramped_style_variant:
632//             return cramped_style(style);
633//         case math_subscript_style_variant:
634//             return sub_style(style);
635//         case math_superscript_style_variant:
636//             return sup_style(style);
637//         case math_small_style_variant:
638//             return small_style(style);
639//         case math_smaller_style_variant:
640//             return smaller_style(style);
641//         case math_numerator_style_variant:
642//             return num_style(style);
643//         case math_denominator_style_variant:
644//             return denom_style(style);
645//         case math_double_superscript_variant:
646//             return sup_sup_style(style);
647//         default:
648//             return style;
649//     }
650// }
651
652halfword tex_math_style_variant(halfword style, halfword parameter)
653{
654    return ((unsigned int) tex_get_math_parameter(style,parameter,NULL) >> (4 * (7 - style))) & 0xF;
655}
656
657int tex_math_has_class_option(halfword cls, int option)
658{
659    halfword value = count_parameter(first_math_options_code + cls);
660    if (value == no_class_options) {
661        unsigned parent = (unsigned) count_parameter(first_math_parent_code + cls);
662        cls = (parent >> 16) & 0xFF;
663        if (! valid_math_class_code(cls)) {
664            return 0;
665        }
666        value = count_parameter(first_math_options_code + cls);
667    }
668    return (value & option) == option;
669}
670
671int tex_math_has_class_parent(halfword cls)
672{
673    halfword value = count_parameter(first_math_options_code + cls);
674    if (value == no_class_options) {
675        unsigned parent = (unsigned) count_parameter(first_math_parent_code + cls);
676        return (parent >> 16) & 0xFF;
677    }
678    return 0;
679}
680
681static void tex_aux_unsave_math(void)
682{
683    tex_unsave();
684    lmt_save_state.save_stack_data.ptr -= saved_math_n_of_records;
685    tex_flush_node_list(lmt_dir_state.text_dir_ptr);
686    lmt_dir_state.text_dir_ptr = saved_math_direction;
687}
688
689/*tex
690
691    Sometimes it is necessary to destroy an mlist. The following subroutine empties the current
692    list, assuming that |abs(mode) = mmode| aka |is_m_mode(mode)|.
693
694*/
695
696void tex_flush_math(void)
697{
698    halfword head = cur_list.head;
699    tex_flush_node_list(node_next(head));
700    tex_flush_node_list(cur_list.incomplete_noad);
701    node_next(head) = null;
702    cur_list.tail = head;
703    cur_list.incomplete_noad = null;
704}
705
706/*tex A printing helper. */
707
708static void tex_aux_print_parameter(const char *what, halfword style, halfword param, halfword indirect, halfword value)
709{
710    tex_begin_diagnostic();
711    tex_print_format("{%s ", what);
712    if (indirect >= 0 && indirect <= last_math_indirect) {
713        tex_print_str(lmt_interface.math_indirect_values[indirect].name);
714        tex_print_char(' ');
715    }
716    if (param < math_parameter_last) {
717        tex_print_cmd_chr(math_parameter_cmd, param);
718    } else {
719        tex_print_format("%x %x ", math_parameter_spacing_left(param), math_parameter_spacing_right(param));
720    }
721    tex_print_cmd_chr(math_style_cmd, style);
722    tex_print_char('=');
723    switch (math_parameter_value_type(param)) {
724        case math_integer_parameter:
725        case math_style_parameter:
726            tex_print_int(value);
727            break;
728        case math_dimension_parameter:
729            tex_print_dimension(value, pt_unit);
730            break;
731        case math_muglue_parameter:
732            tex_print_spec(value, mu_unit);
733            break;
734        default:
735            tex_print_int(value);
736            break;
737    }
738    tex_print_char('}');
739    tex_end_diagnostic();
740}
741
742static void tex_aux_print_fam(const char *what, halfword size, halfword fam)
743{
744    tex_begin_diagnostic();
745    tex_print_format("{%s %C family %i: %F}", what, define_family_cmd, size, fam, tex_fam_fnt(fam, size));
746    tex_end_diagnostic();
747}
748
749/*tex
750    Before we can do anything in math mode, we need fonts. We can use |max_n_of_math_families|
751    instead of 256 but we need to pack in bytes anyway so there is no gain.
752*/
753
754int tex_fam_fnt(int fam, int size)
755{
756    /* todo check valid fam */
757    sa_tree_item item;
758    sa_get_item_4(lmt_math_state.fam_head, fam + (256 * size), &item);
759    return (int) item.int_value;
760}
761
762void tex_def_fam_fnt(int fam, int size, int fnt, int level)
763{
764    sa_tree_item item;
765    item.int_value = fnt;
766    sa_set_item_4(lmt_math_state.fam_head, fam + (256 * size), item, level);
767    if (tracing_assigns_par > 1) {
768        tex_aux_print_fam("assigning", size, fam);
769    }
770    tex_fixup_math_parameters(fam, size, fnt, level);
771}
772
773static void tex_aux_unsave_math_fam_data(int gl)
774{
775    if (lmt_math_state.fam_head->stack) {
776        while (lmt_math_state.fam_head->sa_stack_ptr > 0 && abs(lmt_math_state.fam_head->stack[lmt_math_state.fam_head->sa_stack_ptr].level) >= (int) gl) {
777            sa_stack_item item = lmt_math_state.fam_head->stack[lmt_math_state.fam_head->sa_stack_ptr];
778            if (item.level > 0) {
779                sa_rawset_item_4(lmt_math_state.fam_head, item.code, item.value_1);
780                /*tex Now do a trace message, if requested. */
781                if (tracing_restores_par > 1) {
782                    int size = item.code / 256;
783                    int fam = item.code % 256;
784                    tex_aux_print_fam("restoring", size, fam);
785                }
786            }
787            --lmt_math_state.fam_head->sa_stack_ptr;
788        }
789    }
790}
791
792/*tex Math parameters, we have a lot of them! Todo: move the style into 2 */
793
794void tex_def_math_parameter(int style, int param, scaled value, int level, int indirect, int fixup)
795{
796    if (fixup && ignore_math_parameter(param) == 2) {
797        return;
798    } else { 
799        sa_tree_item item1, item2;
800        int different = 1;
801        if (level <= 1) {
802            if (math_parameter_value_type(param) == math_muglue_parameter) {
803                sa_get_item_8(lmt_math_state.par_head, (param + (math_parameter_max_range * style)), &item1, &item2);
804                if (item2.int_value == indirect_math_regular && item1.int_value > thick_muskip_code) {
805                    if (lmt_node_memory_state.nodesizes[item1.int_value]) {
806                        tex_free_node(item1.int_value, glue_spec_size);
807                    }
808                }
809            }
810        } else { 
811            /*tex Less tracing at the cost of a lookup. */
812            sa_get_item_8(lmt_math_state.par_head, (param + (math_parameter_max_range * style)), &item1, &item2);
813            different = item1.int_value != value || item2.int_value != indirect;
814        }
815     // if (different) { // maybe, because we did a lookup anyway, but there is little gain 
816            item1.int_value = value;
817            item2.int_value = indirect;
818            sa_set_item_8(lmt_math_state.par_head, (param + (math_parameter_max_range * style)), item1, item2, level);
819            if (different && tracing_assigns_par > 1) {
820                tex_aux_print_parameter("assigning", style, param, indirect, value);
821            }
822     // }
823    }
824}
825
826// mukern .. there is no mudimen
827
828scaled tex_get_math_parameter(int style, int param, halfword *type)
829{
830    halfword indirect, value;
831    sa_tree_item v1, v2;
832    sa_get_item_8(lmt_math_state.par_head, (param + (math_parameter_max_range * style)), &v1, &v2);
833    indirect = v2.int_value == lmt_math_state.par_head->dflt.int_value ? indirect_math_unset : v2.uint_value;
834    value = v1.int_value;
835    switch (indirect) {
836        case indirect_math_unset:
837            if (type) {
838                *type = no_val_level;
839            }
840            return MATHPARAMDEFAULT;
841        /* we stored nothing */
842        case indirect_math_regular:
843            switch (math_parameter_value_type(param)) {
844                case math_dimension_parameter:
845                    if (type) {
846                        *type = dimension_val_level;
847                    }
848                    return value;
849                case math_muglue_parameter:
850                    if (type) {
851                        *type = muglue_val_level;
852                    }
853                    return value <= thick_muskip_code ? muglue_parameter(value) : value;
854             // case math_integer_parameter:
855             // case math_style_parameter:
856                default:
857                    if (type) {
858                        *type = integer_val_level;
859                    }
860                    return value;
861            }
862        /* we stored cs */
863        case indirect_math_integer:
864            if (! value) {
865                if (type) {
866                    *type = integer_val_level;
867                }
868                return value;
869            } else if (eq_type(value) == integer_cmd) {
870                if (type) {
871                    *type = integer_val_level;
872                }
873                return eq_value(value);
874            } else {
875                goto MISMATCH;
876            }
877        case indirect_math_dimension:
878            if (! value) {
879                if (type) {
880                    *type = dimension_val_level;
881                }
882                return value;
883            } else if (eq_type(value) == dimension_cmd) {
884                if (type) {
885                    *type = dimension_val_level;
886                }
887                return eq_value(value);
888            } else if (eq_type(value) == posit_cmd) {
889                if (type) {
890                    *type = dimension_val_level;
891                }
892                return tex_posit_to_dimension(eq_value(value));
893            } else {
894                goto MISMATCH;
895            }
896        case indirect_math_mugluespec:
897            if (! value) {
898                if (type) {
899                    *type = muglue_val_level;
900                }
901                return value;
902            } else {
903                switch (eq_type(value)) {
904                    case mugluespec_cmd:
905                        if (type) {
906                            *type = muglue_val_level;
907                        }
908                        return eq_value(value);
909                    default:
910                        goto MISMATCH;
911                }
912
913            }
914        case indirect_math_gluespec:
915            if (! value) {
916                if (type) {
917                    *type = glue_val_level;
918                }
919                return value;
920            } else {
921                switch (eq_type(value)) {
922                    case gluespec_cmd:
923                        if (type) {
924                            *type = glue_val_level;
925                        }
926                        return eq_value(value);
927                    default:
928                        goto MISMATCH;
929                }
930            }
931        /* we stored chr */
932        case indirect_math_register_integer:
933            if (! value) {
934                if (type) {
935                    *type = integer_val_level;
936                }
937                return value;
938            } else if (eq_type(value) == register_integer_reference_cmd) {
939                if (type) {
940                    *type = integer_val_level;
941                }
942                return eq_value(value);
943            } else {
944                goto MISMATCH;
945            }
946        case indirect_math_register_dimension:
947            if (! value) {
948                if (type) {
949                    *type = dimension_val_level;
950                }
951                return value;
952            } else if (eq_type(value) == register_dimension_reference_cmd) {
953                if (type) {
954                    *type = dimension_val_level;
955                }
956                return eq_value(value);
957            } else {
958                goto MISMATCH;
959            }
960        case indirect_math_register_gluespec:
961            if (! value) {
962                if (type) {
963                    *type = glue_val_level;
964                }
965                return value;
966            } else if (eq_type(value) == register_glue_reference_cmd) {
967                if (type) {
968                    *type = glue_val_level;
969                }
970                return eq_value(value);
971            } else {
972                goto MISMATCH;
973            }
974        case indirect_math_register_mugluespec:
975            if (! value) {
976                if (type) {
977                    *type = muglue_val_level;
978                }
979                return value;
980            } else if (eq_type(value) == register_muglue_reference_cmd) {
981                if (type) {
982                    *type = muglue_val_level;
983                }
984                return eq_value(value);
985            } else {
986                goto MISMATCH;
987            }
988        case indirect_math_internal_integer:
989            if (! value) {
990                if (type) {
991                    *type = integer_val_level;
992                }
993                return value;
994            } else if (eq_type(value) == internal_integer_reference_cmd) {
995                if (type) {
996                    *type = integer_val_level;
997                }
998                return eq_value(value);
999            } else {
1000                goto MISMATCH;
1001            }
1002        case indirect_math_internal_dimension:
1003            if (! value) {
1004                if (type) {
1005                    *type = dimension_val_level;
1006                }
1007                return value;
1008            } else if (eq_type(value) == internal_dimension_reference_cmd) {
1009                if (type) {
1010                    *type = dimension_val_level;
1011                }
1012                return eq_value(value);
1013            } else {
1014                goto MISMATCH;
1015            }
1016        case indirect_math_internal_gluespec:
1017            if (! value) {
1018                if (type) {
1019                    *type = glue_val_level;
1020                }
1021                return value;
1022            } else if (eq_type(value) == internal_glue_reference_cmd) {
1023                if (type) {
1024                    *type = glue_val_level;
1025                }
1026                return eq_value(value);
1027            } else {
1028                goto MISMATCH;
1029            }
1030        case indirect_math_internal_mugluespec:
1031            if (! value) {
1032                if (type) {
1033                    *type = muglue_val_level;
1034                }
1035                return value;
1036            } else  if (eq_type(value) == internal_muglue_reference_cmd) {
1037                if (type) {
1038                    *type = muglue_val_level;
1039                }
1040                return eq_value(value);
1041            } else {
1042                goto MISMATCH;
1043            }
1044        default:
1045          MISMATCH:
1046            tex_handle_error(
1047                normal_error_type,
1048                "Invalid inherited math parameter",
1049                "You probably changed the type of the inherited math parameter, so I will "
1050                "use zero instead."
1051            );
1052            return 0;
1053    }
1054}
1055
1056int tex_has_math_parameter(int style, int param)
1057{
1058    sa_tree_item v1, v2;
1059    sa_get_item_8(lmt_math_state.par_head, (param + (math_parameter_max_range * style)), &v1, &v2);
1060    return v2.int_value == lmt_math_state.par_head->dflt.int_value ? indirect_math_unset : v2.uint_value;
1061}
1062
1063static void tex_aux_unsave_math_parameter_data(int gl)
1064{
1065    if (lmt_math_state.par_head->stack) {
1066     // printf("unsaving level %i >= gl %i\n", lmt_math_state.par_head->stack[lmt_math_state.par_head->sa_stack_ptr].level, gl);
1067        while (lmt_math_state.par_head->sa_stack_ptr > 0 && abs(lmt_math_state.par_head->stack[lmt_math_state.par_head->sa_stack_ptr].level) >= gl) {
1068            sa_stack_item item = lmt_math_state.par_head->stack[lmt_math_state.par_head->sa_stack_ptr];
1069            if (item.level > 0) {
1070                int param = item.code % math_parameter_max_range;
1071                int style = item.code / math_parameter_max_range;
1072                if (math_parameter_value_type(param) == math_muglue_parameter) {
1073                    sa_tree_item item1, item2;
1074                    sa_get_item_8(lmt_math_state.par_head, item.code, &item1, &item2);
1075                    if (item2.int_value == indirect_math_regular && item1.int_value > thick_muskip_code) {
1076                     /* if (tex_valid_node(item1.int_value)) { */
1077                        if (lmt_node_memory_state.nodesizes[item1.int_value]) {
1078                            // printf("HERE 2.1: %i %i / %i %i / %i\n",item2.int_value,item1.int_value, item.value_1.int_value, item.value_2.int_value, node_type(item1.int_value));
1079                            tex_free_node(item1.int_value, glue_spec_size);
1080                        } else {
1081                            // printf("HERE 2.2: %i %i / %i %i / %i\n",item2.int_value,item1.int_value, item.value_1.int_value, item.value_2.int_value, node_type(item1.int_value));
1082                        }
1083                    }
1084                }
1085                sa_rawset_item_8(lmt_math_state.par_head, item.code, item.value_1, item.value_2);
1086                /*tex Do a trace message, if requested. */
1087                if (tracing_restores_par > 1) {
1088                    int indirect = item.value_2.int_value;
1089                    tex_aux_print_parameter("restoring", style, param, indirect, tex_get_math_parameter(style, param, NULL));
1090                }
1091            }
1092         // printf("restored %i @ %i\n",lmt_math_state.par_head->sa_stack_ptr,gl);
1093            --lmt_math_state.par_head->sa_stack_ptr;
1094        }
1095    }
1096}
1097
1098/*tex Saving and unsaving of both: */
1099
1100void tex_unsave_math_data(int grouplevel)
1101{
1102    tex_aux_unsave_math_fam_data(grouplevel);
1103    tex_aux_unsave_math_parameter_data(grouplevel);
1104}
1105
1106/*tex Dumping and undumping: */
1107
1108void tex_dump_math_data(dumpstream f)
1109{
1110    if (! lmt_math_state.fam_head) {
1111        lmt_math_state.fam_head = sa_new_tree(mathfont_sparse_identifier, MATHFONTSTACK, MATHFONTSTEP, 4, (sa_tree_item) { .int_value = MATHFONTDEFAULT });
1112    }
1113    sa_dump_tree(f, lmt_math_state.fam_head);
1114    if (! lmt_math_state.par_head) {
1115        lmt_math_state.par_head = sa_new_tree(mathparam_sparse_identifier, MATHPARAMSTACK, MATHPARAMSTEP, 8, (sa_tree_item) { .int_value = MATHPARAMDEFAULT });
1116    }
1117    sa_dump_tree(f, lmt_math_state.par_head);
1118}
1119
1120void tex_undump_math_data(dumpstream f)
1121{
1122    lmt_math_state.fam_head = sa_undump_tree(f);
1123    lmt_math_state.par_head = sa_undump_tree(f);
1124}
1125
1126void tex_initialize_math(void)
1127{
1128    if (! lmt_math_state.fam_head) {
1129        lmt_math_state.fam_head = sa_new_tree(mathfont_sparse_identifier, MATHFONTSTACK, MATHFONTSTEP, 4, (sa_tree_item) { .int_value = MATHFONTDEFAULT });
1130    }
1131    if (! lmt_math_state.par_head) {
1132        lmt_math_state.par_head = sa_new_tree(mathparam_sparse_identifier, MATHPARAMSTACK, MATHPARAMSTEP, 8, (sa_tree_item) { .int_value = MATHPARAMDEFAULT });
1133        tex_initialize_math_spacing();
1134    }
1135    return;
1136}
1137
1138/*tex
1139
1140    Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope, Clo, Pun, or Inn, for purposes
1141    of spacing and line breaking. An |ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|,
1142    |close_noad|, |punct_noad|, or |inner_noad| is used to represent portions of the various types.
1143    For example, an |=| sign in a formula leads to the creation of a |rel_noad| whose |nucleus| field
1144    is a representation of an equals sign (usually |fam = 0|, |character = 075|). A formula preceded
1145    by |\mathrel| also results in a |rel_noad|. When a |rel_noad| is followed by an |op_noad|, say,
1146    and possibly separated by one or more ordinary nodes (not noads), \TEX\ will insert a penalty
1147    node (with the current |rel_penalty|) just after the formula that corresponds to the |rel_noad|,
1148    unless there already was a penalty immediately following; and a \quote {thick space} will be
1149    inserted just before the formula that corresponds to the |op_noad|.
1150
1151    A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually has a |subtype = normal|. The
1152    only exception is that an |op_noad| might have |subtype = limits| or |no_limits|, if the normal
1153    positioning of limits has been overridden for this operator.
1154
1155    A |radical_noad| also has a |left_delimiter| field, which usually represents a square root sign.
1156
1157    A |fraction_noad| has a |right_delimiter| field as well as a |left_delimiter|.
1158
1159    Delimiter fields have four subfields called |small_fam|, |small_char|, |large_fam|, |large_char|.
1160    These subfields represent variable-size delimiters by giving the \quote {small} and \quote
1161    {large} starting characters, as explained in Chapter~17 of {\em The \TEX book}.
1162
1163    A |fraction_noad| is actually quite different from all other noads. It has |thickness|,
1164    |denominator|, and |numerator| fields instead of |nucleus|, |subscr|, and |supscr|. The
1165    |thickness| is a scaled value that tells how thick to make a fraction rule; however, the special
1166    value |preset_rule_thickness| is used to stand for the |preset_rule_thickness| of the current
1167    size. The |numerator| and |denominator| point to mlists that define a fraction; we always have
1168    |type(numerator) = type(denominator) = sub_mlist|. The |left_delimiter| and |right_delimiter|
1169    fields specify delimiters that will be placed at the left and right of the fraction. In this way,
1170    a |fraction_noad| is able to represent all of \TEX's operators |\over|, |\atop|, |\above|,
1171    |\overwithdelims|, |\atopwithdelims|, and |\abovewithdelims|.
1172
1173    The |new_noad| function creates an |ord_noad| that is completely |null|.
1174
1175*/
1176
1177halfword tex_new_sub_box(halfword curbox)
1178{
1179    halfword noad = tex_new_node(simple_noad, ordinary_noad_subtype);
1180    halfword sbox = tex_new_node(sub_box_node, 0);
1181    noad_nucleus(noad) = sbox;
1182    kernel_math_list(sbox) = curbox;
1183    return noad;
1184}
1185
1186static quarterword tex_aux_set_math_char(halfword target, mathcodeval *mval, mathdictval *dval)
1187{
1188    halfword hmcode = tex_get_hm_code(mval->character_value);
1189    kernel_math_character(target) = mval->character_value;
1190    if (variable_family_par == -2) {
1191        /*tex For those (read: context) who want to use this variable class as intended. */
1192        kernel_math_family(target) = cur_fam_par_in_range ? cur_fam_par : mval->family_value;
1193        node_subtype(target) = mval->class_value;
1194    } else if (mval->class_value == math_use_current_family_code) {
1195        /*tex For CMS chairman MS, so that he can answer a ltx question someplace. */
1196        kernel_math_family(target) = cur_fam_par_in_range ? cur_fam_par : mval->family_value;
1197        node_subtype(target) = ordinary_noad_subtype;
1198    } else if (mval->family_value == variable_family_par) {
1199        kernel_math_family(target) = cur_fam_par_in_range ? cur_fam_par : mval->family_value;
1200        node_subtype(target) = mval->class_value;
1201    } else {
1202        kernel_math_family(target) = mval->family_value;
1203        node_subtype(target) = mval->class_value;
1204    }
1205    if (dval) { 
1206        kernel_math_properties(target) = dval->properties;
1207        kernel_math_group(target) = dval->group;
1208        kernel_math_index(target) = dval->index;
1209    }
1210    if ((hmcode & auto_discretionary_normal) == auto_discretionary_normal) { // has_discretionary_normal
1211        math_kernel_node_set_option(target, math_kernel_auto_discretionary);
1212    } 
1213    if ((hmcode & auto_discretionary_italic) == auto_discretionary_italic) {  // has_discretionary_italic
1214        math_kernel_node_set_option(target, math_kernel_full_discretionary);
1215    }
1216    return node_subtype(target);
1217}
1218
1219/*tex
1220
1221    A few more kinds of noads will complete the set: An |under_noad| has its nucleus underlined; an
1222    |over_noad| has it overlined. An |accent_noad| places an accent over its nucleus; the accent
1223    character appears as |math_fam (accent_chr (p))| and |math_character (accent_chr (p))|. A
1224    |vcenter_noad| centers its nucleus vertically with respect to the axis of the formula; in such
1225    noads we always have |type (nucleus (p)) = sub_box|.
1226
1227    And finally, we have the |fence_noad| type, to implement \TEX's |\left| and |\right| as well as
1228    \ETEX's |\middle|. The |nucleus| of such noads is replaced by a |delimiter| field; thus, for
1229    example, |\left(| produces a |fence_noad| such that |delimiter(p)| holds the family and
1230    character codes for all left parentheses. A |fence_noad| of subtype |left_noad_side| never
1231    appears in an mlist except as the first element, and a |fence_noad| with subtype
1232    |right_noad_side| never appears in an mlist except as the last element; furthermore, we either
1233    have both a |left_noad_side| and a |right_noad_side|, or neither one is present.
1234
1235    Math formulas can also contain instructions like |\textstyle| that override \TeX's normal style
1236    rules. A |style_node| is inserted into the data structure to record such instructions; it is
1237    three words long, so it is considered a node instead of a noad. The |subtype| is either
1238    |display_style| or |text_style| or |script_style| or |script_script_style|. The second and
1239    third words of a |style_node| are not used, but they are present because a |choice_node| is
1240    converted to a |style_node|.
1241
1242    \TEX\ uses even numbers 0, 2, 4, 6 to encode the basic styles |display_style|, \dots,
1243    |script_script_style|, and adds~1 to get the \quote {cramped} versions of these styles. This
1244    gives a numerical order that is backwards from the convention of Appendix~G in {\em The \TEX
1245    book}; i.e., a smaller style has a larger numerical value.
1246
1247*/
1248
1249void tex_run_math_style(void) {
1250    switch (cur_chr) { 
1251        case yet_unset_math_style:
1252            { 
1253                halfword style = tex_scan_math_style_identifier(1, 0);
1254                if (is_valid_math_style(style)) {
1255                    halfword noad = tex_new_node(style_node, (quarterword) style);
1256                    cur_list.math_style = style;
1257                    tex_tail_append(noad);
1258                }
1259            }
1260            break;
1261        case scaled_math_style:
1262            { 
1263                halfword noad = tex_new_node(style_node, scaled_math_style);
1264                style_scale(noad) = tex_scan_integer(0, NULL, NULL);
1265             // style_scale(noad) = tex_scan_positive_scale(0);
1266                cur_list.math_scale = style_scale(noad);
1267                tex_tail_append(noad);
1268            }
1269            break;
1270        case currently_set_math_style:
1271            /* no node gets injected */
1272            break; 
1273        default: 
1274            if (is_valid_math_style(cur_chr)) {
1275                halfword noad = tex_new_node(style_node, (quarterword) cur_chr);
1276                cur_list.math_style = cur_chr;
1277                tex_tail_append(noad);
1278            } else {
1279                /*tex For now silently ignored. */
1280            }
1281    }
1282}
1283
1284/*tex
1285
1286    Let's consider now the previously unwritten part of |show_node_list| that displays the things
1287    that can only be present in mlists; this program illustrates how to access the data structures
1288    just defined.
1289
1290    In the context of the following program, |p| points to a node or noad that should be displayed,
1291    and the current string contains the \quote {recursion history} that leads to this point. The
1292    recursion history consists of a dot for each outer level in which |p| is subsidiary to some
1293    node, or in which |p| is subsidiary to the |nucleus| field of some noad; the dot is replaced by
1294    |_| or |^| or |/| or |\\| if |p| is descended from the |subscr| or |supscr| or |denominator| or
1295    |numerator| fields of noads. For example, the current string would be |{\^_/}| if |p| points to
1296    the |ord_noad| for |x| in the (ridiculous) formula {$\sqrt {a ^ {\mathinner {b _
1297    {c \over x+y}}}}$|.
1298
1299*/
1300
1301static void tex_aux_display_choice_noad    (halfword n, int threshold, int max);
1302static void tex_aux_display_parameter_node (halfword n);
1303static void tex_aux_display_simple_noad    (halfword n, int threshold, int max);
1304static void tex_aux_display_radical_noad   (halfword n, int threshold, int max);
1305static void tex_aux_display_accent_noad    (halfword n, int threshold, int max);
1306static void tex_aux_display_fence_noad     (halfword n, int threshold, int max);
1307static void tex_aux_display_fraction_noad  (halfword n, int threshold, int max);
1308
1309static void tex_aux_print_fam_and_char(halfword n)
1310{
1311    tex_print_format(", family %x, character %x, original %x", kernel_math_family(n), kernel_math_character(n));
1312    tex_aux_show_dictionary(n, kernel_math_properties(n), kernel_math_group(n), kernel_math_index(n), tex_fam_fnt(kernel_math_family(n), 0), kernel_math_character(n));
1313}
1314
1315int tex_show_math_node(halfword n, int threshold, int max)
1316{
1317    switch (node_type(n)) {
1318        case style_node:
1319            /* why not shown? */
1320            break;
1321        case choice_node:
1322            tex_aux_display_choice_noad(n, threshold, max);
1323            break;
1324        case parameter_node:
1325            tex_aux_display_parameter_node(n);
1326            break;
1327        case simple_noad:
1328            tex_aux_display_simple_noad(n, threshold, max);
1329            break;
1330        case radical_noad:
1331            tex_aux_display_radical_noad(n, threshold, max);
1332            break;
1333        case accent_noad:
1334            tex_aux_display_accent_noad(n, threshold, max);
1335            break;
1336        case fence_noad:
1337            tex_aux_display_fence_noad(n, threshold, max);
1338            break;
1339        case fraction_noad:
1340            tex_aux_display_fraction_noad(n, threshold, max);
1341            break;
1342        case math_text_char_node:
1343        case math_char_node:
1344            tex_aux_print_fam_and_char(n);
1345            break;
1346        case sub_box_node:
1347            tex_print_node_list(kernel_math_list(n), NULL, threshold, max);
1348            break;
1349        case sub_mlist_node:
1350            if (kernel_math_list(n)) {
1351                tex_print_node_list(kernel_math_list(n), NULL, threshold, max);
1352            } else {
1353                tex_print_str(", empty");
1354            }
1355            break;
1356        default:
1357            return 0;
1358    }
1359    return 1;
1360}
1361
1362static inline halfword tex_aux_valid_delimiter(halfword d)
1363{
1364    return (d && (delimiter_small_family(d) || delimiter_small_character(d) || delimiter_large_family(d) || delimiter_large_character(d))) ? d : null;
1365}
1366
1367static void tex_aux_print_delimiter(halfword d)
1368{
1369    if (delimiter_small_family(d) < 0) {
1370        /*tex This should never happen. */
1371        tex_print_int(-1);
1372    } else if (delimiter_small_family(d) < 16 && delimiter_large_family(d) < 16 && delimiter_small_character(d) < 256 && delimiter_large_character(d) < 256) {
1373        /*tex Traditional tex style. */
1374        int a = delimiter_small_family(d) * 256 + delimiter_small_character(d);
1375        a = a * 0x1000 + delimiter_large_family(d) * 256 + delimiter_large_character(d);
1376        tex_print_format(", code %x", a);
1377    } else if ((delimiter_large_family(d) == 0 && delimiter_large_character(d) == 0) || delimiter_small_character(d) > 65535 || delimiter_large_character(d) > 65535) {
1378        /*tex \LUATEX\ style. */
1379        tex_print_format(", family %x, character %x", delimiter_small_family(d), delimiter_small_character(d));
1380    }
1381}
1382
1383/*tex
1384
1385    The next subroutine will descend to another level of recursion when a subsidiary mlist needs to
1386    be displayed. The parameter |c| indicates what character is to become part of the recursion
1387    history. An empty mlist is distinguished from a missing field, because these are not equivalent
1388    (as explained above).
1389
1390    There are more fields and options that we have to report here so that is a todo for a rainy day 
1391    with nothing else to do. 
1392
1393*/
1394
1395static void tex_aux_display_common_noad(halfword n, int threshold, int max)
1396{
1397    tex_print_node_list(noad_nucleus(n), "nucleus", threshold, max);
1398    tex_print_node_list(noad_supscr(n), "superscript", threshold, max);
1399    tex_print_node_list(noad_subscr(n), "subscript", threshold, max);
1400    tex_print_node_list(noad_supprescr(n), "superprescript", threshold, max);
1401    tex_print_node_list(noad_subprescr(n), "subprescript", threshold, max);
1402    tex_print_node_list(noad_prime(n), "primescript", threshold, max);
1403    tex_print_node_list(noad_new_hlist(n), "newhlist", threshold, max);
1404}
1405
1406static void tex_aux_display_parameter_node(halfword n)
1407{
1408    tex_print_format(", id %i, style %i", parameter_name(n), parameter_style(n));
1409}
1410
1411static void tex_aux_display_choice_noad(halfword n, int threshold, int max)
1412{
1413    switch (node_subtype(n)) { 
1414        case normal_choice_subtype: 
1415            tex_print_node_list(choice_display_mlist(n), "display", threshold, max);
1416            tex_print_node_list(choice_text_mlist(n), "text", threshold, max);
1417            tex_print_node_list(choice_script_mlist(n), "script", threshold, max);
1418            tex_print_node_list(choice_script_script_mlist(n), "scriptscript", threshold, max);
1419            break;
1420        case discretionary_choice_subtype: 
1421            tex_print_format(", class %i", choice_class(n));
1422            tex_print_node_list(choice_pre_break(n), "pre", threshold, max);
1423            tex_print_node_list(choice_post_break(n), "post", threshold, max);
1424            tex_print_node_list(choice_no_break(n), "replace", threshold, max);
1425            break;
1426    }
1427}
1428
1429static void tex_aux_display_simple_noad(halfword n, int threshold, int max)
1430{
1431    if (noad_source(n)) {
1432        tex_print_format(", source %i", noad_source(n));
1433    }
1434    tex_aux_display_common_noad(n, threshold, max);
1435}
1436
1437static void tex_aux_display_radical_noad(halfword n, int threshold, int max) /* todo: more fields */
1438{
1439    if (noad_width(n)) {
1440        tex_print_format(", width %p", noad_width(n));
1441    }
1442    if (radical_height(n)) {
1443        tex_print_format(", height %p", radical_height(n));
1444    }
1445    if (radical_depth(n)) {
1446        tex_print_format(", depth %p", radical_depth(n));
1447    }
1448    if (radical_size(n)) {
1449        tex_print_format(", size %i", radical_size(n));
1450    }
1451    if (noad_source(n) != 0) {
1452        tex_print_format(", source %i", noad_source(n));
1453    }
1454    if (noad_options(n)) {
1455        tex_print_format(", options %x", noad_options(n));
1456    }
1457    if (radical_left_delimiter(n)) { 
1458        tex_print_str(", left");
1459        tex_aux_print_delimiter(radical_left_delimiter(n));
1460    }
1461    if (radical_right_delimiter(n)) { 
1462        tex_print_str(", right");
1463        tex_aux_print_delimiter(radical_right_delimiter(n));
1464    }
1465    if (radical_degree(n)) {
1466        tex_print_node_list(radical_degree(n), "degree", threshold, max);
1467    }
1468    tex_aux_display_common_noad(n, threshold, max);
1469}
1470
1471static void tex_aux_display_accent_noad(halfword n, int threshold, int max) /* todo: more fields */
1472{
1473    halfword top_char = accent_top_character(n);
1474    halfword bottom_char = accent_bottom_character(n);
1475    halfword fraction = accent_fraction(n);
1476    if (fraction) {
1477        tex_print_str(", fraction ");
1478        tex_print_int(fraction);
1479    }
1480    switch (node_subtype(n)) {
1481        case bothflexible_accent_subtype:
1482            if (top_char) {
1483                tex_print_str(", top ");
1484                tex_aux_print_fam_and_char(top_char);
1485            }
1486            if (bottom_char) {
1487                tex_print_str(", bottom ");
1488                tex_aux_print_fam_and_char(bottom_char);
1489            }
1490            if (! (top_char || bottom_char)) {
1491                tex_print_str(", overlay ");
1492                tex_aux_print_fam_and_char(accent_middle_character(n));
1493            }
1494            break;
1495        case fixedtop_accent_subtype:
1496            if (top_char) {
1497                tex_print_str(", fixed top ");
1498                tex_aux_print_fam_and_char(top_char);
1499            }
1500            if (bottom_char) {
1501                tex_print_str(", bottom ");
1502                tex_aux_print_fam_and_char(bottom_char);
1503            }
1504            break;
1505        case fixedbottom_accent_subtype:
1506            if (top_char) {
1507                tex_print_str(", top ");
1508                tex_aux_print_fam_and_char(top_char);
1509            }
1510            if (bottom_char) {
1511                tex_print_str(", fixed bottom ");
1512                tex_aux_print_fam_and_char(bottom_char);
1513            }
1514            break;
1515        case fixedboth_accent_subtype:
1516            if (top_char) {
1517                tex_print_str(", fixed top ");
1518                tex_aux_print_fam_and_char(top_char);
1519            }
1520            if (bottom_char) {
1521                tex_print_str(", fixed bottom ");
1522                tex_aux_print_fam_and_char(bottom_char);
1523            }
1524            break;
1525    }
1526    tex_aux_display_common_noad(n, threshold, max);
1527}
1528
1529static void tex_aux_display_fence_noad(halfword n, int threshold, int max) /* todo: more fields */
1530{
1531    if (noad_height(n)) {
1532        tex_print_format(", height %p", noad_height(n));
1533    }
1534    if (noad_depth(n)) {
1535        tex_print_format(", depth %p", noad_depth(n));
1536    }
1537    if (fence_top_overshoot(n)) {
1538        tex_print_format(", top %p", fence_top_overshoot(n));
1539    }
1540    if (fence_bottom_overshoot(n)) {
1541        tex_print_format(", top %p", fence_bottom_overshoot(n));
1542    }
1543    if (get_noad_main_class(n) != unset_noad_class) {
1544        tex_print_format(", class %i", get_noad_main_class(n));
1545    }
1546    if (get_noad_left_class(n) != unset_noad_class) {
1547        tex_print_format(", leftclass %i", get_noad_left_class(n));
1548    }
1549    if (get_noad_right_class(n) != unset_noad_class) {
1550        tex_print_format(", rightclass %i", get_noad_right_class(n));
1551    }
1552    if (noad_source(n) != 0) {
1553        tex_print_format(", source %i", noad_source(n));
1554    }
1555    if (noad_options(n)) {
1556        tex_print_format(", options %x", noad_options(n));
1557    }
1558    tex_aux_print_delimiter(fence_delimiter(n));
1559    tex_print_node_list(fence_delimiter_top(n), "top", threshold, max);
1560    tex_print_node_list(fence_delimiter_bottom(n), "bottom", threshold, max);
1561}
1562
1563static void tex_aux_display_fraction_noad(halfword n, int threshold, int max) /* todo: more fields */
1564{
1565    halfword leftdelimiter = tex_aux_valid_delimiter(fraction_left_delimiter(n));
1566    halfword rightdelimiter = tex_aux_valid_delimiter(fraction_right_delimiter(n));
1567    tex_print_str(", thickness ");
1568    if (fraction_rule_thickness(n) == preset_rule_thickness) {
1569        tex_print_str("default");
1570    } else {
1571        tex_print_dimension(fraction_rule_thickness(n), pt_unit);
1572    }
1573    if (leftdelimiter) {
1574        tex_print_str(", leftdelimiter ");
1575        tex_aux_print_delimiter(leftdelimiter);
1576    }
1577    if (rightdelimiter) {
1578        tex_print_str(", rightdelimiter ");
1579        tex_aux_print_delimiter(rightdelimiter);
1580    }
1581    if (noad_source(n) != 0) {
1582        tex_print_str(", source ");
1583        tex_print_int(noad_source(n));
1584    }
1585    if (noad_options(n)) {
1586        tex_print_str(", options ");
1587        tex_print_qhex(noad_options(n));
1588    }
1589    tex_print_node_list(fraction_numerator(n), "numerator", threshold, max);
1590    tex_print_node_list(fraction_denominator(n), "denominator", threshold, max);
1591}
1592
1593/*tex
1594
1595    The routines that \TEX\ uses to create mlists are similar to those we have just seen for the
1596    generation of hlists and vlists. But it is necessary to make \quote {noads} as well as nodes,
1597    so the reader should review the discussion of math mode data structures before trying to make
1598    sense out of the following program.
1599
1600    Here is a little routine that needs to be done whenever a subformula is about to be processed.
1601    The parameter is a code like |math_group|.
1602
1603*/
1604
1605static void tex_aux_new_save_level_math(quarterword group)
1606{
1607    halfword direction = math_direction_par;
1608    saved_math_initialize();
1609    saved_math_direction = lmt_dir_state.text_dir_ptr;
1610    lmt_save_state.save_stack_data.ptr += saved_math_n_of_records;
1611    lmt_dir_state.text_dir_ptr = tex_new_dir(normal_dir_subtype, direction);
1612    tex_new_save_level(group);
1613    update_tex_par_direction(direction);
1614    update_tex_text_direction(direction);
1615}
1616
1617static void tex_aux_push_math(quarterword group, int style, int outerstyle)
1618{
1619    halfword main_style = cur_list.math_main_style;
1620    halfword parent_style = cur_list.math_style;
1621    if (math_direction_par != text_direction_par) {
1622        cur_list.math_dir = 1;
1623    }
1624    cur_list.math_begin = math_begin_class_par;
1625    cur_list.math_end = math_end_class_par;
1626    if (outerstyle >= 0) {
1627        cur_list.math_main_style = outerstyle;
1628        main_style = outerstyle;
1629        cur_list.math_parent_style = outerstyle;
1630        parent_style = outerstyle;
1631    }
1632    tex_push_nest();
1633    cur_list.mode = inline_mmode;
1634    cur_list.incomplete_noad = null;
1635    cur_list.math_style = style;
1636    cur_list.math_main_style = main_style;
1637    cur_list.math_parent_style = parent_style;
1638    cur_list.math_scale = internal_math_scale_par;
1639    tex_aux_new_save_level_math(group);
1640    update_tex_math_left_class(unset_noad_class);
1641    update_tex_math_right_class(unset_noad_class);
1642}
1643
1644static void tex_aux_enter_inline_math(int style, int where)
1645{
1646    (void) where;
1647    tex_aux_push_math(math_inline_group, style, style);
1648    update_tex_family(0, unused_math_family);
1649    if (every_math_par) {
1650        tex_begin_token_list(every_math_par, every_math_text);
1651    }
1652}
1653
1654static void tex_aux_enter_display_math(halfword cmd, int where);
1655
1656/*tex
1657
1658    We get into math mode from horizontal mode when a |$| (i.e., a |math_shift| character) is
1659    scanned. We must check to see whether this |$| is immediately followed by another, in case
1660    display math mode is called for.
1661
1662*/
1663
1664void tex_run_math_initialize(void)
1665{
1666    switch(cur_cmd) {
1667        case math_shift_cmd:
1668            /*tex |get_x_token| would fail on |\ifmmode|! */
1669            lmt_nest_state.math_mode = 1;
1670            tex_get_token();
1671            lmt_nest_state.math_mode = 0;
1672            if (cur_cmd == math_shift_cmd && cur_list.mode > nomode) {
1673                tex_aux_enter_display_math(math_shift_cmd, 1);
1674            } else {
1675                tex_back_input(cur_tok);
1676                tex_aux_enter_inline_math(text_style, 1);
1677            }
1678            break;
1679        case math_shift_cs_cmd:
1680            if (cur_chr == begin_math_mode_code) {
1681                tex_aux_enter_inline_math(tex_scan_math_style_identifier(0, 0), 2);
1682            } else if (cur_chr == begin_display_math_code && cur_list.mode > nomode) {
1683                tex_aux_enter_display_math(begin_display_math_code, 2);
1684            } else if (cur_chr == begin_inline_math_code) {
1685                tex_aux_enter_inline_math(text_style, 3);
1686            } else {
1687                tex_you_cant_error("math shift 1");
1688            }
1689            break;
1690        default:
1691            tex_you_cant_error("math shift 2");
1692            break;
1693    }
1694}
1695
1696/*tex
1697
1698    We get into ordinary math mode from display math mode when |\eqno| or |\leqno| appears. In such
1699    cases |cur_chr| will be 0 or~1, respectively; the value of |cur_chr| is placed onto |save_stack|
1700    for safe keeping. When \TEX\ is in display math mode, |cur_group = math_shift_group|, so it is
1701    not necessary for the |start_eq_no| procedure to test for this condition.
1702
1703*/
1704
1705void tex_run_math_equation_number(void) {
1706    if (cur_group == math_display_group) {
1707        saved_equation_number_initialize();
1708        saved_equation_number_location = cur_chr;
1709        lmt_save_state.save_stack_data.ptr += saved_equation_number_n_of_records;
1710        tex_aux_enter_inline_math(text_style, 4);
1711    } else {
1712        tex_off_save();
1713    }
1714}
1715
1716/*tex
1717
1718    Subformulas of math formulas cause a new level of math mode to be entered, on the semantic nest
1719    as well as the save stack. These subformulas arise in several ways: (1)~A left brace by itself
1720    indicates the beginning of a subformula that will be put into a box, thereby freezing its glue
1721    and preventing line breaks. (2)~A subscript or superscript is treated as a subformula if it is
1722    not a single character; the same applies to the nucleus of things like |\underline|. (3)~The
1723    |\left| primitive initiates a subformula that will be terminated by a matching |\right|. The
1724    group codes placed on |save_stack| in these three cases are |math_group|, |math_group|, and
1725    |math_left_group|, respectively.
1726
1727    Here is the code that handles case (1); the other cases are not quite as trivial, so we shall
1728    consider them later.
1729
1730*/
1731
1732void tex_run_math_left_brace(void)
1733{
1734    if (math_grouping_mode_par) {
1735        /*tex This is an experiment. Some tracing has to be adapted probably. */
1736        tex_new_save_level(math_simple_group);
1737        update_tex_internal_math_style(cur_mode == mmode ? cur_list.math_style : -1);
1738        update_tex_internal_math_scale(cur_mode == mmode ? cur_list.math_scale : -1);
1739    } else {
1740        halfword q = tex_new_node(math_char_node, 0);
1741        halfword n = tex_new_node(simple_noad, ordinary_noad_subtype);
1742        tex_tail_append(n);
1743        noad_nucleus(n) = q;
1744        tex_back_input(cur_tok);
1745        tex_aux_scan_math(q, cur_list.math_style, 0, 0, 0, 0, unset_noad_class, unset_noad_class);
1746    }
1747}
1748
1749/*tex
1750
1751    If the inline directions of |\pardir| and |\mathdir| are opposite, then this function will
1752    return true. Discovering that fact is somewhat odd because it needs traversal of the
1753    |save_stack|. The occurance of displayed equations is weird enough that this is probably still
1754    better than having yet another field in the |input_stack| structures.
1755
1756    None of this makes much sense if the inline direction of either one of |\pardir| or |\mathdir|
1757    is vertical, but in that case the current math machinery is ill suited anyway so I do not
1758    bother to test that. We now just return the direction.
1759
1760*/
1761
1762static int tex_aux_pre_math_par_direction(void)
1763{
1764    return tex_located_save_value(internal_integer_location(par_direction_code));
1765}
1766
1767/*tex
1768
1769    When we enter display math mode, we need to call |line_break| to process the partial paragraph
1770    that has just been interrupted by the display. Then we can set the proper values of
1771    |display_width| and |display_indent| and |pre_display_size|.
1772
1773    In 2025 \LUATEX\ there is a patch for display lists that have only dir changes while before we 
1774    only checked for par nodes;. This because text direction nodes get injected automatically. This 
1775    relates to display math being abused for determining the length of the preceding line as well 
1776    as equation numbers. In \LUAMETATEX\ we don't have the side effect of (empty) equation numbers. 
1777    
1778    We also have indentation to deal with (two models) and then need to clean up a list, and not 
1779    just some last node (I'm not sure if \LUATEX\ does that right now but will check it). So, in 
1780    \LUAMETATEX\ we control this by some parameter. 
1781 
1782    Keep in mind that we can't test interferences because in \CONTEXT\ we don't use display math 
1783    this way. There can also me more kind of nodes. We never use these features. For testing 
1784    purposes we have a parameter that controls this. 
1785
1786*/
1787
1788static void tex_aux_enter_display_math(halfword cmd, int where)
1789{
1790    (void) where;
1791    if (math_display_mode_par) {
1792        tex_aux_push_math(math_inline_group, display_style, display_style);
1793        cur_list.math_mode = cmd; 
1794        cur_list.mode = inline_mmode; /* new */
1795        update_tex_family(0, unused_math_family);
1796        if (every_display_par) {
1797            tex_begin_token_list(every_display_par, every_display_text);
1798        }
1799    } else { 
1800        /*tex new or partial |pre_display_size| */
1801        scaled size;
1802        /*tex new |display_width| */
1803        scaled width;
1804        /*tex new |display_indent| */
1805        scaled indent;
1806        /*tex
1807            Deal with |\noindent$$| or |$${ }$$| or the 2nd of |$${ }$$| |$${ }$$|. 
1808        */
1809        if (cur_list.head == cur_list.tail) {
1810            /* temp nodes */
1811            tex_pop_nest();
1812            size = - max_dimension;
1813        } else if (empty_paragraph_mode_par && ! node_next(cur_list.tail) && tex_is_effectively_empty(node_next(cur_list.head), empty_paragraph_mode_par)) {
1814            /*tex
1815                |resume_after_display| inserts a |par_node|, but if there is another display
1816                immediately following, we have to get rid of that node.
1817            */
1818            tex_flush_node_list(node_next(cur_list.head));
1819            node_next(cur_list.head) = null;
1820            cur_list.tail = cur_list.head;
1821            tex_pop_nest(); /* pops only head */
1822            size = - max_dimension;
1823        } else {
1824            tex_line_break(math_display_group, math_par_context, 1);
1825         // size = tex_actual_box_width(lmt_linebreak_state.just_box, tex_x_over_n(tex_get_font_em_width(cur_font_par), scaling_factor) * math_pre_display_gap_factor_par);
1826            size = tex_actual_box_width(lmt_linebreak_state.just_box, scaledround((tex_get_font_em_width(cur_font_par) / scaling_factor_double) * math_pre_display_gap_factor_par));
1827        }
1828        /*tex
1829            Now we are in vertical mode, working on the list that will contain the display. A displayed
1830            equation is considered to be three lines long, so we calculate the length and offset of line
1831            number |prev_graf + 2|.
1832        */
1833        if (par_shape_par) {
1834            /*tex scope of paragraph shape specification */
1835            int n = tex_get_specification_count(par_shape_par);
1836            if (n > 0) {
1837                if (cur_list.prev_graf + 2 < n) {
1838                    n = cur_list.prev_graf + 2;
1839                }
1840                indent = tex_get_specification_indent(par_shape_par, n) ;
1841                width = tex_get_specification_width(par_shape_par, n);
1842                indent = swap_parshape_indent(pre_display_direction_par, indent, width);
1843            } else {
1844                width = hsize_par;
1845                indent = 0;
1846            }
1847        } else if ((hang_indent_par != 0) && (((hang_after_par >= 0) && (cur_list.prev_graf + 2 > hang_after_par)) || (cur_list.prev_graf + 1 < -hang_after_par))) {
1848            halfword hangindent = swap_hang_indent(pre_display_direction_par, hang_indent_par);
1849            width = hsize_par - abs(hangindent);
1850            indent = hangindent > 0 ? hangindent : 0;
1851        } else {
1852            width = hsize_par;
1853            indent = 0;
1854        }
1855        tex_aux_push_math(math_display_group, display_style, display_style);
1856        cur_list.mode = mmode;
1857        update_tex_family(0, unused_math_family);
1858        update_tex_pre_display_size(size);
1859        update_tex_display_width(width);
1860        update_tex_display_indent(indent);
1861        update_tex_pre_display_direction(tex_aux_pre_math_par_direction());
1862        if (every_display_par) {
1863            tex_begin_token_list(every_display_par, every_display_text);
1864        }
1865        if (lmt_nest_state.nest_data.ptr == 1) {
1866            tex_build_page(before_display_page_context, 0);
1867        }
1868    }
1869}
1870
1871/*tex
1872
1873    The next routine parses all variations of a delimiter code. The |extcode| tells what syntax form
1874    to use (\TEX\ or \LUATEX) , the |doclass| tells whether or not read a math class also (for
1875    |\delimiter| c.s.). The class is passed on for conversion to |\mathchar|.
1876
1877*/
1878
1879static delcodeval tex_aux_scan_extdef_del_code(int extcode, int doclass)
1880{
1881    delcodeval d = tex_no_del_code();
1882    switch (extcode) {
1883        case tex_mathcode:
1884            /*tex This is the easiest: |\delcode|,*/
1885            {
1886                halfword v = tex_scan_integer(0, NULL, NULL);
1887                /*tex |MFCCFCC| or |FCCFCC| */
1888                if (doclass) {
1889                    d.small.class_value = (short) (v / 0x1000000);
1890                    v = (v & 0xFFFFFF);
1891                }
1892                if (v > 0xFFFFFF) {
1893                    tex_handle_error(
1894                        normal_error_type,
1895                        "Invalid delimiter code",
1896                        "I'm going to use 0 instead of that illegal code value."
1897                    );
1898                    v = 0;
1899                }
1900                d.small.family_value = (short) (v / 0x100000);
1901                d.small.character_value = (v % 0x100000) / 0x1000;
1902                d.large.family_value = (short) ((v & 0xFFF) / 0x100);
1903                d.large.character_value = (v % 0x100);
1904                /* */
1905                d.small.character_value = math_character_part(d.small.character_value);
1906                d.large.character_value = math_character_part(d.large.character_value);
1907            }
1908            break;
1909        case umath_mathcode:
1910            /*tex |\Udelcode|: |<0-7><0-0xFF><0-0x10FFFF>| or |<0-0xFF><0-0x10FFFF>| */
1911            {
1912                if (doclass) {
1913                    d.small.class_value = (short) tex_scan_math_class_number(0);
1914                }
1915                d.small.family_value = (short) tex_scan_math_family_number();
1916                d.small.character_value = tex_scan_math_char_number();
1917                if (d.small.family_value < 0 || d.small.family_value > max_math_family_index) {
1918                    tex_handle_error(
1919                        normal_error_type,
1920                        "Invalid delimiter family",
1921                        "I'm going to use family 0 instead."
1922                    );
1923                    d.small.family_value = 0;
1924                    d.small.character_value = 0;
1925                }
1926            }
1927            break;
1928        default:
1929            /*tex Something's gone wrong! */
1930            tex_confusion("unknown extcode, case 1");
1931            break;
1932    }
1933    d.large.class_value = d.small.class_value;
1934    return d;
1935}
1936
1937void tex_scan_extdef_del_code(int level, int extcode)
1938{
1939    delcodeval d;
1940    int chr = tex_scan_char_number(0);
1941    tex_scan_optional_equals();
1942    d = tex_aux_scan_extdef_del_code(extcode, 0);
1943    tex_set_del_code(chr, d, (quarterword) level);
1944}
1945
1946mathdictval tex_scan_mathdict(void)
1947{
1948    mathdictval d = tex_no_dict_code(); /* use this one directly */
1949    d.properties = (unsigned short) tex_scan_math_properties_number();
1950    d.group = (unsigned short) tex_scan_math_group_number();
1951    d.index = (unsigned int) tex_scan_math_index_number();
1952    return d;
1953}
1954
1955/*tex
1956
1957    The \LUATEX\ number def variant is gone so we no longer accent |"<6bits><6bits><20bits>| where 
1958    the largest numeric value is $2^32-1$, and the top of bit 21 can't be used as it contains 
1959    invalid USV's and |scan_integer| won't accept families 128-255 because these use bit 32 (so 
1960    plenty of reasons to drop it).
1961
1962*/
1963
1964mathcodeval tex_scan_mathchar(int extcode)
1965{
1966    mathcodeval d = tex_no_math_code(); /* use this one directly */
1967    switch (extcode) {
1968        case tex_mathcode:
1969            /*tex |"<4bits><4bits><8bits>| */
1970            {
1971                halfword v = tex_scan_integer(0, NULL, NULL);
1972                if (v >= 0) {
1973                    if (v > 0xFFFF) {
1974                        v = 0xFFFF;
1975                    }
1976                    d.class_value = (short) math_old_class_part(v);
1977                    d.family_value = (short) math_old_family_part(v);
1978                    d.character_value = math_old_character_part(v);
1979                }
1980            }
1981            break;
1982        case umath_mathcode:
1983            /*tex |"<6bits>"<6bits>"<20bits>| */
1984            {
1985                d.class_value = (short) tex_scan_math_class_number(0);
1986                d.family_value = (short) tex_scan_math_family_number();
1987                d.character_value = tex_scan_math_char_number();
1988            }
1989            break;
1990        default:
1991            tex_confusion("unknown extcode, case 2");
1992            break;
1993    }
1994    if (d.class_value < 0 || d.character_value > max_math_character_code || d.class_value > max_math_class_code || d.family_value > max_math_family_index) {
1995        tex_handle_error(
1996            normal_error_type,
1997            "Invalid math code",
1998            "I'm going to use 0 instead of that illegal code value."
1999        );
2000        d.class_value = 0;
2001        d.family_value = 0;
2002        d.character_value = 0;
2003    }
2004    return d;
2005}
2006
2007halfword tex_new_math_spec(mathcodeval m, quarterword code)
2008{
2009    halfword s = tex_new_node(math_spec_node, code);
2010    math_spec_class(s) = (singleword) m.class_value;
2011    math_spec_family(s) = (singleword) m.family_value;
2012    math_spec_character(s) = m.character_value;
2013    return s;
2014}
2015
2016halfword tex_new_math_dict_spec(mathdictval d, mathcodeval m, quarterword code)
2017{
2018    halfword s = tex_new_node(math_spec_node, code);
2019    math_spec_class(s) = (singleword) m.class_value;
2020    math_spec_family(s) = (singleword) m.family_value;
2021    math_spec_character(s) = m.character_value;
2022    math_spec_properties(s) = (quarterword) d.properties;
2023    math_spec_group(s) = (quarterword) d.group;
2024    math_spec_index(s) = d.index;
2025    return s;
2026}
2027
2028mathcodeval tex_get_math_spec(halfword s)
2029{
2030    mathcodeval m = tex_no_math_code();
2031    if (s) {
2032        m.class_value = math_spec_class(s);
2033        m.family_value = math_spec_family(s);
2034        m.character_value = math_spec_character(s);
2035    }
2036    return m;
2037}
2038
2039mathdictval tex_get_math_dict(halfword s)
2040{
2041    mathdictval d = tex_no_dict_code();
2042    if (s) {
2043        d.properties = math_spec_properties(s);
2044        d.group = math_spec_group(s);
2045        d.index = math_spec_index(s);
2046    }
2047    return d;
2048}
2049
2050halfword tex_scan_math_spec(int optional_equal)
2051{
2052    mathcodeval m;
2053    if (optional_equal) {
2054        tex_scan_optional_equals();
2055    }
2056    m = tex_scan_mathchar(umath_mathcode);
2057    return tex_new_math_spec(m, mathspec_mathcode);
2058}
2059
2060void tex_scan_extdef_math_code(int level, int extcode)
2061{
2062    mathcodeval d;
2063    int chr = tex_scan_char_number(0);
2064    tex_scan_optional_equals();
2065    d = tex_scan_mathchar(extcode);
2066    tex_set_math_code(chr, d, (quarterword) level);
2067}
2068
2069/*tex This reads in a delcode when actually a mathcode is needed. */
2070
2071mathcodeval tex_scan_delimiter_as_mathchar(int extcode)
2072{
2073    delcodeval dval = tex_aux_scan_extdef_del_code(extcode, 1);
2074    return dval.small;
2075}
2076
2077/*tex
2078
2079    Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad are broken down into subfields
2080    called |type| and either |math_list| or |(math_fam, math_character)|. The job of |scan_math| is
2081    to figure out what to place in one of these principal fields; it looks at the subformula that
2082    comes next in the input, and places an encoding of that subformula into a given word of |mem|.
2083
2084    already prepared: every [component, degree, radical, over, under, accent, prime, subscript,
2085    superscript]
2086
2087    toks      : every_subscript_par
2088    toks_text : every_subscipt_text or every_math_text (for tracing)
2089
2090*/
2091
2092/*tex 
2093    For some reason |$\char44$| gives an undefined |$| when we made that character active in math. 
2094*/
2095
2096static void tex_aux_report_active(int where, const char *what, int code, int character) 
2097{
2098    tex_begin_diagnostic();
2099    tex_print_format("[active: location %i, %s, code %i, char %i]",where, what, code, character);
2100    tex_end_diagnostic();
2101}
2102
2103static void tex_aux_append_math_char(mathcodeval mval, mathdictval dval, int automatic);
2104
2105int tex_check_active_math_char(int character)
2106{
2107    halfword code = tex_get_am_code(character);
2108    if (code) {
2109        switch (code) {
2110            case alignment_tab_cmd:              
2111            case superscript_cmd:                
2112            case subscript_cmd:                  
2113            case letter_cmd:                     
2114            case other_char_cmd:
2115            case active_char_cmd:
2116                cur_cmd = code;
2117                cur_chr = character;
2118                cur_tok = token_val(cur_cmd, cur_chr);
2119                if (tracing_commands_par >= 4) {
2120                    switch (code) {
2121                        case alignment_tab_cmd:              
2122                        case superscript_cmd:                
2123                        case subscript_cmd:                  
2124                            tex_aux_report_active(4, "control", code, character);
2125                            break;
2126                        case letter_cmd:                     
2127                        case other_char_cmd:
2128                            tex_aux_report_active(4, "inject", code, character);
2129                            break;
2130                        case active_char_cmd:
2131                            tex_aux_report_active(4, "active", code, character);
2132                            break;
2133                    }
2134                }
2135                return 1;
2136            default: 
2137                if (tracing_commands_par >= 4) {
2138                    tex_aux_report_active(4, "ignore", code, character);
2139                }
2140                return 1;
2141        }
2142    } else { 
2143        return 0;
2144    }
2145}
2146
2147int tex_pass_active_math_char(int character)
2148{
2149    halfword code = tex_get_am_code(character);
2150    if (code) {
2151        return 1;
2152    } else { 
2153        return 0;
2154    }
2155}
2156
2157static int tex_aux_scan_active_math_char(mathcodeval *mval, int where)
2158{
2159    halfword character = mval->character_value;
2160    halfword code = tex_get_am_code(character);
2161    if (code) {
2162        switch (code) {
2163            case alignment_tab_cmd:              
2164            case superscript_cmd:                
2165            case subscript_cmd:                  
2166                cur_cmd = code;
2167                cur_chr = character;
2168                cur_tok = token_val(cur_cmd, cur_chr);
2169                tex_back_input(cur_tok);
2170                if (tracing_commands_par >= 4) {
2171                    tex_aux_report_active(where, "control", code, character);
2172                }
2173                return 1;
2174            case letter_cmd:                     
2175            case other_char_cmd:
2176                cur_cmd = code;
2177                cur_chr = character;
2178                cur_tok = token_val(cur_cmd, cur_chr);
2179                if (tracing_commands_par >= 4) {
2180                    tex_aux_report_active(where, "inject", code, character);
2181                }
2182                return 0;
2183            case active_char_cmd:
2184                /*tex 
2185                    We reset the code so that we don't get a loop, which means that the macro that 
2186                    gets invoked has to set the amcode again if needed. 
2187                */
2188                tex_set_am_code(character, other_char_cmd, cur_level);
2189                goto COMMON;
2190            //  cur_cs = tex_active_to_cs(character, 1);
2191            //  cur_cmd = eq_type(cur_cs);
2192            //  cur_chr = eq_value(cur_cs);
2193            //  tex_x_token();
2194            //  tex_back_input(cur_tok);
2195            //  if (tracing_commands_par >= 4) {
2196            //      tex_aux_report_active(where, "active", code, character);
2197            //  }
2198            //  return 1;
2199            default: 
2200                if (tracing_commands_par >= 4) {
2201                    tex_aux_report_active(where, "ignore", code, character);
2202                }
2203                return 1;
2204        }
2205    } else if (mval->class_value == active_math_class_value) {
2206        /*tex We might eventually drop this feature in favor of the amcode. */
2207      COMMON:
2208        cur_cs = tex_active_to_cs(character, 1);
2209        cur_cmd = eq_type(cur_cs);
2210        cur_chr = eq_value(cur_cs);
2211        tex_x_token();
2212        tex_back_input(cur_tok);
2213        if (tracing_commands_par >= 4) {
2214            tex_aux_report_active(where, "active", code, character);
2215        }
2216        return 1;
2217    } else { 
2218     // if (tracing_commands_par >= 4) {
2219     //     tex_aux_report_active(where, "keep", code, mval->character_value);
2220     // }
2221        return 0;
2222    }
2223}
2224
2225static int tex_aux_scan_math(halfword target, halfword style, int usetextfont, halfword toks, halfword toks_text, int nocomponent, halfword cls, halfword all)
2226{
2227    mathcodeval mval = tex_no_math_code();
2228    mathdictval dval = tex_no_dict_code();
2229    lmt_math_state.last_atom = cls;
2230  RESTART:
2231    do {
2232        tex_get_x_token();
2233    } while (cur_cmd == spacer_cmd || cur_cmd == relax_cmd);
2234//  RESWITCH:
2235    switch (cur_cmd) {
2236        case char_number_cmd:
2237            /* The |\glyph| variant is accepted but no keywords here. */
2238            cur_chr = tex_scan_char_number(0);
2239            // fall through 
2240        case letter_cmd:
2241        case other_char_cmd:
2242        case char_given_cmd:
2243            mval = tex_get_math_code(cur_chr);
2244            if (tex_aux_scan_active_math_char(&mval, 1)) { 
2245                goto RESTART; /* rescan pushed back token */
2246            } else {
2247                dval = tex_fake_math_dict(mval.character_value);
2248                break;
2249            }
2250    //  case char_number_cmd:
2251    //      /* The |\glyph| variant is accepted but no keywords here. */
2252    //      cur_chr = tex_scan_char_number();
2253    //      cur_cmd = char_given_cmd;
2254    //      goto RESWITCH;
2255        case math_char_number_cmd:
2256            switch (cur_chr) {
2257                case math_char_number_code:
2258                    mval = tex_scan_mathchar(tex_mathcode);
2259                 // dval = tex_fake_math_dict(mval.character_value);
2260                    break;
2261                case math_xchar_number_code:
2262                    mval = tex_scan_mathchar(umath_mathcode);
2263                    dval = tex_fake_math_dict(mval.character_value);
2264                    break;
2265                case math_char_ignore_code:
2266                    break;
2267                default:
2268                    tex_confusion("scan math char, case 1");
2269                    break;
2270            }
2271            break;
2272        case mathspec_cmd:
2273            mval = tex_get_math_spec(cur_chr);
2274            dval = tex_get_math_dict(cur_chr);
2275            break;
2276        case delimiter_number_cmd:
2277            switch (cur_chr) {
2278                case math_delimiter_code:
2279                    mval = tex_scan_delimiter_as_mathchar(tex_mathcode);
2280                    break;
2281                case math_udelimiter_code:
2282                    mval = tex_scan_delimiter_as_mathchar(umath_mathcode);
2283                    break;
2284                default:
2285                    tex_confusion("scan math char, case 2");
2286                    break;
2287            }
2288            break;
2289		case math_component_cmd:
2290			if (nocomponent) {
2291                goto DEFAULT;
2292            } else {
2293                saved_math_group_initialize();
2294                saved_math_group_all_class = unset_noad_class;
2295                saved_math_group_pointer = target;
2296			    lmt_save_state.save_stack_data.ptr += saved_math_group_n_of_records;
2297			    tex_aux_push_math(math_component_group, style, -1);
2298                if (usetextfont) {
2299                    tex_set_math_text_font(style, usetextfont);
2300                }
2301    		    tex_aux_math_math_component(cur_list.tail, 0);
2302                tex_finish_math_group();
2303    			return 1;
2304		    }
2305        case left_brace_cmd:
2306            goto SCAN_SUBFORMULA;
2307        default:
2308            /*tex
2309                The pointer |p| is placed on |save_stack| while a complex subformula is being
2310                scanned.
2311            */
2312          DEFAULT:
2313            tex_back_input(cur_tok);
2314            tex_scan_left_brace();
2315          SCAN_SUBFORMULA:
2316            saved_math_group_initialize();
2317            saved_math_group_all_class = all;
2318            saved_math_group_pointer = target;
2319            lmt_save_state.save_stack_data.ptr += saved_math_group_n_of_records;
2320            tex_aux_push_math(math_group, style, -1);
2321            toks = every_math_atom_par;
2322            toks_text = every_math_atom_text; 
2323            if (toks) {
2324                tex_begin_token_list(toks, (quarterword) toks_text);
2325            }
2326            if (usetextfont) {
2327                tex_set_math_text_font(style, usetextfont);
2328            }
2329            return 1;
2330    }
2331    node_type(target) = math_char_node; /* The sizes can best match! */
2332    if (glyph_options_par & glyph_option_no_italic_correction) {
2333        math_kernel_node_set_option(target, math_kernel_no_italic_correction);
2334    }
2335    if (glyph_options_par & glyph_option_no_left_kern) {
2336        math_kernel_node_set_option(target, math_kernel_no_left_pair_kern);
2337    }
2338    if (glyph_options_par & glyph_option_no_right_kern) {
2339        math_kernel_node_set_option(target, math_kernel_no_right_pair_kern);
2340    }
2341    tex_aux_set_math_char(target, &mval, &dval);
2342    return 0;
2343}
2344
2345/*tex
2346
2347    The |append_math_char| procedure creates a new noad appropriate to a given math code, and
2348    appends it to the current mlist. However, if the math code is sufficiently large, the |cur_chr|
2349    is treated as an active character and nothing is appended.
2350
2351*/
2352
2353static void tex_aux_append_math_accent(mathcodeval mval, mathdictval dval)
2354{
2355    halfword accent = tex_new_node(accent_noad, bothflexible_accent_subtype);
2356    quarterword subtype = ordinary_noad_subtype;
2357    tex_tail_append(accent);
2358    if (! (mval.character_value == 0 && mval.family_value == 0)) {
2359        halfword q = tex_new_node(math_char_node, 0);
2360        subtype = tex_aux_set_math_char(q, &mval, &dval);
2361        accent_top_character(accent) = q;
2362    }
2363    {
2364        halfword q = tex_new_node(math_char_node, subtype);
2365        noad_nucleus(accent) = q;
2366        tex_aux_scan_math(q, tex_math_style_variant(cur_list.math_style, math_parameter_accent_variant), 0, 0, 0, 0, unset_noad_class, unset_noad_class);
2367    }
2368}
2369
2370/*tex 
2371    Fences are actually constructs and middle sort of interferes here: we keep a sort of flat fence
2372    sequence so middle ends a group and opens a new one. 
2373
2374*/
2375
2376static void tex_aux_append_math_fence(halfword fence, quarterword mathclass)
2377{
2378    switch (mathclass) {
2379        case open_noad_subtype:
2380            {
2381                tex_aux_push_math(math_fence_group, cur_list.math_style, -1);
2382                node_subtype(fence) = left_fence_side;
2383                node_next(cur_list.head) = fence;
2384                cur_list.tail = fence;
2385                cur_list.delimiter = fence;
2386            }
2387            break;
2388        case close_noad_subtype:
2389            {
2390                halfword q = tex_aux_finish_math_list(fence);
2391                halfword n = tex_new_node(simple_noad, fenced_noad_subtype);
2392                halfword l = tex_new_node(sub_mlist_node, 0);
2393                tex_aux_unsave_math();
2394                tex_tail_append(n);
2395                node_subtype(fence) = right_fence_side;
2396                noad_nucleus(n) = l;
2397                noad_options(n) |= noad_option_unpack_list;
2398                kernel_math_list(noad_nucleus(n)) = q;
2399            }
2400            break;
2401        case middle_noad_subtype:
2402            { 
2403                halfword q = tex_aux_finish_math_list(fence);
2404                tex_aux_unsave_math();
2405                tex_aux_push_math(math_fence_group, cur_list.math_style, -1);
2406                node_subtype(fence) = middle_fence_side;
2407                node_next(cur_list.head) = q;
2408                cur_list.tail = fence;
2409                cur_list.delimiter = fence;
2410            }
2411            break;
2412    }
2413}
2414
2415static void tex_aux_append_math_fence_val(mathcodeval mval, mathdictval dval, quarterword mathclass)
2416{
2417    halfword fence = tex_new_node(fence_noad, middle_fence_side);
2418    halfword delimiter = tex_new_node(delimiter_node, mval.class_value);
2419    (void) dval; /* maybe todo */
2420    fence_delimiter(fence) = delimiter;
2421    delimiter_small_family(delimiter) = mval.family_value;
2422    delimiter_small_character(delimiter) = mval.character_value;
2423    delimiter_large_family(delimiter) = mval.family_value;
2424    delimiter_large_character(delimiter) = mval.character_value;
2425    tex_set_noad_classes(fence, mval.class_value);
2426    /* todo : share the next three with the regular fences */
2427    noad_options(fence) |= noad_option_no_check;
2428    if (mathclass == middle_noad_subtype && cur_group != math_fence_group) { 
2429        tex_aux_append_math_fence_val(tex_no_math_code(), tex_no_dict_code(), open_noad_subtype);
2430    }
2431    tex_aux_append_math_fence(fence, mathclass);
2432}
2433
2434static void tex_trace_continuation_atom(const char *s)
2435{
2436    if (tracing_math_par >= 2) {
2437        tex_begin_diagnostic();
2438        tex_print_format("[math: continuation atom %s]", s);
2439        tex_end_diagnostic();
2440    }
2441}
2442
2443halfword tex_new_math_continuation_atom(halfword node, halfword attr)
2444{
2445    if (! node) {
2446        /*tex We create a continuation atom. */
2447        halfword list = tex_new_node(sub_mlist_node, 0);
2448        node = tex_new_node(simple_noad, ordinary_noad_subtype);
2449        noad_nucleus(node) = list;
2450        tex_trace_continuation_atom("added");
2451    } else if (! tex_math_scripts_allowed(node)) { 
2452        tex_trace_continuation_atom("not allowed");
2453        return node; 
2454    } else { 
2455        tex_trace_continuation_atom("updated");
2456    }
2457    if (math_double_script_mode_par >= 0) { 
2458        /* todo: a flag that we keep classes */
2459        int options = (math_double_script_mode_par >> 24) & 0xFF;
2460        node_subtype(node) = (math_double_script_mode_par >> 16) & 0xFF;
2461        noad_class_left(node) = (math_double_script_mode_par >> 8) & 0xFF;
2462        noad_class_right(node) = (math_double_script_mode_par >> 0) & 0xFF;
2463        if (options & inherit_class_double_atom_option) {
2464            noad_options(node) |= noad_option_inherit_class; 
2465        }
2466        if (options & discard_shape_kern_double_atom_option) {
2467            noad_options(node) |= noad_option_discard_shape_kern; 
2468        }
2469        if (options & realign_scripts_double_atom_option) {
2470            noad_options(node) |= noad_option_realign_scripts; 
2471        }
2472        if (options & reorder_double_pre_script_atom_option) {
2473            noad_options(node) |= noad_option_reorder_pre_scripts; 
2474        }
2475    }
2476    noad_options(node) |= noad_option_continuation;
2477    if (attr) {
2478        tex_attach_attribute_list_copy(node, attr);
2479    }
2480    return node; 
2481}
2482
2483static halfword tex_math_double_atom(int followup)
2484{
2485    halfword tail = tex_new_math_continuation_atom(null, cur_list.tail); /*tex We make a new one. */
2486    tex_math_set_scripts_options(tail);
2487    if (followup && ! has_noad_option_continuation(cur_list.tail)) {
2488        noad_options(cur_list.tail) |= noad_option_continuation_head;
2489        noad_options(cur_list.tail) |= noad_option_continuation_kernel;
2490     // printf("SET DA %i\n",cur_list.tail);
2491    }
2492    tex_tail_append(tail);
2493    return tail; 
2494}
2495
2496static void tex_aux_append_math_char(mathcodeval mval, mathdictval dval, int automatic)
2497{
2498    if (tex_aux_scan_active_math_char(&mval, 2)) { 
2499        return; /* rescan pushed back token */
2500    } else { 
2501        if (automatic && tex_math_has_class_option(mval.class_value, auto_inject_class_option)) {
2502            switch (mval.class_value) { 
2503                case accent_noad_subtype:
2504                    tex_aux_append_math_accent(mval, dval);
2505                    return;
2506                case open_noad_subtype:
2507                case close_noad_subtype:
2508                case middle_noad_subtype:
2509                    tex_aux_append_math_fence_val(mval, dval, mval.class_value);
2510                    return;
2511            }
2512        } 
2513        {
2514            halfword q = tex_new_node(math_char_node, 0);
2515            halfword tail = cur_list.tail;
2516            if (mval.class_value == prime_noad_subtype && tex_math_scripts_allowed(tail)) {
2517                tex_aux_set_math_char(q, &mval, &dval);
2518                if (noad_prime(tail)) {
2519                    switch (node_type(noad_prime(tail))) { 
2520                        case math_char_node:
2521                            {
2522                                halfword n = tex_new_node(sub_mlist_node, 0);
2523                                halfword a = tex_new_node(simple_noad, ordinary_noad_subtype);
2524                                halfword b = tex_new_node(simple_noad, ordinary_noad_subtype);
2525                                noad_nucleus(a) = noad_prime(tail);
2526                                noad_nucleus(b) = q;
2527                                tex_couple_nodes(a, b);
2528                                kernel_math_list(n) = a;
2529                                noad_prime(tail) = n;
2530                                break;
2531                            }
2532                        case sub_mlist_node:
2533                            {
2534                                halfword n = tex_new_node(simple_noad, ordinary_noad_subtype);
2535                                noad_nucleus(n) = q;
2536                                tex_couple_nodes(tex_tail_of_node_list(kernel_math_list(noad_prime(tail))),n);
2537                                break;
2538                            }
2539                        default:
2540                            {
2541                                /* todo: message that we discard */
2542                                tex_flush_node_list(noad_prime(tail));
2543                                noad_prime(tail) = q;
2544                                break;
2545                            }
2546                    }
2547                } else { 
2548                    noad_prime(tail) = q;
2549                    if (! noad_script_order(tail)) {
2550                        noad_script_order(tail) = script_primescript_first;
2551                    }
2552                }
2553            } else {
2554                halfword p = tex_new_node(simple_noad, ordinary_noad_subtype);
2555                noad_nucleus(p) = q;
2556                if (glyph_options_par & glyph_option_no_italic_correction) {
2557                    math_kernel_node_set_option(q, math_kernel_no_italic_correction);
2558                }
2559                node_subtype(p) = tex_aux_set_math_char(q, &mval, &dval);
2560                tex_math_set_scripts_options(p);
2561                tex_tail_append(p);
2562            }
2563        }
2564    }
2565}
2566
2567/*tex
2568
2569    The |append_math_char_in_text| procedure creates a new node representing a math char in text
2570    code, and appends it to the current list. However, if the math code is sufficiently large, the
2571    |cur_chr| is treated as an active character and nothing is appended.
2572
2573*/
2574
2575static void tex_aux_append_math_char_in_text(mathcodeval mval, mathdictval dval)
2576{
2577    (void) dval;
2578    if (tex_aux_scan_active_math_char(&mval, 3)) {
2579        return; /* rescan pushed back token */
2580    } else { 
2581        halfword p = tex_new_char_node(glyph_character_subtype, tex_fam_fnt(mval.family_value, text_size), mval.character_value, 1); /* todo: data */
2582        tex_tail_append(p);
2583    }
2584}
2585
2586void tex_run_math_letter(void) 
2587{
2588    tex_aux_append_math_char(tex_get_math_code(cur_chr), tex_fake_math_dict(cur_chr), 1);
2589}
2590
2591void tex_run_math_char_number(void) {
2592    /*tex 
2593        Both |\char| and |\glyph| get the same treatment. Scanning can change |cur_chr| so we do 
2594        that first. We no longer check for active here! 
2595    */
2596    mathcodeval mval = tex_no_math_code();
2597    mathdictval dval = tex_no_dict_code();
2598    cur_chr = tex_scan_char_number(0); 
2599    mval.character_value = cur_chr;
2600    mval.family_value = (short) cur_fam_par;
2601 // tex_aux_append_math_char(tex_get_math_code(cur_chr), tex_fake_math_dict(cur_chr));
2602    tex_aux_append_math_char(mval, dval, 1);
2603}
2604
2605void tex_run_math_math_spec(void)
2606{
2607    tex_aux_append_math_char(tex_get_math_spec(cur_chr), tex_get_math_dict(cur_chr), 1);
2608}
2609
2610void tex_run_text_math_spec(void)
2611{
2612    tex_aux_append_math_char_in_text(tex_get_math_spec(cur_chr), tex_get_math_dict(cur_chr));
2613}
2614
2615int tex_scan_math_cmd_val(mathcodeval *mval, mathdictval *dval)
2616{
2617    do {
2618        tex_get_x_token();
2619    } while (cur_cmd == spacer_cmd);
2620    switch (cur_cmd) {
2621        case mathspec_cmd:
2622            *mval = tex_get_math_spec(cur_chr);
2623            break;
2624        case math_char_number_cmd:
2625            switch (cur_chr) {
2626                case math_char_number_code:
2627                    *mval = tex_scan_mathchar(tex_mathcode);
2628                    break;
2629                case math_xchar_number_code:
2630                    *mval = tex_scan_mathchar(umath_mathcode);
2631                    *dval = tex_fake_math_dict(mval->character_value);
2632                    break;
2633                case math_dictionary_number_code:
2634                    *dval = tex_scan_mathdict();
2635                    *mval = tex_scan_mathchar(umath_mathcode);
2636                    break;
2637                case math_char_ignore_code:
2638                    break;
2639                default:
2640                    /* no message yet */
2641                    return 0;
2642            }
2643            break;
2644        case delimiter_number_cmd:
2645            switch (cur_chr) {
2646                case math_delimiter_code:
2647                    *mval = tex_scan_delimiter_as_mathchar(tex_mathcode);
2648                    break;
2649                case math_udelimiter_code:
2650                    *mval = tex_scan_delimiter_as_mathchar(umath_mathcode);
2651                    break;
2652                default:
2653                    /* no message yet */
2654                    return 0;
2655            }
2656            break;
2657        /*tex 
2658            This is/was an experiment but could work out ambigiuous in some cases so when I bring it 
2659            back it will be under more strict control. So, for instance a register would make us 
2660            enter the default branch but a direct number the other case. In the meantiem we no longer 
2661            use the direct char approach (for delimiters mostly) so we can comment it. 
2662        */
2663      // case letter_cmd: 
2664      // case other_char_cmd: 
2665      //     mval->character_value = cur_chr; 
2666      //     break; 
2667        default:
2668            /*tex
2669                We could do a fast |tex_scan_something_internal| here but this branch is not that 
2670                critical. 
2671            */     
2672            {
2673                halfword n = 0;
2674                tex_back_input(cur_tok);
2675                n = tex_scan_integer(0, NULL, NULL);
2676                *mval = tex_mathchar_from_integer(n, umath_mathcode);
2677            }
2678            break;
2679    }
2680    return 1;
2681}
2682
2683int tex_scan_math_code_val(halfword code, mathcodeval *mval, mathdictval *dval)
2684{
2685    switch (code) {
2686        case math_char_number_code:
2687            *mval = tex_scan_mathchar(tex_mathcode);
2688            break;
2689        case math_xchar_number_code:
2690            *mval = tex_scan_mathchar(umath_mathcode);
2691            *dval = tex_fake_math_dict(mval->character_value);
2692            break;
2693        case math_dictionary_number_code:
2694            *dval = tex_scan_mathdict();
2695            *mval = tex_scan_mathchar(umath_mathcode);
2696            break;
2697        case math_class_number_code:
2698            {
2699                halfword family = cur_fam_par;
2700                halfword mathclass  = tex_scan_math_class_number(0);
2701                tex_scan_math_cmd_val(mval, dval);
2702                mval->class_value = (short) mathclass;
2703                mval->family_value = (short) family;
2704            }
2705            break;
2706        case math_char_ignore_code:
2707            break;
2708        default:
2709            /* no message yet */
2710            tex_back_input(cur_tok);
2711            return 0;
2712    }
2713    return 1;
2714}
2715
2716void tex_run_text_math_char_number(void) {
2717    mathcodeval mval = tex_no_math_code();
2718    mathdictval dval = tex_no_dict_code();
2719    if (tex_scan_math_code_val(cur_chr, &mval, &dval)) {
2720        tex_aux_append_math_char_in_text(mval, dval);
2721    }
2722}
2723
2724void tex_run_math_math_char_number(void) {
2725    mathcodeval mval = tex_no_math_code();
2726    mathdictval dval = tex_no_dict_code();
2727    if (tex_scan_math_code_val(cur_chr, &mval, &dval)) {
2728        tex_aux_append_math_char(mval, dval, 1);
2729    }
2730}
2731
2732void tex_run_math_delimiter_number(void) {
2733    switch (cur_chr) {
2734        case math_delimiter_code:
2735            tex_aux_append_math_char(tex_scan_delimiter_as_mathchar(tex_mathcode), tex_no_dict_code(), 0);
2736            break;
2737        case math_udelimiter_code:
2738            tex_aux_append_math_char(tex_scan_delimiter_as_mathchar(umath_mathcode), tex_no_dict_code(), 0);
2739            break;
2740    }
2741}
2742
2743/*tex 
2744    In original \TEX\ the subtype overlaps the class. Here we are more strict: a subtype is the
2745    main class as in original \TEX\ but we also have overloads: main, left and right. The subtype 
2746    drives the rendering, the others the spacing etc. 
2747*/
2748
2749static void tex_aux_math_math_component(halfword target, int append)
2750{
2751    quarterword subtype = unset_noad_class;
2752    quarterword allclass = unset_noad_class;
2753    halfword style = cur_list.math_style;
2754    int usetextfont = math_atom_no_font_option;
2755    tex_reset_noad_classes(target);
2756    switch (cur_chr) {
2757        case math_component_ordinary_code:
2758            subtype = ordinary_noad_subtype;
2759            break;
2760        case math_component_operator_code:
2761            subtype = operator_noad_subtype;
2762            break;
2763        case math_component_binary_code:
2764            subtype = binary_noad_subtype;
2765            break;
2766        case math_component_relation_code:
2767            subtype = relation_noad_subtype;
2768            break;
2769        case math_component_open_code:
2770            subtype = open_noad_subtype;
2771            break;
2772        case math_component_close_code:
2773            subtype = close_noad_subtype;
2774            break;
2775        case math_component_punctuation_code:
2776            subtype = punctuation_noad_subtype;
2777            break;
2778        case math_component_variable_code:
2779            subtype = variable_noad_subtype;
2780            break;
2781        case math_component_inner_code:
2782            subtype = inner_noad_subtype;
2783            break;
2784        case math_component_under_code:
2785            subtype = under_noad_subtype;
2786            style = tex_math_style_variant(style, math_parameter_under_line_variant);
2787            break;
2788        case math_component_over_code:
2789            subtype = over_noad_subtype;
2790            style = tex_math_style_variant(style, math_parameter_over_line_variant);
2791            break;
2792        case math_component_fraction_code:
2793            subtype = fraction_noad_subtype;
2794            break;
2795        case math_component_radical_code:
2796            subtype = radical_noad_subtype;
2797            break;
2798        case math_component_middle_code:
2799            subtype = middle_noad_subtype;
2800            break;
2801        case math_component_accent_code:
2802            subtype = accent_noad_subtype;
2803            break;
2804        case math_component_fenced_code:
2805            subtype = fenced_noad_subtype;
2806            break;
2807        case math_component_ghost_code:
2808            subtype = ghost_noad_subtype;
2809            break;
2810        case math_component_atom_code:
2811            {
2812                halfword attrlist = null;
2813                while (1) {
2814                    switch (tex_scan_character("aclmnoprstuvACLMNOPRSTUV", 0, 1, 0)) {
2815                        case 'a': case 'A':
2816                            switch (tex_scan_character("ltLT", 0, 0, 0)) {
2817                                case 'l': case 'L':
2818                                    if (tex_scan_mandate_keyword("all", 2)) {
2819                                        allclass = (quarterword) tex_scan_math_class_number(0);
2820                                        if (! valid_math_class_code(allclass)) {
2821                                            allclass = unset_noad_class;
2822                                        }
2823                                    }
2824                                    break;
2825                                case 't': case 'T':
2826                                    if (tex_scan_mandate_keyword("attr", 2)) {
2827                                        attrlist = tex_scan_attribute(attrlist);
2828                                    }
2829                                    break;
2830                                default:
2831                                    tex_aux_show_keyword_error("attr|all");
2832                                    goto DONE;
2833                            }
2834                            break;
2835                        case 'c': case 'C':
2836                            switch (tex_scan_character("aloLAO", 0, 0, 0)) {
2837                                case 'a': case 'A':
2838                                    if (tex_scan_mandate_keyword("carryover", 2)) {
2839                                        noad_options(target) |= noad_option_carry_over_classes;
2840                                    }
2841                                    break;
2842                                case 'l': case 'L':
2843                                    if (tex_scan_mandate_keyword("class", 2)) {
2844                                        subtype = (quarterword) tex_scan_math_class_number(0);
2845                                        if (! valid_math_class_code(subtype)) {
2846                                            subtype = ordinary_noad_subtype;
2847                                        }
2848                                        set_noad_main_class(target, subtype);
2849                                    }
2850                                    break;
2851                                case 'o': case 'O':
2852                                    if (tex_scan_mandate_keyword("continuation", 2)) {
2853                                        noad_options(target) |= noad_option_continuation;
2854                                    }
2855                                    break;
2856                                default:
2857                                    tex_aux_show_keyword_error("class|continuation");
2858                                    goto DONE;
2859                            }
2860                            break;
2861                        case 'l': case 'L':
2862                            switch (tex_scan_character("eiEI", 0, 0, 0)) {
2863                                case 'e': case 'E':
2864                                    if (tex_scan_mandate_keyword("leftclass", 2)) {
2865                                        halfword c = tex_scan_math_class_number(0);
2866                                        if (! valid_math_class_code(c)) {
2867                                            c = ordinary_noad_subtype;
2868                                        }
2869                                        set_noad_left_class(target, c);
2870                                    }
2871                                    break;
2872                                case 'i': case 'I':
2873                                    if (tex_scan_mandate_keyword("limits", 2)) {
2874                                        noad_options(target) |= noad_option_limits;
2875                                    }
2876                                    break;
2877                                default:
2878                                    tex_aux_show_keyword_error("leftclass|limits");
2879                                    goto DONE;
2880                            }
2881                            break;
2882                        case 'm': case 'M':
2883                            if (tex_scan_mandate_keyword("mathfont", 1)) {
2884                                usetextfont = math_atom_math_font_option;
2885                            }
2886                            break;
2887                        case 'n': case 'N':
2888                            /*tex A bit over the top, three steps but a push back is still worse. We can scan for 'no'. */
2889                            if (tex_scan_character("oO", 0, 0, 0)) {
2890                                switch (tex_scan_character("loLO", 0, 0, 0)) {
2891                                    case 'l': case 'L':
2892                                        if (tex_scan_mandate_keyword("nolimits", 3)) {
2893                                            noad_options(target) |= noad_option_no_limits;
2894                                        }
2895                                        break;
2896                                    case 'o': case 'O':
2897                                        if (tex_scan_mandate_keyword("nooverflow", 3)) {
2898                                            noad_options(target) |= noad_option_no_overflow;
2899                                        }
2900                                        break;
2901                                    default:
2902                                        tex_aux_show_keyword_error("nolimits|nooverflow");
2903                                        goto DONE;
2904                                }
2905                            }
2906                            break;
2907                        case 'o': case 'O':
2908                            /* no names, just numbers, we might also do that with other noads */
2909                            if (tex_scan_mandate_keyword("options", 1)) {
2910                                noad_options(target) = tex_scan_integer(0, NULL, NULL);
2911                            }
2912                            break;
2913                        case 'p': case 'P':
2914                            if (tex_scan_mandate_keyword("phantom", 1)) {
2915                                noad_options(target) |= noad_option_phantom;
2916                            }
2917                            break;
2918                        case 'r': case 'R':
2919                            if (tex_scan_mandate_keyword("rightclass", 1)) {
2920                                halfword c = tex_scan_math_class_number(0);
2921                                if (! valid_math_class_code(c)) {
2922                                    c = ordinary_noad_subtype;
2923                                }
2924                                set_noad_right_class(target, c);
2925                            }
2926                            break;
2927                        case 's': case 'S':
2928                            switch (tex_scan_character("ioIO", 0, 0, 0)) {
2929                                case 'i': case 'I':
2930                                    if (tex_scan_mandate_keyword("single", 2)) {
2931                                        noad_options(target) |= noad_option_single;
2932                                    }
2933                                    break;
2934                                case 'o': case 'O':
2935                                    if (tex_scan_mandate_keyword("source", 2)) {
2936                                        noad_source(target) = tex_scan_integer(0, NULL, NULL);
2937                                    }
2938                                    break;
2939                                default:
2940                                    tex_aux_show_keyword_error("single|source");
2941                                    goto DONE;
2942                            }
2943                            break;
2944                       case 't': case 'T':
2945                            if (tex_scan_mandate_keyword("textfont", 1)) {
2946                                usetextfont = math_atom_text_font_option;
2947                            }
2948                            break;
2949                        case 'u': case 'U':
2950                            /*tex A bit over the top, three steps but a push back is still worse. We can scan for 'un'. */
2951                            if (tex_scan_character("nN", 0, 0, 0)) {
2952                                switch (tex_scan_character("prPR", 0, 0, 0)) {
2953                                    case 'p': case 'P':
2954                                        if (tex_scan_mandate_keyword("unpack", 3)) {
2955                                            noad_options(target) |= noad_option_unpack_list;
2956                                        }
2957                                        break;
2958                                    case 'r': case 'R':
2959                                        if (tex_scan_mandate_keyword("unroll", 3)) {
2960                                            noad_options(target) |= noad_option_unroll_list;
2961                                        }
2962                                        break;
2963                                    default:
2964                                        tex_aux_show_keyword_error("unpack|unroll");
2965                                        goto DONE;
2966                                }
2967                            }
2968                            break;
2969                        case 'v': case 'V':
2970                            if (tex_scan_mandate_keyword("void", 1)) {
2971                                noad_options(target) |= noad_option_void;
2972                            }
2973                            break;
2974                        default:
2975                            goto DONE;
2976                    }
2977                }
2978              DONE:
2979                if (attrlist) {
2980                    tex_attach_attribute_list_attribute(target, attrlist);
2981                }
2982                if (subtype == unset_noad_class) {
2983                    if (get_noad_left_class(target) != unset_noad_class && get_noad_right_class(target) != unset_noad_class) {
2984                        subtype = ordinary_noad_subtype;
2985                    } else {
2986                        /* mandate, maybe we will just force a keyword */
2987                        subtype = (quarterword) tex_scan_math_class_number(0);
2988                    }
2989                }
2990            }
2991            break;
2992    }
2993    if (! valid_math_class_code(subtype)) {
2994        subtype = ordinary_noad_subtype;
2995    }
2996    /*tex 
2997        Now we can scan for the content: 
2998    */
2999    {
3000        halfword content = tex_new_node(math_char_node, 0);
3001        noad_nucleus(target) = content;
3002        node_subtype(target) = subtype;
3003        if (append) {
3004            tex_tail_append(target);
3005        }
3006        tex_aux_scan_math(content, style, usetextfont, 0, 0, 0, subtype, allclass);
3007    }
3008}
3009
3010void tex_run_math_math_component(void)
3011{
3012    halfword n = tex_new_node(simple_noad, ordinary_noad_subtype);
3013    tex_math_set_scripts_options(n);
3014    tex_aux_math_math_component(n, 1);
3015}
3016
3017int tex_is_math_disc(halfword n)
3018{
3019    return
3020        n && node_type(n) == hlist_node && box_list(n) && node_type(box_list(n)) == disc_node &&
3021        disc_class(box_list(n)) != unset_disc_class && ! node_next(box_list(n));
3022}
3023
3024halfword tex_math_make_disc(halfword d)
3025{
3026    halfword q = tex_new_node(sub_mlist_node, 0);
3027    halfword n = tex_new_node(simple_noad, (quarterword) disc_class(d));
3028    kernel_math_list(q) = d;
3029    noad_nucleus(n) = q;
3030    noad_options(n) = noad_option_unpack_list;
3031    return n;
3032}
3033
3034/*tex
3035    Easiest is to permit all modifiers and just ignore those that make no sense. We then can
3036    stepwise support whatever modifier we like later on.
3037*/
3038
3039void tex_run_math_modifier(void)
3040{
3041    halfword tail = cur_list.tail;
3042    if (cur_list.head != tail) {
3043        switch (node_type(tail)) {
3044            case simple_noad:
3045                switch (cur_chr) {
3046                    case adapt_to_left_modifier_code:
3047                        noad_options(tail) = unset_option(noad_options(tail), noad_option_adapt_to_right_size);
3048                        noad_options(tail) |= noad_option_adapt_to_left_size;
3049                        break;
3050                    case adapt_to_right_modifier_code:
3051                        noad_options(tail) = unset_option(noad_options(tail), noad_option_adapt_to_left_size);
3052                        noad_options(tail) |= noad_option_adapt_to_right_size;
3053                        break;
3054                    /* todo: actually this one can also be used for other types */
3055                    case axis_modifier_code:
3056                        noad_options(tail) |= noad_option_axis;
3057                        break;
3058                    case no_axis_modifier_code:
3059                        noad_options(tail) |= noad_option_no_axis;
3060                        break;
3061                    case phantom_modifier_code:
3062                        noad_options(tail) |= noad_option_phantom;
3063                        break;
3064                    case void_modifier_code:
3065                        noad_options(tail) |= noad_option_void;
3066                        break;
3067                    case source_modifier_code:
3068                        if (tex_scan_keyword("nucleus")) {
3069                            noad_options(tail) |= noad_option_source_on_nucleus;    
3070                        }
3071                        noad_source(tail) = tex_scan_integer(0, NULL, NULL);
3072                        break;
3073                    case openup_height_modifier_code:
3074                        noad_options(tail) |= noad_option_openup_height;
3075                        noad_height(tail) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
3076                        break;
3077                    case openup_depth_modifier_code:
3078                        noad_options(tail) |= noad_option_openup_depth;
3079                        noad_depth(tail) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
3080                        break;
3081                    case display_limits_modifier_code:
3082                        noad_options(tail) = unset_option(noad_options(tail), noad_option_limits | noad_option_no_limits);
3083                        break;
3084                    case limits_modifier_code:
3085                        noad_options(tail) = unset_option(noad_options(tail), noad_option_no_limits);
3086                        noad_options(tail) |= noad_option_limits;
3087                        break;
3088                    case no_limits_modifier_code:
3089                        noad_options(tail) = unset_option(noad_options(tail), noad_option_limits);
3090                        noad_options(tail) |= noad_option_no_limits;
3091                        break;
3092                }
3093            default:
3094                switch (node_type(tail)) {
3095                    case accent_noad:
3096                        switch (cur_chr) {
3097                            case source_modifier_code:
3098                                if (tex_scan_keyword("nucleus")) {
3099                                    noad_options(tail) |= noad_option_source_on_nucleus;    
3100                                }
3101                                noad_source(tail) = tex_scan_integer(0, NULL, NULL);
3102                                break;
3103                        }
3104
3105                }
3106                break;
3107        }
3108    }
3109}
3110
3111/*tex
3112
3113     Delimiter fields of noads are filled in by the |scan_delimiter| routine. The first parameter
3114     of this procedure is the |mem| address where the delimiter is to be placed; the second tells
3115     if this delimiter follows |\radical| or not.
3116
3117*/
3118
3119static void tex_aux_scan_delimiter(halfword target, int code, int mathclass)
3120{
3121    delcodeval dval = tex_no_del_code();
3122    mathcodeval mval = tex_no_math_code();
3123    switch (code) {
3124        case no_mathcode:
3125            /* can be integrated */
3126            do {
3127                tex_get_x_token();
3128            } while (cur_cmd == spacer_cmd || cur_cmd == relax_cmd);
3129            switch (cur_cmd) {
3130                case letter_cmd:
3131                case other_char_cmd:
3132                    dval = tex_get_del_code(cur_chr);
3133                    if (tex_has_del_code(dval)) { 
3134                        goto REALDELIMITER;
3135                    } else { 
3136                        mval = tex_get_math_code(cur_chr);
3137                        goto FAKEDELIMITER;
3138                    }
3139                case delimiter_number_cmd:
3140                    switch (cur_chr) {
3141                        case math_delimiter_code:
3142                            /*tex |\delimiter| */
3143                            dval = tex_aux_scan_extdef_del_code(tex_mathcode, 1);
3144                            break;
3145                        case math_udelimiter_code:
3146                            /*tex |\Udelimiter| */
3147                            dval = tex_aux_scan_extdef_del_code(umath_mathcode, 1);
3148                            break;
3149                        default:
3150                            tex_confusion("scan delimiter, case 1");
3151                            break;
3152                    }
3153                    goto REALDELIMITER;
3154                case mathspec_cmd:
3155                    mval = tex_get_math_spec(cur_chr);
3156                    goto FAKEDELIMITER;
3157                case math_char_number_cmd:
3158                    switch (cur_chr) {
3159                        case math_char_number_code:
3160                            mval = tex_scan_mathchar(tex_mathcode);
3161                            break;
3162                        case math_xchar_number_code:
3163                            mval = tex_scan_mathchar(umath_mathcode);
3164                            break;
3165                        case math_char_ignore_code:
3166                            break;
3167                        default:
3168                            tex_confusion("scan math char, case 1");
3169                            break;
3170                    }
3171                    goto FAKEDELIMITER;
3172            }
3173            break;
3174        case tex_mathcode:
3175            /*tex |\radical| */
3176            dval = tex_aux_scan_extdef_del_code(tex_mathcode, 1);
3177            goto REALDELIMITER;
3178        case umath_mathcode:
3179            /*tex |\Uradical| */
3180            dval = tex_aux_scan_extdef_del_code(umath_mathcode, 0);
3181            goto REALDELIMITER;
3182        default:
3183            tex_confusion("scan delimiter, case 2");
3184            goto REALDELIMITER;
3185    }
3186  FAKEDELIMITER:
3187    if (mathclass != unset_noad_class) {
3188        mval.class_value = (short) mathclass; 
3189    }
3190    dval.small = mval;
3191    dval.large = mval;
3192  REALDELIMITER:
3193    if (! target) {
3194        return;
3195    } else if (tex_has_del_code(dval)) {
3196        mathdictval dict = tex_fake_math_dict(dval.small.character_value);
3197        node_subtype(target) = dval.small.class_value; 
3198        delimiter_small_family(target) = dval.small.family_value;
3199        delimiter_small_character(target) = dval.small.character_value;
3200        delimiter_large_family(target) = dval.large.family_value;
3201        delimiter_large_character(target) = dval.large.character_value;
3202        delimiter_math_properties(target) = dict.properties; 
3203        delimiter_math_group(target) = dict.group; 
3204        delimiter_math_index(target) = dict.index; 
3205    } else {
3206        tex_back_input(cur_tok);
3207        tex_handle_error(
3208            normal_error_type,
3209            "Missing delimiter (. inserted)",
3210            "I was expecting to see something like '(' or '\\{' or '\\}' here. Acceptable\n"
3211            "delimiters are characters whose \\delcode is nonnegative, or you can use\n"
3212            "'\\delimiter <delimiter code>'."
3213        );
3214        node_subtype(target) = unset_noad_class;
3215        delimiter_small_family(target) = 0;
3216        delimiter_small_character(target) = 0;
3217        delimiter_large_family(target) = 0;
3218        delimiter_large_character(target) = 0;
3219    }
3220    return;
3221}
3222
3223void tex_run_math_radical(void)
3224{
3225    halfword code = cur_chr;
3226    fullword options = 0;
3227    halfword radical = tex_new_node(radical_noad, (quarterword) code);
3228    halfword style = yet_unset_math_style;
3229    halfword variant = 0; /* quad, harmless */
3230    halfword attrlist = null;
3231    halfword symbolattrlist = null;
3232    halfword top = null;
3233    halfword bottom = null;
3234    tex_tail_append_callback(radical); /* old/new experiment */
3235    /* only kewords to UI ones? */
3236    while (1) {
3237        switch (tex_scan_character("abdehlmnrstuwABDEHLMNRSTUW", 0, 1, 0)) {
3238            case 0:
3239                goto DONE;
3240            case 'a': case 'A':
3241                if (tex_scan_mandate_keyword("attr", 1)) {
3242                    attrlist = tex_scan_attribute(attrlist);
3243                }
3244                break;
3245            case 'b': case 'B':
3246                if (tex_scan_mandate_keyword("bottom", 1)) {
3247                    bottom = 1;
3248                }
3249                break;
3250            case 'd': case 'D':
3251                if (tex_scan_mandate_keyword("depth", 1)) {
3252                    radical_depth(radical) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
3253                }
3254                break;
3255            case 'e': case 'E':
3256                if (tex_scan_mandate_keyword("exact", 1)) {
3257                    options = options | noad_option_exact;
3258                }
3259                break;
3260            case 'h': case 'H':
3261                if (tex_scan_mandate_keyword("height", 1)) {
3262                    radical_height(radical) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
3263                }
3264                break;
3265            case 'l': case 'L':
3266                if (tex_scan_mandate_keyword("left", 1)) {
3267                     options = options | noad_option_left;
3268                }
3269                break;
3270            case 'm': case 'M':
3271                if (tex_scan_mandate_keyword("middle", 1)) {
3272                    options = options | noad_option_middle;
3273                }
3274                break;
3275            case 'n': case 'N':
3276                switch (tex_scan_character("oO", 0, 0, 0)) {
3277                    case 'o': case 'O':
3278                        switch (tex_scan_character("orOR", 0, 0, 0)) {
3279                            case 'r': case 'R':
3280                                if (tex_scan_mandate_keyword("norule", 3)) {
3281                                    options |= noad_option_no_rule;
3282                                }
3283                                break;
3284                            case 'o': case 'O':
3285                                if (tex_scan_mandate_keyword("nooverflow", 3)) {
3286                                    options |= noad_option_no_overflow;
3287                                }
3288                                break;
3289                            default:
3290                                tex_aux_show_keyword_error("norule|nooverflow");
3291                                goto DONE;
3292                        }
3293                        break;
3294                    default:
3295                        tex_aux_show_keyword_error("norule|nooverflow");
3296                        goto DONE;
3297                }
3298                break;
3299            case 'r': case 'R':
3300                switch (tex_scan_character("ieIE", 0, 0, 0)) {
3301                    case 'i': case 'I':
3302                        if (tex_scan_mandate_keyword("right", 2)) {
3303                            options = options | noad_option_right;
3304                        }
3305                        break;
3306                    case 'e': case 'E':
3307                        if (tex_scan_mandate_keyword("reflected", 2)) {
3308                            options |= noad_option_reflected;
3309                        }
3310                        break;
3311                    default:
3312                        tex_aux_show_keyword_error("right|reflected");
3313                        goto DONE;
3314                }
3315                break;
3316            case 's': case 'S':
3317                switch (tex_scan_character("hiotyHIOTY", 0, 0, 0)) {
3318                    case 'h': case 'H':
3319                        if (tex_scan_mandate_keyword("shrink", 2)) {
3320                             options = options | noad_option_shrink;
3321                        }
3322                        break;
3323                    case 'i': case 'I':
3324                        if (tex_scan_mandate_keyword("size", 2)) {
3325                            radical_size(radical) = tex_scan_integer(0, NULL, NULL);
3326                        }
3327                        break;
3328                    case 't': case 'T':
3329                        switch (tex_scan_character("ryRY", 0, 0, 0)) {
3330                            case 'r': case 'R':
3331                                if (tex_scan_mandate_keyword("stretch", 3)) {
3332                                     options = options | noad_option_stretch;
3333                                }
3334                                break;
3335                            case 'y': case 'Y':
3336                                if (tex_scan_mandate_keyword("style", 3)) {
3337                                    switch (code) {
3338                                        case normal_radical_subtype:
3339                                        case radical_radical_subtype:
3340                                        case root_radical_subtype:
3341                                        case rooted_radical_subtype:
3342                                        case delimited_radical_subtype:
3343                                            style = tex_scan_math_style_identifier(1, 0); /* tolerant */
3344                                            if (style < 0) { 
3345                                                style = yet_unset_math_style;
3346                                            }
3347                                            break;
3348                                        default:
3349                                            /* ignore */
3350                                            break;
3351                                    }
3352                                }
3353                                break;
3354                            default: 
3355                                tex_aux_show_keyword_error("style|stretch");
3356                                goto DONE;
3357                        }
3358                        break;
3359                    case 'o': case 'O':
3360                        if (tex_scan_mandate_keyword("source", 2)) {
3361                            noad_source(radical) = tex_scan_integer(0, NULL, NULL);
3362                        }
3363                        break;
3364                    case 'y': case 'Y':
3365                        if (tex_scan_mandate_keyword("symbolattr", 2)) {
3366                            symbolattrlist = tex_scan_extra_attribute(symbolattrlist);
3367                        }
3368                        break;
3369                    default:
3370                        tex_aux_show_keyword_error("style|source|stretch|shrink|symbolattr");
3371                        goto DONE;
3372                }
3373                break;
3374            case 't': case 'T':
3375                if (tex_scan_mandate_keyword("top", 1)) {
3376                    top = 1;
3377                }
3378                break;
3379            case 'u': case 'U':
3380                if (tex_scan_mandate_keyword("usecallback", 1)) {
3381                    options = options | noad_option_use_callback;
3382                }
3383                break;
3384            case 'w': case 'W':
3385                if (tex_scan_mandate_keyword("width", 1)) {
3386                    noad_width(radical) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
3387                }
3388                break;
3389            default:
3390                goto DONE;
3391        }
3392    }
3393  DONE:
3394    if (style == yet_unset_math_style) {
3395        switch (code) {
3396            case normal_radical_subtype:
3397            case radical_radical_subtype:
3398            case root_radical_subtype:
3399                variant = math_parameter_radical_variant;
3400                break;
3401            case under_delimiter_radical_subtype:
3402                variant = math_parameter_under_delimiter_variant;
3403                break;
3404            case over_delimiter_radical_subtype:
3405                variant = math_parameter_over_delimiter_variant;
3406                break;
3407            case delimiter_under_radical_subtype:
3408                variant = math_parameter_delimiter_under_variant;
3409                break;
3410            case delimiter_over_radical_subtype:
3411                variant = math_parameter_delimiter_over_variant;
3412                break;
3413            case delimited_radical_subtype:
3414                variant = math_parameter_radical_variant; /* math_parameter_delimited_variant */
3415                break;
3416            case h_extensible_radical_subtype:
3417                variant = math_parameter_h_extensible_variant;
3418                break;
3419        }
3420        style = variant ? tex_math_style_variant(cur_list.math_style, variant) : cur_list.math_style;
3421    }
3422    if (attrlist) {
3423        tex_attach_attribute_list_attribute(radical, attrlist);
3424    }
3425    if (symbolattrlist) { 
3426        add_attribute_reference(symbolattrlist);
3427        noad_extra_attr(radical) = symbolattrlist;
3428    }
3429    noad_options(radical) = options;
3430    set_noad_style(radical, style);
3431    {
3432        switch (code) {
3433            case normal_radical_subtype:
3434                {
3435                    halfword left = tex_new_node(delimiter_node, 0);
3436                    radical_left_delimiter(radical) = left;
3437                    tex_aux_scan_delimiter(left, tex_mathcode, unset_noad_class);
3438                }
3439                break;
3440            case radical_radical_subtype:
3441            case root_radical_subtype:
3442            case rooted_radical_subtype:
3443            case delimited_radical_subtype:
3444                {
3445                    halfword left = tex_new_node(delimiter_node, 0);
3446                    radical_left_delimiter(radical) = left;
3447                    tex_aux_scan_delimiter(left, umath_mathcode, unset_noad_class);
3448                }
3449                switch (code) {
3450                    case rooted_radical_subtype:
3451                    case delimited_radical_subtype:
3452                        {
3453                            halfword right = tex_new_node(delimiter_node, 0);
3454                            radical_right_delimiter(radical) = right;
3455                            tex_aux_scan_delimiter(right, umath_mathcode, unset_noad_class);
3456                        }
3457                }
3458                break;
3459            case under_delimiter_radical_subtype:
3460            case over_delimiter_radical_subtype:
3461            case delimiter_under_radical_subtype:
3462            case delimiter_over_radical_subtype:
3463            case h_extensible_radical_subtype:
3464                {
3465                    halfword left = tex_new_node(delimiter_node, 0);
3466                    radical_left_delimiter(radical) = left;
3467                    tex_aux_scan_delimiter(left, umath_mathcode, unset_noad_class);
3468                }
3469                break;
3470            default:
3471                tex_confusion("scan math radical");
3472                break;
3473        }
3474        if (top) { 
3475            top = tex_new_node(delimiter_node, 0);
3476            radical_top_delimiter(radical) = top;
3477            tex_aux_scan_delimiter(top, umath_mathcode, unset_noad_class);
3478        }
3479        if (bottom) { 
3480            bottom = tex_new_node(delimiter_node, 0);
3481            radical_bottom_delimiter(radical) = bottom;
3482            tex_aux_scan_delimiter(bottom, umath_mathcode, unset_noad_class);
3483        }
3484    }
3485    switch (code) {
3486        case h_extensible_radical_subtype:
3487            /*tex type will change */
3488            {
3489                halfword q = tex_new_node(sub_box_node, 0);
3490                noad_nucleus(radical) = q;
3491                break;
3492            }
3493        case root_radical_subtype:
3494        case rooted_radical_subtype:
3495            {
3496                /*tex Here the |style| option is not that usefull as we remap anyway. */
3497                saved_radical_initialize();
3498                saved_radical_degree_done = 0; 
3499                saved_radical_style =  style;
3500                lmt_save_state.save_stack_data.ptr += saved_radical_n_of_records;
3501                tex_aux_push_math(math_radical_group, tex_math_style_variant(style, math_parameter_degree_variant), -1);
3502                tex_scan_left_brace();
3503                break;
3504            }
3505        default :
3506            {
3507                halfword q = tex_new_node(math_char_node, 0);
3508                noad_nucleus(radical) = q;
3509                tex_aux_scan_math(q, tex_math_style_variant(style, variant ? variant : math_parameter_radical_variant), 0, 0, 0, 0, unset_noad_class, unset_noad_class);
3510                break;
3511            }
3512    }
3513}
3514
3515void tex_finish_math_radical(void)
3516{
3517    tex_aux_unsave_math();
3518    {
3519        halfword whatever = tex_new_node(sub_mlist_node, 0);
3520        halfword content = tex_aux_finish_math_list(null);
3521        halfword radical = cur_list.tail;
3522        kernel_math_list(whatever) = content;
3523        switch (saved_radical_current_component()) {
3524            case 0: 
3525                {
3526                    halfword style = saved_radical_current_style();
3527                    radical_degree(radical) = whatever;
3528                    saved_radical_update_component();
3529                    tex_aux_push_math(math_radical_group, tex_math_style_variant(style, math_parameter_radical_variant), -1);
3530                    tex_scan_left_brace();     
3531                }
3532                break;
3533            case 1: 
3534                {
3535                    noad_nucleus(radical) = whatever;
3536                    lmt_save_state.save_stack_data.ptr -= saved_radical_n_of_records;
3537                }
3538                break;
3539            default: 
3540                tex_confusion("scan radical");
3541                break;
3542        }
3543    }
3544}
3545
3546void tex_run_math_accent(void)
3547{
3548    mathcodeval t = tex_no_math_code();
3549    mathcodeval b = tex_no_math_code();
3550    mathcodeval o = tex_no_math_code();
3551    halfword code = cur_chr;
3552    halfword accent = tex_new_node(accent_noad, bothflexible_accent_subtype);
3553    quarterword subtype = ordinary_noad_subtype;
3554    halfword mathclass = accent_noad_subtype;
3555    halfword attrlist = null;
3556    halfword symbolattrlist = null;
3557    if (cur_cmd == accent_cmd) {
3558        tex_handle_error(
3559            normal_error_type,
3560            "Please use \\mathaccent for accents in math mode",
3561            "I'm changing \\accent to \\mathaccent here; wish me luck. (Accents are not the\n"
3562            "same in formulas as they are in text.)" );
3563    }
3564    tex_tail_append_callback(accent);
3565    switch (code) {
3566        case math_accent_code:
3567            /*tex |\mathaccent| */
3568            t = tex_scan_mathchar(tex_mathcode);
3569            break;
3570        case math_uaccent_code:
3571            /*tex |\Umathaccent| */
3572            while (1) {
3573                switch (tex_scan_character("abcefknostuABCEFKNOSTU", 0, 1, 0)) {
3574                    case 'a': case 'A':
3575                        switch (tex_scan_character("txTX", 0, 0, 0)) {
3576                            case 't': case 'T':
3577                                if (tex_scan_mandate_keyword("attr", 2)) {
3578                                    attrlist = tex_scan_attribute(attrlist);
3579                                }
3580                                break;
3581                         // case 'x': case 'X':
3582                         //     if (tex_scan_mandate_keyword("axis", 2)) {
3583                         //         noad_options(accent) |= noad_option_axis;
3584                         //     }
3585                         //     break;
3586                            default:
3587                         //     tex_aux_show_keyword_error("attr|axis");
3588                                tex_aux_show_keyword_error("attr");
3589                                goto DONE;
3590                        }
3591                        break;
3592                    case 'b': case 'B':
3593                        switch (tex_scan_character("aoAo", 0, 0, 0)) {
3594                            case 'a': case 'A':
3595                                if (tex_scan_mandate_keyword("base", 2)) {
3596                                    noad_options(accent) |= noad_option_auto_base;
3597                                }
3598                                break;
3599                            case 'o': case 'O':
3600                                /*tex bottom [fixed] <char> */
3601                                /*tex both [fixed] <char> [fixed] <char> */
3602                                if (tex_scan_character("t", 0, 0, 0)) {
3603                                     switch (tex_scan_character("htHT", 0, 0, 0)) {
3604                                         case 'h': case 'H':
3605                                            /*tex top bottom */
3606                                            if (tex_scan_keyword("fixed")) {
3607                                                node_subtype(accent) = fixedtop_accent_subtype;
3608                                            }
3609                                            t = tex_scan_mathchar(umath_mathcode);
3610                                            if (tex_scan_keyword("fixed")) {
3611                                                node_subtype(accent) = fixedboth_accent_subtype;
3612                                            }
3613                                            b = tex_scan_mathchar(umath_mathcode);
3614                                            goto DONE;
3615                                         case 't': case 'T':
3616                                             if (tex_scan_mandate_keyword("bottom", 4)) {
3617                                                /*tex bottom */
3618                                                if (tex_scan_keyword("fixed")) {
3619                                                    node_subtype(accent) = fixedbottom_accent_subtype;
3620                                                }
3621                                                b = tex_scan_mathchar(umath_mathcode);
3622                                             }
3623                                            goto DONE;
3624                                        default:
3625                                            tex_aux_show_keyword_error("both|bottom");
3626                                            goto DONE;
3627                                     }
3628                                }
3629                                goto DONE;
3630                            default:
3631                                tex_aux_show_keyword_error("base|both|bottom");
3632                                goto DONE;
3633                        }
3634                        break;
3635                    case 'c': case 'C':
3636                        switch (tex_scan_character("elEL", 0, 0, 0)) {
3637                            case 'e': case 'E':
3638                                if (tex_scan_mandate_keyword("center", 2)) {
3639                                    noad_options(accent) |= noad_option_center;
3640                                }
3641                                break;
3642                            case 'l': case 'L':
3643                                if (tex_scan_mandate_keyword("class", 2)) {
3644                                    halfword c = (quarterword) tex_scan_math_class_number(0);
3645                                    if (valid_math_class_code(c)) {
3646                                        mathclass = c;
3647                                    }
3648                                }
3649                                break;
3650                            default:
3651                                tex_aux_show_keyword_error("center|class");
3652                                goto DONE;
3653                        }
3654                        break;
3655                    case 'e': case 'E':
3656                        if (tex_scan_mandate_keyword("exact", 1)) {
3657                            noad_options(accent) |= noad_option_exact;
3658                        }
3659                        break;
3660                    case 'f': case 'F':
3661                        switch (tex_scan_character("frFR", 0, 0, 0)) {
3662                            case 'r': case 'R':
3663                                if (tex_scan_mandate_keyword("fraction", 2)) {
3664                                    accent_fraction(accent) = tex_scan_integer(0, NULL, NULL);
3665                                }
3666                                break;
3667                            case 'f': case 'F':
3668                                /*tex fixed <char> */
3669                                if (tex_scan_mandate_keyword("fixed", 2)) {
3670                                    node_subtype(accent) = fixedtop_accent_subtype;
3671                                    t = tex_scan_mathchar(umath_mathcode);
3672                                }
3673                                goto DONE;
3674                            default:
3675                                tex_aux_show_keyword_error("fraction|fixed");
3676                                goto DONE;
3677                        }
3678                    case 'k': case 'K':
3679                        if (tex_scan_mandate_keyword("keepbase", 1)) {
3680                            noad_options(accent) |= noad_option_keep_base;
3681                        }
3682                        break;
3683                    case 'n': case 'N':
3684                        if (tex_scan_mandate_keyword("nooverflow", 1)) {
3685                            /*tex 
3686                                Actually there never is an overflow but for consistency we do 
3687                                accept this key. Mayebe in the future it will be used. 
3688                            */
3689                            noad_options(accent) |= noad_option_no_overflow;
3690                        }
3691                        break;
3692                    case 'o': case 'O':
3693                        /*tex overlay [fixed] <char> */
3694                        if (tex_scan_mandate_keyword("overlay", 1)) {
3695                            if (tex_scan_keyword("fixed")) {
3696                                node_subtype(accent) = fixedtop_accent_subtype;
3697                            }
3698                            o = tex_scan_mathchar(umath_mathcode);
3699                        }
3700                        goto DONE;
3701                    case 's': case 'S':
3702                        switch (tex_scan_character("hiotyHIOTY", 0, 0, 0)) {
3703                            case 'h': case 'H':
3704                                if (tex_scan_mandate_keyword("shrink", 2)) {
3705                                    noad_options(accent) |= noad_option_shrink;
3706                                }
3707                                break;
3708                            case 'i': case 'I':
3709                                if (tex_scan_mandate_keyword("single", 2)) {
3710                                    noad_options(accent) |= noad_option_single;
3711                                }
3712                                break;
3713                            case 'o': case 'O':
3714                                if (tex_scan_mandate_keyword("source", 2)) {
3715                                    noad_source(accent) = tex_scan_integer(0, NULL, NULL);
3716                                }
3717                                break;
3718                            case 't': case 'T':
3719                                if (tex_scan_mandate_keyword("stretch", 2)) {
3720                                    noad_options(accent) |= noad_option_stretch;
3721                                }
3722                                break;
3723                            case 'y': case 'Y':
3724                                if (tex_scan_mandate_keyword("symbolattr", 2)) {
3725                                    symbolattrlist = tex_scan_extra_attribute(symbolattrlist);
3726                                }
3727                                break;
3728                            default:
3729                                tex_aux_show_keyword_error("source|stretch|shrink|single|symbolattr");
3730                                goto DONE;
3731                        }
3732                        break;
3733                    case 't': case 'T':
3734                        /*tex top [fixed] <char> */
3735                        if (tex_scan_mandate_keyword("top", 1)) {
3736                            if (tex_scan_keyword("fixed")) {
3737                                node_subtype(accent) = fixedtop_accent_subtype;
3738                            }
3739                            t = tex_scan_mathchar(umath_mathcode);
3740                        }
3741                        goto DONE;
3742                    case 'u': case 'U':
3743                        if (tex_scan_mandate_keyword("usecallback", 1)) {
3744                            noad_options(accent) |= noad_option_use_callback;
3745                        }
3746                        break;
3747                    default:
3748                        /*tex top <char> */
3749                        t = tex_scan_mathchar(umath_mathcode);
3750                        goto DONE;
3751                }
3752            }
3753        default:
3754            tex_confusion("scan math accent");
3755    }
3756  DONE:
3757    if (attrlist) {
3758        tex_attach_attribute_list_attribute(accent, attrlist);
3759    }
3760    if (symbolattrlist) { 
3761        add_attribute_reference(symbolattrlist);
3762        noad_extra_attr(accent) = symbolattrlist;
3763    }
3764    if (! (t.character_value == 0 && t.family_value == 0)) {
3765        halfword n = tex_new_node(math_char_node, 0);
3766        subtype = tex_aux_set_math_char(n, &t, NULL);
3767        accent_top_character(accent) = n;
3768    }
3769    if (! (b.character_value == 0 && b.family_value == 0)) {
3770        halfword n = tex_new_node(math_char_node, 0);
3771        subtype = tex_aux_set_math_char(n, &b, NULL);
3772        accent_bottom_character(accent) = n;
3773    }
3774    if (! (o.character_value == 0 && o.family_value == 0)) {
3775        halfword n = tex_new_node(math_char_node, 0);
3776        subtype = tex_aux_set_math_char(n, &o, NULL);
3777        accent_middle_character(accent) = n;
3778    }
3779    {
3780        halfword n = tex_new_node(math_char_node, subtype);
3781        noad_nucleus(accent) = n;
3782        tex_aux_scan_math(n, tex_math_style_variant(cur_list.math_style, math_parameter_accent_variant), 0, 0, 0, 0, unset_noad_class, unset_noad_class);
3783    }
3784    set_noad_main_class(accent, mathclass);
3785}
3786
3787/*tex
3788
3789    The routine that scans the four mlists of a |\mathchoice| is very much like the routine that
3790    builds discretionary nodes. Finally, the |\mathchoice| primitive creates a |choice_node|,
3791    which has special subfields |display_mlist|, |text_mlist|, |script_mlist|, and
3792    |script_script_mlist| pointing to the mlists for each style.
3793
3794*/
3795
3796void tex_run_math_choice(void) {
3797    switch (cur_chr) {
3798        case math_choice_code:
3799            /*tex |\mathchoice| */
3800            {
3801                halfword n = tex_new_node(choice_node, normal_choice_subtype);
3802                tex_tail_append(n);
3803                saved_choice_initialize();
3804                saved_choice_count = math_display_choice;
3805                lmt_save_state.save_stack_data.ptr += saved_choice_n_of_records;
3806                tex_aux_push_math(math_choice_group, display_style, -1);
3807                tex_scan_left_brace();
3808                break;
3809            }
3810        case math_discretionary_code:
3811            /*tex |\mathdiscretionary| */
3812            {
3813                halfword n = tex_new_node(choice_node, discretionary_choice_subtype);
3814                choice_class(n) = unset_noad_class;
3815                while (1) {
3816                    switch (tex_scan_character("cC", 0, 1, 0)) {
3817                        case 0:
3818                            goto DONE;
3819                        case 'c': case 'C':
3820                            if (tex_scan_mandate_keyword("class", 1)) {
3821                                choice_class(n) = tex_scan_math_class_number(0);
3822                            }
3823                            break;
3824                        default:
3825                            goto DONE;
3826                    }
3827                }
3828              DONE:
3829                tex_tail_append(n);
3830                saved_choice_initialize();
3831                saved_choice_count = math_pre_break_choice;
3832                lmt_save_state.save_stack_data.ptr += saved_choice_n_of_records;
3833                tex_aux_push_math(math_choice_group, cur_list.math_style, -1);
3834                tex_scan_left_brace();
3835                break;
3836            }
3837        case math_stack_code:
3838            /*tex |\mathstack| */
3839            {
3840             // halfword m = tex_new_node(sub_mlist_node, 0); /* was for some reason a math_char_node */
3841                halfword m = tex_new_node(math_char_node, 0);
3842                halfword n = tex_new_node(simple_noad, ordinary_noad_subtype);
3843                halfword s = tex_math_style_variant(cur_list.math_style, math_parameter_stack_variant);
3844                tex_tail_append(n);
3845                noad_nucleus(n) = m;
3846                tex_scan_left_brace();
3847                saved_math_group_initialize();
3848                saved_math_group_pointer = m;
3849                saved_math_group_all_class = unset_noad_class;
3850                lmt_save_state.save_stack_data.ptr += saved_math_group_n_of_records;
3851                tex_aux_push_math(math_stack_group, s, -1);
3852                break;
3853            }
3854    }
3855}
3856
3857int tex_current_math_style(void)
3858{
3859    return is_m_mode(cur_list.mode) ? cur_list.math_style : -1;
3860}
3861
3862int tex_current_math_main_style(void)
3863{
3864    return is_m_mode(cur_list.mode) ? cur_list.math_main_style : -1;
3865}
3866
3867int tex_current_math_parent_style(void)
3868{
3869    return is_m_mode(cur_list.mode) ? cur_list.math_parent_style : -1;
3870}
3871
3872void tex_finish_math_choice(void)
3873{
3874    tex_aux_unsave_math();
3875    {
3876        halfword content = tex_aux_finish_math_list(null);
3877        halfword choice = saved_choice_current_component();
3878        if (choice >= 0) {
3879            int style = cur_list.math_style;
3880            switch (node_subtype(cur_list.tail)) { 
3881                case normal_choice_subtype:
3882                    switch (choice) {
3883                        case math_display_choice:
3884                            choice_display_mlist(cur_list.tail) = content;
3885                            style = text_style;
3886                            break;
3887                        case math_text_choice:
3888                            choice_text_mlist(cur_list.tail) = content;
3889                            style = script_style;
3890                            break;
3891                        case math_script_choice:
3892                            choice_script_mlist(cur_list.tail) = content;
3893                            style = script_script_style;
3894                            break;
3895                        case math_script_script_choice:
3896                            choice_script_script_mlist(cur_list.tail) = content;
3897                            lmt_save_state.save_stack_data.ptr -= saved_choice_n_of_records;
3898                            return;
3899                    }
3900                    break;
3901                case discretionary_choice_subtype:
3902                    switch (choice) {
3903                        case math_pre_break_choice:
3904                            choice_pre_break(cur_list.tail) = content;
3905                            style = display_style;
3906                            break;
3907                        case math_post_break_choice:
3908                            choice_post_break(cur_list.tail) = content;
3909                            style = text_style;
3910                            break;
3911                        case math_no_break_choice:
3912                            choice_no_break(cur_list.tail) = content;
3913                            style = script_style;
3914                            lmt_save_state.save_stack_data.ptr -= saved_choice_n_of_records;
3915                            return;
3916                    }
3917                    break;
3918            }
3919            saved_choice_update_component();
3920            tex_aux_push_math(math_choice_group, style, -1);
3921            tex_scan_left_brace();
3922        } else {
3923            tex_confusion("scan build choices");
3924        }
3925    }
3926}
3927
3928void tex_finish_math_fraction(void)
3929{
3930    tex_aux_unsave_math();
3931    {
3932        halfword content = tex_aux_finish_math_list(null);
3933        halfword over = saved_fraction_current_component();
3934        if (over >= 0) { 
3935            halfword autostyle = saved_fraction_current_autostyle();
3936            halfword userstyle = saved_fraction_current_userstyle();
3937            halfword fraction = cur_list.tail;
3938            set_noad_style(fraction, userstyle);
3939            switch (over) {
3940                case math_numerator_above:
3941                    kernel_math_list(fraction_numerator(fraction)) = content;
3942                    break;
3943                case math_denominator_above:
3944                    kernel_math_list(fraction_denominator(fraction)) = content;
3945                    lmt_save_state.save_stack_data.ptr -= saved_fraction_n_of_records;
3946                    return;
3947            }
3948            saved_fraction_update_component();
3949            tex_aux_push_math(math_fraction_group, autostyle, -1);
3950            tex_scan_left_brace();
3951        } else {
3952            tex_confusion("scan build fraction");
3953        }
3954    }
3955}
3956
3957void tex_finish_math_operator(void)
3958{
3959    tex_aux_unsave_math();
3960    {
3961        halfword content = tex_aux_finish_math_list(null);
3962        halfword over = saved_operator_current_component();
3963        if (over >= 0) {
3964            halfword fenced = cur_list.tail;
3965            switch (over) {
3966                case math_limits_top:
3967                    if (content) { 
3968                        halfword top = tex_new_node(sub_mlist_node, 0);
3969                        fence_delimiter_top(fenced) = top;
3970                        kernel_math_list(top) = content;
3971                    }
3972                    break;
3973                case math_limits_bottom:
3974                    if (content) { 
3975                        halfword bottom = tex_new_node(sub_mlist_node, 0);
3976                        fence_delimiter_bottom(fenced) = bottom;
3977                        kernel_math_list(bottom) = content;
3978                    }
3979                    lmt_save_state.save_stack_data.ptr -= saved_operator_n_of_records;
3980                    return;
3981            }
3982            saved_operator_update_component();
3983            tex_aux_push_math(math_operator_group, tex_math_style_variant(cur_list.math_style, math_parameter_subscript_variant), -1);
3984            tex_scan_left_brace();
3985        } else { 
3986            tex_confusion("math operator");
3987        }
3988    }
3989}
3990
3991/*tex
3992
3993    Subscripts and superscripts are attached to the previous nucleus by the action procedure called
3994    |sub_sup|.
3995
3996*/
3997
3998void tex_run_math_script(void)
3999{
4000    int code = cur_chr;
4001    halfword tail = cur_list.tail;
4002    switch (cur_cmd) {
4003        case subscript_cmd:
4004            code = math_sub_script_code;
4005            break;
4006        case superscript_cmd:
4007            code = math_super_script_code;
4008            break;
4009    }
4010    switch (code) {
4011        case math_no_script_space_code:
4012            {
4013                halfword glue = tex_new_glue_node(zero_glue, conditional_math_glue);
4014                tex_tail_append(glue);
4015                tex_add_glue_option(glue, glue_option_no_auto_break);
4016            }
4017            return;
4018        case math_no_ruling_space_code:
4019            {
4020                halfword glue = tex_new_glue_node(zero_glue, rulebased_math_glue);
4021                tex_tail_append(glue);
4022                tex_add_glue_option(glue, glue_option_no_auto_break);
4023            }
4024            return;
4025        case math_sub_script_code:
4026            tex_get_token();
4027            if (cur_tok == underscore_token || cur_cmd == subscript_cmd) {
4028                tex_get_token(); 
4029                if (cur_tok == underscore_token || cur_cmd == subscript_cmd) {
4030                    tex_get_token();
4031                    if (cur_tok == underscore_token || cur_cmd == subscript_cmd) {
4032                        code = math_indexed_sub_pre_script_code; /* ____ */
4033                    } else {
4034                        tex_back_input(cur_tok);
4035                        code = math_sub_pre_script_code; /* ___ */
4036                    }
4037                } else {
4038                    tex_back_input(cur_tok);
4039                    code = math_indexed_sub_script_code; /* __ */
4040                }
4041            } else {
4042                tex_back_input(cur_tok); /* _ */
4043            }
4044            break;
4045        case math_super_script_code:
4046            tex_get_token();
4047            if (cur_tok == circumflex_token || cur_cmd == superscript_cmd) {
4048                tex_get_token();
4049                if (cur_tok == circumflex_token || cur_cmd == superscript_cmd) {
4050                    tex_get_token();
4051                    if (cur_tok == circumflex_token || cur_cmd == superscript_cmd) {
4052                        code = math_indexed_super_pre_script_code; /* ^^^^ */
4053                    } else {
4054                        tex_back_input(cur_tok);
4055                        code = math_super_pre_script_code; /* ^^^ */
4056                    }
4057                } else {
4058                    tex_back_input(cur_tok);
4059                    code = math_indexed_super_script_code; /* ^^ */
4060                }
4061            } else {
4062                tex_back_input(cur_tok); /* ^ */
4063            }
4064            break;
4065        case math_no_script_code:
4066            if (tex_math_scripts_allowed(cur_list.tail)) {
4067                noad_options(cur_list.tail) |= noad_option_no_more_scripts;
4068            }
4069            return; 
4070    }
4071    if (tail == cur_list.head) { 
4072        tail = tex_math_double_atom(0);
4073    } else if (tex_math_no_more_scripts(tail)) { 
4074        tail = tex_math_double_atom(1);
4075    } else if (! tex_math_scripts_allowed(tail)) { 
4076        tail = tex_math_double_atom(0);
4077    }
4078    switch (code) {
4079        case math_sub_script_code:
4080        case math_no_sub_script_code:
4081        case math_indexed_sub_script_code:
4082            {
4083                if (noad_subscr(tail)) {
4084                    tail = tex_math_double_atom(1);
4085                    if (math_double_script_mode_par < 0) { 
4086                        tex_handle_error(
4087                            normal_error_type,
4088                            "Double subscript",
4089                            "I treat 'x_1_2' essentially like 'x_1{}_2'."
4090                        );
4091                    }
4092                }
4093                switch (code) {
4094                    case math_no_sub_script_code:
4095                        noad_options(tail) |= noad_option_no_sub_script;
4096                        break;
4097                    case math_indexed_sub_script_code:
4098                        noad_options(tail) |= noad_option_indexed_sub_script;
4099                        break;
4100                }
4101                {
4102                    halfword n = tex_new_node(math_char_node, 0);
4103                    noad_subscr(tail) = n;
4104                    tex_aux_scan_math(n, tex_math_style_variant(cur_list.math_style, math_parameter_subscript_variant), 0, 0, 0, 1, unset_noad_class, unset_noad_class);
4105                    if (! noad_script_order(tail)) {
4106                        noad_script_order(tail) = script_subscript_first;
4107                    }
4108                }
4109                break;
4110            }
4111        case math_sub_pre_script_code:
4112        case math_no_sub_pre_script_code:
4113        case math_indexed_sub_pre_script_code:
4114            {
4115                if (noad_subprescr(tail)) {
4116                    int limitation = node_type(tail) == fraction_noad; /*tex See remark at node definition. */
4117                    tail = tex_math_double_atom(1);
4118                    if (math_double_script_mode_par < 0) { 
4119                        tex_handle_error(
4120                            normal_error_type,
4121                            limitation ? "Fractions take no pre subscript directly" : "Double pre subscript",
4122                            "I just ignore it; consider wrapping this element."
4123                        );
4124                    }
4125                }
4126                switch (code) {
4127                    case math_no_sub_pre_script_code:
4128                        noad_options(tail) |= noad_option_no_sub_pre_script;
4129                        break;
4130                    case math_indexed_sub_pre_script_code:
4131                        noad_options(tail) |= noad_option_indexed_sub_pre_script;
4132                        break;
4133                }
4134                {
4135                    halfword n = tex_new_node(math_char_node, 0);
4136                    noad_subprescr(tail) = n;
4137                    tex_aux_scan_math(n, tex_math_style_variant(cur_list.math_style, math_parameter_subscript_variant), 0, 0, 0, 1, unset_noad_class, unset_noad_class);
4138                }
4139                break;
4140            }
4141        case math_super_script_code:
4142        case math_no_super_script_code:
4143        case math_indexed_super_script_code:
4144            {
4145                if (noad_supscr(tail)) {
4146                    tail = tex_math_double_atom(1);
4147                    if (math_double_script_mode_par < 0) { 
4148                        tex_handle_error(
4149                            normal_error_type,
4150                            "Double superscript",
4151                            "I treat 'x^1^2' essentially like 'x^1{}^2'."
4152                        );
4153                    }
4154                }
4155                switch (code) {
4156                    case math_no_super_script_code:
4157                        noad_options(tail) |= noad_option_no_super_script;
4158                        break;
4159                    case math_indexed_super_script_code:
4160                        noad_options(tail) |= noad_option_indexed_super_script;
4161                        break;
4162                }
4163                {
4164                    halfword n = tex_new_node(math_char_node, 0);
4165                    noad_supscr(tail) = n;
4166                    if (! noad_script_order(tail)) {
4167                        noad_script_order(tail) = script_superscript_first;
4168                    }
4169                    tex_aux_scan_math(n, tex_math_style_variant(cur_list.math_style, math_parameter_superscript_variant), 0, 0, 0, 1, unset_noad_class, unset_noad_class);
4170                }
4171                break;
4172            }
4173        case math_super_pre_script_code:
4174        case math_no_super_pre_script_code:
4175        case math_indexed_super_pre_script_code:
4176            {
4177                if (noad_supprescr(tail)) {
4178                    int limitation = node_type(tail) == fraction_noad; /*tex See remark at node definition. */
4179                    tail = tex_math_double_atom(1);
4180                    if (math_double_script_mode_par < 0) { 
4181                        tex_handle_error(
4182                            normal_error_type,
4183                            limitation ? "Fractions take no pre superscript directly" : "Double pre superscript",
4184                            "I just ignore it; consider wrapping this element."
4185                        );
4186                    }
4187                }
4188                switch (code) {
4189                    case math_no_super_script_code:
4190                        noad_options(tail) |= noad_option_no_super_pre_script;
4191                        break;
4192                    case math_indexed_super_pre_script_code:
4193                        noad_options(tail) |= noad_option_indexed_super_pre_script;
4194                        break;
4195                }
4196                {
4197                    halfword n = tex_new_node(math_char_node, 0);
4198                    noad_supprescr(tail) = n;
4199                    tex_aux_scan_math(n, tex_math_style_variant(cur_list.math_style, math_parameter_superscript_variant), 0, 0, 0, 1, unset_noad_class, unset_noad_class);
4200                }
4201                break;
4202            }
4203        case math_prime_script_code:
4204            {
4205                if (noad_prime(tail)) {
4206                    tail = tex_math_double_atom(1);
4207                    if (math_double_script_mode_par < 0) { 
4208                        tex_handle_error(
4209                            normal_error_type,
4210                            "Double prime script",
4211                            "I'll add a dummy nucleus."
4212                        );
4213                    }
4214                }
4215                {
4216                    halfword n = tex_new_node(math_char_node, 0);
4217                    noad_prime(tail) = n;
4218                    if (! noad_script_order(tail)) {
4219                        noad_script_order(tail) = script_primescript_first;
4220                    }
4221                    /* maybe it's own variant */
4222                    tex_aux_scan_math(n, tex_math_style_variant(cur_list.math_style, math_parameter_superscript_variant), 0, 0, 0, 1, unset_noad_class, unset_noad_class);
4223                }
4224                break;
4225            }
4226    }
4227}
4228
4229/*tex
4230
4231    An operation like |\over| causes the current mlist to go into a state of suspended animation:
4232    |incomplete_noad| points to a |fraction_noad| that contains the mlist-so-far as its numerator,
4233    while the denominator is yet to come. Finally when the mlist is finished, the denominator will
4234    go into the incomplete fraction noad, and that noad will become the whole formula, unless it is
4235    surrounded by |\left| and |\right| delimiters.
4236
4237    We can probably replace the |incomplete_noad_par| trickery because we can now look back in the
4238    list using the |alink| field. But not now.
4239
4240*/
4241
4242void tex_run_math_fraction(void)
4243{
4244    /*tex The type of generalized fraction we are scanning: */
4245    halfword code = cur_chr;
4246    if (cur_list.incomplete_noad) {
4247        /*tex Recovery code. */
4248        switch (code) {
4249            case math_above_delimited_code:
4250            case math_over_delimited_code:
4251            case math_atop_delimited_code:
4252            case math_u_above_delimited_code:
4253            case math_u_over_delimited_code:
4254            case math_u_atop_delimited_code:
4255            case math_u_skewed_delimited_code:
4256            case math_u_stretched_delimited_code:
4257                tex_aux_scan_delimiter(null, no_mathcode, unset_noad_class);
4258                tex_aux_scan_delimiter(null, no_mathcode, unset_noad_class);
4259                break;
4260        }
4261        switch (code) {
4262            case math_above_code:
4263            case math_above_delimited_code:
4264            case math_u_above_code:
4265            case math_u_above_delimited_code:
4266                tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
4267                break;
4268        }
4269        /*tex This is somewhat weird, this error here. */
4270        tex_handle_error(
4271            normal_error_type,
4272            "Ambiguous; you need another { and }",
4273            "I'm ignoring this fraction specification, since I don't know whether a\n"
4274            "construction like 'x \\over y \\over z' means '{x \\over y} \\over z' or\n"
4275            "'x \\over {y \\over z}'."
4276        );
4277    } else {
4278        halfword fraction = tex_new_node(fraction_noad, 0);
4279        halfword numerator = tex_new_node(sub_mlist_node, 0);
4280        halfword denominator = null;
4281        halfword autostyle = tex_math_style_variant(cur_list.math_style, math_parameter_fraction_variant);
4282        halfword userstyle = -1; // todo: yet_unset_math_style; 
4283        halfword attrlist = null;
4284        halfword symbolattrlist = null;
4285        fullword options = 0;
4286        halfword mathclass = fraction_noad_subtype;
4287        halfword rulethickness = preset_rule_thickness;
4288        int ruledone = 0;
4289        fraction_h_factor(fraction) = scaling_factor;
4290        fraction_v_factor(fraction) = scaling_factor;
4291        switch (code) {
4292            case math_above_code:
4293            case math_above_delimited_code:
4294                node_subtype(fraction) = above_fraction_subtype;
4295                goto NEXTSTEP1;
4296            case math_over_code:
4297            case math_over_delimited_code:
4298                node_subtype(fraction) = over_fraction_subtype;
4299                goto NEXTSTEP1;
4300            case math_atop_code:
4301            case math_atop_delimited_code:
4302                node_subtype(fraction) = atop_fraction_subtype;
4303              NEXTSTEP1:
4304                {
4305                    cur_list.incomplete_noad = fraction;
4306                    fraction_numerator(fraction) = numerator;
4307                    kernel_math_list(numerator) = node_next(cur_list.head);
4308                    node_next(cur_list.head) = null;
4309                    cur_list.tail = cur_list.head;
4310                    cur_list.math_style = autostyle;
4311                    break;
4312                }
4313            case math_u_above_code:
4314            case math_u_above_delimited_code:
4315                node_subtype(fraction) = above_fraction_subtype;
4316                goto NEXTSTEP2;
4317            case math_u_over_code:
4318            case math_u_over_delimited_code:
4319                node_subtype(fraction) = over_fraction_subtype;
4320                goto NEXTSTEP2;
4321            case math_u_atop_code:
4322            case math_u_atop_delimited_code:
4323                node_subtype(fraction) = atop_fraction_subtype;
4324                goto NEXTSTEP2;
4325            case math_u_skewed_code:
4326            case math_u_skewed_delimited_code:
4327                node_subtype(fraction) = skewed_fraction_subtype;
4328                goto NEXTSTEP2;
4329            case math_u_stretched_code:
4330            case math_u_stretched_delimited_code:
4331                node_subtype(fraction) = stretched_fraction_subtype;
4332              NEXTSTEP2:
4333                {
4334                    cur_list.incomplete_noad = null;
4335                    denominator = tex_new_node(sub_mlist_node, 0);
4336                    tex_tail_append_callback(fraction); /* old/new experiment */
4337                    fraction_numerator(fraction) = numerator;
4338                    fraction_denominator(fraction) = denominator;
4339                    break;
4340                }
4341        }
4342        switch (code) {
4343            case math_u_skewed_code:
4344            case math_u_skewed_delimited_code:
4345            case math_u_stretched_code:
4346            case math_u_stretched_delimited_code:
4347                {
4348                    halfword q = tex_new_node(delimiter_node, 0);
4349                    fraction_middle_delimiter(fraction) = q;
4350                    tex_aux_scan_delimiter(q, no_mathcode, unset_noad_class);
4351                    break;
4352                }
4353        }
4354        switch (code) {
4355            case math_above_delimited_code:
4356            case math_over_delimited_code:
4357            case math_atop_delimited_code:
4358            case math_u_above_delimited_code:
4359            case math_u_over_delimited_code:
4360            case math_u_atop_delimited_code:
4361            case math_u_skewed_delimited_code:
4362            case math_u_stretched_delimited_code:
4363                {
4364                    halfword left = tex_new_node(delimiter_node, 0);
4365                    halfword right = tex_new_node(delimiter_node, 0);
4366                    fraction_left_delimiter(fraction) = left;
4367                    fraction_right_delimiter(fraction) = right;
4368                    tex_aux_scan_delimiter(left, no_mathcode, open_noad_subtype);
4369                    tex_aux_scan_delimiter(right, no_mathcode, close_noad_subtype);
4370                    break;
4371                }
4372        }
4373        switch (code) {
4374            /*tex We can't have keyword here because of compatibility reasons. */
4375            case math_above_code:
4376            case math_above_delimited_code:
4377                rulethickness = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
4378                break;
4379            case math_over_code:
4380            case math_over_delimited_code:
4381                rulethickness = preset_rule_thickness;
4382                break;
4383            case math_atop_code:
4384            case math_atop_delimited_code:
4385                break;
4386            /*tex
4387                But here we can! For practical reasons we accept the rule related options
4388                and in principle we cold do with one command.
4389            */
4390            case math_u_above_code:
4391            case math_u_above_delimited_code:
4392                goto OPTIONS;
4393            case math_u_atop_code:
4394            case math_u_atop_delimited_code:
4395            case math_u_over_code:
4396            case math_u_over_delimited_code:
4397                ruledone = 1;
4398                goto OPTIONS;
4399            case math_u_stretched_code:
4400            case math_u_stretched_delimited_code:
4401            case math_u_skewed_code:
4402            case math_u_skewed_delimited_code:
4403                ruledone = 1;
4404              OPTIONS:
4405                while (1) {
4406                    switch (tex_scan_character("acefhnpstuvACEFHNPSTUV", 0, 1, 0)) {
4407                        case 'a': case 'A':
4408                            if (tex_scan_mandate_keyword("attr", 1)) {
4409                                attrlist = tex_scan_attribute(attrlist);
4410                            }
4411                            break;
4412                        case 'c': case 'C':
4413                            if (tex_scan_mandate_keyword("class", 1)) {
4414                                halfword c = (quarterword) tex_scan_math_class_number(0);
4415                                if (valid_math_class_code(c)) {
4416                                    mathclass = c;
4417                                }
4418                            }
4419                            break;
4420                        case 'e': case 'E':
4421                            /* not used */
4422                            if (tex_scan_mandate_keyword("exact", 1)) {
4423                                options |= noad_option_exact;
4424                            }
4425                            break;
4426                        case 'f': case 'F':
4427                            if (tex_scan_mandate_keyword("font", 1)) {
4428                                ruledone = 1;
4429                                options |= noad_option_prefer_font_thickness;
4430                            }
4431                            break;
4432                        case 'h': case 'H':
4433                            if (tex_scan_mandate_keyword("hfactor", 1)) {
4434                                fraction_h_factor(fraction) = tex_scan_integer(0, NULL, NULL);
4435                            }
4436                            break;
4437                        case 'n': case 'N':
4438                            /*tex A bit over the top, three steps but a push back is still worse. */
4439                            if (tex_scan_character("oO", 0, 0, 0)) {
4440                                switch (tex_scan_character("aoAO", 0, 0, 0)) {
4441                                    case 'a': case 'A':
4442                                        if (tex_scan_mandate_keyword("noaxis", 3)) {
4443                                            options |= noad_option_no_axis;
4444                                        }
4445                                        break;
4446                                    case 'o': case 'O':
4447                                        if (tex_scan_mandate_keyword("nooverflow", 3)) {
4448                                            options |= noad_option_no_overflow;
4449                                        }
4450                                        break;
4451                                    default:
4452                                        tex_aux_show_keyword_error("noaxis|nooverflow");
4453                                        goto DONE;
4454                                }
4455                            }
4456                            break;
4457                        case 'p': case 'P':
4458                            /* not used */
4459                            if (tex_scan_mandate_keyword("proportional", 1)) {
4460                                options |= noad_option_proportional;
4461                            }
4462                            break;
4463                        case 's': case 'S':
4464                            switch (tex_scan_character("toyTOY", 0, 0, 0)) {
4465                                case 't': case 'T':
4466                                    if (tex_scan_mandate_keyword("style", 2)) {
4467                                        halfword style = tex_scan_math_style_identifier(1, 0); /* tolerant */
4468                                        if (denominator && style >= 0) {
4469                                            userstyle = style;
4470                                        } else {
4471                                            /* just ignore */
4472                                        }
4473                                    }
4474                                    break;
4475                                case 'o': case 'O':
4476                                    if (tex_scan_mandate_keyword("source", 2)) {
4477                                        noad_source(fraction) = tex_scan_integer(0, NULL, NULL);
4478                                    }
4479                                    break;
4480                                case 'y': case 'Y':
4481                                    if (tex_scan_mandate_keyword("symbolattr", 2)) {
4482                                        symbolattrlist = tex_scan_extra_attribute(symbolattrlist);
4483                                    }
4484                                    break;
4485                                default:
4486                                    tex_aux_show_keyword_error("style|source|symbolattr");
4487                                    goto DONE;
4488                            }
4489                            break;
4490                        case 't': case 'T':
4491                            if (tex_scan_mandate_keyword("thickness", 1)) {
4492                                ruledone = 1;
4493                                rulethickness = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
4494                            }
4495                            break;
4496                        case 'u': case 'U':
4497                            if (tex_scan_mandate_keyword("usecallback", 1)) {
4498                                ruledone = 1;
4499                                options |= noad_option_use_callback;
4500                            }
4501                            break;
4502                        case 'v': case 'V':
4503                            if (tex_scan_mandate_keyword("vfactor", 1)) {
4504                                fraction_v_factor(fraction) = tex_scan_integer(0, NULL, NULL);
4505                            }
4506                            break;
4507                        default:
4508                            goto DONE;
4509                    }
4510                }
4511              DONE:
4512                if (! ruledone) {
4513                    rulethickness = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
4514                }
4515                break;
4516        }
4517        fraction_rule_thickness(fraction) = rulethickness;
4518        noad_options(fraction) = options;
4519        set_noad_main_class(fraction, mathclass);
4520        if (attrlist) {
4521            tex_attach_attribute_list_attribute(fraction, attrlist);
4522        }
4523        if (symbolattrlist) { 
4524            add_attribute_reference(symbolattrlist);
4525            noad_extra_attr(fraction) = symbolattrlist;
4526        }
4527        if (denominator) {
4528            /*tex
4529                In this case we need to pick up two math groups, and after some playing around using
4530                a variant of choices made most sense.
4531            */
4532            saved_fraction_initialize();
4533            saved_fraction_variant = math_numerator_above;
4534            saved_fraction_autostyle = autostyle; 
4535            saved_fraction_userstyle = userstyle; 
4536            lmt_save_state.save_stack_data.ptr += saved_fraction_n_of_records;
4537            cur_list.math_flatten = 0;
4538            tex_aux_push_math(math_fraction_group, autostyle, -1);
4539            tex_scan_left_brace();
4540        } else {
4541            /*tex
4542                This is the pre/post variant. Actually, this variant is the reason why math scanning
4543                code is somewhat complex, this |incomplete_noad| stuff.
4544            */
4545        }
4546    }
4547}
4548
4549/*tex
4550
4551    At the end of a math formula or subformula, the |finish_math_list| routine is called upon to
4552    return a halfword to the newly completed mlist, and to pop the nest back to the enclosing
4553    semantic level. The parameter to |finish_math_list|, if not null, points to a |fence_noad| that
4554    ends the current mlist; this |fence_noad| has not yet been appended.
4555
4556*/
4557
4558static halfword tex_aux_finish_math_list(halfword p)
4559{
4560    halfword q = null;
4561    /*tex So now we're ready to proceed. */
4562    if (cur_list.incomplete_noad) {
4563        halfword denominator = fraction_denominator(cur_list.incomplete_noad);
4564        if (denominator) {
4565            node_type(denominator) = sub_mlist_node;
4566        } else {
4567            denominator = tex_new_node(sub_mlist_node, 0);
4568            fraction_denominator(cur_list.incomplete_noad) = denominator;
4569            q = denominator;
4570        }
4571        kernel_math_list(denominator) = node_next(cur_list.head);
4572        if (p) {
4573            halfword numerator = fraction_numerator(cur_list.incomplete_noad);
4574            q = kernel_math_list(numerator);
4575            if ((node_type(q) != fence_noad) || (node_subtype(q) != left_fence_side) || (! cur_list.delimiter)) {
4576                tex_confusion("right fence");
4577            }
4578            kernel_math_list(numerator) = node_next(cur_list.delimiter);
4579            node_next(cur_list.delimiter) = cur_list.incomplete_noad;
4580            node_next(cur_list.incomplete_noad) = p;
4581        } else {
4582            q = cur_list.incomplete_noad;
4583        }
4584    } else {
4585        node_next(cur_list.tail) = p;
4586        q = node_next(cur_list.head);
4587    }
4588    tex_pop_nest();
4589    return q;
4590}
4591
4592/*tex
4593    Here traditional \TEX\ does some flattening but it can interfrere. It is for instance needed
4594    in order to find the skew of an accented character which happens at the outer level but that
4595    bit of code now does that recursively. I need to check why the accent was flattened so we 
4596    keep the original code here for testing.
4597
4598    A \CONTEXT\ test case: |$\tilde{x}'$| i.e.\ primes!
4599*/
4600
4601static void tex_aux_flatten_math_list(halfword parent)
4602{
4603    halfword p = kernel_math_list(parent);
4604    if (p && ! node_next(p)) {
4605        switch (node_type(p)) {
4606            case simple_noad:
4607                {
4608                    // how about the options and class
4609                    if (! noad_has_following_scripts(p) && tex_math_has_class_option(node_subtype(p), flatten_class_option) && ! noad_source(p)) {
4610                        halfword n = noad_nucleus(p);
4611                        halfword s = parent;
4612                        node_type(s) = node_type(n);
4613                        tex_math_copy_char_data(s, n, 1);
4614                        tex_attach_attribute_list_copy(s, n);
4615                        tex_flush_node(p);
4616                    }
4617                    break;
4618                }
4619            case accent_noad:
4620                {
4621                    halfword tail = cur_list.tail;
4622                    if (saved_math_group_pointer == noad_nucleus(tail) && node_type(tail) == simple_noad) {
4623                        switch (node_subtype(tail)) {
4624                            case ordinary_noad_subtype:
4625                                tex_couple_nodes(node_prev(tail), p);
4626                                noad_nucleus(tail) = null;
4627                                noad_subscr(tail) = null;
4628                                noad_supscr(tail) = null;
4629                                noad_prime(tail) = null;
4630                                tex_attach_attribute_list_copy(p, tail);
4631                                tex_flush_node(tail);
4632                                cur_list.tail = p;
4633                                break;
4634                        }
4635                    }
4636                    break;
4637                }
4638        }
4639    }
4640}
4641
4642/*tex
4643
4644    Now at last we're ready to see what happens when a right brace occurs in a math formula. Two
4645    special cases are simplified here: braces are effectively removed when they surround a single
4646    Ord without sub- and/or superscripts, or when they surround an accent that is the nucleus of
4647    an Ord atom.
4648
4649*/
4650
4651void tex_finish_math_group(void)
4652{
4653    int old_style = cur_list.math_style;
4654    halfword p, parent;
4655    quarterword allclass; 
4656    tex_aux_unsave_math();
4657    lmt_save_state.save_stack_data.ptr -= saved_math_group_n_of_records;
4658    parent = saved_math_group_pointer;
4659    allclass = (quarterword) saved_math_group_all_class;
4660    node_type(parent) = sub_mlist_node; /* can be math_char_node */
4661    p = tex_aux_finish_math_list(null); /* this incomplete trickery */
4662    kernel_math_list(parent) = p;
4663    if (cur_list.math_flatten) {
4664        tex_aux_flatten_math_list(parent);
4665    }
4666    /*tex
4667        If needed, here we pickup a next \quote {argument}, so we sort of finish a group and reopen
4668        a new one. It is somewhat curious that we use a character node here.
4669    */
4670    if (allclass != unset_noad_class) {
4671        while (p) {
4672            if (node_type(p) == simple_noad) { 
4673             // node_subtype(p) = allclass; 
4674                if (get_noad_main_class(p) == unset_noad_class) {
4675                    set_noad_main_class(p, allclass); 
4676                }
4677                if (get_noad_left_class(p) == unset_noad_class) {
4678                    set_noad_left_class(p, allclass); 
4679                }
4680                if (get_noad_right_class(p) == unset_noad_class) {
4681                    set_noad_right_class(p, allclass); 
4682                }
4683            }
4684            p = node_next(p);
4685        }
4686        /* */
4687    }
4688    if (node_next(saved_math_group_pointer) > 0) {
4689        halfword q = tex_new_node(math_char_node, 0); /* hm */
4690        noad_nucleus(node_next(saved_math_group_pointer)) = q;
4691        node_next(saved_math_group_pointer) = null;
4692        saved_math_group_pointer = q;
4693        tex_aux_scan_math(q, old_style, 0, 0, 0, 0, unset_noad_class, unset_noad_class);
4694        /*tex restart */
4695    }
4696}
4697
4698/*tex
4699
4700    We have dealt with all constructions of math mode except |\left| and |\right|, so the picture is
4701    completed by the following sections of the program. The |middle| feature of \ETEX\ allows one or
4702    several |\middle| delimiters to appear between |\left| and |\right|.
4703
4704*/
4705
4706void tex_run_math_fence(void)
4707{
4708    scaled ht = 0;
4709    scaled dp = 0;
4710    scaled top = 0;
4711    scaled bottom = 0;
4712    fullword options = 0;
4713    halfword variant = 0;
4714    halfword mainclass = unset_noad_class;
4715    halfword leftclass = unset_noad_class;
4716    halfword rightclass = unset_noad_class;
4717    halfword source = 0;
4718    halfword factor = scaling_factor;
4719    halfword attrlist = null;
4720    halfword symbolattrlist = null;
4721    quarterword st = (quarterword) cur_chr;
4722    halfword style = cur_list.math_style;
4723    halfword call = tex_tail_fetch_callback();
4724    if (math_check_fences_par) { 
4725        options |= noad_option_no_check;
4726    }
4727    switch (st) {
4728        case left_operator_side:
4729        case no_fence_side:
4730            break;
4731        case extended_left_fence_side:   /*tex |\Uleft| */
4732            st = left_fence_side;
4733            break;
4734        case extended_middle_fence_side: /*tex |\Umiddle| */
4735            st = middle_fence_side;
4736            break;
4737        case extended_right_fence_side:  /*tex |\Uright| */
4738            st = right_fence_side;
4739            break;
4740        default :
4741            goto CHECK_PAIRING;
4742    }
4743    while (1) {
4744        /* todo: break down  */
4745        switch (tex_scan_character("abcdefhlmnprstuvABCDEFHLMNPRSTUV", 0, 1, 0)) {
4746            case 0:
4747                goto CHECK_PAIRING;
4748            case 'a': case 'A':
4749                switch (tex_scan_character("tuxTUX", 0, 0, 0)) {
4750                    case 't': case 'T':
4751                        if (tex_scan_mandate_keyword("attr", 2)) {
4752                            attrlist = tex_scan_attribute(attrlist);
4753                        }
4754                        break;
4755                    case 'u': case 'U':
4756                        if (tex_scan_mandate_keyword("auto", 2)) {
4757                            options |= noad_option_auto;
4758                        }
4759                        break;
4760                    case 'x': case 'X':
4761                        if (tex_scan_mandate_keyword("axis", 2)) {
4762                            options |= noad_option_axis;
4763                        }
4764                        break;
4765                    default:
4766                        tex_aux_show_keyword_error("auto|attr|axis");
4767                        goto CHECK_PAIRING;
4768                }
4769                break;
4770            case 'b': case 'B':
4771                if (tex_scan_mandate_keyword("bottom", 1)) {
4772                    bottom = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
4773                }
4774                break;
4775            case 'c': case 'C':
4776                if (tex_scan_mandate_keyword("class", 1)) {
4777                    mainclass = tex_scan_math_class_number(0);
4778                }
4779                break;
4780            case 'd': case 'D':
4781                if (tex_scan_mandate_keyword("depth", 1)) {
4782                    dp = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
4783                }
4784                break;
4785            case 'e': case 'E':
4786                if (tex_scan_mandate_keyword("exact", 1)) {
4787                    options |= noad_option_exact;
4788                }
4789                break;
4790            case 'f': case 'F':
4791                if (tex_scan_mandate_keyword("factor", 1)) {
4792                    factor = tex_scan_integer(0, NULL, NULL);
4793                }
4794                break;
4795            case 'h': case 'H':
4796                if (tex_scan_mandate_keyword("height", 1)) {
4797                    ht = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
4798                }
4799                break;
4800            case 'l': case 'L':
4801                switch (tex_scan_character("eiEI", 0, 0, 0)) {
4802                    case 'e': case 'E':
4803                        if (tex_scan_mandate_keyword("leftclass", 2)) {
4804                            halfword c = tex_scan_math_class_number(0);
4805                         // if (! valid_math_class_code(c)) {
4806                            if (valid_math_class_code(c)) {
4807                                leftclass = c;
4808                            }
4809                        }
4810                        break;
4811                    case 'i': case 'I':
4812                        if (tex_scan_mandate_keyword("limits", 2)) {
4813                            options = unset_option(options, noad_option_no_limits);
4814                            options |= noad_option_limits;
4815                        }
4816                        break;
4817                    default:
4818                        tex_aux_show_keyword_error("leftclass|limits");
4819                        goto CHECK_PAIRING;
4820                }
4821                break;
4822            case 'n': case 'N':
4823                switch (tex_scan_character("oO", 0, 0, 0)) {
4824                    case 'o': case 'O':
4825                        switch (tex_scan_character("acloACLO", 0, 0, 0)) {
4826                            case 'a': case 'A':
4827                                if (tex_scan_mandate_keyword("noaxis", 3)) {
4828                                    options |= noad_option_no_axis;
4829                                }
4830                                break;
4831                            case 'c': case 'C':
4832                                if (tex_scan_mandate_keyword("nocheck", 3)) {
4833                                    options |= noad_option_no_check;
4834                                }
4835                                break;
4836                            case 'l': case 'L':
4837                                if (tex_scan_mandate_keyword("nolimits", 3)) {
4838                                    options = unset_option(options, noad_option_limits);
4839                                    options |= noad_option_no_limits;
4840                                }
4841                                break;
4842                            case 'o': case 'O':
4843                                if (tex_scan_mandate_keyword("nooverflow", 3)) {
4844                                    options |= noad_option_no_overflow;
4845                                }
4846                                break;
4847                            default:
4848                                tex_aux_show_keyword_error("noaxis|nolimits|nocheck|nooverflow");
4849                                goto CHECK_PAIRING;
4850                        }
4851                        break;
4852                    default:
4853                        goto CHECK_PAIRING;
4854                }
4855                break;
4856            case 'm': case 'M':
4857                if (tex_scan_mandate_keyword("middle", 1)) {
4858                    options |= noad_option_auto_middle;
4859                }
4860                break;
4861            case 'p': case 'P':
4862                if (tex_scan_mandate_keyword("phantom", 1)) {
4863                    options |= noad_option_phantom;
4864                }
4865                break;
4866            case 'r': case 'R':
4867                if (tex_scan_mandate_keyword("rightclass", 1)) {
4868                    halfword c = tex_scan_math_class_number(0);
4869                 // if (valid_math_class_code(c)) {
4870                    if (valid_math_class_code(c)) {
4871                        rightclass = c;
4872                    }
4873                }
4874                break;
4875            case 's': case 'S':
4876                switch (tex_scan_character("cioyCIOY", 0, 0, 0)) {
4877                    case 'c': case 'C':
4878                        if (tex_scan_mandate_keyword("scale", 2)) {
4879                            options |= noad_option_scale;
4880                        }
4881                        break;
4882                    case 'i': case 'I':
4883                        if (tex_scan_mandate_keyword("single", 2)) {
4884                            options |= noad_option_single;
4885                        }
4886                        break;
4887                    case 'o': case 'O':
4888                        if (tex_scan_mandate_keyword("source", 2)) {
4889                            source = tex_scan_integer(0, NULL, NULL);
4890                        }
4891                        break;
4892                    case 'y': case 'Y':
4893                        if (tex_scan_mandate_keyword("symbolattr", 2)) {
4894                            symbolattrlist = tex_scan_extra_attribute(symbolattrlist);
4895                        }
4896                        break;
4897                    default:
4898                        tex_aux_show_keyword_error("scale|source|single|size|symbolattr");
4899                        goto CHECK_PAIRING;
4900                }
4901                break;
4902            case 't': case 'T':
4903                if (tex_scan_mandate_keyword("top", 1)) {
4904                    top = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
4905                }
4906                break;
4907            case 'u': case 'U':
4908                if (tex_scan_mandate_keyword("usecallback", 1)) {
4909                    options |= noad_option_use_callback;
4910                }
4911                break;
4912            case 'v': case 'V':
4913                switch (tex_scan_character("aoAO", 0, 0, 0)) {
4914                    case 'a': case 'A':
4915                        if (tex_scan_mandate_keyword("variant", 2)) {
4916                            variant = tex_scan_integer(0, NULL, NULL);
4917                        }
4918                        break;
4919                    case 'o': case 'O':
4920                        if (tex_scan_mandate_keyword("void", 2)) {
4921                            options |= noad_option_void;
4922                        }
4923                        break;
4924                    default:
4925                        tex_aux_show_keyword_error("void|variant");
4926                        goto CHECK_PAIRING;
4927                }
4928                break;
4929            default:
4930                goto CHECK_PAIRING;
4931        }
4932    }
4933  CHECK_PAIRING:
4934    switch (st) {
4935        case no_fence_side:
4936        case left_fence_side:
4937            break;
4938        case left_operator_side:
4939            {
4940                /* becomes a class option */
4941                int indisplay = style == display_style || style == cramped_display_style;
4942                /* options |= noad_option_no_check; */ /*tex Best just expect a dummy right. */
4943                if (! (has_option(options, noad_option_limits) || has_option(options, noad_option_no_limits))) {
4944                    /* otherwise we don't enter the placement function */
4945                    options |= indisplay ? noad_option_limits : noad_option_no_limits;
4946                }
4947            }
4948            break;
4949        default:
4950            if (cur_group != math_fence_group) {
4951                tex_aux_append_math_fence_val(tex_no_math_code(), tex_no_dict_code(), open_noad_subtype);
4952            }
4953            switch (cur_group) {
4954                case math_fence_group:
4955                    break;
4956                case math_inline_group:
4957                case math_display_group:
4958                case math_number_group:
4959                    tex_aux_scan_delimiter(null, no_mathcode, unset_noad_class);
4960                    if (st == middle_fence_side) {
4961                        tex_handle_error(
4962                            normal_error_type,
4963                            "Extra \\middle",
4964                            "I'm ignoring a \\middle that had no matching \\left."
4965                        );
4966                    } else {
4967                        tex_handle_error(
4968                            normal_error_type,
4969                            "Extra \\right",
4970                            "I'm ignoring a \\right that had no matching \\left."
4971                        );
4972                    }
4973                    break;
4974                default:
4975                    tex_off_save();
4976            }
4977    }
4978    /*tex
4979        Now we only have a no, left, middle or right case left.
4980    */
4981    {
4982        halfword fence = tex_new_node(fence_noad, st);
4983        halfword delimiter = tex_new_node(delimiter_node, 0);
4984        halfword autoclass = unset_noad_class;
4985        fence_delimiter(fence) = delimiter;
4986        fence_delimiter_variant(fence) = variant;
4987        noad_height(fence) = ht;
4988        noad_depth(fence) = dp;
4989        noad_options(fence) = options;
4990        tex_set_noad_classes(fence, mainclass);
4991        if (leftclass != unset_noad_class) {
4992            set_noad_left_class(fence, leftclass);
4993        }
4994        if (rightclass != unset_noad_class) {
4995            set_noad_right_class(fence, rightclass);
4996        }
4997        noad_italic(fence) = 0;
4998        noad_source(fence) = source;
4999        /* */
5000        fence_top_overshoot(fence) = top;
5001        fence_bottom_overshoot(fence) = bottom;
5002        /* */
5003        if (symbolattrlist) { 
5004            add_attribute_reference(symbolattrlist);
5005            noad_extra_attr(fence) = symbolattrlist;
5006        }
5007        /*tex
5008            By setting this here, we can get rid of the hard coded values in |mlist_to_hlist| which
5009            sort of interfere (or at least confuse) things there. When set, the |leftclass| and
5010            |rightclass| settings win anyway.
5011        */
5012        if (mainclass == unset_noad_class) {
5013            mainclass = node_subtype(delimiter);
5014            if (mainclass == unset_noad_class || mainclass == ordinary_noad_subtype) {
5015                switch (st) {
5016                    case left_fence_side:
5017                        mainclass = open_noad_subtype;
5018                        break;
5019                    case middle_fence_side:
5020                        mainclass = middle_noad_subtype;
5021                        break;
5022                    case right_fence_side:
5023                        mainclass = close_noad_subtype;
5024                        break;
5025                }
5026            }
5027            set_noad_main_class(fence, mainclass);
5028        }
5029        /* */
5030        switch (st) {
5031            case left_fence_side:
5032                autoclass = open_noad_subtype;
5033                break;
5034            case middle_fence_side:
5035                autoclass = middle_noad_subtype; /* we need a way to overload this */
5036                break;
5037            case right_fence_side:
5038                autoclass = close_noad_subtype;
5039                break;
5040        }
5041        /* */
5042        tex_aux_scan_delimiter(delimiter, no_mathcode, autoclass);
5043        /* */
5044        if (attrlist) {
5045            tex_attach_attribute_list_attribute(fence, attrlist);
5046            tex_attach_attribute_list_attribute(delimiter, attrlist);
5047        }
5048        switch (st) {
5049            case left_fence_side:
5050                tex_aux_append_math_fence(fence, open_noad_subtype);
5051                fence_nesting_factor(fence) = factor;
5052                break;
5053            case middle_fence_side:
5054                tex_aux_append_math_fence(fence, middle_noad_subtype);
5055                break;
5056            case right_fence_side:
5057                tex_aux_append_math_fence(fence, close_noad_subtype);
5058                fence_nesting_factor(fence) = factor;
5059                break;
5060            case left_operator_side:
5061                {
5062                    tex_aux_push_math(math_fence_group, style, -1);
5063                    node_next(cur_list.head) = fence;
5064                    cur_list.tail = fence;
5065                    cur_list.delimiter = fence;
5066                    saved_operator_initialize();
5067                    saved_operator_variant = math_limits_top;
5068                    lmt_save_state.save_stack_data.ptr += saved_operator_n_of_records;
5069                    tex_aux_push_math(math_operator_group, tex_math_style_variant(style, math_parameter_superscript_variant), -1);
5070                    tex_scan_left_brace();
5071                }
5072                break;
5073            case no_fence_side:
5074                {
5075                    halfword n = tex_new_node(simple_noad, fenced_noad_subtype);
5076                    halfword l = tex_new_node(sub_mlist_node, 0);
5077                    if (attrlist) {
5078                        tex_attach_attribute_list_attribute(n, attrlist);
5079                        tex_attach_attribute_list_attribute(l, attrlist);
5080                    }
5081                    tex_tail_append(n);
5082                    set_noad_main_class(n, mainclass); /*tex Really needed here! */
5083                    noad_nucleus(n) = l;
5084                    kernel_math_list(noad_nucleus(n)) = fence;
5085                }
5086                break;
5087            default:
5088                tex_confusion("left right fence");
5089                break;
5090        }
5091        if (! tex_tail_apply_callback(fence, call)) {
5092            tex_confusion("messed up fence");
5093        }
5094    }
5095}
5096
5097/*tex
5098
5099    \TEX\ gets to the following part of the program when the first |$| ending a display has been
5100    scanned.
5101
5102*/
5103
5104static void tex_aux_check_second_math_shift(void)
5105{
5106    tex_get_x_token();
5107    if (cur_cmd != math_shift_cmd) {
5108        tex_back_input(cur_tok);
5109        tex_handle_error(
5110            normal_error_type,
5111            "Display math should end with $$",
5112            "The '$' that I just saw supposedly matches a previous '$$'. So I shall assume\n"
5113            "that you typed '$$' both times."
5114        );
5115    }
5116}
5117
5118static void tex_aux_check_display_math_end(void)
5119{
5120    switch (cur_chr) { 
5121        case end_display_math_code:
5122        case end_math_mode_code:
5123            return;
5124    }
5125    tex_handle_error(
5126        normal_error_type,
5127        "Display math should end with \\Ustopdisplaymath or \\Ustopmathmode",
5128        "I shall assume that you typed that."
5129    );
5130}
5131
5132static void tex_aux_check_inline_math_end(void)
5133{
5134    switch (cur_chr) { 
5135        case end_inline_math_code:
5136        case end_math_mode_code:
5137            return;
5138    }
5139    tex_handle_error(
5140        normal_error_type,
5141        "Inline math should end with \\Ustopmath or \\Ustopmathmode",
5142        "I shall assume that you typed that."
5143    );
5144}
5145
5146static void tex_aux_resume_after_display(void)
5147{
5148    switch (cur_group) {
5149        case math_display_group:
5150            tex_aux_unsave_math();
5151            cur_list.prev_graf += 3;
5152            tex_push_nest();
5153            cur_list.mode = hmode;
5154            cur_list.space_factor = default_space_factor;
5155            /*tex 
5156                This needs to be intercepted in the display math start! Todo! Why do we have this 
5157                anyway. 
5158            */
5159            tex_tail_append(tex_new_par_node(parameter_par_subtype));
5160            tex_get_x_token();
5161            if (cur_cmd != spacer_cmd) {
5162                tex_back_input(cur_tok);
5163            }
5164            if (lmt_nest_state.nest_data.ptr == 1) {
5165                tex_build_page(after_display_page_context, 0);
5166            }
5167            break;
5168        default:    
5169            tex_confusion("finishing display math");
5170            break;
5171    }
5172}
5173
5174/*tex
5175
5176    The fuziest part of math mode processing occurs when a displayed formula is being centered and
5177    placed with an optional equation number. At this time we are in vertical mode (or internal
5178    vertical mode).
5179
5180    \starttabulate
5181    \NC \type {p} \NC points to the mlist for the formula \NC \NR
5182    \NC \type {a} \NC is either |null| or it points to a box containing the equation number \NC \NR
5183    \NC \type {l} \NC is true if there was an |\leqno| (so |a| is a horizontal box) \NC \NR
5184    \stoptabulate
5185
5186    Per 2022 we ditched display mode in \CONTEXT\ LMTX\ so the code related to display math is now
5187    completely frozen, if only because testing has become unreasonable. There is anyway not much more
5188    to do here.
5189
5190*/
5191
5192static void tex_aux_inject_display_skip(quarterword param, quarterword subtype)
5193{
5194    if (param > 0) {
5195        switch (display_skip_mode_par) {
5196            case display_skip_default :
5197            case display_skip_always :
5198                break;
5199            case display_skip_non_zero:
5200                if (tex_glue_is_zero(glue_parameter(param))) {
5201                    return;
5202                } else {
5203                    break;
5204                }
5205            case display_skip_ignore:
5206                return;
5207            default:
5208                /*tex > 3 reserved for future use */
5209                break;
5210        }
5211        tex_tail_append(tex_new_param_glue_node(param, subtype));
5212    }
5213}
5214
5215static void tex_aux_finish_displayed_math(int atleft, halfword eqnumber, halfword equation)
5216{
5217    /*tex box containing the equation */
5218    halfword equation_box;
5219    /*tex width of the equation */
5220    scaled equation_width;
5221    /*tex width of the line */
5222    scaled line_width;
5223    /*tex width of equation number */
5224    scaled number_width;
5225    /*tex width of equation number plus space to separate from equation */
5226    scaled number_plus_gap_width;
5227    /*tex move the line right this much */
5228    scaled indent;
5229    /*tex displacement of equation in the line */
5230    scaled displacement;
5231    /*tex glue parameter codes for before and after */
5232    quarterword glue_above, glue_below;
5233    /*tex glue parameter subtypes for before and after */
5234    quarterword subtype_above, subtype_below;
5235    /*tex for equation numbers */
5236    scaled eqno_width;
5237    /*tex true if the math and surrounding (par) dirs are different */
5238    int swap_dir = math_direction_par != pre_display_direction_par;
5239    if (eqnumber && swap_dir) {
5240        atleft = ! atleft;
5241    }
5242    /* */
5243    lmt_packaging_state.post_adjust_tail = post_adjust_head;
5244    lmt_packaging_state.pre_adjust_tail = pre_adjust_head;
5245    lmt_packaging_state.post_migrate_tail = post_migrate_head;
5246    lmt_packaging_state.pre_migrate_tail = pre_migrate_head;
5247    /* */
5248    equation_box = tex_hpack(equation, 0, packing_additional, direction_unknown, holding_none_option, box_limit_none);
5249    node_subtype(equation_box) = equation_list;
5250    attach_current_attribute_list(equation_box);
5251    equation = box_list(equation_box);
5252    /* */
5253    equation_width = box_width(equation_box);
5254    line_width = display_width_par;
5255    indent = display_indent_par;
5256    if (eqnumber) {
5257        number_width = box_width(eqnumber);
5258        eqno_width = number_width;
5259        number_plus_gap_width = number_width + tex_round_xn_over_d(math_eqno_gap_step_par, tex_get_math_quad_style(text_style), scaling_factor);
5260        node_subtype(eqnumber) = equation_number_list;
5261        /*tex attach_current_attribute_list(eqno_box); */
5262    } else {
5263        number_width = 0;
5264        eqno_width = 0;
5265        number_plus_gap_width = 0;
5266    }
5267    if (equation_width + number_plus_gap_width > line_width) {
5268        /*tex
5269
5270            The user can force the equation number to go on a separate line by causing its width to
5271            be zero.
5272
5273        */
5274        if ((number_width != 0) && ((equation_width - lmt_packaging_state.total_shrink[normal_glue_order] + number_plus_gap_width <= line_width)
5275                || (lmt_packaging_state.total_shrink[fi_glue_order] != 0)
5276                || (lmt_packaging_state.total_shrink[fil_glue_order] != 0)
5277                || (lmt_packaging_state.total_shrink[fill_glue_order] != 0)
5278                || (lmt_packaging_state.total_shrink[filll_glue_order] != 0))) {
5279            box_list(equation_box) = null;
5280            tex_flush_node(equation_box);
5281            equation_box = tex_hpack(equation, line_width - number_plus_gap_width, packing_exactly, direction_unknown, holding_none_option, box_limit_none);
5282            node_subtype(equation_box) = equation_list;
5283            attach_current_attribute_list(equation_box);
5284        } else {
5285            number_width = 0;
5286            if (equation_width > line_width) {
5287                box_list(equation_box) = null;
5288                tex_flush_node(equation_box);
5289                equation_box = tex_hpack(equation, line_width, packing_exactly, direction_unknown, holding_none_option, box_limit_none);
5290                node_subtype(equation_box) = equation_list;
5291                attach_current_attribute_list(equation_box);
5292            }
5293        }
5294        equation_width = box_width(equation_box);
5295    }
5296    /*tex
5297
5298        We try first to center the display without regard to the existence of the equation number.
5299        If that would make it too close (where \quote {too close} means that the space between
5300        display and equation number is less than the width of the equation number), we either
5301        center it in the remaining space or move it as far from the equation number as possible.
5302        The latter alternative is taken only if the display begins with glue, since we assume that
5303        the user put glue there to control the spacing precisely.
5304
5305    */
5306    displacement = tex_half_scaled(line_width - equation_width);
5307    if ((number_width > 0) && (displacement < 2 * number_width)) {
5308        /*tex too close */
5309        displacement = tex_half_scaled(line_width - equation_width - number_width);
5310        /*
5311        if (p && !is_char_node(p) && node_type(p) == glue_node)
5312            d = 0;
5313        */ /* kind of weird this, so why not just */
5314        if (equation && node_type(equation) == glue_node) {
5315            displacement = 0;
5316        }
5317    }
5318    tex_tail_append(tex_new_penalty_node(pre_display_penalty_par, before_display_penalty_subtype));
5319    if ((displacement + indent <= pre_display_size_par) || ((cur_list.math_dir == dir_lefttoright) &&   atleft)
5320                                                        || ((cur_list.math_dir == dir_righttoleft) && ! atleft)) {
5321        /*tex not enough clearance */
5322        glue_above = above_display_skip_code;
5323        subtype_above = above_display_skip_glue;
5324        glue_below = below_display_skip_code;
5325        subtype_below = below_display_skip_glue;
5326    } else {
5327        glue_above = above_display_short_skip_code;
5328        subtype_above = above_display_short_skip_glue;
5329        glue_below = below_display_short_skip_code;
5330        subtype_below = below_display_short_skip_glue;
5331    }
5332    /*tex
5333
5334        If the equation number is set on a line by itself, either before or after the formula, we
5335        append an infinite penalty so that no page break will separate the display from its number;
5336        and we use the same size and displacement for all three potential lines of the display,
5337        even though |\parshape| may specify them differently; |\leqno| on a forced single line due
5338        to |width=0|; it follows that |type(a) = hlist_node|.
5339
5340    */
5341    if (eqnumber && atleft && (number_width == 0)) {
5342     /* if (math_direction_par == dir_lefttoright) { */
5343            box_shift_amount(eqnumber) = 0;
5344     /* } else { */
5345     /* } */
5346        tex_append_to_vlist(eqnumber, lua_key_index(equation_number), NULL);
5347        tex_tail_append(tex_new_penalty_node(infinite_penalty, equation_number_penalty_subtype));
5348    } else {
5349        tex_aux_inject_display_skip(glue_above, subtype_above);
5350    }
5351    if (number_width != 0) {
5352        /*tex
5353            This has been brought in sync with \LUATEX\ 2025 but I can't test it right now. I'll 
5354            check it when there is a use case. 
5355        */
5356        scaled shift = line_width - equation_width - number_width - displacement;
5357        halfword move = tex_new_kern_node(shift, explicit_kern_subtype);
5358        if (atleft) {
5359            scaled amount; 
5360            if (math_direction_par == dir_lefttoright) {
5361                /*tex TRT + TLT + \eqno : (swap_dir=true,  math_direction_par=TLT, l=true) */
5362                /*tex TLT + TLT + \leqno: (swap_dir=false, math_direction_par=TLT, l=true) */
5363                amount = shift + number_width;
5364            } else {
5365                /*tex TLT + TRT + \eqno : (swap_dir=true,  math_direction_par=TRT, l=true) */
5366                /*tex TRT + TRT + \leqno: (swap_dir=false, math_direction_par=TRT, l=true) */
5367                amount = line_width - equation_width;
5368            }
5369            { 
5370                halfword kern = tex_new_kern_node(amount, explicit_kern_subtype);
5371                tex_try_couple_nodes(eqnumber, move);
5372                tex_try_couple_nodes(move, equation_box);
5373                tex_try_couple_nodes(equation_box, kern);
5374                equation_box = eqnumber;
5375            }
5376        } else {
5377            scaled amount; 
5378            if (swap_dir) {
5379                if (math_direction_par == dir_lefttoright) {
5380                    /*tex TRT + TLT + \leqno: (swap_dir=true,  math_direction_par=TLT, l=false) */
5381                    amount = line_width - equation_width;
5382                } else {
5383                    /*tex TLT + TRT + \leqno: (swap_dir=true,  math_direction_par=TRT, l=false) */
5384                    amount = shift + equation_width;
5385                }
5386            } else {
5387                if (math_direction_par == dir_lefttoright) {
5388                    /*tex TLT + TLT + \eqno : (swap_dir=false, math_direction_par=TLT, l=false) */
5389                    amount = displacement;
5390                } else {
5391                    /*tex TRT + TRT + \eqno : (swap_dir=false, math_direction_par=TRT, l=false) */
5392                    amount = shift + number_width;
5393                }
5394            }
5395            { 
5396                halfword kern = tex_new_kern_node(amount, explicit_kern_subtype);
5397                tex_try_couple_nodes(kern, equation_box);
5398                tex_try_couple_nodes(equation_box, move);
5399                tex_try_couple_nodes(move, eqnumber);
5400                equation_box = kern;
5401            }
5402        }
5403        equation_box = tex_hpack(equation_box, 0, packing_additional, direction_unknown, holding_none_option, box_limit_none);
5404        node_subtype(equation_box) = equation_list; /* new */
5405        attach_current_attribute_list(equation_box);
5406        box_shift_amount(equation_box) = indent;
5407    } else {
5408        box_shift_amount(equation_box) = indent + displacement;
5409    }
5410    /* */
5411    if (pre_adjust_head != lmt_packaging_state.pre_adjust_tail) {
5412        tex_inject_adjust_list(pre_adjust_head, 1, lmt_linebreak_state.just_box, NULL);
5413    }
5414    lmt_packaging_state.pre_adjust_tail = null;
5415    /* Pre-migrate content (callback). */
5416    if (pre_migrate_head != lmt_packaging_state.pre_migrate_tail) {
5417        tex_append_list(pre_migrate_head, lmt_packaging_state.pre_migrate_tail);
5418     // if (! lmt_page_builder_state.output_active) {
5419     //     lmt_append_line_filter_callback(pre_migrate_append_line_context, 0);
5420     // }
5421    }
5422    /* */
5423    tex_append_to_vlist(equation_box, lua_key_index(equation), NULL); /* eqbox has the formula */
5424    if (eqnumber && number_width == 0 && ! atleft) {
5425        tex_tail_append(tex_new_penalty_node(infinite_penalty, equation_number_penalty_subtype));
5426     /* if (math_direction_par == dir_lefttoright) { */
5427            box_shift_amount(eqnumber) = indent + line_width - eqno_width ;
5428     /* } else { */
5429     /* } */
5430        tex_append_to_vlist(eqnumber, lua_key_index(equation_number), NULL);
5431        glue_below = 0; /* shouldn't this be an option */
5432    }
5433    /* */
5434    if (post_migrate_head != lmt_packaging_state.post_migrate_tail) {
5435        tex_append_list(post_migrate_head, lmt_packaging_state.post_migrate_tail);
5436        // if (! lmt_page_builder_state.output_active) {
5437        //     lmt_append_line_filter_callback(post_migrate_append_line_context, 0);
5438        // }
5439    }
5440    lmt_packaging_state.post_migrate_tail = null;
5441    if (lmt_packaging_state.post_adjust_tail) {
5442        if (post_adjust_head != lmt_packaging_state.post_adjust_tail) {
5443            tex_inject_adjust_list(post_adjust_head, 1, null, NULL);
5444        }
5445        lmt_packaging_state.post_adjust_tail = null;
5446    }
5447    lmt_packaging_state.except = 0;
5448    /* */
5449    tex_tail_append(tex_new_penalty_node(post_display_penalty_par, after_display_penalty_subtype));
5450    tex_aux_inject_display_skip(glue_below, subtype_below);
5451    tex_aux_resume_after_display();
5452}
5453
5454/*tex
5455
5456    A |math_node|, which occurs only in horizontal lists, appears before and after mathematical
5457    formulas. The |subtype| field is |before| before the formula and |after| after it. There is a
5458    |surround| field, which represents the amount of surrounding space inserted by |\mathsurround|.
5459
5460    As an outcome of the math upgrading sub project that Mikael Sundqvist and I undertook end 2021
5461    and beginning 2022 Mikael suggested penalties surrounding inline formulas so there you have it:
5462    |\preinlinepanelty| and |\postinlinepanelty|.
5463
5464*/
5465
5466/* make proper mappers (see 5967 in texmlist) */
5467
5468static inline int tex_aux_class_from_glyph(halfword n) 
5469{
5470    return node_subtype(n) - (node_subtype(n) > glyph_math_extra_subtype ? glyph_math_extra_subtype : glyph_math_ordinary_subtype);
5471}
5472
5473static inline int tex_aux_class_from_list(halfword n) 
5474{
5475    switch (node_subtype(n)) { 
5476        case math_fraction_list: 
5477            return fraction_noad_subtype;
5478        case math_accent_list: 
5479            return accent_noad_subtype;
5480        case math_radical_list: 
5481            return radical_noad_subtype;
5482        default: 
5483            return 0;
5484    }
5485}
5486
5487static int tex_aux_short_math(halfword m)
5488{
5489 // tex_show_node_list(m,10000,10000);
5490    if (m) { 
5491        /*tex 
5492            These are the cases we catch, the class option drives succes. 
5493
5494            \starttyping
5495            kern[] glyph[subtype -> class] vlist[scripts] kern[] 
5496                   hlist[subtype -> class] 
5497                   vlist[subtype -> class] 
5498            \stoptyping
5499            
5500        */
5501        switch (node_type(m)) { 
5502            case kern_node:
5503                /*tex Do we need to test for some size here? */
5504                m = node_next(m); 
5505                break;
5506            case hlist_node:
5507            case vlist_node:
5508                /*tex 
5509                    These are actually more extensive constructs that we don't want to analyze any 
5510                    further (for being single characters). 
5511                */
5512                if (! node_next(m) && tex_math_has_class_option(tex_aux_class_from_list(m), short_inline_class_option)) {
5513                    scaled threshold = short_inline_math_threshold_par;
5514                    if (threshold > 0 && box_width(m) <= threshold) {
5515                        return 1;
5516                    }
5517                }
5518                return 0;
5519        } 
5520        /*tex We don't have a list so we check for a list now. */
5521        if (m) { 
5522            switch (node_type(m)) { 
5523                case glyph_node: 
5524                    if (tex_math_has_class_option(tex_aux_class_from_glyph(m), short_inline_class_option)) {
5525                        m = node_next(m);
5526                        break;
5527                    } else { 
5528                        return 0;
5529                    }
5530                default: 
5531                    return 0;
5532                }
5533        } else { 
5534            return 0;
5535        }
5536        /*tex We accept optional sub, super or combined scripts. */
5537        if (m) { 
5538            switch (node_type(m)) { 
5539                case vlist_node:
5540                case hlist_node:
5541                    switch (node_subtype(m)) { 
5542                        case math_sup_list:
5543                        case math_sub_list:
5544                        case math_prime_list:
5545                        case math_pre_post_sup_list:
5546                        case math_pre_post_sub_list:
5547                        case math_scripts_list:
5548                           m = node_next(m);
5549                           break;
5550                    }
5551                /* default */
5552            }
5553        } 
5554        /*tex We ignore trailing kerns (for now). We could test for size. */
5555        if (m && node_type(m) == kern_node) {
5556            m = node_next(m);
5557        } 
5558        return ! m;
5559    }
5560    return 0;
5561}
5562
5563void tex_run_math_shift(void) 
5564{
5565    switch (cur_group) {
5566        case math_inline_group:
5567        case math_display_group:
5568        case math_number_group:
5569            {
5570                /*tex box containing equation number */
5571                halfword eqnumber = null;
5572                /*tex Use |\leqno| instead of |\eqno|, we default to right. */
5573                int atleft = 0;
5574                /*tex |mmode| or |-mmode| */
5575                int mode = cur_list.mode;
5576                int mathmode = cur_list.math_mode; 
5577                /*tex this pops the nest, the formula */
5578                halfword mathlist = tex_aux_finish_math_list(null);
5579                int mathleft = cur_list.math_begin;
5580                int mathright = cur_list.math_end;
5581                if (cur_cmd == math_shift_cs_cmd) { 
5582                    switch (cur_chr) { 
5583                        case begin_inline_math_code:
5584                        case begin_display_math_code:
5585                        case begin_math_mode_code:
5586                            tex_you_cant_error(NULL);
5587                            break;
5588                    }
5589                }
5590                if (cur_list.mode == -mode) { // todo: symbolic 
5591                    /*tex end of equation number */
5592                  AGAIN:
5593                    switch (cur_cmd) {
5594                        case math_shift_cmd:
5595                            tex_aux_check_second_math_shift();
5596                            break;
5597                        case end_paragraph_cmd:
5598                            tex_get_x_token();
5599                            goto AGAIN;
5600                        default:
5601                            tex_aux_check_display_math_end();
5602                            break;
5603                    }
5604                    tex_run_mlist_to_hlist(mathlist, 0, text_style, unset_noad_class, unset_noad_class);
5605                    eqnumber = tex_hpack(node_next(temp_head), 0, packing_additional, direction_unknown, holding_none_option, box_limit_none);
5606                    attach_current_attribute_list(eqnumber);
5607                    tex_aux_unsave_math();
5608                    /*tex now |cur_group = math_shift_group| */
5609                    lmt_save_state.save_stack_data.ptr -= saved_equation_number_n_of_records;
5610                 // if (saved_type(saved_equation_number_location_entry) == equation_number_location_save_type) {
5611                        atleft = saved_equation_number_location == left_location_code;
5612                        mode = cur_list.mode;
5613                        mathlist = tex_aux_finish_math_list(null);
5614                 // } else {
5615                 //     tex_confusion("after math");
5616                 // }
5617                }
5618                if (mode == inline_mmode) { 
5619             // if (mode < 0) {
5620                    /*tex
5621
5622                        The |unsave| is done after everything else here; hence an appearance of |\mathsurround|
5623                        inside of |$...$| affects the spacing at these particular |$|'s. This is consistent
5624                        with the conventions of |$$ ... $$|, since |\abovedisplayskip| inside a display affects
5625                        the space above that display.
5626
5627                    */
5628                    halfword beginmath = tex_new_node(math_node, begin_inline_math);
5629                    halfword endmath = tex_new_node(math_node, end_inline_math);
5630                    halfword shortmath = 0;
5631                    switch (cur_list.math_main_style) { 
5632                        case display_style:       
5633                        case cramped_display_style:       
5634                            math_options(beginmath) |= math_option_display;
5635                            math_options(endmath) |= math_option_display;
5636                            break;
5637                    }
5638                    switch (cur_list.math_main_style) { 
5639                        case cramped_display_style:       
5640                        case cramped_text_style:          
5641                        case cramped_script_style:        
5642                        case cramped_script_script_style: 
5643                            math_options(beginmath) |= math_option_cramped;
5644                            math_options(endmath) |= math_option_cramped;
5645                            break;
5646                    }
5647                    if (mathmode) { 
5648                        switch (cur_cmd) { 
5649                            case math_shift_cs_cmd: 
5650                                if (cur_chr != end_display_math_code && cur_chr != end_math_mode_code) {
5651                                    tex_aux_check_second_math_shift();
5652                                }
5653                                break;
5654                            case math_shift_cmd: 
5655                                tex_aux_check_second_math_shift();
5656                                break;
5657                        }
5658                    } else if (cur_cmd == math_shift_cs_cmd) {
5659                        tex_aux_check_inline_math_end();
5660                    }
5661                    tex_tail_append(beginmath);
5662                    if (pre_inline_penalty_par != max_integer) {
5663                        math_penalty(beginmath) = pre_inline_penalty_par;
5664                    }
5665                    /*tex begin mathskip code */
5666                    switch (math_skip_mode_par) {
5667                        case math_skip_surround_when_zero:
5668                            if (! tex_glue_is_zero(math_skip_par)) {
5669                                tex_copy_glue_values(beginmath, math_skip_par);
5670                            } else {
5671                                math_surround(beginmath) = math_surround_par;
5672                            }
5673                            break ;
5674                        case math_skip_always_left:
5675                        case math_skip_always_both:
5676                        case math_skip_only_when_skip:
5677                            tex_copy_glue_values(beginmath, math_skip_par);
5678                            break ;
5679                        case math_skip_always_right:
5680                        case math_skip_ignore:
5681                            break ;
5682                        case math_skip_always_surround:
5683                        default:
5684                            math_surround(beginmath) = math_surround_par;
5685                            break;
5686                    }
5687                    /*tex end mathskip code */
5688                    if (cur_list.math_dir) {
5689                        tex_tail_append(tex_new_dir(normal_dir_subtype, math_direction_par)); 
5690                    }
5691                    tex_run_mlist_to_hlist(mathlist, cur_list.mode > nomode, is_valid_math_style(cur_list.math_main_style) ?  cur_list.math_main_style : text_style, cur_list.math_begin, cur_list.math_end);
5692                    shortmath = tex_aux_short_math(node_next(temp_head));
5693                    tex_try_couple_nodes(cur_list.tail, node_next(temp_head));
5694                    cur_list.tail = tex_tail_of_node_list(cur_list.tail);
5695                    if (cur_list.math_dir) {
5696                        tex_tail_append(tex_new_dir(cancel_dir_subtype, math_direction_par));
5697                    }
5698                    cur_list.math_dir = 0;
5699                    tex_tail_append(endmath);
5700                    /* */
5701                    if (post_inline_penalty_par != max_integer) {
5702                        math_penalty(endmath) = post_inline_penalty_par;
5703                    }
5704                    /*tex begin mathskip code */
5705                    switch (math_skip_mode_par) {
5706                        case math_skip_surround_when_zero :
5707                            if (! tex_glue_is_zero(math_skip_par)) {
5708                                tex_copy_glue_values(endmath, math_skip_par);
5709                                math_surround(endmath) = 0;
5710                            } else {
5711                                math_surround(endmath) = math_surround_par;
5712                            }
5713                            break;
5714                        case math_skip_always_right:
5715                        case math_skip_always_both:
5716                        case math_skip_only_when_skip:
5717                            tex_copy_glue_values(endmath, math_skip_par);
5718                            break;
5719                        case math_skip_always_left:
5720                        case math_skip_ignore:
5721                            break;
5722                        case math_skip_always_surround:
5723                        default:
5724                            math_surround(endmath) = math_surround_par;
5725                            break;
5726                    }
5727                    /*tex end mathskip code */
5728                    if (shortmath) {
5729                        if (pre_short_inline_penalty_par != max_integer) {
5730                            math_penalty(beginmath) = pre_short_inline_penalty_par;
5731                        }
5732                        if (post_short_inline_penalty_par != max_integer) {
5733                            math_penalty(endmath) = post_short_inline_penalty_par;
5734                        }
5735                        tex_add_math_option(beginmath, math_option_short);
5736                        tex_add_math_option(endmath, math_option_short);
5737                    }
5738                    cur_list.space_factor = default_space_factor;
5739                    mathleft = cur_list.math_begin;
5740                    mathright = cur_list.math_end;
5741                    math_tolerance(beginmath) = math_tolerance_par;
5742                    math_pre_tolerance(beginmath) = math_pre_tolerance_par;
5743                    math_tolerance(endmath) = tolerance_par;
5744                    math_pre_tolerance(endmath) = pre_tolerance_par;
5745                    tex_aux_unsave_math();
5746                } else {
5747                    if (! eqnumber) {
5748                        if (cur_cmd == math_shift_cmd) {
5749                            tex_aux_check_second_math_shift();
5750                        } else {
5751                            tex_aux_check_display_math_end();
5752                        }
5753                    }
5754                    tex_run_mlist_to_hlist(mathlist, 0, display_style, cur_list.math_begin, cur_list.math_end);
5755                    mathleft = cur_list.math_begin;
5756                    mathright = cur_list.math_end;
5757                    tex_aux_finish_displayed_math(atleft, eqnumber, node_next(temp_head));
5758                }
5759                /* local */
5760                update_tex_math_left_class(mathleft);
5761                update_tex_math_right_class(mathright);
5762                /* global */
5763                lmt_math_state.last_left = mathleft;
5764                lmt_math_state.last_right = mathright;
5765            }
5766            break;
5767        default:
5768            tex_off_save();
5769            break;
5770    }
5771}
5772
5773/*tex
5774
5775    When |\halign| appears in a display, the alignment routines operate essentially as they do in
5776    vertical mode. Then the following program is activated, with |p| and |q| pointing to the
5777    beginning and end of the resulting list, and with |aux_save| holding the |prev_depth| value.
5778
5779*/
5780
5781void tex_finish_display_alignment(halfword head, halfword tail, halfword prevdepth)
5782{
5783    tex_handle_assignments();
5784  AGAIN:
5785    switch (cur_cmd) {
5786        case math_shift_cmd:
5787            tex_aux_check_second_math_shift();
5788            break;
5789        case end_paragraph_cmd:
5790            tex_get_x_token();
5791            goto AGAIN;
5792        default:
5793            tex_aux_check_display_math_end();
5794            break;
5795    }
5796    tex_pop_nest();
5797    tex_tail_append(tex_new_penalty_node(pre_display_penalty_par, before_display_penalty_subtype));
5798    tex_aux_inject_display_skip(above_display_skip_code, above_display_skip_glue);
5799    node_next(cur_list.tail) = head;
5800    if (head && tail) {
5801        cur_list.tail = tail;
5802    }
5803    tex_tail_append(tex_new_penalty_node(post_display_penalty_par, after_display_penalty_subtype));
5804    tex_aux_inject_display_skip(below_display_skip_code, below_display_skip_glue);
5805    cur_list.prev_depth = prevdepth;
5806    tex_aux_resume_after_display();
5807}
5808
5809/*
5810
5811    Turning macros into functions brought the mingw64 bin down from 2548224 to 2511360 bytes but
5812    not the linux one, so I guess mingw doesn't inline (yet, in 2020).
5813
5814*/
5815
5816static void tex_aux_define_inl_math_parameters(int size, int param, scaled value, int level)
5817{
5818    switch (size) {
5819        case script_size:
5820            tex_def_math_parameter(script_style, param, value, level, indirect_math_regular, 1);
5821            tex_def_math_parameter(cramped_script_style, param, value, level, indirect_math_regular, 1);
5822            break;
5823        case script_script_size:
5824            tex_def_math_parameter(script_script_style, param, value, level, indirect_math_regular, 1);
5825            tex_def_math_parameter(cramped_script_script_style, param, value, level, indirect_math_regular, 1);
5826            break;
5827        default:
5828            tex_def_math_parameter(text_style, param, value, level, indirect_math_regular, 1);
5829            tex_def_math_parameter(cramped_text_style, param, value, level, indirect_math_regular, 1);
5830            break;
5831    }
5832}
5833
5834static void tex_aux_define_dis_math_parameters(int size, int param, scaled value, int level)
5835{
5836    if (size == text_size) {
5837        tex_def_math_parameter(display_style, param, value, level, indirect_math_regular, 1);
5838        tex_def_math_parameter(cramped_display_style, param, value, level, indirect_math_regular, 1);
5839    }
5840}
5841
5842/*tex
5843    In principle we could save some storage in the format file with:
5844
5845    \starttyping
5846    static void tex_aux_define_all_math_parameters(int size, int param, scaled value, int level)
5847    {
5848        tex_def_math_parameter(all_math_styles, param, value, level, indirect_math_regular);
5849    } 
5850    \stoptyping
5851
5852    and then do this (we also need to move |all_math_styles| up in the enum to keep ranges compact):
5853
5854    \starttyping
5855    if (! sa_get_item_8(lmt_math_state.par_head, (param + (math_parameter_max_range * style)), &item1, &item2)) {
5856        sa_get_item_8(lmt_math_state.par_head, (param + (math_parameter_max_range * all_math_styles)), &item1, &item2);
5857    }
5858    \stoptyping
5859
5860    but in practice we actually get a bit larger file. 
5861*/
5862
5863static void tex_aux_define_all_math_parameters(int size, int param, scaled value, int level)
5864{
5865    switch (size) {
5866        case script_size:
5867            tex_def_math_parameter(script_style, param, value, level, indirect_math_regular, 1);
5868            tex_def_math_parameter(cramped_script_style, param, value, level, indirect_math_regular, 1);
5869            break;
5870        case script_script_size:
5871            tex_def_math_parameter(script_script_style, param, value, level, indirect_math_regular, 1);
5872            tex_def_math_parameter(cramped_script_script_style, param, value, level, indirect_math_regular, 1);
5873            break;
5874        default:
5875            tex_def_math_parameter(text_style, param, value, level, indirect_math_regular, 1);
5876            tex_def_math_parameter(cramped_text_style, param, value, level, indirect_math_regular, 1);
5877            tex_def_math_parameter(display_style, param, value, level, indirect_math_regular, 1);
5878            tex_def_math_parameter(cramped_display_style, param, value, level, indirect_math_regular, 1);
5879            break;
5880    }
5881}
5882
5883/*tex
5884
5885    Here are the math parameters that are font-dependant. Before an mlist is converted to an hlist,
5886    \TEX\ makes sure that the fonts in family~2 have enough parameters to be math symbol fonts, and
5887    that the fonts in family~3 have enough parameters to be math extension fonts. The math-symbol
5888    parameters are referred to by using the following macros, which take a size code as their
5889    parameter; for example, |num1 (cur_size)| gives the value of the |num1| parameter for the
5890    current size.
5891
5892    The math extension parameters have similar macros, but the size code is omitted (since it is
5893    always |cur_size| when we refer to such parameters).
5894
5895*/
5896
5897# define total_mathsy_parameters 22
5898# define total_mathex_parameters 13
5899
5900# define mathsy(A,B) font_parameter(tex_fam_fnt(2,A),B)
5901# define mathex(A,B) font_parameter(tex_fam_fnt(3,A),B)
5902
5903# define math_x_height(A)          mathsy(A,5)  /*tex height of |x| */
5904# define math_quad(A)              mathsy(A,6)  /*tex |18mu| */
5905# define num1(A)                   mathsy(A,8)  /*tex numerator shift-up in display styles */
5906# define num2(A)                   mathsy(A,9)  /*tex numerator shift-up in non-display, non-|\atop| */
5907# define num3(A)                   mathsy(A,10) /*tex numerator shift-up in non-display |\atop| */
5908# define denom1(A)                 mathsy(A,11) /*tex denominator shift-down in display styles */
5909# define denom2(A)                 mathsy(A,12) /*tex denominator shift-down in non-display styles */
5910# define sup1(A)                   mathsy(A,13) /*tex superscript shift-up in uncramped display style */
5911# define sup2(A)                   mathsy(A,14) /*tex superscript shift-up in uncramped non-display */
5912# define sup3(A)                   mathsy(A,15) /*tex superscript shift-up in cramped styles */
5913# define sub1(A)                   mathsy(A,16) /*tex subscript shift-down if superscript is absent */
5914# define sub2(A)                   mathsy(A,17) /*tex subscript shift-down if superscript is present */
5915# define sup_drop(A)               mathsy(A,18) /*tex superscript baseline below top of large box */
5916# define sub_drop(A)               mathsy(A,19) /*tex subscript baseline below bottom of large box */
5917# define delim1(A)                 mathsy(A,20) /*tex size of |\atopwithdelims| delimiters in display styles */
5918# define delim2(A)                 mathsy(A,21) /*tex size of |\atopwithdelims| delimiters in non-displays */
5919# define axis_height(A)            mathsy(A,22) /*tex height of fraction lines above the baseline */
5920
5921# define default_rule_thickness(A) mathex(A,8)  /*tex thickness of |\over| bars */
5922# define big_operator_spacing1(A)  mathex(A,9)  /*tex minimum clearance above a displayed op */
5923# define big_operator_spacing2(A)  mathex(A,10) /*tex minimum clearance below a displayed op */
5924# define big_operator_spacing3(A)  mathex(A,11) /*tex minimum baselineskip above displayed op */
5925# define big_operator_spacing4(A)  mathex(A,12) /*tex minimum baselineskip below displayed op */
5926# define big_operator_spacing5(A)  mathex(A,13) /*tex padding above and below displayed limits */
5927
5928# define math_parameter(a,b) ((font_math_parameter_count(a) >= b) ? font_math_parameter(a,b) : undefined_math_parameter)
5929
5930static inline scaled tex_aux_get_font_math_parameter(scaled scale, halfword f, int id)
5931{
5932    scaled v = math_parameter(f, id);
5933    if (v == undefined_math_parameter) {
5934        return v;
5935    } else {
5936        return v ? scaledround(0.001 * scale * v) : 0;
5937    }
5938}
5939
5940static inline scaled tex_aux_get_font_math_quantity(scaled scale, halfword v)
5941{
5942    return v ? scaledround(0.001 * scale * v) : 0;
5943}
5944
5945/*tex
5946    The next function is called when we define a family, but first we define a few helpers
5947    for identifying traditional math fonts. Watch the hard codes family check (gone now)!
5948*/
5949
5950void tex_fixup_math_parameters(int fam, int size, int f, int level)
5951{
5952    scaled scale = tex_get_math_font_scale(f, size);
5953
5954    if (tracing_math_par > 1) {
5955        tex_begin_diagnostic();
5956        tex_print_format("[math: fixing up font, family %i, size %i, font %i, level %i]", fam, size, f, level);
5957        tex_end_diagnostic();
5958    }
5959
5960    /*tex These apply to all: */
5961//    tex_get_parameter(cur_font_par,  scaled_slant_per_point_code + 1)
5962
5963    tex_aux_define_all_math_parameters(size, math_parameter_quad,     tex_aux_get_font_math_quantity (scale, font_size(f)),                      level);
5964    tex_aux_define_all_math_parameters(size, math_parameter_exheight, tex_aux_get_font_math_quantity (scale, font_parameter(f, ex_height_code)), level);
5965    tex_aux_define_all_math_parameters(size, math_parameter_axis,     tex_aux_get_font_math_parameter(scale, f, AxisHeight),                     level);
5966
5967    tex_aux_define_all_math_parameters(size, math_parameter_accent_base_height,               tex_aux_get_font_math_parameter(scale, f, AccentBaseHeight),                  level);
5968    tex_aux_define_all_math_parameters(size, math_parameter_accent_base_depth,                tex_aux_get_font_math_parameter(scale, f, AccentBaseDepth),                   level); /* engine, reserved */
5969    tex_aux_define_all_math_parameters(size, math_parameter_flattened_accent_base_height,     tex_aux_get_font_math_parameter(scale, f, FlattenedAccentBaseHeight),         level);
5970    tex_aux_define_all_math_parameters(size, math_parameter_flattened_accent_base_depth,      tex_aux_get_font_math_parameter(scale, f, FlattenedAccentBaseDepth),          level); /* engine, reserved */
5971    tex_aux_define_all_math_parameters(size, math_parameter_overbar_kern,                     tex_aux_get_font_math_parameter(scale, f, OverbarExtraAscender),              level);
5972    tex_aux_define_all_math_parameters(size, math_parameter_overbar_rule,                     tex_aux_get_font_math_parameter(scale, f, OverbarRuleThickness),              level);
5973    tex_aux_define_all_math_parameters(size, math_parameter_overbar_vgap,                     tex_aux_get_font_math_parameter(scale, f, OverbarVerticalGap),                level);
5974    tex_aux_define_all_math_parameters(size, math_parameter_underbar_kern,                    tex_aux_get_font_math_parameter(scale, f, UnderbarExtraDescender),            level);
5975    tex_aux_define_all_math_parameters(size, math_parameter_underbar_rule,                    tex_aux_get_font_math_parameter(scale, f, UnderbarRuleThickness ),            level);
5976    tex_aux_define_all_math_parameters(size, math_parameter_underbar_vgap,                    tex_aux_get_font_math_parameter(scale, f, UnderbarVerticalGap),               level);
5977    tex_aux_define_all_math_parameters(size, math_parameter_under_delimiter_vgap,             tex_aux_get_font_math_parameter(scale, f, StretchStackGapAboveMin),           level);
5978    tex_aux_define_all_math_parameters(size, math_parameter_under_delimiter_bgap,             tex_aux_get_font_math_parameter(scale, f, StretchStackBottomShiftDown),       level);
5979    tex_aux_define_all_math_parameters(size, math_parameter_over_delimiter_vgap,              tex_aux_get_font_math_parameter(scale, f, StretchStackGapBelowMin),           level);
5980    tex_aux_define_all_math_parameters(size, math_parameter_over_delimiter_bgap,              tex_aux_get_font_math_parameter(scale, f, StretchStackTopShiftUp),            level);
5981    tex_aux_define_all_math_parameters(size, math_parameter_radical_kern,                     tex_aux_get_font_math_parameter(scale, f, RadicalExtraAscender),              level);
5982    tex_aux_define_all_math_parameters(size, math_parameter_radical_rule,                     tex_aux_get_font_math_parameter(scale, f, RadicalRuleThickness),              level);
5983    tex_aux_define_all_math_parameters(size, math_parameter_radical_degree_before,            tex_aux_get_font_math_parameter(scale, f, RadicalKernBeforeDegree),           level);
5984    tex_aux_define_all_math_parameters(size, math_parameter_radical_degree_after,             tex_aux_get_font_math_parameter(scale, f, RadicalKernAfterDegree),            level);
5985    tex_aux_define_all_math_parameters(size, math_parameter_subscript_shift_drop,             tex_aux_get_font_math_parameter(scale, f, SubscriptBaselineDropMin),          level);
5986    tex_aux_define_all_math_parameters(size, math_parameter_superscript_shift_drop,           tex_aux_get_font_math_parameter(scale, f, SuperscriptBaselineDropMax),        level);
5987    tex_aux_define_all_math_parameters(size, math_parameter_subscript_shift_down,             tex_aux_get_font_math_parameter(scale, f, SubscriptShiftDown),                level);
5988    tex_aux_define_all_math_parameters(size, math_parameter_prime_shift_drop,                 tex_aux_get_font_math_parameter(scale, f, PrimeBaselineDropMax),              level); /* engine, default 0 */
5989    tex_aux_define_all_math_parameters(size, math_parameter_subscript_top_max,                tex_aux_get_font_math_parameter(scale, f, SubscriptTopMax),                   level);
5990    tex_aux_define_all_math_parameters(size, math_parameter_superscript_bottom_min,           tex_aux_get_font_math_parameter(scale, f, SuperscriptBottomMin),              level);
5991    tex_aux_define_all_math_parameters(size, math_parameter_superscript_subscript_bottom_max, tex_aux_get_font_math_parameter(scale, f, SuperscriptBottomMaxWithSubscript), level);
5992    tex_aux_define_all_math_parameters(size, math_parameter_subscript_superscript_vgap,       tex_aux_get_font_math_parameter(scale, f, SubSuperscriptGapMin),              level);
5993    tex_aux_define_all_math_parameters(size, math_parameter_limit_above_vgap,                 tex_aux_get_font_math_parameter(scale, f, UpperLimitGapMin),                  level);
5994    tex_aux_define_all_math_parameters(size, math_parameter_limit_above_bgap,                 tex_aux_get_font_math_parameter(scale, f, UpperLimitBaselineRiseMin),         level);
5995    tex_aux_define_all_math_parameters(size, math_parameter_limit_below_vgap,                 tex_aux_get_font_math_parameter(scale, f, LowerLimitGapMin),                  level);
5996    tex_aux_define_all_math_parameters(size, math_parameter_limit_below_bgap,                 tex_aux_get_font_math_parameter(scale, f, LowerLimitBaselineDropMin),         level);
5997    tex_aux_define_all_math_parameters(size, math_parameter_nolimit_sub_factor,               tex_aux_get_font_math_parameter(scale, f, NoLimitSubFactor),                  level); /* engine, default 0 */
5998    tex_aux_define_all_math_parameters(size, math_parameter_nolimit_sup_factor,               tex_aux_get_font_math_parameter(scale, f, NoLimitSupFactor),                  level); /* engine, default 0 */
5999    tex_aux_define_all_math_parameters(size, math_parameter_skewed_fraction_hgap,             tex_aux_get_font_math_parameter(scale, f, SkewedFractionHorizontalGap),       level);
6000    tex_aux_define_all_math_parameters(size, math_parameter_skewed_fraction_vgap,             tex_aux_get_font_math_parameter(scale, f, SkewedFractionVerticalGap),         level);
6001    tex_aux_define_all_math_parameters(size, math_parameter_space_before_script,              tex_aux_get_font_math_parameter(scale, f, SpaceBeforeScript),                 level); /* engine, default 0 */
6002    tex_aux_define_all_math_parameters(size, math_parameter_space_between_script,             tex_aux_get_font_math_parameter(scale, f, SpaceBetweenScript),                level); /* engine, default 0 */
6003    tex_aux_define_all_math_parameters(size, math_parameter_space_after_script,               tex_aux_get_font_math_parameter(scale, f, SpaceAfterScript),                  level);
6004    tex_aux_define_all_math_parameters(size, math_parameter_connector_overlap_min,            tex_aux_get_font_math_parameter(scale, f, MinConnectorOverlap),               level); /* engine, default 0 */
6005    tex_aux_define_all_math_parameters(size, math_parameter_superscript_snap,                 tex_aux_get_font_math_parameter(scale, f, SuperscriptSnap),                   level);
6006    tex_aux_define_all_math_parameters(size, math_parameter_subscript_snap,                   tex_aux_get_font_math_parameter(scale, f, SubscriptSnap),                     level); /* engine, default 0 */
6007    tex_aux_define_all_math_parameters(size, math_parameter_fraction_rule,                    tex_aux_get_font_math_parameter(scale, f, FractionRuleThickness),             level); /* engine, default 0 */
6008
6009    tex_aux_define_all_math_parameters(size, math_parameter_prime_space_after,                  tex_aux_get_font_math_parameter(scale, f, PrimeSpaceAfter),                 level); /* engine, default 0 */
6010    tex_aux_define_all_math_parameters(size, math_parameter_skewed_delimiter_tolerance,         tex_aux_get_font_math_parameter(scale, f, SkewedDelimiterTolerance),        level); /* engine, default 0 */
6011    tex_aux_define_all_math_parameters(size, math_parameter_accent_top_shift_up,                tex_aux_get_font_math_parameter(scale, f, AccentTopShiftUp),                level); /* engine, undefined */
6012    tex_aux_define_all_math_parameters(size, math_parameter_accent_bottom_shift_down,           tex_aux_get_font_math_parameter(scale, f, AccentBottomShiftDown),           level); /* engine, undefined */
6013    tex_aux_define_all_math_parameters(size, math_parameter_accent_top_overshoot,               tex_aux_get_font_math_parameter(scale, f, AccentTopOvershoot),              level); /* engine, default 0 */
6014    tex_aux_define_all_math_parameters(size, math_parameter_accent_bottom_overshoot,            tex_aux_get_font_math_parameter(scale, f, AccentBottomOvershoot),           level); /* engine, default 0 */
6015    tex_aux_define_all_math_parameters(size, math_parameter_accent_superscript_drop,            tex_aux_get_font_math_parameter(scale, f, AccentSuperscriptDrop),           level); /* engine, default 0 */
6016    tex_aux_define_all_math_parameters(size, math_parameter_accent_superscript_percent,         tex_aux_get_font_math_parameter(scale, f, AccentSuperscriptPercent),        level); /* engine, default 0 */
6017    tex_aux_define_all_math_parameters(size, math_parameter_accent_extend_margin,               tex_aux_get_font_math_parameter(scale, f, AccentExtendMargin),              level); /* engine, undefined */
6018    tex_aux_define_all_math_parameters(size, math_parameter_flattened_accent_top_shift_up,      tex_aux_get_font_math_parameter(scale, f, FlattenedAccentTopShiftUp),       level); /* engine, undefined */
6019    tex_aux_define_all_math_parameters(size, math_parameter_flattened_accent_bottom_shift_down, tex_aux_get_font_math_parameter(scale, f, FlattenedAccentBottomShiftDown),  level); /* engine, undefined */
6020    tex_aux_define_all_math_parameters(size, math_parameter_delimiter_extend_margin,            tex_aux_get_font_math_parameter(scale, f, DelimiterExtendMargin),           level); /* engine, undefined */
6021    tex_aux_define_all_math_parameters(size, math_parameter_radical_extensible_after,           tex_aux_get_font_math_parameter(scale, f, RadicalKernAfterExtensible),      level); /* engine, undefined */
6022    tex_aux_define_all_math_parameters(size, math_parameter_radical_extensible_before,          tex_aux_get_font_math_parameter(scale, f, RadicalKernBeforeExtensible),     level); /* engine, undefined */
6023
6024    /*tex Percentages: */
6025
6026    tex_aux_define_all_math_parameters(size, math_parameter_prime_raise,          math_parameter(f, PrimeRaisePercent),               level); /* engine, default 0 */
6027    tex_aux_define_all_math_parameters(size, math_parameter_prime_raise_composed, math_parameter(f, PrimeRaiseComposedPercent),       level); /* engine, default 0 */
6028    tex_aux_define_all_math_parameters(size, math_parameter_radical_degree_raise, math_parameter(f, RadicalDegreeBottomRaisePercent), level);
6029
6030    /*tex Not all are official \OPENTYPE: */
6031
6032    tex_aux_define_all_math_parameters(size, math_parameter_x_scale, scaling_factor, level);
6033    tex_aux_define_all_math_parameters(size, math_parameter_y_scale, scaling_factor, level);
6034
6035    /*tex Most are zero and have to be set at by the macro package (if at all):. */
6036
6037    tex_aux_define_all_math_parameters(size, math_parameter_limit_above_kern,              0, level);
6038    tex_aux_define_all_math_parameters(size, math_parameter_limit_below_kern,              0, level);
6039    tex_aux_define_all_math_parameters(size, math_parameter_extra_superscript_shift,       0, level);
6040    tex_aux_define_all_math_parameters(size, math_parameter_extra_subscript_shift,         0, level);
6041    tex_aux_define_all_math_parameters(size, math_parameter_extra_superprescript_shift,    0, level);
6042    tex_aux_define_all_math_parameters(size, math_parameter_extra_subprescript_shift,      0, level);
6043    tex_aux_define_all_math_parameters(size, math_parameter_rule_height,                   0, level);
6044    tex_aux_define_all_math_parameters(size, math_parameter_rule_depth,                    0, level);
6045    tex_aux_define_all_math_parameters(size, math_parameter_extra_superscript_space,       0, level);
6046    tex_aux_define_all_math_parameters(size, math_parameter_extra_subscript_space,         0, level);
6047    tex_aux_define_all_math_parameters(size, math_parameter_extra_superprescript_space,    0, level);
6048    tex_aux_define_all_math_parameters(size, math_parameter_extra_subprescript_space,      0, level);
6049
6050    /*tex A special one: */
6051
6052    if (math_parameter(f, SubscriptShiftDownWithSuperscript) != undefined_math_parameter) { /* engine */
6053        tex_aux_define_all_math_parameters(size, math_parameter_subscript_superscript_shift_down, tex_aux_get_font_math_parameter(scale, f, SubscriptShiftDownWithSuperscript), level);
6054    } else {
6055        tex_aux_define_all_math_parameters(size, math_parameter_subscript_superscript_shift_down, tex_aux_get_font_math_parameter(scale, f, SubscriptShiftDown),                level);
6056    }
6057
6058    /*tex These differentiate between display and inline: */
6059
6060    tex_aux_define_dis_math_parameters(size, math_parameter_delimiter_percent,   math_parameter(f, DelimiterDisplayPercent), level); /* engine, undefined */
6061    tex_aux_define_inl_math_parameters(size, math_parameter_delimiter_percent,   math_parameter(f, DelimiterPercent),        level); /* engine, undefined */
6062
6063    tex_aux_define_dis_math_parameters(size, math_parameter_operator_size,       tex_aux_get_font_math_parameter(scale, f, DisplayOperatorMinHeight),                 level);
6064    tex_aux_define_inl_math_parameters(size, math_parameter_radical_vgap,        tex_aux_get_font_math_parameter(scale, f, RadicalVerticalGap),                       level);
6065    tex_aux_define_dis_math_parameters(size, math_parameter_radical_vgap,        tex_aux_get_font_math_parameter(scale, f, RadicalDisplayStyleVerticalGap),           level);
6066    tex_aux_define_inl_math_parameters(size, math_parameter_stack_num_up,        tex_aux_get_font_math_parameter(scale, f, StackTopShiftUp),                          level);
6067    tex_aux_define_dis_math_parameters(size, math_parameter_stack_num_up,        tex_aux_get_font_math_parameter(scale, f, StackTopDisplayStyleShiftUp),              level);
6068    tex_aux_define_inl_math_parameters(size, math_parameter_stack_denom_down,    tex_aux_get_font_math_parameter(scale, f, StackBottomShiftDown),                     level);
6069    tex_aux_define_dis_math_parameters(size, math_parameter_stack_denom_down,    tex_aux_get_font_math_parameter(scale, f, StackBottomDisplayStyleShiftDown),         level);
6070    tex_aux_define_inl_math_parameters(size, math_parameter_stack_vgap,          tex_aux_get_font_math_parameter(scale, f, StackGapMin),                              level);
6071    tex_aux_define_dis_math_parameters(size, math_parameter_stack_vgap,          tex_aux_get_font_math_parameter(scale, f, StackDisplayStyleGapMin),                  level);
6072    tex_aux_define_inl_math_parameters(size, math_parameter_fraction_num_vgap,   tex_aux_get_font_math_parameter(scale, f, FractionNumeratorGapMin),                  level);
6073    tex_aux_define_dis_math_parameters(size, math_parameter_fraction_num_vgap,   tex_aux_get_font_math_parameter(scale, f, FractionNumeratorDisplayStyleGapMin),      level);
6074    tex_aux_define_inl_math_parameters(size, math_parameter_fraction_num_up,     tex_aux_get_font_math_parameter(scale, f, FractionNumeratorShiftUp),                 level);
6075    tex_aux_define_dis_math_parameters(size, math_parameter_fraction_num_up,     tex_aux_get_font_math_parameter(scale, f, FractionNumeratorDisplayStyleShiftUp),     level);
6076    tex_aux_define_inl_math_parameters(size, math_parameter_fraction_denom_vgap, tex_aux_get_font_math_parameter(scale, f, FractionDenominatorGapMin),                level);
6077    tex_aux_define_dis_math_parameters(size, math_parameter_fraction_denom_vgap, tex_aux_get_font_math_parameter(scale, f, FractionDenominatorDisplayStyleGapMin),    level);
6078    tex_aux_define_inl_math_parameters(size, math_parameter_fraction_denom_down, tex_aux_get_font_math_parameter(scale, f, FractionDenominatorShiftDown),             level);
6079    tex_aux_define_dis_math_parameters(size, math_parameter_fraction_denom_down, tex_aux_get_font_math_parameter(scale, f, FractionDenominatorDisplayStyleShiftDown), level);
6080    tex_aux_define_inl_math_parameters(size, math_parameter_fraction_del_size,   tex_aux_get_font_math_parameter(scale, f, FractionDelimiterSize),                    level); /* engine, undefined */
6081    tex_aux_define_dis_math_parameters(size, math_parameter_fraction_del_size,   tex_aux_get_font_math_parameter(scale, f, FractionDelimiterDisplayStyleSize),        level); /* engine, undefined */
6082
6083    tex_aux_define_dis_math_parameters(size, math_parameter_delimiter_shortfall, tex_aux_get_font_math_parameter(scale, f, DelimiterDisplayShortfall),                level); /* engine, undefined */
6084    tex_aux_define_inl_math_parameters(size, math_parameter_delimiter_shortfall, tex_aux_get_font_math_parameter(scale, f, DelimiterShortfall),                       level); /* engine, undefined */
6085
6086    /*tex A few more specials: */
6087
6088    switch (size) {
6089        case script_size:
6090            tex_def_math_parameter(script_style,         math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUp),        level, indirect_math_regular, 1);
6091            tex_def_math_parameter(cramped_script_style, math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUpCramped), level, indirect_math_regular, 1);
6092            tex_def_math_parameter(script_style,         math_parameter_prime_shift_up,       tex_aux_get_font_math_parameter(scale, f, PrimeShiftUp),              level, indirect_math_regular, 1); /* engine, default 0 */
6093            tex_def_math_parameter(cramped_script_style, math_parameter_prime_shift_up,       tex_aux_get_font_math_parameter(scale, f, PrimeShiftUpCramped),       level, indirect_math_regular, 1); /* engine, default 0 */
6094            break;
6095        case script_script_size:
6096            tex_def_math_parameter(script_script_style,         math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUp),        level, indirect_math_regular, 1);
6097            tex_def_math_parameter(cramped_script_script_style, math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUpCramped), level, indirect_math_regular, 1);
6098            tex_def_math_parameter(script_script_style,         math_parameter_prime_shift_up,       tex_aux_get_font_math_parameter(scale, f, PrimeShiftUp),              level, indirect_math_regular, 1); /* engine, default 0 */
6099            tex_def_math_parameter(cramped_script_script_style, math_parameter_prime_shift_up,       tex_aux_get_font_math_parameter(scale, f, PrimeShiftUpCramped),       level, indirect_math_regular, 1); /* engine, default 0 */
6100            break;
6101        default:
6102            tex_def_math_parameter(display_style,         math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUp),        level, indirect_math_regular, 1);
6103            tex_def_math_parameter(cramped_display_style, math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUpCramped), level, indirect_math_regular, 1);
6104            tex_def_math_parameter(text_style,            math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUp),        level, indirect_math_regular, 1);
6105            tex_def_math_parameter(cramped_text_style,    math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUpCramped), level, indirect_math_regular, 1);
6106            tex_def_math_parameter(display_style,         math_parameter_prime_shift_up,       tex_aux_get_font_math_parameter(scale, f, PrimeShiftUp),              level, indirect_math_regular, 1); /* engine, default 0 */
6107            tex_def_math_parameter(cramped_display_style, math_parameter_prime_shift_up,       tex_aux_get_font_math_parameter(scale, f, PrimeShiftUpCramped),       level, indirect_math_regular, 1); /* engine, default 0 */
6108            tex_def_math_parameter(text_style,            math_parameter_prime_shift_up,       tex_aux_get_font_math_parameter(scale, f, PrimeShiftUp),              level, indirect_math_regular, 1); /* engine, default 0 */
6109            tex_def_math_parameter(cramped_text_style,    math_parameter_prime_shift_up,       tex_aux_get_font_math_parameter(scale, f, PrimeShiftUpCramped),       level, indirect_math_regular, 1); /* engine, default 0 */
6110            break;
6111    }
6112
6113}
6114
6115/*tex
6116
6117    There is some trickery here. The values are actually pointers and in \LUATEX\ the predefined
6118    muglue ones are small numbers that are way below the normal node values. So, they are kind
6119    of save signals. However, in \LUAMETATEX\ we use zero based internal codes (because that is
6120    nicer for the interface.
6121
6122*/
6123
6124void tex_set_display_styles(halfword code, halfword value, halfword level, halfword indirect)
6125{
6126    tex_def_math_parameter(display_style,         code, value, level, indirect, 0);
6127    tex_def_math_parameter(cramped_display_style, code, value, level, indirect, 0);
6128}
6129
6130void tex_set_text_styles(halfword code, halfword value, halfword level, halfword indirect)
6131{
6132    tex_def_math_parameter(text_style,         code, value, level, indirect, 0);
6133    tex_def_math_parameter(cramped_text_style, code, value, level, indirect, 0);
6134}
6135
6136void tex_set_main_styles(halfword code, halfword value, halfword level, halfword indirect)
6137{
6138    for (int style = display_style; style <= cramped_text_style; style++) {
6139        tex_def_math_parameter(style, code, value, level, indirect, 0);
6140    }
6141}
6142
6143void tex_set_script_styles(halfword code, halfword value, halfword level, halfword indirect)
6144{
6145    tex_def_math_parameter(script_style,         code, value, level, indirect, 0);
6146    tex_def_math_parameter(cramped_script_style, code, value, level, indirect, 0);
6147}
6148
6149void tex_set_script_script_styles(halfword code, halfword value, halfword level, halfword indirect)
6150{
6151    tex_def_math_parameter(script_script_style,         code, value, level, indirect, 0);
6152    tex_def_math_parameter(cramped_script_script_style, code, value, level, indirect, 0);
6153}
6154
6155void tex_set_all_styles(halfword code, halfword value, halfword level, halfword indirect)
6156{
6157    for (int style = display_style; style <= cramped_script_script_style; style++) {
6158        tex_def_math_parameter(style, code, value, level, indirect, 0);
6159    }
6160}
6161
6162void tex_set_uncramped_styles(halfword code, halfword value, halfword level, halfword indirect)
6163{
6164    for (int style = display_style; style <= script_script_style; style += 2) {
6165        tex_def_math_parameter(style, code, value, level, indirect, 0);
6166    }
6167}
6168
6169void tex_set_cramped_styles(halfword code, halfword value, halfword level, halfword indirect)
6170{
6171    for (int style = cramped_display_style; style <= cramped_script_script_style; style += 2) {
6172        tex_def_math_parameter(style, code, value, level, indirect, 0);
6173    }
6174}
6175
6176void tex_set_split_styles(halfword code, halfword value, halfword level, halfword indirect)
6177{
6178    tex_set_display_styles      (code, value, level, indirect);
6179    tex_set_text_styles         (code, value, level, indirect);
6180    tex_set_script_styles       (code, 0,     level, indirect);
6181    tex_set_script_script_styles(code, 0,     level, indirect);
6182}
6183
6184void tex_set_unsplit_styles(halfword code, halfword value, halfword level, halfword indirect)
6185{
6186    tex_set_script_styles       (code, value, level, indirect);
6187    tex_set_script_script_styles(code, value, level, indirect);
6188}
6189
6190void tex_reset_all_styles(halfword level)
6191{
6192    for (int code = math_parameter_atom_pairs_first; code <= math_parameter_atom_pairs_last; code++) {
6193        tex_set_all_styles(code, zero_muskip_code, level, indirect_math_unset);
6194    }
6195}
6196
6197static inline halfword tex_aux_math_class_default(halfword mathclass) {
6198    return (mathclass << 24) + (mathclass << 16) + (mathclass << 8) + mathclass;
6199}
6200
6201static inline void tex_set_math_class_default(halfword mathclass, halfword parent, halfword options)
6202{
6203    tex_word_define(0, internal_integer_location(first_math_class_code   + mathclass), tex_aux_math_class_default(parent));
6204    tex_word_define(0, internal_integer_location(first_math_atom_code    + mathclass), tex_aux_math_class_default(mathclass));
6205    tex_word_define(0, internal_integer_location(first_math_options_code + mathclass), options);
6206    tex_word_define(0, internal_integer_location(first_math_parent_code  + mathclass), tex_aux_math_class_default(mathclass));
6207}
6208
6209static void tex_aux_set_math_atom_rule(halfword left, halfword right, halfword newleft, halfword newright)
6210{
6211    tex_set_all_styles(math_parameter_rules_pair(left, right), (newleft << 16) + newright, level_one, indirect_math_regular);
6212}
6213
6214/*tex
6215
6216    Originally a penalty of 10000 signaled that no penalty has to be included but because we want 
6217    to control penalties in nested sequences (like open and close bound sequences) we need to be 
6218    able to go up (multiply by a factor) or down (divide by a factor). Therefore we need to be able
6219    to set a penalty to 10000 as a start. so that it will be ignored unless we apply a factor. For 
6220    that reason we now use 10001 instead. 
6221
6222*/
6223
6224void tex_initialize_math_spacing(void)
6225{
6226
6227    for (int mathclass = 0; mathclass <= max_math_class_code; mathclass++) {
6228        tex_set_math_class_default(mathclass, mathclass, no_class_options);
6229        /*tex We do this here as there is no real need for yet another initializer. */
6230        tex_word_define(0, internal_integer_location(first_math_pre_penalty_code  + mathclass), math_default_penalty);
6231        tex_word_define(0, internal_integer_location(first_math_post_penalty_code + mathclass), math_default_penalty);
6232        tex_word_define(0, internal_integer_location(first_math_display_pre_penalty_code  + mathclass), math_default_penalty);
6233        tex_word_define(0, internal_integer_location(first_math_display_post_penalty_code + mathclass), math_default_penalty);
6234    }
6235
6236    tex_reset_all_styles(level_one);
6237    
6238    tex_set_math_class_default(ordinary_noad_subtype,    ordinary_noad_subtype,    no_italic_correction_class_option | 
6239                                                                                   check_ligature_class_option | 
6240                                                                                   check_kern_pair_class_option | 
6241                                                                                   flatten_class_option);
6242    tex_set_math_class_default(operator_noad_subtype,    operator_noad_subtype,    check_ligature_class_option |
6243                                                                                   check_kern_pair_class_option);
6244    tex_set_math_class_default(binary_noad_subtype,      binary_noad_subtype,      no_italic_correction_class_option | 
6245                                                                                   check_ligature_class_option | 
6246                                                                                   check_kern_pair_class_option | 
6247                                                                                   flatten_class_option);
6248    tex_set_math_class_default(relation_noad_subtype,    relation_noad_subtype,    no_italic_correction_class_option | 
6249                                                                                   check_ligature_class_option | 
6250                                                                                   check_kern_pair_class_option | 
6251                                                                                   flatten_class_option | 
6252                                                                                   omit_penalty_class_option);
6253    tex_set_math_class_default(open_noad_subtype,        open_noad_subtype,        no_italic_correction_class_option | 
6254                                                                                /* open_fence_class_option | */
6255                                                                                   check_ligature_class_option |
6256                                                                                   check_kern_pair_class_option); 
6257    tex_set_math_class_default(close_noad_subtype,       close_noad_subtype,       no_italic_correction_class_option | 
6258                                                                                /* close_fence_class_option | */
6259                                                                                   check_ligature_class_option |
6260                                                                                   check_kern_pair_class_option); 
6261    tex_set_math_class_default(punctuation_noad_subtype, punctuation_noad_subtype, no_italic_correction_class_option | 
6262                                                                                   check_ligature_class_option | 
6263                                                                                   check_kern_pair_class_option | 
6264                                                                                   flatten_class_option);
6265    tex_set_math_class_default(variable_noad_subtype,    ordinary_noad_subtype,    no_italic_correction_class_option);
6266    tex_set_math_class_default(active_noad_subtype,      ordinary_noad_subtype,    no_italic_correction_class_option);
6267    tex_set_math_class_default(inner_noad_subtype,       inner_noad_subtype,       flatten_class_option);
6268    tex_set_math_class_default(under_noad_subtype,       ordinary_noad_subtype,    no_class_options);
6269    tex_set_math_class_default(over_noad_subtype,        ordinary_noad_subtype,    no_class_options);
6270    tex_set_math_class_default(fraction_noad_subtype,    ordinary_noad_subtype,    no_class_options);
6271    tex_set_math_class_default(radical_noad_subtype,     ordinary_noad_subtype,    no_class_options);
6272    tex_set_math_class_default(middle_noad_subtype,      open_noad_subtype,        no_italic_correction_class_option); /* | middle_fence_class_option= */
6273    tex_set_math_class_default(accent_noad_subtype,      ordinary_noad_subtype,    no_class_options);
6274    tex_set_math_class_default(fenced_noad_subtype,      inner_noad_subtype   ,    no_class_options);
6275    tex_set_math_class_default(ghost_noad_subtype,       ordinary_noad_subtype,    no_class_options);
6276    tex_set_math_class_default(vcenter_noad_subtype,     ordinary_noad_subtype,    no_class_options);
6277    tex_set_math_class_default(prime_noad_subtype,       ordinary_noad_subtype,    no_class_options);
6278
6279    tex_aux_set_math_atom_rule(math_begin_class,         binary_noad_subtype,      ordinary_noad_subtype,    ordinary_noad_subtype);
6280    tex_aux_set_math_atom_rule(binary_noad_subtype,      math_end_class,           ordinary_noad_subtype,    ordinary_noad_subtype);
6281
6282    tex_aux_set_math_atom_rule(binary_noad_subtype,      binary_noad_subtype,      binary_noad_subtype,      ordinary_noad_subtype);
6283    tex_aux_set_math_atom_rule(operator_noad_subtype,    binary_noad_subtype,      operator_noad_subtype,    ordinary_noad_subtype);
6284    tex_aux_set_math_atom_rule(open_noad_subtype,        binary_noad_subtype,      open_noad_subtype,        ordinary_noad_subtype);
6285    tex_aux_set_math_atom_rule(punctuation_noad_subtype, binary_noad_subtype,      punctuation_noad_subtype, ordinary_noad_subtype);
6286    tex_aux_set_math_atom_rule(relation_noad_subtype,    binary_noad_subtype,      relation_noad_subtype,    ordinary_noad_subtype);
6287
6288    tex_aux_set_math_atom_rule(binary_noad_subtype,      close_noad_subtype,       ordinary_noad_subtype,    close_noad_subtype);
6289    tex_aux_set_math_atom_rule(binary_noad_subtype,      punctuation_noad_subtype, ordinary_noad_subtype,    punctuation_noad_subtype);
6290    tex_aux_set_math_atom_rule(binary_noad_subtype,      relation_noad_subtype,    ordinary_noad_subtype,    relation_noad_subtype);
6291
6292    tex_aux_set_math_atom_rule(relation_noad_subtype,    close_noad_subtype,       ordinary_noad_subtype,    close_noad_subtype);
6293    tex_aux_set_math_atom_rule(relation_noad_subtype,    punctuation_noad_subtype, ordinary_noad_subtype,    punctuation_noad_subtype);
6294
6295    /* */
6296
6297//    math_parameter_spacing_pair(ordinary_noad_subtype,ordinary_noad_subtype)
6298
6299    tex_set_all_styles   (math_parameter_spacing_pair(ordinary_noad_subtype,    operator_noad_subtype),    thin_muskip_code,  level_one, indirect_math_regular);
6300    tex_set_split_styles (math_parameter_spacing_pair(ordinary_noad_subtype,    binary_noad_subtype),      med_muskip_code,   level_one, indirect_math_regular);
6301    tex_set_split_styles (math_parameter_spacing_pair(ordinary_noad_subtype,    relation_noad_subtype),    thick_muskip_code, level_one, indirect_math_regular);
6302    tex_set_split_styles (math_parameter_spacing_pair(ordinary_noad_subtype,    inner_noad_subtype),       thin_muskip_code,  level_one, indirect_math_regular);
6303
6304    tex_set_all_styles   (math_parameter_spacing_pair(operator_noad_subtype,    ordinary_noad_subtype),    thin_muskip_code,  level_one, indirect_math_regular);
6305    tex_set_all_styles   (math_parameter_spacing_pair(operator_noad_subtype,    operator_noad_subtype),    thin_muskip_code,  level_one, indirect_math_regular);
6306    tex_set_split_styles (math_parameter_spacing_pair(operator_noad_subtype,    relation_noad_subtype),    thick_muskip_code, level_one, indirect_math_regular);
6307    tex_set_split_styles (math_parameter_spacing_pair(operator_noad_subtype,    inner_noad_subtype),       thin_muskip_code,  level_one, indirect_math_regular);
6308
6309    tex_set_all_styles   (math_parameter_spacing_pair(operator_noad_subtype,    fraction_noad_subtype),    thin_muskip_code,  level_one, indirect_math_regular);
6310    tex_set_all_styles   (math_parameter_spacing_pair(operator_noad_subtype,    radical_noad_subtype),     thin_muskip_code,  level_one, indirect_math_regular);
6311    tex_set_all_styles   (math_parameter_spacing_pair(fraction_noad_subtype,    operator_noad_subtype),    thin_muskip_code,  level_one, indirect_math_regular);
6312    tex_set_all_styles   (math_parameter_spacing_pair(radical_noad_subtype,     operator_noad_subtype),    thin_muskip_code,  level_one, indirect_math_regular);
6313
6314    tex_set_split_styles (math_parameter_spacing_pair(binary_noad_subtype,      ordinary_noad_subtype),    med_muskip_code,   level_one, indirect_math_regular);
6315    tex_set_split_styles (math_parameter_spacing_pair(binary_noad_subtype,      operator_noad_subtype),    med_muskip_code,   level_one, indirect_math_regular);
6316    tex_set_split_styles (math_parameter_spacing_pair(binary_noad_subtype,      open_noad_subtype),        med_muskip_code,   level_one, indirect_math_regular);
6317    tex_set_split_styles (math_parameter_spacing_pair(binary_noad_subtype,      inner_noad_subtype),       med_muskip_code,   level_one, indirect_math_regular);
6318
6319    tex_set_split_styles (math_parameter_spacing_pair(binary_noad_subtype,      middle_noad_subtype),      med_muskip_code,   level_one, indirect_math_regular);
6320    tex_set_split_styles (math_parameter_spacing_pair(binary_noad_subtype,      fraction_noad_subtype),    med_muskip_code,   level_one, indirect_math_regular);
6321    tex_set_split_styles (math_parameter_spacing_pair(binary_noad_subtype,      radical_noad_subtype),     med_muskip_code,   level_one, indirect_math_regular);
6322    tex_set_split_styles (math_parameter_spacing_pair(middle_noad_subtype,      binary_noad_subtype),      med_muskip_code,   level_one, indirect_math_regular);
6323    tex_set_split_styles (math_parameter_spacing_pair(fraction_noad_subtype,    binary_noad_subtype),      med_muskip_code,   level_one, indirect_math_regular);
6324    tex_set_split_styles (math_parameter_spacing_pair(radical_noad_subtype,     binary_noad_subtype),      med_muskip_code,   level_one, indirect_math_regular);
6325
6326    tex_set_split_styles (math_parameter_spacing_pair(relation_noad_subtype,    ordinary_noad_subtype),    thick_muskip_code, level_one, indirect_math_regular);
6327    tex_set_split_styles (math_parameter_spacing_pair(relation_noad_subtype,    operator_noad_subtype),    thick_muskip_code, level_one, indirect_math_regular);
6328    tex_set_split_styles (math_parameter_spacing_pair(relation_noad_subtype,    open_noad_subtype),        thick_muskip_code, level_one, indirect_math_regular);
6329    tex_set_split_styles (math_parameter_spacing_pair(relation_noad_subtype,    inner_noad_subtype),       thick_muskip_code, level_one, indirect_math_regular);
6330
6331    tex_set_split_styles (math_parameter_spacing_pair(relation_noad_subtype,    middle_noad_subtype),      thick_muskip_code, level_one, indirect_math_regular);
6332    tex_set_split_styles (math_parameter_spacing_pair(relation_noad_subtype,    fraction_noad_subtype),    thick_muskip_code, level_one, indirect_math_regular);
6333    tex_set_split_styles (math_parameter_spacing_pair(relation_noad_subtype,    radical_noad_subtype),     thick_muskip_code, level_one, indirect_math_regular);
6334    tex_set_split_styles (math_parameter_spacing_pair(middle_noad_subtype,      relation_noad_subtype),    thick_muskip_code, level_one, indirect_math_regular);
6335    tex_set_split_styles (math_parameter_spacing_pair(fraction_noad_subtype,    relation_noad_subtype),    thick_muskip_code, level_one, indirect_math_regular);
6336    tex_set_split_styles (math_parameter_spacing_pair(radical_noad_subtype,     relation_noad_subtype),    thick_muskip_code, level_one, indirect_math_regular);
6337
6338    tex_set_all_styles   (math_parameter_spacing_pair(close_noad_subtype,       operator_noad_subtype),    thin_muskip_code,  level_one, indirect_math_regular);
6339    tex_set_split_styles (math_parameter_spacing_pair(close_noad_subtype,       binary_noad_subtype),      med_muskip_code,   level_one, indirect_math_regular);
6340    tex_set_split_styles (math_parameter_spacing_pair(close_noad_subtype,       relation_noad_subtype),    thick_muskip_code, level_one, indirect_math_regular);
6341    tex_set_split_styles (math_parameter_spacing_pair(close_noad_subtype,       inner_noad_subtype),       thin_muskip_code,  level_one, indirect_math_regular);
6342
6343    tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, ordinary_noad_subtype),    thin_muskip_code,  level_one, indirect_math_regular);
6344    tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, operator_noad_subtype),    thin_muskip_code,  level_one, indirect_math_regular);
6345    tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, relation_noad_subtype),    thin_muskip_code,  level_one, indirect_math_regular);
6346    tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, open_noad_subtype),        thin_muskip_code,  level_one, indirect_math_regular);
6347    tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, close_noad_subtype),       thin_muskip_code,  level_one, indirect_math_regular);
6348    tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, punctuation_noad_subtype), thin_muskip_code,  level_one, indirect_math_regular);
6349    tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, inner_noad_subtype),       thin_muskip_code,  level_one, indirect_math_regular);
6350
6351    tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, fraction_noad_subtype),    thin_muskip_code,  level_one, indirect_math_regular);
6352    tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, middle_noad_subtype),      thin_muskip_code,  level_one, indirect_math_regular);
6353    tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, radical_noad_subtype),     thin_muskip_code,  level_one, indirect_math_regular);
6354    tex_set_split_styles (math_parameter_spacing_pair(fraction_noad_subtype,    punctuation_noad_subtype), thin_muskip_code,  level_one, indirect_math_regular);
6355    tex_set_split_styles (math_parameter_spacing_pair(middle_noad_subtype,      punctuation_noad_subtype), thin_muskip_code,  level_one, indirect_math_regular);
6356    tex_set_split_styles (math_parameter_spacing_pair(radical_noad_subtype,     punctuation_noad_subtype), thin_muskip_code,  level_one, indirect_math_regular);
6357
6358    tex_set_split_styles (math_parameter_spacing_pair(inner_noad_subtype,       ordinary_noad_subtype),    thin_muskip_code,  level_one, indirect_math_regular);
6359    tex_set_all_styles   (math_parameter_spacing_pair(inner_noad_subtype,       operator_noad_subtype),    thin_muskip_code,  level_one, indirect_math_regular);
6360    tex_set_split_styles (math_parameter_spacing_pair(inner_noad_subtype,       binary_noad_subtype),      med_muskip_code,   level_one, indirect_math_regular);
6361    tex_set_split_styles (math_parameter_spacing_pair(inner_noad_subtype,       relation_noad_subtype),    thick_muskip_code, level_one, indirect_math_regular);
6362    tex_set_split_styles (math_parameter_spacing_pair(inner_noad_subtype,       open_noad_subtype),        thin_muskip_code,  level_one, indirect_math_regular);
6363    tex_set_split_styles (math_parameter_spacing_pair(inner_noad_subtype,       punctuation_noad_subtype), thin_muskip_code,  level_one, indirect_math_regular);
6364    tex_set_split_styles (math_parameter_spacing_pair(inner_noad_subtype,       inner_noad_subtype),       thin_muskip_code,  level_one, indirect_math_regular);
6365
6366    tex_set_split_styles (math_parameter_spacing_pair(inner_noad_subtype,       middle_noad_subtype),      thin_muskip_code,  level_one, indirect_math_regular);
6367    tex_set_split_styles (math_parameter_spacing_pair(fraction_noad_subtype,    inner_noad_subtype),       thin_muskip_code,  level_one, indirect_math_regular);
6368    tex_set_split_styles (math_parameter_spacing_pair(radical_noad_subtype,     inner_noad_subtype),       thin_muskip_code,  level_one, indirect_math_regular);
6369    tex_set_split_styles (math_parameter_spacing_pair(middle_noad_subtype,      inner_noad_subtype),       thin_muskip_code,  level_one, indirect_math_regular);
6370    tex_set_split_styles (math_parameter_spacing_pair(fraction_noad_subtype,    inner_noad_subtype),       thin_muskip_code,  level_one, indirect_math_regular);
6371    tex_set_split_styles (math_parameter_spacing_pair(radical_noad_subtype,     inner_noad_subtype),       thin_muskip_code,  level_one, indirect_math_regular);
6372
6373    /* */
6374
6375    tex_set_all_styles   (math_parameter_x_scale, scaling_factor, level_one, indirect_math_regular);
6376    tex_set_all_styles   (math_parameter_y_scale, scaling_factor, level_one, indirect_math_regular);
6377
6378    /* could be initialize_math_defaults */
6379
6380    tex_set_all_styles   (math_parameter_over_line_variant,       math_variant_presets[math_cramped_style_variant],      level_one, indirect_math_regular);
6381    tex_set_all_styles   (math_parameter_under_line_variant,      math_variant_presets[math_normal_style_variant],       level_one, indirect_math_regular);
6382    tex_set_all_styles   (math_parameter_over_delimiter_variant,  math_variant_presets[math_small_style_variant],        level_one, indirect_math_regular);
6383    tex_set_all_styles   (math_parameter_under_delimiter_variant, math_variant_presets[math_small_style_variant],        level_one, indirect_math_regular);
6384    tex_set_all_styles   (math_parameter_delimiter_over_variant,  math_variant_presets[math_normal_style_variant],       level_one, indirect_math_regular);
6385    tex_set_all_styles   (math_parameter_delimiter_under_variant, math_variant_presets[math_normal_style_variant],       level_one, indirect_math_regular);
6386    tex_set_all_styles   (math_parameter_h_extensible_variant,    math_variant_presets[math_normal_style_variant],       level_one, indirect_math_regular);
6387    tex_set_all_styles   (math_parameter_v_extensible_variant,    math_variant_presets[math_normal_style_variant],       level_one, indirect_math_regular);
6388    tex_set_all_styles   (math_parameter_fraction_variant,        math_variant_presets[math_cramped_style_variant],      level_one, indirect_math_regular);
6389    tex_set_all_styles   (math_parameter_radical_variant,         math_variant_presets[math_cramped_style_variant],      level_one, indirect_math_regular);
6390    tex_set_all_styles   (math_parameter_degree_variant,          math_variant_presets[math_double_superscript_variant], level_one, indirect_math_regular);
6391    tex_set_all_styles   (math_parameter_accent_variant,          math_variant_presets[math_cramped_style_variant],      level_one, indirect_math_regular);
6392    tex_set_all_styles   (math_parameter_top_accent_variant,      math_variant_presets[math_cramped_style_variant],      level_one, indirect_math_regular);
6393    tex_set_all_styles   (math_parameter_bottom_accent_variant,   math_variant_presets[math_cramped_style_variant],      level_one, indirect_math_regular);
6394    tex_set_all_styles   (math_parameter_overlay_accent_variant,  math_variant_presets[math_cramped_style_variant],      level_one, indirect_math_regular);
6395    tex_set_all_styles   (math_parameter_numerator_variant,       math_variant_presets[math_numerator_style_variant],    level_one, indirect_math_regular);
6396    tex_set_all_styles   (math_parameter_denominator_variant,     math_variant_presets[math_denominator_style_variant],  level_one, indirect_math_regular);
6397    tex_set_all_styles   (math_parameter_superscript_variant,     math_variant_presets[math_superscript_style_variant],  level_one, indirect_math_regular);
6398    tex_set_all_styles   (math_parameter_subscript_variant,       math_variant_presets[math_subscript_style_variant],    level_one, indirect_math_regular);
6399    tex_set_all_styles   (math_parameter_prime_variant,           math_variant_presets[math_superscript_style_variant],  level_one, indirect_math_regular);
6400    tex_set_all_styles   (math_parameter_stack_variant,           math_variant_presets[math_numerator_style_variant],    level_one, indirect_math_regular);
6401}
6402
6403/*tex
6404
6405    This needs to be called just at the start of |mlist_to_hlist|, for backward compatibility with
6406    |\scriptspace|.
6407
6408*/
6409
6410void tex_finalize_math_parameters(void)
6411{
6412    int saved_trace = tracing_assigns_par;
6413    tracing_assigns_par = 0;
6414    if (tex_get_math_parameter(display_style,               math_parameter_space_after_script, NULL) == undefined_math_parameter) {
6415        tex_def_math_parameter(display_style,               math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular, 1);
6416        tex_def_math_parameter(text_style,                  math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular, 1);
6417        tex_def_math_parameter(script_style,                math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular, 1);
6418        tex_def_math_parameter(script_script_style,         math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular, 1);
6419        tex_def_math_parameter(cramped_display_style,       math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular, 1);
6420        tex_def_math_parameter(cramped_text_style,          math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular, 1);
6421        tex_def_math_parameter(cramped_script_style,        math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular, 1);
6422        tex_def_math_parameter(cramped_script_script_style, math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular, 1);
6423    }
6424    tracing_assigns_par = saved_trace;
6425}
6426
6427static void tex_aux_math_parameter_error(int style, int param, const char *name)
6428{
6429    if (param >= 0) {
6430        tex_handle_error(
6431            normal_error_type,
6432            "Math error: parameter '%s' with id %i in style %i is not set", 
6433            name, param, style,
6434            "Sorry, but I can't typeset math unless various parameters have been set. This is\n"
6435            "normally done by loading special math fonts into the math family slots. Your font\n"
6436            "set is lacking at least the parameter mentioned earlier."
6437        );
6438    } else {
6439        tex_formatted_error("math", "invalid parameter '%s' in style %i", name, style);
6440    }
6441    return;
6442}
6443
6444/*tex
6445    For the moment this is experimental.
6446*/
6447
6448static inline scaled tex_aux_max_scale(int style, int param)
6449{
6450    scaled scale = tex_get_math_parameter(style, param, NULL);
6451    if (scale > max_math_scaling_factor) {
6452        return max_math_scaling_factor;
6453    } else if (scale < 0) {
6454        return 0;
6455    } else {
6456        return scale;
6457    }
6458}
6459
6460/*tex
6461
6462    The non-staticness of this function is for the benefit of |texmath.w|. Watch out, this one
6463    uses the style! The style and size numbers don't match because we have cramped styles.
6464
6465*/
6466
6467scaled tex_get_math_quad_style(int style)
6468{
6469    scaled scale = tex_aux_max_scale(style, math_parameter_x_scale);
6470    scaled value = tex_get_math_parameter(style, math_parameter_quad, NULL);
6471    if (value == undefined_math_parameter) {
6472        tex_aux_math_parameter_error(style, -1, "quad");
6473        return 0;
6474    } else {
6475        return scaledround(0.001 * value * scale);
6476    }
6477}
6478
6479/*tex
6480
6481    For this reason the next one is different because it is called with a size specifier instead
6482    of a style specifier.
6483
6484*/
6485
6486scaled tex_get_math_axis_size(int size)
6487{
6488    scaled value;
6489    switch (size) {
6490        case script_size       : size = script_style;        break;
6491        case script_script_size: size = script_script_style; break;
6492        default                : size = text_style;          break;
6493    }
6494    value = tex_get_math_parameter(size, math_parameter_axis, NULL);
6495    if (value == undefined_math_parameter) {
6496        tex_aux_math_parameter_error(size, -1, "axis");
6497        return 0;
6498    } else {
6499        return value;
6500    }
6501}
6502
6503scaled tex_get_math_quad_size(int size) 
6504{
6505    switch (size) {
6506        case script_size       : size = script_style;        break;
6507        case script_script_size: size = script_script_style; break;
6508        default                : size = text_style;          break;
6509    }
6510    return tex_get_math_parameter(size, math_parameter_quad, NULL);
6511}
6512
6513scaled tex_get_math_exheight_size(int size)
6514{
6515    switch (size) {
6516        case script_size       : size = script_style;        break;
6517        case script_script_size: size = script_script_style; break;
6518        default                : size = text_style;          break;
6519    }
6520    return tex_get_math_parameter(size, math_parameter_exheight, NULL);
6521}
6522
6523scaled tex_get_math_quad_size_scaled(int size) /* used in cur_mu */
6524{
6525    scaled value, scale;
6526    switch (size) {
6527        case script_size       : size = script_style;        break;
6528        case script_script_size: size = script_script_style; break;
6529        default                : size = text_style;          break;
6530    }
6531    value = tex_get_math_parameter(size, math_parameter_quad, NULL);
6532    scale = tex_aux_max_scale(size, math_parameter_x_scale);
6533 /* return tex_x_over_n(scaledround(0.001 * value * scale), 18); */
6534    return scaledround(0.001 * value * scale / 18.0); /* no glyphscale here .. kind of inconsistent */
6535}
6536
6537scaled tex_get_math_quad_size_unscaled(int size) /* used in cur_mu */
6538{
6539    switch (size) {
6540        case script_size       : size = script_style;        break;
6541        case script_script_size: size = script_script_style; break;
6542        default                : size = text_style;          break;
6543    }
6544    return scaledround(tex_get_math_parameter(size, math_parameter_quad, NULL) / 18.0);
6545}
6546
6547static int tex_aux_math_parameter_okay(int param) 
6548{
6549    if (ignore_math_parameter(param) == 1) {
6550        if (tracing_math_par > 1) {
6551            tex_begin_diagnostic();
6552            tex_print_format("[math: parameter, name %s, ignored]", lmt_name_of_math_parameter(param));
6553            tex_end_diagnostic();
6554        }
6555        return 0;
6556    } else { 
6557        return 1; 
6558    }
6559}
6560
6561scaled tex_get_math_parameter_checked(int style, int param)
6562{
6563    if (tex_aux_math_parameter_okay(param)) {
6564        scaled value = tex_get_math_parameter(style, param, NULL);
6565        if (value == undefined_math_parameter) {
6566            tex_aux_math_parameter_error(style, param, lmt_name_of_math_parameter(param));
6567            return 0;
6568        } else {
6569            return value;
6570        }
6571    } else {
6572        return 0;
6573    }
6574}
6575
6576scaled tex_get_math_parameter_default(int style, int param, scaled dflt)
6577{
6578    if (tex_aux_math_parameter_okay(param)) {
6579        scaled value = tex_get_math_parameter(style, param, NULL);
6580        if (value == undefined_math_parameter) {
6581            return dflt;
6582        } else {
6583            return value;
6584        }
6585    } else {
6586        return dflt;
6587    }
6588}
6589
6590void tex_run_math_italic_correction(void) {
6591    if (cur_chr == italic_correction_code) {    
6592        tex_tail_append(tex_new_kern_node(0, explicit_kern_subtype)); /* maybe math_shape_kern */
6593    }
6594}
6595
6596/* */
6597
6598scaled tex_get_math_x_parameter(int style, int param)
6599{
6600    if (tex_aux_math_parameter_okay(param)) {
6601        scaled scale = tex_aux_max_scale(style, math_parameter_x_scale);
6602        scaled value = tex_get_math_parameter(style, param, NULL);
6603        if (value == undefined_math_parameter) {
6604            return value;  // ?? scaledround(value * scale * 0.001);
6605        } else {
6606            return value ? scaledround(0.000000001 * glyph_scale_par * glyph_x_scale_par * value * scale) : 0;
6607        }
6608    } else {
6609        return 0;
6610    }
6611}
6612
6613scaled tex_get_math_x_parameter_checked(int style, int param)
6614{
6615    if (tex_aux_math_parameter_okay(param)) {
6616        scaled scale = tex_aux_max_scale(style, math_parameter_x_scale);
6617        scaled value = tex_get_math_parameter(style, param, NULL);
6618        if (value == undefined_math_parameter) {
6619            tex_aux_math_parameter_error(style, param, lmt_name_of_math_parameter(param));
6620            return 0;
6621        } else {
6622            return value ? scaledround(0.000000001 * glyph_scale_par * glyph_x_scale_par * value * scale) : 0;
6623        }
6624    } else {
6625        return 0;
6626    }
6627}
6628
6629scaled tex_get_math_x_parameter_default(int style, int param, scaled dflt)
6630{
6631    if (tex_aux_math_parameter_okay(param)) {
6632        scaled scale = tex_aux_max_scale(style, math_parameter_x_scale);
6633        scaled value = tex_get_math_parameter(style, param, NULL);
6634        if (value == undefined_math_parameter) {
6635            return dflt;
6636        } else{
6637            return value ? scaledround(0.000000001 * glyph_scale_par * glyph_x_scale_par * value * scale) : 0;
6638        }
6639    } else {
6640        return dflt;
6641    }
6642}
6643
6644scaled tex_get_math_y_parameter(int style, int param)
6645{
6646    if (tex_aux_math_parameter_okay(param)) {
6647        scaled scale = tex_aux_max_scale(style, math_parameter_y_scale);
6648        scaled value = tex_get_math_parameter(style, param, NULL);
6649        if (value == undefined_math_parameter) {
6650            return value;
6651        } else{
6652            return value ? scaledround(0.000000001 * glyph_scale_par * glyph_y_scale_par * value * scale) : 0;
6653        }
6654    } else {
6655        return 0;
6656    }
6657}
6658
6659scaled tex_get_math_y_parameter_checked(int style, int param)
6660{
6661    if (tex_aux_math_parameter_okay(param)) {
6662        scaled scale = tex_aux_max_scale(style, math_parameter_y_scale);
6663        scaled value = tex_get_math_parameter(style, param, NULL);
6664        if (value == undefined_math_parameter) {
6665            tex_aux_math_parameter_error(style, param, lmt_name_of_math_parameter(param));
6666            return 0;
6667        } else {
6668            return value ? scaledround(0.000000001 * glyph_scale_par * glyph_y_scale_par * value * scale) : 0;
6669        }
6670    } else {
6671        return 0;
6672    }
6673}
6674
6675scaled tex_get_math_y_parameter_default(int style, int param, scaled dflt)
6676{
6677    if (tex_aux_math_parameter_okay(param)) {
6678        scaled scale = tex_aux_max_scale(style, math_parameter_y_scale);
6679        scaled value = tex_get_math_parameter(style, param, NULL);
6680        if (value == undefined_math_parameter) {
6681            return dflt;
6682        } else {
6683            return value ? scaledround(0.000000001 * glyph_scale_par * glyph_y_scale_par * value * scale) : 0;
6684        }
6685    } else {
6686        return dflt;
6687    }
6688}
6689
6690scaled tex_get_font_math_parameter(int font, int size, int param)
6691{
6692    scaled scale = tex_get_math_font_scale(font, size);
6693    scaled value = tex_aux_get_font_math_parameter(scale, font, param);
6694    if (value == undefined_math_parameter) {
6695        return undefined_math_parameter;
6696    } else {
6697        return value ? scaledround(0.001 * glyph_scale_par * value) : 0;
6698    }
6699}
6700
6701/* maybe more precission, so multiply all and divide by 0.000000001 */
6702
6703scaled tex_get_font_math_y_parameter(int font, int size, int param)
6704{
6705    scaled scale = tex_get_math_font_scale(font, size);
6706    scaled value = tex_aux_get_font_math_parameter(scale, font, param);
6707    if (value == undefined_math_parameter) {
6708        return undefined_math_parameter;
6709    } else {
6710        return value ? scaledround(0.000001 * glyph_scale_par * glyph_y_scale_par * value) : 0;
6711    }
6712}
6713
6714scaled tex_get_font_math_x_parameter(int font, int size, int param)
6715{
6716    scaled scale = tex_get_math_font_scale(font, size);
6717    scaled value = tex_aux_get_font_math_parameter(scale, font, param);
6718    if (value == undefined_math_parameter) {
6719        return undefined_math_parameter;
6720    } else {
6721        return value ? scaledround(0.000001 * glyph_scale_par * glyph_x_scale_par * value) : 0;
6722    }
6723}
6724
6725halfword tex_to_math_spacing_parameter(halfword left, halfword right)
6726{
6727    halfword param = math_parameter_spacing_pair(left,right);
6728    return (param >= math_parameter_atom_pairs_first && param <= math_parameter_atom_pairs_last) ? param : -1;
6729}
6730
6731halfword tex_to_math_rules_parameter(halfword left, halfword right)
6732{
6733    halfword param = math_parameter_rules_pair(left,right);
6734    return (param >= math_parameter_atom_rules_first && param <= math_parameter_atom_rules_last) ? param : -1;
6735}
6736
6737void tex_set_default_math_codes(void)
6738{
6739    mathcodeval mval = tex_no_math_code();
6740    /*tex This will remap old font families at runtime. */
6741    mval.class_value = math_use_current_family_code;
6742    /*tex Upright math digts come from family 0. */
6743    for (int d = '0'; d <= '9'; d++) {
6744        mval.character_value = d;
6745        tex_set_math_code(d, mval, level_one);
6746    }
6747    /* In traditional fonts math italic has family 1. */
6748    mval.family_value = 1;
6749    for (int u = 'A'; u <= 'Z'; u++) {
6750        mval.character_value = u;
6751        tex_set_math_code(u, mval, level_one);
6752    }
6753    for (int l = 'a'; l <= 'z'; l++) {
6754        mval.character_value = l;
6755        tex_set_math_code(l, mval, level_one);
6756    }
6757    /*tex This is kind of standard. */
6758    tex_set_del_code('.', (delcodeval) { { 0, 0, 0, }, { 0, 0, 0 } }, level_one);
6759}
6760
6761int tex_in_main_math_style(halfword style)
6762{
6763    switch (style) {
6764        case display_style:
6765        case text_style:
6766            return 1;
6767        /*
6768         case cramped_display_style:
6769         case cramped_text_style:
6770            return 0; // could be parameter driven
6771        */
6772        default: 
6773            return 0;
6774    }
6775}
6776