Move all command implementations into a single 'commands' directory.
[pspp] / src / language / commands / frequencies.c
1 /*
2   PSPP - a program for statistical analysis.
3   Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2014, 2015 Free Software Foundation, Inc.
4
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation, either version 3 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20 #include <stdlib.h>
21 #include <gsl/gsl_histogram.h>
22
23 #include "data/case.h"
24 #include "data/casegrouper.h"
25 #include "data/casereader.h"
26 #include "data/dataset.h"
27 #include "data/dictionary.h"
28 #include "data/format.h"
29 #include "data/missing-values.h"
30 #include "data/settings.h"
31 #include "data/value-labels.h"
32 #include "data/variable.h"
33
34 #include "language/commands/split-file.h"
35
36 #include "language/command.h"
37 #include "language/lexer/lexer.h"
38 #include "language/lexer/variable-parser.h"
39 #include "language/commands/freq.h"
40
41 #include "libpspp/array.h"
42 #include "libpspp/bit-vector.h"
43 #include "libpspp/compiler.h"
44 #include "libpspp/hmap.h"
45 #include "libpspp/message.h"
46 #include "libpspp/misc.h"
47
48 #include "math/histogram.h"
49 #include "math/moments.h"
50 #include "math/chart-geometry.h"
51
52
53 #include "output/charts/barchart.h"
54 #include "output/charts/piechart.h"
55 #include "output/charts/plot-hist.h"
56 #include "output/pivot-table.h"
57
58 #include "gl/minmax.h"
59 #include "gl/xalloc.h"
60
61 #include "gettext.h"
62 #define _(msgid) gettext (msgid)
63 #define N_(msgid) msgid
64
65 /* Percentiles to calculate. */
66
67 struct percentile
68   {
69     double p;        /* The percentile to calculate, between 0 and 1. */
70     bool show;       /* True to show this percentile in the statistics box. */
71   };
72
73 static int
74 percentile_compare_3way (const void *a_, const void *b_)
75 {
76   const struct percentile *a = a_;
77   const struct percentile *b = b_;
78
79   return a->p < b->p ? -1 : a->p > b->p;
80 }
81
82 enum
83   {
84     FRQ_FREQ,
85     FRQ_PERCENT
86   };
87
88 enum sortprops
89   {
90     FRQ_AFREQ,
91     FRQ_DFREQ,
92     FRQ_AVALUE,
93     FRQ_DVALUE
94   };
95
96 #define STATISTICS                                      \
97   S(FRQ_ST_MEAN,       "MEAN",      N_("Mean"))         \
98   S(FRQ_ST_SEMEAN,     "SEMEAN",    N_("S.E. Mean"))    \
99   S(FRQ_ST_MEDIAN,     "MEDIAN",    N_("Median"))       \
100   S(FRQ_ST_MODE,       "MODE",      N_("Mode"))         \
101   S(FRQ_ST_STDDEV,     "STDDEV",    N_("Std Dev"))      \
102   S(FRQ_ST_VARIANCE,   "VARIANCE",  N_("Variance"))     \
103   S(FRQ_ST_KURTOSIS,   "KURTOSIS",  N_("Kurtosis"))     \
104   S(FRQ_ST_SEKURTOSIS, "SEKURTOSIS",N_("S.E. Kurt"))    \
105   S(FRQ_ST_SKEWNESS,   "SKEWNESS",  N_("Skewness"))     \
106   S(FRQ_ST_SESKEWNESS, "SESKEWNESS",N_("S.E. Skew"))    \
107   S(FRQ_ST_RANGE,      "RANGE",     N_("Range"))        \
108   S(FRQ_ST_MINIMUM,    "MINIMUM",   N_("Minimum"))      \
109   S(FRQ_ST_MAXIMUM,    "MAXIMUM",   N_("Maximum"))      \
110   S(FRQ_ST_SUM,        "SUM",       N_("Sum"))
111
112 enum frq_statistic
113   {
114 #define S(ENUM, KEYWORD, NAME) ENUM,
115 STATISTICS
116 #undef S
117   };
118
119 enum {
120 #define S(ENUM, KEYWORD, NAME) +1
121   FRQ_ST_count = STATISTICS,
122 #undef S
123 };
124
125 static const char *st_keywords[FRQ_ST_count] = {
126 #define S(ENUM, KEYWORD, NAME) KEYWORD,
127   STATISTICS
128 #undef S
129 };
130
131 static const char *st_names[FRQ_ST_count] = {
132 #define S(ENUM, KEYWORD, NAME) NAME,
133   STATISTICS
134 #undef S
135 };
136
137 struct freq_tab
138   {
139     struct hmap data;           /* Hash table for accumulating counts. */
140     struct freq *valid;         /* Valid freqs. */
141     size_t n_valid;             /* Number of total freqs. */
142     const struct dictionary *dict; /* Source of entries in the table. */
143
144     struct freq *missing;       /* Missing freqs. */
145     size_t n_missing;           /* Number of missing freqs. */
146
147     /* Statistics. */
148     double total_cases;         /* Sum of weights of all cases. */
149     double valid_cases;         /* Sum of weights of valid cases. */
150   };
151
152 struct frq_chart
153   {
154     double x_min;               /* X axis minimum value. */
155     double x_max;               /* X axis maximum value. */
156     int y_scale;                /* Y axis scale: FRQ_FREQ or FRQ_PERCENT. */
157
158     /* Histograms only. */
159     double y_max;               /* Y axis maximum value. */
160     bool draw_normal;           /* Whether to draw normal curve. */
161
162     /* Pie charts only. */
163     bool include_missing;       /* Whether to include missing values. */
164   };
165
166 /* Per-variable frequency data. */
167 struct var_freqs
168   {
169     const struct variable *var;
170
171     /* Freqency table. */
172     struct freq_tab tab;        /* Frequencies table to use. */
173
174     /* Statistics. */
175     double stat[FRQ_ST_count];
176     double *percentiles;
177
178     /* Variable attributes. */
179     int width;
180   };
181
182 struct frq_proc
183   {
184     struct var_freqs *vars;
185     size_t n_vars;
186
187     /* Percentiles to calculate and possibly display. */
188     struct percentile *percentiles;
189     size_t median_idx;
190     size_t n_percentiles;
191
192     /* Frequency table display. */
193     long int max_categories;         /* Maximum categories to show. */
194     int sort;                   /* FRQ_AVALUE or FRQ_DVALUE
195                                    or FRQ_AFREQ or FRQ_DFREQ. */
196
197     /* Statistics. */
198     unsigned long stats;
199
200     /* Histogram and pie chart settings. */
201     struct frq_chart *hist, *pie, *bar;
202
203     bool warn;
204   };
205
206
207 struct freq_compare_aux
208   {
209     bool by_freq;
210     bool ascending_freq;
211
212     int width;
213     bool ascending_value;
214   };
215
216 static void calc_stats (const struct frq_proc *,
217                         const struct var_freqs *, double d[FRQ_ST_count]);
218
219 static void do_piechart(const struct frq_chart *pie,
220                         const struct variable *var,
221                         const struct freq_tab *frq_tab);
222
223 static void do_barchart(const struct frq_chart *bar,
224                         const struct variable **var,
225                         const struct freq_tab *frq_tab);
226
227 static struct frq_stats_table *frq_stats_table_submit (
228   struct frq_stats_table *, const struct frq_proc *,
229   const struct dictionary *, const struct variable *wv,
230   const struct ccase *example);
231 static void frq_stats_table_destroy (struct frq_stats_table *);
232
233 static int
234 compare_freq (const void *a_, const void *b_, const void *aux_)
235 {
236   const struct freq_compare_aux *aux = aux_;
237   const struct freq *a = a_;
238   const struct freq *b = b_;
239
240   if (aux->by_freq && a->count != b->count)
241     {
242       int cmp = a->count > b->count ? 1 : -1;
243       return aux->ascending_freq ? cmp : -cmp;
244     }
245   else
246     {
247       int cmp = value_compare_3way (a->values, b->values, aux->width);
248       return aux->ascending_value ? cmp : -cmp;
249     }
250 }
251
252 /* Create a gsl_histogram from a freq_tab */
253 static struct histogram *freq_tab_to_hist (const struct frq_proc *,
254                                            const struct var_freqs *);
255
256 static void
257 put_freq_row (struct pivot_table *table, int var_idx,
258               double frequency, double percent,
259               double valid_percent, double cum_percent)
260 {
261   double entries[] = { frequency, percent, valid_percent, cum_percent };
262   for (size_t i = 0; i < sizeof entries / sizeof *entries; i++)
263     if (entries[i] != SYSMIS)
264       pivot_table_put2 (table, i, var_idx,
265                         pivot_value_new_number (entries[i]));
266 }
267
268 /* Displays a full frequency table for variable V. */
269 static void
270 dump_freq_table (const struct var_freqs *vf, const struct variable *wv)
271 {
272   const struct freq_tab *ft = &vf->tab;
273
274   struct pivot_table *table = pivot_table_create__ (pivot_value_new_variable (
275                                                       vf->var), "Frequencies");
276   pivot_table_set_weight_var (table, wv);
277
278   pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Statistics"),
279                           N_("Frequency"), PIVOT_RC_COUNT,
280                           N_("Percent"), PIVOT_RC_PERCENT,
281                           N_("Valid Percent"), PIVOT_RC_PERCENT,
282                           N_("Cumulative Percent"), PIVOT_RC_PERCENT);
283
284   struct pivot_dimension *variable = pivot_dimension_create__ (
285     table, PIVOT_AXIS_ROW, pivot_value_new_variable (vf->var));
286
287   double cum_freq = 0.0;
288   double cum_percent = 0.0;
289   struct pivot_category *valid = NULL;
290   for (const struct freq *f = ft->valid; f < ft->missing; f++)
291     {
292       cum_freq += f->count;
293       double valid_percent = f->count / ft->valid_cases * 100.0;
294       cum_percent += valid_percent;
295
296       if (!valid)
297         valid = pivot_category_create_group (variable->root, N_("Valid"));
298       int var_idx = pivot_category_create_leaf (
299         valid, pivot_value_new_var_value (vf->var, &f->values[0]));
300       put_freq_row (table, var_idx, f->count,
301                     f->count / ft->total_cases * 100.0,
302                     valid_percent, cum_percent);
303     }
304
305   struct pivot_category *missing = NULL;
306   size_t n_categories = ft->n_valid + ft->n_missing;
307   for (const struct freq *f = ft->missing; f < &ft->valid[n_categories]; f++)
308     {
309       cum_freq += f->count;
310
311       if (!missing)
312         missing = pivot_category_create_group (variable->root, N_("Missing"));
313       int var_idx = pivot_category_create_leaf (
314         missing, pivot_value_new_var_value (vf->var, &f->values[0]));
315       put_freq_row (table, var_idx, f->count,
316                     f->count / ft->total_cases * 100.0, SYSMIS, SYSMIS);
317     }
318
319   int var_idx = pivot_category_create_leaf (
320     variable->root, pivot_value_new_text (N_("Total")));
321   put_freq_row (table, var_idx, cum_freq, cum_percent, SYSMIS, SYSMIS);
322
323   pivot_table_submit (table);
324 }
325 \f
326 /* Statistical display. */
327
328 static double
329 calc_percentile (double p, double valid_cases, double x1, double x2)
330 {
331   double s, dummy;
332
333   s = (settings_get_algorithm () != COMPATIBLE
334        ? modf ((valid_cases - 1) * p, &dummy)
335        : modf ((valid_cases + 1) * p - 1, &dummy));
336
337   return x1 + (x2 - x1) * s;
338 }
339
340 /* Calculates all of the percentiles for VF within FRQ. */
341 static void
342 calc_percentiles (const struct frq_proc *frq, struct var_freqs *vf)
343 {
344   if (!frq->n_percentiles)
345     return;
346
347   if (!vf->percentiles)
348     vf->percentiles = xnmalloc (frq->n_percentiles, sizeof *vf->percentiles);
349
350   const struct freq_tab *ft = &vf->tab;
351   const double W = ft->valid_cases;
352   size_t idx = 0;
353
354   double rank = 0;
355   for (const struct freq *f = ft->valid; f < ft->missing; f++)
356     {
357       rank += f->count;
358       for (; idx < frq->n_percentiles; idx++)
359         {
360           struct percentile *pc = &frq->percentiles[idx];
361           double tp;
362
363           tp = (settings_get_algorithm () == ENHANCED
364                 ? (W - 1) * pc->p
365                 : (W + 1) * pc->p - 1);
366
367           if (rank <= tp)
368             break;
369
370           if (tp + 1 < rank || f + 1 >= ft->missing)
371             vf->percentiles[idx] = f->values[0].f;
372           else
373             vf->percentiles[idx] = calc_percentile (pc->p, W, f->values[0].f,
374                                                     f[1].values[0].f);
375         }
376     }
377   for (; idx < frq->n_percentiles; idx++)
378     vf->percentiles[idx] = (ft->n_valid > 0
379                             ? ft->valid[ft->n_valid - 1].values[0].f
380                             : SYSMIS);
381 }
382
383 /* Returns true iff the value in struct freq F is non-missing
384    for variable V. */
385 static bool
386 not_missing (const void *f_, const void *v_)
387 {
388   const struct freq *f = f_;
389   const struct variable *v = v_;
390
391   return !var_is_value_missing (v, f->values);
392 }
393
394 /* Summarizes the frequency table data for variable V. */
395 static void
396 postprocess_freq_tab (const struct frq_proc *frq, struct var_freqs *vf)
397 {
398   struct freq_tab *ft = &vf->tab;
399
400   /* Extract data from hash table. */
401   size_t count = hmap_count (&ft->data);
402   struct freq *freqs = freq_hmap_extract (&ft->data);
403
404   /* Put data into ft. */
405   ft->valid = freqs;
406   ft->n_valid = partition (freqs, count, sizeof *freqs, not_missing, vf->var);
407   ft->missing = freqs + ft->n_valid;
408   ft->n_missing = count - ft->n_valid;
409
410   /* Sort data. */
411   struct freq_compare_aux aux = {
412     .by_freq = frq->sort == FRQ_AFREQ || frq->sort == FRQ_DFREQ,
413     .ascending_freq = frq->sort != FRQ_DFREQ,
414     .width = vf->width,
415     .ascending_value = frq->sort != FRQ_DVALUE,
416   };
417   sort (ft->valid, ft->n_valid, sizeof *ft->valid, compare_freq, &aux);
418   sort (ft->missing, ft->n_missing, sizeof *ft->missing, compare_freq, &aux);
419
420   /* Summary statistics. */
421   ft->valid_cases = 0.0;
422   for (size_t i = 0; i < ft->n_valid; ++i)
423     ft->valid_cases += ft->valid[i].count;
424
425   ft->total_cases = ft->valid_cases;
426   for (size_t i = 0; i < ft->n_missing; ++i)
427     ft->total_cases += ft->missing[i].count;
428 }
429
430 /* Add data from case C to the frequency table. */
431 static void
432 calc (struct frq_proc *frq, const struct ccase *c, const struct dataset *ds)
433 {
434   double weight = dict_get_case_weight (dataset_dict (ds), c, &frq->warn);
435   for (size_t i = 0; i < frq->n_vars; i++)
436     {
437       struct var_freqs *vf = &frq->vars[i];
438       const union value *value = case_data (c, vf->var);
439       size_t hash = value_hash (value, vf->width, 0);
440       struct freq *f;
441
442       f = freq_hmap_search (&vf->tab.data, value, vf->width, hash);
443       if (f == NULL)
444         f = freq_hmap_insert (&vf->tab.data, value, vf->width, hash);
445
446       f->count += weight;
447     }
448 }
449
450 static void
451 output_splits_once (bool *need_splits, const struct dataset *ds,
452                     const struct ccase *c)
453 {
454   if (*need_splits)
455     {
456       output_split_file_values (ds, c);
457       *need_splits = false;
458     }
459 }
460
461 /* Finishes up with the variables after frequencies have been
462    calculated.  Displays statistics, percentiles, ... */
463 static struct frq_stats_table *
464 postcalc (struct frq_proc *frq, const struct dataset *ds,
465           struct ccase *example, struct frq_stats_table *fst)
466 {
467   const struct dictionary *dict = dataset_dict (ds);
468   const struct variable *wv = dict_get_weight (dict);
469
470   for (size_t i = 0; i < frq->n_vars; i++)
471     {
472       struct var_freqs *vf = &frq->vars[i];
473       postprocess_freq_tab (frq, vf);
474       calc_percentiles (frq, vf);
475     }
476
477   enum split_type st = dict_get_split_type (dict);
478   bool need_splits = true;
479   if (frq->stats)
480     {
481       if (st != SPLIT_LAYERED)
482         output_splits_once (&need_splits, ds, example);
483       fst = frq_stats_table_submit (fst, frq, dict, wv, example);
484     }
485
486   for (size_t i = 0; i < frq->n_vars; i++)
487     {
488       struct var_freqs *vf = &frq->vars[i];
489
490       /* Frequencies tables. */
491       if (vf->tab.n_valid + vf->tab.n_missing <= frq->max_categories)
492         {
493           output_splits_once (&need_splits, ds, example);
494           dump_freq_table (vf, wv);
495         }
496
497       if (frq->hist && var_is_numeric (vf->var) && vf->tab.n_valid > 0)
498         {
499           double d[FRQ_ST_count];
500           struct histogram *histogram;
501
502           calc_stats (frq, vf, d);
503
504           histogram = freq_tab_to_hist (frq, vf);
505
506           if (histogram)
507             {
508               output_splits_once (&need_splits, ds, example);
509               chart_submit (histogram_chart_create (
510                               histogram->gsl_hist, var_to_string(vf->var),
511                               vf->tab.valid_cases,
512                               d[FRQ_ST_MEAN],
513                               d[FRQ_ST_STDDEV],
514                               frq->hist->draw_normal));
515
516               statistic_destroy (&histogram->parent);
517             }
518         }
519
520       if (frq->pie)
521         {
522           output_splits_once (&need_splits, ds, example);
523           do_piechart(frq->pie, vf->var, &vf->tab);
524         }
525
526       if (frq->bar)
527         {
528           output_splits_once (&need_splits, ds, example);
529           do_barchart(frq->bar, &vf->var, &vf->tab);
530         }
531
532       free (vf->tab.valid);
533       freq_hmap_destroy (&vf->tab.data, vf->width);
534     }
535
536   return fst;
537 }
538
539 static void
540 frq_run (struct frq_proc *frq, struct dataset *ds)
541 {
542   struct frq_stats_table *fst = NULL;
543   struct casegrouper *grouper = casegrouper_create_splits (proc_open (ds),
544                                                            dataset_dict (ds));
545   struct casereader *group;
546   while (casegrouper_get_next_group (grouper, &group))
547     {
548       for (size_t i = 0; i < frq->n_vars; i++)
549         hmap_init (&frq->vars[i].tab.data);
550
551       struct ccase *example = casereader_peek (group, 0);
552
553       struct ccase *c;
554       for (; (c = casereader_read (group)) != NULL; case_unref (c))
555         calc (frq, c, ds);
556       fst = postcalc (frq, ds, example, fst);
557       casereader_destroy (group);
558
559       case_unref (example);
560     }
561   frq_stats_table_destroy (fst);
562   casegrouper_destroy (grouper);
563   proc_commit (ds);
564 }
565
566 static void
567 add_percentile (struct frq_proc *frq, double p, bool show,
568                 size_t *allocated_percentiles)
569 {
570   if (frq->n_percentiles >= *allocated_percentiles)
571     frq->percentiles = x2nrealloc (frq->percentiles, allocated_percentiles,
572                                    sizeof *frq->percentiles);
573   frq->percentiles[frq->n_percentiles++] = (struct percentile) {
574     .p = p,
575     .show = show,
576   };
577 }
578
579 int
580 cmd_frequencies (struct lexer *lexer, struct dataset *ds)
581 {
582   bool ok = false;
583   const struct variable **vars = NULL;
584
585   size_t allocated_percentiles = 0;
586
587   const unsigned long DEFAULT_STATS = (BIT_INDEX (FRQ_ST_MEAN)
588                                        | BIT_INDEX (FRQ_ST_STDDEV)
589                                        | BIT_INDEX (FRQ_ST_MINIMUM)
590                                        | BIT_INDEX (FRQ_ST_MAXIMUM));
591   struct frq_proc frq = {
592     .sort = FRQ_AVALUE,
593     .stats = DEFAULT_STATS,
594     .max_categories = LONG_MAX,
595     .median_idx = SIZE_MAX,
596     .warn = true,
597   };
598
599   lex_match (lexer, T_SLASH);
600   if (lex_match_id (lexer, "VARIABLES") && !lex_force_match (lexer, T_EQUALS))
601     goto done;
602
603   if (!parse_variables_const (lexer, dataset_dict (ds),
604                               &vars, &frq.n_vars, PV_NO_DUPLICATE))
605     goto done;
606
607   frq.vars = xcalloc (frq.n_vars, sizeof *frq.vars);
608   for (size_t i = 0; i < frq.n_vars; ++i)
609     {
610       frq.vars[i].var = vars[i];
611       frq.vars[i].width = var_get_width (vars[i]);
612     }
613
614   while (lex_token (lexer) != T_ENDCMD)
615     {
616       lex_match (lexer, T_SLASH);
617
618       if (lex_match_id (lexer, "STATISTICS"))
619         {
620           lex_match (lexer, T_EQUALS);
621           frq.stats = 0;
622
623           int ofs = lex_ofs (lexer);
624           while (lex_token (lexer) != T_ENDCMD
625                  && lex_token (lexer) != T_SLASH)
626             {
627               for (int s = 0; s < FRQ_ST_count; s++)
628                 if (lex_match_id (lexer, st_keywords[s]))
629                   {
630                     frq.stats |= 1 << s;
631                     goto next;
632                   }
633
634               if (lex_match_id (lexer, "DEFAULT"))
635                 frq.stats = DEFAULT_STATS;
636               else if (lex_match (lexer, T_ALL))
637                 frq.stats = (1 << FRQ_ST_count) - 1;
638               else if (lex_match_id (lexer, "NONE"))
639                 frq.stats = 0;
640               else
641                 {
642 #define S(ENUM, KEYWORD, NAME) KEYWORD,
643                   lex_error_expecting (lexer,
644                                        STATISTICS
645                                        "DEFAULT", "ALL", "NONE");
646 #undef S
647                   goto done;
648                 }
649
650             next:;
651             }
652
653           if (lex_ofs (lexer) == ofs)
654             frq.stats = DEFAULT_STATS;
655         }
656       else if (lex_match_id (lexer, "PERCENTILES"))
657         {
658           lex_match (lexer, T_EQUALS);
659           while (lex_token (lexer) != T_ENDCMD
660                  && lex_token (lexer) != T_SLASH)
661             {
662               if (!lex_force_num_range_closed (lexer, "PERCENTILES", 0, 100))
663                 goto done;
664               add_percentile (&frq, lex_number (lexer) / 100.0, true,
665                               &allocated_percentiles);
666               lex_get (lexer);
667               lex_match (lexer, T_COMMA);
668             }
669         }
670       else if (lex_match_id (lexer, "FORMAT"))
671         {
672           lex_match (lexer, T_EQUALS);
673           while (lex_token (lexer) != T_ENDCMD
674                  && lex_token (lexer) != T_SLASH)
675             {
676               if (lex_match_id (lexer, "TABLE"))
677                 {
678                 }
679               else if (lex_match_id (lexer, "NOTABLE"))
680                 frq.max_categories = 0;
681               else if (lex_match_id (lexer, "LIMIT"))
682                 {
683                   if (!lex_force_match (lexer, T_LPAREN)
684                       || !lex_force_int_range (lexer, "LIMIT", 0, INT_MAX))
685                     goto done;
686
687                   frq.max_categories = lex_integer (lexer);
688                   lex_get (lexer);
689
690                   if (!lex_force_match (lexer, T_RPAREN))
691                     goto done;
692                 }
693               else if (lex_match_id (lexer, "AVALUE"))
694                 frq.sort = FRQ_AVALUE;
695               else if (lex_match_id (lexer, "DVALUE"))
696                 frq.sort = FRQ_DVALUE;
697               else if (lex_match_id (lexer, "AFREQ"))
698                 frq.sort = FRQ_AFREQ;
699               else if (lex_match_id (lexer, "DFREQ"))
700                 frq.sort = FRQ_DFREQ;
701               else
702                 {
703                   lex_error_expecting (lexer, "TABLE", "NOTABLE",
704                                        "LIMIT", "AVALUE", "DVALUE",
705                                        "AFREQ", "DFREQ");
706                   goto done;
707                 }
708             }
709         }
710       else if (lex_match_id (lexer, "NTILES"))
711         {
712           lex_match (lexer, T_EQUALS);
713
714           if (!lex_force_int_range (lexer, "NTILES", 0, INT_MAX))
715             goto done;
716
717           int n = lex_integer (lexer);
718           lex_get (lexer);
719           for (int i = 0; i < n + 1; ++i)
720             add_percentile (&frq, i / (double) n, true, &allocated_percentiles);
721         }
722       else if (lex_match_id (lexer, "ALGORITHM"))
723         {
724           lex_match (lexer, T_EQUALS);
725
726           if (lex_match_id (lexer, "COMPATIBLE"))
727             settings_set_cmd_algorithm (COMPATIBLE);
728           else if (lex_match_id (lexer, "ENHANCED"))
729             settings_set_cmd_algorithm (ENHANCED);
730           else
731             {
732               lex_error_expecting (lexer, "COMPATIBLE", "ENHANCED");
733               goto done;
734             }
735         }
736       else if (lex_match_id (lexer, "HISTOGRAM"))
737         {
738           double hi_min = -DBL_MAX;
739           double hi_max = DBL_MAX;
740           int hi_scale = FRQ_FREQ;
741           int hi_freq = INT_MIN;
742           int hi_pcnt = INT_MIN;
743           bool hi_draw_normal = false;
744
745           lex_match (lexer, T_EQUALS);
746
747           while (lex_token (lexer) != T_ENDCMD
748                  && lex_token (lexer) != T_SLASH)
749             {
750               if (lex_match_id (lexer, "NORMAL"))
751                 hi_draw_normal = true;
752               else if (lex_match_id (lexer, "NONORMAL"))
753                 hi_draw_normal = false;
754               else if (lex_match_id (lexer, "FREQ"))
755                 {
756                   hi_scale = FRQ_FREQ;
757                   if (lex_match (lexer, T_LPAREN))
758                     {
759                       if (!lex_force_int_range (lexer, "FREQ", 1, INT_MAX))
760                         goto done;
761                       hi_freq = lex_integer (lexer);
762                       lex_get (lexer);
763                       if (!lex_force_match (lexer, T_RPAREN))
764                         goto done;
765                     }
766                 }
767               else if (lex_match_id (lexer, "PERCENT"))
768                 {
769                   hi_scale = FRQ_PERCENT;
770                   if (lex_match (lexer, T_LPAREN))
771                     {
772                       if (!lex_force_int_range (lexer, "PERCENT", 1, INT_MAX))
773                         goto done;
774                       hi_pcnt = lex_integer (lexer);
775                       lex_get (lexer);
776                       if (!lex_force_match (lexer, T_RPAREN))
777                         goto done;
778                     }
779                 }
780               else if (lex_match_id (lexer, "MINIMUM"))
781                 {
782                   if (!lex_force_match (lexer, T_LPAREN)
783                       || !lex_force_num_range_closed (lexer, "MINIMUM",
784                                                       -DBL_MAX, hi_max))
785                     goto done;
786                   hi_min = lex_number (lexer);
787                   lex_get (lexer);
788                   if (!lex_force_match (lexer, T_RPAREN))
789                     goto done;
790                 }
791               else if (lex_match_id (lexer, "MAXIMUM"))
792                 {
793                   if (!lex_force_match (lexer, T_LPAREN)
794                       || !lex_force_num_range_closed (lexer, "MAXIMUM",
795                                                       hi_min, DBL_MAX))
796                     goto done;
797                   hi_max = lex_number (lexer);
798                   lex_get (lexer);
799                   if (!lex_force_match (lexer, T_RPAREN))
800                     goto done;
801                 }
802               else
803                 {
804                   lex_error_expecting (lexer, "NORMAL", "NONORMAL",
805                                        "FREQ", "PERCENT", "MINIMUM", "MAXIMUM");
806                   goto done;
807                 }
808             }
809
810           free (frq.hist);
811           frq.hist = xmalloc (sizeof *frq.hist);
812           *frq.hist = (struct frq_chart) {
813             .x_min = hi_min,
814             .x_max = hi_max,
815             .y_scale = hi_scale,
816             .y_max = hi_scale == FRQ_FREQ ? hi_freq : hi_pcnt,
817             .draw_normal = hi_draw_normal,
818             .include_missing = false,
819           };
820
821           add_percentile (&frq, .25, false, &allocated_percentiles);
822           add_percentile (&frq, .75, false, &allocated_percentiles);
823         }
824       else if (lex_match_id (lexer, "PIECHART"))
825         {
826           double pie_min = -DBL_MAX;
827           double pie_max = DBL_MAX;
828           bool pie_missing = true;
829
830           lex_match (lexer, T_EQUALS);
831           while (lex_token (lexer) != T_ENDCMD
832                  && lex_token (lexer) != T_SLASH)
833             {
834               if (lex_match_id (lexer, "MINIMUM"))
835                 {
836                   if (!lex_force_match (lexer, T_LPAREN)
837                       || !lex_force_num_range_closed (lexer, "MINIMUM",
838                                                       -DBL_MAX, pie_max))
839                     goto done;
840                   pie_min = lex_number (lexer);
841                   lex_get (lexer);
842                   if (!lex_force_match (lexer, T_RPAREN))
843                     goto done;
844                 }
845               else if (lex_match_id (lexer, "MAXIMUM"))
846                 {
847                   if (!lex_force_match (lexer, T_LPAREN)
848                       || !lex_force_num_range_closed (lexer, "MAXIMUM",
849                                                       pie_min, DBL_MAX))
850                     goto done;
851                   pie_max = lex_number (lexer);
852                   lex_get (lexer);
853                   if (!lex_force_match (lexer, T_RPAREN))
854                     goto done;
855                 }
856               else if (lex_match_id (lexer, "MISSING"))
857                 pie_missing = true;
858               else if (lex_match_id (lexer, "NOMISSING"))
859                 pie_missing = false;
860               else
861                 {
862                   lex_error_expecting (lexer, "MINIMUM", "MAXIMUM",
863                                        "MISSING", "NOMISSING");
864                   goto done;
865                 }
866             }
867
868           free (frq.pie);
869           frq.pie = xmalloc (sizeof *frq.pie);
870           *frq.pie = (struct frq_chart) {
871             .x_min = pie_min,
872             .x_max = pie_max,
873             .include_missing = pie_missing,
874           };
875         }
876       else if (lex_match_id (lexer, "BARCHART"))
877         {
878           double bar_min = -DBL_MAX;
879           double bar_max = DBL_MAX;
880           bool bar_freq = true;
881
882           lex_match (lexer, T_EQUALS);
883           while (lex_token (lexer) != T_ENDCMD
884                  && lex_token (lexer) != T_SLASH)
885             {
886               if (lex_match_id (lexer, "MINIMUM"))
887                 {
888                   if (!lex_force_match (lexer, T_LPAREN)
889                       || !lex_force_num_range_closed (lexer, "MINIMUM",
890                                                       -DBL_MAX, bar_max))
891                     goto done;
892                   bar_min = lex_number (lexer);
893                   lex_get (lexer);
894                   if (!lex_force_match (lexer, T_RPAREN))
895                     goto done;
896                 }
897               else if (lex_match_id (lexer, "MAXIMUM"))
898                 {
899                   if (!lex_force_match (lexer, T_LPAREN)
900                       || !lex_force_num_range_closed (lexer, "MAXIMUM",
901                                                       bar_min, DBL_MAX))
902                     goto done;
903                   bar_max = lex_number (lexer);
904                   lex_get (lexer);
905                   if (!lex_force_match (lexer, T_RPAREN))
906                     goto done;
907                 }
908               else if (lex_match_id (lexer, "FREQ"))
909                 {
910                   if (lex_match (lexer, T_LPAREN))
911                     {
912                       if (!lex_force_num_range_open (lexer, "FREQ", 0, DBL_MAX))
913                         goto done;
914                       /* XXX TODO */
915                       lex_get (lexer);
916                       if (!lex_force_match (lexer, T_RPAREN))
917                         goto done;
918                     }
919                   bar_freq = true;
920                 }
921               else if (lex_match_id (lexer, "PERCENT"))
922                 {
923                   if (lex_match (lexer, T_LPAREN))
924                     {
925                       if (!lex_force_num_range_open (lexer, "PERCENT",
926                                                      0, DBL_MAX))
927                         goto done;
928                       /* XXX TODO */
929                       lex_get (lexer);
930                       if (!lex_force_match (lexer, T_RPAREN))
931                         goto done;
932                     }
933                   bar_freq = false;
934                 }
935               else
936                 {
937                   lex_error_expecting (lexer, "MINIMUM", "MAXIMUM",
938                                        "FREQ", "PERCENT");
939                   goto done;
940                 }
941             }
942
943           free (frq.bar);
944           frq.bar = xmalloc (sizeof *frq.bar);
945           *frq.bar = (struct frq_chart) {
946             .x_min = bar_min,
947             .x_max = bar_max,
948             .include_missing = false,
949             .y_scale = bar_freq ? FRQ_FREQ : FRQ_PERCENT,
950           };
951         }
952       else if (lex_match_id (lexer, "MISSING"))
953         {
954           lex_match (lexer, T_EQUALS);
955
956           while (lex_token (lexer) != T_ENDCMD
957                  && lex_token (lexer) != T_SLASH)
958             {
959               if (lex_match_id (lexer, "EXCLUDE"))
960                 {
961                   /* XXX TODO */
962                 }
963               else if (lex_match_id (lexer, "INCLUDE"))
964                 {
965                   /* XXX TODO */
966                 }
967               else
968                 {
969                   lex_error_expecting (lexer, "EXCLUDE", "INCLUDE");
970                   goto done;
971                 }
972             }
973         }
974       else if (lex_match_id (lexer, "ORDER"))
975         {
976           lex_match (lexer, T_EQUALS);
977           /* XXX TODO */
978           if (!lex_match_id (lexer, "ANALYSIS")
979               && !lex_match_id (lexer, "VARIABLE"))
980             {
981               lex_error_expecting (lexer, "ANALYSIS", "VARIABLE");
982               goto done;
983             }
984         }
985       else
986         {
987           lex_error_expecting (lexer, "STATISTICS", "PERCENTILES", "FORMAT",
988                                "NTILES", "ALGORITHM", "HISTOGRAM", "PIECHART",
989                                "BARCHART", "MISSING", "ORDER");
990           goto done;
991         }
992     }
993
994   if (frq.stats & BIT_INDEX (FRQ_ST_MEDIAN))
995     add_percentile (&frq, .5, false, &allocated_percentiles);
996
997   if (frq.n_percentiles > 0)
998     {
999       qsort (frq.percentiles, frq.n_percentiles, sizeof *frq.percentiles,
1000              percentile_compare_3way);
1001
1002       /* Combine equal percentiles. */
1003       size_t o = 1;
1004       for (int i = 1; i < frq.n_percentiles; ++i)
1005         {
1006           struct percentile *prev = &frq.percentiles[o - 1];
1007           struct percentile *this = &frq.percentiles[i];
1008           if (this->p != prev->p)
1009             frq.percentiles[o++] = *this;
1010           else if (this->show)
1011             prev->show = true;
1012         }
1013       frq.n_percentiles = o;
1014
1015       for (size_t i = 0; i < frq.n_percentiles; i++)
1016         if (frq.percentiles[i].p == 0.5)
1017           {
1018             frq.median_idx = i;
1019             break;
1020           }
1021     }
1022
1023   frq_run (&frq, ds);
1024   ok = true;
1025
1026 done:
1027   free (vars);
1028   for (size_t i = 0; i < frq.n_vars; i++)
1029     free (frq.vars[i].percentiles);
1030   free (frq.vars);
1031   free (frq.bar);
1032   free (frq.pie);
1033   free (frq.hist);
1034   free (frq.percentiles);
1035
1036   return ok ? CMD_SUCCESS : CMD_FAILURE;
1037 }
1038
1039 static double
1040 calculate_iqr (const struct frq_proc *frq, const struct var_freqs *vf)
1041 {
1042   double q1 = SYSMIS;
1043   double q3 = SYSMIS;
1044
1045   /* This cannot work unless the 25th and 75th percentile are calculated */
1046   assert (frq->n_percentiles >= 2);
1047   for (int i = 0; i < frq->n_percentiles; i++)
1048     {
1049       struct percentile *pc = &frq->percentiles[i];
1050
1051       if (fabs (0.25 - pc->p) < DBL_EPSILON)
1052         q1 = vf->percentiles[i];
1053       else if (fabs (0.75 - pc->p) < DBL_EPSILON)
1054         q3 = vf->percentiles[i];
1055     }
1056
1057   return q1 == SYSMIS || q3 == SYSMIS ? SYSMIS : q3 - q1;
1058 }
1059
1060 static bool
1061 chart_includes_value (const struct frq_chart *chart,
1062                       const struct variable *var,
1063                       const union value *value)
1064 {
1065   if (!chart->include_missing && var_is_value_missing (var, value))
1066     return false;
1067
1068   if (var_is_numeric (var)
1069       && ((chart->x_min != SYSMIS && value->f < chart->x_min)
1070           || (chart->x_max != SYSMIS && value->f > chart->x_max)))
1071     return false;
1072
1073   return true;
1074 }
1075
1076 /* Create a gsl_histogram from a freq_tab */
1077 static struct histogram *
1078 freq_tab_to_hist (const struct frq_proc *frq, const struct var_freqs *vf)
1079 {
1080   /* Find out the extremes of the x value, within the range to be included in
1081      the histogram, and sum the total frequency of those values. */
1082   double x_min = DBL_MAX;
1083   double x_max = -DBL_MAX;
1084   double valid_freq = 0;
1085   for (size_t i = 0; i < vf->tab.n_valid; i++)
1086     {
1087       const struct freq *f = &vf->tab.valid[i];
1088       if (chart_includes_value (frq->hist, vf->var, f->values))
1089         {
1090           x_min = MIN (x_min, f->values[0].f);
1091           x_max = MAX (x_max, f->values[0].f);
1092           valid_freq += f->count;
1093         }
1094     }
1095
1096   if (valid_freq <= 0)
1097     return NULL;
1098
1099   double iqr = calculate_iqr (frq, vf);
1100
1101   double bin_width =
1102     (iqr > 0
1103      ? 2 * iqr / pow (valid_freq, 1.0 / 3.0)       /* Freedman-Diaconis. */
1104      : (x_max - x_min) / (1 + log2 (valid_freq))); /* Sturges */
1105
1106   struct histogram *histogram = histogram_create (bin_width, x_min, x_max);
1107   if (histogram == NULL)
1108     return NULL;
1109
1110   for (size_t i = 0; i < vf->tab.n_valid; i++)
1111     {
1112       const struct freq *f = &vf->tab.valid[i];
1113       if (chart_includes_value (frq->hist, vf->var, f->values))
1114         histogram_add (histogram, f->values[0].f, f->count);
1115     }
1116
1117   return histogram;
1118 }
1119
1120
1121 /* Allocate an array of struct freqs and fill them from the data in FRQ_TAB,
1122    according to the parameters of CATCHART
1123    N_SLICES will contain the number of slices allocated.
1124    The caller is responsible for freeing slices
1125 */
1126 static struct freq *
1127 pick_cat_counts (const struct frq_chart *catchart,
1128                  const struct freq_tab *frq_tab,
1129                  int *n_slicesp)
1130 {
1131   int n_slices = 0;
1132   struct freq *slices = xnmalloc (frq_tab->n_valid + frq_tab->n_missing, sizeof *slices);
1133
1134   for (size_t i = 0; i < frq_tab->n_valid; i++)
1135     {
1136       struct freq *f = &frq_tab->valid[i];
1137       if (f->count >= catchart->x_min && f->count <= catchart->x_max)
1138         slices[n_slices++] = *f;
1139     }
1140
1141
1142   if (catchart->include_missing)
1143     {
1144       for (size_t i = 0; i < frq_tab->n_missing; i++)
1145         {
1146           const struct freq *f = &frq_tab->missing[i];
1147           slices[n_slices].count += f->count;
1148
1149           if (i == 0)
1150             slices[n_slices].values[0] = f->values[0];
1151         }
1152
1153       if (frq_tab->n_missing > 0)
1154         n_slices++;
1155     }
1156
1157   *n_slicesp = n_slices;
1158   return slices;
1159 }
1160
1161
1162 /* Allocate an array of struct freqs and fill them from the data in FRQ_TAB,
1163    according to the parameters of CATCHART
1164    N_SLICES will contain the number of slices allocated.
1165    The caller is responsible for freeing slices
1166 */
1167 static struct freq **
1168 pick_cat_counts_ptr (const struct frq_chart *catchart,
1169                      const struct freq_tab *frq_tab,
1170                      int *n_slicesp)
1171 {
1172   int n_slices = 0;
1173   struct freq **slices = xnmalloc (frq_tab->n_valid + frq_tab->n_missing, sizeof *slices);
1174
1175   for (size_t i = 0; i < frq_tab->n_valid; i++)
1176     {
1177       struct freq *f = &frq_tab->valid[i];
1178       if (f->count >= catchart->x_min && f->count <= catchart->x_max)
1179         slices[n_slices++] = f;
1180     }
1181
1182   if (catchart->include_missing)
1183     for (size_t i = 0; i < frq_tab->n_missing; i++)
1184       {
1185         const struct freq *f = &frq_tab->missing[i];
1186         if (i == 0)
1187           {
1188             slices[n_slices] = xmalloc (sizeof *slices[n_slices]);
1189             slices[n_slices]->values[0] = f->values[0];
1190           }
1191
1192         slices[n_slices]->count += f->count;
1193       }
1194
1195   *n_slicesp = n_slices;
1196   return slices;
1197 }
1198
1199 static void
1200 do_piechart (const struct frq_chart *pie, const struct variable *var,
1201              const struct freq_tab *frq_tab)
1202 {
1203   int n_slices;
1204   struct freq *slices = pick_cat_counts (pie, frq_tab, &n_slices);
1205
1206   if (n_slices < 2)
1207     msg (SW, _("Omitting pie chart for %s, which has only %d unique values."),
1208          var_get_name (var), n_slices);
1209   else if (n_slices > 50)
1210     msg (SW, _("Omitting pie chart for %s, which has over 50 unique values."),
1211          var_get_name (var));
1212   else
1213     chart_submit (piechart_create (var, slices, n_slices));
1214
1215   free (slices);
1216 }
1217
1218 static void
1219 do_barchart (const struct frq_chart *bar, const struct variable **var,
1220              const struct freq_tab *frq_tab)
1221 {
1222   int n_slices;
1223   struct freq **slices = pick_cat_counts_ptr (bar, frq_tab, &n_slices);
1224
1225   if (n_slices < 1)
1226     msg (SW, _("Omitting bar chart, which has no values."));
1227   else
1228     chart_submit (barchart_create (
1229                     var, 1,
1230                     bar->y_scale == FRQ_FREQ ? _("Count") : _("Percent"),
1231                     bar->y_scale == FRQ_PERCENT,
1232                     slices, n_slices));
1233   free (slices);
1234 }
1235
1236 /* Calculates all the pertinent statistics for VF, putting them in array
1237    D[]. */
1238 static void
1239 calc_stats (const struct frq_proc *frq, const struct var_freqs *vf,
1240             double d[FRQ_ST_count])
1241 {
1242   const struct freq_tab *ft = &vf->tab;
1243
1244   /* Calculate the mode.  If there is more than one mode, we take the
1245      smallest. */
1246   int most_often = -1;
1247   double X_mode = SYSMIS;
1248   for (const struct freq *f = ft->valid; f < ft->missing; f++)
1249     if (most_often < f->count)
1250       {
1251         most_often = f->count;
1252         X_mode = f->values[0].f;
1253       }
1254
1255   /* Calculate moments. */
1256   struct moments *m = moments_create (MOMENT_KURTOSIS);
1257   for (const struct freq *f = ft->valid; f < ft->missing; f++)
1258     moments_pass_one (m, f->values[0].f, f->count);
1259   for (const struct freq *f = ft->valid; f < ft->missing; f++)
1260     moments_pass_two (m, f->values[0].f, f->count);
1261   moments_calculate (m, NULL, &d[FRQ_ST_MEAN], &d[FRQ_ST_VARIANCE],
1262                      &d[FRQ_ST_SKEWNESS], &d[FRQ_ST_KURTOSIS]);
1263   moments_destroy (m);
1264
1265   /* Formulae below are taken from _SPSS Statistical Algorithms_. */
1266   double W = ft->valid_cases;
1267   if (ft->n_valid > 0)
1268     {
1269       d[FRQ_ST_MINIMUM] = ft->valid[0].values[0].f;
1270       d[FRQ_ST_MAXIMUM] = ft->valid[ft->n_valid - 1].values[0].f;
1271       d[FRQ_ST_RANGE] = d[FRQ_ST_MAXIMUM] - d[FRQ_ST_MINIMUM];
1272     }
1273   else
1274     {
1275       d[FRQ_ST_MINIMUM] = SYSMIS;
1276       d[FRQ_ST_MAXIMUM] = SYSMIS;
1277       d[FRQ_ST_RANGE] = SYSMIS;
1278     }
1279   d[FRQ_ST_MODE] = X_mode;
1280   d[FRQ_ST_SUM] = d[FRQ_ST_MEAN] * W;
1281   d[FRQ_ST_STDDEV] = sqrt (d[FRQ_ST_VARIANCE]);
1282   d[FRQ_ST_SEMEAN] = d[FRQ_ST_STDDEV] / sqrt (W);
1283   d[FRQ_ST_SESKEWNESS] = calc_seskew (W);
1284   d[FRQ_ST_SEKURTOSIS] = calc_sekurt (W);
1285   d[FRQ_ST_MEDIAN] = (frq->median_idx != SIZE_MAX
1286                       ? vf->percentiles[frq->median_idx]
1287                       : SYSMIS);
1288 }
1289
1290 static bool
1291 all_string_variables (const struct frq_proc *frq)
1292 {
1293   for (size_t i = 0; i < frq->n_vars; i++)
1294     if (var_is_numeric (frq->vars[i].var))
1295       return false;
1296
1297   return true;
1298 }
1299 \f
1300 struct frq_stats_table
1301   {
1302     struct pivot_table *table;
1303     struct pivot_splits *splits;
1304   };
1305
1306 /* Displays a table of all the statistics requested. */
1307 static struct frq_stats_table *
1308 frq_stats_table_create (const struct frq_proc *frq,
1309                         const struct dictionary *dict,
1310                         const struct variable *wv)
1311 {
1312   if (all_string_variables (frq))
1313     return NULL;
1314
1315   struct pivot_table *table = pivot_table_create (N_("Statistics"));
1316   pivot_table_set_weight_var (table, wv);
1317
1318   struct pivot_dimension *variables
1319     = pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Variables"));
1320   for (size_t i = 0; i < frq->n_vars; i++)
1321     if (!var_is_alpha (frq->vars[i].var))
1322       pivot_category_create_leaf (variables->root,
1323                                   pivot_value_new_variable (frq->vars[i].var));
1324
1325   struct pivot_dimension *statistics = pivot_dimension_create (
1326     table, PIVOT_AXIS_ROW, N_("Statistics"));
1327   struct pivot_category *n = pivot_category_create_group (
1328     statistics->root, N_("N"));
1329   pivot_category_create_leaves (n,
1330                                 N_("Valid"), PIVOT_RC_COUNT,
1331                                 N_("Missing"), PIVOT_RC_COUNT);
1332   for (int i = 0; i < FRQ_ST_count; i++)
1333     if (frq->stats & BIT_INDEX (i))
1334       pivot_category_create_leaf (statistics->root,
1335                                   pivot_value_new_text (st_names[i]));
1336   struct pivot_category *percentiles = NULL;
1337   for (size_t i = 0; i < frq->n_percentiles; i++)
1338     {
1339       const struct percentile *pc = &frq->percentiles[i];
1340
1341       if (!pc->show)
1342         continue;
1343
1344       if (!percentiles)
1345         percentiles = pivot_category_create_group (
1346           statistics->root, N_("Percentiles"));
1347       pivot_category_create_leaf (percentiles, pivot_value_new_integer (
1348                                     pc->p * 100.0));
1349     }
1350
1351   struct pivot_splits *splits = pivot_splits_create (table, PIVOT_AXIS_COLUMN,
1352                                                      dict);
1353
1354   struct frq_stats_table *fst = xmalloc (sizeof *fst);
1355   *fst = (struct frq_stats_table) { .table = table, .splits = splits };
1356   return fst;
1357 }
1358
1359 static struct frq_stats_table *
1360 frq_stats_table_submit (struct frq_stats_table *fst,
1361                         const struct frq_proc *frq,
1362                         const struct dictionary *dict,
1363                         const struct variable *wv,
1364                         const struct ccase *example)
1365 {
1366   if (!fst)
1367     {
1368       fst = frq_stats_table_create (frq, dict, wv);
1369       if (!fst)
1370         return NULL;
1371     }
1372   pivot_splits_new_split (fst->splits, example);
1373
1374   int var_idx = 0;
1375   for (size_t i = 0; i < frq->n_vars; i++)
1376     {
1377       struct var_freqs *vf = &frq->vars[i];
1378       if (var_is_alpha (vf->var))
1379         continue;
1380
1381       const struct freq_tab *ft = &vf->tab;
1382
1383       int row = 0;
1384       pivot_splits_put2 (fst->splits, fst->table, var_idx, row++,
1385                         pivot_value_new_number (ft->valid_cases));
1386       pivot_splits_put2 (fst->splits, fst->table, var_idx, row++,
1387                         pivot_value_new_number (
1388                           ft->total_cases - ft->valid_cases));
1389
1390       double stat_values[FRQ_ST_count];
1391       calc_stats (frq, vf, stat_values);
1392       for (int j = 0; j < FRQ_ST_count; j++)
1393         {
1394           if (!(frq->stats & BIT_INDEX (j)))
1395             continue;
1396
1397           union value v = { .f = vf->tab.n_valid ? stat_values[j] : SYSMIS };
1398           struct pivot_value *pv
1399             = (j == FRQ_ST_MODE || j == FRQ_ST_MINIMUM || j == FRQ_ST_MAXIMUM
1400                ? pivot_value_new_var_value (vf->var, &v)
1401                : pivot_value_new_number (v.f));
1402           pivot_splits_put2 (fst->splits, fst->table, var_idx, row++, pv);
1403         }
1404
1405       for (size_t j = 0; j < frq->n_percentiles; j++)
1406         {
1407           const struct percentile *pc = &frq->percentiles[j];
1408           if (!pc->show)
1409             continue;
1410
1411           union value v = {
1412             .f = vf->tab.n_valid ? vf->percentiles[j] : SYSMIS
1413           };
1414           pivot_splits_put2 (fst->splits, fst->table, var_idx, row++,
1415                              pivot_value_new_var_value (vf->var, &v));
1416         }
1417
1418       var_idx++;
1419     }
1420
1421   if (!fst->splits)
1422     {
1423       frq_stats_table_destroy (fst);
1424       return NULL;
1425     }
1426   return fst;
1427 }
1428
1429 static void
1430 frq_stats_table_destroy (struct frq_stats_table *fst)
1431 {
1432   if (!fst)
1433     return;
1434
1435   pivot_table_submit (fst->table);
1436   pivot_splits_destroy (fst->splits);
1437   free (fst);
1438 }