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.
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;
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.
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;
}
/* 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)
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)
{
struct pivot_splits_var
{
struct pivot_dimension *dimension;
+ const struct variable *var;
int width;
- size_t idx;
- struct fmt_spec format;
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 variable *const *vars = dict_get_split_vars (dict);
struct pivot_splits_var *psvars = xnmalloc (n, sizeof *psvars);
- for (size_t i = 0; i < n; i++)
+ for (size_t i = n - 1; i < n; i--)
{
const struct variable *var = vars[i];
struct pivot_splits_var *psvar = &psvars[i];
*psvar = (struct pivot_splits_var) {
.width = var_get_width (var),
- .idx = var_get_case_index (var),
- .format = *var_get_print_format (var),
.values = HMAP_INITIALIZER (psvar->values),
.dimension = d,
+ .var = var,
};
}
.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];
hmap_destroy (&psvar->values);
}
free (ps->vars);
+
free (ps->encoding);
free (ps);
}
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 *c)
+pivot_splits_new_split (struct pivot_splits *ps, const struct ccase *example)
{
if (!ps)
return;
- for (size_t i = ps->n - 1; i < ps->n; i--)
+ for (size_t i = 0; i < ps->n; i++)
{
struct pivot_splits_var *psvar = &ps->vars[i];
- const union value *value = case_data_idx (c, psvar->idx);
+ const union value *value = case_data (example, psvar->var);
struct pivot_splits_value *psval = pivot_splits_value_find (psvar, value);
if (!psval)
{
value_clone (&psval->value, value, psvar->width);
psval->leaf = pivot_category_create_leaf (
psvar->dimension->root,
- pivot_value_new_value (value, psvar->width, &psvar->format,
- ps->encoding));
+ 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));
+ }
}
-size_t
+static size_t
pivot_splits_get_dindexes (const struct pivot_splits *ps, size_t *dindexes)
{
if (!ps)
assert (ps->dindexes[0] != SIZE_MAX);
for (size_t i = 0; i < ps->n; i++)
- dindexes[i] = ps->dindexes[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)
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)
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,
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,