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