Rename ctables_freq to ctables_cell.
[pspp] / src / language / stats / ctables.c
index 07de68d919361ecb73cd7c8b269b1698b21295f7..aee54a1f52245e54d1ab859be77a19403d12a916 100644 (file)
@@ -165,15 +165,15 @@ struct ctables_domain
   {
     struct hmap_node node;
 
-    const struct ctables_freq *example;
+    const struct ctables_cell *example;
 
     double valid;
     double missing;
   };
 
-struct ctables_freq
+struct ctables_cell
   {
-    /* In struct ctables's 'ft' hmap.  Indexed by all the values in all the
+    /* In struct ctables's 'cells' hmap.  Indexed by all the values in all the
        axes (except the scalar variable, if any). */
     struct hmap_node node;
 
@@ -293,7 +293,7 @@ struct ctables_table
     struct ctables_axis *axes[PIVOT_N_AXES];
     struct var_array2 vaas[PIVOT_N_AXES];
     enum pivot_axis_type summary_axis;
-    struct hmap ft;
+    struct hmap cells;
     struct hmap domains[N_CTDTS];
 
     enum pivot_axis_type slabels_position;
@@ -388,6 +388,10 @@ struct ctables_cat_value
       };
   };
 
+static const struct ctables_cat_value *ctables_categories_match (
+  const struct ctables_categories *, const union value *,
+  const struct variable *);
+
 static void
 ctables_cat_value_uninit (struct ctables_cat_value *cv)
 {
@@ -1736,7 +1740,7 @@ ctables_summary_add (union ctables_summary *s,
 }
 
 static double
-ctables_summary_value (const struct ctables_freq *f,
+ctables_summary_value (const struct ctables_cell *f,
                        union ctables_summary *s,
                        const struct ctables_summary_spec *ss)
 {
@@ -1878,20 +1882,20 @@ ctables_summary_value (const struct ctables_freq *f,
   NOT_REACHED ();
 }
 
-struct ctables_freq_sort_aux
+struct ctables_cell_sort_aux
   {
     const struct ctables_table *t;
     enum pivot_axis_type a;
   };
 
 static int
-ctables_freq_compare_3way (const void *a_, const void *b_, const void *aux_)
+ctables_cell_compare_3way (const void *a_, const void *b_, const void *aux_)
 {
-  const struct ctables_freq_sort_aux *aux = aux_;
-  struct ctables_freq *const *ap = a_;
-  struct ctables_freq *const *bp = b_;
-  const struct ctables_freq *a = *ap;
-  const struct ctables_freq *b = *bp;
+  const struct ctables_cell_sort_aux *aux = aux_;
+  struct ctables_cell *const *ap = a_;
+  struct ctables_cell *const *bp = b_;
+  const struct ctables_cell *a = *ap;
+  const struct ctables_cell *b = *bp;
 
   size_t a_idx = a->axes[aux->a].vaa_idx;
   size_t b_idx = b->axes[aux->a].vaa_idx;
@@ -1902,11 +1906,51 @@ ctables_freq_compare_3way (const void *a_, const void *b_, const void *aux_)
   for (size_t i = 0; i < va->n; i++)
     if (i != va->scale_idx)
       {
-        int cmp = value_compare_3way (&a->axes[aux->a].values[i],
-                                      &b->axes[aux->a].values[i],
-                                      var_get_width (va->vars[i]));
-        if (cmp)
+        const struct variable *var = va->vars[i];
+        const union value *val_a = &a->axes[aux->a].values[i];
+        const union value *val_b = &b->axes[aux->a].values[i];
+        int cmp = value_compare_3way (val_a, val_b, var_get_width (var));
+        if (!cmp)
+          continue;
+
+        const struct ctables_categories *cats = aux->t->categories[var_get_dict_index (var)];
+        if (!cats)
           return cmp;
+        else if (cats->n_values)
+          {
+            const struct ctables_cat_value *a_cv = ctables_categories_match (cats, val_a, var);
+            const struct ctables_cat_value *b_cv = ctables_categories_match (cats, val_b, var);
+            assert (a_cv && b_cv);
+            return (a_cv == b_cv ? cmp
+                    : a_cv > b_cv ? 1
+                    : -1);
+          }
+        else
+          {
+            switch (cats->key)
+              {
+              case CTCS_VALUE:
+                /* Nothing to do. */
+                break;
+
+              case CTCS_LABEL:
+                {
+                  const char *a_label = var_lookup_value_label (var, val_a);
+                  const char *b_label = var_lookup_value_label (var, val_b);
+                  int label_cmp = (a_label
+                                   ? (b_label ? strcmp (a_label, b_label) : 1)
+                                   : (b_label ? -1 : 0));
+                  if (label_cmp)
+                    cmp = label_cmp;
+                }
+                break;
+
+              case CTCS_FUNCTION:
+                NOT_REACHED ();
+              }
+
+            return cats->sort_ascending ? cmp : -cmp;
+          }
       }
   return 0;
 }
@@ -1930,7 +1974,7 @@ ctables_freq_compare_3way (const void *a_, const void *b_, const void *aux_)
  */
 
 static struct ctables_domain *
-ctables_domain_insert (struct ctables_table *t, struct ctables_freq *f,
+ctables_domain_insert (struct ctables_table *t, struct ctables_cell *f,
                        enum ctables_domain_type domain)
 {
   size_t hash = 0;
@@ -1950,7 +1994,7 @@ ctables_domain_insert (struct ctables_table *t, struct ctables_freq *f,
   struct ctables_domain *d;
   HMAP_FOR_EACH_WITH_HASH (d, struct ctables_domain, node, hash, &t->domains[domain])
     {
-      const struct ctables_freq *df = d->example;
+      const struct ctables_cell *df = d->example;
       for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
         {
           size_t idx = f->axes[a].vaa_idx;
@@ -2022,10 +2066,10 @@ ctables_categories_match (const struct ctables_categories *cats,
 }
 
 static void
-ctables_freqtab_insert (struct ctables_table *t,
-                        const struct ccase *c,
-                        size_t ir, size_t ic, size_t il,
-                        double weight)
+ctables_cell_insert (struct ctables_table *t,
+                     const struct ccase *c,
+                     size_t ir, size_t ic, size_t il,
+                     double weight)
 {
   size_t ix[PIVOT_N_AXES] = {
     [PIVOT_AXIS_ROW] = ir,
@@ -2042,12 +2086,29 @@ ctables_freqtab_insert (struct ctables_table *t,
           if (i == va->scale_idx)
             continue;
 
-          const struct ctables_categories *cats = t->categories[var_get_dict_index (va->vars[i])];
-          if (!cats || !cats->n_values)
-            continue;
+          const struct variable *var = va->vars[i];
+          const union value *value = case_data (c, var);
 
-          if (!ctables_categories_match (cats, case_data (c, va->vars[i]), va->vars[i]))
+          enum mv_class missing = var_is_value_missing (var, value);
+          if (missing == MV_SYSTEM)
             return;
+
+          const struct ctables_categories *cats = t->categories[var_get_dict_index (var)];
+          if (!cats)
+            {
+              if (missing)
+                return;
+            }
+          else if (cats->n_values)
+            {
+              if (!ctables_categories_match (cats, value, var))
+                return;
+            }
+          else
+            {
+              if (missing && !cats->include_missing)
+                return;
+            }
         }
     }
 
@@ -2062,8 +2123,8 @@ ctables_freqtab_insert (struct ctables_table *t,
                              var_get_width (va->vars[i]), hash);
     }
 
-  struct ctables_freq *f;
-  HMAP_FOR_EACH_WITH_HASH (f, struct ctables_freq, node, hash, &t->ft)
+  struct ctables_cell *f;
+  HMAP_FOR_EACH_WITH_HASH (f, struct ctables_cell, node, hash, &t->cells)
     {
       for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
         {
@@ -2100,7 +2161,7 @@ ctables_freqtab_insert (struct ctables_table *t,
     ctables_summary_init (&f->summaries[i], &ss->summaries[i]);
   for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
     f->domains[dt] = ctables_domain_insert (t, f, dt);
-  hmap_insert (&t->ft, &f->node, hash);
+  hmap_insert (&t->cells, &f->node, hash);
 
 summarize:
   for (size_t i = 0; i < ss->n_summaries; i++)
@@ -2223,7 +2284,7 @@ ctables_execute (struct dataset *ds, struct ctables *ct)
           for (size_t ir = 0; ir < t->vaas[PIVOT_AXIS_ROW].n; ir++)
             for (size_t ic = 0; ic < t->vaas[PIVOT_AXIS_COLUMN].n; ic++)
               for (size_t il = 0; il < t->vaas[PIVOT_AXIS_LAYER].n; il++)
-                ctables_freqtab_insert (t, c, ir, ic, il, weight);
+                ctables_cell_insert (t, c, ir, ic, il, weight);
         }
     }
   casereader_destroy (input);
@@ -2261,16 +2322,16 @@ ctables_execute (struct dataset *ds, struct ctables *ct)
 
           assert (t->axes[a]);
 
-          struct ctables_freq **sorted = xnmalloc (t->ft.count, sizeof *sorted);
+          struct ctables_cell **sorted = xnmalloc (t->cells.count, sizeof *sorted);
 
-          struct ctables_freq *f;
+          struct ctables_cell *f;
           size_t n = 0;
-          HMAP_FOR_EACH (f, struct ctables_freq, node, &t->ft)
+          HMAP_FOR_EACH (f, struct ctables_cell, node, &t->cells)
             sorted[n++] = f;
-          assert (n == t->ft.count);
+          assert (n == t->cells.count);
 
-          struct ctables_freq_sort_aux aux = { .t = t, .a = a };
-          sort (sorted, n, sizeof *sorted, ctables_freq_compare_3way, &aux);
+          struct ctables_cell_sort_aux aux = { .t = t, .a = a };
+          sort (sorted, n, sizeof *sorted, ctables_cell_compare_3way, &aux);
 
           size_t max_depth = 0;
           for (size_t j = 0; j < t->vaas[a].n; j++)
@@ -2282,14 +2343,14 @@ ctables_execute (struct dataset *ds, struct ctables *ct)
           int prev_leaf = 0;
           for (size_t j = 0; j < n; j++)
             {
-              struct ctables_freq *f = sorted[j];
+              struct ctables_cell *f = sorted[j];
               const struct var_array *va = &t->vaas[a].vas[f->axes[a].vaa_idx];
 
               size_t n_common = 0;
               bool new_subtable = false;
               if (j > 0)
                 {
-                  struct ctables_freq *prev = sorted[j - 1];
+                  struct ctables_cell *prev = sorted[j - 1];
                   if (prev->axes[a].vaa_idx == f->axes[a].vaa_idx)
                     {
                       for (; n_common < va->n; n_common++)
@@ -2368,8 +2429,8 @@ ctables_execute (struct dataset *ds, struct ctables *ct)
           free (sorted);
           free (groups);
         }
-      struct ctables_freq *f;
-      HMAP_FOR_EACH (f, struct ctables_freq, node, &t->ft)
+      struct ctables_cell *f;
+      HMAP_FOR_EACH (f, struct ctables_cell, node, &t->cells)
         {
           const struct var_array *ss = &t->vaas[t->summary_axis].vas[f->axes[t->summary_axis].vaa_idx];
           for (size_t j = 0; j < ss->n_summaries; j++)
@@ -2396,36 +2457,6 @@ ctables_execute (struct dataset *ds, struct ctables *ct)
       pivot_table_submit (pt);
     }
 
-#if 0
-  for (size_t i = 0; i < ct->n_tables; i++)
-    {
-      struct ctables_table *t = ct->tables[i];
-
-      for (size_t j = 0; j < t->n_fts; j++)
-        {
-          struct ctables_freqtab *ft = t->fts[j];
-          struct ctables_freq *f, *next;
-          HMAP_FOR_EACH_SAFE (f, next, struct ctables_freq, node, &ft->data)
-            {
-              hmap_delete (&ft->data, &f->node);
-              for (size_t k = 0; k < ft->n_summaries; k++)
-                ctables_summary_uninit (&f->summaries[k], &ft->summaries[k]);
-              free (f->summaries);
-              for (size_t k = 0; k < ft->vars.n; k++)
-                {
-                  const struct variable *var = ft->vars.vars[k];
-                  value_destroy (&f->values[k], var_get_width (var));
-                }
-              free (f);
-            }
-          hmap_destroy (&ft->data);
-          var_array_uninit (&ft->vars);
-          free (ft);
-        }
-      free (t->fts);
-    }
-#endif
-  
   return proc_commit (ds);
 }
 
@@ -2643,7 +2674,7 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
 
       struct ctables_table *t = xmalloc (sizeof *t);
       *t = (struct ctables_table) {
-        .ft = HMAP_INITIALIZER (t->ft),
+        .cells = HMAP_INITIALIZER (t->cells),
         .slabels_position = PIVOT_AXIS_COLUMN,
         .slabels_visible = true,
         .row_labels = CTLP_NORMAL,