1 /*
2 * collect.c: Helpers to collect ranges of data.
3 *
4 * Authors:
5 * Morten Welinder <terra@gnome.org>
6 * Jukka-Pekka Iivonen <iivonen@iki.fi>
7 */
8
9 #include <gnumeric-config.h>
10 #include <gnumeric.h>
11 #include <collect.h>
12
13 #include <func.h>
14 #include <application.h>
15 #include <value.h>
16 #include <expr.h>
17 #include <expr-impl.h>
18 #include <gnm-datetime.h>
19 #include <workbook.h>
20 #include <sheet.h>
21 #include <ranges.h>
22 #include <number-match.h>
23 #include <goffice/goffice.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 /* ------------------------------------------------------------------------- */
28
29 typedef struct {
30 /* key */
31 GnmValue *value;
32 CollectFlags flags;
33
34 /* result */
35 int n;
36 gnm_float *data;
37 GnmValue *error;
38 } SingleFloatsCacheEntry;
39
40 static void
single_floats_cache_entry_free(SingleFloatsCacheEntry * entry)41 single_floats_cache_entry_free (SingleFloatsCacheEntry *entry)
42 {
43 value_release (entry->value);
44 value_release (entry->error);
45 g_free (entry->data);
46 g_free (entry);
47 }
48
49 static guint
single_floats_cache_entry_hash(const SingleFloatsCacheEntry * entry)50 single_floats_cache_entry_hash (const SingleFloatsCacheEntry *entry)
51 {
52 return value_hash (entry->value) ^ (guint)entry->flags;
53 }
54
55 static gboolean
single_floats_cache_entry_equal(const SingleFloatsCacheEntry * a,const SingleFloatsCacheEntry * b)56 single_floats_cache_entry_equal (const SingleFloatsCacheEntry *a,
57 const SingleFloatsCacheEntry *b)
58
59 {
60 return (a->flags == b->flags &&
61 value_equal (a->value, b->value));
62 }
63
64 /* ------------------------------------------------------------------------- */
65
66 typedef struct {
67 /* key */
68 GnmValue *vx;
69 GnmValue *vy;
70 CollectFlags flags;
71
72 /* result */
73 int n;
74 gnm_float *data_x;
75 gnm_float *data_y;
76 GnmValue *error;
77 } PairsFloatsCacheEntry;
78
79 static void
pairs_floats_cache_entry_free(PairsFloatsCacheEntry * entry)80 pairs_floats_cache_entry_free (PairsFloatsCacheEntry *entry)
81 {
82 value_release (entry->vx);
83 value_release (entry->vy);
84 value_release (entry->error);
85 g_free (entry->data_x);
86 g_free (entry->data_y);
87 g_free (entry);
88 }
89
90 static guint
pairs_floats_cache_entry_hash(const PairsFloatsCacheEntry * entry)91 pairs_floats_cache_entry_hash (const PairsFloatsCacheEntry *entry)
92 {
93 /* FIXME: this does not consider the sheet, ie. the same pair of */
94 /* ranges on two different sheets yields the same hash value */
95 return value_hash (entry->vx) ^ (value_hash (entry->vy) << 1) ^ (guint)entry->flags;
96 }
97
98 static gboolean
pairs_floats_cache_entry_equal(const PairsFloatsCacheEntry * a,const PairsFloatsCacheEntry * b)99 pairs_floats_cache_entry_equal (const PairsFloatsCacheEntry *a,
100 const PairsFloatsCacheEntry *b)
101
102 {
103 return (a->flags == b->flags &&
104 value_equal (a->vx, b->vx) &&
105 value_equal (a->vy, b->vy));
106 }
107
108 /* ------------------------------------------------------------------------- */
109
110
111 static gulong cache_handler;
112 static GHashTable *single_floats_cache;
113 static GHashTable *pairs_floats_cache;
114 static size_t total_cache_size;
115
116 static void
clear_caches(void)117 clear_caches (void)
118 {
119 if (!cache_handler)
120 return;
121
122 g_signal_handler_disconnect (gnm_app_get_app (), cache_handler);
123 cache_handler = 0;
124
125 g_hash_table_destroy (single_floats_cache);
126 single_floats_cache = NULL;
127 g_hash_table_destroy (pairs_floats_cache);
128 pairs_floats_cache = NULL;
129
130 total_cache_size = 0;
131 }
132
133 static void
create_caches(void)134 create_caches (void)
135 {
136 if (cache_handler)
137 return;
138
139 cache_handler =
140 g_signal_connect (gnm_app_get_app (), "recalc-clear-caches",
141 G_CALLBACK (clear_caches), NULL);
142
143 single_floats_cache = g_hash_table_new_full
144 ((GHashFunc)single_floats_cache_entry_hash,
145 (GEqualFunc)single_floats_cache_entry_equal,
146 (GDestroyNotify)single_floats_cache_entry_free,
147 NULL);
148 pairs_floats_cache = g_hash_table_new_full
149 ((GHashFunc)pairs_floats_cache_entry_hash,
150 (GEqualFunc)pairs_floats_cache_entry_equal,
151 (GDestroyNotify)pairs_floats_cache_entry_free,
152 NULL);
153
154 total_cache_size = 0;
155 }
156
157 static gboolean
cb_prune(gpointer key,gpointer value,gpointer user)158 cb_prune (gpointer key, gpointer value, gpointer user)
159 {
160 return TRUE;
161 }
162
163 static void
prune_caches(void)164 prune_caches (void)
165 {
166 if (total_cache_size > GNM_DEFAULT_ROWS * 32) {
167 if (0) g_printerr ("Pruning collect cache from size %ld.\n",
168 (long)total_cache_size);
169
170 total_cache_size = 0;
171 g_hash_table_foreach_remove (single_floats_cache,
172 cb_prune,
173 NULL);
174 g_hash_table_foreach_remove (pairs_floats_cache,
175 cb_prune,
176 NULL);
177 }
178 }
179
180 static SingleFloatsCacheEntry *
get_single_floats_cache_entry(GnmValue const * value,CollectFlags flags)181 get_single_floats_cache_entry (GnmValue const *value, CollectFlags flags)
182 {
183 SingleFloatsCacheEntry key;
184
185 if (flags & (COLLECT_INFO | COLLECT_IGNORE_SUBTOTAL))
186 return NULL;
187
188 create_caches ();
189
190 key.value = (GnmValue *)value;
191 key.flags = flags;
192
193 return g_hash_table_lookup (single_floats_cache, &key);
194 }
195
196 static PairsFloatsCacheEntry *
get_pairs_floats_cache_entry(GnmValue const * vx,GnmValue const * vy,CollectFlags flags)197 get_pairs_floats_cache_entry (GnmValue const *vx, GnmValue const *vy,
198 CollectFlags flags)
199 {
200 PairsFloatsCacheEntry key;
201
202 if (flags & (COLLECT_INFO | COLLECT_IGNORE_SUBTOTAL))
203 return NULL;
204
205 create_caches ();
206
207 key.vx = (GnmValue *)vx;
208 key.vy = (GnmValue *)vy;
209 key.flags = flags;
210
211 return g_hash_table_lookup (pairs_floats_cache, &key);
212 }
213
214 static SingleFloatsCacheEntry *
get_or_fake_cache_entry(GnmValue const * key,CollectFlags flags,GnmEvalPos const * ep)215 get_or_fake_cache_entry (GnmValue const *key, CollectFlags flags,
216 GnmEvalPos const *ep)
217 {
218 SingleFloatsCacheEntry *ce;
219
220 ce = get_single_floats_cache_entry (key, flags);
221 if (ce) return ce;
222
223 if (flags & COLLECT_ORDER_IRRELEVANT) {
224 ce = get_single_floats_cache_entry (key, flags | COLLECT_SORT);
225 if (ce)
226 return ce;
227 }
228
229 if (flags & COLLECT_SORT) {
230 /* FIXME: Try unsorted. */
231 }
232
233 return NULL;
234 }
235
236 static PairsFloatsCacheEntry *
get_or_fake_pairs_cache_entry(GnmValue const * key_x,GnmValue const * key_y,CollectFlags flags,GnmEvalPos const * ep)237 get_or_fake_pairs_cache_entry (GnmValue const *key_x, GnmValue const *key_y,
238 CollectFlags flags,
239 GnmEvalPos const *ep)
240 {
241 PairsFloatsCacheEntry *ce;
242
243 ce = get_pairs_floats_cache_entry (key_x, key_y, flags);
244 if (ce) return ce;
245
246 /* FIXME: we should also try the pairs switched */
247
248 return NULL;
249 }
250
251 static GnmValue *
get_single_cache_key_from_value(GnmValue const * r,GnmEvalPos const * ep)252 get_single_cache_key_from_value (GnmValue const *r, GnmEvalPos const *ep)
253 {
254 GnmValue *key;
255 GnmSheetRange sr;
256 GnmRangeRef const *rr;
257 Sheet *end_sheet;
258 int h, w;
259 const int min_size = 25;
260
261 rr = value_get_rangeref (r);
262 gnm_rangeref_normalize (rr, ep, &sr.sheet, &end_sheet, &sr.range);
263 if (sr.sheet != end_sheet)
264 return NULL; /* 3D */
265
266 h = range_height (&sr.range);
267 w = range_width (&sr.range);
268 if (h < min_size && w < min_size && h * w < min_size)
269 return NULL;
270
271 key = value_new_cellrange_r (sr.sheet, &sr.range);
272
273 return key;
274 }
275
276 static GnmValue *
get_single_cache_key(GnmExpr const * e,GnmEvalPos const * ep)277 get_single_cache_key (GnmExpr const *e, GnmEvalPos const *ep)
278 {
279 GnmValue *r = gnm_expr_get_range (e);
280
281 if (r) {
282 GnmValue *v = get_single_cache_key_from_value (r, ep);
283 value_release (r);
284 return v;
285 } else
286 return NULL;
287
288 }
289
290 /* ------------------------------------------------------------------------- */
291
292 static int
float_compare(const void * a_,const void * b_)293 float_compare (const void *a_, const void *b_)
294 {
295 gnm_float const *a = a_;
296 gnm_float const *b = b_;
297
298 if (*a < *b)
299 return -1;
300 else if (*a == *b)
301 return 0;
302 else
303 return 1;
304 }
305
306 typedef struct {
307 guint alloc_count;
308 gnm_float *data;
309 guint count;
310 CollectFlags flags;
311 GSList *info;
312 GODateConventions const *date_conv;
313 } collect_floats_t;
314
315 static GnmValue *
callback_function_collect(GnmEvalPos const * ep,GnmValue const * value,void * closure)316 callback_function_collect (GnmEvalPos const *ep, GnmValue const *value,
317 void *closure)
318 {
319 gnm_float x = 0;
320 collect_floats_t *cl = closure;
321 gboolean ignore = FALSE;
322
323 switch (value ? value->v_any.type : VALUE_EMPTY) {
324 case VALUE_EMPTY:
325 if (cl->flags & COLLECT_IGNORE_BLANKS)
326 ignore = TRUE;
327 else if (cl->flags & COLLECT_ZERO_BLANKS)
328 x = 0;
329 else
330 return value_new_error_VALUE (ep);
331 break;
332
333 case VALUE_BOOLEAN:
334 if (cl->flags & COLLECT_IGNORE_BOOLS)
335 ignore = TRUE;
336 else if (cl->flags & COLLECT_ZEROONE_BOOLS)
337 x = value_get_as_float (value);
338 else
339 return value_new_error_VALUE (ep);
340 break;
341
342 case VALUE_CELLRANGE:
343 case VALUE_ARRAY:
344 /* Ranges and arrays are not singleton values treat as errors */
345
346 case VALUE_ERROR:
347 if (cl->flags & COLLECT_IGNORE_ERRORS)
348 ignore = TRUE;
349 else if (cl->flags & COLLECT_ZERO_ERRORS)
350 x = 0;
351 else
352 return value_new_error_VALUE (ep);
353 break;
354
355 case VALUE_FLOAT:
356 x = value_get_as_float (value);
357 break;
358
359 case VALUE_STRING:
360 if (cl->flags & COLLECT_COERCE_STRINGS) {
361 GnmValue *vc = format_match_number (value_peek_string (value),
362 NULL,
363 cl->date_conv);
364 gboolean bad = !vc || VALUE_IS_BOOLEAN (vc);
365 if (vc) {
366 x = value_get_as_float (vc);
367 value_release (vc);
368 } else
369 x = 0;
370
371 if (bad)
372 return value_new_error_VALUE (ep);
373 } else if (cl->flags & COLLECT_IGNORE_STRINGS)
374 ignore = TRUE;
375 else if (cl->flags & COLLECT_ZERO_STRINGS)
376 x = 0;
377 else
378 return value_new_error_VALUE (ep);
379 break;
380
381 default:
382 g_warning ("Trouble in callback_function_collect. (%d)",
383 value->v_any.type);
384 ignore = TRUE;
385 }
386
387 if (ignore) {
388 if (cl->flags & COLLECT_INFO)
389 cl->info = g_slist_prepend (cl->info, GUINT_TO_POINTER (cl->count));
390 else {
391 return NULL;
392 }
393 }
394
395 if (cl->count == cl->alloc_count) {
396 cl->alloc_count = cl->alloc_count * 2 + 20;
397 cl->data = g_renew (gnm_float, cl->data, cl->alloc_count);
398 }
399
400 cl->data[cl->count++] = x;
401 return NULL;
402 }
403
404 /**
405 * collect_floats: (skip):
406 *
407 * exprlist: List of expressions to evaluate.
408 * cr: Current location (for resolving relative cells).
409 * flags: COLLECT_IGNORE_STRINGS: silently ignore strings.
410 * COLLECT_COERCE_STRINGS: coerce string into numbers
411 * COLLECT_ZERO_STRINGS: count strings as 0.
412 * (Alternative: return #VALUE!.)
413 * COLLECT_IGNORE_BOOLS: silently ignore bools.
414 * COLLECT_ZEROONE_BOOLS: count FALSE as 0, TRUE as 1.
415 * (Alternative: return #VALUE!.)
416 * COLLECT_IGNORE_SUBTOTAL : ignore expressions that include
417 * the function SUBTOTAL directly and ignore any content
418 * in filtered rows.
419 * n: Output parameter for number of floats.
420 *
421 * Return value:
422 * NULL in case of strict and a blank.
423 * A copy of the error in the case of strict and an error.
424 * Non-NULL in case of success. Then n will be set.
425 *
426 * Evaluate a list of expressions and return the result as an array of
427 * gnm_float.
428 */
429 gnm_float *
collect_floats(int argc,GnmExprConstPtr const * argv,GnmEvalPos const * ep,CollectFlags flags,int * n,GnmValue ** error,GSList ** info,gboolean * constp)430 collect_floats (int argc, GnmExprConstPtr const *argv,
431 GnmEvalPos const *ep, CollectFlags flags,
432 int *n, GnmValue **error, GSList **info,
433 gboolean *constp)
434 {
435 collect_floats_t cl;
436 CellIterFlags iter_flags = CELL_ITER_ALL;
437 GnmValue *key = NULL;
438 CollectFlags keyflags = flags & ~COLLECT_ORDER_IRRELEVANT;
439 gboolean strict;
440
441 if (constp)
442 *constp = FALSE;
443
444 if (info) {
445 *info = NULL;
446 g_return_val_if_fail (!(flags & COLLECT_SORT), NULL);
447 flags |= COLLECT_INFO;
448 } else {
449 if (flags & COLLECT_IGNORE_BLANKS)
450 iter_flags = CELL_ITER_IGNORE_BLANK;
451 flags &= ~COLLECT_INFO;
452 }
453
454 /* ---------------------------------------- */
455 /* Try cache. */
456
457 if (argc == 1 &&
458 (flags & (COLLECT_INFO | COLLECT_IGNORE_SUBTOTAL)) == 0) {
459 key = get_single_cache_key (argv[0], ep);
460 }
461 if (key) {
462 SingleFloatsCacheEntry *ce =
463 get_or_fake_cache_entry (key, keyflags, ep);
464 if (ce) {
465 value_release (key);
466 if (ce->error) {
467 *error = value_dup (ce->error);
468 return NULL;
469 }
470 *n = ce->n;
471 if (constp) {
472 *constp = TRUE;
473 return ce->data;
474 }
475 return g_memdup (ce->data, *n * sizeof (gnm_float));
476 }
477 }
478
479 /* ---------------------------------------- */
480
481 if (flags & COLLECT_IGNORE_SUBTOTAL)
482 iter_flags |= (CELL_ITER_IGNORE_SUBTOTAL |
483 CELL_ITER_IGNORE_FILTERED);
484
485 strict = (flags & (COLLECT_IGNORE_ERRORS | COLLECT_ZERO_ERRORS)) == 0;
486
487 cl.alloc_count = 0;
488 cl.data = NULL;
489 cl.count = 0;
490 cl.flags = flags;
491 cl.info = NULL;
492 cl.date_conv = sheet_date_conv (ep->sheet);
493
494 *error = function_iterate_argument_values
495 (ep, &callback_function_collect, &cl,
496 argc, argv,
497 strict, iter_flags);
498 if (*error) {
499 g_assert (VALUE_IS_ERROR (*error));
500 g_free (cl.data);
501 cl.data = NULL;
502 cl.count = 0;
503 g_slist_free (cl.info);
504 cl.info = NULL;
505 } else {
506 if (cl.data == NULL) {
507 cl.alloc_count = 1;
508 cl.data = g_new (gnm_float, cl.alloc_count);
509 }
510
511 if (flags & COLLECT_SORT) {
512 qsort (cl.data, cl.count, sizeof (cl.data[0]),
513 float_compare);
514 }
515 }
516
517 if (info)
518 *info = cl.info;
519 *n = cl.count;
520
521 if (key) {
522 SingleFloatsCacheEntry *ce = g_new (SingleFloatsCacheEntry, 1);
523 SingleFloatsCacheEntry *ce2;
524 ce->value = key;
525 ce->flags = keyflags;
526 ce->n = *n;
527 ce->error = value_dup (*error);
528 if (cl.data == NULL)
529 ce->data = NULL;
530 else if (constp) {
531 *constp = TRUE;
532 ce->data = cl.data;
533 } else
534 ce->data = g_memdup (cl.data, MAX (1, *n) * sizeof (gnm_float));
535 prune_caches ();
536
537 /*
538 * We looked for the entry earlier and it was not there.
539 * However, sub-calculation might have added it so be careful
540 * to adjust sizes and replace the not-so-old entry.
541 * See bug 627079.
542 */
543 ce2 = g_hash_table_lookup (single_floats_cache, ce);
544 if (ce2)
545 total_cache_size -= 1 + ce2->n;
546
547 g_hash_table_replace (single_floats_cache, ce, ce);
548 total_cache_size += 1 + *n;
549 }
550 return cl.data;
551 }
552
553 /* ------------------------------------------------------------------------- */
554 /* Like collect_floats, but takes a value instead of an expression list.
555 Presumably most useful when the value is an array. */
556
557 gnm_float *
collect_floats_value(GnmValue const * val,GnmEvalPos const * ep,CollectFlags flags,int * n,GnmValue ** error)558 collect_floats_value (GnmValue const *val, GnmEvalPos const *ep,
559 CollectFlags flags, int *n, GnmValue **error)
560 {
561 GnmExpr expr_val;
562 GnmExprConstPtr argv[1] = { &expr_val };
563
564 gnm_expr_constant_init (&expr_val.constant, val);
565 return collect_floats (1, argv, ep, flags, n, error, NULL, NULL);
566 }
567
568 /* ------------------------------------------------------------------------- */
569 /**
570 * collect_floats_value_with_info:
571 * @val: #GnmValue
572 * @ep: #GnmEvalPos
573 * @flags: #CollectFlags
574 * @n:
575 * @info: (element-type guint):
576 * @error:
577 *
578 * Like collect_floats_value, but keeps info on missing values
579 **/
580
581 gnm_float *
collect_floats_value_with_info(GnmValue const * val,GnmEvalPos const * ep,CollectFlags flags,int * n,GSList ** info,GnmValue ** error)582 collect_floats_value_with_info (GnmValue const *val, GnmEvalPos const *ep,
583 CollectFlags flags, int *n, GSList **info,
584 GnmValue **error)
585 {
586 GnmExpr expr_val;
587 GnmExprConstPtr argv[1] = { &expr_val };
588 gnm_float *res;
589
590 gnm_expr_constant_init (&expr_val.constant, val);
591 res = collect_floats (1, argv, ep, flags, n, error, info, NULL);
592
593 if (info)
594 *info = g_slist_reverse (*info);
595
596 return res;
597 }
598
599
600 /* ------------------------------------------------------------------------- */
601
602 /**
603 * float_range_function:
604 * @argc: number of arguments
605 * @argv: (in) (array length=argc): function arguments
606 * @ei: #GnmFuncEvalInfo describing evaluation context
607 * @func: (scope call): implementation function
608 * @flags: #CollectFlags flags describing the collection and interpretation
609 * of values from @argv.
610 * @func_error: A #GnmStdError to use to @func indicates an error.
611 *
612 * This implements a Gnumeric sheet function that operates on a list of
613 * numbers. This function collects the arguments and uses @func to do
614 * the actual computation.
615 *
616 * Returns: (transfer full): Function result or error value.
617 **/
618 GnmValue *
float_range_function(int argc,GnmExprConstPtr const * argv,GnmFuncEvalInfo * ei,float_range_function_t func,CollectFlags flags,GnmStdError func_error)619 float_range_function (int argc, GnmExprConstPtr const *argv,
620 GnmFuncEvalInfo *ei,
621 float_range_function_t func,
622 CollectFlags flags,
623 GnmStdError func_error)
624 {
625 GnmValue *error = NULL;
626 gnm_float *vals, res;
627 int n, err;
628 gboolean constp;
629
630 vals = collect_floats (argc, argv, ei->pos, flags, &n, &error,
631 NULL, &constp);
632 if (!vals)
633 return error;
634
635 err = func (vals, n, &res);
636 if (!constp) g_free (vals);
637
638 if (err)
639 return value_new_error_std (ei->pos, func_error);
640 else
641 return value_new_float (res);
642 }
643
644 /* ------------------------------------------------------------------------- */
645
646 /**
647 * gnm_slist_sort_merge:
648 * @list_1: (element-type guint) (transfer container): a sorted list of
649 * unsigned integers with no duplicates.
650 * @list_2: (element-type guint) (transfer container): another one
651 *
652 * gnm_slist_sort_merge merges two lists of unsigned integers.
653 *
654 * Returns: (element-type guint) (transfer container): the mergedlist.
655 **/
656 GSList *
gnm_slist_sort_merge(GSList * l1,GSList * l2)657 gnm_slist_sort_merge (GSList *l1,
658 GSList *l2)
659 {
660 GSList list, *l;
661
662 l = &list;
663
664 while (l1 && l2) {
665 if (GPOINTER_TO_UINT (l1->data) <= GPOINTER_TO_UINT (l2->data)) {
666 if (GPOINTER_TO_UINT (l1->data) == GPOINTER_TO_UINT (l2->data)) {
667 /* remove duplicates */
668 GSList *m = l2;
669 l2 = l2->next;
670 m->next = NULL;
671 g_slist_free_1 (m);
672 }
673 l = l->next = l1;
674 l1 = l1->next;
675 } else {
676 l = l->next = l2;
677 l2 = l2->next;
678 }
679 }
680 l->next = l1 ? l1 : l2;
681
682 return list.next;
683 }
684
685
686 /**
687 * gnm_strip_missing:
688 * @data: (inout) (array length=n): Array
689 * @n: (inout): Number of elements in @data.
690 * @missing: (element-type guint): indices of elements to remove in increasing
691 * order.
692 *
693 * This removes the data elements from @data whose indices are given by
694 * @missing. @n is the number of elements and it updated upon return.
695 **/
696 void
gnm_strip_missing(gnm_float * data,int * n,GSList * missing)697 gnm_strip_missing (gnm_float *data, int *n, GSList *missing)
698 {
699 unsigned src, dst;
700
701 if (missing == NULL)
702 return;
703
704 for (src = dst = 0; (int)dst < *n; src++) {
705 if (missing && src == GPOINTER_TO_UINT (missing->data)) {
706 missing = missing->next;
707 (*n)--;
708 } else {
709 data[dst] = data[src];
710 dst++;
711 }
712 }
713 }
714
715 static PairsFloatsCacheEntry *
collect_float_pairs_ce(GnmValue const * vx,GnmValue const * vy,GnmEvalPos const * ep,CollectFlags flags)716 collect_float_pairs_ce (GnmValue const *vx, GnmValue const *vy,
717 GnmEvalPos const *ep, CollectFlags flags)
718 {
719 PairsFloatsCacheEntry *ce = g_new0 (PairsFloatsCacheEntry, 1);
720 GSList *missing0 = NULL, *missing1 = NULL;
721 int n0, n1;
722
723 ce->flags = flags;
724
725 ce->data_x = collect_floats_value_with_info (vx, ep, flags,
726 &n0, &missing0, &ce->error);
727 if (ce->error)
728 goto err;
729
730 ce->data_y = collect_floats_value_with_info (vy, ep, flags,
731 &n1, &missing1, &ce->error);
732
733 if (ce->error)
734 goto err;
735
736 if (n0 != n1) {
737 ce->n = -1;
738 goto err;
739 }
740
741 if (missing0 || missing1) {
742 missing0 = gnm_slist_sort_merge (missing0, missing1);
743 missing1 = NULL;
744 gnm_strip_missing (ce->data_x, &n0, missing0);
745 gnm_strip_missing (ce->data_y, &n1, missing0);
746 }
747 ce->n = n0;
748
749 err:
750 if (ce->n <= 0) {
751 g_free (ce->data_x);
752 ce->data_x = NULL;
753 g_free (ce->data_y);
754 ce->data_y = NULL;
755 }
756
757 g_slist_free (missing0);
758 g_slist_free (missing1);
759
760 return ce;
761 }
762
763 /**
764 * collect_float_pairs: (skip)
765 * @v0: value describing first data range
766 * @v1: value describing second data range
767 * @ep: evaluation position
768 * @flags: flags describing how to handle value types
769 * @xs0: (out) (array length=n): return location for first data vector
770 * @xs1: (out) (array length=n): return location for second data vector
771 * @n: (out): return location for number of data points
772 * @constp: (out) (optional): Return location for a flag describing who own
773 * the vectors returned in @xs0 and @xs1. If present and %TRUE, the
774 * resulting data vectors in @xs0 and @xs1 are not owned by the caller.
775 * If not-present or %FALSE, the callers owns and must free the result.
776 *
777 * If @n is not positive upon return, no data has been allocated.
778 * If @n is negative upon return, the two ranges had different sizes.
779 *
780 * Note: introspection cannot handle this functions parameter mix.
781 *
782 * Returns: (transfer full) (nullable): Error value.
783 */
784 GnmValue *
collect_float_pairs(GnmValue const * vx,GnmValue const * vy,GnmEvalPos const * ep,CollectFlags flags,gnm_float ** xs0,gnm_float ** xs1,int * n,gboolean * constp)785 collect_float_pairs (GnmValue const *vx, GnmValue const *vy,
786 GnmEvalPos const *ep, CollectFlags flags,
787 gnm_float **xs0, gnm_float **xs1, int *n,
788 gboolean *constp)
789 {
790 GnmValue *key_x = NULL;
791 GnmValue *key_y = NULL;
792 PairsFloatsCacheEntry *ce = NULL;
793 gboolean use_cache, free_keys = TRUE;
794
795 if (VALUE_IS_CELLRANGE (vx))
796 key_x = get_single_cache_key_from_value (vx, ep);
797 if (VALUE_IS_CELLRANGE (vy))
798 key_y = get_single_cache_key_from_value (vy, ep);
799
800 if ((use_cache = (key_x && key_y)))
801 ce = get_or_fake_pairs_cache_entry (key_x, key_y, flags, ep);
802
803 if (!ce) {
804 ce = collect_float_pairs_ce (vx, vy, ep, flags);
805 if (use_cache) {
806 PairsFloatsCacheEntry *ce2;
807 ce->vx = key_x;
808 ce->vy = key_y;
809 free_keys = FALSE;
810
811 /*
812 * We looked for the entry earlier and it was not there.
813 * However, sub-calculation might have added it so be careful
814 * to adjust sizes and replace the not-so-old entry.
815 * See bug 627079.
816 */
817 ce2 = g_hash_table_lookup (pairs_floats_cache, ce);
818 if (ce2)
819 total_cache_size -= 1 + ce2->n;
820
821 g_hash_table_replace (pairs_floats_cache, ce, ce);
822 total_cache_size += 1 + ce->n;
823 }
824 }
825
826 if (free_keys) {
827 value_release (key_x);
828 value_release (key_y);
829 }
830
831 if (ce == NULL)
832 return value_new_error_VALUE (ep);
833 else {
834 if (ce->error) {
835 if (use_cache)
836 return value_dup (ce->error);
837 else {
838 GnmValue *ret = ce->error;
839 ce->error = NULL;
840 pairs_floats_cache_entry_free (ce);
841 return ret;
842 }
843 }
844 *n = ce->n;
845 if (ce->n <= 0) {
846 if (!use_cache)
847 pairs_floats_cache_entry_free (ce);
848 *xs0 = NULL;
849 *xs1 = NULL;
850 if (constp)
851 *constp = FALSE;
852 return NULL;
853 }
854 if (use_cache) {
855 if (constp) {
856 *xs0 = ce->data_x;
857 *xs1 = ce->data_y;
858 *constp = TRUE;
859 } else {
860 *xs0 = g_memdup (ce->data_x, *n * sizeof (gnm_float));
861 *xs1 = g_memdup (ce->data_y, *n * sizeof (gnm_float));
862 }
863 } else {
864 if (constp)
865 *constp = FALSE;
866 *xs0 = ce->data_x;
867 *xs1 = ce->data_y;
868 ce->data_x = NULL;
869 ce->data_y = NULL;
870 pairs_floats_cache_entry_free (ce);
871 }
872 return NULL;
873 }
874 }
875
876 /**
877 * float_range_function2d:
878 * @val0: First range
879 * @val1: Second range
880 * @ei: #GnmFuncEvalInfo describing evaluation context
881 * @func: (scope call): implementation function
882 * @flags: #CollectFlags flags describing the collection and interpretation
883 * of values from @val0 and @val1.
884 * @func_error: A #GnmStdError to use to @func indicates an error.
885 * @data: user data for @func
886 *
887 * This implements a Gnumeric sheet function that operates on a matched
888 * pair of ranges. This function collects the arguments and uses @func to do
889 * the actual computation.
890 *
891 * Returns: (transfer full): Function result or error value.
892 **/
893 GnmValue *
float_range_function2d(GnmValue const * val0,GnmValue const * val1,GnmFuncEvalInfo * ei,float_range_function2d_t func,CollectFlags flags,GnmStdError func_error,gpointer data)894 float_range_function2d (GnmValue const *val0, GnmValue const *val1,
895 GnmFuncEvalInfo *ei,
896 float_range_function2d_t func,
897 CollectFlags flags,
898 GnmStdError func_error,
899 gpointer data)
900 {
901 gnm_float *vals0, *vals1;
902 int n;
903 GnmValue *res;
904 gnm_float fres;
905 gboolean constp = FALSE;
906
907 res = collect_float_pairs (val0, val1, ei->pos, flags,
908 &vals0, &vals1, &n, &constp);
909 if (res)
910 return res;
911
912 if (n <= 0)
913 return value_new_error_std (ei->pos, func_error);
914
915 if (func (vals0, vals1, n, &fres, data))
916 res = value_new_error_std (ei->pos, func_error);
917 else
918 res = value_new_float (fres);
919
920 if (!constp) {
921 g_free (vals0);
922 g_free (vals1);
923 }
924 return res;
925 }
926
927 /**
928 * float_range_function2:
929 * @val0: First range
930 * @val1: Second range
931 * @ei: #GnmFuncEvalInfo describing evaluation context
932 * @func: (scope call): implementation function
933 * @flags: #CollectFlags flags describing the collection and interpretation
934 * of values from @val0 and @val1.
935 * @func_error: A #GnmStdError to use to @func indicates an error.
936 *
937 * This implements a Gnumeric sheet function that operates on a matched
938 * pair of ranges. This function collects the arguments and uses @func to do
939 * the actual computation.
940 *
941 * Returns: (transfer full): Function result or error value.
942 **/
943 GnmValue *
float_range_function2(GnmValue const * val0,GnmValue const * val1,GnmFuncEvalInfo * ei,float_range_function2_t func,CollectFlags flags,GnmStdError func_error)944 float_range_function2 (GnmValue const *val0, GnmValue const *val1,
945 GnmFuncEvalInfo *ei,
946 float_range_function2_t func,
947 CollectFlags flags,
948 GnmStdError func_error)
949 {
950 return float_range_function2d (val0, val1, ei,
951 (float_range_function2d_t)func,
952 flags,
953 func_error,
954 NULL);
955 }
956
957 /* ------------------------------------------------------------------------- */
958 /* ------------------------------------------------------------------------- */
959
960 typedef struct {
961 GPtrArray *data;
962 CollectFlags flags;
963 } collect_strings_t;
964
965 static GnmValue *
callback_function_collect_strings(GnmEvalPos const * ep,GnmValue const * value,void * closure)966 callback_function_collect_strings (GnmEvalPos const *ep, GnmValue const *value,
967 void *closure)
968 {
969 char *text;
970 collect_strings_t *cl = closure;
971
972 if (VALUE_IS_EMPTY (value)) {
973 if (cl->flags & COLLECT_IGNORE_BLANKS)
974 text = NULL;
975 else
976 text = g_strdup ("");
977 } else
978 text = value_get_as_string (value);
979
980 if (text)
981 g_ptr_array_add (cl->data, text);
982
983 return NULL;
984 }
985
986 static void
collect_strings_free(GPtrArray * data)987 collect_strings_free (GPtrArray *data)
988 {
989 g_ptr_array_foreach (data, (GFunc)g_free, NULL);
990 g_ptr_array_free (data, TRUE);
991 }
992
993 /**
994 * collect_strings:
995 * @argc: number of arguments
996 * @argv: (in) (array length=argc): function arguments
997 * @ep: Evaluation position
998 * @flags: #CollectFlags flags describing the collection and interpretation
999 * of values from @argv.
1000 * @error: (out): Error return value
1001 *
1002 * Evaluate a list of expressions and return the result as a #GPtrArray of
1003 * strings.
1004 *
1005 * Returns: (transfer full) (nullable) (element-type utf8): array of strings.
1006 */
1007 static GPtrArray *
collect_strings(int argc,GnmExprConstPtr const * argv,GnmEvalPos const * ep,CollectFlags flags,GnmValue ** error)1008 collect_strings (int argc, GnmExprConstPtr const *argv,
1009 GnmEvalPos const *ep, CollectFlags flags,
1010 GnmValue **error)
1011 {
1012 collect_strings_t cl;
1013 CellIterFlags iter_flags = CELL_ITER_ALL;
1014 gboolean strict;
1015
1016 /* We don't handle these flags */
1017 g_return_val_if_fail (!(flags & COLLECT_ZERO_ERRORS), NULL);
1018 g_return_val_if_fail (!(flags & COLLECT_ZERO_STRINGS), NULL);
1019 g_return_val_if_fail (!(flags & COLLECT_ZEROONE_BOOLS), NULL);
1020 g_return_val_if_fail (!(flags & COLLECT_ZERO_BLANKS), NULL);
1021
1022 if (flags & COLLECT_IGNORE_BLANKS)
1023 iter_flags = CELL_ITER_IGNORE_BLANK;
1024
1025 strict = (flags & (COLLECT_IGNORE_ERRORS | COLLECT_ZERO_ERRORS)) == 0;
1026
1027 cl.data = g_ptr_array_new ();
1028 cl.flags = flags;
1029
1030 *error = function_iterate_argument_values
1031 (ep, &callback_function_collect_strings, &cl,
1032 argc, argv,
1033 strict, iter_flags);
1034 if (*error) {
1035 g_assert (VALUE_IS_ERROR (*error));
1036 collect_strings_free (cl.data);
1037 return NULL;
1038 }
1039
1040 return cl.data;
1041 }
1042
1043 /**
1044 * string_range_function:
1045 * @argc: number of arguments
1046 * @argv: (in) (array length=argc): function arguments
1047 * @ei: #GnmFuncEvalInfo describing evaluation context
1048 * @func: (scope call): implementation function
1049 * @flags: #CollectFlags flags describing the collection and interpretation
1050 * of values from @argv.
1051 * @func_error: A #GnmStdError to use to @func indicates an error.
1052 *
1053 * This implements a Gnumeric sheet function that operates on a list of
1054 * strings. This function collects the arguments and uses @func to do
1055 * the actual computation.
1056 *
1057 * Returns: (transfer full): Function result or error value.
1058 **/
1059 GnmValue *
string_range_function(int argc,GnmExprConstPtr const * argv,GnmFuncEvalInfo * ei,string_range_function_t func,gpointer user,CollectFlags flags,GnmStdError func_error)1060 string_range_function (int argc, GnmExprConstPtr const *argv,
1061 GnmFuncEvalInfo *ei,
1062 string_range_function_t func,
1063 gpointer user,
1064 CollectFlags flags,
1065 GnmStdError func_error)
1066 {
1067 GnmValue *error = NULL;
1068 GPtrArray *vals;
1069 char *res = NULL;
1070 int err;
1071
1072 vals = collect_strings (argc, argv, ei->pos, flags, &error);
1073 if (!vals)
1074 return error;
1075
1076 err = func (vals, &res, user);
1077
1078 collect_strings_free (vals);
1079
1080 if (err) {
1081 g_free (res);
1082 return value_new_error_std (ei->pos, func_error);
1083 } else {
1084 return value_new_string_nocopy (res);
1085 }
1086 }
1087