Initial implementation of the Kruskal-Wallis test.
[pspp-builds.git] / src / language / stats / npar.c
1 /* PSPP - a program for statistical analysis. -*-c-*-
2    Copyright (C) 2006, 2008, 2009, 2010 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <language/stats/npar.h>
20 #include "npar-summary.h"
21
22 #include <stdlib.h>
23 #include <math.h>
24
25 #include "xalloc.h"
26 #include <data/case.h>
27 #include <data/casegrouper.h>
28 #include <data/casereader.h>
29 #include <data/dictionary.h>
30 #include <data/procedure.h>
31 #include <data/settings.h>
32 #include <data/variable.h>
33 #include <language/command.h>
34 #include <language/lexer/lexer.h>
35 #include <language/lexer/variable-parser.h>
36 #include <language/lexer/value-parser.h>
37 #include <language/stats/binomial.h>
38 #include <language/stats/chisquare.h>
39 #include <language/stats/kruskal-wallis.h>
40 #include <language/stats/wilcoxon.h>
41 #include <language/stats/sign.h>
42 #include <libpspp/assertion.h>
43 #include <libpspp/cast.h>
44 #include <libpspp/hash.h>
45 #include <libpspp/message.h>
46 #include <libpspp/pool.h>
47 #include <libpspp/str.h>
48 #include <libpspp/taint.h>
49 #include <math/moments.h>
50
51 #include "gettext.h"
52 #define _(msgid) gettext (msgid)
53
54 /* Settings for subcommand specifiers. */
55 enum missing_type
56   {
57     MISS_ANALYSIS,
58     MISS_LISTWISE,
59   };
60
61 /* Array indices for STATISTICS subcommand. */
62 enum
63   {
64     NPAR_ST_DESCRIPTIVES = 0,
65     NPAR_ST_QUARTILES = 1,
66     NPAR_ST_ALL = 2,
67     NPAR_ST_count
68   };
69
70 /* NPAR TESTS structure. */
71 struct cmd_npar_tests
72   {
73     /* Count variables indicating how many
74        of the subcommands have been given. */
75     int chisquare;
76     int binomial;
77     int wilcoxon;
78     int sign;
79     int kruskal_wallis;
80     int missing;
81     int method;
82     int statistics;
83
84     /* How missing values should be treated */
85     long miss;
86
87     /* Which statistics have been requested */
88     int a_statistics[NPAR_ST_count];
89   };
90
91
92 struct npar_specs
93 {
94   struct pool *pool;
95   struct npar_test **test;
96   size_t n_tests;
97
98   const struct variable **vv; /* Compendium of all variables
99                                   (those mentioned on ANY subcommand */
100   int n_vars; /* Number of variables in vv */
101
102   enum mv_class filter;    /* Missing values to filter. */
103
104   bool descriptives;       /* Descriptive statistics should be calculated */
105   bool quartiles;          /* Quartiles should be calculated */
106
107   bool exact;  /* Whether exact calculations have been requested */
108   double timer;   /* Maximum time (in minutes) to wait for exact calculations */
109 };
110
111
112 /* Prototype for custom subcommands of NPAR TESTS. */
113 static int npar_chisquare (struct lexer *, struct dataset *, struct npar_specs *);
114 static int npar_binomial (struct lexer *, struct dataset *,  struct npar_specs *);
115 static int npar_wilcoxon (struct lexer *, struct dataset *, struct npar_specs *);
116 static int npar_sign (struct lexer *, struct dataset *, struct npar_specs *);
117 static int npar_kruskal_wallis (struct lexer *, struct dataset *, struct npar_specs *);
118 static int npar_method (struct lexer *, struct npar_specs *);
119
120 /* Command parsing functions. */
121 static int parse_npar_tests (struct lexer *lexer, struct dataset *ds, struct cmd_npar_tests *p,
122                              struct npar_specs *npar_specs );
123
124 static int
125 parse_npar_tests (struct lexer *lexer, struct dataset *ds, struct cmd_npar_tests *npt,
126                   struct npar_specs *nps)
127 {
128   npt->chisquare = 0;
129   npt->binomial = 0;
130   npt->wilcoxon = 0;
131   npt->sign = 0;
132   npt->missing = 0;
133   npt->miss = MISS_ANALYSIS;
134   npt->method = 0;
135   npt->statistics = 0;
136   memset (npt->a_statistics, 0, sizeof npt->a_statistics);
137   for (;;)
138     {
139       if (lex_match_hyphenated_word (lexer, "CHISQUARE"))
140         {
141           lex_match (lexer, '=');
142           npt->chisquare++;
143           switch (npar_chisquare (lexer, ds, nps))
144             {
145             case 0:
146               goto lossage;
147             case 1:
148               break;
149             case 2:
150               lex_error (lexer, NULL);
151               goto lossage;
152             default:
153               NOT_REACHED ();
154             }
155         }
156       else if (lex_match_hyphenated_word (lexer, "BINOMIAL"))
157         {
158           lex_match (lexer, '=');
159           npt->binomial++;
160           switch (npar_binomial (lexer, ds, nps))
161             {
162             case 0:
163               goto lossage;
164             case 1:
165               break;
166             case 2:
167               lex_error (lexer, NULL);
168               goto lossage;
169             default:
170               NOT_REACHED ();
171             }
172         }
173       else if (lex_match_hyphenated_word (lexer, "K-W") ||
174                lex_match_hyphenated_word (lexer, "KRUSKAL-WALLIS"))
175         {
176           lex_match (lexer, '=');
177           npt->kruskal_wallis++;
178           switch (npar_kruskal_wallis (lexer, ds, nps))
179             {
180             case 0:
181               goto lossage;
182             case 1:
183               break;
184             case 2:
185               lex_error (lexer, NULL);
186               goto lossage;
187             default:
188               NOT_REACHED ();
189             }
190         }
191       else if (lex_match_hyphenated_word (lexer, "WILCOXON"))
192         {
193           lex_match (lexer, '=');
194           npt->wilcoxon++;
195           switch (npar_wilcoxon (lexer, ds, nps))
196             {
197             case 0:
198               goto lossage;
199             case 1:
200               break;
201             case 2:
202               lex_error (lexer, NULL);
203               goto lossage;
204             default:
205               NOT_REACHED ();
206             }
207         }
208       else if (lex_match_hyphenated_word (lexer, "SIGN"))
209         {
210           lex_match (lexer, '=');
211           npt->sign++;
212           switch (npar_sign (lexer, ds, nps))
213             {
214             case 0:
215               goto lossage;
216             case 1:
217               break;
218             case 2:
219               lex_error (lexer, NULL);
220               goto lossage;
221             default:
222               NOT_REACHED ();
223             }
224         }
225       else if (lex_match_hyphenated_word (lexer, "MISSING"))
226         {
227           lex_match (lexer, '=');
228           npt->missing++;
229           if (npt->missing > 1)
230             {
231               msg (SE, _ ("The %s subcommand may be given only once."), "MISSING");
232               goto lossage;
233             }
234           while (lex_token (lexer) != '/' && lex_token (lexer) != '.')
235             {
236               if (lex_match_hyphenated_word (lexer, "ANALYSIS"))
237                 npt->miss = MISS_ANALYSIS;
238               else if (lex_match_hyphenated_word (lexer, "LISTWISE"))
239                 npt->miss = MISS_LISTWISE;
240               else if (lex_match_hyphenated_word (lexer, "INCLUDE"))
241                 nps->filter = MV_SYSTEM;
242               else if (lex_match_hyphenated_word (lexer, "EXCLUDE"))
243                 nps->filter = MV_ANY;
244               else
245                 {
246                   lex_error (lexer, NULL);
247                   goto lossage;
248                 }
249               lex_match (lexer, ',');
250             }
251         }
252       else if (lex_match_hyphenated_word (lexer, "METHOD"))
253         {
254           lex_match (lexer, '=');
255           npt->method++;
256           if (npt->method > 1)
257             {
258               msg (SE, _ ("The %s subcommand may be given only once."), "METHOD");
259               goto lossage;
260             }
261           switch (npar_method (lexer, nps))
262             {
263             case 0:
264               goto lossage;
265             case 1:
266               break;
267             case 2:
268               lex_error (lexer, NULL);
269               goto lossage;
270             default:
271               NOT_REACHED ();
272             }
273         }
274       else if (lex_match_hyphenated_word (lexer, "STATISTICS"))
275         {
276           lex_match (lexer, '=');
277           npt->statistics++;
278           while (lex_token (lexer) != '/' && lex_token (lexer) != '.')
279             {
280               if (lex_match_hyphenated_word (lexer, "DESCRIPTIVES"))
281                 npt->a_statistics[NPAR_ST_DESCRIPTIVES] = 1;
282               else if (lex_match_hyphenated_word (lexer, "QUARTILES"))
283                 npt->a_statistics[NPAR_ST_QUARTILES] = 1;
284               else if (lex_match (lexer, T_ALL))
285                 npt->a_statistics[NPAR_ST_ALL] = 1;
286               else
287                 {
288                   lex_error (lexer, NULL);
289                   goto lossage;
290                 }
291               lex_match (lexer, ',');
292             }
293         }
294       else if ( settings_get_syntax () != COMPATIBLE && lex_match_id (lexer, "ALGORITHM"))
295         {
296           lex_match (lexer, '=');
297           if (lex_match_id (lexer, "COMPATIBLE"))
298             settings_set_cmd_algorithm (COMPATIBLE);
299           else if (lex_match_id (lexer, "ENHANCED"))
300             settings_set_cmd_algorithm (ENHANCED);
301           }
302         if (!lex_match (lexer, '/'))
303           break;
304       }
305
306     if (lex_token (lexer) != '.')
307       {
308         lex_error (lexer, _ ("expecting end of command"));
309         goto lossage;
310       }
311
312   return true;
313
314 lossage:
315   return false;
316 }
317
318
319
320
321 static void one_sample_insert_variables (const struct npar_test *test,
322                                          struct const_hsh_table *variables);
323
324 static void two_sample_insert_variables (const struct npar_test *test,
325                                          struct const_hsh_table *variables);
326
327
328 static void n_sample_insert_variables (const struct npar_test *test,
329                                          struct const_hsh_table *variables);
330
331
332
333 static void
334 npar_execute (struct casereader *input,
335              const struct npar_specs *specs,
336              const struct dataset *ds)
337 {
338   int t;
339   struct descriptives *summary_descriptives = NULL;
340
341   for ( t = 0 ; t < specs->n_tests; ++t )
342     {
343       const struct npar_test *test = specs->test[t];
344       if ( NULL == test->execute )
345         {
346           msg (SW, _ ("NPAR subcommand not currently implemented."));
347           continue;
348         }
349       test->execute (ds, casereader_clone (input), specs->filter, test, specs->exact, specs->timer);
350     }
351
352   if ( specs->descriptives )
353     {
354       summary_descriptives = xnmalloc (sizeof (*summary_descriptives),
355                                        specs->n_vars);
356
357       npar_summary_calc_descriptives (summary_descriptives,
358                                       casereader_clone (input),
359                                       dataset_dict (ds),
360                                       specs->vv, specs->n_vars,
361                                       specs->filter);
362     }
363
364   if ( (specs->descriptives || specs->quartiles)
365        && !taint_has_tainted_successor (casereader_get_taint (input)) )
366     do_summary_box (summary_descriptives, specs->vv, specs->n_vars );
367
368   free (summary_descriptives);
369   casereader_destroy (input);
370 }
371
372
373 int
374 cmd_npar_tests (struct lexer *lexer, struct dataset *ds)
375 {
376   struct cmd_npar_tests cmd;
377   bool ok;
378   int i;
379   struct npar_specs npar_specs = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
380   struct const_hsh_table *var_hash;
381   struct casegrouper *grouper;
382   struct casereader *input, *group;
383
384   npar_specs.pool = pool_create ();
385   npar_specs.filter = MV_ANY;
386
387   var_hash = const_hsh_create_pool (npar_specs.pool, 0,
388                                     compare_vars_by_name, hash_var_by_name,
389                                     NULL, NULL);
390
391   if ( ! parse_npar_tests (lexer, ds, &cmd, &npar_specs) )
392     {
393       pool_destroy (npar_specs.pool);
394       return CMD_FAILURE;
395     }
396
397   for (i = 0; i < npar_specs.n_tests; ++i )
398     {
399       const struct npar_test *test = npar_specs.test[i];
400       test->insert_variables (test, var_hash);
401     }
402
403   npar_specs.vv = (const struct variable **) const_hsh_sort (var_hash);
404   npar_specs.n_vars = const_hsh_count (var_hash);
405
406   if ( cmd.statistics )
407     {
408       int i;
409
410       for ( i = 0 ; i < NPAR_ST_count; ++i )
411         {
412           if ( cmd.a_statistics[i] )
413             {
414               switch ( i )
415                 {
416                 case NPAR_ST_DESCRIPTIVES:
417                   npar_specs.descriptives = true;
418                   break;
419                 case NPAR_ST_QUARTILES:
420                   npar_specs.quartiles = true;
421                   break;
422                 case NPAR_ST_ALL:
423                   npar_specs.quartiles = true;
424                   npar_specs.descriptives = true;
425                   break;
426                 default:
427                   NOT_REACHED ();
428                 };
429             }
430         }
431     }
432
433   input = proc_open (ds);
434   if ( cmd.miss == MISS_LISTWISE )
435     {
436       input = casereader_create_filter_missing (input,
437                                                 npar_specs.vv,
438                                                 npar_specs.n_vars,
439                                                 npar_specs.filter,
440                                                 NULL, NULL);
441     }
442
443
444   grouper = casegrouper_create_splits (input, dataset_dict (ds));
445   while (casegrouper_get_next_group (grouper, &group))
446     npar_execute (group, &npar_specs, ds);
447   ok = casegrouper_destroy (grouper);
448   ok = proc_commit (ds) && ok;
449
450   const_hsh_destroy (var_hash);
451
452   pool_destroy (npar_specs.pool);
453
454   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
455 }
456
457 static int
458 npar_chisquare (struct lexer *lexer, struct dataset *ds,
459                 struct npar_specs *specs)
460 {
461   struct chisquare_test *cstp = pool_alloc (specs->pool, sizeof (*cstp));
462   struct one_sample_test *tp = &cstp->parent;
463   struct npar_test *nt = &tp->parent;
464
465   nt->execute = chisquare_execute;
466   nt->insert_variables = one_sample_insert_variables;
467
468   if (!parse_variables_const_pool (lexer, specs->pool, dataset_dict (ds),
469                                    &tp->vars, &tp->n_vars,
470                                    PV_NO_SCRATCH | PV_NO_DUPLICATE))
471     {
472       return 2;
473     }
474
475   cstp->ranged = false;
476
477   if ( lex_match (lexer, '('))
478     {
479       cstp->ranged = true;
480       if ( ! lex_force_num (lexer)) return 0;
481       cstp->lo = lex_integer (lexer);
482       lex_get (lexer);
483       lex_force_match (lexer, ',');
484       if (! lex_force_num (lexer) ) return 0;
485       cstp->hi = lex_integer (lexer);
486       if ( cstp->lo >= cstp->hi )
487         {
488           msg (ME,
489               _ ("The specified value of HI (%d) is "
490                 "lower than the specified value of LO (%d)"),
491               cstp->hi, cstp->lo);
492           return 0;
493         }
494       lex_get (lexer);
495       if (! lex_force_match (lexer, ')')) return 0;
496     }
497
498   cstp->n_expected = 0;
499   cstp->expected = NULL;
500   if ( lex_match (lexer, '/') )
501     {
502       if ( lex_match_id (lexer, "EXPECTED") )
503         {
504           lex_force_match (lexer, '=');
505           if ( ! lex_match_id (lexer, "EQUAL") )
506             {
507               double f;
508               int n;
509               while ( lex_is_number (lexer) )
510                 {
511                   int i;
512                   n = 1;
513                   f = lex_number (lexer);
514                   lex_get (lexer);
515                   if ( lex_match (lexer, '*'))
516                     {
517                       n = f;
518                       f = lex_number (lexer);
519                       lex_get (lexer);
520                     }
521                   lex_match (lexer, ',');
522
523                   cstp->n_expected += n;
524                   cstp->expected = pool_realloc (specs->pool,
525                                                  cstp->expected,
526                                                  sizeof (double) *
527                                                  cstp->n_expected);
528                   for ( i = cstp->n_expected - n ;
529                         i < cstp->n_expected;
530                         ++i )
531                     cstp->expected[i] = f;
532
533                 }
534             }
535         }
536       else
537         lex_put_back (lexer, '/');
538     }
539
540   if ( cstp->ranged && cstp->n_expected > 0 &&
541        cstp->n_expected != cstp->hi - cstp->lo + 1 )
542     {
543       msg (ME,
544           _ ("%d expected values were given, but the specified "
545             "range (%d-%d) requires exactly %d values."),
546           cstp->n_expected, cstp->lo, cstp->hi,
547           cstp->hi - cstp->lo +1);
548       return 0;
549     }
550
551   specs->n_tests++;
552   specs->test = pool_realloc (specs->pool,
553                               specs->test,
554                               sizeof (*specs->test) * specs->n_tests);
555
556   specs->test[specs->n_tests - 1] = nt;
557
558   return 1;
559 }
560
561
562 static int
563 npar_binomial (struct lexer *lexer, struct dataset *ds,
564                struct npar_specs *specs)
565 {
566   struct binomial_test *btp = pool_alloc (specs->pool, sizeof (*btp));
567   struct one_sample_test *tp = &btp->parent;
568   struct npar_test *nt = &tp->parent;
569
570   nt->execute = binomial_execute;
571   nt->insert_variables = one_sample_insert_variables;
572
573   btp->category1 = btp->category2 = btp->cutpoint = SYSMIS;
574
575   btp->p = 0.5;
576
577   if ( lex_match (lexer, '(') )
578     {
579       if ( lex_force_num (lexer) )
580         {
581           btp->p = lex_number (lexer);
582           lex_get (lexer);
583           lex_force_match (lexer, ')');
584         }
585       else
586         return 0;
587     }
588   else
589     /* Kludge: q2c swallows the '=' so put it back here  */
590      lex_put_back (lexer, '=');
591
592   if (lex_match (lexer, '=') )
593     {
594       if (parse_variables_const_pool (lexer, specs->pool, dataset_dict (ds),
595                                       &tp->vars, &tp->n_vars,
596                                       PV_NUMERIC | PV_NO_SCRATCH | PV_NO_DUPLICATE) )
597         {
598           if (lex_match (lexer, '('))
599             {
600               lex_force_num (lexer);
601               btp->category1 = lex_number (lexer);
602               lex_get (lexer);
603               if ( lex_match (lexer, ','))
604                 {
605                   if ( ! lex_force_num (lexer) ) return 2;
606                   btp->category2 = lex_number (lexer);
607                   lex_get (lexer);
608                 }
609               else
610                 {
611                   btp->cutpoint = btp->category1;
612                 }
613
614               lex_force_match (lexer, ')');
615             }
616         }
617       else
618         return 2;
619
620     }
621
622   specs->n_tests++;
623   specs->test = pool_realloc (specs->pool,
624                               specs->test,
625                               sizeof (*specs->test) * specs->n_tests);
626
627   specs->test[specs->n_tests - 1] = nt;
628
629   return 1;
630 }
631
632
633 static bool
634 parse_two_sample_related_test (struct lexer *lexer,
635                                     const struct dictionary *dict,
636                                     struct two_sample_test *test_parameters,
637                                     struct pool *pool
638                                     );
639
640
641 static bool
642 parse_two_sample_related_test (struct lexer *lexer,
643                                const struct dictionary *dict,
644                                struct two_sample_test *test_parameters,
645                                struct pool *pool
646                                )
647 {
648   int n = 0;
649   bool paired = false;
650   bool with = false;
651   const struct variable **vlist1;
652   size_t n_vlist1;
653
654   const struct variable **vlist2;
655   size_t n_vlist2;
656
657   test_parameters->parent.insert_variables = two_sample_insert_variables;
658
659   if (!parse_variables_const_pool (lexer, pool,
660                                    dict,
661                                    &vlist1, &n_vlist1,
662                                    PV_NUMERIC | PV_NO_SCRATCH | PV_NO_DUPLICATE) )
663     return false;
664
665   if ( lex_match (lexer, T_WITH))
666     {
667       with = true;
668       if ( !parse_variables_const_pool (lexer, pool, dict,
669                                         &vlist2, &n_vlist2,
670                                         PV_NUMERIC | PV_NO_SCRATCH | PV_NO_DUPLICATE) )
671         return false;
672
673       paired = (lex_match (lexer, '(') &&
674                 lex_match_id (lexer, "PAIRED") && lex_match (lexer, ')'));
675     }
676
677
678   if ( with )
679     {
680       if (paired)
681         {
682           if ( n_vlist1 != n_vlist2)
683             msg (SE, _ ("PAIRED was specified but the number of variables "
684                        "preceding WITH (%zu) did not match the number "
685                        "following (%zu)."), n_vlist1, n_vlist2);
686
687           test_parameters->n_pairs = n_vlist1 ;
688         }
689       else
690         {
691           test_parameters->n_pairs = n_vlist1 * n_vlist2;
692         }
693     }
694   else
695     {
696       test_parameters->n_pairs = (n_vlist1 * (n_vlist1 - 1)) / 2 ;
697     }
698
699   test_parameters->pairs =
700     pool_alloc (pool, sizeof ( variable_pair) * test_parameters->n_pairs);
701
702   if ( with )
703     {
704       if (paired)
705         {
706           int i;
707           assert (n_vlist1 == n_vlist2);
708           for ( i = 0 ; i < n_vlist1; ++i )
709             {
710               test_parameters->pairs[n][1] = vlist1[i];
711               test_parameters->pairs[n][0] = vlist2[i];
712               n++;
713             }
714         }
715       else
716         {
717           int i,j;
718           for ( i = 0 ; i < n_vlist1; ++i )
719             {
720               for ( j = 0 ; j < n_vlist2; ++j )
721                 {
722                   test_parameters->pairs[n][1] = vlist1[i];
723                   test_parameters->pairs[n][0] = vlist2[j];
724                   n++;
725                 }
726             }
727         }
728     }
729   else
730     {
731       int i,j;
732       for ( i = 0 ; i < n_vlist1 - 1; ++i )
733         {
734           for ( j = i + 1 ; j < n_vlist1; ++j )
735             {
736               assert ( n < test_parameters->n_pairs);
737               test_parameters->pairs[n][1] = vlist1[i];
738               test_parameters->pairs[n][0] = vlist1[j];
739               n++;
740             }
741         }
742     }
743
744   assert ( n == test_parameters->n_pairs);
745
746   return true;
747 }
748
749
750 static bool
751 parse_n_sample_related_test (struct lexer *lexer,
752                              const struct dictionary *dict,
753                              struct n_sample_test *nst,
754                              struct pool *pool
755                              )
756 {
757   if (!parse_variables_const_pool (lexer, pool,
758                                    dict,
759                                    &nst->vars, &nst->n_vars,
760                                    PV_NUMERIC | PV_NO_SCRATCH | PV_NO_DUPLICATE) )
761     return false;
762
763   if ( ! lex_force_match (lexer, T_BY))
764     return false;
765
766   nst->indep_var = parse_variable_const (lexer, dict);
767
768   if ( ! lex_force_match (lexer, '('))
769     return false;
770
771   value_init (&nst->val1, var_get_width (nst->indep_var));
772   if ( ! parse_value (lexer, &nst->val1, var_get_width (nst->indep_var)))
773     {
774       value_destroy (&nst->val1, var_get_width (nst->indep_var));
775       return false;
776     }
777
778   if ( ! lex_force_match (lexer, ','))
779     return false;
780
781   value_init (&nst->val2, var_get_width (nst->indep_var));
782   if ( ! parse_value (lexer, &nst->val2, var_get_width (nst->indep_var)))
783     {
784       value_destroy (&nst->val2, var_get_width (nst->indep_var));
785       return false;
786     }
787
788   if ( ! lex_force_match (lexer, ')'))
789     return false;
790
791   return true;
792 }
793
794 static int
795 npar_wilcoxon (struct lexer *lexer,
796                struct dataset *ds,
797                struct npar_specs *specs )
798 {
799
800
801   struct two_sample_test *tp = pool_alloc (specs->pool, sizeof (*tp));
802   struct npar_test *nt = &tp->parent;
803   nt->execute = wilcoxon_execute;
804
805   if (!parse_two_sample_related_test (lexer, dataset_dict (ds),
806                                       tp, specs->pool) )
807     return 0;
808
809   specs->n_tests++;
810   specs->test = pool_realloc (specs->pool,
811                               specs->test,
812                               sizeof (*specs->test) * specs->n_tests);
813   specs->test[specs->n_tests - 1] = nt;
814
815   return 1;
816 }
817
818 static int
819 npar_sign (struct lexer *lexer, struct dataset *ds,
820            struct npar_specs *specs)
821 {
822   struct two_sample_test *tp = pool_alloc (specs->pool, sizeof (*tp));
823   struct npar_test *nt = &tp->parent;
824
825   nt->execute = sign_execute;
826
827   if (!parse_two_sample_related_test (lexer, dataset_dict (ds),
828                                       tp, specs->pool) )
829     return 0;
830
831   specs->n_tests++;
832   specs->test = pool_realloc (specs->pool,
833                               specs->test,
834                               sizeof (*specs->test) * specs->n_tests);
835   specs->test[specs->n_tests - 1] = nt;
836
837   return 1;
838 }
839
840 static int
841 npar_kruskal_wallis (struct lexer *lexer, struct dataset *ds,
842                       struct npar_specs *specs)
843 {
844   struct n_sample_test *tp = pool_alloc (specs->pool, sizeof (*tp));
845   struct npar_test *nt = &tp->parent;
846
847   nt->insert_variables = n_sample_insert_variables;
848
849   nt->execute = kruskal_wallis_execute;
850
851   if (!parse_n_sample_related_test (lexer, dataset_dict (ds),
852                                       tp, specs->pool) )
853     return 0;
854
855   specs->n_tests++;
856   specs->test = pool_realloc (specs->pool,
857                               specs->test,
858                               sizeof (*specs->test) * specs->n_tests);
859   specs->test[specs->n_tests - 1] = nt;
860
861   return 1;
862 }
863
864 /* Insert the variables for TEST into VAR_HASH */
865 static void
866 one_sample_insert_variables (const struct npar_test *test,
867                              struct const_hsh_table *var_hash)
868 {
869   int i;
870   const struct one_sample_test *ost = UP_CAST (test, const struct one_sample_test, parent);
871
872   for ( i = 0 ; i < ost->n_vars ; ++i )
873     const_hsh_insert (var_hash, ost->vars[i]);
874 }
875
876
877 static void
878 two_sample_insert_variables (const struct npar_test *test,
879                              struct const_hsh_table *var_hash)
880 {
881   int i;
882   const struct two_sample_test *tst = UP_CAST (test, const struct two_sample_test, parent);
883
884   for ( i = 0 ; i < tst->n_pairs ; ++i )
885     {
886       variable_pair *pair = &tst->pairs[i];
887
888       const_hsh_insert (var_hash, (*pair)[0]);
889       const_hsh_insert (var_hash, (*pair)[1]);
890     }
891 }
892
893 static void 
894 n_sample_insert_variables (const struct npar_test *test,
895                            struct const_hsh_table *var_hash)
896 {
897   int i;
898   const struct n_sample_test *tst = UP_CAST (test, const struct n_sample_test, parent);
899
900   for ( i = 0 ; i < tst->n_vars ; ++i )
901     const_hsh_insert (var_hash, tst->vars[i]);
902
903   const_hsh_insert (var_hash, tst->indep_var);
904 }
905
906 static int
907 npar_method (struct lexer *lexer,  struct npar_specs *specs)
908 {
909   if ( lex_match_id (lexer, "EXACT") )
910     {
911       specs->exact = true;
912       specs->timer = 0.0;
913       if (lex_match_id (lexer, "TIMER"))
914         {
915           specs->timer = 5.0;
916
917           if ( lex_match (lexer, '('))
918             {
919               if ( lex_force_num (lexer) )
920                 {
921                   specs->timer = lex_number (lexer);
922                   lex_get (lexer);
923                 }
924               lex_force_match (lexer, ')');
925             }
926         }
927     }
928
929   return 1;
930 }