Sorting categories by explicit values.
[pspp] / src / language / stats / ctables.c
index 3954637f33b70327f7c751c59b1c1b4515d0949f..8277a702b28a165150f1191c0795aa763b1b761c 100644 (file)
@@ -314,7 +314,6 @@ struct ctables_table
 
     struct ctables_chisq *chisq;
     struct ctables_pairwise *pairwise;
-
   };
 
 struct ctables_var
@@ -389,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)
 {
@@ -1903,11 +1906,25 @@ 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)
-          return 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 && 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);
+          }
+
+        return cmp;
       }
   return 0;
 }
@@ -1979,6 +1996,49 @@ ctables_domain_insert (struct ctables_table *t, struct ctables_freq *f,
   return d;
 }
 
+static const struct ctables_cat_value *
+ctables_categories_match (const struct ctables_categories *cats,
+                          const union value *v, const struct variable *var)
+{
+  const struct ctables_cat_value *othernm = NULL;
+  for (size_t i = cats->n_values; i-- > 0; )
+    {
+      const struct ctables_cat_value *cv = &cats->values[i];
+      switch (cv->type)
+        {
+        case CCVT_NUMBER:
+          if (cv->number == v->f)
+            return cv;
+          break;
+
+        case CCVT_STRING:
+          NOT_REACHED ();
+
+        case CCVT_RANGE:
+          if ((cv->range[0] == -DBL_MAX || v->f >= cv->range[0])
+              && (cv->range[1] == DBL_MAX || v->f <= cv->range[1]))
+            return cv;
+          break;
+
+        case CCVT_MISSING:
+          if (var_is_value_missing (var, v))
+            return cv;
+          break;
+
+        case CCVT_OTHERNM:
+          if (!othernm)
+            othernm = cv;
+          break;
+
+        case CCVT_SUBTOTAL:
+        case CCVT_HSUBTOTAL:
+          break;
+        }
+    }
+
+  return var_is_value_missing (var, v) ? NULL : othernm;
+}
+
 static void
 ctables_freqtab_insert (struct ctables_table *t,
                         const struct ccase *c,
@@ -1992,6 +2052,23 @@ ctables_freqtab_insert (struct ctables_table *t,
   };
   const struct var_array *ss = &t->vaas[t->summary_axis].vas[ix[t->summary_axis]];
 
+  for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
+    {
+      const struct var_array *va = &t->vaas[a].vas[ix[a]];
+      for (size_t i = 0; i < va->n; i++)
+        {
+          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;
+
+          if (!ctables_categories_match (cats, case_data (c, va->vars[i]), va->vars[i]))
+            return;
+        }
+    }
+
   size_t hash = 0;
   for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
     {