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