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