eee08700adbae4e2304d30b44d478fafe8c01263
[pspp-builds.git] / src / data / casegrouper.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2007 Free Software Foundation, Inc.
3
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.
8
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.
13
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
17    02110-1301, USA. */
18
19 #include <config.h>
20
21 #include <data/casegrouper.h>
22
23 #include <stdlib.h>
24
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>
30
31 #include "xalloc.h"
32
33 struct casegrouper 
34   {
35     struct casereader *reader;
36     struct taint *taint;
37     
38     bool (*same_group) (const struct ccase *, const struct ccase *, void *aux);
39     void (*destroy) (void *aux);
40     void *aux;
41   };
42
43 struct casegrouper *
44 casegrouper_create_func (struct casereader *reader,
45                          bool (*same_group) (const struct ccase *,
46                                              const struct ccase *,
47                                              void *aux),
48                          void (*destroy) (void *aux),
49                          void *aux) 
50 {
51   struct casegrouper *grouper = xmalloc (sizeof *grouper);
52   grouper->reader = casereader_rename (reader);
53   grouper->taint = taint_clone (casereader_get_taint (grouper->reader));
54   grouper->same_group = same_group;
55   grouper->destroy = destroy;
56   grouper->aux = aux;
57   return grouper;
58 }
59
60 /* FIXME: we really shouldn't need a temporary casewriter for the
61    common case where we read an entire group's data before going
62    on to the next. */
63 bool
64 casegrouper_get_next_group (struct casegrouper *grouper,
65                             struct casereader **reader)
66 {
67   if (grouper->same_group != NULL) 
68     {
69       struct casewriter *writer;
70       struct ccase group_case, tmp;
71
72       if (!casereader_read (grouper->reader, &group_case)) 
73         {
74           *reader = NULL;
75           return false; 
76         }
77
78       writer = autopaging_writer_create (casereader_get_value_cnt (grouper->reader));
79       case_clone (&tmp, &group_case);
80       casewriter_write (writer, &tmp);
81
82       while (casereader_peek (grouper->reader, 0, &tmp)
83              && grouper->same_group (&group_case, &tmp, grouper->aux))
84         {
85           struct ccase discard;
86           casereader_read (grouper->reader, &discard);
87           case_destroy (&discard);
88           casewriter_write (writer, &tmp); 
89         }
90       case_destroy (&tmp);
91       case_destroy (&group_case);
92
93       *reader = casewriter_make_reader (writer);
94       return true;
95     }
96   else 
97     {
98       if (grouper->reader != NULL) 
99         {
100           *reader = grouper->reader;
101           grouper->reader = NULL;
102           return true;
103         }
104       else
105         return false;
106     } 
107 }
108
109 bool
110 casegrouper_destroy (struct casegrouper *grouper) 
111 {
112   if (grouper != NULL) 
113     {
114       struct taint *taint = grouper->taint;
115       bool ok;
116
117       casereader_destroy (grouper->reader);
118       if (grouper->destroy != NULL)
119         grouper->destroy (grouper->aux);
120       free (grouper);
121
122       ok = !taint_has_tainted_successor (taint);
123       taint_destroy (taint);
124       return ok;
125     }
126   else
127     return true;
128 }
129
130 struct casegrouper_vars 
131   {
132     const struct variable **vars;
133     size_t var_cnt;
134   };
135
136 static bool
137 casegrouper_vars_same_group (const struct ccase *a, const struct ccase *b,
138                              void *cv_) 
139 {
140   struct casegrouper_vars *cv = cv_;
141   return case_compare (a, b, cv->vars, cv->var_cnt) == 0;
142 }
143
144 static void
145 casegrouper_vars_destroy (void *cv_) 
146 {
147   struct casegrouper_vars *cv = cv_;
148   free (cv->vars);
149   free (cv);
150 }
151
152 struct casegrouper *
153 casegrouper_create_vars (struct casereader *reader,
154                          const struct variable *const *vars,
155                          size_t var_cnt) 
156 {
157   if (var_cnt > 0) 
158     {
159       struct casegrouper_vars *cv = xmalloc (sizeof *cv);
160       cv->vars = xmemdup (vars, sizeof *vars * var_cnt);
161       cv->var_cnt = var_cnt;
162       return casegrouper_create_func (reader,
163                                       casegrouper_vars_same_group,
164                                       casegrouper_vars_destroy,
165                                       cv);
166     }
167   else
168     return casegrouper_create_func (reader, NULL, NULL, NULL);
169 }
170
171 struct casegrouper *
172 casegrouper_create_splits (struct casereader *reader,
173                            const struct dictionary *dict) 
174 {
175   return casegrouper_create_vars (reader,
176                                   dict_get_split_vars (dict),
177                                   dict_get_split_cnt (dict));
178 }
179
180 struct casegrouper *
181 casegrouper_create_case_ordering (struct casereader *reader,
182                                   const struct case_ordering *co) 
183 {
184   const struct variable **vars;
185   size_t var_cnt;
186   struct casegrouper *grouper;
187
188   case_ordering_get_vars (co, &vars, &var_cnt);
189   grouper = casegrouper_create_vars (reader, vars, var_cnt);
190   free (vars);
191
192   return grouper;
193 }