double percentile; /* CTSF_PTILE only. */
char *label;
struct fmt_spec format; /* XXX extra CTABLES formats */
+ size_t axis_idx;
};
static void
return axis;
}
+static bool
+has_digit (const char *s)
+{
+ return s[strcspn (s, "0123456789")] != '\0';
+}
+
static struct ctables_axis *
ctables_axis_parse_postfix (struct ctables_axis_parse_ctx *ctx)
{
/* Parse format. */
struct fmt_spec format;
const struct fmt_spec *formatp;
- if (lex_token (ctx->lexer) == T_ID)
+ if (lex_token (ctx->lexer) == T_ID
+ && has_digit (lex_tokcstr (ctx->lexer)))
{
if (!parse_format_specifier (ctx->lexer, &format)
|| !fmt_check_output (&format)
struct msg_location *loc = lex_ofs_location (ctx->lexer, start_ofs,
lex_ofs (ctx->lexer) - 1);
+ printf ("add %s\n", ctables_summary_function_name (function));
add_summary_spec (sub, function, percentile, label, formatp, loc, sv);
free (label);
msg_location_destroy (loc);
goto error;
sv = CSV_TOTAL;
}
- else if (lex_force_match (ctx->lexer, T_RBRACK))
+ else if (lex_match (ctx->lexer, T_RBRACK))
{
if (sv == CSV_TOTAL && !lex_force_match (ctx->lexer, T_RBRACK))
goto error;
return sub;
}
- else
- goto error;
}
error:
}
}
+struct merge_item
+ {
+ size_t tiebreaker;
+ const struct ctables_summary_spec_set *set;
+ size_t ofs;
+ };
+
+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->percentile != bs->percentile)
+ return as->percentile < bs->percentile ? 1 : -1;
+ return strcmp (as->label, bs->label);
+}
+
static bool
ctables_execute (struct dataset *ds, struct ctables *ct)
{
t->stacks[a] = (struct ctables_stack) { .nests = nest, .n = 1 };
}
- for (size_t i = 0; i < t->stacks[t->summary_axis].n; i++)
+ struct ctables_stack *stack = &t->stacks[t->summary_axis];
+ for (size_t i = 0; i < stack->n; i++)
{
- struct ctables_nest *nest = &t->stacks[t->summary_axis].nests[i];
+ struct ctables_nest *nest = &stack->nests[i];
if (!nest->specs[CSV_CELL].n)
{
struct ctables_summary_spec_set *specs = &nest->specs[CSV_CELL];
ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
&nest->specs[CSV_CELL]);
}
+
+ struct ctables_summary_spec_set merged = { .n = 0 };
+ struct merge_item *items = xnmalloc (2 * stack->n, sizeof *items);
+ size_t n_left = 0;
+ for (size_t j = 0; j < stack->n; j++)
+ {
+ const struct ctables_nest *nest = &stack->nests[j];
+ if (!nest->n)
+ continue;
+
+ for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
+ {
+ items[n_left] = (struct merge_item) {
+ .tiebreaker = n_left,
+ .set = &nest->specs[sv]
+ };
+ n_left++;
+ }
+ }
+
+ while (n_left > 0)
+ {
+ struct merge_item min = items[0];
+ for (size_t j = 1; j < n_left; j++)
+ if (merge_item_compare_3way (&items[j], &min) < 0)
+ min = items[j];
+
+ /* XXX Add to 'merged' */
+ if (merged.n >= merged.allocated)
+ merged.specs = x2nrealloc (merged.specs, &merged.allocated,
+ sizeof *merged.specs);
+ merged.specs[merged.n++] = min.set->specs[min.ofs];
+
+ for (size_t j = 0; j < n_left; )
+ {
+ if (merge_item_compare_3way (&items[j], &min) == 0)
+ {
+ struct merge_item *item = &items[j];
+ item->set->specs[item->ofs].axis_idx = merged.n - 1;
+ if (++item->ofs >= item->set->n)
+ {
+ items[j] = items[--n_left];
+ continue;
+ }
+ }
+ j++;
+ }
+ }
+
+ for (size_t j = 0; j < merged.n; j++)
+ printf ("%s\n", ctables_summary_function_name (merged.specs[j].function));
}
struct casereader *input = casereader_create_filter_weight (proc_open (ds),