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