static GnmValue * gnumeric_hdate_year (GnmFuncEvalInfo * ei, GnmValue const * const *argv) { int year, month, day; int hyear, hmonth, hday; gnumeric_hdate_get_date (argv, &year, &month, &day); if (0 != hdate_gdate_to_hdate (day, month, year, &hday, &hmonth, &hyear)) return value_new_error_VALUE (ei->pos); return value_new_int (hyear); }
static GnmValue * callback_function_and (GnmEvalPos const *ep, GnmValue const *value, void *closure) { int *result = closure; if (!VALUE_IS_STRING (value)) { gboolean err; *result = value_get_as_bool (value, &err) && *result; if (err) return value_new_error_VALUE (ep); } return NULL; }
static GnmValue * gnumeric_hdate_heb (GnmFuncEvalInfo * ei, GnmValue const * const *argv) { int year, month, day; int hyear, hmonth, hday; GString *res; gnumeric_hdate_get_date (argv, &year, &month, &day); if (0 != hdate_gdate_to_hdate (day, month, year, &hday, &hmonth, &hyear)) return value_new_error_VALUE (ei->pos); res = g_string_new (NULL); build_hdate (res, hyear, hmonth, hday); return value_new_string_nocopy (g_string_free (res, FALSE)); }
/* * We need a horizontal strip of 5 cells containing: * * 0. Formula cell. * 1: X value cell. * 2: Y target value. * 3: Min value. * 4: Max value. */ static void dialog_goal_seek_test (Sheet *sheet, const GnmRange *range) { GoalSeekState state; GnmCell *cell; int r, c; GoalSeekStatus status; g_return_if_fail (range->start.row == range->end.row); g_return_if_fail (range->start.col + 4 == range->end.col); memset (&state, 0, sizeof (state)); r = range->start.row; c = range->start.col; state.wb = sheet->workbook; state.sheet = sheet; state.set_cell = sheet_cell_fetch (sheet, c + 0, r); state.change_cell = sheet_cell_fetch (sheet, c + 1, r); state.old_value = value_dup (state.change_cell->value); cell = sheet_cell_fetch (sheet, c + 2, r); state.target_value = value_get_as_float (cell->value); cell = sheet_cell_fetch (sheet, c + 3, r); state.xmin = VALUE_IS_EMPTY (cell->value) ? -max_range_val : value_get_as_float (cell->value); cell = sheet_cell_fetch (sheet, c + 4, r); state.xmax = VALUE_IS_EMPTY (cell->value) ? max_range_val : value_get_as_float (cell->value); status = gnumeric_goal_seek (&state); if (status == GOAL_SEEK_OK) { /* Nothing */ } else { sheet_cell_set_value (state.change_cell, value_new_error_VALUE (NULL)); } value_release (state.old_value); }
static GnmValue * gnumeric_and (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv) { int result = -1; /* Yes, AND is actually strict. */ GnmValue *v = function_iterate_argument_values (ei->pos, callback_function_and, &result, argc, argv, TRUE, CELL_ITER_IGNORE_BLANK); if (v != NULL) return v; /* See if there was any value worth using */ if (result == -1) return value_new_error_VALUE (ei->pos); return value_new_bool (result); }
static GnmValue * gnumeric_hdate (GnmFuncEvalInfo * ei, GnmValue const * const *argv) { int year, month, day; int hyear, hmonth, hday; char *res; gnumeric_hdate_get_date (argv, &year, &month, &day); if (0 != hdate_gdate_to_hdate (day, month, year, &hday, &hmonth, &hyear)) return value_new_error_VALUE (ei->pos); res = g_strdup_printf ("%d %s %d", hday + 1, hdate_get_hebrew_month_name (hmonth), hyear); return value_new_string_nocopy (res); }
GnmValue * gnm_ifs_func (GPtrArray *data, GPtrArray *crits, GnmValue const *vals, float_range_function_t fun, GnmStdError err, GnmEvalPos const *ep, CollectFlags flags) { int sx, sy, x, y; unsigned ui, N = 0, nalloc = 0; gnm_float *xs = NULL; GnmValue *res = NULL; gnm_float fres; g_return_val_if_fail (data->len == crits->len, NULL); if (flags & ~(COLLECT_IGNORE_STRINGS | COLLECT_IGNORE_BOOLS | COLLECT_IGNORE_BLANKS | COLLECT_IGNORE_ERRORS)) { g_warning ("unsupported flags in gnm_ifs_func %x", flags); } sx = value_area_get_width (vals, ep); sy = value_area_get_height (vals, ep); for (ui = 0; ui < data->len; ui++) { GnmValue const *datai = g_ptr_array_index (data, ui); if (value_area_get_width (datai, ep) != sx || value_area_get_height (datai, ep) != sy) return value_new_error_VALUE (ep); } for (y = 0; y < sy; y++) { for (x = 0; x < sx; x++) { GnmValue const *v; gboolean match = TRUE; for (ui = 0; match && ui < crits->len; ui++) { GnmCriteria *crit = g_ptr_array_index (crits, ui); GnmValue const *datai = g_ptr_array_index (data, ui); v = value_area_get_x_y (datai, x, y, ep); match = crit->fun (v, crit); } if (!match) continue; // Match. Maybe collect the data point. v = value_area_get_x_y (vals, x, y, ep); if ((flags & COLLECT_IGNORE_STRINGS) && VALUE_IS_STRING (v)) continue; if ((flags & COLLECT_IGNORE_BOOLS) && VALUE_IS_BOOLEAN (v)) continue; if ((flags & COLLECT_IGNORE_BLANKS) && VALUE_IS_EMPTY (v)) continue; if ((flags & COLLECT_IGNORE_ERRORS) && VALUE_IS_ERROR (v)) continue; if (VALUE_IS_ERROR (v)) { res = value_dup (v); goto out; } if (N >= nalloc) { nalloc = (2 * nalloc) + 100; xs = g_renew (gnm_float, xs, nalloc); } xs[N++] = value_get_as_float (v); } } if (fun (xs, N, &fres)) { res = value_new_error_std (ep, err); } else res = value_new_float (fres); out: g_free (xs); return res; }
/** * collect_float_pairs: * @v0: value describing first data range * @v1: value describing second data range * @ep: evaluation position * @flags: flags describing how to handle value types * @xs0: return location for first data vector * @xs1: return location for second data vector * @n: return location for number of data points * @constp: optional return location for an indicator of the return vectors * being owned by this function as opposed to the normal copy owned by the * caller. * * If @n is not positive upon return, no data has been allocated. * If @n is negative upon return, the two ranges had different * sizes. */ 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) { GnmValue *key_x = NULL; GnmValue *key_y = NULL; PairsFloatsCacheEntry *ce = NULL; gboolean use_cache, free_keys = TRUE; if (vx->type == VALUE_CELLRANGE) key_x = get_single_cache_key_from_value (vx, ep); if (vy->type == VALUE_CELLRANGE) key_y = get_single_cache_key_from_value (vy, ep); if ((use_cache = (key_x && key_y))) ce = get_or_fake_pairs_cache_entry (key_x, key_y, flags, ep); if (!ce) { ce = collect_float_pairs_ce (vx, vy, ep, flags); if (use_cache) { PairsFloatsCacheEntry *ce2; ce->vx = key_x; ce->vy = key_y; free_keys = FALSE; /* * We looked for the entry earlier and it was not there. * However, sub-calculation might have added it so be careful * to adjust sizes and replace the not-so-old entry. * See bug 627079. */ ce2 = g_hash_table_lookup (pairs_floats_cache, ce); if (ce2) total_cache_size -= 1 + ce2->n; g_hash_table_replace (pairs_floats_cache, ce, ce); total_cache_size += 1 + ce->n; } } if (free_keys) { value_release (key_x); value_release (key_y); } if (ce == NULL) return value_new_error_VALUE (ep); else { if (ce->error) { if (use_cache) return value_dup (ce->error); else { GnmValue *ret = ce->error; ce->error = NULL; pairs_floats_cache_entry_free (ce); return ret; } } *n = ce->n; if (ce->n <= 0) { if (!use_cache) pairs_floats_cache_entry_free (ce); *xs0 = NULL; *xs1 = NULL; if (constp) *constp = FALSE; return NULL; } if (use_cache) { if (constp) { *xs0 = ce->data_x; *xs1 = ce->data_y; *constp = TRUE; } else { *xs0 = g_memdup (ce->data_x, *n * sizeof (gnm_float)); *xs1 = g_memdup (ce->data_y, *n * sizeof (gnm_float)); } } else { if (constp) *constp = FALSE; *xs0 = ce->data_x; *xs1 = ce->data_y; ce->data_x = NULL; ce->data_y = NULL; pairs_floats_cache_entry_free (ce); } return NULL; } }
static GnmValue * callback_function_collect (GnmEvalPos const *ep, GnmValue const *value, void *closure) { gnm_float x = 0; collect_floats_t *cl = closure; gboolean ignore = FALSE; switch (value ? value->type : VALUE_EMPTY) { case VALUE_EMPTY: if (cl->flags & COLLECT_IGNORE_BLANKS) ignore = TRUE; else if (cl->flags & COLLECT_ZERO_BLANKS) x = 0; else return value_new_error_VALUE (ep); break; case VALUE_BOOLEAN: if (cl->flags & COLLECT_IGNORE_BOOLS) ignore = TRUE; else if (cl->flags & COLLECT_ZEROONE_BOOLS) x = value_get_as_float (value); else return value_new_error_VALUE (ep); break; case VALUE_CELLRANGE : case VALUE_ARRAY : /* Ranges and arrays are not singleton values treat as errors */ case VALUE_ERROR: if (cl->flags & COLLECT_IGNORE_ERRORS) ignore = TRUE; else if (cl->flags & COLLECT_ZERO_ERRORS) x = 0; else return value_new_error_VALUE (ep); break; case VALUE_FLOAT: x = value_get_as_float (value); break; case VALUE_STRING: if (cl->flags & COLLECT_COERCE_STRINGS) { GnmValue *vc = format_match_number (value_peek_string (value), NULL, cl->date_conv); gboolean bad = !vc || VALUE_IS_BOOLEAN (vc); if (vc) { x = value_get_as_float (vc); value_release (vc); } else x = 0; if (bad) return value_new_error_VALUE (ep); } else if (cl->flags & COLLECT_IGNORE_STRINGS) ignore = TRUE; else if (cl->flags & COLLECT_ZERO_STRINGS) x = 0; else return value_new_error_VALUE (ep); break; default: g_warning ("Trouble in callback_function_collect. (%d)", value->type); ignore = TRUE; } if (ignore) { if (cl->flags & COLLECT_INFO) cl->info = g_slist_prepend (cl->info, GUINT_TO_POINTER (cl->count)); else { return NULL; } } if (cl->count == cl->alloc_count) { cl->alloc_count = cl->alloc_count * 2 + 20; cl->data = g_renew (gnm_float, cl->data, cl->alloc_count); } cl->data[cl->count++] = x; return NULL; }
GnmValue * get_vdb (gnm_float cost, gnm_float salvage, gnm_float life, gnm_float start_period, gnm_float end_period, gnm_float factor, gboolean flag) { gnm_float fVdb; gnm_float fIntStart = gnm_floor (start_period); gnm_float fIntEnd = gnm_ceil (end_period); fVdb = 0.0; if ( flag ) { int i, nLoopStart, nLoopEnd; if (fIntEnd > G_MAXINT || fIntEnd - fIntStart > 10000 /* arbitrary */) return value_new_error_VALUE (NULL); nLoopStart = (int) fIntStart; nLoopEnd = (int) fIntEnd; for (i = nLoopStart + 1; i <= nLoopEnd; i++) { gnm_float fTerm; fTerm = ScGetGDA (cost, salvage, life, i, factor); if ( i == nLoopStart+1 ) fTerm *= ( MIN( end_period, fIntStart + 1.0 ) - start_period ); else if ( i == nLoopEnd ) fTerm *= ( end_period + 1.0 - fIntEnd ); fVdb += fTerm; } } else { gnm_float fPart = 0; double fIntEnd = gnm_ceil (end_period); if (start_period > fIntStart) { // First period is partial. Calculate the excess as // the pro-rata value of the first period as-if it // was not partial. double tempcost = cost - ScInterVDB( cost, salvage, life, life, fIntStart, factor); fPart += (start_period - fIntStart) * ScInterVDB( tempcost, salvage, life, life - fIntStart, 1, factor); } if (end_period < fIntEnd) { // Last period is partial. Calculate the excess as // the pro-rata value of the last period as-if it // was not partial. double em1 = fIntEnd - 1; // Start of last period double tempcost = cost - ScInterVDB (cost, salvage, life, life, em1, factor); fPart += (fIntEnd - end_period) * ScInterVDB (tempcost, salvage, life, life - em1, 1, factor); } cost -= ScInterVDB (cost, salvage, life, life, fIntStart, factor); fVdb = ScInterVDB (cost, salvage, life, life - fIntStart, fIntEnd - fIntStart, factor); fVdb -= fPart; } return value_new_float (fVdb); }
/** * FIXME: In the long term this needs optimising. **/ static GnmValue * val_to_base (GnmFuncEvalInfo *ei, GnmValue const *value, GnmValue const *aplaces, int src_base, int dest_base, gnm_float min_value, gnm_float max_value, Val2BaseFlags flags) { int digit, min, max, places; gnm_float v; GString *buffer; GnmValue *vstring = NULL; g_return_val_if_fail (src_base > 1 && src_base <= 36, value_new_error_VALUE (ei->pos)); g_return_val_if_fail (dest_base > 1 && dest_base <= 36, value_new_error_VALUE (ei->pos)); /* func.c ought to take care of this. */ if (VALUE_IS_BOOLEAN (value)) return value_new_error_VALUE (ei->pos); if (aplaces && VALUE_IS_BOOLEAN (aplaces)) return value_new_error_VALUE (ei->pos); switch (value->type) { default: return value_new_error_NUM (ei->pos); case VALUE_STRING: if (flags & V2B_STRINGS_GENERAL) { vstring = format_match_number (value_peek_string (value), NULL, workbook_date_conv (ei->pos->sheet->workbook)); if (!vstring || !VALUE_IS_FLOAT (vstring)) { value_release (vstring); return value_new_error_VALUE (ei->pos); } } else { char const *str = value_peek_string (value); size_t len; gboolean hsuffix = FALSE; char *err; if ((flags & V2B_STRINGS_BLANK_ZERO) && *str == 0) str = "0"; /* This prevents leading spaces, signs, etc, and "". */ if (!g_ascii_isalnum (*str)) return value_new_error_NUM (ei->pos); len = strlen (str); /* We check length in bytes. Since we are going to require nothing but digits, that is fine. */ if ((flags & V2B_STRINGS_MAXLEN) && len > 10) return value_new_error_NUM (ei->pos); if (flags & V2B_STRINGS_0XH) { if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) str += 2; else if (str[len - 1] == 'h' || str[len - 1] == 'H') hsuffix = TRUE; } v = g_ascii_strtoll (str, &err, src_base); if (err == str || err[hsuffix] != 0) return value_new_error_NUM (ei->pos); if (v < min_value || v > max_value) return value_new_error_NUM (ei->pos); break; } /* Fall through. */ case VALUE_FLOAT: { gnm_float val = gnm_fake_trunc (value_get_as_float (vstring ? vstring : value)); char buf[GNM_MANT_DIG + 10]; char *err; value_release (vstring); if (val < min_value || val > max_value) return value_new_error_NUM (ei->pos); g_ascii_formatd (buf, sizeof (buf) - 1, "%.0" GNM_FORMAT_f, val); v = g_ascii_strtoll (buf, &err, src_base); if (*err != 0) return value_new_error_NUM (ei->pos); break; } } if (src_base != 10) { gnm_float b10 = gnm_pow (src_base, 10); if (v >= b10 / 2) /* N's complement */ v = v - b10; } if (flags & V2B_NUMBER) return value_new_float (v); if (v < 0) { min = 1; max = 10; v += gnm_pow (dest_base, max); } else { if (v == 0) min = max = 1; else min = max = (int)(gnm_log (v + 0.5) / gnm_log (dest_base)) + 1; } if (aplaces) { gnm_float fplaces = value_get_as_float (aplaces); if (fplaces < min || fplaces > 10) return value_new_error_NUM (ei->pos); places = (int)fplaces; if (v >= 0 && places > max) max = places; } else places = 1; buffer = g_string_sized_new (max); g_string_set_size (buffer, max); for (digit = max - 1; digit >= 0; digit--) { int thisdigit = gnm_fmod (v + 0.5, dest_base); v = gnm_floor ((v + 0.5) / dest_base); buffer->str[digit] = thisdigit["0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"]; } return value_new_string_nocopy (g_string_free (buffer, FALSE)); }
static GnmValue * gnumeric_interpolation (GnmFuncEvalInfo *ei, GnmValue const * const *argv) { gnm_float *vals0, *vals1, *vals2, *fres; int n0, n2; int interp; GnmValue *error = NULL; GnmValue *res; CollectFlags flags; GnmEvalPos const * const ep = ei->pos; GnmValue const * const PtInterpolation = argv[2]; int r, i; GSList *missing2 = NULL, *missing; INTERPPROC interpproc = NULL; gboolean constp = FALSE; /* argv[2] */ int const cols = value_area_get_width (PtInterpolation, ep); int const rows = value_area_get_height (PtInterpolation, ep); if (rows == 0 || cols != 1) { res = value_new_error_std (ei->pos, GNM_ERROR_VALUE); return res; } flags = COLLECT_IGNORE_BLANKS | COLLECT_IGNORE_STRINGS | COLLECT_IGNORE_BOOLS | COLLECT_IGNORE_ERRORS; vals2 = collect_floats_value_with_info (PtInterpolation, ei->pos, flags, &n2, &missing2, &error); if (error) { g_slist_free (missing2); return error; } /* argv[3] */ if (argv[3]) { interp = (int) gnm_floor (value_get_as_float (argv[3])); if (interp < 0 || interp > INTERPOLATION_SPLINE_AVG) { g_slist_free (missing2); g_free (vals2); return value_new_error_VALUE (ei->pos); } } else interp = INTERPOLATION_LINEAR; switch (interp) { case INTERPOLATION_LINEAR: interpproc = linear_interpolation; break; case INTERPOLATION_LINEAR_AVG: interpproc = linear_averaging; n2--; break; case INTERPOLATION_STAIRCASE: interpproc = staircase_interpolation; break; case INTERPOLATION_STAIRCASE_AVG: interpproc = staircase_averaging; n2--; break; case INTERPOLATION_SPLINE: interpproc = spline_interpolation; break; case INTERPOLATION_SPLINE_AVG: interpproc = spline_averaging; n2--; break; } if (n2 <= 0) { g_slist_free (missing2); g_free (vals2); return value_new_error_std (ei->pos, GNM_ERROR_VALUE); } /* argv[0] & argv[1] */ flags = COLLECT_IGNORE_BLANKS | COLLECT_IGNORE_STRINGS | COLLECT_IGNORE_BOOLS; error = collect_float_pairs (argv[0], argv[1], ei->pos, flags, &vals0, &vals1, &n0, &constp); if (error) { g_slist_free (missing2); g_free (vals2); return error; } /* Check whether the abscissae are increasing, if not order them */ if (!gnm_range_increasing (vals0, n0)) { gboolean switched = TRUE; if (constp) { vals0 = g_memdup (vals0, sizeof(gnm_float) * n0); vals1 = g_memdup (vals1, sizeof(gnm_float) * n0); constp = FALSE; } while (switched) { gnm_float *val; switched = FALSE; for (i = 1, val = vals0; i < n0; i++, val++) { if (*val == *(val + 1)) { res = value_new_error_std (ei->pos, GNM_ERROR_VALUE) ; goto done; } if (*val > *(val + 1)) { gnm_float v = *val; *val = *(val + 1); *(val + 1) = v; v = *(vals1 + i); *(vals1 + i) = *(vals1 + i - 1); *(vals1 + i - 1) = v; switched = TRUE; } } } } { int n = n2; if (missing2) gnm_strip_missing (vals2, &n, missing2); res = value_new_array_non_init (1 , n2); i = 0; res->v_array.vals[0] = g_new (GnmValue *, n2); fres = interpproc (vals0, vals1, n0, vals2, n); missing = missing2; if (fres) { i = 0; for (r = 0 ; r < n2; ++r) if (missing && r == GPOINTER_TO_INT (missing->data)) { missing = missing->next; res->v_array.vals[0][r] = value_new_error_std (ei->pos, GNM_ERROR_VALUE); } else res->v_array.vals[0][r] = value_new_float (fres[i++]); g_free (fres); } else { for( r = 0 ; r < n2; ++r) res->v_array.vals[0][r] = value_new_error_std (ei->pos, GNM_ERROR_VALUE); } } done: g_slist_free (missing2); if (!constp) { g_free (vals0); g_free (vals1); } g_free (vals2); return res; }