1 /* PSPP - computes sample statistics.
2 Copyright (C) 2007 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 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, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 #include <data/casegrouper.h>
25 #include <data/case-ordering.h>
26 #include <data/casereader.h>
27 #include <data/casewriter.h>
28 #include <data/dictionary.h>
29 #include <libpspp/taint.h>
36 struct casereader *reader; /* Source of input cases. */
37 struct taint *taint; /* Error status for casegrouper. */
39 /* Functions for grouping cases. */
40 bool (*same_group) (const struct ccase *, const struct ccase *, void *aux);
41 void (*destroy) (void *aux);
45 /* Creates and returns a new casegrouper that takes its input
46 from READER. SAME_GROUP is used to decide which cases are in
47 a group: it returns true if the pair of cases provided are in
48 the same group, false otherwise. DESTROY will be called when
49 the casegrouper is destroyed and should free any storage
52 SAME_GROUP may be a null pointer. If so, READER's entire
53 contents is considered to be a single group. */
55 casegrouper_create_func (struct casereader *reader,
56 bool (*same_group) (const struct ccase *,
59 void (*destroy) (void *aux),
62 struct casegrouper *grouper = xmalloc (sizeof *grouper);
63 grouper->reader = casereader_rename (reader);
64 grouper->taint = taint_clone (casereader_get_taint (grouper->reader));
65 grouper->same_group = same_group;
66 grouper->destroy = destroy;
71 /* Obtains the next group of cases from GROUPER. Returns true if
72 successful, false if no groups remain. If successful, *READER
73 is set to the casereader for the new group; otherwise, it is
76 casegrouper_get_next_group (struct casegrouper *grouper,
77 struct casereader **reader)
79 /* FIXME: we really shouldn't need a temporary casewriter for
80 the common case where we read an entire group's data before
81 going on to the next. */
82 if (grouper->same_group != NULL)
84 struct casewriter *writer;
85 struct ccase group_case, tmp;
87 if (!casereader_read (grouper->reader, &group_case))
93 writer = autopaging_writer_create (casereader_get_value_cnt (grouper->reader));
94 case_clone (&tmp, &group_case);
95 casewriter_write (writer, &tmp);
97 while (casereader_peek (grouper->reader, 0, &tmp)
98 && grouper->same_group (&group_case, &tmp, grouper->aux))
100 struct ccase discard;
101 casereader_read (grouper->reader, &discard);
102 case_destroy (&discard);
103 casewriter_write (writer, &tmp);
106 case_destroy (&group_case);
108 *reader = casewriter_make_reader (writer);
113 if (grouper->reader != NULL)
115 *reader = grouper->reader;
116 grouper->reader = NULL;
127 /* Destroys GROUPER. Returns false if GROUPER's input casereader
128 or any state derived from it had become tainted, which means
129 that an I/O error or other serious error occurred in
130 processing data derived from GROUPER; otherwise, return true. */
132 casegrouper_destroy (struct casegrouper *grouper)
136 struct taint *taint = grouper->taint;
139 casereader_destroy (grouper->reader);
140 if (grouper->destroy != NULL)
141 grouper->destroy (grouper->aux);
144 ok = !taint_has_tainted_successor (taint);
145 taint_destroy (taint);
152 /* Casegrouper based on equal values of variables from case to
155 /* Casegrouper based on equal variables. */
156 struct casegrouper_vars
158 const struct variable **vars; /* Variables to compare. */
159 size_t var_cnt; /* Number of variables. */
162 static bool casegrouper_vars_same_group (const struct ccase *,
163 const struct ccase *,
165 static void casegrouper_vars_destroy (void *);
167 /* Creates and returns a casegrouper that reads data from READER
168 and breaks it into contiguous groups of cases that have equal
169 values for the VAR_CNT variables in VARS. If VAR_CNT is 0,
170 then all the cases will be put in a single group. */
172 casegrouper_create_vars (struct casereader *reader,
173 const struct variable *const *vars,
178 struct casegrouper_vars *cv = xmalloc (sizeof *cv);
179 cv->vars = xmemdup (vars, sizeof *vars * var_cnt);
180 cv->var_cnt = var_cnt;
181 return casegrouper_create_func (reader,
182 casegrouper_vars_same_group,
183 casegrouper_vars_destroy,
187 return casegrouper_create_func (reader, NULL, NULL, NULL);
190 /* Creates and returns a casegrouper that reads data from READER
191 and breaks it into contiguous groups of cases that have equal
192 values for the SPLIT FILE variables in DICT. If DICT has no
193 SPLIT FILE variables, then all the cases will be put into a
196 casegrouper_create_splits (struct casereader *reader,
197 const struct dictionary *dict)
199 return casegrouper_create_vars (reader,
200 dict_get_split_vars (dict),
201 dict_get_split_cnt (dict));
204 /* Creates and returns a casegrouper that reads data from READER
205 and breaks it into contiguous groups of cases that have equal
206 values for the variables used for sorting in CO. If CO is
207 empty (contains no sort keys), then all the cases will be put
208 into a single group. */
210 casegrouper_create_case_ordering (struct casereader *reader,
211 const struct case_ordering *co)
213 const struct variable **vars;
215 struct casegrouper *grouper;
217 case_ordering_get_vars (co, &vars, &var_cnt);
218 grouper = casegrouper_create_vars (reader, vars, var_cnt);
224 /* "same_group" function for an equal-variables casegrouper. */
226 casegrouper_vars_same_group (const struct ccase *a, const struct ccase *b,
229 struct casegrouper_vars *cv = cv_;
230 return case_compare (a, b, cv->vars, cv->var_cnt) == 0;
233 /* "destroy" for an equal-variables casegrouper. */
235 casegrouper_vars_destroy (void *cv_)
237 struct casegrouper_vars *cv = cv_;