+ /* Grand total. */
+ x->total = 0.0;
+ for (col = 0; col < x->n_cols; col++)
+ x->total += x->col_tot[col];
+}
+
+static struct tab_table *
+create_crosstab_table (struct crosstabs_proc *proc, struct pivot_table *pt)
+{
+ struct tuple
+ {
+ int value;
+ const char *name;
+ };
+ static const struct tuple names[] =
+ {
+ {CRS_CL_COUNT, N_("count")},
+ {CRS_CL_ROW, N_("row %")},
+ {CRS_CL_COLUMN, N_("column %")},
+ {CRS_CL_TOTAL, N_("total %")},
+ {CRS_CL_EXPECTED, N_("expected")},
+ {CRS_CL_RESIDUAL, N_("residual")},
+ {CRS_CL_SRESIDUAL, N_("std. resid.")},
+ {CRS_CL_ASRESIDUAL, N_("adj. resid.")},
+ };
+ const int n_names = sizeof names / sizeof *names;
+ const struct tuple *t;
+
+ struct tab_table *table;
+ struct string title;
+ int i;
+
+ table = tab_create (pt->n_consts + 1 + pt->n_cols + 1,
+ (pt->n_entries / pt->n_cols) * 3 / 2 * proc->n_cells + 10,
+ true);
+ tab_headers (table, pt->n_consts + 1, 0, 2, 0);
+
+ /* First header line. */
+ tab_joint_text (table, pt->n_consts + 1, 0,
+ (pt->n_consts + 1) + (pt->n_cols - 1), 0,
+ TAB_CENTER | TAT_TITLE, var_get_name (pt->vars[COL_VAR]));
+
+ tab_hline (table, TAL_1, pt->n_consts + 1,
+ pt->n_consts + 2 + pt->n_cols - 2, 1);
+
+ /* Second header line. */
+ for (i = 2; i < pt->n_consts + 2; i++)
+ tab_joint_text (table, pt->n_consts + 2 - i - 1, 0,
+ pt->n_consts + 2 - i - 1, 1,
+ TAB_RIGHT | TAT_TITLE, var_to_string (pt->vars[i]));
+ tab_text (table, pt->n_consts + 2 - 2, 1, TAB_RIGHT | TAT_TITLE,
+ var_get_name (pt->vars[ROW_VAR]));
+ for (i = 0; i < pt->n_cols; i++)
+ table_value_missing (proc, table, pt->n_consts + 2 + i - 1, 1, TAB_RIGHT,
+ &pt->cols[i], pt->vars[COL_VAR]);
+ tab_text (table, pt->n_consts + 2 + pt->n_cols - 1, 1, TAB_CENTER, _("Total"));
+
+ tab_hline (table, TAL_1, 0, pt->n_consts + 2 + pt->n_cols - 1, 2);
+ tab_vline (table, TAL_1, pt->n_consts + 2 + pt->n_cols - 1, 0, 1);
+
+ /* Title. */
+ ds_init_empty (&title);
+ for (i = 0; i < pt->n_consts + 2; i++)
+ {
+ if (i)
+ ds_put_cstr (&title, " * ");
+ ds_put_cstr (&title, var_get_name (pt->vars[i]));
+ }
+ for (i = 0; i < pt->n_consts; i++)
+ {
+ const struct variable *var = pt->const_vars[i];
+ size_t ofs;
+
+ ds_put_format (&title, ", %s=", var_get_name (var));
+
+ /* Insert the formatted value of the variable, then trim
+ leading spaces in what was just inserted. */
+ ofs = ds_length (&title);
+ data_out (&pt->const_values[i], var_get_print_format (var),
+ ds_put_uninit (&title, var_get_width (var)));
+ ds_remove (&title, ofs, ss_cspan (ds_substr (&title, ofs, SIZE_MAX),
+ ss_cstr (" ")));
+ }
+
+ ds_put_cstr (&title, " [");
+ i = 0;
+ for (t = names; t < &names[n_names]; t++)
+ if (proc->cells & (1u << t->value))
+ {
+ if (i++)
+ ds_put_cstr (&title, ", ");
+ ds_put_cstr (&title, gettext (t->name));
+ }
+ ds_put_cstr (&title, "].");
+
+ tab_title (table, "%s", ds_cstr (&title));
+ ds_destroy (&title);
+
+ tab_offset (table, 0, 2);
+ return table;
+}
+
+static struct tab_table *
+create_chisq_table (struct pivot_table *pt)
+{
+ struct tab_table *chisq;
+
+ chisq = tab_create (6 + (pt->n_vars - 2),
+ pt->n_entries / pt->n_cols * 3 / 2 * N_CHISQ + 10,
+ 1);
+ tab_headers (chisq, 1 + (pt->n_vars - 2), 0, 1, 0);
+
+ tab_title (chisq, _("Chi-square tests."));
+
+ tab_offset (chisq, pt->n_vars - 2, 0);
+ tab_text (chisq, 0, 0, TAB_LEFT | TAT_TITLE, _("Statistic"));
+ tab_text (chisq, 1, 0, TAB_RIGHT | TAT_TITLE, _("Value"));
+ tab_text (chisq, 2, 0, TAB_RIGHT | TAT_TITLE, _("df"));
+ tab_text (chisq, 3, 0, TAB_RIGHT | TAT_TITLE,
+ _("Asymp. Sig. (2-sided)"));
+ tab_text (chisq, 4, 0, TAB_RIGHT | TAT_TITLE,
+ _("Exact. Sig. (2-sided)"));
+ tab_text (chisq, 5, 0, TAB_RIGHT | TAT_TITLE,
+ _("Exact. Sig. (1-sided)"));
+ tab_offset (chisq, 0, 1);
+
+ return chisq;
+}
+
+/* Symmetric measures. */
+static struct tab_table *
+create_sym_table (struct pivot_table *pt)
+{
+ struct tab_table *sym;
+
+ sym = tab_create (6 + (pt->n_vars - 2),
+ pt->n_entries / pt->n_cols * 7 + 10, 1);
+ tab_headers (sym, 2 + (pt->n_vars - 2), 0, 1, 0);
+ tab_title (sym, _("Symmetric measures."));
+
+ tab_offset (sym, pt->n_vars - 2, 0);
+ tab_text (sym, 0, 0, TAB_LEFT | TAT_TITLE, _("Category"));
+ tab_text (sym, 1, 0, TAB_LEFT | TAT_TITLE, _("Statistic"));
+ tab_text (sym, 2, 0, TAB_RIGHT | TAT_TITLE, _("Value"));
+ tab_text (sym, 3, 0, TAB_RIGHT | TAT_TITLE, _("Asymp. Std. Error"));
+ tab_text (sym, 4, 0, TAB_RIGHT | TAT_TITLE, _("Approx. T"));
+ tab_text (sym, 5, 0, TAB_RIGHT | TAT_TITLE, _("Approx. Sig."));
+ tab_offset (sym, 0, 1);
+
+ return sym;
+}
+
+/* Risk estimate. */
+static struct tab_table *
+create_risk_table (struct pivot_table *pt)
+{
+ struct tab_table *risk;
+
+ risk = tab_create (4 + (pt->n_vars - 2), pt->n_entries / pt->n_cols * 4 + 10,
+ 1);
+ tab_headers (risk, 1 + pt->n_vars - 2, 0, 2, 0);
+ tab_title (risk, _("Risk estimate."));
+
+ tab_offset (risk, pt->n_vars - 2, 0);
+ tab_joint_text_format (risk, 2, 0, 3, 0, TAB_CENTER | TAT_TITLE,
+ _("95%% Confidence Interval"));
+ tab_text (risk, 0, 1, TAB_LEFT | TAT_TITLE, _("Statistic"));
+ tab_text (risk, 1, 1, TAB_RIGHT | TAT_TITLE, _("Value"));
+ tab_text (risk, 2, 1, TAB_RIGHT | TAT_TITLE, _("Lower"));
+ tab_text (risk, 3, 1, TAB_RIGHT | TAT_TITLE, _("Upper"));
+ tab_hline (risk, TAL_1, 2, 3, 1);
+ tab_vline (risk, TAL_1, 2, 0, 1);
+ tab_offset (risk, 0, 2);
+
+ return risk;
+}
+
+/* Directional measures. */
+static struct tab_table *
+create_direct_table (struct pivot_table *pt)
+{
+ struct tab_table *direct;
+
+ direct = tab_create (7 + (pt->n_vars - 2),
+ pt->n_entries / pt->n_cols * 7 + 10, 1);
+ tab_headers (direct, 3 + (pt->n_vars - 2), 0, 1, 0);
+ tab_title (direct, _("Directional measures."));
+
+ tab_offset (direct, pt->n_vars - 2, 0);
+ tab_text (direct, 0, 0, TAB_LEFT | TAT_TITLE, _("Category"));
+ tab_text (direct, 1, 0, TAB_LEFT | TAT_TITLE, _("Statistic"));
+ tab_text (direct, 2, 0, TAB_LEFT | TAT_TITLE, _("Type"));
+ tab_text (direct, 3, 0, TAB_RIGHT | TAT_TITLE, _("Value"));
+ tab_text (direct, 4, 0, TAB_RIGHT | TAT_TITLE, _("Asymp. Std. Error"));
+ tab_text (direct, 5, 0, TAB_RIGHT | TAT_TITLE, _("Approx. T"));
+ tab_text (direct, 6, 0, TAB_RIGHT | TAT_TITLE, _("Approx. Sig."));
+ tab_offset (direct, 0, 1);
+
+ return direct;
+}
+
+
+/* Delete missing rows and columns for statistical analysis when
+ /MISSING=REPORT. */
+static void
+delete_missing (struct pivot_table *pt)
+{
+ int r, c;
+
+ for (r = 0; r < pt->n_rows; r++)
+ if (var_is_num_missing (pt->vars[ROW_VAR], pt->rows[r].f, MV_USER))
+ {
+ for (c = 0; c < pt->n_cols; c++)
+ pt->mat[c + r * pt->n_cols] = 0.;
+ pt->ns_rows--;
+ }
+
+
+ for (c = 0; c < pt->n_cols; c++)
+ if (var_is_num_missing (pt->vars[COL_VAR], pt->cols[c].f, MV_USER))
+ {
+ for (r = 0; r < pt->n_rows; r++)
+ pt->mat[c + r * pt->n_cols] = 0.;
+ pt->ns_cols--;
+ }