Basic MGET tests work.
[pspp] / src / language / stats / matrix.c
index 32a06914d927780eeebde185896c41da35a6428c..17b66de22ede53192e1bcb74e504a982cc2845c6 100644 (file)
@@ -3486,6 +3486,7 @@ struct matrix_cmd
           {
             struct matrix_state *state;
             struct file_handle *file;
+            char *encoding;
             struct stringi_set rowtypes;
           }
         mget;
@@ -4319,6 +4320,7 @@ matrix_cmd_execute_display (struct display_command *cmd)
       for (size_t j = 0; j < sizeof values / sizeof *values; j++)
         pivot_table_put2 (table, j, i, pivot_value_new_integer (values[j]));
     }
+  free (vars);
   pivot_table_submit (table);
 }
 \f
@@ -5598,9 +5600,8 @@ error:
 }
 
 static void
-matrix_cmd_execute_get__ (struct get_command *get,
-                          const struct dictionary *dict,
-                          struct casereader *reader)
+matrix_cmd_execute_get__ (struct get_command *get, struct casereader *reader,
+                          const struct dictionary *dict)
 {
   struct variable **vars;
   size_t n_vars = 0;
@@ -5713,35 +5714,52 @@ matrix_cmd_execute_get__ (struct get_command *get,
   free (vars);
 }
 
-static void
-matrix_cmd_execute_get (struct get_command *get)
+static bool
+matrix_open_casereader (const char *command_name,
+                        struct file_handle *file, const char *encoding,
+                        struct dataset *dataset,
+                        struct casereader **readerp, struct dictionary **dictp)
 {
-  struct dictionary *dict;
-  struct casereader *reader;
-  if (get->file)
+  if (file)
     {
-       reader = any_reader_open_and_decode (get->file, get->encoding,
-                                            &dict, NULL);
-       if (!reader)
-         return;
+       *readerp = any_reader_open_and_decode (file, encoding, dictp, NULL);
+       return *readerp != NULL;
     }
   else
     {
-      if (dict_get_var_cnt (dataset_dict (get->dataset)) == 0)
+      if (dict_get_var_cnt (dataset_dict (dataset)) == 0)
         {
-          msg (ME, _("GET cannot read empty active file."));
-          return;
+          msg (ME, _("The %s command cannot read empty active file."),
+               command_name);
+          return false;
         }
-      reader = proc_open (get->dataset);
-      dict = dict_ref (dataset_dict (get->dataset));
+      *readerp = proc_open (dataset);
+      *dictp = dict_ref (dataset_dict (dataset));
+      return true;
     }
+}
 
-  matrix_cmd_execute_get__ (get, dict, reader);
-
+static void
+matrix_close_casereader (struct file_handle *file, struct dataset *dataset,
+                         struct casereader *reader, struct dictionary *dict)
+{
   dict_unref (dict);
   casereader_destroy (reader);
-  if (!get->file)
-    proc_commit (get->dataset);
+  if (!file)
+    proc_commit (dataset);
+}
+
+static void
+matrix_cmd_execute_get (struct get_command *get)
+{
+  struct casereader *r;
+  struct dictionary *d;
+  if (matrix_open_casereader ("GET", get->file, get->encoding, get->dataset,
+                              &r, &d))
+    {
+      matrix_cmd_execute_get__ (get, r, d);
+      matrix_close_casereader (get->file, get->dataset, r, d);
+    }
 }
 \f
 static const char *
@@ -6149,11 +6167,17 @@ static struct matrix_cmd *
 matrix_parse_mget (struct matrix_state *s)
 {
   struct matrix_cmd *cmd = xmalloc (sizeof *cmd);
-  *cmd = (struct matrix_cmd) { .type = MCMD_MGET, .mget = { .state = s } };
+  *cmd = (struct matrix_cmd) {
+    .type = MCMD_MGET,
+    .mget = {
+      .state = s,
+      .rowtypes = STRINGI_SET_INITIALIZER (cmd->mget.rowtypes),
+    },
+  };
 
   struct mget_command *mget = &cmd->mget;
 
-  for (;;)
+  while (lex_token (s->lexer) != T_ENDCMD)
     {
       if (lex_match_id (s->lexer, "FILE"))
         {
@@ -6164,6 +6188,17 @@ matrix_parse_mget (struct matrix_state *s)
           if (!mget->file)
             goto error;
         }
+      else if (lex_match_id (s->lexer, "ENCODING"))
+       {
+         lex_match (s->lexer, T_EQUALS);
+         if (!lex_force_string (s->lexer))
+           goto error;
+
+          free (mget->encoding);
+          mget->encoding = ss_xstrdup (lex_tokss (s->lexer));
+
+         lex_get (s->lexer);
+       }
       else if (lex_match_id (s->lexer, "TYPE"))
         {
           lex_match (s->lexer, T_EQUALS);
@@ -6182,11 +6217,7 @@ matrix_parse_mget (struct matrix_state *s)
           lex_error_expecting (s->lexer, "FILE", "TYPE");
           goto error;
         }
-      if (lex_token (s->lexer) == T_ENDCMD)
-        break;
-
-      if (!lex_force_match (s->lexer, T_SLASH))
-        goto error;
+      lex_match (s->lexer, T_SLASH);
     }
   return cmd;
 
@@ -6227,7 +6258,9 @@ matrix_mget_commit_var (struct ccase **rows, size_t n_rows,
                         const struct dictionary *d,
                         const struct variable *rowtype_var,
                         struct matrix_state *s, size_t si, size_t fi,
-                        size_t cs, size_t cn)
+                        size_t cs, size_t cn,
+                        struct pivot_table *pt,
+                        struct pivot_dimension *var_dimension)
 {
   if (!n_rows)
     return;
@@ -6274,6 +6307,12 @@ matrix_mget_commit_var (struct ccase **rows, size_t n_rows,
         }
     }
 
+  int var_index = pivot_category_create_leaf (
+    var_dimension->root, pivot_value_new_user_text (ds_cstr (&name), SIZE_MAX));
+  double values[] = { n_rows, cn };
+  for (size_t j = 0; j < sizeof values / sizeof *values; j++)
+    pivot_table_put2 (pt, j, var_index, pivot_value_new_integer (values[j]));
+
   if (n_missing)
     msg (SE, ngettext ("Matrix data file variable %s contains a missing "
                        "value, which was treated as zero.",
@@ -6292,8 +6331,10 @@ static bool
 var_changed (const struct ccase *ca, const struct ccase *cb,
              const struct variable *var)
 {
-  return !value_equal (case_data (ca, var), case_data (cb, var),
-                       var_get_width (var));
+  return (ca && cb
+          ? !value_equal (case_data (ca, var), case_data (cb, var),
+                          var_get_width (var))
+          : ca || cb);
 }
 
 static bool
@@ -6311,28 +6352,25 @@ vars_changed (const struct ccase *ca, const struct ccase *cb,
 }
 
 static void
-matrix_cmd_execute_mget (struct mget_command *mget)
+matrix_cmd_execute_mget__ (struct mget_command *mget,
+                           struct casereader *r, const struct dictionary *d,
+                           struct pivot_table *pt,
+                           struct pivot_dimension *var_dimension)
 {
-  struct dictionary *d;
-  struct casereader *r = any_reader_open_and_decode (mget->file, "UTF-8",
-                                                     &d, NULL);
-  if (!r)
-    return;
-
   const struct variable *rowtype_ = get_a8_var (d, "ROWTYPE_");
   const struct variable *varname_ = get_a8_var (d, "VARNAME_");
   if (!rowtype_ || !varname_)
-    goto exit;
+    return;
 
   if (var_get_dict_index (rowtype_) >= var_get_dict_index (varname_))
     {
       msg (SE, _("ROWTYPE_ must precede VARNAME_ in matrix data file."));
-      goto exit;
+      return;
     }
   if (var_get_dict_index (varname_) + 1 >= dict_get_var_cnt (d))
     {
       msg (SE, _("Matrix data file contains no continuous variables."));
-      goto exit;
+      return;
     }
 
   for (size_t i = 0; i < dict_get_var_cnt (d); i++)
@@ -6343,7 +6381,7 @@ matrix_cmd_execute_mget (struct mget_command *mget)
           msg (SE,
                _("Matrix data file contains unexpected string variable %s."),
                var_get_name (v));
-          goto exit;
+          return;
         }
     }
 
@@ -6372,25 +6410,24 @@ matrix_cmd_execute_mget (struct mget_command *mget)
   struct ccase *c;
   while ((c = casereader_read (r)) != NULL)
     {
-      bool sd = vars_changed (sc, c, d, ss, sn);
-      bool fd = sd || vars_changed (fc, c, d, fs, fn);
-      bool md = fd || !cc || var_changed (cc, c, rowtype_) || var_changed (cc, c, varname_);
-      if (sd)
-        {
-          si++;
-          case_unref (sc);
-          sc = case_ref (c);
-        }
-      if (fd)
+      enum
         {
-          fi++;
-          case_unref (fc);
-          fc = case_ref (c);
+          SPLITS_CHANGED,
+          FACTORS_CHANGED,
+          ROWTYPE_CHANGED,
+          NOTHING_CHANGED
         }
-      if (md)
+      change
+        = (sn && (!sc || vars_changed (sc, c, d, ss, sn)) ? SPLITS_CHANGED
+           : fn && (!fc || vars_changed (fc, c, d, fs, fn)) ? FACTORS_CHANGED
+           : !cc || var_changed (cc, c, rowtype_) ? ROWTYPE_CHANGED
+           : NOTHING_CHANGED);
+
+      if (change != NOTHING_CHANGED)
         {
           matrix_mget_commit_var (rows, n_rows, d, rowtype_,
-                                  mget->state, si, fi, cs, cn);
+                                  mget->state, si, fi, cs, cn,
+                                  pt, var_dimension);
           n_rows = 0;
           case_unref (cc);
           cc = case_ref (c);
@@ -6399,13 +6436,60 @@ matrix_cmd_execute_mget (struct mget_command *mget)
       if (n_rows >= allocated_rows)
         rows = x2nrealloc (rows, &allocated_rows, sizeof *rows);
       rows[n_rows++] = c;
+
+      if (change == SPLITS_CHANGED)
+        {
+          si++;
+          case_unref (sc);
+          sc = case_ref (c);
+
+          /* Reset the factor number, if there are factors. */
+          if (fn)
+            {
+              fi = 1;
+              case_unref (fc);
+              fc = case_ref (c);
+            }
+        }
+      else if (change == FACTORS_CHANGED)
+        {
+          fi++;
+          case_unref (fc);
+          fc = case_ref (c);
+        }
     }
   matrix_mget_commit_var (rows, n_rows, d, rowtype_,
-                          mget->state, si, fi, cs, cn);
+                          mget->state, si, fi, cs, cn,
+                          pt, var_dimension);
   free (rows);
 
-exit:
-  casereader_destroy (r);
+  case_unref (sc);
+  case_unref (fc);
+  case_unref (cc);
+}
+
+static void
+matrix_cmd_execute_mget (struct mget_command *mget)
+{
+  struct casereader *r;
+  struct dictionary *d;
+  if (matrix_open_casereader ("MGET", mget->file, mget->encoding,
+                              mget->state->dataset, &r, &d))
+    {
+      struct pivot_table *pt = pivot_table_create (
+        N_("Matrix Variables Created by MGET"));
+      pivot_dimension_create (pt, PIVOT_AXIS_COLUMN, N_("Dimension"),
+                              N_("Rows"), N_("Columns"));
+      struct pivot_dimension *var_dimension = pivot_dimension_create (
+        pt, PIVOT_AXIS_ROW, N_("Variable"));
+      matrix_cmd_execute_mget__ (mget, r, d, pt, var_dimension);
+      if (var_dimension->n_leaves)
+        pivot_table_submit (pt);
+      else
+        pivot_table_unref (pt);
+
+      matrix_close_casereader (mget->file, mget->state->dataset, r, d);
+    }
 }
 \f
 static bool