Fixed bug in val_labs_remove.
[pspp] / src / crosstabs.q
index 0a1e891f2470228ff5c53375f3f58a7cc76a3d39..7d9f334f9a5e7c6f39fe234ba54390dfc273a954 100644 (file)
@@ -14,8 +14,8 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
 
 /* FIXME:
 
@@ -38,6 +38,7 @@
 #include "algorithm.h"
 #include "alloc.h"
 #include "case.h"
+#include "dictionary.h"
 #include "hash.h"
 #include "pool.h"
 #include "command.h"
 #include "magic.h"
 #include "misc.h"
 #include "output.h"
+#include "str.h"
 #include "tab.h"
 #include "value-labels.h"
 #include "var.h"
 #include "vfm.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+/* (headers) */
+
 #include "debug-print.h"
 
 /* (specification)
    crosstabs (crs_):
-     *tables=custom;
+     *^tables=custom;
      +variables=custom;
      +missing=miss:!table/include/report;
      +write[wr_]=none,cells,all;
@@ -105,6 +113,22 @@ struct crosstab
                                   larger indices first. */
   };
 
+/* Integer mode variable info. */
+struct var_range
+  {
+    int min;                   /* Minimum value. */
+    int max;                   /* Maximum value + 1. */
+    int count;                 /* max - min. */
+  };
+
+static inline struct var_range *
+get_var_range (struct variable *v) 
+{
+  assert (v != NULL);
+  assert (v->aux != NULL);
+  return v->aux;
+}
+
 /* Indexes into crosstab.v. */
 enum
   {
@@ -278,8 +302,8 @@ crs_custom_tables (struct cmd_crosstabs *cmd UNUSED)
   struct var_set *var_set;
   int n_by;
   struct variable ***by = NULL;
-  int *by_nvar = NULL;
-  int nx = 1;
+  size_t *by_nvar = NULL;
+  size_t nx = 1;
   int success = 0;
 
   /* Ensure that this is a TABLES subcommand. */
@@ -297,17 +321,22 @@ crs_custom_tables (struct cmd_crosstabs *cmd UNUSED)
   
   for (n_by = 0; ;)
     {
-      by = xrealloc (by, sizeof *by * (n_by + 1));
-      by_nvar = xrealloc (by_nvar, sizeof *by_nvar * (n_by + 1));
+      by = xnrealloc (by, n_by + 1, sizeof *by);
+      by_nvar = xnrealloc (by_nvar, n_by + 1, sizeof *by_nvar);
       if (!parse_var_set_vars (var_set, &by[n_by], &by_nvar[n_by],
                                PV_NO_DUPLICATE | PV_NO_SCRATCH))
        goto done;
+      if (xalloc_oversized (nx, by_nvar[n_by])) 
+        {
+          msg (SE, _("Too many crosstabulation variables or dimensions."));
+          goto done;
+        }
       nx *= by_nvar[n_by];
       n_by++;
 
       if (!lex_match (T_BY))
        {
-         if (n_by < 1)
+         if (n_by < 2)
            {
              lex_error (_("expecting BY"));
              goto done;
@@ -318,10 +347,10 @@ crs_custom_tables (struct cmd_crosstabs *cmd UNUSED)
     }
   
   {
-    int *by_iter = xcalloc (sizeof *by_iter * n_by);
+    int *by_iter = xcalloc (n_by, sizeof *by_iter);
     int i;
 
-    xtab = xrealloc (xtab, sizeof *xtab * (nxtab + nx));
+    xtab = xnrealloc (xtab, nxtab + nx, sizeof *xtab);
     for (i = 0; i < nx; i++)
       {
        struct crosstab *x;
@@ -384,8 +413,8 @@ crs_custom_variables (struct cmd_crosstabs *cmd UNUSED)
   
   for (;;)
     {
-      int orig_nv = variables_cnt;
-      int i;
+      size_t orig_nv = variables_cnt;
+      size_t i;
 
       long min, max;
       
@@ -426,11 +455,13 @@ crs_custom_variables (struct cmd_crosstabs *cmd UNUSED)
        }
       lex_get ();
       
-      for (i = orig_nv; i < variables_cnt; i++)
-       {
-         variables[i]->p.crs.min = min;
-         variables[i]->p.crs.max = max + 1.;
-         variables[i]->p.crs.count = max - min + 1;
+      for (i = orig_nv; i < variables_cnt; i++) 
+        {
+          struct var_range *vr = xmalloc (sizeof *vr);
+          vr->min = min;
+         vr->max = max + 1.;
+         vr->count = max - min + 1;
+          var_attach_aux (variables[i], vr, var_dtor_free);
        }
       
       if (token == '/')
@@ -475,14 +506,14 @@ precalc (void *aux UNUSED)
 
          x->ofs = n_sorted_tab;
 
-         for (j = 2; j < x->nvar; j++)
-           count *= x->vars[j - 2]->p.crs.count;
-
-         sorted_tab = xrealloc (sorted_tab,
-                                sizeof *sorted_tab * (n_sorted_tab + count));
+         for (j = 2; j < x->nvar; j++) 
+            count *= get_var_range (x->vars[j - 2])->count;
+          
+         sorted_tab = xnrealloc (sorted_tab,
+                                  n_sorted_tab + count, sizeof *sorted_tab);
          v = local_alloc (sizeof *v * x->nvar);
-         for (j = 2; j < x->nvar; j++)
-           v[j] = x->vars[j]->p.crs.min;
+         for (j = 2; j < x->nvar; j++) 
+            v[j] = get_var_range (x->vars[j])->min; 
          for (j = 0; j < count; j++)
            {
              struct table_entry *te;
@@ -493,28 +524,32 @@ precalc (void *aux UNUSED)
              te->table = i;
              
              {
-               const int mat_size = (x->vars[0]->p.crs.count
-                                     * x->vars[1]->p.crs.count);
+                int row_cnt = get_var_range (x->vars[0])->count;
+                int col_cnt = get_var_range (x->vars[1])->count;
+               const int mat_size = row_cnt * col_cnt;
                int m;
                
-               te->u.data = xmalloc (sizeof *te->u.data * mat_size);
+               te->u.data = xnmalloc (mat_size, sizeof *te->u.data);
                for (m = 0; m < mat_size; m++)
                  te->u.data[m] = 0.;
              }
              
              for (k = 2; k < x->nvar; k++)
                te->values[k].f = v[k];
-             for (k = 2; k < x->nvar; k++)
-               if (++v[k] >= x->vars[k]->p.crs.max)
-                 v[k] = x->vars[k]->p.crs.min;
-               else
-                 break;
+             for (k = 2; k < x->nvar; k++) 
+                {
+                  struct var_range *vr = get_var_range (x->vars[k]);
+                  if (++v[k] >= vr->max)
+                    v[k] = vr->min;
+                  else
+                    break; 
+                }
            }
          local_free (v);
        }
 
-      sorted_tab = xrealloc (sorted_tab,
-                            sizeof *sorted_tab * (n_sorted_tab + 1));
+      sorted_tab = xnrealloc (sorted_tab,
+                              n_sorted_tab + 1, sizeof *sorted_tab);
       sorted_tab[n_sorted_tab] = NULL;
     }
 }
@@ -546,11 +581,11 @@ calc_general (struct ccase *c, void *aux UNUSED)
        assert (x != NULL);
        for (j = 0; j < x->nvar; j++)
          {
-           if ((cmd.miss == CRS_TABLE
-                && is_missing (case_data (c, x->vars[j]->fv), x->vars[j]))
+            const union value *v = case_data (c, x->vars[j]->fv);
+            const struct missing_values *mv = &x->vars[j]->miss;
+           if ((cmd.miss == CRS_TABLE && mv_is_value_missing (mv, v))
                || (cmd.miss == CRS_INCLUDE
-                   && is_system_missing (case_data (c, x->vars[j]->fv),
-                                          x->vars[j])))
+                   && mv_is_value_system_missing (mv, v)))
              {
                x->missing += weight;
                goto next_crosstab;
@@ -615,11 +650,13 @@ calc_integer (struct ccase *c, void *aux UNUSED)
       for (i = 0; i < x->nvar; i++)
        {
          struct variable *const v = x->vars[i];
+          struct var_range *vr = get_var_range (v);
          double value = case_num (c, v->fv);
          
          /* Note that the first test also rules out SYSMIS. */
-         if ((value < v->p.crs.min || value >= v->p.crs.max)
-             || (cmd.miss == CRS_TABLE && is_num_user_missing (value, v)))
+         if ((value < vr->min || value >= vr->max)
+             || (cmd.miss == CRS_TABLE
+                  && mv_is_num_user_missing (&v->miss, value)))
            {
              x->missing += weight;
              goto next_crosstab;
@@ -627,15 +664,19 @@ calc_integer (struct ccase *c, void *aux UNUSED)
          
          if (i > 1)
            {
-             ofs += fact * ((int) value - v->p.crs.min);
-             fact *= v->p.crs.count;
+             ofs += fact * ((int) value - vr->min);
+             fact *= vr->count;
            }
        }
       
       {
-       const int row = case_num (c, x->vars[ROW_VAR]->fv) - x->vars[ROW_VAR]->p.crs.min;
-       const int col = case_num (c, x->vars[COL_VAR]->fv) - x->vars[COL_VAR]->p.crs.min;
-       const int col_dim = x->vars[COL_VAR]->p.crs.count;
+        struct variable *row_var = x->vars[ROW_VAR];
+       const int row = case_num (c, row_var->fv) - get_var_range (row_var)->min;
+
+        struct variable *col_var = x->vars[COL_VAR];
+       const int col = case_num (c, col_var->fv) - get_var_range (col_var)->min;
+
+       const int col_dim = get_var_range (col_var)->count;
 
        sorted_tab[ofs]->u.data[col + row * col_dim] += weight;
       }
@@ -804,8 +845,8 @@ make_summary_table (void)
       else
        {
          const struct crosstab *const x = xtab[(*pb)->table];
-         const int n_cols = x->vars[COL_VAR]->p.crs.count;
-         const int n_rows = x->vars[ROW_VAR]->p.crs.count;
+         const int n_cols = get_var_range (x->vars[COL_VAR])->count;
+         const int n_rows = get_var_range (x->vars[ROW_VAR])->count;
          const int count = n_cols * n_rows;
            
          for (valid = 0.; pb < pe; pb++)
@@ -1156,13 +1197,13 @@ output_pivot_table (struct table_entry **pb, struct table_entry **pe,
       /* Allocate memory space for the column and row totals. */
       if (n_rows > *maxrows)
        {
-         *row_totp = xrealloc (*row_totp, sizeof **row_totp * n_rows);
+         *row_totp = xnrealloc (*row_totp, n_rows, sizeof **row_totp);
          row_tot = *row_totp;
          *maxrows = n_rows;
        }
       if (n_cols > *maxcols)
        {
-         *col_totp = xrealloc (*col_totp, sizeof **col_totp * n_cols);
+         *col_totp = xnrealloc (*col_totp, n_cols, sizeof **col_totp);
          col_tot = *col_totp;
          *maxcols = n_cols;
        }
@@ -1178,7 +1219,7 @@ output_pivot_table (struct table_entry **pb, struct table_entry **pe,
          /* Allocate memory space for the matrix. */
          if (n_cols * n_rows > *maxcells)
            {
-             *matp = xrealloc (*matp, sizeof **matp * n_cols * n_rows);
+             *matp = xnrealloc (*matp, n_cols * n_rows, sizeof **matp);
              *maxcells = n_cols * n_rows;
            }
          
@@ -1375,7 +1416,7 @@ delete_missing (void)
     int r;
 
     for (r = 0; r < n_rows; r++)
-      if (is_num_user_missing (rows[r].f, x->vars[ROW_VAR]))
+      if (mv_is_num_user_missing (&x->vars[ROW_VAR]->miss, rows[r].f))
        {
          int c;
 
@@ -1389,7 +1430,7 @@ delete_missing (void)
     int c;
 
     for (c = 0; c < n_cols; c++)
-      if (is_num_user_missing (cols[c].f, x->vars[COL_VAR]))
+      if (mv_is_num_user_missing (&x->vars[COL_VAR]->miss, cols[c].f))
        {
          int r;
 
@@ -1569,7 +1610,7 @@ compare_value (const void *a_, const void *b_, void *width_)
 
 /* Given an array of ENTRY_CNT table_entry structures starting at
    ENTRIES, creates a sorted list of the values that the variable
-   with index VAR_INDEX takes on.  The values are returned as a
+   with index VAR_IDX takes on.  The values are returned as a
    malloc()'darray stored in *VALUES, with the number of values
    stored in *VALUE_CNT.
    */
@@ -1577,12 +1618,14 @@ static void
 enum_var_values (struct table_entry **entries, int entry_cnt, int var_idx,
                  union value **values, int *value_cnt)
 {
+  struct variable *v = xtab[(*entries)->table]->vars[var_idx];
+
   if (mode == GENERAL)
     {
-      int width = xtab[(*entries)->table]->vars[var_idx]->width;
+      int width = v->width;
       int i;
 
-      *values = xmalloc (sizeof **values * entry_cnt);
+      *values = xnmalloc (entry_cnt, sizeof **values);
       for (i = 0; i < entry_cnt; i++)
         (*values)[i] = entries[i]->values[var_idx];
       *value_cnt = sort_unique (*values, entry_cnt, sizeof **values,
@@ -1590,15 +1633,14 @@ enum_var_values (struct table_entry **entries, int entry_cnt, int var_idx,
     }
   else
     {
-      struct crosstab_proc *crs
-        = &xtab[(*entries)->table]->vars[var_idx]->p.crs;
+      struct var_range *vr = get_var_range (v);
       int i;
       
       assert (mode == INTEGER);
-      *values = xmalloc (sizeof **values * crs->count);
-      for (i = 0; i < crs->count; i++)
-       (*values)[i].f = i + crs->min;
-      *value_cnt = crs->count;
+      *values = xnmalloc (vr->count, sizeof **values);
+      for (i = 0; i < vr->count; i++)
+       (*values)[i].f = i + vr->min;
+      *value_cnt = vr->count;
     }
 }
 
@@ -1609,7 +1651,7 @@ static void
 table_value_missing (struct tab_table *table, int c, int r, unsigned char opt,
                     const union value *v, const struct variable *var)
 {
-  struct len_string s;
+  struct fixed_string s;
 
   const char *label = val_labs_find (var->val_labs, *v);
   if (label) 
@@ -1621,7 +1663,7 @@ table_value_missing (struct tab_table *table, int c, int r, unsigned char opt,
   s.string = tab_alloc (table, var->print.w);
   format_short (s.string, &var->print, v);
   s.length = strlen (s.string);
-  if (cmd.miss == CRS_REPORT && is_num_user_missing (v->f, var))
+  if (cmd.miss == CRS_REPORT && mv_is_num_user_missing (&var->miss, v->f))
     s.string[s.length++] = 'M';
   while (s.length && *s.string == ' ')
     {
@@ -1656,7 +1698,7 @@ format_cell_entry (struct tab_table *table, int c, int r, double value,
 {
   const struct fmt_spec f = {FMT_F, 10, 1};
   union value v;
-  struct len_string s;
+  struct fixed_string s;
   
   s.length = 10;
   s.string = tab_alloc (table, 16);
@@ -1704,8 +1746,9 @@ display_crosstabulation (void)
             int mark_missing = 0;
             double expected_value = row_tot[r] * col_tot[c] / W;
             if (cmd.miss == CRS_REPORT
-                && (is_num_user_missing (cols[c].f, x->vars[COL_VAR])
-                    || is_num_user_missing (rows[r].f, x->vars[ROW_VAR])))
+                && (mv_is_num_user_missing (&x->vars[COL_VAR]->miss, cols[c].f)
+                    || mv_is_num_user_missing (&x->vars[ROW_VAR]->miss,
+                                               rows[r].f)))
               mark_missing = 1;
            for (i = 0; i < num_cells; i++)
              {
@@ -1770,7 +1813,7 @@ display_crosstabulation (void)
         int mark_missing = 0;
 
         if (cmd.miss == CRS_REPORT
-            && is_num_user_missing (rows[r].f, x->vars[ROW_VAR]))
+            && mv_is_num_user_missing (&x->vars[ROW_VAR]->miss, rows[r].f))
           mark_missing = 1;
 
         for (i = 0; i < num_cells; i++)
@@ -1826,7 +1869,7 @@ display_crosstabulation (void)
         int i;
            
         if (cmd.miss == CRS_REPORT && c < n_cols 
-            && is_num_user_missing (cols[c].f, x->vars[COL_VAR]))
+            && mv_is_num_user_missing (&x->vars[COL_VAR]->miss, cols[c].f))
           mark_missing = 1;
 
         for (i = 0; i < num_cells; i++)
@@ -2477,7 +2520,7 @@ calc_symmetric (double v[N_SYMMETRIC], double ase[N_SYMMETRIC],
       {
        int r, c;
 
-       cum = xmalloc (sizeof *cum * n_cols * n_rows);
+       cum = xnmalloc (n_cols * n_rows, sizeof *cum);
        for (c = 0; c < n_cols; c++)
          {
            double ct = 0.;
@@ -2820,10 +2863,10 @@ calc_directional (double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
   /* Lambda. */
   if (cmd.a_statistics[CRS_ST_LAMBDA])
     {
-      double *fim = xmalloc (sizeof *fim * n_rows);
-      int *fim_index = xmalloc (sizeof *fim_index * n_rows);
-      double *fmj = xmalloc (sizeof *fmj * n_cols);
-      int *fmj_index = xmalloc (sizeof *fmj_index * n_cols);
+      double *fim = xnmalloc (n_rows, sizeof *fim);
+      int *fim_index = xnmalloc (n_rows, sizeof *fim_index);
+      double *fmj = xnmalloc (n_cols, sizeof *fmj);
+      int *fmj_index = xnmalloc (n_cols, sizeof *fmj_index);
       double sum_fim, sum_fmj;
       double rm, cm;
       int rm_index, cm_index;