From abfa0658e514afae881427b737e590f12733fd86 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 24 Feb 2022 12:37:16 -0800 Subject: [PATCH] PCOMPUTE works --- src/language/stats/ctables.c | 91 ++++++++++++++++++--------------- tests/language/stats/ctables.at | 41 ++++++++++++++- 2 files changed, 89 insertions(+), 43 deletions(-) diff --git a/src/language/stats/ctables.c b/src/language/stats/ctables.c index 05fb957439..d605b63ed5 100644 --- a/src/language/stats/ctables.c +++ b/src/language/stats/ctables.c @@ -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); } } diff --git a/tests/language/stats/ctables.at b/tests/language/stats/ctables.at index d6bfc8ac25..210a93e870 100644 --- a/tests/language/stats/ctables.at +++ b/tests/language/stats/ctables.at @@ -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 -- 2.30.2