spv-legacy-decoder: Set data_index and presentation_index in leaves.
[pspp] / src / output / pivot-table.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2017, 2018 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "output/pivot-table.h"
20
21 #include <math.h>
22 #include <stdlib.h>
23
24 #include "data/data-out.h"
25 #include "data/settings.h"
26 #include "data/value.h"
27 #include "data/variable.h"
28 #include "data/file-name.h"
29 #include "libpspp/array.h"
30 #include "libpspp/assertion.h"
31 #include "libpspp/hash-functions.h"
32 #include "libpspp/i18n.h"
33 #include "output/driver.h"
34 #include "output/spv/spv-table-look.h"
35
36 #include "gl/c-ctype.h"
37 #include "gl/configmake.h"
38 #include "gl/intprops.h"
39 #include "gl/minmax.h"
40 #include "gl/relocatable.h"
41 #include "gl/xalloc.h"
42 #include "gl/xmemdup0.h"
43 #include "gl/xsize.h"
44
45 #include "gettext.h"
46 #define _(msgid) gettext (msgid)
47 #define N_(msgid) msgid
48
49 static void pivot_table_use_rc (const struct pivot_table *, const char *s,
50                                 struct fmt_spec *, bool *honor_small);
51 \f
52 /* Pivot table display styling. */
53
54 /* Returns the name of AREA. */
55 const char *
56 pivot_area_to_string (enum pivot_area area)
57 {
58   switch (area)
59     {
60     case PIVOT_AREA_TITLE: return "title";
61     case PIVOT_AREA_CAPTION: return "caption";
62     case PIVOT_AREA_FOOTER: return "footer";
63     case PIVOT_AREA_CORNER: return "corner";
64     case PIVOT_AREA_COLUMN_LABELS: return "column labels";
65     case PIVOT_AREA_ROW_LABELS: return "row labels";
66     case PIVOT_AREA_DATA: return "data";
67     case PIVOT_AREA_LAYERS: return "layers";
68     case PIVOT_N_AREAS: default: return "**error**";
69     }
70 }
71
72 /* Returns the name of BORDER. */
73 const char *
74 pivot_border_to_string (enum pivot_border border)
75 {
76   switch (border)
77     {
78     case PIVOT_BORDER_TITLE:
79       return "title";
80
81     case PIVOT_BORDER_OUTER_LEFT:
82       return "left outer frame";
83     case PIVOT_BORDER_OUTER_TOP:
84       return "top outer frame";
85     case PIVOT_BORDER_OUTER_RIGHT:
86       return "right outer frame";
87     case PIVOT_BORDER_OUTER_BOTTOM:
88       return "bottom outer frame";
89
90     case PIVOT_BORDER_INNER_LEFT:
91       return "left inner frame";
92     case PIVOT_BORDER_INNER_TOP:
93       return "top inner frame";
94     case PIVOT_BORDER_INNER_RIGHT:
95       return "right inner frame";
96     case PIVOT_BORDER_INNER_BOTTOM:
97       return "bottom inner frame";
98
99     case PIVOT_BORDER_DATA_LEFT:
100       return "data area left";
101     case PIVOT_BORDER_DATA_TOP:
102       return "data area top";
103
104     case PIVOT_BORDER_DIM_ROW_HORZ:
105       return "row label horizontal dimension border";
106     case PIVOT_BORDER_DIM_ROW_VERT:
107       return "row label vertical dimension border";
108     case PIVOT_BORDER_DIM_COL_HORZ:
109       return "column label horizontal dimension border";
110     case PIVOT_BORDER_DIM_COL_VERT:
111       return "column label vertical dimension border";
112
113     case PIVOT_BORDER_CAT_ROW_HORZ:
114       return "row label horizontal category border";
115     case PIVOT_BORDER_CAT_ROW_VERT:
116       return "row label vertical category border";
117     case PIVOT_BORDER_CAT_COL_HORZ:
118       return "column label horizontal category border";
119     case PIVOT_BORDER_CAT_COL_VERT:
120       return "column label vertical category border";
121
122     case PIVOT_N_BORDERS:
123     default:
124       return "**error**";
125     }
126 }
127
128 void
129 pivot_table_sizing_uninit (struct pivot_table_sizing *sizing)
130 {
131   if (sizing)
132     {
133       free (sizing->widths);
134       free (sizing->breaks);
135       free (sizing->keeps);
136     }
137 }
138 \f
139 /* Pivot table looks. */
140
141 static const struct pivot_table_look *
142 default_look (const struct pivot_table_look *new)
143 {
144   static struct pivot_table_look *look;
145   if (new)
146     {
147       pivot_table_look_unref (look);
148       look = pivot_table_look_ref (new);
149     }
150   else if (!look)
151     {
152       char *error = pivot_table_look_read ("default.stt", &look);
153       if (error)
154         {
155           free (error);
156           look = pivot_table_look_ref (pivot_table_look_builtin_default ());
157         }
158     }
159   return look;
160 }
161
162 const struct pivot_table_look *
163 pivot_table_look_get_default (void)
164 {
165   return default_look (NULL);
166 }
167
168 void
169 pivot_table_look_set_default (const struct pivot_table_look *look)
170 {
171   default_look (look);
172 }
173
174 char * WARN_UNUSED_RESULT
175 pivot_table_look_read (const char *name, struct pivot_table_look **lookp)
176 {
177   *lookp = NULL;
178
179   /* Construct search path. */
180   const char *path[4];
181   size_t n = 0;
182   path[n++] = ".";
183   const char *home = getenv ("HOME");
184   char *allocated = NULL;
185   if (home != NULL)
186     path[n++] = allocated = xasprintf ("%s/.pspp/looks", home);
187   char *allocated2;
188   path[n++] = relocate2 (PKGDATADIR "/looks", &allocated2);
189   path[n++] = NULL;
190
191   /* Search path. */
192   char *file = fn_search_path (name, (char **) path);
193   if (!file)
194     {
195       char *name2 = xasprintf ("%s.stt", name);
196       file = fn_search_path (name2, (char **) path);
197       free (name2);
198     }
199   free (allocated);
200   free (allocated2);
201   if (!file)
202     return xasprintf ("%s: not found", name);
203
204   /* Read file. */
205   char *error = spv_table_look_read (file, lookp);
206   free (file);
207   return error;
208 }
209
210 const struct pivot_table_look *
211 pivot_table_look_builtin_default (void)
212 {
213   static struct pivot_table_look look = {
214     .ref_cnt = 1,
215
216     .omit_empty = true,
217     .row_labels_in_corner = true,
218     .width_ranges = {
219       [TABLE_HORZ] = { 36, 72 },
220       [TABLE_VERT] = { 36, 120 },
221     },
222
223     .areas = {
224 #define AREA(BOLD, H, V, L, R, T, B) {                         \
225     .cell_style = {                                             \
226       .halign = TABLE_HALIGN_##H,                               \
227       .valign = TABLE_VALIGN_##V,                               \
228       .margin = { [TABLE_HORZ][0] = L, [TABLE_HORZ][1] = R,     \
229                   [TABLE_VERT][0] = T, [TABLE_VERT][1] = B },   \
230     },                                                          \
231     .font_style = {                                             \
232       .bold = BOLD,                                             \
233       .fg = { [0] = CELL_COLOR_BLACK, [1] = CELL_COLOR_BLACK},  \
234       .bg = { [0] = CELL_COLOR_WHITE, [1] = CELL_COLOR_WHITE},  \
235       .size = 9,                                                \
236       .typeface = (char *) "Sans Serif",                        \
237     },                                                          \
238   }
239       [PIVOT_AREA_TITLE]         = AREA(true,  CENTER, CENTER,  8,11,1,8),
240       [PIVOT_AREA_CAPTION]       = AREA(false, LEFT,   TOP,     8,11,1,1),
241       [PIVOT_AREA_FOOTER]        = AREA(false, LEFT,   TOP,    11, 8,2,3),
242       [PIVOT_AREA_CORNER]        = AREA(false, LEFT,   BOTTOM,  8,11,1,1),
243       [PIVOT_AREA_COLUMN_LABELS] = AREA(false, CENTER, BOTTOM,  8,11,1,3),
244       [PIVOT_AREA_ROW_LABELS]    = AREA(false, LEFT,   TOP,     8,11,1,3),
245       [PIVOT_AREA_DATA]          = AREA(false, MIXED,  TOP,     8,11,1,1),
246       [PIVOT_AREA_LAYERS]        = AREA(false, LEFT,   BOTTOM,  8,11,1,3),
247 #undef AREA
248     },
249
250     .borders = {
251 #define BORDER(STROKE) { .stroke = STROKE, .color = CELL_COLOR_BLACK }
252       [PIVOT_BORDER_TITLE]        = BORDER(TABLE_STROKE_NONE),
253       [PIVOT_BORDER_OUTER_LEFT]   = BORDER(TABLE_STROKE_NONE),
254       [PIVOT_BORDER_OUTER_TOP]    = BORDER(TABLE_STROKE_NONE),
255       [PIVOT_BORDER_OUTER_RIGHT]  = BORDER(TABLE_STROKE_NONE),
256       [PIVOT_BORDER_OUTER_BOTTOM] = BORDER(TABLE_STROKE_NONE),
257       [PIVOT_BORDER_INNER_LEFT]   = BORDER(TABLE_STROKE_THICK),
258       [PIVOT_BORDER_INNER_TOP]    = BORDER(TABLE_STROKE_THICK),
259       [PIVOT_BORDER_INNER_RIGHT]  = BORDER(TABLE_STROKE_THICK),
260       [PIVOT_BORDER_INNER_BOTTOM] = BORDER(TABLE_STROKE_THICK),
261       [PIVOT_BORDER_DATA_LEFT]    = BORDER(TABLE_STROKE_THICK),
262       [PIVOT_BORDER_DATA_TOP]     = BORDER(TABLE_STROKE_THICK),
263       [PIVOT_BORDER_DIM_ROW_HORZ] = BORDER(TABLE_STROKE_SOLID),
264       [PIVOT_BORDER_DIM_ROW_VERT] = BORDER(TABLE_STROKE_NONE),
265       [PIVOT_BORDER_DIM_COL_HORZ] = BORDER(TABLE_STROKE_SOLID),
266       [PIVOT_BORDER_DIM_COL_VERT] = BORDER(TABLE_STROKE_SOLID),
267       [PIVOT_BORDER_CAT_ROW_HORZ] = BORDER(TABLE_STROKE_NONE),
268       [PIVOT_BORDER_CAT_ROW_VERT] = BORDER(TABLE_STROKE_NONE),
269       [PIVOT_BORDER_CAT_COL_HORZ] = BORDER(TABLE_STROKE_SOLID),
270       [PIVOT_BORDER_CAT_COL_VERT] = BORDER(TABLE_STROKE_SOLID),
271     },
272   };
273
274   return &look;
275 }
276
277 struct pivot_table_look *
278 pivot_table_look_new_builtin_default (void)
279 {
280   return pivot_table_look_unshare (
281     pivot_table_look_ref (pivot_table_look_builtin_default ()));
282 }
283
284 struct pivot_table_look *
285 pivot_table_look_ref (const struct pivot_table_look *look_)
286 {
287   assert (look_->ref_cnt > 0);
288
289   struct pivot_table_look *look = CONST_CAST (struct pivot_table_look *, look_);
290   look->ref_cnt++;
291   return look;
292 }
293
294 static char *
295 xstrdup_if_nonempty (const char *s)
296 {
297   return s && s[0] ? xstrdup (s) : NULL;
298 }
299
300 struct pivot_table_look *
301 pivot_table_look_unshare (struct pivot_table_look *old)
302 {
303   assert (old->ref_cnt > 0);
304   if (old->ref_cnt == 1)
305     return old;
306
307   pivot_table_look_unref (old);
308
309   struct pivot_table_look *new = xmemdup (old, sizeof *old);
310   new->ref_cnt = 1;
311   new->name = xstrdup_if_nonempty (old->name);
312   for (size_t i = 0; i < PIVOT_N_AREAS; i++)
313     table_area_style_copy (NULL, &new->areas[i], &old->areas[i]);
314   new->continuation = xstrdup_if_nonempty (old->continuation);
315
316   return new;
317 }
318
319 void
320 pivot_table_look_unref (struct pivot_table_look *look)
321 {
322   if (look)
323     {
324       assert (look->ref_cnt > 0);
325       if (!--look->ref_cnt)
326         {
327           free (look->name);
328           for (size_t i = 0; i < PIVOT_N_AREAS; i++)
329             table_area_style_uninit (&look->areas[i]);
330           free (look->continuation);
331           free (look);
332         }
333     }
334 }
335 \f
336 /* Axes. */
337
338 /* Returns the name of AXIS_TYPE. */
339 const char *
340 pivot_axis_type_to_string (enum pivot_axis_type axis_type)
341 {
342   switch (axis_type)
343     {
344     case PIVOT_AXIS_LAYER:
345       return "layer";
346
347     case PIVOT_AXIS_ROW:
348       return "row";
349
350     case PIVOT_AXIS_COLUMN:
351       return "column";
352
353     default:
354       return "<error>";
355     }
356 }
357
358 static enum pivot_axis_type
359 pivot_axis_type_transpose (enum pivot_axis_type axis_type)
360 {
361   assert (axis_type == PIVOT_AXIS_ROW || axis_type == PIVOT_AXIS_COLUMN);
362   return (axis_type == PIVOT_AXIS_ROW ? PIVOT_AXIS_COLUMN : PIVOT_AXIS_ROW);
363 }
364
365 /* Implementation of PIVOT_AXIS_FOR_EACH. */
366 size_t *
367 pivot_axis_iterator_next (size_t *indexes, const struct pivot_axis *axis)
368 {
369   if (!indexes)
370     {
371       if (axis->n_dimensions)
372         for (size_t i = 0; i < axis->n_dimensions; i++)
373           if (axis->dimensions[i]->n_leaves == 0)
374             return NULL;
375
376       size_t size = axis->n_dimensions * sizeof *indexes;
377       return xzalloc (MAX (size, 1));
378     }
379
380   for (size_t i = 0; i < axis->n_dimensions; i++)
381     {
382       const struct pivot_dimension *d = axis->dimensions[i];
383       if (++indexes[i] < d->n_leaves)
384         return indexes;
385
386       indexes[i] = 0;
387     }
388
389   free (indexes);
390   return NULL;
391 }
392 \f
393 /* Dimensions. */
394
395 static void
396 pivot_category_set_rc (struct pivot_category *category, const char *s)
397 {
398   if (!s)
399     return;
400
401   pivot_table_use_rc (category->dimension->table, s,
402                       &category->format, &category->honor_small);
403
404   /* Ensure that the category itself, in addition to the cells within it, takes
405      the format.  (It's kind of rare for a category to have a numeric format
406      though.) */
407   struct pivot_value *name = category->name;
408   if (name->type == PIVOT_VALUE_NUMERIC && !name->numeric.format.w)
409     pivot_table_use_rc (category->dimension->table, s,
410                         &name->numeric.format, &name->numeric.honor_small);
411 }
412
413 static void
414 pivot_category_create_leaves_valist (struct pivot_category *parent,
415                                      va_list args)
416 {
417   const char *s;
418   while ((s = va_arg (args, const char *)))
419     {
420       if (!strncmp (s, "RC_", 3))
421         {
422           assert (parent->n_subs);
423           pivot_category_set_rc (parent->subs[parent->n_subs - 1], s);
424         }
425       else
426         pivot_category_create_leaf (parent, pivot_value_new_text (s));
427     }
428 }
429
430 /* Creates a new dimension with the given NAME in TABLE and returns it.  The
431    dimension is added to axis AXIS_TYPE, becoming the outermost dimension on
432    that axis.
433
434    NAME should be a translatable name, but not actually translated yet,
435    e.g. enclosed in N_().  To use a different kind of value for a name, use
436    pivot_dimension_create__() instead.
437
438    The optional varargs parameters may be used to add an initial set of
439    categories to the dimension.  Each string should be a translatable category
440    name, but not actually translated yet, e.g. enclosed in N_().  Each string
441    may optionally be followod by a PIVOT_RC_* string that specifies the default
442    numeric format for cells in this category. */
443 struct pivot_dimension * SENTINEL (0)
444 (pivot_dimension_create) (struct pivot_table *table,
445                           enum pivot_axis_type axis_type,
446                           const char *name, ...)
447 {
448   struct pivot_dimension *d = pivot_dimension_create__ (
449     table, axis_type, pivot_value_new_text (name));
450
451   va_list args;
452   va_start (args, name);
453   pivot_category_create_leaves_valist (d->root, args);
454   va_end (args);
455
456   return d;
457 }
458
459 /* Creates a new dimension with the given NAME in TABLE and returns it.  The
460    dimension is added to axis AXIS_TYPE, becoming the outermost dimension on
461    that axis. */
462 struct pivot_dimension *
463 pivot_dimension_create__ (struct pivot_table *table,
464                           enum pivot_axis_type axis_type,
465                           struct pivot_value *name)
466 {
467   assert (pivot_table_is_empty (table));
468
469   struct pivot_dimension *d = xmalloc (sizeof *d);
470   *d = (struct pivot_dimension) {
471     .table = table,
472     .axis_type = axis_type,
473     .level = table->axes[axis_type].n_dimensions,
474     .top_index = table->n_dimensions,
475     .root = xmalloc (sizeof *d->root),
476   };
477
478   struct pivot_category *root = d->root;
479   *root = (struct pivot_category) {
480     .name = name,
481     .parent = NULL,
482     .dimension = d,
483     .show_label = false,
484     .data_index = SIZE_MAX,
485     .presentation_index = SIZE_MAX,
486   };
487
488   table->dimensions = xrealloc (
489     table->dimensions, (table->n_dimensions + 1) * sizeof *table->dimensions);
490   table->dimensions[table->n_dimensions++] = d;
491
492   struct pivot_axis *axis = &table->axes[axis_type];
493   axis->dimensions = xrealloc (
494     axis->dimensions, (axis->n_dimensions + 1) * sizeof *axis->dimensions);
495   axis->dimensions[axis->n_dimensions++] = d;
496
497   if (axis_type == PIVOT_AXIS_LAYER)
498     {
499       free (table->current_layer);
500       table->current_layer = xcalloc (axis[PIVOT_AXIS_LAYER].n_dimensions,
501                                       sizeof *table->current_layer);
502     }
503
504   /* axis->extent and axis->label_depth will be calculated later. */
505
506   return d;
507 }
508
509 void
510 pivot_dimension_destroy (struct pivot_dimension *d)
511 {
512   if (!d)
513     return;
514
515   pivot_category_destroy (d->root);
516   free (d->data_leaves);
517   free (d->presentation_leaves);
518   free (d);
519 }
520
521 /* Returns the first leaf node in an in-order traversal that is a child of
522    CAT. */
523 static const struct pivot_category * UNUSED
524 pivot_category_first_leaf (const struct pivot_category *cat)
525 {
526   if (pivot_category_is_leaf (cat))
527     return cat;
528
529   for (size_t i = 0; i < cat->n_subs; i++)
530     {
531       const struct pivot_category *first
532         = pivot_category_first_leaf (cat->subs[i]);
533       if (first)
534         return first;
535     }
536
537   return NULL;
538 }
539
540 /* Returns the next leaf node in an in-order traversal starting at CAT, which
541    must be a leaf. */
542 static const struct pivot_category * UNUSED
543 pivot_category_next_leaf (const struct pivot_category *cat)
544 {
545   assert (pivot_category_is_leaf (cat));
546
547   for (;;)
548     {
549       const struct pivot_category *parent = cat->parent;
550       if (!parent)
551         return NULL;
552       for (size_t i = cat->group_index + 1; i < parent->n_subs; i++)
553         {
554           const struct pivot_category *next
555             = pivot_category_first_leaf (parent->subs[i]);
556           if (next)
557             return next;
558         }
559
560       cat = cat->parent;
561     }
562 }
563
564 static void
565 pivot_category_add_child (struct pivot_category *child)
566 {
567   struct pivot_category *parent = child->parent;
568
569   assert (pivot_category_is_group (parent));
570   if (parent->n_subs >= parent->allocated_subs)
571     parent->subs = x2nrealloc (parent->subs, &parent->allocated_subs,
572                                sizeof *parent->subs);
573   parent->subs[parent->n_subs++] = child;
574 }
575
576 /* Adds leaf categories as a child of PARENT.  To create top-level categories
577    within dimension 'd', pass 'd->root' for PARENT.
578
579    Each of the varargs parameters should be a string, each of which should be a
580    translatable category name, but not actually translated yet, e.g. enclosed
581    in N_().  Each string may optionally be followod by a PIVOT_RC_* string that
582    specifies the default numeric format for cells in this category.
583
584    Returns the category index, which is just a 0-based array index, for the
585    first new category.
586
587    Leaves have to be created in in-order, that is, don't create a group and add
588    some leaves, then add leaves outside the group and try to add more leaves
589    inside it. */
590 int SENTINEL (0)
591 (pivot_category_create_leaves) (struct pivot_category *parent, ...)
592 {
593   int retval = parent->dimension->n_leaves;
594
595   va_list args;
596   va_start (args, parent);
597   pivot_category_create_leaves_valist (parent, args);
598   va_end (args);
599
600   return retval;
601 }
602
603 /* Creates a new leaf category with the given NAME as a child of PARENT.  To
604    create a top-level category within dimension 'd', pass 'd->root' for PARENT.
605    Returns the category index, which is just a 0-based array index, for the new
606    category.
607
608    Leaves have to be created in in-order, that is, don't create a group and add
609    some leaves, then add leaves outside the group and try to add more leaves
610    inside it. */
611 int
612 pivot_category_create_leaf (struct pivot_category *parent,
613                             struct pivot_value *name)
614 {
615   return pivot_category_create_leaf_rc (parent, name, NULL);
616 }
617
618 /* Creates a new leaf category with the given NAME as a child of PARENT.  To
619    create a top-level category within dimension 'd', pass 'd->root' for PARENT.
620    Returns the category index, which is just a 0-based array index, for the new
621    category.
622
623    If RC is nonnull and the name of a result category, the category is assigned
624    that result category.
625
626    Leaves have to be created in in-order, that is, don't create a group and add
627    some leaves, then add leaves outside the group and try to add more leaves
628    inside it. */
629 int
630 pivot_category_create_leaf_rc (struct pivot_category *parent,
631                                struct pivot_value *name, const char *rc)
632 {
633   struct pivot_dimension *d = parent->dimension;
634
635   struct pivot_category *leaf = xmalloc (sizeof *leaf);
636   *leaf = (struct pivot_category) {
637     .name = name,
638     .parent = parent,
639     .dimension = d,
640     .group_index = parent->n_subs,
641     .data_index = d->n_leaves,
642     .presentation_index = d->n_leaves,
643   };
644
645   if (d->n_leaves >= d->allocated_leaves)
646     {
647       d->data_leaves = x2nrealloc (d->data_leaves, &d->allocated_leaves,
648                                    sizeof *d->data_leaves);
649       d->presentation_leaves = xrealloc (
650         d->presentation_leaves,
651         d->allocated_leaves * sizeof *d->presentation_leaves);
652     }
653
654   d->data_leaves[d->n_leaves] = leaf;
655   d->presentation_leaves[d->n_leaves] = leaf;
656   d->n_leaves++;
657
658   pivot_category_add_child (leaf);
659
660   /* Make sure that the new child is the last in in-order. */
661   assert (!pivot_category_next_leaf (leaf));
662
663   pivot_category_set_rc (leaf, rc);
664
665   return leaf->data_index;
666 }
667
668 /* Adds a new category group named NAME as a child of PARENT.  To create a
669    top-level group within dimension 'd', pass 'd->root' for PARENT.
670
671    NAME should be a translatable name, but not actually translated yet,
672    e.g. enclosed in N_().  To use a different kind of value for a name, use
673    pivot_category_create_group__() instead.
674
675    The optional varargs parameters may be used to add an initial set of
676    categories to the group.  Each string should be a translatable category
677    name, but not actually translated yet, e.g. enclosed in N_().  Each string
678    may optionally be followod by a PIVOT_RC_* string that specifies the default
679    numeric format for cells in this category.
680
681    Returns the new group. */
682 struct pivot_category * SENTINEL (0)
683 (pivot_category_create_group) (struct pivot_category *parent,
684                                const char *name, ...)
685 {
686   struct pivot_category *group = pivot_category_create_group__ (
687     parent, pivot_value_new_text (name));
688
689   va_list args;
690   va_start (args, name);
691   pivot_category_create_leaves_valist (group, args);
692   va_end (args);
693
694   return group;
695 }
696
697 /* Adds a new category group named NAME as a child of PARENT.  To create a
698    top-level group within dimension 'd', pass 'd->root' for PARENT.  Returns
699    the new group. */
700 struct pivot_category *
701 pivot_category_create_group__ (struct pivot_category *parent,
702                                struct pivot_value *name)
703 {
704   struct pivot_dimension *d = parent->dimension;
705
706   struct pivot_category *group = xmalloc (sizeof *group);
707   *group = (struct pivot_category) {
708     .name = name,
709     .parent = parent,
710     .dimension = d,
711     .show_label = true,
712     .group_index = parent->n_subs,
713     .data_index = SIZE_MAX,
714     .presentation_index = SIZE_MAX,
715   };
716
717   pivot_category_add_child (group);
718
719   return group;
720 }
721
722 void
723 pivot_category_destroy (struct pivot_category *c)
724 {
725   if (!c)
726     return;
727
728   pivot_value_destroy (c->name);
729   for (size_t i = 0; i < c->n_subs; i++)
730     pivot_category_destroy (c->subs[i]);
731   free (c->subs);
732   free (c);
733 }
734 \f
735 /* Result classes.
736
737    These are usually the easiest way to control the formatting of numeric data
738    in a pivot table.  See pivot_dimension_create() for an explanation of their
739    use.  */
740 struct result_class
741   {
742     const char *name;           /* "RC_*". */
743     struct fmt_spec format;
744   };
745
746 /* Formats for most of the result classes. */
747 static struct result_class result_classes[] =
748   {
749     { PIVOT_RC_INTEGER,      { FMT_F,   40, 0 } },
750     { PIVOT_RC_PERCENT,      { FMT_PCT, 40, 1 } },
751     { PIVOT_RC_CORRELATION,  { FMT_F,   40, 3 } },
752     { PIVOT_RC_SIGNIFICANCE, { FMT_F,   40, 3 } },
753     { PIVOT_RC_RESIDUAL,     { FMT_F,   40, 2 } },
754     { PIVOT_RC_COUNT,        { 0, 0, 0 } },
755     { PIVOT_RC_OTHER,        { 0, 0, 0 } },
756   };
757
758 /* Has PIVOT_RC_COUNT been overridden by the user? */
759 static bool overridden_count_format;
760
761 static struct result_class *
762 pivot_result_class_find (const char *s)
763 {
764   for (size_t i = 0; i < sizeof result_classes / sizeof *result_classes; i++)
765     if (!strcmp (s, result_classes[i].name))
766       return &result_classes[i];
767   return NULL;
768 }
769
770 static void
771 pivot_table_use_rc (const struct pivot_table *table, const char *s,
772                     struct fmt_spec *format, bool *honor_small)
773 {
774   if (s)
775     {
776       if (!strcmp (s, PIVOT_RC_OTHER))
777         {
778           *format = *settings_get_format ();
779           *honor_small = true;
780         }
781       else if (!strcmp (s, PIVOT_RC_COUNT) && !overridden_count_format)
782         {
783           *format = table->weight_format;
784           *honor_small = false;
785         }
786       else
787         {
788           const struct result_class *rc = pivot_result_class_find (s);
789           if (rc)
790             {
791               *format = rc->format;
792               *honor_small = false;
793             }
794           else
795             {
796               printf ("unknown class %s\n", s);
797             }
798         }
799     }
800 }
801
802 /* Sets the format specification for the result class named S (which should not
803    include the RC_ prefix) to *FORMAT.  Returns true if successful, false if S
804    does not name a known result class. */
805 bool
806 pivot_result_class_change (const char *s_, const struct fmt_spec *format)
807 {
808   char *s = xasprintf ("RC_%s", s_);
809   struct result_class *rc = pivot_result_class_find (s);
810   if (rc)
811     {
812       rc->format = *format;
813       if (!strcmp (s, PIVOT_RC_COUNT))
814         overridden_count_format = true;
815     }
816   free (s);
817
818   return rc != NULL;
819 }
820
821 bool
822 is_pivot_result_class (const char *s)
823 {
824   return pivot_result_class_find (s) != NULL;
825 }
826 \f
827 /* Pivot tables. */
828
829 static struct pivot_cell *pivot_table_insert_cell (struct pivot_table *,
830                                                    const size_t *dindexes);
831 static void pivot_table_delete_cell (struct pivot_table *,
832                                      struct pivot_cell *);
833
834 /* Creates and returns a new pivot table with the given TITLE.  TITLE should be
835    a text string marked for translation but not actually translated yet,
836    e.g. N_("Descriptive Statistics").  The un-translated text string is used as
837    the pivot table's subtype.
838
839    This function is a shortcut for pivot_table_create__() for the most common
840    case.  Use pivot_table_create__() directly if the title should be some kind
841    of value other than an ordinary text string, or if the subtype should be
842    different from the title.
843
844    See the large comment at the top of pivot-table.h for general advice on
845    creating pivot tables. */
846 struct pivot_table *
847 pivot_table_create (const char *title)
848 {
849   return pivot_table_create__ (pivot_value_new_text (title), title);
850 }
851
852 /* Creates and returns a new pivot table with the given TITLE, and takes
853    ownership of TITLE.  The new pivot table's subtype is SUBTYPE, which should
854    be an untranslated English string that describes the contents of the table
855    at a high level without being specific about the variables or other context
856    involved.
857
858    TITLE and SUBTYPE may be NULL, but in that case the client must add them
859    later because they are both mandatory for a pivot table.
860
861    See the large comment at the top of pivot-table.h for general advice on
862    creating pivot tables. */
863 struct pivot_table *
864 pivot_table_create__ (struct pivot_value *title, const char *subtype)
865 {
866   struct pivot_table *table = xzalloc (sizeof *table);
867   table->ref_cnt = 1;
868   table->show_title = true;
869   table->show_caption = true;
870   table->weight_format = (struct fmt_spec) { FMT_F, 40, 0 };
871   table->title = title;
872   table->subtype = subtype ? pivot_value_new_text (subtype) : NULL;
873   table->command_c = output_get_command_name ();
874   table->look = pivot_table_look_ref (pivot_table_look_get_default ());
875   table->settings = fmt_settings_copy (settings_get_fmt_settings ());
876   table->small = settings_get_small ();
877
878   hmap_init (&table->cells);
879
880   return table;
881 }
882
883 /* Creates and returns a new pivot table with the given TITLE and a single cell
884    with the given CONTENT.
885
886    This is really just for error handling. */
887 struct pivot_table *
888 pivot_table_create_for_text (struct pivot_value *title,
889                              struct pivot_value *content)
890 {
891   struct pivot_table *table = pivot_table_create__ (title, "Error");
892
893   struct pivot_dimension *d = pivot_dimension_create (
894     table, PIVOT_AXIS_ROW, N_("Error"));
895   d->hide_all_labels = true;
896   pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
897
898   pivot_table_put1 (table, 0, content);
899
900   return table;
901 }
902
903 /* Increases TABLE's reference count, indicating that it has an additional
904    owner.  A pivot table that is shared among multiple owners must not be
905    modified. */
906 struct pivot_table *
907 pivot_table_ref (const struct pivot_table *table_)
908 {
909   struct pivot_table *table = CONST_CAST (struct pivot_table *, table_);
910   table->ref_cnt++;
911   return table;
912 }
913
914 static char *
915 xstrdup_if_nonnull (const char *s)
916 {
917   return s ? xstrdup (s) : NULL;
918 }
919
920 static struct pivot_table_sizing
921 clone_sizing (const struct pivot_table_sizing *s)
922 {
923   return (struct pivot_table_sizing) {
924     .widths = (s->n_widths
925                ? xmemdup (s->widths, s->n_widths * sizeof *s->widths)
926                : NULL),
927     .n_widths = s->n_widths,
928
929     .breaks = (s->n_breaks
930                ? xmemdup (s->breaks, s->n_breaks * sizeof *s->breaks)
931                : NULL),
932     .n_breaks = s->n_breaks,
933
934     .keeps = (s->n_keeps
935               ? xmemdup (s->keeps, s->n_keeps * sizeof *s->keeps)
936               : NULL),
937     .n_keeps = s->n_keeps,
938   };
939 }
940
941 static struct pivot_footnote **
942 clone_footnotes (struct pivot_footnote **old, size_t n)
943 {
944   if (!n)
945     return NULL;
946
947   struct pivot_footnote **new = xmalloc (n * sizeof *new);
948   for (size_t i = 0; i < n; i++)
949     {
950       new[i] = xmalloc (sizeof *new[i]);
951       *new[i] = (struct pivot_footnote) {
952         .idx = old[i]->idx,
953         .content = pivot_value_clone (old[i]->content),
954         .marker = pivot_value_clone (old[i]->marker),
955         .show = old[i]->show,
956       };
957     }
958   return new;
959 }
960
961 static struct pivot_category *
962 clone_category (struct pivot_category *old,
963                 struct pivot_dimension *new_dimension,
964                 struct pivot_category *new_parent)
965 {
966   struct pivot_category *new = xmalloc (sizeof *new);
967   *new = (struct pivot_category) {
968     .name = pivot_value_clone (old->name),
969     .parent = new_parent,
970     .dimension = new_dimension,
971     .label_depth = old->label_depth,
972     .extra_depth = old->extra_depth,
973
974     .subs = (old->n_subs
975              ? xzalloc (old->n_subs * sizeof *new->subs)
976              : NULL),
977     .n_subs = old->n_subs,
978     .allocated_subs = old->n_subs,
979
980     .show_label = old->show_label,
981     .show_label_in_corner = old->show_label_in_corner,
982
983     .format = old->format,
984     .group_index = old->group_index,
985     .data_index = old->data_index,
986     .presentation_index = old->presentation_index,
987   };
988
989   if (pivot_category_is_leaf (old))
990     {
991       assert (new->data_index < new_dimension->n_leaves);
992       new->dimension->data_leaves[new->data_index] = new;
993
994       assert (new->presentation_index < new_dimension->n_leaves);
995       new->dimension->presentation_leaves[new->presentation_index] = new;
996     }
997
998   for (size_t i = 0; i < new->n_subs; i++)
999     new->subs[i] = clone_category (old->subs[i], new_dimension, new);
1000
1001   return new;
1002 }
1003
1004 static struct pivot_dimension *
1005 clone_dimension (struct pivot_dimension *old, struct pivot_table *new_pt)
1006 {
1007   struct pivot_dimension *new = xmalloc (sizeof *new);
1008   *new = (struct pivot_dimension) {
1009     .table = new_pt,
1010     .axis_type = old->axis_type,
1011     .level = old->level,
1012     .top_index = old->top_index,
1013     .data_leaves = xzalloc (old->n_leaves * sizeof *new->data_leaves),
1014     .presentation_leaves = xzalloc (old->n_leaves
1015                                     * sizeof *new->presentation_leaves),
1016     .n_leaves = old->n_leaves,
1017     .allocated_leaves = old->n_leaves,
1018     .hide_all_labels = old->hide_all_labels,
1019     .label_depth = old->label_depth,
1020   };
1021
1022   new->root = clone_category (old->root, new, NULL);
1023
1024   return new;
1025 }
1026
1027 static struct pivot_dimension **
1028 clone_dimensions (struct pivot_dimension **old, size_t n,
1029                   struct pivot_table *new_pt)
1030 {
1031   if (!n)
1032     return NULL;
1033
1034   struct pivot_dimension **new = xmalloc (n * sizeof *new);
1035   for (size_t i = 0; i < n; i++)
1036     new[i] = clone_dimension (old[i], new_pt);
1037   return new;
1038 }
1039
1040 struct pivot_table *
1041 pivot_table_unshare (struct pivot_table *old)
1042 {
1043   assert (old->ref_cnt > 0);
1044   if (old->ref_cnt == 1)
1045     return old;
1046
1047   pivot_table_unref (old);
1048
1049   struct pivot_table *new = xmalloc (sizeof *new);
1050   *new = (struct pivot_table) {
1051     .ref_cnt = 1,
1052
1053     .look = pivot_table_look_ref (old->look),
1054
1055     .rotate_inner_column_labels = old->rotate_inner_column_labels,
1056     .rotate_outer_row_labels = old->rotate_outer_row_labels,
1057     .show_grid_lines = old->show_grid_lines,
1058     .show_title = old->show_title,
1059     .show_caption = old->show_caption,
1060     .current_layer = (old->current_layer
1061                       ? xmemdup (old->current_layer,
1062                                  old->axes[PIVOT_AXIS_LAYER].n_dimensions
1063                                  * sizeof *new->current_layer)
1064                       : NULL),
1065     .show_values = old->show_values,
1066     .show_variables = old->show_variables,
1067     .weight_format = old->weight_format,
1068
1069     .sizing = {
1070       [TABLE_HORZ] = clone_sizing (&old->sizing[TABLE_HORZ]),
1071       [TABLE_VERT] = clone_sizing (&old->sizing[TABLE_VERT]),
1072     },
1073
1074     .settings = fmt_settings_copy (&old->settings),
1075     .grouping = old->grouping,
1076     .small = old->small,
1077
1078     .command_local = xstrdup_if_nonnull (old->command_local),
1079     .command_c = xstrdup_if_nonnull (old->command_c),
1080     .language = xstrdup_if_nonnull (old->language),
1081     .locale = xstrdup_if_nonnull (old->locale),
1082
1083     .dataset = xstrdup_if_nonnull (old->dataset),
1084     .datafile = xstrdup_if_nonnull (old->datafile),
1085     .date = old->date,
1086
1087     .footnotes = clone_footnotes (old->footnotes, old->n_footnotes),
1088     .n_footnotes = old->n_footnotes,
1089     .allocated_footnotes = old->n_footnotes,
1090
1091     .title = pivot_value_clone (old->title),
1092     .subtype = pivot_value_clone (old->subtype),
1093     .corner_text = pivot_value_clone (old->corner_text),
1094     .caption = pivot_value_clone (old->caption),
1095     .notes = xstrdup_if_nonnull (old->notes),
1096
1097     .dimensions = clone_dimensions (old->dimensions, old->n_dimensions, new),
1098     .n_dimensions = old->n_dimensions,
1099
1100     .cells = HMAP_INITIALIZER (new->cells),
1101   };
1102
1103   for (size_t i = 0; i < PIVOT_N_AXES; i++)
1104     {
1105       struct pivot_axis *new_axis = &new->axes[i];
1106       const struct pivot_axis *old_axis = &old->axes[i];
1107
1108       *new_axis = (struct pivot_axis) {
1109         .dimensions = xmalloc (old_axis->n_dimensions
1110                                * sizeof *new_axis->dimensions),
1111         .n_dimensions = old_axis->n_dimensions,
1112         .extent = old_axis->extent,
1113         .label_depth = old_axis->label_depth,
1114       };
1115
1116       for (size_t i = 0; i < new_axis->n_dimensions; i++)
1117         new_axis->dimensions[i] = new->dimensions[
1118           old_axis->dimensions[i]->top_index];
1119     }
1120
1121   const struct pivot_cell *old_cell;
1122   size_t *dindexes = xmalloc (old->n_dimensions * sizeof *dindexes);
1123   HMAP_FOR_EACH (old_cell, struct pivot_cell, hmap_node, &old->cells)
1124     {
1125       for (size_t i = 0; i < old->n_dimensions; i++)
1126         dindexes[i] = old_cell->idx[i];
1127       struct pivot_cell *new_cell
1128         = pivot_table_insert_cell (new, dindexes);
1129       new_cell->value = pivot_value_clone (old_cell->value);
1130     }
1131   free (dindexes);
1132
1133   return new;
1134 }
1135
1136 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
1137    If TABLE no longer has any owners, it is freed. */
1138 void
1139 pivot_table_unref (struct pivot_table *table)
1140 {
1141   if (!table)
1142     return;
1143   assert (table->ref_cnt > 0);
1144   if (--table->ref_cnt)
1145     return;
1146
1147   free (table->current_layer);
1148   pivot_table_look_unref (table->look);
1149
1150   for (int i = 0; i < TABLE_N_AXES; i++)
1151     pivot_table_sizing_uninit (&table->sizing[i]);
1152
1153   fmt_settings_uninit (&table->settings);
1154
1155   free (table->command_local);
1156   free (table->command_c);
1157   free (table->language);
1158   free (table->locale);
1159
1160   free (table->dataset);
1161   free (table->datafile);
1162
1163   for (size_t i = 0; i < table->n_footnotes; i++)
1164     pivot_footnote_destroy (table->footnotes[i]);
1165   free (table->footnotes);
1166
1167   pivot_value_destroy (table->title);
1168   pivot_value_destroy (table->subtype);
1169   pivot_value_destroy (table->corner_text);
1170   pivot_value_destroy (table->caption);
1171   free (table->notes);
1172
1173   for (size_t i = 0; i < table->n_dimensions; i++)
1174     pivot_dimension_destroy (table->dimensions[i]);
1175   free (table->dimensions);
1176
1177   for (size_t i = 0; i < PIVOT_N_AXES; i++)
1178     free (table->axes[i].dimensions);
1179
1180   struct pivot_cell *cell, *next_cell;
1181   HMAP_FOR_EACH_SAFE (cell, next_cell, struct pivot_cell, hmap_node,
1182                       &table->cells)
1183     pivot_table_delete_cell (table, cell);
1184
1185   hmap_destroy (&table->cells);
1186
1187   free (table);
1188 }
1189
1190 /* Returns true if TABLE has more than one owner.  A pivot table that is shared
1191    among multiple owners must not be modified. */
1192 bool
1193 pivot_table_is_shared (const struct pivot_table *table)
1194 {
1195   return table->ref_cnt > 1;
1196 }
1197
1198 /* Swaps axes A and B in TABLE. */
1199 void
1200 pivot_table_swap_axes (struct pivot_table *table,
1201                        enum pivot_axis_type a, enum pivot_axis_type b)
1202 {
1203   if (a == b)
1204     return;
1205
1206   struct pivot_axis tmp = table->axes[a];
1207   table->axes[a] = table->axes[b];
1208   table->axes[b] = tmp;
1209
1210   for (int a = 0; a < PIVOT_N_AXES; a++)
1211     {
1212       struct pivot_axis *axis = &table->axes[a];
1213       for (size_t d = 0; d < axis->n_dimensions; d++)
1214         axis->dimensions[d]->axis_type = a;
1215     }
1216
1217   if (a == PIVOT_AXIS_LAYER || b == PIVOT_AXIS_LAYER)
1218     {
1219       free (table->current_layer);
1220       table->current_layer = xzalloc (
1221         table->axes[PIVOT_AXIS_LAYER].n_dimensions
1222         * sizeof *table->current_layer);
1223     }
1224 }
1225
1226 /* Swaps the row and column axes in TABLE. */
1227 void
1228 pivot_table_transpose (struct pivot_table *table)
1229 {
1230   pivot_table_swap_axes (table, PIVOT_AXIS_ROW, PIVOT_AXIS_COLUMN);
1231 }
1232
1233 static void
1234 pivot_table_update_axes (struct pivot_table *table)
1235 {
1236   for (int a = 0; a < PIVOT_N_AXES; a++)
1237     {
1238       struct pivot_axis *axis = &table->axes[a];
1239
1240       for (size_t d = 0; d < axis->n_dimensions; d++)
1241         {
1242           struct pivot_dimension *dim = axis->dimensions[d];
1243           dim->axis_type = a;
1244           dim->level = d;
1245         }
1246     }
1247 }
1248
1249 /* Moves DIM from its current location in TABLE to POS within AXIS.  POS of 0
1250    is the innermost dimension, 1 is the next one out, and so on. */
1251 void
1252 pivot_table_move_dimension (struct pivot_table *table,
1253                             struct pivot_dimension *dim,
1254                             enum pivot_axis_type axis, size_t pos)
1255 {
1256   assert (dim->table == table);
1257
1258   struct pivot_axis *old_axis = &table->axes[dim->axis_type];
1259   struct pivot_axis *new_axis = &table->axes[axis];
1260   pos = MIN (pos, new_axis->n_dimensions);
1261
1262   if (old_axis == new_axis && pos == dim->level)
1263     {
1264       /* No change. */
1265       return;
1266     }
1267
1268
1269   /* Update the current layer, if necessary.  If we're moving within the layer
1270      axis, preserve the current layer. */
1271   if (dim->axis_type == PIVOT_AXIS_LAYER)
1272     {
1273       if (axis == PIVOT_AXIS_LAYER)
1274         {
1275           /* Rearranging the layer axis. */
1276           move_element (table->current_layer, old_axis->n_dimensions,
1277                         sizeof *table->current_layer,
1278                         dim->level, pos);
1279         }
1280       else
1281         {
1282           /* A layer is becoming a row or column. */
1283           remove_element (table->current_layer, old_axis->n_dimensions,
1284                           sizeof *table->current_layer, dim->level);
1285         }
1286     }
1287   else if (axis == PIVOT_AXIS_LAYER)
1288     {
1289       /* A row or column is becoming a layer. */
1290       table->current_layer = xrealloc (
1291         table->current_layer,
1292         (new_axis->n_dimensions + 1) * sizeof *table->current_layer);
1293       insert_element (table->current_layer, new_axis->n_dimensions,
1294                       sizeof *table->current_layer, pos);
1295       table->current_layer[pos] = 0;
1296     }
1297
1298   /* Remove DIM from its current axis. */
1299   remove_element (old_axis->dimensions, old_axis->n_dimensions,
1300                   sizeof *old_axis->dimensions, dim->level);
1301   old_axis->n_dimensions--;
1302
1303   /* Insert DIM into its new axis. */
1304   new_axis->dimensions = xrealloc (
1305     new_axis->dimensions,
1306     (new_axis->n_dimensions + 1) * sizeof *new_axis->dimensions);
1307   insert_element (new_axis->dimensions, new_axis->n_dimensions,
1308                   sizeof *new_axis->dimensions, pos);
1309   new_axis->dimensions[pos] = dim;
1310   new_axis->n_dimensions++;
1311
1312   pivot_table_update_axes (table);
1313 }
1314
1315
1316 const struct pivot_table_look *
1317 pivot_table_get_look (const struct pivot_table *table)
1318 {
1319   return table->look;
1320 }
1321
1322 void
1323 pivot_table_set_look (struct pivot_table *table,
1324                       const struct pivot_table_look *look)
1325 {
1326   pivot_table_look_unref (table->look);
1327   table->look = pivot_table_look_ref (look);
1328 }
1329
1330 /* Sets the format used for PIVOT_RC_COUNT cells to the one used for variable
1331    WV, which should be the weight variable for the dictionary whose data or
1332    statistics are being put into TABLE.
1333
1334    This has no effect if WV is NULL. */
1335 void
1336 pivot_table_set_weight_var (struct pivot_table *table,
1337                             const struct variable *wv)
1338 {
1339   if (wv)
1340     pivot_table_set_weight_format (table, var_get_print_format (wv));
1341 }
1342
1343 /* Sets the format used for PIVOT_RC_COUNT cells to WFMT, which should be the
1344    format for the dictionary whose data or statistics are being put into TABLE.
1345
1346    This has no effect if WFMT is NULL. */
1347 void
1348 pivot_table_set_weight_format (struct pivot_table *table,
1349                                const struct fmt_spec *wfmt)
1350 {
1351   if (wfmt)
1352     table->weight_format = *wfmt;
1353 }
1354
1355 /* Returns true if TABLE has no cells, false otherwise. */
1356 bool
1357 pivot_table_is_empty (const struct pivot_table *table)
1358 {
1359   return hmap_is_empty (&table->cells);
1360 }
1361
1362 static unsigned int
1363 pivot_cell_hash_indexes (const size_t *indexes, size_t n_idx)
1364 {
1365   return hash_bytes (indexes, n_idx * sizeof *indexes, 0);
1366 }
1367
1368 static bool
1369 equal_indexes (const size_t *a, const unsigned int *b, size_t n)
1370 {
1371   for (size_t i = 0; i < n; i++)
1372     if (a[i] != b[i])
1373       return false;
1374
1375   return true;
1376 }
1377
1378 static struct pivot_cell *
1379 pivot_table_lookup_cell__ (const struct pivot_table *table,
1380                             const size_t *dindexes, unsigned int hash)
1381 {
1382   struct pivot_cell *cell;
1383   HMAP_FOR_EACH_WITH_HASH (cell, struct pivot_cell, hmap_node, hash,
1384                            &table->cells)
1385     if (equal_indexes (dindexes, cell->idx, table->n_dimensions))
1386       return cell;
1387   return false;
1388 }
1389
1390 static struct pivot_cell *
1391 pivot_cell_allocate (size_t n_idx)
1392 {
1393   struct pivot_cell *cell UNUSED;
1394   return xmalloc (sizeof *cell + n_idx * sizeof *cell->idx);
1395 }
1396
1397 static struct pivot_cell *
1398 pivot_table_insert_cell (struct pivot_table *table, const size_t *dindexes)
1399 {
1400   unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1401   struct pivot_cell *cell = pivot_table_lookup_cell__ (table, dindexes, hash);
1402   if (!cell)
1403     {
1404       cell = pivot_cell_allocate (table->n_dimensions);
1405       for (size_t i = 0; i < table->n_dimensions; i++)
1406         cell->idx[i] = dindexes[i];
1407       cell->value = NULL;
1408       hmap_insert (&table->cells, &cell->hmap_node, hash);
1409     }
1410   return cell;
1411 }
1412
1413 /* Puts VALUE in the cell in TABLE whose indexes are given by the N indexes in
1414    DINDEXES.  N must be the number of dimensions in TABLE.  Takes ownership of
1415    VALUE.
1416
1417    If VALUE is a numeric value without a specified format, this function checks
1418    each of the categories designated by DINDEXES[] and takes the format from
1419    the first category with a result class.  If none has a result class, uses
1420    the overall default numeric format. */
1421 void
1422 pivot_table_put (struct pivot_table *table, const size_t *dindexes, size_t n,
1423                  struct pivot_value *value)
1424 {
1425   assert (n == table->n_dimensions);
1426   for (size_t i = 0; i < n; i++)
1427     assert (dindexes[i] < table->dimensions[i]->n_leaves);
1428
1429   if (value->type == PIVOT_VALUE_NUMERIC && !value->numeric.format.w)
1430     {
1431       for (size_t i = 0; i < table->n_dimensions; i++)
1432         {
1433           const struct pivot_dimension *d = table->dimensions[i];
1434           if (dindexes[i] < d->n_leaves)
1435             {
1436               const struct pivot_category *c = d->data_leaves[dindexes[i]];
1437               if (c->format.w)
1438                 {
1439                   value->numeric.format = c->format;
1440                   value->numeric.honor_small = c->honor_small;
1441                   goto done;
1442                 }
1443             }
1444         }
1445       value->numeric.format = *settings_get_format ();
1446       value->numeric.honor_small = true;
1447
1448     done:;
1449     }
1450
1451   struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1452   pivot_value_destroy (cell->value);
1453   cell->value = value;
1454 }
1455
1456 /* Puts VALUE in the cell in TABLE with index IDX1.  TABLE must have 1
1457    dimension.  Takes ownership of VALUE.  */
1458 void
1459 pivot_table_put1 (struct pivot_table *table, size_t idx1,
1460                   struct pivot_value *value)
1461 {
1462   size_t dindexes[] = { idx1 };
1463   pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1464 }
1465
1466 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2).  TABLE must have 2
1467    dimensions.  Takes ownership of VALUE.  */
1468 void
1469 pivot_table_put2 (struct pivot_table *table, size_t idx1, size_t idx2,
1470                   struct pivot_value *value)
1471 {
1472   size_t dindexes[] = { idx1, idx2 };
1473   pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1474 }
1475
1476 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3).  TABLE must
1477    have 3 dimensions.  Takes ownership of VALUE.  */
1478 void
1479 pivot_table_put3 (struct pivot_table *table, size_t idx1, size_t idx2,
1480                   size_t idx3, struct pivot_value *value)
1481 {
1482   size_t dindexes[] = { idx1, idx2, idx3 };
1483   pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1484 }
1485
1486 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3, IDX4).  TABLE
1487    must have 4 dimensions.  Takes ownership of VALUE.  */
1488 void
1489 pivot_table_put4 (struct pivot_table *table, size_t idx1, size_t idx2,
1490                   size_t idx3, size_t idx4, struct pivot_value *value)
1491 {
1492   size_t dindexes[] = { idx1, idx2, idx3, idx4 };
1493   pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1494 }
1495
1496 /* Creates and returns a new footnote in TABLE with the given CONTENT and an
1497    automatically assigned marker.
1498
1499    The footnote will only appear in output if it is referenced.  Use
1500    pivot_value_add_footnote() to add a reference to the footnote. */
1501 struct pivot_footnote *
1502 pivot_table_create_footnote (struct pivot_table *table,
1503                              struct pivot_value *content)
1504 {
1505   return pivot_table_create_footnote__ (table, table->n_footnotes,
1506                                         NULL, content);
1507 }
1508
1509 static struct pivot_value *
1510 pivot_make_default_footnote_marker (int idx, bool show_numeric_markers)
1511 {
1512   char text[INT_BUFSIZE_BOUND (size_t)];
1513   if (show_numeric_markers)
1514     snprintf (text, sizeof text, "%d", idx + 1);
1515   else
1516     str_format_26adic (idx + 1, false, text, sizeof text);
1517   return pivot_value_new_user_text (text, -1);
1518 }
1519
1520 /* Creates or modifies a footnote in TABLE with 0-based number IDX (and creates
1521    all lower indexes as a side effect).  If MARKER is nonnull, sets the
1522    footnote's marker; if CONTENT is nonnull, sets the footnote's content. */
1523 struct pivot_footnote *
1524 pivot_table_create_footnote__ (struct pivot_table *table, size_t idx,
1525                                struct pivot_value *marker,
1526                                struct pivot_value *content)
1527 {
1528   if (idx >= table->n_footnotes)
1529     {
1530       while (idx >= table->allocated_footnotes)
1531         table->footnotes = x2nrealloc (table->footnotes,
1532                                        &table->allocated_footnotes,
1533                                        sizeof *table->footnotes);
1534       while (idx >= table->n_footnotes)
1535         {
1536           struct pivot_footnote *f = xmalloc (sizeof *f);
1537           f->idx = table->n_footnotes;
1538           f->marker = pivot_make_default_footnote_marker (
1539             f->idx, table->look->show_numeric_markers);
1540           f->content = NULL;
1541           f->show = true;
1542
1543           table->footnotes[table->n_footnotes++] = f;
1544         }
1545     }
1546
1547   struct pivot_footnote *f = table->footnotes[idx];
1548   if (marker)
1549     {
1550       pivot_value_destroy (f->marker);
1551       f->marker = marker;
1552     }
1553   if (content)
1554     {
1555       pivot_value_destroy (f->content);
1556       f->content = content;
1557     }
1558   return f;
1559 }
1560
1561 /* Frees the data owned by F. */
1562 void
1563 pivot_footnote_destroy (struct pivot_footnote *f)
1564 {
1565   if (f)
1566     {
1567       pivot_value_destroy (f->content);
1568       pivot_value_destroy (f->marker);
1569       free (f);
1570     }
1571 }
1572
1573 /* Converts per-axis presentation-order indexes, given in PINDEXES, into data
1574    indexes for each dimension in TABLE in DINDEXES[]. */
1575 void
1576 pivot_table_convert_indexes_ptod (const struct pivot_table *table,
1577                                   const size_t *pindexes[PIVOT_N_AXES],
1578                                   size_t dindexes[/* table->n_dimensions */])
1579 {
1580   for (size_t i = 0; i < PIVOT_N_AXES; i++)
1581     {
1582       const struct pivot_axis *axis = &table->axes[i];
1583
1584       for (size_t j = 0; j < axis->n_dimensions; j++)
1585         {
1586           const struct pivot_dimension *d = axis->dimensions[j];
1587           dindexes[d->top_index]
1588             = d->presentation_leaves[pindexes[i][j]]->data_index;
1589         }
1590     }
1591 }
1592
1593 size_t *
1594 pivot_table_enumerate_axis (const struct pivot_table *table,
1595                             enum pivot_axis_type axis_type,
1596                             const size_t *layer_indexes, bool omit_empty,
1597                             size_t *n)
1598 {
1599   const struct pivot_axis *axis = &table->axes[axis_type];
1600   if (!axis->n_dimensions)
1601     {
1602       size_t *enumeration = xnmalloc (2, sizeof *enumeration);
1603       enumeration[0] = 0;
1604       enumeration[1] = SIZE_MAX;
1605       if (n)
1606         *n = 1;
1607       return enumeration;
1608     }
1609   else if (!axis->extent)
1610     {
1611       size_t *enumeration = xmalloc (sizeof *enumeration);
1612       *enumeration = SIZE_MAX;
1613       if (n)
1614         *n = 0;
1615       return enumeration;
1616     }
1617
1618   size_t *enumeration = xnmalloc (xsum (xtimes (axis->extent,
1619                                                 axis->n_dimensions), 1),
1620                                   sizeof *enumeration);
1621   size_t *p = enumeration;
1622   size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
1623
1624   size_t *axis_indexes;
1625   PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1626     {
1627       if (omit_empty)
1628         {
1629           enum pivot_axis_type axis2_type
1630             = pivot_axis_type_transpose (axis_type);
1631
1632           size_t *axis2_indexes;
1633           PIVOT_AXIS_FOR_EACH (axis2_indexes, &table->axes[axis2_type])
1634             {
1635               const size_t *pindexes[PIVOT_N_AXES];
1636               pindexes[PIVOT_AXIS_LAYER] = layer_indexes;
1637               pindexes[axis_type] = axis_indexes;
1638               pindexes[axis2_type] = axis2_indexes;
1639               pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
1640               if (pivot_table_get (table, dindexes))
1641                 goto found;
1642             }
1643           continue;
1644
1645         found:
1646           free (axis2_indexes);
1647         }
1648
1649       memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1650       p += axis->n_dimensions;
1651     }
1652   if (omit_empty && p == enumeration)
1653     {
1654       PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1655         {
1656           memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1657           p += axis->n_dimensions;
1658         }
1659     }
1660   *p = SIZE_MAX;
1661   if (n)
1662     *n = (p - enumeration) / axis->n_dimensions;
1663
1664   free (dindexes);
1665   return enumeration;
1666 }
1667
1668 static struct pivot_cell *
1669 pivot_table_lookup_cell (const struct pivot_table *table,
1670                          const size_t *dindexes)
1671 {
1672   unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1673   return pivot_table_lookup_cell__ (table, dindexes, hash);
1674 }
1675
1676 const struct pivot_value *
1677 pivot_table_get (const struct pivot_table *table, const size_t *dindexes)
1678 {
1679   const struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1680   return cell ? cell->value : NULL;
1681 }
1682
1683 struct pivot_value *
1684 pivot_table_get_rw (struct pivot_table *table, const size_t *dindexes)
1685 {
1686   struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1687   if (!cell->value)
1688     cell->value = pivot_value_new_user_text ("", -1);
1689   return cell->value;
1690 }
1691
1692 static void
1693 pivot_table_delete_cell (struct pivot_table *table, struct pivot_cell *cell)
1694 {
1695   hmap_delete (&table->cells, &cell->hmap_node);
1696   pivot_value_destroy (cell->value);
1697   free (cell);
1698 }
1699
1700 bool
1701 pivot_table_delete (struct pivot_table *table, const size_t *dindexes)
1702 {
1703   struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1704   if (cell)
1705     {
1706       pivot_table_delete_cell (table, cell);
1707       return true;
1708     }
1709   else
1710     return false;
1711 }
1712
1713 static void
1714 distribute_extra_depth (struct pivot_category *category, size_t extra_depth)
1715 {
1716   if (pivot_category_is_group (category) && category->n_subs)
1717     for (size_t i = 0; i < category->n_subs; i++)
1718       distribute_extra_depth (category->subs[i], extra_depth);
1719   else
1720     category->extra_depth += extra_depth;
1721 }
1722
1723 static void
1724 pivot_category_assign_label_depth (struct pivot_category *category,
1725                                    bool dimension_labels_in_corner)
1726 {
1727   category->extra_depth = 0;
1728
1729   if (pivot_category_is_group (category))
1730     {
1731       size_t depth = 0;
1732       for (size_t i = 0; i < category->n_subs; i++)
1733         {
1734           pivot_category_assign_label_depth (category->subs[i], false);
1735           depth = MAX (depth, category->subs[i]->label_depth);
1736         }
1737
1738       for (size_t i = 0; i < category->n_subs; i++)
1739         {
1740           struct pivot_category *sub = category->subs[i];
1741
1742           size_t extra_depth = depth - sub->label_depth;
1743           if (extra_depth)
1744             distribute_extra_depth (sub, extra_depth);
1745
1746           sub->label_depth = depth;
1747         }
1748
1749       category->show_label_in_corner = (category->show_label
1750                                         && dimension_labels_in_corner);
1751       category->label_depth
1752         = (category->show_label && !category->show_label_in_corner
1753            ? depth + 1 : depth);
1754     }
1755   else
1756     category->label_depth = 1;
1757 }
1758
1759 static bool
1760 pivot_axis_assign_label_depth (struct pivot_table *table,
1761                              enum pivot_axis_type axis_type,
1762                              bool dimension_labels_in_corner)
1763 {
1764   struct pivot_axis *axis = &table->axes[axis_type];
1765   bool any_label_shown_in_corner = false;
1766   axis->label_depth = 0;
1767   axis->extent = 1;
1768   for (size_t i = 0; i < axis->n_dimensions; i++)
1769     {
1770       struct pivot_dimension *d = axis->dimensions[i];
1771       pivot_category_assign_label_depth (d->root, dimension_labels_in_corner);
1772       d->label_depth = d->hide_all_labels ? 0 : d->root->label_depth;
1773       axis->label_depth += d->label_depth;
1774       axis->extent *= d->n_leaves;
1775
1776       if (d->root->show_label_in_corner)
1777         any_label_shown_in_corner = true;
1778     }
1779   return any_label_shown_in_corner;
1780 }
1781
1782 void
1783 pivot_table_assign_label_depth (struct pivot_table *table)
1784 {
1785   pivot_axis_assign_label_depth (table, PIVOT_AXIS_COLUMN, false);
1786   if (pivot_axis_assign_label_depth (
1787         table, PIVOT_AXIS_ROW, (table->look->row_labels_in_corner
1788                                 && !table->corner_text))
1789       && table->axes[PIVOT_AXIS_COLUMN].label_depth == 0)
1790     table->axes[PIVOT_AXIS_COLUMN].label_depth = 1;
1791   pivot_axis_assign_label_depth (table, PIVOT_AXIS_LAYER, false);
1792 }
1793 \f
1794 static void
1795 indent (int indentation)
1796 {
1797   for (int i = 0; i < indentation * 2; i++)
1798     putchar (' ');
1799 }
1800
1801 static void
1802 pivot_value_dump (const struct pivot_value *value,
1803                   const struct pivot_table *pt)
1804 {
1805   char *s = pivot_value_to_string (value, pt);
1806   fputs (s, stdout);
1807   free (s);
1808 }
1809
1810 static void
1811 pivot_table_dump_value (const struct pivot_value *value, const char *name,
1812                         const struct pivot_table *pt, int indentation)
1813 {
1814   if (value)
1815     {
1816       indent (indentation);
1817       printf ("%s: ", name);
1818       pivot_value_dump (value, pt);
1819       putchar ('\n');
1820     }
1821 }
1822
1823 static void
1824 pivot_table_dump_string (const char *string, const char *name, int indentation)
1825 {
1826   if (string)
1827     {
1828       indent (indentation);
1829       printf ("%s: %s\n", name, string);
1830     }
1831 }
1832
1833 static void
1834 pivot_category_dump (const struct pivot_category *c,
1835                      const struct pivot_table *pt, int indentation)
1836 {
1837   indent (indentation);
1838   printf ("%s \"", pivot_category_is_leaf (c) ? "leaf" : "group");
1839   pivot_value_dump (c->name, pt);
1840   printf ("\" ");
1841
1842   if (pivot_category_is_leaf (c))
1843     printf ("data_index=%zu\n", c->data_index);
1844   else
1845     {
1846       printf (" (label %s)", c->show_label ? "shown" : "hidden");
1847       printf ("\n");
1848
1849       for (size_t i = 0; i < c->n_subs; i++)
1850         pivot_category_dump (c->subs[i], pt, indentation + 1);
1851     }
1852 }
1853
1854 void
1855 pivot_dimension_dump (const struct pivot_dimension *d,
1856                       const struct pivot_table *pt, int indentation)
1857 {
1858   indent (indentation);
1859   printf ("%s dimension %zu (where 0=innermost), label_depth=%d:\n",
1860           pivot_axis_type_to_string (d->axis_type), d->level, d->label_depth);
1861
1862   pivot_category_dump (d->root, pt, indentation + 1);
1863 }
1864
1865 static void
1866 table_area_style_dump (enum pivot_area area, const struct table_area_style *a,
1867                        int indentation)
1868 {
1869   indent (indentation);
1870   printf ("%s: ", pivot_area_to_string (area));
1871   font_style_dump (&a->font_style);
1872   putchar (' ');
1873   cell_style_dump (&a->cell_style);
1874   putchar ('\n');
1875 }
1876
1877 static void
1878 table_border_style_dump (enum pivot_border border,
1879                          const struct table_border_style *b, int indentation)
1880 {
1881   indent (indentation);
1882   printf ("%s: %s ", pivot_border_to_string (border),
1883           table_stroke_to_string (b->stroke));
1884   cell_color_dump (&b->color);
1885   putchar ('\n');
1886 }
1887
1888 static char ***
1889 compose_headings (const struct pivot_table *pt,
1890                   const struct pivot_axis *axis,
1891                   const size_t *column_enumeration)
1892 {
1893   if (!axis->n_dimensions || !axis->extent || !axis->label_depth)
1894     return NULL;
1895
1896   char ***headings = xnmalloc (axis->label_depth, sizeof *headings);
1897   for (size_t i = 0; i < axis->label_depth; i++)
1898     headings[i] = xcalloc (axis->extent, sizeof **headings);
1899
1900   const size_t *indexes;
1901   size_t column = 0;
1902   PIVOT_ENUMERATION_FOR_EACH (indexes, column_enumeration, axis)
1903     {
1904       int row = axis->label_depth - 1;
1905       for (int dim_index = 0; dim_index < axis->n_dimensions; dim_index++)
1906         {
1907           const struct pivot_dimension *d = axis->dimensions[dim_index];
1908           if (d->hide_all_labels)
1909             continue;
1910           for (const struct pivot_category *c
1911                  = d->presentation_leaves[indexes[dim_index]];
1912                c;
1913                c = c->parent)
1914             {
1915               if (pivot_category_is_leaf (c) || (c->show_label
1916                                                  && !c->show_label_in_corner))
1917                 {
1918                   headings[row][column] = pivot_value_to_string (c->name, pt);
1919                   if (!*headings[row][column])
1920                     headings[row][column] = xstrdup ("<blank>");
1921                   row--;
1922                 }
1923             }
1924         }
1925       column++;
1926     }
1927
1928   return headings;
1929 }
1930
1931 static void
1932 free_headings (const struct pivot_axis *axis, char ***headings)
1933 {
1934   for (size_t i = 0; i < axis->label_depth; i++)
1935     {
1936       for (size_t j = 0; j < axis->extent; j++)
1937         free (headings[i][j]);
1938       free (headings[i]);
1939     }
1940   free (headings);
1941 }
1942
1943 static void
1944 pivot_table_sizing_dump (const char *name,
1945                          const int width_ranges[2],
1946                          const struct pivot_table_sizing *s,
1947                          int indentation)
1948 {
1949   indent (indentation);
1950   printf ("%ss: min=%d, max=%d\n", name, width_ranges[0], width_ranges[1]);
1951   if (s->n_widths)
1952     {
1953       indent (indentation + 1);
1954       printf ("%s widths:", name);
1955       for (size_t i = 0; i < s->n_widths; i++)
1956         printf (" %d", s->widths[i]);
1957       printf ("\n");
1958     }
1959   if (s->n_breaks)
1960     {
1961       indent (indentation + 1);
1962       printf ("break after %ss:", name);
1963       for (size_t i = 0; i < s->n_breaks; i++)
1964         printf (" %zu", s->breaks[i]);
1965       printf ("\n");
1966     }
1967   if (s->n_keeps)
1968     {
1969       indent (indentation + 1);
1970       printf ("keep %ss together:", name);
1971       for (size_t i = 0; i < s->n_keeps; i++)
1972         printf (" [%zu,%zu]",
1973                 s->keeps[i].ofs,
1974                 s->keeps[i].ofs + s->keeps[i].n - 1);
1975       printf ("\n");
1976     }
1977 }
1978
1979 void
1980 pivot_table_dump (const struct pivot_table *table, int indentation)
1981 {
1982   if (!table)
1983     return;
1984
1985   pivot_table_assign_label_depth (CONST_CAST (struct pivot_table *, table));
1986
1987   pivot_table_dump_value (table->title, "title", table, indentation);
1988   pivot_table_dump_value (table->subtype, "subtype", table, indentation);
1989   pivot_table_dump_string (table->command_c, "command", indentation);
1990   pivot_table_dump_string (table->dataset, "dataset", indentation);
1991   pivot_table_dump_string (table->datafile, "datafile", indentation);
1992   pivot_table_dump_string (table->notes, "notes", indentation);
1993   pivot_table_dump_string (table->look->name, "table-look", indentation);
1994   if (table->date)
1995     {
1996       indent (indentation);
1997
1998       struct tm *tm = localtime (&table->date);
1999       printf ("date: %d-%02d-%02d %d:%02d:%02d\n", tm->tm_year + 1900,
2000               tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
2001               tm->tm_sec);
2002     }
2003
2004   indent (indentation);
2005   printf ("sizing:\n");
2006   pivot_table_sizing_dump ("column", table->look->width_ranges[TABLE_HORZ],
2007                            &table->sizing[TABLE_HORZ], indentation + 1);
2008   pivot_table_sizing_dump ("row", table->look->width_ranges[TABLE_VERT],
2009                            &table->sizing[TABLE_VERT], indentation + 1);
2010
2011   indent (indentation);
2012   printf ("areas:\n");
2013   for (enum pivot_area area = 0; area < PIVOT_N_AREAS; area++)
2014     table_area_style_dump (area, &table->look->areas[area], indentation + 1);
2015
2016   indent (indentation);
2017   printf ("borders:\n");
2018   for (enum pivot_border border = 0; border < PIVOT_N_BORDERS; border++)
2019     table_border_style_dump (border, &table->look->borders[border],
2020                              indentation + 1);
2021
2022   for (size_t i = 0; i < table->n_dimensions; i++)
2023     pivot_dimension_dump (table->dimensions[i], table, indentation);
2024
2025   /* Presentation and data indexes. */
2026   size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
2027
2028   const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2029   if (layer_axis->n_dimensions)
2030     {
2031       indent (indentation);
2032       printf ("current layer:");
2033
2034       for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2035         {
2036           const struct pivot_dimension *d = layer_axis->dimensions[i];
2037           char *name = pivot_value_to_string (d->root->name, table);
2038           char *value = pivot_value_to_string (
2039             d->data_leaves[table->current_layer[i]]->name, table);
2040           printf (" %s=%s", name, value);
2041           free (value);
2042           free (name);
2043         }
2044
2045       putchar ('\n');
2046     }
2047
2048   size_t *layer_indexes;
2049   size_t layer_iteration = 0;
2050   PIVOT_AXIS_FOR_EACH (layer_indexes, &table->axes[PIVOT_AXIS_LAYER])
2051     {
2052       indent (indentation);
2053       printf ("layer %zu:", layer_iteration++);
2054
2055       const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2056       for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2057         {
2058           const struct pivot_dimension *d = layer_axis->dimensions[i];
2059
2060           fputs (i == 0 ? " " : ", ", stdout);
2061           pivot_value_dump (d->root->name, table);
2062           fputs (" =", stdout);
2063
2064           struct pivot_value **names = xnmalloc (d->n_leaves, sizeof *names);
2065           size_t n_names = 0;
2066           for (const struct pivot_category *c
2067                  = d->presentation_leaves[layer_indexes[i]];
2068                c;
2069                c = c->parent)
2070             {
2071               if (pivot_category_is_leaf (c) || c->show_label)
2072                 names[n_names++] = c->name;
2073             }
2074
2075           for (size_t i = n_names; i-- > 0;)
2076             {
2077               putchar (' ');
2078               pivot_value_dump (names[i], table);
2079             }
2080           free (names);
2081         }
2082       putchar ('\n');
2083
2084       size_t *column_enumeration = pivot_table_enumerate_axis (
2085         table, PIVOT_AXIS_COLUMN, layer_indexes, table->look->omit_empty, NULL);
2086       size_t *row_enumeration = pivot_table_enumerate_axis (
2087         table, PIVOT_AXIS_ROW, layer_indexes, table->look->omit_empty, NULL);
2088
2089       char ***column_headings = compose_headings (
2090         table, &table->axes[PIVOT_AXIS_COLUMN], column_enumeration);
2091       for (size_t y = 0; y < table->axes[PIVOT_AXIS_COLUMN].label_depth; y++)
2092         {
2093           indent (indentation + 1);
2094           for (size_t x = 0; x < table->axes[PIVOT_AXIS_COLUMN].extent; x++)
2095             {
2096               if (x)
2097                 fputs ("; ", stdout);
2098               if (column_headings[y][x])
2099                 fputs (column_headings[y][x], stdout);
2100             }
2101           putchar ('\n');
2102         }
2103       free_headings (&table->axes[PIVOT_AXIS_COLUMN], column_headings);
2104
2105       indent (indentation + 1);
2106       printf ("-----------------------------------------------\n");
2107
2108       char ***row_headings = compose_headings (
2109         table, &table->axes[PIVOT_AXIS_ROW], row_enumeration);
2110
2111       size_t x = 0;
2112       const size_t *pindexes[PIVOT_N_AXES]
2113         = { [PIVOT_AXIS_LAYER] = layer_indexes };
2114       PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
2115                                   &table->axes[PIVOT_AXIS_ROW])
2116         {
2117           indent (indentation + 1);
2118
2119           size_t i = 0;
2120           for (size_t y = 0; y < table->axes[PIVOT_AXIS_ROW].label_depth; y++)
2121             {
2122               if (i++)
2123                 fputs ("; ", stdout);
2124               if (row_headings[y][x])
2125                 fputs (row_headings[y][x], stdout);
2126             }
2127
2128           printf (" | ");
2129
2130           i = 0;
2131           PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
2132                                       column_enumeration,
2133                                       &table->axes[PIVOT_AXIS_COLUMN])
2134             {
2135               if (i++)
2136                 printf ("; ");
2137
2138               pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
2139               const struct pivot_value *value = pivot_table_get (
2140                 table, dindexes);
2141               if (value)
2142                 pivot_value_dump (value, table);
2143             }
2144           printf ("\n");
2145
2146           x++;
2147         }
2148
2149       free (column_enumeration);
2150       free (row_enumeration);
2151       free_headings (&table->axes[PIVOT_AXIS_ROW], row_headings);
2152     }
2153
2154   pivot_table_dump_value (table->caption, "caption", table, indentation);
2155
2156   for (size_t i = 0; i < table->n_footnotes; i++)
2157     {
2158       const struct pivot_footnote *f = table->footnotes[i];
2159       indent (indentation);
2160       putchar ('[');
2161       if (f->marker)
2162         pivot_value_dump (f->marker, table);
2163       else
2164         printf ("%zu", f->idx);
2165       putchar (']');
2166       pivot_value_dump (f->content, table);
2167       putchar ('\n');
2168     }
2169
2170   free (dindexes);
2171 }
2172 \f
2173 static const char *
2174 consume_int (const char *p, size_t *n)
2175 {
2176   *n = 0;
2177   while (c_isdigit (*p))
2178     *n = *n * 10 + (*p++ - '0');
2179   return p;
2180 }
2181
2182 static size_t
2183 pivot_format_inner_template (struct string *out, const char *template,
2184                              char escape,
2185                              struct pivot_value **values, size_t n_values,
2186                              const struct pivot_table *pt)
2187 {
2188   size_t args_consumed = 0;
2189   while (*template && *template != ':')
2190     {
2191       if (*template == '\\' && template[1])
2192         {
2193           ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2194           template += 2;
2195         }
2196       else if (*template == escape)
2197         {
2198           size_t index;
2199           template = consume_int (template + 1, &index);
2200           if (index >= 1 && index <= n_values)
2201             {
2202               pivot_value_format (values[index - 1], pt, out);
2203               args_consumed = MAX (args_consumed, index);
2204             }
2205         }
2206       else
2207         ds_put_byte (out, *template++);
2208     }
2209   return args_consumed;
2210 }
2211
2212 static const char *
2213 pivot_extract_inner_template (const char *template, const char **p)
2214 {
2215   *p = template;
2216
2217   for (;;)
2218     {
2219       if (*template == '\\' && template[1] != '\0')
2220         template += 2;
2221       else if (*template == ':')
2222         return template + 1;
2223       else if (*template == '\0')
2224         return template;
2225       else
2226         template++;
2227     }
2228 }
2229
2230 static void
2231 pivot_format_template (struct string *out, const char *template,
2232                        const struct pivot_argument *args, size_t n_args,
2233                        const struct pivot_table *pt)
2234 {
2235   while (*template)
2236     {
2237       if (*template == '\\' && template[1] != '\0')
2238         {
2239           ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2240           template += 2;
2241         }
2242       else if (*template == '^')
2243         {
2244           size_t index;
2245           template = consume_int (template + 1, &index);
2246           if (index >= 1 && index <= n_args && args[index - 1].n > 0)
2247             pivot_value_format (args[index - 1].values[0], pt, out);
2248         }
2249       else if (*template == '[')
2250         {
2251           const char *tmpl[2];
2252           template = pivot_extract_inner_template (template + 1, &tmpl[0]);
2253           template = pivot_extract_inner_template (template, &tmpl[1]);
2254           template += *template == ']';
2255
2256           size_t index;
2257           template = consume_int (template, &index);
2258           if (index < 1 || index > n_args)
2259             continue;
2260
2261           const struct pivot_argument *arg = &args[index - 1];
2262           size_t left = arg->n;
2263           while (left)
2264             {
2265               struct pivot_value **values = arg->values + (arg->n - left);
2266               int tmpl_idx = left == arg->n && *tmpl[0] != ':' ? 0 : 1;
2267               char escape = "%^"[tmpl_idx];
2268               size_t used = pivot_format_inner_template (
2269                 out, tmpl[tmpl_idx], escape, values, left, pt);
2270               if (!used || used > left)
2271                 break;
2272               left -= used;
2273             }
2274         }
2275       else
2276         ds_put_byte (out, *template++);
2277     }
2278 }
2279
2280 static enum settings_value_show
2281 interpret_show (enum settings_value_show global_show,
2282                 enum settings_value_show table_show,
2283                 enum settings_value_show value_show,
2284                 bool has_label)
2285 {
2286   return (!has_label ? SETTINGS_VALUE_SHOW_VALUE
2287           : value_show != SETTINGS_VALUE_SHOW_DEFAULT ? value_show
2288           : table_show != SETTINGS_VALUE_SHOW_DEFAULT ? table_show
2289           : global_show);
2290 }
2291
2292 /* Appends a text representation of the body of VALUE to OUT.  Settings on
2293    PT control whether variable and value labels are included.
2294
2295    The "body" omits subscripts and superscripts and footnotes.
2296
2297    Returns true if OUT is a number (or a number plus a value label), false
2298    otherwise.  */
2299 bool
2300 pivot_value_format_body (const struct pivot_value *value,
2301                          const struct pivot_table *pt,
2302                          struct string *out)
2303 {
2304   enum settings_value_show show;
2305   bool numeric = false;
2306
2307   switch (value->type)
2308     {
2309     case PIVOT_VALUE_NUMERIC:
2310       show = interpret_show (settings_get_show_values (),
2311                              pt->show_values,
2312                              value->numeric.show,
2313                              value->numeric.value_label != NULL);
2314       if (show & SETTINGS_VALUE_SHOW_VALUE)
2315         {
2316           const struct fmt_spec *f = &value->numeric.format;
2317           const struct fmt_spec *format
2318             = (f->type == FMT_F
2319                && value->numeric.honor_small
2320                && value->numeric.x != 0
2321                && fabs (value->numeric.x) < pt->small
2322                ? &(struct fmt_spec) { .type = FMT_E, .w = 40, .d = f->d }
2323                : f);
2324
2325           char *s = data_out (&(union value) { .f = value->numeric.x },
2326                               "UTF-8", format, &pt->settings);
2327           ds_put_cstr (out, s + strspn (s, " "));
2328           free (s);
2329         }
2330       if (show & SETTINGS_VALUE_SHOW_LABEL)
2331         {
2332           if (show & SETTINGS_VALUE_SHOW_VALUE)
2333             ds_put_byte (out, ' ');
2334           ds_put_cstr (out, value->numeric.value_label);
2335         }
2336       numeric = !(show & SETTINGS_VALUE_SHOW_LABEL);
2337       break;
2338
2339     case PIVOT_VALUE_STRING:
2340       show = interpret_show (settings_get_show_values (),
2341                              pt->show_values,
2342                              value->string.show,
2343                              value->string.value_label != NULL);
2344       if (show & SETTINGS_VALUE_SHOW_VALUE)
2345         {
2346           if (value->string.hex)
2347             {
2348               for (const uint8_t *p = CHAR_CAST (uint8_t *, value->string.s);
2349                    *p; p++)
2350                 ds_put_format (out, "%02X", *p);
2351             }
2352           else
2353             ds_put_cstr (out, value->string.s);
2354         }
2355       if (show & SETTINGS_VALUE_SHOW_LABEL)
2356         {
2357           if (show & SETTINGS_VALUE_SHOW_VALUE)
2358             ds_put_byte (out, ' ');
2359           ds_put_cstr (out, value->string.value_label);
2360         }
2361       break;
2362
2363     case PIVOT_VALUE_VARIABLE:
2364       show = interpret_show (settings_get_show_variables (),
2365                              pt->show_variables,
2366                              value->variable.show,
2367                              value->variable.var_label != NULL);
2368       if (show & SETTINGS_VALUE_SHOW_VALUE)
2369         ds_put_cstr (out, value->variable.var_name);
2370       if (show & SETTINGS_VALUE_SHOW_LABEL)
2371         {
2372           if (show & SETTINGS_VALUE_SHOW_VALUE)
2373             ds_put_byte (out, ' ');
2374           ds_put_cstr (out, value->variable.var_label);
2375         }
2376       break;
2377
2378     case PIVOT_VALUE_TEXT:
2379       ds_put_cstr (out, value->text.local);
2380       break;
2381
2382     case PIVOT_VALUE_TEMPLATE:
2383       pivot_format_template (out, value->template.local, value->template.args,
2384                              value->template.n_args, pt);
2385       break;
2386     }
2387
2388   return numeric;
2389 }
2390
2391 /* Appends a text representation of VALUE to OUT.  Settings on
2392    PT control whether variable and value labels are included.
2393
2394    Subscripts and footnotes are included. */
2395 void
2396 pivot_value_format (const struct pivot_value *value,
2397                     const struct pivot_table *pt,
2398                     struct string *out)
2399 {
2400   pivot_value_format_body (value, pt, out);
2401
2402   if (value->n_subscripts)
2403     {
2404       for (size_t i = 0; i < value->n_subscripts; i++)
2405         ds_put_format (out, "%c%s", i ? ',' : '_', value->subscripts[i]);
2406     }
2407
2408   for (size_t i = 0; i < value->n_footnotes; i++)
2409     {
2410       ds_put_byte (out, '[');
2411
2412       size_t idx = value->footnote_indexes[i];
2413       const struct pivot_footnote *f = pt->footnotes[idx];
2414       pivot_value_format (f->marker, pt, out);
2415
2416       ds_put_byte (out, ']');
2417     }
2418 }
2419
2420 /* Returns a text representation of VALUE.  The caller must free the string,
2421    with free(). */
2422 char *
2423 pivot_value_to_string (const struct pivot_value *value,
2424                        const struct pivot_table *pt)
2425 {
2426   struct string s = DS_EMPTY_INITIALIZER;
2427   pivot_value_format (value, pt, &s);
2428   return ds_steal_cstr (&s);
2429 }
2430
2431 char *
2432 pivot_value_to_string_defaults (const struct pivot_value *value)
2433 {
2434   static const struct pivot_table pt = {
2435     .show_values = SETTINGS_VALUE_SHOW_DEFAULT,
2436     .show_variables = SETTINGS_VALUE_SHOW_DEFAULT,
2437   };
2438   return pivot_value_to_string (value, &pt);
2439 }
2440
2441 struct pivot_value *
2442 pivot_value_clone (const struct pivot_value *old)
2443 {
2444   if (!old)
2445     return NULL;
2446
2447   struct pivot_value *new = xmemdup (old, sizeof *new);
2448   if (old->font_style)
2449     {
2450       new->font_style = xmalloc (sizeof *new->font_style);
2451       font_style_copy (NULL, new->font_style, old->font_style);
2452     }
2453   if (old->cell_style)
2454     new->cell_style = xmemdup (old->cell_style, sizeof *new->cell_style);
2455   if (old->n_subscripts)
2456     {
2457       new->subscripts = xnmalloc (old->n_subscripts, sizeof *new->subscripts);
2458       for (size_t i = 0; i < old->n_subscripts; i++)
2459         new->subscripts[i] = xstrdup (old->subscripts[i]);
2460     }
2461   if (old->n_footnotes)
2462     new->footnote_indexes = xmemdup (
2463       old->footnote_indexes, old->n_footnotes * sizeof *new->footnote_indexes);
2464
2465   switch (new->type)
2466     {
2467     case PIVOT_VALUE_NUMERIC:
2468       new->numeric.var_name = xstrdup_if_nonnull (new->numeric.var_name);
2469       new->numeric.value_label = xstrdup_if_nonnull (new->numeric.value_label);
2470       break;
2471
2472     case PIVOT_VALUE_STRING:
2473       new->string.s = xstrdup (new->string.s);
2474       new->string.var_name = xstrdup_if_nonnull (new->string.var_name);
2475       new->string.value_label = xstrdup_if_nonnull (new->string.value_label);
2476       break;
2477
2478     case PIVOT_VALUE_VARIABLE:
2479       new->variable.var_name = xstrdup_if_nonnull (new->variable.var_name);
2480       new->variable.var_label = xstrdup_if_nonnull (new->variable.var_label);
2481       break;
2482
2483     case PIVOT_VALUE_TEXT:
2484       new->text.local = xstrdup (old->text.local);
2485       new->text.c = (old->text.c == old->text.local ? new->text.local
2486                      : xstrdup (old->text.c));
2487       new->text.id = (old->text.id == old->text.local ? new->text.local
2488                       : old->text.id == old->text.c ? new->text.c
2489                       : xstrdup (old->text.id));
2490       break;
2491
2492     case PIVOT_VALUE_TEMPLATE:
2493       new->template.local = xstrdup (old->template.local);
2494       new->template.id = (old->template.id == old->template.local
2495                           ? new->template.local
2496                           : xstrdup (old->template.id));
2497       new->template.args = xmalloc (new->template.n_args
2498                                     * sizeof *new->template.args);
2499       for (size_t i = 0; i < old->template.n_args; i++)
2500         pivot_argument_copy (&new->template.args[i],
2501                              &old->template.args[i]);
2502       break;
2503
2504     default:
2505       NOT_REACHED ();
2506     }
2507   return new;
2508 }
2509
2510 /* Frees the data owned by V. */
2511 void
2512 pivot_value_destroy (struct pivot_value *value)
2513 {
2514   if (value)
2515     {
2516       font_style_uninit (value->font_style);
2517       free (value->font_style);
2518       free (value->cell_style);
2519       free (value->footnote_indexes);
2520
2521       for (size_t i = 0; i < value->n_subscripts; i++)
2522         free (value->subscripts[i]);
2523       free (value->subscripts);
2524
2525       switch (value->type)
2526         {
2527         case PIVOT_VALUE_NUMERIC:
2528           free (value->numeric.var_name);
2529           free (value->numeric.value_label);
2530           break;
2531
2532         case PIVOT_VALUE_STRING:
2533           free (value->string.s);
2534           free (value->string.var_name);
2535           free (value->string.value_label);
2536           break;
2537
2538         case PIVOT_VALUE_VARIABLE:
2539           free (value->variable.var_name);
2540           free (value->variable.var_label);
2541           break;
2542
2543         case PIVOT_VALUE_TEXT:
2544           free (value->text.local);
2545           if (value->text.c != value->text.local)
2546             free (value->text.c);
2547           if (value->text.id != value->text.local
2548               && value->text.id != value->text.c)
2549             free (value->text.id);
2550           break;
2551
2552         case PIVOT_VALUE_TEMPLATE:
2553           free (value->template.local);
2554           if (value->template.id != value->template.local)
2555             free (value->template.id);
2556           for (size_t i = 0; i < value->template.n_args; i++)
2557             pivot_argument_uninit (&value->template.args[i]);
2558           free (value->template.args);
2559           break;
2560
2561         default:
2562           NOT_REACHED ();
2563         }
2564       free (value);
2565     }
2566 }
2567
2568 /* Sets AREA to the style to use for VALUE, with defaults coming from
2569    DEFAULT_STYLE for the parts of the style that VALUE doesn't override. */
2570 void
2571 pivot_value_get_style (struct pivot_value *value,
2572                        const struct font_style *base_font_style,
2573                        const struct cell_style *base_cell_style,
2574                        struct table_area_style *area)
2575 {
2576   font_style_copy (NULL, &area->font_style, (value->font_style
2577                                              ? value->font_style
2578                                              : base_font_style));
2579   area->cell_style = *(value->cell_style
2580                        ? value->cell_style
2581                        : base_cell_style);
2582 }
2583
2584 /* Copies AREA into VALUE's style. */
2585 void
2586 pivot_value_set_style (struct pivot_value *value,
2587                        const struct table_area_style *area)
2588 {
2589   pivot_value_set_font_style (value, &area->font_style);
2590   pivot_value_set_cell_style (value, &area->cell_style);
2591 }
2592
2593 void
2594 pivot_value_set_font_style (struct pivot_value *value,
2595                             const struct font_style *font_style)
2596 {
2597   if (value->font_style)
2598     font_style_uninit (value->font_style);
2599   else
2600     value->font_style = xmalloc (sizeof *value->font_style);
2601   font_style_copy (NULL, value->font_style, font_style);
2602 }
2603
2604 void
2605 pivot_value_set_cell_style (struct pivot_value *value,
2606                             const struct cell_style *cell_style)
2607 {
2608   if (!value->cell_style)
2609     value->cell_style = xmalloc (sizeof *value->cell_style);
2610   *value->cell_style = *cell_style;
2611 }
2612
2613 void
2614 pivot_argument_copy (struct pivot_argument *dst,
2615                      const struct pivot_argument *src)
2616 {
2617   *dst = (struct pivot_argument) {
2618     .n = src->n,
2619     .values = xmalloc (src->n * sizeof *dst->values),
2620   };
2621
2622   for (size_t i = 0; i < src->n; i++)
2623     dst->values[i] = pivot_value_clone (src->values[i]);
2624 }
2625
2626 /* Frees the data owned by ARG (but not ARG itself). */
2627 void
2628 pivot_argument_uninit (struct pivot_argument *arg)
2629 {
2630   if (arg)
2631     {
2632       for (size_t i = 0; i < arg->n; i++)
2633         pivot_value_destroy (arg->values[i]);
2634       free (arg->values);
2635     }
2636 }
2637
2638 /* Creates and returns a new pivot_value whose contents is the null-terminated
2639    string TEXT.  Takes ownership of TEXT.
2640
2641    This function is for text strings provided by the user (with the exception
2642    that pivot_value_new_variable() should be used for variable names).  For
2643    strings that are part of the PSPP user interface, such as names of
2644    procedures, statistics, annotations, error messages, etc., use
2645    pivot_value_new_text(). */
2646 struct pivot_value *
2647 pivot_value_new_user_text_nocopy (char *text)
2648 {
2649   struct pivot_value *value = xmalloc (sizeof *value);
2650   *value = (struct pivot_value) {
2651     .type = PIVOT_VALUE_TEXT,
2652     .text = {
2653       .local = text,
2654       .c = text,
2655       .id = text,
2656       .user_provided = true,
2657     }
2658   };
2659   return value;
2660 }
2661
2662 /* Creates and returns a new pivot_value whose contents is the LENGTH bytes of
2663    TEXT.  Use SIZE_MAX if TEXT is null-teriminated and its length is not known
2664    in advance.
2665
2666    This function is for text strings provided by the user (with the exception
2667    that pivot_value_new_variable() should be used for variable names).  For
2668    strings that are part of the PSPP user interface, such as names of
2669    procedures, statistics, annotations, error messages, etc., use
2670    pivot_value_new_text().j
2671
2672    The caller retains ownership of TEXT.*/
2673 struct pivot_value *
2674 pivot_value_new_user_text (const char *text, size_t length)
2675 {
2676   return pivot_value_new_user_text_nocopy (
2677     xmemdup0 (text, length != SIZE_MAX ? length : strlen (text)));
2678 }
2679
2680 /* Creates and returns new pivot_value whose contents is TEXT, which should be
2681    a translatable string, but not actually translated yet, e.g. enclosed in
2682    N_().  This function is for text strings that are part of the PSPP user
2683    interface, such as names of procedures, statistics, annotations, error
2684    messages, etc.  For strings that come from the user, use
2685    pivot_value_new_user_text(). */
2686 struct pivot_value *
2687 pivot_value_new_text (const char *text)
2688 {
2689   char *c = xstrdup (text);
2690   char *local = xstrdup (gettext (c));
2691
2692   struct pivot_value *value = xmalloc (sizeof *value);
2693   *value = (struct pivot_value) {
2694     .type = PIVOT_VALUE_TEXT,
2695     .text = {
2696       .local = local,
2697       .c = c,
2698       .id = c,
2699       .user_provided = false,
2700     }
2701   };
2702   return value;
2703 }
2704
2705 /* Same as pivot_value_new_text() but its argument is a printf()-like format
2706    string. */
2707 struct pivot_value * PRINTF_FORMAT (1, 2)
2708 pivot_value_new_text_format (const char *format, ...)
2709 {
2710   va_list args;
2711   va_start (args, format);
2712   char *c = xvasprintf (format, args);
2713   va_end (args);
2714
2715   va_start (args, format);
2716   char *local = xvasprintf (gettext (format), args);
2717   va_end (args);
2718
2719   struct pivot_value *value = xmalloc (sizeof *value);
2720   *value = (struct pivot_value) {
2721     .type = PIVOT_VALUE_TEXT,
2722     .text = {
2723       .local = local,
2724       .c = c,
2725       .id = xstrdup (c),
2726       .user_provided = false,
2727     }
2728   };
2729   return value;
2730 }
2731
2732 /* Returns a new pivot_value that represents X.
2733
2734    The format to use for X is unspecified.  Usually the easiest way to specify
2735    a format is through assigning a result class to one of the categories that
2736    the pivot_value will end up in.  If that is not suitable, then the caller
2737    can use pivot_value_set_rc() or assign directly to value->numeric.format. */
2738 struct pivot_value *
2739 pivot_value_new_number (double x)
2740 {
2741   struct pivot_value *value = xmalloc (sizeof *value);
2742   *value = (struct pivot_value) {
2743     .type = PIVOT_VALUE_NUMERIC,
2744     .numeric = { .x = x, },
2745   };
2746   return value;
2747 }
2748
2749 /* Returns a new pivot_value that represents X, formatted as an integer. */
2750 struct pivot_value *
2751 pivot_value_new_integer (double x)
2752 {
2753   struct pivot_value *value = pivot_value_new_number (x);
2754   value->numeric.format = (struct fmt_spec) { FMT_F, 40, 0 };
2755   return value;
2756 }
2757
2758 /* Returns a new pivot_value that represents VALUE, formatted as for
2759    VARIABLE. */
2760 struct pivot_value *
2761 pivot_value_new_var_value (const struct variable *variable,
2762                            const union value *value)
2763 {
2764   struct pivot_value *pv = pivot_value_new_value (
2765     value, var_get_width (variable), var_get_print_format (variable),
2766     var_get_encoding (variable));
2767
2768   char *var_name = xstrdup (var_get_name (variable));
2769   if (var_is_alpha (variable))
2770     pv->string.var_name = var_name;
2771   else
2772     pv->numeric.var_name = var_name;
2773
2774   const char *label = var_lookup_value_label (variable, value);
2775   if (label)
2776     {
2777       if (var_is_alpha (variable))
2778         pv->string.value_label = xstrdup (label);
2779       else
2780         pv->numeric.value_label = xstrdup (label);
2781     }
2782
2783   return pv;
2784 }
2785
2786 /* Returns a new pivot_value that represents VALUE, with the given WIDTH,
2787    formatted with FORMAT.  For a string value, ENCODING must be its character
2788    encoding. */
2789 struct pivot_value *
2790 pivot_value_new_value (const union value *value, int width,
2791                        const struct fmt_spec *format, const char *encoding)
2792 {
2793   struct pivot_value *pv = xzalloc (sizeof *pv);
2794   if (width > 0)
2795     {
2796       char *s = recode_string (UTF8, encoding, CHAR_CAST (char *, value->s),
2797                                width);
2798       size_t n = strlen (s);
2799       while (n > 0 && s[n - 1] == ' ')
2800         s[--n] = '\0';
2801
2802       pv->type = PIVOT_VALUE_STRING;
2803       pv->string.s = s;
2804       pv->string.hex = format->type == FMT_AHEX;
2805     }
2806   else
2807     {
2808       pv->type = PIVOT_VALUE_NUMERIC;
2809       pv->numeric.x = value->f;
2810       pv->numeric.format = *format;
2811     }
2812
2813   return pv;
2814 }
2815
2816 /* Returns a new pivot_value for VARIABLE. */
2817 struct pivot_value *
2818 pivot_value_new_variable (const struct variable *variable)
2819 {
2820   struct pivot_value *value = xmalloc (sizeof *value);
2821   *value = (struct pivot_value) {
2822     .type = PIVOT_VALUE_VARIABLE,
2823     .variable = {
2824       .var_name = xstrdup (var_get_name (variable)),
2825       .var_label = xstrdup_if_nonempty (var_get_label (variable)),
2826     },
2827   };
2828   return value;
2829 }
2830
2831 /* Attaches a reference to FOOTNOTE to V. */
2832 void
2833 pivot_value_add_footnote (struct pivot_value *v,
2834                           const struct pivot_footnote *footnote)
2835 {
2836   /* Some legacy tables include numerous duplicate footnotes.  Suppress
2837      them. */
2838   for (size_t i = 0; i < v->n_footnotes; i++)
2839     if (v->footnote_indexes[i] == footnote->idx)
2840       return;
2841
2842   v->footnote_indexes = xrealloc (
2843     v->footnote_indexes, (v->n_footnotes + 1) * sizeof *v->footnote_indexes);
2844   v->footnote_indexes[v->n_footnotes++] = footnote->idx;
2845 }
2846
2847 /* If VALUE is a numeric value, and RC is a result class such as
2848    PIVOT_RC_COUNT, changes VALUE's format to the result class's. */
2849 void
2850 pivot_value_set_rc (const struct pivot_table *table, struct pivot_value *value,
2851                     const char *rc)
2852 {
2853   if (value->type == PIVOT_VALUE_NUMERIC)
2854     pivot_table_use_rc (table, rc,
2855                         &value->numeric.format, &value->numeric.honor_small);
2856 }
2857