Make the missing value code do more work, so that its callers can do
[pspp-builds.git] / src / language / stats / rank.q
1 /* PSPP - RANK. -*-c-*-
2
3 Copyright (C) 2005, 2006 Free Software Foundation, Inc.
4         Ben Pfaff <blp@gnu.org>.
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., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA. */
20
21 #include <config.h>
22
23 #include "sort-criteria.h"
24
25 #include <data/dictionary.h>
26 #include <data/format.h>
27 #include <data/missing-values.h>
28 #include <data/procedure.h>
29 #include <data/variable.h>
30 #include <data/case.h>
31 #include <data/casefile.h>
32 #include <data/fastfile.h>
33 #include <data/storage-stream.h>
34 #include <language/command.h>
35 #include <language/stats/sort-criteria.h>
36 #include <limits.h>
37 #include <libpspp/compiler.h>
38 #include <math/sort.h>
39 #include <output/table.h>
40 #include <output/manager.h>
41
42 #include <gsl/gsl_cdf.h>
43 #include <math.h>
44
45 #include "gettext.h"
46 #define _(msgid) gettext (msgid)
47
48 /* (headers) */
49
50 /* (specification)
51    "RANK" (rank_):
52    *^variables=custom;
53    +rank=custom;
54    +normal=custom;
55    +percent=custom;
56    +ntiles=custom;
57    +rfraction=custom;
58    +proportion=custom;
59    +n=custom;
60    +savage=custom;
61    +print=print:!yes/no;
62    +fraction=fraction:!blom/tukey/vw/rankit;
63    +ties=ties:!mean/low/high/condense;
64    missing=miss:!exclude/include.
65 */
66 /* (declarations) */
67 /* (functions) */
68
69 typedef double (*rank_function_t) (double c, double cc, double cc_1, 
70                                  int i, double w);
71
72 static double rank_proportion (double c, double cc, double cc_1, 
73                                int i, double w);
74
75 static double rank_normal (double c, double cc, double cc_1, 
76                            int i, double w);
77
78 static double rank_percent (double c, double cc, double cc_1, 
79                             int i, double w);
80
81 static double rank_rfraction (double c, double cc, double cc_1, 
82                               int i, double w);
83
84 static double rank_rank (double c, double cc, double cc_1, 
85                          int i, double w);
86
87 static double rank_n (double c, double cc, double cc_1, 
88                       int i, double w);
89
90 static double rank_savage (double c, double cc, double cc_1, 
91                       int i, double w);
92
93 static double rank_ntiles (double c, double cc, double cc_1, 
94                       int i, double w);
95
96
97 enum RANK_FUNC
98   {
99     RANK,
100     NORMAL,
101     PERCENT,
102     RFRACTION,
103     PROPORTION,
104     N,
105     NTILES,
106     SAVAGE,
107     n_RANK_FUNCS
108   };
109
110 static const struct fmt_spec dest_format[n_RANK_FUNCS] = {
111   {FMT_F, 9, 3}, /* rank */
112   {FMT_F, 6, 4}, /* normal */
113   {FMT_F, 6, 2}, /* percent */
114   {FMT_F, 6, 4}, /* rfraction */
115   {FMT_F, 6, 4}, /* proportion */
116   {FMT_F, 6, 0}, /* n */
117   {FMT_F, 3, 0}, /* ntiles */
118   {FMT_F, 8, 4}  /* savage */
119 };
120
121 static const char * const function_name[n_RANK_FUNCS] = {
122   "RANK",
123   "NORMAL",
124   "PERCENT",
125   "RFRACTION",
126   "PROPORTION",
127   "N",
128   "NTILES",
129   "SAVAGE"
130 };
131
132 static const rank_function_t rank_func[n_RANK_FUNCS] = {
133   rank_rank,
134   rank_normal,
135   rank_percent,
136   rank_rfraction,
137   rank_proportion,
138   rank_n,
139   rank_ntiles,
140   rank_savage 
141   };
142
143
144 struct rank_spec
145 {
146   enum RANK_FUNC rfunc;
147   struct variable **destvars;
148 };
149
150
151 /* Categories of missing values to exclude. */
152 static enum mv_class exclude_values;
153
154 static struct rank_spec *rank_specs;
155 static size_t n_rank_specs;
156
157 static struct sort_criteria *sc;
158
159 static struct variable **group_vars;
160 static size_t n_group_vars;
161
162 static struct variable **src_vars;
163 static size_t n_src_vars;
164
165
166 static int k_ntiles;
167
168 static struct cmd_rank cmd;
169
170 static struct casefile *rank_sorted_casefile (struct casefile *cf, 
171                                               const struct sort_criteria *, 
172                                               const struct dictionary *,
173                                               const struct rank_spec *rs, 
174                                               int n_rank_specs,
175                                               int idx,
176                                               const struct missing_values *miss
177                                               );
178 static const char *
179 fraction_name(void)
180 {
181   static char name[10];
182   switch ( cmd.fraction ) 
183     {
184     case RANK_BLOM:
185       strcpy (name, "BLOM");
186       break;
187     case RANK_RANKIT:
188       strcpy (name, "RANKIT");
189       break;
190     case RANK_TUKEY:
191       strcpy (name, "TUKEY");
192       break;
193     case RANK_VW:
194       strcpy (name, "VW");
195       break;
196     default:
197       NOT_REACHED ();
198     }
199   return name;
200 }
201
202 /* Create a label on DEST_VAR, describing its derivation from SRC_VAR and F */
203 static void
204 create_var_label (struct variable *dest_var, 
205                   const struct variable *src_var, enum RANK_FUNC f)
206 {
207   struct string label;
208   ds_init_empty (&label);
209
210   if ( n_group_vars > 0 ) 
211     {
212       struct string group_var_str;
213       int g;
214
215       ds_init_empty (&group_var_str);
216
217       for (g = 0 ; g < n_group_vars ; ++g ) 
218         {
219           if ( g > 0 ) ds_put_cstr (&group_var_str, " ");
220           ds_put_cstr (&group_var_str, var_get_name (group_vars[g]));
221         }
222
223       ds_put_format (&label, _("%s of %s by %s"), function_name[f], 
224                      var_get_name (src_var), ds_cstr (&group_var_str));
225       ds_destroy (&group_var_str);
226     }
227   else
228     ds_put_format (&label, _("%s of %s"),
229                    function_name[f], var_get_name (src_var));  
230
231   var_set_label (dest_var, ds_cstr (&label));
232
233   ds_destroy (&label);
234 }
235
236
237 static bool 
238 rank_cmd (struct dataset *ds, const struct sort_criteria *sc, 
239           const struct rank_spec *rank_specs, int n_rank_specs)
240 {
241   struct sort_criteria criteria;
242   bool result = true;
243   int i;
244   const int n_splits = dict_get_split_cnt (dataset_dict (ds));
245
246   criteria.crit_cnt = n_splits + n_group_vars + 1;
247   criteria.crits = xnmalloc (criteria.crit_cnt, sizeof *criteria.crits);
248   for (i = 0; i < n_splits ; i++) 
249     {
250       struct variable *v = dict_get_split_vars (dataset_dict (ds))[i];
251       criteria.crits[i].fv = var_get_case_index (v);
252       criteria.crits[i].width = var_get_width (v);
253       criteria.crits[i].dir = SRT_ASCEND;
254     }
255   for (i = 0; i < n_group_vars; i++) 
256     {
257       criteria.crits[i + n_splits].fv = var_get_case_index (group_vars[i]);
258       criteria.crits[i + n_splits].width = var_get_width (group_vars[i]);
259       criteria.crits[i + n_splits].dir = SRT_ASCEND;
260     }
261   for (i = 0 ; i < sc->crit_cnt ; ++i )
262     {
263       struct casefile *out ;
264       struct casefile *cf ; 
265       struct casereader *reader ;
266       struct casefile *sorted_cf ;
267
268       /* Obtain active file in CF. */
269       if (!procedure (ds, NULL, NULL))
270         goto error;
271
272       cf = proc_capture_output (ds);
273
274       /* Sort CF into SORTED_CF. */
275       reader = casefile_get_destructive_reader (cf) ;
276       criteria.crits[criteria.crit_cnt - 1] = sc->crits[i];
277       assert ( sc->crits[i].fv == var_get_case_index (src_vars[i]) );
278       sorted_cf = sort_execute (reader, &criteria, NULL);
279       casefile_destroy (cf);
280
281       out = rank_sorted_casefile (sorted_cf, &criteria,
282                                   dataset_dict (ds),
283                                   rank_specs, n_rank_specs, 
284                                   i, var_get_missing_values (src_vars[i]));
285       if ( NULL == out ) 
286         {
287           result = false ;
288           continue ;
289         }
290       
291       proc_set_source (ds, storage_source_create (out));
292     }
293
294   free (criteria.crits);
295   return result ; 
296
297 error:
298   free (criteria.crits);
299   return false ;
300 }
301
302 /* Hardly a rank function !! */
303 static double 
304 rank_n (double c UNUSED, double cc UNUSED, double cc_1 UNUSED, 
305           int i UNUSED, double w)
306 {
307   return w;
308 }
309
310
311 static double 
312 rank_rank (double c, double cc, double cc_1, 
313           int i, double w UNUSED)
314 {
315   double rank;
316   if ( c >= 1.0 ) 
317     {
318       switch (cmd.ties)
319         {
320         case RANK_LOW:
321           rank = cc_1 + 1;
322           break;
323         case RANK_HIGH:
324           rank = cc;
325           break;
326         case RANK_MEAN:
327           rank = cc_1 + (c + 1.0)/ 2.0;
328           break;
329         case RANK_CONDENSE:
330           rank = i;
331           break;
332         default:
333           NOT_REACHED ();
334         }
335     }
336   else
337     {
338       switch (cmd.ties)
339         {
340         case RANK_LOW:
341           rank = cc_1;
342           break;
343         case RANK_HIGH:
344           rank = cc;
345           break;
346         case RANK_MEAN:
347           rank = cc_1 + c / 2.0 ;
348           break;
349         case RANK_CONDENSE:
350           rank = i;
351           break;
352         default:
353           NOT_REACHED ();
354         }
355     }
356
357   return rank;
358 }
359
360
361 static double 
362 rank_rfraction (double c, double cc, double cc_1, 
363                 int i, double w)
364 {
365   return rank_rank (c, cc, cc_1, i, w) / w ;
366 }
367
368
369 static double 
370 rank_percent (double c, double cc, double cc_1, 
371                 int i, double w)
372 {
373   return rank_rank (c, cc, cc_1, i, w) * 100.0 / w ;
374 }
375
376
377 static double 
378 rank_proportion (double c, double cc, double cc_1, 
379                  int i, double w)
380 {
381   const double r =  rank_rank (c, cc, cc_1, i, w) ;
382
383   double f;
384   
385   switch ( cmd.fraction ) 
386     {
387     case RANK_BLOM:
388       f =  (r - 3.0/8.0) / (w + 0.25);
389       break;
390     case RANK_RANKIT:
391       f = (r - 0.5) / w ;
392       break;
393     case RANK_TUKEY:
394       f = (r - 1.0/3.0) / (w + 1.0/3.0);
395       break;
396     case RANK_VW:
397       f = r / ( w + 1.0);
398       break;
399     default:
400       NOT_REACHED ();
401     }
402
403
404   return (f > 0) ? f : SYSMIS;
405 }
406
407 static double 
408 rank_normal (double c, double cc, double cc_1, 
409              int i, double w)
410 {
411   double f = rank_proportion (c, cc, cc_1, i, w);
412   
413   return gsl_cdf_ugaussian_Pinv (f);
414 }
415
416 static double 
417 rank_ntiles (double c, double cc, double cc_1, 
418                 int i, double w)
419 {
420   double r = rank_rank (c, cc, cc_1, i, w);  
421
422
423   return ( floor (( r * k_ntiles) / ( w + 1) ) + 1);
424 }
425
426 /* Expected value of the order statistics from an exponential distribution */
427 static double
428 ee (int j, double w_star)
429 {
430   int k;
431   double sum = 0.0;
432   
433   for (k = 1 ; k <= j; k++) 
434     sum += 1.0 / ( w_star + 1 - k );
435
436   return sum;
437 }
438
439
440 static double 
441 rank_savage (double c, double cc, double cc_1, 
442                 int i UNUSED, double w)
443 {
444   double int_part;
445   const int i_1 = floor (cc_1);
446   const int i_2 = floor (cc);
447
448   const double w_star = (modf (w, &int_part) == 0 ) ? w : floor (w) + 1;
449
450   const double g_1 = cc_1 - i_1;
451   const double g_2 = cc - i_2;
452
453   /* The second factor is infinite, when the first is zero.
454      Therefore, evaluate the second, only when the first is non-zero */
455   const double expr1 =  (1 - g_1) ? (1 - g_1) * ee(i_1+1, w_star) : ( 1 - g_1);
456   const double expr2 =  g_2 ? g_2 * ee (i_2+1, w_star) : g_2 ;
457   
458   if ( i_1 == i_2 ) 
459     return ee (i_1 + 1, w_star) - 1;
460   
461   if ( i_1 + 1 == i_2 )
462     return ( ( expr1 + expr2 )/c ) - 1;
463
464   if ( i_1 + 2 <= i_2 ) 
465     {
466       int j;
467       double sigma = 0.0;
468       for (j = i_1 + 2 ; j <= i_2; ++j )
469         sigma += ee (j, w_star);
470       return ( (expr1 + expr2 + sigma) / c) -1;
471     }
472
473   NOT_REACHED();
474 }
475
476
477 /* Rank the casefile belonging to CR, starting from the current
478    postition of CR continuing up to and including the ENDth case.
479
480    RS points to an array containing  the rank specifications to
481    use. N_RANK_SPECS is the number of elements of RS.
482
483
484    DEST_VAR_INDEX is the index into the rank_spec destvar element 
485    to be used for this ranking.
486
487    Prerequisites: 1. The casefile must be sorted according to CRITERION.
488                   2. W is the sum of the non-missing caseweights for this 
489                   range of the casefile.
490 */
491 static void
492 rank_cases (struct casereader *cr,
493             unsigned long end,
494             const struct dictionary *dict,
495             const struct sort_criterion *criterion,
496             const struct missing_values *mv,
497             double w,
498             const struct rank_spec *rs, 
499             int n_rank_specs, 
500             int dest_var_index,
501             struct casefile *dest)
502 {
503   bool warn = true;
504   double cc = 0.0;
505   double cc_1;
506   int iter = 1;
507
508   const int fv = criterion->fv;
509   const int width = criterion->width;
510
511   while (casereader_cnum (cr) < end)
512     {
513       struct casereader *lookahead;
514       const union value *this_value;
515       struct ccase this_case, lookahead_case;
516       double c;
517       int i;
518       size_t n = 0;
519
520       if (!casereader_read_xfer (cr, &this_case))
521         break;
522       
523       this_value = case_data_idx (&this_case, fv);
524       c = dict_get_case_weight (dict, &this_case, &warn);
525               
526       lookahead = casereader_clone (cr);
527       n = 0;
528       while (casereader_cnum (lookahead) < end
529              && casereader_read_xfer (lookahead, &lookahead_case))
530         {
531           const union value *lookahead_value = case_data_idx (&lookahead_case, fv);
532           int diff = compare_values (this_value, lookahead_value, width);
533
534           if (diff != 0) 
535             {
536               /* Make sure the casefile was sorted */
537               assert ( diff == ((criterion->dir == SRT_ASCEND) ? -1 :1));
538
539               case_destroy (&lookahead_case);
540               break; 
541             }
542
543           c += dict_get_case_weight (dict, &lookahead_case, &warn);
544           case_destroy (&lookahead_case);
545           n++;
546         }
547       casereader_destroy (lookahead);
548
549       cc_1 = cc;
550       if ( !mv_is_value_missing (mv, this_value, exclude_values) )
551         cc += c;
552
553       do
554         {
555           for (i = 0; i < n_rank_specs; ++i) 
556             {
557               const struct variable *dst_var = rs[i].destvars[dest_var_index];
558
559               if  ( mv_is_value_missing (mv, this_value, exclude_values) )
560                 case_data_rw (&this_case, dst_var)->f = SYSMIS;
561               else
562                 case_data_rw (&this_case, dst_var)->f = 
563                   rank_func[rs[i].rfunc](c, cc, cc_1, iter, w);
564             }
565           casefile_append_xfer (dest, &this_case); 
566         }
567       while (n-- > 0 && casereader_read_xfer (cr, &this_case));
568
569       if ( !mv_is_value_missing (mv, this_value, exclude_values) )
570         iter++;
571     }
572
573   /* If this isn't true, then all the results will be wrong */
574   assert ( w == cc );
575 }
576
577 static bool
578 same_group (const struct ccase *a, const struct ccase *b,
579             const struct sort_criteria *crit)
580 {
581   size_t i;
582
583   for (i = 0; i < crit->crit_cnt - 1; i++)
584     {
585       struct sort_criterion *c = &crit->crits[i];
586       if (compare_values (case_data_idx (a, c->fv),
587                           case_data_idx (b, c->fv), c->width) != 0)
588         return false;
589     }
590
591   return true;
592 }
593
594 static struct casefile *
595 rank_sorted_casefile (struct casefile *cf, 
596                       const struct sort_criteria *crit, 
597                       const struct dictionary *dict,
598                       const struct rank_spec *rs, 
599                       int n_rank_specs, 
600                       int dest_idx, 
601                       const struct missing_values *mv)
602 {
603   struct casefile *dest = fastfile_create (casefile_get_value_cnt (cf));
604   struct casereader *lookahead = casefile_get_reader (cf, NULL);
605   struct casereader *pos = casereader_clone (lookahead);
606   struct ccase group_case;
607   bool warn = true;
608
609   struct sort_criterion *ultimate_crit = &crit->crits[crit->crit_cnt - 1];
610
611   if (casereader_read (lookahead, &group_case)) 
612     {
613       struct ccase this_case;
614       const union value *this_value ;
615       double w = 0.0;
616       this_value = case_data_idx( &group_case, ultimate_crit->fv);
617
618       if ( !mv_is_value_missing (mv, this_value, exclude_values) )
619         w = dict_get_case_weight (dict, &group_case, &warn);
620
621       while (casereader_read (lookahead, &this_case)) 
622         {
623           const union value *this_value = 
624             case_data_idx(&this_case, ultimate_crit->fv);
625           double c = dict_get_case_weight (dict, &this_case, &warn);
626           if (!same_group (&group_case, &this_case, crit)) 
627             {
628               rank_cases (pos, casereader_cnum (lookahead) - 1,
629                           dict,
630                           ultimate_crit, 
631                           mv, w, 
632                           rs, n_rank_specs, 
633                           dest_idx, dest);
634
635               w = 0.0;
636               case_destroy (&group_case);
637               case_move (&group_case, &this_case);
638             }
639           if ( !mv_is_value_missing (mv, this_value, exclude_values) )
640             w += c;
641           case_destroy (&this_case);
642         }
643       case_destroy (&group_case);
644       rank_cases (pos, ULONG_MAX, dict, ultimate_crit, mv, w,
645                   rs, n_rank_specs, dest_idx, dest);
646     }
647
648   if (casefile_error (dest))
649     {
650       casefile_destroy (dest);
651       dest = NULL;
652     }
653   
654   casefile_destroy (cf);
655   return dest;
656 }
657
658
659 /* Transformation function to enumerate all the cases */
660 static int 
661 create_resort_key (void *key_var_, struct ccase *cc, casenumber case_num)
662 {
663   struct variable *key_var = key_var_;
664
665   case_data_rw(cc, key_var)->f = case_num;
666   
667   return TRNS_CONTINUE;
668 }
669
670
671 /* Create and return a new variable in which to store the ranks of SRC_VAR
672    accoring to the rank function F.
673    VNAME is the name of the variable to be created.
674    If VNAME is NULL, then a name will be automatically chosen.
675  */
676 static struct variable *
677 create_rank_variable (struct dictionary *dict, enum RANK_FUNC f, 
678                       const struct variable *src_var, 
679                       const char *vname)
680 {
681   int i;
682   struct variable *var = NULL; 
683   char name[SHORT_NAME_LEN + 1];
684
685   if ( vname ) 
686     var = dict_create_var(dict, vname, 0);
687
688   if ( NULL == var )
689     {
690       snprintf (name, SHORT_NAME_LEN + 1, "%c%s", 
691                 function_name[f][0], var_get_name (src_var));
692   
693       var = dict_create_var(dict, name, 0);
694     }
695
696   i = 1;
697   while( NULL == var )
698     {
699       char func_abb[4];
700       snprintf(func_abb, 4, "%s", function_name[f]);
701       snprintf(name, SHORT_NAME_LEN + 1, "%s%03d", func_abb, 
702                i);
703
704       var = dict_create_var(dict, name, 0);
705       if (i++ >= 999) 
706         break;
707     }
708
709   i = 1;
710   while ( NULL == var )
711     {
712       char func_abb[3];
713       snprintf(func_abb, 3, "%s", function_name[f]);
714
715       snprintf(name, SHORT_NAME_LEN + 1, 
716                "RNK%s%02d", func_abb, i);
717
718       var = dict_create_var(dict, name, 0);
719       if ( i++ >= 99 ) 
720         break;
721     }
722   
723   if ( NULL == var ) 
724     {
725       msg(ME, _("Cannot create new rank variable.  All candidates in use."));
726       return NULL;
727     }
728
729   var_set_both_formats (var, &dest_format[f]); 
730
731   return var;
732 }
733
734
735 static void
736 rank_cleanup(void)
737 {
738   int i;
739
740   free (group_vars);
741   group_vars = NULL;
742   n_group_vars = 0;
743   
744   for (i = 0 ; i <  n_rank_specs ; ++i )
745     {
746       free (rank_specs[i].destvars);
747     }
748       
749   free (rank_specs);
750   rank_specs = NULL;
751   n_rank_specs = 0;
752
753   sort_destroy_criteria (sc);
754   sc = NULL;
755
756   free (src_vars);
757   src_vars = NULL;
758   n_src_vars = 0;
759 }
760
761 int
762 cmd_rank (struct lexer *lexer, struct dataset *ds)
763 {
764   bool result;
765   struct variable *order;
766   size_t i;
767   n_rank_specs = 0;
768
769   if ( !parse_rank (lexer, ds, &cmd, NULL) )
770     {
771       rank_cleanup ();
772     return CMD_FAILURE;
773     }
774
775   /* If /MISSING=INCLUDE is set, then user missing values are ignored */
776   exclude_values = cmd.miss == RANK_INCLUDE ? MV_SYSTEM : MV_ANY;
777
778   /* Default to /RANK if no function subcommands are given */
779   if ( !( cmd.sbc_normal  || cmd.sbc_ntiles || cmd.sbc_proportion || 
780           cmd.sbc_rfraction || cmd.sbc_savage || cmd.sbc_n || 
781           cmd.sbc_percent || cmd.sbc_rank ) )
782     {
783       assert ( n_rank_specs == 0 );
784       
785       rank_specs = xmalloc (sizeof (*rank_specs));
786       rank_specs[0].rfunc = RANK;
787       rank_specs[0].destvars = 
788         xcalloc (sc->crit_cnt, sizeof (struct variable *));
789
790       n_rank_specs = 1;
791     }
792
793   assert ( sc->crit_cnt == n_src_vars);
794
795   /* Create variables for all rank destinations which haven't
796      already been created with INTO.
797      Add labels to all the destination variables.
798   */
799   for (i = 0 ; i <  n_rank_specs ; ++i )
800     {
801       int v;
802       for ( v = 0 ; v < n_src_vars ;  v ++ ) 
803         {
804           if ( rank_specs[i].destvars[v] == NULL ) 
805             {
806               rank_specs[i].destvars[v] = 
807                 create_rank_variable (dataset_dict(ds), rank_specs[i].rfunc, src_vars[v], NULL);
808             }
809       
810           create_var_label ( rank_specs[i].destvars[v],
811                              src_vars[v],
812                              rank_specs[i].rfunc);
813         }
814     }
815
816   if ( cmd.print == RANK_YES ) 
817     {
818       int v;
819
820       tab_output_text (0, _("Variables Created By RANK"));
821       tab_output_text (0, "\n");
822   
823       for (i = 0 ; i <  n_rank_specs ; ++i )
824         {
825           for ( v = 0 ; v < n_src_vars ;  v ++ ) 
826             {
827               if ( n_group_vars > 0 )
828                 {
829                   struct string varlist;
830                   int g;
831
832                   ds_init_empty (&varlist);
833                   for ( g = 0 ; g < n_group_vars ; ++g ) 
834                     {
835                       ds_put_cstr (&varlist, var_get_name (group_vars[g]));
836
837                       if ( g < n_group_vars - 1)
838                         ds_put_cstr (&varlist, " ");
839                     }
840
841                   if ( rank_specs[i].rfunc == NORMAL || 
842                        rank_specs[i].rfunc == PROPORTION ) 
843                     tab_output_text (TAT_PRINTF,
844                                      _("%s into %s(%s of %s using %s BY %s)"), 
845                                      var_get_name (src_vars[v]),
846                                      var_get_name (rank_specs[i].destvars[v]),
847                                      function_name[rank_specs[i].rfunc],
848                                      var_get_name (src_vars[v]),
849                                      fraction_name(),
850                                      ds_cstr (&varlist)
851                                      );
852                     
853                   else
854                     tab_output_text (TAT_PRINTF,
855                                      _("%s into %s(%s of %s BY %s)"), 
856                                      var_get_name (src_vars[v]),
857                                      var_get_name (rank_specs[i].destvars[v]),
858                                      function_name[rank_specs[i].rfunc],
859                                      var_get_name (src_vars[v]),
860                                      ds_cstr (&varlist)
861                                      );
862                   ds_destroy (&varlist);
863                 }
864               else
865                 {
866                   if ( rank_specs[i].rfunc == NORMAL || 
867                        rank_specs[i].rfunc == PROPORTION ) 
868                     tab_output_text (TAT_PRINTF,
869                                      _("%s into %s(%s of %s using %s)"), 
870                                      var_get_name (src_vars[v]),
871                                      var_get_name (rank_specs[i].destvars[v]),
872                                      function_name[rank_specs[i].rfunc],
873                                      var_get_name (src_vars[v]),
874                                      fraction_name()
875                                      );
876                     
877                   else
878                     tab_output_text (TAT_PRINTF,
879                                      _("%s into %s(%s of %s)"), 
880                                      var_get_name (src_vars[v]),
881                                      var_get_name (rank_specs[i].destvars[v]),
882                                      function_name[rank_specs[i].rfunc],
883                                      var_get_name (src_vars[v])
884                                      );
885                 }
886             }
887         }
888     }
889
890   if ( cmd.sbc_fraction && 
891        ( ! cmd.sbc_normal && ! cmd.sbc_proportion) )
892     msg(MW, _("FRACTION has been specified, but NORMAL and PROPORTION rank functions have not been requested.  The FRACTION subcommand will be ignored.") );
893
894   /* Add a variable which we can sort by to get back the original
895      order */
896   order = dict_create_var_assert (dataset_dict (ds), "$ORDER_", 0);
897
898   add_transformation (ds, create_resort_key, 0, order);
899
900   /* Do the ranking */
901   result = rank_cmd (ds, sc, rank_specs, n_rank_specs);
902
903   /* Put the active file back in its original order */
904   {
905     struct sort_criteria criteria;
906     struct sort_criterion restore_criterion ;
907     restore_criterion.fv = var_get_case_index (order);
908     restore_criterion.width = 0;
909     restore_criterion.dir = SRT_ASCEND;
910
911     criteria.crits = &restore_criterion;
912     criteria.crit_cnt = 1;
913     
914     sort_active_file_in_place (ds, &criteria);
915 }
916
917   /* ... and we don't need our sort key anymore. So delete it */
918   dict_delete_var (dataset_dict (ds), order);
919
920   rank_cleanup();
921
922   return (result ? CMD_SUCCESS : CMD_CASCADING_FAILURE);
923 }
924
925
926 /* Parser for the variables sub command  
927    Returns 1 on success */
928 static int
929 rank_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_rank *cmd UNUSED, void *aux UNUSED)
930 {
931   static const int terminators[2] = {T_BY, 0};
932
933   lex_match (lexer, '=');
934
935   if ((lex_token (lexer) != T_ID || dict_lookup_var (dataset_dict (ds), lex_tokid (lexer)) == NULL)
936       && lex_token (lexer) != T_ALL)
937       return 2;
938
939   sc = sort_parse_criteria (lexer, dataset_dict (ds), 
940                             &src_vars, &n_src_vars, 0, terminators);
941
942   if ( lex_match (lexer, T_BY)  )
943     {
944       if ((lex_token (lexer) != T_ID || dict_lookup_var (dataset_dict (ds), lex_tokid (lexer)) == NULL))
945         {
946           return 2;
947         }
948
949       if (!parse_variables (lexer, dataset_dict (ds), 
950                             &group_vars, &n_group_vars,
951                             PV_NO_DUPLICATE | PV_NO_SCRATCH) )
952         {
953           free (group_vars);
954           return 0;
955         }
956     }
957
958   return 1;
959 }
960
961
962 /* Parse the [/rank INTO var1 var2 ... varN ] clause */
963 static int
964 parse_rank_function (struct lexer *lexer, struct dictionary *dict, struct cmd_rank *cmd UNUSED, enum RANK_FUNC f)
965 {
966   int var_count = 0;
967   
968   n_rank_specs++;
969   rank_specs = xnrealloc(rank_specs, n_rank_specs, sizeof *rank_specs);
970   rank_specs[n_rank_specs - 1].rfunc = f;
971   rank_specs[n_rank_specs - 1].destvars = NULL;
972
973   rank_specs[n_rank_specs - 1].destvars = 
974             xcalloc (sc->crit_cnt, sizeof (struct variable *));
975           
976   if (lex_match_id (lexer, "INTO"))
977     {
978       struct variable *destvar;
979
980       while( lex_token (lexer) == T_ID ) 
981         {
982
983           if ( dict_lookup_var (dict, lex_tokid (lexer)) != NULL )
984             {
985               msg(SE, _("Variable %s already exists."), lex_tokid (lexer));
986               return 0;
987             }
988           if ( var_count >= sc->crit_cnt ) 
989             {
990               msg(SE, _("Too many variables in INTO clause."));
991               return 0;
992             }
993
994           destvar = create_rank_variable (dict, f, src_vars[var_count], lex_tokid (lexer));
995           rank_specs[n_rank_specs - 1].destvars[var_count] = destvar ;
996
997           lex_get (lexer);
998           ++var_count;
999         }
1000     }
1001
1002   return 1;
1003 }
1004
1005
1006 static int
1007 rank_custom_rank (struct lexer *lexer, struct dataset *ds, struct cmd_rank *cmd, void *aux UNUSED )
1008 {
1009   struct dictionary *dict = dataset_dict (ds);
1010
1011   return parse_rank_function (lexer, dict, cmd, RANK);
1012 }
1013
1014 static int
1015 rank_custom_normal (struct lexer *lexer, struct dataset *ds, struct cmd_rank *cmd, void *aux UNUSED )
1016 {
1017   struct  dictionary *dict = dataset_dict (ds);
1018
1019   return parse_rank_function (lexer, dict, cmd, NORMAL);
1020 }
1021
1022 static int
1023 rank_custom_percent (struct lexer *lexer, struct dataset *ds, struct cmd_rank *cmd, void *aux UNUSED )
1024 {
1025   struct dictionary *dict = dataset_dict (ds);
1026
1027   return parse_rank_function (lexer, dict, cmd, PERCENT);
1028 }
1029
1030 static int
1031 rank_custom_rfraction (struct lexer *lexer, struct dataset *ds, struct cmd_rank *cmd, void *aux UNUSED )
1032 {
1033   struct dictionary *dict = dataset_dict (ds);
1034
1035   return parse_rank_function (lexer, dict, cmd, RFRACTION);
1036 }
1037
1038 static int
1039 rank_custom_proportion (struct lexer *lexer, struct dataset *ds, struct cmd_rank *cmd, void *aux UNUSED )
1040 {
1041   struct dictionary *dict = dataset_dict (ds);
1042
1043   return parse_rank_function (lexer, dict, cmd, PROPORTION);
1044 }
1045
1046 static int
1047 rank_custom_n (struct lexer *lexer, struct dataset *ds, struct cmd_rank *cmd, void *aux UNUSED )
1048 {
1049   struct dictionary *dict = dataset_dict (ds);
1050
1051   return parse_rank_function (lexer, dict, cmd, N);
1052 }
1053
1054 static int
1055 rank_custom_savage (struct lexer *lexer, struct dataset *ds, struct cmd_rank *cmd, void *aux UNUSED )
1056 {
1057   struct dictionary *dict = dataset_dict (ds);
1058
1059   return parse_rank_function (lexer, dict, cmd, SAVAGE);
1060 }
1061
1062
1063 static int
1064 rank_custom_ntiles (struct lexer *lexer, struct dataset *ds, struct cmd_rank *cmd, void *aux UNUSED )
1065 {
1066   struct dictionary *dict = dataset_dict (ds);
1067
1068   if ( lex_force_match (lexer, '(') ) 
1069     {
1070       if ( lex_force_int (lexer) ) 
1071         {
1072           k_ntiles = lex_integer (lexer);
1073           lex_get (lexer);
1074           lex_force_match (lexer, ')');
1075         }
1076       else
1077         return 0;
1078     }
1079   else
1080     return 0;
1081
1082   return parse_rank_function (lexer, dict, cmd, NTILES);
1083 }