chart: Fix format string issues in chart_write_title callers.
[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           double mean, var, n;
452
453           ds_init_empty (&str);
454           ds_put_format (&str, "%s ", var_get_name (dependent_var[v]));
455
456           factor_to_string (fctr, result, &str);
457
458           moments1_calculate ((struct moments1 *) result->metrics[v].moments,
459                               &n, &mean, &var, NULL,  NULL);
460           histogram_plot ((struct histogram *) result->metrics[v].histogram,
461                           ds_cstr (&str),
462                           n, mean, sqrt (var), false);
463
464           ds_destroy (&str);
465         }
466     }
467 }
468
469
470
471 static void
472 show_boxplot_groups (const struct variable **dependent_var,
473                      int n_dep_var,
474                      const struct xfactor *fctr)
475 {
476   int v;
477
478   for (v = 0; v < n_dep_var; ++v)
479     {
480       struct ll *ll;
481       int f = 0;
482       struct chart *ch = chart_create ();
483       double y_min = DBL_MAX;
484       double y_max = -DBL_MAX;
485
486       for (ll = ll_head (&fctr->result_list);
487            ll != ll_null (&fctr->result_list);
488            ll = ll_next (ll))
489         {
490           const struct extremum  *max, *min;
491           const struct factor_result *result =
492             ll_data (ll, struct factor_result, ll);
493
494           const struct ll_list *max_list =
495             extrema_list (result->metrics[v].maxima);
496
497           const struct ll_list *min_list =
498             extrema_list (result->metrics[v].minima);
499
500           if ( ll_is_empty (max_list))
501             {
502               msg (MW, _("Not creating plot because data set is empty."));
503               continue;
504             }
505
506           max = (const struct extremum *)
507             ll_data (ll_head(max_list), struct extremum, ll);
508
509           min = (const struct extremum *)
510             ll_data (ll_head (min_list), struct extremum, ll);
511
512           y_max = MAX (y_max, max->value);
513           y_min = MIN (y_min, min->value);
514         }
515
516       boxplot_draw_yscale (ch, y_max, y_min);
517
518       if ( fctr->indep_var[0])
519         chart_write_title (ch, _("Boxplot of %s vs. %s"),
520                            var_to_string (dependent_var[v]),
521                            var_to_string (fctr->indep_var[0]) );
522       else
523         chart_write_title (ch, _("Boxplot of %s"),
524                            var_to_string (dependent_var[v]));
525
526       for (ll = ll_head (&fctr->result_list);
527            ll != ll_null (&fctr->result_list);
528            ll = ll_next (ll))
529         {
530           const struct factor_result *result =
531             ll_data (ll, struct factor_result, ll);
532
533           struct string str;
534           const double box_width = (ch->data_right - ch->data_left)
535             / (ll_count (&fctr->result_list) * 2.0 ) ;
536
537           const double box_centre = (f++ * 2 + 1) * box_width + ch->data_left;
538
539           ds_init_empty (&str);
540           factor_to_string_concise (fctr, result, &str);
541
542           boxplot_draw_boxplot (ch,
543                                 box_centre, box_width,
544                                 (const struct box_whisker *)
545                                  result->metrics[v].box_whisker,
546                                 ds_cstr (&str));
547
548           ds_destroy (&str);
549         }
550
551       chart_submit (ch);
552     }
553 }
554
555
556
557 static void
558 show_boxplot_variables (const struct variable **dependent_var,
559                         int n_dep_var,
560                         const struct xfactor *fctr
561                         )
562
563 {
564   int v;
565   struct ll *ll;
566   const struct ll_list *result_list = &fctr->result_list;
567
568   for (ll = ll_head (result_list);
569        ll != ll_null (result_list);
570        ll = ll_next (ll))
571
572     {
573       struct string title;
574       struct chart *ch = chart_create ();
575       double y_min = DBL_MAX;
576       double y_max = -DBL_MAX;
577
578       const struct factor_result *result =
579         ll_data (ll, struct factor_result, ll);
580
581       const double box_width = (ch->data_right - ch->data_left)
582         / (n_dep_var * 2.0 ) ;
583
584       for (v = 0; v < n_dep_var; ++v)
585         {
586           const struct ll *max_ll =
587             ll_head (extrema_list (result->metrics[v].maxima));
588           const struct ll *min_ll =
589             ll_head (extrema_list (result->metrics[v].minima));
590
591           const struct extremum  *max =
592             (const struct extremum *) ll_data (max_ll, struct extremum, ll);
593
594           const struct extremum  *min =
595             (const struct extremum *) ll_data (min_ll, struct extremum, ll);
596
597           y_max = MAX (y_max, max->value);
598           y_min = MIN (y_min, min->value);
599         }
600
601
602       boxplot_draw_yscale (ch, y_max, y_min);
603
604       ds_init_empty (&title);
605       factor_to_string (fctr, result, &title);
606
607 #if 0
608       ds_put_format (&title, "%s = ", var_get_name (fctr->indep_var[0]));
609       var_append_value_name (fctr->indep_var[0], &result->value[0], &title);
610 #endif
611
612       chart_write_title (ch, "%s", ds_cstr (&title));
613       ds_destroy (&title);
614
615       for (v = 0; v < n_dep_var; ++v)
616         {
617           struct string str;
618           const double box_centre = (v * 2 + 1) * box_width + ch->data_left;
619
620           ds_init_empty (&str);
621           ds_init_cstr (&str, var_get_name (dependent_var[v]));
622
623           boxplot_draw_boxplot (ch,
624                                 box_centre, box_width,
625                                 (const struct box_whisker *) result->metrics[v].box_whisker,
626                                 ds_cstr (&str));
627
628           ds_destroy (&str);
629         }
630
631       chart_submit (ch);
632     }
633 }
634
635
636 /* Show all the appropriate tables */
637 static void
638 output_examine (const struct dictionary *dict)
639 {
640   struct ll *ll;
641
642   show_summary (dependent_vars, n_dependent_vars, dict, &level0_factor);
643
644   if ( cmd.a_statistics[XMN_ST_EXTREME] )
645     show_extremes (dependent_vars, n_dependent_vars, &level0_factor);
646
647   if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES] )
648     show_descriptives (dependent_vars, n_dependent_vars, &level0_factor);
649
650   if ( cmd.sbc_percentiles)
651     show_percentiles (dependent_vars, n_dependent_vars, &level0_factor);
652
653   if ( cmd.sbc_plot)
654     {
655       if (cmd.a_plot[XMN_PLT_BOXPLOT])
656         show_boxplot_groups (dependent_vars, n_dependent_vars, &level0_factor);
657
658       if (cmd.a_plot[XMN_PLT_HISTOGRAM])
659         show_histogram (dependent_vars, n_dependent_vars, &level0_factor);
660
661       if (cmd.a_plot[XMN_PLT_NPPLOT])
662         show_npplot (dependent_vars, n_dependent_vars, &level0_factor);
663     }
664
665   for (ll = ll_head (&factor_list);
666        ll != ll_null (&factor_list); ll = ll_next (ll))
667     {
668       struct xfactor *factor = ll_data (ll, struct xfactor, ll);
669       show_summary (dependent_vars, n_dependent_vars, dict, factor);
670
671       if ( cmd.a_statistics[XMN_ST_EXTREME] )
672         show_extremes (dependent_vars, n_dependent_vars, factor);
673
674       if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES] )
675         show_descriptives (dependent_vars, n_dependent_vars, factor);
676
677       if ( cmd.sbc_percentiles)
678         show_percentiles (dependent_vars, n_dependent_vars, factor);
679
680       if (cmd.a_plot[XMN_PLT_BOXPLOT] &&
681           cmd.cmp == XMN_GROUPS)
682         show_boxplot_groups (dependent_vars, n_dependent_vars, factor);
683
684
685       if (cmd.a_plot[XMN_PLT_BOXPLOT] &&
686           cmd.cmp == XMN_VARIABLES)
687         show_boxplot_variables (dependent_vars, n_dependent_vars,
688                                 factor);
689
690       if (cmd.a_plot[XMN_PLT_HISTOGRAM])
691         show_histogram (dependent_vars, n_dependent_vars, factor);
692
693       if (cmd.a_plot[XMN_PLT_NPPLOT])
694         show_npplot (dependent_vars, n_dependent_vars, factor);
695     }
696 }
697
698 /* Parse the PERCENTILES subcommand */
699 static int
700 xmn_custom_percentiles (struct lexer *lexer, struct dataset *ds UNUSED,
701                         struct cmd_examine *p UNUSED, void *aux UNUSED)
702 {
703   lex_match (lexer, '=');
704
705   lex_match (lexer, '(');
706
707   while ( lex_is_number (lexer) )
708     {
709       subc_list_double_push (&percentile_list, lex_number (lexer));
710
711       lex_get (lexer);
712
713       lex_match (lexer, ',') ;
714     }
715   lex_match (lexer, ')');
716
717   lex_match (lexer, '=');
718
719   if ( lex_match_id (lexer, "HAVERAGE"))
720     percentile_algorithm = PC_HAVERAGE;
721
722   else if ( lex_match_id (lexer, "WAVERAGE"))
723     percentile_algorithm = PC_WAVERAGE;
724
725   else if ( lex_match_id (lexer, "ROUND"))
726     percentile_algorithm = PC_ROUND;
727
728   else if ( lex_match_id (lexer, "EMPIRICAL"))
729     percentile_algorithm = PC_EMPIRICAL;
730
731   else if ( lex_match_id (lexer, "AEMPIRICAL"))
732     percentile_algorithm = PC_AEMPIRICAL;
733
734   else if ( lex_match_id (lexer, "NONE"))
735     percentile_algorithm = PC_NONE;
736
737
738   if ( 0 == subc_list_double_count (&percentile_list))
739     {
740       subc_list_double_push (&percentile_list, 5);
741       subc_list_double_push (&percentile_list, 10);
742       subc_list_double_push (&percentile_list, 25);
743       subc_list_double_push (&percentile_list, 50);
744       subc_list_double_push (&percentile_list, 75);
745       subc_list_double_push (&percentile_list, 90);
746       subc_list_double_push (&percentile_list, 95);
747     }
748
749   return 1;
750 }
751
752 /* TOTAL and NOTOTAL are simple, mutually exclusive flags */
753 static int
754 xmn_custom_total (struct lexer *lexer UNUSED, struct dataset *ds UNUSED,
755                   struct cmd_examine *p, void *aux UNUSED)
756 {
757   if ( p->sbc_nototal )
758     {
759       msg (SE, _("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
760       return 0;
761     }
762
763   return 1;
764 }
765
766 static int
767 xmn_custom_nototal (struct lexer *lexer UNUSED, struct dataset *ds UNUSED,
768                     struct cmd_examine *p, void *aux UNUSED)
769 {
770   if ( p->sbc_total )
771     {
772       msg (SE, _("%s and %s are mutually exclusive"), "TOTAL", "NOTOTAL");
773       return 0;
774     }
775
776   return 1;
777 }
778
779
780
781 /* Parser for the variables sub command
782    Returns 1 on success */
783 static int
784 xmn_custom_variables (struct lexer *lexer, struct dataset *ds,
785                       struct cmd_examine *cmd,
786                       void *aux UNUSED)
787 {
788   const struct dictionary *dict = dataset_dict (ds);
789   lex_match (lexer, '=');
790
791   if ( (lex_token (lexer) != T_ID || dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
792        && lex_token (lexer) != T_ALL)
793     {
794       return 2;
795     }
796
797   if (!parse_variables_const (lexer, dict, &dependent_vars, &n_dependent_vars,
798                               PV_NO_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH) )
799     {
800       free (dependent_vars);
801       return 0;
802     }
803
804   assert (n_dependent_vars);
805
806
807   if ( lex_match (lexer, T_BY))
808     {
809       int success ;
810       success =  examine_parse_independent_vars (lexer, dict, cmd);
811       if ( success != 1 )
812         {
813           free (dependent_vars);
814         }
815       return success;
816     }
817
818   return 1;
819 }
820
821
822
823 /* Parse the clause specifying the factors */
824 static int
825 examine_parse_independent_vars (struct lexer *lexer,
826                                 const struct dictionary *dict,
827                                 struct cmd_examine *cmd)
828 {
829   int success;
830   struct xfactor *sf = xmalloc (sizeof *sf);
831
832   ll_init (&sf->result_list);
833
834   if ( (lex_token (lexer) != T_ID ||
835         dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
836        && lex_token (lexer) != T_ALL)
837     {
838       free ( sf ) ;
839       return 2;
840     }
841
842   sf->indep_var[0] = parse_variable (lexer, dict);
843   sf->indep_var[1] = NULL;
844
845   if ( lex_token (lexer) == T_BY )
846     {
847       lex_match (lexer, T_BY);
848
849       if ( (lex_token (lexer) != T_ID ||
850             dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
851            && lex_token (lexer) != T_ALL)
852         {
853           free (sf);
854           return 2;
855         }
856
857       sf->indep_var[1] = parse_variable (lexer, dict);
858
859       ll_push_tail (&factor_list, &sf->ll);
860     }
861   else
862     ll_push_tail (&factor_list, &sf->ll);
863
864   lex_match (lexer, ',');
865
866   if ( lex_token (lexer) == '.' || lex_token (lexer) == '/' )
867     return 1;
868
869   success =  examine_parse_independent_vars (lexer, dict, cmd);
870
871   if ( success != 1 )
872     free ( sf ) ;
873
874   return success;
875 }
876
877 static void
878 examine_group (struct cmd_examine *cmd, struct casereader *reader, int level,
879                const struct dictionary *dict, struct xfactor *factor)
880 {
881   struct ccase *c;
882   const struct variable *wv = dict_get_weight (dict);
883   int v;
884   int n_extrema = 1;
885   struct factor_result *result = xzalloc (sizeof (*result));
886   int i;
887
888   for (i = 0; i < 2; i++)
889     if (factor->indep_var[i])
890       value_init (&result->value[i], var_get_width (factor->indep_var[i]));
891
892   result->metrics = xcalloc (n_dependent_vars, sizeof (*result->metrics));
893
894   if ( cmd->a_statistics[XMN_ST_EXTREME] )
895     n_extrema = cmd->st_n;
896
897
898   c = casereader_peek (reader, 0);
899   if (c != NULL)
900     {
901       if ( level > 0)
902         for (i = 0; i < 2; i++)
903           if (factor->indep_var[i])
904             value_copy (&result->value[i], case_data (c, factor->indep_var[i]),
905                         var_get_width (factor->indep_var[i]));
906       case_unref (c);
907     }
908
909   for (v = 0; v < n_dependent_vars; ++v)
910     {
911       struct casewriter *writer;
912       struct casereader *input = casereader_clone (reader);
913
914       result->metrics[v].moments = moments1_create (MOMENT_KURTOSIS);
915       result->metrics[v].minima = extrema_create (n_extrema, EXTREME_MINIMA);
916       result->metrics[v].maxima = extrema_create (n_extrema, EXTREME_MAXIMA);
917       result->metrics[v].cmin = DBL_MAX;
918
919       if (cmd->a_statistics[XMN_ST_DESCRIPTIVES] ||
920           cmd->a_plot[XMN_PLT_BOXPLOT] ||
921           cmd->a_plot[XMN_PLT_NPPLOT] ||
922           cmd->sbc_percentiles)
923         {
924           /* In this case, we need to sort the data, so we create a sorting
925              casewriter */
926           struct subcase up_ordering;
927           subcase_init_var (&up_ordering, dependent_vars[v], SC_ASCEND);
928           writer = sort_create_writer (&up_ordering,
929                                        casereader_get_proto (reader));
930           subcase_destroy (&up_ordering);
931         }
932       else
933         {
934           /* but in this case, sorting is unnecessary, so an ordinary
935              casewriter is sufficient */
936           writer =
937             autopaging_writer_create (casereader_get_proto (reader));
938         }
939
940
941       /* Sort or just iterate, whilst calculating moments etc */
942       while ((c = casereader_read (input)) != NULL)
943         {
944           int n_vals = caseproto_get_n_widths (casereader_get_proto (reader));
945           const casenumber loc = case_data_idx (c, n_vals - 1)->f;
946
947           const double weight = wv ? case_data (c, wv)->f : 1.0;
948           const union value *value = case_data (c, dependent_vars[v]);
949
950           if (weight != SYSMIS)
951             minimize (&result->metrics[v].cmin, weight);
952
953           moments1_add (result->metrics[v].moments,
954                         value->f,
955                         weight);
956
957           result->metrics[v].n += weight;
958
959           if ( ! var_is_value_missing (dependent_vars[v], value, MV_ANY) )
960             result->metrics[v].n_valid += weight;
961
962           extrema_add (result->metrics[v].maxima,
963                        value->f,
964                        weight,
965                        loc);
966
967           extrema_add (result->metrics[v].minima,
968                        value->f,
969                        weight,
970                        loc);
971
972           casewriter_write (writer, c);
973         }
974       casereader_destroy (input);
975       result->metrics[v].up_reader = casewriter_make_reader (writer);
976     }
977
978   /* If percentiles or descriptives have been requested, then a
979      second pass through the data (which has now been sorted)
980      is necessary */
981   if ( cmd->a_statistics[XMN_ST_DESCRIPTIVES] ||
982        cmd->a_plot[XMN_PLT_BOXPLOT] ||
983        cmd->a_plot[XMN_PLT_NPPLOT] ||
984        cmd->sbc_percentiles)
985     {
986       for (v = 0; v < n_dependent_vars; ++v)
987         {
988           int i;
989           int n_os;
990           struct order_stats **os ;
991           struct factor_metrics *metric = &result->metrics[v];
992
993           metric->n_ptiles = percentile_list.n_data;
994
995           metric->ptl = xcalloc (metric->n_ptiles,
996                                  sizeof (struct percentile *));
997
998           metric->quartiles = xcalloc (3, sizeof (*metric->quartiles));
999
1000           for (i = 0 ; i < metric->n_ptiles; ++i)
1001             {
1002               metric->ptl[i] = (struct percentile *)
1003                 percentile_create (percentile_list.data[i] / 100.0, metric->n_valid);
1004
1005               if ( percentile_list.data[i] == 25)
1006                 metric->quartiles[0] = metric->ptl[i];
1007               else if ( percentile_list.data[i] == 50)
1008                 metric->quartiles[1] = metric->ptl[i];
1009               else if ( percentile_list.data[i] == 75)
1010                 metric->quartiles[2] = metric->ptl[i];
1011             }
1012
1013           metric->tukey_hinges = tukey_hinges_create (metric->n_valid, metric->cmin);
1014           metric->trimmed_mean = trimmed_mean_create (metric->n_valid, 0.05);
1015
1016           n_os = metric->n_ptiles + 2;
1017
1018          if ( cmd->a_plot[XMN_PLT_NPPLOT] )
1019             {
1020               metric->np = np_create (metric->moments);
1021               n_os ++;
1022             }
1023
1024           os = xcalloc (sizeof (struct order_stats *), n_os);
1025
1026           for (i = 0 ; i < metric->n_ptiles ; ++i )
1027             {
1028               os[i] = (struct order_stats *) metric->ptl[i];
1029             }
1030
1031           os[i] = (struct order_stats *) metric->tukey_hinges;
1032           os[i+1] = (struct order_stats *) metric->trimmed_mean;
1033
1034           if (cmd->a_plot[XMN_PLT_NPPLOT])
1035             os[i+2] = metric->np;
1036
1037           order_stats_accumulate (os, n_os,
1038                                   casereader_clone (metric->up_reader),
1039                                   wv, dependent_vars[v], MV_ANY);
1040           free (os);
1041         }
1042     }
1043
1044   /* FIXME: Do this in the above loop */
1045   if ( cmd->a_plot[XMN_PLT_HISTOGRAM] )
1046     {
1047       struct ccase *c;
1048       struct casereader *input = casereader_clone (reader);
1049
1050       for (v = 0; v < n_dependent_vars; ++v)
1051         {
1052           const struct extremum  *max, *min;
1053           struct factor_metrics *metric = &result->metrics[v];
1054
1055           const struct ll_list *max_list =
1056             extrema_list (result->metrics[v].maxima);
1057
1058           const struct ll_list *min_list =
1059             extrema_list (result->metrics[v].minima);
1060
1061           if ( ll_is_empty (max_list))
1062             {
1063               msg (MW, _("Not creating plot because data set is empty."));
1064               continue;
1065             }
1066
1067           assert (! ll_is_empty (min_list));
1068
1069           max = (const struct extremum *)
1070             ll_data (ll_head(max_list), struct extremum, ll);
1071
1072           min = (const struct extremum *)
1073             ll_data (ll_head (min_list), struct extremum, ll);
1074
1075           metric->histogram = histogram_create (10, min->value, max->value);
1076         }
1077
1078       while ((c = casereader_read (input)) != NULL)
1079         {
1080           const double weight = wv ? case_data (c, wv)->f : 1.0;
1081
1082           for (v = 0; v < n_dependent_vars; ++v)
1083             {
1084               struct factor_metrics *metric = &result->metrics[v];
1085               if ( metric->histogram)
1086                 histogram_add ((struct histogram *) metric->histogram,
1087                                case_data (c, dependent_vars[v])->f, weight);
1088             }
1089           case_unref (c);
1090         }
1091       casereader_destroy (input);
1092     }
1093
1094   /* In this case, a third iteration is required */
1095   if (cmd->a_plot[XMN_PLT_BOXPLOT])
1096     {
1097       for (v = 0; v < n_dependent_vars; ++v)
1098         {
1099           struct factor_metrics *metric = &result->metrics[v];
1100           int n_vals = caseproto_get_n_widths (casereader_get_proto (
1101                                                  metric->up_reader));
1102
1103           metric->box_whisker =
1104             box_whisker_create ((struct tukey_hinges *) metric->tukey_hinges,
1105                                 cmd->v_id, n_vals - 1);
1106
1107           order_stats_accumulate ((struct order_stats **) &metric->box_whisker,
1108                                   1,
1109                                   casereader_clone (metric->up_reader),
1110                                   wv, dependent_vars[v], MV_ANY);
1111         }
1112     }
1113
1114   ll_push_tail (&factor->result_list, &result->ll);
1115   casereader_destroy (reader);
1116 }
1117
1118
1119 static void
1120 run_examine (struct cmd_examine *cmd, struct casereader *input,
1121              struct dataset *ds)
1122 {
1123   struct ll *ll;
1124   const struct dictionary *dict = dataset_dict (ds);
1125   struct ccase *c;
1126   struct casereader *level0 = casereader_clone (input);
1127
1128   c = casereader_peek (input, 0);
1129   if (c == NULL)
1130     {
1131       casereader_destroy (input);
1132       return;
1133     }
1134
1135   output_split_file_values (ds, c);
1136   case_unref (c);
1137
1138   ll_init (&level0_factor.result_list);
1139
1140   examine_group (cmd, level0, 0, dict, &level0_factor);
1141
1142   for (ll = ll_head (&factor_list);
1143        ll != ll_null (&factor_list);
1144        ll = ll_next (ll))
1145     {
1146       struct xfactor *factor = ll_data (ll, struct xfactor, ll);
1147
1148       struct casereader *group = NULL;
1149       struct casereader *level1;
1150       struct casegrouper *grouper1 = NULL;
1151
1152       level1 = casereader_clone (input);
1153       level1 = sort_execute_1var (level1, factor->indep_var[0]);
1154       grouper1 = casegrouper_create_vars (level1, &factor->indep_var[0], 1);
1155
1156       while (casegrouper_get_next_group (grouper1, &group))
1157         {
1158           struct casereader *group_copy = casereader_clone (group);
1159
1160           if ( !factor->indep_var[1])
1161             examine_group (cmd, group_copy, 1, dict, factor);
1162           else
1163             {
1164               int n_groups = 0;
1165               struct casereader *group2 = NULL;
1166               struct casegrouper *grouper2 = NULL;
1167
1168               group_copy = sort_execute_1var (group_copy,
1169                                               factor->indep_var[1]);
1170
1171               grouper2 = casegrouper_create_vars (group_copy,
1172                                                   &factor->indep_var[1], 1);
1173
1174               while (casegrouper_get_next_group (grouper2, &group2))
1175                 {
1176                   examine_group (cmd, group2, 2, dict, factor);
1177                   n_groups++;
1178                 }
1179               casegrouper_destroy (grouper2);
1180             }
1181
1182           casereader_destroy (group);
1183         }
1184       casegrouper_destroy (grouper1);
1185     }
1186
1187   casereader_destroy (input);
1188
1189   output_examine (dict);
1190
1191   factor_destroy (&level0_factor);
1192
1193   {
1194     struct ll *ll;
1195     for (ll = ll_head (&factor_list);
1196          ll != ll_null (&factor_list);
1197          ll = ll_next (ll))
1198       {
1199         struct xfactor *f = ll_data (ll, struct xfactor, ll);
1200         factor_destroy (f);
1201       }
1202   }
1203
1204 }
1205
1206
1207 static void
1208 show_summary (const struct variable **dependent_var, int n_dep_var,
1209               const struct dictionary *dict,
1210               const struct xfactor *fctr)
1211 {
1212   const struct variable *wv = dict_get_weight (dict);
1213   const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
1214
1215   static const char *subtitle[]=
1216     {
1217       N_("Valid"),
1218       N_("Missing"),
1219       N_("Total")
1220     };
1221
1222   int v, j;
1223   int heading_columns = 1;
1224   int n_cols;
1225   const int heading_rows = 3;
1226   struct tab_table *tbl;
1227
1228   int n_rows ;
1229   n_rows = n_dep_var;
1230
1231   assert (fctr);
1232
1233   if ( fctr->indep_var[0] )
1234     {
1235       heading_columns = 2;
1236
1237       if ( fctr->indep_var[1] )
1238         {
1239           heading_columns = 3;
1240         }
1241     }
1242
1243   n_rows *= ll_count (&fctr->result_list);
1244   n_rows += heading_rows;
1245
1246   n_cols = heading_columns + 6;
1247
1248   tbl = tab_create (n_cols, n_rows, 0);
1249   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
1250
1251   tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
1252
1253   /* Outline the box */
1254   tab_box (tbl,
1255            TAL_2, TAL_2,
1256            -1, -1,
1257            0, 0,
1258            n_cols - 1, n_rows - 1);
1259
1260   /* Vertical lines for the data only */
1261   tab_box (tbl,
1262            -1, -1,
1263            -1, TAL_1,
1264            heading_columns, 0,
1265            n_cols - 1, n_rows - 1);
1266
1267
1268   tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
1269   tab_hline (tbl, TAL_1, heading_columns, n_cols - 1, 1 );
1270   tab_hline (tbl, TAL_1, heading_columns, n_cols - 1, heading_rows -1 );
1271
1272   tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
1273
1274
1275   tab_title (tbl, _("Case Processing Summary"));
1276
1277   tab_joint_text (tbl, heading_columns, 0,
1278                   n_cols -1, 0,
1279                   TAB_CENTER | TAT_TITLE,
1280                   _("Cases"));
1281
1282   /* Remove lines ... */
1283   tab_box (tbl,
1284            -1, -1,
1285            TAL_0, TAL_0,
1286            heading_columns, 0,
1287            n_cols - 1, 0);
1288
1289   for (j = 0 ; j < 3 ; ++j)
1290     {
1291       tab_text (tbl, heading_columns + j * 2 , 2, TAB_CENTER | TAT_TITLE,
1292                 _("N"));
1293
1294       tab_text (tbl, heading_columns + j * 2 + 1, 2, TAB_CENTER | TAT_TITLE,
1295                 _("Percent"));
1296
1297       tab_joint_text (tbl, heading_columns + j * 2 , 1,
1298                       heading_columns + j * 2 + 1, 1,
1299                       TAB_CENTER | TAT_TITLE,
1300                       subtitle[j]);
1301
1302       tab_box (tbl, -1, -1,
1303                TAL_0, TAL_0,
1304                heading_columns + j * 2, 1,
1305                heading_columns + j * 2 + 1, 1);
1306     }
1307
1308
1309   /* Titles for the independent variables */
1310   if ( fctr->indep_var[0] )
1311     {
1312       tab_text (tbl, 1, heading_rows - 1, TAB_CENTER | TAT_TITLE,
1313                 var_to_string (fctr->indep_var[0]));
1314
1315       if ( fctr->indep_var[1] )
1316         {
1317           tab_text (tbl, 2, heading_rows - 1, TAB_CENTER | TAT_TITLE,
1318                     var_to_string (fctr->indep_var[1]));
1319         }
1320     }
1321
1322   for (v = 0 ; v < n_dep_var ; ++v)
1323     {
1324       int j = 0;
1325       struct ll *ll;
1326       const union value *last_value = NULL;
1327
1328       if ( v > 0 )
1329         tab_hline (tbl, TAL_1, 0, n_cols -1 ,
1330                    v * ll_count (&fctr->result_list)
1331                    + heading_rows);
1332
1333       tab_text (tbl,
1334                 0,
1335                 v * ll_count (&fctr->result_list) + heading_rows,
1336                 TAB_LEFT | TAT_TITLE,
1337                 var_to_string (dependent_var[v])
1338                 );
1339
1340
1341       for (ll = ll_head (&fctr->result_list);
1342            ll != ll_null (&fctr->result_list); ll = ll_next (ll))
1343         {
1344           double n;
1345           const struct factor_result *result =
1346             ll_data (ll, struct factor_result, ll);
1347
1348           if ( fctr->indep_var[0] )
1349             {
1350
1351               if ( last_value == NULL ||
1352                    !value_equal (last_value, &result->value[0],
1353                                  var_get_width (fctr->indep_var[0])))
1354                 {
1355                   struct string str;
1356
1357                   last_value = &result->value[0];
1358                   ds_init_empty (&str);
1359
1360                   var_append_value_name (fctr->indep_var[0], &result->value[0],
1361                                          &str);
1362
1363                   tab_text (tbl, 1,
1364                             heading_rows + j +
1365                             v * ll_count (&fctr->result_list),
1366                             TAB_LEFT | TAT_TITLE,
1367                             ds_cstr (&str));
1368
1369                   ds_destroy (&str);
1370
1371                   if ( fctr->indep_var[1] && j > 0)
1372                     tab_hline (tbl, TAL_1, 1, n_cols - 1,
1373                                heading_rows + j +
1374                                v * ll_count (&fctr->result_list));
1375                 }
1376
1377               if ( fctr->indep_var[1])
1378                 {
1379                   struct string str;
1380
1381                   ds_init_empty (&str);
1382
1383                   var_append_value_name (fctr->indep_var[1],
1384                                          &result->value[1], &str);
1385
1386                   tab_text (tbl, 2,
1387                             heading_rows + j +
1388                             v * ll_count (&fctr->result_list),
1389                             TAB_LEFT | TAT_TITLE,
1390                             ds_cstr (&str));
1391
1392                   ds_destroy (&str);
1393                 }
1394             }
1395
1396
1397           moments1_calculate (result->metrics[v].moments,
1398                               &n, &result->metrics[v].mean,
1399                               &result->metrics[v].variance,
1400                               &result->metrics[v].skewness,
1401                               &result->metrics[v].kurtosis);
1402
1403           result->metrics[v].se_mean = sqrt (result->metrics[v].variance / n) ;
1404
1405           /* Total Valid */
1406           tab_double (tbl, heading_columns,
1407                      heading_rows + j + v * ll_count (&fctr->result_list),
1408                      TAB_LEFT,
1409                      n, wfmt);
1410
1411           tab_text (tbl, heading_columns + 1,
1412                     heading_rows + j + v * ll_count (&fctr->result_list),
1413                     TAB_RIGHT | TAT_PRINTF,
1414                     "%g%%", n * 100.0 / result->metrics[v].n);
1415
1416           /* Total Missing */
1417           tab_double (tbl, heading_columns + 2,
1418                      heading_rows + j + v * ll_count (&fctr->result_list),
1419                      TAB_LEFT,
1420                      result->metrics[v].n - n,
1421                      wfmt);
1422
1423           tab_text (tbl, heading_columns + 3,
1424                     heading_rows + j + v * ll_count (&fctr->result_list),
1425                     TAB_RIGHT | TAT_PRINTF,
1426                     "%g%%",
1427                     (result->metrics[v].n - n) * 100.0 / result->metrics[v].n
1428                     );
1429
1430           /* Total Valid + Missing */
1431           tab_double (tbl, heading_columns + 4,
1432                      heading_rows + j + v * ll_count (&fctr->result_list),
1433                      TAB_LEFT,
1434                      result->metrics[v].n,
1435                      wfmt);
1436
1437           tab_text (tbl, heading_columns + 5,
1438                     heading_rows + j + v * ll_count (&fctr->result_list),
1439                     TAB_RIGHT | TAT_PRINTF,
1440                     "%g%%",
1441                     (result->metrics[v].n) * 100.0 / result->metrics[v].n
1442                     );
1443
1444           ++j;
1445         }
1446     }
1447
1448
1449   tab_submit (tbl);
1450 }
1451
1452 #define DESCRIPTIVE_ROWS 13
1453
1454 static void
1455 show_descriptives (const struct variable **dependent_var,
1456                    int n_dep_var,
1457                    const struct xfactor *fctr)
1458 {
1459   int v;
1460   int heading_columns = 3;
1461   int n_cols;
1462   const int heading_rows = 1;
1463   struct tab_table *tbl;
1464
1465   int n_rows ;
1466   n_rows = n_dep_var;
1467
1468   assert (fctr);
1469
1470   if ( fctr->indep_var[0] )
1471     {
1472       heading_columns = 4;
1473
1474       if ( fctr->indep_var[1] )
1475         {
1476           heading_columns = 5;
1477         }
1478     }
1479
1480   n_rows *= ll_count (&fctr->result_list) * DESCRIPTIVE_ROWS;
1481   n_rows += heading_rows;
1482
1483   n_cols = heading_columns + 2;
1484
1485   tbl = tab_create (n_cols, n_rows, 0);
1486   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
1487
1488   tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
1489
1490   /* Outline the box */
1491   tab_box (tbl,
1492            TAL_2, TAL_2,
1493            -1, -1,
1494            0, 0,
1495            n_cols - 1, n_rows - 1);
1496
1497
1498   tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
1499   tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
1500
1501   tab_vline (tbl, TAL_1, n_cols - 1, 0, n_rows - 1);
1502
1503
1504   if ( fctr->indep_var[0])
1505     tab_text (tbl, 1, 0, TAT_TITLE, var_to_string (fctr->indep_var[0]));
1506
1507   if ( fctr->indep_var[1])
1508     tab_text (tbl, 2, 0, TAT_TITLE, var_to_string (fctr->indep_var[1]));
1509
1510   for (v = 0 ; v < n_dep_var ; ++v )
1511     {
1512       struct ll *ll;
1513       int i = 0;
1514
1515       const int row_var_start =
1516         v * DESCRIPTIVE_ROWS * ll_count(&fctr->result_list);
1517
1518       tab_text (tbl,
1519                 0,
1520                 heading_rows + row_var_start,
1521                 TAB_LEFT | TAT_TITLE,
1522                 var_to_string (dependent_var[v])
1523                 );
1524
1525       for (ll = ll_head (&fctr->result_list);
1526            ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
1527         {
1528           const struct factor_result *result =
1529             ll_data (ll, struct factor_result, ll);
1530
1531           const double t =
1532             gsl_cdf_tdist_Qinv ((1 - cmd.n_cinterval[0] / 100.0) / 2.0,
1533                                       result->metrics[v].n - 1);
1534
1535           if ( i > 0 || v > 0 )
1536             {
1537               const int left_col = (i == 0) ? 0 : 1;
1538               tab_hline (tbl, TAL_1, left_col, n_cols - 1,
1539                          heading_rows + row_var_start + i * DESCRIPTIVE_ROWS);
1540             }
1541
1542           if ( fctr->indep_var[0])
1543             {
1544               struct string vstr;
1545               ds_init_empty (&vstr);
1546               var_append_value_name (fctr->indep_var[0],
1547                                      &result->value[0], &vstr);
1548
1549               tab_text (tbl, 1,
1550                         heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
1551                         TAB_LEFT,
1552                         ds_cstr (&vstr)
1553                         );
1554
1555               ds_destroy (&vstr);
1556             }
1557
1558
1559           tab_text (tbl, n_cols - 4,
1560                     heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
1561                     TAB_LEFT,
1562                     _("Mean"));
1563
1564           tab_text (tbl, n_cols - 4,
1565                     heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
1566                     TAB_LEFT | TAT_PRINTF,
1567                     _("%g%% Confidence Interval for Mean"),
1568                     cmd.n_cinterval[0]);
1569
1570           tab_text (tbl, n_cols - 3,
1571                     heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
1572                     TAB_LEFT,
1573                     _("Lower Bound"));
1574
1575           tab_text (tbl, n_cols - 3,
1576                     heading_rows + row_var_start + 2 + i * DESCRIPTIVE_ROWS,
1577                     TAB_LEFT,
1578                     _("Upper Bound"));
1579
1580           tab_text (tbl, n_cols - 4,
1581                     heading_rows + row_var_start + 3 + i * DESCRIPTIVE_ROWS,
1582                     TAB_LEFT | TAT_PRINTF,
1583                     _("5%% Trimmed Mean"));
1584
1585           tab_text (tbl, n_cols - 4,
1586                     heading_rows + row_var_start + 4 + i * DESCRIPTIVE_ROWS,
1587                     TAB_LEFT,
1588                     _("Median"));
1589
1590           tab_text (tbl, n_cols - 4,
1591                     heading_rows + row_var_start + 5 + i * DESCRIPTIVE_ROWS,
1592                     TAB_LEFT,
1593                     _("Variance"));
1594
1595           tab_text (tbl, n_cols - 4,
1596                     heading_rows + row_var_start + 6 + i * DESCRIPTIVE_ROWS,
1597                     TAB_LEFT,
1598                     _("Std. Deviation"));
1599
1600           tab_text (tbl, n_cols - 4,
1601                     heading_rows + row_var_start + 7 + i * DESCRIPTIVE_ROWS,
1602                     TAB_LEFT,
1603                     _("Minimum"));
1604
1605           tab_text (tbl, n_cols - 4,
1606                     heading_rows + row_var_start + 8 + i * DESCRIPTIVE_ROWS,
1607                     TAB_LEFT,
1608                     _("Maximum"));
1609
1610           tab_text (tbl, n_cols - 4,
1611                     heading_rows + row_var_start + 9 + i * DESCRIPTIVE_ROWS,
1612                     TAB_LEFT,
1613                     _("Range"));
1614
1615           tab_text (tbl, n_cols - 4,
1616                     heading_rows + row_var_start + 10 + i * DESCRIPTIVE_ROWS,
1617                     TAB_LEFT,
1618                     _("Interquartile Range"));
1619
1620
1621           tab_text (tbl, n_cols - 4,
1622                     heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
1623                     TAB_LEFT,
1624                     _("Skewness"));
1625
1626           tab_text (tbl, n_cols - 4,
1627                     heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
1628                     TAB_LEFT,
1629                     _("Kurtosis"));
1630
1631
1632           /* Now the statistics ... */
1633
1634           tab_double (tbl, n_cols - 2,
1635                     heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
1636                      TAB_CENTER,
1637                      result->metrics[v].mean,
1638                      NULL);
1639
1640           tab_double (tbl, n_cols - 1,
1641                     heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
1642                      TAB_CENTER,
1643                      result->metrics[v].se_mean,
1644                      NULL);
1645
1646
1647           tab_double (tbl, n_cols - 2,
1648                      heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
1649                      TAB_CENTER,
1650                      result->metrics[v].mean - t *
1651                       result->metrics[v].se_mean,
1652                      NULL);
1653
1654           tab_double (tbl, n_cols - 2,
1655                      heading_rows + row_var_start + 2 + i * DESCRIPTIVE_ROWS,
1656                      TAB_CENTER,
1657                      result->metrics[v].mean + t *
1658                       result->metrics[v].se_mean,
1659                      NULL);
1660
1661
1662           tab_double (tbl, n_cols - 2,
1663                      heading_rows + row_var_start + 3 + i * DESCRIPTIVE_ROWS,
1664                      TAB_CENTER,
1665                      trimmed_mean_calculate ((struct trimmed_mean *) result->metrics[v].trimmed_mean),
1666                      NULL);
1667
1668
1669           tab_double (tbl, n_cols - 2,
1670                      heading_rows + row_var_start + 4 + i * DESCRIPTIVE_ROWS,
1671                      TAB_CENTER,
1672                      percentile_calculate (result->metrics[v].quartiles[1], percentile_algorithm),
1673                      NULL);
1674
1675
1676           tab_double (tbl, n_cols - 2,
1677                      heading_rows + row_var_start + 5 + i * DESCRIPTIVE_ROWS,
1678                      TAB_CENTER,
1679                      result->metrics[v].variance,
1680                      NULL);
1681
1682           tab_double (tbl, n_cols - 2,
1683                      heading_rows + row_var_start + 6 + i * DESCRIPTIVE_ROWS,
1684                      TAB_CENTER,
1685                      sqrt (result->metrics[v].variance),
1686                      NULL);
1687
1688           tab_double (tbl, n_cols - 2,
1689                      heading_rows + row_var_start + 10 + i * DESCRIPTIVE_ROWS,
1690                      TAB_CENTER,
1691                      percentile_calculate (result->metrics[v].quartiles[2],
1692                                            percentile_algorithm) -
1693                      percentile_calculate (result->metrics[v].quartiles[0],
1694                                            percentile_algorithm),
1695                      NULL);
1696
1697
1698           tab_double (tbl, n_cols - 2,
1699                      heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
1700                      TAB_CENTER,
1701                      result->metrics[v].skewness,
1702                      NULL);
1703
1704           tab_double (tbl, n_cols - 2,
1705                      heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
1706                      TAB_CENTER,
1707                      result->metrics[v].kurtosis,
1708                      NULL);
1709
1710           tab_double (tbl, n_cols - 1,
1711                      heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
1712                      TAB_CENTER,
1713                      calc_seskew (result->metrics[v].n),
1714                      NULL);
1715
1716           tab_double (tbl, n_cols - 1,
1717                      heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
1718                      TAB_CENTER,
1719                      calc_sekurt (result->metrics[v].n),
1720                      NULL);
1721
1722           {
1723             struct extremum *minimum, *maximum ;
1724
1725             struct ll *max_ll = ll_head (extrema_list (result->metrics[v].maxima));
1726             struct ll *min_ll = ll_head (extrema_list (result->metrics[v].minima));
1727
1728             maximum = ll_data (max_ll, struct extremum, ll);
1729             minimum = ll_data (min_ll, struct extremum, ll);
1730
1731             tab_double (tbl, n_cols - 2,
1732                        heading_rows + row_var_start + 7 + i * DESCRIPTIVE_ROWS,
1733                        TAB_CENTER,
1734                        minimum->value,
1735                        NULL);
1736
1737             tab_double (tbl, n_cols - 2,
1738                        heading_rows + row_var_start + 8 + i * DESCRIPTIVE_ROWS,
1739                        TAB_CENTER,
1740                        maximum->value,
1741                        NULL);
1742
1743             tab_double (tbl, n_cols - 2,
1744                        heading_rows + row_var_start + 9 + i * DESCRIPTIVE_ROWS,
1745                        TAB_CENTER,
1746                        maximum->value - minimum->value,
1747                        NULL);
1748           }
1749         }
1750     }
1751
1752   tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
1753
1754   tab_title (tbl, _("Descriptives"));
1755
1756   tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE,
1757             _("Statistic"));
1758
1759   tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE,
1760             _("Std. Error"));
1761
1762   tab_submit (tbl);
1763 }
1764
1765
1766
1767 static void
1768 show_extremes (const struct variable **dependent_var,
1769                int n_dep_var,
1770                const struct xfactor *fctr)
1771 {
1772   int v;
1773   int heading_columns = 3;
1774   int n_cols;
1775   const int heading_rows = 1;
1776   struct tab_table *tbl;
1777
1778   int n_rows ;
1779   n_rows = n_dep_var;
1780
1781   assert (fctr);
1782
1783   if ( fctr->indep_var[0] )
1784     {
1785       heading_columns = 4;
1786
1787       if ( fctr->indep_var[1] )
1788         {
1789           heading_columns = 5;
1790         }
1791     }
1792
1793   n_rows *= ll_count (&fctr->result_list) * cmd.st_n * 2;
1794   n_rows += heading_rows;
1795
1796   n_cols = heading_columns + 2;
1797
1798   tbl = tab_create (n_cols, n_rows, 0);
1799   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
1800
1801   tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
1802
1803   /* Outline the box */
1804   tab_box (tbl,
1805            TAL_2, TAL_2,
1806            -1, -1,
1807            0, 0,
1808            n_cols - 1, n_rows - 1);
1809
1810
1811   tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
1812   tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
1813   tab_vline (tbl, TAL_1, n_cols - 1, 0, n_rows - 1);
1814
1815   if ( fctr->indep_var[0])
1816     tab_text (tbl, 1, 0, TAT_TITLE, var_to_string (fctr->indep_var[0]));
1817
1818   if ( fctr->indep_var[1])
1819     tab_text (tbl, 2, 0, TAT_TITLE, var_to_string (fctr->indep_var[1]));
1820
1821   for (v = 0 ; v < n_dep_var ; ++v )
1822     {
1823       struct ll *ll;
1824       int i = 0;
1825       const int row_var_start = v * cmd.st_n * 2 * ll_count(&fctr->result_list);
1826
1827       tab_text (tbl,
1828                 0,
1829                 heading_rows + row_var_start,
1830                 TAB_LEFT | TAT_TITLE,
1831                 var_to_string (dependent_var[v])
1832                 );
1833
1834       for (ll = ll_head (&fctr->result_list);
1835            ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
1836         {
1837           int e ;
1838           struct ll *min_ll;
1839           struct ll *max_ll;
1840           const int row_result_start = i * cmd.st_n * 2;
1841
1842           const struct factor_result *result =
1843             ll_data (ll, struct factor_result, ll);
1844
1845           if (i > 0 || v > 0)
1846             tab_hline (tbl, TAL_1, 1, n_cols - 1,
1847                        heading_rows + row_var_start + row_result_start);
1848
1849           tab_hline (tbl, TAL_1, heading_columns - 2, n_cols - 1,
1850                      heading_rows + row_var_start + row_result_start + cmd.st_n);
1851
1852           for ( e = 1; e <= cmd.st_n; ++e )
1853             {
1854               tab_text (tbl, n_cols - 3,
1855                         heading_rows + row_var_start + row_result_start + e - 1,
1856                         TAB_RIGHT | TAT_PRINTF,
1857                         _("%d"), e);
1858
1859               tab_text (tbl, n_cols - 3,
1860                         heading_rows + row_var_start + row_result_start + cmd.st_n + e - 1,
1861                         TAB_RIGHT | TAT_PRINTF,
1862                         _("%d"), e);
1863             }
1864
1865
1866           min_ll = ll_head (extrema_list (result->metrics[v].minima));
1867           for (e = 0; e < cmd.st_n;)
1868             {
1869               struct extremum *minimum = ll_data (min_ll, struct extremum, ll);
1870               double weight = minimum->weight;
1871
1872               while (weight-- > 0 && e < cmd.st_n)
1873                 {
1874                   tab_double (tbl, n_cols - 1,
1875                              heading_rows + row_var_start + row_result_start + cmd.st_n + e,
1876                              TAB_RIGHT,
1877                              minimum->value,
1878                              NULL);
1879
1880
1881                   tab_fixed (tbl, n_cols - 2,
1882                              heading_rows + row_var_start +
1883                              row_result_start + cmd.st_n + e,
1884                              TAB_RIGHT,
1885                              minimum->location,
1886                              10, 0);
1887                   ++e;
1888                 }
1889
1890               min_ll = ll_next (min_ll);
1891             }
1892
1893
1894           max_ll = ll_head (extrema_list (result->metrics[v].maxima));
1895           for (e = 0; e < cmd.st_n;)
1896             {
1897               struct extremum *maximum = ll_data (max_ll, struct extremum, ll);
1898               double weight = maximum->weight;
1899
1900               while (weight-- > 0 && e < cmd.st_n)
1901                 {
1902                   tab_double (tbl, n_cols - 1,
1903                              heading_rows + row_var_start +
1904                               row_result_start + e,
1905                              TAB_RIGHT,
1906                              maximum->value,
1907                              NULL);
1908
1909
1910                   tab_fixed (tbl, n_cols - 2,
1911                              heading_rows + row_var_start +
1912                              row_result_start + e,
1913                              TAB_RIGHT,
1914                              maximum->location,
1915                              10, 0);
1916                   ++e;
1917                 }
1918
1919               max_ll = ll_next (max_ll);
1920             }
1921
1922
1923           if ( fctr->indep_var[0])
1924             {
1925               struct string vstr;
1926               ds_init_empty (&vstr);
1927               var_append_value_name (fctr->indep_var[0],
1928                                      &result->value[0], &vstr);
1929
1930               tab_text (tbl, 1,
1931                         heading_rows + row_var_start + row_result_start,
1932                         TAB_LEFT,
1933                         ds_cstr (&vstr)
1934                         );
1935
1936               ds_destroy (&vstr);
1937             }
1938
1939
1940           tab_text (tbl, n_cols - 4,
1941                     heading_rows + row_var_start + row_result_start,
1942                     TAB_RIGHT,
1943                     _("Highest"));
1944
1945           tab_text (tbl, n_cols - 4,
1946                     heading_rows + row_var_start + row_result_start + cmd.st_n,
1947                     TAB_RIGHT,
1948                     _("Lowest"));
1949         }
1950     }
1951
1952   tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
1953
1954
1955   tab_title (tbl, _("Extreme Values"));
1956
1957
1958   tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE,
1959             _("Case Number"));
1960
1961
1962   tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE,
1963             _("Value"));
1964
1965   tab_submit (tbl);
1966 }
1967
1968 #define PERCENTILE_ROWS 2
1969
1970 static void
1971 show_percentiles (const struct variable **dependent_var,
1972                   int n_dep_var,
1973                   const struct xfactor *fctr)
1974 {
1975   int i;
1976   int v;
1977   int heading_columns = 2;
1978   int n_cols;
1979   const int n_percentiles = subc_list_double_count (&percentile_list);
1980   const int heading_rows = 2;
1981   struct tab_table *tbl;
1982
1983   int n_rows ;
1984   n_rows = n_dep_var;
1985
1986   assert (fctr);
1987
1988   if ( fctr->indep_var[0] )
1989     {
1990       heading_columns = 3;
1991
1992       if ( fctr->indep_var[1] )
1993         {
1994           heading_columns = 4;
1995         }
1996     }
1997
1998   n_rows *= ll_count (&fctr->result_list) * PERCENTILE_ROWS;
1999   n_rows += heading_rows;
2000
2001   n_cols = heading_columns + n_percentiles;
2002
2003   tbl = tab_create (n_cols, n_rows, 0);
2004   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
2005
2006   tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
2007
2008   /* Outline the box */
2009   tab_box (tbl,
2010            TAL_2, TAL_2,
2011            -1, -1,
2012            0, 0,
2013            n_cols - 1, n_rows - 1);
2014
2015
2016   tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
2017   tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
2018
2019   if ( fctr->indep_var[0])
2020     tab_text (tbl, 1, 1, TAT_TITLE, var_to_string (fctr->indep_var[0]));
2021
2022   if ( fctr->indep_var[1])
2023     tab_text (tbl, 2, 1, TAT_TITLE, var_to_string (fctr->indep_var[1]));
2024
2025   for (v = 0 ; v < n_dep_var ; ++v )
2026     {
2027       double hinges[3];
2028       struct ll *ll;
2029       int i = 0;
2030
2031       const int row_var_start =
2032         v * PERCENTILE_ROWS * ll_count(&fctr->result_list);
2033
2034       tab_text (tbl,
2035                 0,
2036                 heading_rows + row_var_start,
2037                 TAB_LEFT | TAT_TITLE,
2038                 var_to_string (dependent_var[v])
2039                 );
2040
2041       for (ll = ll_head (&fctr->result_list);
2042            ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
2043         {
2044           int j;
2045           const struct factor_result *result =
2046             ll_data (ll, struct factor_result, ll);
2047
2048           if ( i > 0 || v > 0 )
2049             {
2050               const int left_col = (i == 0) ? 0 : 1;
2051               tab_hline (tbl, TAL_1, left_col, n_cols - 1,
2052                          heading_rows + row_var_start + i * PERCENTILE_ROWS);
2053             }
2054
2055           if ( fctr->indep_var[0])
2056             {
2057               struct string vstr;
2058               ds_init_empty (&vstr);
2059               var_append_value_name (fctr->indep_var[0],
2060                                      &result->value[0], &vstr);
2061
2062               tab_text (tbl, 1,
2063                         heading_rows + row_var_start + i * PERCENTILE_ROWS,
2064                         TAB_LEFT,
2065                         ds_cstr (&vstr)
2066                         );
2067
2068               ds_destroy (&vstr);
2069             }
2070
2071
2072           tab_text (tbl, n_cols - n_percentiles - 1,
2073                     heading_rows + row_var_start + i * PERCENTILE_ROWS,
2074                     TAB_LEFT,
2075                     ptile_alg_desc [percentile_algorithm]);
2076
2077
2078           tab_text (tbl, n_cols - n_percentiles - 1,
2079                     heading_rows + row_var_start + 1 + i * PERCENTILE_ROWS,
2080                     TAB_LEFT,
2081                     _("Tukey's Hinges"));
2082
2083
2084           tab_vline (tbl, TAL_1, n_cols - n_percentiles -1, heading_rows, n_rows - 1);
2085
2086           tukey_hinges_calculate ((struct tukey_hinges *) result->metrics[v].tukey_hinges,
2087                                   hinges);
2088
2089           for (j = 0; j < n_percentiles; ++j)
2090             {
2091               double hinge = SYSMIS;
2092               tab_double (tbl, n_cols - n_percentiles + j,
2093                          heading_rows + row_var_start + i * PERCENTILE_ROWS,
2094                          TAB_CENTER,
2095                          percentile_calculate (result->metrics[v].ptl[j],
2096                                                percentile_algorithm),
2097                          NULL
2098                          );
2099
2100               if ( result->metrics[v].ptl[j]->ptile == 0.5)
2101                 hinge = hinges[1];
2102               else if ( result->metrics[v].ptl[j]->ptile == 0.25)
2103                 hinge = hinges[0];
2104               else if ( result->metrics[v].ptl[j]->ptile == 0.75)
2105                 hinge = hinges[2];
2106
2107               if ( hinge != SYSMIS)
2108                 tab_double (tbl, n_cols - n_percentiles + j,
2109                            heading_rows + row_var_start + 1 + i * PERCENTILE_ROWS,
2110                            TAB_CENTER,
2111                            hinge,
2112                            NULL
2113                            );
2114
2115             }
2116         }
2117     }
2118
2119   tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
2120
2121   tab_title (tbl, _("Percentiles"));
2122
2123
2124   for (i = 0 ; i < n_percentiles; ++i )
2125     {
2126       tab_text (tbl, n_cols - n_percentiles + i, 1,
2127                 TAB_CENTER | TAT_TITLE | TAT_PRINTF,
2128                 _("%g"),
2129                 subc_list_double_at (&percentile_list, i)
2130                 );
2131
2132
2133     }
2134
2135   tab_joint_text (tbl,
2136                   n_cols - n_percentiles, 0,
2137                   n_cols - 1, 0,
2138                   TAB_CENTER | TAT_TITLE,
2139                   _("Percentiles"));
2140
2141   /* Vertical lines for the data only */
2142   tab_box (tbl,
2143            -1, -1,
2144            -1, TAL_1,
2145            n_cols - n_percentiles, 1,
2146            n_cols - 1, n_rows - 1);
2147
2148   tab_hline (tbl, TAL_1, n_cols - n_percentiles, n_cols - 1, 1);
2149
2150
2151   tab_submit (tbl);
2152 }
2153
2154
2155 static void
2156 factor_to_string_concise (const struct xfactor *fctr,
2157                           const struct factor_result *result,
2158                           struct string *str
2159                           )
2160 {
2161   if (fctr->indep_var[0])
2162     {
2163       var_append_value_name (fctr->indep_var[0], &result->value[0], str);
2164
2165       if ( fctr->indep_var[1] )
2166         {
2167           ds_put_cstr (str, ",");
2168
2169           var_append_value_name (fctr->indep_var[1], &result->value[1], str);
2170
2171           ds_put_cstr (str, ")");
2172         }
2173     }
2174 }
2175
2176
2177 static void
2178 factor_to_string (const struct xfactor *fctr,
2179                   const struct factor_result *result,
2180                   struct string *str
2181                   )
2182 {
2183   if (fctr->indep_var[0])
2184     {
2185       ds_put_format (str, "(%s = ", var_get_name (fctr->indep_var[0]));
2186
2187       var_append_value_name (fctr->indep_var[0], &result->value[0], str);
2188
2189       if ( fctr->indep_var[1] )
2190         {
2191           ds_put_cstr (str, ",");
2192           ds_put_format (str, "%s = ", var_get_name (fctr->indep_var[1]));
2193
2194           var_append_value_name (fctr->indep_var[1], &result->value[1], str);
2195         }
2196       ds_put_cstr (str, ")");
2197     }
2198 }
2199
2200
2201
2202
2203 /*
2204   Local Variables:
2205   mode: c
2206   End:
2207 */