Made EXAMINE cope properly with splits.
[pspp-builds.git] / src / examine.q
1 /* PSPP - EXAMINE data for normality . -*-c-*-
2
3 Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
4 Author: John Darrington 2004
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA. */
20
21 #include <config.h>
22 #include "error.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <math.h>
26 #include "alloc.h"
27 #include "str.h"
28 #include "case.h"
29 #include "command.h"
30 #include "lexer.h"
31 #include "error.h"
32 #include "magic.h"
33 #include "misc.h"
34 #include "tab.h"
35 #include "som.h"
36 #include "value-labels.h"
37 #include "var.h"
38 #include "vfm.h"
39 #include "hash.h"
40 #include "casefile.h"
41
42 /* (specification)
43    "EXAMINE" (xmn_):
44    *variables=custom;
45    +total=custom;
46    +nototal=custom;
47    +missing=miss:pairwise/!listwise,
48    rep:report/!noreport,
49    incl:include/!exclude;
50    +compare=cmp:variables/!groups;
51    +cinterval=double;
52    +statistics[st_]=descriptives,:extreme(*d:n),all,none.
53 */
54
55 /* (declarations) */
56
57 /* (functions) */
58
59
60 static struct cmd_examine cmd;
61
62 static struct variable **dependent_vars;
63
64 static int n_dependent_vars;
65
66 static struct hsh_table *hash_table_factors;
67
68
69 struct factor 
70 {
71   /* The independent variable for this factor */
72   struct variable *indep_var;
73
74   /* The list of values of the independent variable */
75   struct hsh_table *hash_table_val;
76
77   /* The subfactor (if any) */
78   struct factor *subfactor;
79
80 };
81
82
83 /* Parse the clause specifying the factors */
84 static int examine_parse_independent_vars(struct cmd_examine *cmd, 
85                                           struct hsh_table *hash_factors );
86
87
88
89
90 /* Functions to support hashes of factors */
91 int compare_factors(const struct factor *f1, const struct factor *f2, 
92                     void *aux);
93
94 unsigned hash_factor(const struct factor *f, void *aux);
95
96 void free_factor(struct factor *f, void *aux UNUSED);
97
98
99 /* Output functions */
100 static void show_summary(struct variable **dependent_var, int n_dep_var, 
101                          struct factor *f);
102
103 static void show_descriptives(struct variable **dependent_var, 
104                               int n_dep_var, 
105                               struct factor *factor);
106
107
108 static void show_extremes(struct variable **dependent_var, 
109                           int n_dep_var, 
110                           struct factor *factor,
111                           int n_extremities);
112
113
114 /* Per Split function */
115 static void run_examine(const struct casefile *cf, void *cmd_);
116
117 static void output_examine(void);
118
119
120 int
121 cmd_examine(void)
122 {
123
124
125   if ( !parse_examine(&cmd) )
126     return CMD_FAILURE;
127   
128   if ( cmd.st_n == SYSMIS ) 
129     cmd.st_n = 5;
130
131   if ( ! cmd.sbc_cinterval) 
132     cmd.n_cinterval[0] = 95.0;
133
134
135
136   multipass_procedure_with_splits (run_examine, &cmd);
137
138
139   hsh_destroy(hash_table_factors);
140
141
142   return CMD_SUCCESS;
143 };
144
145
146 /* Show all the appropriate tables */
147 static void
148 output_examine(void)
149 {
150
151   /* Show totals if appropriate */
152   if ( ! cmd.sbc_nototal || 
153        ! hash_table_factors || 0 == hsh_count (hash_table_factors))
154     {
155       show_summary(dependent_vars, n_dependent_vars,0);
156
157       if ( cmd.sbc_statistics ) 
158         {
159           if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES]) 
160             show_descriptives(dependent_vars, n_dependent_vars, 0);
161           
162           if ( cmd.a_statistics[XMN_ST_EXTREME]) 
163             show_extremes(dependent_vars, n_dependent_vars, 0, cmd.st_n);
164         }
165     }
166
167   /* Show grouped statistics  if appropriate */
168   if ( hash_table_factors && 0 != hsh_count (hash_table_factors))
169     {
170       struct hsh_iterator hi;
171       struct factor *f;
172
173       for(f = hsh_first(hash_table_factors,&hi);
174           f != 0;
175           f = hsh_next(hash_table_factors,&hi)) 
176         {
177           show_summary(dependent_vars, n_dependent_vars,f);
178
179           if ( cmd.sbc_statistics )
180             {
181               if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES])
182                 show_descriptives(dependent_vars, n_dependent_vars, f);
183               
184               if ( cmd.a_statistics[XMN_ST_EXTREME])
185                 show_extremes(dependent_vars, n_dependent_vars, f, cmd.st_n);
186             }
187         }
188     }
189
190
191 }
192
193
194
195 /* TOTAL and NOTOTAL are simple, mutually exclusive flags */
196 static int
197 xmn_custom_total(struct cmd_examine *p)
198 {
199   if ( p->sbc_nototal ) 
200     {
201       msg (SE, _("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
202       return 0;
203     }
204
205   return 1;
206 }
207
208 static int
209 xmn_custom_nototal(struct cmd_examine *p)
210 {
211   if ( p->sbc_total ) 
212     {
213       msg (SE, _("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
214       return 0;
215     }
216
217   return 1;
218 }
219
220
221 /* Compare two factors */
222 int 
223 compare_factors (const struct factor *f1, 
224                  const struct factor *f2, 
225                  void *aux UNUSED)
226 {
227   int v1_cmp;
228
229   v1_cmp = strcmp(f1->v1->name, f2->v1->name);
230
231   if ( 0 != v1_cmp ) 
232     return v1_cmp;
233
234   if ( f1->v2 == 0 && f2->v2 == 0 ) 
235     return 0;
236
237   if ( f1->v2 == 0 && f2->v2 != 0 ) 
238     return -1;
239
240   if ( f1->v2 != 0 && f2->v2 == 0 ) 
241     return +1;
242
243   return strcmp(f1->v2->name, f2->v2->name);
244
245 }
246
247 /* Create a hash of a factor */
248 unsigned 
249 hash_factor( const struct factor *f, 
250              void *aux UNUSED)
251 {
252   unsigned h;
253   h = hsh_hash_string(f->v1->name);
254   
255   if ( f->v2 ) 
256     h += hsh_hash_string(f->v2->name);
257
258   return h;
259 }
260
261
262 /* Free up a factor */
263 void
264 free_factor(struct factor *f, void *aux UNUSED)
265 {
266   hsh_destroy(f->hash_table_v1);
267   hsh_destroy(f->hash_table_v2);
268
269   free(f);
270 }
271
272
273 /* Parser for the variables sub command */
274 static int
275 xmn_custom_variables(struct cmd_examine *cmd )
276 {
277
278   lex_match('=');
279
280   if ((token != T_ID || dict_lookup_var (default_dict, tokid) == NULL)
281       && token != T_ALL)
282     return 2;
283   
284   if (!parse_variables (default_dict, &dependent_vars, &n_dependent_vars,
285                         PV_NO_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH) )
286     {
287       free (dependent_vars);
288       return 0;
289     }
290
291   assert(n_dependent_vars);
292
293   if ( lex_match(T_BY))
294     {
295       hash_table_factors = hsh_create(4, 
296                                       (hsh_compare_func *) compare_factors, 
297                                       (hsh_hash_func *) hash_factor, 
298                                       (hsh_free_func *) free_factor, 0);
299
300       return examine_parse_independent_vars(cmd, hash_table_factors);
301     }
302
303   
304   
305   return 1;
306 }
307
308
309 /* Parse the clause specifying the factors */
310 static int
311 examine_parse_independent_vars(struct cmd_examine *cmd, 
312                                struct hsh_table *hash_table_factors )
313 {
314   struct factor *f = 0;
315
316   if ((token != T_ID || dict_lookup_var (default_dict, tokid) == NULL)
317       && token != T_ALL)
318     return 2;
319
320   if ( !f ) 
321     {
322       f = xmalloc(sizeof(struct factor));
323       f->v2 = 0;
324       f->v1 = 0;
325       f->hash_table_v2 = 0;
326       f->hash_table_v1 = 0;
327     }
328   
329   f->v1 = parse_variable();
330   
331   if ( ! f->hash_table_v1 ) 
332     f->hash_table_v1 = hsh_create(4,(hsh_compare_func *)compare_values,
333                                   (hsh_hash_func *)hash_value,
334                                   0,(void *) f->v1->width);
335
336   if ( token == T_BY ) 
337     {
338       lex_match(T_BY);
339       if ((token != T_ID || dict_lookup_var (default_dict, tokid) == NULL)
340           && token != T_ALL)
341         return 2;
342
343       f->v2 = parse_variable();
344       
345       if ( !f->hash_table_v2 ) 
346         {
347           f->hash_table_v2 = hsh_create(4,
348                                         (hsh_compare_func *) compare_values,
349                                         (hsh_hash_func *) hash_value,
350                                         0, 
351                                         (void *) f->v2->width);
352         }
353     }
354
355   hsh_insert(hash_table_factors, f);
356   
357   lex_match(',');
358
359   if ( token == '.' || token == '/' ) 
360     return 1;
361
362   return examine_parse_independent_vars(cmd, hash_table_factors);
363 }
364
365
366 void populate_descriptives(struct tab_table *t, int col, int row);
367
368
369 void populate_extremities(struct tab_table *t, int col, int row, int n);
370
371
372 /* Show the descriptives table */
373 void
374 show_descriptives(struct variable **dependent_var, 
375                   int n_dep_var, 
376                   struct factor *factor)
377 {
378   int i;
379   int heading_columns ;
380   int n_cols;
381   const int n_stat_rows = 13;
382
383   const int heading_rows = 1;
384   int n_rows = heading_rows ;
385
386   struct tab_table *t;
387
388
389   if ( !factor ) 
390     {
391       heading_columns = 1;
392       n_rows += n_dep_var * n_stat_rows;
393     }
394   else
395     {
396       assert(factor->v1);
397       if ( factor->v2 == 0 ) 
398         {
399           heading_columns = 2;
400           n_rows += n_dep_var * hsh_count(factor->hash_table_v1) * n_stat_rows;
401         }
402       else
403         {
404           heading_columns = 3;
405           n_rows += n_dep_var * hsh_count(factor->hash_table_v1) * 
406             hsh_count(factor->hash_table_v2) * n_stat_rows ;
407         }
408     }
409
410   n_cols = heading_columns + 4;
411
412   t = tab_create (n_cols, n_rows, 0);
413
414   tab_headers (t, heading_columns, 0, heading_rows, 0);
415
416   tab_dim (t, tab_natural_dimensions);
417
418   /* Outline the box and have no internal lines*/
419   tab_box (t, 
420            TAL_2, TAL_2,
421            -1, -1,
422            0, 0,
423            n_cols - 1, n_rows - 1);
424
425   tab_hline (t, TAL_2, 0, n_cols - 1, heading_rows );
426
427   tab_vline (t, TAL_1, heading_columns, 0, n_rows - 1);
428   tab_vline (t, TAL_2, n_cols - 2, 0, n_rows - 1);
429   tab_vline (t, TAL_1, n_cols - 1, 0, n_rows - 1);
430
431   tab_text (t, n_cols - 2, 0, TAB_CENTER | TAT_TITLE, _("Statistic"));
432   tab_text (t, n_cols - 1, 0, TAB_CENTER | TAT_TITLE, _("Std. Error"));
433
434
435   for ( i = 0 ; i < n_dep_var ; ++i ) 
436     {
437       int row;
438       int n_subfactors = 1;
439       int n_factors = 1;
440         
441       if ( factor ) 
442         {
443           n_factors = hsh_count(factor->hash_table_v1);
444           if (  factor->v2 ) 
445             n_subfactors = hsh_count(factor->hash_table_v2);
446         }
447
448
449       row = heading_rows + i * n_stat_rows * n_factors * n_subfactors; 
450
451       if ( i > 0 )
452         tab_hline(t, TAL_1, 0, n_cols - 1, row );
453
454
455
456       if ( factor  )
457         {
458           struct hsh_iterator hi;
459           union value *v;
460           int count = 0;
461
462       tab_text (t, 1, heading_rows - 1, TAB_CENTER | TAT_TITLE, 
463                 var_to_string(factor->v1));
464
465
466
467           for ( v  = hsh_first(factor->hash_table_v1, &hi);
468                 v != 0;
469                 v  = hsh_next(factor->hash_table_v1,  &hi))
470             {
471               struct hsh_iterator h2;
472               union value *vv;
473                 
474               tab_text (t, 1, 
475                         row  + count * n_subfactors * n_stat_rows,
476                         TAB_RIGHT | TAT_TITLE, 
477                         value_to_string(v, factor->v1)
478                         );
479
480               if ( count > 0 ) 
481                 tab_hline (t, TAL_1, 1, n_cols - 1,  
482                            row  + count * n_subfactors * n_stat_rows);
483
484               if ( factor->v2 ) 
485                 {
486                   int count2=0;
487
488                   tab_text (t, 2, heading_rows - 1, TAB_CENTER | TAT_TITLE, 
489                             var_to_string(factor->v2));
490
491                   for ( vv = hsh_first(factor->hash_table_v2, &h2);
492                         vv != 0;
493                         vv = hsh_next(factor->hash_table_v2, &h2))
494                     {
495                         
496                       tab_text(t, 2, 
497                                row
498                                + count * n_subfactors * n_stat_rows 
499                                + count2 * n_stat_rows,
500                                TAB_RIGHT | TAT_TITLE ,
501                                value_to_string(vv, factor->v2)
502                                );
503
504                       if ( count2 > 0 ) 
505                         tab_hline (t, TAL_1, 2, n_cols - 1,  
506                                    row
507                                    + count * n_subfactors * n_stat_rows 
508                                    + count2 * n_stat_rows);
509                                
510                       populate_descriptives(t, heading_columns,
511                                             row
512                                             + count * n_subfactors 
513                                               * n_stat_rows 
514                                             + count2 * n_stat_rows);
515                                             
516                         
517                       count2++;
518                     }
519                 }
520               else
521                 {
522                   populate_descriptives(t, heading_columns, 
523                                         row  
524                                         + count * n_subfactors * n_stat_rows);
525                 }
526
527               count ++;
528             }
529         }
530       else
531         {
532           populate_descriptives(t, heading_columns, 
533                                 row);
534         }
535
536       tab_text (t, 
537                 0, row,
538                 TAB_LEFT | TAT_TITLE, 
539                 var_to_string(dependent_var[i])
540                 );
541
542     }
543
544   tab_title (t, 0, _("Descriptives"));
545
546   tab_submit(t);
547 }
548
549
550
551 /* Fill in the descriptives data */
552 void
553 populate_descriptives(struct tab_table *t, int col, int row)
554 {
555
556   tab_text (t, col, 
557             row,
558             TAB_LEFT | TAT_TITLE,
559             _("Mean"));
560
561
562   tab_text (t, col, 
563             row + 1,
564             TAB_LEFT | TAT_TITLE | TAT_PRINTF,
565             _("%g%% Confidence Interval for Mean"), cmd.n_cinterval[0]);
566
567   tab_text (t, col + 1,  
568             row + 1,
569             TAB_LEFT | TAT_TITLE,
570             _("Upper Bound"));
571
572   tab_text (t, col + 1, 
573             row  + 2,
574             TAB_LEFT | TAT_TITLE,
575             _("Lower Bound"));
576
577
578   tab_text (t, col, 
579             row + 3,
580             TAB_LEFT | TAT_TITLE,
581             _("5% Trimmed Mean"));
582
583   tab_text (t, col, 
584             row + 4,
585             TAB_LEFT | TAT_TITLE,
586             _("Median"));
587
588   tab_text (t, col, 
589             row + 5,
590             TAB_LEFT | TAT_TITLE,
591             _("Variance"));
592
593   tab_text (t, col, 
594             row + 6,
595             TAB_LEFT | TAT_TITLE,
596             _("Std. Deviation"));
597
598   tab_text (t, col, 
599             row + 7,
600             TAB_LEFT | TAT_TITLE,
601             _("Minimum"));
602
603   tab_text (t, col, 
604             row + 8,
605             TAB_LEFT | TAT_TITLE,
606             _("Maximum"));
607
608   tab_text (t, col, 
609             row + 9,
610             TAB_LEFT | TAT_TITLE,
611             _("Range"));
612
613   tab_text (t, col, 
614             row + 10,
615             TAB_LEFT | TAT_TITLE,
616             _("Interquartile Range"));
617
618   tab_text (t, col, 
619             row + 11,
620             TAB_LEFT | TAT_TITLE,
621             _("Skewness"));
622
623   tab_text (t, col, 
624             row + 12,
625             TAB_LEFT | TAT_TITLE,
626             _("Kurtosis"));
627 }
628
629
630 void
631 show_summary(struct variable **dependent_var, 
632              int n_dep_var, 
633              struct factor *factor)
634 {
635   static const char *subtitle[]=
636     {
637       N_("Valid"),
638       N_("Missing"),
639       N_("Total")
640     };
641
642   int i;
643   int heading_columns ;
644   int n_cols;
645   const int heading_rows = 3;
646   struct tab_table *t;
647
648   int n_rows = heading_rows;
649
650   if ( !factor ) 
651     {
652       heading_columns = 1;
653       n_rows += n_dep_var;
654     }
655   else
656     {
657       assert(factor->v1);
658       if ( factor->v2 == 0 ) 
659         {
660           heading_columns = 2;
661           n_rows += n_dep_var * hsh_count(factor->hash_table_v1);
662         }
663       else
664         {
665           heading_columns = 3;
666           n_rows += n_dep_var * hsh_count(factor->hash_table_v1) * 
667             hsh_count(factor->hash_table_v2) ;
668         }
669     }
670
671
672   n_cols = heading_columns + 6;
673
674   t = tab_create (n_cols,n_rows,0);
675   tab_headers (t, heading_columns, 0, heading_rows, 0);
676
677   tab_dim (t, tab_natural_dimensions);
678   
679   /* Outline the box and have vertical internal lines*/
680   tab_box (t, 
681            TAL_2, TAL_2,
682            -1, TAL_1,
683            0, 0,
684            n_cols - 1, n_rows - 1);
685
686   tab_hline (t, TAL_2, 0, n_cols - 1, heading_rows );
687   tab_hline (t, TAL_1, heading_columns, n_cols - 1, 1 );
688   tab_hline (t, TAL_1, 0, n_cols - 1, heading_rows -1 );
689
690   tab_vline (t, TAL_2, heading_columns, 0, n_rows - 1);
691
692
693   tab_title (t, 0, _("Case Processing Summary"));
694   
695
696   tab_joint_text(t, heading_columns, 0, 
697                  n_cols -1, 0,
698                  TAB_CENTER | TAT_TITLE,
699                  _("Cases"));
700
701   /* Remove lines ... */
702   tab_box (t, 
703            -1, -1,
704            TAL_0, TAL_0,
705            heading_columns, 0,
706            n_cols - 1, 0);
707
708   if ( factor ) 
709     {
710       tab_text (t, 1, heading_rows - 1, TAB_CENTER | TAT_TITLE, 
711                 var_to_string(factor->v1));
712
713       if ( factor->v2 ) 
714         tab_text (t, 2, heading_rows - 1, TAB_CENTER | TAT_TITLE, 
715                   var_to_string(factor->v2));
716     }
717
718   for ( i = 0 ; i < 3 ; ++i ) 
719     {
720       tab_text (t, heading_columns + i*2 , 2, TAB_CENTER | TAT_TITLE, _("N"));
721       tab_text (t, heading_columns + i*2 + 1, 2, TAB_CENTER | TAT_TITLE, 
722                 _("Percent"));
723
724       tab_joint_text(t, heading_columns + i*2 , 1,
725                      heading_columns + i*2 + 1, 1,
726                      TAB_CENTER | TAT_TITLE,
727                      subtitle[i]);
728
729       tab_box (t, -1, -1,
730                TAL_0, TAL_0,
731                heading_columns + i*2, 1,
732                heading_columns + i*2 + 1, 1);
733
734     }
735
736
737   for ( i = 0 ; i < n_dep_var ; ++i ) 
738     {
739       int n_subfactors = 1;
740       int n_factors = 1;
741         
742       if ( factor ) 
743         {
744           n_factors = hsh_count(factor->hash_table_v1);
745           if (  factor->v2 ) 
746             n_subfactors = hsh_count(factor->hash_table_v2);
747         }
748
749       tab_text (t, 
750                 0, i * n_factors * n_subfactors + heading_rows,
751                 TAB_LEFT | TAT_TITLE, 
752                 var_to_string(dependent_var[i])
753                 );
754
755       if ( factor  )
756         {
757           struct hsh_iterator hi;
758           union value *v;
759           int count = 0;
760
761           for ( v  = hsh_first(factor->hash_table_v1, &hi);
762                 v != 0;
763                 v  = hsh_next(factor->hash_table_v1,  &hi))
764             {
765               struct hsh_iterator h2;
766               union value *vv;
767                 
768               tab_text (t, 1, 
769                         i * n_factors * n_subfactors + heading_rows
770                         + count * n_subfactors,
771                         TAB_RIGHT | TAT_TITLE, 
772                         value_to_string(v, factor->v1)
773                         );
774
775               if ( factor->v2 ) 
776                 {
777                   int count2=0;
778                   for ( vv = hsh_first(factor->hash_table_v2, &h2);
779                         vv != 0;
780                         vv = hsh_next(factor->hash_table_v2, &h2))
781                     {
782                         
783                       tab_text(t, 2, 
784                                i * n_factors * n_subfactors + heading_rows
785                                + count * n_subfactors + count2,
786                                TAB_RIGHT | TAT_TITLE ,
787                                value_to_string(vv, factor->v2)
788                                );
789                         
790                       count2++;
791                     }
792                 }
793               count ++;
794             }
795         }
796     }
797
798
799   tab_submit (t);
800   
801 }
802
803
804
805 static int bad_weight_warn = 1;
806
807 static void 
808 run_examine(const struct casefile *cf, void *cmd_)
809 {
810   struct hsh_iterator hi;
811   struct factor *fctr;
812
813   struct casereader *r;
814   struct ccase c;
815
816   const struct cmd_examine *cmd = (struct cmd_examine *) cmd_;
817
818   /* Make sure we haven't got rubbish left over from a 
819      previous split */
820   if ( hash_table_factors ) 
821     {
822       for ( fctr = hsh_first(hash_table_factors, &hi);
823             fctr != 0;
824             fctr = hsh_next (hash_table_factors, &hi) )
825         {
826           hsh_clear(fctr->hash_table_v1);
827           if ( fctr->hash_table_v2  ) 
828               hsh_clear(fctr->hash_table_v2);
829         }
830     }
831
832
833   for(r = casefile_get_reader (cf);
834       casereader_read (r, &c) ;
835       case_destroy (&c)) 
836     {
837       int i;
838
839       const double weight = 
840         dict_get_case_weight(default_dict, &c, &bad_weight_warn);
841
842       if ( hash_table_factors ) 
843         {
844           for ( fctr = hsh_first(hash_table_factors, &hi);
845                 fctr != 0;
846                 fctr = hsh_next (hash_table_factors, &hi) )
847             {
848               const union value *val = case_data (&c, fctr->v1->fv);
849               hsh_insert(fctr->hash_table_v1, (void *) val);
850
851               if ( fctr->hash_table_v2  ) 
852                 {
853                   val = case_data (&c, fctr->v2->fv);
854                   hsh_insert(fctr->hash_table_v2, (void *) val);
855                 }
856             }
857         }
858
859     }
860
861   output_examine();
862 }
863
864
865 static void 
866 show_extremes(struct variable **dependent_var, 
867                           int n_dep_var, 
868                           struct factor *factor,
869                           int n_extremities)
870 {
871   int i;
872   int heading_columns ;
873   int n_cols;
874   const int heading_rows = 1;
875   struct tab_table *t;
876
877   int n_rows = heading_rows;
878
879   if ( !factor ) 
880     {
881       heading_columns = 1 + 1;
882       n_rows += n_dep_var * 2 * n_extremities;
883     }
884   else
885     {
886       assert(factor->v1);
887       if ( factor->v2 == 0 ) 
888         {
889           heading_columns = 2 + 1;
890           n_rows += n_dep_var * 2 * n_extremities 
891             * hsh_count(factor->hash_table_v1);
892         }
893       else
894         {
895           heading_columns = 3 + 1;
896           n_rows += n_dep_var * 2 * n_extremities 
897             * hsh_count(factor->hash_table_v1)
898             * hsh_count(factor->hash_table_v2) ;
899         }
900     }
901
902
903   n_cols = heading_columns + 3;
904
905   t = tab_create (n_cols,n_rows,0);
906   tab_headers (t, heading_columns, 0, heading_rows, 0);
907
908   tab_dim (t, tab_natural_dimensions);
909   
910   /* Outline the box and have vertical internal lines*/
911   tab_box (t, 
912            TAL_2, TAL_2,
913            -1, TAL_1,
914            0, 0,
915            n_cols - 1, n_rows - 1);
916
917
918
919   tab_hline (t, TAL_2, 0, n_cols - 1, heading_rows );
920
921   tab_title (t, 0, _("Extreme Values"));
922
923
924
925
926   /* Remove lines ... */
927   tab_box (t, 
928            -1, -1,
929            TAL_0, TAL_0,
930            heading_columns, 0,
931            n_cols - 1, 0);
932
933   if ( factor ) 
934     {
935       tab_text (t, 1, heading_rows - 1, TAB_CENTER | TAT_TITLE, 
936                 var_to_string(factor->v1));
937
938       if ( factor->v2 ) 
939         tab_text (t, 2, heading_rows - 1, TAB_CENTER | TAT_TITLE, 
940                   var_to_string(factor->v2));
941     }
942
943   tab_text (t, n_cols - 1, 0, TAB_CENTER | TAT_TITLE, _("Value"));
944   tab_text (t, n_cols - 2, 0, TAB_CENTER | TAT_TITLE, _("Case Number"));
945
946
947   for ( i = 0 ; i < n_dep_var ; ++i ) 
948     {
949       int n_subfactors = 1;
950       int n_factors = 1;
951         
952       if ( factor ) 
953         {
954           n_factors = hsh_count(factor->hash_table_v1);
955           if (  factor->v2 ) 
956             n_subfactors = hsh_count(factor->hash_table_v2);
957         }
958
959       tab_text (t, 
960                 0, i * 2 * n_extremities * n_factors * 
961                 n_subfactors + heading_rows,
962                 TAB_LEFT | TAT_TITLE, 
963                 var_to_string(dependent_var[i])
964                 );
965
966
967       if ( i > 0 ) 
968         tab_hline (t, 
969                    TAL_1, 0, n_cols - 1,  
970                    heading_rows + 2 * n_extremities * 
971                    (i * n_factors * n_subfactors )
972                     );
973
974       if ( factor  )
975         {
976           struct hsh_iterator hi;
977           union value *v;
978           int count = 0;
979
980           for ( v  = hsh_first(factor->hash_table_v1, &hi);
981                 v != 0;
982                 v  = hsh_next(factor->hash_table_v1,  &hi))
983             {
984               struct hsh_iterator h2;
985               union value *vv;
986                 
987               tab_text (t, 1, heading_rows + 2 * n_extremities * 
988                         (i * n_factors * n_subfactors 
989                          + count * n_subfactors),
990                         TAB_RIGHT | TAT_TITLE, 
991                         value_to_string(v, factor->v1)
992                         );
993
994               if ( count > 0 ) 
995                 tab_hline (t, TAL_1, 1, n_cols - 1,  
996                            heading_rows + 2 * n_extremities *
997                            (i * n_factors * n_subfactors 
998                             + count * n_subfactors));
999
1000
1001               if ( factor->v2 ) 
1002                 {
1003                   int count2=0;
1004                   for ( vv = hsh_first(factor->hash_table_v2, &h2);
1005                         vv != 0;
1006                         vv = hsh_next(factor->hash_table_v2, &h2))
1007                     {
1008                         
1009                       tab_text(t, 2, heading_rows + 2 * n_extremities *
1010                                (i * n_factors * n_subfactors 
1011                                 + count * n_subfactors + count2 ),
1012                                TAB_RIGHT | TAT_TITLE ,
1013                                value_to_string(vv, factor->v2)
1014                                );
1015
1016
1017                       if ( count2 > 0 ) 
1018                         tab_hline (t, TAL_1, 2, n_cols - 1,  
1019                                    heading_rows + 2 * n_extremities *
1020                                    (i * n_factors * n_subfactors 
1021                                     + count * n_subfactors + count2 ));
1022
1023                       populate_extremities(t,3, 
1024                                            heading_rows + 2 * n_extremities *
1025                                            (i * n_factors * n_subfactors 
1026                                             + count * n_subfactors + count2),
1027                                            n_extremities );
1028                                            
1029                       count2++;
1030                     }
1031                 }
1032               else
1033                 {
1034                   populate_extremities(t,2, 
1035                                        heading_rows + 2 * n_extremities *
1036                                        (i * n_factors * n_subfactors 
1037                                         + count * n_subfactors),
1038                                         n_extremities);
1039                 }
1040
1041               count ++;
1042             }
1043         }
1044       else
1045         {
1046           populate_extremities(t, 1, 
1047                                heading_rows + 2 * n_extremities *
1048                                (i * n_factors * n_subfactors ),
1049                                n_extremities);
1050
1051         }
1052     }
1053
1054
1055   tab_submit (t);
1056
1057 }
1058
1059
1060
1061 /* Fill in the extremities table */
1062 void 
1063 populate_extremities(struct tab_table *t, int col, int row, int n)
1064 {
1065   int i;
1066
1067   tab_text(t, col, row,
1068            TAB_RIGHT | TAT_TITLE ,
1069            _("Highest")
1070            );
1071
1072
1073   tab_text(t, col, row + n ,
1074            TAB_RIGHT | TAT_TITLE ,
1075            _("Lowest")
1076            );
1077
1078
1079   for (i = 0; i < n ; ++i ) 
1080     {
1081       tab_float(t, col + 1, row + i,
1082                 TAB_RIGHT | TAT_TITLE,
1083                 i + 1, 8, 0);
1084
1085       tab_float(t, col + 1, row + i + n,
1086                 TAB_RIGHT | TAT_TITLE,
1087                 i + 1, 8, 0);
1088     }
1089 }