Changed the definition of factors to be self referential.
[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" (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)
226 {
227   int indep_var_cmp = strcmp(f1->indep_var->name, f2->indep_var->name);
228
229   if ( 0 != indep_var_cmp ) 
230     return indep_var_cmp;
231
232   /* If the names are identical, and there are no subfactors then
233    the factors are identical */
234   if ( ! f1->subfactor &&  ! f2->subfactor ) 
235     return 0;
236     
237   /* ... otherwise we must compare the subfactors */
238
239   return compare_factors(f1->subfactor, f2->subfactor, aux);
240
241 }
242
243 /* Create a hash of a factor */
244 unsigned 
245 hash_factor( const struct factor *f, void *aux)
246 {
247   unsigned h;
248   h = hsh_hash_string(f->indep_var->name);
249   
250   if ( f->subfactor ) 
251     h += hash_factor(f->subfactor, aux);
252
253   return h;
254 }
255
256
257 /* Free up a factor */
258 void
259 free_factor(struct factor *f, void *aux)
260 {
261   hsh_destroy(f->hash_table_val);
262
263   if ( f->subfactor ) 
264     free_factor(f->subfactor, aux);
265
266   free(f);
267 }
268
269
270 /* Parser for the variables sub command */
271 static int
272 xmn_custom_variables(struct cmd_examine *cmd )
273 {
274
275   lex_match('=');
276
277   if ((token != T_ID || dict_lookup_var (default_dict, tokid) == NULL)
278       && token != T_ALL)
279     return 2;
280   
281   if (!parse_variables (default_dict, &dependent_vars, &n_dependent_vars,
282                         PV_NO_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH) )
283     {
284       free (dependent_vars);
285       return 0;
286     }
287
288   assert(n_dependent_vars);
289
290   if ( lex_match(T_BY))
291     {
292       hash_table_factors = hsh_create(4, 
293                                       (hsh_compare_func *) compare_factors, 
294                                       (hsh_hash_func *) hash_factor, 
295                                       (hsh_free_func *) free_factor, 0);
296
297       return examine_parse_independent_vars(cmd, hash_table_factors);
298     }
299
300   
301   
302   return 1;
303 }
304
305
306 /* Parse the clause specifying the factors */
307 static int
308 examine_parse_independent_vars(struct cmd_examine *cmd, 
309                                struct hsh_table *hash_table_factors )
310 {
311   struct factor *f = 0;
312
313   if ((token != T_ID || dict_lookup_var (default_dict, tokid) == NULL)
314       && token != T_ALL)
315     return 2;
316
317   if ( !f ) 
318     {
319       f = xmalloc(sizeof(struct factor));
320       f->indep_var = 0;
321       f->hash_table_val = 0;
322       f->subfactor = 0;
323     }
324   
325   f->indep_var = parse_variable();
326   
327   if ( ! f->hash_table_val ) 
328     f->hash_table_val = hsh_create(4,(hsh_compare_func *)compare_values,
329                                   (hsh_hash_func *)hash_value,
330                                   0,(void *) f->indep_var->width);
331
332   if ( token == T_BY ) 
333     {
334       lex_match(T_BY);
335
336       if ((token != T_ID || dict_lookup_var (default_dict, tokid) == NULL)
337           && token != T_ALL)
338         return 2;
339
340       f->subfactor = xmalloc(sizeof(struct factor));
341
342       f->subfactor->indep_var = parse_variable();
343       
344       f->subfactor->subfactor = 0;
345
346       f->subfactor->hash_table_val = 
347         hsh_create(4,
348                    (hsh_compare_func *) compare_values,
349                    (hsh_hash_func *) hash_value,
350                    0, 
351                    (void *) f->subfactor->indep_var->width);
352     }
353
354   hsh_insert(hash_table_factors, f);
355   
356   lex_match(',');
357
358   if ( token == '.' || token == '/' ) 
359     return 1;
360
361   return examine_parse_independent_vars(cmd, hash_table_factors);
362 }
363
364
365 void populate_descriptives(struct tab_table *t, int col, int row);
366
367
368 void populate_extremities(struct tab_table *t, int col, int row, int n);
369
370
371 /* Show the descriptives table */
372 void
373 show_descriptives(struct variable **dependent_var, 
374                   int n_dep_var, 
375                   struct factor *factor)
376 {
377   int i;
378   int heading_columns ;
379   int n_cols;
380   const int n_stat_rows = 13;
381
382   const int heading_rows = 1;
383   int n_rows = heading_rows ;
384
385   struct tab_table *t;
386
387
388   if ( !factor ) 
389     {
390       heading_columns = 1;
391       n_rows += n_dep_var * n_stat_rows;
392     }
393   else
394     {
395       assert(factor->indep_var);
396       if ( factor->subfactor == 0 ) 
397         {
398           heading_columns = 2;
399           n_rows += n_dep_var * hsh_count(factor->hash_table_val) * n_stat_rows;
400         }
401       else
402         {
403           heading_columns = 3;
404           n_rows += n_dep_var * hsh_count(factor->hash_table_val) * 
405             hsh_count(factor->subfactor->hash_table_val) * n_stat_rows ;
406         }
407     }
408
409   n_cols = heading_columns + 4;
410
411   t = tab_create (n_cols, n_rows, 0);
412
413   tab_headers (t, heading_columns + 1, 0, heading_rows, 0);
414
415   tab_dim (t, tab_natural_dimensions);
416
417   /* Outline the box and have no internal lines*/
418   tab_box (t, 
419            TAL_2, TAL_2,
420            -1, -1,
421            0, 0,
422            n_cols - 1, n_rows - 1);
423
424   tab_hline (t, TAL_2, 0, n_cols - 1, heading_rows );
425
426   tab_vline (t, TAL_1, heading_columns, 0, n_rows - 1);
427   tab_vline (t, TAL_2, n_cols - 2, 0, n_rows - 1);
428   tab_vline (t, TAL_1, n_cols - 1, 0, n_rows - 1);
429
430   tab_text (t, n_cols - 2, 0, TAB_CENTER | TAT_TITLE, _("Statistic"));
431   tab_text (t, n_cols - 1, 0, TAB_CENTER | TAT_TITLE, _("Std. Error"));
432
433
434   for ( i = 0 ; i < n_dep_var ; ++i ) 
435     {
436       int row;
437       int n_subfactors = 1;
438       int n_factors = 1;
439         
440       if ( factor ) 
441         {
442           n_factors = hsh_count(factor->hash_table_val);
443           if (  factor->subfactor ) 
444             n_subfactors = hsh_count(factor->subfactor->hash_table_val);
445         }
446
447
448       row = heading_rows + i * n_stat_rows * n_factors * n_subfactors; 
449
450       if ( i > 0 )
451         tab_hline(t, TAL_1, 0, n_cols - 1, row );
452
453
454
455       if ( factor  )
456         {
457           struct hsh_iterator hi;
458           union value *v;
459           int count = 0;
460
461       tab_text (t, 1, heading_rows - 1, TAB_CENTER | TAT_TITLE, 
462                 var_to_string(factor->indep_var));
463
464
465
466           for ( v  = hsh_first(factor->hash_table_val, &hi);
467                 v != 0;
468                 v  = hsh_next(factor->hash_table_val,  &hi))
469             {
470               struct hsh_iterator h2;
471               union value *vv;
472                 
473               tab_text (t, 1, 
474                         row  + count * n_subfactors * n_stat_rows,
475                         TAB_RIGHT | TAT_TITLE, 
476                         value_to_string(v, factor->indep_var)
477                         );
478
479               if ( count > 0 ) 
480                 tab_hline (t, TAL_1, 1, n_cols - 1,  
481                            row  + count * n_subfactors * n_stat_rows);
482
483               if ( factor->subfactor ) 
484                 {
485                   int count2=0;
486
487                   tab_text (t, 2, heading_rows - 1, TAB_CENTER | TAT_TITLE, 
488                             var_to_string(factor->subfactor->indep_var));
489
490                   for ( vv = hsh_first(factor->subfactor->hash_table_val, &h2);
491                         vv != 0;
492                         vv = hsh_next(factor->subfactor->hash_table_val, &h2))
493                     {
494                         
495                       tab_text(t, 2, 
496                                row
497                                + count * n_subfactors * n_stat_rows 
498                                + count2 * n_stat_rows,
499                                TAB_RIGHT | TAT_TITLE ,
500                                value_to_string(vv, factor->subfactor->indep_var)
501                                );
502
503                       if ( count2 > 0 ) 
504                         tab_hline (t, TAL_1, 2, n_cols - 1,  
505                                    row
506                                    + count * n_subfactors * n_stat_rows 
507                                    + count2 * n_stat_rows);
508                                
509                       populate_descriptives(t, heading_columns,
510                                             row
511                                             + count * n_subfactors 
512                                               * n_stat_rows 
513                                             + count2 * n_stat_rows);
514                                             
515                         
516                       count2++;
517                     }
518                 }
519               else
520                 {
521                   populate_descriptives(t, heading_columns, 
522                                         row  
523                                         + count * n_subfactors * n_stat_rows);
524                 }
525
526               count ++;
527             }
528         }
529       else
530         {
531           populate_descriptives(t, heading_columns, 
532                                 row);
533         }
534
535       tab_text (t, 
536                 0, row,
537                 TAB_LEFT | TAT_TITLE, 
538                 var_to_string(dependent_var[i])
539                 );
540
541     }
542
543   tab_title (t, 0, _("Descriptives"));
544
545   tab_submit(t);
546 }
547
548
549
550 /* Fill in the descriptives data */
551 void
552 populate_descriptives(struct tab_table *t, int col, int row)
553 {
554
555   tab_text (t, col, 
556             row,
557             TAB_LEFT | TAT_TITLE,
558             _("Mean"));
559
560
561   tab_text (t, col, 
562             row + 1,
563             TAB_LEFT | TAT_TITLE | TAT_PRINTF,
564             _("%g%% Confidence Interval for Mean"), cmd.n_cinterval[0]);
565
566   tab_text (t, col + 1,  
567             row + 1,
568             TAB_LEFT | TAT_TITLE,
569             _("Upper Bound"));
570
571   tab_text (t, col + 1, 
572             row  + 2,
573             TAB_LEFT | TAT_TITLE,
574             _("Lower Bound"));
575
576
577   tab_text (t, col, 
578             row + 3,
579             TAB_LEFT | TAT_TITLE,
580             _("5% Trimmed Mean"));
581
582   tab_text (t, col, 
583             row + 4,
584             TAB_LEFT | TAT_TITLE,
585             _("Median"));
586
587   tab_text (t, col, 
588             row + 5,
589             TAB_LEFT | TAT_TITLE,
590             _("Variance"));
591
592   tab_text (t, col, 
593             row + 6,
594             TAB_LEFT | TAT_TITLE,
595             _("Std. Deviation"));
596
597   tab_text (t, col, 
598             row + 7,
599             TAB_LEFT | TAT_TITLE,
600             _("Minimum"));
601
602   tab_text (t, col, 
603             row + 8,
604             TAB_LEFT | TAT_TITLE,
605             _("Maximum"));
606
607   tab_text (t, col, 
608             row + 9,
609             TAB_LEFT | TAT_TITLE,
610             _("Range"));
611
612   tab_text (t, col, 
613             row + 10,
614             TAB_LEFT | TAT_TITLE,
615             _("Interquartile Range"));
616
617   tab_text (t, col, 
618             row + 11,
619             TAB_LEFT | TAT_TITLE,
620             _("Skewness"));
621
622   tab_text (t, col, 
623             row + 12,
624             TAB_LEFT | TAT_TITLE,
625             _("Kurtosis"));
626 }
627
628
629 void
630 show_summary(struct variable **dependent_var, 
631              int n_dep_var, 
632              struct factor *factor)
633 {
634   static const char *subtitle[]=
635     {
636       N_("Valid"),
637       N_("Missing"),
638       N_("Total")
639     };
640
641   int i;
642   int heading_columns ;
643   int n_cols;
644   const int heading_rows = 3;
645   struct tab_table *t;
646
647   int n_rows = heading_rows;
648
649   if ( !factor ) 
650     {
651       heading_columns = 1;
652       n_rows += n_dep_var;
653     }
654   else
655     {
656       assert(factor->indep_var);
657       if ( factor->subfactor == 0 ) 
658         {
659           heading_columns = 2;
660           n_rows += n_dep_var * hsh_count(factor->hash_table_val);
661         }
662       else
663         {
664           heading_columns = 3;
665           n_rows += n_dep_var * hsh_count(factor->hash_table_val) * 
666             hsh_count(factor->subfactor->hash_table_val) ;
667         }
668     }
669
670
671   n_cols = heading_columns + 6;
672
673   t = tab_create (n_cols,n_rows,0);
674   tab_headers (t, heading_columns, 0, heading_rows, 0);
675
676   tab_dim (t, tab_natural_dimensions);
677   
678   /* Outline the box and have vertical internal lines*/
679   tab_box (t, 
680            TAL_2, TAL_2,
681            -1, TAL_1,
682            0, 0,
683            n_cols - 1, n_rows - 1);
684
685   tab_hline (t, TAL_2, 0, n_cols - 1, heading_rows );
686   tab_hline (t, TAL_1, heading_columns, n_cols - 1, 1 );
687   tab_hline (t, TAL_1, 0, n_cols - 1, heading_rows -1 );
688
689   tab_vline (t, TAL_2, heading_columns, 0, n_rows - 1);
690
691
692   tab_title (t, 0, _("Case Processing Summary"));
693   
694
695   tab_joint_text(t, heading_columns, 0, 
696                  n_cols -1, 0,
697                  TAB_CENTER | TAT_TITLE,
698                  _("Cases"));
699
700   /* Remove lines ... */
701   tab_box (t, 
702            -1, -1,
703            TAL_0, TAL_0,
704            heading_columns, 0,
705            n_cols - 1, 0);
706
707   if ( factor ) 
708     {
709       tab_text (t, 1, heading_rows - 1, TAB_CENTER | TAT_TITLE, 
710                 var_to_string(factor->indep_var));
711
712       if ( factor->subfactor ) 
713         tab_text (t, 2, heading_rows - 1, TAB_CENTER | TAT_TITLE, 
714                   var_to_string(factor->subfactor->indep_var));
715     }
716
717   for ( i = 0 ; i < 3 ; ++i ) 
718     {
719       tab_text (t, heading_columns + i*2 , 2, TAB_CENTER | TAT_TITLE, _("N"));
720       tab_text (t, heading_columns + i*2 + 1, 2, TAB_CENTER | TAT_TITLE, 
721                 _("Percent"));
722
723       tab_joint_text(t, heading_columns + i*2 , 1,
724                      heading_columns + i*2 + 1, 1,
725                      TAB_CENTER | TAT_TITLE,
726                      subtitle[i]);
727
728       tab_box (t, -1, -1,
729                TAL_0, TAL_0,
730                heading_columns + i*2, 1,
731                heading_columns + i*2 + 1, 1);
732
733     }
734
735
736   for ( i = 0 ; i < n_dep_var ; ++i ) 
737     {
738       int n_subfactors = 1;
739       int n_factors = 1;
740         
741       if ( factor ) 
742         {
743           n_factors = hsh_count(factor->hash_table_val);
744           if (  factor->subfactor ) 
745             n_subfactors = hsh_count(factor->subfactor->hash_table_val);
746         }
747
748       tab_text (t, 
749                 0, i * n_factors * n_subfactors + heading_rows,
750                 TAB_LEFT | TAT_TITLE, 
751                 var_to_string(dependent_var[i])
752                 );
753
754       if ( factor  )
755         {
756           struct hsh_iterator hi;
757           union value *v;
758           int count = 0;
759
760           for ( v  = hsh_first(factor->hash_table_val, &hi);
761                 v != 0;
762                 v  = hsh_next(factor->hash_table_val,  &hi))
763             {
764               struct hsh_iterator h2;
765               union value *vv;
766                 
767               tab_text (t, 1, 
768                         i * n_factors * n_subfactors + heading_rows
769                         + count * n_subfactors,
770                         TAB_RIGHT | TAT_TITLE, 
771                         value_to_string(v, factor->indep_var)
772                         );
773
774               if ( factor->subfactor ) 
775                 {
776                   int count2=0;
777                   for ( vv = hsh_first(factor->subfactor->hash_table_val, &h2);
778                         vv != 0;
779                         vv = hsh_next(factor->subfactor->hash_table_val, &h2))
780                     {
781                         
782                       tab_text(t, 2, 
783                                i * n_factors * n_subfactors + heading_rows
784                                + count * n_subfactors + count2,
785                                TAB_RIGHT | TAT_TITLE ,
786                                value_to_string(vv, factor->subfactor->indep_var)
787                                );
788                         
789                       count2++;
790                     }
791                 }
792               count ++;
793             }
794         }
795     }
796
797
798   tab_submit (t);
799   
800 }
801
802
803
804 static int bad_weight_warn = 1;
805
806 static void 
807 run_examine(const struct casefile *cf, void *cmd_)
808 {
809   struct hsh_iterator hi;
810   struct factor *fctr;
811
812   struct casereader *r;
813   struct ccase c;
814
815   const struct cmd_examine *cmd = (struct cmd_examine *) cmd_;
816
817   /* Make sure we haven't got rubbish left over from a 
818      previous split */
819   if ( hash_table_factors ) 
820     {
821       for ( fctr = hsh_first(hash_table_factors, &hi);
822             fctr != 0;
823             fctr = hsh_next (hash_table_factors, &hi) )
824         {
825           hsh_clear(fctr->hash_table_val);
826
827           while ( (fctr = fctr->subfactor) )
828             hsh_clear(fctr->hash_table_val);
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->indep_var->fv);
849               hsh_insert(fctr->hash_table_val, (void *) val);
850
851               if ( fctr->subfactor  ) 
852                 {
853                   val = case_data (&c, fctr->subfactor->indep_var->fv);
854                   hsh_insert(fctr->subfactor->hash_table_val, (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->indep_var);
887       if ( factor->subfactor == 0 ) 
888         {
889           heading_columns = 2 + 1;
890           n_rows += n_dep_var * 2 * n_extremities 
891             * hsh_count(factor->hash_table_val);
892         }
893       else
894         {
895           heading_columns = 3 + 1;
896           n_rows += n_dep_var * 2 * n_extremities 
897             * hsh_count(factor->hash_table_val)
898             * hsh_count(factor->subfactor->hash_table_val) ;
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->indep_var));
937
938       if ( factor->subfactor ) 
939         tab_text (t, 2, heading_rows - 1, TAB_CENTER | TAT_TITLE, 
940                   var_to_string(factor->subfactor->indep_var));
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_val);
955           if (  factor->subfactor ) 
956             n_subfactors = hsh_count(factor->subfactor->hash_table_val);
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_val, &hi);
981                 v != 0;
982                 v  = hsh_next(factor->hash_table_val,  &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->indep_var)
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->subfactor ) 
1002                 {
1003                   int count2=0;
1004                   for ( vv = hsh_first(factor->subfactor->hash_table_val, &h2);
1005                         vv != 0;
1006                         vv = hsh_next(factor->subfactor->hash_table_val, &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->subfactor->indep_var)
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 }