Sat Dec 27 16:16:49 2003 Ben Pfaff <blp@gnu.org>
[pspp-builds.git] / src / flip.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
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.
9
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.
14
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
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include <assert.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <float.h>
25 #include <stdlib.h>
26 #include "alloc.h"
27 #include "command.h"
28 #include "error.h"
29 #include "lexer.h"
30 #include "misc.h"
31 #include "str.h"
32 #include "var.h"
33 #include "vfm.h"
34
35 /* Variables to transpose. */
36 static struct variable **var;
37 static int nvar;
38
39 /* Variable containing new variable names. */
40 static struct variable *newnames;
41
42 /* List of variable names. */
43 struct varname
44   {
45     struct varname *next;
46     char name[1];
47   };
48
49 /* New variable names. */
50 static struct varname *new_names_head, *new_names_tail;
51 static int case_count;
52
53 static int build_dictionary (void);
54
55 /* Parses and executes FLIP. */
56 int
57 cmd_flip (void)
58 {
59   lex_match_id ("FLIP");
60   lex_match ('/');
61   if (lex_match_id ("VARIABLES"))
62     {
63       lex_match ('=');
64       if (!parse_variables (default_dict, &var, &nvar, PV_NO_DUPLICATE))
65         return CMD_FAILURE;
66       lex_match ('/');
67     }
68   else
69     dict_get_vars (default_dict, &var, &nvar, 1u << DC_SYSTEM);
70
71   lex_match ('/');
72   if (lex_match_id ("NEWNAMES"))
73     {
74       lex_match ('=');
75       newnames = parse_variable ();
76       if (!newnames)
77         {
78           free (var);
79           return CMD_FAILURE;
80         }
81     }
82   else
83     newnames = dict_lookup_var (default_dict, "CASE_LBL");
84
85   if (newnames)
86     {
87       int i;
88       
89       for (i = 0; i < nvar; i++)
90         if (var[i] == newnames)
91           {
92             memmove (&var[i], &var[i + 1], sizeof *var * (nvar - i - 1));
93             nvar--;
94             break;
95           }
96     }
97
98   case_count = 0;
99   temp_trns = temporary = 0;
100   vfm_sink = &flip_stream;
101   new_names_tail = NULL;
102   procedure (NULL, NULL, NULL);
103
104   dict_clear (default_dict);
105   if (!build_dictionary ())
106     {
107       discard_variables ();
108       free (var);
109       return CMD_FAILURE;
110     }
111
112   free (var);
113   return lex_end_of_command ();
114 }
115
116 /* Make a new variable with base name NAME, which is bowdlerized and
117    mangled until acceptable, and returns success. */
118 static int
119 make_new_var (char name[])
120 {
121   /* Fix invalid characters. */
122   {
123     char *cp;
124   
125     for (cp = name; *cp && !isspace (*cp); cp++)
126       {
127         *cp = toupper ((unsigned char) *cp);
128         if (!isalpha (*cp) && *cp != '@' && *cp != '#'
129             && (cp == name || (*cp != '.' && *cp != '$' && *cp != '_')))
130           {
131             if (cp == name)
132               *cp = 'V';        /* _ not valid in first position. */
133             else
134               *cp = '_';
135           }
136       }
137     *cp = 0;
138   }
139   
140   if (dict_create_var (default_dict, name, 0))
141     return 1;
142
143   /* Add numeric extensions until acceptable. */
144   {
145     int len = (int) strlen (name);
146     char n[9];
147     int i;
148
149     for (i = 1; i < 10000000; i++)
150       {
151         int ofs = min (7 - intlog10 (i), len);
152         memcpy (n, name, ofs);
153         sprintf (&n[ofs], "%d", i);
154
155         if (dict_create_var (default_dict, n, 0))
156           return 1;
157       }
158   }
159
160   msg (SE, _("Could not create acceptable variant for variable %s."), name);
161   return 0;
162 }
163
164 /* Make a new dictionary for all the new variable names. */
165 static int
166 build_dictionary (void)
167 {
168   if (!dict_create_var (default_dict, "CASE_LBL", 8))
169     assert (0);
170
171   if (!new_names_tail)
172     {
173       int i;
174       
175       if (case_count > 99999)
176         {
177           msg (SE, _("Cannot create more than 99999 variable names."));
178           return 0;
179         }
180       
181       for (i = 0; i < case_count; i++)
182         {
183           struct variable *v;
184           char s[9];
185
186           sprintf (s, "VAR%03d", i);
187           v = dict_create_var (default_dict, s, 0);
188           assert (v != NULL);
189         }
190     }
191   else
192     {
193       struct varname *v, *n;
194
195       new_names_tail->next = NULL;
196       for (v = new_names_head; v; v = n)
197         {
198           n = v->next;
199           if (!make_new_var (v->name))
200             {
201               for (; v; v = n)
202                 {
203                   n = v->next;
204                   free (v);
205                 }
206               return 0;
207             }
208           free (v);
209         }
210     }
211   
212   return 1;
213 }
214      
215
216 /* Each case to be transposed. */
217 struct flip_case
218   {
219     struct flip_case *next;
220     double v[1];
221   };
222
223 /* Sink: Cases during transposition. */
224 static int internal;                    /* Internal vs. external flipping. */
225 static char *sink_old_names;            /* Old variable names. */
226 static unsigned long sink_cases;        /* Number of cases. */
227 static struct flip_case *head, *tail;   /* internal == 1: Cases. */
228 static FILE *sink_file;                 /* internal == 0: Temporary file. */
229
230 /* Source: Cases after transposition. */
231 static struct flip_case *src;           /* Internal transposition records. */
232 static char *src_old_names;             /* Old variable names. */
233 static unsigned long src_cases;         /* Number of cases. */
234 static FILE *src_file;                  /* src == NULL: Temporary file. */
235
236 /* Initialize the FLIP stream. */
237 static void 
238 flip_stream_init (void)
239 {
240   internal = 1;
241   sink_cases = 0;
242   tail = NULL;
243   
244   {
245     size_t n = nvar;
246     char *p;
247     int i;
248     
249     for (i = 0; i < nvar; i++)
250       n += strlen (var[i]->name);
251     p = sink_old_names = xmalloc (n);
252     for (i = 0; i < nvar; i++)
253       p = stpcpy (p, var[i]->name) + 1;
254   }
255 }
256
257 /* Reads the FLIP stream and passes it to write_case(). */
258 static void
259 flip_stream_read (void)
260 {
261   if (src || (src == NULL && src_file == NULL))
262     {
263       /* Internal transposition, or empty file. */
264       int i, j;
265       char *p = src_old_names;
266       
267       for (i = 0; i < nvar; i++)
268         {
269           struct flip_case *iter;
270           
271           st_bare_pad_copy (temp_case->data[0].s, p, 8);
272           p = strchr (p, 0) + 1;
273
274           for (iter = src, j = 1; iter; iter = iter->next, j++)
275             temp_case->data[j].f = iter->v[i];
276
277           if (!write_case ())
278             return;
279         }
280     }
281   else
282     {
283       int i;
284       char *p = src_old_names;
285       
286       for (i = 0; i < nvar; i++)
287         {
288           st_bare_pad_copy (temp_case->data[0].s, p, 8);
289           p = strchr (p, 0) + 1;
290
291           if (fread (&temp_case->data[1], sizeof (double), src_cases,
292                      src_file) != src_cases)
293             msg (FE, _("Error reading FLIP source file: %s."),
294                  strerror (errno));
295
296           if (!write_case ())
297             return;
298         }
299     }
300 }
301
302 /* Writes temp_case to the FLIP stream. */
303 static void
304 flip_stream_write (void)
305 {
306   sink_cases++;
307
308   if (newnames)
309     {
310       struct varname *v;
311       char name[INT_DIGITS + 2];
312
313       if (newnames->type == NUMERIC)
314         sprintf (name, "V%d", (int) temp_case->data[newnames->fv].f);
315       else
316         {
317           int width = min (newnames->width, 8);
318           memcpy (name, temp_case->data[newnames->fv].s, width);
319           name[width] = 0;
320         }
321
322       v = xmalloc (sizeof (struct varname) + strlen (name) - 1);
323       strcpy (v->name, name);
324       
325       if (new_names_tail == NULL)
326         new_names_head = v;
327       else
328         new_names_tail->next = v;
329       new_names_tail = v;
330     }
331   else
332     case_count++;
333
334   if (internal)
335     {
336 #if 0
337       flip_case *c = malloc (sizeof (flip_case)
338                              + sizeof (double) * (nvar - 1));
339       
340       if (c != NULL)
341         {
342           /* Write to internal file. */
343           int i;
344
345           for (i = 0; i < nvar; i++)
346             if (var[i]->type == NUMERIC)
347               c->v[i] = temp_case->data[var[i]->fv].f;
348             else
349               c->v[i] = SYSMIS;
350
351           if (tail == NULL)
352             head = c;
353           else
354             tail->next = c;
355           tail = c;
356           
357           return;
358         }
359       else
360 #endif
361         {
362           /* Initialize external file. */
363           struct flip_case *iter, *next;
364
365           internal = 0;
366
367           sink_file = tmpfile ();
368           if (!sink_file)
369             msg (FE, _("Could not create temporary file for FLIP."));
370
371           if (tail)
372             tail->next = NULL;
373           for (iter = head; iter; iter = next)
374             {
375               next = iter->next;
376
377               if (fwrite (iter->v, sizeof (double), nvar, sink_file)
378                   != (size_t) nvar)
379                 msg (FE, _("Error writing FLIP file: %s."),
380                      strerror (errno));
381               free (iter);
382             }
383         }
384     }
385
386   /* Write to external file. */
387   {
388     double *d = local_alloc (sizeof *d * nvar);
389     int i;
390
391     for (i = 0; i < nvar; i++)
392       if (var[i]->type == NUMERIC)
393         d[i] = temp_case->data[var[i]->fv].f;
394       else
395         d[i] = SYSMIS;
396           
397     if (fwrite (d, sizeof *d, nvar, sink_file) != (size_t) nvar)
398       msg (FE, _("Error writing FLIP file: %s."),
399            strerror (errno));
400
401     local_free (d);
402   }
403 }
404       
405 /* Transpose the external file. */
406 static void
407 transpose_external_file (void)
408 {
409   unsigned long n_cases;
410   unsigned long cur_case;
411   double *case_buf, *temp_buf;
412
413   n_cases = 4 * 1024 * 1024 / ((nvar + 1) * sizeof *case_buf);
414   if (n_cases < 2)
415     n_cases = 2;
416   for (;;)
417     {
418       assert (n_cases >= 2 /* 1 */);
419       case_buf = ((n_cases <= 2 ? xmalloc : (void *(*)(size_t)) malloc)
420                   ((nvar + 1) * sizeof *case_buf * n_cases));
421       if (case_buf)
422         break;
423
424       n_cases /= 2;
425       if (n_cases < 2)
426         n_cases = 2;
427     }
428
429   /* A temporary buffer that holds n_cases elements. */
430   temp_buf = &case_buf[nvar * n_cases];
431
432   src_file = tmpfile ();
433   if (!src_file)
434     msg (FE, _("Error creating FLIP source file."));
435   
436   if (fseek (sink_file, 0, SEEK_SET) != 0)
437     msg (FE, _("Error rewinding FLIP file: %s."), strerror (errno));
438
439   for (cur_case = 0; cur_case < sink_cases; )
440     {
441       unsigned long read_cases = min (sink_cases - cur_case, n_cases);
442       int i;
443
444       if (read_cases != fread (case_buf, sizeof *case_buf * nvar,
445                                read_cases, sink_file))
446         msg (FE, _("Error reading FLIP file: %s."), strerror (errno));
447
448       for (i = 0; i < nvar; i++)
449         {
450           unsigned long j;
451           
452           for (j = 0; j < read_cases; j++)
453             temp_buf[j] = case_buf[i + j * nvar];
454
455           if (fseek (src_file,
456                      sizeof *case_buf * (cur_case + i * sink_cases),
457                      SEEK_SET) != 0)
458             msg (FE, _("Error seeking FLIP source file: %s."),
459                        strerror (errno));
460
461           if (fwrite (temp_buf, sizeof *case_buf, read_cases, src_file)
462               != read_cases)
463             msg (FE, _("Error writing FLIP source file: %s."),
464                  strerror (errno));
465         }
466
467       cur_case += read_cases;
468     }
469
470   if (fseek (src_file, 0, SEEK_SET) != 0)
471     msg (FE, _("Error rewind FLIP source file: %s."), strerror (errno));
472
473   fclose (sink_file);
474
475   free (case_buf);
476 }
477
478 /* Change the FLIP stream from sink to source mode. */
479 static void
480 flip_stream_mode (void)
481 {
482   src_cases = sink_cases;
483   src_old_names = sink_old_names;
484   sink_old_names = NULL;
485   
486   if (internal)
487     {
488       if (tail)
489         {
490           tail->next = NULL;
491           src = head;
492         }
493       else
494         {
495           src = NULL;
496           src_file = NULL;
497         }
498     }
499   else
500     {
501       src = NULL;
502       transpose_external_file ();
503     }
504 }
505
506 /* Destroy source's internal data. */
507 static void
508 flip_stream_destroy_source (void)
509 {
510   free (src_old_names);
511   if (internal)
512     {
513       struct flip_case *iter, *next;
514
515       for (iter = src; iter; iter = next)
516         {
517           next = iter->next;
518           free (iter);
519         }
520     }
521   else
522     fclose (src_file);
523 }
524
525 /* Destroy sink's internal data. */
526 static void
527 flip_stream_destroy_sink (void)
528 {
529   struct flip_case *iter, *next;
530   
531   free (sink_old_names);
532   if (tail == NULL)
533     return;
534
535   tail->next = NULL;
536   for (iter = head; iter; iter = next)
537     {
538       next = iter->next;
539       free (iter);
540     }
541 }
542
543 struct case_stream flip_stream = 
544   {
545     flip_stream_init,
546     flip_stream_read,
547     flip_stream_write,
548     flip_stream_mode,
549     flip_stream_destroy_source,
550     flip_stream_destroy_sink,
551     "FLIP",
552   };