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,
222 [TABLE_HORZ] = { 36, 72 },
223 [TABLE_VERT] = { 36, 120 },
227 #define AREA(BOLD, H, V, L, R, T, B) { \
229 .halign = TABLE_HALIGN_##H, \
230 .valign = TABLE_VALIGN_##V, \
231 .margin = { [TABLE_HORZ][0] = L, [TABLE_HORZ][1] = R, \
232 [TABLE_VERT][0] = T, [TABLE_VERT][1] = B }, \
236 .fg = { [0] = CELL_COLOR_BLACK, [1] = CELL_COLOR_BLACK}, \
237 .bg = { [0] = CELL_COLOR_WHITE, [1] = CELL_COLOR_WHITE}, \
239 .typeface = (char *) "Sans Serif", \
242 [PIVOT_AREA_TITLE] = AREA(true, CENTER, CENTER, 8,11,1,8),
243 [PIVOT_AREA_CAPTION] = AREA(false, LEFT, TOP, 8,11,1,1),
244 [PIVOT_AREA_FOOTER] = AREA(false, LEFT, TOP, 11, 8,2,3),
245 [PIVOT_AREA_CORNER] = AREA(false, LEFT, BOTTOM, 8,11,1,1),
246 [PIVOT_AREA_COLUMN_LABELS] = AREA(false, CENTER, BOTTOM, 8,11,1,3),
247 [PIVOT_AREA_ROW_LABELS] = AREA(false, LEFT, TOP, 8,11,1,3),
248 [PIVOT_AREA_DATA] = AREA(false, MIXED, TOP, 8,11,1,1),
249 [PIVOT_AREA_LAYERS] = AREA(false, LEFT, BOTTOM, 8,11,1,3),
254 #define BORDER(STROKE) { .stroke = STROKE, .color = CELL_COLOR_BLACK }
255 [PIVOT_BORDER_TITLE] = BORDER(TABLE_STROKE_NONE),
256 [PIVOT_BORDER_OUTER_LEFT] = BORDER(TABLE_STROKE_NONE),
257 [PIVOT_BORDER_OUTER_TOP] = BORDER(TABLE_STROKE_NONE),
258 [PIVOT_BORDER_OUTER_RIGHT] = BORDER(TABLE_STROKE_NONE),
259 [PIVOT_BORDER_OUTER_BOTTOM] = BORDER(TABLE_STROKE_NONE),
260 [PIVOT_BORDER_INNER_LEFT] = BORDER(TABLE_STROKE_THICK),
261 [PIVOT_BORDER_INNER_TOP] = BORDER(TABLE_STROKE_THICK),
262 [PIVOT_BORDER_INNER_RIGHT] = BORDER(TABLE_STROKE_THICK),
263 [PIVOT_BORDER_INNER_BOTTOM] = BORDER(TABLE_STROKE_THICK),
264 [PIVOT_BORDER_DATA_LEFT] = BORDER(TABLE_STROKE_THICK),
265 [PIVOT_BORDER_DATA_TOP] = BORDER(TABLE_STROKE_THICK),
266 [PIVOT_BORDER_DIM_ROW_HORZ] = BORDER(TABLE_STROKE_SOLID),
267 [PIVOT_BORDER_DIM_ROW_VERT] = BORDER(TABLE_STROKE_NONE),
268 [PIVOT_BORDER_DIM_COL_HORZ] = BORDER(TABLE_STROKE_SOLID),
269 [PIVOT_BORDER_DIM_COL_VERT] = BORDER(TABLE_STROKE_SOLID),
270 [PIVOT_BORDER_CAT_ROW_HORZ] = BORDER(TABLE_STROKE_NONE),
271 [PIVOT_BORDER_CAT_ROW_VERT] = BORDER(TABLE_STROKE_NONE),
272 [PIVOT_BORDER_CAT_COL_HORZ] = BORDER(TABLE_STROKE_SOLID),
273 [PIVOT_BORDER_CAT_COL_VERT] = BORDER(TABLE_STROKE_SOLID),
280 struct pivot_table_look *
281 pivot_table_look_new_builtin_default (void)
283 return pivot_table_look_unshare (
284 pivot_table_look_ref (pivot_table_look_builtin_default ()));
287 struct pivot_table_look *
288 pivot_table_look_ref (const struct pivot_table_look *look_)
290 assert (look_->ref_cnt > 0);
292 struct pivot_table_look *look = CONST_CAST (struct pivot_table_look *, look_);
298 xstrdup_if_nonempty (const char *s)
300 return s && s[0] ? xstrdup (s) : NULL;
303 struct pivot_table_look *
304 pivot_table_look_unshare (struct pivot_table_look *old)
306 assert (old->ref_cnt > 0);
307 if (old->ref_cnt == 1)
310 pivot_table_look_unref (old);
312 struct pivot_table_look *new = xmemdup (old, sizeof *old);
314 new->name = xstrdup_if_nonempty (old->name);
315 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
316 table_area_style_copy (NULL, &new->areas[i], &old->areas[i]);
317 new->continuation = xstrdup_if_nonempty (old->continuation);
323 pivot_table_look_unref (struct pivot_table_look *look)
327 assert (look->ref_cnt > 0);
328 if (!--look->ref_cnt)
331 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
332 table_area_style_uninit (&look->areas[i]);
333 free (look->continuation);
341 /* Returns the name of AXIS_TYPE. */
343 pivot_axis_type_to_string (enum pivot_axis_type axis_type)
347 case PIVOT_AXIS_LAYER:
353 case PIVOT_AXIS_COLUMN:
361 static enum pivot_axis_type
362 pivot_axis_type_transpose (enum pivot_axis_type axis_type)
364 assert (axis_type == PIVOT_AXIS_ROW || axis_type == PIVOT_AXIS_COLUMN);
365 return (axis_type == PIVOT_AXIS_ROW ? PIVOT_AXIS_COLUMN : PIVOT_AXIS_ROW);
368 /* Implementation of PIVOT_AXIS_FOR_EACH. */
370 pivot_axis_iterator_next (size_t *indexes, const struct pivot_axis *axis)
374 if (axis->n_dimensions)
375 for (size_t i = 0; i < axis->n_dimensions; i++)
376 if (axis->dimensions[i]->n_leaves == 0)
379 size_t size = axis->n_dimensions * sizeof *indexes;
380 return xzalloc (MAX (size, 1));
383 for (size_t i = 0; i < axis->n_dimensions; i++)
385 const struct pivot_dimension *d = axis->dimensions[i];
386 if (++indexes[i] < d->n_leaves)
399 pivot_category_set_rc (struct pivot_category *category, const char *s)
404 pivot_table_use_rc (category->dimension->table, s,
405 &category->format, &category->honor_small);
407 /* Ensure that the category itself, in addition to the cells within it, takes
408 the format. (It's kind of rare for a category to have a numeric format
410 struct pivot_value *name = category->name;
411 if (name->type == PIVOT_VALUE_NUMERIC && !name->numeric.format.w)
412 pivot_table_use_rc (category->dimension->table, s,
413 &name->numeric.format, &name->numeric.honor_small);
417 pivot_category_create_leaves_valist (struct pivot_category *parent,
421 while ((s = va_arg (args, const char *)))
423 if (!strncmp (s, "RC_", 3))
425 assert (parent->n_subs);
426 pivot_category_set_rc (parent->subs[parent->n_subs - 1], s);
429 pivot_category_create_leaf (parent, pivot_value_new_text (s));
433 /* Creates a new dimension with the given NAME in TABLE and returns it. The
434 dimension is added to axis AXIS_TYPE, becoming the outermost dimension on
437 NAME should be a translatable name, but not actually translated yet,
438 e.g. enclosed in N_(). To use a different kind of value for a name, use
439 pivot_dimension_create__() instead.
441 The optional varargs parameters may be used to add an initial set of
442 categories to the dimension. Each string should be a translatable category
443 name, but not actually translated yet, e.g. enclosed in N_(). Each string
444 may optionally be followod by a PIVOT_RC_* string that specifies the default
445 numeric format for cells in this category. */
446 struct pivot_dimension * SENTINEL (0)
447 (pivot_dimension_create) (struct pivot_table *table,
448 enum pivot_axis_type axis_type,
449 const char *name, ...)
451 struct pivot_dimension *d = pivot_dimension_create__ (
452 table, axis_type, pivot_value_new_text (name));
455 va_start (args, name);
456 pivot_category_create_leaves_valist (d->root, args);
462 /* Creates a new dimension with the given NAME in TABLE and returns it. The
463 dimension is added to axis AXIS_TYPE, becoming the outermost dimension on
465 struct pivot_dimension *
466 pivot_dimension_create__ (struct pivot_table *table,
467 enum pivot_axis_type axis_type,
468 struct pivot_value *name)
470 assert (pivot_table_is_empty (table));
472 struct pivot_dimension *d = xmalloc (sizeof *d);
473 *d = (struct pivot_dimension) {
475 .axis_type = axis_type,
476 .level = table->axes[axis_type].n_dimensions,
477 .top_index = table->n_dimensions,
478 .root = xmalloc (sizeof *d->root),
481 struct pivot_category *root = d->root;
482 *root = (struct pivot_category) {
487 .data_index = SIZE_MAX,
488 .presentation_index = SIZE_MAX,
491 table->dimensions = xrealloc (
492 table->dimensions, (table->n_dimensions + 1) * sizeof *table->dimensions);
493 table->dimensions[table->n_dimensions++] = d;
495 struct pivot_axis *axis = &table->axes[axis_type];
496 axis->dimensions = xrealloc (
497 axis->dimensions, (axis->n_dimensions + 1) * sizeof *axis->dimensions);
498 axis->dimensions[axis->n_dimensions++] = d;
500 if (axis_type == PIVOT_AXIS_LAYER)
502 free (table->current_layer);
503 table->current_layer = xcalloc (axis[PIVOT_AXIS_LAYER].n_dimensions,
504 sizeof *table->current_layer);
507 /* axis->extent and axis->label_depth will be calculated later. */
513 pivot_dimension_destroy (struct pivot_dimension *d)
518 pivot_category_destroy (d->root);
519 free (d->data_leaves);
520 free (d->presentation_leaves);
524 /* Returns the first leaf node in an in-order traversal that is a child of
526 static const struct pivot_category * UNUSED
527 pivot_category_first_leaf (const struct pivot_category *cat)
529 if (pivot_category_is_leaf (cat))
532 for (size_t i = 0; i < cat->n_subs; i++)
534 const struct pivot_category *first
535 = pivot_category_first_leaf (cat->subs[i]);
543 /* Returns the next leaf node in an in-order traversal starting at CAT, which
545 static const struct pivot_category * UNUSED
546 pivot_category_next_leaf (const struct pivot_category *cat)
548 assert (pivot_category_is_leaf (cat));
552 const struct pivot_category *parent = cat->parent;
555 for (size_t i = cat->group_index + 1; i < parent->n_subs; i++)
557 const struct pivot_category *next
558 = pivot_category_first_leaf (parent->subs[i]);
568 pivot_category_add_child (struct pivot_category *child)
570 struct pivot_category *parent = child->parent;
572 assert (pivot_category_is_group (parent));
573 if (parent->n_subs >= parent->allocated_subs)
574 parent->subs = x2nrealloc (parent->subs, &parent->allocated_subs,
575 sizeof *parent->subs);
576 parent->subs[parent->n_subs++] = child;
579 /* Adds leaf categories as a child of PARENT. To create top-level categories
580 within dimension 'd', pass 'd->root' for PARENT.
582 Each of the varargs parameters should be a string, each of which should be a
583 translatable category name, but not actually translated yet, e.g. enclosed
584 in N_(). Each string may optionally be followod by a PIVOT_RC_* string that
585 specifies the default numeric format for cells in this category.
587 Returns the category index, which is just a 0-based array index, for the
590 Leaves have to be created in in-order, that is, don't create a group and add
591 some leaves, then add leaves outside the group and try to add more leaves
594 (pivot_category_create_leaves) (struct pivot_category *parent, ...)
596 int retval = parent->dimension->n_leaves;
599 va_start (args, parent);
600 pivot_category_create_leaves_valist (parent, args);
606 /* Creates a new leaf category with the given NAME as a child of PARENT. To
607 create a top-level category within dimension 'd', pass 'd->root' for PARENT.
608 Returns the category index, which is just a 0-based array index, for the new
611 Leaves have to be created in in-order, that is, don't create a group and add
612 some leaves, then add leaves outside the group and try to add more leaves
615 pivot_category_create_leaf (struct pivot_category *parent,
616 struct pivot_value *name)
618 return pivot_category_create_leaf_rc (parent, name, NULL);
621 /* Creates a new leaf category with the given NAME as a child of PARENT. To
622 create a top-level category within dimension 'd', pass 'd->root' for PARENT.
623 Returns the category index, which is just a 0-based array index, for the new
626 If RC is nonnull and the name of a result category, the category is assigned
627 that result category.
629 Leaves have to be created in in-order, that is, don't create a group and add
630 some leaves, then add leaves outside the group and try to add more leaves
633 pivot_category_create_leaf_rc (struct pivot_category *parent,
634 struct pivot_value *name, const char *rc)
636 struct pivot_dimension *d = parent->dimension;
638 struct pivot_category *leaf = xmalloc (sizeof *leaf);
639 *leaf = (struct pivot_category) {
643 .group_index = parent->n_subs,
644 .data_index = d->n_leaves,
645 .presentation_index = d->n_leaves,
648 if (d->n_leaves >= d->allocated_leaves)
650 d->data_leaves = x2nrealloc (d->data_leaves, &d->allocated_leaves,
651 sizeof *d->data_leaves);
652 d->presentation_leaves = xrealloc (
653 d->presentation_leaves,
654 d->allocated_leaves * sizeof *d->presentation_leaves);
657 d->data_leaves[d->n_leaves] = leaf;
658 d->presentation_leaves[d->n_leaves] = leaf;
661 pivot_category_add_child (leaf);
663 /* Make sure that the new child is the last in in-order. */
664 assert (!pivot_category_next_leaf (leaf));
666 pivot_category_set_rc (leaf, rc);
668 return leaf->data_index;
671 /* Adds a new category group named NAME as a child of PARENT. To create a
672 top-level group within dimension 'd', pass 'd->root' for PARENT.
674 NAME should be a translatable name, but not actually translated yet,
675 e.g. enclosed in N_(). To use a different kind of value for a name, use
676 pivot_category_create_group__() instead.
678 The optional varargs parameters may be used to add an initial set of
679 categories to the group. Each string should be a translatable category
680 name, but not actually translated yet, e.g. enclosed in N_(). Each string
681 may optionally be followod by a PIVOT_RC_* string that specifies the default
682 numeric format for cells in this category.
684 Returns the new group. */
685 struct pivot_category * SENTINEL (0)
686 (pivot_category_create_group) (struct pivot_category *parent,
687 const char *name, ...)
689 struct pivot_category *group = pivot_category_create_group__ (
690 parent, pivot_value_new_text (name));
693 va_start (args, name);
694 pivot_category_create_leaves_valist (group, args);
700 /* Adds a new category group named NAME as a child of PARENT. To create a
701 top-level group within dimension 'd', pass 'd->root' for PARENT. Returns
703 struct pivot_category *
704 pivot_category_create_group__ (struct pivot_category *parent,
705 struct pivot_value *name)
707 struct pivot_dimension *d = parent->dimension;
709 struct pivot_category *group = xmalloc (sizeof *group);
710 *group = (struct pivot_category) {
715 .group_index = parent->n_subs,
716 .data_index = SIZE_MAX,
717 .presentation_index = SIZE_MAX,
720 pivot_category_add_child (group);
726 pivot_category_destroy (struct pivot_category *c)
731 pivot_value_destroy (c->name);
732 for (size_t i = 0; i < c->n_subs; i++)
733 pivot_category_destroy (c->subs[i]);
740 These are usually the easiest way to control the formatting of numeric data
741 in a pivot table. See pivot_dimension_create() for an explanation of their
745 const char *name; /* "RC_*". */
746 struct fmt_spec format;
749 /* Formats for most of the result classes. */
750 static struct result_class result_classes[] =
752 { PIVOT_RC_INTEGER, { .type = FMT_F, .w = 40, .d = 0 } },
753 { PIVOT_RC_PERCENT, { .type = FMT_PCT, .w = 40, .d = 1 } },
754 { PIVOT_RC_CORRELATION, { .type = FMT_F, .w = 40, .d = 3 } },
755 { PIVOT_RC_SIGNIFICANCE, { .type = FMT_F, .w = 40, .d = 3 } },
756 { PIVOT_RC_RESIDUAL, { .type = FMT_F, .w = 40, .d = 2 } },
757 { PIVOT_RC_COUNT, { 0, 0, 0 } },
758 { PIVOT_RC_OTHER, { 0, 0, 0 } },
761 /* Has PIVOT_RC_COUNT been overridden by the user? */
762 static bool overridden_count_format;
764 static struct result_class *
765 pivot_result_class_find (const char *s)
767 for (size_t i = 0; i < sizeof result_classes / sizeof *result_classes; i++)
768 if (!strcmp (s, result_classes[i].name))
769 return &result_classes[i];
774 pivot_table_use_rc (const struct pivot_table *table, const char *s,
775 struct fmt_spec *format, bool *honor_small)
779 if (!strcmp (s, PIVOT_RC_OTHER))
781 *format = *settings_get_format ();
784 else if (!strcmp (s, PIVOT_RC_COUNT) && !overridden_count_format)
786 *format = table->weight_format;
787 *honor_small = false;
791 const struct result_class *rc = pivot_result_class_find (s);
794 *format = rc->format;
795 *honor_small = false;
799 printf ("unknown class %s\n", s);
805 /* Sets the format specification for the result class named S (which should not
806 include the RC_ prefix) to *FORMAT. Returns true if successful, false if S
807 does not name a known result class. */
809 pivot_result_class_change (const char *s_, const struct fmt_spec *format)
811 char *s = xasprintf ("RC_%s", s_);
812 struct result_class *rc = pivot_result_class_find (s);
815 rc->format = *format;
816 if (!strcmp (s, PIVOT_RC_COUNT))
817 overridden_count_format = true;
825 is_pivot_result_class (const char *s)
827 return pivot_result_class_find (s) != NULL;
832 static struct pivot_cell *pivot_table_insert_cell (struct pivot_table *,
833 const size_t *dindexes);
834 static void pivot_table_delete_cell (struct pivot_table *,
835 struct pivot_cell *);
837 /* Creates and returns a new pivot table with the given TITLE. TITLE should be
838 a text string marked for translation but not actually translated yet,
839 e.g. N_("Descriptive Statistics"). The un-translated text string is used as
840 the pivot table's subtype.
842 This function is a shortcut for pivot_table_create__() for the most common
843 case. Use pivot_table_create__() directly if the title should be some kind
844 of value other than an ordinary text string, or if the subtype should be
845 different from the title.
847 See the large comment at the top of pivot-table.h for general advice on
848 creating pivot tables. */
850 pivot_table_create (const char *title)
852 return pivot_table_create__ (pivot_value_new_text (title), title);
855 /* Creates and returns a new pivot table with the given TITLE, and takes
856 ownership of TITLE. The new pivot table's subtype is SUBTYPE, which should
857 be an untranslated English string that describes the contents of the table
858 at a high level without being specific about the variables or other context
861 TITLE and SUBTYPE may be NULL, but in that case the client must add them
862 later because they are both mandatory for a pivot table.
864 See the large comment at the top of pivot-table.h for general advice on
865 creating pivot tables. */
867 pivot_table_create__ (struct pivot_value *title, const char *subtype)
869 struct pivot_table *table = xmalloc (sizeof *table);
870 *table = (struct pivot_table) {
873 .show_caption = true,
874 .weight_format = (struct fmt_spec) { .type = FMT_F, .w = 40 },
876 .subtype = subtype ? pivot_value_new_text (subtype) : NULL,
877 .command_c = xstrdup_if_nonempty (output_get_command_name ()),
878 .look = pivot_table_look_ref (pivot_table_look_get_default ()),
879 .settings = fmt_settings_copy (settings_get_fmt_settings ()),
880 .small = settings_get_small (),
881 .cells = HMAP_INITIALIZER (table->cells),
886 /* Creates and returns a new pivot table with the given TITLE and a single cell
887 with the given CONTENT.
889 This is really just for error handling. */
891 pivot_table_create_for_text (struct pivot_value *title,
892 struct pivot_value *content)
894 struct pivot_table *table = pivot_table_create__ (title, "Error");
896 struct pivot_dimension *d = pivot_dimension_create (
897 table, PIVOT_AXIS_ROW, N_("Error"));
898 d->hide_all_labels = true;
899 pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
901 pivot_table_put1 (table, 0, content);
906 /* Increases TABLE's reference count, indicating that it has an additional
907 owner. A pivot table that is shared among multiple owners must not be
910 pivot_table_ref (const struct pivot_table *table_)
912 struct pivot_table *table = CONST_CAST (struct pivot_table *, table_);
917 static struct pivot_table_sizing
918 clone_sizing (const struct pivot_table_sizing *s)
920 return (struct pivot_table_sizing) {
921 .widths = (s->n_widths
922 ? xmemdup (s->widths, s->n_widths * sizeof *s->widths)
924 .n_widths = s->n_widths,
926 .breaks = (s->n_breaks
927 ? xmemdup (s->breaks, s->n_breaks * sizeof *s->breaks)
929 .n_breaks = s->n_breaks,
932 ? xmemdup (s->keeps, s->n_keeps * sizeof *s->keeps)
934 .n_keeps = s->n_keeps,
938 static struct pivot_footnote **
939 clone_footnotes (struct pivot_footnote **old, size_t n)
944 struct pivot_footnote **new = xmalloc (n * sizeof *new);
945 for (size_t i = 0; i < n; i++)
947 new[i] = xmalloc (sizeof *new[i]);
948 *new[i] = (struct pivot_footnote) {
950 .content = pivot_value_clone (old[i]->content),
951 .marker = pivot_value_clone (old[i]->marker),
952 .show = old[i]->show,
958 static struct pivot_category *
959 clone_category (struct pivot_category *old,
960 struct pivot_dimension *new_dimension,
961 struct pivot_category *new_parent)
963 struct pivot_category *new = xmalloc (sizeof *new);
964 *new = (struct pivot_category) {
965 .name = pivot_value_clone (old->name),
966 .parent = new_parent,
967 .dimension = new_dimension,
968 .label_depth = old->label_depth,
969 .extra_depth = old->extra_depth,
972 ? xcalloc (old->n_subs, sizeof *new->subs)
974 .n_subs = old->n_subs,
975 .allocated_subs = old->n_subs,
977 .show_label = old->show_label,
978 .show_label_in_corner = old->show_label_in_corner,
980 .format = old->format,
981 .group_index = old->group_index,
982 .data_index = old->data_index,
983 .presentation_index = old->presentation_index,
986 if (pivot_category_is_leaf (old))
988 assert (new->data_index < new_dimension->n_leaves);
989 new->dimension->data_leaves[new->data_index] = new;
991 assert (new->presentation_index < new_dimension->n_leaves);
992 new->dimension->presentation_leaves[new->presentation_index] = new;
995 for (size_t i = 0; i < new->n_subs; i++)
996 new->subs[i] = clone_category (old->subs[i], new_dimension, new);
1001 static struct pivot_dimension *
1002 clone_dimension (struct pivot_dimension *old, struct pivot_table *new_pt)
1004 struct pivot_dimension *new = xmalloc (sizeof *new);
1005 *new = (struct pivot_dimension) {
1007 .axis_type = old->axis_type,
1008 .level = old->level,
1009 .top_index = old->top_index,
1010 .data_leaves = xcalloc (old->n_leaves , sizeof *new->data_leaves),
1011 .presentation_leaves = xcalloc (old->n_leaves
1012 , sizeof *new->presentation_leaves),
1013 .n_leaves = old->n_leaves,
1014 .allocated_leaves = old->n_leaves,
1015 .hide_all_labels = old->hide_all_labels,
1016 .label_depth = old->label_depth,
1019 new->root = clone_category (old->root, new, NULL);
1024 static struct pivot_dimension **
1025 clone_dimensions (struct pivot_dimension **old, size_t n,
1026 struct pivot_table *new_pt)
1031 struct pivot_dimension **new = xmalloc (n * sizeof *new);
1032 for (size_t i = 0; i < n; i++)
1033 new[i] = clone_dimension (old[i], new_pt);
1037 struct pivot_table *
1038 pivot_table_unshare (struct pivot_table *old)
1040 assert (old->ref_cnt > 0);
1041 if (old->ref_cnt == 1)
1044 pivot_table_unref (old);
1046 struct pivot_table *new = xmalloc (sizeof *new);
1047 *new = (struct pivot_table) {
1050 .look = pivot_table_look_ref (old->look),
1052 .rotate_inner_column_labels = old->rotate_inner_column_labels,
1053 .rotate_outer_row_labels = old->rotate_outer_row_labels,
1054 .show_grid_lines = old->show_grid_lines,
1055 .show_title = old->show_title,
1056 .show_caption = old->show_caption,
1057 .current_layer = (old->current_layer
1058 ? xmemdup (old->current_layer,
1059 old->axes[PIVOT_AXIS_LAYER].n_dimensions
1060 * sizeof *new->current_layer)
1062 .show_values = old->show_values,
1063 .show_variables = old->show_variables,
1064 .weight_format = old->weight_format,
1067 [TABLE_HORZ] = clone_sizing (&old->sizing[TABLE_HORZ]),
1068 [TABLE_VERT] = clone_sizing (&old->sizing[TABLE_VERT]),
1071 .settings = fmt_settings_copy (&old->settings),
1072 .grouping = old->grouping,
1073 .small = old->small,
1075 .command_local = xstrdup_if_nonnull (old->command_local),
1076 .command_c = xstrdup_if_nonnull (old->command_c),
1077 .language = xstrdup_if_nonnull (old->language),
1078 .locale = xstrdup_if_nonnull (old->locale),
1080 .dataset = xstrdup_if_nonnull (old->dataset),
1081 .datafile = xstrdup_if_nonnull (old->datafile),
1084 .footnotes = clone_footnotes (old->footnotes, old->n_footnotes),
1085 .n_footnotes = old->n_footnotes,
1086 .allocated_footnotes = old->n_footnotes,
1088 .title = pivot_value_clone (old->title),
1089 .subtype = pivot_value_clone (old->subtype),
1090 .corner_text = pivot_value_clone (old->corner_text),
1091 .caption = pivot_value_clone (old->caption),
1092 .notes = xstrdup_if_nonnull (old->notes),
1094 .dimensions = clone_dimensions (old->dimensions, old->n_dimensions, new),
1095 .n_dimensions = old->n_dimensions,
1097 .cells = HMAP_INITIALIZER (new->cells),
1100 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1102 struct pivot_axis *new_axis = &new->axes[i];
1103 const struct pivot_axis *old_axis = &old->axes[i];
1105 *new_axis = (struct pivot_axis) {
1106 .dimensions = xmalloc (old_axis->n_dimensions
1107 * sizeof *new_axis->dimensions),
1108 .n_dimensions = old_axis->n_dimensions,
1109 .extent = old_axis->extent,
1110 .label_depth = old_axis->label_depth,
1113 for (size_t i = 0; i < new_axis->n_dimensions; i++)
1114 new_axis->dimensions[i] = new->dimensions[
1115 old_axis->dimensions[i]->top_index];
1118 const struct pivot_cell *old_cell;
1119 size_t *dindexes = xmalloc (old->n_dimensions * sizeof *dindexes);
1120 HMAP_FOR_EACH (old_cell, struct pivot_cell, hmap_node, &old->cells)
1122 for (size_t i = 0; i < old->n_dimensions; i++)
1123 dindexes[i] = old_cell->idx[i];
1124 struct pivot_cell *new_cell
1125 = pivot_table_insert_cell (new, dindexes);
1126 new_cell->value = pivot_value_clone (old_cell->value);
1133 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
1134 If TABLE no longer has any owners, it is freed. */
1136 pivot_table_unref (struct pivot_table *table)
1140 assert (table->ref_cnt > 0);
1141 if (--table->ref_cnt)
1144 free (table->current_layer);
1145 pivot_table_look_unref (table->look);
1147 for (int i = 0; i < TABLE_N_AXES; i++)
1148 pivot_table_sizing_uninit (&table->sizing[i]);
1150 fmt_settings_uninit (&table->settings);
1152 free (table->command_local);
1153 free (table->command_c);
1154 free (table->language);
1155 free (table->locale);
1157 free (table->dataset);
1158 free (table->datafile);
1160 for (size_t i = 0; i < table->n_footnotes; i++)
1161 pivot_footnote_destroy (table->footnotes[i]);
1162 free (table->footnotes);
1164 pivot_value_destroy (table->title);
1165 pivot_value_destroy (table->subtype);
1166 pivot_value_destroy (table->corner_text);
1167 pivot_value_destroy (table->caption);
1168 free (table->notes);
1170 for (size_t i = 0; i < table->n_dimensions; i++)
1171 pivot_dimension_destroy (table->dimensions[i]);
1172 free (table->dimensions);
1174 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1175 free (table->axes[i].dimensions);
1177 struct pivot_cell *cell, *next_cell;
1178 HMAP_FOR_EACH_SAFE (cell, next_cell, struct pivot_cell, hmap_node,
1180 pivot_table_delete_cell (table, cell);
1182 hmap_destroy (&table->cells);
1187 /* Returns true if TABLE has more than one owner. A pivot table that is shared
1188 among multiple owners must not be modified. */
1190 pivot_table_is_shared (const struct pivot_table *table)
1192 return table->ref_cnt > 1;
1196 pivot_table_set_value__ (struct pivot_value **dstp, struct pivot_value *src)
1198 pivot_value_destroy (*dstp);
1202 /* Changes the title of TABLE to TITLE. Takes ownership of TITLE. */
1204 pivot_table_set_title (struct pivot_table *table, struct pivot_value *title)
1206 pivot_table_set_value__ (&table->title, title);
1209 /* Changes the subtype of TABLE to SUBTYPE. Takes ownership of SUBTYPE. */
1211 pivot_table_set_subtype (struct pivot_table *table, struct pivot_value *subtype)
1213 pivot_table_set_value__ (&table->subtype, subtype);
1216 /* Changes the corner text of TABLE to CORNER_TEXT. Takes ownership of
1219 pivot_table_set_corner_text (struct pivot_table *table,
1220 struct pivot_value *corner_text)
1222 pivot_table_set_value__ (&table->corner_text, corner_text);
1225 /* Changes the caption of TABLE to CAPTION. Takes ownership of CAPTION. */
1227 pivot_table_set_caption (struct pivot_table *table, struct pivot_value *caption)
1229 pivot_table_set_value__ (&table->caption, caption);
1232 /* Swaps axes A and B in TABLE. */
1234 pivot_table_swap_axes (struct pivot_table *table,
1235 enum pivot_axis_type a, enum pivot_axis_type b)
1240 struct pivot_axis tmp = table->axes[a];
1241 table->axes[a] = table->axes[b];
1242 table->axes[b] = tmp;
1244 for (int a = 0; a < PIVOT_N_AXES; a++)
1246 struct pivot_axis *axis = &table->axes[a];
1247 for (size_t d = 0; d < axis->n_dimensions; d++)
1248 axis->dimensions[d]->axis_type = a;
1251 if (a == PIVOT_AXIS_LAYER || b == PIVOT_AXIS_LAYER)
1253 free (table->current_layer);
1254 table->current_layer = xzalloc (
1255 table->axes[PIVOT_AXIS_LAYER].n_dimensions
1256 * sizeof *table->current_layer);
1260 /* Swaps the row and column axes in TABLE. */
1262 pivot_table_transpose (struct pivot_table *table)
1264 pivot_table_swap_axes (table, PIVOT_AXIS_ROW, PIVOT_AXIS_COLUMN);
1268 pivot_table_update_axes (struct pivot_table *table)
1270 for (int a = 0; a < PIVOT_N_AXES; a++)
1272 struct pivot_axis *axis = &table->axes[a];
1274 for (size_t d = 0; d < axis->n_dimensions; d++)
1276 struct pivot_dimension *dim = axis->dimensions[d];
1283 /* Moves DIM from its current location in TABLE to POS within AXIS. POS of 0
1284 is the innermost dimension, 1 is the next one out, and so on. */
1286 pivot_table_move_dimension (struct pivot_table *table,
1287 struct pivot_dimension *dim,
1288 enum pivot_axis_type axis, size_t pos)
1290 assert (dim->table == table);
1292 struct pivot_axis *old_axis = &table->axes[dim->axis_type];
1293 struct pivot_axis *new_axis = &table->axes[axis];
1294 pos = MIN (pos, new_axis->n_dimensions);
1296 if (old_axis == new_axis && pos == dim->level)
1302 /* Update the current layer, if necessary. If we're moving within the layer
1303 axis, preserve the current layer. */
1304 if (dim->axis_type == PIVOT_AXIS_LAYER)
1306 if (axis == PIVOT_AXIS_LAYER)
1308 /* Rearranging the layer axis. */
1309 move_element (table->current_layer, old_axis->n_dimensions,
1310 sizeof *table->current_layer,
1315 /* A layer is becoming a row or column. */
1316 remove_element (table->current_layer, old_axis->n_dimensions,
1317 sizeof *table->current_layer, dim->level);
1320 else if (axis == PIVOT_AXIS_LAYER)
1322 /* A row or column is becoming a layer. */
1323 table->current_layer = xrealloc (
1324 table->current_layer,
1325 (new_axis->n_dimensions + 1) * sizeof *table->current_layer);
1326 insert_element (table->current_layer, new_axis->n_dimensions,
1327 sizeof *table->current_layer, pos);
1328 table->current_layer[pos] = 0;
1331 /* Remove DIM from its current axis. */
1332 remove_element (old_axis->dimensions, old_axis->n_dimensions,
1333 sizeof *old_axis->dimensions, dim->level);
1334 old_axis->n_dimensions--;
1336 /* Insert DIM into its new axis. */
1337 new_axis->dimensions = xrealloc (
1338 new_axis->dimensions,
1339 (new_axis->n_dimensions + 1) * sizeof *new_axis->dimensions);
1340 insert_element (new_axis->dimensions, new_axis->n_dimensions,
1341 sizeof *new_axis->dimensions, pos);
1342 new_axis->dimensions[pos] = dim;
1343 new_axis->n_dimensions++;
1345 pivot_table_update_axes (table);
1349 const struct pivot_table_look *
1350 pivot_table_get_look (const struct pivot_table *table)
1356 pivot_table_set_look (struct pivot_table *table,
1357 const struct pivot_table_look *look)
1359 pivot_table_look_unref (table->look);
1360 table->look = pivot_table_look_ref (look);
1363 /* Sets the format used for PIVOT_RC_COUNT cells to the one used for variable
1364 WV, which should be the weight variable for the dictionary whose data or
1365 statistics are being put into TABLE.
1367 This has no effect if WV is NULL. */
1369 pivot_table_set_weight_var (struct pivot_table *table,
1370 const struct variable *wv)
1373 pivot_table_set_weight_format (table, var_get_print_format (wv));
1376 /* Sets the format used for PIVOT_RC_COUNT cells to WFMT, which should be the
1377 format for the dictionary whose data or statistics are being put into TABLE.
1379 This has no effect if WFMT is NULL. */
1381 pivot_table_set_weight_format (struct pivot_table *table,
1382 const struct fmt_spec *wfmt)
1385 table->weight_format = *wfmt;
1388 /* Returns true if TABLE has no cells, false otherwise. */
1390 pivot_table_is_empty (const struct pivot_table *table)
1392 return hmap_is_empty (&table->cells);
1396 pivot_cell_hash_indexes (const size_t *indexes, size_t n_idx)
1398 return hash_bytes (indexes, n_idx * sizeof *indexes, 0);
1402 equal_indexes (const size_t *a, const unsigned int *b, size_t n)
1404 for (size_t i = 0; i < n; i++)
1411 static struct pivot_cell *
1412 pivot_table_lookup_cell__ (const struct pivot_table *table,
1413 const size_t *dindexes, unsigned int hash)
1415 struct pivot_cell *cell;
1416 HMAP_FOR_EACH_WITH_HASH (cell, struct pivot_cell, hmap_node, hash,
1418 if (equal_indexes (dindexes, cell->idx, table->n_dimensions))
1423 static struct pivot_cell *
1424 pivot_cell_allocate (size_t n_idx)
1426 struct pivot_cell *cell UNUSED;
1427 return xmalloc (sizeof *cell + n_idx * sizeof *cell->idx);
1430 static struct pivot_cell *
1431 pivot_table_insert_cell (struct pivot_table *table, const size_t *dindexes)
1433 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1434 struct pivot_cell *cell = pivot_table_lookup_cell__ (table, dindexes, hash);
1437 cell = pivot_cell_allocate (table->n_dimensions);
1438 for (size_t i = 0; i < table->n_dimensions; i++)
1439 cell->idx[i] = dindexes[i];
1441 hmap_insert (&table->cells, &cell->hmap_node, hash);
1446 /* Puts VALUE in the cell in TABLE whose indexes are given by the N indexes in
1447 DINDEXES. The order of the indexes is the same as the order in which the
1448 dimensions were created. N must be the number of dimensions in TABLE.
1449 Takes ownership of VALUE.
1451 If VALUE is a numeric value without a specified format, this function checks
1452 each of the categories designated by DINDEXES[] and takes the format from
1453 the first category with a result class. If none has a result class, uses
1454 the overall default numeric format. */
1456 pivot_table_put (struct pivot_table *table, const size_t *dindexes, size_t n,
1457 struct pivot_value *value)
1459 assert (n == table->n_dimensions);
1460 for (size_t i = 0; i < n; i++)
1461 assert (dindexes[i] < table->dimensions[i]->n_leaves);
1463 if (value->type == PIVOT_VALUE_NUMERIC && !value->numeric.format.w)
1465 for (size_t i = 0; i < table->n_dimensions; i++)
1467 const struct pivot_dimension *d = table->dimensions[i];
1468 if (dindexes[i] < d->n_leaves)
1470 const struct pivot_category *c = d->data_leaves[dindexes[i]];
1473 value->numeric.format = c->format;
1474 value->numeric.honor_small = c->honor_small;
1479 value->numeric.format = *settings_get_format ();
1480 value->numeric.honor_small = true;
1485 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1486 pivot_value_destroy (cell->value);
1487 cell->value = value;
1490 /* Puts VALUE in the cell in TABLE with index IDX1. TABLE must have 1
1491 dimension. Takes ownership of VALUE. */
1493 pivot_table_put1 (struct pivot_table *table, size_t idx1,
1494 struct pivot_value *value)
1496 size_t dindexes[] = { idx1 };
1497 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1500 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2). TABLE must have 2
1501 dimensions. Takes ownership of VALUE. */
1503 pivot_table_put2 (struct pivot_table *table, size_t idx1, size_t idx2,
1504 struct pivot_value *value)
1506 size_t dindexes[] = { idx1, idx2 };
1507 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1510 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3). TABLE must
1511 have 3 dimensions. Takes ownership of VALUE. */
1513 pivot_table_put3 (struct pivot_table *table, size_t idx1, size_t idx2,
1514 size_t idx3, struct pivot_value *value)
1516 size_t dindexes[] = { idx1, idx2, idx3 };
1517 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1520 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3, IDX4). TABLE
1521 must have 4 dimensions. Takes ownership of VALUE. */
1523 pivot_table_put4 (struct pivot_table *table, size_t idx1, size_t idx2,
1524 size_t idx3, size_t idx4, struct pivot_value *value)
1526 size_t dindexes[] = { idx1, idx2, idx3, idx4 };
1527 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1530 /* Creates and returns a new footnote in TABLE with the given CONTENT and an
1531 automatically assigned marker.
1533 The footnote will only appear in output if it is referenced. Use
1534 pivot_value_add_footnote() to add a reference to the footnote. */
1535 struct pivot_footnote *
1536 pivot_table_create_footnote (struct pivot_table *table,
1537 struct pivot_value *content)
1539 return pivot_table_create_footnote__ (table, table->n_footnotes,
1544 pivot_footnote_format_marker (const struct pivot_footnote *f,
1545 const struct pivot_table *pt,
1549 pivot_value_format_body (f->marker, pt, s);
1550 else if (pt->look->show_numeric_markers)
1551 ds_put_format (s, "%zu", f->idx + 1);
1554 char text[INT_BUFSIZE_BOUND (size_t)];
1555 str_format_26adic (f->idx + 1, false, text, sizeof text);
1556 ds_put_cstr (s, text);
1561 pivot_footnote_marker_string (const struct pivot_footnote *f,
1562 const struct pivot_table *pt)
1564 struct string s = DS_EMPTY_INITIALIZER;
1565 pivot_footnote_format_marker (f, pt, &s);
1566 return ds_steal_cstr (&s);
1569 /* Creates or modifies a footnote in TABLE with 0-based number IDX (and creates
1570 all lower indexes as a side effect). If MARKER is nonnull, sets the
1571 footnote's marker; if CONTENT is nonnull, sets the footnote's content. */
1572 struct pivot_footnote *
1573 pivot_table_create_footnote__ (struct pivot_table *table, size_t idx,
1574 struct pivot_value *marker,
1575 struct pivot_value *content)
1577 if (idx >= table->n_footnotes)
1579 while (idx >= table->allocated_footnotes)
1580 table->footnotes = x2nrealloc (table->footnotes,
1581 &table->allocated_footnotes,
1582 sizeof *table->footnotes);
1583 while (idx >= table->n_footnotes)
1585 struct pivot_footnote *f = xmalloc (sizeof *f);
1586 *f = (struct pivot_footnote) {
1587 .idx = table->n_footnotes,
1590 table->footnotes[table->n_footnotes++] = f;
1594 struct pivot_footnote *f = table->footnotes[idx];
1597 pivot_value_destroy (f->marker);
1602 pivot_value_destroy (f->content);
1603 f->content = content;
1608 /* Frees the data owned by F. */
1610 pivot_footnote_destroy (struct pivot_footnote *f)
1614 pivot_value_destroy (f->content);
1615 pivot_value_destroy (f->marker);
1620 /* Converts per-axis presentation-order indexes, given in PINDEXES, into data
1621 indexes for each dimension in TABLE in DINDEXES[]. */
1623 pivot_table_convert_indexes_ptod (const struct pivot_table *table,
1624 const size_t *pindexes[PIVOT_N_AXES],
1625 size_t dindexes[/* table->n_dimensions */])
1627 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1629 const struct pivot_axis *axis = &table->axes[i];
1631 for (size_t j = 0; j < axis->n_dimensions; j++)
1633 const struct pivot_dimension *d = axis->dimensions[j];
1634 size_t pindex = pindexes[i][j];
1635 dindexes[d->top_index] = d->presentation_leaves[pindex]->data_index;
1641 pivot_table_enumerate_axis (const struct pivot_table *table,
1642 enum pivot_axis_type axis_type,
1643 const size_t *layer_indexes, bool omit_empty,
1646 const struct pivot_axis *axis = &table->axes[axis_type];
1647 if (!axis->n_dimensions)
1649 size_t *enumeration = xnmalloc (2, sizeof *enumeration);
1651 enumeration[1] = SIZE_MAX;
1656 else if (!axis->extent)
1658 size_t *enumeration = xmalloc (sizeof *enumeration);
1659 *enumeration = SIZE_MAX;
1665 size_t *enumeration = xnmalloc (xsum (xtimes (axis->extent,
1666 axis->n_dimensions), 1),
1667 sizeof *enumeration);
1668 size_t *p = enumeration;
1669 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
1671 size_t *axis_indexes;
1672 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1676 enum pivot_axis_type axis2_type
1677 = pivot_axis_type_transpose (axis_type);
1679 size_t *axis2_indexes;
1680 PIVOT_AXIS_FOR_EACH (axis2_indexes, &table->axes[axis2_type])
1682 const size_t *pindexes[PIVOT_N_AXES];
1683 pindexes[PIVOT_AXIS_LAYER] = layer_indexes;
1684 pindexes[axis_type] = axis_indexes;
1685 pindexes[axis2_type] = axis2_indexes;
1686 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
1687 if (pivot_table_get (table, dindexes))
1693 free (axis2_indexes);
1696 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1697 p += axis->n_dimensions;
1699 if (omit_empty && p == enumeration)
1701 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1703 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1704 p += axis->n_dimensions;
1709 *n = (p - enumeration) / axis->n_dimensions;
1715 static struct pivot_cell *
1716 pivot_table_lookup_cell (const struct pivot_table *table,
1717 const size_t *dindexes)
1719 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1720 return pivot_table_lookup_cell__ (table, dindexes, hash);
1723 const struct pivot_value *
1724 pivot_table_get (const struct pivot_table *table, const size_t *dindexes)
1726 const struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1727 return cell ? cell->value : NULL;
1730 struct pivot_value *
1731 pivot_table_get_rw (struct pivot_table *table, const size_t *dindexes)
1733 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1735 cell->value = pivot_value_new_user_text ("", -1);
1740 pivot_table_delete_cell (struct pivot_table *table, struct pivot_cell *cell)
1742 hmap_delete (&table->cells, &cell->hmap_node);
1743 pivot_value_destroy (cell->value);
1748 pivot_table_delete (struct pivot_table *table, const size_t *dindexes)
1750 struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1753 pivot_table_delete_cell (table, cell);
1761 distribute_extra_depth (struct pivot_category *category, size_t extra_depth)
1763 if (pivot_category_is_group (category) && category->n_subs)
1764 for (size_t i = 0; i < category->n_subs; i++)
1765 distribute_extra_depth (category->subs[i], extra_depth);
1767 category->extra_depth += extra_depth;
1771 pivot_category_assign_label_depth (struct pivot_category *category,
1772 bool dimension_labels_in_corner)
1774 category->extra_depth = 0;
1776 if (pivot_category_is_group (category))
1779 for (size_t i = 0; i < category->n_subs; i++)
1781 pivot_category_assign_label_depth (category->subs[i], false);
1782 depth = MAX (depth, category->subs[i]->label_depth);
1785 for (size_t i = 0; i < category->n_subs; i++)
1787 struct pivot_category *sub = category->subs[i];
1789 size_t extra_depth = depth - sub->label_depth;
1791 distribute_extra_depth (sub, extra_depth);
1793 sub->label_depth = depth;
1796 category->show_label_in_corner = (category->show_label
1797 && dimension_labels_in_corner);
1798 category->label_depth
1799 = (category->show_label && !category->show_label_in_corner
1800 ? depth + 1 : depth);
1803 category->label_depth = 1;
1807 pivot_axis_assign_label_depth (struct pivot_table *table,
1808 enum pivot_axis_type axis_type,
1809 bool dimension_labels_in_corner)
1811 struct pivot_axis *axis = &table->axes[axis_type];
1812 bool any_label_shown_in_corner = false;
1813 axis->label_depth = 0;
1815 for (size_t i = 0; i < axis->n_dimensions; i++)
1817 struct pivot_dimension *d = axis->dimensions[i];
1818 pivot_category_assign_label_depth (d->root, dimension_labels_in_corner);
1819 d->label_depth = d->hide_all_labels ? 0 : d->root->label_depth;
1820 axis->label_depth += d->label_depth;
1821 axis->extent *= d->n_leaves;
1823 if (d->root->show_label_in_corner)
1824 any_label_shown_in_corner = true;
1826 return any_label_shown_in_corner;
1830 pivot_table_assign_label_depth (struct pivot_table *table)
1832 pivot_axis_assign_label_depth (table, PIVOT_AXIS_COLUMN, false);
1833 if (pivot_axis_assign_label_depth (
1834 table, PIVOT_AXIS_ROW, (table->look->row_labels_in_corner
1835 && !table->corner_text))
1836 && table->axes[PIVOT_AXIS_COLUMN].label_depth == 0)
1837 table->axes[PIVOT_AXIS_COLUMN].label_depth = 1;
1838 pivot_axis_assign_label_depth (table, PIVOT_AXIS_LAYER, false);
1842 indent (int indentation)
1844 for (int i = 0; i < indentation * 2; i++)
1849 pivot_value_dump (const struct pivot_value *value,
1850 const struct pivot_table *pt)
1852 char *s = pivot_value_to_string (value, pt);
1858 pivot_table_dump_value (const struct pivot_value *value, const char *name,
1859 const struct pivot_table *pt, int indentation)
1863 indent (indentation);
1864 printf ("%s: ", name);
1865 pivot_value_dump (value, pt);
1871 pivot_table_dump_string (const char *string, const char *name, int indentation)
1875 indent (indentation);
1876 printf ("%s: %s\n", name, string);
1881 pivot_category_dump (const struct pivot_category *c,
1882 const struct pivot_table *pt, int indentation)
1884 indent (indentation);
1885 printf ("%s \"", pivot_category_is_leaf (c) ? "leaf" : "group");
1886 pivot_value_dump (c->name, pt);
1889 if (pivot_category_is_leaf (c))
1890 printf ("data_index=%zu\n", c->data_index);
1893 printf (" (label %s)", c->show_label ? "shown" : "hidden");
1896 for (size_t i = 0; i < c->n_subs; i++)
1897 pivot_category_dump (c->subs[i], pt, indentation + 1);
1902 pivot_dimension_dump (const struct pivot_dimension *d,
1903 const struct pivot_table *pt, int indentation)
1905 indent (indentation);
1906 printf ("%s dimension %zu (where 0=innermost), label_depth=%d:\n",
1907 pivot_axis_type_to_string (d->axis_type), d->level, d->label_depth);
1909 pivot_category_dump (d->root, pt, indentation + 1);
1913 table_area_style_dump (enum pivot_area area, const struct table_area_style *a,
1916 indent (indentation);
1917 printf ("%s: ", pivot_area_to_string (area));
1918 font_style_dump (&a->font_style);
1920 cell_style_dump (&a->cell_style);
1925 table_border_style_dump (enum pivot_border border,
1926 const struct table_border_style *b, int indentation)
1928 indent (indentation);
1929 printf ("%s: %s ", pivot_border_to_string (border),
1930 table_stroke_to_string (b->stroke));
1931 cell_color_dump (&b->color);
1936 compose_headings (const struct pivot_table *pt,
1937 const struct pivot_axis *axis,
1938 const size_t *column_enumeration)
1940 if (!axis->n_dimensions || !axis->extent || !axis->label_depth)
1943 char ***headings = xnmalloc (axis->label_depth, sizeof *headings);
1944 for (size_t i = 0; i < axis->label_depth; i++)
1945 headings[i] = xcalloc (axis->extent, sizeof **headings);
1947 const size_t *indexes;
1949 PIVOT_ENUMERATION_FOR_EACH (indexes, column_enumeration, axis)
1951 int row = axis->label_depth - 1;
1952 for (int dim_index = 0; dim_index < axis->n_dimensions; dim_index++)
1954 const struct pivot_dimension *d = axis->dimensions[dim_index];
1955 if (d->hide_all_labels)
1957 for (const struct pivot_category *c
1958 = d->presentation_leaves[indexes[dim_index]];
1962 if (pivot_category_is_leaf (c) || (c->show_label
1963 && !c->show_label_in_corner))
1965 headings[row][column] = pivot_value_to_string (c->name, pt);
1966 if (!*headings[row][column])
1968 free (headings[row][column]);
1969 headings[row][column] = xstrdup ("<blank>");
1982 free_headings (const struct pivot_axis *axis, char ***headings)
1986 for (size_t i = 0; i < axis->label_depth; i++)
1988 for (size_t j = 0; j < axis->extent; j++)
1989 free (headings[i][j]);
1996 pivot_table_sizing_dump (const char *name,
1997 const int width_ranges[2],
1998 const struct pivot_table_sizing *s,
2001 indent (indentation);
2002 printf ("%ss: min=%d, max=%d\n", name, width_ranges[0], width_ranges[1]);
2005 indent (indentation + 1);
2006 printf ("%s widths:", name);
2007 for (size_t i = 0; i < s->n_widths; i++)
2008 printf (" %d", s->widths[i]);
2013 indent (indentation + 1);
2014 printf ("break after %ss:", name);
2015 for (size_t i = 0; i < s->n_breaks; i++)
2016 printf (" %zu", s->breaks[i]);
2021 indent (indentation + 1);
2022 printf ("keep %ss together:", name);
2023 for (size_t i = 0; i < s->n_keeps; i++)
2024 printf (" [%zu,%zu]",
2026 s->keeps[i].ofs + s->keeps[i].n - 1);
2032 dump_leaf (const struct pivot_table *table, const struct pivot_category *c)
2036 dump_leaf (table, c->parent);
2037 if (pivot_category_is_leaf (c) || c->show_label)
2040 pivot_value_dump (c->name, table);
2046 pivot_table_dump (const struct pivot_table *table, int indentation)
2051 pivot_table_assign_label_depth (CONST_CAST (struct pivot_table *, table));
2053 pivot_table_dump_value (table->title, "title", table, indentation);
2054 pivot_table_dump_value (table->subtype, "subtype", table, indentation);
2055 pivot_table_dump_string (table->command_c, "command", indentation);
2056 pivot_table_dump_string (table->dataset, "dataset", indentation);
2057 pivot_table_dump_string (table->datafile, "datafile", indentation);
2058 pivot_table_dump_string (table->notes, "notes", indentation);
2059 pivot_table_dump_string (table->look->name, "table-look", indentation);
2062 indent (indentation);
2064 struct tm *tm = localtime (&table->date);
2065 printf ("date: %d-%02d-%02d %d:%02d:%02d\n", tm->tm_year + 1900,
2066 tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
2070 indent (indentation);
2071 printf ("sizing:\n");
2072 pivot_table_sizing_dump ("column", table->look->width_ranges[TABLE_HORZ],
2073 &table->sizing[TABLE_HORZ], indentation + 1);
2074 pivot_table_sizing_dump ("row", table->look->width_ranges[TABLE_VERT],
2075 &table->sizing[TABLE_VERT], indentation + 1);
2077 indent (indentation);
2078 printf ("areas:\n");
2079 for (enum pivot_area area = 0; area < PIVOT_N_AREAS; area++)
2080 table_area_style_dump (area, &table->look->areas[area], indentation + 1);
2082 indent (indentation);
2083 printf ("borders:\n");
2084 for (enum pivot_border border = 0; border < PIVOT_N_BORDERS; border++)
2085 table_border_style_dump (border, &table->look->borders[border],
2088 for (size_t i = 0; i < table->n_dimensions; i++)
2089 pivot_dimension_dump (table->dimensions[i], table, indentation);
2091 /* Presentation and data indexes. */
2092 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
2094 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2095 if (layer_axis->n_dimensions)
2097 indent (indentation);
2098 printf ("current layer:");
2100 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2102 const struct pivot_dimension *d = layer_axis->dimensions[i];
2103 char *name = pivot_value_to_string (d->root->name, table);
2104 printf (" %s", name);
2107 size_t ofs = table->current_layer[i];
2108 if (ofs < d->n_leaves)
2110 char *value = pivot_value_to_string (d->data_leaves[ofs]->name,
2112 printf ("=%s", value);
2120 size_t *layer_indexes;
2121 size_t layer_iteration = 0;
2122 PIVOT_AXIS_FOR_EACH (layer_indexes, &table->axes[PIVOT_AXIS_LAYER])
2124 indent (indentation);
2125 printf ("layer %zu:", layer_iteration++);
2127 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2128 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2130 const struct pivot_dimension *d = layer_axis->dimensions[i];
2132 fputs (i == 0 ? " " : ", ", stdout);
2133 pivot_value_dump (d->root->name, table);
2134 fputs (" =", stdout);
2136 dump_leaf (table, d->presentation_leaves[layer_indexes[i]]);
2140 size_t *column_enumeration = pivot_table_enumerate_axis (
2141 table, PIVOT_AXIS_COLUMN, layer_indexes, table->look->omit_empty, NULL);
2142 size_t *row_enumeration = pivot_table_enumerate_axis (
2143 table, PIVOT_AXIS_ROW, layer_indexes, table->look->omit_empty, NULL);
2145 /* Print column headings.
2147 Ordinarily the test for nonnull 'column_headings' would be
2148 unnecessary, because 'column_headings' is null only if the axis's
2149 label_depth is 0, but there is a special case for the column axis only
2150 in pivot_table_assign_label_depth(). */
2151 char ***column_headings = compose_headings (
2152 table, &table->axes[PIVOT_AXIS_COLUMN], column_enumeration);
2153 if (column_headings)
2155 for (size_t y = 0; y < table->axes[PIVOT_AXIS_COLUMN].label_depth; y++)
2157 indent (indentation + 1);
2158 for (size_t x = 0; x < table->axes[PIVOT_AXIS_COLUMN].extent; x++)
2161 fputs ("; ", stdout);
2162 if (column_headings && column_headings[y] && column_headings[y][x])
2163 fputs (column_headings[y][x], stdout);
2167 free_headings (&table->axes[PIVOT_AXIS_COLUMN], column_headings);
2170 indent (indentation + 1);
2171 printf ("-----------------------------------------------\n");
2173 char ***row_headings = compose_headings (
2174 table, &table->axes[PIVOT_AXIS_ROW], row_enumeration);
2177 const size_t *pindexes[PIVOT_N_AXES]
2178 = { [PIVOT_AXIS_LAYER] = layer_indexes };
2179 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
2180 &table->axes[PIVOT_AXIS_ROW])
2182 indent (indentation + 1);
2185 for (size_t y = 0; y < table->axes[PIVOT_AXIS_ROW].label_depth; y++)
2188 fputs ("; ", stdout);
2189 if (row_headings[y][x])
2190 fputs (row_headings[y][x], stdout);
2196 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
2198 &table->axes[PIVOT_AXIS_COLUMN])
2203 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
2204 const struct pivot_value *value = pivot_table_get (
2207 pivot_value_dump (value, table);
2214 free (column_enumeration);
2215 free (row_enumeration);
2216 free_headings (&table->axes[PIVOT_AXIS_ROW], row_headings);
2219 pivot_table_dump_value (table->caption, "caption", table, indentation);
2221 for (size_t i = 0; i < table->n_footnotes; i++)
2223 const struct pivot_footnote *f = table->footnotes[i];
2224 indent (indentation);
2227 pivot_value_dump (f->marker, table);
2229 printf ("%zu", f->idx);
2231 pivot_value_dump (f->content, table);
2239 consume_int (const char *p, size_t *n)
2242 while (c_isdigit (*p))
2243 *n = *n * 10 + (*p++ - '0');
2248 pivot_format_inner_template (struct string *out, const char *template,
2250 struct pivot_value **values, size_t n_values,
2251 const struct pivot_table *pt)
2253 size_t args_consumed = 0;
2254 while (*template && *template != ':')
2256 if (*template == '\\' && template[1])
2258 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2261 else if (*template == escape)
2264 template = consume_int (template + 1, &index);
2265 if (index >= 1 && index <= n_values)
2267 pivot_value_format (values[index - 1], pt, out);
2268 args_consumed = MAX (args_consumed, index);
2272 ds_put_byte (out, *template++);
2274 return args_consumed;
2278 pivot_extract_inner_template (const char *template, const char **p)
2284 if (*template == '\\' && template[1] != '\0')
2286 else if (*template == ':')
2287 return template + 1;
2288 else if (*template == '\0')
2296 pivot_format_template (struct string *out, const char *template,
2297 const struct pivot_argument *args, size_t n_args,
2298 const struct pivot_table *pt)
2302 if (*template == '\\' && template[1] != '\0')
2304 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2307 else if (*template == '^')
2310 template = consume_int (template + 1, &index);
2311 if (index >= 1 && index <= n_args && args[index - 1].n > 0)
2312 pivot_value_format (args[index - 1].values[0], pt, out);
2314 else if (*template == '[')
2316 const char *tmpl[2];
2317 template = pivot_extract_inner_template (template + 1, &tmpl[0]);
2318 template = pivot_extract_inner_template (template, &tmpl[1]);
2319 template += *template == ']';
2322 template = consume_int (template, &index);
2323 if (index < 1 || index > n_args)
2326 const struct pivot_argument *arg = &args[index - 1];
2327 size_t left = arg->n;
2330 struct pivot_value **values = arg->values + (arg->n - left);
2331 int tmpl_idx = left == arg->n && *tmpl[0] != ':' ? 0 : 1;
2332 char escape = "%^"[tmpl_idx];
2333 size_t used = pivot_format_inner_template (
2334 out, tmpl[tmpl_idx], escape, values, left, pt);
2335 if (!used || used > left)
2341 ds_put_byte (out, *template++);
2345 static enum settings_value_show
2346 interpret_show (enum settings_value_show global_show,
2347 enum settings_value_show table_show,
2348 enum settings_value_show value_show,
2351 return (!has_label ? SETTINGS_VALUE_SHOW_VALUE
2352 : value_show != SETTINGS_VALUE_SHOW_DEFAULT ? value_show
2353 : table_show != SETTINGS_VALUE_SHOW_DEFAULT ? table_show
2357 /* Appends to OUT the actual text content from the given Pango MARKUP. */
2359 get_text_from_markup (const char *markup, struct string *out)
2361 xmlParserCtxt *parser = xmlCreatePushParserCtxt (NULL, NULL, NULL, 0, NULL);
2364 ds_put_cstr (out, markup);
2368 xmlParseChunk (parser, "<xml>", strlen ("<xml>"), false);
2369 xmlParseChunk (parser, markup, strlen (markup), false);
2370 xmlParseChunk (parser, "</xml>", strlen ("</xml>"), true);
2372 if (parser->wellFormed)
2374 xmlChar *s = xmlNodeGetContent (xmlDocGetRootElement (parser->myDoc));
2375 ds_put_cstr (out, CHAR_CAST (char *, s));
2379 ds_put_cstr (out, markup);
2380 xmlFreeDoc (parser->myDoc);
2381 xmlFreeParserCtxt (parser);
2384 static const struct pivot_table pivot_value_format_defaults = {
2385 .show_values = SETTINGS_VALUE_SHOW_DEFAULT,
2386 .show_variables = SETTINGS_VALUE_SHOW_DEFAULT,
2387 .settings = FMT_SETTINGS_INIT,
2390 /* Appends a text representation of the body of VALUE to OUT. Settings on PT
2391 control whether variable and value labels are included (pass NULL for PT to
2392 get default formatting in the absence of a pivot table).
2394 The "body" omits subscripts and superscripts and footnotes.
2396 Returns true if OUT is a number (or a number plus a value label), false
2399 pivot_value_format_body (const struct pivot_value *value,
2400 const struct pivot_table *pt_,
2403 const struct pivot_table *pt = pt_ ? pt_ : &pivot_value_format_defaults;
2404 enum settings_value_show show;
2405 bool numeric = false;
2407 switch (value->type)
2409 case PIVOT_VALUE_NUMERIC:
2410 show = interpret_show (settings_get_show_values (),
2412 value->numeric.show,
2413 value->numeric.value_label != NULL);
2414 if (show & SETTINGS_VALUE_SHOW_VALUE)
2416 const struct fmt_spec *f = &value->numeric.format;
2417 const struct fmt_spec *format
2419 && value->numeric.honor_small
2420 && value->numeric.x != 0
2421 && fabs (value->numeric.x) < pt->small
2422 ? &(struct fmt_spec) { .type = FMT_E, .w = 40, .d = f->d }
2425 char *s = data_out (&(union value) { .f = value->numeric.x },
2426 "UTF-8", format, &pt->settings);
2427 ds_put_cstr (out, s + strspn (s, " "));
2430 if (show & SETTINGS_VALUE_SHOW_LABEL)
2432 if (show & SETTINGS_VALUE_SHOW_VALUE)
2433 ds_put_byte (out, ' ');
2434 ds_put_cstr (out, value->numeric.value_label);
2436 numeric = !(show & SETTINGS_VALUE_SHOW_LABEL);
2439 case PIVOT_VALUE_STRING:
2440 show = interpret_show (settings_get_show_values (),
2443 value->string.value_label != NULL);
2444 if (show & SETTINGS_VALUE_SHOW_VALUE)
2446 if (value->string.hex)
2448 for (const uint8_t *p = CHAR_CAST (uint8_t *, value->string.s);
2450 ds_put_format (out, "%02X", *p);
2453 ds_put_cstr (out, value->string.s);
2455 if (show & SETTINGS_VALUE_SHOW_LABEL)
2457 if (show & SETTINGS_VALUE_SHOW_VALUE)
2458 ds_put_byte (out, ' ');
2459 ds_put_cstr (out, value->string.value_label);
2463 case PIVOT_VALUE_VARIABLE:
2464 show = interpret_show (settings_get_show_variables (),
2466 value->variable.show,
2467 value->variable.var_label != NULL);
2468 if (show & SETTINGS_VALUE_SHOW_VALUE)
2469 ds_put_cstr (out, value->variable.var_name);
2470 if (show & SETTINGS_VALUE_SHOW_LABEL)
2472 if (show & SETTINGS_VALUE_SHOW_VALUE)
2473 ds_put_byte (out, ' ');
2474 ds_put_cstr (out, value->variable.var_label);
2478 case PIVOT_VALUE_TEXT:
2479 if (value->ex && value->ex->font_style && value->ex->font_style->markup)
2480 get_text_from_markup (value->text.local, out);
2482 ds_put_cstr (out, value->text.local);
2485 case PIVOT_VALUE_TEMPLATE:
2486 pivot_format_template (out, value->template.local, value->template.args,
2487 value->template.n_args, pt);
2494 /* Appends a text representation of VALUE to OUT. Settings on PT control
2495 whether variable and value labels are included (pass NULL for PT to get
2496 default formatting in the absence of a pivot table).
2498 Subscripts and footnotes are included.
2500 Returns true if OUT is a number (or a number plus a value label), false
2503 pivot_value_format (const struct pivot_value *value,
2504 const struct pivot_table *pt_,
2507 const struct pivot_table *pt = pt_ ? pt_ : &pivot_value_format_defaults;
2508 bool numeric = pivot_value_format_body (value, pt, out);
2510 const struct pivot_value_ex *ex = value->ex;
2513 if (ex->n_subscripts)
2515 for (size_t i = 0; i < ex->n_subscripts; i++)
2516 ds_put_format (out, "%c%s", i ? ',' : '_', ex->subscripts[i]);
2519 for (size_t i = 0; i < ex->n_footnotes; i++)
2521 ds_put_byte (out, '[');
2523 size_t idx = ex->footnote_indexes[i];
2524 const struct pivot_footnote *f = pt->footnotes[idx];
2525 pivot_footnote_format_marker (f, pt, out);
2527 ds_put_byte (out, ']');
2534 /* Returns a text representation of VALUE. The caller must free the string,
2535 with free(). Settings on PT control whether variable and value labels are
2536 included (pass NULL for PT to get default formatting in the absence of a
2539 pivot_value_to_string (const struct pivot_value *value,
2540 const struct pivot_table *pt)
2542 struct string s = DS_EMPTY_INITIALIZER;
2543 pivot_value_format (value, pt, &s);
2544 return ds_steal_cstr (&s);
2547 struct pivot_value *
2548 pivot_value_clone (const struct pivot_value *old)
2553 struct pivot_value *new = xmemdup (old, sizeof *new);
2555 new->ex = pivot_value_ex_clone (old->ex);
2559 case PIVOT_VALUE_NUMERIC:
2560 new->numeric.var_name = xstrdup_if_nonnull (new->numeric.var_name);
2561 new->numeric.value_label = xstrdup_if_nonnull (new->numeric.value_label);
2564 case PIVOT_VALUE_STRING:
2565 new->string.s = xstrdup (new->string.s);
2566 new->string.var_name = xstrdup_if_nonnull (new->string.var_name);
2567 new->string.value_label = xstrdup_if_nonnull (new->string.value_label);
2570 case PIVOT_VALUE_VARIABLE:
2571 new->variable.var_name = xstrdup_if_nonnull (new->variable.var_name);
2572 new->variable.var_label = xstrdup_if_nonnull (new->variable.var_label);
2575 case PIVOT_VALUE_TEXT:
2576 new->text.local = xstrdup (old->text.local);
2577 new->text.c = (old->text.c == old->text.local ? new->text.local
2578 : xstrdup_if_nonnull (old->text.c));
2579 new->text.id = (old->text.id == old->text.local ? new->text.local
2580 : old->text.id == old->text.c ? new->text.c
2581 : xstrdup_if_nonnull (old->text.id));
2584 case PIVOT_VALUE_TEMPLATE:
2585 new->template.local = xstrdup (old->template.local);
2586 new->template.id = (old->template.id == old->template.local
2587 ? new->template.local
2588 : xstrdup (old->template.id));
2589 new->template.args = xmalloc (new->template.n_args
2590 * sizeof *new->template.args);
2591 for (size_t i = 0; i < old->template.n_args; i++)
2592 pivot_argument_copy (&new->template.args[i],
2593 &old->template.args[i]);
2602 /* Frees the data owned by V. */
2604 pivot_value_destroy (struct pivot_value *value)
2608 pivot_value_ex_destroy (value->ex);
2609 switch (value->type)
2611 case PIVOT_VALUE_NUMERIC:
2612 free (value->numeric.var_name);
2613 free (value->numeric.value_label);
2616 case PIVOT_VALUE_STRING:
2617 free (value->string.s);
2618 free (value->string.var_name);
2619 free (value->string.value_label);
2622 case PIVOT_VALUE_VARIABLE:
2623 free (value->variable.var_name);
2624 free (value->variable.var_label);
2627 case PIVOT_VALUE_TEXT:
2628 free (value->text.local);
2629 if (value->text.c != value->text.local)
2630 free (value->text.c);
2631 if (value->text.id != value->text.local
2632 && value->text.id != value->text.c)
2633 free (value->text.id);
2636 case PIVOT_VALUE_TEMPLATE:
2637 free (value->template.local);
2638 if (value->template.id != value->template.local)
2639 free (value->template.id);
2640 for (size_t i = 0; i < value->template.n_args; i++)
2641 pivot_argument_uninit (&value->template.args[i]);
2642 free (value->template.args);
2652 /* Sets AREA to the style to use for VALUE, with defaults coming from
2653 DEFAULT_STYLE for the parts of the style that VALUE doesn't override. */
2655 pivot_value_get_style (struct pivot_value *value,
2656 const struct font_style *base_font_style,
2657 const struct cell_style *base_cell_style,
2658 struct table_area_style *area)
2660 const struct pivot_value_ex *ex = pivot_value_ex (value);
2661 font_style_copy (NULL, &area->font_style,
2662 ex->font_style ? ex->font_style : base_font_style);
2663 area->cell_style = *(ex->cell_style ? ex->cell_style : base_cell_style);
2666 /* Copies AREA into VALUE's style. */
2668 pivot_value_set_style (struct pivot_value *value,
2669 const struct table_area_style *area)
2671 pivot_value_set_font_style (value, &area->font_style);
2672 pivot_value_set_cell_style (value, &area->cell_style);
2676 pivot_value_set_font_style (struct pivot_value *value,
2677 const struct font_style *font_style)
2679 struct pivot_value_ex *ex = pivot_value_ex_rw (value);
2681 font_style_uninit (ex->font_style);
2683 ex->font_style = xmalloc (sizeof *ex->font_style);
2684 font_style_copy (NULL, ex->font_style, font_style);
2688 pivot_value_set_cell_style (struct pivot_value *value,
2689 const struct cell_style *cell_style)
2691 struct pivot_value_ex *ex = pivot_value_ex_rw (value);
2692 if (!ex->cell_style)
2693 ex->cell_style = xmalloc (sizeof *ex->cell_style);
2694 *ex->cell_style = *cell_style;
2698 pivot_argument_copy (struct pivot_argument *dst,
2699 const struct pivot_argument *src)
2701 *dst = (struct pivot_argument) {
2703 .values = xmalloc (src->n * sizeof *dst->values),
2706 for (size_t i = 0; i < src->n; i++)
2707 dst->values[i] = pivot_value_clone (src->values[i]);
2710 /* Frees the data owned by ARG (but not ARG itself). */
2712 pivot_argument_uninit (struct pivot_argument *arg)
2716 for (size_t i = 0; i < arg->n; i++)
2717 pivot_value_destroy (arg->values[i]);
2722 /* Creates and returns a new pivot_value whose contents is the null-terminated
2723 string TEXT. Takes ownership of TEXT.
2725 This function is for text strings provided by the user (with the exception
2726 that pivot_value_new_variable() should be used for variable names). For
2727 strings that are part of the PSPP user interface, such as names of
2728 procedures, statistics, annotations, error messages, etc., use
2729 pivot_value_new_text(). */
2730 struct pivot_value *
2731 pivot_value_new_user_text_nocopy (char *text)
2733 struct pivot_value *value = xmalloc (sizeof *value);
2734 *value = (struct pivot_value) {
2736 .type = PIVOT_VALUE_TEXT,
2740 .user_provided = true,
2746 /* Creates and returns a new pivot_value whose contents is the LENGTH bytes of
2747 TEXT. Use SIZE_MAX if TEXT is null-teriminated and its length is not known
2750 This function is for text strings provided by the user (with the exception
2751 that pivot_value_new_variable() should be used for variable names). For
2752 strings that are part of the PSPP user interface, such as names of
2753 procedures, statistics, annotations, error messages, etc., use
2754 pivot_value_new_text().
2756 The caller retains ownership of TEXT. */
2757 struct pivot_value *
2758 pivot_value_new_user_text (const char *text, size_t length)
2760 return pivot_value_new_user_text_nocopy (
2761 xmemdup0 (text, length != SIZE_MAX ? length : strlen (text)));
2764 /* Creates and returns new pivot_value whose contents is TEXT, which should be
2765 a translatable string, but not actually translated yet, e.g. enclosed in
2766 N_(). This function is for text strings that are part of the PSPP user
2767 interface, such as names of procedures, statistics, annotations, error
2768 messages, etc. For strings that come from the user, use
2769 pivot_value_new_user_text(). */
2770 struct pivot_value *
2771 pivot_value_new_text (const char *text)
2773 char *c = xstrdup (text);
2774 char *local = xstrdup (gettext (c));
2776 struct pivot_value *value = xmalloc (sizeof *value);
2777 *value = (struct pivot_value) {
2779 .type = PIVOT_VALUE_TEXT,
2783 .user_provided = false,
2789 /* Same as pivot_value_new_text() but its argument is a printf()-like format
2790 string. The format string should generally be enclosed in N_(). */
2791 struct pivot_value * PRINTF_FORMAT (1, 2)
2792 pivot_value_new_text_format (const char *format, ...)
2795 va_start (args, format);
2796 char *c = xvasprintf (format, args);
2799 va_start (args, format);
2800 char *local = xvasprintf (gettext (format), args);
2803 struct pivot_value *value = xmalloc (sizeof *value);
2804 *value = (struct pivot_value) {
2806 .type = PIVOT_VALUE_TEXT,
2810 .user_provided = false,
2816 /* Returns a new pivot_value that represents X.
2818 The format to use for X is unspecified. Usually the easiest way to specify
2819 a format is through assigning a result class to one of the categories that
2820 the pivot_value will end up in. If that is not suitable, then the caller
2821 can use pivot_value_set_rc() or assign directly to value->numeric.format. */
2822 struct pivot_value *
2823 pivot_value_new_number (double x)
2825 struct pivot_value *value = xmalloc (sizeof *value);
2826 *value = (struct pivot_value) {
2828 .type = PIVOT_VALUE_NUMERIC,
2835 /* Returns a new pivot_value that represents X, formatted as an integer. */
2836 struct pivot_value *
2837 pivot_value_new_integer (double x)
2839 struct pivot_value *value = pivot_value_new_number (x);
2840 value->numeric.format = (struct fmt_spec) { .type = FMT_F, .w = 40 };
2844 /* Returns a new pivot_value that represents VALUE, formatted as for
2846 struct pivot_value *
2847 pivot_value_new_var_value (const struct variable *variable,
2848 const union value *value)
2850 struct pivot_value *pv = pivot_value_new_value (
2851 value, var_get_width (variable), var_get_print_format (variable),
2852 var_get_encoding (variable));
2854 char *var_name = xstrdup (var_get_name (variable));
2855 if (var_is_alpha (variable))
2856 pv->string.var_name = var_name;
2858 pv->numeric.var_name = var_name;
2860 const char *label = var_lookup_value_label (variable, value);
2863 if (var_is_alpha (variable))
2864 pv->string.value_label = xstrdup (label);
2866 pv->numeric.value_label = xstrdup (label);
2872 /* Returns a new pivot_value that represents VALUE, with the given WIDTH,
2873 formatted with FORMAT. For a string value, ENCODING must be its character
2875 struct pivot_value *
2876 pivot_value_new_value (const union value *value, int width,
2877 const struct fmt_spec *format, const char *encoding)
2879 struct pivot_value *pv = XZALLOC (struct pivot_value);
2882 char *s = recode_string (UTF8, encoding, CHAR_CAST (char *, value->s),
2884 size_t n = strlen (s);
2885 while (n > 0 && s[n - 1] == ' ')
2888 pv->type = PIVOT_VALUE_STRING;
2890 pv->string.hex = format->type == FMT_AHEX;
2894 pv->type = PIVOT_VALUE_NUMERIC;
2895 pv->numeric.x = value->f;
2896 pv->numeric.format = *format;
2902 /* Returns a new pivot_value for VARIABLE. */
2903 struct pivot_value *
2904 pivot_value_new_variable (const struct variable *variable)
2906 return pivot_value_new_variable__ (var_get_name (variable),
2907 var_get_label (variable));
2910 /* Returns a new pivot_value for a variable with the given NAME and optional
2912 struct pivot_value *
2913 pivot_value_new_variable__ (const char *name, const char *label)
2915 struct pivot_value *value = xmalloc (sizeof *value);
2916 *value = (struct pivot_value) {
2918 .type = PIVOT_VALUE_VARIABLE,
2919 .var_name = xstrdup (name),
2920 .var_label = xstrdup_if_nonempty (label),
2926 /* Attaches a reference to FOOTNOTE to V. */
2928 pivot_value_add_footnote (struct pivot_value *v,
2929 const struct pivot_footnote *footnote)
2931 struct pivot_value_ex *ex = pivot_value_ex_rw (v);
2933 /* Some legacy tables include numerous duplicate footnotes. Suppress
2935 for (size_t i = 0; i < ex->n_footnotes; i++)
2936 if (ex->footnote_indexes[i] == footnote->idx)
2939 ex->footnote_indexes = xrealloc (
2940 ex->footnote_indexes,
2941 (ex->n_footnotes + 1) * sizeof *ex->footnote_indexes);
2942 ex->footnote_indexes[ex->n_footnotes++] = footnote->idx;
2943 pivot_value_sort_footnotes (v);
2947 compare_footnote_indexes (const void *a_, const void *b_)
2949 const size_t *ap = a_;
2950 const size_t *bp = b_;
2953 return a < b ? -1 : a > b;
2956 /* Sorts the footnote references in V in the standard ascending order.
2958 This is only necessary if code adds (plural) footnotes to a pivot_value by
2959 itself, because pivot_value_add_footnote() does it automatically. */
2961 pivot_value_sort_footnotes (struct pivot_value *v)
2963 if (v->ex && v->ex->n_footnotes > 1)
2964 qsort (v->ex->footnote_indexes, v->ex->n_footnotes,
2965 sizeof *v->ex->footnote_indexes, compare_footnote_indexes);
2968 /* If VALUE is a numeric value, and RC is a result class such as
2969 PIVOT_RC_COUNT, changes VALUE's format to the result class's. */
2971 pivot_value_set_rc (const struct pivot_table *table, struct pivot_value *value,
2974 if (value->type == PIVOT_VALUE_NUMERIC)
2975 pivot_table_use_rc (table, rc,
2976 &value->numeric.format, &value->numeric.honor_small);
2979 /* pivot_value_ex. */
2981 struct pivot_value_ex *
2982 pivot_value_ex_rw (struct pivot_value *value)
2985 value->ex = xzalloc (sizeof *value->ex);
2989 struct pivot_value_ex *
2990 pivot_value_ex_clone (const struct pivot_value_ex *old)
2992 struct font_style *font_style = NULL;
2993 if (old->font_style)
2995 font_style = xmalloc (sizeof *font_style);
2996 font_style_copy (NULL, font_style, old->font_style);
2999 char **subscripts = NULL;
3000 if (old->n_subscripts)
3002 subscripts = xnmalloc (old->n_subscripts, sizeof *subscripts);
3003 for (size_t i = 0; i < old->n_subscripts; i++)
3004 subscripts[i] = xstrdup (old->subscripts[i]);
3007 struct pivot_value_ex *new = xmalloc (sizeof *new);
3008 *new = (struct pivot_value_ex) {
3009 .font_style = font_style,
3010 .cell_style = (old->cell_style
3011 ? xmemdup (old->cell_style, sizeof *new->cell_style)
3013 .subscripts = subscripts,
3014 .n_subscripts = old->n_subscripts,
3015 .footnote_indexes = (
3017 ? xmemdup (old->footnote_indexes,
3018 old->n_footnotes * sizeof *new->footnote_indexes)
3020 .n_footnotes = old->n_footnotes
3026 pivot_value_ex_destroy (struct pivot_value_ex *ex)
3030 font_style_uninit (ex->font_style);
3031 free (ex->font_style);
3032 free (ex->cell_style);
3033 free (ex->footnote_indexes);
3035 for (size_t i = 0; i < ex->n_subscripts; i++)
3036 free (ex->subscripts[i]);
3037 free (ex->subscripts);
3044 struct pivot_splits_value
3046 struct hmap_node hmap_node;
3051 struct pivot_splits_var
3053 struct pivot_dimension *dimension;
3054 const struct variable *var;
3059 struct pivot_splits_dup
3061 struct hmap_node hmap_node;
3062 union value *values;
3067 struct pivot_splits_var *vars;
3073 size_t dindexes[MAX_SPLITS];
3078 /* Adds a dimension for each layered split file variable in DICT to PT on AXIS.
3079 These dimensions should be the last dimensions added to PT (the
3080 pivot_splits_put*() functions rely on this). Returns a new pivot_splits
3081 structure if any dimensions were added, otherwise a null pointer.
3083 See the large comment on split file handling in pivot-table.h for more
3085 struct pivot_splits *
3086 pivot_splits_create (struct pivot_table *pt,
3087 enum pivot_axis_type axis,
3088 const struct dictionary *dict)
3090 if (dict_get_split_type (dict) != SPLIT_LAYERED)
3093 size_t n = dict_get_n_splits (dict);
3094 assert (n <= MAX_SPLITS);
3096 const struct variable *const *vars = dict_get_split_vars (dict);
3097 struct pivot_splits_var *psvars = xnmalloc (n, sizeof *psvars);
3098 for (size_t i = n - 1; i < n; i--)
3100 const struct variable *var = vars[i];
3101 struct pivot_splits_var *psvar = &psvars[i];
3103 struct pivot_dimension *d = pivot_dimension_create__ (
3104 pt, axis, pivot_value_new_variable (var));
3105 d->root->show_label = true;
3107 *psvar = (struct pivot_splits_var) {
3108 .width = var_get_width (var),
3109 .values = HMAP_INITIALIZER (psvar->values),
3115 struct pivot_splits *ps = xmalloc (sizeof *ps);
3116 *ps = (struct pivot_splits) {
3119 .encoding = xstrdup (dict_get_encoding (dict)),
3120 .dups = HMAP_INITIALIZER (ps->dups),
3121 .dindexes = { [0] = SIZE_MAX },
3129 pivot_splits_destroy (struct pivot_splits *ps)
3134 if (ps->warnings_left < 0)
3135 msg (SW, ngettext ("Suppressed %d additional warning about duplicate "
3137 "Suppressed %d additional warnings about duplicate "
3138 "split values.", -ps->warnings_left),
3139 -ps->warnings_left);
3141 struct pivot_splits_dup *dup, *next_dup;
3142 HMAP_FOR_EACH_SAFE (dup, next_dup, struct pivot_splits_dup, hmap_node,
3145 for (size_t i = 0; i < ps->n; i++)
3146 value_destroy (&dup->values[i], ps->vars[i].width);
3150 hmap_destroy (&ps->dups);
3152 for (size_t i = 0; i < ps->n; i++)
3154 struct pivot_splits_var *psvar = &ps->vars[i];
3155 struct pivot_splits_value *psval, *next;
3156 HMAP_FOR_EACH_SAFE (psval, next, struct pivot_splits_value, hmap_node,
3159 value_destroy (&psval->value, psvar->width);
3160 hmap_delete (&psvar->values, &psval->hmap_node);
3163 hmap_destroy (&psvar->values);
3167 free (ps->encoding);
3171 static struct pivot_splits_value *
3172 pivot_splits_value_find (struct pivot_splits_var *psvar,
3173 const union value *value)
3175 struct pivot_splits_value *psval;
3176 HMAP_FOR_EACH_WITH_HASH (psval, struct pivot_splits_value, hmap_node,
3177 value_hash (value, psvar->width, 0), &psvar->values)
3178 if (value_equal (&psval->value, value, psvar->width))
3184 pivot_splits_find_dup (struct pivot_splits *ps, const struct ccase *example)
3186 unsigned int hash = 0;
3187 for (size_t i = 0; i < ps->n; i++)
3189 struct pivot_splits_var *psvar = &ps->vars[i];
3190 const union value *value = case_data (example, psvar->var);
3191 hash = value_hash (value, psvar->width, hash);
3193 struct pivot_splits_dup *dup;
3194 HMAP_FOR_EACH_WITH_HASH (dup, struct pivot_splits_dup, hmap_node, hash,
3198 for (size_t i = 0; i < ps->n && equal; i++)
3200 struct pivot_splits_var *psvar = &ps->vars[i];
3201 const union value *value = case_data (example, psvar->var);
3202 equal = value_equal (value, &dup->values[i], psvar->width);
3208 union value *values = xmalloc (ps->n * sizeof *values);
3209 for (size_t i = 0; i < ps->n; i++)
3211 struct pivot_splits_var *psvar = &ps->vars[i];
3212 const union value *value = case_data (example, psvar->var);
3213 value_clone (&values[i], value, psvar->width);
3216 dup = xmalloc (sizeof *dup);
3217 dup->values = values;
3218 hmap_insert (&ps->dups, &dup->hmap_node, hash);
3222 /* Begins adding data for a new split file group to the pivot table associated
3223 with PS. EXAMPLE should be a case from the new split file group.
3225 This is a no-op if PS is NULL.
3227 See the large comment on split file handling in pivot-table.h for more
3230 pivot_splits_new_split (struct pivot_splits *ps, const struct ccase *example)
3235 for (size_t i = 0; i < ps->n; i++)
3237 struct pivot_splits_var *psvar = &ps->vars[i];
3238 const union value *value = case_data (example, psvar->var);
3239 struct pivot_splits_value *psval = pivot_splits_value_find (psvar, value);
3242 psval = xmalloc (sizeof *psval);
3243 hmap_insert (&psvar->values, &psval->hmap_node,
3244 value_hash (value, psvar->width, 0));
3245 value_clone (&psval->value, value, psvar->width);
3246 psval->leaf = pivot_category_create_leaf (
3247 psvar->dimension->root,
3248 pivot_value_new_var_value (psvar->var, value));
3251 ps->dindexes[i] = psval->leaf;
3254 if (pivot_splits_find_dup (ps, example))
3256 if (ps->warnings_left-- > 0)
3258 struct string s = DS_EMPTY_INITIALIZER;
3259 for (size_t i = 0; i < ps->n; i++)
3262 ds_put_cstr (&s, ", ");
3264 struct pivot_splits_var *psvar = &ps->vars[i];
3265 const union value *value = case_data (example, psvar->var);
3266 ds_put_format (&s, "%s = ", var_get_name (psvar->var));
3268 char *s2 = data_out (value, ps->encoding,
3269 var_get_print_format (psvar->var),
3270 settings_get_fmt_settings ());
3271 ds_put_cstr (&s, s2 + strspn (s2, " "));
3274 msg (SW, _("When SPLIT FILE is in effect, the input data must be "
3275 "sorted by the split variables (for example, using SORT "
3276 "CASES), but multiple runs of cases with the same split "
3277 "values were found separated by cases with different "
3278 "values. Each run will be analyzed separately. The "
3279 "duplicate split values are: %s"), ds_cstr (&s));
3283 struct pivot_splits_var *psvar = &ps->vars[0];
3284 const union value *value = case_data (example, psvar->var);
3285 ps->dindexes[0] = pivot_category_create_leaf (
3286 psvar->dimension->root,
3287 pivot_value_new_var_value (psvar->var, value));
3292 pivot_splits_get_dindexes (const struct pivot_splits *ps, size_t *dindexes)
3297 assert (ps->dindexes[0] != SIZE_MAX);
3298 for (size_t i = 0; i < ps->n; i++)
3299 dindexes[ps->n - i - 1] = ps->dindexes[i];
3303 /* Puts VALUE in the cell in TABLE with index IDX1. TABLE must have 1
3304 dimension plus the split file dimensions from PS (if nonnull). Takes
3307 See the large comment on split file handling in pivot-table.h for more
3310 pivot_splits_put1 (struct pivot_splits *ps, struct pivot_table *table,
3311 size_t idx1, struct pivot_value *value)
3313 size_t dindexes[1 + MAX_SPLITS];
3314 size_t *p = dindexes;
3316 p += pivot_splits_get_dindexes (ps, p);
3317 pivot_table_put (table, dindexes, p - dindexes, value);
3320 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2). TABLE must have 2
3321 dimensions plus the split file dimensions from PS (if nonnull). Takes
3324 See the large comment on split file handling in pivot-table.h for more
3327 pivot_splits_put2 (struct pivot_splits *ps, struct pivot_table *table,
3328 size_t idx1, size_t idx2, struct pivot_value *value)
3330 size_t dindexes[2 + MAX_SPLITS];
3331 size_t *p = dindexes;
3334 p += pivot_splits_get_dindexes (ps, p);
3335 pivot_table_put (table, dindexes, p - dindexes, value);
3338 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3). TABLE must
3339 have 3 dimensions plus the split file dimensions from PS (if nonnull).
3340 Takes ownership of VALUE.
3342 See the large comment on split file handling in pivot-table.h for more
3345 pivot_splits_put3 (struct pivot_splits *ps, struct pivot_table *table,
3346 size_t idx1, size_t idx2, size_t idx3,
3347 struct pivot_value *value)
3349 size_t dindexes[3 + MAX_SPLITS];
3350 size_t *p = dindexes;
3354 p += pivot_splits_get_dindexes (ps, p);
3355 pivot_table_put (table, dindexes, p - dindexes, value);
3358 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3, IDX4). TABLE
3359 must have 4 dimensions plus the split file dimensions from PS (if nonnull).
3360 Takes ownership of VALUE.
3362 See the large comment on split file handling in pivot-table.h for more
3365 pivot_splits_put4 (struct pivot_splits *ps, struct pivot_table *table,
3366 size_t idx1, size_t idx2, size_t idx3, size_t idx4,
3367 struct pivot_value *value)
3369 size_t dindexes[4 + MAX_SPLITS];
3370 size_t *p = dindexes;
3375 p += pivot_splits_get_dindexes (ps, p);
3376 pivot_table_put (table, dindexes, p - dindexes, value);