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 TABLE.
1377 This has no effect if WFMT is NULL. */
1379 pivot_table_set_weight_format (struct pivot_table *table,
1380 const struct fmt_spec *wfmt)
1383 table->weight_format = *wfmt;
1386 /* Returns true if TABLE has no cells, false otherwise. */
1388 pivot_table_is_empty (const struct pivot_table *table)
1390 return hmap_is_empty (&table->cells);
1394 pivot_cell_hash_indexes (const size_t *indexes, size_t n_idx)
1396 return hash_bytes (indexes, n_idx * sizeof *indexes, 0);
1400 equal_indexes (const size_t *a, const unsigned int *b, size_t n)
1402 for (size_t i = 0; i < n; i++)
1409 static struct pivot_cell *
1410 pivot_table_lookup_cell__ (const struct pivot_table *table,
1411 const size_t *dindexes, unsigned int hash)
1413 struct pivot_cell *cell;
1414 HMAP_FOR_EACH_WITH_HASH (cell, struct pivot_cell, hmap_node, hash,
1416 if (equal_indexes (dindexes, cell->idx, table->n_dimensions))
1421 static struct pivot_cell *
1422 pivot_cell_allocate (size_t n_idx)
1424 struct pivot_cell *cell UNUSED;
1425 return xmalloc (sizeof *cell + n_idx * sizeof *cell->idx);
1428 static struct pivot_cell *
1429 pivot_table_insert_cell (struct pivot_table *table, const size_t *dindexes)
1431 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1432 struct pivot_cell *cell = pivot_table_lookup_cell__ (table, dindexes, hash);
1435 cell = pivot_cell_allocate (table->n_dimensions);
1436 for (size_t i = 0; i < table->n_dimensions; i++)
1437 cell->idx[i] = dindexes[i];
1439 hmap_insert (&table->cells, &cell->hmap_node, hash);
1444 /* Puts VALUE in the cell in TABLE whose indexes are given by the N indexes in
1445 DINDEXES. The order of the indexes is the same as the order in which the
1446 dimensions were created. N must be the number of dimensions in TABLE.
1447 Takes ownership of VALUE.
1449 If VALUE is a numeric value without a specified format, this function checks
1450 each of the categories designated by DINDEXES[] and takes the format from
1451 the first category with a result class. If none has a result class, uses
1452 the overall default numeric format. */
1454 pivot_table_put (struct pivot_table *table, const size_t *dindexes, size_t n,
1455 struct pivot_value *value)
1457 assert (n == table->n_dimensions);
1458 for (size_t i = 0; i < n; i++)
1459 assert (dindexes[i] < table->dimensions[i]->n_leaves);
1461 if (value->type == PIVOT_VALUE_NUMERIC && !value->numeric.format.w)
1463 for (size_t i = 0; i < table->n_dimensions; i++)
1465 const struct pivot_dimension *d = table->dimensions[i];
1466 if (dindexes[i] < d->n_leaves)
1468 const struct pivot_category *c = d->data_leaves[dindexes[i]];
1471 value->numeric.format = c->format;
1472 value->numeric.honor_small = c->honor_small;
1477 value->numeric.format = *settings_get_format ();
1478 value->numeric.honor_small = true;
1483 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1484 pivot_value_destroy (cell->value);
1485 cell->value = value;
1488 /* Puts VALUE in the cell in TABLE with index IDX1. TABLE must have 1
1489 dimension. Takes ownership of VALUE. */
1491 pivot_table_put1 (struct pivot_table *table, size_t idx1,
1492 struct pivot_value *value)
1494 size_t dindexes[] = { idx1 };
1495 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1498 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2). TABLE must have 2
1499 dimensions. Takes ownership of VALUE. */
1501 pivot_table_put2 (struct pivot_table *table, size_t idx1, size_t idx2,
1502 struct pivot_value *value)
1504 size_t dindexes[] = { idx1, idx2 };
1505 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1508 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3). TABLE must
1509 have 3 dimensions. Takes ownership of VALUE. */
1511 pivot_table_put3 (struct pivot_table *table, size_t idx1, size_t idx2,
1512 size_t idx3, struct pivot_value *value)
1514 size_t dindexes[] = { idx1, idx2, idx3 };
1515 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1518 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3, IDX4). TABLE
1519 must have 4 dimensions. Takes ownership of VALUE. */
1521 pivot_table_put4 (struct pivot_table *table, size_t idx1, size_t idx2,
1522 size_t idx3, size_t idx4, struct pivot_value *value)
1524 size_t dindexes[] = { idx1, idx2, idx3, idx4 };
1525 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1528 /* Creates and returns a new footnote in TABLE with the given CONTENT and an
1529 automatically assigned marker.
1531 The footnote will only appear in output if it is referenced. Use
1532 pivot_value_add_footnote() to add a reference to the footnote. */
1533 struct pivot_footnote *
1534 pivot_table_create_footnote (struct pivot_table *table,
1535 struct pivot_value *content)
1537 return pivot_table_create_footnote__ (table, table->n_footnotes,
1542 pivot_footnote_format_marker (const struct pivot_footnote *f,
1543 const struct pivot_table *pt,
1547 pivot_value_format_body (f->marker, pt, s);
1548 else if (pt->look->show_numeric_markers)
1549 ds_put_format (s, "%zu", f->idx + 1);
1552 char text[INT_BUFSIZE_BOUND (size_t)];
1553 str_format_26adic (f->idx + 1, false, text, sizeof text);
1554 ds_put_cstr (s, text);
1559 pivot_footnote_marker_string (const struct pivot_footnote *f,
1560 const struct pivot_table *pt)
1562 struct string s = DS_EMPTY_INITIALIZER;
1563 pivot_footnote_format_marker (f, pt, &s);
1564 return ds_steal_cstr (&s);
1567 /* Creates or modifies a footnote in TABLE with 0-based number IDX (and creates
1568 all lower indexes as a side effect). If MARKER is nonnull, sets the
1569 footnote's marker; if CONTENT is nonnull, sets the footnote's content. */
1570 struct pivot_footnote *
1571 pivot_table_create_footnote__ (struct pivot_table *table, size_t idx,
1572 struct pivot_value *marker,
1573 struct pivot_value *content)
1575 if (idx >= table->n_footnotes)
1577 while (idx >= table->allocated_footnotes)
1578 table->footnotes = x2nrealloc (table->footnotes,
1579 &table->allocated_footnotes,
1580 sizeof *table->footnotes);
1581 while (idx >= table->n_footnotes)
1583 struct pivot_footnote *f = xmalloc (sizeof *f);
1584 *f = (struct pivot_footnote) {
1585 .idx = table->n_footnotes,
1588 table->footnotes[table->n_footnotes++] = f;
1592 struct pivot_footnote *f = table->footnotes[idx];
1595 pivot_value_destroy (f->marker);
1600 pivot_value_destroy (f->content);
1601 f->content = content;
1606 /* Frees the data owned by F. */
1608 pivot_footnote_destroy (struct pivot_footnote *f)
1612 pivot_value_destroy (f->content);
1613 pivot_value_destroy (f->marker);
1618 /* Converts per-axis presentation-order indexes, given in PINDEXES, into data
1619 indexes for each dimension in TABLE in DINDEXES[]. */
1621 pivot_table_convert_indexes_ptod (const struct pivot_table *table,
1622 const size_t *pindexes[PIVOT_N_AXES],
1623 size_t dindexes[/* table->n_dimensions */])
1625 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1627 const struct pivot_axis *axis = &table->axes[i];
1629 for (size_t j = 0; j < axis->n_dimensions; j++)
1631 const struct pivot_dimension *d = axis->dimensions[j];
1632 size_t pindex = pindexes[i][j];
1633 dindexes[d->top_index] = d->presentation_leaves[pindex]->data_index;
1639 pivot_table_enumerate_axis (const struct pivot_table *table,
1640 enum pivot_axis_type axis_type,
1641 const size_t *layer_indexes, bool omit_empty,
1644 const struct pivot_axis *axis = &table->axes[axis_type];
1645 if (!axis->n_dimensions)
1647 size_t *enumeration = xnmalloc (2, sizeof *enumeration);
1649 enumeration[1] = SIZE_MAX;
1654 else if (!axis->extent)
1656 size_t *enumeration = xmalloc (sizeof *enumeration);
1657 *enumeration = SIZE_MAX;
1663 size_t *enumeration = xnmalloc (xsum (xtimes (axis->extent,
1664 axis->n_dimensions), 1),
1665 sizeof *enumeration);
1666 size_t *p = enumeration;
1667 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
1669 size_t *axis_indexes;
1670 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1674 enum pivot_axis_type axis2_type
1675 = pivot_axis_type_transpose (axis_type);
1677 size_t *axis2_indexes;
1678 PIVOT_AXIS_FOR_EACH (axis2_indexes, &table->axes[axis2_type])
1680 const size_t *pindexes[PIVOT_N_AXES];
1681 pindexes[PIVOT_AXIS_LAYER] = layer_indexes;
1682 pindexes[axis_type] = axis_indexes;
1683 pindexes[axis2_type] = axis2_indexes;
1684 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
1685 if (pivot_table_get (table, dindexes))
1691 free (axis2_indexes);
1694 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1695 p += axis->n_dimensions;
1697 if (omit_empty && p == enumeration)
1699 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1701 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1702 p += axis->n_dimensions;
1707 *n = (p - enumeration) / axis->n_dimensions;
1713 static struct pivot_cell *
1714 pivot_table_lookup_cell (const struct pivot_table *table,
1715 const size_t *dindexes)
1717 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1718 return pivot_table_lookup_cell__ (table, dindexes, hash);
1721 const struct pivot_value *
1722 pivot_table_get (const struct pivot_table *table, const size_t *dindexes)
1724 const struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1725 return cell ? cell->value : NULL;
1728 struct pivot_value *
1729 pivot_table_get_rw (struct pivot_table *table, const size_t *dindexes)
1731 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1733 cell->value = pivot_value_new_user_text ("", -1);
1738 pivot_table_delete_cell (struct pivot_table *table, struct pivot_cell *cell)
1740 hmap_delete (&table->cells, &cell->hmap_node);
1741 pivot_value_destroy (cell->value);
1746 pivot_table_delete (struct pivot_table *table, const size_t *dindexes)
1748 struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1751 pivot_table_delete_cell (table, cell);
1759 distribute_extra_depth (struct pivot_category *category, size_t extra_depth)
1761 if (pivot_category_is_group (category) && category->n_subs)
1762 for (size_t i = 0; i < category->n_subs; i++)
1763 distribute_extra_depth (category->subs[i], extra_depth);
1765 category->extra_depth += extra_depth;
1769 pivot_category_assign_label_depth (struct pivot_category *category,
1770 bool dimension_labels_in_corner)
1772 category->extra_depth = 0;
1774 if (pivot_category_is_group (category))
1777 for (size_t i = 0; i < category->n_subs; i++)
1779 pivot_category_assign_label_depth (category->subs[i], false);
1780 depth = MAX (depth, category->subs[i]->label_depth);
1783 for (size_t i = 0; i < category->n_subs; i++)
1785 struct pivot_category *sub = category->subs[i];
1787 size_t extra_depth = depth - sub->label_depth;
1789 distribute_extra_depth (sub, extra_depth);
1791 sub->label_depth = depth;
1794 category->show_label_in_corner = (category->show_label
1795 && dimension_labels_in_corner);
1796 category->label_depth
1797 = (category->show_label && !category->show_label_in_corner
1798 ? depth + 1 : depth);
1801 category->label_depth = 1;
1805 pivot_axis_assign_label_depth (struct pivot_table *table,
1806 enum pivot_axis_type axis_type,
1807 bool dimension_labels_in_corner)
1809 struct pivot_axis *axis = &table->axes[axis_type];
1810 bool any_label_shown_in_corner = false;
1811 axis->label_depth = 0;
1813 for (size_t i = 0; i < axis->n_dimensions; i++)
1815 struct pivot_dimension *d = axis->dimensions[i];
1816 pivot_category_assign_label_depth (d->root, dimension_labels_in_corner);
1817 d->label_depth = d->hide_all_labels ? 0 : d->root->label_depth;
1818 axis->label_depth += d->label_depth;
1819 axis->extent *= d->n_leaves;
1821 if (d->root->show_label_in_corner)
1822 any_label_shown_in_corner = true;
1824 return any_label_shown_in_corner;
1828 pivot_table_assign_label_depth (struct pivot_table *table)
1830 pivot_axis_assign_label_depth (table, PIVOT_AXIS_COLUMN, false);
1831 if (pivot_axis_assign_label_depth (
1832 table, PIVOT_AXIS_ROW, (table->look->row_labels_in_corner
1833 && !table->corner_text))
1834 && table->axes[PIVOT_AXIS_COLUMN].label_depth == 0)
1835 table->axes[PIVOT_AXIS_COLUMN].label_depth = 1;
1836 pivot_axis_assign_label_depth (table, PIVOT_AXIS_LAYER, false);
1840 indent (int indentation)
1842 for (int i = 0; i < indentation * 2; i++)
1847 pivot_value_dump (const struct pivot_value *value,
1848 const struct pivot_table *pt)
1850 char *s = pivot_value_to_string (value, pt);
1856 pivot_table_dump_value (const struct pivot_value *value, const char *name,
1857 const struct pivot_table *pt, int indentation)
1861 indent (indentation);
1862 printf ("%s: ", name);
1863 pivot_value_dump (value, pt);
1869 pivot_table_dump_string (const char *string, const char *name, int indentation)
1873 indent (indentation);
1874 printf ("%s: %s\n", name, string);
1879 pivot_category_dump (const struct pivot_category *c,
1880 const struct pivot_table *pt, int indentation)
1882 indent (indentation);
1883 printf ("%s \"", pivot_category_is_leaf (c) ? "leaf" : "group");
1884 pivot_value_dump (c->name, pt);
1887 if (pivot_category_is_leaf (c))
1888 printf ("data_index=%zu\n", c->data_index);
1891 printf (" (label %s)", c->show_label ? "shown" : "hidden");
1894 for (size_t i = 0; i < c->n_subs; i++)
1895 pivot_category_dump (c->subs[i], pt, indentation + 1);
1900 pivot_dimension_dump (const struct pivot_dimension *d,
1901 const struct pivot_table *pt, int indentation)
1903 indent (indentation);
1904 printf ("%s dimension %zu (where 0=innermost), label_depth=%d:\n",
1905 pivot_axis_type_to_string (d->axis_type), d->level, d->label_depth);
1907 pivot_category_dump (d->root, pt, indentation + 1);
1911 table_area_style_dump (enum pivot_area area, const struct table_area_style *a,
1914 indent (indentation);
1915 printf ("%s: ", pivot_area_to_string (area));
1916 font_style_dump (&a->font_style);
1918 cell_style_dump (&a->cell_style);
1923 table_border_style_dump (enum pivot_border border,
1924 const struct table_border_style *b, int indentation)
1926 indent (indentation);
1927 printf ("%s: %s ", pivot_border_to_string (border),
1928 table_stroke_to_string (b->stroke));
1929 cell_color_dump (&b->color);
1934 compose_headings (const struct pivot_table *pt,
1935 const struct pivot_axis *axis,
1936 const size_t *column_enumeration)
1938 if (!axis->n_dimensions || !axis->extent || !axis->label_depth)
1941 char ***headings = xnmalloc (axis->label_depth, sizeof *headings);
1942 for (size_t i = 0; i < axis->label_depth; i++)
1943 headings[i] = xcalloc (axis->extent, sizeof **headings);
1945 const size_t *indexes;
1947 PIVOT_ENUMERATION_FOR_EACH (indexes, column_enumeration, axis)
1949 int row = axis->label_depth - 1;
1950 for (int dim_index = 0; dim_index < axis->n_dimensions; dim_index++)
1952 const struct pivot_dimension *d = axis->dimensions[dim_index];
1953 if (d->hide_all_labels)
1955 for (const struct pivot_category *c
1956 = d->presentation_leaves[indexes[dim_index]];
1960 if (pivot_category_is_leaf (c) || (c->show_label
1961 && !c->show_label_in_corner))
1963 headings[row][column] = pivot_value_to_string (c->name, pt);
1964 if (!*headings[row][column])
1966 free (headings[row][column]);
1967 headings[row][column] = xstrdup ("<blank>");
1980 free_headings (const struct pivot_axis *axis, char ***headings)
1984 for (size_t i = 0; i < axis->label_depth; i++)
1986 for (size_t j = 0; j < axis->extent; j++)
1987 free (headings[i][j]);
1994 pivot_table_sizing_dump (const char *name,
1995 const int width_ranges[2],
1996 const struct pivot_table_sizing *s,
1999 indent (indentation);
2000 printf ("%ss: min=%d, max=%d\n", name, width_ranges[0], width_ranges[1]);
2003 indent (indentation + 1);
2004 printf ("%s widths:", name);
2005 for (size_t i = 0; i < s->n_widths; i++)
2006 printf (" %d", s->widths[i]);
2011 indent (indentation + 1);
2012 printf ("break after %ss:", name);
2013 for (size_t i = 0; i < s->n_breaks; i++)
2014 printf (" %zu", s->breaks[i]);
2019 indent (indentation + 1);
2020 printf ("keep %ss together:", name);
2021 for (size_t i = 0; i < s->n_keeps; i++)
2022 printf (" [%zu,%zu]",
2024 s->keeps[i].ofs + s->keeps[i].n - 1);
2030 dump_leaf (const struct pivot_table *table, const struct pivot_category *c)
2034 dump_leaf (table, c->parent);
2035 if (pivot_category_is_leaf (c) || c->show_label)
2038 pivot_value_dump (c->name, table);
2044 pivot_table_dump (const struct pivot_table *table, int indentation)
2049 pivot_table_assign_label_depth (CONST_CAST (struct pivot_table *, table));
2051 pivot_table_dump_value (table->title, "title", table, indentation);
2052 pivot_table_dump_value (table->subtype, "subtype", table, indentation);
2053 pivot_table_dump_string (table->command_c, "command", indentation);
2054 pivot_table_dump_string (table->dataset, "dataset", indentation);
2055 pivot_table_dump_string (table->datafile, "datafile", indentation);
2056 pivot_table_dump_string (table->notes, "notes", indentation);
2057 pivot_table_dump_string (table->look->name, "table-look", indentation);
2060 indent (indentation);
2062 struct tm *tm = localtime (&table->date);
2063 printf ("date: %d-%02d-%02d %d:%02d:%02d\n", tm->tm_year + 1900,
2064 tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
2068 indent (indentation);
2069 printf ("sizing:\n");
2070 pivot_table_sizing_dump ("column", table->look->col_heading_width_range,
2071 &table->sizing[TABLE_HORZ], indentation + 1);
2072 pivot_table_sizing_dump ("row", table->look->row_heading_width_range,
2073 &table->sizing[TABLE_VERT], indentation + 1);
2075 indent (indentation);
2076 printf ("areas:\n");
2077 for (enum pivot_area area = 0; area < PIVOT_N_AREAS; area++)
2078 table_area_style_dump (area, &table->look->areas[area], indentation + 1);
2080 indent (indentation);
2081 printf ("borders:\n");
2082 for (enum pivot_border border = 0; border < PIVOT_N_BORDERS; border++)
2083 table_border_style_dump (border, &table->look->borders[border],
2086 for (size_t i = 0; i < table->n_dimensions; i++)
2087 pivot_dimension_dump (table->dimensions[i], table, indentation);
2089 /* Presentation and data indexes. */
2090 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
2092 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2093 if (layer_axis->n_dimensions)
2095 indent (indentation);
2096 printf ("current layer:");
2098 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2100 const struct pivot_dimension *d = layer_axis->dimensions[i];
2101 char *name = pivot_value_to_string (d->root->name, table);
2102 printf (" %s", name);
2105 size_t ofs = table->current_layer[i];
2106 if (ofs < d->n_leaves)
2108 char *value = pivot_value_to_string (d->data_leaves[ofs]->name,
2110 printf ("=%s", value);
2118 size_t *layer_indexes;
2119 size_t layer_iteration = 0;
2120 PIVOT_AXIS_FOR_EACH (layer_indexes, &table->axes[PIVOT_AXIS_LAYER])
2122 indent (indentation);
2123 printf ("layer %zu:", layer_iteration++);
2125 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2126 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2128 const struct pivot_dimension *d = layer_axis->dimensions[i];
2130 fputs (i == 0 ? " " : ", ", stdout);
2131 pivot_value_dump (d->root->name, table);
2132 fputs (" =", stdout);
2134 dump_leaf (table, d->presentation_leaves[layer_indexes[i]]);
2138 size_t *column_enumeration = pivot_table_enumerate_axis (
2139 table, PIVOT_AXIS_COLUMN, layer_indexes, table->look->omit_empty, NULL);
2140 size_t *row_enumeration = pivot_table_enumerate_axis (
2141 table, PIVOT_AXIS_ROW, layer_indexes, table->look->omit_empty, NULL);
2143 /* Print column headings.
2145 Ordinarily the test for nonnull 'column_headings' would be
2146 unnecessary, because 'column_headings' is null only if the axis's
2147 label_depth is 0, but there is a special case for the column axis only
2148 in pivot_table_assign_label_depth(). */
2149 char ***column_headings = compose_headings (
2150 table, &table->axes[PIVOT_AXIS_COLUMN], column_enumeration);
2151 if (column_headings)
2153 for (size_t y = 0; y < table->axes[PIVOT_AXIS_COLUMN].label_depth; y++)
2155 indent (indentation + 1);
2156 for (size_t x = 0; x < table->axes[PIVOT_AXIS_COLUMN].extent; x++)
2159 fputs ("; ", stdout);
2160 if (column_headings && column_headings[y] && column_headings[y][x])
2161 fputs (column_headings[y][x], stdout);
2165 free_headings (&table->axes[PIVOT_AXIS_COLUMN], column_headings);
2168 indent (indentation + 1);
2169 printf ("-----------------------------------------------\n");
2171 char ***row_headings = compose_headings (
2172 table, &table->axes[PIVOT_AXIS_ROW], row_enumeration);
2175 const size_t *pindexes[PIVOT_N_AXES]
2176 = { [PIVOT_AXIS_LAYER] = layer_indexes };
2177 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
2178 &table->axes[PIVOT_AXIS_ROW])
2180 indent (indentation + 1);
2183 for (size_t y = 0; y < table->axes[PIVOT_AXIS_ROW].label_depth; y++)
2186 fputs ("; ", stdout);
2187 if (row_headings[y][x])
2188 fputs (row_headings[y][x], stdout);
2194 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
2196 &table->axes[PIVOT_AXIS_COLUMN])
2201 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
2202 const struct pivot_value *value = pivot_table_get (
2205 pivot_value_dump (value, table);
2212 free (column_enumeration);
2213 free (row_enumeration);
2214 free_headings (&table->axes[PIVOT_AXIS_ROW], row_headings);
2217 pivot_table_dump_value (table->caption, "caption", table, indentation);
2219 for (size_t i = 0; i < table->n_footnotes; i++)
2221 const struct pivot_footnote *f = table->footnotes[i];
2222 indent (indentation);
2225 pivot_value_dump (f->marker, table);
2227 printf ("%zu", f->idx);
2229 pivot_value_dump (f->content, table);
2237 consume_int (const char *p, size_t *n)
2240 while (c_isdigit (*p))
2241 *n = *n * 10 + (*p++ - '0');
2246 pivot_format_inner_template (struct string *out, const char *template,
2248 struct pivot_value **values, size_t n_values,
2249 const struct pivot_table *pt)
2251 size_t args_consumed = 0;
2252 while (*template && *template != ':')
2254 if (*template == '\\' && template[1])
2256 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2259 else if (*template == escape)
2262 template = consume_int (template + 1, &index);
2263 if (index >= 1 && index <= n_values)
2265 pivot_value_format (values[index - 1], pt, out);
2266 args_consumed = MAX (args_consumed, index);
2270 ds_put_byte (out, *template++);
2272 return args_consumed;
2276 pivot_extract_inner_template (const char *template, const char **p)
2282 if (*template == '\\' && template[1] != '\0')
2284 else if (*template == ':')
2285 return template + 1;
2286 else if (*template == '\0')
2294 pivot_format_template (struct string *out, const char *template,
2295 const struct pivot_argument *args, size_t n_args,
2296 const struct pivot_table *pt)
2300 if (*template == '\\' && template[1] != '\0')
2302 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2305 else if (*template == '^')
2308 template = consume_int (template + 1, &index);
2309 if (index >= 1 && index <= n_args && args[index - 1].n > 0)
2310 pivot_value_format (args[index - 1].values[0], pt, out);
2312 else if (*template == '[')
2314 const char *tmpl[2];
2315 template = pivot_extract_inner_template (template + 1, &tmpl[0]);
2316 template = pivot_extract_inner_template (template, &tmpl[1]);
2317 template += *template == ']';
2320 template = consume_int (template, &index);
2321 if (index < 1 || index > n_args)
2324 const struct pivot_argument *arg = &args[index - 1];
2325 size_t left = arg->n;
2328 struct pivot_value **values = arg->values + (arg->n - left);
2329 int tmpl_idx = left == arg->n && *tmpl[0] != ':' ? 0 : 1;
2330 char escape = "%^"[tmpl_idx];
2331 size_t used = pivot_format_inner_template (
2332 out, tmpl[tmpl_idx], escape, values, left, pt);
2333 if (!used || used > left)
2339 ds_put_byte (out, *template++);
2343 static enum settings_value_show
2344 interpret_show (enum settings_value_show global_show,
2345 enum settings_value_show table_show,
2346 enum settings_value_show value_show,
2349 return (!has_label ? SETTINGS_VALUE_SHOW_VALUE
2350 : value_show != SETTINGS_VALUE_SHOW_DEFAULT ? value_show
2351 : table_show != SETTINGS_VALUE_SHOW_DEFAULT ? table_show
2355 /* Appends to OUT the actual text content from the given Pango MARKUP. */
2357 get_text_from_markup (const char *markup, struct string *out)
2359 xmlParserCtxt *parser = xmlCreatePushParserCtxt (NULL, NULL, NULL, 0, NULL);
2362 ds_put_cstr (out, markup);
2366 xmlParseChunk (parser, "<xml>", strlen ("<xml>"), false);
2367 xmlParseChunk (parser, markup, strlen (markup), false);
2368 xmlParseChunk (parser, "</xml>", strlen ("</xml>"), true);
2370 if (parser->wellFormed)
2372 xmlChar *s = xmlNodeGetContent (xmlDocGetRootElement (parser->myDoc));
2373 ds_put_cstr (out, CHAR_CAST (char *, s));
2377 ds_put_cstr (out, markup);
2378 xmlFreeDoc (parser->myDoc);
2379 xmlFreeParserCtxt (parser);
2382 static const struct pivot_table pivot_value_format_defaults = {
2383 .show_values = SETTINGS_VALUE_SHOW_DEFAULT,
2384 .show_variables = SETTINGS_VALUE_SHOW_DEFAULT,
2385 .settings = FMT_SETTINGS_INIT,
2388 /* Appends a text representation of the body of VALUE to OUT. Settings on PT
2389 control whether variable and value labels are included (pass NULL for PT to
2390 get default formatting in the absence of a pivot table).
2392 The "body" omits subscripts and superscripts and footnotes.
2394 Returns true if OUT is a number (or a number plus a value label), false
2397 pivot_value_format_body (const struct pivot_value *value,
2398 const struct pivot_table *pt_,
2401 const struct pivot_table *pt = pt_ ? pt_ : &pivot_value_format_defaults;
2402 enum settings_value_show show;
2403 bool numeric = false;
2405 switch (value->type)
2407 case PIVOT_VALUE_NUMERIC:
2408 show = interpret_show (settings_get_show_values (),
2410 value->numeric.show,
2411 value->numeric.value_label != NULL);
2412 if (show & SETTINGS_VALUE_SHOW_VALUE)
2414 const struct fmt_spec *f = &value->numeric.format;
2415 const struct fmt_spec *format
2417 && value->numeric.honor_small
2418 && value->numeric.x != 0
2419 && fabs (value->numeric.x) < pt->small
2420 ? &(struct fmt_spec) { .type = FMT_E, .w = 40, .d = f->d }
2423 char *s = data_out (&(union value) { .f = value->numeric.x },
2424 "UTF-8", format, &pt->settings);
2425 ds_put_cstr (out, s + strspn (s, " "));
2428 if (show & SETTINGS_VALUE_SHOW_LABEL)
2430 if (show & SETTINGS_VALUE_SHOW_VALUE)
2431 ds_put_byte (out, ' ');
2432 ds_put_cstr (out, value->numeric.value_label);
2434 numeric = !(show & SETTINGS_VALUE_SHOW_LABEL);
2437 case PIVOT_VALUE_STRING:
2438 show = interpret_show (settings_get_show_values (),
2441 value->string.value_label != NULL);
2442 if (show & SETTINGS_VALUE_SHOW_VALUE)
2444 if (value->string.hex)
2446 for (const uint8_t *p = CHAR_CAST (uint8_t *, value->string.s);
2448 ds_put_format (out, "%02X", *p);
2451 ds_put_cstr (out, value->string.s);
2453 if (show & SETTINGS_VALUE_SHOW_LABEL)
2455 if (show & SETTINGS_VALUE_SHOW_VALUE)
2456 ds_put_byte (out, ' ');
2457 ds_put_cstr (out, value->string.value_label);
2461 case PIVOT_VALUE_VARIABLE:
2462 show = interpret_show (settings_get_show_variables (),
2464 value->variable.show,
2465 value->variable.var_label != NULL);
2466 if (show & SETTINGS_VALUE_SHOW_VALUE)
2467 ds_put_cstr (out, value->variable.var_name);
2468 if (show & SETTINGS_VALUE_SHOW_LABEL)
2470 if (show & SETTINGS_VALUE_SHOW_VALUE)
2471 ds_put_byte (out, ' ');
2472 ds_put_cstr (out, value->variable.var_label);
2476 case PIVOT_VALUE_TEXT:
2477 if (value->ex && value->ex->font_style && value->ex->font_style->markup)
2478 get_text_from_markup (value->text.local, out);
2480 ds_put_cstr (out, value->text.local);
2483 case PIVOT_VALUE_TEMPLATE:
2484 pivot_format_template (out, value->template.local, value->template.args,
2485 value->template.n_args, pt);
2492 /* Appends a text representation of VALUE to OUT. Settings on PT control
2493 whether variable and value labels are included (pass NULL for PT to get
2494 default formatting in the absence of a pivot table).
2496 Subscripts and footnotes are included.
2498 Returns true if OUT is a number (or a number plus a value label), false
2501 pivot_value_format (const struct pivot_value *value,
2502 const struct pivot_table *pt_,
2505 const struct pivot_table *pt = pt_ ? pt_ : &pivot_value_format_defaults;
2506 bool numeric = pivot_value_format_body (value, pt, out);
2508 const struct pivot_value_ex *ex = value->ex;
2511 if (ex->n_subscripts)
2513 for (size_t i = 0; i < ex->n_subscripts; i++)
2514 ds_put_format (out, "%c%s", i ? ',' : '_', ex->subscripts[i]);
2517 for (size_t i = 0; i < ex->n_footnotes; i++)
2519 ds_put_byte (out, '[');
2521 size_t idx = ex->footnote_indexes[i];
2522 const struct pivot_footnote *f = pt->footnotes[idx];
2523 pivot_footnote_format_marker (f, pt, out);
2525 ds_put_byte (out, ']');
2532 /* Returns a text representation of VALUE. The caller must free the string,
2533 with free(). Settings on PT control whether variable and value labels are
2534 included (pass NULL for PT to get default formatting in the absence of a
2537 pivot_value_to_string (const struct pivot_value *value,
2538 const struct pivot_table *pt)
2540 struct string s = DS_EMPTY_INITIALIZER;
2541 pivot_value_format (value, pt, &s);
2542 return ds_steal_cstr (&s);
2545 struct pivot_value *
2546 pivot_value_clone (const struct pivot_value *old)
2551 struct pivot_value *new = xmemdup (old, sizeof *new);
2553 new->ex = pivot_value_ex_clone (old->ex);
2557 case PIVOT_VALUE_NUMERIC:
2558 new->numeric.var_name = xstrdup_if_nonnull (new->numeric.var_name);
2559 new->numeric.value_label = xstrdup_if_nonnull (new->numeric.value_label);
2562 case PIVOT_VALUE_STRING:
2563 new->string.s = xstrdup (new->string.s);
2564 new->string.var_name = xstrdup_if_nonnull (new->string.var_name);
2565 new->string.value_label = xstrdup_if_nonnull (new->string.value_label);
2568 case PIVOT_VALUE_VARIABLE:
2569 new->variable.var_name = xstrdup_if_nonnull (new->variable.var_name);
2570 new->variable.var_label = xstrdup_if_nonnull (new->variable.var_label);
2573 case PIVOT_VALUE_TEXT:
2574 new->text.local = xstrdup (old->text.local);
2575 new->text.c = (old->text.c == old->text.local ? new->text.local
2576 : xstrdup_if_nonnull (old->text.c));
2577 new->text.id = (old->text.id == old->text.local ? new->text.local
2578 : old->text.id == old->text.c ? new->text.c
2579 : xstrdup_if_nonnull (old->text.id));
2582 case PIVOT_VALUE_TEMPLATE:
2583 new->template.local = xstrdup (old->template.local);
2584 new->template.id = (old->template.id == old->template.local
2585 ? new->template.local
2586 : xstrdup (old->template.id));
2587 new->template.args = xmalloc (new->template.n_args
2588 * sizeof *new->template.args);
2589 for (size_t i = 0; i < old->template.n_args; i++)
2590 pivot_argument_copy (&new->template.args[i],
2591 &old->template.args[i]);
2600 /* Frees the data owned by V. */
2602 pivot_value_destroy (struct pivot_value *value)
2606 pivot_value_ex_destroy (value->ex);
2607 switch (value->type)
2609 case PIVOT_VALUE_NUMERIC:
2610 free (value->numeric.var_name);
2611 free (value->numeric.value_label);
2614 case PIVOT_VALUE_STRING:
2615 free (value->string.s);
2616 free (value->string.var_name);
2617 free (value->string.value_label);
2620 case PIVOT_VALUE_VARIABLE:
2621 free (value->variable.var_name);
2622 free (value->variable.var_label);
2625 case PIVOT_VALUE_TEXT:
2626 free (value->text.local);
2627 if (value->text.c != value->text.local)
2628 free (value->text.c);
2629 if (value->text.id != value->text.local
2630 && value->text.id != value->text.c)
2631 free (value->text.id);
2634 case PIVOT_VALUE_TEMPLATE:
2635 free (value->template.local);
2636 if (value->template.id != value->template.local)
2637 free (value->template.id);
2638 for (size_t i = 0; i < value->template.n_args; i++)
2639 pivot_argument_uninit (&value->template.args[i]);
2640 free (value->template.args);
2650 /* Sets AREA to the style to use for VALUE, with defaults coming from
2651 DEFAULT_STYLE for the parts of the style that VALUE doesn't override. */
2653 pivot_value_get_style (struct pivot_value *value,
2654 const struct font_style *base_font_style,
2655 const struct cell_style *base_cell_style,
2656 struct table_area_style *area)
2658 const struct pivot_value_ex *ex = pivot_value_ex (value);
2659 font_style_copy (NULL, &area->font_style,
2660 ex->font_style ? ex->font_style : base_font_style);
2661 area->cell_style = *(ex->cell_style ? ex->cell_style : base_cell_style);
2664 /* Copies AREA into VALUE's style. */
2666 pivot_value_set_style (struct pivot_value *value,
2667 const struct table_area_style *area)
2669 pivot_value_set_font_style (value, &area->font_style);
2670 pivot_value_set_cell_style (value, &area->cell_style);
2674 pivot_value_set_font_style (struct pivot_value *value,
2675 const struct font_style *font_style)
2677 struct pivot_value_ex *ex = pivot_value_ex_rw (value);
2679 font_style_uninit (ex->font_style);
2681 ex->font_style = xmalloc (sizeof *ex->font_style);
2682 font_style_copy (NULL, ex->font_style, font_style);
2686 pivot_value_set_cell_style (struct pivot_value *value,
2687 const struct cell_style *cell_style)
2689 struct pivot_value_ex *ex = pivot_value_ex_rw (value);
2690 if (!ex->cell_style)
2691 ex->cell_style = xmalloc (sizeof *ex->cell_style);
2692 *ex->cell_style = *cell_style;
2696 pivot_argument_copy (struct pivot_argument *dst,
2697 const struct pivot_argument *src)
2699 *dst = (struct pivot_argument) {
2701 .values = xmalloc (src->n * sizeof *dst->values),
2704 for (size_t i = 0; i < src->n; i++)
2705 dst->values[i] = pivot_value_clone (src->values[i]);
2708 /* Frees the data owned by ARG (but not ARG itself). */
2710 pivot_argument_uninit (struct pivot_argument *arg)
2714 for (size_t i = 0; i < arg->n; i++)
2715 pivot_value_destroy (arg->values[i]);
2720 /* Creates and returns a new pivot_value whose contents is the null-terminated
2721 string TEXT. Takes ownership of TEXT.
2723 This function is for text strings provided by the user (with the exception
2724 that pivot_value_new_variable() should be used for variable names). For
2725 strings that are part of the PSPP user interface, such as names of
2726 procedures, statistics, annotations, error messages, etc., use
2727 pivot_value_new_text(). */
2728 struct pivot_value *
2729 pivot_value_new_user_text_nocopy (char *text)
2731 struct pivot_value *value = xmalloc (sizeof *value);
2732 *value = (struct pivot_value) {
2734 .type = PIVOT_VALUE_TEXT,
2738 .user_provided = true,
2744 /* Creates and returns a new pivot_value whose contents is the LENGTH bytes of
2745 TEXT. Use SIZE_MAX if TEXT is null-teriminated and its length is not known
2748 This function is for text strings provided by the user (with the exception
2749 that pivot_value_new_variable() should be used for variable names). For
2750 strings that are part of the PSPP user interface, such as names of
2751 procedures, statistics, annotations, error messages, etc., use
2752 pivot_value_new_text().
2754 The caller retains ownership of TEXT. */
2755 struct pivot_value *
2756 pivot_value_new_user_text (const char *text, size_t length)
2758 return pivot_value_new_user_text_nocopy (
2759 xmemdup0 (text, length != SIZE_MAX ? length : strlen (text)));
2762 /* Creates and returns new pivot_value whose contents is TEXT, which should be
2763 a translatable string, but not actually translated yet, e.g. enclosed in
2764 N_(). This function is for text strings that are part of the PSPP user
2765 interface, such as names of procedures, statistics, annotations, error
2766 messages, etc. For strings that come from the user, use
2767 pivot_value_new_user_text(). */
2768 struct pivot_value *
2769 pivot_value_new_text (const char *text)
2771 char *c = xstrdup (text);
2772 char *local = xstrdup (gettext (c));
2774 struct pivot_value *value = xmalloc (sizeof *value);
2775 *value = (struct pivot_value) {
2777 .type = PIVOT_VALUE_TEXT,
2781 .user_provided = false,
2787 /* Same as pivot_value_new_text() but its argument is a printf()-like format
2788 string. The format string should generally be enclosed in N_(). */
2789 struct pivot_value * PRINTF_FORMAT (1, 2)
2790 pivot_value_new_text_format (const char *format, ...)
2793 va_start (args, format);
2794 char *c = xvasprintf (format, args);
2797 va_start (args, format);
2798 char *local = xvasprintf (gettext (format), args);
2801 struct pivot_value *value = xmalloc (sizeof *value);
2802 *value = (struct pivot_value) {
2804 .type = PIVOT_VALUE_TEXT,
2808 .user_provided = false,
2814 /* Returns a new pivot_value that represents X.
2816 The format to use for X is unspecified. Usually the easiest way to specify
2817 a format is through assigning a result class to one of the categories that
2818 the pivot_value will end up in. If that is not suitable, then the caller
2819 can use pivot_value_set_rc() or assign directly to value->numeric.format. */
2820 struct pivot_value *
2821 pivot_value_new_number (double x)
2823 struct pivot_value *value = xmalloc (sizeof *value);
2824 *value = (struct pivot_value) {
2826 .type = PIVOT_VALUE_NUMERIC,
2833 /* Returns a new pivot_value that represents X, formatted as an integer. */
2834 struct pivot_value *
2835 pivot_value_new_integer (double x)
2837 struct pivot_value *value = pivot_value_new_number (x);
2838 value->numeric.format = (struct fmt_spec) { .type = FMT_F, .w = 40 };
2842 /* Returns a new pivot_value that represents VALUE, formatted as for
2844 struct pivot_value *
2845 pivot_value_new_var_value (const struct variable *variable,
2846 const union value *value)
2848 struct pivot_value *pv = pivot_value_new_value (
2849 value, var_get_width (variable), var_get_print_format (variable),
2850 var_get_encoding (variable));
2852 char *var_name = xstrdup (var_get_name (variable));
2853 if (var_is_alpha (variable))
2854 pv->string.var_name = var_name;
2856 pv->numeric.var_name = var_name;
2858 const char *label = var_lookup_value_label (variable, value);
2861 if (var_is_alpha (variable))
2862 pv->string.value_label = xstrdup (label);
2864 pv->numeric.value_label = xstrdup (label);
2870 /* Returns a new pivot_value that represents VALUE, with the given WIDTH,
2871 formatted with FORMAT. For a string value, ENCODING must be its character
2873 struct pivot_value *
2874 pivot_value_new_value (const union value *value, int width,
2875 const struct fmt_spec *format, const char *encoding)
2877 struct pivot_value *pv = XZALLOC (struct pivot_value);
2880 char *s = recode_string (UTF8, encoding, CHAR_CAST (char *, value->s),
2882 size_t n = strlen (s);
2883 while (n > 0 && s[n - 1] == ' ')
2886 pv->type = PIVOT_VALUE_STRING;
2888 pv->string.hex = format->type == FMT_AHEX;
2892 pv->type = PIVOT_VALUE_NUMERIC;
2893 pv->numeric.x = value->f;
2894 pv->numeric.format = *format;
2900 /* Returns a new pivot_value for VARIABLE. */
2901 struct pivot_value *
2902 pivot_value_new_variable (const struct variable *variable)
2904 return pivot_value_new_variable__ (var_get_name (variable),
2905 var_get_label (variable));
2908 /* Returns a new pivot_value for a variable with the given NAME and optional
2910 struct pivot_value *
2911 pivot_value_new_variable__ (const char *name, const char *label)
2913 struct pivot_value *value = xmalloc (sizeof *value);
2914 *value = (struct pivot_value) {
2916 .type = PIVOT_VALUE_VARIABLE,
2917 .var_name = xstrdup (name),
2918 .var_label = xstrdup_if_nonnull (label),
2924 /* Attaches a reference to FOOTNOTE to V. */
2926 pivot_value_add_footnote (struct pivot_value *v,
2927 const struct pivot_footnote *footnote)
2929 struct pivot_value_ex *ex = pivot_value_ex_rw (v);
2931 /* Some legacy tables include numerous duplicate footnotes. Suppress
2933 for (size_t i = 0; i < ex->n_footnotes; i++)
2934 if (ex->footnote_indexes[i] == footnote->idx)
2937 ex->footnote_indexes = xrealloc (
2938 ex->footnote_indexes,
2939 (ex->n_footnotes + 1) * sizeof *ex->footnote_indexes);
2940 ex->footnote_indexes[ex->n_footnotes++] = footnote->idx;
2941 pivot_value_sort_footnotes (v);
2945 compare_footnote_indexes (const void *a_, const void *b_)
2947 const size_t *ap = a_;
2948 const size_t *bp = b_;
2951 return a < b ? -1 : a > b;
2954 /* Sorts the footnote references in V in the standard ascending order.
2956 This is only necessary if code adds (plural) footnotes to a pivot_value by
2957 itself, because pivot_value_add_footnote() does it automatically. */
2959 pivot_value_sort_footnotes (struct pivot_value *v)
2961 if (v->ex && v->ex->n_footnotes > 1)
2962 qsort (v->ex->footnote_indexes, v->ex->n_footnotes,
2963 sizeof *v->ex->footnote_indexes, compare_footnote_indexes);
2966 /* If VALUE is a numeric value, and RC is a result class such as
2967 PIVOT_RC_COUNT, changes VALUE's format to the result class's. */
2969 pivot_value_set_rc (const struct pivot_table *table, struct pivot_value *value,
2972 if (value->type == PIVOT_VALUE_NUMERIC)
2973 pivot_table_use_rc (table, rc,
2974 &value->numeric.format, &value->numeric.honor_small);
2977 /* pivot_value_ex. */
2979 struct pivot_value_ex *
2980 pivot_value_ex_rw (struct pivot_value *value)
2983 value->ex = xzalloc (sizeof *value->ex);
2987 struct pivot_value_ex *
2988 pivot_value_ex_clone (const struct pivot_value_ex *old)
2990 struct font_style *font_style = NULL;
2991 if (old->font_style)
2993 font_style = xmalloc (sizeof *font_style);
2994 font_style_copy (NULL, font_style, old->font_style);
2997 char **subscripts = NULL;
2998 if (old->n_subscripts)
3000 subscripts = xnmalloc (old->n_subscripts, sizeof *subscripts);
3001 for (size_t i = 0; i < old->n_subscripts; i++)
3002 subscripts[i] = xstrdup (old->subscripts[i]);
3005 struct pivot_value_ex *new = xmalloc (sizeof *new);
3006 *new = (struct pivot_value_ex) {
3007 .font_style = font_style,
3008 .cell_style = (old->cell_style
3009 ? xmemdup (old->cell_style, sizeof *new->cell_style)
3011 .subscripts = subscripts,
3012 .n_subscripts = old->n_subscripts,
3013 .footnote_indexes = (
3015 ? xmemdup (old->footnote_indexes,
3016 old->n_footnotes * sizeof *new->footnote_indexes)
3018 .n_footnotes = old->n_footnotes
3024 pivot_value_ex_destroy (struct pivot_value_ex *ex)
3028 font_style_uninit (ex->font_style);
3029 free (ex->font_style);
3030 free (ex->cell_style);
3031 free (ex->footnote_indexes);
3033 for (size_t i = 0; i < ex->n_subscripts; i++)
3034 free (ex->subscripts[i]);
3035 free (ex->subscripts);
3042 struct pivot_splits_value
3044 struct hmap_node hmap_node;
3049 struct pivot_splits_var
3051 struct pivot_dimension *dimension;
3052 const struct variable *var;
3057 struct pivot_splits_dup
3059 struct hmap_node hmap_node;
3060 union value *values;
3065 struct pivot_splits_var *vars;
3071 size_t dindexes[MAX_SPLITS];
3076 /* Adds a dimension for each layered split file variable in DICT to PT on AXIS.
3077 These dimensions should be the last dimensions added to PT (the
3078 pivot_splits_put*() functions rely on this). Returns a new pivot_splits
3079 structure if any dimensions were added, otherwise a null pointer.
3081 See the large comment on split file handling in pivot-table.h for more
3083 struct pivot_splits *
3084 pivot_splits_create (struct pivot_table *pt,
3085 enum pivot_axis_type axis,
3086 const struct dictionary *dict)
3088 if (dict_get_split_type (dict) != SPLIT_LAYERED)
3091 size_t n = dict_get_n_splits (dict);
3092 assert (n <= MAX_SPLITS);
3094 const struct variable *const *vars = dict_get_split_vars (dict);
3095 struct pivot_splits_var *psvars = xnmalloc (n, sizeof *psvars);
3096 for (size_t i = n - 1; i < n; i--)
3098 const struct variable *var = vars[i];
3099 struct pivot_splits_var *psvar = &psvars[i];
3101 struct pivot_dimension *d = pivot_dimension_create__ (
3102 pt, axis, pivot_value_new_variable (var));
3103 d->root->show_label = true;
3105 *psvar = (struct pivot_splits_var) {
3106 .width = var_get_width (var),
3107 .values = HMAP_INITIALIZER (psvar->values),
3113 struct pivot_splits *ps = xmalloc (sizeof *ps);
3114 *ps = (struct pivot_splits) {
3117 .encoding = xstrdup (dict_get_encoding (dict)),
3118 .dups = HMAP_INITIALIZER (ps->dups),
3119 .dindexes = { [0] = SIZE_MAX },
3127 pivot_splits_destroy (struct pivot_splits *ps)
3132 if (ps->warnings_left < 0)
3133 msg (SW, ngettext ("Suppressed %d additional warning about duplicate "
3135 "Suppressed %d additional warnings about duplicate "
3136 "split values.", -ps->warnings_left),
3137 -ps->warnings_left);
3139 struct pivot_splits_dup *dup, *next_dup;
3140 HMAP_FOR_EACH_SAFE (dup, next_dup, struct pivot_splits_dup, hmap_node,
3143 for (size_t i = 0; i < ps->n; i++)
3144 value_destroy (&dup->values[i], ps->vars[i].width);
3148 hmap_destroy (&ps->dups);
3150 for (size_t i = 0; i < ps->n; i++)
3152 struct pivot_splits_var *psvar = &ps->vars[i];
3153 struct pivot_splits_value *psval, *next;
3154 HMAP_FOR_EACH_SAFE (psval, next, struct pivot_splits_value, hmap_node,
3157 value_destroy (&psval->value, psvar->width);
3158 hmap_delete (&psvar->values, &psval->hmap_node);
3161 hmap_destroy (&psvar->values);
3165 free (ps->encoding);
3169 static struct pivot_splits_value *
3170 pivot_splits_value_find (struct pivot_splits_var *psvar,
3171 const union value *value)
3173 struct pivot_splits_value *psval;
3174 HMAP_FOR_EACH_WITH_HASH (psval, struct pivot_splits_value, hmap_node,
3175 value_hash (value, psvar->width, 0), &psvar->values)
3176 if (value_equal (&psval->value, value, psvar->width))
3182 pivot_splits_find_dup (struct pivot_splits *ps, const struct ccase *example)
3184 unsigned int hash = 0;
3185 for (size_t i = 0; i < ps->n; i++)
3187 struct pivot_splits_var *psvar = &ps->vars[i];
3188 const union value *value = case_data (example, psvar->var);
3189 hash = value_hash (value, psvar->width, hash);
3191 struct pivot_splits_dup *dup;
3192 HMAP_FOR_EACH_WITH_HASH (dup, struct pivot_splits_dup, hmap_node, hash,
3196 for (size_t i = 0; i < ps->n && equal; i++)
3198 struct pivot_splits_var *psvar = &ps->vars[i];
3199 const union value *value = case_data (example, psvar->var);
3200 equal = value_equal (value, &dup->values[i], psvar->width);
3206 union value *values = xmalloc (ps->n * sizeof *values);
3207 for (size_t i = 0; i < ps->n; i++)
3209 struct pivot_splits_var *psvar = &ps->vars[i];
3210 const union value *value = case_data (example, psvar->var);
3211 value_clone (&values[i], value, psvar->width);
3214 dup = xmalloc (sizeof *dup);
3215 dup->values = values;
3216 hmap_insert (&ps->dups, &dup->hmap_node, hash);
3220 /* Begins adding data for a new split file group to the pivot table associated
3221 with PS. EXAMPLE should be a case from the new split file group.
3223 This is a no-op if PS is NULL.
3225 See the large comment on split file handling in pivot-table.h for more
3228 pivot_splits_new_split (struct pivot_splits *ps, const struct ccase *example)
3233 for (size_t i = 0; i < ps->n; i++)
3235 struct pivot_splits_var *psvar = &ps->vars[i];
3236 const union value *value = case_data (example, psvar->var);
3237 struct pivot_splits_value *psval = pivot_splits_value_find (psvar, value);
3240 psval = xmalloc (sizeof *psval);
3241 hmap_insert (&psvar->values, &psval->hmap_node,
3242 value_hash (value, psvar->width, 0));
3243 value_clone (&psval->value, value, psvar->width);
3244 psval->leaf = pivot_category_create_leaf (
3245 psvar->dimension->root,
3246 pivot_value_new_var_value (psvar->var, value));
3249 ps->dindexes[i] = psval->leaf;
3252 if (pivot_splits_find_dup (ps, example))
3254 if (ps->warnings_left-- > 0)
3256 struct string s = DS_EMPTY_INITIALIZER;
3257 for (size_t i = 0; i < ps->n; i++)
3260 ds_put_cstr (&s, ", ");
3262 struct pivot_splits_var *psvar = &ps->vars[i];
3263 const union value *value = case_data (example, psvar->var);
3264 ds_put_format (&s, "%s = ", var_get_name (psvar->var));
3266 char *s2 = data_out (value, ps->encoding,
3267 var_get_print_format (psvar->var),
3268 settings_get_fmt_settings ());
3269 ds_put_cstr (&s, s2 + strspn (s2, " "));
3272 msg (SW, _("When SPLIT FILE is in effect, the input data must be "
3273 "sorted by the split variables (for example, using SORT "
3274 "CASES), but multiple runs of cases with the same split "
3275 "values were found separated by cases with different "
3276 "values. Each run will be analyzed separately. The "
3277 "duplicate split values are: %s"), ds_cstr (&s));
3281 struct pivot_splits_var *psvar = &ps->vars[0];
3282 const union value *value = case_data (example, psvar->var);
3283 ps->dindexes[0] = pivot_category_create_leaf (
3284 psvar->dimension->root,
3285 pivot_value_new_var_value (psvar->var, value));
3290 pivot_splits_get_dindexes (const struct pivot_splits *ps, size_t *dindexes)
3295 assert (ps->dindexes[0] != SIZE_MAX);
3296 for (size_t i = 0; i < ps->n; i++)
3297 dindexes[ps->n - i - 1] = ps->dindexes[i];
3301 /* Puts VALUE in the cell in TABLE with index IDX1. TABLE must have 1
3302 dimension plus the split file dimensions from PS (if nonnull). Takes
3305 See the large comment on split file handling in pivot-table.h for more
3308 pivot_splits_put1 (struct pivot_splits *ps, struct pivot_table *table,
3309 size_t idx1, struct pivot_value *value)
3311 size_t dindexes[1 + MAX_SPLITS];
3312 size_t *p = dindexes;
3314 p += pivot_splits_get_dindexes (ps, p);
3315 pivot_table_put (table, dindexes, p - dindexes, value);
3318 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2). TABLE must have 2
3319 dimensions plus the split file dimensions from PS (if nonnull). Takes
3322 See the large comment on split file handling in pivot-table.h for more
3325 pivot_splits_put2 (struct pivot_splits *ps, struct pivot_table *table,
3326 size_t idx1, size_t idx2, struct pivot_value *value)
3328 size_t dindexes[2 + MAX_SPLITS];
3329 size_t *p = dindexes;
3332 p += pivot_splits_get_dindexes (ps, p);
3333 pivot_table_put (table, dindexes, p - dindexes, value);
3336 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3). TABLE must
3337 have 3 dimensions plus the split file dimensions from PS (if nonnull).
3338 Takes ownership of VALUE.
3340 See the large comment on split file handling in pivot-table.h for more
3343 pivot_splits_put3 (struct pivot_splits *ps, struct pivot_table *table,
3344 size_t idx1, size_t idx2, size_t idx3,
3345 struct pivot_value *value)
3347 size_t dindexes[3 + MAX_SPLITS];
3348 size_t *p = dindexes;
3352 p += pivot_splits_get_dindexes (ps, p);
3353 pivot_table_put (table, dindexes, p - dindexes, value);
3356 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3, IDX4). TABLE
3357 must have 4 dimensions plus the split file dimensions from PS (if nonnull).
3358 Takes ownership of VALUE.
3360 See the large comment on split file handling in pivot-table.h for more
3363 pivot_splits_put4 (struct pivot_splits *ps, struct pivot_table *table,
3364 size_t idx1, size_t idx2, size_t idx3, size_t idx4,
3365 struct pivot_value *value)
3367 size_t dindexes[4 + MAX_SPLITS];
3368 size_t *p = dindexes;
3373 p += pivot_splits_get_dindexes (ps, p);
3374 pivot_table_put (table, dindexes, p - dindexes, value);