20221212
[devspec.git] / devspec.en_US / project / recutils / src / rec-aggregate.c
blobe28f9d847867e92ea4ad30d61e6ba0093360e3f1
1 /* -*- mode: C -*-
3 * File: rec-aggregate.c
4 * Date: Mon Apr 23 11:05:57 2012
6 * GNU recutils - Support for aggregate functions
8 */
10 /* Copyright (C) 2012-2019 Jose E. Marchesi */
12 /* This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include <config.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <float.h>
31 #include <math.h>
32 #include <minmax.h>
34 #include <rec-utils.h>
35 #include <rec.h>
38 * Data structures.
41 #define MAX_FUNCTIONS 40
43 struct rec_aggregate_reg_elem_s
45 char *name;
46 rec_aggregate_t function;
49 struct rec_aggregate_reg_s
51 struct rec_aggregate_reg_elem_s functions[MAX_FUNCTIONS];
52 size_t num_functions;
55 /* Static functions defined in this file. */
57 static char *rec_aggregate_std_count (rec_rset_t rset,
58 rec_record_t record,
59 const char *field_name);
61 static char *rec_aggregate_std_avg (rec_rset_t rset,
62 rec_record_t record,
63 const char *field_name);
64 static double rec_aggregate_std_avg_record (rec_record_t record,
65 const char *field_name);
67 static char *rec_aggregate_std_sum (rec_rset_t rset,
68 rec_record_t record,
69 const char *field_name);
70 static double rec_aggregate_std_sum_record (rec_record_t record,
71 const char *field_name);
73 static char *rec_aggregate_std_min (rec_rset_t rset,
74 rec_record_t record,
75 const char *field_name);
76 static double rec_aggregate_std_min_record (rec_record_t record,
77 const char *field_name);
79 static char *rec_aggregate_std_max (rec_rset_t rset,
80 rec_record_t record,
81 const char *field_name);
82 static double rec_aggregate_std_max_record (rec_record_t record,
83 const char *field_name);
86 * Static structure containing the descriptors of the standard
87 * aggregates.
89 * The aggregate names must be in lower-case, even if the matching is
90 * case-insensitive.
93 struct rec_aggregate_descriptor_s
95 const char *name;
96 rec_aggregate_t func;
99 #define NUM_STD_AGGREGATES 5
101 static struct rec_aggregate_descriptor_s std_aggregates[] =
102 {{"count", &rec_aggregate_std_count},
103 {"avg", &rec_aggregate_std_avg},
104 {"sum", &rec_aggregate_std_sum},
105 {"min", &rec_aggregate_std_min},
106 {"max", &rec_aggregate_std_max}};
109 * Public functions.
112 rec_aggregate_reg_t
113 rec_aggregate_reg_new (void)
115 rec_aggregate_reg_t new;
117 new = malloc (sizeof (struct rec_aggregate_reg_s));
118 if (new)
119 new->num_functions = 0;
121 return new;
124 void
125 rec_aggregate_reg_destroy (rec_aggregate_reg_t func_reg)
127 if (func_reg)
129 size_t i = 0;
131 for (i = 0; i < func_reg->num_functions; i++)
132 free (func_reg->functions[i].name);
133 free (func_reg);
137 bool
138 rec_aggregate_reg_add (rec_aggregate_reg_t func_reg,
139 const char *name,
140 rec_aggregate_t function)
142 bool function_replaced = false;
143 size_t i = 0;
145 for (i = 0; i < func_reg->num_functions; i++)
146 if (strcmp (name, func_reg->functions[i].name) == 0)
148 /* Replace the existing function. */
149 func_reg->functions[i].function = function;
150 function_replaced = true;
151 break;
154 if (!function_replaced)
156 /* Insert the function into a new entry in the registry. */
158 if (func_reg->num_functions == MAX_FUNCTIONS)
159 /* FIXME: this is crappy as hell. */
160 return false;
162 func_reg->functions[func_reg->num_functions].name = strdup (name);
163 func_reg->functions[func_reg->num_functions].function = function;
164 func_reg->num_functions++;
167 return true;
170 rec_aggregate_t
171 rec_aggregate_reg_get (rec_aggregate_reg_t func_reg,
172 const char *name)
174 size_t i = 0;
175 rec_aggregate_t res = NULL;
177 for (i = 0; i < func_reg->num_functions; i++)
178 if (strcasecmp (func_reg->functions[i].name, name) == 0)
180 res = func_reg->functions[i].function;
181 break;
184 return res;
187 void
188 rec_aggregate_reg_add_standard (rec_aggregate_reg_t func_reg)
190 size_t i = 0;
192 for (i = 0; i < NUM_STD_AGGREGATES; i++)
193 rec_aggregate_reg_add (func_reg, std_aggregates[i].name, std_aggregates[i].func);
196 bool
197 rec_aggregate_std_p (const char *name)
199 bool found = false;
200 size_t i = 0;
202 for (i = 0; i < NUM_STD_AGGREGATES; i++)
203 if (strcasecmp (name, std_aggregates[i].name) == 0)
205 found = true;
206 break;
209 return found;
213 * Private functions.
216 static char *
217 rec_aggregate_std_count (rec_rset_t rset,
218 rec_record_t record,
219 const char *field_name)
221 char *result = NULL;
222 size_t count = 0;
224 if (record)
225 count = rec_record_get_num_fields_by_name (record, field_name);
226 else if (rset)
228 rec_record_t rec = NULL;
229 rec_mset_iterator_t iter = rec_mset_iterator (rec_rset_mset (rset));
231 while (rec_mset_iterator_next (&iter, MSET_RECORD, (void *) &rec, NULL))
232 count = count + rec_record_get_num_fields_by_name (rec, field_name);
233 rec_mset_iterator_free (&iter);
236 /* Return the count as a string. Note that if NULL is returned it
237 will be returned by this function below to signal the
238 end-of-memory condition. */
240 asprintf (&result, "%zu", count);
241 return result;
244 static char *
245 rec_aggregate_std_avg (rec_rset_t rset,
246 rec_record_t record,
247 const char *field_name)
249 char *result = NULL;
250 double avg = 0;
252 if (record)
253 avg = rec_aggregate_std_avg_record (record, field_name);
254 else if (rset)
256 size_t num_records = 0;
257 rec_record_t rec = NULL;
258 rec_mset_iterator_t iter = rec_mset_iterator (rec_rset_mset (rset));
260 while (rec_mset_iterator_next (&iter, MSET_RECORD, (void *) &rec, NULL))
262 avg = avg + rec_aggregate_std_avg_record (rec, field_name);
263 num_records++;
265 rec_mset_iterator_free (&iter);
267 if (num_records != 0)
268 avg = avg / num_records;
271 /* Return the average as a string. Note that if NULL is returned it
272 will be returned by this function below to signal the
273 end-of-memory condition. */
274 asprintf (&result, "%g", avg);
276 return result;
279 static double
280 rec_aggregate_std_avg_record (rec_record_t record,
281 const char *field_name)
283 double avg = 0;
284 rec_field_t field;
285 size_t num_fields = 0;
286 rec_mset_iterator_t iter = rec_mset_iterator (rec_record_mset (record));
288 while (rec_mset_iterator_next (&iter, MSET_FIELD, (void *) &field, NULL))
290 double field_value_double = 0;
291 const char *field_value = rec_field_value (field);
293 if (rec_field_name_equal_p (rec_field_name (field), field_name)
294 && rec_atod (field_value, &field_value_double))
296 avg = avg + field_value_double;
297 num_fields++;
300 rec_mset_iterator_free (&iter);
302 if (num_fields != 0)
303 avg = avg / num_fields;
305 return avg;
308 #define REC_AGGREGATE_ACCUM_FUNC(NAME, OP, INIT_VAL) \
309 static char * \
310 rec_aggregate_std_##NAME (rec_rset_t rset, \
311 rec_record_t record, \
312 const char *field_name) \
314 char *result = NULL; \
315 double val = INIT_VAL; \
317 if (record) \
318 val = rec_aggregate_std_##NAME##_record (record, field_name); \
319 else if (rset) \
321 rec_record_t rec = NULL; \
322 rec_mset_iterator_t iter = rec_mset_iterator (rec_rset_mset (rset)); \
323 while (rec_mset_iterator_next (&iter, MSET_RECORD, (void *) &rec, NULL)) \
324 val = OP (val, rec_aggregate_std_##NAME##_record (rec, field_name)); \
325 rec_mset_iterator_free (&iter); \
328 /* Return the val as a string. Note that if NULL is returned it */ \
329 /* will be returned by this function below to signal the */ \
330 /* end-of-memory condition. */ \
331 asprintf (&result, "%g", val); \
333 return result; \
336 static double \
337 rec_aggregate_std_##NAME##_record (rec_record_t record, \
338 const char *field_name) \
340 /* Calculate the val of the fields in a given record. Fields */ \
341 /* not representing a real value are ignored. */ \
343 double val = INIT_VAL; \
344 rec_field_t field; \
345 rec_mset_iterator_t iter = rec_mset_iterator (rec_record_mset (record)); \
347 while (rec_mset_iterator_next (&iter, MSET_FIELD, (void *) &field, NULL)) \
349 const char *field_value = rec_field_value (field); \
350 double field_value_double = 0; \
352 if (rec_field_name_equal_p (rec_field_name (field), field_name) \
353 && rec_atod (field_value, &field_value_double)) \
354 val = OP (val, field_value_double); \
356 rec_mset_iterator_free (&iter); \
358 return val; \
362 * Aggregate: Sum(Field)
365 static inline double
366 op_sum (double op1, double op2)
368 return op1 + op2;
371 REC_AGGREGATE_ACCUM_FUNC(sum, op_sum, 0);
374 * Aggregate: Min(Field)
375 * Aggregate: Max(Field)
378 REC_AGGREGATE_ACCUM_FUNC(min, MIN, DBL_MAX);
379 REC_AGGREGATE_ACCUM_FUNC(max, MAX, DBL_MIN);
381 /* End of rec-aggregate.c */