1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007, 2009 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 struct ccase *(*translate) (struct ccase *input, 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 return the translated case, with OUTPUT_VALUE_CNT values,
45 and populate it based on INPUT and auxiliary data AUX.
46 TRANSLATE must destroy its input case.
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 struct ccase *(*translate) (struct ccase *input,
59 bool (*destroy) (void *aux),
62 struct casereader_translator *ct = xmalloc (sizeof *ct);
63 struct casereader *reader;
64 ct->subreader = casereader_rename (subreader);
65 ct->translate = translate;
66 ct->destroy = destroy;
68 reader = casereader_create_sequential (
69 NULL, output_value_cnt, casereader_get_case_cnt (ct->subreader),
70 &casereader_translator_class, ct);
71 taint_propagate (casereader_get_taint (ct->subreader),
72 casereader_get_taint (reader));
76 /* Internal read function for translating casereader. */
78 casereader_translator_read (struct casereader *reader UNUSED,
81 struct casereader_translator *ct = ct_;
82 struct ccase *tmp = casereader_read (ct->subreader);
84 tmp = ct->translate (tmp, ct->aux);
88 /* Internal destroy function for translating casereader. */
90 casereader_translator_destroy (struct casereader *reader UNUSED, void *ct_)
92 struct casereader_translator *ct = ct_;
93 casereader_destroy (ct->subreader);
94 ct->destroy (ct->aux);
98 /* Casereader class for translating casereader. */
99 static const struct casereader_class casereader_translator_class =
101 casereader_translator_read,
102 casereader_translator_destroy,
109 struct casereader_append_numeric
113 new_value_func *func;
115 void (*destroy) (void *aux);
118 static bool can_destroy (void *can_);
120 static struct ccase *can_translate (struct ccase *, void *can_);
122 /* Creates and returns a new casereader whose cases are produced
123 by reading from SUBREADER and appending an additional value,
124 generated by FUNC. AUX is an optional parameter which
125 gets passed to FUNC. FUNC will also receive N as it, which is
126 the ordinal number of the case in the reader. DESTROY is an
127 optional parameter used to destroy AUX.
129 After this function is called, SUBREADER must not ever again
130 be referenced directly. It will be destroyed automatically
131 when the translating casereader is destroyed. */
133 casereader_create_append_numeric (struct casereader *subreader,
134 new_value_func func, void *aux,
135 void (*destroy) (void *aux))
137 struct casereader_append_numeric *can = xmalloc (sizeof *can);
138 can->value_ofs = casereader_get_value_cnt (subreader);
142 can->destroy = destroy;
143 return casereader_create_translator (subreader, can->value_ofs + 1,
144 can_translate, can_destroy, can);
148 static struct ccase *
149 can_translate (struct ccase *c, void *can_)
151 struct casereader_append_numeric *can = can_;
152 double new_value = can->func (c, can->n++, can->aux);
153 c = case_unshare_and_resize (c, can->value_ofs + 1);
154 case_data_rw_idx (c, can->value_ofs)->f = new_value;
159 can_destroy (void *can_)
161 struct casereader_append_numeric *can = can_;
163 can->destroy (can->aux);
170 struct arithmetic_sequence
177 next_arithmetic (const struct ccase *c UNUSED,
181 struct arithmetic_sequence *as = aux;
182 return n * as->increment + as->first;
185 /* Creates and returns a new casereader whose cases are produced
186 by reading from SUBREADER and appending an additional value,
187 which takes the value FIRST in the first case, FIRST +
188 INCREMENT in the second case, FIRST + INCREMENT * 2 in the
189 third case, and so on.
191 After this function is called, SUBREADER must not ever again
192 be referenced directly. It will be destroyed automatically
193 when the translating casereader is destroyed. */
195 casereader_create_arithmetic_sequence (struct casereader *subreader,
196 double first, double increment)
198 struct arithmetic_sequence *as = xzalloc (sizeof *as);
200 as->increment = increment;
201 return casereader_create_append_numeric (subreader, next_arithmetic,
208 struct casereader_append_rank
210 struct casereader *clone;
212 const struct variable *var;
213 const struct variable *weight;
218 distinct_func *distinct;
220 enum rank_error *err;
224 static bool car_destroy (void *car_);
226 static struct ccase *car_translate (struct ccase *input, void *car_);
228 /* Creates and returns a new casereader whose cases are produced
229 by reading from SUBREADER and appending an additional value,
230 which is the rank of the observation. W is the weight variable
231 of the dictionary containing V, or NULL if there is no weight
234 The following preconditions must be met:
236 1. SUBREADER must be sorted on V.
238 2. The weight variables, must be non-negative.
240 If either of these preconditions are not satisfied, then the rank
241 variables may not be correct. In this case, if ERR is non-null,
242 it will be set according to the erroneous conditions encountered.
244 If DISTINCT_CALLBACK is non-null, then it will be called exactly
245 once for every case containing a distinct value of V. AUX is
246 an auxilliary pointer passed to DISTINCT_CALLBACK.
248 After this function is called, SUBREADER must not ever again
249 be referenced directly. It will be destroyed automatically
250 when the translating casereader is destroyed. */
252 casereader_create_append_rank (struct casereader *subreader,
253 const struct variable *v,
254 const struct variable *w,
255 enum rank_error *err,
256 distinct_func *distinct_callback,
260 struct casereader_append_rank *car = xmalloc (sizeof *car);
261 car->value_ofs = casereader_get_value_cnt (subreader);
267 car->clone = casereader_clone (subreader);
268 car->distinct = distinct_callback;
271 car->prev_value = SYSMIS;
273 return casereader_create_translator (subreader, car->value_ofs + 1,
274 car_translate, car_destroy, car);
279 car_destroy (void *car_)
281 struct casereader_append_rank *car = car_;
282 casereader_destroy (car->clone);
287 static struct ccase *
288 car_translate (struct ccase *input, void *car_)
290 struct casereader_append_rank *car = car_;
292 const double value = case_data (input, car->var)->f;
294 if ( car->prev_value != SYSMIS)
296 if (car->err && value < car->prev_value)
297 *car->err |= RANK_ERR_UNSORTED;
300 if ( car->n_common == 1)
307 weight = case_data (input, car->weight)->f;
308 if ( car->err && weight < 0 )
309 *car->err |= RANK_ERR_NEGATIVE_WEIGHT;
314 struct ccase *c = casereader_peek (car->clone, car->n + ++k);
317 vxx = case_data (c, car->var)->f;
323 double w = case_data (c, car->weight)->f;
325 if ( car->err && w < 0 )
326 *car->err |= RANK_ERR_NEGATIVE_WEIGHT;
336 while (vxx == value);
337 car->mean_rank = car->cc + (weight + 1) / 2.0;
341 car->distinct (value, car->n_common, weight, car->aux);
348 input = case_unshare_and_resize (input, car->value_ofs + 1);
349 case_data_rw_idx (input, car->value_ofs)->f = car->mean_rank ;
350 car->prev_value = value;