#include <stdlib.h>
#include "data/data-out.h"
+#include "data/dictionary.h"
#include "data/settings.h"
#include "data/value.h"
#include "data/variable.h"
.omit_empty = true,
.row_labels_in_corner = true,
- .width_ranges = {
- [TABLE_HORZ] = { 36, 72 },
- [TABLE_VERT] = { 36, 120 },
- },
+ .col_heading_width_range = { 36, 72 },
+ .row_heading_width_range = { 36, 120 },
.areas = {
#define AREA(BOLD, H, V, L, R, T, B) { \
{
if (!strcmp (s, PIVOT_RC_OTHER))
{
- *format = *settings_get_format ();
+ *format = settings_get_format ();
*honor_small = true;
}
else if (!strcmp (s, PIVOT_RC_COUNT) && !overridden_count_format)
include the RC_ prefix) to *FORMAT. Returns true if successful, false if S
does not name a known result class. */
bool
-pivot_result_class_change (const char *s_, const struct fmt_spec *format)
+pivot_result_class_change (const char *s_, struct fmt_spec format)
{
char *s = xasprintf ("RC_%s", s_);
struct result_class *rc = pivot_result_class_find (s);
if (rc)
{
- rc->format = *format;
+ rc->format = format;
if (!strcmp (s, PIVOT_RC_COUNT))
overridden_count_format = true;
}
}
/* Sets the format used for PIVOT_RC_COUNT cells to WFMT, which should be the
- format for the dictionary whose data or statistics are being put into TABLE.
-
- This has no effect if WFMT is NULL. */
+ format for the dictionary whose data or statistics are being put into
+ TABLE. */
void
-pivot_table_set_weight_format (struct pivot_table *table,
- const struct fmt_spec *wfmt)
+pivot_table_set_weight_format (struct pivot_table *table, struct fmt_spec wfmt)
{
- if (wfmt)
- table->weight_format = *wfmt;
+ wfmt.w = 40;
+ table->weight_format = wfmt;
}
/* Returns true if TABLE has no cells, false otherwise. */
}
/* 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
}
}
}
- value->numeric.format = *settings_get_format ();
+ value->numeric.format = settings_get_format ();
value->numeric.honor_small = true;
done:;
{
headings[row][column] = pivot_value_to_string (c->name, pt);
if (!*headings[row][column])
- headings[row][column] = xstrdup ("<blank>");
+ {
+ free (headings[row][column]);
+ headings[row][column] = xstrdup ("<blank>");
+ }
row--;
}
}
}
}
+static void
+dump_leaf (const struct pivot_table *table, const struct pivot_category *c)
+{
+ if (c)
+ {
+ dump_leaf (table, c->parent);
+ if (pivot_category_is_leaf (c) || c->show_label)
+ {
+ putchar (' ');
+ pivot_value_dump (c->name, table);
+ }
+ }
+}
+
void
pivot_table_dump (const struct pivot_table *table, int indentation)
{
indent (indentation);
printf ("sizing:\n");
- pivot_table_sizing_dump ("column", table->look->width_ranges[TABLE_HORZ],
+ pivot_table_sizing_dump ("column", table->look->col_heading_width_range,
&table->sizing[TABLE_HORZ], indentation + 1);
- pivot_table_sizing_dump ("row", table->look->width_ranges[TABLE_VERT],
+ pivot_table_sizing_dump ("row", table->look->row_heading_width_range,
&table->sizing[TABLE_VERT], indentation + 1);
indent (indentation);
{
const struct pivot_dimension *d = layer_axis->dimensions[i];
char *name = pivot_value_to_string (d->root->name, table);
- char *value = pivot_value_to_string (
- d->data_leaves[table->current_layer[i]]->name, table);
- printf (" %s=%s", name, value);
- free (value);
+ printf (" %s", name);
free (name);
+
+ size_t ofs = table->current_layer[i];
+ if (ofs < d->n_leaves)
+ {
+ char *value = pivot_value_to_string (d->data_leaves[ofs]->name,
+ table);
+ printf ("=%s", value);
+ free (value);
+ }
}
putchar ('\n');
pivot_value_dump (d->root->name, table);
fputs (" =", stdout);
- struct pivot_value **names = xnmalloc (d->n_leaves, sizeof *names);
- size_t n_names = 0;
- for (const struct pivot_category *c
- = d->presentation_leaves[layer_indexes[i]];
- c;
- c = c->parent)
- {
- if (pivot_category_is_leaf (c) || c->show_label)
- names[n_names++] = c->name;
- }
-
- for (size_t i = n_names; i-- > 0;)
- {
- putchar (' ');
- pivot_value_dump (names[i], table);
- }
- free (names);
+ dump_leaf (table, d->presentation_leaves[layer_indexes[i]]);
}
putchar ('\n');
value->numeric.value_label != NULL);
if (show & SETTINGS_VALUE_SHOW_VALUE)
{
- const struct fmt_spec *f = &value->numeric.format;
- const struct fmt_spec *format
- = (f->type == FMT_F
+ struct fmt_spec f = value->numeric.format;
+ const struct fmt_spec format
+ = (f.type == FMT_F
&& value->numeric.honor_small
&& value->numeric.x != 0
&& fabs (value->numeric.x) < pt->small
- ? &(struct fmt_spec) { .type = FMT_E, .w = 40, .d = f->d }
+ ? (struct fmt_spec) { .type = FMT_E, .w = 40, .d = f.d }
: f);
char *s = data_out (&(union value) { .f = value->numeric.x },
encoding. */
struct pivot_value *
pivot_value_new_value (const union value *value, int width,
- const struct fmt_spec *format, const char *encoding)
+ struct fmt_spec format, const char *encoding)
{
struct pivot_value *pv = XZALLOC (struct pivot_value);
if (width > 0)
pv->type = PIVOT_VALUE_STRING;
pv->string.s = s;
- pv->string.hex = format->type == FMT_AHEX;
+ pv->string.hex = format.type == FMT_AHEX;
}
else
{
pv->type = PIVOT_VALUE_NUMERIC;
pv->numeric.x = value->f;
- pv->numeric.format = *format;
+ pv->numeric.format = format;
}
return pv;
.variable = {
.type = PIVOT_VALUE_VARIABLE,
.var_name = xstrdup (name),
- .var_label = xstrdup_if_nonempty (label),
+ .var_label = xstrdup_if_nonnull (label),
},
};
return value;
free (ex);
}
}
+\f
+/* 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);
+}