EXAMINE: Extend test for empty dataset and fix bug.
[pspp] / src / language / stats / examine.c
1 /*
2   PSPP - a program for statistical analysis.
3   Copyright (C) 2012, 2013  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
21 #include <math.h>
22 #include <gsl/gsl_cdf.h>
23
24 #include "libpspp/assertion.h"
25 #include "libpspp/message.h"
26 #include "libpspp/pool.h"
27
28
29 #include "data/dataset.h"
30 #include "data/dictionary.h"
31 #include "data/casegrouper.h"
32 #include "data/casereader.h"
33 #include "data/casewriter.h"
34 #include "data/caseproto.h"
35 #include "data/subcase.h"
36
37
38 #include "data/format.h"
39
40 #include "math/interaction.h"
41 #include "math/box-whisker.h"
42 #include "math/categoricals.h"
43 #include "math/chart-geometry.h"
44 #include "math/histogram.h"
45 #include "math/moments.h"
46 #include "math/np.h"
47 #include "math/sort.h"
48 #include "math/order-stats.h"
49 #include "math/percentiles.h"
50 #include "math/tukey-hinges.h"
51 #include "math/trimmed-mean.h"
52
53 #include "output/charts/boxplot.h"
54 #include "output/charts/np-plot.h"
55 #include "output/charts/spreadlevel-plot.h"
56 #include "output/charts/plot-hist.h"
57
58 #include "language/command.h"
59 #include "language/lexer/lexer.h"
60 #include "language/lexer/value-parser.h"
61 #include "language/lexer/variable-parser.h"
62
63 #include "output/tab.h"
64
65 #include "gettext.h"
66 #define _(msgid) gettext (msgid)
67 #define N_(msgid) msgid
68
69 static void 
70 append_value_name (const struct variable *var, const union value *val, struct string *str)
71 {
72   var_append_value_name (var, val, str);
73   if ( var_is_value_missing (var, val, MV_ANY))
74     ds_put_cstr (str, _(" (missing)"));
75 }
76
77 enum bp_mode
78   {
79     BP_GROUPS,
80     BP_VARIABLES
81   };
82
83
84 /* Indices for the ex_proto member (below) */
85 enum
86   {
87     EX_VAL,  /* value */
88     EX_ID,   /* identity */
89     EX_WT    /* weight */
90   };
91
92
93 struct examine
94 {
95   struct pool *pool;
96
97   /* A caseproto used to contain the data subsets under examination,
98      see (enum above)   */
99   struct caseproto *ex_proto;
100
101   size_t n_dep_vars;
102   const struct variable **dep_vars;
103
104   size_t n_iacts;
105   struct interaction **iacts;
106
107   enum mv_class dep_excl;
108   enum mv_class fctr_excl;
109
110   const struct dictionary *dict;
111
112   struct categoricals *cats;
113
114   /* how many extremities to display */
115   int disp_extremes;
116   int calc_extremes;
117   bool descriptives;
118
119   double conf;
120
121   bool missing_pw;
122
123   /* The case index of the ID value (or -1) if not applicable */
124   size_t id_idx;
125   int id_width;
126
127   enum pc_alg pc_alg;
128   double *ptiles;
129   size_t n_percentiles;
130   
131   bool npplot;
132   bool histogramplot;
133   bool boxplot;
134   bool spreadlevelplot;
135   int sl_power;
136
137   enum bp_mode boxplot_mode;
138
139   const struct variable *id_var;
140
141   const struct variable *wv;
142 };
143
144 struct extremity
145 {
146   /* The value of this extremity */
147   double val;
148
149   /* Either the casenumber or the value of the variable specified
150      by the /ID subcommand which corresponds to this extremity */
151   union value identity;
152 };
153
154 struct exploratory_stats
155 {
156   double missing;
157   double non_missing;
158
159   struct moments *mom;
160
161   /* Most operations need a sorted reader/writer */
162   struct casewriter *sorted_writer;
163   struct casereader *sorted_reader;
164
165   struct extremity *minima;
166   struct extremity *maxima;
167
168   /* 
169      Minimum should alway equal mimima[0].val.
170      Likewise, maximum should alway equal maxima[0].val.
171      This redundancy exists as an optimisation effort.
172      Some statistics (eg histogram) require early calculation
173      of the min and max
174   */
175   double minimum;
176   double maximum;
177
178   struct trimmed_mean *trimmed_mean;
179   struct percentile *quartiles[3];
180   struct percentile **percentiles;
181
182   struct tukey_hinges *hinges;
183
184   /* The data for the NP Plots */
185   struct np *np;
186
187   struct histogram *histogram;
188
189   /* The data for the box plots */
190   struct box_whisker *box_whisker;
191
192   /* Total weight */
193   double cc;
194
195   /* The minimum weight */
196   double cmin;
197 };
198
199
200 /* Returns an array of (iact->n_vars) pointers to union value initialised to NULL.
201    The caller must free this array when no longer required. */
202 static const union value **
203 previous_value_alloc (const struct interaction *iact)
204 {
205   int ivar_idx;
206
207   const union value **prev_val = xcalloc (iact->n_vars, sizeof (*prev_val));
208
209   for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
210     prev_val[ivar_idx] = NULL;
211
212   return prev_val;
213 }
214
215 /* Set the contents of PREV_VAL to the values of C indexed by the variables of IACT */
216 static int
217 previous_value_record (const struct interaction *iact, const struct ccase *c, const union value **prev_val)
218 {
219   int ivar_idx;
220   int diff_idx = -1;
221
222   for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
223     {
224       const struct variable *ivar = iact->vars[ivar_idx];
225       const int width = var_get_width (ivar);
226       const union value *val = case_data (c, ivar);
227                   
228       if (prev_val[ivar_idx])
229         if (! value_equal (prev_val[ivar_idx], val, width))
230           {
231             diff_idx = ivar_idx;
232             break;
233           }
234     }
235
236   for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
237     {
238       const struct variable *ivar = iact->vars[ivar_idx];
239       const union value *val = case_data (c, ivar);
240       
241       prev_val[ivar_idx] = val;
242     }
243   return diff_idx;
244 }
245
246
247 static void
248 show_boxplot_grouped (const struct examine *cmd, int iact_idx)
249 {
250   int v;
251
252   const struct interaction *iact = cmd->iacts[iact_idx];
253   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
254
255   for (v = 0; v < cmd->n_dep_vars; ++v)
256     {
257       double y_min = DBL_MAX;
258       double y_max = -DBL_MAX;
259       int grp;
260       struct boxplot *boxplot;
261       struct string title;
262       ds_init_empty (&title);
263
264       if (iact->n_vars > 0)
265         {
266           struct string istr;
267           ds_init_empty (&istr);
268           interaction_to_string (iact, &istr);
269           ds_put_format (&title, _("Boxplot of %s vs. %s"),
270                          var_to_string (cmd->dep_vars[v]),
271                          ds_cstr (&istr));
272           ds_destroy (&istr);
273         }
274       else
275         ds_put_format (&title, _("Boxplot of %s"), var_to_string (cmd->dep_vars[v]));
276       
277       for (grp = 0; grp < n_cats; ++grp)
278         {
279           const struct exploratory_stats *es =
280             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
281
282           if ( y_min > es[v].minimum)
283             y_min = es[v].minimum;
284
285           if ( y_max < es[v].maximum)
286             y_max = es[v].maximum;
287         }
288       
289       boxplot = boxplot_create (y_min, y_max, ds_cstr (&title));
290
291       ds_destroy (&title);
292
293       for (grp = 0; grp < n_cats; ++grp)
294         {
295           int ivar_idx;
296           struct string label;
297
298           const struct ccase *c =
299             categoricals_get_case_by_category_real (cmd->cats,  iact_idx, grp);
300
301           const struct exploratory_stats *es =
302             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
303
304           ds_init_empty (&label);
305           for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
306             {
307               struct string l;
308               const struct variable *ivar = iact->vars[ivar_idx];
309               const union value *val = case_data (c, ivar);
310               ds_init_empty (&l);
311
312               append_value_name (ivar, val, &l);
313               ds_ltrim (&l, ss_cstr (" "));
314
315               ds_put_substring (&label, l.ss);
316               if (ivar_idx < iact->n_vars - 1)
317                 ds_put_cstr (&label, "; ");
318
319               ds_destroy (&l);
320             }
321
322           boxplot_add_box (boxplot, es[v].box_whisker, ds_cstr (&label));
323
324           ds_destroy (&label);
325         }
326       
327       boxplot_submit (boxplot);
328     }
329 }
330
331 static void
332 show_boxplot_variabled (const struct examine *cmd, int iact_idx)
333 {
334   int grp;
335   const struct interaction *iact = cmd->iacts[iact_idx];
336   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
337
338   for (grp = 0; grp < n_cats; ++grp)
339     {
340       struct boxplot *boxplot;
341       int v;
342       double y_min = DBL_MAX;
343       double y_max = -DBL_MAX;
344
345       const struct ccase *c =
346         categoricals_get_case_by_category_real (cmd->cats,  iact_idx, grp);
347
348       struct string title;
349       ds_init_empty (&title);
350
351       for (v = 0; v < cmd->n_dep_vars; ++v)
352         {
353           const struct exploratory_stats *es =
354             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
355
356           if ( y_min > es[v].minimum)
357             y_min = es[v].minimum;
358
359           if ( y_max < es[v].maximum)
360             y_max = es[v].maximum;
361         }
362
363       if ( iact->n_vars == 0)
364         ds_put_format (&title, _("Boxplot"));
365       else
366         {
367           int ivar_idx;
368           struct string label;
369           ds_init_empty (&label);
370           for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
371             {
372               const struct variable *ivar = iact->vars[ivar_idx];
373               const union value *val = case_data (c, ivar);
374               
375               ds_put_cstr (&label, var_to_string (ivar));
376               ds_put_cstr (&label, " = ");
377               append_value_name (ivar, val, &label);
378               ds_put_cstr (&label, "; ");
379             }
380
381           ds_put_format (&title, _("Boxplot of %s"),
382                          ds_cstr (&label));
383
384           ds_destroy (&label);
385         }
386
387       boxplot = boxplot_create (y_min, y_max, ds_cstr (&title));
388
389       ds_destroy (&title);
390
391       for (v = 0; v < cmd->n_dep_vars; ++v)
392         {
393           const struct exploratory_stats *es =
394             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
395
396           boxplot_add_box (boxplot, es[v].box_whisker, 
397                            var_to_string (cmd->dep_vars[v]));
398         }
399
400       boxplot_submit (boxplot);
401     }
402 }
403
404
405 static void
406 show_npplot (const struct examine *cmd, int iact_idx)
407 {
408   const struct interaction *iact = cmd->iacts[iact_idx];
409   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
410
411   int v;
412
413   for (v = 0; v < cmd->n_dep_vars; ++v)
414     {
415       int grp;
416       for (grp = 0; grp < n_cats; ++grp)
417         {
418           struct chart_item *npp, *dnpp;
419           struct casereader *reader;
420           struct np *np;
421
422           int ivar_idx;
423           const struct ccase *c =
424             categoricals_get_case_by_category_real (cmd->cats,
425                                                     iact_idx, grp);
426
427           const struct exploratory_stats *es =
428             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
429
430           struct string label;
431           ds_init_cstr (&label, 
432                         var_to_string (cmd->dep_vars[v]));
433
434           if ( iact->n_vars > 0)
435             {
436               ds_put_cstr (&label, " (");
437               for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
438                 {
439                   const struct variable *ivar = iact->vars[ivar_idx];
440                   const union value *val = case_data (c, ivar);
441                   
442                   ds_put_cstr (&label, var_to_string (ivar));
443                   ds_put_cstr (&label, " = ");
444                   append_value_name (ivar, val, &label);
445                   ds_put_cstr (&label, "; ");
446                   
447                 }
448               ds_put_cstr (&label, ")");
449             }
450           
451           np = es[v].np;
452           reader = casewriter_make_reader (np->writer);
453           np->writer = NULL;
454
455           npp = np_plot_create (np, reader, ds_cstr (&label));
456           dnpp = dnp_plot_create (np, reader, ds_cstr (&label));
457
458           if (npp == NULL || dnpp == NULL)
459             {
460               msg (MW, _("Not creating NP plot because data set is empty."));
461               chart_item_unref (npp);
462               chart_item_unref (dnpp);
463             }
464           else
465             {
466               chart_item_submit (npp);
467               chart_item_submit (dnpp);
468             }
469           casereader_destroy (reader);
470
471           ds_destroy (&label);
472         }
473     }
474 }
475
476 static void
477 show_spreadlevel (const struct examine *cmd, int iact_idx)
478 {
479   const struct interaction *iact = cmd->iacts[iact_idx];
480   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
481
482   int v;
483
484   /* Spreadlevel when there are no levels is not useful */
485   if (iact->n_vars == 0)
486     return;
487
488   for (v = 0; v < cmd->n_dep_vars; ++v)
489     {
490       int grp;
491       struct chart_item *sl;
492
493       struct string label;
494       ds_init_cstr (&label, 
495                     var_to_string (cmd->dep_vars[v]));
496
497       if (iact->n_vars > 0)
498         {
499           ds_put_cstr (&label, " (");
500           interaction_to_string (iact, &label);
501           ds_put_cstr (&label, ")");
502         }
503       
504       sl = spreadlevel_plot_create (ds_cstr (&label), cmd->sl_power);
505
506       for (grp = 0; grp < n_cats; ++grp)
507         {
508           const struct exploratory_stats *es =
509             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
510
511           double median = percentile_calculate (es[v].quartiles[1], cmd->pc_alg);
512
513           double iqr = percentile_calculate (es[v].quartiles[2], cmd->pc_alg) -
514             percentile_calculate (es[v].quartiles[0], cmd->pc_alg);
515
516           spreadlevel_plot_add (sl, iqr, median);
517         }
518
519       if (sl == NULL)
520         msg (MW, _("Not creating spreadlevel chart for %s"), ds_cstr (&label));
521       else 
522         chart_item_submit (sl);
523
524       ds_destroy (&label);
525     }
526 }
527
528
529 static void
530 show_histogram (const struct examine *cmd, int iact_idx)
531 {
532   const struct interaction *iact = cmd->iacts[iact_idx];
533   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
534
535   int v;
536
537   for (v = 0; v < cmd->n_dep_vars; ++v)
538     {
539       int grp;
540       for (grp = 0; grp < n_cats; ++grp)
541         {
542           double n, mean, var;
543           int ivar_idx;
544           const struct ccase *c =
545             categoricals_get_case_by_category_real (cmd->cats,
546                                                     iact_idx, grp);
547
548           const struct exploratory_stats *es =
549             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
550
551           struct string label;
552
553           if (es[v].histogram == NULL)
554             continue;
555
556           ds_init_cstr (&label, 
557                         var_to_string (cmd->dep_vars[v]));
558
559           if ( iact->n_vars > 0)
560             {
561               ds_put_cstr (&label, " (");
562               for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
563                 {
564                   const struct variable *ivar = iact->vars[ivar_idx];
565                   const union value *val = case_data (c, ivar);
566                   
567                   ds_put_cstr (&label, var_to_string (ivar));
568                   ds_put_cstr (&label, " = ");
569                   append_value_name (ivar, val, &label);
570                   ds_put_cstr (&label, "; ");
571                   
572                 }
573               ds_put_cstr (&label, ")");
574             }
575
576
577           moments_calculate (es[v].mom, &n, &mean, &var, NULL, NULL);
578
579           chart_item_submit
580             ( histogram_chart_create (es[v].histogram->gsl_hist,
581                                       ds_cstr (&label), n, mean,
582                                       sqrt (var), false));
583
584           
585           ds_destroy (&label);
586         }
587     }
588 }
589
590 static void
591 percentiles_report (const struct examine *cmd, int iact_idx)
592 {
593   const struct interaction *iact = cmd->iacts[iact_idx];
594   int i, v;
595   const int heading_columns = 1 + iact->n_vars + 1;
596   const int heading_rows = 2;
597   struct tab_table *t;
598
599   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
600
601   const int rows_per_cat = 2;
602   const int rows_per_var = n_cats * rows_per_cat;
603
604   const int nr = heading_rows + cmd->n_dep_vars * rows_per_var;
605   const int nc = heading_columns + cmd->n_percentiles;
606
607   t = tab_create (nc, nr);
608
609   tab_title (t, _("Percentiles"));
610
611   tab_headers (t, heading_columns, 0, heading_rows, 0);
612
613   /* Internal Vertical lines */
614   tab_box (t, -1, -1, -1, TAL_1,
615            heading_columns, 0, nc - 1, nr - 1);
616
617   /* External Frame */
618   tab_box (t, TAL_2, TAL_2, -1, -1,
619            0, 0, nc - 1, nr - 1);
620
621   tab_hline (t, TAL_2, 0, nc - 1, heading_rows);
622   tab_vline (t, TAL_2, heading_columns, 0, nr - 1);
623
624   tab_joint_text (t, heading_columns, 0,
625                   nc - 1, 0,
626                   TAT_TITLE | TAB_CENTER,
627                   _("Percentiles")
628                   );
629
630   tab_hline (t, TAL_1, heading_columns, nc - 1, 1);
631
632
633   for (i = 0; i < cmd->n_percentiles; ++i)
634     {
635       tab_text_format (t, heading_columns + i, 1,
636                        TAT_TITLE | TAB_CENTER,
637                        _("%g"), cmd->ptiles[i]);
638     }
639
640   for (i = 0; i < iact->n_vars; ++i)
641     {
642       tab_text (t,
643                 1 + i, 1,
644                 TAT_TITLE,
645                 var_to_string (iact->vars[i])
646                 );
647     }
648
649
650
651   if (n_cats > 0)
652     {
653       tab_vline (t, TAL_1, heading_columns - 1, heading_rows, nr - 1);
654
655       for (v = 0; v < cmd->n_dep_vars; ++v)
656         {
657           const union value **prev_vals = previous_value_alloc (iact);
658
659           int ivar_idx;
660           if ( v > 0 )
661             tab_hline (t, TAL_1, 0, nc - 1, heading_rows + v * rows_per_var);
662         
663           tab_text (t,
664                     0, heading_rows + v * rows_per_var,
665                     TAT_TITLE | TAB_LEFT,
666                     var_to_string (cmd->dep_vars[v])
667                     );
668
669           for (i = 0; i < n_cats; ++i)
670             {
671               const struct ccase *c =
672                 categoricals_get_case_by_category_real (cmd->cats,
673                                                         iact_idx, i);
674
675               const struct exploratory_stats *ess =
676                 categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, i);
677
678               const struct exploratory_stats *es = ess + v;
679
680               int diff_idx = previous_value_record (iact, c, prev_vals);
681
682               double hinges[3];
683               int p;
684
685               for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
686                 {
687                   const struct variable *ivar = iact->vars[ivar_idx];
688                   const union value *val = case_data (c, ivar);
689
690                   if (( diff_idx != -1 && diff_idx <= ivar_idx)
691                       || i == 0)
692                     {              
693                       struct string str;
694                       ds_init_empty (&str);
695                       append_value_name (ivar, val, &str);
696               
697                       tab_text (t,
698                                 1 + ivar_idx,
699                                 heading_rows + v * rows_per_var + i * rows_per_cat,
700                                 TAT_TITLE | TAB_LEFT,
701                                 ds_cstr (&str)
702                                 );
703                   
704                       ds_destroy (&str);
705                     }
706                 }
707
708               if ( diff_idx != -1 && diff_idx < iact->n_vars)
709                 {
710                   tab_hline (t, TAL_1, 1 + diff_idx, nc - 1,
711                              heading_rows + v * rows_per_var + i * rows_per_cat
712                              );
713                 }
714
715               tab_text (t, heading_columns - 1, 
716                         heading_rows + v * rows_per_var + i * rows_per_cat,
717                         TAT_TITLE | TAB_LEFT,
718                         gettext (ptile_alg_desc [cmd->pc_alg]));
719
720               tukey_hinges_calculate (es->hinges, hinges);
721
722               for (p = 0; p < cmd->n_percentiles; ++p)
723                 {
724                   tab_double (t, heading_columns + p, 
725                               heading_rows + v * rows_per_var + i * rows_per_cat,
726                               0,
727                               percentile_calculate (es->percentiles[p], cmd->pc_alg),
728                               NULL, RC_OTHER);
729               
730                   if (cmd->ptiles[p] == 25.0)
731                     {
732                       tab_double (t, heading_columns + p, 
733                                   heading_rows + v * rows_per_var + i * rows_per_cat + 1,
734                                   0,
735                                   hinges[0],
736                                   NULL, RC_OTHER);
737                     }
738                   else if (cmd->ptiles[p] == 50.0)
739                     {
740                       tab_double (t, heading_columns + p, 
741                                   heading_rows + v * rows_per_var + i * rows_per_cat + 1,
742                                   0,
743                                   hinges[1],
744                                   NULL, RC_OTHER);
745                     }
746                   else if (cmd->ptiles[p] == 75.0)
747                     {
748                       tab_double (t, heading_columns + p, 
749                                   heading_rows + v * rows_per_var + i * rows_per_cat + 1,
750                                   0,
751                                   hinges[2],
752                                   NULL, RC_OTHER);
753                     }
754                 }
755
756
757               tab_text (t, heading_columns - 1, 
758                         heading_rows + v * rows_per_var + i * rows_per_cat + 1,
759                         TAT_TITLE | TAB_LEFT,
760                         _("Tukey's Hinges"));
761           
762             }
763
764           free (prev_vals);
765         }
766     }
767   tab_submit (t);
768 }
769
770 static void
771 descriptives_report (const struct examine *cmd, int iact_idx)
772 {
773   const struct interaction *iact = cmd->iacts[iact_idx];
774   int i, v;
775   const int heading_columns = 1 + iact->n_vars + 2;
776   const int heading_rows = 1;
777   struct tab_table *t;
778
779   size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
780
781   const int rows_per_cat = 13;
782   const int rows_per_var = n_cats * rows_per_cat;
783
784   const int nr = heading_rows + cmd->n_dep_vars * rows_per_var;
785   const int nc = 2 + heading_columns;
786
787   t = tab_create (nc, nr);
788
789   tab_title (t, _("Descriptives"));
790
791   tab_headers (t, heading_columns, 0, heading_rows, 0);
792
793   /* Internal Vertical lines */
794   tab_box (t, -1, -1, -1, TAL_1,
795            heading_columns, 0, nc - 1, nr - 1);
796
797   /* External Frame */
798   tab_box (t, TAL_2, TAL_2, -1, -1,
799            0, 0, nc - 1, nr - 1);
800
801   tab_hline (t, TAL_2, 0, nc - 1, heading_rows);
802   tab_vline (t, TAL_2, heading_columns, 0, nr - 1);
803
804
805   tab_text (t, heading_columns, 0, TAB_CENTER | TAT_TITLE,
806             _("Statistic"));
807
808   tab_text (t, heading_columns + 1, 0, TAB_CENTER | TAT_TITLE,
809             _("Std. Error"));
810
811   for (i = 0; i < iact->n_vars; ++i)
812     {
813       tab_text (t,
814                 1 + i, 0,
815                 TAT_TITLE,
816                 var_to_string (iact->vars[i])
817                 );
818     }
819
820   for (v = 0; v < cmd->n_dep_vars; ++v)
821     {
822       const union value **prev_val = previous_value_alloc (iact);
823
824       int ivar_idx;
825       if ( v > 0 )
826         tab_hline (t, TAL_1, 0, nc - 1, heading_rows + v * rows_per_var);
827         
828       tab_text (t,
829                 0, heading_rows + v * rows_per_var,
830                 TAT_TITLE | TAB_LEFT,
831                 var_to_string (cmd->dep_vars[v])
832                 );
833
834       for (i = 0; i < n_cats; ++i)
835         {
836           const struct ccase *c =
837             categoricals_get_case_by_category_real (cmd->cats,
838                                                     iact_idx, i);
839
840           const struct exploratory_stats *ess =
841             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, i);
842
843           const struct exploratory_stats *es = ess + v;
844
845           const int diff_idx = previous_value_record (iact, c, prev_val);
846
847           double m0, m1, m2, m3, m4;
848           double tval;
849
850           moments_calculate (es->mom, &m0, &m1, &m2, &m3, &m4);
851
852           tval = gsl_cdf_tdist_Qinv ((1.0 - cmd->conf) / 2.0, m0 - 1.0);
853
854           for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
855             {
856               const struct variable *ivar = iact->vars[ivar_idx];
857               const union value *val = case_data (c, ivar);
858
859               if (( diff_idx != -1 && diff_idx <= ivar_idx)
860                   || i == 0)
861                 {              
862                   struct string str;
863                   ds_init_empty (&str);
864                   append_value_name (ivar, val, &str);
865               
866                   tab_text (t,
867                             1 + ivar_idx,
868                             heading_rows + v * rows_per_var + i * rows_per_cat,
869                             TAT_TITLE | TAB_LEFT,
870                             ds_cstr (&str)
871                             );
872                   
873                   ds_destroy (&str);
874                 }
875             }
876
877           if ( diff_idx != -1 && diff_idx < iact->n_vars)
878             {
879               tab_hline (t, TAL_1, 1 + diff_idx, nc - 1,
880                          heading_rows + v * rows_per_var + i * rows_per_cat
881                          );
882             }
883
884           tab_text (t,
885                     1 + iact->n_vars,
886                     heading_rows + v * rows_per_var + i * rows_per_cat,
887                     TAB_LEFT,
888                     _("Mean")
889                     );
890
891           tab_double (t,
892                       1 + iact->n_vars + 2,
893                       heading_rows + v * rows_per_var + i * rows_per_cat,
894                       0, m1, NULL, RC_OTHER);
895
896           tab_double (t,
897                       1 + iact->n_vars + 3,
898                       heading_rows + v * rows_per_var + i * rows_per_cat,
899                       0, calc_semean (m2, m0), NULL, RC_OTHER);
900
901           tab_text_format (t,
902                            1 + iact->n_vars,
903                            heading_rows + v * rows_per_var + i * rows_per_cat + 1,
904                            TAB_LEFT,
905                            _("%g%% Confidence Interval for Mean"),
906                            cmd->conf * 100.0
907                            );
908           
909           tab_text (t,
910                     1 + iact->n_vars + 1,
911                     heading_rows + v * rows_per_var + i * rows_per_cat + 1,
912                     TAB_LEFT,
913                     _("Lower Bound")
914                     );
915
916           tab_double (t,
917                       1 + iact->n_vars + 2,
918                       heading_rows + v * rows_per_var + i * rows_per_cat + 1,
919                       0, m1 - tval * calc_semean (m2, m0), NULL, RC_OTHER);
920
921
922           tab_text (t,
923                     1 + iact->n_vars + 1,
924                     heading_rows + v * rows_per_var + i * rows_per_cat + 2,
925                     TAB_LEFT,
926                     _("Upper Bound")
927                     );
928
929           tab_double (t,
930                       1 + iact->n_vars + 2,
931                       heading_rows + v * rows_per_var + i * rows_per_cat + 2,
932                       0, m1 + tval * calc_semean (m2, m0), NULL, RC_OTHER);
933
934
935           tab_text (t,
936                     1 + iact->n_vars,
937                     heading_rows + v * rows_per_var + i * rows_per_cat + 3,
938                     TAB_LEFT,
939                     _("5% Trimmed Mean")
940                     );
941
942           tab_double (t,
943                       1 + iact->n_vars + 2,
944                       heading_rows + v * rows_per_var + i * rows_per_cat + 3,
945                       0,
946                       trimmed_mean_calculate (es->trimmed_mean),
947                       NULL, RC_OTHER);
948
949           tab_text (t,
950                     1 + iact->n_vars,
951                     heading_rows + v * rows_per_var + i * rows_per_cat + 4,
952                     TAB_LEFT,
953                     _("Median")
954                     );
955           
956           tab_double (t,
957                       1 + iact->n_vars + 2,
958                       heading_rows + v * rows_per_var + i * rows_per_cat + 4,
959                       0,
960                       percentile_calculate (es->quartiles[1], cmd->pc_alg),
961                       NULL, RC_OTHER);
962
963
964           tab_text (t,
965                     1 + iact->n_vars,
966                     heading_rows + v * rows_per_var + i * rows_per_cat + 5,
967                     TAB_LEFT,
968                     _("Variance")
969                     );
970
971           tab_double (t,
972                       1 + iact->n_vars + 2,
973                       heading_rows + v * rows_per_var + i * rows_per_cat + 5,
974                       0, m2, NULL, RC_OTHER);
975
976           tab_text (t,
977                     1 + iact->n_vars,
978                     heading_rows + v * rows_per_var + i * rows_per_cat + 6,
979                     TAB_LEFT,
980                     _("Std. Deviation")
981                     );
982
983           tab_double (t,
984                       1 + iact->n_vars + 2,
985                       heading_rows + v * rows_per_var + i * rows_per_cat + 6,
986                       0, sqrt (m2), NULL, RC_OTHER);
987
988           tab_text (t,
989                     1 + iact->n_vars,
990                     heading_rows + v * rows_per_var + i * rows_per_cat + 7,
991                     TAB_LEFT,
992                     _("Minimum")
993                     );
994
995           tab_double (t,
996                       1 + iact->n_vars + 2,
997                       heading_rows + v * rows_per_var + i * rows_per_cat + 7,
998                       0, 
999                       es->minima[0].val,
1000                       NULL, RC_OTHER);
1001
1002           tab_text (t,
1003                     1 + iact->n_vars,
1004                     heading_rows + v * rows_per_var + i * rows_per_cat + 8,
1005                     TAB_LEFT,
1006                     _("Maximum")
1007                     );
1008
1009           tab_double (t,
1010                       1 + iact->n_vars + 2,
1011                       heading_rows + v * rows_per_var + i * rows_per_cat + 8,
1012                       0, 
1013                       es->maxima[0].val,
1014                       NULL, RC_OTHER);
1015
1016           tab_text (t,
1017                     1 + iact->n_vars,
1018                     heading_rows + v * rows_per_var + i * rows_per_cat + 9,
1019                     TAB_LEFT,
1020                     _("Range")
1021                     );
1022
1023           tab_double (t,
1024                       1 + iact->n_vars + 2,
1025                       heading_rows + v * rows_per_var + i * rows_per_cat + 9,
1026                       0, 
1027                       es->maxima[0].val - es->minima[0].val,
1028                       NULL, RC_OTHER);
1029
1030           tab_text (t,
1031                     1 + iact->n_vars,
1032                     heading_rows + v * rows_per_var + i * rows_per_cat + 10,
1033                     TAB_LEFT,
1034                     _("Interquartile Range")
1035                     );
1036
1037
1038           tab_double (t,
1039                       1 + iact->n_vars + 2,
1040                       heading_rows + v * rows_per_var + i * rows_per_cat + 10,
1041                       0,
1042                       percentile_calculate (es->quartiles[2], cmd->pc_alg) - 
1043                       percentile_calculate (es->quartiles[0], cmd->pc_alg),
1044                       NULL, RC_OTHER);
1045
1046
1047
1048
1049           tab_text (t,
1050                     1 + iact->n_vars,
1051                     heading_rows + v * rows_per_var + i * rows_per_cat + 11,
1052                     TAB_LEFT,
1053                     _("Skewness")
1054                     );
1055
1056           tab_double (t,
1057                       1 + iact->n_vars + 2,
1058                       heading_rows + v * rows_per_var + i * rows_per_cat + 11,
1059                       0, m3, NULL, RC_OTHER);
1060
1061           tab_double (t,
1062                       1 + iact->n_vars + 3,
1063                       heading_rows + v * rows_per_var + i * rows_per_cat + 11,
1064                       0, calc_seskew (m0), NULL, RC_OTHER);
1065
1066           tab_text (t,
1067                     1 + iact->n_vars,
1068                     heading_rows + v * rows_per_var + i * rows_per_cat + 12,
1069                     TAB_LEFT,
1070                     _("Kurtosis")
1071                     );
1072
1073           tab_double (t,
1074                       1 + iact->n_vars + 2,
1075                       heading_rows + v * rows_per_var + i * rows_per_cat + 12,
1076                       0, m4, NULL, RC_OTHER);
1077
1078           tab_double (t,
1079                       1 + iact->n_vars + 3,
1080                       heading_rows + v * rows_per_var + i * rows_per_cat + 12,
1081                       0, calc_sekurt (m0), NULL, RC_OTHER);
1082         }
1083
1084       free (prev_val);
1085     }
1086   tab_submit (t);
1087 }
1088
1089
1090 static void
1091 extremes_report (const struct examine *cmd, int iact_idx)
1092 {
1093   const struct interaction *iact = cmd->iacts[iact_idx];
1094   int i, v;
1095   const int heading_columns = 1 + iact->n_vars + 2;
1096   const int heading_rows = 1;
1097   struct tab_table *t;
1098
1099   size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
1100
1101   const int rows_per_cat = 2 * cmd->disp_extremes;
1102   const int rows_per_var = n_cats * rows_per_cat;
1103
1104   const int nr = heading_rows + cmd->n_dep_vars * rows_per_var;
1105   const int nc = 2 + heading_columns;
1106
1107   t = tab_create (nc, nr);
1108
1109   tab_title (t, _("Extreme Values"));
1110
1111   tab_headers (t, heading_columns, 0, heading_rows, 0);
1112
1113   /* Internal Vertical lines */
1114   tab_box (t, -1, -1, -1, TAL_1,
1115            heading_columns, 0, nc - 1, nr - 1);
1116
1117   /* External Frame */
1118   tab_box (t, TAL_2, TAL_2, -1, -1,
1119            0, 0, nc - 1, nr - 1);
1120
1121   tab_hline (t, TAL_2, 0, nc - 1, heading_rows);
1122   tab_vline (t, TAL_2, heading_columns, 0, nr - 1);
1123
1124
1125   if ( cmd->id_var ) 
1126     tab_text (t, heading_columns, 0, TAB_CENTER | TAT_TITLE,
1127               var_to_string (cmd->id_var));
1128   else
1129     tab_text (t, heading_columns, 0, TAB_CENTER | TAT_TITLE,
1130               _("Case Number"));
1131
1132   tab_text (t, heading_columns + 1, 0, TAB_CENTER | TAT_TITLE,
1133             _("Value"));
1134
1135   for (i = 0; i < iact->n_vars; ++i)
1136     {
1137       tab_text (t,
1138                 1 + i, 0,
1139                 TAT_TITLE,
1140                 var_to_string (iact->vars[i])
1141                 );
1142     }
1143
1144   for (v = 0; v < cmd->n_dep_vars; ++v)
1145     {
1146       const union value **prev_val = previous_value_alloc (iact);
1147
1148       int ivar_idx;
1149       if ( v > 0 )
1150         tab_hline (t, TAL_1, 0, nc - 1, heading_rows + v * rows_per_var);
1151         
1152       tab_text (t,
1153                 0, heading_rows + v * rows_per_var,
1154                 TAT_TITLE,
1155                 var_to_string (cmd->dep_vars[v])
1156                 );
1157
1158       for (i = 0; i < n_cats; ++i)
1159         {
1160           int e;
1161           const struct ccase *c =
1162             categoricals_get_case_by_category_real (cmd->cats, iact_idx, i);
1163
1164           const struct exploratory_stats *ess =
1165             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, i);
1166
1167           const struct exploratory_stats *es = ess + v;
1168
1169           int diff_idx = previous_value_record (iact, c, prev_val);
1170
1171           for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
1172             {
1173               const struct variable *ivar = iact->vars[ivar_idx];
1174               const union value *val = case_data (c, ivar);
1175
1176               if (( diff_idx != -1 && diff_idx <= ivar_idx)
1177                   || i == 0)
1178                 {              
1179                   struct string str;
1180                   ds_init_empty (&str);
1181                   append_value_name (ivar, val, &str);
1182               
1183                   tab_text (t,
1184                             1 + ivar_idx,
1185                             heading_rows + v * rows_per_var + i * rows_per_cat,
1186                             TAT_TITLE | TAB_LEFT,
1187                             ds_cstr (&str)
1188                             );
1189                   
1190                   ds_destroy (&str);
1191                 }
1192             }
1193
1194           if ( diff_idx != -1 && diff_idx < iact->n_vars)
1195             {
1196               tab_hline (t, TAL_1, 1 + diff_idx, nc - 1,
1197                          heading_rows + v * rows_per_var + i * rows_per_cat
1198                          );
1199             }
1200           
1201           tab_text (t,
1202                     heading_columns - 2,
1203                     heading_rows + v * rows_per_var + i * rows_per_cat,
1204                     TAB_RIGHT,
1205                     _("Highest"));
1206
1207
1208           tab_hline (t, TAL_1, heading_columns - 2, nc - 1,
1209                      heading_rows + v * rows_per_var + i * rows_per_cat + cmd->disp_extremes
1210                      );
1211
1212           tab_text (t,
1213                     heading_columns - 2,
1214                     heading_rows + v * rows_per_var + i * rows_per_cat + cmd->disp_extremes,
1215                     TAB_RIGHT,
1216                     _("Lowest"));
1217
1218           for (e = 0 ; e < cmd->disp_extremes; ++e)
1219             {
1220               tab_double (t,
1221                           heading_columns - 1,
1222                           heading_rows + v * rows_per_var + i * rows_per_cat + e,
1223                           TAB_RIGHT,
1224                           e + 1,
1225                           NULL, RC_INTEGER);
1226
1227               /* The casenumber */
1228               if (cmd->id_var)
1229                 tab_value (t,
1230                            heading_columns,
1231                            heading_rows + v * rows_per_var + i * rows_per_cat + e,
1232                            TAB_RIGHT,
1233                            &es->maxima[e].identity,
1234                            cmd->id_var,
1235                            NULL);
1236               else 
1237                 tab_double (t,
1238                           heading_columns,
1239                             heading_rows + v * rows_per_var + i * rows_per_cat + e,
1240                             TAB_RIGHT,
1241                             es->maxima[e].identity.f,
1242                             NULL, RC_INTEGER);
1243
1244               tab_double (t,
1245                          heading_columns + 1,
1246                          heading_rows + v * rows_per_var + i * rows_per_cat + e,
1247                          0,
1248                          es->maxima[e].val,
1249                           var_get_print_format (cmd->dep_vars[v]), RC_OTHER);
1250                          
1251
1252               tab_double (t,
1253                           heading_columns - 1,
1254                           heading_rows + v * rows_per_var + i * rows_per_cat + cmd->disp_extremes + e,
1255                           TAB_RIGHT,
1256                           e + 1,
1257                           NULL, RC_INTEGER);
1258
1259               /* The casenumber */
1260               if (cmd->id_var)
1261                 tab_value (t,
1262                            heading_columns,
1263                            heading_rows + v * rows_per_var + i * rows_per_cat + cmd->disp_extremes + e,
1264                            TAB_RIGHT,
1265                            &es->minima[e].identity,
1266                            cmd->id_var,
1267                            NULL);
1268               else
1269                 tab_double (t,
1270                             heading_columns,
1271                             heading_rows + v * rows_per_var + i * rows_per_cat + cmd->disp_extremes + e,
1272                             TAB_RIGHT,
1273                             es->minima[e].identity.f,
1274                             NULL, RC_INTEGER);
1275
1276               tab_double (t,
1277                           heading_columns + 1,
1278                           heading_rows + v * rows_per_var + i * rows_per_cat + cmd->disp_extremes + e,
1279                           0,
1280                           es->minima[e].val,
1281                           var_get_print_format (cmd->dep_vars[v]), RC_OTHER);
1282             }
1283         }
1284       free (prev_val);
1285     }
1286
1287   tab_submit (t);
1288 }
1289
1290
1291 static void
1292 summary_report (const struct examine *cmd, int iact_idx)
1293 {
1294   const struct interaction *iact = cmd->iacts[iact_idx];
1295   int i, v;
1296   const int heading_columns = 1 + iact->n_vars;
1297   const int heading_rows = 3;
1298   struct tab_table *t;
1299
1300   const struct fmt_spec *wfmt = cmd->wv ? var_get_print_format (cmd->wv) : &F_8_0;
1301
1302   size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
1303
1304   const int nr = heading_rows + n_cats * cmd->n_dep_vars;
1305   const int nc = 6 + heading_columns;
1306
1307   t = tab_create (nc, nr);
1308   tab_set_format (t, RC_WEIGHT, wfmt);
1309   tab_title (t, _("Case Processing Summary"));
1310
1311   tab_headers (t, heading_columns, 0, heading_rows, 0);
1312
1313   /* Internal Vertical lines */
1314   tab_box (t, -1, -1, -1, TAL_1,
1315            heading_columns, 0, nc - 1, nr - 1);
1316
1317   /* External Frame */
1318   tab_box (t, TAL_2, TAL_2, -1, -1,
1319            0, 0, nc - 1, nr - 1);
1320
1321   tab_hline (t, TAL_2, 0, nc - 1, heading_rows);
1322   tab_vline (t, TAL_2, heading_columns, 0, nr - 1);
1323
1324   tab_joint_text (t, heading_columns, 0,
1325                   nc - 1, 0, TAB_CENTER | TAT_TITLE, _("Cases"));
1326   tab_joint_text (t,
1327                   heading_columns, 1,
1328                   heading_columns + 1, 1,
1329                   TAB_CENTER | TAT_TITLE, _("Valid"));
1330
1331   tab_joint_text (t,
1332                   heading_columns + 2, 1, 
1333                   heading_columns + 3, 1,
1334                   TAB_CENTER | TAT_TITLE, _("Missing"));
1335
1336   tab_joint_text (t,
1337                   heading_columns + 4, 1,
1338                   heading_columns + 5, 1,
1339                   TAB_CENTER | TAT_TITLE, _("Total"));
1340
1341   for (i = 0; i < 3; ++i)
1342     {
1343       tab_text (t, heading_columns + i * 2, 2, TAB_CENTER | TAT_TITLE,
1344                 _("N"));
1345       tab_text (t, heading_columns + i * 2 + 1, 2, TAB_CENTER | TAT_TITLE,
1346                 _("Percent"));
1347     }
1348
1349   for (i = 0; i < iact->n_vars; ++i)
1350     {
1351       tab_text (t,
1352                 1 + i, 2,
1353                 TAT_TITLE,
1354                 var_to_string (iact->vars[i])
1355                 );
1356     }
1357
1358   if (n_cats > 0)
1359     for (v = 0; v < cmd->n_dep_vars; ++v)
1360       {
1361         int ivar_idx;
1362         const union value **prev_values = previous_value_alloc (iact);
1363
1364         if ( v > 0 )
1365           tab_hline (t, TAL_1, 0, nc - 1, heading_rows + v * n_cats);
1366
1367         tab_text (t,
1368                   0, heading_rows + n_cats * v,
1369                   TAT_TITLE,
1370                   var_to_string (cmd->dep_vars[v])
1371                   );
1372
1373
1374         for (i = 0; i < n_cats; ++i)
1375           {
1376             double total;
1377             const struct exploratory_stats *es;
1378
1379             const struct ccase *c =
1380               categoricals_get_case_by_category_real (cmd->cats,
1381                                                       iact_idx, i);
1382             if (c)
1383               {
1384                 int diff_idx = previous_value_record (iact, c, prev_values);
1385
1386                 if ( diff_idx != -1 && diff_idx < iact->n_vars - 1)
1387                   tab_hline (t, TAL_1, 1 + diff_idx, nc - 1,
1388                              heading_rows + n_cats * v + i );
1389
1390                 for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
1391                   {
1392                     const struct variable *ivar = iact->vars[ivar_idx];
1393                     const union value *val = case_data (c, ivar);
1394
1395                     if (( diff_idx != -1 && diff_idx <= ivar_idx)
1396                         || i == 0)
1397                       {              
1398                         struct string str;
1399                         ds_init_empty (&str);
1400                         append_value_name (ivar, val, &str);
1401               
1402                         tab_text (t,
1403                                   1 + ivar_idx, heading_rows + n_cats * v + i,
1404                                   TAT_TITLE | TAB_LEFT,
1405                                   ds_cstr (&str)
1406                                   );
1407                   
1408                         ds_destroy (&str);
1409                       }
1410                   }
1411               }
1412
1413
1414             es = categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, i);
1415   
1416           
1417             total = es[v].missing + es[v].non_missing;
1418             tab_double (t, 
1419                         heading_columns + 0,
1420                         heading_rows + n_cats * v + i,
1421                         0,
1422                         es[v].non_missing,
1423                         NULL, RC_WEIGHT);
1424
1425
1426             tab_text_format (t, 
1427                              heading_columns + 1,
1428                              heading_rows + n_cats * v + i,
1429                              0,
1430                              "%g%%",
1431                              100.0 * es[v].non_missing / total
1432                              );
1433
1434
1435             tab_double (t, 
1436                         heading_columns + 2,
1437                         heading_rows + n_cats * v + i,
1438                         0,
1439                         es[v].missing,
1440                         NULL, RC_WEIGHT);
1441
1442             tab_text_format (t, 
1443                              heading_columns + 3,
1444                              heading_rows + n_cats * v + i,
1445                              0,
1446                              "%g%%",
1447                              100.0 * es[v].missing / total
1448                              );
1449             tab_double (t, 
1450                         heading_columns + 4,
1451                         heading_rows + n_cats * v + i,
1452                         0,
1453                         total,
1454                         NULL, RC_WEIGHT);
1455
1456             /* This can only be 100% can't it? */
1457             tab_text_format (t, 
1458                              heading_columns + 5,
1459                              heading_rows + n_cats * v + i,
1460                              0,
1461                              "%g%%",
1462                              100.0 * (es[v].missing + es[v].non_missing)/ total
1463                              );
1464           }
1465         free (prev_values);
1466       }
1467
1468   tab_hline (t, TAL_1, heading_columns, nc - 1, 1);
1469   tab_hline (t, TAL_1, heading_columns, nc - 1, 2);
1470
1471   tab_submit (t);
1472 }
1473
1474 /* Attempt to parse an interaction from LEXER */
1475 static struct interaction *
1476 parse_interaction (struct lexer *lexer, struct examine *ex)
1477 {
1478   const struct variable *v = NULL;
1479   struct interaction *iact = NULL;
1480   
1481   if ( lex_match_variable (lexer, ex->dict, &v))
1482     {
1483       iact = interaction_create (v);
1484
1485       while (lex_match (lexer, T_BY))
1486         {
1487           if (!lex_match_variable (lexer, ex->dict, &v))
1488             {
1489               interaction_destroy (iact);
1490               return NULL;
1491             }
1492           interaction_add_variable (iact, v);
1493         }
1494       lex_match (lexer, T_COMMA);
1495     }
1496   
1497   return iact;
1498 }
1499
1500
1501 static void *
1502 create_n (const void *aux1, void *aux2 UNUSED)
1503 {
1504   int v;
1505   
1506   const struct examine *examine = aux1;
1507   struct exploratory_stats *es = pool_calloc (examine->pool, examine->n_dep_vars, sizeof (*es));
1508   struct subcase ordering;
1509   subcase_init (&ordering, 0, 0, SC_ASCEND);
1510
1511   for (v = 0; v < examine->n_dep_vars; v++)
1512     {
1513       es[v].sorted_writer = sort_create_writer (&ordering, examine->ex_proto);
1514       es[v].sorted_reader = NULL;
1515
1516       es[v].mom = moments_create (MOMENT_KURTOSIS);
1517       es[v].cmin = DBL_MAX;
1518
1519       es[v].maximum = -DBL_MAX;
1520       es[v].minimum =  DBL_MAX;
1521     }
1522
1523   subcase_destroy (&ordering);
1524   return es;
1525 }
1526
1527 static void
1528 update_n (const void *aux1, void *aux2 UNUSED, void *user_data,
1529           const struct ccase *c, double weight)
1530 {
1531   int v;
1532   const struct examine *examine = aux1;
1533   struct exploratory_stats *es = user_data;
1534   
1535   bool this_case_is_missing = false;
1536   /* LISTWISE missing must be dealt with here */
1537   if (!examine->missing_pw)
1538     {
1539       for (v = 0; v < examine->n_dep_vars; v++)
1540         {
1541           const struct variable *var = examine->dep_vars[v];
1542
1543           if (var_is_value_missing (var, case_data (c, var), examine->dep_excl))
1544             {
1545               es[v].missing += weight;
1546               this_case_is_missing = true;
1547             }
1548         }
1549     }
1550
1551   if (this_case_is_missing)
1552     return;
1553
1554   for (v = 0; v < examine->n_dep_vars; v++)
1555     {
1556       struct ccase *outcase ;
1557       const struct variable *var = examine->dep_vars[v];
1558       const double x = case_data (c, var)->f;
1559       
1560       if (var_is_value_missing (var, case_data (c, var), examine->dep_excl))
1561         {
1562           es[v].missing += weight;
1563           continue;
1564         }
1565
1566       outcase = case_create (examine->ex_proto);
1567
1568       if (x > es[v].maximum)
1569         es[v].maximum = x;
1570
1571       if (x < es[v].minimum)
1572         es[v].minimum =  x;
1573
1574       es[v].non_missing += weight;
1575
1576       moments_pass_one (es[v].mom, x, weight);
1577
1578       /* Save the value and the ID to the writer */
1579       assert (examine->id_idx != -1);
1580       case_data_rw_idx (outcase, EX_VAL)->f = x;
1581       value_copy (case_data_rw_idx (outcase, EX_ID),
1582                   case_data_idx (c, examine->id_idx), examine->id_width);
1583
1584       case_data_rw_idx (outcase, EX_WT)->f = weight;
1585       
1586       es[v].cc += weight;
1587
1588       if (es[v].cmin > weight)
1589         es[v].cmin = weight;
1590
1591       casewriter_write (es[v].sorted_writer, outcase);
1592     }
1593 }
1594
1595 static void
1596 calculate_n (const void *aux1, void *aux2 UNUSED, void *user_data)
1597 {
1598   int v;
1599   const struct examine *examine = aux1;
1600   struct exploratory_stats *es = user_data;
1601
1602   for (v = 0; v < examine->n_dep_vars; v++)
1603     {
1604       int i;
1605       casenumber imin = 0;
1606       casenumber imax;
1607       struct casereader *reader;
1608       struct ccase *c;
1609
1610       if (examine->histogramplot && es[v].non_missing > 0)
1611         {
1612           /* Sturges Rule */
1613           double bin_width = fabs (es[v].minimum - es[v].maximum)
1614             / (1 + log2 (es[v].cc))
1615             ;
1616
1617           es[v].histogram =
1618             histogram_create (bin_width, es[v].minimum, es[v].maximum);
1619         }
1620
1621       es[v].sorted_reader = casewriter_make_reader (es[v].sorted_writer);
1622       es[v].sorted_writer = NULL;
1623
1624       imax = casereader_get_case_cnt (es[v].sorted_reader);
1625
1626       es[v].maxima = pool_calloc (examine->pool, examine->calc_extremes, sizeof (*es[v].maxima));
1627       es[v].minima = pool_calloc (examine->pool, examine->calc_extremes, sizeof (*es[v].minima));
1628       for (i = 0; i < examine->calc_extremes; ++i)
1629         {
1630           value_init_pool (examine->pool, &es[v].maxima[i].identity, examine->id_width) ;
1631           value_init_pool (examine->pool, &es[v].minima[i].identity, examine->id_width) ;
1632         }
1633       
1634       for (reader = casereader_clone (es[v].sorted_reader);
1635            (c = casereader_read (reader)) != NULL; case_unref (c))
1636         {
1637           const double val = case_data_idx (c, EX_VAL)->f;
1638           const double wt = case_data_idx (c, EX_WT)->f;
1639
1640           moments_pass_two (es[v].mom, val, wt);
1641
1642           if (es[v].histogram)
1643             histogram_add (es[v].histogram, val, wt);
1644
1645           if (imin < examine->calc_extremes)
1646             {
1647               int x;
1648               for (x = imin; x < examine->calc_extremes; ++x)
1649                 {
1650                   struct extremity *min = &es[v].minima[x];
1651                   min->val = val;
1652                   value_copy (&min->identity, case_data_idx (c, EX_ID), examine->id_width);
1653                 }
1654               imin ++;
1655             }
1656
1657           imax --;
1658           if (imax < examine->calc_extremes)
1659             {
1660               int x;
1661
1662               for (x = imax; x < imax + 1; ++x)
1663                 {
1664                   struct extremity *max;
1665
1666                   if (x >= examine->calc_extremes) 
1667                     break;
1668
1669                   max = &es[v].maxima[x];
1670                   max->val = val;
1671                   value_copy (&max->identity, case_data_idx (c, EX_ID), examine->id_width);
1672                 }
1673             }
1674         }
1675       casereader_destroy (reader);
1676
1677       if (examine->calc_extremes > 0 && es[v].non_missing > 0)
1678         {
1679           assert (es[v].minima[0].val == es[v].minimum);
1680           assert (es[v].maxima[0].val == es[v].maximum);
1681         }
1682
1683       {
1684         const int n_os = 5 + examine->n_percentiles;
1685         struct order_stats **os ;
1686         es[v].percentiles = pool_calloc (examine->pool, examine->n_percentiles, sizeof (*es[v].percentiles));
1687
1688         es[v].trimmed_mean = trimmed_mean_create (es[v].cc, 0.05);
1689
1690         os = xcalloc (n_os, sizeof *os);
1691         os[0] = &es[v].trimmed_mean->parent;
1692
1693         es[v].quartiles[0] = percentile_create (0.25, es[v].cc);
1694         es[v].quartiles[1] = percentile_create (0.5,  es[v].cc);
1695         es[v].quartiles[2] = percentile_create (0.75, es[v].cc);
1696
1697         os[1] = &es[v].quartiles[0]->parent;
1698         os[2] = &es[v].quartiles[1]->parent;
1699         os[3] = &es[v].quartiles[2]->parent;
1700
1701         es[v].hinges = tukey_hinges_create (es[v].cc, es[v].cmin);
1702         os[4] = &es[v].hinges->parent;
1703
1704         for (i = 0; i < examine->n_percentiles; ++i)
1705           {
1706             es[v].percentiles[i] = percentile_create (examine->ptiles[i] / 100.00, es[v].cc);
1707             os[5 + i] = &es[v].percentiles[i]->parent;
1708           }
1709
1710         order_stats_accumulate_idx (os, n_os,
1711                                     casereader_clone (es[v].sorted_reader),
1712                                     EX_WT, EX_VAL);
1713
1714         free (os);
1715       }
1716
1717       if (examine->boxplot)
1718         {
1719           struct order_stats *os;
1720
1721           es[v].box_whisker = box_whisker_create (es[v].hinges, 
1722                                                   EX_ID, examine->id_var);
1723
1724           os = &es[v].box_whisker->parent;
1725           order_stats_accumulate_idx (&os, 1,
1726                                       casereader_clone (es[v].sorted_reader),
1727                                       EX_WT, EX_VAL);
1728         }
1729
1730       if (examine->npplot)
1731         {
1732           double n, mean, var;
1733           struct order_stats *os;
1734
1735           moments_calculate (es[v].mom, &n, &mean, &var, NULL, NULL);
1736           
1737           es[v].np = np_create (n, mean, var);
1738
1739           os = &es[v].np->parent;
1740
1741           order_stats_accumulate_idx (&os, 1,
1742                                       casereader_clone (es[v].sorted_reader),
1743                                       EX_WT, EX_VAL);
1744         }
1745
1746     }
1747 }
1748
1749 static void
1750 cleanup_exploratory_stats (struct examine *cmd)
1751
1752   int i;
1753   for (i = 0; i < cmd->n_iacts; ++i)
1754     {
1755       int v;
1756       const size_t n_cats =  categoricals_n_count (cmd->cats, i);
1757
1758       for (v = 0; v < cmd->n_dep_vars; ++v)
1759         {
1760           int grp;
1761           for (grp = 0; grp < n_cats; ++grp)
1762             {
1763               int q;
1764               const struct exploratory_stats *es =
1765                 categoricals_get_user_data_by_category_real (cmd->cats, i, grp);
1766
1767               struct order_stats *os = &es[v].hinges->parent;
1768               struct statistic  *stat = &os->parent;
1769               stat->destroy (stat);
1770
1771               for (q = 0; q < 3 ; q++)
1772                 {
1773                   os = &es[v].quartiles[q]->parent;
1774                   stat = &os->parent;
1775                   stat->destroy (stat);
1776                 }
1777
1778               for (q = 0; q < cmd->n_percentiles ; q++)
1779                 {
1780                   os = &es[v].percentiles[q]->parent;
1781                   stat = &os->parent;
1782                   stat->destroy (stat);
1783                 }
1784
1785               os = &es[v].trimmed_mean->parent;
1786               stat = &os->parent;
1787               stat->destroy (stat);
1788
1789               os = &es[v].np->parent;
1790               if (os)
1791                 {
1792                   stat = &os->parent;
1793                   stat->destroy (stat);
1794                 }
1795
1796               statistic_destroy (&es[v].histogram->parent);
1797               moments_destroy (es[v].mom);
1798
1799               casereader_destroy (es[v].sorted_reader);
1800             }
1801         }
1802     }
1803 }
1804
1805
1806 static void
1807 run_examine (struct examine *cmd, struct casereader *input)
1808 {
1809   int i;
1810   struct ccase *c;
1811   struct casereader *reader;
1812
1813   struct payload payload;
1814   payload.create = create_n;
1815   payload.update = update_n;
1816   payload.calculate = calculate_n;
1817   payload.destroy = NULL;
1818   
1819   cmd->wv = dict_get_weight (cmd->dict);
1820
1821   cmd->cats
1822     = categoricals_create (cmd->iacts, cmd->n_iacts,  
1823                            cmd->wv, cmd->dep_excl, cmd->fctr_excl);
1824
1825   categoricals_set_payload (cmd->cats, &payload, cmd, NULL);
1826
1827   if (cmd->id_var == NULL)
1828     {
1829       struct ccase *c = casereader_peek (input,  0);
1830
1831       cmd->id_idx = case_get_value_cnt (c);
1832       input = casereader_create_arithmetic_sequence (input, 1.0, 1.0);
1833
1834       case_unref (c);
1835     }
1836
1837   for (reader = input;
1838        (c = casereader_read (reader)) != NULL; case_unref (c))
1839     {
1840       categoricals_update (cmd->cats, c);
1841     }
1842   casereader_destroy (reader);
1843   categoricals_done (cmd->cats);
1844
1845   for (i = 0; i < cmd->n_iacts; ++i)
1846     {
1847       summary_report (cmd, i);
1848
1849       const size_t n_cats =  categoricals_n_count (cmd->cats, i);
1850       if (n_cats == 0)
1851         continue;
1852
1853       if (cmd->disp_extremes > 0)
1854         extremes_report (cmd, i);
1855
1856       if (cmd->n_percentiles > 0)
1857         percentiles_report (cmd, i);
1858
1859       if (cmd->boxplot)
1860         {
1861           switch (cmd->boxplot_mode)
1862             {
1863             case BP_GROUPS:
1864               show_boxplot_grouped (cmd, i);
1865               break;
1866             case BP_VARIABLES:
1867               show_boxplot_variabled (cmd, i);
1868               break;
1869             default:
1870               NOT_REACHED ();
1871               break;
1872             }
1873         }
1874
1875       if (cmd->histogramplot)
1876         show_histogram (cmd, i);
1877
1878       if (cmd->npplot)
1879         show_npplot (cmd, i);
1880
1881       if (cmd->spreadlevelplot)
1882         show_spreadlevel (cmd, i);
1883
1884       if (cmd->descriptives)
1885         descriptives_report (cmd, i);
1886     }
1887
1888   cleanup_exploratory_stats (cmd);
1889   categoricals_destroy (cmd->cats);
1890 }
1891
1892
1893 int
1894 cmd_examine (struct lexer *lexer, struct dataset *ds)
1895 {
1896   int i;
1897   bool nototals_seen = false;
1898   bool totals_seen = false;
1899
1900   struct interaction **iacts_mem = NULL;
1901   struct examine examine;
1902   bool percentiles_seen = false;
1903
1904   examine.missing_pw = false;
1905   examine.disp_extremes = 0;
1906   examine.calc_extremes = 0;
1907   examine.descriptives = false;
1908   examine.conf = 0.95;
1909   examine.pc_alg = PC_HAVERAGE;
1910   examine.ptiles = NULL;
1911   examine.n_percentiles = 0;
1912   examine.id_idx = -1;
1913   examine.id_width = 0;
1914   examine.id_var = NULL;
1915   examine.boxplot_mode = BP_GROUPS;
1916   
1917   examine.ex_proto = caseproto_create ();
1918
1919   examine.pool = pool_create ();
1920
1921   /* Allocate space for the first interaction.
1922      This is interaction is an empty one (for the totals).
1923      If no totals are requested, we will simply ignore this
1924      interaction.
1925   */
1926   examine.n_iacts = 1;
1927   examine.iacts = iacts_mem = pool_zalloc (examine.pool, sizeof (struct interaction *));
1928   examine.iacts[0] = interaction_create (NULL);
1929
1930   examine.dep_excl = MV_ANY;
1931   examine.fctr_excl = MV_ANY;
1932   examine.histogramplot = false;
1933   examine.npplot = false;
1934   examine.boxplot = false;
1935   examine.spreadlevelplot = false;
1936   examine.sl_power = 0;
1937   
1938   examine.dict = dataset_dict (ds);
1939
1940   /* Accept an optional, completely pointless "/VARIABLES=" */
1941   lex_match (lexer, T_SLASH);
1942   if (lex_match_id  (lexer, "VARIABLES"))
1943     {
1944       if (! lex_force_match (lexer, T_EQUALS) )
1945         goto error;
1946     }
1947
1948   if (!parse_variables_const (lexer, examine.dict,
1949                               &examine.dep_vars, &examine.n_dep_vars,
1950                               PV_NO_DUPLICATE | PV_NUMERIC))
1951     goto error;
1952
1953   if (lex_match (lexer, T_BY))
1954     {
1955       struct interaction *iact = NULL;
1956       do
1957         {
1958           iact = parse_interaction (lexer, &examine);
1959           if (iact)
1960             {
1961               examine.n_iacts++;
1962               iacts_mem = 
1963                 pool_nrealloc (examine.pool, iacts_mem,
1964                                examine.n_iacts,
1965                                sizeof (*iacts_mem));
1966               
1967               iacts_mem[examine.n_iacts - 1] = iact;
1968             }
1969         }
1970       while (iact);
1971     }
1972
1973
1974   while (lex_token (lexer) != T_ENDCMD)
1975     {
1976       lex_match (lexer, T_SLASH);
1977
1978       if (lex_match_id (lexer, "STATISTICS"))
1979         {
1980           lex_match (lexer, T_EQUALS);
1981
1982           while (lex_token (lexer) != T_ENDCMD
1983                  && lex_token (lexer) != T_SLASH)
1984             {
1985               if (lex_match_id (lexer, "DESCRIPTIVES"))
1986                 {
1987                   examine.descriptives = true;
1988                 }
1989               else if (lex_match_id (lexer, "EXTREME"))
1990                 {
1991                   int extr = 5;
1992                   if (lex_match (lexer, T_LPAREN))
1993                     {
1994                       extr = lex_integer (lexer);
1995
1996                       if (extr < 0)
1997                         {
1998                           msg (MW, _("%s may not be negative. Using default value (%g)."), "EXTREME", 5.0);
1999                           extr = 5;
2000                         }
2001
2002                       lex_get (lexer);
2003                       if (! lex_force_match (lexer, T_RPAREN))
2004                         goto error;
2005                     }
2006                   examine.disp_extremes  = extr;
2007                 }
2008               else if (lex_match_id (lexer, "NONE"))
2009                 {
2010                 }
2011               else if (lex_match (lexer, T_ALL))
2012                 {
2013                   if (examine.disp_extremes == 0)
2014                     examine.disp_extremes = 5;
2015                 }
2016               else
2017                 {
2018                   lex_error (lexer, NULL);
2019                   goto error;
2020                 }
2021             }
2022         }
2023       else if (lex_match_id (lexer, "PERCENTILES"))
2024         {
2025           percentiles_seen = true;
2026           if (lex_match (lexer, T_LPAREN))
2027             {
2028               while (lex_is_number (lexer))
2029                 {
2030                   double p = lex_number (lexer);
2031                   
2032                   if ( p <= 0 || p >= 100.0)
2033                     {
2034                       lex_error (lexer,
2035                                  _("Percentiles must lie in the range (0, 100)"));
2036                       goto error;
2037                     }
2038
2039                   examine.n_percentiles++;
2040                   examine.ptiles =
2041                     xrealloc (examine.ptiles,
2042                               sizeof (*examine.ptiles) *
2043                               examine.n_percentiles);
2044
2045                   examine.ptiles[examine.n_percentiles - 1] = p;
2046
2047                   lex_get (lexer);
2048                   lex_match (lexer, T_COMMA);
2049                 }
2050               if (!lex_force_match (lexer, T_RPAREN))
2051                 goto error;
2052             }
2053
2054           lex_match (lexer, T_EQUALS);
2055
2056           while (lex_token (lexer) != T_ENDCMD
2057                  && lex_token (lexer) != T_SLASH)
2058             {
2059               if (lex_match_id (lexer, "HAVERAGE"))
2060                 {
2061                   examine.pc_alg = PC_HAVERAGE;
2062                 }
2063               else if (lex_match_id (lexer, "WAVERAGE"))
2064                 {
2065                   examine.pc_alg = PC_WAVERAGE;
2066                 }
2067               else if (lex_match_id (lexer, "ROUND"))
2068                 {
2069                   examine.pc_alg = PC_ROUND;
2070                 }
2071               else if (lex_match_id (lexer, "EMPIRICAL"))
2072                 {
2073                   examine.pc_alg = PC_EMPIRICAL;
2074                 }
2075               else if (lex_match_id (lexer, "AEMPIRICAL"))
2076                 {
2077                   examine.pc_alg = PC_AEMPIRICAL;
2078                 }
2079               else if (lex_match_id (lexer, "NONE"))
2080                 {
2081                   examine.pc_alg = PC_NONE;
2082                 }
2083               else
2084                 {
2085                   lex_error (lexer, NULL);
2086                   goto error;
2087                 }
2088             }
2089         }
2090       else if (lex_match_id (lexer, "TOTAL"))
2091         {
2092           totals_seen = true;
2093         }
2094       else if (lex_match_id (lexer, "NOTOTAL"))
2095         {
2096           nototals_seen = true;
2097         }
2098       else if (lex_match_id (lexer, "MISSING"))
2099         {
2100           lex_match (lexer, T_EQUALS);
2101
2102           while (lex_token (lexer) != T_ENDCMD
2103                  && lex_token (lexer) != T_SLASH)
2104             {
2105               if (lex_match_id (lexer, "LISTWISE"))
2106                 {
2107                   examine.missing_pw = false;
2108                 }
2109               else if (lex_match_id (lexer, "PAIRWISE"))
2110                 {
2111                   examine.missing_pw = true;
2112                 }
2113               else if (lex_match_id (lexer, "EXCLUDE"))
2114                 {
2115                   examine.dep_excl = MV_ANY;
2116                 }
2117               else if (lex_match_id (lexer, "INCLUDE"))
2118                 {
2119                   examine.dep_excl = MV_SYSTEM;
2120                 }
2121               else if (lex_match_id (lexer, "REPORT"))
2122                 {
2123                   examine.fctr_excl = MV_NEVER;
2124                 }
2125               else if (lex_match_id (lexer, "NOREPORT"))
2126                 {
2127                   examine.fctr_excl = MV_ANY;
2128                 }
2129               else
2130                 {
2131                   lex_error (lexer, NULL);
2132                   goto error;
2133                 }
2134             }
2135         }
2136       else if (lex_match_id (lexer, "COMPARE"))
2137         {
2138           lex_match (lexer, T_EQUALS);
2139           if (lex_match_id (lexer, "VARIABLES"))
2140             {
2141               examine.boxplot_mode = BP_VARIABLES;
2142             }
2143           else if (lex_match_id (lexer, "GROUPS"))
2144             {
2145               examine.boxplot_mode = BP_GROUPS;
2146             }
2147           else
2148             {
2149               lex_error (lexer, NULL);
2150               goto error;
2151             }
2152         }
2153       else if (lex_match_id (lexer, "PLOT"))
2154         {
2155           lex_match (lexer, T_EQUALS);
2156
2157           while (lex_token (lexer) != T_ENDCMD
2158                  && lex_token (lexer) != T_SLASH)
2159             {
2160               if (lex_match_id (lexer, "BOXPLOT"))
2161                 {
2162                   examine.boxplot = true;
2163                 }
2164               else if (lex_match_id (lexer, "NPPLOT"))
2165                 {
2166                   examine.npplot = true;
2167                 }
2168               else if (lex_match_id (lexer, "HISTOGRAM"))
2169                 {
2170                   examine.histogramplot = true;
2171                 }
2172               else if (lex_match_id (lexer, "SPREADLEVEL"))
2173                 {
2174                   examine.spreadlevelplot = true;
2175                   examine.sl_power = 0;
2176                   if (lex_match (lexer, T_LPAREN) && lex_force_int (lexer))
2177                     {
2178                       examine.sl_power = lex_integer (lexer);
2179
2180                       lex_get (lexer);
2181                       if (! lex_force_match (lexer, T_RPAREN))
2182                         goto error;
2183                     }
2184                 }
2185               else if (lex_match_id (lexer, "NONE"))
2186                 {
2187                   examine.histogramplot = false;
2188                   examine.npplot = false;
2189                   examine.boxplot = false;
2190                 }
2191               else if (lex_match (lexer, T_ALL))
2192                 {
2193                   examine.histogramplot = true;
2194                   examine.npplot = true;
2195                   examine.boxplot = true;
2196                 }
2197               else 
2198                 {
2199                   lex_error (lexer, NULL);
2200                   goto error;
2201                 }
2202               lex_match (lexer, T_COMMA);
2203             }          
2204         }
2205       else if (lex_match_id (lexer, "CINTERVAL"))
2206         {
2207           if ( !lex_force_num (lexer))
2208             goto error;
2209         
2210           examine.conf = lex_number (lexer);
2211           lex_get (lexer);
2212         }
2213       else if (lex_match_id (lexer, "ID"))
2214         {
2215           lex_match (lexer, T_EQUALS);
2216
2217           examine.id_var = parse_variable_const (lexer, examine.dict);
2218         }
2219       else
2220         {
2221           lex_error (lexer, NULL);
2222           goto error;
2223         }
2224     }
2225
2226
2227   if ( totals_seen && nototals_seen)
2228     {
2229       msg (SE, _("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
2230       goto error;
2231     }
2232
2233   /* If totals have been requested or if there are no factors
2234      in this analysis, then the totals need to be included. */
2235   if ( !nototals_seen || examine.n_iacts == 1)
2236     {
2237       examine.iacts = &iacts_mem[0];
2238     }
2239   else
2240     {
2241       examine.n_iacts--;
2242       examine.iacts = &iacts_mem[1];
2243       interaction_destroy (iacts_mem[0]);
2244     }
2245
2246
2247   if ( examine.id_var )
2248     {
2249       examine.id_idx = var_get_case_index (examine.id_var);
2250       examine.id_width = var_get_width (examine.id_var);
2251     }
2252
2253   examine.ex_proto = caseproto_add_width (examine.ex_proto, 0); /* value */
2254   examine.ex_proto = caseproto_add_width (examine.ex_proto, examine.id_width);   /* id */
2255   examine.ex_proto = caseproto_add_width (examine.ex_proto, 0); /* weight */
2256
2257
2258   if (examine.disp_extremes > 0)
2259     {
2260       examine.calc_extremes = examine.disp_extremes;
2261     }
2262
2263   if (examine.descriptives && examine.calc_extremes == 0)
2264     {
2265       /* Descriptives always displays the max and min */
2266       examine.calc_extremes = 1;
2267     }
2268
2269   if (percentiles_seen && examine.n_percentiles == 0)
2270     {
2271       examine.n_percentiles = 7;
2272       examine.ptiles = xcalloc (examine.n_percentiles,
2273                                 sizeof (*examine.ptiles));
2274
2275       examine.ptiles[0] = 5;
2276       examine.ptiles[1] = 10;
2277       examine.ptiles[2] = 25;
2278       examine.ptiles[3] = 50;
2279       examine.ptiles[4] = 75;
2280       examine.ptiles[5] = 90;
2281       examine.ptiles[6] = 95;
2282     }
2283
2284   assert (examine.calc_extremes >= examine.disp_extremes);
2285   {
2286     struct casegrouper *grouper;
2287     struct casereader *group;
2288     bool ok;
2289     
2290     grouper = casegrouper_create_splits (proc_open (ds), examine.dict);
2291     while (casegrouper_get_next_group (grouper, &group))
2292       run_examine (&examine, group);
2293     ok = casegrouper_destroy (grouper);
2294     ok = proc_commit (ds) && ok;
2295   }
2296
2297   caseproto_unref (examine.ex_proto);
2298
2299   for (i = 0; i < examine.n_iacts; ++i)
2300     interaction_destroy (examine.iacts[i]);
2301   free (examine.ptiles);
2302   free (examine.dep_vars);
2303   pool_destroy (examine.pool);
2304
2305   return CMD_SUCCESS;
2306
2307  error:
2308   caseproto_unref (examine.ex_proto);
2309   examine.iacts = iacts_mem;
2310   for (i = 0; i < examine.n_iacts; ++i)
2311     interaction_destroy (examine.iacts[i]);
2312   free (examine.dep_vars);
2313   free (examine.ptiles);
2314   pool_destroy (examine.pool);
2315
2316   return CMD_FAILURE;
2317 }