output: Introduce pivot tables.
[pspp] / src / language / stats / rank.c
index a29a3fd11f7870a746b1d26c06d8a63eaae96690..cd3833dbba7a78583ad0aa5016754c8990ad863c 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2005, 2006, 2007, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc
+   Copyright (C) 2005, 2006, 2007, 2009, 2010, 2011, 2012, 2013, 2014, 2016 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
@@ -41,8 +41,7 @@
 #include "libpspp/pool.h"
 #include "libpspp/string-set.h"
 #include "libpspp/taint.h"
-
-#include "output/tab.h"
+#include "output/pivot-table.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -162,7 +161,7 @@ try_new_name (const char *new_name,
 }
 
 /* Returns a variable name for storing ranks of a variable named SRC_NAME
-   accoring to the rank function F.  The name chosen will not be one already in
+   according to the rank function F.  The name chosen will not be one already in
    DICT or NEW_NAMES.
 
    If successful, adds the new name to NEW_NAMES and returns the name added.
@@ -257,7 +256,7 @@ parse_into (struct lexer *lexer, struct rank *cmd,
 
   cmd->rs = pool_realloc (cmd->pool, cmd->rs, sizeof (*cmd->rs) * (cmd->n_rs + 1));
   rs = &cmd->rs[cmd->n_rs];
-      
+
   if (lex_match_id (lexer, "RANK"))
     {
       rs->rfunc = RANK;
@@ -290,13 +289,13 @@ parse_into (struct lexer *lexer, struct rank *cmd,
     {
       if ( !lex_force_match (lexer, T_LPAREN))
        return false;
-      
+
       if (! lex_force_int (lexer) )
        return false;
-      
+
       cmd->k_ntiles = lex_integer (lexer);
       lex_get (lexer);
-      
+
       if ( !lex_force_match (lexer, T_RPAREN))
        return false;
 
@@ -304,6 +303,7 @@ parse_into (struct lexer *lexer, struct rank *cmd,
     }
   else
     {
+      lex_error (lexer, NULL);
       return false;
     }
 
@@ -318,7 +318,7 @@ parse_into (struct lexer *lexer, struct rank *cmd,
          const char *name = lex_tokcstr (lexer);
 
          if ( var_count >= subcase_get_n_fields (&cmd->sc) )
-            msg (SE, _("Too many variables in INTO clause."));
+            msg (SE, _("Too many variables in %s clause."), "INTO");
          else if ( dict_lookup_var (cmd->dict, name) != NULL )
             msg (SE, _("Variable %s already exists."), name);
           else if (string_set_contains (new_names, name))
@@ -637,7 +637,7 @@ create_var_label (struct rank *cmd, const struct variable *src_var,
                    function_name[f], var_get_name (src_var));
 
   pool_label = pool_strdup (cmd->pool, ds_cstr (&label));
-  
+
   ds_destroy (&label);
 
   return pool_label;
@@ -649,7 +649,6 @@ cmd_rank (struct lexer *lexer, struct dataset *ds)
   struct string_set new_names;
   struct rank rank;
   struct rank_spec *rs;
-  int i;
 
   subcase_init_empty (&rank.sc);
 
@@ -667,7 +666,8 @@ cmd_rank (struct lexer *lexer, struct dataset *ds)
   string_set_init (&new_names);
 
   if (lex_match_id (lexer, "VARIABLES"))
-    lex_force_match (lexer, T_EQUALS);
+    if (! lex_force_match (lexer, T_EQUALS))
+      goto error;
 
   if (!parse_sort_criteria (lexer, rank.dict,
                            &rank.sc,
@@ -687,10 +687,12 @@ cmd_rank (struct lexer *lexer, struct dataset *ds)
 
   while (lex_token (lexer) != T_ENDCMD )
     {
-      lex_force_match (lexer, T_SLASH);
+      if (! lex_force_match (lexer, T_SLASH))
+       goto error;
       if (lex_match_id (lexer, "TIES"))
        {
-         lex_force_match (lexer, T_EQUALS);
+         if (! lex_force_match (lexer, T_EQUALS))
+           goto error;
          if (lex_match_id (lexer, "MEAN"))
            {
              rank.ties = TIES_MEAN;
@@ -715,7 +717,8 @@ cmd_rank (struct lexer *lexer, struct dataset *ds)
        }
       else if (lex_match_id (lexer, "FRACTION"))
        {
-         lex_force_match (lexer, T_EQUALS);
+         if (! lex_force_match (lexer, T_EQUALS))
+           goto error;
          if (lex_match_id (lexer, "BLOM"))
            {
              rank.fraction = FRAC_BLOM;
@@ -740,7 +743,8 @@ cmd_rank (struct lexer *lexer, struct dataset *ds)
        }
       else if (lex_match_id (lexer, "PRINT"))
        {
-         lex_force_match (lexer, T_EQUALS);
+         if (! lex_force_match (lexer, T_EQUALS))
+           goto error;
          if (lex_match_id (lexer, "YES"))
            {
              rank.print = true;
@@ -757,7 +761,8 @@ cmd_rank (struct lexer *lexer, struct dataset *ds)
        }
       else if (lex_match_id (lexer, "MISSING"))
        {
-         lex_force_match (lexer, T_EQUALS);
+         if (! lex_force_match (lexer, T_EQUALS))
+           goto error;
          if (lex_match_id (lexer, "INCLUDE"))
            {
              rank.exclude = MV_SYSTEM;
@@ -795,11 +800,9 @@ cmd_rank (struct lexer *lexer, struct dataset *ds)
      created with INTO. */
   for (rs = rank.rs; rs < &rank.rs[rank.n_rs]; rs++)
     {
-      int v;
-
       rs->dest_labels = pool_calloc (rank.pool, rank.n_vars,
                                      sizeof *rs->dest_labels);
-      for ( v = 0 ; v < rank.n_vars ;  v ++ )
+      for (int v = 0 ; v < rank.n_vars ;  v ++ )
         {
           const char **dst_name = &rs->dest_names[v];
           if ( *dst_name == NULL )
@@ -818,72 +821,54 @@ cmd_rank (struct lexer *lexer, struct dataset *ds)
 
   if ( rank.print )
     {
-      int v;
+      struct pivot_table *table = pivot_table_create (
+        N_("Variables Created by RANK"));
+      table->omit_empty = true;
 
-      tab_output_text (0, _("Variables Created By RANK"));
-      tab_output_text (0, "");
+      pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("New Variable"),
+                              N_("New Variable"), N_("Function"),
+                              N_("Fraction"), N_("Grouping Variables"));
 
-      for (i = 0 ; i <  rank.n_rs ; ++i )
+      struct pivot_dimension *variables = pivot_dimension_create (
+        table, PIVOT_AXIS_ROW, N_("Existing Variable"),
+        N_("Existing Variable"));
+      variables->root->show_label = true;
+
+      for (size_t i = 0 ; i <  rank.n_rs ; ++i )
        {
-         for ( v = 0 ; v < rank.n_vars ;  v ++ )
+         for (size_t v = 0 ; v < rank.n_vars ;  v ++ )
            {
-             if ( rank.n_group_vars > 0 )
-               {
-                 struct string varlist;
-                 int g;
-
-                 ds_init_empty (&varlist);
-                 for ( g = 0 ; g < rank.n_group_vars ; ++g )
-                   {
-                     ds_put_cstr (&varlist, var_get_name (rank.group_vars[g]));
-
-                     if ( g < rank.n_group_vars - 1)
-                       ds_put_cstr (&varlist, " ");
-                   }
-
-                 if ( rank.rs[i].rfunc == NORMAL ||
-                      rank.rs[i].rfunc == PROPORTION )
-                   tab_output_text_format (0,
-                                            _("%s into %s(%s of %s using %s BY %s)"),
-                                            var_get_name (rank.vars[v]),
-                                            rank.rs[i].dest_names[v],
-                                            function_name[rank.rs[i].rfunc],
-                                            var_get_name (rank.vars[v]),
-                                            fraction_name (&rank),
-                                            ds_cstr (&varlist));
-
-                 else
-                   tab_output_text_format (0,
-                                            _("%s into %s(%s of %s BY %s)"),
-                                            var_get_name (rank.vars[v]),
-                                            rank.rs[i].dest_names[v],
-                                            function_name[rank.rs[i].rfunc],
-                                            var_get_name (rank.vars[v]),
-                                            ds_cstr (&varlist));
-                 ds_destroy (&varlist);
-               }
-             else
-               {
-                 if ( rank.rs[i].rfunc == NORMAL ||
-                      rank.rs[i].rfunc == PROPORTION )
-                   tab_output_text_format (0,
-                                            _("%s into %s(%s of %s using %s)"),
-                                            var_get_name (rank.vars[v]),
-                                            rank.rs[i].dest_names[v],
-                                            function_name[rank.rs[i].rfunc],
-                                            var_get_name (rank.vars[v]),
-                                            fraction_name (&rank));
-
-                 else
-                   tab_output_text_format (0,
-                                            _("%s into %s(%s of %s)"),
-                                            var_get_name (rank.vars[v]),
-                                            rank.rs[i].dest_names[v],
-                                            function_name[rank.rs[i].rfunc],
-                                            var_get_name (rank.vars[v]));
-               }
+              int row_idx = pivot_category_create_leaf (
+                variables->root, pivot_value_new_variable (rank.vars[v]));
+
+              struct string group_vars = DS_EMPTY_INITIALIZER;
+              for (int g = 0 ; g < rank.n_group_vars ; ++g )
+                {
+                  if (g)
+                    ds_put_byte (&group_vars, ' ');
+                  ds_put_cstr (&group_vars, var_get_name (rank.group_vars[g]));
+                }
+
+              enum rank_func rfunc = rank.rs[i].rfunc;
+              bool has_fraction = rfunc == NORMAL || rfunc == PROPORTION;
+              const char *entries[] =
+                {
+                  rank.rs[i].dest_names[v],
+                  function_name[rank.rs[i].rfunc],
+                  has_fraction ? fraction_name (&rank) : NULL,
+                  rank.n_group_vars ? ds_cstr (&group_vars) : NULL,
+                };
+              for (size_t j = 0; j < sizeof entries / sizeof *entries; j++)
+                {
+                  const char *entry = entries[j];
+                  if (entry)
+                    pivot_table_put2 (table, j, row_idx,
+                                      pivot_value_new_user_text (entry, -1));
+                }
            }
        }
+
+      pivot_table_submit (table);
     }
 
   /* Do the ranking */
@@ -1123,7 +1108,7 @@ rank_cmd (struct dataset *ds, const struct rank *cmd)
 
           var = dict_create_var_assert (d, rs->dest_names[i], 0);
           var_set_both_formats (var, &dest_format[rs->rfunc]);
-          var_set_label (var, rs->dest_labels[i], false);
+          var_set_label (var, rs->dest_labels[i]);
 
           iv->output_vars[j] = var;
         }