MATRIX MGET with factor and splits and TYPE
[pspp] / src / language / stats / matrix.c
index 8445e64ac3fff4ed9794ecdaf3402c5ad50a86cb..a9690a42f101a04b112fb65f0976b8922da55812 100644 (file)
@@ -6248,17 +6248,44 @@ get_a8_var (const struct dictionary *d, const char *name)
 }
 
 static bool
-is_rowtype (const union value *v, const char *rowtype)
+var_changed (const struct ccase *ca, const struct ccase *cb,
+             const struct variable *var)
 {
-  struct substring vs = ss_buffer (CHAR_CAST (char *, v->s), 8);
-  ss_rtrim (&vs, ss_cstr (" "));
-  return ss_equals_case (vs, ss_cstr (rowtype));
+  return (ca && cb
+          ? !value_equal (case_data (ca, var), case_data (cb, var),
+                          var_get_width (var))
+          : ca || cb);
+}
+
+static bool
+vars_changed (const struct ccase *ca, const struct ccase *cb,
+              const struct dictionary *d,
+              size_t first_var, size_t n_vars)
+{
+  for (size_t i = 0; i < n_vars; i++)
+    {
+      const struct variable *v = dict_get_var (d, first_var + i);
+      if (var_changed (ca, cb, v))
+        return true;
+    }
+  return false;
+}
+
+static bool
+vars_all_missing (const struct ccase *c, const struct dictionary *d,
+                  size_t first_var, size_t n_vars)
+{
+  for (size_t i = 0; i < n_vars; i++)
+    if (case_num (c, dict_get_var (d, first_var + i)) != SYSMIS)
+      return false;
+  return true;
 }
 
 static void
 matrix_mget_commit_var (struct ccase **rows, size_t n_rows,
                         const struct dictionary *d,
                         const struct variable *rowtype_var,
+                        const struct stringi_set *accepted_rowtypes,
                         struct matrix_state *s,
                         size_t ss, size_t sn, size_t si,
                         size_t fs, size_t fn, size_t fi,
@@ -6267,19 +6294,27 @@ matrix_mget_commit_var (struct ccase **rows, size_t n_rows,
                         struct pivot_dimension *var_dimension)
 {
   if (!n_rows)
-    return;
+    goto exit;
+
+  /* Is this a matrix for pooled data, either where there are no factor
+     variables or the factor variables are missing? */
+  bool pooled = !fn || vars_all_missing (rows[0], d, fs, fn);
 
-  const union value *rowtype_ = case_data (rows[0], rowtype_var);
-  const char *name_prefix = (is_rowtype (rowtype_, "COV") ? "CV"
-                             : is_rowtype (rowtype_, "CORR") ? "CR"
-                             : is_rowtype (rowtype_, "MEAN") ? "MN"
-                             : is_rowtype (rowtype_, "STDDEV") ? "SD"
-                             : is_rowtype (rowtype_, "N") ? "NC"
-                             : "CN");
+  struct substring rowtype = case_ss (rows[0], rowtype_var);
+  ss_rtrim (&rowtype, ss_cstr (" "));
+  if (!stringi_set_is_empty (accepted_rowtypes)
+      && !stringi_set_contains_len (accepted_rowtypes,
+                                    rowtype.string, rowtype.length))
+    goto exit;
 
   struct string name = DS_EMPTY_INITIALIZER;
-  ds_put_cstr (&name, name_prefix);
-  if (fi > 0)
+  ds_put_cstr (&name, (ss_equals_case (rowtype, ss_cstr ("COV")) ? "CV"
+                       : ss_equals_case (rowtype, ss_cstr ("CORR")) ? "CR"
+                       : ss_equals_case (rowtype, ss_cstr ("MEAN")) ? "MN"
+                       : ss_equals_case (rowtype, ss_cstr ("STDDEV")) ? "SD"
+                       : ss_equals_case (rowtype, ss_cstr ("N")) ? "NC"
+                       : "CN"));
+  if (!pooled)
     ds_put_format (&name, "F%zu", fi);
   if (si > 0)
     ds_put_format (&name, "S%zu", si);
@@ -6291,7 +6326,7 @@ matrix_mget_commit_var (struct ccase **rows, size_t n_rows,
     {
       msg (SW, _("Matrix data file contains variable with existing name %s."),
            ds_cstr (&name));
-      goto exit;
+      goto exit_free_name;
     }
 
   gsl_matrix *m = gsl_matrix_alloc (n_rows, cn);
@@ -6324,7 +6359,8 @@ matrix_mget_commit_var (struct ccase **rows, size_t n_rows,
   for (size_t j = 0; j < fn; j++)
     {
       struct variable *var = dict_get_var (d, fs + j);
-      const union value *value = case_data (rows[0], var);
+      const union value sysmis = { .f = SYSMIS };
+      const union value *value = pooled ? &sysmis : case_data (rows[0], var);
       pivot_table_put2 (pt, j + sn, var_index,
                         pivot_value_new_var_value (var, value));
     }
@@ -6340,36 +6376,14 @@ matrix_mget_commit_var (struct ccase **rows, size_t n_rows,
          ds_cstr (&name), n_missing);
   mv->value = m;
 
-exit:
+exit_free_name:
   ds_destroy (&name);
+
+exit:
   for (size_t y = 0; y < n_rows; y++)
     case_unref (rows[y]);
 }
 
-static bool
-var_changed (const struct ccase *ca, const struct ccase *cb,
-             const struct variable *var)
-{
-  return (ca && cb
-          ? !value_equal (case_data (ca, var), case_data (cb, var),
-                          var_get_width (var))
-          : ca || cb);
-}
-
-static bool
-vars_changed (const struct ccase *ca, const struct ccase *cb,
-              const struct dictionary *d,
-              size_t first_var, size_t n_vars)
-{
-  for (size_t i = 0; i < n_vars; i++)
-    {
-      const struct variable *v = dict_get_var (d, first_var + i);
-      if (var_changed (ca, cb, v))
-        return true;
-    }
-  return false;
-}
-
 static void
 matrix_cmd_execute_mget__ (struct mget_command *mget,
                            struct casereader *r, const struct dictionary *d)
@@ -6437,7 +6451,7 @@ matrix_cmd_execute_mget__ (struct mget_command *mget,
   if (fn > 0)
     {
       struct pivot_category *factors = pivot_category_create_group (
-        attr_dimension->root, N_("Factor Values"));
+        attr_dimension->root, N_("Factors"));
       for (size_t i = 0; i < fn; i++)
         pivot_category_create_leaf (factors, pivot_value_new_variable (
                                       dict_get_var (d, fs + i)));
@@ -6453,6 +6467,8 @@ matrix_cmd_execute_mget__ (struct mget_command *mget,
   struct ccase *c;
   while ((c = casereader_read (r)) != NULL)
     {
+      bool row_has_factors = fn && !vars_all_missing (c, d, fs, fn);
+
       enum
         {
           SPLITS_CHANGED,
@@ -6468,7 +6484,8 @@ matrix_cmd_execute_mget__ (struct mget_command *mget,
 
       if (change != NOTHING_CHANGED)
         {
-          matrix_mget_commit_var (rows, n_rows, d, rowtype_,
+          matrix_mget_commit_var (rows, n_rows, d,
+                                  rowtype_, &mget->rowtypes,
                                   mget->state,
                                   ss, sn, si,
                                   fs, fn, fi,
@@ -6492,19 +6509,23 @@ matrix_cmd_execute_mget__ (struct mget_command *mget,
           /* Reset the factor number, if there are factors. */
           if (fn)
             {
-              fi = 1;
+              fi = 0;
+              if (row_has_factors)
+                fi++;
               case_unref (fc);
               fc = case_ref (c);
             }
         }
       else if (change == FACTORS_CHANGED)
         {
-          fi++;
+          if (row_has_factors)
+            fi++;
           case_unref (fc);
           fc = case_ref (c);
         }
     }
-  matrix_mget_commit_var (rows, n_rows, d, rowtype_,
+  matrix_mget_commit_var (rows, n_rows, d,
+                          rowtype_, &mget->rowtypes,
                           mget->state,
                           ss, sn, si,
                           fs, fn, fi,