pivot table procedure conceptually works
[pspp] / src / language / data-io / pivot.c
diff --git a/src/language/data-io/pivot.c b/src/language/data-io/pivot.c
new file mode 100644 (file)
index 0000000..22d7d1b
--- /dev/null
@@ -0,0 +1,189 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "data/casereader.h"
+#include "data/dataset.h"
+#include "data/subcase.h"
+#include "language/command.h"
+#include "language/lexer/lexer.h"
+#include "language/lexer/variable-parser.h"
+#include "math/sort.h"
+#include "output/tab.h"
+#include "output/table-item.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+enum
+  {
+    ROW_VAR,
+    COL_VAR,
+    DATA_VAR
+  };
+
+struct cmd_pivot
+  {
+    const struct variable **vars[3];
+    size_t n_vars[3];
+  };
+
+struct ccase *
+take_first_case (struct ccase *first, struct ccase *second, void *aux UNUSED)
+{
+  case_unref (second);
+  return first;
+}
+
+int
+cmd_debug_pivot (struct lexer *lexer, struct dataset *ds)
+{
+  const struct dictionary *dict = dataset_dict (ds);
+  struct cmd_pivot p;
+  struct casereader *reader;
+  struct subcase col_ordering, total_ordering, row_ordering;
+  struct casereader *columns, *cells, *columns_reader;
+  struct ccase *row, *prev, *column;
+  int col_idx;
+
+  memset (&p, 0, sizeof p);
+  while (lex_token (lexer) != T_ENDCMD)
+    {
+      lex_match (lexer, T_SLASH);
+      if (lex_match_id (lexer, "ROWS") )
+        {
+          lex_match (lexer, T_EQUALS);
+          if (!parse_variables_const (lexer, dict,
+                                      &p.vars[ROW_VAR], &p.n_vars[ROW_VAR],
+                                      PV_NO_DUPLICATE | PV_NO_SCRATCH))
+            return CMD_FAILURE;
+        }
+      else if (lex_match_id (lexer, "COLUMNS") )
+        {
+          lex_match (lexer, T_EQUALS);
+          if (!parse_variables_const (lexer, dict,
+                                      &p.vars[COL_VAR], &p.n_vars[COL_VAR],
+                                      PV_NO_DUPLICATE | PV_NO_SCRATCH))
+            return CMD_FAILURE;
+        }
+      else if (lex_match_id (lexer, "DATA") )
+        {
+          lex_match (lexer, T_EQUALS);
+          if (!parse_variables_const (lexer, dict,
+                                      &p.vars[DATA_VAR], &p.n_vars[DATA_VAR],
+                                      PV_NO_DUPLICATE | PV_NO_SCRATCH))
+            return CMD_FAILURE;
+        }
+    }
+
+
+  reader = proc_open (ds);
+
+  subcase_init_vars (&col_ordering, p.vars[COL_VAR], p.n_vars[COL_VAR]);
+  columns = sort_distinct_execute (casereader_clone (reader), &col_ordering,
+                                   take_first_case, NULL, NULL);
+  printf ("%lld column combinations\n",
+          (long long int) casereader_count_cases (columns));
+
+  subcase_init_vars (&total_ordering, p.vars[ROW_VAR], p.n_vars[ROW_VAR]);
+  subcase_add_vars_always (&total_ordering,
+                           p.vars[COL_VAR], p.n_vars[COL_VAR]);
+  cells = sort_distinct_execute (reader, &total_ordering,
+                                 take_first_case, NULL, NULL);
+  printf ("%lld cells\n",
+          (long long int) casereader_count_cases (cells));
+
+  row = prev = column = NULL;
+  columns_reader = NULL;
+  subcase_init_vars (&row_ordering, p.vars[ROW_VAR], p.n_vars[ROW_VAR]);
+  for (;;)
+    {
+      struct ccase *c;
+
+      c = casereader_read (cells);
+      if (!c)
+        break;
+
+      if (!row || !subcase_equal (&row_ordering, row, &row_ordering, c))
+        {
+          int i;
+
+          if (row)
+            putchar ('\n');
+          case_unref (prev);
+          prev = row;
+          row = case_ref (c);
+
+          i = 0;
+          if (prev)
+            for (; i < p.n_vars[ROW_VAR]; i++)
+              {
+                if (!value_equal (case_data (row, p.vars[ROW_VAR][i]),
+                                  case_data (prev, p.vars[ROW_VAR][i]),
+                                  var_get_width (p.vars[ROW_VAR][i])))
+                  break;
+                printf ("        ");
+              }
+          for (; i < p.n_vars[ROW_VAR]; i++)
+            {
+              union value value;
+              const char *label;
+
+              value.f = case_num (row, p.vars[ROW_VAR][i]);
+              label = var_lookup_value_label (p.vars[ROW_VAR][i], &value);
+              if (label)
+                printf ("%7s ", label);
+              else
+                printf ("%7.0f ", value.f);
+            }
+          printf ("| ");
+
+          case_unref (column);
+          casereader_destroy (columns_reader);
+          columns_reader = casereader_clone (columns);
+          column = casereader_read (columns_reader);
+          col_idx = 0;
+        }
+
+      while (!subcase_equal (&col_ordering, column, &col_ordering, c))
+        {
+          case_unref (column);
+          column = casereader_read (columns_reader);
+          printf ("        ");
+          col_idx++;
+        }
+
+      printf ("%7.0f ", case_num (c, p.vars[DATA_VAR][0]));
+      col_idx++;
+      column = casereader_read (columns_reader);
+    }
+  if (row)
+    putchar ('\n');
+  case_unref (row);
+  case_unref (prev);
+  case_unref (column);
+  casereader_destroy (columns_reader);
+
+  casereader_destroy (columns);
+  casereader_destroy (cells);
+  subcase_destroy (&col_ordering);
+  subcase_destroy (&row_ordering);
+
+  proc_commit (ds);
+
+  return CMD_SUCCESS;
+}