Fix assertion for proper Huffman merge pattern: 0 == 1 modulo 1.
[pspp] / src / som.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include "som.h"
22 #include "error.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include "output.h"
26 #include "debug-print.h"
27
28 /* Table. */
29 int table_num = 1;
30 int subtable_num;
31 \f
32 /* Increments table_num so different procedures' output can be
33    distinguished. */
34 void
35 som_new_series (void)
36 {
37   if (subtable_num != 0)
38     {
39       table_num++;
40       subtable_num = 0;
41     }
42 }
43
44 /* Ejects the paper for all active devices. */
45 void
46 som_eject_page (void)
47 {
48   struct outp_driver *d;
49
50   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
51     outp_eject_page (d);
52 }
53
54 /* Skip down a single line on all active devices. */
55 void
56 som_blank_line (void)
57 {
58   struct outp_driver *d;
59   
60   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
61     if (d->page_open && d->cp_y != 0)
62       d->cp_y += d->font_height;
63 }
64 \f
65 /* Driver. */
66 static struct outp_driver *d=0;
67
68 /* Table. */
69 static struct som_table *t=0;
70
71 /* Flags. */
72 static unsigned flags;
73
74 /* Number of columns, rows. */
75 static int nc, nr;
76
77 /* Number of columns or rows in left, right, top, bottom headers. */
78 static int hl, hr, ht, hb;
79
80 /* Column style. */
81 static int cs;
82
83 /* Table height, width. */
84 static int th, tw;
85
86 static void render_columns (void);
87 static void render_simple (void);
88 static void render_segments (void);
89
90 static void output_table (struct outp_driver *, struct som_table *);
91
92 /* Output table T to appropriate output devices. */
93 void
94 som_submit (struct som_table *t)
95 {
96 #if GLOBAL_DEBUGGING
97   static int entry;
98   
99   assert (entry++ == 0);
100 #endif
101
102   t->class->table (t);
103   t->class->flags (&flags);
104   t->class->count (&nc, &nr);
105   t->class->headers (&hl, &hr, &ht, &hb);
106
107 #if GLOBAL_DEBUGGING
108   if (hl + hr > nc || ht + hb > nr)
109     {
110       printf ("headers: (l,r)=(%d,%d), (t,b)=(%d,%d) in table size (%d,%d)\n",
111               hl, hr, ht, hb, nc, nr);
112       abort ();
113     }
114   else if (hl + hr == nc)
115     printf ("warning: headers (l,r)=(%d,%d) in table width %d\n", hl, hr, nc);
116   else if (ht + hb == nr)
117     printf ("warning: headers (t,b)=(%d,%d) in table height %d\n", ht, hb, nr);
118 #endif
119
120   t->class->columns (&cs);
121
122   if (!(flags & SOMF_NO_TITLE))
123     subtable_num++;
124     
125   {
126     struct outp_driver *d;
127
128     for (d = outp_drivers (NULL); d; d = outp_drivers (d))
129       output_table (d, t);
130   }
131   
132 #if GLOBAL_DEBUGGING
133   assert (--entry == 0);
134 #endif
135 }
136
137 /* Output table TABLE to driver DRIVER. */
138 static void
139 output_table (struct outp_driver *driver, struct som_table *table)
140 {
141   d = driver;
142   t = table;
143
144   assert (d->driver_open);
145   if (!d->page_open && !d->class->open_page (d))
146     {
147       d->device = OUTP_DEV_DISABLED;
148       return;
149     }
150   
151   if (d->class->special)
152     {
153       driver->class->submit (d, t);
154       return;
155     }
156   
157   t->class->driver (d);
158   t->class->area (&tw, &th);
159   
160   if (!(flags & SOMF_NO_SPACING) && d->cp_y != 0)
161     d->cp_y += d->font_height;
162         
163   if (cs != SOM_COL_NONE
164       && 2 * (tw + d->prop_em_width) <= d->width
165       && nr - (ht + hb) > 5)
166     render_columns ();
167   else if (tw < d->width && th + d->cp_y < d->length)
168     render_simple ();
169   else 
170     render_segments ();
171 }
172
173 /* Render the table into multiple columns. */
174 static void
175 render_columns (void)
176 {
177   int y0, y1;
178   int max_len = 0;
179   int index = 0;
180   
181   assert (cs == SOM_COL_DOWN);
182   assert (d->cp_x == 0);
183
184   for (y0 = ht; y0 < nr - hb; y0 = y1)
185     {
186       int len;
187       
188       t->class->cumulate (SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
189
190       if (y0 == y1)
191         {
192           assert (d->cp_y);
193           outp_eject_page (d);
194         } else {
195           if (len > max_len)
196             max_len = len;
197
198           t->class->title (index++, 0);
199           t->class->render (0, y0, nc, y1);
200           
201           d->cp_x += tw + 2 * d->prop_em_width;
202           if (d->cp_x + tw > d->width)
203             {
204               d->cp_x = 0;
205               d->cp_y += max_len;
206               max_len = 0;
207             }
208         }
209     }
210   
211   if (d->cp_x > 0)
212     {
213       d->cp_x = 0;
214       d->cp_y += max_len;
215     }
216 }
217
218 /* Render the table by itself on the current page. */
219 static void
220 render_simple (void)
221 {
222   assert (d->cp_x == 0);
223   assert (tw < d->width && th + d->cp_y < d->length);
224
225   t->class->title (0, 0);
226   t->class->render (hl, ht, nc - hr, nr - hb);
227   d->cp_y += th;
228 }
229
230 /* General table breaking routine. */
231 static void
232 render_segments (void)
233 {
234   int count = 0;
235   
236   int x_index;
237   int x0, x1;
238   
239   assert (d->cp_x == 0);
240
241   for (x_index = 0, x0 = hl; x0 < nc - hr; x0 = x1, x_index++)
242     {
243       int y_index;
244       int y0, y1;
245       
246       t->class->cumulate (SOM_COLUMNS, x0, &x1, d->width, NULL);
247       if (x_index == 0 && x1 != nc - hr)
248         x_index++;
249
250       for (y_index = 0, y0 = ht; y0 < nr - hb; y0 = y1, y_index++)
251         {
252           int len;
253       
254           if (count++ != 0 && d->cp_y != 0)
255             d->cp_y += d->font_height;
256               
257           t->class->cumulate (SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
258           if (y_index == 0 && y1 != nr - hb)
259             y_index++;
260
261           if (y0 == y1)
262             {
263               assert (d->cp_y);
264               outp_eject_page (d);
265             } else {
266               t->class->title (x_index ? x_index : y_index,
267                                x_index ? y_index : 0);
268               t->class->render (x0, y0, x1, y1);
269           
270               d->cp_y += len;
271             }
272         }
273     }
274 }