1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007 Free Software Foundation, Inc.
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.
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.
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/>. */
18 #include <data/val-type.h>
19 #include <data/casereader.h>
22 #include <data/casereader-provider.h>
23 #include <libpspp/taint.h>
27 /* Casereader that applies a user-supplied function to translate
28 each case into another in an arbitrary fashion. */
30 /* A translating casereader. */
31 struct casereader_translator
33 struct casereader *subreader; /* Source of input cases. */
35 void (*translate) (struct ccase *input, struct ccase *output, void *aux);
36 bool (*destroy) (void *aux);
40 static const struct casereader_class casereader_translator_class;
42 /* Creates and returns a new casereader whose cases are produced
43 by reading from SUBREADER and passing through TRANSLATE, which
44 must create case OUTPUT, with OUTPUT_VALUE_CNT values, and
45 populate it based on INPUT and auxiliary data AUX. TRANSLATE
46 must also destroy INPUT.
48 When the translating casereader is destroyed, DESTROY will be
49 called to allow any state maintained by TRANSLATE to be freed.
51 After this function is called, SUBREADER must not ever again
52 be referenced directly. It will be destroyed automatically
53 when the translating casereader is destroyed. */
55 casereader_create_translator (struct casereader *subreader,
56 size_t output_value_cnt,
57 void (*translate) (struct ccase *input,
60 bool (*destroy) (void *aux),
63 struct casereader_translator *ct = xmalloc (sizeof *ct);
64 struct casereader *reader;
65 ct->subreader = casereader_rename (subreader);
66 ct->translate = translate;
67 ct->destroy = destroy;
69 reader = casereader_create_sequential (
70 NULL, output_value_cnt, casereader_get_case_cnt (ct->subreader),
71 &casereader_translator_class, ct);
72 taint_propagate (casereader_get_taint (ct->subreader),
73 casereader_get_taint (reader));
77 /* Internal read function for translating casereader. */
79 casereader_translator_read (struct casereader *reader UNUSED,
80 void *ct_, struct ccase *c)
82 struct casereader_translator *ct = ct_;
85 if (casereader_read (ct->subreader, &tmp))
87 ct->translate (&tmp, c, ct->aux);
94 /* Internal destroy function for translating casereader. */
96 casereader_translator_destroy (struct casereader *reader UNUSED, void *ct_)
98 struct casereader_translator *ct = ct_;
99 casereader_destroy (ct->subreader);
100 ct->destroy (ct->aux);
104 /* Casereader class for translating casereader. */
105 static const struct casereader_class casereader_translator_class =
107 casereader_translator_read,
108 casereader_translator_destroy,
115 struct casereader_append_numeric
119 new_value_func *func;
121 void (*destroy) (void *aux);
124 static bool can_destroy (void *can_);
126 static void can_translate (struct ccase *input, struct ccase *output,
129 /* Creates and returns a new casereader whose cases are produced
130 by reading from SUBREADER and appending an additional value,
131 generated by FUNC. AUX is an optional parameter which
132 gets passed to FUNC. FUNC will also receive N as it, which is
133 the ordinal number of the case in the reader. DESTROY is an
134 optional parameter used to destroy AUX.
136 After this function is called, SUBREADER must not ever again
137 be referenced directly. It will be destroyed automatically
138 when the translating casereader is destroyed. */
140 casereader_create_append_numeric (struct casereader *subreader,
141 new_value_func func, void *aux,
142 void (*destroy) (void *aux))
144 struct casereader_append_numeric *can = xmalloc (sizeof *can);
145 can->value_ofs = casereader_get_value_cnt (subreader);
149 can->destroy = destroy;
150 return casereader_create_translator (subreader, can->value_ofs + 1,
151 can_translate, can_destroy, can);
156 can_translate (struct ccase *input, struct ccase *output, void *can_)
158 struct casereader_append_numeric *can = can_;
159 double new_value = can->func (input, can->n++, can->aux);
160 case_nullify (output);
161 case_move (output, input);
162 case_resize (output, can->value_ofs + 1);
163 case_data_rw_idx (output, can->value_ofs)->f = new_value;
167 can_destroy (void *can_)
169 struct casereader_append_numeric *can = can_;
171 can->destroy (can->aux);
178 struct arithmetic_sequence
185 next_arithmetic (const struct ccase *c UNUSED,
189 struct arithmetic_sequence *as = aux;
190 return n * as->increment + as->first;
193 /* Creates and returns a new casereader whose cases are produced
194 by reading from SUBREADER and appending an additional value,
195 which takes the value FIRST in the first case, FIRST +
196 INCREMENT in the second case, FIRST + INCREMENT * 2 in the
197 third case, and so on.
199 After this function is called, SUBREADER must not ever again
200 be referenced directly. It will be destroyed automatically
201 when the translating casereader is destroyed. */
203 casereader_create_arithmetic_sequence (struct casereader *subreader,
204 double first, double increment)
206 struct arithmetic_sequence *as = xzalloc (sizeof *as);
208 as->increment = increment;
209 return casereader_create_append_numeric (subreader, next_arithmetic,
216 struct casereader_append_rank
218 struct casereader *clone;
220 const struct variable *var;
221 const struct variable *weight;
226 distinct_func *distinct;
228 enum rank_error *err;
232 static bool car_destroy (void *car_);
234 static void car_translate (struct ccase *input, struct ccase *output,
237 /* Creates and returns a new casereader whose cases are produced
238 by reading from SUBREADER and appending an additional value,
239 which is the rank of the observation. W is the weight variable
240 of the dictionary containing V, or NULL if there is no weight
243 The following preconditions must be met:
245 1. SUBREADER must be sorted on V.
247 2. The weight variables, must be non-negative.
249 If either of these preconditions are not satisfied, then the rank
250 variables may not be correct. In this case, if ERR is non-null,
251 it will be set according to the erroneous conditions encountered.
253 If DISTINCT_CALLBACK is non-null, then it will be called exactly
254 once for every case containing a distinct value of V. AUX is
255 an auxilliary pointer passed to DISTINCT_CALLBACK.
257 After this function is called, SUBREADER must not ever again
258 be referenced directly. It will be destroyed automatically
259 when the translating casereader is destroyed. */
261 casereader_create_append_rank (struct casereader *subreader,
262 const struct variable *v,
263 const struct variable *w,
264 enum rank_error *err,
265 distinct_func *distinct_callback,
269 struct casereader_append_rank *car = xmalloc (sizeof *car);
270 car->value_ofs = casereader_get_value_cnt (subreader);
276 car->clone = casereader_clone (subreader);
277 car->distinct = distinct_callback;
280 car->prev_value = SYSMIS;
282 return casereader_create_translator (subreader, car->value_ofs + 1,
283 car_translate, car_destroy, car);
288 car_destroy (void *car_)
290 struct casereader_append_rank *car = car_;
291 casereader_destroy (car->clone);
298 car_translate (struct ccase *input, struct ccase *output, void *car_)
300 struct casereader_append_rank *car = car_;
302 const double value = case_data (input, car->var)->f;
304 if ( car->prev_value != SYSMIS)
306 if (car->err && value < car->prev_value)
307 *car->err |= RANK_ERR_UNSORTED;
310 if ( car->n_common == 1)
317 weight = case_data (input, car->weight)->f;
318 if ( car->err && weight < 0 )
319 *car->err |= RANK_ERR_NEGATIVE_WEIGHT;
325 if ( ! casereader_peek (car->clone, car->n + ++k, &c))
327 vxx = case_data (&c, car->var)->f;
333 double w = case_data (&c, car->weight)->f;
335 if ( car->err && w < 0 )
336 *car->err |= RANK_ERR_NEGATIVE_WEIGHT;
346 while (vxx == value);
347 car->mean_rank = car->cc + (weight + 1) / 2.0;
351 car->distinct (value, car->n_common, weight, car->aux);
358 case_nullify (output);
359 case_move (output, input);
360 case_resize (output, car->value_ofs + 1);
361 case_data_rw_idx (output, car->value_ofs)->f = car->mean_rank ;
362 car->prev_value = value;