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