New function for adding a numbering column to a casereader.
[pspp-builds.git] / src / data / casereader-translator.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <data/casereader.h>
20
21 #include <stdlib.h>
22
23 #include <data/casereader-provider.h>
24 #include <libpspp/taint.h>
25
26 #include "xalloc.h"
27
28 /* Casereader that applies a user-supplied function to translate
29    each case into another in an arbitrary fashion. */
30
31 /* A translating casereader. */
32 struct casereader_translator
33   {
34     struct casereader *subreader; /* Source of input cases. */
35
36     void (*translate) (struct ccase *input, struct ccase *output, void *aux);
37     bool (*destroy) (void *aux);
38     void *aux;
39   };
40
41 static const struct casereader_class casereader_translator_class;
42
43 /* Creates and returns a new casereader whose cases are produced
44    by reading from SUBREADER and passing through TRANSLATE, which
45    must create case OUTPUT, with OUTPUT_VALUE_CNT values, and
46    populate it based on INPUT and auxiliary data AUX.  TRANSLATE
47    must also destroy INPUT.
48
49    When the translating casereader is destroyed, DESTROY will be
50    called to allow any state maintained by TRANSLATE to be freed.
51
52    After this function is called, SUBREADER must not ever again
53    be referenced directly.  It will be destroyed automatically
54    when the translating casereader is destroyed. */
55 struct casereader *
56 casereader_create_translator (struct casereader *subreader,
57                               size_t output_value_cnt,
58                               void (*translate) (struct ccase *input,
59                                                  struct ccase *output,
60                                                  void *aux),
61                               bool (*destroy) (void *aux),
62                               void *aux)
63 {
64   struct casereader_translator *ct = xmalloc (sizeof *ct);
65   struct casereader *reader;
66   ct->subreader = casereader_rename (subreader);
67   ct->translate = translate;
68   ct->destroy = destroy;
69   ct->aux = aux;
70   reader = casereader_create_sequential (
71     NULL, output_value_cnt, casereader_get_case_cnt (ct->subreader),
72     &casereader_translator_class, ct);
73   taint_propagate (casereader_get_taint (ct->subreader),
74                    casereader_get_taint (reader));
75   return reader;
76 }
77
78 /* Internal read function for translating casereader. */
79 static bool
80 casereader_translator_read (struct casereader *reader UNUSED,
81                             void *ct_, struct ccase *c)
82 {
83   struct casereader_translator *ct = ct_;
84   struct ccase tmp;
85
86   if (casereader_read (ct->subreader, &tmp))
87     {
88       ct->translate (&tmp, c, ct->aux);
89       return true;
90     }
91   else
92     return false;
93 }
94
95 /* Internal destroy function for translating casereader. */
96 static void
97 casereader_translator_destroy (struct casereader *reader UNUSED, void *ct_)
98 {
99   struct casereader_translator *ct = ct_;
100   casereader_destroy (ct->subreader);
101   ct->destroy (ct->aux);
102   free (ct);
103 }
104
105 /* Casereader class for translating casereader. */
106 static const struct casereader_class casereader_translator_class =
107   {
108     casereader_translator_read,
109     casereader_translator_destroy,
110     NULL,
111     NULL,
112   };
113 \f
114 struct casereader_arithmetic_sequence 
115   {
116     int value_ofs;
117     double first;
118     double increment;
119     casenumber n;
120   };
121
122 static void cas_translate (struct ccase *input, struct ccase *output,
123                            void *aux);
124 static bool cas_destroy (void *aux);
125
126 /* Creates and returns a new casereader whose cases are produced
127    by reading from SUBREADER and appending an additional value,
128    which takes the value FIRST in the first case, FIRST +
129    INCREMENT in the second case, FIRST + INCREMENT * 2 in the
130    third case, and so on.
131
132    After this function is called, SUBREADER must not ever again
133    be referenced directly.  It will be destroyed automatically
134    when the translating casereader is destroyed. */
135 struct casereader *
136 casereader_create_arithmetic_sequence (struct casereader *subreader,
137                                        double first, double increment)
138 {
139   /* This could be implemented with a great deal more efficiency
140      and generality.  However, this implementation is easy. */
141   struct casereader_arithmetic_sequence *cas = xmalloc (sizeof *cas);
142   cas->value_ofs = casereader_get_value_cnt (subreader);
143   cas->first = first;
144   cas->increment = increment;
145   cas->n = 0;
146   return casereader_create_translator (subreader, cas->value_ofs + 1,
147                                        cas_translate, cas_destroy, cas);
148 }
149
150 static void
151 cas_translate (struct ccase *input, struct ccase *output, void *cas_)
152 {
153   struct casereader_arithmetic_sequence *cas = cas_;
154   case_nullify (output);
155   case_move (output, input);
156   case_resize (output, cas->value_ofs + 1);
157   case_data_rw_idx (output, cas->value_ofs)->f
158     = cas->first + cas->increment * cas->n++;
159 }
160
161 static bool
162 cas_destroy (void *cas_) 
163 {
164   struct casereader_arithmetic_sequence *cas = cas_;
165   free (cas);
166   return true;
167 }