f599fd78f0b399f65ce773d52e39ad5707cee335
[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 <limits.h>
26 #include <stdlib.h>
27 #include "alloc.h"
28 #include "command.h"
29 #include "error.h"
30 #include "lexer.h"
31 #include "misc.h"
32 #include "str.h"
33 #include "var.h"
34 #include "vfm.h"
35
36 /* Variables to transpose. */
37 static struct variable **var;
38 static int nvar;
39
40 /* Variable containing new variable names. */
41 static struct variable *newnames;
42
43 /* List of variable names. */
44 struct varname
45   {
46     struct varname *next;
47     char name[9];
48   };
49
50 /* New variable names. */
51 static struct varname *new_names_head, *new_names_tail;
52 static int case_count;
53
54 static int build_dictionary (void);
55
56 /* Parses and executes FLIP. */
57 int
58 cmd_flip (void)
59 {
60   lex_match_id ("FLIP");
61   lex_match ('/');
62   if (lex_match_id ("VARIABLES"))
63     {
64       lex_match ('=');
65       if (!parse_variables (default_dict, &var, &nvar, PV_NO_DUPLICATE))
66         return CMD_FAILURE;
67       lex_match ('/');
68     }
69   else
70     dict_get_vars (default_dict, &var, &nvar, 1u << DC_SYSTEM);
71
72   lex_match ('/');
73   if (lex_match_id ("NEWNAMES"))
74     {
75       lex_match ('=');
76       newnames = parse_variable ();
77       if (!newnames)
78         {
79           free (var);
80           return CMD_FAILURE;
81         }
82     }
83   else
84     newnames = dict_lookup_var (default_dict, "CASE_LBL");
85
86   if (newnames)
87     {
88       int i;
89       
90       for (i = 0; i < nvar; i++)
91         if (var[i] == newnames)
92           {
93             memmove (&var[i], &var[i + 1], sizeof *var * (nvar - i - 1));
94             nvar--;
95             break;
96           }
97     }
98
99   case_count = 0;
100   temp_trns = temporary = 0;
101   vfm_sink = &flip_stream;
102   new_names_tail = NULL;
103   procedure (NULL, NULL, NULL, NULL);
104
105   dict_clear (default_dict);
106   if (!build_dictionary ())
107     {
108       discard_variables ();
109       free (var);
110       return CMD_FAILURE;
111     }
112
113   free (var);
114   return lex_end_of_command ();
115 }
116
117 /* Make a new variable with base name NAME, which is bowdlerized and
118    mangled until acceptable, and returns success. */
119 static int
120 make_new_var (char name[])
121 {
122   /* Fix invalid characters. */
123   {
124     char *cp;
125   
126     for (cp = name; *cp && !isspace (*cp); cp++)
127       {
128         *cp = toupper ((unsigned char) *cp);
129         if (!isalpha (*cp) && *cp != '@' && *cp != '#'
130             && (cp == name || (*cp != '.' && *cp != '$' && *cp != '_'
131                                && !isdigit (*cp))))
132           {
133             if (cp == name)
134               *cp = 'V';        /* _ not valid in first position. */
135             else
136               *cp = '_';
137           }
138       }
139     *cp = 0;
140   }
141   
142   if (dict_create_var (default_dict, name, 0))
143     return 1;
144
145   /* Add numeric extensions until acceptable. */
146   {
147     int len = (int) strlen (name);
148     char n[9];
149     int i;
150
151     for (i = 1; i < 10000000; i++)
152       {
153         int ofs = min (7 - intlog10 (i), len);
154         memcpy (n, name, ofs);
155         sprintf (&n[ofs], "%d", i);
156
157         if (dict_create_var (default_dict, n, 0))
158           return 1;
159       }
160   }
161
162   msg (SE, _("Could not create acceptable variant for variable %s."), name);
163   return 0;
164 }
165
166 /* Make a new dictionary for all the new variable names. */
167 static int
168 build_dictionary (void)
169 {
170   dict_create_var_assert (default_dict, "CASE_LBL", 8);
171
172   if (!new_names_tail)
173     {
174       int i;
175       
176       if (case_count > 99999)
177         {
178           msg (SE, _("Cannot create more than 99999 variable names."));
179           return 0;
180         }
181       
182       for (i = 0; i < case_count; i++)
183         {
184           struct variable *v;
185           char s[9];
186
187           sprintf (s, "VAR%03d", i);
188           v = dict_create_var_assert (default_dict, s, 0);
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 (write_case_func *write_case, write_case_data wc_data)
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 (wc_data))
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 (wc_data))
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 = xmalloc (sizeof (struct varname));
311       if (newnames->type == NUMERIC) 
312         {
313           double f = temp_case->data[newnames->fv].f;
314
315           if (f == SYSMIS)
316             strcpy (v->name, "VSYSMIS");
317           else if (f < INT_MIN)
318             strcpy (v->name, "VNEGINF");
319           else if (f > INT_MAX)
320             strcpy (v->name, "VPOSINF");
321           else 
322             {
323               char name[INT_DIGITS + 2];
324               sprintf (name, "V%d", (int) f);
325               strncpy (v->name, name, 8);
326               name[8] = 0; 
327             }
328         }
329       else
330         {
331           int width = min (newnames->width, 8);
332           memcpy (v->name, temp_case->data[newnames->fv].s, width);
333           v->name[width] = 0;
334         }
335       
336       if (new_names_tail == NULL)
337         new_names_head = v;
338       else
339         new_names_tail->next = v;
340       new_names_tail = v;
341     }
342   else
343     case_count++;
344
345   if (internal)
346     {
347 #if 0
348       flip_case *c = malloc (sizeof (flip_case)
349                              + sizeof (double) * (nvar - 1));
350       
351       if (c != NULL)
352         {
353           /* Write to internal file. */
354           int i;
355
356           for (i = 0; i < nvar; i++)
357             if (var[i]->type == NUMERIC)
358               c->v[i] = temp_case->data[var[i]->fv].f;
359             else
360               c->v[i] = SYSMIS;
361
362           if (tail == NULL)
363             head = c;
364           else
365             tail->next = c;
366           tail = c;
367           
368           return;
369         }
370       else
371 #endif
372         {
373           /* Initialize external file. */
374           struct flip_case *iter, *next;
375
376           internal = 0;
377
378           sink_file = tmpfile ();
379           if (!sink_file)
380             msg (FE, _("Could not create temporary file for FLIP."));
381
382           if (tail)
383             tail->next = NULL;
384           for (iter = head; iter; iter = next)
385             {
386               next = iter->next;
387
388               if (fwrite (iter->v, sizeof (double), nvar, sink_file)
389                   != (size_t) nvar)
390                 msg (FE, _("Error writing FLIP file: %s."),
391                      strerror (errno));
392               free (iter);
393             }
394         }
395     }
396
397   /* Write to external file. */
398   {
399     double *d = local_alloc (sizeof *d * nvar);
400     int i;
401
402     for (i = 0; i < nvar; i++)
403       if (var[i]->type == NUMERIC)
404         d[i] = temp_case->data[var[i]->fv].f;
405       else
406         d[i] = SYSMIS;
407           
408     if (fwrite (d, sizeof *d, nvar, sink_file) != (size_t) nvar)
409       msg (FE, _("Error writing FLIP file: %s."),
410            strerror (errno));
411
412     local_free (d);
413   }
414 }
415       
416 /* Transpose the external file. */
417 static void
418 transpose_external_file (void)
419 {
420   unsigned long n_cases;
421   unsigned long cur_case;
422   double *case_buf, *temp_buf;
423
424   n_cases = 4 * 1024 * 1024 / ((nvar + 1) * sizeof *case_buf);
425   if (n_cases < 2)
426     n_cases = 2;
427   for (;;)
428     {
429       assert (n_cases >= 2 /* 1 */);
430       case_buf = ((n_cases <= 2 ? xmalloc : (void *(*)(size_t)) malloc)
431                   ((nvar + 1) * sizeof *case_buf * n_cases));
432       if (case_buf)
433         break;
434
435       n_cases /= 2;
436       if (n_cases < 2)
437         n_cases = 2;
438     }
439
440   /* A temporary buffer that holds n_cases elements. */
441   temp_buf = &case_buf[nvar * n_cases];
442
443   src_file = tmpfile ();
444   if (!src_file)
445     msg (FE, _("Error creating FLIP source file."));
446   
447   if (fseek (sink_file, 0, SEEK_SET) != 0)
448     msg (FE, _("Error rewinding FLIP file: %s."), strerror (errno));
449
450   for (cur_case = 0; cur_case < sink_cases; )
451     {
452       unsigned long read_cases = min (sink_cases - cur_case, n_cases);
453       int i;
454
455       if (read_cases != fread (case_buf, sizeof *case_buf * nvar,
456                                read_cases, sink_file))
457         msg (FE, _("Error reading FLIP file: %s."), strerror (errno));
458
459       for (i = 0; i < nvar; i++)
460         {
461           unsigned long j;
462           
463           for (j = 0; j < read_cases; j++)
464             temp_buf[j] = case_buf[i + j * nvar];
465
466           if (fseek (src_file,
467                      sizeof *case_buf * (cur_case + i * sink_cases),
468                      SEEK_SET) != 0)
469             msg (FE, _("Error seeking FLIP source file: %s."),
470                        strerror (errno));
471
472           if (fwrite (temp_buf, sizeof *case_buf, read_cases, src_file)
473               != read_cases)
474             msg (FE, _("Error writing FLIP source file: %s."),
475                  strerror (errno));
476         }
477
478       cur_case += read_cases;
479     }
480
481   if (fseek (src_file, 0, SEEK_SET) != 0)
482     msg (FE, _("Error rewind FLIP source file: %s."), strerror (errno));
483
484   fclose (sink_file);
485
486   free (case_buf);
487 }
488
489 /* Change the FLIP stream from sink to source mode. */
490 static void
491 flip_stream_mode (void)
492 {
493   src_cases = sink_cases;
494   src_old_names = sink_old_names;
495   sink_old_names = NULL;
496   
497   if (internal)
498     {
499       if (tail)
500         {
501           tail->next = NULL;
502           src = head;
503         }
504       else
505         {
506           src = NULL;
507           src_file = NULL;
508         }
509     }
510   else
511     {
512       src = NULL;
513       transpose_external_file ();
514     }
515 }
516
517 /* Destroy source's internal data. */
518 static void
519 flip_stream_destroy_source (void)
520 {
521   free (src_old_names);
522   if (internal)
523     {
524       struct flip_case *iter, *next;
525
526       for (iter = src; iter; iter = next)
527         {
528           next = iter->next;
529           free (iter);
530         }
531     }
532   else
533     fclose (src_file);
534 }
535
536 /* Destroy sink's internal data. */
537 static void
538 flip_stream_destroy_sink (void)
539 {
540   struct flip_case *iter, *next;
541   
542   free (sink_old_names);
543   if (tail == NULL)
544     return;
545
546   tail->next = NULL;
547   for (iter = head; iter; iter = next)
548     {
549       next = iter->next;
550       free (iter);
551     }
552 }
553
554 struct case_stream flip_stream = 
555   {
556     flip_stream_init,
557     flip_stream_read,
558     flip_stream_write,
559     flip_stream_mode,
560     flip_stream_destroy_source,
561     flip_stream_destroy_sink,
562     "FLIP",
563   };