1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Range lookup functions
6 * Michael Meeks <michael@ximian.com>
7 * Jukka-Pekka Iivonen <iivonen@iki.fi>
8 * JP Rosevear <jpr@arcavia.com>
9 * Morten Welinder (terra@gnome.org)
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <gnumeric-config.h>
30 #include <parse-util.h>
31 #include <dependent.h>
38 #include <expr-impl.h>
39 #include <application.h>
40 #include <expr-name.h>
45 #include <parse-util.h>
48 #include <goffice/goffice.h>
49 #include <gnm-plugin.h>
54 GNM_PLUGIN_MODULE_HEADER
;
56 /* -------------------------------------------------------------------------- */
64 } LookupBisectionCacheItemElem
;
68 LookupBisectionCacheItemElem
*data
;
69 } LookupBisectionCacheItem
;
72 lookup_bisection_cache_item_free (LookupBisectionCacheItem
*item
)
79 bisection_compare_string (const void *a_
, const void *b_
)
81 const LookupBisectionCacheItemElem
*a
= a_
;
82 const LookupBisectionCacheItemElem
*b
= b_
;
83 return g_utf8_collate (a
->u
.str
, b
->u
.str
);
87 bisection_compare_float (const void *a_
, const void *b_
)
89 const LookupBisectionCacheItemElem
*a
= a_
;
90 const LookupBisectionCacheItemElem
*b
= b_
;
95 else if (a
->u
.f
> b
->u
.f
)
102 /* -------------------------------------------------------------------------- */
104 static gboolean debug_lookup_caches
;
105 static GStringChunk
*lookup_string_pool
;
106 static GOMemChunk
*lookup_float_pool
;
107 static GHashTable
*linear_hlookup_string_cache
;
108 static GHashTable
*linear_hlookup_float_cache
;
109 static GHashTable
*linear_hlookup_bool_cache
;
110 static GHashTable
*linear_vlookup_string_cache
;
111 static GHashTable
*linear_vlookup_float_cache
;
112 static GHashTable
*linear_vlookup_bool_cache
;
113 static GHashTable
*bisection_hlookup_string_cache
;
114 static GHashTable
*bisection_hlookup_float_cache
;
115 static GHashTable
*bisection_hlookup_bool_cache
;
116 static GHashTable
*bisection_vlookup_string_cache
;
117 static GHashTable
*bisection_vlookup_float_cache
;
118 static GHashTable
*bisection_vlookup_bool_cache
;
119 static size_t total_cache_size
;
120 static size_t protect_string_pool
;
121 static size_t protect_float_pool
;
126 if (!linear_hlookup_string_cache
)
129 if (debug_lookup_caches
)
130 g_printerr ("Clearing lookup caches [%ld]\n", (long)total_cache_size
);
132 total_cache_size
= 0;
136 g_hash_table_destroy (linear_hlookup_string_cache
);
137 linear_hlookup_string_cache
= NULL
;
139 g_hash_table_destroy (linear_hlookup_float_cache
);
140 linear_hlookup_float_cache
= NULL
;
142 g_hash_table_destroy (linear_hlookup_bool_cache
);
143 linear_hlookup_bool_cache
= NULL
;
147 g_hash_table_destroy (linear_vlookup_string_cache
);
148 linear_vlookup_string_cache
= NULL
;
150 g_hash_table_destroy (linear_vlookup_float_cache
);
151 linear_vlookup_float_cache
= NULL
;
153 g_hash_table_destroy (linear_vlookup_bool_cache
);
154 linear_vlookup_bool_cache
= NULL
;
158 g_hash_table_destroy (bisection_hlookup_string_cache
);
159 bisection_hlookup_string_cache
= NULL
;
161 g_hash_table_destroy (bisection_hlookup_float_cache
);
162 bisection_hlookup_float_cache
= NULL
;
164 g_hash_table_destroy (bisection_hlookup_bool_cache
);
165 bisection_hlookup_bool_cache
= NULL
;
169 g_hash_table_destroy (bisection_vlookup_string_cache
);
170 bisection_vlookup_string_cache
= NULL
;
172 g_hash_table_destroy (bisection_vlookup_float_cache
);
173 bisection_vlookup_float_cache
= NULL
;
175 g_hash_table_destroy (bisection_vlookup_bool_cache
);
176 bisection_vlookup_bool_cache
= NULL
;
180 if (!protect_string_pool
) {
181 g_string_chunk_free (lookup_string_pool
);
182 lookup_string_pool
= NULL
;
185 if (!protect_float_pool
) {
186 go_mem_chunk_destroy (lookup_float_pool
, TRUE
);
187 lookup_float_pool
= NULL
;
194 if (linear_hlookup_string_cache
)
197 total_cache_size
= 0;
199 if (!lookup_string_pool
)
200 lookup_string_pool
= g_string_chunk_new (100 * 1024);
202 if (!lookup_float_pool
)
204 go_mem_chunk_new ("lookup float pool",
206 sizeof (gnm_float
) * 1000);
208 linear_hlookup_string_cache
= g_hash_table_new_full
209 ((GHashFunc
)value_hash
,
210 (GEqualFunc
)value_equal
,
211 (GDestroyNotify
)value_release
,
212 (GDestroyNotify
)g_hash_table_destroy
);
213 linear_hlookup_float_cache
= g_hash_table_new_full
214 ((GHashFunc
)value_hash
,
215 (GEqualFunc
)value_equal
,
216 (GDestroyNotify
)value_release
,
217 (GDestroyNotify
)g_hash_table_destroy
);
218 linear_hlookup_bool_cache
= g_hash_table_new_full
219 ((GHashFunc
)value_hash
,
220 (GEqualFunc
)value_equal
,
221 (GDestroyNotify
)value_release
,
222 (GDestroyNotify
)g_hash_table_destroy
);
224 linear_vlookup_string_cache
= g_hash_table_new_full
225 ((GHashFunc
)value_hash
,
226 (GEqualFunc
)value_equal
,
227 (GDestroyNotify
)value_release
,
228 (GDestroyNotify
)g_hash_table_destroy
);
229 linear_vlookup_float_cache
= g_hash_table_new_full
230 ((GHashFunc
)value_hash
,
231 (GEqualFunc
)value_equal
,
232 (GDestroyNotify
)value_release
,
233 (GDestroyNotify
)g_hash_table_destroy
);
234 linear_vlookup_bool_cache
= g_hash_table_new_full
235 ((GHashFunc
)value_hash
,
236 (GEqualFunc
)value_equal
,
237 (GDestroyNotify
)value_release
,
238 (GDestroyNotify
)g_hash_table_destroy
);
240 bisection_hlookup_string_cache
= g_hash_table_new_full
241 ((GHashFunc
)value_hash
,
242 (GEqualFunc
)value_equal
,
243 (GDestroyNotify
)value_release
,
244 (GDestroyNotify
)lookup_bisection_cache_item_free
);
245 bisection_hlookup_float_cache
= g_hash_table_new_full
246 ((GHashFunc
)value_hash
,
247 (GEqualFunc
)value_equal
,
248 (GDestroyNotify
)value_release
,
249 (GDestroyNotify
)lookup_bisection_cache_item_free
);
250 bisection_hlookup_bool_cache
= g_hash_table_new_full
251 ((GHashFunc
)value_hash
,
252 (GEqualFunc
)value_equal
,
253 (GDestroyNotify
)value_release
,
254 (GDestroyNotify
)lookup_bisection_cache_item_free
);
256 bisection_vlookup_string_cache
= g_hash_table_new_full
257 ((GHashFunc
)value_hash
,
258 (GEqualFunc
)value_equal
,
259 (GDestroyNotify
)value_release
,
260 (GDestroyNotify
)lookup_bisection_cache_item_free
);
261 bisection_vlookup_float_cache
= g_hash_table_new_full
262 ((GHashFunc
)value_hash
,
263 (GEqualFunc
)value_equal
,
264 (GDestroyNotify
)value_release
,
265 (GDestroyNotify
)lookup_bisection_cache_item_free
);
266 bisection_vlookup_bool_cache
= g_hash_table_new_full
267 ((GHashFunc
)value_hash
,
268 (GEqualFunc
)value_equal
,
269 (GDestroyNotify
)value_release
,
270 (GDestroyNotify
)lookup_bisection_cache_item_free
);
276 if (total_cache_size
> 10 * GNM_DEFAULT_ROWS
) {
282 /* -------------------------------------------------------------------------- */
285 * We use an extra level of pointers for "cache" here to avoid problems
286 * in the case where we later prune the caches. The pointer to the
287 * GHashTable* will stay valid.
292 GHashTable
*h
, **cache
;
296 get_linear_lookup_cache (GnmFuncEvalInfo
*ei
,
297 GnmValue
const *data
, GnmValueType datatype
,
298 gboolean vertical
, LinearLookupInfo
*pinfo
)
302 pinfo
->is_new
= FALSE
;
303 pinfo
->key_copy
= NULL
;
309 pinfo
->cache
= vertical
310 ? &linear_vlookup_string_cache
311 : &linear_hlookup_string_cache
;
314 pinfo
->cache
= vertical
315 ? &linear_vlookup_float_cache
316 : &linear_hlookup_float_cache
;
319 pinfo
->cache
= vertical
320 ? &linear_vlookup_bool_cache
321 : &linear_hlookup_bool_cache
;
324 g_assert_not_reached ();
328 switch (data
->v_any
.type
) {
329 case VALUE_CELLRANGE
: {
331 GnmRangeRef
const *rr
= value_get_rangeref (data
);
333 gnm_rangeref_normalize (rr
, ei
->pos
, &sr
.sheet
, &end_sheet
,
335 if (sr
.sheet
!= end_sheet
)
336 return NULL
; /* 3D */
338 key
= pinfo
->key_copy
=
339 value_new_cellrange_r (sr
.sheet
, &sr
.range
);
349 pinfo
->h
= g_hash_table_lookup (*pinfo
->cache
, key
);
350 if (pinfo
->h
== NULL
) {
352 pinfo
->is_new
= TRUE
;
353 if (datatype
== VALUE_STRING
)
354 pinfo
->h
= g_hash_table_new (g_str_hash
, g_str_equal
);
356 pinfo
->h
= g_hash_table_new
357 ((GHashFunc
)gnm_float_hash
,
358 (GEqualFunc
)gnm_float_equal
);
359 if (!pinfo
->key_copy
)
360 pinfo
->key_copy
= value_dup (key
);
362 value_release (pinfo
->key_copy
);
363 pinfo
->key_copy
= NULL
;
370 linear_lookup_cache_commit (LinearLookupInfo
*pinfo
)
373 * It is possible to have an entry for the key already in the
374 * cache. In that case we fail to deduct from total_cache_size
375 * but it really doesn't matter.
377 total_cache_size
+= g_hash_table_size (pinfo
->h
);
379 g_hash_table_replace (*pinfo
->cache
, pinfo
->key_copy
, pinfo
->h
);
383 * We use an extra level of pointers for "cache" here to avoid problems
384 * in the case where we later prune the caches. The pointer to the
385 * GHashTable* will stay valid.
391 LookupBisectionCacheItem
*item
;
392 } BisectionLookupInfo
;
394 static LookupBisectionCacheItem
*
395 get_bisection_lookup_cache (GnmFuncEvalInfo
*ei
,
396 GnmValue
const *data
, GnmValueType datatype
,
397 gboolean vertical
, BisectionLookupInfo
*pinfo
)
401 pinfo
->is_new
= FALSE
;
402 pinfo
->key_copy
= NULL
;
406 /* The "&" here is for the pruning case. */
409 pinfo
->cache
= vertical
410 ? &bisection_vlookup_string_cache
411 : &bisection_hlookup_string_cache
;
414 pinfo
->cache
= vertical
415 ? &bisection_vlookup_float_cache
416 : &bisection_hlookup_float_cache
;
419 pinfo
->cache
= vertical
420 ? &bisection_vlookup_bool_cache
421 : &bisection_hlookup_bool_cache
;
424 g_assert_not_reached ();
428 switch (data
->v_any
.type
) {
429 case VALUE_CELLRANGE
: {
431 GnmRangeRef
const *rr
= value_get_rangeref (data
);
433 gnm_rangeref_normalize (rr
, ei
->pos
, &sr
.sheet
, &end_sheet
,
435 if (sr
.sheet
!= end_sheet
)
436 return NULL
; /* 3D */
438 key
= pinfo
->key_copy
= value_new_cellrange_r (sr
.sheet
, &sr
.range
);
448 pinfo
->item
= g_hash_table_lookup (*pinfo
->cache
, key
);
449 if (pinfo
->item
== NULL
) {
451 pinfo
->is_new
= TRUE
;
452 pinfo
->item
= g_new0 (LookupBisectionCacheItem
, 1);
453 if (!pinfo
->key_copy
)
454 pinfo
->key_copy
= value_dup (key
);
456 value_release (pinfo
->key_copy
);
457 pinfo
->key_copy
= NULL
;
464 bisection_lookup_cache_commit (BisectionLookupInfo
*pinfo
)
467 * It is possible to have an entry for the key already in the
468 * cache. In that case we fail to deduct from total_cache_size
469 * but it really doesn't matter.
471 total_cache_size
+= pinfo
->item
->n
;
473 g_hash_table_replace (*pinfo
->cache
, pinfo
->key_copy
, pinfo
->item
);
477 /* -------------------------------------------------------------------------- */
480 find_type_valid (GnmValue
const *find
)
482 /* Excel does not lookup errors or blanks */
483 if (VALUE_IS_EMPTY (find
))
485 return VALUE_IS_NUMBER (find
) || VALUE_IS_STRING (find
);
489 find_compare_type_valid (GnmValue
const *find
, GnmValue
const *val
)
494 if (find
->v_any
.type
== val
->v_any
.type
)
497 /* Note: floats do not match bools. */
502 /* -------------------------------------------------------------------------- */
505 is_pattern_match (const char *s
)
508 if (*s
== '*' || *s
== '?' || *s
== '~')
516 calc_length (GnmValue
const *data
, GnmEvalPos
const *ep
, gboolean vertical
)
519 return value_area_get_height (data
, ep
);
521 return value_area_get_width (data
, ep
);
524 static const GnmValue
*
525 get_elem (GnmValue
const *data
, guint ui
,
526 GnmEvalPos
const *ep
, gboolean vertical
)
529 return value_area_get_x_y (data
, 0, ui
, ep
);
531 return value_area_get_x_y (data
, ui
, 0, ep
);
534 enum { LOOKUP_NOT_THERE
= -1, LOOKUP_DATA_ERROR
= -2 };
538 find_index_linear_equal_string (GnmFuncEvalInfo
*ei
,
539 GnmValue
const *find
, GnmValue
const *data
,
546 LinearLookupInfo info
;
548 h
= get_linear_lookup_cache (ei
, data
, VALUE_STRING
, vertical
,
551 return LOOKUP_DATA_ERROR
;
554 int lp
, length
= calc_length (data
, ei
->pos
, vertical
);
556 protect_string_pool
++;
558 for (lp
= 0; lp
< length
; lp
++) {
559 GnmValue
const *v
= get_elem (data
, lp
, ei
->pos
, vertical
);
562 if (!find_compare_type_valid (find
, v
))
565 vc
= g_utf8_casefold (value_peek_string (v
), -1);
566 if (!g_hash_table_lookup_extended (h
, vc
, NULL
, NULL
)) {
567 char *sc
= g_string_chunk_insert (lookup_string_pool
, vc
);
568 g_hash_table_insert (h
, sc
, GINT_TO_POINTER (lp
));
574 linear_lookup_cache_commit (&info
);
576 protect_string_pool
--;
579 sc
= g_utf8_casefold (value_peek_string (find
), -1);
580 found
= g_hash_table_lookup_extended (h
, sc
, NULL
, &pres
);
583 return found
? GPOINTER_TO_INT (pres
) : LOOKUP_NOT_THERE
;
587 find_index_linear_equal_float (GnmFuncEvalInfo
*ei
,
588 GnmValue
const *find
, GnmValue
const *data
,
595 LinearLookupInfo info
;
597 /* This handles floats and bools, but with separate caches. */
598 h
= get_linear_lookup_cache (ei
, data
, find
->v_any
.type
, vertical
,
601 return LOOKUP_DATA_ERROR
;
604 int lp
, length
= calc_length (data
, ei
->pos
, vertical
);
606 protect_float_pool
++;
608 for (lp
= 0; lp
< length
; lp
++) {
609 GnmValue
const *v
= get_elem (data
, lp
, ei
->pos
, vertical
);
612 if (!find_compare_type_valid (find
, v
))
615 f2
= value_get_as_float (v
);
617 if (!g_hash_table_lookup_extended (h
, &f2
, NULL
, NULL
)) {
618 gnm_float
*fp
= go_mem_chunk_alloc (lookup_float_pool
);
620 g_hash_table_insert (h
, fp
, GINT_TO_POINTER (lp
));
624 linear_lookup_cache_commit (&info
);
626 protect_float_pool
--;
629 f
= value_get_as_float (find
);
630 found
= g_hash_table_lookup_extended (h
, &f
, NULL
, &pres
);
632 return found
? GPOINTER_TO_INT (pres
) : LOOKUP_NOT_THERE
;
636 find_index_linear (GnmFuncEvalInfo
*ei
,
637 GnmValue
const *find
, GnmValue
const *data
,
640 if (VALUE_IS_STRING (find
))
641 return find_index_linear_equal_string
642 (ei
, find
, data
, vertical
);
644 if (VALUE_IS_NUMBER (find
))
645 return find_index_linear_equal_float
646 (ei
, find
, data
, vertical
);
648 /* I don't think we can get here. */
649 return LOOKUP_DATA_ERROR
;
654 wildcard_string_match (const char *key
, LookupBisectionCacheItem
*bc
)
658 int i
, res
= LOOKUP_NOT_THERE
;
660 /* FIXME: Do we want to anchor at the end here? */
661 if (gnm_regcomp_XL (&rx
, key
, GO_REG_ICASE
, TRUE
, TRUE
) != GO_REG_OK
) {
662 g_warning ("Unexpected regcomp result");
663 return LOOKUP_DATA_ERROR
;
666 for (i
= 0; i
< bc
->n
; i
++) {
667 if (go_regexec (&rx
, bc
->data
[i
].u
.str
, 1, &rm
, 0) == GO_REG_OK
) {
668 res
= bc
->data
[i
].index
;
677 #undef DEBUG_BISECTION
680 find_index_bisection (GnmFuncEvalInfo
*ei
,
681 GnmValue
const *find
, GnmValue
const *data
,
682 gint type
, gboolean vertical
)
684 int high
, low
, lastlow
, res
;
685 LookupBisectionCacheItem
*bc
;
687 int (*comparer
) (const void *,const void *);
688 LookupBisectionCacheItemElem key
;
689 BisectionLookupInfo info
;
691 bc
= get_bisection_lookup_cache (ei
, data
, find
->v_any
.type
, vertical
,
694 return LOOKUP_DATA_ERROR
;
696 stringp
= VALUE_IS_STRING (find
);
697 comparer
= stringp
? bisection_compare_string
: bisection_compare_float
;
700 int lp
, length
= calc_length (data
, ei
->pos
, vertical
);
702 bc
->data
= g_new (LookupBisectionCacheItemElem
, length
+ 1);
705 protect_string_pool
++;
707 for (lp
= 0; lp
< length
; lp
++) {
708 GnmValue
const *v
= get_elem (data
, lp
, ei
->pos
, vertical
);
709 if (!find_compare_type_valid (find
, v
))
713 char *vc
= g_utf8_casefold (value_peek_string (v
), -1);
714 bc
->data
[bc
->n
].u
.str
= g_string_chunk_insert (lookup_string_pool
, vc
);
717 bc
->data
[bc
->n
].u
.f
= value_get_as_float (v
);
719 bc
->data
[bc
->n
].index
= lp
;
723 bc
->data
= g_renew (LookupBisectionCacheItemElem
,
726 bisection_lookup_cache_commit (&info
);
729 protect_string_pool
--;
732 #ifdef DEBUG_BISECTION
733 g_printerr ("find=%s\n", value_peek_string (find
));
737 return wildcard_string_match (value_peek_string (find
), bc
);
740 char *vc
= g_utf8_casefold (value_peek_string (find
), -1);
741 key
.u
.str
= g_string_chunk_insert (lookup_string_pool
, vc
);
744 #ifdef DEBUG_BISECTION
746 for (lp
= 0; lp
< bc
->n
; lp
++) {
747 g_printerr ("Data %d: %g\n", lp
, bc
->data
[lp
].u
.f
);
750 key
.u
.f
= value_get_as_float (find
);
753 lastlow
= LOOKUP_NOT_THERE
;
756 while (low
<= high
) {
757 int mid
= (low
+ high
) / 2;
758 int c
= comparer (&key
, bc
->data
+ mid
);
759 #ifdef DEBUG_BISECTION
761 g_printerr ("Comparing to #%d %g: %d\n",
762 mid
, bc
->data
[mid
].u
.f
, c
);
767 * For some sick reason, XLS wants to take the first
768 * or last match, depending on type. We could put
769 * this into the cache if we wanted to.
771 int dir
= (type
> 0 ? +1 : -1);
772 while (mid
+ dir
> 0 &&
774 comparer (&key
, bc
->data
+ (mid
+ dir
)) == 0)
776 return bc
->data
[mid
].index
;
779 c
= -c
; /* Reverse sorted data. */
789 res
= LOOKUP_NOT_THERE
;
793 #ifdef DEBUG_BISECTION
794 g_printerr ("res=%d\n", res
);
797 res
= bc
->data
[res
].index
;
798 #ifdef DEBUG_BISECTION
799 g_printerr (" index=%d\n", res
);
805 /***************************************************************************/
807 static GnmFuncHelp
const help_address
[] = {
808 { GNM_FUNC_HELP_NAME
, F_("ADDRESS:cell address as text")},
809 { GNM_FUNC_HELP_ARG
, F_("row_num:row number")},
810 { GNM_FUNC_HELP_ARG
, F_("col_num:column number")},
811 { GNM_FUNC_HELP_ARG
, F_("abs_num:1 for an absolute, 2 for a row absolute and column "
812 "relative, 3 for a row relative and column absolute, "
813 "and 4 for a relative reference; defaults to 1")},
814 { GNM_FUNC_HELP_ARG
, F_("a1:if TRUE, an A1-style reference is provided, "
815 "otherwise an R1C1-style reference; defaults to TRUE")},
816 { GNM_FUNC_HELP_ARG
, F_("text:name of the worksheet, defaults to no sheet")},
817 { GNM_FUNC_HELP_NOTE
, F_("If @{row_num} or @{col_num} is less than one, ADDRESS returns "
819 { GNM_FUNC_HELP_NOTE
, F_("If @{abs_num} is greater than 4 ADDRESS returns #VALUE!")},
820 { GNM_FUNC_HELP_EXAMPLES
, "=ADDRESS(5,4)" },
821 { GNM_FUNC_HELP_EXAMPLES
, "=ADDRESS(5,4,4)" },
822 { GNM_FUNC_HELP_EXAMPLES
, "=ADDRESS(5,4,4,FALSE)" },
823 { GNM_FUNC_HELP_EXAMPLES
, "=ADDRESS(5,4,4,FALSE,\"Sheet99\")" },
824 { GNM_FUNC_HELP_SEEALSO
, "COLUMNNUMBER"},
829 gnumeric_address (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
831 GnmConventionsOut out
;
837 const char *sheet_name
= args
[4] ? value_peek_string (args
[4]) : NULL
;
839 switch (args
[2] ? value_get_as_int (args
[2]) : 1) {
840 case 1: case 5: ref
.col_relative
= ref
.row_relative
= FALSE
; break;
842 ref
.col_relative
= TRUE
;
843 ref
.row_relative
= FALSE
;
846 ref
.col_relative
= FALSE
;
847 ref
.row_relative
= TRUE
;
849 case 4: case 8: ref
.col_relative
= ref
.row_relative
= TRUE
; break;
852 return value_new_error_VALUE (ei
->pos
);
856 sheet
= workbook_sheet_by_name (ei
->pos
->sheet
->workbook
,
858 /* For unknown or missing sheet, use current sheet. */
860 sheet
= ei
->pos
->sheet
;
863 row
= ref
.row
= value_get_as_int (args
[0]) - 1;
864 col
= ref
.col
= value_get_as_int (args
[1]) - 1;
865 out
.pp
= parse_pos_init_evalpos (&pp
, ei
->pos
);
866 out
.convs
= gnm_conventions_default
;
868 if (NULL
!= args
[3]) {
869 /* MS Excel is ridiculous. This is a special case */
870 if (!value_get_as_bool (args
[3], &err
)) {
871 out
.convs
= gnm_conventions_xls_r1c1
;
872 if (ref
.col_relative
)
873 col
= ei
->pos
->eval
.col
+ (++ref
.col
);
874 if (ref
.row_relative
)
875 row
= ei
->pos
->eval
.row
+ (++ref
.row
);
878 return value_new_error_VALUE (ei
->pos
);
881 if (col
< 0 || col
>= gnm_sheet_get_max_cols (sheet
))
882 return value_new_error_VALUE (ei
->pos
);
883 if (row
< 0 || row
>= gnm_sheet_get_max_rows (sheet
))
884 return value_new_error_VALUE (ei
->pos
);
886 if (!out
.convs
->r1c1_addresses
)
887 pp
.eval
.col
= pp
.eval
.row
= 0;
889 if (sheet_name
&& sheet_name
[0]) {
890 out
.accum
= gnm_expr_conv_quote (out
.convs
, sheet_name
);
891 g_string_append_c (out
.accum
, out
.convs
->sheet_name_sep
);
892 } else if (sheet_name
) {
893 /* A crazy case. Invalid name, but ends up unquoted. */
894 out
.accum
= g_string_new (NULL
);
895 g_string_append_c (out
.accum
, out
.convs
->sheet_name_sep
);
897 out
.accum
= g_string_new (NULL
);
898 cellref_as_string (&out
, &ref
, TRUE
);
900 return value_new_string_nocopy (g_string_free (out
.accum
, FALSE
));
903 /***************************************************************************/
905 static GnmFuncHelp
const help_areas
[] = {
906 { GNM_FUNC_HELP_NAME
, F_("AREAS:number of areas in @{reference}")},
907 { GNM_FUNC_HELP_ARG
, F_("reference:range")},
908 { GNM_FUNC_HELP_EXAMPLES
, "=AREAS(A1,B2,C3)" },
909 { GNM_FUNC_HELP_SEEALSO
, "ADDRESS,INDEX,INDIRECT,OFFSET"},
913 /* TODO : we need to rethink EXPR_SET as an operator vs a value type */
915 gnumeric_areas (GnmFuncEvalInfo
*ei
, int argc
, GnmExprConstPtr
const *argv
)
920 if (argc
!= 1 || argv
[0] == NULL
)
921 return value_new_error_VALUE (ei
->pos
);
925 switch (GNM_EXPR_GET_OPER (expr
)) {
926 case GNM_EXPR_OP_CONSTANT
:
927 if (VALUE_IS_ERROR (expr
->constant
.value
))
928 return value_dup (expr
->constant
.value
);
929 if (!VALUE_IS_CELLRANGE (expr
->constant
.value
))
932 case GNM_EXPR_OP_CELLREF
:
933 case GNM_EXPR_OP_RANGE_CTOR
:
934 case GNM_EXPR_OP_INTERSECT
:
938 case GNM_EXPR_OP_FUNCALL
: {
939 GnmValue
*v
= gnm_expr_eval (expr
, ei
->pos
,
940 GNM_EXPR_EVAL_PERMIT_NON_SCALAR
);
941 if (VALUE_IS_CELLRANGE (v
))
947 case GNM_EXPR_OP_NAME
:
948 if (expr_name_is_active (expr
->name
.name
)) {
949 expr
= expr
->name
.name
->texpr
->expr
;
954 case GNM_EXPR_OP_SET
:
955 res
= expr
->set
.argc
;
958 case GNM_EXPR_OP_PAREN
:
959 expr
= expr
->unary
.value
;
967 return value_new_int (res
);
968 return value_new_error_VALUE (ei
->pos
);
971 /***************************************************************************/
973 static GnmFuncHelp
const help_choose
[] = {
974 { GNM_FUNC_HELP_NAME
, F_("CHOOSE:the (@{index}+1)th argument")},
975 { GNM_FUNC_HELP_ARG
, F_("index:positive number")},
976 { GNM_FUNC_HELP_ARG
, F_("value1:first value")},
977 { GNM_FUNC_HELP_ARG
, F_("value2:second value")},
978 { GNM_FUNC_HELP_DESCRIPTION
, F_("CHOOSE returns its (@{index}+1)th argument.")},
979 { GNM_FUNC_HELP_NOTE
, F_("@{index} is truncated to an integer. If @{index} < 1 "
980 "or the truncated @{index} > number of values, CHOOSE "
982 { GNM_FUNC_HELP_EXAMPLES
, "=CHOOSE(3,\"Apple\",\"Orange\",\"Grape\",\"Perry\")" },
983 { GNM_FUNC_HELP_SEEALSO
, "IF"},
988 gnumeric_choose (GnmFuncEvalInfo
*ei
, int argc
, GnmExprConstPtr
const *argv
)
995 return value_new_error_VALUE (ei
->pos
);
997 #warning TODO add array eval
998 v
= gnm_expr_eval (argv
[0], ei
->pos
, GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
1002 if (!VALUE_IS_FLOAT (v
)) {
1004 return value_new_error_VALUE (ei
->pos
);
1007 index
= value_get_as_int (v
);
1009 for (i
= 1; i
< argc
; i
++) {
1012 return gnm_expr_eval (argv
[i
], ei
->pos
,
1013 GNM_EXPR_EVAL_PERMIT_NON_SCALAR
);
1015 return value_new_error_VALUE (ei
->pos
);
1018 /***************************************************************************/
1020 static GnmFuncHelp
const help_vlookup
[] = {
1021 { GNM_FUNC_HELP_NAME
, F_("VLOOKUP:search the first column of @{range} for @{value}")},
1022 { GNM_FUNC_HELP_ARG
, F_("value:search value")},
1023 { GNM_FUNC_HELP_ARG
, F_("range:range to search")},
1024 { GNM_FUNC_HELP_ARG
, F_("column:1-based column offset indicating the return values")},
1025 { GNM_FUNC_HELP_ARG
, F_("approximate:if false, an exact match of @{value} "
1026 "must be found; defaults to TRUE")},
1027 { GNM_FUNC_HELP_ARG
, F_("as_index:if true, the 0-based row offset is "
1028 "returned; defaults to FALSE")},
1029 { GNM_FUNC_HELP_DESCRIPTION
, F_("VLOOKUP function finds the row in @{range} that has a first "
1030 "cell similar to @{value}. If @{approximate} is not true it "
1031 "finds the row with an exact equality. If @{approximate} is "
1032 "true, it finds the last row with first value less than "
1034 "@{value}. If @{as_index} is true the 0-based row offset "
1036 { GNM_FUNC_HELP_NOTE
, F_("If @{approximate} is true, "
1037 "then the values must be sorted in order of ascending value.")},
1038 { GNM_FUNC_HELP_NOTE
, F_("VLOOKUP returns #REF! if @{column} falls outside @{range}.")},
1039 { GNM_FUNC_HELP_SEEALSO
, "HLOOKUP"},
1040 { GNM_FUNC_HELP_END
}
1044 gnumeric_vlookup (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
1046 GnmValue
const *find
= args
[0];
1047 int col_idx
= value_get_as_int (args
[2]);
1048 gboolean approx
= args
[3] ? value_get_as_checked_bool (args
[3]) : TRUE
;
1049 gboolean as_index
= args
[4] && value_get_as_checked_bool (args
[4]);
1051 gboolean is_string_match
;
1053 if (!find_type_valid (find
))
1054 return value_new_error_NA (ei
->pos
);
1056 return value_new_error_VALUE (ei
->pos
);
1057 if (col_idx
> value_area_get_width (args
[1], ei
->pos
))
1058 return value_new_error_REF (ei
->pos
);
1060 is_string_match
= (!approx
&&
1061 VALUE_IS_STRING (find
) &&
1062 is_pattern_match (value_peek_string (find
)));
1064 index
= is_string_match
1065 ? find_index_bisection (ei
, find
, args
[1], 0, TRUE
)
1067 ? find_index_bisection (ei
, find
, args
[1], 1, TRUE
)
1068 : find_index_linear (ei
, find
, args
[1], TRUE
));
1069 if (index
== LOOKUP_DATA_ERROR
)
1070 return value_new_error_VALUE (ei
->pos
); /* 3D */
1073 return value_new_int (index
);
1078 v
= value_area_fetch_x_y (args
[1], col_idx
-1, index
, ei
->pos
);
1079 g_return_val_if_fail (v
!= NULL
, NULL
);
1080 return value_dup (v
);
1083 return value_new_error_NA (ei
->pos
);
1086 /***************************************************************************/
1088 static GnmFuncHelp
const help_hlookup
[] = {
1089 { GNM_FUNC_HELP_NAME
, F_("HLOOKUP:search the first row of @{range} for @{value}")},
1090 { GNM_FUNC_HELP_ARG
, F_("value:search value")},
1091 { GNM_FUNC_HELP_ARG
, F_("range:range to search")},
1092 { GNM_FUNC_HELP_ARG
, F_("row:1-based row offset indicating the return values ")},
1093 { GNM_FUNC_HELP_ARG
, F_("approximate:if false, an exact match of @{value} "
1094 "must be found; defaults to TRUE")},
1095 { GNM_FUNC_HELP_ARG
, F_("as_index:if true, the 0-based column offset is "
1096 "returned; defaults to FALSE")},
1097 { GNM_FUNC_HELP_DESCRIPTION
, F_("HLOOKUP function finds the row in @{range} that has a first "
1098 "cell similar to @{value}. If @{approximate} is not true it "
1099 "finds the column with an exact equality. If @{approximate} is "
1100 "true, it finds the last column with first value less than or "
1102 "@{value}. If @{as_index} is true the 0-based column offset "
1104 { GNM_FUNC_HELP_NOTE
, F_("If @{approximate} is true, "
1105 "then the values must be sorted in order of ascending value.")},
1106 { GNM_FUNC_HELP_NOTE
, F_("HLOOKUP returns #REF! if @{row} falls outside @{range}.")},
1107 { GNM_FUNC_HELP_SEEALSO
, "VLOOKUP"},
1108 { GNM_FUNC_HELP_END
}
1112 gnumeric_hlookup (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
1114 GnmValue
const *find
= args
[0];
1115 int row_idx
= value_get_as_int (args
[2]);
1116 gboolean approx
= args
[3] ? value_get_as_checked_bool (args
[3]) : TRUE
;
1117 gboolean as_index
= args
[4] && value_get_as_checked_bool (args
[4]);
1119 gboolean is_string_match
;
1121 if (!find_type_valid (find
))
1122 return value_new_error_NA (ei
->pos
);
1124 return value_new_error_VALUE (ei
->pos
);
1125 if (row_idx
> value_area_get_height (args
[1], ei
->pos
))
1126 return value_new_error_REF (ei
->pos
);
1128 is_string_match
= (!approx
&&
1129 VALUE_IS_STRING (find
) &&
1130 is_pattern_match (value_peek_string (find
)));
1132 index
= is_string_match
1133 ? find_index_bisection (ei
, find
, args
[1], 0, FALSE
)
1135 ? find_index_bisection (ei
, find
, args
[1], 1, FALSE
)
1136 : find_index_linear (ei
, find
, args
[1], FALSE
));
1137 if (index
== LOOKUP_DATA_ERROR
)
1138 return value_new_error_VALUE (ei
->pos
); /* 3D */
1141 return value_new_int (index
);
1146 v
= value_area_fetch_x_y (args
[1], index
, row_idx
-1, ei
->pos
);
1147 g_return_val_if_fail (v
!= NULL
, NULL
);
1148 return value_dup (v
);
1151 return value_new_error_NA (ei
->pos
);
1154 /***************************************************************************/
1156 static GnmFuncHelp
const help_lookup
[] = {
1157 { GNM_FUNC_HELP_NAME
, F_("LOOKUP:contents of @{vector2} at the corresponding location to "
1158 "@{value} in @{vector1}")},
1159 { GNM_FUNC_HELP_ARG
, F_("value:value to look up")},
1160 { GNM_FUNC_HELP_ARG
, F_("vector1:range to search:")},
1161 { GNM_FUNC_HELP_ARG
, F_("vector2:range of return values")},
1162 { GNM_FUNC_HELP_DESCRIPTION
, F_("If @{vector1} has more rows than columns, LOOKUP searches "
1163 "the first row of @{vector1}, otherwise the first column. "
1164 "If @{vector2} is omitted the return value is taken from "
1165 "the last row or column of @{vector1}.")},
1166 { GNM_FUNC_HELP_NOTE
, F_("If LOOKUP can't find @{value} it uses the largest value less "
1168 { GNM_FUNC_HELP_NOTE
, F_("The data must be sorted.")},
1169 { GNM_FUNC_HELP_NOTE
, F_("If @{value} is smaller than the first value it returns #N/A.")},
1170 { GNM_FUNC_HELP_NOTE
, F_("If the corresponding location does not exist in @{vector2}, "
1171 "it returns #N/A.")},
1172 { GNM_FUNC_HELP_SEEALSO
, "VLOOKUP,HLOOKUP"},
1173 { GNM_FUNC_HELP_END
}
1177 gnumeric_lookup (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
1180 GnmValue
const *v
= args
[0];
1181 GnmValue
const *area
= args
[1];
1182 GnmValue
const *lookup
= args
[2];
1183 GnmValue
*result
, *xlookup
= NULL
;
1184 gboolean vertical_search
= (value_area_get_width (area
, ei
->pos
) <
1185 value_area_get_height (area
, ei
->pos
));
1186 gboolean vertical_lookup
;
1187 gboolean is_cellrange
;
1189 if (!find_type_valid (v
))
1190 return value_new_error_NA (ei
->pos
);
1193 int width
= value_area_get_width (lookup
, ei
->pos
);
1194 int height
= value_area_get_height (lookup
, ei
->pos
);
1196 if (width
> 1 && height
> 1) {
1197 return value_new_error_NA (ei
->pos
);
1200 vertical_lookup
= (width
< height
);
1201 is_cellrange
= VALUE_IS_CELLRANGE (lookup
);
1206 * Extend the lookup range all the way. This is utterly
1207 * insane, but Excel really does reach beyond the stated
1210 range_init_value (&r
, lookup
, &ei
->pos
->eval
);
1211 range_normalize (&r
);
1212 if (vertical_lookup
)
1213 r
.end
.row
= gnm_sheet_get_last_row (ei
->pos
->sheet
);
1215 r
.end
.col
= gnm_sheet_get_last_col (ei
->pos
->sheet
);
1217 lookup
= xlookup
= value_new_cellrange_r (ei
->pos
->sheet
, &r
);
1222 vertical_lookup
= vertical_search
;
1223 is_cellrange
= FALSE
; /* Doesn't matter. */
1226 index
= find_index_bisection (ei
, v
, area
, 1, vertical_search
);
1229 int width
= value_area_get_width (lookup
, ei
->pos
);
1230 int height
= value_area_get_height (lookup
, ei
->pos
);
1233 if (vertical_lookup
)
1234 x
= width
- 1, y
= index
;
1236 x
= index
, y
= height
- 1;
1238 if (x
< width
&& y
< height
)
1239 result
= value_dup (value_area_fetch_x_y (lookup
, x
, y
, ei
->pos
));
1240 else if (is_cellrange
)
1241 /* We went off the sheet. */
1242 result
= value_new_int (0);
1244 /* We went outside an array. */
1245 result
= value_new_error_NA (ei
->pos
);
1247 result
= value_new_error_NA (ei
->pos
);
1249 value_release (xlookup
);
1254 /***************************************************************************/
1256 static GnmFuncHelp
const help_match
[] = {
1257 { GNM_FUNC_HELP_NAME
, F_("MATCH:the index of @{seek} in @{vector}")},
1258 { GNM_FUNC_HELP_ARG
, F_("seek:value to find")},
1259 { GNM_FUNC_HELP_ARG
, F_("vector:n by 1 or 1 by n range to be searched")},
1260 { GNM_FUNC_HELP_ARG
, F_("type:+1 (the default) to find the largest value \xe2\x89\xa4 @{seek}, "
1261 "0 to find the first value = @{seek}, or"
1262 "-1 to find the smallest value \xe2\x89\xa5 @{seek}")},
1263 { GNM_FUNC_HELP_DESCRIPTION
, F_("MATCH searches @{vector} for @{seek} and returns the 1-based index.")},
1264 { GNM_FUNC_HELP_NOTE
, F_(" For @{type} = -1 the data must be sorted in descending order; "
1265 "for @{type} = +1 the data must be sorted in ascending order.")},
1266 { GNM_FUNC_HELP_NOTE
, F_("If @{seek} could not be found, #N/A is returned.")},
1267 { GNM_FUNC_HELP_NOTE
, F_("If @{vector} is neither n by 1 nor 1 by n, #N/A is returned.")},
1268 { GNM_FUNC_HELP_SEEALSO
, "LOOKUP"},
1269 { GNM_FUNC_HELP_END
}
1273 gnumeric_match (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
1275 int type
, index
= -1;
1276 int width
= value_area_get_width (args
[1], ei
->pos
);
1277 int height
= value_area_get_height (args
[1], ei
->pos
);
1279 GnmValue
const *find
= args
[0];
1280 gboolean is_string_match
;
1282 if (!find_type_valid (find
))
1283 return value_new_error_NA (ei
->pos
);
1285 if (width
> 1 && height
> 1)
1286 return value_new_error_NA (ei
->pos
);
1287 vertical
= (width
> 1 ? FALSE
: TRUE
);
1289 type
= VALUE_IS_EMPTY (args
[2]) ? 1 : value_get_as_int (args
[2]);
1291 is_string_match
= (type
== 0 &&
1292 VALUE_IS_STRING (find
) &&
1293 is_pattern_match (value_peek_string (find
)));
1295 if (type
== 0 && !is_string_match
)
1296 index
= find_index_linear (ei
, find
, args
[1], vertical
);
1298 index
= find_index_bisection (ei
, find
, args
[1], type
,
1302 case LOOKUP_DATA_ERROR
: return value_new_error_VALUE (ei
->pos
);
1303 case LOOKUP_NOT_THERE
: return value_new_error_NA (ei
->pos
);
1304 default: return value_new_int (index
+ 1);
1308 /***************************************************************************/
1310 static GnmFuncHelp
const help_indirect
[] = {
1311 { GNM_FUNC_HELP_NAME
, F_("INDIRECT:contents of the cell pointed to by the @{ref_text} string")},
1312 { GNM_FUNC_HELP_ARG
, F_("ref_text:textual reference")},
1313 { GNM_FUNC_HELP_ARG
, F_("format:if true, @{ref_text} is given in A1-style, "
1314 "otherwise it is given in R1C1 style; defaults to true")},
1315 { GNM_FUNC_HELP_NOTE
, F_("If @{ref_text} is not a valid reference in the style determined "
1316 "by @{format}, INDIRECT returns #REF!")},
1317 { GNM_FUNC_HELP_EXAMPLES
, "If A1 contains 3.14 and A2 contains \"A1\", then\n"
1318 "INDIRECT(A2) equals 3.14." },
1319 { GNM_FUNC_HELP_SEEALSO
, "AREAS,INDEX,CELL"},
1320 { GNM_FUNC_HELP_END
}
1324 gnumeric_indirect (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
1327 GnmValue
*res
= NULL
;
1328 GnmExprTop
const *texpr
;
1329 char const *text
= value_peek_string (args
[0]);
1330 GnmConventions
const *convs
= gnm_conventions_default
;
1332 if (args
[1] && !value_get_as_checked_bool (args
[1]))
1333 convs
= gnm_conventions_xls_r1c1
;
1335 texpr
= gnm_expr_parse_str (text
,
1336 parse_pos_init_evalpos (&pp
, ei
->pos
),
1337 GNM_EXPR_PARSE_DEFAULT
, convs
, NULL
);
1339 if (texpr
!= NULL
) {
1340 res
= gnm_expr_top_get_range (texpr
);
1341 gnm_expr_top_unref (texpr
);
1343 return (res
!= NULL
) ? res
: value_new_error_REF (ei
->pos
);
1346 /*****************************************************************************/
1348 static GnmFuncHelp
const help_index
[] = {
1349 { GNM_FUNC_HELP_NAME
, F_("INDEX:reference to a cell in the given @{array}")},
1350 { GNM_FUNC_HELP_ARG
, F_("array:cell or inline array")},
1351 { GNM_FUNC_HELP_ARG
, F_("row:desired row, defaults to 1")},
1352 { GNM_FUNC_HELP_ARG
, F_("col:desired column, defaults to 1")},
1353 { GNM_FUNC_HELP_ARG
, F_("area:from which area to select a cell, defaults to 1")},
1354 { GNM_FUNC_HELP_DESCRIPTION
, F_("INDEX gives a reference to a cell "
1355 "in the given @{array}. "
1356 "The cell is selected by @{row} and @{col}, "
1357 "which count the rows and "
1358 "columns in the array.")},
1359 { GNM_FUNC_HELP_NOTE
, F_("If the reference falls outside the range of @{array},"
1360 " INDEX returns #REF!")},
1361 { GNM_FUNC_HELP_EXAMPLES
, F_("Let us assume that the cells A1, A2, ..., A5"
1362 " contain numbers 11.4, "
1363 "17.3, 21.3, 25.9, and 40.1. Then INDEX(A1:A5,4,1,1) equals 25.9") },
1364 { GNM_FUNC_HELP_END
}
1368 gnumeric_index (GnmFuncEvalInfo
*ei
, int argc
, GnmExprConstPtr
const *argv
)
1370 GnmExpr
const *source
;
1371 int elem
[3] = { 0, 0, 0 };
1376 if (argc
== 0 || argc
> 4)
1377 return value_new_error_VALUE (ei
->pos
);
1380 /* This is crazy. */
1381 while (GNM_EXPR_GET_OPER (source
) == GNM_EXPR_OP_PAREN
)
1382 source
= source
->unary
.value
;
1384 v
= gnm_expr_eval (source
, ei
->pos
, GNM_EXPR_EVAL_PERMIT_NON_SCALAR
);
1385 if (VALUE_IS_ERROR (v
))
1388 for (i
= 0; i
+ 1 < argc
&& i
< (int)G_N_ELEMENTS (elem
); i
++) {
1389 GnmValue
*vi
= value_coerce_to_number (
1390 gnm_expr_eval (argv
[i
+ 1], ei
->pos
, GNM_EXPR_EVAL_SCALAR_NON_EMPTY
),
1396 elem
[i
] = value_get_as_int (vi
) - 1;
1400 if (GNM_EXPR_GET_OPER (source
) == GNM_EXPR_OP_SET
) {
1401 int w
= value_area_get_height (v
, ei
->pos
);
1404 if (i
< 0 || i
>= w
) {
1406 return value_new_error_REF (ei
->pos
);
1408 vi
= value_dup (value_area_fetch_x_y (v
, 0, i
, ei
->pos
));
1411 } else if (elem
[2] != 0) {
1413 return value_new_error_REF (ei
->pos
);
1417 elem
[1] >= value_area_get_width (v
, ei
->pos
) ||
1419 elem
[0] >= value_area_get_height (v
, ei
->pos
)) {
1421 return value_new_error_REF (ei
->pos
);
1424 #warning Work out a way to fall back to returning value when a reference is unneeded
1425 if (VALUE_IS_CELLRANGE (v
)) {
1426 GnmRangeRef
const *src
= &v
->v_range
.cell
;
1427 GnmCellRef a
= src
->a
, b
= src
->b
;
1428 Sheet
*start_sheet
, *end_sheet
;
1431 gnm_rangeref_normalize (src
, ei
->pos
, &start_sheet
, &end_sheet
, &r
);
1432 r
.start
.row
+= elem
[0];
1433 r
.start
.col
+= elem
[1];
1434 a
.row
= r
.start
.row
; if (a
.row_relative
) a
.row
-= ei
->pos
->eval
.row
;
1435 b
.row
= r
.start
.row
; if (b
.row_relative
) b
.row
-= ei
->pos
->eval
.row
;
1436 a
.col
= r
.start
.col
; if (a
.col_relative
) a
.col
-= ei
->pos
->eval
.col
;
1437 b
.col
= r
.start
.col
; if (b
.col_relative
) b
.col
-= ei
->pos
->eval
.col
;
1438 res
= value_new_cellrange_unsafe (&a
, &b
);
1439 } else if (VALUE_IS_ARRAY (v
))
1440 res
= value_dup (value_area_fetch_x_y (v
, elem
[1], elem
[0], ei
->pos
));
1442 res
= value_new_error_REF (ei
->pos
);
1447 /***************************************************************************/
1449 static GnmFuncHelp
const help_column
[] = {
1450 { GNM_FUNC_HELP_NAME
, F_("COLUMN:vector of column numbers") },
1451 { GNM_FUNC_HELP_ARG
, F_("x:reference, defaults to the position of the current expression") },
1452 { GNM_FUNC_HELP_DESCRIPTION
, F_("COLUMN function returns a Nx1 array containing the sequence "
1454 "from the first column to the last column of @{x}.") },
1455 { GNM_FUNC_HELP_NOTE
, F_("If @{x} is neither an array nor a reference nor a range, "
1456 "returns #VALUE!") },
1457 { GNM_FUNC_HELP_EXAMPLES
, "=COLUMN(A1:C4)" },
1458 { GNM_FUNC_HELP_EXAMPLES
, "=COLUMN(A:C)" },
1459 { GNM_FUNC_HELP_EXAMPLES
, F_("column() in G13 equals 7.") },
1460 { GNM_FUNC_HELP_SEEALSO
, "COLUMNS,ROW,ROWS" },
1461 { GNM_FUNC_HELP_END
}
1465 gnumeric_column (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
1469 GnmValue
const *ref
= args
[0];
1472 col
= ei
->pos
->eval
.col
+ 1; /* user visible counts from 0 */
1473 if (eval_pos_is_array_context (ei
->pos
))
1474 width
= ei
->pos
->array
->cols
;
1476 return value_new_int (col
);
1477 } else if (VALUE_IS_CELLRANGE (ref
)) {
1481 gnm_rangeref_normalize (&ref
->v_range
.cell
, ei
->pos
, &tmp
, &tmp
, &r
);
1482 col
= r
.start
.col
+ 1;
1483 width
= range_width (&r
);
1485 return value_new_error_VALUE (ei
->pos
);
1488 return value_new_int (col
);
1490 res
= value_new_array (width
, 1);
1491 for (i
= width
; i
-- > 0 ; )
1492 value_array_set (res
, i
, 0, value_new_int (col
+ i
));
1496 /***************************************************************************/
1498 static GnmFuncHelp
const help_columnnumber
[] = {
1499 { GNM_FUNC_HELP_NAME
, F_("COLUMNNUMBER:column number for the given column called @{name}")},
1500 { GNM_FUNC_HELP_ARG
, F_("name:column name such as \"IV\"")},
1501 { GNM_FUNC_HELP_NOTE
, F_("If @{name} is invalid, COLUMNNUMBER returns #VALUE!")},
1502 { GNM_FUNC_HELP_EXAMPLES
, "=COLUMNNUMBER(\"E\")" },
1503 { GNM_FUNC_HELP_SEEALSO
, "ADDRESS"},
1504 { GNM_FUNC_HELP_END
}
1508 gnumeric_columnnumber (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
1510 char const *name
= value_peek_string (args
[0]);
1512 unsigned char relative
;
1513 char const *after
= col_parse (name
,
1514 gnm_sheet_get_size (ei
->pos
->sheet
),
1517 if (after
== NULL
|| *after
)
1518 return value_new_error_VALUE (ei
->pos
);
1520 return value_new_int (colno
+ 1);
1523 /***************************************************************************/
1525 static GnmFuncHelp
const help_columns
[] = {
1526 { GNM_FUNC_HELP_NAME
, F_("COLUMNS:number of columns in @{reference}")},
1527 { GNM_FUNC_HELP_ARG
, F_("reference:array or area")},
1528 { GNM_FUNC_HELP_NOTE
, F_("If @{reference} is neither an array nor a reference nor a range, "
1529 "COLUMNS returns #VALUE!")},
1530 { GNM_FUNC_HELP_EXAMPLES
, "=COLUMNS(H2:J3)" },
1531 { GNM_FUNC_HELP_SEEALSO
, "COLUMN,ROW,ROWS"},
1532 { GNM_FUNC_HELP_END
}
1536 gnumeric_columns (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
1538 return value_new_int (value_area_get_width (args
[0], ei
->pos
));
1541 /***************************************************************************/
1543 static GnmFuncHelp
const help_offset
[] = {
1544 { GNM_FUNC_HELP_NAME
, F_("OFFSET:an offset cell range")},
1545 { GNM_FUNC_HELP_ARG
, F_("range:reference or range")},
1546 { GNM_FUNC_HELP_ARG
, F_("row:number of rows to offset @{range}")},
1547 { GNM_FUNC_HELP_ARG
, F_("col:number of columns to offset @{range}")},
1548 { GNM_FUNC_HELP_ARG
, F_("height:height of the offset range, defaults to height of @{range}")},
1549 { GNM_FUNC_HELP_ARG
, F_("width:width of the offset range, defaults to width of @{range}")},
1550 { GNM_FUNC_HELP_DESCRIPTION
, F_("OFFSET returns the cell range starting at offset "
1551 "(@{row},@{col}) from @{range} of height @{height} and "
1552 "width @{width}.")},
1553 { GNM_FUNC_HELP_NOTE
, F_("If @{range} is neither a reference nor a range, OFFSET "
1554 "returns #VALUE!")},
1555 { GNM_FUNC_HELP_SEEALSO
, "COLUMN,COLUMNS,ROWS,INDEX,INDIRECT,ADDRESS"},
1556 { GNM_FUNC_HELP_END
}
1560 gnumeric_offset (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
1563 int row_offset
, col_offset
;
1565 /* Copy the references so we can change them */
1566 GnmCellRef a
= args
[0]->v_range
.cell
.a
;
1567 GnmCellRef b
= args
[0]->v_range
.cell
.b
;
1569 row_offset
= value_get_as_int (args
[1]);
1570 col_offset
= value_get_as_int (args
[2]);
1571 a
.row
+= row_offset
;
1572 a
.col
+= col_offset
;
1573 if (a
.row
< 0 || a
.col
< 0 ||
1574 a
.row
>= gnm_sheet_get_max_rows (ei
->pos
->sheet
) || a
.col
>= gnm_sheet_get_max_cols (ei
->pos
->sheet
))
1575 return value_new_error_REF (ei
->pos
);
1577 if (args
[3] != NULL
) {
1578 tmp
= value_get_as_int (args
[3]);
1580 return value_new_error_VALUE (ei
->pos
);
1581 b
.row
= a
.row
+ tmp
- 1;
1583 b
.row
+= row_offset
;
1584 if (b
.col
< 0 || b
.row
>= gnm_sheet_get_max_rows (ei
->pos
->sheet
))
1585 return value_new_error_REF (ei
->pos
);
1586 if (args
[4] != NULL
) {
1587 tmp
= value_get_as_int (args
[4]);
1589 return value_new_error_VALUE (ei
->pos
);
1590 b
.col
= a
.col
+ tmp
- 1;
1592 b
.col
+= col_offset
;
1593 if (b
.col
< 0 || b
.col
>= gnm_sheet_get_max_cols (ei
->pos
->sheet
))
1594 return value_new_error_REF (ei
->pos
);
1596 return value_new_cellrange_unsafe (&a
, &b
);
1599 /***************************************************************************/
1601 static GnmFuncHelp
const help_row
[] = {
1602 { GNM_FUNC_HELP_NAME
, F_("ROW:vector of row numbers") },
1603 { GNM_FUNC_HELP_ARG
, F_("x:reference, defaults to the position of the current expression") },
1604 { GNM_FUNC_HELP_DESCRIPTION
, F_("ROW function returns a 1xN array containing the "
1605 "sequence of integers "
1606 "from the first row to the last row of @{x}.") },
1607 { GNM_FUNC_HELP_NOTE
, F_("If @{x} is neither an array nor a reference nor a range, "
1608 "returns #VALUE!") },
1609 { GNM_FUNC_HELP_EXAMPLES
, "=ROW(A1:D3)" },
1610 { GNM_FUNC_HELP_EXAMPLES
, "=ROW(1:3)" },
1611 { GNM_FUNC_HELP_SEEALSO
, "COLUMN,COLUMNS,ROWS" },
1612 { GNM_FUNC_HELP_END
}
1616 gnumeric_row (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
1620 GnmValue
const *ref
= args
[0];
1623 row
= ei
->pos
->eval
.row
+ 1; /* user visible counts from 0 */
1624 if (ei
->pos
->array
!= NULL
)
1625 n
= ei
->pos
->array
->rows
;
1627 return value_new_int (row
);
1628 } else if (VALUE_IS_CELLRANGE (ref
)) {
1632 gnm_rangeref_normalize (&ref
->v_range
.cell
, ei
->pos
, &tmp
, &tmp
, &r
);
1633 row
= r
.start
.row
+ 1;
1634 n
= range_height (&r
);
1636 return value_new_error_VALUE (ei
->pos
);
1639 return value_new_int (row
);
1641 res
= value_new_array (1, n
);
1642 for (i
= n
; i
-- > 0 ; )
1643 value_array_set (res
, 0, i
, value_new_int (row
+ i
));
1647 /***************************************************************************/
1649 static GnmFuncHelp
const help_rows
[] = {
1650 { GNM_FUNC_HELP_NAME
, F_("ROWS:number of rows in @{reference}")},
1651 { GNM_FUNC_HELP_ARG
, F_("reference:array, reference, or range")},
1652 { GNM_FUNC_HELP_NOTE
, F_("If @{reference} is neither an array nor a reference nor a range, "
1653 "ROWS returns #VALUE!")},
1654 { GNM_FUNC_HELP_EXAMPLES
, "=ROWS(H7:I13)" },
1655 { GNM_FUNC_HELP_SEEALSO
, "COLUMN,COLUMNS,ROW"},
1656 { GNM_FUNC_HELP_END
}
1660 gnumeric_rows (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
1662 return value_new_int (value_area_get_height (args
[0], ei
->pos
));
1665 /***************************************************************************/
1667 static GnmFuncHelp
const help_sheets
[] = {
1668 { GNM_FUNC_HELP_NAME
, F_("SHEETS:number of sheets in @{reference}")},
1669 { GNM_FUNC_HELP_ARG
, F_("reference:array, reference, or range, defaults to the maximum range")},
1670 { GNM_FUNC_HELP_NOTE
, F_("If @{reference} is neither an array nor a reference nor a range, "
1671 "SHEETS returns #VALUE!")},
1672 { GNM_FUNC_HELP_EXAMPLES
, "=SHEETS()" },
1673 { GNM_FUNC_HELP_SEEALSO
, "COLUMNS,ROWS"},
1674 { GNM_FUNC_HELP_END
}
1678 gnumeric_sheets (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
1680 Workbook
const *wb
= ei
->pos
->sheet
->workbook
;
1681 GnmValue
const *v
= args
[0];
1684 if (VALUE_IS_CELLRANGE (v
)) {
1685 GnmRangeRef
const *r
= &v
->v_range
.cell
;
1686 int ans_min
, ans_max
, a
, b
;
1688 a
= r
->a
.sheet
? r
->a
.sheet
->index_in_wb
: -1;
1689 b
= r
->b
.sheet
? r
->b
.sheet
->index_in_wb
: -1;
1691 ans_min
= MIN (a
,b
);
1692 ans_max
= MAX (a
,b
);
1695 return value_new_int (1);
1697 return value_new_int (ans_max
- ans_min
+ 1);
1699 return value_new_int (1);
1701 return value_new_int (workbook_sheet_count (wb
));
1704 /***************************************************************************/
1705 static GnmFuncHelp
const help_sheet
[] = {
1706 { GNM_FUNC_HELP_NAME
, F_("SHEET:sheet number of @{reference}")},
1707 { GNM_FUNC_HELP_ARG
, F_("reference:reference or literal sheet name, defaults to the current sheet")},
1708 { GNM_FUNC_HELP_NOTE
, F_("If @{reference} is neither a reference "
1709 "nor a literal sheet name, "
1710 "SHEET returns #VALUE!")},
1711 { GNM_FUNC_HELP_EXAMPLES
, "=SHEET()" },
1712 { GNM_FUNC_HELP_EXAMPLES
, "=SHEET(\"Sheet1\")" },
1713 { GNM_FUNC_HELP_SEEALSO
, "SHEETS,ROW,COLUMNNUMBER"},
1714 { GNM_FUNC_HELP_END
}
1718 gnumeric_sheet (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
1720 Workbook
const *wb
= ei
->pos
->sheet
->workbook
;
1721 GnmValue
const *v
= args
[0];
1725 if (VALUE_IS_CELLRANGE (v
)) {
1726 GnmRangeRef
const *r
= &v
->v_range
.cell
;
1729 a
= r
->a
.sheet
? r
->a
.sheet
->index_in_wb
: -1;
1730 b
= r
->b
.sheet
? r
->b
.sheet
->index_in_wb
: -1;
1732 if (a
== -1 && b
== -1)
1733 n
= ei
->pos
->sheet
->index_in_wb
;
1734 else if (a
== b
|| a
* b
< 0)
1737 return value_new_error_NUM (ei
->pos
);
1738 } else if (VALUE_IS_STRING (v
)) {
1739 Sheet
*sheet
= workbook_sheet_by_name
1740 (wb
, value_peek_string (v
));
1742 return value_new_error_NUM (ei
->pos
);
1743 n
= sheet
->index_in_wb
;
1745 return value_new_error_VALUE (ei
->pos
);
1747 n
= ei
->pos
->sheet
->index_in_wb
;
1749 return value_new_int (1 + n
);
1751 /***************************************************************************/
1753 static GnmFuncHelp
const help_hyperlink
[] = {
1754 { GNM_FUNC_HELP_NAME
, F_("HYPERLINK:second or first arguments")},
1755 { GNM_FUNC_HELP_ARG
, F_("link_location:string")},
1756 { GNM_FUNC_HELP_ARG
, F_("label:string, optional")},
1757 { GNM_FUNC_HELP_DESCRIPTION
, F_("HYPERLINK function currently returns its 2nd argument, "
1758 "or if that is omitted the 1st argument.")},
1759 { GNM_FUNC_HELP_EXAMPLES
, "=HYPERLINK(\"www.gnome.org\",\"GNOME\")" },
1760 { GNM_FUNC_HELP_EXAMPLES
, "=HYPERLINK(\"www.gnome.org\")" },
1761 { GNM_FUNC_HELP_END
}
1765 gnumeric_hyperlink (GnmFuncEvalInfo
*ei
, GnmValue
const * const *args
)
1767 GnmValue
const * v
= args
[1];
1770 return value_dup (v
);
1773 /***************************************************************************/
1775 static GnmFuncHelp
const help_transpose
[] = {
1776 { GNM_FUNC_HELP_NAME
, F_("TRANSPOSE:the transpose of @{matrix}")},
1777 { GNM_FUNC_HELP_ARG
, F_("matrix:range")},
1778 { GNM_FUNC_HELP_SEEALSO
, "FLIP,MMULT"},
1779 { GNM_FUNC_HELP_END
}
1784 gnumeric_transpose (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1786 GnmEvalPos
const * const ep
= ei
->pos
;
1787 GnmValue
const * const matrix
= argv
[0];
1791 int const cols
= value_area_get_width (matrix
, ep
);
1792 int const rows
= value_area_get_height (matrix
, ep
);
1794 /* Return the value directly for a singleton */
1795 if (rows
== 1 && cols
== 1)
1796 return value_dup (value_area_get_x_y (matrix
, 0, 0, ep
));
1798 /* REMEMBER this is a transpose */
1799 res
= value_new_array_non_init (rows
, cols
);
1801 for (r
= 0; r
< rows
; ++r
) {
1802 res
->v_array
.vals
[r
] = g_new (GnmValue
*, cols
);
1803 for (c
= 0; c
< cols
; ++c
)
1804 res
->v_array
.vals
[r
][c
] = value_dup(
1805 value_area_get_x_y (matrix
, c
, r
, ep
));
1811 /***************************************************************************/
1813 static GnmFuncHelp
const help_flip
[] = {
1814 { GNM_FUNC_HELP_NAME
, F_("FLIP:@{matrix} flipped")},
1815 { GNM_FUNC_HELP_ARG
, F_("matrix:range")},
1816 { GNM_FUNC_HELP_ARG
, F_("vertical:if true, @{matrix} is flipped vertically, "
1817 "otherwise horizontally; defaults to TRUE")},
1818 { GNM_FUNC_HELP_SEEALSO
, "TRANSPOSE"},
1819 { GNM_FUNC_HELP_END
}
1824 gnumeric_flip (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1826 GnmEvalPos
const * const ep
= ei
->pos
;
1827 GnmValue
const * const matrix
= argv
[0];
1830 gboolean vertical
= argv
[1] ? value_get_as_checked_bool (argv
[1]) : TRUE
;
1832 int const cols
= value_area_get_width (matrix
, ep
);
1833 int const rows
= value_area_get_height (matrix
, ep
);
1835 /* Return the value directly for a singleton */
1836 if (rows
== 1 && cols
== 1)
1837 return value_dup (value_area_get_x_y (matrix
, 0, 0, ep
));
1839 res
= value_new_array_non_init (cols
, rows
);
1842 for (c
= 0; c
< cols
; ++c
) {
1843 res
->v_array
.vals
[c
] = g_new (GnmValue
*, rows
);
1844 for (r
= 0; r
< rows
; ++r
)
1845 res
->v_array
.vals
[c
][rows
- r
- 1] = value_dup
1846 (value_area_get_x_y (matrix
, c
, r
, ep
));
1849 for (c
= 0; c
< cols
; ++c
) {
1850 res
->v_array
.vals
[c
] = g_new (GnmValue
*, rows
);
1851 for (r
= 0; r
< rows
; ++r
)
1852 res
->v_array
.vals
[c
][r
] = value_dup
1853 (value_area_get_x_y (matrix
, cols
- c
- 1, r
, ep
));
1859 /***************************************************************************/
1860 static GnmFuncHelp
const help_array
[] = {
1861 { GNM_FUNC_HELP_NAME
, F_("ARRAY:vertical array of the arguments")},
1862 { GNM_FUNC_HELP_ARG
, F_("v:value")},
1863 { GNM_FUNC_HELP_SEEALSO
, "TRANSPOSE"},
1864 { GNM_FUNC_HELP_END
}
1869 callback_function_array (GnmEvalPos
const *ep
, GnmValue
const *value
, void *closure
)
1871 GSList
**list
= closure
;
1873 *list
= g_slist_prepend
1874 (*list
, value
? value_dup (value
) : value_new_empty ());
1879 gnumeric_array (GnmFuncEvalInfo
*ei
, int argc
, GnmExprConstPtr
const *argv
)
1881 GSList
*list
= NULL
, *l
;
1883 GnmValue
*val
= function_iterate_argument_values
1884 (ei
->pos
, callback_function_array
, &list
,
1885 argc
, argv
, FALSE
, CELL_ITER_ALL
);
1888 g_slist_free_full (list
, (GDestroyNotify
)value_release
);
1891 list
= g_slist_reverse (list
);
1892 len
= g_slist_length (list
);
1895 g_slist_free_full (list
, (GDestroyNotify
)value_release
);
1896 return value_new_error_VALUE (ei
->pos
);
1901 g_slist_free (list
);
1905 val
= value_new_array_empty (1, len
);
1907 for (l
= list
, i
= 0; l
!= NULL
; l
= l
->next
, i
++)
1908 val
->v_array
.vals
[0][i
] = l
->data
;
1910 g_slist_free (list
);
1915 /***************************************************************************/
1917 static GnmFuncHelp
const help_sort
[] = {
1918 { GNM_FUNC_HELP_NAME
, F_("SORT:sorted list of numbers as vertical array")},
1919 { GNM_FUNC_HELP_ARG
, F_("ref:list of numbers")},
1920 { GNM_FUNC_HELP_ARG
, F_("order:0 (descending order) or 1 (ascending order); defaults to 0")},
1921 { GNM_FUNC_HELP_NOTE
, F_("Strings, booleans, and empty cells are ignored.")},
1922 { GNM_FUNC_HELP_EXAMPLES
, F_("SORT({4,3,5}) evaluates to {5,4,3}")},
1923 { GNM_FUNC_HELP_SEEALSO
, ("ARRAY")},
1924 { GNM_FUNC_HELP_END
}
1928 gnumeric_sort (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1932 GnmValue
*result
= NULL
;
1934 xs
= collect_floats_value (argv
[0], ei
->pos
,
1935 COLLECT_IGNORE_STRINGS
|
1936 COLLECT_IGNORE_BOOLS
|
1937 COLLECT_IGNORE_BLANKS
|
1943 switch (argv
[1] ? value_get_as_int (argv
[1]) : 0) {
1945 result
= value_new_array_empty (1, n
);
1947 for (i
= 0, j
= n
- 1; i
< n
; i
++, j
--)
1948 result
->v_array
.vals
[0][i
] = value_new_float (xs
[j
]);
1951 result
= value_new_array_empty (1, n
);
1953 for (i
= 0; i
< n
; i
++)
1954 result
->v_array
.vals
[0][i
] = value_new_float (xs
[i
]);
1957 result
= value_new_error_VALUE (ei
->pos
);
1966 /***************************************************************************/
1968 GnmFuncDescriptor
const lookup_functions
[] = {
1969 { "address", "ff|fbs",
1970 help_address
, gnumeric_address
, NULL
, NULL
, NULL
,
1971 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_EXHAUSTIVE
},
1973 help_areas
, NULL
, gnumeric_areas
, NULL
, NULL
,
1974 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1976 help_choose
, NULL
, gnumeric_choose
, NULL
, NULL
,
1977 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1979 help_column
, gnumeric_column
, NULL
, NULL
, NULL
,
1980 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1981 { "columnnumber", "s",
1982 help_columnnumber
, gnumeric_columnnumber
, NULL
, NULL
, NULL
,
1983 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1985 help_columns
, gnumeric_columns
, NULL
, NULL
, NULL
,
1986 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1987 { "hlookup", "EAf|bb",
1988 help_hlookup
, gnumeric_hlookup
, NULL
, NULL
, NULL
,
1989 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1990 { "hyperlink", "S|S",
1991 help_hyperlink
, gnumeric_hyperlink
, NULL
, NULL
, NULL
,
1992 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_SUBSET
, GNM_FUNC_TEST_STATUS_BASIC
},
1993 { "indirect", "s|b",
1994 help_indirect
, gnumeric_indirect
, NULL
, NULL
, NULL
,
1995 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1997 help_index
, NULL
, gnumeric_index
, NULL
, NULL
,
1998 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
2000 help_lookup
, gnumeric_lookup
, NULL
, NULL
, NULL
,
2001 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
2003 help_match
, gnumeric_match
, NULL
, NULL
, NULL
,
2004 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
2005 { "offset", "rff|ff",
2006 help_offset
, gnumeric_offset
, NULL
, NULL
, NULL
,
2007 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
2009 help_row
, gnumeric_row
, NULL
, NULL
, NULL
,
2010 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
2012 help_rows
, gnumeric_rows
, NULL
, NULL
, NULL
,
2013 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
2015 help_sheets
, gnumeric_sheets
, NULL
, NULL
, NULL
,
2016 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
2018 help_sheet
, gnumeric_sheet
, NULL
, NULL
, NULL
,
2019 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
2021 help_sort
, gnumeric_sort
, NULL
, NULL
, NULL
,
2022 GNM_FUNC_RETURNS_NON_SCALAR
, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
2024 help_transpose
, gnumeric_transpose
, NULL
, NULL
, NULL
,
2025 GNM_FUNC_RETURNS_NON_SCALAR
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
2026 { "vlookup", "EAf|bb",
2027 help_vlookup
, gnumeric_vlookup
, NULL
, NULL
, NULL
,
2028 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
2030 help_array
, NULL
, gnumeric_array
, NULL
, NULL
,
2031 GNM_FUNC_RETURNS_NON_SCALAR
, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
2033 help_flip
, gnumeric_flip
, NULL
, NULL
, NULL
,
2034 GNM_FUNC_RETURNS_NON_SCALAR
, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
2039 G_MODULE_EXPORT
void
2040 go_plugin_init (GOPlugin
*plugin
, GOCmdContext
*cc
)
2042 debug_lookup_caches
= gnm_debug_flag ("lookup-caches");
2043 g_signal_connect (gnm_app_get_app (), "recalc-clear-caches",
2044 G_CALLBACK (clear_caches
), NULL
);
2047 G_MODULE_EXPORT
void
2048 go_plugin_shutdown (GOPlugin
*plugin
, GOCmdContext
*cc
)
2050 g_signal_handlers_disconnect_by_func (gnm_app_get_app (),
2051 G_CALLBACK (clear_caches
), NULL
);
2053 if (protect_string_pool
) {
2054 g_printerr ("Imbalance in string pool: %d\n", (int)protect_string_pool
);
2055 protect_string_pool
= 0;
2057 if (protect_float_pool
) {
2058 g_printerr ("Imbalance in float pool: %d\n", (int)protect_float_pool
);
2059 protect_float_pool
= 0;