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