1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2008, 2009, 2010 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/subcase.h>
20 #include <data/case.h>
21 #include <data/variable.h>
22 #include <libpspp/assertion.h>
26 static void invalidate_proto (struct subcase *sc);
28 /* Initializes SC as a subcase that contains no fields. */
30 subcase_init_empty (struct subcase *sc)
37 /* Initializes SC as a subcase with fields extracted from the
38 N_VARS variables in VARS, with ascending sort order. */
40 subcase_init_vars (struct subcase *sc,
41 const struct variable *const *vars, size_t n_vars)
45 sc->fields = xnmalloc (n_vars, sizeof *sc->fields);
46 sc->n_fields = n_vars;
48 for (i = 0; i < n_vars; i++)
50 struct subcase_field *field = &sc->fields[i];
51 field->case_index = var_get_case_index (vars[i]);
52 field->width = var_get_width (vars[i]);
53 field->direction = SC_ASCEND;
57 /* Initializes SC as a subcase with a single field extracted
58 from VAR, with the sort order specified by DIRECTION. */
60 subcase_init_var (struct subcase *sc, const struct variable *var,
61 enum subcase_direction direction)
63 subcase_init_empty (sc);
64 subcase_add_var (sc, var, direction);
69 subcase_init (struct subcase *sc, int index, int width,
70 enum subcase_direction direction)
72 subcase_init_empty (sc);
73 subcase_add (sc, index, width, direction);
77 /* Removes all the fields from SC. */
79 subcase_clear (struct subcase *sc)
82 invalidate_proto (sc);
85 /* Initializes SC with the same fields as ORIG. */
87 subcase_clone (struct subcase *sc, const struct subcase *orig)
89 sc->fields = xmemdup (orig->fields, orig->n_fields * sizeof *orig->fields);
90 sc->n_fields = orig->n_fields;
91 sc->proto = orig->proto ? caseproto_ref (orig->proto) : NULL;
94 /* Frees the memory owned by SC (but not SC itself). */
96 subcase_destroy (struct subcase *sc)
99 caseproto_unref (sc->proto);
102 /* Returns true if VAR already has a field in SC,
105 subcase_contains_var (const struct subcase *sc, const struct variable *var)
107 return subcase_contains (sc, var_get_case_index (var));
110 /* Returns true if CASE_INDEX already has a field in SC,
113 subcase_contains (const struct subcase *sc, int case_index)
117 for (i = 0; i < sc->n_fields; i++)
118 if (sc->fields[i].case_index == case_index)
124 /* Add a field for VAR to SC, with DIRECTION as the sort order.
125 Returns true if successful, false if VAR already has a field
128 subcase_add_var (struct subcase *sc, const struct variable *var,
129 enum subcase_direction direction)
131 if (!subcase_contains_var (sc, var))
133 subcase_add_var_always (sc, var, direction);
140 /* Add a field for CASE_INDEX, WIDTH to SC, with DIRECTION as the sort order.
141 Returns true if successful, false if CASE_INDEX already has a field
144 subcase_add (struct subcase *sc, int case_index, int width,
145 enum subcase_direction direction)
147 if (!subcase_contains (sc, case_index))
149 subcase_add_always (sc, case_index, width, direction);
156 /* Add a field for VAR to SC, with DIRECTION as the sort order,
157 regardless of whether VAR already has a field in SC. */
159 subcase_add_var_always (struct subcase *sc, const struct variable *var,
160 enum subcase_direction direction)
162 return subcase_add_always (sc, var_get_case_index (var),
163 var_get_width (var), direction);
167 subcase_add_vars_always (struct subcase *sc,
168 const struct variable *const *vars, size_t n_vars,
169 enum subcase_direction direction)
173 for (i = 0; i < n_vars; i++)
174 subcase_add_var_always (sc, vars[i], direction);
177 /* Add a field for CASE_INDEX, WIDTH to SC, with DIRECTION as the
178 sort order, regardless of whether CASE_INDEX already has a
181 subcase_add_always (struct subcase *sc, int case_index, int width,
182 enum subcase_direction direction)
184 struct subcase_field *field;
186 sc->fields = xnrealloc (sc->fields, sc->n_fields + 1, sizeof *sc->fields);
187 field = &sc->fields[sc->n_fields++];
188 field->case_index = case_index;
189 field->width = width;
190 field->direction = direction;
191 invalidate_proto (sc);
194 /* Adds a field to SC for each column in PROTO, so that SC
195 contains all of the columns in PROTO in the same order as a
196 case conforming to PROTO. The fields are added with
197 ascending direction. */
199 subcase_add_proto_always (struct subcase *sc, const struct caseproto *proto)
201 size_t n = caseproto_get_n_widths (proto);
204 sc->fields = xnrealloc (sc->fields, sc->n_fields + n, sizeof *sc->fields);
205 for (i = 0; i < n; i++)
207 struct subcase_field *field = &sc->fields[sc->n_fields++];
208 field->case_index = i;
209 field->width = caseproto_get_width (proto, i);
210 field->direction = SC_ASCEND;
212 invalidate_proto (sc);
216 subcase_concat (struct subcase *sc, const struct subcase *other)
220 for (i = 0; i < other->n_fields; i++)
222 const struct subcase_field *f = &other->fields[i];
223 subcase_add (sc, f->case_index, f->width, f->direction);
228 subcase_concat_always (struct subcase *sc, const struct subcase *other)
232 for (i = 0; i < other->n_fields; i++)
234 const struct subcase_field *f = &other->fields[i];
235 subcase_add_always (sc, f->case_index, f->width, f->direction);
240 subcase_project (struct subcase *sc, size_t offset)
244 for (i = 0; i < sc->n_fields; i++)
245 sc->fields[i].case_index = i + offset;
248 /* Obtains a caseproto for a case described by SC. The caller
249 must not modify or unref the returned case prototype. */
250 const struct caseproto *
251 subcase_get_proto (const struct subcase *sc_)
253 struct subcase *sc = CONST_CAST (struct subcase *, sc_);
255 if (sc->proto == NULL)
259 sc->proto = caseproto_create ();
260 for (i = 0; i < sc->n_fields; i++)
261 sc->proto = caseproto_add_width (sc->proto, sc->fields[i].width);
266 /* Returns true if and only if A and B are conformable, which
267 means that they have the same number of fields and that each
268 corresponding field in A and B have the same width. */
270 subcase_conformable (const struct subcase *a, const struct subcase *b)
276 if (a->n_fields != b->n_fields)
278 for (i = 0; i < a->n_fields; i++)
279 if (a->fields[i].width != b->fields[i].width)
284 /* Copies the fields represented by SC from C into VALUES.
285 VALUES must have space for at least subcase_get_n_fields(SC)
288 subcase_extract (const struct subcase *sc, const struct ccase *c,
289 union value values[])
293 for (i = 0; i < sc->n_fields; i++)
295 const struct subcase_field *field = &sc->fields[i];
296 union value *value = &values[i];
297 value_copy (value, case_data_idx (c, field->case_index), field->width);
301 /* Copies the data in VALUES into the fields in C represented by
302 SC. VALUES must have at least subcase_get_n_fields(SC) array
303 elements, and C must be large enough to contain all the fields
306 subcase_inject (const struct subcase *sc,
307 const union value values[], struct ccase *c)
311 for (i = 0; i < sc->n_fields; i++)
313 const struct subcase_field *field = &sc->fields[i];
314 const union value *value = &values[i];
315 value_copy (case_data_rw_idx (c, field->case_index), value,
320 /* Copies the fields in SRC represented by SRC_SC into the
321 corresponding fields in DST respresented by DST_SC. SRC_SC
322 and DST_SC must be conformable (as tested by
323 subcase_conformable()).
325 DST must not be shared. */
327 subcase_copy (const struct subcase *src_sc, const struct ccase *src,
328 const struct subcase *dst_sc, struct ccase *dst)
332 expensive_assert (subcase_conformable (src_sc, dst_sc));
333 for (i = 0; i < src_sc->n_fields; i++)
335 const struct subcase_field *src_field = &src_sc->fields[i];
336 const struct subcase_field *dst_field = &dst_sc->fields[i];
337 value_copy (case_data_rw_idx (dst, dst_field->case_index),
338 case_data_idx (src, src_field->case_index),
343 /* Compares the fields in A specified in A_SC against the fields
344 in B specified in B_SC. Returns -1, 0, or 1 if A's fields are
345 lexicographically less than, equal to, or greater than B's
346 fields, respectively.
348 A_SC and B_SC must be conformable (as tested by
349 subcase_conformable()). */
351 subcase_compare_3way (const struct subcase *a_sc, const struct ccase *a,
352 const struct subcase *b_sc, const struct ccase *b)
356 expensive_assert (subcase_conformable (a_sc, b_sc));
357 for (i = 0; i < a_sc->n_fields; i++)
359 const struct subcase_field *a_field = &a_sc->fields[i];
360 const struct subcase_field *b_field = &b_sc->fields[i];
361 int cmp = value_compare_3way (case_data_idx (a, a_field->case_index),
362 case_data_idx (b, b_field->case_index),
365 return a_field->direction == SC_ASCEND ? cmp : -cmp;
370 /* Compares the values in A against the values in B specified by
371 SC's fields. Returns -1, 0, or 1 if A's values are
372 lexicographically less than, equal to, or greater than B's
373 values, respectively. */
375 subcase_compare_3way_xc (const struct subcase *sc,
376 const union value a[], const struct ccase *b)
380 for (i = 0; i < sc->n_fields; i++)
382 const struct subcase_field *field = &sc->fields[i];
383 int cmp = value_compare_3way (&a[i],
384 case_data_idx (b, field->case_index),
387 return field->direction == SC_ASCEND ? cmp : -cmp;
392 /* Compares the values in A specified by SC's fields against the
393 values in B. Returns -1, 0, or 1 if A's values are
394 lexicographically less than, equal to, or greater than B's
395 values, respectively. */
397 subcase_compare_3way_cx (const struct subcase *sc,
398 const struct ccase *a, const union value b[])
400 return -subcase_compare_3way_xc (sc, b, a);
403 /* Compares the values in A against the values in B, using SC to
404 obtain the number and width of each value. Returns -1, 0, or
405 1 if A's values are lexicographically less than, equal to, or
406 greater than B's values, respectively. */
408 subcase_compare_3way_xx (const struct subcase *sc,
409 const union value a[], const union value b[])
413 for (i = 0; i < sc->n_fields; i++)
415 const struct subcase_field *field = &sc->fields[i];
416 int cmp = value_compare_3way (a++, b++, field->width);
418 return field->direction == SC_ASCEND ? cmp : -cmp;
423 /* Compares the fields in A specified in A_SC against the fields
424 in B specified in B_SC. Returns true if the fields' values
425 are equal, false otherwise.
427 A_SC and B_SC must be conformable (as tested by
428 subcase_conformable()). */
430 subcase_equal (const struct subcase *a_sc, const struct ccase *a,
431 const struct subcase *b_sc, const struct ccase *b)
433 return subcase_compare_3way (a_sc, a, b_sc, b) == 0;
436 /* Compares the values in A against the values in B specified by
437 SC's fields. Returns true if A's values are equal to B's
438 values, otherwise false. */
440 subcase_equal_xc (const struct subcase *sc,
441 const union value a[], const struct ccase *b)
443 return subcase_compare_3way_xc (sc, a, b) == 0;
446 /* Compares the values in A specified by SC's fields against the
447 values in B. Returns true if A's values are equal to B's
448 values, otherwise false. */
450 subcase_equal_cx (const struct subcase *sc,
451 const struct ccase *a, const union value b[])
453 return subcase_compare_3way_cx (sc, a, b) == 0;
456 /* Compares the values in A against the values in B, using SC to
457 obtain the number and width of each value. Returns true if
458 A's values are equal to B's values, otherwise false. */
460 subcase_equal_xx (const struct subcase *sc,
461 const union value a[], const union value b[])
463 return subcase_compare_3way_xx (sc, a, b) == 0;
466 /* Discards SC's case prototype. (It will be recreated if needed
469 invalidate_proto (struct subcase *sc)
471 caseproto_unref (sc->proto);