PCOMPUTE works
authorBen Pfaff <bpfaff@vmware.com>
Thu, 24 Feb 2022 20:37:16 +0000 (12:37 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Sat, 2 Apr 2022 01:48:55 +0000 (18:48 -0700)
src/language/stats/ctables.c
tests/language/stats/ctables.at

index 05fb957439772a87d4ee66f02d7ba86592f49bc9..d605b63ed596702e24057d755753f0bc63ca9e9a 100644 (file)
@@ -1535,7 +1535,7 @@ ctables_recursive_check_postcompute (const struct ctables_pcexpr *e,
             msg_at (SE, pc_cat->location,
                     _("Computed category &%s references a category not included "
                       "in the category list."),
-                    cat->pc->name);
+                    pc_cat->pc->name);
             msg_at (SN, e->location, _("This is the missing category."));
             msg_at (SN, cats_location,
                     _("To fix the problem, add the missing category to the "
@@ -3113,7 +3113,7 @@ ctables_pcexpr_evaluate_nonterminal (
 
 static double
 ctables_pcexpr_evaluate_category (const struct ctables_pcexpr_evaluate_ctx *ctx,
-                                  const struct ctables_category *cat)
+                                  const struct ctables_cell_value *pc_cv)
 {
   const struct ctables_section *s = ctx->section;
 
@@ -3122,16 +3122,11 @@ ctables_pcexpr_evaluate_category (const struct ctables_pcexpr_evaluate_ctx *ctx,
     {
       const struct ctables_nest *nest = s->nests[a];
       for (size_t i = 0; i < nest->n; i++)
-        if (a == ctx->pc_a && i == ctx->pc_a_idx)
-          {
-            /* XXX anything other than just a constant.... need a higher level
-               loop to go through occurrences */
-            hash = hash_pointer (cat, hash);
-            hash = hash_double (cat->number, hash);
-          }
-        else if (i != nest->scale_idx)
+        if (i != nest->scale_idx)
           {
-            const struct ctables_cell_value *cv = &ctx->cell->axes[a].cvs[i];
+            const struct ctables_cell_value *cv
+              = (a == ctx->pc_a && i == ctx->pc_a_idx ? pc_cv
+                 : &ctx->cell->axes[a].cvs[i]);
             hash = hash_pointer (cv->category, hash);
             if (cv->category->type != CCT_TOTAL
                 && cv->category->type != CCT_SUBTOTAL
@@ -3148,29 +3143,21 @@ ctables_pcexpr_evaluate_category (const struct ctables_pcexpr_evaluate_ctx *ctx,
         {
           const struct ctables_nest *nest = s->nests[a];
           for (size_t i = 0; i < nest->n; i++)
-            {
-              const struct ctables_cell_value *p_cv = &ctx->cell->axes[a].cvs[i];
-              const struct ctables_cell_value *t_cv = &tc->axes[a].cvs[i];
-
-              if (i == nest->scale_idx)
-                {
-                  /* Nothing to do. */
-                }
-              else if (a == ctx->pc_a && i == ctx->pc_a_idx)
-                {
-                  /* XXX anything other than a constant.... */
-                  if (t_cv->category != cat || t_cv->value.f != cat->number)
-                    goto not_equal;
-                }
-              else if (p_cv->category != t_cv->category
-                       || (p_cv->category->type != CCT_TOTAL
-                           && p_cv->category->type != CCT_SUBTOTAL
-                           && p_cv->category->type != CCT_POSTCOMPUTE
-                           && !value_equal (&p_cv->value,
-                                            &t_cv->value,
-                                            var_get_width (nest->vars[i]))))
-                goto not_equal;
-            }
+            if (i != nest->scale_idx)
+              {
+                const struct ctables_cell_value *p_cv
+                  = (a == ctx->pc_a && i == ctx->pc_a_idx ? pc_cv
+                     : &ctx->cell->axes[a].cvs[i]);
+                const struct ctables_cell_value *t_cv = &tc->axes[a].cvs[i];
+                if (p_cv->category != t_cv->category
+                    || (p_cv->category->type != CCT_TOTAL
+                        && p_cv->category->type != CCT_SUBTOTAL
+                        && p_cv->category->type != CCT_POSTCOMPUTE
+                        && !value_equal (&p_cv->value,
+                                         &t_cv->value,
+                                         var_get_width (nest->vars[i]))))
+                  goto not_equal;
+              }
         }
 
       goto found;
@@ -3196,19 +3183,40 @@ ctables_pcexpr_evaluate (const struct ctables_pcexpr_evaluate_ctx *ctx,
     case CTPO_CONSTANT:
       return e->number;
 
+    case CTPO_CAT_RANGE:
+      {
+        struct ctables_cell_value cv = {
+          .category = ctables_find_category_for_postcompute (ctx->cats, e)
+        };
+        assert (cv.category != NULL);
+
+        struct hmap *occurrences = &ctx->section->occurrences[ctx->pc_a][ctx->pc_a_idx];
+        const struct ctables_occurrence *o;
+
+        double sum = 0.0;
+        const struct variable *var = ctx->section->nests[ctx->pc_a]->vars[ctx->pc_a_idx];
+        HMAP_FOR_EACH (o, struct ctables_occurrence, node, occurrences)
+          if (ctables_categories_match (ctx->cats, &o->value, var) == cv.category)
+            {
+              cv.value = o->value;
+              sum += ctables_pcexpr_evaluate_category (ctx, &cv);
+            }
+        return sum;
+      }
+
     case CTPO_CAT_NUMBER:
     case CTPO_CAT_STRING:
-    case CTPO_CAT_RANGE:
     case CTPO_CAT_MISSING:
     case CTPO_CAT_OTHERNM:
     case CTPO_CAT_SUBTOTAL:
     case CTPO_CAT_TOTAL:
       {
-        struct ctables_category *cat = ctables_find_category_for_postcompute (
-          ctx->cats, e);
-        assert (cat != NULL);
-
-        return ctables_pcexpr_evaluate_category (ctx, cat);
+        struct ctables_cell_value cv = {
+          .category = ctables_find_category_for_postcompute (ctx->cats, e),
+          .value = { .f = e->number },
+        };
+        assert (cv.category != NULL);
+        return ctables_pcexpr_evaluate_category (ctx, &cv);
       }
 
     case CTPO_ADD:
@@ -3968,8 +3976,7 @@ ctables_section_recurse_add_empty_categories (
           const struct ctables_category *cat = &categories->cats[i];
           if (cat->type == CCT_POSTCOMPUTE)
             {
-              printf ("%s:%d\n", __FILE__, __LINE__);
-              cats[a][a_idx] = cat;
+q              cats[a][a_idx] = cat;
               ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
             }
         }
index d6bfc8ac2581a0b1521db07cbfd54e660dfb2936..210a93e8707adb551fe551b7689438548518b756 100644 (file)
@@ -37,7 +37,11 @@ dnl   * MISSING.
 dnl - VLABELS.
 dnl - SMISSING.
 dnl - Test WEIGHT and adjustment weights.
-dnl - PCOMPUTE and PPROPERTIES.
+dnl - Test PCOMPUTE and PPROPERTIES.
+dnl - PCOMPUTE:
+dnl   * multi-dimensional
+dnl   * MISSING, OTHERNM
+dnl   * strings
 dnl - HIDESMALLCOUNTS.
 dnl - Are string ranges a thing?
 
@@ -805,3 +809,38 @@ AT_CHECK([pspp ctables.sps -O box=unicode -O width=120], [0], [dnl
 ╰─────────────────────────────────────────────────────────┴───────┴───────┴─────────┴───────┴────────┴──────┴──────────╯
 ])
 AT_CLEANUP
+
+AT_SETUP([CTABLES PCOMPUTE])
+AT_CHECK([ln $top_srcdir/examples/nhtsa.sav . || cp $top_srcdir/examples/nhtsa.sav .])
+AT_DATA([ctables.sps],
+[[GET 'nhtsa.sav'.
+CTABLES
+    /PCOMPUTE &x=EXPR([3] + [4])
+    /PCOMPUTE &y=EXPR([4] + [5])
+    /PPROPERTIES &x LABEL='3+4' HIDESOURCECATS=YES
+    /PPROPERTIES &y LABEL='4+5'
+    /TABLE=qn105ba BY qns1
+    /CATEGORIES VARIABLES=qns1 [1, 2, SUBTOTAL, 3, 4, 5, &x, &y, SUBTOTAL]
+]])
+AT_CHECK([pspp ctables.sps -O box=unicode -O width=120], [0], [dnl
+                                                      Custom Tables
+╭─────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────╮
+│                                                         │ S1. Including yourself, how many members of this household │
+│                                                         │                    are age 16 or older?                    │
+│                                                         ├───────┬───────┬─────────┬───────┬────────┬──────┬──────────┤
+│                                                         │   1   │   2   │ Subtotal│   5   │   3+4  │  4+5 │ Subtotal │
+│                                                         ├───────┼───────┼─────────┼───────┼────────┼──────┼──────────┤
+│                                                         │ Count │ Count │  Count  │ Count │  Count │ Count│   Count  │
+├─────────────────────────────────────────────────────────┼───────┼───────┼─────────┼───────┼────────┼──────┼──────────┤
+│105b. How likely is it that drivers who have  Almost     │    147│    246│      393│     11│      81│    30│        92│
+│had too much to drink to drive safely will A. certain    │       │       │         │       │        │      │          │
+│Get stopped by the police?                    Very likely│    384│    552│      936│     14│     171│    65│       185│
+│                                              Somewhat   │    590│   1249│     1839│     20│     265│    92│       285│
+│                                              likely     │       │       │         │       │        │      │          │
+│                                              Somewhat   │    278│    647│      925│      6│     116│    38│       122│
+│                                              unlikely   │       │       │         │       │        │      │          │
+│                                              Very       │    141│    290│      431│      4│      59│    22│        63│
+│                                              unlikely   │       │       │         │       │        │      │          │
+╰─────────────────────────────────────────────────────────┴───────┴───────┴─────────┴───────┴────────┴──────┴──────────╯
+])
+AT_CLEANUP