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/>. */
19 #include <data/casegrouper.h>
23 #include <data/case-ordering.h>
24 #include <data/casereader.h>
25 #include <data/casewriter.h>
26 #include <data/dictionary.h>
27 #include <libpspp/taint.h>
34 struct casereader *reader; /* Source of input cases. */
35 struct taint *taint; /* Error status for casegrouper. */
37 /* Functions for grouping cases. */
38 bool (*same_group) (const struct ccase *, const struct ccase *, void *aux);
39 void (*destroy) (void *aux);
43 /* Creates and returns a new casegrouper that takes its input
44 from READER. SAME_GROUP is used to decide which cases are in
45 a group: it returns true if the pair of cases provided are in
46 the same group, false otherwise. DESTROY will be called when
47 the casegrouper is destroyed and should free any storage
50 SAME_GROUP may be a null pointer. If so, READER's entire
51 contents is considered to be a single group. */
53 casegrouper_create_func (struct casereader *reader,
54 bool (*same_group) (const struct ccase *,
57 void (*destroy) (void *aux),
60 struct casegrouper *grouper = xmalloc (sizeof *grouper);
61 grouper->reader = casereader_rename (reader);
62 grouper->taint = taint_clone (casereader_get_taint (grouper->reader));
63 grouper->same_group = same_group;
64 grouper->destroy = destroy;
69 /* Obtains the next group of cases from GROUPER. Returns true if
70 successful, false if no groups remain. If successful, *READER
71 is set to the casereader for the new group; otherwise, it is
74 casegrouper_get_next_group (struct casegrouper *grouper,
75 struct casereader **reader)
77 /* FIXME: we really shouldn't need a temporary casewriter for
78 the common case where we read an entire group's data before
79 going on to the next. */
80 if (grouper->same_group != NULL)
82 struct casewriter *writer;
83 struct ccase group_case, tmp;
85 if (!casereader_read (grouper->reader, &group_case))
91 writer = autopaging_writer_create (casereader_get_value_cnt (grouper->reader));
92 case_clone (&tmp, &group_case);
93 casewriter_write (writer, &tmp);
95 while (casereader_peek (grouper->reader, 0, &tmp)
96 && grouper->same_group (&group_case, &tmp, grouper->aux))
99 casereader_read (grouper->reader, &discard);
100 case_destroy (&discard);
101 casewriter_write (writer, &tmp);
104 case_destroy (&group_case);
106 *reader = casewriter_make_reader (writer);
111 if (grouper->reader != NULL)
113 if (!casereader_is_empty (grouper->reader))
115 *reader = grouper->reader;
116 grouper->reader = NULL;
121 casereader_destroy (grouper->reader);
122 grouper->reader = NULL;
134 /* Destroys GROUPER. Returns false if GROUPER's input casereader
135 or any state derived from it had become tainted, which means
136 that an I/O error or other serious error occurred in
137 processing data derived from GROUPER; otherwise, return true. */
139 casegrouper_destroy (struct casegrouper *grouper)
143 struct taint *taint = grouper->taint;
146 casereader_destroy (grouper->reader);
147 if (grouper->destroy != NULL)
148 grouper->destroy (grouper->aux);
151 ok = !taint_has_tainted_successor (taint);
152 taint_destroy (taint);
159 /* Casegrouper based on equal values of variables from case to
162 /* Casegrouper based on equal variables. */
163 struct casegrouper_vars
165 const struct variable **vars; /* Variables to compare. */
166 size_t var_cnt; /* Number of variables. */
169 static bool casegrouper_vars_same_group (const struct ccase *,
170 const struct ccase *,
172 static void casegrouper_vars_destroy (void *);
174 /* Creates and returns a casegrouper that reads data from READER
175 and breaks it into contiguous groups of cases that have equal
176 values for the VAR_CNT variables in VARS. If VAR_CNT is 0,
177 then all the cases will be put in a single group. */
179 casegrouper_create_vars (struct casereader *reader,
180 const struct variable *const *vars,
185 struct casegrouper_vars *cv = xmalloc (sizeof *cv);
186 cv->vars = xmemdup (vars, sizeof *vars * var_cnt);
187 cv->var_cnt = var_cnt;
188 return casegrouper_create_func (reader,
189 casegrouper_vars_same_group,
190 casegrouper_vars_destroy,
194 return casegrouper_create_func (reader, NULL, NULL, NULL);
197 /* Creates and returns a casegrouper that reads data from READER
198 and breaks it into contiguous groups of cases that have equal
199 values for the SPLIT FILE variables in DICT. If DICT has no
200 SPLIT FILE variables, then all the cases will be put into a
203 casegrouper_create_splits (struct casereader *reader,
204 const struct dictionary *dict)
206 return casegrouper_create_vars (reader,
207 dict_get_split_vars (dict),
208 dict_get_split_cnt (dict));
211 /* Creates and returns a casegrouper that reads data from READER
212 and breaks it into contiguous groups of cases that have equal
213 values for the variables used for sorting in CO. If CO is
214 empty (contains no sort keys), then all the cases will be put
215 into a single group. */
217 casegrouper_create_case_ordering (struct casereader *reader,
218 const struct case_ordering *co)
220 const struct variable **vars;
222 struct casegrouper *grouper;
224 case_ordering_get_vars (co, &vars, &var_cnt);
225 grouper = casegrouper_create_vars (reader, vars, var_cnt);
231 /* "same_group" function for an equal-variables casegrouper. */
233 casegrouper_vars_same_group (const struct ccase *a, const struct ccase *b,
236 struct casegrouper_vars *cv = cv_;
237 return case_compare (a, b, cv->vars, cv->var_cnt) == 0;
240 /* "destroy" for an equal-variables casegrouper. */
242 casegrouper_vars_destroy (void *cv_)
244 struct casegrouper_vars *cv = cv_;