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