struct variable *var;
};
+static void ctables_summary_spec_set_clone (struct ctables_summary_spec_set *,
+ const struct ctables_summary_spec_set *);
static void ctables_summary_spec_set_uninit (struct ctables_summary_spec_set *);
/* A nested sequence of variables, e.g. a > b > c. */
double percentile; /* CTSF_PTILE only. */
char *label;
struct fmt_spec format; /* XXX extra CTABLES formats */
+ size_t axis_idx;
};
+static void
+ctables_summary_spec_clone (struct ctables_summary_spec *dst,
+ const struct ctables_summary_spec *src)
+{
+ *dst = *src;
+ dst->label = xstrdup (src->label);
+}
+
static void
ctables_summary_spec_uninit (struct ctables_summary_spec *s)
{
free (s->label);
}
+static void
+ctables_summary_spec_set_clone (struct ctables_summary_spec_set *dst,
+ const struct ctables_summary_spec_set *src)
+{
+ struct ctables_summary_spec *specs = xnmalloc (src->n, sizeof *specs);
+ for (size_t i = 0; i < src->n; i++)
+ ctables_summary_spec_clone (&specs[i], &src->specs[i]);
+
+ *dst = (struct ctables_summary_spec_set) {
+ .specs = specs,
+ .n = src->n,
+ .allocated = src->n,
+ .var = src->var
+ };
+}
+
static void
ctables_summary_spec_set_uninit (struct ctables_summary_spec_set *set)
{
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)
{
if (!sub || !lex_match (ctx->lexer, T_LBRACK))
return sub;
- bool totals = false;
+ enum ctables_summary_variant sv = CSV_CELL;
for (;;)
{
int start_ofs = lex_ofs (ctx->lexer);
/* 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);
- add_summary_spec (sub, function, percentile, label, formatp, loc,
- totals);
+ 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);
- if (lex_match (ctx->lexer, T_COMMA))
+ lex_match (ctx->lexer, T_COMMA);
+ if (sv == CSV_CELL && lex_match_id (ctx->lexer, "TOTALS"))
{
- if (!totals && lex_match_id (ctx->lexer, "TOTALS"))
- {
- if (!lex_force_match (ctx->lexer, T_LBRACK))
- goto error;
- totals = true;
- }
+ if (!lex_force_match (ctx->lexer, T_LBRACK))
+ goto error;
+ sv = CSV_TOTAL;
}
- else if (lex_force_match (ctx->lexer, T_RBRACK))
+ else if (lex_match (ctx->lexer, T_RBRACK))
{
- if (totals && !lex_force_match (ctx->lexer, T_RBRACK))
+ if (sv == CSV_TOTAL && !lex_force_match (ctx->lexer, T_RBRACK))
goto error;
return sub;
}
- else
- goto error;
}
error:
summary_src = a;
else
NOT_REACHED ();
- stack.nests[stack.n++] = (struct ctables_nest) {
+
+ struct ctables_nest *new = &stack.nests[stack.n++];
+ *new = (struct ctables_nest) {
.vars = vars,
.scale_idx = (a->scale_idx != SIZE_MAX ? a->scale_idx
: b->scale_idx != SIZE_MAX ? a->n + b->scale_idx
: SIZE_MAX),
.n = n,
- .specs = {
- [CSV_CELL] = summary_src->specs[CSV_CELL],
- [CSV_TOTAL] = summary_src->specs[CSV_TOTAL],
- },
};
+ for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
+ ctables_summary_spec_set_clone (&new->specs[sv], &summary_src->specs[sv]);
}
ctables_stack_uninit (&s0);
ctables_stack_uninit (&s1);
.scale_idx = a->scale ? 0 : SIZE_MAX,
};
if (a->specs[CSV_CELL].n || a->scale)
- for (size_t i = 0; i < N_CSVS; i++)
+ for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
{
- nest->specs[i] = a->specs[i];
- nest->specs[i].var = a->var.var;
+ ctables_summary_spec_set_clone (&nest->specs[sv], &a->specs[sv]);
+ nest->specs[sv].var = a->var.var;
}
return (struct ctables_stack) { .nests = nest, .n = 1 };
}
}
+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 *sss = &nest->specs[CSV_CELL];
- sss->specs = xmalloc (sizeof *sss->specs);
- sss->n = 1;
+ struct ctables_summary_spec_set *specs = &nest->specs[CSV_CELL];
+ specs->specs = xmalloc (sizeof *specs->specs);
+ specs->n = 1;
enum ctables_summary_function function
- = sss->var ? CTSF_MEAN : CTSF_COUNT;
- struct ctables_var var = { .is_mrset = false, .var = sss->var };
+ = specs->var ? CTSF_MEAN : CTSF_COUNT;
+ struct ctables_var var = { .is_mrset = false, .var = specs->var };
- *sss->specs = (struct ctables_summary_spec) {
+ *specs->specs = (struct ctables_summary_spec) {
.function = function,
.format = ctables_summary_default_format (function, &var),
.label = ctables_summary_default_label (function, 0),
};
- if (!sss->var)
- sss->var = nest->vars[0];
+ if (!specs->var)
+ specs->var = nest->vars[0];
- nest->specs[CSV_TOTAL] = nest->specs[CSV_CELL];
+ ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
+ &nest->specs[CSV_CELL]);
}
else if (!nest->specs[CSV_TOTAL].n)
- nest->specs[CSV_TOTAL] = 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),