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