Fix bug #11612, "q2c documentation does not agree with code".
[pspp-builds.git] / src / language / stats / rank.q
1 /* PSPP - RANK. -*-c-*-
2
3 Copyright (C) 2005 Free Software Foundation, Inc.
4 Author: John Darrington 2005
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/procedure.h>
27 #include <data/variable.h>
28 #include <language/command.h>
29 #include <language/stats/sort-criteria.h>
30 #include <libpspp/compiler.h>
31 #include <math/sort.h>
32
33 #include "gettext.h"
34 #define _(msgid) gettext (msgid)
35
36 /* (headers) */
37
38 /* (specification)
39    "RANK" (rank_):
40    *^variables=custom;
41    +rank=custom;
42    +normal=custom;
43    +percent=custom;
44    +ntiles=custom;
45    +rfraction=custom;
46    +proportion=custom;
47    +n=custom;
48    +savage=custom;
49    +print=print:!yes/no;
50    missing=miss:!exclude/include.
51 */
52 /* (declarations) */
53 /* (functions) */
54
55
56
57 enum RANK_FUNC
58   {
59     RANK,
60     NORMAL,
61     PERCENT,
62     RFRACTION,
63     PROPORTION,
64     N,
65     NTILES,
66     SAVAGE,
67   };
68
69
70 struct rank_spec
71 {
72   enum RANK_FUNC rfunc;
73   struct variable **destvars;
74   struct variable *srcvar;
75 };
76
77
78 static struct rank_spec *rank_specs;
79 static size_t n_rank_specs;
80
81 static struct sort_criteria *sc;
82
83 static struct variable **group_vars;
84 static size_t n_group_vars;
85
86 static struct cmd_rank cmd;
87
88
89
90 int cmd_rank(void);
91
92 int
93 cmd_rank(void)
94 {
95   size_t i;
96   n_rank_specs = 0;
97
98   if ( !parse_rank(&cmd, NULL) )
99     return CMD_FAILURE;
100
101 #if 1
102   for (i = 0 ; i <  sc->crit_cnt ; ++i )
103     {
104       struct sort_criterion *crit = &sc->crits[i];
105       
106       printf("Dir: %d; Index: %d\n", crit->dir, crit->fv);
107     }
108
109   for (i = 0 ; i <  n_group_vars ; ++i )
110     printf("Group var: %s\n",group_vars[0]->name);
111
112   for (i = 0 ; i <  n_rank_specs ; ++i )
113     {
114       int j;
115       printf("Ranks spec %d; Func: %d\n",i, rank_specs[i].rfunc);
116       
117       for (j=0; j < sc->crit_cnt ; ++j )
118         printf("Dest var is \"%s\"\n", rank_specs[i].destvars[j]->name);
119     }
120 #endif 
121
122
123   free(group_vars);
124   
125   for (i = 0 ; i <  n_rank_specs ; ++i )
126     {
127       free(rank_specs[i].destvars);
128     }
129       
130   free(rank_specs);
131
132   sort_destroy_criteria(sc);
133
134   return CMD_SUCCESS;
135 }
136
137
138
139 /* Parser for the variables sub command  
140    Returns 1 on success */
141 static int
142 rank_custom_variables(struct cmd_rank *cmd UNUSED, void *aux UNUSED)
143 {
144   static const int terminators[2] = {T_BY, 0};
145
146   lex_match('=');
147
148   if ((token != T_ID || dict_lookup_var (default_dict, tokid) == NULL)
149       && token != T_ALL)
150       return 2;
151
152   sc = sort_parse_criteria (default_dict, 0, 0, 0, terminators);
153
154   if ( lex_match(T_BY)  )
155     {
156       if ((token != T_ID || dict_lookup_var (default_dict, tokid) == NULL))
157         {
158           return 2;
159         }
160
161       if (!parse_variables (default_dict, &group_vars, &n_group_vars,
162                             PV_NO_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH) )
163         {
164           free (group_vars);
165           return 0;
166         }
167     }
168
169   return 1;
170 }
171
172
173 /* Return a name for a new variable which ranks the variable VAR_NAME,
174    according to the ranking function F.
175    If IDX is non zero, then IDX is used as a disambiguating number.
176    FIXME: This is not very robust.
177 */
178 static char *
179 new_variable_name(const char *ranked_var_name, enum RANK_FUNC f, int idx)
180 {
181   static char new_name[SHORT_NAME_LEN + 1];
182   char temp[SHORT_NAME_LEN + 1];
183  
184   if ( idx == 0 ) 
185     {
186       switch (f) 
187         {
188         case RANK:
189         case RFRACTION:
190           strcpy(new_name,"R");
191           break;
192
193         case NORMAL:
194         case N:
195         case NTILES:
196           strcpy(new_name,"N");
197           break;
198       
199         case PERCENT:
200         case PROPORTION:
201           strcpy(new_name,"P");
202           break;
203
204         case SAVAGE:
205           strcpy(new_name,"S");
206           break;
207
208         default:
209           assert(false);
210           break;
211         }
212   
213       strncat(new_name, ranked_var_name, 7);
214     }
215   else
216     {
217       strncpy(temp, ranked_var_name, 3);
218       snprintf(new_name, SHORT_NAME_LEN, "%s%03d", temp, idx);
219     }
220
221   return new_name;
222 }
223
224 /* Parse the [/rank INTO var1 var2 ... varN ] clause */
225 static int
226 parse_rank_function(struct cmd_rank *cmd UNUSED, enum RANK_FUNC f)
227 {
228   static const struct fmt_spec f8_2 = {FMT_F, 8, 2};
229   int var_count = 0;
230   
231   n_rank_specs++;
232   rank_specs = xnrealloc(rank_specs, n_rank_specs, sizeof *rank_specs);
233   rank_specs[n_rank_specs - 1].rfunc = f;
234
235   rank_specs[n_rank_specs - 1].destvars = 
236             xcalloc (sc->crit_cnt, sizeof (struct variable *));
237           
238   if (lex_match_id("INTO"))
239     {
240       struct variable *destvar;
241
242       while( token == T_ID ) 
243         {
244           ++var_count;
245           if ( dict_lookup_var (default_dict, tokid) != NULL )
246             {
247               msg(SE, _("Variable %s already exists."), tokid);
248               return 0;
249             }
250           if ( var_count > sc->crit_cnt ) 
251             {
252               msg(SE, _("Too many variables in INTO clause."));
253               return 0;
254             }
255
256           destvar = dict_create_var (default_dict, tokid, 0);
257           if ( destvar ) 
258             {
259               destvar->print = destvar->write = f8_2;
260             }
261           
262           rank_specs[n_rank_specs - 1].destvars[var_count - 1] = destvar ;
263
264           lex_get();
265           
266         }
267     }
268
269   /* Allocate rank  variable names to all those which haven't had INTO 
270      variables assigned */
271   while (var_count < sc->crit_cnt)
272     {
273       static int idx=0;
274       struct variable *destvar ; 
275       const struct variable *v = dict_get_var(default_dict,
276                                               sc->crits[var_count].fv);
277
278       char *new_name;
279       
280       do {
281         new_name = new_variable_name(v->name, f, idx);
282
283         destvar = dict_create_var (default_dict, new_name, 0);
284         if (!destvar ) 
285           ++idx;
286
287       } while( !destvar ) ;
288
289       destvar->print = destvar->write = f8_2;
290
291       rank_specs[n_rank_specs - 1].destvars[var_count] = destvar ;
292       
293       ++var_count;
294     }
295
296   return 1;
297 }
298
299
300 static int
301 rank_custom_rank(struct cmd_rank *cmd, void *aux UNUSED )
302 {
303   return parse_rank_function(cmd, RANK);
304 }
305
306 static int
307 rank_custom_normal(struct cmd_rank *cmd, void *aux UNUSED )
308 {
309   return parse_rank_function(cmd, NORMAL);
310 }
311
312 static int
313 rank_custom_percent(struct cmd_rank *cmd, void *aux UNUSED )
314 {
315   return parse_rank_function(cmd, NORMAL);
316 }
317
318 static int
319 rank_custom_rfraction(struct cmd_rank *cmd, void *aux UNUSED )
320 {
321   return parse_rank_function(cmd, RFRACTION);
322 }
323
324 static int
325 rank_custom_proportion(struct cmd_rank *cmd, void *aux UNUSED )
326 {
327   return parse_rank_function(cmd, PROPORTION);
328 }
329
330 static int
331 rank_custom_n(struct cmd_rank *cmd, void *aux UNUSED )
332 {
333   return parse_rank_function(cmd, N);
334 }
335
336 static int
337 rank_custom_savage(struct cmd_rank *cmd, void *aux UNUSED )
338 {
339   return parse_rank_function(cmd, SAVAGE);
340 }
341
342
343 static int
344 rank_custom_ntiles(struct cmd_rank *cmd, void *aux UNUSED )
345 {
346   if ( lex_force_match('(') ) 
347     {
348       if ( lex_force_int() ) 
349         {
350           lex_get();
351           lex_force_match(')');
352         }
353       else
354         return 0;
355     }
356   else
357     return 0;
358
359   return parse_rank_function(cmd, NTILES);
360 }
361
362