1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23 #include "algorithm.h"
25 #include "bitvector.h"
35 /* FIXME: should change weighting variable, etc. */
36 /* These control the ordering produced by
37 compare_variables_given_ordering(). */
40 int forward; /* 1=FORWARD, 0=BACKWARD. */
41 int positional; /* 1=POSITIONAL, 0=ALPHA. */
44 /* Increasing order of variable index. */
45 static struct ordering forward_positional_ordering = {1, 1};
47 static int compare_variables_given_ordering (const void *, const void *,
50 /* Explains how to modify the variables in a dictionary. */
51 struct var_modification
53 /* New variable ordering. */
54 struct variable **reorder_vars;
57 /* DROP/KEEP information. */
58 struct variable **drop_vars;
61 /* New variable names. */
62 struct variable **rename_vars;
67 static int rearrange_dict (struct dictionary *d,
68 const struct var_modification *vm);
70 /* Performs MODIFY VARS command. */
72 cmd_modify_vars (void)
74 /* Bits indicated whether we've already encountered a subcommand of
76 unsigned already_encountered = 0;
78 /* What we're gonna do to the active file. */
79 struct var_modification vm;
82 int ret_code = CMD_FAILURE;
88 msg (SE, _("MODIFY VARS may not be used after TEMPORARY. "
89 "Temporary transformations will be made permanent."));
93 vm.reorder_vars = NULL;
95 vm.rename_vars = NULL;
101 /* Parse each subcommand. */
105 if (lex_match_id ("REORDER"))
107 struct variable **v = NULL;
110 if (already_encountered & 1)
112 msg (SE, _("REORDER subcommand may be given at most once."));
115 already_encountered |= 1;
120 struct ordering ordering;
123 ordering.forward = ordering.positional = 1;
124 if (lex_match_id ("FORWARD"));
125 else if (lex_match_id ("BACKWARD"))
126 ordering.forward = 0;
127 if (lex_match_id ("POSITIONAL"));
128 else if (lex_match_id ("ALPHA"))
129 ordering.positional = 0;
131 if (lex_match (T_ALL) || token == '/' || token == '.')
135 msg (SE, _("Cannot specify ALL after specifying a set "
139 dict_get_vars (default_dict, &v, &nv, 1u << DC_SYSTEM);
143 if (!lex_match ('('))
145 msg (SE, _("`(' expected on REORDER subcommand."));
149 if (!parse_variables (default_dict, &v, &nv,
150 PV_APPEND | PV_NO_DUPLICATE))
155 if (!lex_match (')'))
157 msg (SE, _("`)' expected following variable names on "
158 "REORDER subcommand."));
163 sort (&v[prev_nv], nv - prev_nv, sizeof *v,
164 compare_variables_given_ordering, &ordering);
166 while (token != '/' && token != '.');
171 else if (lex_match_id ("RENAME"))
173 if (already_encountered & 2)
175 msg (SE, _("RENAME subcommand may be given at most once."));
178 already_encountered |= 2;
183 int prev_nv_1 = vm.rename_cnt;
184 int prev_nv_2 = vm.rename_cnt;
186 if (!lex_match ('('))
188 msg (SE, _("`(' expected on RENAME subcommand."));
191 if (!parse_variables (default_dict, &vm.rename_vars, &vm.rename_cnt,
192 PV_APPEND | PV_NO_DUPLICATE))
194 if (!lex_match ('='))
196 msg (SE, _("`=' expected between lists of new and old variable "
197 "names on RENAME subcommand."));
200 if (!parse_DATA_LIST_vars (&vm.new_names, &prev_nv_1, PV_APPEND))
202 if (prev_nv_1 != vm.rename_cnt)
204 msg (SE, _("Differing number of variables in old name list "
205 "(%d) and in new name list (%d)."),
206 vm.rename_cnt - prev_nv_2, prev_nv_1 - prev_nv_2);
207 for (i = 0; i < prev_nv_1; i++)
208 free (vm.new_names[i]);
213 if (!lex_match (')'))
215 msg (SE, _("`)' expected after variable lists on RENAME "
220 while (token != '.' && token != '/');
222 else if (lex_match_id ("KEEP"))
224 struct variable **keep_vars, **all_vars, **drop_vars;
225 int keep_cnt, all_cnt, drop_cnt;
227 if (already_encountered & 4)
229 msg (SE, _("KEEP subcommand may be given at most once. It may not"
230 "be given in conjunction with the DROP subcommand."));
233 already_encountered |= 4;
236 if (!parse_variables (default_dict, &keep_vars, &keep_cnt, PV_NONE))
239 /* Transform the list of variables to keep into a list of
240 variables to drop. First sort the keep list, then figure
241 out which variables are missing. */
242 sort (keep_vars, keep_cnt, sizeof *keep_vars,
243 compare_variables_given_ordering, &forward_positional_ordering);
245 dict_get_vars (default_dict, &all_vars, &all_cnt, 0);
247 drop_cnt = all_cnt - keep_cnt;
248 drop_vars = xmalloc (drop_cnt * sizeof *keep_vars);
249 if (set_difference (all_vars, all_cnt,
253 compare_variables_given_ordering,
254 &forward_positional_ordering)
261 vm.drop_vars = drop_vars;
262 vm.drop_cnt = drop_cnt;
264 else if (lex_match_id ("DROP"))
266 struct variable **drop_vars;
269 if (already_encountered & 4)
271 msg (SE, _("DROP subcommand may be given at most once. It may "
272 "not be given in conjunction with the KEEP "
276 already_encountered |= 4;
279 if (!parse_variables (default_dict, &drop_vars, &drop_cnt, PV_NONE))
281 vm.drop_vars = drop_vars;
282 vm.drop_cnt = drop_cnt;
284 else if (lex_match_id ("MAP"))
286 struct dictionary *temp = dict_clone (default_dict);
287 int success = rearrange_dict (temp, &vm);
290 /* FIXME: display new dictionary. */
297 msg (SE, _("Unrecognized subcommand name `%s'."), tokid);
299 msg (SE, _("Subcommand name expected."));
307 msg (SE, _("`/' or `.' expected."));
313 if (already_encountered & (1 | 4))
316 procedure (NULL, NULL);
319 if (!rearrange_dict (default_dict, &vm))
322 ret_code = CMD_SUCCESS;
325 free (vm.reorder_vars);
326 free (vm.rename_vars);
327 for (i = 0; i < vm.rename_cnt; i++)
328 free (vm.new_names[i]);
334 /* Compares A and B according to the settings in
335 ORDERING, returning a strcmp()-type result. */
337 compare_variables_given_ordering (const void *a_, const void *b_,
340 struct variable *const *pa = a_;
341 struct variable *const *pb = b_;
342 const struct variable *a = *pa;
343 const struct variable *b = *pb;
344 const struct ordering *ordering = ordering_;
347 if (ordering->positional)
348 result = a->index < b->index ? -1 : a->index > b->index;
350 result = strcmp (a->name, b->name);
351 if (!ordering->forward)
356 /* Pairs a variable with a new name. */
359 struct variable *var;
363 /* A algo_compare_func that compares new_name members in struct
364 var_renaming structures A and B. */
366 compare_var_renaming_by_new_name (const void *a_, const void *b_,
369 const struct var_renaming *a = a_;
370 const struct var_renaming *b = b_;
372 return strcmp (a->new_name, b->new_name);
375 /* Returns true if performing VM on dictionary D would not cause
376 problems such as duplicate variable names. Returns false
377 otherwise, and issues an error message. */
379 validate_var_modification (const struct dictionary *d,
380 const struct var_modification *vm)
382 /* Variable reordering can't be a problem, so we don't simulate
383 it. Variable renaming can cause duplicate names, but
384 dropping variables can eliminate them, so we simulate both
386 struct variable **all_vars;
387 struct variable **keep_vars;
388 struct variable **drop_vars;
389 size_t all_cnt, keep_cnt, drop_cnt;
391 struct var_renaming *var_renaming;
395 /* All variables, in index order. */
396 dict_get_vars (d, &all_vars, &all_cnt, 0);
398 /* Drop variables, in index order. */
399 drop_cnt = vm->drop_cnt;
400 drop_vars = xmalloc (drop_cnt * sizeof *drop_vars);
401 memcpy (drop_vars, vm->drop_vars, drop_cnt * sizeof *drop_vars);
402 sort (drop_vars, drop_cnt, sizeof *drop_vars,
403 compare_variables_given_ordering, &forward_positional_ordering);
405 /* Keep variables, in index order. */
406 keep_cnt = all_cnt - drop_cnt;
407 keep_vars = xmalloc (keep_cnt * sizeof *keep_vars);
408 if (set_difference (all_vars, all_cnt,
412 compare_variables_given_ordering,
413 &forward_positional_ordering) != keep_cnt)
416 /* Copy variables into var_renaming array. */
417 var_renaming = xmalloc (keep_cnt * sizeof *var_renaming);
418 for (i = 0; i < keep_cnt; i++)
420 var_renaming[i].var = keep_vars[i];
421 strcpy (var_renaming[i].new_name, keep_vars[i]->name);
424 /* Rename variables in var_renaming array. */
425 for (i = 0; i < vm->rename_cnt; i++)
427 struct variable *const *kv;
428 struct var_renaming *vr;
430 /* Get the var_renaming element. */
431 kv = binary_search (keep_vars, keep_cnt, sizeof *keep_vars,
433 compare_variables_given_ordering,
434 &forward_positional_ordering);
437 vr = var_renaming + (kv - keep_vars);
439 strcpy (vr->new_name, vm->new_names[i]);
442 /* Sort var_renaming array by new names and check for
444 sort (var_renaming, keep_cnt, sizeof *var_renaming,
445 compare_var_renaming_by_new_name, NULL);
446 valid = adjacent_find_equal (var_renaming, keep_cnt, sizeof *var_renaming,
447 compare_var_renaming_by_new_name, NULL) == NULL;
458 /* Reoders, removes, and renames variables in dictionary D
459 according to VM. Returns nonzero if successful, zero if there
460 would have been duplicate variable names if the modifications
461 had been carried out. In the latter case, the dictionary is
464 rearrange_dict (struct dictionary *d, const struct var_modification *vm)
466 char **rename_old_names;
468 struct variable **rename_vars;
469 char **rename_new_names;
474 /* Check whether the modifications will cause duplicate
476 if (!validate_var_modification (d, vm))
479 /* Record the old names of variables to rename. After
480 variables are deleted, we can't depend on the variables to
481 still exist, but we can still look them up by name. */
482 rename_old_names = xmalloc (vm->rename_cnt * sizeof *rename_old_names);
483 for (i = 0; i < vm->rename_cnt; i++)
484 rename_old_names[i] = xstrdup (vm->rename_vars[i]->name);
486 /* Reorder and delete variables. */
487 dict_reorder_vars (d, vm->reorder_vars, vm->reorder_cnt);
488 dict_delete_vars (d, vm->drop_vars, vm->drop_cnt);
490 /* Compose lists of variables to rename and their new names. */
491 rename_vars = xmalloc (vm->rename_cnt * sizeof *rename_vars);
492 rename_new_names = xmalloc (vm->rename_cnt * sizeof *rename_new_names);
494 for (i = 0; i < vm->rename_cnt; i++)
496 struct variable *var = dict_lookup_var (d, rename_old_names[i]);
500 rename_vars[rename_cnt] = var;
501 rename_new_names[rename_cnt] = vm->new_names[i];
506 if (dict_rename_vars (d, rename_vars, rename_new_names, rename_cnt,
511 for (i = 0; i < vm->rename_cnt; i++)
512 free (rename_old_names[i]);
513 free (rename_old_names);
515 free (rename_new_names);