]> git.donarmstrong.com Git - lilypond.git/blob - lily/page-breaking.cc
Run grand-replace for 2010.
[lilypond.git] / lily / page-breaking.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2006--2010 Joe Neeman <joeneeman@gmail.com>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21   This is a utility class for page-breaking algorithms. There are some complex
22   parts of this class, some of which are useful to understand if you intend
23   to write a page breaking algorithm (ie. a subclass of Page_breaking). Most
24   of these complexities were introduced in order to break the problem of
25   page-breaking into simpler subproblems and to hide some of the bookkeeping
26   complexities of page breaking from the page breaking algorithms.
27
28   COMPRESSED LINES
29   There are several functions that actually distribute systems across pages
30   (for example, the space_systems_XXX and pack_systems_XXX functions). If
31   each of these functions had to handle \noPageBreak, it would be a mess.
32   Therefore, we handle \noPageBreak by "compressing" the list of systems
33   before doing any layout: we concatenate any two systems separated by a
34   \noPageBreak into a single system. The page-breaking functions can do their
35   magic without encountering a \noPageBreak; we then "uncompress" the systems
36   at the end. We almost always work with cached_line_details_, which are
37   "compressed."
38
39   CHUNKS
40   The basic operation of a page breaking algorithm is to repeatedly request
41   some systems from the line-breaker and place those systems on some pages.
42   With each repetition, the page breaking algorithm asks the line-breaker for
43   some systems that it thinks will help it achieve a better layout. The
44   Page_breaking class provides functionality to facilitate this in the case
45   that the page breaking algorithm only cares about the number of systems.
46
47   Even if a page breaking algorithm only cares number of systems, there may
48   be many ways to satisfy its request. For example, in a piece with 2 scores
49   and a request for 10 systems, we could return 5 systems from each score or
50   4 from the first and 6 from the second. Even within a score, we might
51   want to try several different line breaking configurations with a fixed
52   system count; if there is a forced \pageBreak, for example, we might wish
53   to tweak the number of systems on both sides of the \pageBreak independently.
54
55   The Page_breaking class takes care of finding these configurations. It
56   divides the piece into "chunks" and sets up the line-breaker in such a way
57   that the number of systems in each chunk can be modified independently.
58   Chunks never cross score boundaries; each title and markup is its own chunk.
59   When a page breaking algorithm requests a number of systems, the Page_breaker
60   stores an array of potential configurations, which the page breaking
61   algorithm can iterate over using current_configuration(vsize).
62
63   LINE_DIVISION
64   A Line_division is simply a way of storing the exact way in which the
65   total number of systems is distributed among chunks. Note that a
66   Line_division may not (in fact, usually will not) describe all of the chunks
67   in the entire book. Rather, it will describe the subset of chunks that lie
68   between some fixed starting and ending point. This subset of chunks changes
69   whenever a page breaking algorithm asks to consider a different pair of
70   starting and ending breakpoints. In particular, a Line_division should be
71   discarded after a call to set_current_breakpoints, since that Line_division
72   refers to a subset of chunks which might be different from the current
73   subset of chunks under consideration.
74 */
75
76 #include "page-breaking.hh"
77
78 #include "international.hh"
79 #include "item.hh"
80 #include "output-def.hh"
81 #include "page-layout-problem.hh"
82 #include "page-spacing.hh"
83 #include "paper-book.hh"
84 #include "paper-score.hh"
85 #include "paper-system.hh"
86 #include "system.hh"
87 #include "warn.hh"
88
89 /* for each forbidden page break, merge the systems around it into one
90    system. */
91 static vector<Line_details>
92 compress_lines (const vector<Line_details> &orig)
93 {
94   vector<Line_details> ret;
95
96   for (vsize i = 0; i < orig.size (); i++)
97     {
98       if (ret.size () && !scm_is_symbol (ret.back ().page_permission_))
99         {
100           Line_details const &old = ret.back ();
101           Line_details compressed = orig[i];
102           Real padding = orig[i].title_ ? old.title_padding_ : old.padding_;
103
104           compressed.extent_[DOWN] = old.extent_[DOWN];
105           compressed.extent_[UP] = old.extent_[UP] + orig[i].extent_.length () + padding;
106           compressed.space_ += old.space_;
107           compressed.inverse_hooke_ += old.inverse_hooke_;
108
109           compressed.compressed_lines_count_ = old.compressed_lines_count_ + 1;
110           compressed.compressed_nontitle_lines_count_ =
111             old.compressed_nontitle_lines_count_ + (compressed.title_ ? 0 : 1);
112
113           // compressed.title_ is true if and only if the first of its
114           // compressed lines was a title.
115           compressed.title_ = old.title_;
116           ret.back () = compressed;
117         }
118       else
119         {
120           ret.push_back (orig[i]);
121           ret.back ().force_ = 0;
122         }
123     }
124   return ret;
125 }
126
127 /* translate the number of systems-per-page into something meaningful for
128    the uncompressed lines.
129 */
130 static vector<vsize>
131 uncompress_solution (vector<vsize> const &systems_per_page,
132                      vector<Line_details> const &compressed)
133 {
134   vector<vsize> ret;
135   vsize start_sys = 0;
136
137   for (vsize i = 0; i < systems_per_page.size (); i++)
138     {
139       int compressed_count = 0;
140       for (vsize j = start_sys; j < start_sys + systems_per_page[i]; j++)
141         compressed_count += compressed[j].compressed_lines_count_ - 1;
142
143       ret.push_back (systems_per_page[i] + compressed_count);
144       start_sys += systems_per_page[i];
145     }
146   return ret;
147 }
148
149 /* for Page_breaking, the start index (when we are dealing with the stuff
150    between a pair of breakpoints) refers to the break_ index of the end of
151    the previous page. So the system index of the start of the current page
152    could either be the same as the end of the previous page or one more than
153    it. */
154
155 /* Turn a break index into the sys index that starts the next page */
156 vsize
157 Page_breaking::next_system (Break_position const &break_pos) const
158 {
159   vsize sys = break_pos.system_spec_index_;
160
161   if (sys == VPOS) /* beginning of the book */
162     return 0;
163   if (system_specs_[sys].pscore_ && !break_pos.score_ender_)
164     return sys; /* the score overflows the previous page */
165   return sys + 1; /* this page starts with a new System_spec */
166 }
167
168 Page_breaking::Page_breaking (Paper_book *pb, Break_predicate is_break)
169 {
170   book_ = pb;
171   system_count_ = 0;
172   ragged_ = to_boolean (pb->paper_->c_variable ("ragged-bottom"));
173   ragged_last_ = to_boolean (pb->paper_->c_variable ("ragged-last-bottom"));
174   systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("systems-per-page"), 0));
175   max_systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("max-systems-per-page"), 0));
176   min_systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("min-systems-per-page"), 0));
177   orphan_penalty_ = robust_scm2int (pb->paper_->c_variable ("orphan-penalty"), 100000);
178
179   if (systems_per_page_ && (max_systems_per_page_ || min_systems_per_page_))
180     {
181       warning (_f ("ignoring min-systems-per-page and max-systems-per-page because systems-per-page was set"));
182       min_systems_per_page_ = max_systems_per_page_ = 0;
183     }
184   if (max_systems_per_page_ && min_systems_per_page_ > max_systems_per_page_)
185     {
186       warning (_f ("min-systems-per-page is larger than max-systems-per-page, ignoring both values"));
187       min_systems_per_page_ = max_systems_per_page_ = 0;
188     }
189
190   create_system_list ();
191   find_chunks_and_breaks (is_break);
192 }
193
194 Page_breaking::~Page_breaking ()
195 {
196 }
197
198 bool
199 Page_breaking::ragged () const
200 {
201   return ragged_;
202 }
203
204 bool
205 Page_breaking::ragged_last () const
206 {
207   return ragged_last_;
208 }
209
210 int
211 Page_breaking::systems_per_page () const
212 {
213   return systems_per_page_;
214 }
215
216 int
217 Page_breaking::max_systems_per_page () const
218 {
219   return max_systems_per_page_;
220 }
221
222 int
223 Page_breaking::min_systems_per_page () const
224 {
225   return min_systems_per_page_;
226 }
227
228 vsize
229 Page_breaking::system_count () const
230 {
231   return system_count_;
232 }
233
234 bool
235 Page_breaking::too_many_lines (int line_count) const
236 {
237   return max_systems_per_page () > 0 && line_count > max_systems_per_page ();
238 }
239
240 bool
241 Page_breaking::too_few_lines (int line_count) const
242 {
243   return line_count < min_systems_per_page ();
244 }
245
246 Real
247 Page_breaking::line_count_penalty (int line_count) const
248 {
249   if (too_many_lines (line_count))
250     return (line_count - max_systems_per_page ()) * TERRIBLE_SPACING_PENALTY;
251   if (too_few_lines (line_count))
252     return (min_systems_per_page () - line_count) * TERRIBLE_SPACING_PENALTY;
253
254   return 0;
255 }
256
257 int
258 Page_breaking::line_count_status (int line_count) const
259 {
260   if (too_many_lines (line_count))
261     return SYSTEM_COUNT_TOO_MANY;
262   if (too_few_lines (line_count))
263     return SYSTEM_COUNT_TOO_FEW;
264
265   return SYSTEM_COUNT_OK;
266 }
267
268 /* translate indices into breaks_ into start-end parameters for the line breaker */
269 void
270 Page_breaking::line_breaker_args (vsize sys,
271                                   Break_position const &start,
272                                   Break_position const &end,
273                                   vsize *line_breaker_start,
274                                   vsize *line_breaker_end)
275 {
276   assert (system_specs_[sys].pscore_);
277   assert (next_system (start) <= sys && sys <= end.system_spec_index_);
278
279   if (start.system_spec_index_ == sys)
280     *line_breaker_start = start.score_break_;
281   else
282     *line_breaker_start = 0;
283
284   if (end.system_spec_index_ == sys)
285     *line_breaker_end = end.score_break_;
286   else
287     *line_breaker_end = VPOS;
288 }
289
290 void
291 Page_breaking::break_into_pieces (vsize start_break, vsize end_break,
292                                   Line_division const &div)
293 {
294   vector<Break_position> chunks = chunk_list (start_break, end_break);
295   bool ignore_div = false;
296   if (chunks.size () != div.size () + 1)
297     {
298       programming_error ("did not find a valid page breaking configuration");
299       ignore_div = true;
300     }
301
302   for (vsize i = 0; i + 1 < chunks.size (); i++)
303     {
304       vsize sys = next_system (chunks[i]);
305       if (system_specs_[sys].pscore_)
306         {
307           vsize start;
308           vsize end;
309           line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
310
311           vector<Column_x_positions> pos = ignore_div
312             ? line_breaking_[sys].best_solution (start, end)
313             : line_breaking_[sys].solve (start, end, div[i]);
314           system_specs_[sys].pscore_->root_system ()->break_into_pieces (pos);
315         }
316     }
317 }
318
319 SCM
320 Page_breaking::systems ()
321 {
322   SCM ret = SCM_EOL;
323   for (vsize sys = 0; sys < system_specs_.size (); sys++)
324     {
325       if (system_specs_[sys].pscore_)
326         {
327           system_specs_[sys].pscore_->root_system ()
328             ->do_break_substitution_and_fixup_refpoints ();
329           SCM lines = system_specs_[sys].pscore_->root_system ()
330             ->get_broken_system_grobs ();
331           ret = scm_cons (lines, ret);
332         }
333       else
334         {
335           Prob *pb = system_specs_[sys].prob_;
336           ret = scm_cons (scm_list_1 (pb->self_scm ()), ret);
337           pb->unprotect ();
338         }
339     }
340   return scm_append (scm_reverse (ret));
341 }
342
343 SCM
344 Page_breaking::make_page (int page_num, bool last) const
345 {
346   bool last_part = ly_scm2bool (book_->paper_->c_variable ("is-last-bookpart"));
347   SCM mod = scm_c_resolve_module ("scm page");
348   SCM make_page_scm = scm_c_module_lookup (mod, "make-page");
349
350   make_page_scm = scm_variable_ref (make_page_scm);
351
352   return scm_apply_0 (make_page_scm,
353                       scm_list_n (book_->self_scm (),
354                                   ly_symbol2scm ("page-number"), scm_from_int (page_num),
355                                   ly_symbol2scm ("is-last-bookpart"), scm_from_bool (last_part),
356                                   ly_symbol2scm ("is-bookpart-last-page"), scm_from_bool (last),
357                                   SCM_UNDEFINED));
358 }
359
360 Real
361 Page_breaking::page_height (int page_num, bool last) const
362 {
363   SCM mod = scm_c_resolve_module ("scm page");
364   SCM page = make_page (page_num, last);
365   SCM calc_height = scm_c_module_lookup (mod, "calc-printable-height");
366   calc_height = scm_variable_ref (calc_height);
367
368   SCM height = scm_apply_1 (calc_height, page, SCM_EOL);
369   return scm_to_double (height);
370 }
371
372 SCM
373 Page_breaking::breakpoint_property (vsize breakpoint, char const *str)
374 {
375   Break_position const &pos = breaks_[breakpoint];
376
377   if (pos.system_spec_index_ == VPOS)
378     return SCM_EOL;
379   if (system_specs_[pos.system_spec_index_].pscore_)
380     return pos.col_->get_property (str);
381   return system_specs_[pos.system_spec_index_].prob_->get_property (str);
382 }
383
384 SCM
385 Page_breaking::get_page_configuration (SCM systems, int page_num, bool ragged, bool last)
386 {
387   SCM dummy_page = make_page (page_num, last);
388   Page_layout_problem layout (book_, dummy_page, systems);
389   return scm_is_pair (systems) ? layout.solution (ragged) : SCM_EOL;
390 }
391
392 SCM
393 Page_breaking::draw_page (SCM systems, SCM configuration, int page_num, bool last)
394 {
395   // Create a stencil for each system.
396   SCM paper_systems = SCM_EOL;
397   for (SCM s = scm_reverse (systems); scm_is_pair (s); s = scm_cdr (s))
398     {
399       SCM paper_system = scm_car (s);
400       if (Grob *g = unsmob_grob (scm_car (s)))
401         {
402           System *sys = dynamic_cast<System*> (g);
403           paper_system = sys->get_paper_system ();
404         }
405
406       paper_systems = scm_cons (paper_system, paper_systems);
407     }
408
409   // Create the page and draw it.
410   SCM page = make_page (page_num, last);
411   SCM page_module = scm_c_resolve_module ("scm page");
412   SCM page_stencil = scm_c_module_lookup (page_module, "page-stencil");
413   page_stencil = scm_variable_ref (page_stencil);
414
415   Prob *p = unsmob_prob (page);
416   p->set_property ("lines", paper_systems);
417   p->set_property ("configuration", configuration);
418   scm_apply_1 (page_stencil, page, SCM_EOL);
419
420   return page;
421 }
422
423 SCM
424 Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
425 {
426   int first_page_number
427     = robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
428   SCM ret = SCM_EOL;
429   SCM label_page_table = book_->top_paper ()->c_variable ("label-page-table");
430   if (label_page_table == SCM_UNDEFINED)
431     label_page_table = SCM_EOL;
432
433   // Build a list of (systems . configuration) pairs. Note that we lay out
434   // the staves and find the configurations before drawing anything. Some
435   // grobs (like tuplet brackets) look at their neighbours while drawing
436   // themselves. If this happens before the neighbouring staves have
437   // been laid out, bad side-effects could happen (in particular,
438   // Align_interface::align_to_ideal_distances might be called).
439   SCM systems_and_configs = SCM_EOL;
440
441   for (vsize i = 0; i < lines_per_page.size (); i++)
442     {
443       int page_num = i + first_page_number;
444       bool bookpart_last_page = (i == lines_per_page.size () - 1);
445       bool rag = ragged () || (bookpart_last_page && ragged_last ());
446       SCM line_count = scm_from_int (lines_per_page[i]);
447       SCM lines = scm_list_head (systems, line_count);
448       SCM config = get_page_configuration (lines, page_num, rag, bookpart_last_page);
449
450       systems_and_configs = scm_cons (scm_cons (lines, config), systems_and_configs);
451       systems = scm_list_tail (systems, line_count);
452     }
453
454   // Now it's safe to make the pages.
455   int page_num = first_page_number + lines_per_page.size () - 1;
456   for (SCM s = systems_and_configs; scm_is_pair (s); s = scm_cdr (s))
457     {
458       SCM lines = scm_caar (s);
459       SCM config = scm_cdar (s);
460       bool bookpart_last_page = (s == systems_and_configs);
461       SCM page = draw_page (lines, config, page_num, bookpart_last_page);
462
463       /* collect labels */
464       SCM page_num_scm = scm_from_int (page_num);
465       for (SCM l = lines ; scm_is_pair (l)  ; l = scm_cdr (l))
466         {
467           SCM labels = SCM_EOL;
468           if (Grob * line = unsmob_grob (scm_car (l)))
469             {
470               System *system = dynamic_cast<System*> (line);
471               labels = system->get_property ("labels");
472             }
473           else if (Prob *prob = unsmob_prob (scm_car (l)))
474             labels = prob->get_property ("labels");
475
476           for (SCM lbls = labels ; scm_is_pair (lbls) ; lbls = scm_cdr (lbls))
477             label_page_table = scm_cons (scm_cons (scm_car (lbls), page_num_scm),
478                                          label_page_table);
479         }
480
481       ret = scm_cons (page, ret);
482       --page_num;
483     }
484   book_->top_paper ()->set_variable (ly_symbol2scm ("label-page-table"), label_page_table);
485   return ret;
486 }
487
488 /* The page-turn-page-breaker needs to have a line-breaker between any two
489    columns with non-NULL page-turn-permission.
490
491    The optimal-breaker needs to have a line-breaker between any two columns
492    with page-break-permission = 'force.
493
494    By using a grob predicate, we can accommodate both of these uses.
495 */
496 void
497 Page_breaking::create_system_list ()
498 {
499   SCM specs = book_->get_system_specs ();
500   for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
501     {
502       if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (scm_car (s))))
503         {
504           system_specs_.push_back (System_spec (ps));
505         }
506       else
507         {
508           Prob *pb = unsmob_prob (scm_car (s));
509           assert (pb);
510
511           pb->protect ();
512           system_specs_.push_back (System_spec (pb));
513         }
514     }
515 }
516
517 void
518 Page_breaking::find_chunks_and_breaks (Break_predicate is_break)
519 {
520   SCM force_sym = ly_symbol2scm ("force");
521
522   chunks_.push_back (Break_position ());
523   breaks_.push_back (Break_position ());
524
525   for (vsize i = 0; i < system_specs_.size (); i++)
526     {
527       if (system_specs_[i].pscore_)
528         {
529           vector<Grob*> cols;
530
531           SCM system_count = system_specs_[i].pscore_->layout ()->c_variable ("system-count");
532           if (scm_is_number (system_count))
533             {
534               // With system-count given, the line configuration for
535               // this score is fixed.  We need to ensure that chunk
536               // boundaries only occur at line breaks.
537               Constrained_breaking breaking (system_specs_[i].pscore_);
538               vector<Line_details> details = breaking.line_details (0, VPOS, scm_to_int (system_count));
539
540               cols.push_back (system_specs_[i].pscore_->root_system ()->used_columns ()[0]);
541               for (vsize j = 0; j < details.size (); j++)
542                 cols.push_back (details[j].last_column_);
543             }
544           else
545             cols = system_specs_[i].pscore_->root_system ()->used_columns ();
546
547           int last_chunk_idx = 0;
548           vector<vsize> line_breaker_columns;
549           line_breaker_columns.push_back (0);
550
551           for (vsize j = 1; j < cols.size (); j++)
552             {
553               bool last = (j == cols.size () - 1);
554               bool break_point = is_break (cols[j]);
555               bool chunk_end = cols[j]->get_property ("page-break-permission") == force_sym;
556               Break_position cur_pos = Break_position (i,
557                                                        line_breaker_columns.size (),
558                                                        cols[j],
559                                                        last);
560
561               // NOTE: even in the breaks_ list, forced_line_count_
562               // refers to the number of lines between a
563               // Break_position and the start of that /chunk/.  This
564               // is needed for system_count_bounds to work correctly,
565               // since it mixes Break_positions from breaks_ and
566               // chunks_.
567               if (scm_is_number (system_count))
568                 cur_pos.forced_line_count_ = j - last_chunk_idx;
569
570               if (break_point || (i == system_specs_.size () - 1 && last))
571                 breaks_.push_back (cur_pos);
572               if (chunk_end || last)
573                 {
574                   chunks_.push_back (cur_pos);
575                   last_chunk_idx = j;
576                 }
577
578               if ((break_point || chunk_end) && !last)
579                 line_breaker_columns.push_back (j);
580             }
581           line_breaking_.push_back (Constrained_breaking (system_specs_[i].pscore_, line_breaker_columns));
582         }
583       else
584         {
585           /* TODO: we want some way of applying Break_p to a prob? */
586           if (i == system_specs_.size () - 1)
587             breaks_.push_back (Break_position (i));
588
589           chunks_.push_back (Break_position (i));
590
591           /* FIXME: shouldn't we push a Null_breaker or similar dummy
592              class? --hwn */
593           line_breaking_.push_back (Constrained_breaking (NULL));
594         }
595     }
596 }
597
598 vector<Break_position>
599 Page_breaking::chunk_list (vsize start_index, vsize end_index)
600 {
601   Break_position start = breaks_[start_index];
602   Break_position end = breaks_[end_index];
603
604   vsize i = 0;
605   for (; i < chunks_.size () && chunks_[i] <= start; i++)
606     ;
607
608   vector<Break_position> ret;
609   ret.push_back (start);
610   for (; i < chunks_.size () && chunks_[i] < end; i++)
611     ret.push_back (chunks_[i]);
612   ret.push_back (end);
613   return ret;
614 }
615
616 vsize
617 Page_breaking::min_system_count (vsize start, vsize end)
618 {
619   vector<Break_position> chunks = chunk_list (start, end);
620   Line_division div = system_count_bounds (chunks, true);
621   vsize ret = 0;
622
623   for (vsize i = 0; i < div.size (); i++)
624     ret += div[i];
625   return ret;
626 }
627
628 vsize
629 Page_breaking::max_system_count (vsize start, vsize end)
630 {
631   vector<Break_position> chunks = chunk_list (start, end);
632   Line_division div = system_count_bounds (chunks, false);
633   vsize ret = 0;
634
635   for (vsize i = 0; i < div.size (); i++)
636     ret += div[i];
637   return ret;
638 }
639
640 Page_breaking::Line_division
641 Page_breaking::system_count_bounds (vector<Break_position> const &chunks,
642                                     bool min)
643 {
644   assert (chunks.size () >= 2);
645
646   Line_division ret;
647   ret.resize (chunks.size () - 1, 1);
648
649   for (vsize i = 0; i + 1 < chunks.size (); i++)
650     {
651       vsize sys = next_system (chunks[i]);
652
653       if (chunks[i+1].forced_line_count_)
654         ret[i] = chunks[i+1].forced_line_count_;
655       else if (system_specs_[sys].pscore_)
656         {
657           vsize start;
658           vsize end;
659           line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
660           ret[i] = min
661             ? line_breaking_[sys].min_system_count (start, end)
662             : line_breaking_[sys].max_system_count (start, end);
663         }
664     }
665
666   return ret;
667 }
668
669 void
670 Page_breaking::set_current_breakpoints (vsize start,
671                                         vsize end,
672                                         vsize system_count,
673                                         Line_division lower_bound,
674                                         Line_division upper_bound)
675 {
676   system_count_ = system_count;
677   current_chunks_ = chunk_list (start, end);
678   current_start_breakpoint_ = start;
679   current_end_breakpoint_ = end;
680   clear_line_details_cache ();
681
682   if (!lower_bound.size ())
683     lower_bound = system_count_bounds (current_chunks_, true);
684   if (!upper_bound.size ())
685     upper_bound = system_count_bounds (current_chunks_, false);
686
687   assert (lower_bound.size () == current_chunks_.size () - 1);
688   assert (upper_bound.size () == current_chunks_.size () - 1);
689
690   Line_division work_in_progress;
691   current_configurations_.clear ();
692   line_divisions_rec (system_count,
693                       lower_bound,
694                       upper_bound,
695                       &work_in_progress);
696
697   /* we only consider a constant number of configurations. Otherwise,
698      this becomes slow when there are many small scores. The constant
699      5 is somewhat arbitrary. */
700   if (current_configurations_.size () > 5)
701     {
702       vector<pair<Real,vsize> > dems_and_indices;
703
704       for (vsize i = 0; i < current_configurations_.size (); i++)
705         {
706           cache_line_details (i);
707           Real dem = 0;
708           for (vsize j = 0; j < cached_line_details_.size (); j++)
709             dem += cached_line_details_[j].force_ * cached_line_details_[j].force_
710               + cached_line_details_[j].break_penalty_;
711
712           dems_and_indices.push_back (pair<Real,vsize> (dem, i));
713         }
714       vector_sort (dems_and_indices, less<pair<Real,vsize> > ());
715
716       vector<Line_division> best_5_configurations;
717       for (vsize i = 0; i < 5; i++)
718         best_5_configurations.push_back (current_configurations_[dems_and_indices[i].second]);
719
720       clear_line_details_cache ();
721       current_configurations_ = best_5_configurations;
722     }
723 }
724
725 void
726 Page_breaking::set_to_ideal_line_configuration (vsize start, vsize end)
727 {
728   current_chunks_ = chunk_list (start, end);
729   current_start_breakpoint_ = start;
730   current_end_breakpoint_ = end;
731   clear_line_details_cache ();
732   system_count_ = 0;
733
734   Line_division div;
735   for (vsize i = 0; i+1 < current_chunks_.size (); i++)
736     {
737       vsize sys = next_system (current_chunks_[i]);
738
739       if (current_chunks_[i+1].forced_line_count_)
740         div.push_back (current_chunks_[i+1].forced_line_count_);
741       else if (system_specs_[sys].pscore_)
742         {
743           line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
744           div.push_back (line_breaking_[sys].best_solution (start, end).size ());
745         }
746       else
747         div.push_back (1);
748
749       system_count_ += div.back ();
750     }
751   current_configurations_.clear ();
752   current_configurations_.push_back (div);
753 }
754
755 vsize
756 Page_breaking::current_configuration_count () const
757 {
758   return current_configurations_.size ();
759 }
760
761 void
762 Page_breaking::cache_line_details (vsize configuration_index)
763 {
764   if (cached_configuration_index_ != configuration_index)
765     {
766       cached_configuration_index_ = configuration_index;
767
768       Line_division &div = current_configurations_[configuration_index];
769       uncompressed_line_details_.clear ();
770       for (vsize i = 0; i + 1 < current_chunks_.size (); i++)
771         {
772           vsize sys = next_system (current_chunks_[i]);
773           if (system_specs_[sys].pscore_)
774             {
775               vsize start;
776               vsize end;
777               line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
778
779               vector<Line_details> details = line_breaking_[sys].line_details (start, end, div[i]);
780               uncompressed_line_details_.insert (uncompressed_line_details_.end (), details.begin (), details.end ());
781             }
782           else
783             {
784               assert (div[i] == 1);
785               uncompressed_line_details_.push_back (Line_details (system_specs_[sys].prob_, book_->paper_));
786             }
787         }
788       cached_line_details_ = compress_lines (uncompressed_line_details_);
789     }
790 }
791
792 void
793 Page_breaking::clear_line_details_cache ()
794 {
795   cached_configuration_index_ = VPOS;
796   cached_line_details_.clear ();
797   uncompressed_line_details_.clear ();
798 }
799
800 void
801 Page_breaking::line_divisions_rec (vsize system_count,
802                                    Line_division const &min_sys,
803                                    Line_division const &max_sys,
804                                    Line_division *cur_division)
805 {
806   vsize my_index = cur_division->size ();
807   vsize others_min = 0;
808   vsize others_max = 0;
809
810   for (vsize i = my_index + 1; i < min_sys.size (); i++)
811     {
812       others_min += min_sys[i];
813       others_max += max_sys[i];
814     }
815   others_max = min (others_max, system_count);
816   vsize real_min = max (min_sys[my_index], system_count - others_max);
817   vsize real_max = min (max_sys[my_index], system_count - others_min);
818
819   if (real_min > real_max || real_min <= 0)
820     {
821       /* this should never happen within a recursive call. If it happens
822          at all, it means that we were called with an unsolvable problem
823          and we should return an empty result */
824       assert (my_index == 0);
825       return;
826     }
827
828   for (vsize i = real_min; i <= real_max; i++)
829     {
830       cur_division->push_back (i);
831       if (my_index == min_sys.size () - 1)
832         current_configurations_.push_back (*cur_division);
833       else
834         line_divisions_rec (system_count - i, min_sys, max_sys, cur_division);
835       cur_division->pop_back ();
836     }
837 }
838
839 vsize
840 Page_breaking::min_page_count (vsize configuration, vsize first_page_num)
841 {
842   vsize ret = 1;
843   vsize page_starter = 0;
844   Real cur_rod_height = 0;
845   Real cur_spring_height = 0;
846   Real cur_page_height = page_height (first_page_num, false);
847   int line_count = 0;
848
849   cache_line_details (configuration);
850
851   if (cached_line_details_.size ())
852     cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[0]);
853
854   for (vsize i = 0; i < cached_line_details_.size (); i++)
855     {
856       Real ext_len = cached_line_details_[i].extent_.length ();
857       Real padding = 0;
858       if (cur_rod_height > 0)
859         padding = cached_line_details_[i].title_ ?
860           cached_line_details_[i-1].title_padding_ : cached_line_details_[i-1].padding_;
861
862       Real next_rod_height = cur_rod_height + ext_len + padding;
863       Real next_spring_height = cur_spring_height + cached_line_details_[i].space_;
864       Real next_height = next_rod_height + (ragged () ? next_spring_height : 0)
865         + min_whitespace_at_bottom_of_page (cached_line_details_[i]);
866       int next_line_count = line_count + cached_line_details_[i].compressed_nontitle_lines_count_;
867
868       if ((!too_few_lines (line_count) && (next_height > cur_page_height && cur_rod_height > 0))
869           || too_many_lines (next_line_count)
870           || (i > 0
871               && cached_line_details_[i-1].page_permission_ == ly_symbol2scm ("force")))
872         {
873           line_count = cached_line_details_[i].compressed_nontitle_lines_count_;
874           cur_rod_height = ext_len;
875           cur_spring_height = cached_line_details_[i].space_;
876           page_starter = i;
877
878           cur_page_height = page_height (first_page_num + ret, false);
879           cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[i]);
880
881           ret++;
882         }
883       else
884         {
885           cur_rod_height = next_rod_height;
886           cur_spring_height = next_spring_height;
887           line_count = next_line_count;
888         }
889     }
890
891   /* there are two potential problems with the last page (because we didn't know
892      it was the last page until after we managed to fit all the systems to it):
893      - we are ragged-last but the last page has a compressed spring
894      - the value returned by page_height (num, true) is smaller than the
895        value returned by page_height (num, false) and it causes the page not to
896        fit.
897
898      In either case, we just need to add one more page. This is because the last
899      line will always fit on the extra page and by adding one more page to the
900      end, the previous page is no longer the last page, so our previous
901      calculations that treated it as a non-last page were ok.
902   */
903
904   cur_page_height = page_height (first_page_num + ret - 1, true);
905   cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[page_starter]);
906   cur_page_height -= min_whitespace_at_bottom_of_page (cached_line_details_.back ());
907
908   Real cur_height = cur_rod_height + ((ragged_last () || ragged ()) ? cur_spring_height : 0);
909   if (!too_few_lines (line_count - cached_line_details_.back ().compressed_nontitle_lines_count_)
910       && cur_height > cur_page_height
911       /* don't increase the page count if the last page had only one system */
912       && cur_rod_height > cached_line_details_.back ().extent_.length ())
913     ret++;
914
915   assert (ret <= cached_line_details_.size ());
916   return ret;
917 }
918
919 // If systems_per_page_ is positive, we don't really try to space on N pages;
920 // we just put the requested number of systems on each page and penalize
921 // if the result doesn't have N pages.
922 Page_spacing_result
923 Page_breaking::space_systems_on_n_pages (vsize configuration, vsize n, vsize first_page_num)
924 {
925   Page_spacing_result ret;
926
927   if (systems_per_page_ > 0)
928     {
929       Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
930       ret.demerits_ += (ret.force_.size () == n) ? 0 : BAD_SPACING_PENALTY;
931       return ret;
932     }
933
934   cache_line_details (configuration);
935   bool valid_n = (n >= min_page_count (configuration, first_page_num)
936                   && n <= cached_line_details_.size ());
937
938   if (!valid_n)
939     programming_error ("number of pages is out of bounds");
940
941   if (n == 1 && valid_n)
942     ret = space_systems_on_1_page (cached_line_details_,
943                                    page_height (first_page_num, is_last ()),
944                                    ragged () || (is_last () && ragged_last ()));
945   else if (n == 2 && valid_n)
946     ret = space_systems_on_2_pages (configuration, first_page_num);
947   else
948     {
949       Page_spacer ps (cached_line_details_, first_page_num, this);
950       ret = ps.solve (n);
951     }
952
953   return finalize_spacing_result (configuration, ret);
954 }
955
956 Real
957 Page_breaking::blank_page_penalty () const
958 {
959   SCM penalty_sym;
960
961   if (is_last ())
962     penalty_sym = ly_symbol2scm ("blank-last-page-force");
963   else if (ends_score ())
964     penalty_sym = ly_symbol2scm ("blank-after-score-page-force");
965   else
966     penalty_sym = ly_symbol2scm ("blank-page-force");
967
968   Break_position const &pos = breaks_[current_end_breakpoint_];
969   if (Paper_score *ps = system_specs_[pos.system_spec_index_].pscore_)
970     return robust_scm2double (ps->layout ()->lookup_variable (penalty_sym), 0.0);
971
972   return robust_scm2double (book_->paper_->lookup_variable (penalty_sym), 0.0);
973 }
974
975 // If systems_per_page_ is positive, we don't really try to space on N
976 // or N+1 pages; see the comment to space_systems_on_n_pages.
977 Page_spacing_result
978 Page_breaking::space_systems_on_n_or_one_more_pages (vsize configuration, vsize n, vsize first_page_num,
979                                                      Real penalty_for_fewer_pages)
980 {
981   Page_spacing_result n_res;
982   Page_spacing_result m_res;
983
984   if (systems_per_page_ > 0)
985     {
986       Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
987       ret.demerits_ += (ret.force_.size () == n || ret.force_.size () == (n-1)) ? 0 : BAD_SPACING_PENALTY;
988       return ret;
989     }
990
991   cache_line_details (configuration);
992   vsize min_p_count = min_page_count (configuration, first_page_num);
993   bool valid_n = n >= min_p_count || n <= cached_line_details_.size ();
994
995   if (!valid_n)
996     programming_error ("both page counts are out of bounds");
997
998   if (n == 1 && valid_n)
999     {
1000       bool rag = ragged () || (is_last () && ragged_last ());
1001       Real height = page_height (first_page_num, is_last ());
1002
1003       if (1 >= min_p_count)
1004         n_res = space_systems_on_1_page (cached_line_details_, height, rag);
1005       if (1 < cached_line_details_.size ())
1006         m_res = space_systems_on_2_pages (configuration, first_page_num);
1007     }
1008   else
1009     {
1010       Page_spacer ps (cached_line_details_, first_page_num, this);
1011       
1012       if (n >= min_p_count || !valid_n)
1013         n_res = ps.solve (n);
1014       if (n < cached_line_details_.size () || !valid_n)
1015         m_res = ps.solve (n+1);
1016     }
1017
1018   m_res = finalize_spacing_result (configuration, m_res);
1019   n_res = finalize_spacing_result (configuration, n_res);
1020
1021   Real page_spacing_weight = robust_scm2double (book_->paper_->c_variable ("page-spacing-weight"), 10);
1022   n_res.demerits_ += penalty_for_fewer_pages * page_spacing_weight;
1023
1024   if (n_res.force_.size ())
1025     n_res.force_.back () += penalty_for_fewer_pages;
1026
1027   return (m_res.demerits_ < n_res.demerits_) ? m_res : n_res;
1028 }
1029
1030 Page_spacing_result
1031 Page_breaking::space_systems_on_best_pages (vsize configuration, vsize first_page_num)
1032 {
1033   vsize min_p_count = min_page_count (configuration, first_page_num);
1034
1035   cache_line_details (configuration);
1036   Page_spacer ps (cached_line_details_, first_page_num, this);
1037   Page_spacing_result best = ps.solve (min_p_count);
1038
1039   for (vsize i = min_p_count+1; i <= cached_line_details_.size (); i++)
1040     {
1041       Page_spacing_result cur = ps.solve (i);
1042       if (cur.demerits_ < best.demerits_)
1043         best = cur;
1044     }
1045
1046   Page_spacing_result ret = finalize_spacing_result (configuration, best);
1047   return ret;
1048 }
1049
1050 Page_spacing_result
1051 Page_breaking::space_systems_with_fixed_number_per_page (vsize configuration,
1052                                                          vsize first_page_num)
1053 {
1054   Page_spacing_result res;
1055   Page_spacing space (page_height (first_page_num, false), this);
1056   vsize line = 0;
1057   vsize page = 0;
1058   vsize page_first_line = 0;
1059
1060   cache_line_details (configuration);
1061   while (line < cached_line_details_.size ())
1062     {
1063       page++;
1064       space.clear ();
1065       space.resize (page_height (first_page_num + page, false));
1066
1067       int system_count_on_this_page = 0;
1068       while (system_count_on_this_page < systems_per_page_
1069              && line < cached_line_details_.size ())
1070         {
1071           Line_details const &cur_line = cached_line_details_[line];
1072           space.append_system (cur_line);
1073           system_count_on_this_page += cur_line.compressed_nontitle_lines_count_;
1074           line++;
1075
1076           if (cur_line.page_permission_ == ly_symbol2scm ("force"))
1077             break;
1078         }
1079
1080       res.systems_per_page_.push_back (line - page_first_line);
1081
1082       res.force_.push_back (space.force_);
1083       res.penalty_ += cached_line_details_[line-1].page_penalty_;
1084       if (system_count_on_this_page != systems_per_page_)
1085         {
1086           res.penalty_ += TERRIBLE_SPACING_PENALTY;
1087           res.system_count_status_ |= ((system_count_on_this_page < systems_per_page_))
1088             ? SYSTEM_COUNT_TOO_FEW : SYSTEM_COUNT_TOO_MANY;
1089         }
1090
1091       page_first_line = line;
1092     }
1093
1094   /* Recalculate forces for the last page because we know now that is
1095      really the last page. */
1096   space.resize (page_height (first_page_num + page, true));
1097   res.force_.back () = space.force_;
1098   return finalize_spacing_result (configuration, res);
1099 }
1100
1101 Page_spacing_result
1102 Page_breaking::pack_systems_on_least_pages (vsize configuration, vsize first_page_num)
1103 {
1104   // TODO: add support for min/max-systems-per-page.
1105   Page_spacing_result res;
1106   vsize page = 0;
1107   vsize page_first_line = 0;
1108   Page_spacing space (page_height (first_page_num, false), this);
1109
1110   cache_line_details (configuration);
1111   for (vsize line = 0; line < cached_line_details_.size (); line++)
1112     {
1113       Real prev_force = space.force_;
1114       space.append_system (cached_line_details_[line]);
1115       if ((line > page_first_line)
1116           && (isinf (space.force_)
1117               || ((line > 0)
1118                   && (cached_line_details_[line-1].page_permission_ == ly_symbol2scm ("force")))))
1119         {
1120           res.systems_per_page_.push_back (line - page_first_line);
1121           res.force_.push_back (prev_force);
1122           res.penalty_ += cached_line_details_[line-1].page_penalty_;
1123           page++;
1124           space.resize (page_height (first_page_num + page, false));
1125           space.clear ();
1126           space.append_system (cached_line_details_[line]);
1127           page_first_line = line;
1128         }
1129
1130       if (line == cached_line_details_.size () - 1)
1131         {
1132           /* This is the last line */
1133           /* When the last page height was computed, we did not know yet that it
1134            * was the last one. If the systems put on it don't fit anymore, the last
1135            * system is moved to a new page */
1136           space.resize (page_height (first_page_num + page, true));
1137           if ((line > page_first_line) && (isinf (space.force_)))
1138             {
1139               res.systems_per_page_.push_back (line - page_first_line);
1140               res.force_.push_back (prev_force);
1141               /* the last page containing the last line */
1142               space.resize (page_height (first_page_num + page + 1, true));
1143               space.clear ();
1144               space.append_system (cached_line_details_[line]);
1145               res.systems_per_page_.push_back (1);
1146               res.force_.push_back (space.force_);
1147               res.penalty_ += cached_line_details_[line-1].page_penalty_;
1148               res.penalty_ += cached_line_details_[line].page_penalty_;
1149             }
1150           else
1151             {
1152               res.systems_per_page_.push_back (line + 1 - page_first_line);
1153               res.force_.push_back (space.force_);
1154               res.penalty_ += cached_line_details_[line].page_penalty_;
1155             }
1156         }
1157     }
1158   return finalize_spacing_result (configuration, res);
1159 }
1160
1161 /* Calculate demerits and fix res.systems_per_page_ so that
1162    it refers to the original line numbers, not the ones given by compress_lines (). */
1163 Page_spacing_result
1164 Page_breaking::finalize_spacing_result (vsize configuration, Page_spacing_result res)
1165 {
1166   if (res.force_.empty ())
1167     return res;
1168
1169   cache_line_details (configuration);
1170   res.systems_per_page_ = uncompress_solution (res.systems_per_page_, cached_line_details_);
1171
1172   Real line_force = 0;
1173   Real line_penalty = 0;
1174   Real page_demerits = res.penalty_;
1175   Real page_weighting = robust_scm2double (book_->paper_->c_variable ("page-spacing-weight"), 10);
1176
1177   for (vsize i = 0; i < uncompressed_line_details_.size (); i++)
1178     {
1179       line_force += uncompressed_line_details_[i].force_ * uncompressed_line_details_[i].force_;
1180       line_penalty += uncompressed_line_details_[i].break_penalty_;
1181     }
1182
1183   for (vsize i = 0; i < res.force_.size (); i++)
1184     {
1185       Real f = res.force_[i];
1186
1187       page_demerits += min(f*f, BAD_SPACING_PENALTY);
1188     }
1189
1190   /* for a while we tried averaging page and line forces across pages instead
1191      of summing them, but it caused a problem: if there is a single page
1192      with a very bad page force (for example because of a forced page break),
1193      the page breaker will put in a _lot_ of pages so that the bad force
1194      becomes averaged out over many pages. */
1195   res.demerits_ = line_force + line_penalty + page_demerits * page_weighting;
1196   return res;
1197
1198 }
1199
1200 /* the cases for page_count = 1 or 2 can be done in O (n) time. Since they
1201    are by far the most common cases, we have special functions for them.
1202
1203    space_systems_on_1_page has a different calling convention than most of the
1204    space_systems functions. This is because space_systems_on_1_page is (unlike
1205    the other space_systems functions) sometimes called on subsets of a full
1206    configuration. */
1207 Page_spacing_result
1208 Page_breaking::space_systems_on_1_page (vector<Line_details> const &lines, Real page_height, bool ragged)
1209 {
1210   Page_spacing space (page_height, this);
1211   Page_spacing_result ret;
1212   int line_count = 0;
1213
1214   for (vsize i = 0; i < lines.size (); i++)
1215     {
1216       space.append_system (lines[i]);
1217       line_count += lines[i].compressed_nontitle_lines_count_;
1218     }
1219
1220   ret.systems_per_page_.push_back (lines.size ());
1221   ret.force_.push_back (ragged ? min (space.force_, 0.0) : space.force_);
1222   ret.penalty_ = line_count_penalty (line_count) + lines.back ().page_penalty_ + lines.back ().turn_penalty_;
1223   ret.system_count_status_ |= line_count_status (line_count);
1224
1225   /* don't do finalize_spacing_result () because we are only an internal function */
1226   return ret;
1227 }
1228
1229 Page_spacing_result
1230 Page_breaking::space_systems_on_2_pages (vsize configuration, vsize first_page_num)
1231 {
1232   Real page1_height = page_height (first_page_num, false);
1233   Real page2_height = page_height (first_page_num + 1, is_last ());
1234   bool ragged1 = ragged ();
1235   bool ragged2 = ragged () || (is_last () && ragged_last ());
1236
1237   /* if there is a forced break, this reduces to 2 1-page problems */
1238   cache_line_details (configuration);
1239   for (vsize i = 0; i + 1 < cached_line_details_.size (); i++)
1240     if (cached_line_details_[i].page_permission_ == ly_symbol2scm ("force"))
1241       {
1242         vector<Line_details> lines1 (cached_line_details_.begin (), cached_line_details_.begin () + i + 1);
1243         vector<Line_details> lines2 (cached_line_details_.begin () + i + 1, cached_line_details_.end ());
1244         Page_spacing_result p1 = space_systems_on_1_page (lines1, page1_height, ragged1);
1245         Page_spacing_result p2 = space_systems_on_1_page (lines2, page2_height, ragged2);
1246
1247         p1.systems_per_page_.push_back (p2.systems_per_page_[0]);
1248         p1.force_.push_back (p2.force_[0]);
1249         p1.penalty_ += p2.penalty_ - cached_line_details_[i].turn_penalty_;
1250         return p1;
1251       }
1252
1253   vector<Real> page1_force;
1254   vector<Real> page2_force;
1255
1256   // page1_penalty and page2_penalty store the penalties based
1257   // on min-systems-per-page and max-systems-per-page.
1258   vector<Real> page1_penalty;
1259   vector<Real> page2_penalty;
1260
1261   // page1_status and page2_status keep track of whether the min-systems-per-page
1262   // and max-systems-per-page constraints are satisfied.
1263   vector<int> page1_status;
1264   vector<int> page2_status;
1265
1266   Page_spacing page1 (page1_height, this);
1267   Page_spacing page2 (page2_height, this);
1268   int page1_line_count = 0;
1269   int page2_line_count = 0;
1270
1271   page1_force.resize (cached_line_details_.size () - 1, infinity_f);
1272   page2_force.resize (cached_line_details_.size () - 1, infinity_f);
1273   page1_penalty.resize (cached_line_details_.size () - 1, infinity_f);
1274   page2_penalty.resize (cached_line_details_.size () - 1, infinity_f);
1275   page1_status.resize (cached_line_details_.size () - 1, 0);
1276   page2_status.resize (cached_line_details_.size () - 1, 0);
1277
1278   /* find the page 1 and page 2 forces for each page-breaking position */
1279   for (vsize i = 0; i < page1_force.size (); i++)
1280     {
1281       page1.append_system (cached_line_details_[i]);
1282       page2.prepend_system (cached_line_details_[cached_line_details_.size () - 1 - i]);
1283       page1_line_count += cached_line_details_[i].compressed_nontitle_lines_count_;
1284       page2_line_count += cached_line_details_[cached_line_details_.size () - 1 - i].compressed_nontitle_lines_count_;
1285
1286       page1_force[i] = (ragged1 && page1.force_ < 0 && i > 0) ? infinity_f : page1.force_;
1287       page1_penalty[i] = line_count_penalty (page1_line_count);
1288       page1_status[i] = line_count_status (page1_line_count);
1289
1290       if (ragged2)
1291         page2_force[page2_force.size () - 1 - i] =
1292           (page2.force_ < 0 && i + 1 < page1_force.size ()) ? infinity_f : 0;
1293       else
1294         page2_force[page2_force.size () - 1 - i] = page2.force_;
1295       page2_penalty[page2_penalty.size () - 1 - i] = line_count_penalty (page2_line_count);
1296       page2_status[page2_penalty.size () - 1 - i] = line_count_status (page2_line_count);
1297     }
1298
1299   /* find the position that minimises the sum of the page forces */
1300   vsize best_sys_count = 1;
1301   Real best_demerits = infinity_f;
1302   for (vsize i = 0; i < page1_force.size (); i++)
1303     {
1304       Real f = page1_force[i] * page1_force[i] + page2_force[i] * page2_force[i];
1305
1306       // NOTE: we treat max-systems-per-page and min-systems-per-page as soft
1307       // constraints. That is, we penalize harshly when they don't happen
1308       // but we don't completely reject.
1309       Real dem = f
1310         + page1_penalty[i] + page2_penalty[i]
1311         + cached_line_details_[i+1].page_penalty_
1312         + cached_line_details_.back ().page_penalty_ + cached_line_details_.back ().turn_penalty_;
1313       if (dem < best_demerits)
1314         {
1315           best_demerits = dem;
1316           best_sys_count = i+1;
1317         }
1318     }
1319
1320   Page_spacing_result ret;
1321   ret.systems_per_page_.push_back (best_sys_count);
1322   ret.systems_per_page_.push_back (cached_line_details_.size () - best_sys_count);
1323   ret.force_.push_back (page1_force[best_sys_count-1]);
1324   ret.force_.push_back (page2_force[best_sys_count-1]);
1325   ret.system_count_status_ = page1_status[best_sys_count-1] | page2_status[best_sys_count-1];
1326   ret.penalty_ = cached_line_details_[best_sys_count-1].page_penalty_
1327     + cached_line_details_.back ().page_penalty_
1328     + cached_line_details_.back ().turn_penalty_
1329     + page1_penalty[best_sys_count-1] + page2_penalty[best_sys_count-1];
1330
1331   /* don't do finalize_spacing_result () because we are only an internal function */
1332   return ret;
1333 }
1334
1335 bool
1336 Page_breaking::all_lines_stretched (vsize configuration)
1337 {
1338   cache_line_details (configuration);
1339   for (vsize i = 0; i < cached_line_details_.size (); i++)
1340     if (cached_line_details_[i].force_ < 0)
1341       return false;
1342
1343   return true;
1344 }
1345
1346 Page_breaking::Line_division
1347 Page_breaking::current_configuration (vsize configuration_index) const
1348 {
1349   return current_configurations_[configuration_index];
1350 }
1351
1352 bool
1353 Page_breaking::is_last () const
1354 {
1355   return current_end_breakpoint_ == last_break_position ();
1356 }
1357
1358 bool
1359 Page_breaking::ends_score () const
1360 {
1361   return breaks_[current_end_breakpoint_].score_ender_;
1362 }
1363
1364 vsize
1365 Page_breaking::last_break_position () const
1366 {
1367   return breaks_.size () - 1;  
1368 }
1369
1370 // This gives the minimum distance between the top of the
1371 // printable area (ie. the bottom of the top-margin) and
1372 // the extent box of the topmost system.
1373 Real
1374 Page_breaking::min_whitespace_at_top_of_page (Line_details const &line) const
1375 {
1376   SCM first_system_spacing = book_->paper_->c_variable ("top-system-spacing");
1377   if (line.title_)
1378     first_system_spacing = book_->paper_->c_variable ("top-title-spacing");
1379
1380   Real min_distance = -infinity_f;
1381   Real padding = 0;
1382
1383   Page_layout_problem::read_spacing_spec (first_system_spacing,
1384                                           &min_distance,
1385                                           ly_symbol2scm ("minimum-distance"));
1386   Page_layout_problem::read_spacing_spec (first_system_spacing,
1387                                           &padding,
1388                                           ly_symbol2scm ("padding"));
1389
1390   // FIXME: take into account the height of the header
1391   return max (0.0, max (padding, min_distance - line.extent_[UP]));
1392 }
1393
1394 Real
1395 Page_breaking::min_whitespace_at_bottom_of_page (Line_details const &line) const
1396 {
1397   SCM last_system_spacing = book_->paper_->c_variable ("bottom-system-spacing");
1398   Real min_distance = -infinity_f;
1399   Real padding = 0;
1400
1401   Page_layout_problem::read_spacing_spec (last_system_spacing,
1402                                           &min_distance,
1403                                           ly_symbol2scm ("minimum-distance"));
1404   Page_layout_problem::read_spacing_spec (last_system_spacing,
1405                                           &padding,
1406                                           ly_symbol2scm ("padding"));
1407
1408   // FIXME: take into account the height of the footer
1409   return max (0.0, max (padding, min_distance + line.extent_[DOWN]));
1410 }
1411
1412 int
1413 Page_breaking::orphan_penalty () const
1414 {
1415   return orphan_penalty_;
1416 }