3 * File: rec-aggregate.c
4 * Date: Mon Apr 23 11:05:57 2012
6 * GNU recutils - Support for aggregate functions
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/>.
34 #include <rec-utils.h>
41 #define MAX_FUNCTIONS 40
43 struct rec_aggregate_reg_elem_s
46 rec_aggregate_t function
;
49 struct rec_aggregate_reg_s
51 struct rec_aggregate_reg_elem_s functions
[MAX_FUNCTIONS
];
55 /* Static functions defined in this file. */
57 static char *rec_aggregate_std_count (rec_rset_t rset
,
59 const char *field_name
);
61 static char *rec_aggregate_std_avg (rec_rset_t rset
,
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
,
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
,
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
,
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
89 * The aggregate names must be in lower-case, even if the matching is
93 struct rec_aggregate_descriptor_s
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
}};
113 rec_aggregate_reg_new (void)
115 rec_aggregate_reg_t
new;
117 new = malloc (sizeof (struct rec_aggregate_reg_s
));
119 new->num_functions
= 0;
125 rec_aggregate_reg_destroy (rec_aggregate_reg_t func_reg
)
131 for (i
= 0; i
< func_reg
->num_functions
; i
++)
132 free (func_reg
->functions
[i
].name
);
138 rec_aggregate_reg_add (rec_aggregate_reg_t func_reg
,
140 rec_aggregate_t function
)
142 bool function_replaced
= false;
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;
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. */
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
++;
171 rec_aggregate_reg_get (rec_aggregate_reg_t func_reg
,
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
;
188 rec_aggregate_reg_add_standard (rec_aggregate_reg_t func_reg
)
192 for (i
= 0; i
< NUM_STD_AGGREGATES
; i
++)
193 rec_aggregate_reg_add (func_reg
, std_aggregates
[i
].name
, std_aggregates
[i
].func
);
197 rec_aggregate_std_p (const char *name
)
202 for (i
= 0; i
< NUM_STD_AGGREGATES
; i
++)
203 if (strcasecmp (name
, std_aggregates
[i
].name
) == 0)
217 rec_aggregate_std_count (rec_rset_t rset
,
219 const char *field_name
)
225 count
= rec_record_get_num_fields_by_name (record
, field_name
);
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
);
245 rec_aggregate_std_avg (rec_rset_t rset
,
247 const char *field_name
)
253 avg
= rec_aggregate_std_avg_record (record
, field_name
);
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
);
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
);
280 rec_aggregate_std_avg_record (rec_record_t record
,
281 const char *field_name
)
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
;
300 rec_mset_iterator_free (&iter
);
303 avg
= avg
/ num_fields
;
308 #define REC_AGGREGATE_ACCUM_FUNC(NAME, OP, INIT_VAL) \
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; \
318 val = rec_aggregate_std_##NAME##_record (record, field_name); \
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); \
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; \
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); \
362 * Aggregate: Sum(Field)
366 op_sum (double op1
, double 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 */