-static int
-merge_item_compare_3way (const struct merge_item *a, const struct merge_item *b)
-{
- const struct ctables_summary_spec *as = &a->set->specs[a->ofs];
- const struct ctables_summary_spec *bs = &b->set->specs[b->ofs];
- if (as->function != bs->function)
- return as->function > bs->function ? 1 : -1;
- else if (as->weighted != bs->weighted)
- return as->weighted > bs->weighted ? 1 : -1;
- else if (as->calc_area != bs->calc_area)
- return as->calc_area > bs->calc_area ? 1 : -1;
- else if (as->percentile != bs->percentile)
- return as->percentile < bs->percentile ? 1 : -1;
-
- const char *as_label = as->label ? as->label : "";
- const char *bs_label = bs->label ? bs->label : "";
- return strcmp (as_label, bs_label);
-}
-
-static void
-ctables_category_format_number (double number, const struct variable *var,
- struct string *s)
-{
- struct pivot_value *pv = pivot_value_new_var_value (
- var, &(union value) { .f = number });
- pivot_value_format (pv, NULL, s);
- pivot_value_destroy (pv);
-}
-
-static void
-ctables_category_format_string (struct substring string,
- const struct variable *var, struct string *out)
-{
- int width = var_get_width (var);
- char *s = xmalloc (width);
- buf_copy_rpad (s, width, string.string, string.length, ' ');
- struct pivot_value *pv = pivot_value_new_var_value (
- var, &(union value) { .s = CHAR_CAST (uint8_t *, s) });
- pivot_value_format (pv, NULL, out);
- pivot_value_destroy (pv);
- free (s);
-}
-
-static bool
-ctables_category_format_label (const struct ctables_category *cat,
- const struct variable *var,
- struct string *s)
-{
- switch (cat->type)
- {
- case CCT_NUMBER:
- ctables_category_format_number (cat->number, var, s);
- return true;
-
- case CCT_STRING:
- ctables_category_format_string (cat->string, var, s);
- return true;
-
- case CCT_NRANGE:
- ctables_category_format_number (cat->nrange[0], var, s);
- ds_put_format (s, " THRU ");
- ctables_category_format_number (cat->nrange[1], var, s);
- return true;
-
- case CCT_SRANGE:
- ctables_category_format_string (cat->srange[0], var, s);
- ds_put_format (s, " THRU ");
- ctables_category_format_string (cat->srange[1], var, s);
- return true;
-
- case CCT_MISSING:
- ds_put_cstr (s, "MISSING");
- return true;
-
- case CCT_OTHERNM:
- ds_put_cstr (s, "OTHERNM");
- return true;
-
- case CCT_POSTCOMPUTE:
- ds_put_format (s, "&%s", cat->pc->name);
- return true;
-
- case CCT_TOTAL:
- case CCT_SUBTOTAL:
- ds_put_cstr (s, cat->total_label);
- return true;
-
- case CCT_VALUE:
- case CCT_LABEL:
- case CCT_FUNCTION:
- case CCT_EXCLUDED_MISSING:
- return false;
- }
-
- return false;
-}
-
-static struct pivot_value *
-ctables_postcompute_label (const struct ctables_categories *cats,
- const struct ctables_category *cat,
- const struct variable *var)
-{
- struct substring in = ss_cstr (cat->pc->label);
- struct substring target = ss_cstr (")LABEL[");
-
- struct string out = DS_EMPTY_INITIALIZER;
- for (;;)
- {
- size_t chunk = ss_find_substring (in, target);
- if (chunk == SIZE_MAX)
- {
- if (ds_is_empty (&out))
- return pivot_value_new_user_text (in.string, in.length);
- else
- {
- ds_put_substring (&out, in);
- return pivot_value_new_user_text_nocopy (ds_steal_cstr (&out));
- }
- }
-
- ds_put_substring (&out, ss_head (in, chunk));
- ss_advance (&in, chunk + target.length);
-
- struct substring idx_s;
- if (!ss_get_until (&in, ']', &idx_s))
- goto error;
- char *tail;
- long int idx = strtol (idx_s.string, &tail, 10);
- if (idx < 1 || idx > cats->n_cats || tail != ss_end (idx_s))
- goto error;
-
- struct ctables_category *cat2 = &cats->cats[idx - 1];
- if (!ctables_category_format_label (cat2, var, &out))
- goto error;
- }
-
-error:
- ds_destroy (&out);
- return pivot_value_new_user_text (cat->pc->label, SIZE_MAX);
-}
-
-static struct pivot_value *
-ctables_category_create_value_label (const struct ctables_categories *cats,
- const struct ctables_category *cat,
- const struct variable *var,
- const union value *value)
-{
- return (cat->type == CCT_POSTCOMPUTE && cat->pc->label
- ? ctables_postcompute_label (cats, cat, var)
- : cat->type == CCT_TOTAL || cat->type == CCT_SUBTOTAL
- ? pivot_value_new_user_text (cat->total_label, SIZE_MAX)
- : pivot_value_new_var_value (var, value));
-}
-