1use crate::Engine;
10use crate::build::PackSpec;
11use crate::eqtb::{Loc, Val};
12use crate::glue::Glue;
13use crate::input::{Level, TokKind};
14use crate::maincontrol::{IGNORE_DEPTH, Mode};
15use crate::nodes::{NodeData, NodeRef, glue_sub, id};
16use crate::primitives_gen::{assign_dimen as dp, assign_glue as gp, assign_int as ip};
17use crate::save::GroupCode;
18use crate::typeset::{
19 AWFUL_BAD, DEPLORABLE, EJECT_PENALTY, INF_BAD, INF_PENALTY, PageContents, PageIns, mark_code,
20};
21
22#[inline]
25pub fn precedes_break(t: u8) -> bool {
26 t < id::MATH
27}
28
29impl Engine {
30 pub fn prune_page_top(&mut self, p: NodeRef, save: bool) -> NodeRef {
36 let head = self.nodes.new_temp();
37 self.nodes.link(head, p);
38 let mut prev_p = head;
39 let mut p = p;
40 let mut r = NodeRef::NULL;
41 while !p.is_null() {
42 match self.nodes.id(p) {
43 id::HLIST | id::VLIST | id::RULE => {
44 let mut g = self.eqtb.glue(gp::SPLITTOPSKIP);
45 let h = self.node_height(p);
46 g.width = if g.width > h { g.width - h } else { 0 };
47 let q = self.new_glue_node(g, glue_sub::SPLIT_TOP_SKIP);
48 self.nodes.link(prev_p, q);
49 self.nodes.link(q, p);
50 break;
51 }
52 id::GLUE | id::KERN | id::PENALTY => {
53 let q = p;
54 p = self.nodes.next(q);
55 self.nodes.set_next(q, NodeRef::NULL);
56 self.nodes.set_next(prev_p, p);
57 if !p.is_null() {
58 self.nodes.set_prev(p, prev_p);
59 }
60 if save {
61 if self.ts.disc_ptr[3].is_null() {
62 self.ts.disc_ptr[3] = q;
63 } else {
64 self.nodes.link(r, q);
65 }
66 r = q;
67 } else {
68 self.nodes.flush_list(q);
69 }
70 }
71 _ => {
72 prev_p = p;
73 p = self.nodes.next(prev_p);
74 }
75 }
76 }
77 let first = self.nodes.next(head);
78 self.nodes.free(head);
79 if !first.is_null() {
80 self.nodes.set_prev(first, NodeRef::NULL);
81 }
82 first
83 }
84
85 pub fn vert_break(&mut self, p: NodeRef, h: i32, d: i32) -> (NodeRef, i32) {
89 let mut prev_p = p;
90 let mut p = p;
91 let mut least_cost = AWFUL_BAD;
92 let mut best_place = NodeRef::NULL;
93 let mut best_hpd = 0;
94 let mut cur_height = 0i32;
95 let mut stretch = [0i32; 5];
96 let mut shrink = 0i32;
97 let mut prev_dp = 0i32;
98 loop {
99 let mut pi: Option<i32> = None;
101 let mut update = false;
102 if p.is_null() {
103 pi = Some(EJECT_PENALTY);
104 } else {
105 match self.nodes.id(p) {
106 id::HLIST | id::VLIST | id::RULE => {
107 cur_height += prev_dp + self.node_height(p);
108 prev_dp = self.node_depth(p);
109 }
110 id::GLUE => {
111 if precedes_break(self.nodes.id(prev_p)) {
112 pi = Some(0);
113 } else {
114 update = true;
115 }
116 }
117 id::KERN => {
118 let n = self.nodes.next(p);
119 let t = if n.is_null() {
120 id::PENALTY
121 } else {
122 self.nodes.id(n)
123 };
124 if t == id::GLUE {
125 pi = Some(0);
126 } else {
127 update = true;
128 }
129 }
130 id::PENALTY => pi = Some(self.nodes.penalty(p)),
131 _ => {}
132 }
133 }
134 if let Some(pi) = pi {
135 if pi < INF_PENALTY {
136 let b = if cur_height < h {
137 if stretch[1..].iter().any(|&s| s != 0) {
138 0
139 } else {
140 retex_arith::badness(h - cur_height, stretch[0])
141 }
142 } else if cur_height - h > shrink {
143 AWFUL_BAD
144 } else {
145 retex_arith::badness(cur_height - h, shrink)
146 };
147 let b = if b < AWFUL_BAD {
148 if pi <= EJECT_PENALTY {
149 pi
150 } else if b < INF_BAD {
151 b + pi
152 } else {
153 DEPLORABLE
154 }
155 } else {
156 b
157 };
158 if b <= least_cost {
159 best_place = p;
160 least_cost = b;
161 best_hpd = cur_height + prev_dp;
162 }
163 if b == AWFUL_BAD || pi <= EJECT_PENALTY {
164 return (best_place, best_hpd);
165 }
166 }
167 let t = self.nodes.id(p);
168 if t == id::GLUE || t == id::KERN {
169 update = true;
170 }
171 }
172 if update {
173 if self.nodes.id(p) == id::KERN {
174 cur_height += prev_dp + self.nodes.width(p);
175 } else {
176 let mut q = self.nodes.glue_spec(p);
177 stretch[(q.stretch_order as usize).min(4)] += q.stretch;
178 shrink += q.shrink;
179 if q.shrink_order != 0 && q.shrink != 0 {
180 self.print_err("Infinite glue shrinkage found in box being split");
181 self.help(&[
182 "The box you are \\vsplitting contains some infinitely",
183 "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",
184 "Such glue doesn't belong there; but you can safely proceed,",
185 "since the offensive shrinkability has been made finite.",
186 ]);
187 self.error();
188 q.shrink_order = 0;
189 if let NodeData::Glue { spec, .. } = self.nodes.data_mut(p) {
190 spec.shrink_order = 0;
191 }
192 }
193 cur_height += prev_dp + q.width;
194 }
195 prev_dp = 0;
196 }
197 if prev_dp > d {
198 cur_height = cur_height + prev_dp - d;
199 prev_dp = d;
200 }
201 prev_p = p;
202 p = self.nodes.next(prev_p);
203 }
204 }
205
206 pub fn vsplit(&mut self, n: u32, h: i32) -> NodeRef {
208 let v = self.eqtb.boxes[n as usize].val;
209 let sd = std::mem::take(&mut self.ts.disc_ptr[3]);
210 self.nodes.flush_list(sd);
211 for m in self.marks.classes.values_mut() {
212 m[mark_code::SPLIT_FIRST] = None;
213 m[mark_code::SPLIT_BOT] = None;
214 }
215 if v.is_null() {
216 return NodeRef::NULL;
217 }
218 if self.nodes.id(v) != id::VLIST {
219 self.print_err("");
220 self.print_esc_str("vsplit");
221 self.printer.print_str(" needs a ");
222 self.print_esc_str("vbox");
223 self.help(&[
224 "The box you are trying to split is an \\hbox.",
225 "i can't split such a box, so I''ll leave it alone.",
226 ]);
227 self.error();
228 return NodeRef::NULL;
229 }
230 let smd = self.eqtb.dimen(dp::SPLITMAXDEPTH);
231 let list = self.nodes.list_ptr(v);
232 let (q, _) = self.vert_break(list, h, smd);
233 let mut p = list;
235 if p == q {
236 self.nodes.set_list_ptr(v, NodeRef::NULL);
237 } else {
238 loop {
239 if let NodeData::Mark { class, mark } = self.nodes.data(p) {
240 let (c, m) = (*class, mark.clone());
241 let e = self.marks.classes.entry(c).or_default();
242 if e[mark_code::SPLIT_FIRST].is_none() {
243 e[mark_code::SPLIT_FIRST] = Some(m.clone());
244 }
245 e[mark_code::SPLIT_BOT] = Some(m);
246 }
247 let nx = self.nodes.next(p);
248 if nx == q {
249 self.nodes.set_next(p, NodeRef::NULL);
250 break;
251 }
252 p = nx;
253 }
254 }
255 let save = self.eqtb.int(ip::SAVINGVDISCARDS) > 0;
256 let q = self.prune_page_top(q, save);
257 let p = self.nodes.list_ptr(v);
258 self.nodes.set_list_ptr(v, NodeRef::NULL);
259 self.nodes.free(v);
260 self.eqtb.boxes[n as usize].val = if q.is_null() {
261 NodeRef::NULL
262 } else {
263 self.vpack(q, PackSpec::NATURAL)
264 };
265 self.vpackage(p, PackSpec::Exactly(h), smd)
266 }
267
268 fn box_error(&mut self, n: u32) {
272 self.error();
273 self.begin_diagnostic();
274 self.printer.print_nl("The following box has been deleted:");
275 let b = self.eqtb.boxes[n as usize].val;
276 self.show_box(b);
277 self.end_diagnostic(true);
278 self.nodes.flush_list(b);
279 self.eqtb.boxes[n as usize].val = NodeRef::NULL;
280 }
281
282 fn ensure_vbox(&mut self, n: u32) {
284 let p = self.eqtb.boxes[n as usize].val;
285 if !p.is_null() && self.nodes.id(p) == id::HLIST {
286 self.print_err("Insertions can only be added to a vbox");
287 self.help(&[
288 "Tut tut: You're trying to \\insert into a",
289 "\\box register that now contains an \\hbox.",
290 "Proceed, and I'll discard its present contents.",
291 ]);
292 self.box_error(n);
293 }
294 }
295
296 fn freeze_page_specs(&mut self, s: PageContents) {
298 self.page.contents = s;
299 self.page.so_far = [0; 8];
300 self.page.fi_stretch = 0;
301 self.page.so_far[0] = self.eqtb.dimen(dp::VSIZE);
302 self.page.max_depth = self.eqtb.dimen(dp::MAXDEPTH);
303 self.page.least_cost = AWFUL_BAD;
304 if self.eqtb.int(ip::TRACINGPAGES) > 0 {
305 self.begin_diagnostic();
306 self.printer.print_nl("%% goal height=");
307 self.printer.print_scaled(self.page.so_far[0]);
308 self.printer.print_str(", max depth=");
309 self.printer.print_scaled(self.page.max_depth);
310 self.end_diagnostic(false);
311 }
312 }
313
314 fn start_new_page(&mut self) {
316 self.page.contents = PageContents::Empty;
317 self.page.page_tail = self.page.page_head;
318 self.nodes.set_next(self.page.page_head, NodeRef::NULL);
319 self.page.last_glue = None;
320 self.page.last_penalty = 0;
321 self.page.last_kern = 0;
322 self.page.last_node_type = -1;
323 self.page.so_far[7] = 0;
324 self.page.max_depth = 0;
325 }
326
327 #[inline]
329 fn set_contrib_tail(&mut self, t: NodeRef) {
330 self.nest[0].tail = t;
331 }
332
333 pub fn build_page(&mut self) {
335 let ch = self.page.contrib_head;
336 if self.nodes.next(ch).is_null() || self.ts.output_active {
337 return;
338 }
339 loop {
340 let p = self.nodes.next(ch);
341 if p.is_null() {
342 break;
343 }
344 self.page.last_penalty = 0;
346 self.page.last_kern = 0;
347 let t = self.nodes.id(p);
348 self.page.last_node_type = crate::typeset::etex_node_type(t, self.nodes.subtype(p));
349 if t == id::GLUE {
350 self.page.last_glue = Some(self.nodes.glue_spec(p));
351 } else {
352 self.page.last_glue = None;
353 if t == id::PENALTY {
354 self.page.last_penalty = self.nodes.penalty(p);
355 } else if t == id::KERN {
356 self.page.last_kern = self.nodes.width(p);
357 }
358 }
359 enum Act {
361 Contribute,
362 UpdateHeights,
363 Recycle,
364 Break(i32),
365 }
366 let contents = self.page.contents;
367 let act = match t {
368 id::HLIST | id::VLIST | id::RULE => {
369 if contents < PageContents::BoxThere {
370 if contents == PageContents::Empty {
372 self.freeze_page_specs(PageContents::BoxThere);
373 } else {
374 self.page.contents = PageContents::BoxThere;
375 }
376 let mut g = self.eqtb.glue(gp::TOPSKIP);
377 let h = self.node_height(p);
378 g.width = if g.width > h { g.width - h } else { 0 };
379 let q = self.new_glue_node(g, glue_sub::TOP_SKIP);
380 self.nodes.link(q, p);
381 self.nodes.link(ch, q);
382 continue;
383 }
384 self.page.so_far[1] += self.page.so_far[7] + self.node_height(p);
385 self.page.so_far[7] = self.node_depth(p);
386 Act::Contribute
387 }
388 id::GLUE => {
389 if contents < PageContents::BoxThere {
390 Act::Recycle
391 } else if precedes_break(self.nodes.id(self.page.page_tail)) {
392 Act::Break(0)
393 } else {
394 Act::UpdateHeights
395 }
396 }
397 id::KERN => {
398 if contents < PageContents::BoxThere {
399 Act::Recycle
400 } else {
401 let n = self.nodes.next(p);
402 if n.is_null() {
403 return;
404 } else if self.nodes.id(n) == id::GLUE {
405 Act::Break(0)
406 } else {
407 Act::UpdateHeights
408 }
409 }
410 }
411 id::PENALTY => {
412 if contents < PageContents::BoxThere {
413 Act::Recycle
414 } else {
415 Act::Break(self.nodes.penalty(p))
416 }
417 }
418 id::INS => {
419 self.append_insertion(p);
420 Act::Contribute
421 }
422 _ => Act::Contribute,
423 };
424 let mut update = matches!(act, Act::UpdateHeights);
425 match act {
426 Act::Recycle => {
427 let n = self.nodes.next(p);
428 self.nodes.set_next(ch, n);
429 self.nodes.set_next(p, NodeRef::NULL);
430 if self.eqtb.int(ip::SAVINGVDISCARDS) > 0 {
431 if self.ts.disc_ptr[2].is_null() {
432 self.ts.disc_ptr[2] = p;
433 } else {
434 let t = self.ts.disc_ptr[1];
435 self.nodes.link(t, p);
436 }
437 self.ts.disc_ptr[1] = p;
438 } else {
439 self.nodes.flush_list(p);
440 }
441 continue;
442 }
443 Act::Break(pi) => {
444 if pi < INF_PENALTY {
445 let b = self.page_badness();
446 let ip_ = self.insert_penalties;
447 let mut c = if b < AWFUL_BAD {
448 if pi <= EJECT_PENALTY {
449 pi
450 } else if b < INF_BAD {
451 b + pi + ip_
452 } else {
453 DEPLORABLE
454 }
455 } else {
456 b
457 };
458 if ip_ >= 10000 {
459 c = AWFUL_BAD;
460 }
461 if self.eqtb.int(ip::TRACINGPAGES) > 0 {
462 self.begin_diagnostic();
463 self.printer.print_nl("%");
464 self.printer.print_str(" t=");
465 self.print_totals();
466 self.printer.print_str(" g=");
467 self.printer.print_scaled(self.page.so_far[0]);
468 self.printer.print_str(" b=");
469 if b == AWFUL_BAD {
470 self.printer.print_char('*' as u32);
471 } else {
472 self.printer.print_int(i64::from(b));
473 }
474 self.printer.print_str(" p=");
475 self.printer.print_int(i64::from(pi));
476 self.printer.print_str(" c=");
477 if c == AWFUL_BAD {
478 self.printer.print_char('*' as u32);
479 } else {
480 self.printer.print_int(i64::from(c));
481 }
482 if c <= self.page.least_cost {
483 self.printer.print_char('#' as u32);
484 }
485 self.end_diagnostic(false);
486 }
487 if c <= self.page.least_cost {
488 self.page.best_break = p;
489 self.page.best_size = self.page.so_far[0];
490 self.page.least_cost = c;
491 for r in self.page.ins.iter_mut() {
492 r.best_ins_ptr = r.last_ins_ptr;
493 }
494 }
495 if c == AWFUL_BAD || pi <= EJECT_PENALTY {
496 self.fire_up(p);
497 if self.ts.output_active {
498 return;
499 }
500 continue;
503 }
504 }
505 if t == id::GLUE || t == id::KERN {
506 update = true;
507 }
508 }
509 _ => {}
510 }
511 if update {
512 if t == id::KERN {
513 self.page.so_far[1] += self.page.so_far[7] + self.nodes.width(p);
514 } else {
515 let q = self.nodes.glue_spec(p);
516 *self.page.stretch_mut(q.stretch_order) += q.stretch;
517 self.page.so_far[6] += q.shrink;
518 if q.shrink_order != 0 && q.shrink != 0 {
519 self.print_err("Infinite glue shrinkage found on current page");
520 self.help(&[
521 "The page about to be output contains some infinitely",
522 "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",
523 "Such glue doesn't belong there; but you can safely proceed,",
524 "since the offensive shrinkability has been made finite.",
525 ]);
526 self.error();
527 if let NodeData::Glue { spec, .. } = self.nodes.data_mut(p) {
528 spec.shrink_order = 0;
529 }
530 }
531 self.page.so_far[1] += self.page.so_far[7] + q.width;
532 }
533 self.page.so_far[7] = 0;
534 }
535 if self.page.so_far[7] > self.page.max_depth {
537 self.page.so_far[1] += self.page.so_far[7] - self.page.max_depth;
538 self.page.so_far[7] = self.page.max_depth;
539 }
540 let n = self.nodes.next(p);
542 let pt = self.page.page_tail;
543 self.nodes.link(pt, p);
544 self.page.page_tail = p;
545 self.nodes.set_next(ch, n);
546 if !n.is_null() {
547 self.nodes.set_prev(n, ch);
548 }
549 self.nodes.set_next(p, NodeRef::NULL);
550 }
551 self.set_contrib_tail(ch);
553 }
554
555 fn page_badness(&self) -> i32 {
557 let s = &self.page.so_far;
558 let (goal, total) = (s[0], s[1]);
559 if total < goal {
560 if self.page.fi_stretch != 0 || s[3] != 0 || s[4] != 0 || s[5] != 0 {
561 0
562 } else {
563 retex_arith::badness(goal - total, s[2])
564 }
565 } else if total - goal > s[6] {
566 AWFUL_BAD
567 } else {
568 retex_arith::badness(total - goal, s[6])
569 }
570 }
571
572 fn append_insertion(&mut self, p: NodeRef) {
574 if self.page.contents == PageContents::Empty {
575 self.freeze_page_specs(PageContents::InsertsOnly);
576 }
577 let n = u32::from(self.nodes.subtype(p));
578 let (pheight, pdepth, pcost, plist) = match self.nodes.data(p) {
579 NodeData::Ins {
580 height,
581 depth,
582 cost,
583 head,
584 ..
585 } => (*height, *depth, *cost, *head),
586 _ => return,
587 };
588 let idx = match self.page.ins.iter().position(|r| r.n >= n) {
589 Some(i) if self.page.ins[i].n == n => i,
590 pos => {
591 let i = pos.unwrap_or(self.page.ins.len());
593 self.ensure_vbox(n);
594 let b = self.eqtb.boxes[n as usize].val;
595 let height = if b.is_null() {
596 0
597 } else {
598 self.node_height(b) + self.node_depth(b)
599 };
600 self.page.ins.insert(
601 i,
602 PageIns {
603 n,
604 split_up: false,
605 height,
606 broken_ptr: NodeRef::NULL,
607 broken_ins: NodeRef::NULL,
608 last_ins_ptr: NodeRef::NULL,
609 best_ins_ptr: NodeRef::NULL,
610 },
611 );
612 let q = self.eqtb.skip[n as usize].val;
613 let c = self.eqtb.count[n as usize].val;
614 let h = if c == 1000 {
615 height
616 } else {
617 x_over_n(height, 1000).wrapping_mul(c)
618 };
619 self.page.so_far[0] = self.page.so_far[0] - h - q.width;
620 *self.page.stretch_mut(q.stretch_order) += q.stretch;
621 self.page.so_far[6] += q.shrink;
622 if q.shrink_order != 0 && q.shrink != 0 {
623 self.print_err("Infinite glue shrinkage inserted from ");
624 self.print_esc_str("skip");
625 self.printer.print_int(i64::from(n));
626 self.help(&[
627 "The correction glue for page breaking with insertions",
628 "must have finite shrinkability. But you may proceed,",
629 "since the offensive shrinkability has been made finite.",
630 ]);
631 self.error();
632 }
633 i
634 }
635 };
636 if self.page.ins[idx].split_up {
637 self.insert_penalties += pcost;
638 return;
639 }
640 self.page.ins[idx].last_ins_ptr = p;
641 let s = &self.page.so_far;
642 let delta = s[0] - s[1] - s[7] + s[6];
643 let c = self.eqtb.count[n as usize].val;
644 let h = if c == 1000 {
645 pheight
646 } else {
647 x_over_n(pheight, 1000).wrapping_mul(c)
648 };
649 let dimen_n = self.eqtb.dimen[n as usize].val;
650 if (h <= 0 || h <= delta) && pheight + self.page.ins[idx].height <= dimen_n {
651 self.page.so_far[0] -= h;
652 self.page.ins[idx].height += pheight;
653 } else {
654 let mut w = if c <= 0 {
656 retex_arith::MAX_DIMEN
657 } else {
658 let mut w = self.page.so_far[0] - self.page.so_far[1] - self.page.so_far[7];
659 if c != 1000 {
660 w = x_over_n(w, c).wrapping_mul(1000);
661 }
662 w
663 };
664 if w > dimen_n - self.page.ins[idx].height {
665 w = dimen_n - self.page.ins[idx].height;
666 }
667 let (q, mut bhpd) = self.vert_break(plist, w, pdepth);
668 self.page.ins[idx].height += bhpd;
669 if self.eqtb.int(ip::TRACINGPAGES) > 0 {
670 self.begin_diagnostic();
671 self.printer.print_nl("% split");
672 self.printer.print_int(i64::from(n));
673 self.printer.print_str(" to ");
674 self.printer.print_scaled(w);
675 self.printer.print_char(',' as u32);
676 self.printer.print_scaled(bhpd);
677 self.printer.print_str(" p=");
678 if q.is_null() {
679 self.printer.print_int(i64::from(EJECT_PENALTY));
680 } else if self.nodes.id(q) == id::PENALTY {
681 self.printer.print_int(i64::from(self.nodes.penalty(q)));
682 } else {
683 self.printer.print_char('0' as u32);
684 }
685 self.end_diagnostic(false);
686 }
687 if c != 1000 {
688 bhpd = x_over_n(bhpd, 1000).wrapping_mul(c);
689 }
690 self.page.so_far[0] -= bhpd;
691 let r = &mut self.page.ins[idx];
692 r.split_up = true;
693 r.broken_ptr = q;
694 r.broken_ins = p;
695 if q.is_null() {
696 self.insert_penalties += EJECT_PENALTY;
697 } else if self.nodes.id(q) == id::PENALTY {
698 self.insert_penalties += self.nodes.penalty(q);
699 }
700 }
701 }
702
703 fn fire_up(&mut self, c: NodeRef) {
705 let bpb = self.page.best_break;
706 if !bpb.is_null() && self.nodes.id(bpb) == id::PENALTY {
708 let pen = self.nodes.penalty(bpb);
709 self.geq_define(Loc::IntPar(ip::OUTPUTPENALTY), Val::Int(i64::from(pen)));
710 if let NodeData::Penalty { penalty } = self.nodes.data_mut(bpb) {
711 *penalty = INF_PENALTY;
712 }
713 } else {
714 self.geq_define(
715 Loc::IntPar(ip::OUTPUTPENALTY),
716 Val::Int(i64::from(INF_PENALTY)),
717 );
718 }
719 for m in self.marks.classes.values_mut() {
720 if m[mark_code::BOT].is_some() {
721 m[mark_code::TOP] = m[mark_code::BOT].clone();
722 m[mark_code::FIRST] = None;
723 }
724 }
725 if c == self.page.best_break {
726 self.page.best_break = NodeRef::NULL;
727 }
728 let best_break = self.page.best_break;
729 let obox = self.output_box_num() as u32;
731 if !self.eqtb.boxes[obox as usize].val.is_null() {
732 self.print_err("");
733 self.print_esc_str("box");
734 self.printer.print_int(i64::from(obox));
735 self.printer.print_str(" is not void");
736 self.help(&[
737 "You shouldn't use \\box255 except in \\output routines.",
738 "Proceed, and I'll discard its present contents.",
739 ]);
740 self.box_error(obox);
741 }
742 self.insert_penalties = 0;
743 let save_split_top_skip = self.eqtb.glue_par[gp::SPLITTOPSKIP as usize].val;
744 let holding = self.eqtb.int(ip::HOLDINGINSERTS) > 0;
745 if !holding {
746 for i in 0..self.page.ins.len() {
749 if !self.page.ins[i].best_ins_ptr.is_null() {
750 let n = self.page.ins[i].n;
751 self.ensure_vbox(n);
752 if self.eqtb.boxes[n as usize].val.is_null() {
753 let b = self.nodes.new_null_box(false);
754 self.eqtb.boxes[n as usize].val = b;
755 }
756 let b = self.eqtb.boxes[n as usize].val;
757 let l = self.nodes.list_ptr(b);
758 self.page.ins[i].last_ins_ptr = self.nodes.tail_of(l);
759 }
760 }
761 }
762 let hold_head = self.nodes.new_temp();
764 let mut q = hold_head;
765 let mut prev_p = self.page.page_head;
766 let mut p = self.nodes.next(prev_p);
767 while p != best_break && !p.is_null() {
768 let t = self.nodes.id(p);
769 if t == id::INS {
770 if !holding {
771 let n = u32::from(self.nodes.subtype(p));
772 let Some(ri) = self.page.ins.iter().position(|r| r.n == n) else {
773 prev_p = p;
774 p = self.nodes.next(prev_p);
775 continue;
776 };
777 let mut wait;
778 if self.page.ins[ri].best_ins_ptr.is_null() {
779 wait = true;
780 } else {
781 wait = false;
782 let bx = self.eqtb.boxes[n as usize].val;
783 let start = self.page.ins[ri].last_ins_ptr;
784 let il = self.nodes.list_ptr(p);
785 if start.is_null() {
786 self.nodes.set_list_ptr(bx, il);
787 } else {
788 self.nodes.link(start, il);
789 }
790 if self.page.ins[ri].best_ins_ptr == p {
791 let r = self.page.ins[ri];
794 if r.split_up && r.broken_ins == p && !r.broken_ptr.is_null() {
795 if start.is_null() && self.nodes.list_ptr(bx) == r.broken_ptr {
796 self.nodes.set_list_ptr(bx, NodeRef::NULL);
797 } else {
798 let mut s = if start.is_null() {
799 self.nodes.list_ptr(bx)
800 } else {
801 start
802 };
803 while !s.is_null() && self.nodes.next(s) != r.broken_ptr {
804 s = self.nodes.next(s);
805 }
806 if !s.is_null() {
807 self.nodes.set_next(s, NodeRef::NULL);
808 }
809 }
810 let sts = match self.nodes.data(p) {
811 NodeData::Ins { spec, .. } => *spec,
812 _ => Glue::ZERO,
813 };
814 self.eqtb.glue_par[gp::SPLITTOPSKIP as usize].val = sts;
815 let rest = self.prune_page_top(r.broken_ptr, false);
816 self.nodes.set_list_ptr(p, rest);
817 if !rest.is_null() {
818 let tmp = self.vpack(rest, PackSpec::NATURAL);
819 let hh = self.node_height(tmp) + self.node_depth(tmp);
820 self.nodes.set_list_ptr(tmp, NodeRef::NULL);
821 self.nodes.free(tmp);
822 if let NodeData::Ins { height, .. } = self.nodes.data_mut(p) {
823 *height = hh;
824 }
825 wait = true;
826 }
827 }
828 self.page.ins[ri].best_ins_ptr = NodeRef::NULL;
829 let l = self.nodes.list_ptr(bx);
830 self.nodes.set_list_ptr(bx, NodeRef::NULL);
831 self.nodes.free(bx);
832 let nb = self.vpack(l, PackSpec::NATURAL);
833 self.eqtb.boxes[n as usize].val = nb;
834 } else {
835 let l = self.nodes.list_ptr(bx);
836 self.page.ins[ri].last_ins_ptr = self.nodes.tail_of(l);
837 }
838 }
839 let nx = self.nodes.next(p);
842 self.nodes.set_next(prev_p, nx);
843 if !nx.is_null() {
844 self.nodes.set_prev(nx, prev_p);
845 }
846 self.nodes.set_next(p, NodeRef::NULL);
847 if wait {
848 self.nodes.link(q, p);
849 q = p;
850 self.insert_penalties += 1;
851 } else {
852 self.nodes.set_list_ptr(p, NodeRef::NULL);
853 self.nodes.free(p);
854 }
855 p = prev_p;
856 }
857 } else if let NodeData::Mark { class, mark } = self.nodes.data(p) {
858 let (cl, m) = (*class, mark.clone());
859 let e = self.marks.classes.entry(cl).or_default();
860 if e[mark_code::FIRST].is_none() {
861 e[mark_code::FIRST] = Some(m.clone());
862 }
863 e[mark_code::BOT] = Some(m);
864 }
865 prev_p = p;
866 p = self.nodes.next(prev_p);
867 }
868 self.eqtb.glue_par[gp::SPLITTOPSKIP as usize].val = save_split_top_skip;
869 let ch = self.page.contrib_head;
872 if !p.is_null() {
873 if self.nodes.next(ch).is_null() {
874 let pt = self.page.page_tail;
875 self.set_contrib_tail(pt);
876 }
877 let pt = self.page.page_tail;
878 let cn = self.nodes.next(ch);
879 self.nodes.set_next(pt, cn);
880 if !cn.is_null() {
881 self.nodes.set_prev(cn, pt);
882 }
883 self.nodes.link(ch, p);
884 self.nodes.set_next(prev_p, NodeRef::NULL);
885 }
886 let save_vbadness = self.eqtb.int_par[ip::VBADNESS as usize].val;
887 let save_vfuzz = self.eqtb.dimen_par[dp::VFUZZ as usize].val;
888 self.eqtb.int_par[ip::VBADNESS as usize].val = INF_BAD;
889 self.eqtb.dimen_par[dp::VFUZZ as usize].val = retex_arith::MAX_DIMEN;
890 let first = self.nodes.next(self.page.page_head);
891 let b = self.vpackage(
892 first,
893 PackSpec::Exactly(self.page.best_size),
894 self.page.max_depth,
895 );
896 self.eqtb.boxes[obox as usize].val = b;
897 self.eqtb.int_par[ip::VBADNESS as usize].val = save_vbadness;
898 self.eqtb.dimen_par[dp::VFUZZ as usize].val = save_vfuzz;
899 self.start_new_page();
900 if q != hold_head {
901 let h = self.nodes.next(hold_head);
902 self.nodes.link(self.page.page_head, h);
903 self.page.page_tail = q;
904 }
905 self.nodes.free(hold_head);
906 self.page.ins.clear();
907 for m in self.marks.classes.values_mut() {
908 if m[mark_code::TOP].is_some() && m[mark_code::FIRST].is_none() {
909 m[mark_code::FIRST] = m[mark_code::TOP].clone();
910 }
911 }
912 let output = self
913 .eqtb
914 .toks_param(crate::primitives_gen::assign_toks::OUTPUT)
915 .clone();
916 if !output.is_empty() {
917 if self.dead_cycles >= self.eqtb.int(ip::MAXDEADCYCLES) {
918 self.print_err("Output loop---");
919 self.printer.print_int(i64::from(self.dead_cycles));
920 self.printer.print_str(" consecutive dead cycles");
921 self.help(&[
922 "I've concluded that your \\output is awry; it never does a",
923 "\\shipout, so I'm shipping \\box255 out myself. Next time",
924 "increase \\maxdeadcycles if you want me to be more patient!",
925 ]);
926 self.error();
927 } else {
928 self.ts.output_active = true;
930 self.dead_cycles += 1;
931 self.push_nest(Mode::InternalVertical);
932 self.cur_list_mut().prev_depth = IGNORE_DEPTH;
933 let line = self.input.line();
934 self.cur_list_mut().mode_line = -line;
935 self.begin_token_list(output, TokKind::OutputText);
936 self.new_save_level(GroupCode::Output);
937 self.normal_paragraph();
938 self.scan_left_brace();
939 return;
940 }
941 }
942 let ph = self.page.page_head;
944 let first = self.nodes.next(ph);
945 if !first.is_null() {
946 if self.nodes.next(ch).is_null() {
947 let pt = self.page.page_tail;
948 self.set_contrib_tail(pt);
949 } else {
950 let pt = self.page.page_tail;
951 let cn = self.nodes.next(ch);
952 self.nodes.link(pt, cn);
953 }
954 self.nodes.link(ch, first);
955 self.nodes.set_next(ph, NodeRef::NULL);
956 self.page.page_tail = ph;
957 }
958 let pd = std::mem::take(&mut self.ts.disc_ptr[2]);
959 self.nodes.flush_list(pd);
960 let b = self.eqtb.boxes[obox as usize].val;
961 self.eqtb.boxes[obox as usize].val = NodeRef::NULL;
962 if !b.is_null() {
963 self.ship_out(b);
964 }
965 }
966
967 pub fn resume_output(&mut self) {
969 let ok = matches!(
970 self.input.stack.last(),
971 Some(Level::Tokens(t)) if t.loc >= t.list.len()
972 && (t.kind == TokKind::OutputText || t.kind == TokKind::BackedUp)
973 );
974 if !ok {
975 self.print_err("Unbalanced output routine");
976 self.help(&[
977 "Your sneaky output routine has problematic {'s and/or }'s.",
978 "I can't handle that very well; good luck.",
979 ]);
980 self.error();
981 let mut guard = 0;
982 loop {
983 self.get_token();
984 guard += 1;
985 let done = matches!(
986 self.input.stack.last(),
987 Some(Level::Tokens(t)) if t.loc >= t.list.len()
988 );
989 if done || guard > 1_000_000 {
990 break;
991 }
992 }
993 }
994 self.end_token_list();
995 self.end_graf();
996 self.unsave();
997 self.ts.output_active = false;
998 self.insert_penalties = 0;
999 let obox = self.output_box_num() as u32;
1001 if !self.eqtb.boxes[obox as usize].val.is_null() {
1002 self.print_err("Output routine didn't use all of ");
1003 self.print_esc_str("box");
1004 self.printer.print_int(i64::from(obox));
1005 self.help(&[
1006 "Your \\output commands should empty \\box255,",
1007 "e.g., by saying `\\shipout\\box255'.",
1008 "Proceed; I'll discard its present contents.",
1009 ]);
1010 self.box_error(obox);
1011 }
1012 let (head, tail) = (self.cur_list().head, self.cur_list().tail);
1013 if tail != head {
1014 let pt = self.page.page_tail;
1015 let first = self.nodes.next(head);
1016 self.nodes.link(pt, first);
1017 self.page.page_tail = tail;
1018 self.nodes.set_next(head, NodeRef::NULL);
1019 }
1020 let ph = self.page.page_head;
1021 let ch = self.page.contrib_head;
1022 let first = self.nodes.next(ph);
1023 if !first.is_null() {
1024 if self.nodes.next(ch).is_null() {
1025 let pt = self.page.page_tail;
1026 self.set_contrib_tail(pt);
1027 }
1028 let pt = self.page.page_tail;
1029 let cn = self.nodes.next(ch);
1030 self.nodes.set_next(pt, cn);
1031 if !cn.is_null() {
1032 self.nodes.set_prev(cn, pt);
1033 }
1034 self.nodes.link(ch, first);
1035 self.nodes.set_next(ph, NodeRef::NULL);
1036 self.page.page_tail = ph;
1037 }
1038 let pd = std::mem::take(&mut self.ts.disc_ptr[2]);
1039 self.nodes.flush_list(pd);
1040 self.pop_nest();
1041 self.build_page();
1042 }
1043}
1044
1045fn x_over_n(x: i32, n: i32) -> i32 {
1047 retex_arith::x_over_n(x, n).map_or(0, |d| d.quotient)
1048}