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