FREQUENCIES and layered split file has tests that work
[pspp] / src / output / pivot-table.c
index 1e7d2fbb5ac6b72308f54ace365ade5c4a6053f9..6d1efb882b4748b3a009d6d3e0e64c05b0b1e624 100644 (file)
@@ -3043,6 +3043,7 @@ struct pivot_splits_value
 struct pivot_splits_var
   {
     struct pivot_dimension *dimension;
+    char *name;
     int width;
     size_t idx;
     struct fmt_spec format;
@@ -3056,8 +3057,17 @@ struct pivot_splits
     char *encoding;
 
     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,
@@ -3071,7 +3081,7 @@ pivot_splits_create (struct pivot_table *pt,
 
   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];
@@ -3082,6 +3092,7 @@ pivot_splits_create (struct pivot_table *pt,
 
       *psvar = (struct pivot_splits_var) {
         .width = var_get_width (var),
+        .name = xstrdup (var_get_name (var)),
         .idx = var_get_case_index (var),
         .format = *var_get_print_format (var),
         .values = HMAP_INITIALIZER (psvar->values),
@@ -3095,16 +3106,25 @@ pivot_splits_create (struct pivot_table *pt,
     .n = n,
     .encoding = xstrdup (dict_get_encoding (dict)),
     .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);
+
   for (size_t i = 0; i < ps->n; i++)
     {
       struct pivot_splits_var *psvar = &ps->vars[i];
@@ -3116,6 +3136,7 @@ pivot_splits_destroy (struct pivot_splits *ps)
           hmap_delete (&psvar->values, &psval->hmap_node);
           free (psval);
         }
+      free (psvar->name);
       hmap_destroy (&psvar->values);
     }
   free (ps->vars);
@@ -3135,16 +3156,24 @@ pivot_splits_value_find (struct pivot_splits_var *psvar,
   return NULL;
 }
 
+/* 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--)
+  size_t n_new = 0;
+  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_idx (example, psvar->idx);
       struct pivot_splits_value *psval = pivot_splits_value_find (psvar, value);
       if (!psval)
         {
@@ -3156,13 +3185,50 @@ pivot_splits_new_split (struct pivot_splits *ps, const struct ccase *c)
             psvar->dimension->root,
             pivot_value_new_value (value, psvar->width, &psvar->format,
                                    ps->encoding));
+          n_new++;
         }
 
       ps->dindexes[i] = psval->leaf;
     }
+
+  if (!n_new)
+    {
+      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_idx (example, psvar->idx);
+              ds_put_format (&s, "%s = ", psvar->name);
+
+              char *s2 = data_out (value, ps->encoding, &psvar->format,
+                                   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_idx (example, psvar->idx);
+      ps->dindexes[0] = pivot_category_create_leaf (
+        psvar->dimension->root,
+        pivot_value_new_value (value, psvar->width, &psvar->format,
+                               ps->encoding));
+    }
 }
 
-size_t
+static size_t
 pivot_splits_get_dindexes (const struct pivot_splits *ps, size_t *dindexes)
 {
   if (!ps)
@@ -3174,6 +3240,12 @@ pivot_splits_get_dindexes (const struct pivot_splits *ps, size_t *dindexes)
   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)
@@ -3185,6 +3257,12 @@ pivot_splits_put1 (struct pivot_splits *ps, struct pivot_table *table,
   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)
@@ -3197,6 +3275,12 @@ pivot_splits_put2 (struct pivot_splits *ps, struct pivot_table *table,
   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,
@@ -3211,6 +3295,12 @@ pivot_splits_put3 (struct pivot_splits *ps, struct pivot_table *table,
   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,