Separate table functions that format their arguments from those that don't.
[pspp-builds.git] / src / language / stats / examine.q
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2004, 2008, 2009 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <gsl/gsl_cdf.h>
20 #include <libpspp/message.h>
21 #include <math.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <math/sort.h>
26 #include <math/order-stats.h>
27 #include <math/percentiles.h>
28 #include <math/tukey-hinges.h>
29 #include <math/box-whisker.h>
30 #include <math/trimmed-mean.h>
31 #include <math/extrema.h>
32 #include <math/np.h>
33 #include <data/case.h>
34 #include <data/casegrouper.h>
35 #include <data/casereader.h>
36 #include <data/casewriter.h>
37 #include <data/dictionary.h>
38 #include <data/procedure.h>
39 #include <data/subcase.h>
40 #include <data/value-labels.h>
41 #include <data/variable.h>
42 #include <language/command.h>
43 #include <language/dictionary/split-file.h>
44 #include <language/lexer/lexer.h>
45 #include <libpspp/compiler.h>
46 #include <libpspp/hash.h>
47 #include <libpspp/message.h>
48 #include <libpspp/misc.h>
49 #include <libpspp/str.h>
50 #include <math/moments.h>
51 #include <output/charts/box-whisker.h>
52 #include <output/charts/cartesian.h>
53 #include <output/manager.h>
54 #include <output/table.h>
55
56 #include "minmax.h"
57 #include "xalloc.h"
58
59 #include "gettext.h"
60 #define _(msgid) gettext (msgid)
61 #define N_(msgid) msgid
62
63 /* (headers) */
64 #include <output/chart.h>
65 #include <output/charts/plot-hist.h>
66 #include <output/charts/plot-chart.h>
67 #include <math/histogram.h>
68
69 /* (specification)
70    "EXAMINE" (xmn_):
71    *^variables=custom;
72    +total=custom;
73    +nototal=custom;
74    missing=miss:pairwise/!listwise,
75    rep:report/!noreport,
76    incl:include/!exclude;
77    +compare=cmp:variables/!groups;
78    +percentiles=custom;
79    +id=var;
80    +plot[plt_]=stemleaf,boxplot,npplot,:spreadlevel(*d:n),histogram,all,none;
81    +cinterval=double;
82    +statistics[st_]=descriptives,:extreme(*d:n),all,none.
83 */
84
85 /* (declarations) */
86
87 /* (functions) */
88
89
90 static struct cmd_examine cmd;
91
92 static const struct variable **dependent_vars;
93 static size_t n_dependent_vars;
94
95 /* PERCENTILES */
96
97 static subc_list_double percentile_list;
98 static enum pc_alg percentile_algorithm;
99
100 struct factor_metrics
101 {
102   struct moments1 *moments;
103
104   struct percentile **ptl;
105   size_t n_ptiles;
106
107   struct statistic *tukey_hinges;
108   struct statistic *box_whisker;
109   struct statistic *trimmed_mean;
110   struct statistic *histogram;
111   struct order_stats *np;
112
113   /* Three quartiles indexing into PTL */
114   struct percentile **quartiles;
115
116   /* A reader sorted in ASCENDING order */
117   struct casereader *up_reader;
118
119   /* The minimum value of all the weights */
120   double cmin;
121
122   /* Sum of all weights, including those for missing values */
123   double n;
124
125   /* Sum of weights of non_missing values */
126   double n_valid;
127
128   double mean;
129
130   double variance;
131
132   double skewness;
133
134   double kurtosis;
135
136   double se_mean;
137
138   struct extrema *minima;
139   struct extrema *maxima;
140 };
141
142 struct factor_result
143 {
144   struct ll ll;
145
146   union value value[2];
147
148   /* An array of factor metrics, one for each variable */
149   struct factor_metrics *metrics;
150 };
151
152 struct xfactor
153 {
154   /* We need to make a list of this structure */
155   struct ll ll;
156
157   /* The independent variable */
158   const struct variable const* indep_var[2];
159
160   /* A list of results for this factor */
161   struct ll_list result_list ;
162 };
163
164
165 static void
166 factor_destroy (struct xfactor *fctr)
167 {
168   struct ll *ll = ll_head (&fctr->result_list);
169   while (ll != ll_null (&fctr->result_list))
170     {
171       int v;
172       struct factor_result *result =
173         ll_data (ll, struct factor_result, ll);
174       int i;
175
176       for (v = 0; v < n_dependent_vars; ++v)
177         {
178           int i;
179           moments1_destroy (result->metrics[v].moments);
180           extrema_destroy (result->metrics[v].minima);
181           extrema_destroy (result->metrics[v].maxima);
182           statistic_destroy (result->metrics[v].trimmed_mean);
183           statistic_destroy (result->metrics[v].tukey_hinges);
184           statistic_destroy (result->metrics[v].box_whisker);
185           statistic_destroy (result->metrics[v].histogram);
186           for (i = 0 ; i < result->metrics[v].n_ptiles; ++i)
187             statistic_destroy ((struct statistic *) result->metrics[v].ptl[i]);
188           free (result->metrics[v].ptl);
189           free (result->metrics[v].quartiles);
190           casereader_destroy (result->metrics[v].up_reader);
191         }
192
193       for (i = 0; i < 2; i++)
194         if (fctr->indep_var[i])
195           value_destroy (&result->value[i],
196                          var_get_width (fctr->indep_var[i]));
197       free (result->metrics);
198       ll = ll_next (ll);
199       free (result);
200     }
201 }
202
203 static struct xfactor level0_factor;
204 static struct ll_list factor_list;
205
206 /* Parse the clause specifying the factors */
207 static int examine_parse_independent_vars (struct lexer *lexer,
208                                            const struct dictionary *dict,
209                                            struct cmd_examine *cmd);
210
211 /* Output functions */
212 static void show_summary (const struct variable **dependent_var, int n_dep_var,
213                           const struct dictionary *dict,
214                           const struct xfactor *f);
215
216
217 static void show_descriptives (const struct variable **dependent_var,
218                                int n_dep_var,
219                                const struct xfactor *f);
220
221
222 static void show_percentiles (const struct variable **dependent_var,
223                                int n_dep_var,
224                                const struct xfactor *f);
225
226
227 static void show_extremes (const struct variable **dependent_var,
228                            int n_dep_var,
229                            const struct xfactor *f);
230
231
232
233
234 /* Per Split function */
235 static void run_examine (struct cmd_examine *, struct casereader *,
236                          struct dataset *);
237
238 static void output_examine (const struct dictionary *dict);
239
240
241 void factor_calc (const struct ccase *c, int case_no,
242                   double weight, bool case_missing);
243
244
245 /* Represent a factor as a string, so it can be
246    printed in a human readable fashion */
247 static void factor_to_string (const struct xfactor *fctr,
248                               const struct factor_result *result,
249                               struct string *str);
250
251 /* Represent a factor as a string, so it can be
252    printed in a human readable fashion,
253    but sacrificing some readablility for the sake of brevity */
254 static void
255 factor_to_string_concise (const struct xfactor *fctr,
256                           const struct factor_result *result,
257                           struct string *str
258                           );
259
260
261
262 /* Categories of missing values to exclude. */
263 static enum mv_class exclude_values;
264
265 int
266 cmd_examine (struct lexer *lexer, struct dataset *ds)
267 {
268   struct casegrouper *grouper;
269   struct casereader *group;
270   bool ok;
271
272   subc_list_double_create (&percentile_list);
273   percentile_algorithm = PC_HAVERAGE;
274
275   ll_init (&factor_list);
276
277   if ( !parse_examine (lexer, ds, &cmd, NULL) )
278     {
279       subc_list_double_destroy (&percentile_list);
280       return CMD_FAILURE;
281     }
282
283   /* If /MISSING=INCLUDE is set, then user missing values are ignored */
284   exclude_values = cmd.incl == XMN_INCLUDE ? MV_SYSTEM : MV_ANY;
285
286   if ( cmd.st_n == SYSMIS )
287     cmd.st_n = 5;
288
289   if ( ! cmd.sbc_cinterval)
290     cmd.n_cinterval[0] = 95.0;
291
292   /* If descriptives have been requested, make sure the
293      quartiles are calculated */
294   if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES] )
295     {
296       subc_list_double_push (&percentile_list, 25);
297       subc_list_double_push (&percentile_list, 50);
298       subc_list_double_push (&percentile_list, 75);
299     }
300
301   grouper = casegrouper_create_splits (proc_open (ds), dataset_dict (ds));
302
303   while (casegrouper_get_next_group (grouper, &group))
304     {
305       struct casereader *reader =
306         casereader_create_arithmetic_sequence (group, 1, 1);
307
308       run_examine (&cmd, reader, ds);
309     }
310
311   ok = casegrouper_destroy (grouper);
312   ok = proc_commit (ds) && ok;
313
314   if ( dependent_vars )
315     free (dependent_vars);
316
317   subc_list_double_destroy (&percentile_list);
318
319   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
320 };
321
322
323 /* Plot the normal and detrended normal plots for RESULT.
324    Label the plots with LABEL */
325 static void
326 np_plot (struct np *np, const char *label)
327 {
328   double yfirst = 0, ylast = 0;
329
330   double x_lower;
331   double x_upper;
332   double slack;
333
334   /* Normal Plot */
335   struct chart *np_chart;
336
337   /* Detrended Normal Plot */
338   struct chart *dnp_chart;
339
340   /* The slope and intercept of the ideal normal probability line */
341   const double slope = 1.0 / np->stddev;
342   const double intercept = -np->mean / np->stddev;
343
344   if ( np->n < 1.0 )
345     {
346       msg (MW, _("Not creating plot because data set is empty."));
347       return ;
348     }
349
350   np_chart = chart_create ();
351   dnp_chart = chart_create ();
352
353   if ( !np_chart || ! dnp_chart )
354     return ;
355
356   chart_write_title (np_chart, _("Normal Q-Q Plot of %s"), label);
357   chart_write_xlabel (np_chart, _("Observed Value"));
358   chart_write_ylabel (np_chart, _("Expected Normal"));
359
360   chart_write_title (dnp_chart, _("Detrended Normal Q-Q Plot of %s"),
361                      label);
362   chart_write_xlabel (dnp_chart, _("Observed Value"));
363   chart_write_ylabel (dnp_chart, _("Dev from Normal"));
364
365   yfirst = gsl_cdf_ugaussian_Pinv (1 / (np->n + 1));
366   ylast = gsl_cdf_ugaussian_Pinv (np->n / (np->n + 1));
367
368   /* Need to make sure that both the scatter plot and the ideal fit into the
369      plot */
370   x_lower = MIN (np->y_min, (yfirst - intercept) / slope) ;
371   x_upper = MAX (np->y_max, (ylast  - intercept) / slope) ;
372   slack = (x_upper - x_lower)  * 0.05 ;
373
374   chart_write_xscale (np_chart, x_lower - slack, x_upper + slack, 5);
375   chart_write_xscale (dnp_chart, np->y_min, np->y_max, 5);
376
377   chart_write_yscale (np_chart, yfirst, ylast, 5);
378   chart_write_yscale (dnp_chart, np->dns_min, np->dns_max, 5);
379
380   {
381     struct casereader *reader = casewriter_make_reader (np->writer);
382     struct ccase *c;
383     while ((c = casereader_read (reader)) != NULL)
384       {
385         chart_datum (np_chart, 0, case_data_idx (c, NP_IDX_Y)->f, case_data_idx (c, NP_IDX_NS)->f);
386         chart_datum (dnp_chart, 0, case_data_idx (c, NP_IDX_Y)->f, case_data_idx (c, NP_IDX_DNS)->f);
387
388         case_unref (c);
389       }
390     casereader_destroy (reader);
391   }
392
393   chart_line (dnp_chart, 0, 0, np->y_min, np->y_max , CHART_DIM_X);
394   chart_line (np_chart, slope, intercept, yfirst, ylast , CHART_DIM_Y);
395
396   chart_submit (np_chart);
397   chart_submit (dnp_chart);
398 }
399
400
401 static void
402 show_npplot (const struct variable **dependent_var,
403              int n_dep_var,
404              const struct xfactor *fctr)
405 {
406   int v;
407
408   for (v = 0; v < n_dep_var; ++v)
409     {
410       struct ll *ll;
411       for (ll = ll_head (&fctr->result_list);
412            ll != ll_null (&fctr->result_list);
413            ll = ll_next (ll))
414         {
415           struct string str;
416           const struct factor_result *result =
417             ll_data (ll, struct factor_result, ll);
418
419           ds_init_empty (&str);
420           ds_put_format (&str, "%s ", var_get_name (dependent_var[v]));
421
422           factor_to_string (fctr, result, &str);
423
424           np_plot ((struct np*) result->metrics[v].np, ds_cstr(&str));
425
426           statistic_destroy ((struct statistic *)result->metrics[v].np);
427
428           ds_destroy (&str);
429         }
430     }
431 }
432
433
434 static void
435 show_histogram (const struct variable **dependent_var,
436                 int n_dep_var,
437                 const struct xfactor *fctr)
438 {
439   int v;
440
441   for (v = 0; v < n_dep_var; ++v)
442     {
443       struct ll *ll;
444       for (ll = ll_head (&fctr->result_list);
445            ll != ll_null (&fctr->result_list);
446            ll = ll_next (ll))
447         {
448           struct string str;
449           const struct factor_result *result =
450             ll_data (ll, struct factor_result, ll);
451
452           ds_init_empty (&str);
453           ds_put_format (&str, "%s ", var_get_name (dependent_var[v]));
454
455           factor_to_string (fctr, result, &str);
456
457           histogram_plot ((struct histogram *) result->metrics[v].histogram,
458                           ds_cstr (&str),
459                           (struct moments1 *) result->metrics[v].moments);
460
461           ds_destroy (&str);
462         }
463     }
464 }
465
466
467
468 static void
469 show_boxplot_groups (const struct variable **dependent_var,
470                      int n_dep_var,
471                      const struct xfactor *fctr)
472 {
473   int v;
474
475   for (v = 0; v < n_dep_var; ++v)
476     {
477       struct ll *ll;
478       int f = 0;
479       struct chart *ch = chart_create ();
480       double y_min = DBL_MAX;
481       double y_max = -DBL_MAX;
482
483       for (ll = ll_head (&fctr->result_list);
484            ll != ll_null (&fctr->result_list);
485            ll = ll_next (ll))
486         {
487           const struct extremum  *max, *min;
488           const struct factor_result *result =
489             ll_data (ll, struct factor_result, ll);
490
491           const struct ll_list *max_list =
492             extrema_list (result->metrics[v].maxima);
493
494           const struct ll_list *min_list =
495             extrema_list (result->metrics[v].minima);
496
497           if ( ll_is_empty (max_list))
498             {
499               msg (MW, _("Not creating plot because data set is empty."));
500               continue;
501             }
502
503           max = (const struct extremum *)
504             ll_data (ll_head(max_list), struct extremum, ll);
505
506           min = (const struct extremum *)
507             ll_data (ll_head (min_list), struct extremum, ll);
508
509           y_max = MAX (y_max, max->value);
510           y_min = MIN (y_min, min->value);
511         }
512
513       boxplot_draw_yscale (ch, y_max, y_min);
514
515       if ( fctr->indep_var[0])
516         chart_write_title (ch, _("Boxplot of %s vs. %s"),
517                            var_to_string (dependent_var[v]),
518                            var_to_string (fctr->indep_var[0]) );
519       else
520         chart_write_title (ch, _("Boxplot of %s"),
521                            var_to_string (dependent_var[v]));
522
523       for (ll = ll_head (&fctr->result_list);
524            ll != ll_null (&fctr->result_list);
525            ll = ll_next (ll))
526         {
527           const struct factor_result *result =
528             ll_data (ll, struct factor_result, ll);
529
530           struct string str;
531           const double box_width = (ch->data_right - ch->data_left)
532             / (ll_count (&fctr->result_list) * 2.0 ) ;
533
534           const double box_centre = (f++ * 2 + 1) * box_width + ch->data_left;
535
536           ds_init_empty (&str);
537           factor_to_string_concise (fctr, result, &str);
538
539           boxplot_draw_boxplot (ch,
540                                 box_centre, box_width,
541                                 (const struct box_whisker *)
542                                  result->metrics[v].box_whisker,
543                                 ds_cstr (&str));
544
545           ds_destroy (&str);
546         }
547
548       chart_submit (ch);
549     }
550 }
551
552
553
554 static void
555 show_boxplot_variables (const struct variable **dependent_var,
556                         int n_dep_var,
557                         const struct xfactor *fctr
558                         )
559
560 {
561   int v;
562   struct ll *ll;
563   const struct ll_list *result_list = &fctr->result_list;
564
565   for (ll = ll_head (result_list);
566        ll != ll_null (result_list);
567        ll = ll_next (ll))
568
569     {
570       struct string title;
571       struct chart *ch = chart_create ();
572       double y_min = DBL_MAX;
573       double y_max = -DBL_MAX;
574
575       const struct factor_result *result =
576         ll_data (ll, struct factor_result, ll);
577
578       const double box_width = (ch->data_right - ch->data_left)
579         / (n_dep_var * 2.0 ) ;
580
581       for (v = 0; v < n_dep_var; ++v)
582         {
583           const struct ll *max_ll =
584             ll_head (extrema_list (result->metrics[v].maxima));
585           const struct ll *min_ll =
586             ll_head (extrema_list (result->metrics[v].minima));
587
588           const struct extremum  *max =
589             (const struct extremum *) ll_data (max_ll, struct extremum, ll);
590
591           const struct extremum  *min =
592             (const struct extremum *) ll_data (min_ll, struct extremum, ll);
593
594           y_max = MAX (y_max, max->value);
595           y_min = MIN (y_min, min->value);
596         }
597
598
599       boxplot_draw_yscale (ch, y_max, y_min);
600
601       ds_init_empty (&title);
602       factor_to_string (fctr, result, &title);
603
604 #if 0
605       ds_put_format (&title, "%s = ", var_get_name (fctr->indep_var[0]));
606       var_append_value_name (fctr->indep_var[0], &result->value[0], &title);
607 #endif
608
609       chart_write_title (ch, ds_cstr (&title));
610       ds_destroy (&title);
611
612       for (v = 0; v < n_dep_var; ++v)
613         {
614           struct string str;
615           const double box_centre = (v * 2 + 1) * box_width + ch->data_left;
616
617           ds_init_empty (&str);
618           ds_init_cstr (&str, var_get_name (dependent_var[v]));
619
620           boxplot_draw_boxplot (ch,
621                                 box_centre, box_width,
622                                 (const struct box_whisker *) result->metrics[v].box_whisker,
623                                 ds_cstr (&str));
624
625           ds_destroy (&str);
626         }
627
628       chart_submit (ch);
629     }
630 }
631
632
633 /* Show all the appropriate tables */
634 static void
635 output_examine (const struct dictionary *dict)
636 {
637   struct ll *ll;
638
639   show_summary (dependent_vars, n_dependent_vars, dict, &level0_factor);
640
641   if ( cmd.a_statistics[XMN_ST_EXTREME] )
642     show_extremes (dependent_vars, n_dependent_vars, &level0_factor);
643
644   if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES] )
645     show_descriptives (dependent_vars, n_dependent_vars, &level0_factor);
646
647   if ( cmd.sbc_percentiles)
648     show_percentiles (dependent_vars, n_dependent_vars, &level0_factor);
649
650   if ( cmd.sbc_plot)
651     {
652       if (cmd.a_plot[XMN_PLT_BOXPLOT])
653         show_boxplot_groups (dependent_vars, n_dependent_vars, &level0_factor);
654
655       if (cmd.a_plot[XMN_PLT_HISTOGRAM])
656         show_histogram (dependent_vars, n_dependent_vars, &level0_factor);
657
658       if (cmd.a_plot[XMN_PLT_NPPLOT])
659         show_npplot (dependent_vars, n_dependent_vars, &level0_factor);
660     }
661
662   for (ll = ll_head (&factor_list);
663        ll != ll_null (&factor_list); ll = ll_next (ll))
664     {
665       struct xfactor *factor = ll_data (ll, struct xfactor, ll);
666       show_summary (dependent_vars, n_dependent_vars, dict, factor);
667
668       if ( cmd.a_statistics[XMN_ST_EXTREME] )
669         show_extremes (dependent_vars, n_dependent_vars, factor);
670
671       if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES] )
672         show_descriptives (dependent_vars, n_dependent_vars, factor);
673
674       if ( cmd.sbc_percentiles)
675         show_percentiles (dependent_vars, n_dependent_vars, factor);
676
677       if (cmd.a_plot[XMN_PLT_BOXPLOT] &&
678           cmd.cmp == XMN_GROUPS)
679         show_boxplot_groups (dependent_vars, n_dependent_vars, factor);
680
681
682       if (cmd.a_plot[XMN_PLT_BOXPLOT] &&
683           cmd.cmp == XMN_VARIABLES)
684         show_boxplot_variables (dependent_vars, n_dependent_vars,
685                                 factor);
686
687       if (cmd.a_plot[XMN_PLT_HISTOGRAM])
688         show_histogram (dependent_vars, n_dependent_vars, factor);
689
690       if (cmd.a_plot[XMN_PLT_NPPLOT])
691         show_npplot (dependent_vars, n_dependent_vars, factor);
692     }
693 }
694
695 /* Parse the PERCENTILES subcommand */
696 static int
697 xmn_custom_percentiles (struct lexer *lexer, struct dataset *ds UNUSED,
698                         struct cmd_examine *p UNUSED, void *aux UNUSED)
699 {
700   lex_match (lexer, '=');
701
702   lex_match (lexer, '(');
703
704   while ( lex_is_number (lexer) )
705     {
706       subc_list_double_push (&percentile_list, lex_number (lexer));
707
708       lex_get (lexer);
709
710       lex_match (lexer, ',') ;
711     }
712   lex_match (lexer, ')');
713
714   lex_match (lexer, '=');
715
716   if ( lex_match_id (lexer, "HAVERAGE"))
717     percentile_algorithm = PC_HAVERAGE;
718
719   else if ( lex_match_id (lexer, "WAVERAGE"))
720     percentile_algorithm = PC_WAVERAGE;
721
722   else if ( lex_match_id (lexer, "ROUND"))
723     percentile_algorithm = PC_ROUND;
724
725   else if ( lex_match_id (lexer, "EMPIRICAL"))
726     percentile_algorithm = PC_EMPIRICAL;
727
728   else if ( lex_match_id (lexer, "AEMPIRICAL"))
729     percentile_algorithm = PC_AEMPIRICAL;
730
731   else if ( lex_match_id (lexer, "NONE"))
732     percentile_algorithm = PC_NONE;
733
734
735   if ( 0 == subc_list_double_count (&percentile_list))
736     {
737       subc_list_double_push (&percentile_list, 5);
738       subc_list_double_push (&percentile_list, 10);
739       subc_list_double_push (&percentile_list, 25);
740       subc_list_double_push (&percentile_list, 50);
741       subc_list_double_push (&percentile_list, 75);
742       subc_list_double_push (&percentile_list, 90);
743       subc_list_double_push (&percentile_list, 95);
744     }
745
746   return 1;
747 }
748
749 /* TOTAL and NOTOTAL are simple, mutually exclusive flags */
750 static int
751 xmn_custom_total (struct lexer *lexer UNUSED, struct dataset *ds UNUSED,
752                   struct cmd_examine *p, void *aux UNUSED)
753 {
754   if ( p->sbc_nototal )
755     {
756       msg (SE, _("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
757       return 0;
758     }
759
760   return 1;
761 }
762
763 static int
764 xmn_custom_nototal (struct lexer *lexer UNUSED, struct dataset *ds UNUSED,
765                     struct cmd_examine *p, void *aux UNUSED)
766 {
767   if ( p->sbc_total )
768     {
769       msg (SE, _("%s and %s are mutually exclusive"), "TOTAL", "NOTOTAL");
770       return 0;
771     }
772
773   return 1;
774 }
775
776
777
778 /* Parser for the variables sub command
779    Returns 1 on success */
780 static int
781 xmn_custom_variables (struct lexer *lexer, struct dataset *ds,
782                       struct cmd_examine *cmd,
783                       void *aux UNUSED)
784 {
785   const struct dictionary *dict = dataset_dict (ds);
786   lex_match (lexer, '=');
787
788   if ( (lex_token (lexer) != T_ID || dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
789        && lex_token (lexer) != T_ALL)
790     {
791       return 2;
792     }
793
794   if (!parse_variables_const (lexer, dict, &dependent_vars, &n_dependent_vars,
795                               PV_NO_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH) )
796     {
797       free (dependent_vars);
798       return 0;
799     }
800
801   assert (n_dependent_vars);
802
803
804   if ( lex_match (lexer, T_BY))
805     {
806       int success ;
807       success =  examine_parse_independent_vars (lexer, dict, cmd);
808       if ( success != 1 )
809         {
810           free (dependent_vars);
811         }
812       return success;
813     }
814
815   return 1;
816 }
817
818
819
820 /* Parse the clause specifying the factors */
821 static int
822 examine_parse_independent_vars (struct lexer *lexer,
823                                 const struct dictionary *dict,
824                                 struct cmd_examine *cmd)
825 {
826   int success;
827   struct xfactor *sf = xmalloc (sizeof *sf);
828
829   ll_init (&sf->result_list);
830
831   if ( (lex_token (lexer) != T_ID ||
832         dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
833        && lex_token (lexer) != T_ALL)
834     {
835       free ( sf ) ;
836       return 2;
837     }
838
839   sf->indep_var[0] = parse_variable (lexer, dict);
840   sf->indep_var[1] = NULL;
841
842   if ( lex_token (lexer) == T_BY )
843     {
844       lex_match (lexer, T_BY);
845
846       if ( (lex_token (lexer) != T_ID ||
847             dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
848            && lex_token (lexer) != T_ALL)
849         {
850           free (sf);
851           return 2;
852         }
853
854       sf->indep_var[1] = parse_variable (lexer, dict);
855
856       ll_push_tail (&factor_list, &sf->ll);
857     }
858   else
859     ll_push_tail (&factor_list, &sf->ll);
860
861   lex_match (lexer, ',');
862
863   if ( lex_token (lexer) == '.' || lex_token (lexer) == '/' )
864     return 1;
865
866   success =  examine_parse_independent_vars (lexer, dict, cmd);
867
868   if ( success != 1 )
869     free ( sf ) ;
870
871   return success;
872 }
873
874 static void
875 examine_group (struct cmd_examine *cmd, struct casereader *reader, int level,
876                const struct dictionary *dict, struct xfactor *factor)
877 {
878   struct ccase *c;
879   const struct variable *wv = dict_get_weight (dict);
880   int v;
881   int n_extrema = 1;
882   struct factor_result *result = xzalloc (sizeof (*result));
883   int i;
884
885   for (i = 0; i < 2; i++)
886     if (factor->indep_var[i])
887       value_init (&result->value[i], var_get_width (factor->indep_var[i]));
888
889   result->metrics = xcalloc (n_dependent_vars, sizeof (*result->metrics));
890
891   if ( cmd->a_statistics[XMN_ST_EXTREME] )
892     n_extrema = cmd->st_n;
893
894
895   c = casereader_peek (reader, 0);
896   if (c != NULL)
897     {
898       if ( level > 0)
899         for (i = 0; i < 2; i++)
900           if (factor->indep_var[i])
901             value_copy (&result->value[i], case_data (c, factor->indep_var[i]),
902                         var_get_width (factor->indep_var[i]));
903       case_unref (c);
904     }
905
906   for (v = 0; v < n_dependent_vars; ++v)
907     {
908       struct casewriter *writer;
909       struct casereader *input = casereader_clone (reader);
910
911       result->metrics[v].moments = moments1_create (MOMENT_KURTOSIS);
912       result->metrics[v].minima = extrema_create (n_extrema, EXTREME_MINIMA);
913       result->metrics[v].maxima = extrema_create (n_extrema, EXTREME_MAXIMA);
914       result->metrics[v].cmin = DBL_MAX;
915
916       if (cmd->a_statistics[XMN_ST_DESCRIPTIVES] ||
917           cmd->a_plot[XMN_PLT_BOXPLOT] ||
918           cmd->a_plot[XMN_PLT_NPPLOT] ||
919           cmd->sbc_percentiles)
920         {
921           /* In this case, we need to sort the data, so we create a sorting
922              casewriter */
923           struct subcase up_ordering;
924           subcase_init_var (&up_ordering, dependent_vars[v], SC_ASCEND);
925           writer = sort_create_writer (&up_ordering,
926                                        casereader_get_proto (reader));
927           subcase_destroy (&up_ordering);
928         }
929       else
930         {
931           /* but in this case, sorting is unnecessary, so an ordinary
932              casewriter is sufficient */
933           writer =
934             autopaging_writer_create (casereader_get_proto (reader));
935         }
936
937
938       /* Sort or just iterate, whilst calculating moments etc */
939       while ((c = casereader_read (input)) != NULL)
940         {
941           int n_vals = caseproto_get_n_widths (casereader_get_proto (reader));
942           const casenumber loc = case_data_idx (c, n_vals - 1)->f;
943
944           const double weight = wv ? case_data (c, wv)->f : 1.0;
945           const union value *value = case_data (c, dependent_vars[v]);
946
947           if (weight != SYSMIS)
948             minimize (&result->metrics[v].cmin, weight);
949
950           moments1_add (result->metrics[v].moments,
951                         value->f,
952                         weight);
953
954           result->metrics[v].n += weight;
955
956           if ( ! var_is_value_missing (dependent_vars[v], value, MV_ANY) )
957             result->metrics[v].n_valid += weight;
958
959           extrema_add (result->metrics[v].maxima,
960                        value->f,
961                        weight,
962                        loc);
963
964           extrema_add (result->metrics[v].minima,
965                        value->f,
966                        weight,
967                        loc);
968
969           casewriter_write (writer, c);
970         }
971       casereader_destroy (input);
972       result->metrics[v].up_reader = casewriter_make_reader (writer);
973     }
974
975   /* If percentiles or descriptives have been requested, then a
976      second pass through the data (which has now been sorted)
977      is necessary */
978   if ( cmd->a_statistics[XMN_ST_DESCRIPTIVES] ||
979        cmd->a_plot[XMN_PLT_BOXPLOT] ||
980        cmd->a_plot[XMN_PLT_NPPLOT] ||
981        cmd->sbc_percentiles)
982     {
983       for (v = 0; v < n_dependent_vars; ++v)
984         {
985           int i;
986           int n_os;
987           struct order_stats **os ;
988           struct factor_metrics *metric = &result->metrics[v];
989
990           metric->n_ptiles = percentile_list.n_data;
991
992           metric->ptl = xcalloc (metric->n_ptiles,
993                                  sizeof (struct percentile *));
994
995           metric->quartiles = xcalloc (3, sizeof (*metric->quartiles));
996
997           for (i = 0 ; i < metric->n_ptiles; ++i)
998             {
999               metric->ptl[i] = (struct percentile *)
1000                 percentile_create (percentile_list.data[i] / 100.0, metric->n_valid);
1001
1002               if ( percentile_list.data[i] == 25)
1003                 metric->quartiles[0] = metric->ptl[i];
1004               else if ( percentile_list.data[i] == 50)
1005                 metric->quartiles[1] = metric->ptl[i];
1006               else if ( percentile_list.data[i] == 75)
1007                 metric->quartiles[2] = metric->ptl[i];
1008             }
1009
1010           metric->tukey_hinges = tukey_hinges_create (metric->n_valid, metric->cmin);
1011           metric->trimmed_mean = trimmed_mean_create (metric->n_valid, 0.05);
1012
1013           n_os = metric->n_ptiles + 2;
1014
1015          if ( cmd->a_plot[XMN_PLT_NPPLOT] )
1016             {
1017               metric->np = np_create (metric->moments);
1018               n_os ++;
1019             }
1020
1021           os = xcalloc (sizeof (struct order_stats *), n_os);
1022
1023           for (i = 0 ; i < metric->n_ptiles ; ++i )
1024             {
1025               os[i] = (struct order_stats *) metric->ptl[i];
1026             }
1027
1028           os[i] = (struct order_stats *) metric->tukey_hinges;
1029           os[i+1] = (struct order_stats *) metric->trimmed_mean;
1030
1031           if (cmd->a_plot[XMN_PLT_NPPLOT])
1032             os[i+2] = metric->np;
1033
1034           order_stats_accumulate (os, n_os,
1035                                   casereader_clone (metric->up_reader),
1036                                   wv, dependent_vars[v], MV_ANY);
1037           free (os);
1038         }
1039     }
1040
1041   /* FIXME: Do this in the above loop */
1042   if ( cmd->a_plot[XMN_PLT_HISTOGRAM] )
1043     {
1044       struct ccase *c;
1045       struct casereader *input = casereader_clone (reader);
1046
1047       for (v = 0; v < n_dependent_vars; ++v)
1048         {
1049           const struct extremum  *max, *min;
1050           struct factor_metrics *metric = &result->metrics[v];
1051
1052           const struct ll_list *max_list =
1053             extrema_list (result->metrics[v].maxima);
1054
1055           const struct ll_list *min_list =
1056             extrema_list (result->metrics[v].minima);
1057
1058           if ( ll_is_empty (max_list))
1059             {
1060               msg (MW, _("Not creating plot because data set is empty."));
1061               continue;
1062             }
1063
1064           assert (! ll_is_empty (min_list));
1065
1066           max = (const struct extremum *)
1067             ll_data (ll_head(max_list), struct extremum, ll);
1068
1069           min = (const struct extremum *)
1070             ll_data (ll_head (min_list), struct extremum, ll);
1071
1072           metric->histogram = histogram_create (10, min->value, max->value);
1073         }
1074
1075       while ((c = casereader_read (input)) != NULL)
1076         {
1077           const double weight = wv ? case_data (c, wv)->f : 1.0;
1078
1079           for (v = 0; v < n_dependent_vars; ++v)
1080             {
1081               struct factor_metrics *metric = &result->metrics[v];
1082               if ( metric->histogram)
1083                 histogram_add ((struct histogram *) metric->histogram,
1084                                case_data (c, dependent_vars[v])->f, weight);
1085             }
1086           case_unref (c);
1087         }
1088       casereader_destroy (input);
1089     }
1090
1091   /* In this case, a third iteration is required */
1092   if (cmd->a_plot[XMN_PLT_BOXPLOT])
1093     {
1094       for (v = 0; v < n_dependent_vars; ++v)
1095         {
1096           struct factor_metrics *metric = &result->metrics[v];
1097           int n_vals = caseproto_get_n_widths (casereader_get_proto (
1098                                                  metric->up_reader));
1099
1100           metric->box_whisker =
1101             box_whisker_create ((struct tukey_hinges *) metric->tukey_hinges,
1102                                 cmd->v_id, n_vals - 1);
1103
1104           order_stats_accumulate ((struct order_stats **) &metric->box_whisker,
1105                                   1,
1106                                   casereader_clone (metric->up_reader),
1107                                   wv, dependent_vars[v], MV_ANY);
1108         }
1109     }
1110
1111   ll_push_tail (&factor->result_list, &result->ll);
1112   casereader_destroy (reader);
1113 }
1114
1115
1116 static void
1117 run_examine (struct cmd_examine *cmd, struct casereader *input,
1118              struct dataset *ds)
1119 {
1120   struct ll *ll;
1121   const struct dictionary *dict = dataset_dict (ds);
1122   struct ccase *c;
1123   struct casereader *level0 = casereader_clone (input);
1124
1125   c = casereader_peek (input, 0);
1126   if (c == NULL)
1127     {
1128       casereader_destroy (input);
1129       return;
1130     }
1131
1132   output_split_file_values (ds, c);
1133   case_unref (c);
1134
1135   ll_init (&level0_factor.result_list);
1136
1137   examine_group (cmd, level0, 0, dict, &level0_factor);
1138
1139   for (ll = ll_head (&factor_list);
1140        ll != ll_null (&factor_list);
1141        ll = ll_next (ll))
1142     {
1143       struct xfactor *factor = ll_data (ll, struct xfactor, ll);
1144
1145       struct casereader *group = NULL;
1146       struct casereader *level1;
1147       struct casegrouper *grouper1 = NULL;
1148
1149       level1 = casereader_clone (input);
1150       level1 = sort_execute_1var (level1, factor->indep_var[0]);
1151       grouper1 = casegrouper_create_vars (level1, &factor->indep_var[0], 1);
1152
1153       while (casegrouper_get_next_group (grouper1, &group))
1154         {
1155           struct casereader *group_copy = casereader_clone (group);
1156
1157           if ( !factor->indep_var[1])
1158             examine_group (cmd, group_copy, 1, dict, factor);
1159           else
1160             {
1161               int n_groups = 0;
1162               struct casereader *group2 = NULL;
1163               struct casegrouper *grouper2 = NULL;
1164
1165               group_copy = sort_execute_1var (group_copy,
1166                                               factor->indep_var[1]);
1167
1168               grouper2 = casegrouper_create_vars (group_copy,
1169                                                   &factor->indep_var[1], 1);
1170
1171               while (casegrouper_get_next_group (grouper2, &group2))
1172                 {
1173                   examine_group (cmd, group2, 2, dict, factor);
1174                   n_groups++;
1175                 }
1176               casegrouper_destroy (grouper2);
1177             }
1178
1179           casereader_destroy (group);
1180         }
1181       casegrouper_destroy (grouper1);
1182     }
1183
1184   casereader_destroy (input);
1185
1186   output_examine (dict);
1187
1188   factor_destroy (&level0_factor);
1189
1190   {
1191     struct ll *ll;
1192     for (ll = ll_head (&factor_list);
1193          ll != ll_null (&factor_list);
1194          ll = ll_next (ll))
1195       {
1196         struct xfactor *f = ll_data (ll, struct xfactor, ll);
1197         factor_destroy (f);
1198       }
1199   }
1200
1201 }
1202
1203
1204 static void
1205 show_summary (const struct variable **dependent_var, int n_dep_var,
1206               const struct dictionary *dict,
1207               const struct xfactor *fctr)
1208 {
1209   const struct variable *wv = dict_get_weight (dict);
1210   const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
1211
1212   static const char *subtitle[]=
1213     {
1214       N_("Valid"),
1215       N_("Missing"),
1216       N_("Total")
1217     };
1218
1219   int v, j;
1220   int heading_columns = 1;
1221   int n_cols;
1222   const int heading_rows = 3;
1223   struct tab_table *tbl;
1224
1225   int n_rows ;
1226   n_rows = n_dep_var;
1227
1228   assert (fctr);
1229
1230   if ( fctr->indep_var[0] )
1231     {
1232       heading_columns = 2;
1233
1234       if ( fctr->indep_var[1] )
1235         {
1236           heading_columns = 3;
1237         }
1238     }
1239
1240   n_rows *= ll_count (&fctr->result_list);
1241   n_rows += heading_rows;
1242
1243   n_cols = heading_columns + 6;
1244
1245   tbl = tab_create (n_cols, n_rows, 0);
1246   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
1247
1248   tab_dim (tbl, tab_natural_dimensions, NULL);
1249
1250   /* Outline the box */
1251   tab_box (tbl,
1252            TAL_2, TAL_2,
1253            -1, -1,
1254            0, 0,
1255            n_cols - 1, n_rows - 1);
1256
1257   /* Vertical lines for the data only */
1258   tab_box (tbl,
1259            -1, -1,
1260            -1, TAL_1,
1261            heading_columns, 0,
1262            n_cols - 1, n_rows - 1);
1263
1264
1265   tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
1266   tab_hline (tbl, TAL_1, heading_columns, n_cols - 1, 1 );
1267   tab_hline (tbl, TAL_1, heading_columns, n_cols - 1, heading_rows -1 );
1268
1269   tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
1270
1271
1272   tab_title (tbl, _("Case Processing Summary"));
1273
1274   tab_joint_text (tbl, heading_columns, 0,
1275                   n_cols -1, 0,
1276                   TAB_CENTER | TAT_TITLE,
1277                   _("Cases"));
1278
1279   /* Remove lines ... */
1280   tab_box (tbl,
1281            -1, -1,
1282            TAL_0, TAL_0,
1283            heading_columns, 0,
1284            n_cols - 1, 0);
1285
1286   for (j = 0 ; j < 3 ; ++j)
1287     {
1288       tab_text (tbl, heading_columns + j * 2 , 2, TAB_CENTER | TAT_TITLE,
1289                 _("N"));
1290
1291       tab_text (tbl, heading_columns + j * 2 + 1, 2, TAB_CENTER | TAT_TITLE,
1292                 _("Percent"));
1293
1294       tab_joint_text (tbl, heading_columns + j * 2 , 1,
1295                       heading_columns + j * 2 + 1, 1,
1296                       TAB_CENTER | TAT_TITLE,
1297                       subtitle[j]);
1298
1299       tab_box (tbl, -1, -1,
1300                TAL_0, TAL_0,
1301                heading_columns + j * 2, 1,
1302                heading_columns + j * 2 + 1, 1);
1303     }
1304
1305
1306   /* Titles for the independent variables */
1307   if ( fctr->indep_var[0] )
1308     {
1309       tab_text (tbl, 1, heading_rows - 1, TAB_CENTER | TAT_TITLE,
1310                 var_to_string (fctr->indep_var[0]));
1311
1312       if ( fctr->indep_var[1] )
1313         {
1314           tab_text (tbl, 2, heading_rows - 1, TAB_CENTER | TAT_TITLE,
1315                     var_to_string (fctr->indep_var[1]));
1316         }
1317     }
1318
1319   for (v = 0 ; v < n_dep_var ; ++v)
1320     {
1321       int j = 0;
1322       struct ll *ll;
1323       const union value *last_value = NULL;
1324
1325       if ( v > 0 )
1326         tab_hline (tbl, TAL_1, 0, n_cols -1 ,
1327                    v * ll_count (&fctr->result_list)
1328                    + heading_rows);
1329
1330       tab_text (tbl,
1331                 0,
1332                 v * ll_count (&fctr->result_list) + heading_rows,
1333                 TAB_LEFT | TAT_TITLE,
1334                 var_to_string (dependent_var[v])
1335                 );
1336
1337
1338       for (ll = ll_head (&fctr->result_list);
1339            ll != ll_null (&fctr->result_list); ll = ll_next (ll))
1340         {
1341           double n;
1342           const struct factor_result *result =
1343             ll_data (ll, struct factor_result, ll);
1344
1345           if ( fctr->indep_var[0] )
1346             {
1347
1348               if ( last_value == NULL ||
1349                    !value_equal (last_value, &result->value[0],
1350                                  var_get_width (fctr->indep_var[0])))
1351                 {
1352                   struct string str;
1353
1354                   last_value = &result->value[0];
1355                   ds_init_empty (&str);
1356
1357                   var_append_value_name (fctr->indep_var[0], &result->value[0],
1358                                          &str);
1359
1360                   tab_text (tbl, 1,
1361                             heading_rows + j +
1362                             v * ll_count (&fctr->result_list),
1363                             TAB_LEFT | TAT_TITLE,
1364                             ds_cstr (&str));
1365
1366                   ds_destroy (&str);
1367
1368                   if ( fctr->indep_var[1] && j > 0)
1369                     tab_hline (tbl, TAL_1, 1, n_cols - 1,
1370                                heading_rows + j +
1371                                v * ll_count (&fctr->result_list));
1372                 }
1373
1374               if ( fctr->indep_var[1])
1375                 {
1376                   struct string str;
1377
1378                   ds_init_empty (&str);
1379
1380                   var_append_value_name (fctr->indep_var[1],
1381                                          &result->value[1], &str);
1382
1383                   tab_text (tbl, 2,
1384                             heading_rows + j +
1385                             v * ll_count (&fctr->result_list),
1386                             TAB_LEFT | TAT_TITLE,
1387                             ds_cstr (&str));
1388
1389                   ds_destroy (&str);
1390                 }
1391             }
1392
1393
1394           moments1_calculate (result->metrics[v].moments,
1395                               &n, &result->metrics[v].mean,
1396                               &result->metrics[v].variance,
1397                               &result->metrics[v].skewness,
1398                               &result->metrics[v].kurtosis);
1399
1400           result->metrics[v].se_mean = sqrt (result->metrics[v].variance / n) ;
1401
1402           /* Total Valid */
1403           tab_double (tbl, heading_columns,
1404                      heading_rows + j + v * ll_count (&fctr->result_list),
1405                      TAB_LEFT,
1406                      n, wfmt);
1407
1408           tab_text_format (tbl, heading_columns + 1,
1409                            heading_rows + j + v * ll_count (&fctr->result_list),
1410                            TAB_RIGHT,
1411                            "%g%%", n * 100.0 / result->metrics[v].n);
1412
1413           /* Total Missing */
1414           tab_double (tbl, heading_columns + 2,
1415                      heading_rows + j + v * ll_count (&fctr->result_list),
1416                      TAB_LEFT,
1417                      result->metrics[v].n - n,
1418                      wfmt);
1419
1420           tab_text_format (tbl, heading_columns + 3,
1421                            heading_rows + j + v * ll_count (&fctr->result_list),
1422                            TAB_RIGHT,
1423                            "%g%%",
1424                            (result->metrics[v].n - n) * 100.0 / result->metrics[v].n
1425                            );
1426
1427           /* Total Valid + Missing */
1428           tab_double (tbl, heading_columns + 4,
1429                      heading_rows + j + v * ll_count (&fctr->result_list),
1430                      TAB_LEFT,
1431                      result->metrics[v].n,
1432                      wfmt);
1433
1434           tab_text_format (tbl, heading_columns + 5,
1435                            heading_rows + j + v * ll_count (&fctr->result_list),
1436                            TAB_RIGHT,
1437                            "%g%%",
1438                            ((result->metrics[v].n) * 100.0
1439                             / result->metrics[v].n));
1440
1441           ++j;
1442         }
1443     }
1444
1445
1446   tab_submit (tbl);
1447 }
1448
1449 #define DESCRIPTIVE_ROWS 13
1450
1451 static void
1452 show_descriptives (const struct variable **dependent_var,
1453                    int n_dep_var,
1454                    const struct xfactor *fctr)
1455 {
1456   int v;
1457   int heading_columns = 3;
1458   int n_cols;
1459   const int heading_rows = 1;
1460   struct tab_table *tbl;
1461
1462   int n_rows ;
1463   n_rows = n_dep_var;
1464
1465   assert (fctr);
1466
1467   if ( fctr->indep_var[0] )
1468     {
1469       heading_columns = 4;
1470
1471       if ( fctr->indep_var[1] )
1472         {
1473           heading_columns = 5;
1474         }
1475     }
1476
1477   n_rows *= ll_count (&fctr->result_list) * DESCRIPTIVE_ROWS;
1478   n_rows += heading_rows;
1479
1480   n_cols = heading_columns + 2;
1481
1482   tbl = tab_create (n_cols, n_rows, 0);
1483   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
1484
1485   tab_dim (tbl, tab_natural_dimensions, NULL);
1486
1487   /* Outline the box */
1488   tab_box (tbl,
1489            TAL_2, TAL_2,
1490            -1, -1,
1491            0, 0,
1492            n_cols - 1, n_rows - 1);
1493
1494
1495   tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
1496   tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
1497
1498   tab_vline (tbl, TAL_1, n_cols - 1, 0, n_rows - 1);
1499
1500
1501   if ( fctr->indep_var[0])
1502     tab_text (tbl, 1, 0, TAT_TITLE, var_to_string (fctr->indep_var[0]));
1503
1504   if ( fctr->indep_var[1])
1505     tab_text (tbl, 2, 0, TAT_TITLE, var_to_string (fctr->indep_var[1]));
1506
1507   for (v = 0 ; v < n_dep_var ; ++v )
1508     {
1509       struct ll *ll;
1510       int i = 0;
1511
1512       const int row_var_start =
1513         v * DESCRIPTIVE_ROWS * ll_count(&fctr->result_list);
1514
1515       tab_text (tbl,
1516                 0,
1517                 heading_rows + row_var_start,
1518                 TAB_LEFT | TAT_TITLE,
1519                 var_to_string (dependent_var[v])
1520                 );
1521
1522       for (ll = ll_head (&fctr->result_list);
1523            ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
1524         {
1525           const struct factor_result *result =
1526             ll_data (ll, struct factor_result, ll);
1527
1528           const double t =
1529             gsl_cdf_tdist_Qinv ((1 - cmd.n_cinterval[0] / 100.0) / 2.0,
1530                                       result->metrics[v].n - 1);
1531
1532           if ( i > 0 || v > 0 )
1533             {
1534               const int left_col = (i == 0) ? 0 : 1;
1535               tab_hline (tbl, TAL_1, left_col, n_cols - 1,
1536                          heading_rows + row_var_start + i * DESCRIPTIVE_ROWS);
1537             }
1538
1539           if ( fctr->indep_var[0])
1540             {
1541               struct string vstr;
1542               ds_init_empty (&vstr);
1543               var_append_value_name (fctr->indep_var[0],
1544                                      &result->value[0], &vstr);
1545
1546               tab_text (tbl, 1,
1547                         heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
1548                         TAB_LEFT,
1549                         ds_cstr (&vstr)
1550                         );
1551
1552               ds_destroy (&vstr);
1553             }
1554
1555
1556           tab_text (tbl, n_cols - 4,
1557                     heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
1558                     TAB_LEFT,
1559                     _("Mean"));
1560
1561           tab_text_format (tbl, n_cols - 4,
1562                            heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
1563                            TAB_LEFT,
1564                            _("%g%% Confidence Interval for Mean"),
1565                            cmd.n_cinterval[0]);
1566
1567           tab_text (tbl, n_cols - 3,
1568                     heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
1569                     TAB_LEFT,
1570                     _("Lower Bound"));
1571
1572           tab_text (tbl, n_cols - 3,
1573                     heading_rows + row_var_start + 2 + i * DESCRIPTIVE_ROWS,
1574                     TAB_LEFT,
1575                     _("Upper Bound"));
1576
1577           tab_text (tbl, n_cols - 4,
1578                     heading_rows + row_var_start + 3 + i * DESCRIPTIVE_ROWS,
1579                     TAB_LEFT, _("5% Trimmed Mean"));
1580
1581           tab_text (tbl, n_cols - 4,
1582                     heading_rows + row_var_start + 4 + i * DESCRIPTIVE_ROWS,
1583                     TAB_LEFT,
1584                     _("Median"));
1585
1586           tab_text (tbl, n_cols - 4,
1587                     heading_rows + row_var_start + 5 + i * DESCRIPTIVE_ROWS,
1588                     TAB_LEFT,
1589                     _("Variance"));
1590
1591           tab_text (tbl, n_cols - 4,
1592                     heading_rows + row_var_start + 6 + i * DESCRIPTIVE_ROWS,
1593                     TAB_LEFT,
1594                     _("Std. Deviation"));
1595
1596           tab_text (tbl, n_cols - 4,
1597                     heading_rows + row_var_start + 7 + i * DESCRIPTIVE_ROWS,
1598                     TAB_LEFT,
1599                     _("Minimum"));
1600
1601           tab_text (tbl, n_cols - 4,
1602                     heading_rows + row_var_start + 8 + i * DESCRIPTIVE_ROWS,
1603                     TAB_LEFT,
1604                     _("Maximum"));
1605
1606           tab_text (tbl, n_cols - 4,
1607                     heading_rows + row_var_start + 9 + i * DESCRIPTIVE_ROWS,
1608                     TAB_LEFT,
1609                     _("Range"));
1610
1611           tab_text (tbl, n_cols - 4,
1612                     heading_rows + row_var_start + 10 + i * DESCRIPTIVE_ROWS,
1613                     TAB_LEFT,
1614                     _("Interquartile Range"));
1615
1616
1617           tab_text (tbl, n_cols - 4,
1618                     heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
1619                     TAB_LEFT,
1620                     _("Skewness"));
1621
1622           tab_text (tbl, n_cols - 4,
1623                     heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
1624                     TAB_LEFT,
1625                     _("Kurtosis"));
1626
1627
1628           /* Now the statistics ... */
1629
1630           tab_double (tbl, n_cols - 2,
1631                     heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
1632                      TAB_CENTER,
1633                      result->metrics[v].mean,
1634                      NULL);
1635
1636           tab_double (tbl, n_cols - 1,
1637                     heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
1638                      TAB_CENTER,
1639                      result->metrics[v].se_mean,
1640                      NULL);
1641
1642
1643           tab_double (tbl, n_cols - 2,
1644                      heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
1645                      TAB_CENTER,
1646                      result->metrics[v].mean - t *
1647                       result->metrics[v].se_mean,
1648                      NULL);
1649
1650           tab_double (tbl, n_cols - 2,
1651                      heading_rows + row_var_start + 2 + i * DESCRIPTIVE_ROWS,
1652                      TAB_CENTER,
1653                      result->metrics[v].mean + t *
1654                       result->metrics[v].se_mean,
1655                      NULL);
1656
1657
1658           tab_double (tbl, n_cols - 2,
1659                      heading_rows + row_var_start + 3 + i * DESCRIPTIVE_ROWS,
1660                      TAB_CENTER,
1661                      trimmed_mean_calculate ((struct trimmed_mean *) result->metrics[v].trimmed_mean),
1662                      NULL);
1663
1664
1665           tab_double (tbl, n_cols - 2,
1666                      heading_rows + row_var_start + 4 + i * DESCRIPTIVE_ROWS,
1667                      TAB_CENTER,
1668                      percentile_calculate (result->metrics[v].quartiles[1], percentile_algorithm),
1669                      NULL);
1670
1671
1672           tab_double (tbl, n_cols - 2,
1673                      heading_rows + row_var_start + 5 + i * DESCRIPTIVE_ROWS,
1674                      TAB_CENTER,
1675                      result->metrics[v].variance,
1676                      NULL);
1677
1678           tab_double (tbl, n_cols - 2,
1679                      heading_rows + row_var_start + 6 + i * DESCRIPTIVE_ROWS,
1680                      TAB_CENTER,
1681                      sqrt (result->metrics[v].variance),
1682                      NULL);
1683
1684           tab_double (tbl, n_cols - 2,
1685                      heading_rows + row_var_start + 10 + i * DESCRIPTIVE_ROWS,
1686                      TAB_CENTER,
1687                      percentile_calculate (result->metrics[v].quartiles[2],
1688                                            percentile_algorithm) -
1689                      percentile_calculate (result->metrics[v].quartiles[0],
1690                                            percentile_algorithm),
1691                      NULL);
1692
1693
1694           tab_double (tbl, n_cols - 2,
1695                      heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
1696                      TAB_CENTER,
1697                      result->metrics[v].skewness,
1698                      NULL);
1699
1700           tab_double (tbl, n_cols - 2,
1701                      heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
1702                      TAB_CENTER,
1703                      result->metrics[v].kurtosis,
1704                      NULL);
1705
1706           tab_double (tbl, n_cols - 1,
1707                      heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
1708                      TAB_CENTER,
1709                      calc_seskew (result->metrics[v].n),
1710                      NULL);
1711
1712           tab_double (tbl, n_cols - 1,
1713                      heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
1714                      TAB_CENTER,
1715                      calc_sekurt (result->metrics[v].n),
1716                      NULL);
1717
1718           {
1719             struct extremum *minimum, *maximum ;
1720
1721             struct ll *max_ll = ll_head (extrema_list (result->metrics[v].maxima));
1722             struct ll *min_ll = ll_head (extrema_list (result->metrics[v].minima));
1723
1724             maximum = ll_data (max_ll, struct extremum, ll);
1725             minimum = ll_data (min_ll, struct extremum, ll);
1726
1727             tab_double (tbl, n_cols - 2,
1728                        heading_rows + row_var_start + 7 + i * DESCRIPTIVE_ROWS,
1729                        TAB_CENTER,
1730                        minimum->value,
1731                        NULL);
1732
1733             tab_double (tbl, n_cols - 2,
1734                        heading_rows + row_var_start + 8 + i * DESCRIPTIVE_ROWS,
1735                        TAB_CENTER,
1736                        maximum->value,
1737                        NULL);
1738
1739             tab_double (tbl, n_cols - 2,
1740                        heading_rows + row_var_start + 9 + i * DESCRIPTIVE_ROWS,
1741                        TAB_CENTER,
1742                        maximum->value - minimum->value,
1743                        NULL);
1744           }
1745         }
1746     }
1747
1748   tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
1749
1750   tab_title (tbl, _("Descriptives"));
1751
1752   tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE,
1753             _("Statistic"));
1754
1755   tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE,
1756             _("Std. Error"));
1757
1758   tab_submit (tbl);
1759 }
1760
1761
1762
1763 static void
1764 show_extremes (const struct variable **dependent_var,
1765                int n_dep_var,
1766                const struct xfactor *fctr)
1767 {
1768   int v;
1769   int heading_columns = 3;
1770   int n_cols;
1771   const int heading_rows = 1;
1772   struct tab_table *tbl;
1773
1774   int n_rows ;
1775   n_rows = n_dep_var;
1776
1777   assert (fctr);
1778
1779   if ( fctr->indep_var[0] )
1780     {
1781       heading_columns = 4;
1782
1783       if ( fctr->indep_var[1] )
1784         {
1785           heading_columns = 5;
1786         }
1787     }
1788
1789   n_rows *= ll_count (&fctr->result_list) * cmd.st_n * 2;
1790   n_rows += heading_rows;
1791
1792   n_cols = heading_columns + 2;
1793
1794   tbl = tab_create (n_cols, n_rows, 0);
1795   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
1796
1797   tab_dim (tbl, tab_natural_dimensions, NULL);
1798
1799   /* Outline the box */
1800   tab_box (tbl,
1801            TAL_2, TAL_2,
1802            -1, -1,
1803            0, 0,
1804            n_cols - 1, n_rows - 1);
1805
1806
1807   tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
1808   tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
1809   tab_vline (tbl, TAL_1, n_cols - 1, 0, n_rows - 1);
1810
1811   if ( fctr->indep_var[0])
1812     tab_text (tbl, 1, 0, TAT_TITLE, var_to_string (fctr->indep_var[0]));
1813
1814   if ( fctr->indep_var[1])
1815     tab_text (tbl, 2, 0, TAT_TITLE, var_to_string (fctr->indep_var[1]));
1816
1817   for (v = 0 ; v < n_dep_var ; ++v )
1818     {
1819       struct ll *ll;
1820       int i = 0;
1821       const int row_var_start = v * cmd.st_n * 2 * ll_count(&fctr->result_list);
1822
1823       tab_text (tbl,
1824                 0,
1825                 heading_rows + row_var_start,
1826                 TAB_LEFT | TAT_TITLE,
1827                 var_to_string (dependent_var[v])
1828                 );
1829
1830       for (ll = ll_head (&fctr->result_list);
1831            ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
1832         {
1833           int e ;
1834           struct ll *min_ll;
1835           struct ll *max_ll;
1836           const int row_result_start = i * cmd.st_n * 2;
1837
1838           const struct factor_result *result =
1839             ll_data (ll, struct factor_result, ll);
1840
1841           if (i > 0 || v > 0)
1842             tab_hline (tbl, TAL_1, 1, n_cols - 1,
1843                        heading_rows + row_var_start + row_result_start);
1844
1845           tab_hline (tbl, TAL_1, heading_columns - 2, n_cols - 1,
1846                      heading_rows + row_var_start + row_result_start + cmd.st_n);
1847
1848           for ( e = 1; e <= cmd.st_n; ++e )
1849             {
1850               tab_text_format (tbl, n_cols - 3,
1851                                heading_rows + row_var_start + row_result_start + e - 1,
1852                                TAB_RIGHT,
1853                                "%d", e);
1854
1855               tab_text_format (tbl, n_cols - 3,
1856                                heading_rows + row_var_start + row_result_start + cmd.st_n + e - 1,
1857                                TAB_RIGHT,
1858                                "%d", e);
1859             }
1860
1861
1862           min_ll = ll_head (extrema_list (result->metrics[v].minima));
1863           for (e = 0; e < cmd.st_n;)
1864             {
1865               struct extremum *minimum = ll_data (min_ll, struct extremum, ll);
1866               double weight = minimum->weight;
1867
1868               while (weight-- > 0 && e < cmd.st_n)
1869                 {
1870                   tab_double (tbl, n_cols - 1,
1871                              heading_rows + row_var_start + row_result_start + cmd.st_n + e,
1872                              TAB_RIGHT,
1873                              minimum->value,
1874                              NULL);
1875
1876
1877                   tab_fixed (tbl, n_cols - 2,
1878                              heading_rows + row_var_start +
1879                              row_result_start + cmd.st_n + e,
1880                              TAB_RIGHT,
1881                              minimum->location,
1882                              10, 0);
1883                   ++e;
1884                 }
1885
1886               min_ll = ll_next (min_ll);
1887             }
1888
1889
1890           max_ll = ll_head (extrema_list (result->metrics[v].maxima));
1891           for (e = 0; e < cmd.st_n;)
1892             {
1893               struct extremum *maximum = ll_data (max_ll, struct extremum, ll);
1894               double weight = maximum->weight;
1895
1896               while (weight-- > 0 && e < cmd.st_n)
1897                 {
1898                   tab_double (tbl, n_cols - 1,
1899                              heading_rows + row_var_start +
1900                               row_result_start + e,
1901                              TAB_RIGHT,
1902                              maximum->value,
1903                              NULL);
1904
1905
1906                   tab_fixed (tbl, n_cols - 2,
1907                              heading_rows + row_var_start +
1908                              row_result_start + e,
1909                              TAB_RIGHT,
1910                              maximum->location,
1911                              10, 0);
1912                   ++e;
1913                 }
1914
1915               max_ll = ll_next (max_ll);
1916             }
1917
1918
1919           if ( fctr->indep_var[0])
1920             {
1921               struct string vstr;
1922               ds_init_empty (&vstr);
1923               var_append_value_name (fctr->indep_var[0],
1924                                      &result->value[0], &vstr);
1925
1926               tab_text (tbl, 1,
1927                         heading_rows + row_var_start + row_result_start,
1928                         TAB_LEFT,
1929                         ds_cstr (&vstr)
1930                         );
1931
1932               ds_destroy (&vstr);
1933             }
1934
1935
1936           tab_text (tbl, n_cols - 4,
1937                     heading_rows + row_var_start + row_result_start,
1938                     TAB_RIGHT,
1939                     _("Highest"));
1940
1941           tab_text (tbl, n_cols - 4,
1942                     heading_rows + row_var_start + row_result_start + cmd.st_n,
1943                     TAB_RIGHT,
1944                     _("Lowest"));
1945         }
1946     }
1947
1948   tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
1949
1950
1951   tab_title (tbl, _("Extreme Values"));
1952
1953
1954   tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE,
1955             _("Case Number"));
1956
1957
1958   tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE,
1959             _("Value"));
1960
1961   tab_submit (tbl);
1962 }
1963
1964 #define PERCENTILE_ROWS 2
1965
1966 static void
1967 show_percentiles (const struct variable **dependent_var,
1968                   int n_dep_var,
1969                   const struct xfactor *fctr)
1970 {
1971   int i;
1972   int v;
1973   int heading_columns = 2;
1974   int n_cols;
1975   const int n_percentiles = subc_list_double_count (&percentile_list);
1976   const int heading_rows = 2;
1977   struct tab_table *tbl;
1978
1979   int n_rows ;
1980   n_rows = n_dep_var;
1981
1982   assert (fctr);
1983
1984   if ( fctr->indep_var[0] )
1985     {
1986       heading_columns = 3;
1987
1988       if ( fctr->indep_var[1] )
1989         {
1990           heading_columns = 4;
1991         }
1992     }
1993
1994   n_rows *= ll_count (&fctr->result_list) * PERCENTILE_ROWS;
1995   n_rows += heading_rows;
1996
1997   n_cols = heading_columns + n_percentiles;
1998
1999   tbl = tab_create (n_cols, n_rows, 0);
2000   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
2001
2002   tab_dim (tbl, tab_natural_dimensions, NULL);
2003
2004   /* Outline the box */
2005   tab_box (tbl,
2006            TAL_2, TAL_2,
2007            -1, -1,
2008            0, 0,
2009            n_cols - 1, n_rows - 1);
2010
2011
2012   tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
2013   tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
2014
2015   if ( fctr->indep_var[0])
2016     tab_text (tbl, 1, 1, TAT_TITLE, var_to_string (fctr->indep_var[0]));
2017
2018   if ( fctr->indep_var[1])
2019     tab_text (tbl, 2, 1, TAT_TITLE, var_to_string (fctr->indep_var[1]));
2020
2021   for (v = 0 ; v < n_dep_var ; ++v )
2022     {
2023       double hinges[3];
2024       struct ll *ll;
2025       int i = 0;
2026
2027       const int row_var_start =
2028         v * PERCENTILE_ROWS * ll_count(&fctr->result_list);
2029
2030       tab_text (tbl,
2031                 0,
2032                 heading_rows + row_var_start,
2033                 TAB_LEFT | TAT_TITLE,
2034                 var_to_string (dependent_var[v])
2035                 );
2036
2037       for (ll = ll_head (&fctr->result_list);
2038            ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
2039         {
2040           int j;
2041           const struct factor_result *result =
2042             ll_data (ll, struct factor_result, ll);
2043
2044           if ( i > 0 || v > 0 )
2045             {
2046               const int left_col = (i == 0) ? 0 : 1;
2047               tab_hline (tbl, TAL_1, left_col, n_cols - 1,
2048                          heading_rows + row_var_start + i * PERCENTILE_ROWS);
2049             }
2050
2051           if ( fctr->indep_var[0])
2052             {
2053               struct string vstr;
2054               ds_init_empty (&vstr);
2055               var_append_value_name (fctr->indep_var[0],
2056                                      &result->value[0], &vstr);
2057
2058               tab_text (tbl, 1,
2059                         heading_rows + row_var_start + i * PERCENTILE_ROWS,
2060                         TAB_LEFT,
2061                         ds_cstr (&vstr)
2062                         );
2063
2064               ds_destroy (&vstr);
2065             }
2066
2067
2068           tab_text (tbl, n_cols - n_percentiles - 1,
2069                     heading_rows + row_var_start + i * PERCENTILE_ROWS,
2070                     TAB_LEFT,
2071                     ptile_alg_desc [percentile_algorithm]);
2072
2073
2074           tab_text (tbl, n_cols - n_percentiles - 1,
2075                     heading_rows + row_var_start + 1 + i * PERCENTILE_ROWS,
2076                     TAB_LEFT,
2077                     _("Tukey's Hinges"));
2078
2079
2080           tab_vline (tbl, TAL_1, n_cols - n_percentiles -1, heading_rows, n_rows - 1);
2081
2082           tukey_hinges_calculate ((struct tukey_hinges *) result->metrics[v].tukey_hinges,
2083                                   hinges);
2084
2085           for (j = 0; j < n_percentiles; ++j)
2086             {
2087               double hinge = SYSMIS;
2088               tab_double (tbl, n_cols - n_percentiles + j,
2089                          heading_rows + row_var_start + i * PERCENTILE_ROWS,
2090                          TAB_CENTER,
2091                          percentile_calculate (result->metrics[v].ptl[j],
2092                                                percentile_algorithm),
2093                          NULL
2094                          );
2095
2096               if ( result->metrics[v].ptl[j]->ptile == 0.5)
2097                 hinge = hinges[1];
2098               else if ( result->metrics[v].ptl[j]->ptile == 0.25)
2099                 hinge = hinges[0];
2100               else if ( result->metrics[v].ptl[j]->ptile == 0.75)
2101                 hinge = hinges[2];
2102
2103               if ( hinge != SYSMIS)
2104                 tab_double (tbl, n_cols - n_percentiles + j,
2105                            heading_rows + row_var_start + 1 + i * PERCENTILE_ROWS,
2106                            TAB_CENTER,
2107                            hinge,
2108                            NULL
2109                            );
2110
2111             }
2112         }
2113     }
2114
2115   tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
2116
2117   tab_title (tbl, _("Percentiles"));
2118
2119
2120   for (i = 0 ; i < n_percentiles; ++i )
2121     {
2122       tab_text_format (tbl, n_cols - n_percentiles + i, 1,
2123                        TAB_CENTER | TAT_TITLE,
2124                        _("%g"),
2125                        subc_list_double_at (&percentile_list, i));
2126
2127
2128     }
2129
2130   tab_joint_text (tbl,
2131                   n_cols - n_percentiles, 0,
2132                   n_cols - 1, 0,
2133                   TAB_CENTER | TAT_TITLE,
2134                   _("Percentiles"));
2135
2136   /* Vertical lines for the data only */
2137   tab_box (tbl,
2138            -1, -1,
2139            -1, TAL_1,
2140            n_cols - n_percentiles, 1,
2141            n_cols - 1, n_rows - 1);
2142
2143   tab_hline (tbl, TAL_1, n_cols - n_percentiles, n_cols - 1, 1);
2144
2145
2146   tab_submit (tbl);
2147 }
2148
2149
2150 static void
2151 factor_to_string_concise (const struct xfactor *fctr,
2152                           const struct factor_result *result,
2153                           struct string *str
2154                           )
2155 {
2156   if (fctr->indep_var[0])
2157     {
2158       var_append_value_name (fctr->indep_var[0], &result->value[0], str);
2159
2160       if ( fctr->indep_var[1] )
2161         {
2162           ds_put_cstr (str, ",");
2163
2164           var_append_value_name (fctr->indep_var[1], &result->value[1], str);
2165
2166           ds_put_cstr (str, ")");
2167         }
2168     }
2169 }
2170
2171
2172 static void
2173 factor_to_string (const struct xfactor *fctr,
2174                   const struct factor_result *result,
2175                   struct string *str
2176                   )
2177 {
2178   if (fctr->indep_var[0])
2179     {
2180       ds_put_format (str, "(%s = ", var_get_name (fctr->indep_var[0]));
2181
2182       var_append_value_name (fctr->indep_var[0], &result->value[0], str);
2183
2184       if ( fctr->indep_var[1] )
2185         {
2186           ds_put_cstr (str, ",");
2187           ds_put_format (str, "%s = ", var_get_name (fctr->indep_var[1]));
2188
2189           var_append_value_name (fctr->indep_var[1], &result->value[1], str);
2190         }
2191       ds_put_cstr (str, ")");
2192     }
2193 }
2194
2195
2196
2197
2198 /*
2199   Local Variables:
2200   mode: c
2201   End:
2202 */