X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fpivot-table.c;h=93dc74c2d86e8299cbbe3e03e2d6de8a790ef799;hb=9449e8429a4bc1368c322457384d69e22f8d0bb2;hp=6ba4c16f2e0811545b4b5673678900f5b37a1cf3;hpb=861528a56e8fcb470285a2ee0bb87d716f652174;p=pspp diff --git a/src/output/pivot-table.c b/src/output/pivot-table.c index 6ba4c16f2e..93dc74c2d8 100644 --- a/src/output/pivot-table.c +++ b/src/output/pivot-table.c @@ -24,6 +24,7 @@ #include #include "data/data-out.h" +#include "data/dictionary.h" #include "data/settings.h" #include "data/value.h" #include "data/variable.h" @@ -865,20 +866,20 @@ pivot_table_create (const char *title) struct pivot_table * pivot_table_create__ (struct pivot_value *title, const char *subtype) { - struct pivot_table *table = XZALLOC (struct pivot_table); - table->ref_cnt = 1; - table->show_title = true; - table->show_caption = true; - table->weight_format = (struct fmt_spec) { .type = FMT_F, .w = 40 }; - table->title = title; - table->subtype = subtype ? pivot_value_new_text (subtype) : NULL; - table->command_c = xstrdup_if_nonempty (output_get_command_name ()); - table->look = pivot_table_look_ref (pivot_table_look_get_default ()); - table->settings = fmt_settings_copy (settings_get_fmt_settings ()); - table->small = settings_get_small (); - - hmap_init (&table->cells); - + struct pivot_table *table = xmalloc (sizeof *table); + *table = (struct pivot_table) { + .ref_cnt = 1, + .show_title = true, + .show_caption = true, + .weight_format = (struct fmt_spec) { .type = FMT_F, .w = 40 }, + .title = title, + .subtype = subtype ? pivot_value_new_text (subtype) : NULL, + .command_c = xstrdup_if_nonempty (output_get_command_name ()), + .look = pivot_table_look_ref (pivot_table_look_get_default ()), + .settings = fmt_settings_copy (settings_get_fmt_settings ()), + .small = settings_get_small (), + .cells = HMAP_INITIALIZER (table->cells), + }; return table; } @@ -1191,6 +1192,43 @@ pivot_table_is_shared (const struct pivot_table *table) return table->ref_cnt > 1; } +static void +pivot_table_set_value__ (struct pivot_value **dstp, struct pivot_value *src) +{ + pivot_value_destroy (*dstp); + *dstp = src; +} + +/* Changes the title of TABLE to TITLE. Takes ownership of TITLE. */ +void +pivot_table_set_title (struct pivot_table *table, struct pivot_value *title) +{ + pivot_table_set_value__ (&table->title, title); +} + +/* Changes the subtype of TABLE to SUBTYPE. Takes ownership of SUBTYPE. */ +void +pivot_table_set_subtype (struct pivot_table *table, struct pivot_value *subtype) +{ + pivot_table_set_value__ (&table->subtype, subtype); +} + +/* Changes the corner text of TABLE to CORNER_TEXT. Takes ownership of + CORNER_TEXT. */ +void +pivot_table_set_corner_text (struct pivot_table *table, + struct pivot_value *corner_text) +{ + pivot_table_set_value__ (&table->corner_text, corner_text); +} + +/* Changes the caption of TABLE to CAPTION. Takes ownership of CAPTION. */ +void +pivot_table_set_caption (struct pivot_table *table, struct pivot_value *caption) +{ + pivot_table_set_value__ (&table->caption, caption); +} + /* Swaps axes A and B in TABLE. */ void pivot_table_swap_axes (struct pivot_table *table, @@ -1406,8 +1444,9 @@ pivot_table_insert_cell (struct pivot_table *table, const size_t *dindexes) } /* Puts VALUE in the cell in TABLE whose indexes are given by the N indexes in - DINDEXES. N must be the number of dimensions in TABLE. Takes ownership of - VALUE. + DINDEXES. The order of the indexes is the same as the order in which the + dimensions were created. N must be the number of dimensions in TABLE. + Takes ownership of VALUE. If VALUE is a numeric value without a specified format, this function checks each of the categories designated by DINDEXES[] and takes the format from @@ -1939,6 +1978,8 @@ compose_headings (const struct pivot_table *pt, static void free_headings (const struct pivot_axis *axis, char ***headings) { + if (!headings) + return; for (size_t i = 0; i < axis->label_depth; i++) { for (size_t j = 0; j < axis->extent; j++) @@ -2094,21 +2135,30 @@ pivot_table_dump (const struct pivot_table *table, int indentation) size_t *row_enumeration = pivot_table_enumerate_axis ( table, PIVOT_AXIS_ROW, layer_indexes, table->look->omit_empty, NULL); + /* Print column headings. + + Ordinarily the test for nonnull 'column_headings' would be + unnecessary, because 'column_headings' is null only if the axis's + label_depth is 0, but there is a special case for the column axis only + in pivot_table_assign_label_depth(). */ char ***column_headings = compose_headings ( table, &table->axes[PIVOT_AXIS_COLUMN], column_enumeration); - for (size_t y = 0; y < table->axes[PIVOT_AXIS_COLUMN].label_depth; y++) + if (column_headings) { - indent (indentation + 1); - for (size_t x = 0; x < table->axes[PIVOT_AXIS_COLUMN].extent; x++) + for (size_t y = 0; y < table->axes[PIVOT_AXIS_COLUMN].label_depth; y++) { - if (x) - fputs ("; ", stdout); - if (column_headings[y][x]) - fputs (column_headings[y][x], stdout); + indent (indentation + 1); + for (size_t x = 0; x < table->axes[PIVOT_AXIS_COLUMN].extent; x++) + { + if (x) + fputs ("; ", stdout); + if (column_headings && column_headings[y] && column_headings[y][x]) + fputs (column_headings[y][x], stdout); + } + putchar ('\n'); } - putchar ('\n'); + free_headings (&table->axes[PIVOT_AXIS_COLUMN], column_headings); } - free_headings (&table->axes[PIVOT_AXIS_COLUMN], column_headings); indent (indentation + 1); printf ("-----------------------------------------------\n"); @@ -2324,8 +2374,15 @@ get_text_from_markup (const char *markup, struct string *out) xmlFreeParserCtxt (parser); } -/* Appends a text representation of the body of VALUE to OUT. Settings on - PT control whether variable and value labels are included. +static const struct pivot_table pivot_value_format_defaults = { + .show_values = SETTINGS_VALUE_SHOW_DEFAULT, + .show_variables = SETTINGS_VALUE_SHOW_DEFAULT, + .settings = FMT_SETTINGS_INIT, +}; + +/* Appends a text representation of the body of VALUE to OUT. Settings on PT + control whether variable and value labels are included (pass NULL for PT to + get default formatting in the absence of a pivot table). The "body" omits subscripts and superscripts and footnotes. @@ -2333,9 +2390,10 @@ get_text_from_markup (const char *markup, struct string *out) otherwise. */ bool pivot_value_format_body (const struct pivot_value *value, - const struct pivot_table *pt, + const struct pivot_table *pt_, struct string *out) { + const struct pivot_table *pt = pt_ ? pt_ : &pivot_value_format_defaults; enum settings_value_show show; bool numeric = false; @@ -2426,8 +2484,9 @@ pivot_value_format_body (const struct pivot_value *value, return numeric; } -/* Appends a text representation of VALUE to OUT. Settings on - PT control whether variable and value labels are included. +/* Appends a text representation of VALUE to OUT. Settings on PT control + whether variable and value labels are included (pass NULL for PT to get + default formatting in the absence of a pivot table). Subscripts and footnotes are included. @@ -2435,9 +2494,10 @@ pivot_value_format_body (const struct pivot_value *value, otherwise. */ bool pivot_value_format (const struct pivot_value *value, - const struct pivot_table *pt, + const struct pivot_table *pt_, struct string *out) { + const struct pivot_table *pt = pt_ ? pt_ : &pivot_value_format_defaults; bool numeric = pivot_value_format_body (value, pt, out); const struct pivot_value_ex *ex = value->ex; @@ -2465,7 +2525,9 @@ pivot_value_format (const struct pivot_value *value, } /* Returns a text representation of VALUE. The caller must free the string, - with free(). */ + with free(). Settings on PT control whether variable and value labels are + included (pass NULL for PT to get default formatting in the absence of a + pivot table). */ char * pivot_value_to_string (const struct pivot_value *value, const struct pivot_table *pt) @@ -2475,17 +2537,6 @@ pivot_value_to_string (const struct pivot_value *value, return ds_steal_cstr (&s); } -char * -pivot_value_to_string_defaults (const struct pivot_value *value) -{ - static const struct pivot_table pt = { - .show_values = SETTINGS_VALUE_SHOW_DEFAULT, - .show_variables = SETTINGS_VALUE_SHOW_DEFAULT, - .settings = FMT_SETTINGS_INIT, - }; - return pivot_value_to_string (value, &pt); -} - struct pivot_value * pivot_value_clone (const struct pivot_value *old) { @@ -2693,9 +2744,9 @@ pivot_value_new_user_text_nocopy (char *text) that pivot_value_new_variable() should be used for variable names). For strings that are part of the PSPP user interface, such as names of procedures, statistics, annotations, error messages, etc., use - pivot_value_new_text().j + pivot_value_new_text(). - The caller retains ownership of TEXT.*/ + The caller retains ownership of TEXT. */ struct pivot_value * pivot_value_new_user_text (const char *text, size_t length) { @@ -2729,7 +2780,7 @@ pivot_value_new_text (const char *text) } /* Same as pivot_value_new_text() but its argument is a printf()-like format - string. */ + string. The format string should generally be enclosed in N_(). */ struct pivot_value * PRINTF_FORMAT (1, 2) pivot_value_new_text_format (const char *format, ...) { @@ -2980,3 +3031,340 @@ pivot_value_ex_destroy (struct pivot_value_ex *ex) free (ex); } } + +/* pivot_splits */ + +struct pivot_splits_value + { + struct hmap_node hmap_node; + union value value; + int leaf; + }; + +struct pivot_splits_var + { + struct pivot_dimension *dimension; + const struct variable *var; + int width; + struct hmap values; + }; + +struct pivot_splits_dup + { + struct hmap_node hmap_node; + union value *values; + }; + +struct pivot_splits + { + struct pivot_splits_var *vars; + size_t n; + char *encoding; + + struct hmap dups; + + size_t dindexes[MAX_SPLITS]; + + int warnings_left; + }; + +/* Adds a dimension for each layered split file variable in DICT to PT on AXIS. + These dimensions should be the last dimensions added to PT (the + pivot_splits_put*() functions rely on this). Returns a new pivot_splits + structure if any dimensions were added, otherwise a null pointer. + + See the large comment on split file handling in pivot-table.h for more + information. */ +struct pivot_splits * +pivot_splits_create (struct pivot_table *pt, + enum pivot_axis_type axis, + const struct dictionary *dict) +{ + if (dict_get_split_type (dict) != SPLIT_LAYERED) + return NULL; + + size_t n = dict_get_n_splits (dict); + assert (n <= MAX_SPLITS); + + const struct variable *const *vars = dict_get_split_vars (dict); + struct pivot_splits_var *psvars = xnmalloc (n, sizeof *psvars); + for (size_t i = n - 1; i < n; i--) + { + const struct variable *var = vars[i]; + struct pivot_splits_var *psvar = &psvars[i]; + + struct pivot_dimension *d = pivot_dimension_create__ ( + pt, axis, pivot_value_new_variable (var)); + d->root->show_label = true; + + *psvar = (struct pivot_splits_var) { + .width = var_get_width (var), + .values = HMAP_INITIALIZER (psvar->values), + .dimension = d, + .var = var, + }; + } + + struct pivot_splits *ps = xmalloc (sizeof *ps); + *ps = (struct pivot_splits) { + .vars = psvars, + .n = n, + .encoding = xstrdup (dict_get_encoding (dict)), + .dups = HMAP_INITIALIZER (ps->dups), + .dindexes = { [0] = SIZE_MAX }, + .warnings_left = 5, + }; + return ps; +} + +/* Destroys PS. */ +void +pivot_splits_destroy (struct pivot_splits *ps) +{ + if (!ps) + return; + + if (ps->warnings_left < 0) + msg (SW, ngettext ("Suppressed %d additional warning about duplicate " + "split values.", + "Suppressed %d additional warnings about duplicate " + "split values.", -ps->warnings_left), + -ps->warnings_left); + + struct pivot_splits_dup *dup, *next_dup; + HMAP_FOR_EACH_SAFE (dup, next_dup, struct pivot_splits_dup, hmap_node, + &ps->dups) + { + for (size_t i = 0; i < ps->n; i++) + value_destroy (&dup->values[i], ps->vars[i].width); + free (dup->values); + free (dup); + } + hmap_destroy (&ps->dups); + + for (size_t i = 0; i < ps->n; i++) + { + struct pivot_splits_var *psvar = &ps->vars[i]; + struct pivot_splits_value *psval, *next; + HMAP_FOR_EACH_SAFE (psval, next, struct pivot_splits_value, hmap_node, + &psvar->values) + { + value_destroy (&psval->value, psvar->width); + hmap_delete (&psvar->values, &psval->hmap_node); + free (psval); + } + hmap_destroy (&psvar->values); + } + free (ps->vars); + + free (ps->encoding); + free (ps); +} + +static struct pivot_splits_value * +pivot_splits_value_find (struct pivot_splits_var *psvar, + const union value *value) +{ + struct pivot_splits_value *psval; + HMAP_FOR_EACH_WITH_HASH (psval, struct pivot_splits_value, hmap_node, + value_hash (value, psvar->width, 0), &psvar->values) + if (value_equal (&psval->value, value, psvar->width)) + return psval; + return NULL; +} + +static bool +pivot_splits_find_dup (struct pivot_splits *ps, const struct ccase *example) +{ + unsigned int hash = 0; + for (size_t i = 0; i < ps->n; i++) + { + struct pivot_splits_var *psvar = &ps->vars[i]; + const union value *value = case_data (example, psvar->var); + hash = value_hash (value, psvar->width, hash); + } + struct pivot_splits_dup *dup; + HMAP_FOR_EACH_WITH_HASH (dup, struct pivot_splits_dup, hmap_node, hash, + &ps->dups) + { + bool equal = true; + for (size_t i = 0; i < ps->n && equal; i++) + { + struct pivot_splits_var *psvar = &ps->vars[i]; + const union value *value = case_data (example, psvar->var); + equal = value_equal (value, &dup->values[i], psvar->width); + } + if (equal) + return true; + } + + union value *values = xmalloc (ps->n * sizeof *values); + for (size_t i = 0; i < ps->n; i++) + { + struct pivot_splits_var *psvar = &ps->vars[i]; + const union value *value = case_data (example, psvar->var); + value_clone (&values[i], value, psvar->width); + } + + dup = xmalloc (sizeof *dup); + dup->values = values; + hmap_insert (&ps->dups, &dup->hmap_node, hash); + return false; +} + +/* Begins adding data for a new split file group to the pivot table associated + with PS. EXAMPLE should be a case from the new split file group. + + This is a no-op if PS is NULL. + + See the large comment on split file handling in pivot-table.h for more + information. */ +void +pivot_splits_new_split (struct pivot_splits *ps, const struct ccase *example) +{ + if (!ps) + return; + + for (size_t i = 0; i < ps->n; i++) + { + struct pivot_splits_var *psvar = &ps->vars[i]; + const union value *value = case_data (example, psvar->var); + struct pivot_splits_value *psval = pivot_splits_value_find (psvar, value); + if (!psval) + { + psval = xmalloc (sizeof *psval); + hmap_insert (&psvar->values, &psval->hmap_node, + value_hash (value, psvar->width, 0)); + value_clone (&psval->value, value, psvar->width); + psval->leaf = pivot_category_create_leaf ( + psvar->dimension->root, + pivot_value_new_var_value (psvar->var, value)); + } + + ps->dindexes[i] = psval->leaf; + } + + if (pivot_splits_find_dup (ps, example)) + { + if (ps->warnings_left-- > 0) + { + struct string s = DS_EMPTY_INITIALIZER; + for (size_t i = 0; i < ps->n; i++) + { + if (i > 0) + ds_put_cstr (&s, ", "); + + struct pivot_splits_var *psvar = &ps->vars[i]; + const union value *value = case_data (example, psvar->var); + ds_put_format (&s, "%s = ", var_get_name (psvar->var)); + + char *s2 = data_out (value, ps->encoding, + var_get_print_format (psvar->var), + settings_get_fmt_settings ()); + ds_put_cstr (&s, s2 + strspn (s2, " ")); + free (s2); + } + msg (SW, _("When SPLIT FILE is in effect, the input data must be " + "sorted by the split variables (for example, using SORT " + "CASES), but multiple runs of cases with the same split " + "values were found separated by cases with different " + "values. Each run will be analyzed separately. The " + "duplicate split values are: %s"), ds_cstr (&s)); + ds_destroy (&s); + } + + struct pivot_splits_var *psvar = &ps->vars[0]; + const union value *value = case_data (example, psvar->var); + ps->dindexes[0] = pivot_category_create_leaf ( + psvar->dimension->root, + pivot_value_new_var_value (psvar->var, value)); + } +} + +static size_t +pivot_splits_get_dindexes (const struct pivot_splits *ps, size_t *dindexes) +{ + if (!ps) + return 0; + + assert (ps->dindexes[0] != SIZE_MAX); + for (size_t i = 0; i < ps->n; i++) + dindexes[ps->n - i - 1] = ps->dindexes[i]; + return ps->n; +} + +/* Puts VALUE in the cell in TABLE with index IDX1. TABLE must have 1 + dimension plus the split file dimensions from PS (if nonnull). Takes + ownership of VALUE. + + See the large comment on split file handling in pivot-table.h for more + information. */ +void +pivot_splits_put1 (struct pivot_splits *ps, struct pivot_table *table, + size_t idx1, struct pivot_value *value) +{ + size_t dindexes[1 + MAX_SPLITS]; + size_t *p = dindexes; + *p++ = idx1; + p += pivot_splits_get_dindexes (ps, p); + pivot_table_put (table, dindexes, p - dindexes, value); +} + +/* Puts VALUE in the cell in TABLE with index (IDX1, IDX2). TABLE must have 2 + dimensions plus the split file dimensions from PS (if nonnull). Takes + ownership of VALUE. + + See the large comment on split file handling in pivot-table.h for more + information. */ +void +pivot_splits_put2 (struct pivot_splits *ps, struct pivot_table *table, + size_t idx1, size_t idx2, struct pivot_value *value) +{ + size_t dindexes[2 + MAX_SPLITS]; + size_t *p = dindexes; + *p++ = idx1; + *p++ = idx2; + p += pivot_splits_get_dindexes (ps, p); + pivot_table_put (table, dindexes, p - dindexes, value); +} + +/* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3). TABLE must + have 3 dimensions plus the split file dimensions from PS (if nonnull). + Takes ownership of VALUE. + + See the large comment on split file handling in pivot-table.h for more + information. */ +void +pivot_splits_put3 (struct pivot_splits *ps, struct pivot_table *table, + size_t idx1, size_t idx2, size_t idx3, + struct pivot_value *value) +{ + size_t dindexes[3 + MAX_SPLITS]; + size_t *p = dindexes; + *p++ = idx1; + *p++ = idx2; + *p++ = idx3; + p += pivot_splits_get_dindexes (ps, p); + pivot_table_put (table, dindexes, p - dindexes, value); +} + +/* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3, IDX4). TABLE + must have 4 dimensions plus the split file dimensions from PS (if nonnull). + Takes ownership of VALUE. + + See the large comment on split file handling in pivot-table.h for more + information. */ +void +pivot_splits_put4 (struct pivot_splits *ps, struct pivot_table *table, + size_t idx1, size_t idx2, size_t idx3, size_t idx4, + struct pivot_value *value) +{ + size_t dindexes[4 + MAX_SPLITS]; + size_t *p = dindexes; + *p++ = idx1; + *p++ = idx2; + *p++ = idx3; + *p++ = idx4; + p += pivot_splits_get_dindexes (ps, p); + pivot_table_put (table, dindexes, p - dindexes, value); +}