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