1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2017, 2018 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "output/pivot-table.h"
21 #include <libxml/parser.h>
22 #include <libxml/tree.h>
26 #include "data/data-out.h"
27 #include "data/dictionary.h"
28 #include "data/settings.h"
29 #include "data/value.h"
30 #include "data/variable.h"
31 #include "data/file-name.h"
32 #include "libpspp/array.h"
33 #include "libpspp/assertion.h"
34 #include "libpspp/hash-functions.h"
35 #include "libpspp/i18n.h"
36 #include "output/driver.h"
37 #include "output/spv/spv-table-look.h"
39 #include "gl/c-ctype.h"
40 #include "gl/configmake.h"
41 #include "gl/intprops.h"
42 #include "gl/minmax.h"
43 #include "gl/relocatable.h"
44 #include "gl/xalloc.h"
45 #include "gl/xmemdup0.h"
49 #define _(msgid) gettext (msgid)
50 #define N_(msgid) msgid
52 static void pivot_table_use_rc (const struct pivot_table *, const char *s,
53 struct fmt_spec *, bool *honor_small);
55 /* Pivot table display styling. */
57 /* Returns the name of AREA. */
59 pivot_area_to_string (enum pivot_area area)
63 case PIVOT_AREA_TITLE: return "title";
64 case PIVOT_AREA_CAPTION: return "caption";
65 case PIVOT_AREA_FOOTER: return "footer";
66 case PIVOT_AREA_CORNER: return "corner";
67 case PIVOT_AREA_COLUMN_LABELS: return "column labels";
68 case PIVOT_AREA_ROW_LABELS: return "row labels";
69 case PIVOT_AREA_DATA: return "data";
70 case PIVOT_AREA_LAYERS: return "layers";
71 case PIVOT_N_AREAS: default: return "**error**";
75 /* Returns the name of BORDER. */
77 pivot_border_to_string (enum pivot_border border)
81 case PIVOT_BORDER_TITLE:
84 case PIVOT_BORDER_OUTER_LEFT:
85 return "left outer frame";
86 case PIVOT_BORDER_OUTER_TOP:
87 return "top outer frame";
88 case PIVOT_BORDER_OUTER_RIGHT:
89 return "right outer frame";
90 case PIVOT_BORDER_OUTER_BOTTOM:
91 return "bottom outer frame";
93 case PIVOT_BORDER_INNER_LEFT:
94 return "left inner frame";
95 case PIVOT_BORDER_INNER_TOP:
96 return "top inner frame";
97 case PIVOT_BORDER_INNER_RIGHT:
98 return "right inner frame";
99 case PIVOT_BORDER_INNER_BOTTOM:
100 return "bottom inner frame";
102 case PIVOT_BORDER_DATA_LEFT:
103 return "data area left";
104 case PIVOT_BORDER_DATA_TOP:
105 return "data area top";
107 case PIVOT_BORDER_DIM_ROW_HORZ:
108 return "row label horizontal dimension border";
109 case PIVOT_BORDER_DIM_ROW_VERT:
110 return "row label vertical dimension border";
111 case PIVOT_BORDER_DIM_COL_HORZ:
112 return "column label horizontal dimension border";
113 case PIVOT_BORDER_DIM_COL_VERT:
114 return "column label vertical dimension border";
116 case PIVOT_BORDER_CAT_ROW_HORZ:
117 return "row label horizontal category border";
118 case PIVOT_BORDER_CAT_ROW_VERT:
119 return "row label vertical category border";
120 case PIVOT_BORDER_CAT_COL_HORZ:
121 return "column label horizontal category border";
122 case PIVOT_BORDER_CAT_COL_VERT:
123 return "column label vertical category border";
125 case PIVOT_N_BORDERS:
132 pivot_table_sizing_uninit (struct pivot_table_sizing *sizing)
136 free (sizing->widths);
137 free (sizing->breaks);
138 free (sizing->keeps);
142 /* Pivot table looks. */
144 static const struct pivot_table_look *
145 default_look (const struct pivot_table_look *new)
147 static struct pivot_table_look *look;
150 pivot_table_look_unref (look);
151 look = pivot_table_look_ref (new);
155 char *error = pivot_table_look_read ("default.stt", &look);
159 look = pivot_table_look_ref (pivot_table_look_builtin_default ());
165 const struct pivot_table_look *
166 pivot_table_look_get_default (void)
168 return default_look (NULL);
172 pivot_table_look_set_default (const struct pivot_table_look *look)
177 char * WARN_UNUSED_RESULT
178 pivot_table_look_read (const char *name, struct pivot_table_look **lookp)
182 /* Construct search path. */
186 const char *home = getenv ("HOME");
187 char *allocated = NULL;
189 path[n++] = allocated = xasprintf ("%s/.pspp/looks", home);
191 path[n++] = relocate2 (PKGDATADIR "/looks", &allocated2);
195 char *file = fn_search_path (name, (char **) path);
198 char *name2 = xasprintf ("%s.stt", name);
199 file = fn_search_path (name2, (char **) path);
205 return xasprintf ("%s: not found", name);
208 char *error = spv_table_look_read (file, lookp);
213 const struct pivot_table_look *
214 pivot_table_look_builtin_default (void)
216 static struct pivot_table_look look = {
220 .row_labels_in_corner = true,
221 .col_heading_width_range = { 36, 72 },
222 .row_heading_width_range = { 36, 120 },
225 #define AREA(BOLD, H, V, L, R, T, B) { \
227 .halign = TABLE_HALIGN_##H, \
228 .valign = TABLE_VALIGN_##V, \
229 .margin = { [TABLE_HORZ][0] = L, [TABLE_HORZ][1] = R, \
230 [TABLE_VERT][0] = T, [TABLE_VERT][1] = B }, \
234 .fg = { [0] = CELL_COLOR_BLACK, [1] = CELL_COLOR_BLACK}, \
235 .bg = { [0] = CELL_COLOR_WHITE, [1] = CELL_COLOR_WHITE}, \
237 .typeface = (char *) "Sans Serif", \
240 [PIVOT_AREA_TITLE] = AREA(true, CENTER, CENTER, 8,11,1,8),
241 [PIVOT_AREA_CAPTION] = AREA(false, LEFT, TOP, 8,11,1,1),
242 [PIVOT_AREA_FOOTER] = AREA(false, LEFT, TOP, 11, 8,2,3),
243 [PIVOT_AREA_CORNER] = AREA(false, LEFT, BOTTOM, 8,11,1,1),
244 [PIVOT_AREA_COLUMN_LABELS] = AREA(false, CENTER, BOTTOM, 8,11,1,3),
245 [PIVOT_AREA_ROW_LABELS] = AREA(false, LEFT, TOP, 8,11,1,3),
246 [PIVOT_AREA_DATA] = AREA(false, MIXED, TOP, 8,11,1,1),
247 [PIVOT_AREA_LAYERS] = AREA(false, LEFT, BOTTOM, 8,11,1,3),
252 #define BORDER(STROKE) { .stroke = STROKE, .color = CELL_COLOR_BLACK }
253 [PIVOT_BORDER_TITLE] = BORDER(TABLE_STROKE_NONE),
254 [PIVOT_BORDER_OUTER_LEFT] = BORDER(TABLE_STROKE_NONE),
255 [PIVOT_BORDER_OUTER_TOP] = BORDER(TABLE_STROKE_NONE),
256 [PIVOT_BORDER_OUTER_RIGHT] = BORDER(TABLE_STROKE_NONE),
257 [PIVOT_BORDER_OUTER_BOTTOM] = BORDER(TABLE_STROKE_NONE),
258 [PIVOT_BORDER_INNER_LEFT] = BORDER(TABLE_STROKE_THICK),
259 [PIVOT_BORDER_INNER_TOP] = BORDER(TABLE_STROKE_THICK),
260 [PIVOT_BORDER_INNER_RIGHT] = BORDER(TABLE_STROKE_THICK),
261 [PIVOT_BORDER_INNER_BOTTOM] = BORDER(TABLE_STROKE_THICK),
262 [PIVOT_BORDER_DATA_LEFT] = BORDER(TABLE_STROKE_THICK),
263 [PIVOT_BORDER_DATA_TOP] = BORDER(TABLE_STROKE_THICK),
264 [PIVOT_BORDER_DIM_ROW_HORZ] = BORDER(TABLE_STROKE_SOLID),
265 [PIVOT_BORDER_DIM_ROW_VERT] = BORDER(TABLE_STROKE_NONE),
266 [PIVOT_BORDER_DIM_COL_HORZ] = BORDER(TABLE_STROKE_SOLID),
267 [PIVOT_BORDER_DIM_COL_VERT] = BORDER(TABLE_STROKE_SOLID),
268 [PIVOT_BORDER_CAT_ROW_HORZ] = BORDER(TABLE_STROKE_NONE),
269 [PIVOT_BORDER_CAT_ROW_VERT] = BORDER(TABLE_STROKE_NONE),
270 [PIVOT_BORDER_CAT_COL_HORZ] = BORDER(TABLE_STROKE_SOLID),
271 [PIVOT_BORDER_CAT_COL_VERT] = BORDER(TABLE_STROKE_SOLID),
278 struct pivot_table_look *
279 pivot_table_look_new_builtin_default (void)
281 return pivot_table_look_unshare (
282 pivot_table_look_ref (pivot_table_look_builtin_default ()));
285 struct pivot_table_look *
286 pivot_table_look_ref (const struct pivot_table_look *look_)
288 assert (look_->ref_cnt > 0);
290 struct pivot_table_look *look = CONST_CAST (struct pivot_table_look *, look_);
296 xstrdup_if_nonempty (const char *s)
298 return s && s[0] ? xstrdup (s) : NULL;
301 struct pivot_table_look *
302 pivot_table_look_unshare (struct pivot_table_look *old)
304 assert (old->ref_cnt > 0);
305 if (old->ref_cnt == 1)
308 pivot_table_look_unref (old);
310 struct pivot_table_look *new = xmemdup (old, sizeof *old);
312 new->name = xstrdup_if_nonempty (old->name);
313 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
314 table_area_style_copy (NULL, &new->areas[i], &old->areas[i]);
315 new->continuation = xstrdup_if_nonempty (old->continuation);
321 pivot_table_look_unref (struct pivot_table_look *look)
325 assert (look->ref_cnt > 0);
326 if (!--look->ref_cnt)
329 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
330 table_area_style_uninit (&look->areas[i]);
331 free (look->continuation);
339 /* Returns the name of AXIS_TYPE. */
341 pivot_axis_type_to_string (enum pivot_axis_type axis_type)
345 case PIVOT_AXIS_LAYER:
351 case PIVOT_AXIS_COLUMN:
359 static enum pivot_axis_type
360 pivot_axis_type_transpose (enum pivot_axis_type axis_type)
362 assert (axis_type == PIVOT_AXIS_ROW || axis_type == PIVOT_AXIS_COLUMN);
363 return (axis_type == PIVOT_AXIS_ROW ? PIVOT_AXIS_COLUMN : PIVOT_AXIS_ROW);
366 /* Implementation of PIVOT_AXIS_FOR_EACH. */
368 pivot_axis_iterator_next (size_t *indexes, const struct pivot_axis *axis)
372 if (axis->n_dimensions)
373 for (size_t i = 0; i < axis->n_dimensions; i++)
374 if (axis->dimensions[i]->n_leaves == 0)
377 size_t size = axis->n_dimensions * sizeof *indexes;
378 return xzalloc (MAX (size, 1));
381 for (size_t i = 0; i < axis->n_dimensions; i++)
383 const struct pivot_dimension *d = axis->dimensions[i];
384 if (++indexes[i] < d->n_leaves)
397 pivot_category_set_rc (struct pivot_category *category, const char *s)
402 pivot_table_use_rc (category->dimension->table, s,
403 &category->format, &category->honor_small);
405 /* Ensure that the category itself, in addition to the cells within it, takes
406 the format. (It's kind of rare for a category to have a numeric format
408 struct pivot_value *name = category->name;
409 if (name->type == PIVOT_VALUE_NUMERIC && !name->numeric.format.w)
410 pivot_table_use_rc (category->dimension->table, s,
411 &name->numeric.format, &name->numeric.honor_small);
415 pivot_category_create_leaves_valist (struct pivot_category *parent,
419 while ((s = va_arg (args, const char *)))
421 if (!strncmp (s, "RC_", 3))
423 assert (parent->n_subs);
424 pivot_category_set_rc (parent->subs[parent->n_subs - 1], s);
427 pivot_category_create_leaf (parent, pivot_value_new_text (s));
431 /* Creates a new dimension with the given NAME in TABLE and returns it. The
432 dimension is added to axis AXIS_TYPE, becoming the outermost dimension on
435 NAME should be a translatable name, but not actually translated yet,
436 e.g. enclosed in N_(). To use a different kind of value for a name, use
437 pivot_dimension_create__() instead.
439 The optional varargs parameters may be used to add an initial set of
440 categories to the dimension. Each string should be a translatable category
441 name, but not actually translated yet, e.g. enclosed in N_(). Each string
442 may optionally be followod by a PIVOT_RC_* string that specifies the default
443 numeric format for cells in this category. */
444 struct pivot_dimension * SENTINEL (0)
445 (pivot_dimension_create) (struct pivot_table *table,
446 enum pivot_axis_type axis_type,
447 const char *name, ...)
449 struct pivot_dimension *d = pivot_dimension_create__ (
450 table, axis_type, pivot_value_new_text (name));
453 va_start (args, name);
454 pivot_category_create_leaves_valist (d->root, args);
460 /* Creates a new dimension with the given NAME in TABLE and returns it. The
461 dimension is added to axis AXIS_TYPE, becoming the outermost dimension on
463 struct pivot_dimension *
464 pivot_dimension_create__ (struct pivot_table *table,
465 enum pivot_axis_type axis_type,
466 struct pivot_value *name)
468 assert (pivot_table_is_empty (table));
470 struct pivot_dimension *d = xmalloc (sizeof *d);
471 *d = (struct pivot_dimension) {
473 .axis_type = axis_type,
474 .level = table->axes[axis_type].n_dimensions,
475 .top_index = table->n_dimensions,
476 .root = xmalloc (sizeof *d->root),
479 struct pivot_category *root = d->root;
480 *root = (struct pivot_category) {
485 .data_index = SIZE_MAX,
486 .presentation_index = SIZE_MAX,
489 table->dimensions = xrealloc (
490 table->dimensions, (table->n_dimensions + 1) * sizeof *table->dimensions);
491 table->dimensions[table->n_dimensions++] = d;
493 struct pivot_axis *axis = &table->axes[axis_type];
494 axis->dimensions = xrealloc (
495 axis->dimensions, (axis->n_dimensions + 1) * sizeof *axis->dimensions);
496 axis->dimensions[axis->n_dimensions++] = d;
498 if (axis_type == PIVOT_AXIS_LAYER)
500 free (table->current_layer);
501 table->current_layer = xcalloc (axis[PIVOT_AXIS_LAYER].n_dimensions,
502 sizeof *table->current_layer);
505 /* axis->extent and axis->label_depth will be calculated later. */
511 pivot_dimension_destroy (struct pivot_dimension *d)
516 pivot_category_destroy (d->root);
517 free (d->data_leaves);
518 free (d->presentation_leaves);
522 /* Returns the first leaf node in an in-order traversal that is a child of
524 static const struct pivot_category * UNUSED
525 pivot_category_first_leaf (const struct pivot_category *cat)
527 if (pivot_category_is_leaf (cat))
530 for (size_t i = 0; i < cat->n_subs; i++)
532 const struct pivot_category *first
533 = pivot_category_first_leaf (cat->subs[i]);
541 /* Returns the next leaf node in an in-order traversal starting at CAT, which
543 static const struct pivot_category * UNUSED
544 pivot_category_next_leaf (const struct pivot_category *cat)
546 assert (pivot_category_is_leaf (cat));
550 const struct pivot_category *parent = cat->parent;
553 for (size_t i = cat->group_index + 1; i < parent->n_subs; i++)
555 const struct pivot_category *next
556 = pivot_category_first_leaf (parent->subs[i]);
566 pivot_category_add_child (struct pivot_category *child)
568 struct pivot_category *parent = child->parent;
570 assert (pivot_category_is_group (parent));
571 if (parent->n_subs >= parent->allocated_subs)
572 parent->subs = x2nrealloc (parent->subs, &parent->allocated_subs,
573 sizeof *parent->subs);
574 parent->subs[parent->n_subs++] = child;
577 /* Adds leaf categories as a child of PARENT. To create top-level categories
578 within dimension 'd', pass 'd->root' for PARENT.
580 Each of the varargs parameters should be a string, each of which should be a
581 translatable category name, but not actually translated yet, e.g. enclosed
582 in N_(). Each string may optionally be followod by a PIVOT_RC_* string that
583 specifies the default numeric format for cells in this category.
585 Returns the category index, which is just a 0-based array index, for the
588 Leaves have to be created in in-order, that is, don't create a group and add
589 some leaves, then add leaves outside the group and try to add more leaves
592 (pivot_category_create_leaves) (struct pivot_category *parent, ...)
594 int retval = parent->dimension->n_leaves;
597 va_start (args, parent);
598 pivot_category_create_leaves_valist (parent, args);
604 /* Creates a new leaf category with the given NAME as a child of PARENT. To
605 create a top-level category within dimension 'd', pass 'd->root' for PARENT.
606 Returns the category index, which is just a 0-based array index, for the new
609 Leaves have to be created in in-order, that is, don't create a group and add
610 some leaves, then add leaves outside the group and try to add more leaves
613 pivot_category_create_leaf (struct pivot_category *parent,
614 struct pivot_value *name)
616 return pivot_category_create_leaf_rc (parent, name, NULL);
619 /* Creates a new leaf category with the given NAME as a child of PARENT. To
620 create a top-level category within dimension 'd', pass 'd->root' for PARENT.
621 Returns the category index, which is just a 0-based array index, for the new
624 If RC is nonnull and the name of a result category, the category is assigned
625 that result category.
627 Leaves have to be created in in-order, that is, don't create a group and add
628 some leaves, then add leaves outside the group and try to add more leaves
631 pivot_category_create_leaf_rc (struct pivot_category *parent,
632 struct pivot_value *name, const char *rc)
634 struct pivot_dimension *d = parent->dimension;
636 struct pivot_category *leaf = xmalloc (sizeof *leaf);
637 *leaf = (struct pivot_category) {
641 .group_index = parent->n_subs,
642 .data_index = d->n_leaves,
643 .presentation_index = d->n_leaves,
646 if (d->n_leaves >= d->allocated_leaves)
648 d->data_leaves = x2nrealloc (d->data_leaves, &d->allocated_leaves,
649 sizeof *d->data_leaves);
650 d->presentation_leaves = xrealloc (
651 d->presentation_leaves,
652 d->allocated_leaves * sizeof *d->presentation_leaves);
655 d->data_leaves[d->n_leaves] = leaf;
656 d->presentation_leaves[d->n_leaves] = leaf;
659 pivot_category_add_child (leaf);
661 /* Make sure that the new child is the last in in-order. */
662 assert (!pivot_category_next_leaf (leaf));
664 pivot_category_set_rc (leaf, rc);
666 return leaf->data_index;
669 /* Adds a new category group named NAME as a child of PARENT. To create a
670 top-level group within dimension 'd', pass 'd->root' for PARENT.
672 NAME should be a translatable name, but not actually translated yet,
673 e.g. enclosed in N_(). To use a different kind of value for a name, use
674 pivot_category_create_group__() instead.
676 The optional varargs parameters may be used to add an initial set of
677 categories to the group. Each string should be a translatable category
678 name, but not actually translated yet, e.g. enclosed in N_(). Each string
679 may optionally be followod by a PIVOT_RC_* string that specifies the default
680 numeric format for cells in this category.
682 Returns the new group. */
683 struct pivot_category * SENTINEL (0)
684 (pivot_category_create_group) (struct pivot_category *parent,
685 const char *name, ...)
687 struct pivot_category *group = pivot_category_create_group__ (
688 parent, pivot_value_new_text (name));
691 va_start (args, name);
692 pivot_category_create_leaves_valist (group, args);
698 /* Adds a new category group named NAME as a child of PARENT. To create a
699 top-level group within dimension 'd', pass 'd->root' for PARENT. Returns
701 struct pivot_category *
702 pivot_category_create_group__ (struct pivot_category *parent,
703 struct pivot_value *name)
705 struct pivot_dimension *d = parent->dimension;
707 struct pivot_category *group = xmalloc (sizeof *group);
708 *group = (struct pivot_category) {
713 .group_index = parent->n_subs,
714 .data_index = SIZE_MAX,
715 .presentation_index = SIZE_MAX,
718 pivot_category_add_child (group);
724 pivot_category_destroy (struct pivot_category *c)
729 pivot_value_destroy (c->name);
730 for (size_t i = 0; i < c->n_subs; i++)
731 pivot_category_destroy (c->subs[i]);
738 These are usually the easiest way to control the formatting of numeric data
739 in a pivot table. See pivot_dimension_create() for an explanation of their
743 const char *name; /* "RC_*". */
744 struct fmt_spec format;
747 /* Formats for most of the result classes. */
748 static struct result_class result_classes[] =
750 { PIVOT_RC_INTEGER, { .type = FMT_F, .w = 40, .d = 0 } },
751 { PIVOT_RC_PERCENT, { .type = FMT_PCT, .w = 40, .d = 1 } },
752 { PIVOT_RC_CORRELATION, { .type = FMT_F, .w = 40, .d = 3 } },
753 { PIVOT_RC_SIGNIFICANCE, { .type = FMT_F, .w = 40, .d = 3 } },
754 { PIVOT_RC_RESIDUAL, { .type = FMT_F, .w = 40, .d = 2 } },
755 { PIVOT_RC_COUNT, { 0, 0, 0 } },
756 { PIVOT_RC_OTHER, { 0, 0, 0 } },
759 /* Has PIVOT_RC_COUNT been overridden by the user? */
760 static bool overridden_count_format;
762 static struct result_class *
763 pivot_result_class_find (const char *s)
765 for (size_t i = 0; i < sizeof result_classes / sizeof *result_classes; i++)
766 if (!strcmp (s, result_classes[i].name))
767 return &result_classes[i];
772 pivot_table_use_rc (const struct pivot_table *table, const char *s,
773 struct fmt_spec *format, bool *honor_small)
777 if (!strcmp (s, PIVOT_RC_OTHER))
779 *format = *settings_get_format ();
782 else if (!strcmp (s, PIVOT_RC_COUNT) && !overridden_count_format)
784 *format = table->weight_format;
785 *honor_small = false;
789 const struct result_class *rc = pivot_result_class_find (s);
792 *format = rc->format;
793 *honor_small = false;
797 printf ("unknown class %s\n", s);
803 /* Sets the format specification for the result class named S (which should not
804 include the RC_ prefix) to *FORMAT. Returns true if successful, false if S
805 does not name a known result class. */
807 pivot_result_class_change (const char *s_, const struct fmt_spec *format)
809 char *s = xasprintf ("RC_%s", s_);
810 struct result_class *rc = pivot_result_class_find (s);
813 rc->format = *format;
814 if (!strcmp (s, PIVOT_RC_COUNT))
815 overridden_count_format = true;
823 is_pivot_result_class (const char *s)
825 return pivot_result_class_find (s) != NULL;
830 static struct pivot_cell *pivot_table_insert_cell (struct pivot_table *,
831 const size_t *dindexes);
832 static void pivot_table_delete_cell (struct pivot_table *,
833 struct pivot_cell *);
835 /* Creates and returns a new pivot table with the given TITLE. TITLE should be
836 a text string marked for translation but not actually translated yet,
837 e.g. N_("Descriptive Statistics"). The un-translated text string is used as
838 the pivot table's subtype.
840 This function is a shortcut for pivot_table_create__() for the most common
841 case. Use pivot_table_create__() directly if the title should be some kind
842 of value other than an ordinary text string, or if the subtype should be
843 different from the title.
845 See the large comment at the top of pivot-table.h for general advice on
846 creating pivot tables. */
848 pivot_table_create (const char *title)
850 return pivot_table_create__ (pivot_value_new_text (title), title);
853 /* Creates and returns a new pivot table with the given TITLE, and takes
854 ownership of TITLE. The new pivot table's subtype is SUBTYPE, which should
855 be an untranslated English string that describes the contents of the table
856 at a high level without being specific about the variables or other context
859 TITLE and SUBTYPE may be NULL, but in that case the client must add them
860 later because they are both mandatory for a pivot table.
862 See the large comment at the top of pivot-table.h for general advice on
863 creating pivot tables. */
865 pivot_table_create__ (struct pivot_value *title, const char *subtype)
867 struct pivot_table *table = xmalloc (sizeof *table);
868 *table = (struct pivot_table) {
871 .show_caption = true,
872 .weight_format = (struct fmt_spec) { .type = FMT_F, .w = 40 },
874 .subtype = subtype ? pivot_value_new_text (subtype) : NULL,
875 .command_c = xstrdup_if_nonempty (output_get_command_name ()),
876 .look = pivot_table_look_ref (pivot_table_look_get_default ()),
877 .settings = fmt_settings_copy (settings_get_fmt_settings ()),
878 .small = settings_get_small (),
879 .cells = HMAP_INITIALIZER (table->cells),
884 /* Creates and returns a new pivot table with the given TITLE and a single cell
885 with the given CONTENT.
887 This is really just for error handling. */
889 pivot_table_create_for_text (struct pivot_value *title,
890 struct pivot_value *content)
892 struct pivot_table *table = pivot_table_create__ (title, "Error");
894 struct pivot_dimension *d = pivot_dimension_create (
895 table, PIVOT_AXIS_ROW, N_("Error"));
896 d->hide_all_labels = true;
897 pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
899 pivot_table_put1 (table, 0, content);
904 /* Increases TABLE's reference count, indicating that it has an additional
905 owner. A pivot table that is shared among multiple owners must not be
908 pivot_table_ref (const struct pivot_table *table_)
910 struct pivot_table *table = CONST_CAST (struct pivot_table *, table_);
915 static struct pivot_table_sizing
916 clone_sizing (const struct pivot_table_sizing *s)
918 return (struct pivot_table_sizing) {
919 .widths = (s->n_widths
920 ? xmemdup (s->widths, s->n_widths * sizeof *s->widths)
922 .n_widths = s->n_widths,
924 .breaks = (s->n_breaks
925 ? xmemdup (s->breaks, s->n_breaks * sizeof *s->breaks)
927 .n_breaks = s->n_breaks,
930 ? xmemdup (s->keeps, s->n_keeps * sizeof *s->keeps)
932 .n_keeps = s->n_keeps,
936 static struct pivot_footnote **
937 clone_footnotes (struct pivot_footnote **old, size_t n)
942 struct pivot_footnote **new = xmalloc (n * sizeof *new);
943 for (size_t i = 0; i < n; i++)
945 new[i] = xmalloc (sizeof *new[i]);
946 *new[i] = (struct pivot_footnote) {
948 .content = pivot_value_clone (old[i]->content),
949 .marker = pivot_value_clone (old[i]->marker),
950 .show = old[i]->show,
956 static struct pivot_category *
957 clone_category (struct pivot_category *old,
958 struct pivot_dimension *new_dimension,
959 struct pivot_category *new_parent)
961 struct pivot_category *new = xmalloc (sizeof *new);
962 *new = (struct pivot_category) {
963 .name = pivot_value_clone (old->name),
964 .parent = new_parent,
965 .dimension = new_dimension,
966 .label_depth = old->label_depth,
967 .extra_depth = old->extra_depth,
970 ? xcalloc (old->n_subs, sizeof *new->subs)
972 .n_subs = old->n_subs,
973 .allocated_subs = old->n_subs,
975 .show_label = old->show_label,
976 .show_label_in_corner = old->show_label_in_corner,
978 .format = old->format,
979 .group_index = old->group_index,
980 .data_index = old->data_index,
981 .presentation_index = old->presentation_index,
984 if (pivot_category_is_leaf (old))
986 assert (new->data_index < new_dimension->n_leaves);
987 new->dimension->data_leaves[new->data_index] = new;
989 assert (new->presentation_index < new_dimension->n_leaves);
990 new->dimension->presentation_leaves[new->presentation_index] = new;
993 for (size_t i = 0; i < new->n_subs; i++)
994 new->subs[i] = clone_category (old->subs[i], new_dimension, new);
999 static struct pivot_dimension *
1000 clone_dimension (struct pivot_dimension *old, struct pivot_table *new_pt)
1002 struct pivot_dimension *new = xmalloc (sizeof *new);
1003 *new = (struct pivot_dimension) {
1005 .axis_type = old->axis_type,
1006 .level = old->level,
1007 .top_index = old->top_index,
1008 .data_leaves = xcalloc (old->n_leaves , sizeof *new->data_leaves),
1009 .presentation_leaves = xcalloc (old->n_leaves
1010 , sizeof *new->presentation_leaves),
1011 .n_leaves = old->n_leaves,
1012 .allocated_leaves = old->n_leaves,
1013 .hide_all_labels = old->hide_all_labels,
1014 .label_depth = old->label_depth,
1017 new->root = clone_category (old->root, new, NULL);
1022 static struct pivot_dimension **
1023 clone_dimensions (struct pivot_dimension **old, size_t n,
1024 struct pivot_table *new_pt)
1029 struct pivot_dimension **new = xmalloc (n * sizeof *new);
1030 for (size_t i = 0; i < n; i++)
1031 new[i] = clone_dimension (old[i], new_pt);
1035 struct pivot_table *
1036 pivot_table_unshare (struct pivot_table *old)
1038 assert (old->ref_cnt > 0);
1039 if (old->ref_cnt == 1)
1042 pivot_table_unref (old);
1044 struct pivot_table *new = xmalloc (sizeof *new);
1045 *new = (struct pivot_table) {
1048 .look = pivot_table_look_ref (old->look),
1050 .rotate_inner_column_labels = old->rotate_inner_column_labels,
1051 .rotate_outer_row_labels = old->rotate_outer_row_labels,
1052 .show_grid_lines = old->show_grid_lines,
1053 .show_title = old->show_title,
1054 .show_caption = old->show_caption,
1055 .current_layer = (old->current_layer
1056 ? xmemdup (old->current_layer,
1057 old->axes[PIVOT_AXIS_LAYER].n_dimensions
1058 * sizeof *new->current_layer)
1060 .show_values = old->show_values,
1061 .show_variables = old->show_variables,
1062 .weight_format = old->weight_format,
1065 [TABLE_HORZ] = clone_sizing (&old->sizing[TABLE_HORZ]),
1066 [TABLE_VERT] = clone_sizing (&old->sizing[TABLE_VERT]),
1069 .settings = fmt_settings_copy (&old->settings),
1070 .grouping = old->grouping,
1071 .small = old->small,
1073 .command_local = xstrdup_if_nonnull (old->command_local),
1074 .command_c = xstrdup_if_nonnull (old->command_c),
1075 .language = xstrdup_if_nonnull (old->language),
1076 .locale = xstrdup_if_nonnull (old->locale),
1078 .dataset = xstrdup_if_nonnull (old->dataset),
1079 .datafile = xstrdup_if_nonnull (old->datafile),
1082 .footnotes = clone_footnotes (old->footnotes, old->n_footnotes),
1083 .n_footnotes = old->n_footnotes,
1084 .allocated_footnotes = old->n_footnotes,
1086 .title = pivot_value_clone (old->title),
1087 .subtype = pivot_value_clone (old->subtype),
1088 .corner_text = pivot_value_clone (old->corner_text),
1089 .caption = pivot_value_clone (old->caption),
1090 .notes = xstrdup_if_nonnull (old->notes),
1092 .dimensions = clone_dimensions (old->dimensions, old->n_dimensions, new),
1093 .n_dimensions = old->n_dimensions,
1095 .cells = HMAP_INITIALIZER (new->cells),
1098 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1100 struct pivot_axis *new_axis = &new->axes[i];
1101 const struct pivot_axis *old_axis = &old->axes[i];
1103 *new_axis = (struct pivot_axis) {
1104 .dimensions = xmalloc (old_axis->n_dimensions
1105 * sizeof *new_axis->dimensions),
1106 .n_dimensions = old_axis->n_dimensions,
1107 .extent = old_axis->extent,
1108 .label_depth = old_axis->label_depth,
1111 for (size_t i = 0; i < new_axis->n_dimensions; i++)
1112 new_axis->dimensions[i] = new->dimensions[
1113 old_axis->dimensions[i]->top_index];
1116 const struct pivot_cell *old_cell;
1117 size_t *dindexes = xmalloc (old->n_dimensions * sizeof *dindexes);
1118 HMAP_FOR_EACH (old_cell, struct pivot_cell, hmap_node, &old->cells)
1120 for (size_t i = 0; i < old->n_dimensions; i++)
1121 dindexes[i] = old_cell->idx[i];
1122 struct pivot_cell *new_cell
1123 = pivot_table_insert_cell (new, dindexes);
1124 new_cell->value = pivot_value_clone (old_cell->value);
1131 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
1132 If TABLE no longer has any owners, it is freed. */
1134 pivot_table_unref (struct pivot_table *table)
1138 assert (table->ref_cnt > 0);
1139 if (--table->ref_cnt)
1142 free (table->current_layer);
1143 pivot_table_look_unref (table->look);
1145 for (int i = 0; i < TABLE_N_AXES; i++)
1146 pivot_table_sizing_uninit (&table->sizing[i]);
1148 fmt_settings_uninit (&table->settings);
1150 free (table->command_local);
1151 free (table->command_c);
1152 free (table->language);
1153 free (table->locale);
1155 free (table->dataset);
1156 free (table->datafile);
1158 for (size_t i = 0; i < table->n_footnotes; i++)
1159 pivot_footnote_destroy (table->footnotes[i]);
1160 free (table->footnotes);
1162 pivot_value_destroy (table->title);
1163 pivot_value_destroy (table->subtype);
1164 pivot_value_destroy (table->corner_text);
1165 pivot_value_destroy (table->caption);
1166 free (table->notes);
1168 for (size_t i = 0; i < table->n_dimensions; i++)
1169 pivot_dimension_destroy (table->dimensions[i]);
1170 free (table->dimensions);
1172 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1173 free (table->axes[i].dimensions);
1175 struct pivot_cell *cell, *next_cell;
1176 HMAP_FOR_EACH_SAFE (cell, next_cell, struct pivot_cell, hmap_node,
1178 pivot_table_delete_cell (table, cell);
1180 hmap_destroy (&table->cells);
1185 /* Returns true if TABLE has more than one owner. A pivot table that is shared
1186 among multiple owners must not be modified. */
1188 pivot_table_is_shared (const struct pivot_table *table)
1190 return table->ref_cnt > 1;
1194 pivot_table_set_value__ (struct pivot_value **dstp, struct pivot_value *src)
1196 pivot_value_destroy (*dstp);
1200 /* Changes the title of TABLE to TITLE. Takes ownership of TITLE. */
1202 pivot_table_set_title (struct pivot_table *table, struct pivot_value *title)
1204 pivot_table_set_value__ (&table->title, title);
1207 /* Changes the subtype of TABLE to SUBTYPE. Takes ownership of SUBTYPE. */
1209 pivot_table_set_subtype (struct pivot_table *table, struct pivot_value *subtype)
1211 pivot_table_set_value__ (&table->subtype, subtype);
1214 /* Changes the corner text of TABLE to CORNER_TEXT. Takes ownership of
1217 pivot_table_set_corner_text (struct pivot_table *table,
1218 struct pivot_value *corner_text)
1220 pivot_table_set_value__ (&table->corner_text, corner_text);
1223 /* Changes the caption of TABLE to CAPTION. Takes ownership of CAPTION. */
1225 pivot_table_set_caption (struct pivot_table *table, struct pivot_value *caption)
1227 pivot_table_set_value__ (&table->caption, caption);
1230 /* Swaps axes A and B in TABLE. */
1232 pivot_table_swap_axes (struct pivot_table *table,
1233 enum pivot_axis_type a, enum pivot_axis_type b)
1238 struct pivot_axis tmp = table->axes[a];
1239 table->axes[a] = table->axes[b];
1240 table->axes[b] = tmp;
1242 for (int a = 0; a < PIVOT_N_AXES; a++)
1244 struct pivot_axis *axis = &table->axes[a];
1245 for (size_t d = 0; d < axis->n_dimensions; d++)
1246 axis->dimensions[d]->axis_type = a;
1249 if (a == PIVOT_AXIS_LAYER || b == PIVOT_AXIS_LAYER)
1251 free (table->current_layer);
1252 table->current_layer = xzalloc (
1253 table->axes[PIVOT_AXIS_LAYER].n_dimensions
1254 * sizeof *table->current_layer);
1258 /* Swaps the row and column axes in TABLE. */
1260 pivot_table_transpose (struct pivot_table *table)
1262 pivot_table_swap_axes (table, PIVOT_AXIS_ROW, PIVOT_AXIS_COLUMN);
1266 pivot_table_update_axes (struct pivot_table *table)
1268 for (int a = 0; a < PIVOT_N_AXES; a++)
1270 struct pivot_axis *axis = &table->axes[a];
1272 for (size_t d = 0; d < axis->n_dimensions; d++)
1274 struct pivot_dimension *dim = axis->dimensions[d];
1281 /* Moves DIM from its current location in TABLE to POS within AXIS. POS of 0
1282 is the innermost dimension, 1 is the next one out, and so on. */
1284 pivot_table_move_dimension (struct pivot_table *table,
1285 struct pivot_dimension *dim,
1286 enum pivot_axis_type axis, size_t pos)
1288 assert (dim->table == table);
1290 struct pivot_axis *old_axis = &table->axes[dim->axis_type];
1291 struct pivot_axis *new_axis = &table->axes[axis];
1292 pos = MIN (pos, new_axis->n_dimensions);
1294 if (old_axis == new_axis && pos == dim->level)
1300 /* Update the current layer, if necessary. If we're moving within the layer
1301 axis, preserve the current layer. */
1302 if (dim->axis_type == PIVOT_AXIS_LAYER)
1304 if (axis == PIVOT_AXIS_LAYER)
1306 /* Rearranging the layer axis. */
1307 move_element (table->current_layer, old_axis->n_dimensions,
1308 sizeof *table->current_layer,
1313 /* A layer is becoming a row or column. */
1314 remove_element (table->current_layer, old_axis->n_dimensions,
1315 sizeof *table->current_layer, dim->level);
1318 else if (axis == PIVOT_AXIS_LAYER)
1320 /* A row or column is becoming a layer. */
1321 table->current_layer = xrealloc (
1322 table->current_layer,
1323 (new_axis->n_dimensions + 1) * sizeof *table->current_layer);
1324 insert_element (table->current_layer, new_axis->n_dimensions,
1325 sizeof *table->current_layer, pos);
1326 table->current_layer[pos] = 0;
1329 /* Remove DIM from its current axis. */
1330 remove_element (old_axis->dimensions, old_axis->n_dimensions,
1331 sizeof *old_axis->dimensions, dim->level);
1332 old_axis->n_dimensions--;
1334 /* Insert DIM into its new axis. */
1335 new_axis->dimensions = xrealloc (
1336 new_axis->dimensions,
1337 (new_axis->n_dimensions + 1) * sizeof *new_axis->dimensions);
1338 insert_element (new_axis->dimensions, new_axis->n_dimensions,
1339 sizeof *new_axis->dimensions, pos);
1340 new_axis->dimensions[pos] = dim;
1341 new_axis->n_dimensions++;
1343 pivot_table_update_axes (table);
1347 const struct pivot_table_look *
1348 pivot_table_get_look (const struct pivot_table *table)
1354 pivot_table_set_look (struct pivot_table *table,
1355 const struct pivot_table_look *look)
1357 pivot_table_look_unref (table->look);
1358 table->look = pivot_table_look_ref (look);
1361 /* Sets the format used for PIVOT_RC_COUNT cells to the one used for variable
1362 WV, which should be the weight variable for the dictionary whose data or
1363 statistics are being put into TABLE.
1365 This has no effect if WV is NULL. */
1367 pivot_table_set_weight_var (struct pivot_table *table,
1368 const struct variable *wv)
1371 pivot_table_set_weight_format (table, *var_get_print_format (wv));
1374 /* Sets the format used for PIVOT_RC_COUNT cells to WFMT, which should be the
1375 format for the dictionary whose data or statistics are being put into
1378 pivot_table_set_weight_format (struct pivot_table *table, struct fmt_spec wfmt)
1381 table->weight_format = wfmt;
1384 /* Returns true if TABLE has no cells, false otherwise. */
1386 pivot_table_is_empty (const struct pivot_table *table)
1388 return hmap_is_empty (&table->cells);
1392 pivot_cell_hash_indexes (const size_t *indexes, size_t n_idx)
1394 return hash_bytes (indexes, n_idx * sizeof *indexes, 0);
1398 equal_indexes (const size_t *a, const unsigned int *b, size_t n)
1400 for (size_t i = 0; i < n; i++)
1407 static struct pivot_cell *
1408 pivot_table_lookup_cell__ (const struct pivot_table *table,
1409 const size_t *dindexes, unsigned int hash)
1411 struct pivot_cell *cell;
1412 HMAP_FOR_EACH_WITH_HASH (cell, struct pivot_cell, hmap_node, hash,
1414 if (equal_indexes (dindexes, cell->idx, table->n_dimensions))
1419 static struct pivot_cell *
1420 pivot_cell_allocate (size_t n_idx)
1422 struct pivot_cell *cell UNUSED;
1423 return xmalloc (sizeof *cell + n_idx * sizeof *cell->idx);
1426 static struct pivot_cell *
1427 pivot_table_insert_cell (struct pivot_table *table, const size_t *dindexes)
1429 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1430 struct pivot_cell *cell = pivot_table_lookup_cell__ (table, dindexes, hash);
1433 cell = pivot_cell_allocate (table->n_dimensions);
1434 for (size_t i = 0; i < table->n_dimensions; i++)
1435 cell->idx[i] = dindexes[i];
1437 hmap_insert (&table->cells, &cell->hmap_node, hash);
1442 /* Puts VALUE in the cell in TABLE whose indexes are given by the N indexes in
1443 DINDEXES. The order of the indexes is the same as the order in which the
1444 dimensions were created. N must be the number of dimensions in TABLE.
1445 Takes ownership of VALUE.
1447 If VALUE is a numeric value without a specified format, this function checks
1448 each of the categories designated by DINDEXES[] and takes the format from
1449 the first category with a result class. If none has a result class, uses
1450 the overall default numeric format. */
1452 pivot_table_put (struct pivot_table *table, const size_t *dindexes, size_t n,
1453 struct pivot_value *value)
1455 assert (n == table->n_dimensions);
1456 for (size_t i = 0; i < n; i++)
1457 assert (dindexes[i] < table->dimensions[i]->n_leaves);
1459 if (value->type == PIVOT_VALUE_NUMERIC && !value->numeric.format.w)
1461 for (size_t i = 0; i < table->n_dimensions; i++)
1463 const struct pivot_dimension *d = table->dimensions[i];
1464 if (dindexes[i] < d->n_leaves)
1466 const struct pivot_category *c = d->data_leaves[dindexes[i]];
1469 value->numeric.format = c->format;
1470 value->numeric.honor_small = c->honor_small;
1475 value->numeric.format = *settings_get_format ();
1476 value->numeric.honor_small = true;
1481 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1482 pivot_value_destroy (cell->value);
1483 cell->value = value;
1486 /* Puts VALUE in the cell in TABLE with index IDX1. TABLE must have 1
1487 dimension. Takes ownership of VALUE. */
1489 pivot_table_put1 (struct pivot_table *table, size_t idx1,
1490 struct pivot_value *value)
1492 size_t dindexes[] = { idx1 };
1493 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1496 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2). TABLE must have 2
1497 dimensions. Takes ownership of VALUE. */
1499 pivot_table_put2 (struct pivot_table *table, size_t idx1, size_t idx2,
1500 struct pivot_value *value)
1502 size_t dindexes[] = { idx1, idx2 };
1503 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1506 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3). TABLE must
1507 have 3 dimensions. Takes ownership of VALUE. */
1509 pivot_table_put3 (struct pivot_table *table, size_t idx1, size_t idx2,
1510 size_t idx3, struct pivot_value *value)
1512 size_t dindexes[] = { idx1, idx2, idx3 };
1513 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1516 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3, IDX4). TABLE
1517 must have 4 dimensions. Takes ownership of VALUE. */
1519 pivot_table_put4 (struct pivot_table *table, size_t idx1, size_t idx2,
1520 size_t idx3, size_t idx4, struct pivot_value *value)
1522 size_t dindexes[] = { idx1, idx2, idx3, idx4 };
1523 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1526 /* Creates and returns a new footnote in TABLE with the given CONTENT and an
1527 automatically assigned marker.
1529 The footnote will only appear in output if it is referenced. Use
1530 pivot_value_add_footnote() to add a reference to the footnote. */
1531 struct pivot_footnote *
1532 pivot_table_create_footnote (struct pivot_table *table,
1533 struct pivot_value *content)
1535 return pivot_table_create_footnote__ (table, table->n_footnotes,
1540 pivot_footnote_format_marker (const struct pivot_footnote *f,
1541 const struct pivot_table *pt,
1545 pivot_value_format_body (f->marker, pt, s);
1546 else if (pt->look->show_numeric_markers)
1547 ds_put_format (s, "%zu", f->idx + 1);
1550 char text[INT_BUFSIZE_BOUND (size_t)];
1551 str_format_26adic (f->idx + 1, false, text, sizeof text);
1552 ds_put_cstr (s, text);
1557 pivot_footnote_marker_string (const struct pivot_footnote *f,
1558 const struct pivot_table *pt)
1560 struct string s = DS_EMPTY_INITIALIZER;
1561 pivot_footnote_format_marker (f, pt, &s);
1562 return ds_steal_cstr (&s);
1565 /* Creates or modifies a footnote in TABLE with 0-based number IDX (and creates
1566 all lower indexes as a side effect). If MARKER is nonnull, sets the
1567 footnote's marker; if CONTENT is nonnull, sets the footnote's content. */
1568 struct pivot_footnote *
1569 pivot_table_create_footnote__ (struct pivot_table *table, size_t idx,
1570 struct pivot_value *marker,
1571 struct pivot_value *content)
1573 if (idx >= table->n_footnotes)
1575 while (idx >= table->allocated_footnotes)
1576 table->footnotes = x2nrealloc (table->footnotes,
1577 &table->allocated_footnotes,
1578 sizeof *table->footnotes);
1579 while (idx >= table->n_footnotes)
1581 struct pivot_footnote *f = xmalloc (sizeof *f);
1582 *f = (struct pivot_footnote) {
1583 .idx = table->n_footnotes,
1586 table->footnotes[table->n_footnotes++] = f;
1590 struct pivot_footnote *f = table->footnotes[idx];
1593 pivot_value_destroy (f->marker);
1598 pivot_value_destroy (f->content);
1599 f->content = content;
1604 /* Frees the data owned by F. */
1606 pivot_footnote_destroy (struct pivot_footnote *f)
1610 pivot_value_destroy (f->content);
1611 pivot_value_destroy (f->marker);
1616 /* Converts per-axis presentation-order indexes, given in PINDEXES, into data
1617 indexes for each dimension in TABLE in DINDEXES[]. */
1619 pivot_table_convert_indexes_ptod (const struct pivot_table *table,
1620 const size_t *pindexes[PIVOT_N_AXES],
1621 size_t dindexes[/* table->n_dimensions */])
1623 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1625 const struct pivot_axis *axis = &table->axes[i];
1627 for (size_t j = 0; j < axis->n_dimensions; j++)
1629 const struct pivot_dimension *d = axis->dimensions[j];
1630 size_t pindex = pindexes[i][j];
1631 dindexes[d->top_index] = d->presentation_leaves[pindex]->data_index;
1637 pivot_table_enumerate_axis (const struct pivot_table *table,
1638 enum pivot_axis_type axis_type,
1639 const size_t *layer_indexes, bool omit_empty,
1642 const struct pivot_axis *axis = &table->axes[axis_type];
1643 if (!axis->n_dimensions)
1645 size_t *enumeration = xnmalloc (2, sizeof *enumeration);
1647 enumeration[1] = SIZE_MAX;
1652 else if (!axis->extent)
1654 size_t *enumeration = xmalloc (sizeof *enumeration);
1655 *enumeration = SIZE_MAX;
1661 size_t *enumeration = xnmalloc (xsum (xtimes (axis->extent,
1662 axis->n_dimensions), 1),
1663 sizeof *enumeration);
1664 size_t *p = enumeration;
1665 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
1667 size_t *axis_indexes;
1668 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1672 enum pivot_axis_type axis2_type
1673 = pivot_axis_type_transpose (axis_type);
1675 size_t *axis2_indexes;
1676 PIVOT_AXIS_FOR_EACH (axis2_indexes, &table->axes[axis2_type])
1678 const size_t *pindexes[PIVOT_N_AXES];
1679 pindexes[PIVOT_AXIS_LAYER] = layer_indexes;
1680 pindexes[axis_type] = axis_indexes;
1681 pindexes[axis2_type] = axis2_indexes;
1682 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
1683 if (pivot_table_get (table, dindexes))
1689 free (axis2_indexes);
1692 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1693 p += axis->n_dimensions;
1695 if (omit_empty && p == enumeration)
1697 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1699 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1700 p += axis->n_dimensions;
1705 *n = (p - enumeration) / axis->n_dimensions;
1711 static struct pivot_cell *
1712 pivot_table_lookup_cell (const struct pivot_table *table,
1713 const size_t *dindexes)
1715 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1716 return pivot_table_lookup_cell__ (table, dindexes, hash);
1719 const struct pivot_value *
1720 pivot_table_get (const struct pivot_table *table, const size_t *dindexes)
1722 const struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1723 return cell ? cell->value : NULL;
1726 struct pivot_value *
1727 pivot_table_get_rw (struct pivot_table *table, const size_t *dindexes)
1729 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1731 cell->value = pivot_value_new_user_text ("", -1);
1736 pivot_table_delete_cell (struct pivot_table *table, struct pivot_cell *cell)
1738 hmap_delete (&table->cells, &cell->hmap_node);
1739 pivot_value_destroy (cell->value);
1744 pivot_table_delete (struct pivot_table *table, const size_t *dindexes)
1746 struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1749 pivot_table_delete_cell (table, cell);
1757 distribute_extra_depth (struct pivot_category *category, size_t extra_depth)
1759 if (pivot_category_is_group (category) && category->n_subs)
1760 for (size_t i = 0; i < category->n_subs; i++)
1761 distribute_extra_depth (category->subs[i], extra_depth);
1763 category->extra_depth += extra_depth;
1767 pivot_category_assign_label_depth (struct pivot_category *category,
1768 bool dimension_labels_in_corner)
1770 category->extra_depth = 0;
1772 if (pivot_category_is_group (category))
1775 for (size_t i = 0; i < category->n_subs; i++)
1777 pivot_category_assign_label_depth (category->subs[i], false);
1778 depth = MAX (depth, category->subs[i]->label_depth);
1781 for (size_t i = 0; i < category->n_subs; i++)
1783 struct pivot_category *sub = category->subs[i];
1785 size_t extra_depth = depth - sub->label_depth;
1787 distribute_extra_depth (sub, extra_depth);
1789 sub->label_depth = depth;
1792 category->show_label_in_corner = (category->show_label
1793 && dimension_labels_in_corner);
1794 category->label_depth
1795 = (category->show_label && !category->show_label_in_corner
1796 ? depth + 1 : depth);
1799 category->label_depth = 1;
1803 pivot_axis_assign_label_depth (struct pivot_table *table,
1804 enum pivot_axis_type axis_type,
1805 bool dimension_labels_in_corner)
1807 struct pivot_axis *axis = &table->axes[axis_type];
1808 bool any_label_shown_in_corner = false;
1809 axis->label_depth = 0;
1811 for (size_t i = 0; i < axis->n_dimensions; i++)
1813 struct pivot_dimension *d = axis->dimensions[i];
1814 pivot_category_assign_label_depth (d->root, dimension_labels_in_corner);
1815 d->label_depth = d->hide_all_labels ? 0 : d->root->label_depth;
1816 axis->label_depth += d->label_depth;
1817 axis->extent *= d->n_leaves;
1819 if (d->root->show_label_in_corner)
1820 any_label_shown_in_corner = true;
1822 return any_label_shown_in_corner;
1826 pivot_table_assign_label_depth (struct pivot_table *table)
1828 pivot_axis_assign_label_depth (table, PIVOT_AXIS_COLUMN, false);
1829 if (pivot_axis_assign_label_depth (
1830 table, PIVOT_AXIS_ROW, (table->look->row_labels_in_corner
1831 && !table->corner_text))
1832 && table->axes[PIVOT_AXIS_COLUMN].label_depth == 0)
1833 table->axes[PIVOT_AXIS_COLUMN].label_depth = 1;
1834 pivot_axis_assign_label_depth (table, PIVOT_AXIS_LAYER, false);
1838 indent (int indentation)
1840 for (int i = 0; i < indentation * 2; i++)
1845 pivot_value_dump (const struct pivot_value *value,
1846 const struct pivot_table *pt)
1848 char *s = pivot_value_to_string (value, pt);
1854 pivot_table_dump_value (const struct pivot_value *value, const char *name,
1855 const struct pivot_table *pt, int indentation)
1859 indent (indentation);
1860 printf ("%s: ", name);
1861 pivot_value_dump (value, pt);
1867 pivot_table_dump_string (const char *string, const char *name, int indentation)
1871 indent (indentation);
1872 printf ("%s: %s\n", name, string);
1877 pivot_category_dump (const struct pivot_category *c,
1878 const struct pivot_table *pt, int indentation)
1880 indent (indentation);
1881 printf ("%s \"", pivot_category_is_leaf (c) ? "leaf" : "group");
1882 pivot_value_dump (c->name, pt);
1885 if (pivot_category_is_leaf (c))
1886 printf ("data_index=%zu\n", c->data_index);
1889 printf (" (label %s)", c->show_label ? "shown" : "hidden");
1892 for (size_t i = 0; i < c->n_subs; i++)
1893 pivot_category_dump (c->subs[i], pt, indentation + 1);
1898 pivot_dimension_dump (const struct pivot_dimension *d,
1899 const struct pivot_table *pt, int indentation)
1901 indent (indentation);
1902 printf ("%s dimension %zu (where 0=innermost), label_depth=%d:\n",
1903 pivot_axis_type_to_string (d->axis_type), d->level, d->label_depth);
1905 pivot_category_dump (d->root, pt, indentation + 1);
1909 table_area_style_dump (enum pivot_area area, const struct table_area_style *a,
1912 indent (indentation);
1913 printf ("%s: ", pivot_area_to_string (area));
1914 font_style_dump (&a->font_style);
1916 cell_style_dump (&a->cell_style);
1921 table_border_style_dump (enum pivot_border border,
1922 const struct table_border_style *b, int indentation)
1924 indent (indentation);
1925 printf ("%s: %s ", pivot_border_to_string (border),
1926 table_stroke_to_string (b->stroke));
1927 cell_color_dump (&b->color);
1932 compose_headings (const struct pivot_table *pt,
1933 const struct pivot_axis *axis,
1934 const size_t *column_enumeration)
1936 if (!axis->n_dimensions || !axis->extent || !axis->label_depth)
1939 char ***headings = xnmalloc (axis->label_depth, sizeof *headings);
1940 for (size_t i = 0; i < axis->label_depth; i++)
1941 headings[i] = xcalloc (axis->extent, sizeof **headings);
1943 const size_t *indexes;
1945 PIVOT_ENUMERATION_FOR_EACH (indexes, column_enumeration, axis)
1947 int row = axis->label_depth - 1;
1948 for (int dim_index = 0; dim_index < axis->n_dimensions; dim_index++)
1950 const struct pivot_dimension *d = axis->dimensions[dim_index];
1951 if (d->hide_all_labels)
1953 for (const struct pivot_category *c
1954 = d->presentation_leaves[indexes[dim_index]];
1958 if (pivot_category_is_leaf (c) || (c->show_label
1959 && !c->show_label_in_corner))
1961 headings[row][column] = pivot_value_to_string (c->name, pt);
1962 if (!*headings[row][column])
1964 free (headings[row][column]);
1965 headings[row][column] = xstrdup ("<blank>");
1978 free_headings (const struct pivot_axis *axis, char ***headings)
1982 for (size_t i = 0; i < axis->label_depth; i++)
1984 for (size_t j = 0; j < axis->extent; j++)
1985 free (headings[i][j]);
1992 pivot_table_sizing_dump (const char *name,
1993 const int width_ranges[2],
1994 const struct pivot_table_sizing *s,
1997 indent (indentation);
1998 printf ("%ss: min=%d, max=%d\n", name, width_ranges[0], width_ranges[1]);
2001 indent (indentation + 1);
2002 printf ("%s widths:", name);
2003 for (size_t i = 0; i < s->n_widths; i++)
2004 printf (" %d", s->widths[i]);
2009 indent (indentation + 1);
2010 printf ("break after %ss:", name);
2011 for (size_t i = 0; i < s->n_breaks; i++)
2012 printf (" %zu", s->breaks[i]);
2017 indent (indentation + 1);
2018 printf ("keep %ss together:", name);
2019 for (size_t i = 0; i < s->n_keeps; i++)
2020 printf (" [%zu,%zu]",
2022 s->keeps[i].ofs + s->keeps[i].n - 1);
2028 dump_leaf (const struct pivot_table *table, const struct pivot_category *c)
2032 dump_leaf (table, c->parent);
2033 if (pivot_category_is_leaf (c) || c->show_label)
2036 pivot_value_dump (c->name, table);
2042 pivot_table_dump (const struct pivot_table *table, int indentation)
2047 pivot_table_assign_label_depth (CONST_CAST (struct pivot_table *, table));
2049 pivot_table_dump_value (table->title, "title", table, indentation);
2050 pivot_table_dump_value (table->subtype, "subtype", table, indentation);
2051 pivot_table_dump_string (table->command_c, "command", indentation);
2052 pivot_table_dump_string (table->dataset, "dataset", indentation);
2053 pivot_table_dump_string (table->datafile, "datafile", indentation);
2054 pivot_table_dump_string (table->notes, "notes", indentation);
2055 pivot_table_dump_string (table->look->name, "table-look", indentation);
2058 indent (indentation);
2060 struct tm *tm = localtime (&table->date);
2061 printf ("date: %d-%02d-%02d %d:%02d:%02d\n", tm->tm_year + 1900,
2062 tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
2066 indent (indentation);
2067 printf ("sizing:\n");
2068 pivot_table_sizing_dump ("column", table->look->col_heading_width_range,
2069 &table->sizing[TABLE_HORZ], indentation + 1);
2070 pivot_table_sizing_dump ("row", table->look->row_heading_width_range,
2071 &table->sizing[TABLE_VERT], indentation + 1);
2073 indent (indentation);
2074 printf ("areas:\n");
2075 for (enum pivot_area area = 0; area < PIVOT_N_AREAS; area++)
2076 table_area_style_dump (area, &table->look->areas[area], indentation + 1);
2078 indent (indentation);
2079 printf ("borders:\n");
2080 for (enum pivot_border border = 0; border < PIVOT_N_BORDERS; border++)
2081 table_border_style_dump (border, &table->look->borders[border],
2084 for (size_t i = 0; i < table->n_dimensions; i++)
2085 pivot_dimension_dump (table->dimensions[i], table, indentation);
2087 /* Presentation and data indexes. */
2088 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
2090 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2091 if (layer_axis->n_dimensions)
2093 indent (indentation);
2094 printf ("current layer:");
2096 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2098 const struct pivot_dimension *d = layer_axis->dimensions[i];
2099 char *name = pivot_value_to_string (d->root->name, table);
2100 printf (" %s", name);
2103 size_t ofs = table->current_layer[i];
2104 if (ofs < d->n_leaves)
2106 char *value = pivot_value_to_string (d->data_leaves[ofs]->name,
2108 printf ("=%s", value);
2116 size_t *layer_indexes;
2117 size_t layer_iteration = 0;
2118 PIVOT_AXIS_FOR_EACH (layer_indexes, &table->axes[PIVOT_AXIS_LAYER])
2120 indent (indentation);
2121 printf ("layer %zu:", layer_iteration++);
2123 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2124 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2126 const struct pivot_dimension *d = layer_axis->dimensions[i];
2128 fputs (i == 0 ? " " : ", ", stdout);
2129 pivot_value_dump (d->root->name, table);
2130 fputs (" =", stdout);
2132 dump_leaf (table, d->presentation_leaves[layer_indexes[i]]);
2136 size_t *column_enumeration = pivot_table_enumerate_axis (
2137 table, PIVOT_AXIS_COLUMN, layer_indexes, table->look->omit_empty, NULL);
2138 size_t *row_enumeration = pivot_table_enumerate_axis (
2139 table, PIVOT_AXIS_ROW, layer_indexes, table->look->omit_empty, NULL);
2141 /* Print column headings.
2143 Ordinarily the test for nonnull 'column_headings' would be
2144 unnecessary, because 'column_headings' is null only if the axis's
2145 label_depth is 0, but there is a special case for the column axis only
2146 in pivot_table_assign_label_depth(). */
2147 char ***column_headings = compose_headings (
2148 table, &table->axes[PIVOT_AXIS_COLUMN], column_enumeration);
2149 if (column_headings)
2151 for (size_t y = 0; y < table->axes[PIVOT_AXIS_COLUMN].label_depth; y++)
2153 indent (indentation + 1);
2154 for (size_t x = 0; x < table->axes[PIVOT_AXIS_COLUMN].extent; x++)
2157 fputs ("; ", stdout);
2158 if (column_headings && column_headings[y] && column_headings[y][x])
2159 fputs (column_headings[y][x], stdout);
2163 free_headings (&table->axes[PIVOT_AXIS_COLUMN], column_headings);
2166 indent (indentation + 1);
2167 printf ("-----------------------------------------------\n");
2169 char ***row_headings = compose_headings (
2170 table, &table->axes[PIVOT_AXIS_ROW], row_enumeration);
2173 const size_t *pindexes[PIVOT_N_AXES]
2174 = { [PIVOT_AXIS_LAYER] = layer_indexes };
2175 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
2176 &table->axes[PIVOT_AXIS_ROW])
2178 indent (indentation + 1);
2181 for (size_t y = 0; y < table->axes[PIVOT_AXIS_ROW].label_depth; y++)
2184 fputs ("; ", stdout);
2185 if (row_headings[y][x])
2186 fputs (row_headings[y][x], stdout);
2192 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
2194 &table->axes[PIVOT_AXIS_COLUMN])
2199 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
2200 const struct pivot_value *value = pivot_table_get (
2203 pivot_value_dump (value, table);
2210 free (column_enumeration);
2211 free (row_enumeration);
2212 free_headings (&table->axes[PIVOT_AXIS_ROW], row_headings);
2215 pivot_table_dump_value (table->caption, "caption", table, indentation);
2217 for (size_t i = 0; i < table->n_footnotes; i++)
2219 const struct pivot_footnote *f = table->footnotes[i];
2220 indent (indentation);
2223 pivot_value_dump (f->marker, table);
2225 printf ("%zu", f->idx);
2227 pivot_value_dump (f->content, table);
2235 consume_int (const char *p, size_t *n)
2238 while (c_isdigit (*p))
2239 *n = *n * 10 + (*p++ - '0');
2244 pivot_format_inner_template (struct string *out, const char *template,
2246 struct pivot_value **values, size_t n_values,
2247 const struct pivot_table *pt)
2249 size_t args_consumed = 0;
2250 while (*template && *template != ':')
2252 if (*template == '\\' && template[1])
2254 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2257 else if (*template == escape)
2260 template = consume_int (template + 1, &index);
2261 if (index >= 1 && index <= n_values)
2263 pivot_value_format (values[index - 1], pt, out);
2264 args_consumed = MAX (args_consumed, index);
2268 ds_put_byte (out, *template++);
2270 return args_consumed;
2274 pivot_extract_inner_template (const char *template, const char **p)
2280 if (*template == '\\' && template[1] != '\0')
2282 else if (*template == ':')
2283 return template + 1;
2284 else if (*template == '\0')
2292 pivot_format_template (struct string *out, const char *template,
2293 const struct pivot_argument *args, size_t n_args,
2294 const struct pivot_table *pt)
2298 if (*template == '\\' && template[1] != '\0')
2300 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2303 else if (*template == '^')
2306 template = consume_int (template + 1, &index);
2307 if (index >= 1 && index <= n_args && args[index - 1].n > 0)
2308 pivot_value_format (args[index - 1].values[0], pt, out);
2310 else if (*template == '[')
2312 const char *tmpl[2];
2313 template = pivot_extract_inner_template (template + 1, &tmpl[0]);
2314 template = pivot_extract_inner_template (template, &tmpl[1]);
2315 template += *template == ']';
2318 template = consume_int (template, &index);
2319 if (index < 1 || index > n_args)
2322 const struct pivot_argument *arg = &args[index - 1];
2323 size_t left = arg->n;
2326 struct pivot_value **values = arg->values + (arg->n - left);
2327 int tmpl_idx = left == arg->n && *tmpl[0] != ':' ? 0 : 1;
2328 char escape = "%^"[tmpl_idx];
2329 size_t used = pivot_format_inner_template (
2330 out, tmpl[tmpl_idx], escape, values, left, pt);
2331 if (!used || used > left)
2337 ds_put_byte (out, *template++);
2341 static enum settings_value_show
2342 interpret_show (enum settings_value_show global_show,
2343 enum settings_value_show table_show,
2344 enum settings_value_show value_show,
2347 return (!has_label ? SETTINGS_VALUE_SHOW_VALUE
2348 : value_show != SETTINGS_VALUE_SHOW_DEFAULT ? value_show
2349 : table_show != SETTINGS_VALUE_SHOW_DEFAULT ? table_show
2353 /* Appends to OUT the actual text content from the given Pango MARKUP. */
2355 get_text_from_markup (const char *markup, struct string *out)
2357 xmlParserCtxt *parser = xmlCreatePushParserCtxt (NULL, NULL, NULL, 0, NULL);
2360 ds_put_cstr (out, markup);
2364 xmlParseChunk (parser, "<xml>", strlen ("<xml>"), false);
2365 xmlParseChunk (parser, markup, strlen (markup), false);
2366 xmlParseChunk (parser, "</xml>", strlen ("</xml>"), true);
2368 if (parser->wellFormed)
2370 xmlChar *s = xmlNodeGetContent (xmlDocGetRootElement (parser->myDoc));
2371 ds_put_cstr (out, CHAR_CAST (char *, s));
2375 ds_put_cstr (out, markup);
2376 xmlFreeDoc (parser->myDoc);
2377 xmlFreeParserCtxt (parser);
2380 static const struct pivot_table pivot_value_format_defaults = {
2381 .show_values = SETTINGS_VALUE_SHOW_DEFAULT,
2382 .show_variables = SETTINGS_VALUE_SHOW_DEFAULT,
2383 .settings = FMT_SETTINGS_INIT,
2386 /* Appends a text representation of the body of VALUE to OUT. Settings on PT
2387 control whether variable and value labels are included (pass NULL for PT to
2388 get default formatting in the absence of a pivot table).
2390 The "body" omits subscripts and superscripts and footnotes.
2392 Returns true if OUT is a number (or a number plus a value label), false
2395 pivot_value_format_body (const struct pivot_value *value,
2396 const struct pivot_table *pt_,
2399 const struct pivot_table *pt = pt_ ? pt_ : &pivot_value_format_defaults;
2400 enum settings_value_show show;
2401 bool numeric = false;
2403 switch (value->type)
2405 case PIVOT_VALUE_NUMERIC:
2406 show = interpret_show (settings_get_show_values (),
2408 value->numeric.show,
2409 value->numeric.value_label != NULL);
2410 if (show & SETTINGS_VALUE_SHOW_VALUE)
2412 const struct fmt_spec *f = &value->numeric.format;
2413 const struct fmt_spec *format
2415 && value->numeric.honor_small
2416 && value->numeric.x != 0
2417 && fabs (value->numeric.x) < pt->small
2418 ? &(struct fmt_spec) { .type = FMT_E, .w = 40, .d = f->d }
2421 char *s = data_out (&(union value) { .f = value->numeric.x },
2422 "UTF-8", format, &pt->settings);
2423 ds_put_cstr (out, s + strspn (s, " "));
2426 if (show & SETTINGS_VALUE_SHOW_LABEL)
2428 if (show & SETTINGS_VALUE_SHOW_VALUE)
2429 ds_put_byte (out, ' ');
2430 ds_put_cstr (out, value->numeric.value_label);
2432 numeric = !(show & SETTINGS_VALUE_SHOW_LABEL);
2435 case PIVOT_VALUE_STRING:
2436 show = interpret_show (settings_get_show_values (),
2439 value->string.value_label != NULL);
2440 if (show & SETTINGS_VALUE_SHOW_VALUE)
2442 if (value->string.hex)
2444 for (const uint8_t *p = CHAR_CAST (uint8_t *, value->string.s);
2446 ds_put_format (out, "%02X", *p);
2449 ds_put_cstr (out, value->string.s);
2451 if (show & SETTINGS_VALUE_SHOW_LABEL)
2453 if (show & SETTINGS_VALUE_SHOW_VALUE)
2454 ds_put_byte (out, ' ');
2455 ds_put_cstr (out, value->string.value_label);
2459 case PIVOT_VALUE_VARIABLE:
2460 show = interpret_show (settings_get_show_variables (),
2462 value->variable.show,
2463 value->variable.var_label != NULL);
2464 if (show & SETTINGS_VALUE_SHOW_VALUE)
2465 ds_put_cstr (out, value->variable.var_name);
2466 if (show & SETTINGS_VALUE_SHOW_LABEL)
2468 if (show & SETTINGS_VALUE_SHOW_VALUE)
2469 ds_put_byte (out, ' ');
2470 ds_put_cstr (out, value->variable.var_label);
2474 case PIVOT_VALUE_TEXT:
2475 if (value->ex && value->ex->font_style && value->ex->font_style->markup)
2476 get_text_from_markup (value->text.local, out);
2478 ds_put_cstr (out, value->text.local);
2481 case PIVOT_VALUE_TEMPLATE:
2482 pivot_format_template (out, value->template.local, value->template.args,
2483 value->template.n_args, pt);
2490 /* Appends a text representation of VALUE to OUT. Settings on PT control
2491 whether variable and value labels are included (pass NULL for PT to get
2492 default formatting in the absence of a pivot table).
2494 Subscripts and footnotes are included.
2496 Returns true if OUT is a number (or a number plus a value label), false
2499 pivot_value_format (const struct pivot_value *value,
2500 const struct pivot_table *pt_,
2503 const struct pivot_table *pt = pt_ ? pt_ : &pivot_value_format_defaults;
2504 bool numeric = pivot_value_format_body (value, pt, out);
2506 const struct pivot_value_ex *ex = value->ex;
2509 if (ex->n_subscripts)
2511 for (size_t i = 0; i < ex->n_subscripts; i++)
2512 ds_put_format (out, "%c%s", i ? ',' : '_', ex->subscripts[i]);
2515 for (size_t i = 0; i < ex->n_footnotes; i++)
2517 ds_put_byte (out, '[');
2519 size_t idx = ex->footnote_indexes[i];
2520 const struct pivot_footnote *f = pt->footnotes[idx];
2521 pivot_footnote_format_marker (f, pt, out);
2523 ds_put_byte (out, ']');
2530 /* Returns a text representation of VALUE. The caller must free the string,
2531 with free(). Settings on PT control whether variable and value labels are
2532 included (pass NULL for PT to get default formatting in the absence of a
2535 pivot_value_to_string (const struct pivot_value *value,
2536 const struct pivot_table *pt)
2538 struct string s = DS_EMPTY_INITIALIZER;
2539 pivot_value_format (value, pt, &s);
2540 return ds_steal_cstr (&s);
2543 struct pivot_value *
2544 pivot_value_clone (const struct pivot_value *old)
2549 struct pivot_value *new = xmemdup (old, sizeof *new);
2551 new->ex = pivot_value_ex_clone (old->ex);
2555 case PIVOT_VALUE_NUMERIC:
2556 new->numeric.var_name = xstrdup_if_nonnull (new->numeric.var_name);
2557 new->numeric.value_label = xstrdup_if_nonnull (new->numeric.value_label);
2560 case PIVOT_VALUE_STRING:
2561 new->string.s = xstrdup (new->string.s);
2562 new->string.var_name = xstrdup_if_nonnull (new->string.var_name);
2563 new->string.value_label = xstrdup_if_nonnull (new->string.value_label);
2566 case PIVOT_VALUE_VARIABLE:
2567 new->variable.var_name = xstrdup_if_nonnull (new->variable.var_name);
2568 new->variable.var_label = xstrdup_if_nonnull (new->variable.var_label);
2571 case PIVOT_VALUE_TEXT:
2572 new->text.local = xstrdup (old->text.local);
2573 new->text.c = (old->text.c == old->text.local ? new->text.local
2574 : xstrdup_if_nonnull (old->text.c));
2575 new->text.id = (old->text.id == old->text.local ? new->text.local
2576 : old->text.id == old->text.c ? new->text.c
2577 : xstrdup_if_nonnull (old->text.id));
2580 case PIVOT_VALUE_TEMPLATE:
2581 new->template.local = xstrdup (old->template.local);
2582 new->template.id = (old->template.id == old->template.local
2583 ? new->template.local
2584 : xstrdup (old->template.id));
2585 new->template.args = xmalloc (new->template.n_args
2586 * sizeof *new->template.args);
2587 for (size_t i = 0; i < old->template.n_args; i++)
2588 pivot_argument_copy (&new->template.args[i],
2589 &old->template.args[i]);
2598 /* Frees the data owned by V. */
2600 pivot_value_destroy (struct pivot_value *value)
2604 pivot_value_ex_destroy (value->ex);
2605 switch (value->type)
2607 case PIVOT_VALUE_NUMERIC:
2608 free (value->numeric.var_name);
2609 free (value->numeric.value_label);
2612 case PIVOT_VALUE_STRING:
2613 free (value->string.s);
2614 free (value->string.var_name);
2615 free (value->string.value_label);
2618 case PIVOT_VALUE_VARIABLE:
2619 free (value->variable.var_name);
2620 free (value->variable.var_label);
2623 case PIVOT_VALUE_TEXT:
2624 free (value->text.local);
2625 if (value->text.c != value->text.local)
2626 free (value->text.c);
2627 if (value->text.id != value->text.local
2628 && value->text.id != value->text.c)
2629 free (value->text.id);
2632 case PIVOT_VALUE_TEMPLATE:
2633 free (value->template.local);
2634 if (value->template.id != value->template.local)
2635 free (value->template.id);
2636 for (size_t i = 0; i < value->template.n_args; i++)
2637 pivot_argument_uninit (&value->template.args[i]);
2638 free (value->template.args);
2648 /* Sets AREA to the style to use for VALUE, with defaults coming from
2649 DEFAULT_STYLE for the parts of the style that VALUE doesn't override. */
2651 pivot_value_get_style (struct pivot_value *value,
2652 const struct font_style *base_font_style,
2653 const struct cell_style *base_cell_style,
2654 struct table_area_style *area)
2656 const struct pivot_value_ex *ex = pivot_value_ex (value);
2657 font_style_copy (NULL, &area->font_style,
2658 ex->font_style ? ex->font_style : base_font_style);
2659 area->cell_style = *(ex->cell_style ? ex->cell_style : base_cell_style);
2662 /* Copies AREA into VALUE's style. */
2664 pivot_value_set_style (struct pivot_value *value,
2665 const struct table_area_style *area)
2667 pivot_value_set_font_style (value, &area->font_style);
2668 pivot_value_set_cell_style (value, &area->cell_style);
2672 pivot_value_set_font_style (struct pivot_value *value,
2673 const struct font_style *font_style)
2675 struct pivot_value_ex *ex = pivot_value_ex_rw (value);
2677 font_style_uninit (ex->font_style);
2679 ex->font_style = xmalloc (sizeof *ex->font_style);
2680 font_style_copy (NULL, ex->font_style, font_style);
2684 pivot_value_set_cell_style (struct pivot_value *value,
2685 const struct cell_style *cell_style)
2687 struct pivot_value_ex *ex = pivot_value_ex_rw (value);
2688 if (!ex->cell_style)
2689 ex->cell_style = xmalloc (sizeof *ex->cell_style);
2690 *ex->cell_style = *cell_style;
2694 pivot_argument_copy (struct pivot_argument *dst,
2695 const struct pivot_argument *src)
2697 *dst = (struct pivot_argument) {
2699 .values = xmalloc (src->n * sizeof *dst->values),
2702 for (size_t i = 0; i < src->n; i++)
2703 dst->values[i] = pivot_value_clone (src->values[i]);
2706 /* Frees the data owned by ARG (but not ARG itself). */
2708 pivot_argument_uninit (struct pivot_argument *arg)
2712 for (size_t i = 0; i < arg->n; i++)
2713 pivot_value_destroy (arg->values[i]);
2718 /* Creates and returns a new pivot_value whose contents is the null-terminated
2719 string TEXT. Takes ownership of TEXT.
2721 This function is for text strings provided by the user (with the exception
2722 that pivot_value_new_variable() should be used for variable names). For
2723 strings that are part of the PSPP user interface, such as names of
2724 procedures, statistics, annotations, error messages, etc., use
2725 pivot_value_new_text(). */
2726 struct pivot_value *
2727 pivot_value_new_user_text_nocopy (char *text)
2729 struct pivot_value *value = xmalloc (sizeof *value);
2730 *value = (struct pivot_value) {
2732 .type = PIVOT_VALUE_TEXT,
2736 .user_provided = true,
2742 /* Creates and returns a new pivot_value whose contents is the LENGTH bytes of
2743 TEXT. Use SIZE_MAX if TEXT is null-teriminated and its length is not known
2746 This function is for text strings provided by the user (with the exception
2747 that pivot_value_new_variable() should be used for variable names). For
2748 strings that are part of the PSPP user interface, such as names of
2749 procedures, statistics, annotations, error messages, etc., use
2750 pivot_value_new_text().
2752 The caller retains ownership of TEXT. */
2753 struct pivot_value *
2754 pivot_value_new_user_text (const char *text, size_t length)
2756 return pivot_value_new_user_text_nocopy (
2757 xmemdup0 (text, length != SIZE_MAX ? length : strlen (text)));
2760 /* Creates and returns new pivot_value whose contents is TEXT, which should be
2761 a translatable string, but not actually translated yet, e.g. enclosed in
2762 N_(). This function is for text strings that are part of the PSPP user
2763 interface, such as names of procedures, statistics, annotations, error
2764 messages, etc. For strings that come from the user, use
2765 pivot_value_new_user_text(). */
2766 struct pivot_value *
2767 pivot_value_new_text (const char *text)
2769 char *c = xstrdup (text);
2770 char *local = xstrdup (gettext (c));
2772 struct pivot_value *value = xmalloc (sizeof *value);
2773 *value = (struct pivot_value) {
2775 .type = PIVOT_VALUE_TEXT,
2779 .user_provided = false,
2785 /* Same as pivot_value_new_text() but its argument is a printf()-like format
2786 string. The format string should generally be enclosed in N_(). */
2787 struct pivot_value * PRINTF_FORMAT (1, 2)
2788 pivot_value_new_text_format (const char *format, ...)
2791 va_start (args, format);
2792 char *c = xvasprintf (format, args);
2795 va_start (args, format);
2796 char *local = xvasprintf (gettext (format), args);
2799 struct pivot_value *value = xmalloc (sizeof *value);
2800 *value = (struct pivot_value) {
2802 .type = PIVOT_VALUE_TEXT,
2806 .user_provided = false,
2812 /* Returns a new pivot_value that represents X.
2814 The format to use for X is unspecified. Usually the easiest way to specify
2815 a format is through assigning a result class to one of the categories that
2816 the pivot_value will end up in. If that is not suitable, then the caller
2817 can use pivot_value_set_rc() or assign directly to value->numeric.format. */
2818 struct pivot_value *
2819 pivot_value_new_number (double x)
2821 struct pivot_value *value = xmalloc (sizeof *value);
2822 *value = (struct pivot_value) {
2824 .type = PIVOT_VALUE_NUMERIC,
2831 /* Returns a new pivot_value that represents X, formatted as an integer. */
2832 struct pivot_value *
2833 pivot_value_new_integer (double x)
2835 struct pivot_value *value = pivot_value_new_number (x);
2836 value->numeric.format = (struct fmt_spec) { .type = FMT_F, .w = 40 };
2840 /* Returns a new pivot_value that represents VALUE, formatted as for
2842 struct pivot_value *
2843 pivot_value_new_var_value (const struct variable *variable,
2844 const union value *value)
2846 struct pivot_value *pv = pivot_value_new_value (
2847 value, var_get_width (variable), var_get_print_format (variable),
2848 var_get_encoding (variable));
2850 char *var_name = xstrdup (var_get_name (variable));
2851 if (var_is_alpha (variable))
2852 pv->string.var_name = var_name;
2854 pv->numeric.var_name = var_name;
2856 const char *label = var_lookup_value_label (variable, value);
2859 if (var_is_alpha (variable))
2860 pv->string.value_label = xstrdup (label);
2862 pv->numeric.value_label = xstrdup (label);
2868 /* Returns a new pivot_value that represents VALUE, with the given WIDTH,
2869 formatted with FORMAT. For a string value, ENCODING must be its character
2871 struct pivot_value *
2872 pivot_value_new_value (const union value *value, int width,
2873 const struct fmt_spec *format, const char *encoding)
2875 struct pivot_value *pv = XZALLOC (struct pivot_value);
2878 char *s = recode_string (UTF8, encoding, CHAR_CAST (char *, value->s),
2880 size_t n = strlen (s);
2881 while (n > 0 && s[n - 1] == ' ')
2884 pv->type = PIVOT_VALUE_STRING;
2886 pv->string.hex = format->type == FMT_AHEX;
2890 pv->type = PIVOT_VALUE_NUMERIC;
2891 pv->numeric.x = value->f;
2892 pv->numeric.format = *format;
2898 /* Returns a new pivot_value for VARIABLE. */
2899 struct pivot_value *
2900 pivot_value_new_variable (const struct variable *variable)
2902 return pivot_value_new_variable__ (var_get_name (variable),
2903 var_get_label (variable));
2906 /* Returns a new pivot_value for a variable with the given NAME and optional
2908 struct pivot_value *
2909 pivot_value_new_variable__ (const char *name, const char *label)
2911 struct pivot_value *value = xmalloc (sizeof *value);
2912 *value = (struct pivot_value) {
2914 .type = PIVOT_VALUE_VARIABLE,
2915 .var_name = xstrdup (name),
2916 .var_label = xstrdup_if_nonnull (label),
2922 /* Attaches a reference to FOOTNOTE to V. */
2924 pivot_value_add_footnote (struct pivot_value *v,
2925 const struct pivot_footnote *footnote)
2927 struct pivot_value_ex *ex = pivot_value_ex_rw (v);
2929 /* Some legacy tables include numerous duplicate footnotes. Suppress
2931 for (size_t i = 0; i < ex->n_footnotes; i++)
2932 if (ex->footnote_indexes[i] == footnote->idx)
2935 ex->footnote_indexes = xrealloc (
2936 ex->footnote_indexes,
2937 (ex->n_footnotes + 1) * sizeof *ex->footnote_indexes);
2938 ex->footnote_indexes[ex->n_footnotes++] = footnote->idx;
2939 pivot_value_sort_footnotes (v);
2943 compare_footnote_indexes (const void *a_, const void *b_)
2945 const size_t *ap = a_;
2946 const size_t *bp = b_;
2949 return a < b ? -1 : a > b;
2952 /* Sorts the footnote references in V in the standard ascending order.
2954 This is only necessary if code adds (plural) footnotes to a pivot_value by
2955 itself, because pivot_value_add_footnote() does it automatically. */
2957 pivot_value_sort_footnotes (struct pivot_value *v)
2959 if (v->ex && v->ex->n_footnotes > 1)
2960 qsort (v->ex->footnote_indexes, v->ex->n_footnotes,
2961 sizeof *v->ex->footnote_indexes, compare_footnote_indexes);
2964 /* If VALUE is a numeric value, and RC is a result class such as
2965 PIVOT_RC_COUNT, changes VALUE's format to the result class's. */
2967 pivot_value_set_rc (const struct pivot_table *table, struct pivot_value *value,
2970 if (value->type == PIVOT_VALUE_NUMERIC)
2971 pivot_table_use_rc (table, rc,
2972 &value->numeric.format, &value->numeric.honor_small);
2975 /* pivot_value_ex. */
2977 struct pivot_value_ex *
2978 pivot_value_ex_rw (struct pivot_value *value)
2981 value->ex = xzalloc (sizeof *value->ex);
2985 struct pivot_value_ex *
2986 pivot_value_ex_clone (const struct pivot_value_ex *old)
2988 struct font_style *font_style = NULL;
2989 if (old->font_style)
2991 font_style = xmalloc (sizeof *font_style);
2992 font_style_copy (NULL, font_style, old->font_style);
2995 char **subscripts = NULL;
2996 if (old->n_subscripts)
2998 subscripts = xnmalloc (old->n_subscripts, sizeof *subscripts);
2999 for (size_t i = 0; i < old->n_subscripts; i++)
3000 subscripts[i] = xstrdup (old->subscripts[i]);
3003 struct pivot_value_ex *new = xmalloc (sizeof *new);
3004 *new = (struct pivot_value_ex) {
3005 .font_style = font_style,
3006 .cell_style = (old->cell_style
3007 ? xmemdup (old->cell_style, sizeof *new->cell_style)
3009 .subscripts = subscripts,
3010 .n_subscripts = old->n_subscripts,
3011 .footnote_indexes = (
3013 ? xmemdup (old->footnote_indexes,
3014 old->n_footnotes * sizeof *new->footnote_indexes)
3016 .n_footnotes = old->n_footnotes
3022 pivot_value_ex_destroy (struct pivot_value_ex *ex)
3026 font_style_uninit (ex->font_style);
3027 free (ex->font_style);
3028 free (ex->cell_style);
3029 free (ex->footnote_indexes);
3031 for (size_t i = 0; i < ex->n_subscripts; i++)
3032 free (ex->subscripts[i]);
3033 free (ex->subscripts);
3040 struct pivot_splits_value
3042 struct hmap_node hmap_node;
3047 struct pivot_splits_var
3049 struct pivot_dimension *dimension;
3050 const struct variable *var;
3055 struct pivot_splits_dup
3057 struct hmap_node hmap_node;
3058 union value *values;
3063 struct pivot_splits_var *vars;
3069 size_t dindexes[MAX_SPLITS];
3074 /* Adds a dimension for each layered split file variable in DICT to PT on AXIS.
3075 These dimensions should be the last dimensions added to PT (the
3076 pivot_splits_put*() functions rely on this). Returns a new pivot_splits
3077 structure if any dimensions were added, otherwise a null pointer.
3079 See the large comment on split file handling in pivot-table.h for more
3081 struct pivot_splits *
3082 pivot_splits_create (struct pivot_table *pt,
3083 enum pivot_axis_type axis,
3084 const struct dictionary *dict)
3086 if (dict_get_split_type (dict) != SPLIT_LAYERED)
3089 size_t n = dict_get_n_splits (dict);
3090 assert (n <= MAX_SPLITS);
3092 const struct variable *const *vars = dict_get_split_vars (dict);
3093 struct pivot_splits_var *psvars = xnmalloc (n, sizeof *psvars);
3094 for (size_t i = n - 1; i < n; i--)
3096 const struct variable *var = vars[i];
3097 struct pivot_splits_var *psvar = &psvars[i];
3099 struct pivot_dimension *d = pivot_dimension_create__ (
3100 pt, axis, pivot_value_new_variable (var));
3101 d->root->show_label = true;
3103 *psvar = (struct pivot_splits_var) {
3104 .width = var_get_width (var),
3105 .values = HMAP_INITIALIZER (psvar->values),
3111 struct pivot_splits *ps = xmalloc (sizeof *ps);
3112 *ps = (struct pivot_splits) {
3115 .encoding = xstrdup (dict_get_encoding (dict)),
3116 .dups = HMAP_INITIALIZER (ps->dups),
3117 .dindexes = { [0] = SIZE_MAX },
3125 pivot_splits_destroy (struct pivot_splits *ps)
3130 if (ps->warnings_left < 0)
3131 msg (SW, ngettext ("Suppressed %d additional warning about duplicate "
3133 "Suppressed %d additional warnings about duplicate "
3134 "split values.", -ps->warnings_left),
3135 -ps->warnings_left);
3137 struct pivot_splits_dup *dup, *next_dup;
3138 HMAP_FOR_EACH_SAFE (dup, next_dup, struct pivot_splits_dup, hmap_node,
3141 for (size_t i = 0; i < ps->n; i++)
3142 value_destroy (&dup->values[i], ps->vars[i].width);
3146 hmap_destroy (&ps->dups);
3148 for (size_t i = 0; i < ps->n; i++)
3150 struct pivot_splits_var *psvar = &ps->vars[i];
3151 struct pivot_splits_value *psval, *next;
3152 HMAP_FOR_EACH_SAFE (psval, next, struct pivot_splits_value, hmap_node,
3155 value_destroy (&psval->value, psvar->width);
3156 hmap_delete (&psvar->values, &psval->hmap_node);
3159 hmap_destroy (&psvar->values);
3163 free (ps->encoding);
3167 static struct pivot_splits_value *
3168 pivot_splits_value_find (struct pivot_splits_var *psvar,
3169 const union value *value)
3171 struct pivot_splits_value *psval;
3172 HMAP_FOR_EACH_WITH_HASH (psval, struct pivot_splits_value, hmap_node,
3173 value_hash (value, psvar->width, 0), &psvar->values)
3174 if (value_equal (&psval->value, value, psvar->width))
3180 pivot_splits_find_dup (struct pivot_splits *ps, const struct ccase *example)
3182 unsigned int hash = 0;
3183 for (size_t i = 0; i < ps->n; i++)
3185 struct pivot_splits_var *psvar = &ps->vars[i];
3186 const union value *value = case_data (example, psvar->var);
3187 hash = value_hash (value, psvar->width, hash);
3189 struct pivot_splits_dup *dup;
3190 HMAP_FOR_EACH_WITH_HASH (dup, struct pivot_splits_dup, hmap_node, hash,
3194 for (size_t i = 0; i < ps->n && equal; i++)
3196 struct pivot_splits_var *psvar = &ps->vars[i];
3197 const union value *value = case_data (example, psvar->var);
3198 equal = value_equal (value, &dup->values[i], psvar->width);
3204 union value *values = xmalloc (ps->n * sizeof *values);
3205 for (size_t i = 0; i < ps->n; i++)
3207 struct pivot_splits_var *psvar = &ps->vars[i];
3208 const union value *value = case_data (example, psvar->var);
3209 value_clone (&values[i], value, psvar->width);
3212 dup = xmalloc (sizeof *dup);
3213 dup->values = values;
3214 hmap_insert (&ps->dups, &dup->hmap_node, hash);
3218 /* Begins adding data for a new split file group to the pivot table associated
3219 with PS. EXAMPLE should be a case from the new split file group.
3221 This is a no-op if PS is NULL.
3223 See the large comment on split file handling in pivot-table.h for more
3226 pivot_splits_new_split (struct pivot_splits *ps, const struct ccase *example)
3231 for (size_t i = 0; i < ps->n; i++)
3233 struct pivot_splits_var *psvar = &ps->vars[i];
3234 const union value *value = case_data (example, psvar->var);
3235 struct pivot_splits_value *psval = pivot_splits_value_find (psvar, value);
3238 psval = xmalloc (sizeof *psval);
3239 hmap_insert (&psvar->values, &psval->hmap_node,
3240 value_hash (value, psvar->width, 0));
3241 value_clone (&psval->value, value, psvar->width);
3242 psval->leaf = pivot_category_create_leaf (
3243 psvar->dimension->root,
3244 pivot_value_new_var_value (psvar->var, value));
3247 ps->dindexes[i] = psval->leaf;
3250 if (pivot_splits_find_dup (ps, example))
3252 if (ps->warnings_left-- > 0)
3254 struct string s = DS_EMPTY_INITIALIZER;
3255 for (size_t i = 0; i < ps->n; i++)
3258 ds_put_cstr (&s, ", ");
3260 struct pivot_splits_var *psvar = &ps->vars[i];
3261 const union value *value = case_data (example, psvar->var);
3262 ds_put_format (&s, "%s = ", var_get_name (psvar->var));
3264 char *s2 = data_out (value, ps->encoding,
3265 var_get_print_format (psvar->var),
3266 settings_get_fmt_settings ());
3267 ds_put_cstr (&s, s2 + strspn (s2, " "));
3270 msg (SW, _("When SPLIT FILE is in effect, the input data must be "
3271 "sorted by the split variables (for example, using SORT "
3272 "CASES), but multiple runs of cases with the same split "
3273 "values were found separated by cases with different "
3274 "values. Each run will be analyzed separately. The "
3275 "duplicate split values are: %s"), ds_cstr (&s));
3279 struct pivot_splits_var *psvar = &ps->vars[0];
3280 const union value *value = case_data (example, psvar->var);
3281 ps->dindexes[0] = pivot_category_create_leaf (
3282 psvar->dimension->root,
3283 pivot_value_new_var_value (psvar->var, value));
3288 pivot_splits_get_dindexes (const struct pivot_splits *ps, size_t *dindexes)
3293 assert (ps->dindexes[0] != SIZE_MAX);
3294 for (size_t i = 0; i < ps->n; i++)
3295 dindexes[ps->n - i - 1] = ps->dindexes[i];
3299 /* Puts VALUE in the cell in TABLE with index IDX1. TABLE must have 1
3300 dimension plus the split file dimensions from PS (if nonnull). Takes
3303 See the large comment on split file handling in pivot-table.h for more
3306 pivot_splits_put1 (struct pivot_splits *ps, struct pivot_table *table,
3307 size_t idx1, struct pivot_value *value)
3309 size_t dindexes[1 + MAX_SPLITS];
3310 size_t *p = dindexes;
3312 p += pivot_splits_get_dindexes (ps, p);
3313 pivot_table_put (table, dindexes, p - dindexes, value);
3316 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2). TABLE must have 2
3317 dimensions plus the split file dimensions from PS (if nonnull). Takes
3320 See the large comment on split file handling in pivot-table.h for more
3323 pivot_splits_put2 (struct pivot_splits *ps, struct pivot_table *table,
3324 size_t idx1, size_t idx2, struct pivot_value *value)
3326 size_t dindexes[2 + MAX_SPLITS];
3327 size_t *p = dindexes;
3330 p += pivot_splits_get_dindexes (ps, p);
3331 pivot_table_put (table, dindexes, p - dindexes, value);
3334 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3). TABLE must
3335 have 3 dimensions plus the split file dimensions from PS (if nonnull).
3336 Takes ownership of VALUE.
3338 See the large comment on split file handling in pivot-table.h for more
3341 pivot_splits_put3 (struct pivot_splits *ps, struct pivot_table *table,
3342 size_t idx1, size_t idx2, size_t idx3,
3343 struct pivot_value *value)
3345 size_t dindexes[3 + MAX_SPLITS];
3346 size_t *p = dindexes;
3350 p += pivot_splits_get_dindexes (ps, p);
3351 pivot_table_put (table, dindexes, p - dindexes, value);
3354 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3, IDX4). TABLE
3355 must have 4 dimensions plus the split file dimensions from PS (if nonnull).
3356 Takes ownership of VALUE.
3358 See the large comment on split file handling in pivot-table.h for more
3361 pivot_splits_put4 (struct pivot_splits *ps, struct pivot_table *table,
3362 size_t idx1, size_t idx2, size_t idx3, size_t idx4,
3363 struct pivot_value *value)
3365 size_t dindexes[4 + MAX_SPLITS];
3366 size_t *p = dindexes;
3371 p += pivot_splits_get_dindexes (ps, p);
3372 pivot_table_put (table, dindexes, p - dindexes, value);